Arduino Leonardoで多目的ツールの製作

![arduinoLeonardoToolsV2_00.jpg](https://qiita-image-store.s3.amazonaws.com/0/221758/a779d94e-2c54-0e7d-9bb1-6e6058059b92.jpeg) ![arduinoLeonardoToolsV2_01.jpg](https://qiita-image-store.s3.amazonaws.com/0/221758/6e5a0044-9fcc-4668-3c2e-b12ae3f02ffc.jpeg)

##1. はじめに
[Lチカ](https://www.google.co.jp/search?hl=ja&as_q=L%E3%83%81%E3%82%AB)と[Hello, World.](https://www.google.co.jp/search?hl=ja&as_q=Hello%2C+World.)の同時実行のような小ネタからExcelやパソコン作業の省力化、さらには自動測定の治具までこなす[Arduino Leonardo](https://www.google.co.jp/search?hl=ja&as_q=arduino+leonardo)を使用した多目的ツールです。

機能例：

- スイッチひと押しでWindowsをスクリーンロック。離れたところのPCもコマンドでスクリーンロック。
  - Windowsのスクリーンロックはキーボード操作だと「Windowsロゴキー+L」と2手順(Windowsロゴキーのないキーボードだと「Ctrl+Alt+Del, Alt+k」の5手順)かかるのをスイッチひと押しで。
  ![image.png](https://qiita-image-store.s3.amazonaws.com/0/221758/7224de25-017e-52e8-fa2b-091079c6fa71.png)
  - 離れたところのPCはコマンドで。
  ![image.png](https://qiita-image-store.s3.amazonaws.com/0/221758/7542ede4-db27-fbe1-4968-1da6157a98dc.png)

- マウスでは難しい微調整をスムーズに。
  Excel方眼紙の強い味方。セルの大きさをあと1ピクセル調整したいとき十字に並べたスイッチで簡単に。
  ![arduinoLeonardoToolsV2_12.png](https://qiita-image-store.s3.amazonaws.com/0/221758/982fb43a-46cf-6b65-fbc5-e2165799101b.png)

- テストのお供に。
  ArduinoをUART制御の治具として利用しExcel VBA+VISAでArduinoとオシロスコープを一元制御します。


##2. 技術的な目標
1. キーパッドをキーマトリクス化して[GPIO](https://www.google.co.jp/search?hl=ja&as_q=gpio)を節約しそのぶん汎用的に使えるGPIOを増やす
- Serial1の[UART](https://www.google.co.jp/search?hl=ja&as_q=UART)通信でArduinoを制御する


##3. ハードウェア
作成したハードウェアはこちら。
![arduinoLeonardoToolsV2_30.jpg](https://qiita-image-store.s3.amazonaws.com/0/221758/12fbf569-f0bc-d8ed-31d2-2bcf278725e7.jpeg)

保護回路などは特に入っていませんので適宜安全設計を行ってください。この回路の使用に起因する一切の事象について筆者は責任を負いません。

表1 Arduino Leonardoのピンモード

|DIGITAL Pin|Pin Mode    |機能|
|:----------|:-----------|:---|
| 0         |UART Rx     |FTDI FT-232RQ TXDと接続|
| 1         |UART Tx     |FTDI FT-232RQ RXDと接続|
| 2         |OUTPUT      |3x3キーパッド(X)|
| 3         |OUTPUT      |3x3キーパッド(Y)|
| 4         |OUTPUT      |3x3キーパッド(Z)|
| 5         |INPUT       |3x3キーパッド(A)|
| 6         |INPUT       |3x3キーパッド(B)|
| 7         |INPUT       |3x3キーパッド(C)|
| 8         |INPUT_PULLUP|汎用入力#1|
| 9         |INPUT_PULLUP|キーパッドA面/B面切替|
|10         |OUTPUT      |汎用出力#1/LED#1|
|11         |OUTPUT      |汎用出力#2/LED#2|
|12         |OUTPUT      |汎用出力#3/LED#3/リレー#1駆動|
|13         |OUTPUT      |汎用出力#4/LED#4/リレー#2駆動|

表2 Arduino LeonardoのANALOG IN

|ANALOG IN|機能|
|:--------|:---|
|A0 	  |5Vを10kΩの可変抵抗で分圧し入力|
|A1 	  |ジョイスティックのVRx※|
|A2 	  |ジョイスティックのVRy※|
|A3 	  |未使用|
|A4 	  |未使用|
|A5 	  |未使用|
※[Arduino Leonardoとジョイスティックでマウスカーソルを動かす](https://qiita.com/pbjpkas/items/d8ee9938ac33d0d1914f)

###3.1 パーツ
<dl>
<dt>Arduino
<dd><a target=_blank href="https://www.google.co.jp/search?hl=ja&as_q=USB+HID">USB HID</a>(Human Interface Device)機能を備えるArduino Leonardoを使用します。

<dt>基板
<dd><a target=_blank href="http://www.aitendo.com">aitendo</a>の<a target=_blank href="http://www.aitendo.com/product/6976">UBD-ARD53X68</a>(SOIC28-1.27)を使用しています。
<dd>※後継(?)の<a target=_blank href="http://www.aitendo.com/product/16464">UBD-ARD53x68-HL</a>はねじ穴付近の穴の配置が異なるようです。

<dt>ピンソケット
<dd>ブレッドボードへ信号を引き出せるよう足の長い(10mm)ピンソケットを使用しています。

<dt>ブレッドボード、シャーシ
<dd>aitendoで売られている<a target=_blank href="http://www.aitendo.com/product/9477">ミニブレッドボード</a>と<a target=_blank href="http://www.aitendo.com/product/9479">ベース板</a>を使用しています。
<dd>Arduino Leonardoは<a target=_blank href="http://www.aitendo.com/product/7397">両面クションテープ</a>で留めています。
<dd>また、ベース板の裏に<a target=_blank href="http://www.aitendo.com/product/14406">クッションゴム</a>を貼っています。

<dt>キーパッド
<dd>キーパッドは<a target=_blank href="http://www.akizukidenshi.com">秋月電子</a>の<a target=_blank href="http://akizukidenshi.com/catalog/g/gK-12229/">4x3キーパッド作成キット</a>を参考にXYZxABCの3x3マトリクスで組んでいます。
<dd>これはマトリクス回路とソフトウェアの工夫で9個のスイッチを6本のGPIOでスキャンしOFF/ON判定するもので貴重なGPIOの節約になります。従来は9個のタクトスイッチをそのままGPIOへ接続していたのですが汎用出力を増やすため基板化に併せてマトリクス化しました。
<dd>なお、キットは専用基板、ピンヘッダ、タクトスイッチ、抵抗、ダイオードがセットになっていてブレッドボードで使いやすそうです。
<dd><img src="https://qiita-image-store.s3.amazonaws.com/0/221758/df9e46fd-3832-4ecd-6d05-0ce8e86a56d9.jpeg">

<dt>汎用入力#1
<dd>特に用途を決めていない入力端子です。スイッチが押されたらループをbreakしたり。

<dt>汎用出力#1、#2、#3、#4
<dd>特に用途を決めていない出力端子です。2.2kΩの抵抗を介してLEDを接続しています。
<dd>動作のインジケートや2つセットで使って二相パルス、4つセットで使って四相パルスの生成など。
<dd>#3、#4はジャンパ端子を短絡することでリレーの駆動回路と接続します。

<dt>リレー
<dd>秋月電子で売られている<a target=_blank href="http://akizukidenshi.com/catalog/g/gP-01229/">941H-2C-5D</a>を2個載せています。これ1個で2回路のスイッチ切替を同時に行え、例えば音声信号(L/R)の入力切替ができます。
<dd>Arduino Leonardo(というか<a target=_blank href="https://www.google.co.jp/search?hl=ja&as_q=ATmega32U4">ATmega32U4</a>)の絶対最大定格はDigital Outputはピン1つあたり40mA、チップ全体で200mAなので<a target=_blank href="https://www.google.co.jp/search?hl=ja&as_q=2SD2012">2SD2012</a>で駆動回路を組んでいます。R=33kΩ、Diは<a target=_blank href="https://www.google.co.jp/search?hl=ja&as_q=1N4007">1N4007</a>です。
<dd><img src="https://qiita-image-store.s3.amazonaws.com/0/221758/a7c64aac-f163-3fc8-0ff9-d89676948232.jpeg">

<dt>10kΩ可変抵抗
<dd>分圧した電圧をA/D変換しループ内のsleep()の時間調整に使うのを想定して載せています。

<dt>USBシリアル変換器
<dd>秋月電子の<a target=_blank href="http://akizukidenshi.com/catalog/g/gK-09951/">AE-TTL-232R</a>です。はんだ面に印刷された端子のシルクが見えるよう部品面にヘッダピンをはんだ付けしています。
</dl>


##4. ソフトウェア
作成した[スケッチ](https://www.google.co.jp/search?hl=ja&as_q=Arduino+%E3%82%B9%E3%82%B1%E3%83%83%E3%83%81)はこのページの下の方にあります。無保証、無サポートです。

###4.1 操作モード
キーパッドで操作する"キーパッドモード"とシリアル通信で操作する"コマンドモード"があります。

- キーパッドモードとコマンドモードは排他
- キーパッドモードで起動する
- シリアルポートにデータが入ったらコマンドモードに切替わる
- コマンド"quit"、"exit"にてキーパッドモードへ戻る 

###4.2 キーパッドモード
キーパッドは以下のように並べています。1～6と9は抵抗のカラーコードに合わせましたが紫、灰色のタクトスイッチがなかったので7、8は白で代用しています。
![arduinoLeonardoToolsV2_40.jpg](https://qiita-image-store.s3.amazonaws.com/0/221758/355cb928-2a8a-389a-b684-801b90459132.jpeg)

Digital Pin#9に接続したスイッチでA面、B面を切替えます。

- A面は複数のキーを同時に押下されたらキーの番号順に機能を発動します。
- B面はマウス操作を行います。

キーパッドモードかつキーパッドが何も押されていないときに汎用入力#1に接続したスイッチを押下するとANALOG IN A0の電圧をA/D変換して読取り二相パルスの周期に反映します。

表3 キーパッド(A面)

|スイッチ#|機能|
|:--------|:---|
|1        |LチカとHello, Worldを実行する|
|2        |copyを実行する|
|3        |Excelの「行と列を入れ替えて貼り付け」の操作を実行する|
|4        |マウスで図形を描画する|
|5        |汎用出力#1、#2で二相パルスを生成する(汎用出力#1が先行)|
|6        |汎用出力#1、#2で二相パルスを生成する(汎用出力#2が先行)|
|7        |汎用出力#3を500msごとにON/OFFする|
|8        |汎用出力#4を500msごとにON/OFFする|
|9        |スクリーンロックを実行する|

表4 キーパッド(B面)

|スイッチ#|機能|
|:--------|:---|
|1        |2、8と同時押しでマウスホイールスクロール|
|2        |マウスカーソル移動(↓)|
|3        |マウス左ボタン プレス・リリースのトグル|
|4        |マウスカーソル移動(←)|
|5        |移動量の切替(1→3→10→30のサイクリック)|
|6        |マウスカーソル移動(→)|
|7        |マウス左ボタンクリック|
|8        |マウスカーソル移動(↑)|
|9        |マウス右ボタンクリック| 

###4.3 コマンドモード
コマンド、引数1、引数2の最大3パラメータを解釈し、合計で最大63バイトです。

- 通信速度 9600[bps]
- コマンドプロンプトとして"$"を表示する
- Enterキー押下でコマンドを実行する
- サポート外のコマンドが入力されたら"?"を表示する
- コマンド"quit"、"exit"にてキーパッドモードへ戻る
- その他コマンドは表5参照

表5 コマンド一覧

|コマンド|引数1                     |引数2           |機能|
|:-------|:-------------------------|:---------------|:---|
|quit    |なし                      |なし            |コマンドモードを終了しキーパッドモードへ戻る|
|exit    |なし                      |なし            |コマンドモードを終了しキーパッドモードへ戻る|
|?       |なし                      |なし            |コマンドの一覧と簡単な説明を表示する|
|help    |なし                      |なし            |コマンドの一覧と簡単な説明を表示する|
|ver     |なし                      |なし            |ファイル名とビルド日時を表示する|
|echo    |onまたはoff                |なし           |コマンド入力時のエコーバックをON/OFFする|
|scrlock |なし                      |なし            |Windowsのスクリーンロック操作を実行する|
|toneon  |32～22500の整数            |なし            |汎用出力#1を引数で与えられた周波数[Hz]でパルス出力する ※1|
|toneoff |なし                      |なし            |toneonの出力を停止する ※1|
|port    |1～4(汎用出力#1～#4に対応)|lowまたはhigh   |汎用出力#1～#4をLow/Highする ※2|
|key     |押下したいキー            |なし            |キーを打鍵(press&release)する|
|key     |alt                       |押下したいキー  |左Altキーとの同時押しでキーを打鍵する|
|key     |ctrl                      |押下したいキー  |左Ctrlキーとの同時押しでキーを打鍵する|
|key     |shift                     |押下したいキー  |左Shiftキーとの同時押しでキーを打鍵する|
|key     |win                       |押下したいキー  |Windowsロゴキーとの同時押しでキーを打鍵する|
|key     |ctrlalt                   |押下したいキー  |左Ctrlキー、左Altキーとの同時押しでキーを打鍵する|
|str     |文字列                    |なし            |文字列を打鍵する(ASCII CODE 0x20-0x7Fのみ)|
|mouse   |sc                        |なし            |マウス左ボタンのシングルクリック|
|mouse   |wc                        |なし            |マウス左ボタンのダブルクリック|
|mouse   |rc                        |なし            |マウス右ボタンのシングルクリック|
|mouse   |p                         |なし            |マウス左ボタンのプレス|
|mouse   |r                         |なし            |マウス左ボタンのリリース|
|mouse   |scr                       |スクロール量    |マウスホイールの回転|
|mouse   |水平方向の移動量          |垂直方向の移動量  |マウスカーソルの移動|
|typesize|なし                     |なし            |型のサイズをバイト数で表示する|
- ※1 [ArduinoとオシロスコープをExcelで制御して測定する](https://qiita.com/pbjpkas/items/7da7ff99e7226fa84bcb)
- ※2 [ArduinoとオシロスコープをExcelで制御して測定する(2) シャッタースピードの自動測定](https://qiita.com/pbjpkas/items/615a6eca0894c784e87a)

表6 keyコマンドの引数

|キー           |引数の与え方|
|:--------------|:---------|
|ASCII文字      |そのまま入力します|
|Enterキー      |ent|
|Spaceキー      |sp|
|Tabキー        |tab|
|Backspaceキー  |bs|
|Deleteキー     |del|
|Escapeキー     |esc|
|PrintScreenキー|ps|
|↑キー         |ua|
|↓キー         |da|
|←キー         |la|
|→キー         |ra|

##5. スケッチ解説
以下にスケッチの一部を解説します。

###5.1 setup()
キーボード、マウス、UARTの初期化と、GPIOやキーパッドの設定などを行っています。Arduino LeonardoはDigital Pin#0、#1をUARTで使用するときはSerial1を使用します。

```c
const char msg_setup_01[] PROGMEM = "Arduino Leonardo Tools Ver.2\r\n";
const char msg_setup_02[] PROGMEM = "Hit any key to Command Mode.\r\n";
void setup()
{
    Keyboard.begin();
    Mouse.begin();
    Serial1.begin(9600);
    
    gpio_init();
    keypad_init();
    set_periodic_time(DEFAULT_PERIODIC_TIME); // 二相パルスの周期初期化
    set_mouse_moving_distance(1);
    cmd_set_echoback(true);
    cmd_print_msg(&msg_setup_01[0], sizeof(msg_setup_01));
    cmd_print_msg(&msg_setup_02[0], sizeof(msg_setup_02));
}

```

###5.2 loop()
 loop()でモード切替と汎用入力#1を押下されたときの処理をキックします。

- keypad_get_key()を実行しキーが押下されていたらkeypad_execute()へジャンプします。
- キーパッドがB面、かつ、ジョイスティックによるマウスカーソル移動機能が有効化されている場合、ジョイスティックの動きをマウスカーソル移動に反映させます。
- [Serial1.available](https://www.google.co.jp/search?hl=ja&as_q=arduino+Serial.available)()を実行し シリアルポートにデータがあればcmd_rx_data()へジャンプします。
- 汎用入力#1に接続したスイッチが押下されたらset_periodic_time_by_analog_in_a0()を実行します。 

```c
void loop()
{
    int keypad_val;
    keypad_val = keypad_get_key();
    
    digitalWrite(OUTPUT_1, !digitalRead(MODE_SELECT)); // B面の時に点灯する
    if (keypad_val & 0x001ff)
    {
        keypad_execute(keypad_val);
    }
    if( !digitalRead(MODE_SELECT) )
    {
        if( 0x0f == get_joystick_status() )
        {
            move_mouse_cursor_with_joystick();
        }
    }
    if(Serial1.available())
    {
        digitalWrite(OUTPUT_2, HIGH);
        cmd_rx_data();
        digitalWrite(OUTPUT_2, LOW);
    }
    
    if(digitalRead(EXT_INPUT) == SW_PRESSED)
    {
        set_periodic_time_by_analog_in_a0();
    }
}
```

###5.3 キーパッド
キーパッドの押下状態はkeypad_get_key()で取得します。これは9個のキーの押下状態をint型の変数keypad_valに[ビットフィールド](https://www.google.co.jp/search?hl=ja&as_q=%E3%83%93%E3%83%83%E3%83%88%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89)で格納し戻り値で返します。digitalRead()の値はキー押下で0、開放で1ですが戻り値を返す際に押下で1、開放で0としています。

```c
int keypad_get_key(void)
{
    int keypad_val = 0;

    digitalWrite(KEY_X, 0);
    digitalWrite(KEY_Y, 1);
    digitalWrite(KEY_Z, 1);
    keypad_val = keypad_val|((digitalRead(KEY_C)<<6)|(digitalRead(KEY_B)<<3)|(digitalRead(KEY_A)<<0));
    
    digitalWrite(KEY_X, 1); 
    digitalWrite(KEY_Y, 0); 
    digitalWrite(KEY_Z, 1); 
    keypad_val = keypad_val|((digitalRead(KEY_C)<<7)|(digitalRead(KEY_B)<<4)|(digitalRead(KEY_A)<<1));
    
    digitalWrite(KEY_X, 1); 
    digitalWrite(KEY_Y, 1); 
    digitalWrite(KEY_Z, 0); 
    keypad_val = keypad_val|((digitalRead(KEY_C)<<8)|(digitalRead(KEY_B)<<5)|(digitalRead(KEY_A)<<2));
    
    return (~keypad_val) & 0x01ff; //押されているキーの値を1にしてreturnする
}
```

押下されたキーに対応する機能はkeypad_execute()で実行します。マスクするビットを変えながら引数とANDを取り1が立っていたらキーが押下されていると判定します。

```c
void keypad_execute(int keypad_val)
{
    if(digitalRead(MODE_SELECT)) // Mode Selectスイッチがオープン:1、クローズ:0
    {
        if (keypad_val & 0x0001)
        {
            Serial1.println("A1");
            print_hello_world_and_L_blink();
        }
        if (keypad_val & 0x0002)
        {
            Serial1.println("A2");
            win_copy();
        }
//以下略
}
```

###5.4 シリアル通信
cmd_rx_data()は受信データをエコーバックしつつ整形し改行コード(\r、\n)を検出するとcmd_execute()へ投げます。cmd_execute()の戻り値がCMD_QUITの場合はreturnでloop()へ戻りそれ以外はwhile(1)でループし続けます。

```c

void cmd_rx_data(void)
{
    int i;
    int return_val = CMD_OK;
    char buf[CMD_BUF_LENGTH];
    
    /* モード切替時の "Hit any key" のキー操作を捨てる */
    while(Serial1.available()){ Serial1.read(); }
    
    Serial1.print( F("### Command Mode. ###\r\n") );
    Serial1.print( F("### Hit ? to help.###\r\n") );
    Serial1.print( F("$") );
    
    i=0;
    while(1)
    {
        if(Serial1.available())
        {
            buf[i] = Serial1.read();
            if( cmd_get_echoback() )
            {
                Serial1.print(buf[i]); //echo-back
            }
            
            if ( (buf[i] == 0x08) or (buf[i] == 0x7f) ) //BackSpace, Delete
            {
                buf[i] = '\0';
                if(i) i--;
            }
            else if( (buf[i] == '\r') or (buf[i] == '\n') )
            {
                Serial1.print( F("\r\n") );
                buf[i] = '\0';
                return_val = cmd_execute(&buf[0]);
                for(i=0; i<CMD_BUF_LENGTH; i++) buf[i] = '\0';
                i=0;
                
                if(return_val == CMD_QUIT)
                {
                    Serial1.print( F("### Quit Command Mode. ###\r\n") );
                    return;
                }
                else if(return_val == CMD_INVALID)
                {
                    Serial1.print( F("?\r\n") );
                    Serial1.print( F("$") );
                }
                else
                {
                    Serial1.print( F("OK\r\n$") );
                }
            }
            else
            {
                i++;
                if(i>=CMD_BUF_LENGTH)
                {
                    Serial1.print( F("### CMD BUFFER FULL, CLEAR. ###\r\n") );
                    for(i=0; i<CMD_BUF_LENGTH; i++) buf[i] = '\0';
                    i=0;
                }
            }
        }
    }// while
}

```

cmd_execute()は受取った文字列を[sscanf](https://www.google.co.jp/search?hl=ja&as_q=sscanf)()で分解し[strcmp](https://www.google.co.jp/search?hl=ja&as_q=strcmp)()で文字列比較します。比較して一致したら該当するコマンドを実行します。

```c
int cmd_execute(char *buf)
{
    int i, x, y;
    unsigned int ux;
    int return_val = CMD_OK;
    char cmd[CMD_MAX_LENGTH];
    char arg1[ARG_MAX_LENGTH];
    char arg2[ARG_MAX_LENGTH];
    
    strcpy(cmd, "");
    strcpy(arg1, "");
    strcpy(arg2, "");
    sscanf(buf, "%s %s %s", &cmd, &arg1, &arg2);
    
    if     (strcmp(cmd, "help")==0){ cmd_print_help();}
    else if(strcmp(cmd, "?"   )==0){ cmd_print_help();}
    else if(strcmp(cmd, "ver" )==0){ cmd_print_ver(); }
    
    else if((strcmp(cmd, "quit")==0) or (strcmp(cmd, "exit")==0))
    {
        return CMD_QUIT;
    }
//以下略
}
```

###5.5 RAMの節約
Arduino Leonardoが搭載するATmega32U4はFlash ROMが32キロバイトに対してRAMは2.5キロバイトです。ASCII文字1文字で1バイト消費するのでヘルプのようなテキストを書き始めるとどんどんRAMを消費(圧迫)していきます。そこでプログラム実行中に書換えることのないデータはFlash ROMに配置してRAMを節約します。

Flash ROM上の文字列へのアクセスは[Fマクロ](https://www.arduino.cc/reference/en/language/variables/utilities/progmem/)が用意されていますが[PROGMEM](https://www.google.co.jp/search?hl=ja&as_q=PROGMEM)修飾子と[const](https://www.google.co.jp/search?hl=ja&as_q=const)修飾子、Flash ROMのデータにアクセスする関数を使用して実装してみました。

```c
/* データをFlash ROMに配置 */
const char msg_setup_01[] PROGMEM = "Arduino Leonardo Tools Ver.2\r\n";

/* mallocで動的に確保したRAMにFlash ROMのデータをmemcpy_Pでコピーする */
int cmd_print_msg(const char *msg_addr, byte msg_size)
{
    int i;
    char *msg_array = NULL;
    msg_array = (char *)malloc(msg_size);
    if (NULL == msg_array)
    {
        return CMD_MALLOC_ERR;
    }
    
    memcpy_P(msg_array, msg_addr, msg_size);
    for(i=0; i<msg_size; i++)
    {
        Serial1.print(msg_array[i]);
    }
    
    free(msg_array);
    return CMD_OK;
}

/* 呼び出し側 */
cmd_print_msg(&msg_setup_01[0], sizeof(msg_setup_01));
```

##6. 機能紹介
Arduino Leonardoに実装した機能を数点紹介します。

###6.1 Lチカ&Hello, World.
####6.1.1 統合版
最初に試すプログラムは[Lチカ](https://www.google.co.jp/search?hl=ja&as_q=L%E3%83%81%E3%82%AB)とも[Hello, World.](https://www.google.co.jp/search?hl=ja&as_q=Hello%2C+World.)とも言われますがArduino Leonardoならどちらもできるので一つにまとめました。キーボードのキー操作、UARTへの文字出力、Lチカを行います。

```c
void print_hello_world_and_L_blink(void)
{
    unsigned int ui;
    char str[] = "Hello,\tWorld.\r\n";
    
    digitalWrite(OUTPUT_1, HIGH);
    for(ui=0; ui<sizeof(str); ui++)
    {
        keyboard_key(KEY_NUL, KEY_NUL, str[ui]);
        Serial1.print(str[ui]);
    }
    digitalWrite(OUTPUT_1, LOW);
}
```

Excelとターミナルアプリを開いてキーパッドA面でスイッチ1を押下するとLチカとHello, World.を同時に確認できます。
![arduinoLeonardoToolsV2_60.png](https://qiita-image-store.s3.amazonaws.com/0/221758/a5375133-e8ad-d4c2-50dd-88c7bf293318.png)

####6.1.2 Lチカ
位相差1/4周期の二相パルスを作って時間差を伴った点灯を行います。キーパッドA面でスイッチ5、6を押下するとLチカします。2つのスイッチを同時に押下するとスイッチ5→スイッチ6の順に実行します。

```c
void generate_two_phase_pulse(int phase1, int phase2)
{
    int periodic_time = get_periodic_time() >> 2;
    
    digitalWrite(phase1, HIGH);
    delay(periodic_time);
    digitalWrite(phase2, HIGH);
    delay(periodic_time);
    digitalWrite(phase1, LOW);
    delay(periodic_time);
    digitalWrite(phase2, LOW);
    delay(periodic_time);
}
```

####6.1.3 Hello, World.
strコマンド、keyコマンドを使用してHello, World.の打鍵ができます。
str Hello,
key tab
str World.

###6.2 マウスでお絵かき
キーパッドA面でスイッチ4を押下すると以下のような図形を描画します。
![arduinoLeonardoToolsV2_64.png](https://qiita-image-store.s3.amazonaws.com/0/221758/4bb13a77-5140-8629-c1d1-fe332ca59fa8.png)

###6.3 Excelやパソコン作業の省力化
####6.3.1 Excelでコピー
ExcelでHello,とWorld.のセルを選択しキーパッドA面でスイッチ2を押下するとコピーされます。
![arduinoLeonardoToolsV2_61.png](https://qiita-image-store.s3.amazonaws.com/0/221758/9fe38988-9d43-9753-16f4-572726e45952.png)

####6.3.2 Excelで行と列を入替えて値で貼付け
手順の多い操作もスイッチ一発で。キーパッドA面でスイッチ3を押下すると以下のダイアログを開いて、値(V)を選んで、行列を入れ替える(E)にチェックを入れてOKボタンを押してくれます。
![arduinoLeonardoToolsV2_62.png](https://qiita-image-store.s3.amazonaws.com/0/221758/355bdacd-9663-0889-5d0a-276c6a377dc5.png)

![arduinoLeonardoToolsV2_63.png](https://qiita-image-store.s3.amazonaws.com/0/221758/388f0b24-0cf4-6ca2-bdaf-4d6a2c1ad32f.png)

####6.3.3 Excel方眼紙の微調整
![arduinoLeonardoToolsV2_12.png](https://qiita-image-store.s3.amazonaws.com/0/221758/982fb43a-46cf-6b65-fbc5-e2165799101b.png)

1. 調整したい列の列番号の右の境界(または行番号の下の境界)にマウスカーソルを移動する
2. キーパッドB面でスイッチ3を押下しマウス左ボタンのプレスを行う
3. キーパッドB面でスイッチ2、4、6、8でマウスカーソルを移動する
4. キーパッドB面でスイッチ3を押下しマウス左ボタンのリリースを行う

####6.3.4 スクリーンロック
キーパッドA面でスイッチ9を押下するかコマンドモードで"scrlock"を実行すると下記の関数が呼ばれてロックがかかります。
※コマンドモードで"key win l"を実行してもロックがかかります。

```c
void win_screen_lock(void)
{
    Keyboard.press(KEY_LEFT_GUI);
    Keyboard.press('l');
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
}
```

コマンドモードであれば以下の操作でスクリーンロックの解除ができます。
mouse sc (またはkey ent、str xなど)
str xxxxxxxx (xxはパスワード文字列)
key ent

###6.4 自動測定(性能テストの自動化)
ArduinoをUART制御の治具として利用しExcel VBA+VISAでArduinoとオシロスコープを一元制御します。詳細は以下の記事をご参照ください。

Arduinoを信号発生器として使用し測定を行う：

- [ArduinoとオシロスコープをExcelで制御して測定する](https://qiita.com/pbjpkas/items/7da7ff99e7226fa84bcb)

Arduinoをハードウェア操作のコントローラとして使用し測定を行う：

- [ArduinoとオシロスコープをExcelで制御して測定する(2) シャッタースピードの自動測定](https://qiita.com/pbjpkas/items/615a6eca0894c784e87a)


##7. スケッチ
筆者が使用しているArduino IDEはVer. 1.6.4です。

```c:arduinoLeonardoToolsV2.ino
/***********************************************************************
 * Arduino Leonardo Tools Ver.2
 * 2018.1.1 by ka's
 *
 * 2018.3.24
 * Ver 2.0.1
 * - add echo command
 * - add tone command
 *
 * 2018.4.14
 * Ver 2.0.2
 * - add mouse cursor joystick control
 *
 * 2018.6.18
 * Ver 2.0.3
 * - add get_joystick_status()
 ***********************************************************************/
/***********************************************************************
 * Copyright 2018 ka's@pbjpkas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ***********************************************************************/
#include <stdio.h>
#include <string.h>
#include <avr/pgmspace.h>

// Digital Pinの割当
#define KEY_X              2
#define KEY_Y              3
#define KEY_Z              4
#define KEY_A              5
#define KEY_B              6
#define KEY_C              7
#define EXT_INPUT          8 // External Input
#define MODE_SELECT        9 // mode select
#define OUTPUT_1          10 // LED#1
#define OUTPUT_2          11 // LED#2
#define OUTPUT_3          12 // LED#3 / relay#1
#define OUTPUT_4          13 // LED#4 / relay#2

// スイッチ押下判定
#define SW_PRESSED       LOW

// ウェイト時間
#define ANTI_CHATTER      50 //  50ms
#define MOUSE_HOLD       250 // 250ms
#define RELAY_HOLD        50 //  50ms

// キーボードのキーコード
#define KEY_NUL         0x00
#define KEY_PRINTSCREEN 0xCE
#define KEY_SCROLLLOCK  0xCF
#define KEY_PAUSE       0xD0


/***********************************************************************
  関数プロトタイプ宣言
 ***********************************************************************/
// ツール応用例 - Windows Keyboard Shortcut, etc.
void print_hello_world_and_L_blink(void);
void win_copy(void);
void excel_row_col_swap_paste(void);
void win_screen_lock(void);
void mouse_drawing(void);

// ツール応用例 - 二相パルス生成
void set_periodic_time(int val);
int  get_periodic_time(void);
void set_periodic_time_by_analog_in_a0(void);
void generate_two_phase_pulse(int phase1, int phase2);

// Keyboard
void keyboard_key(int primary_key, int secondary_key, int key_code);

// Mouse
void set_mouse_moving_distance(int val);
int  get_mouse_moving_distance(void);
void change_mouse_moving_distance(void);
void mouse_single_click(void);
void mouse_double_click(void);
signed char get_mouse_cursor_movement_amount_by_joystick(int val);
void move_mouse_cursor_with_joystick(void);
char get_joystick_status(void);

// GPIO
void gpio_init(void);

// キーパッドモード
void keypad_init(void);
int  keypad_get_key(void);
void keypad_execute(int keypad_val);

// コマンドモード
void cmd_set_echoback(bool val);
bool cmd_get_echoback(void);
int  cmd_print_msg(const char *msg_addr, byte msg_size);
void cmd_print_help(void);
void cmd_print_ver(void);
int  cmd_str(char *str);
int  cmd_str_to_char(char *str);
int  cmd_key(int primary_key, int secondary_key, char *str);
int  cmd_execute(char *buf);
void cmd_rx_data(void);

// setup, loop
void setup();
void loop();


/***********************************************************************
  ツール応用例 - Windows Keyboard Shortcut, etc.
 ***********************************************************************/
void print_hello_world_and_L_blink(void)
{
    unsigned int ui;
    char str[] = "Hello,\tWorld.\r\n";
    
    digitalWrite(OUTPUT_1, HIGH);
    for(ui=0; ui<sizeof(str); ui++)
    {
        keyboard_key(KEY_NUL, KEY_NUL, str[ui]);
        Serial1.print(str[ui]);
    }
    digitalWrite(OUTPUT_1, LOW);
}

void win_copy(void)
{
    Keyboard.press(KEY_LEFT_CTRL);
    Keyboard.press('c');
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
    delay(500);
}

void excel_row_col_swap_paste(void)
{
    Keyboard.press(KEY_LEFT_CTRL);
    Keyboard.press(KEY_LEFT_ALT);
    Keyboard.press('v');
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
    delay(ANTI_CHATTER);
    
    Keyboard.press('v');
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
    delay(ANTI_CHATTER);
    
    Keyboard.press('e');
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
    delay(ANTI_CHATTER);
    
    Keyboard.press(KEY_TAB);
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
    delay(ANTI_CHATTER);
    
    Keyboard.press(KEY_RETURN);
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
    delay(ANTI_CHATTER);
}

void win_screen_lock(void)
{
#if 0
    Keyboard.press(KEY_LEFT_CTRL);
    Keyboard.press(KEY_LEFT_ALT);
    Keyboard.press(KEY_DELETE);
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
    delay(2000);
    
    Keyboard.press(KEY_LEFT_ALT);
    Keyboard.press('k');
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
    delay(1000);
#else
    Keyboard.press(KEY_LEFT_GUI);
    Keyboard.press('l');
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
#endif
}

void mouse_drawing(void)
{
    int i;
    int x,y;
    
    Mouse.press(MOUSE_LEFT); 
    for(i=0; i<12; i++)
    {
        x = i;
        y = i;
        Mouse.move(x, y, 0);  // X, Y, Wheel
        delay(20);
    }
    for(i=0; i<12; i++)
    {
        x = -i;
        y = i;
        Mouse.move(x, y, 0);  // X, Y, Wheel
        delay(20);
    }
    for(i=0; i<12; i++)
    {
        x = -i;
        y = -i;
        Mouse.move(x, y, 0);  // X, Y, Wheel
        delay(20);
    }
    for(i=0; i<12; i++)
    {
        x = i;
        y = -i;
        Mouse.move(x, y, 0);  // X, Y, Wheel
        delay(20);
    }
    Mouse.release(MOUSE_LEFT); 
}


/***********************************************************************
  ツール応用例 - 二相パルス生成
 ***********************************************************************/
#define DEFAULT_PERIODIC_TIME 400

// パルス周期のアクセサ
static int periodic_time;
void set_periodic_time(int val)
{
    if(val < 0){ val = DEFAULT_PERIODIC_TIME; }
    periodic_time = val;
}
int get_periodic_time(void)
{
    return periodic_time;
}

// パルスの周期をANALOG IN A0に接続した可変抵抗で変更する。analogRead()の戻り値は0～1023でこの値を2倍する。
void set_periodic_time_by_analog_in_a0(void)
{
    static int val_previous = 0;
    int val = analogRead(0);
    set_periodic_time(val*2);
    
    //前回読込み時と値が2以上変わっていたらログに出力する
    if(abs(val_previous-val) >= 2)
    {
        Serial1.println(val);
        val_previous = val;
    }
}

// 二相パルスの生成
void generate_two_phase_pulse(int phase1, int phase2)
{
    int periodic_time = get_periodic_time() >> 2;
    
    digitalWrite(phase1, HIGH);
    delay(periodic_time);
    digitalWrite(phase2, HIGH);
    delay(periodic_time);
    digitalWrite(phase1, LOW);
    delay(periodic_time);
    digitalWrite(phase2, LOW);
    delay(periodic_time);
}


/***********************************************************************
  Keyboard
 ***********************************************************************/
void keyboard_key(int primary_key, int secondary_key, int key_code)
{
    if(primary_key  ){ Keyboard.press(primary_key  ); }
    if(secondary_key){ Keyboard.press(secondary_key); }
    Keyboard.press(key_code);
    delay(ANTI_CHATTER);
    Keyboard.releaseAll();
    delay(ANTI_CHATTER);
}


/***********************************************************************
  Mouse
 ***********************************************************************/
static int mouse_moving_distance;
void set_mouse_moving_distance(int val)
{
    mouse_moving_distance = val;
}
int get_mouse_moving_distance(void)
{
    return mouse_moving_distance;
}
void change_mouse_moving_distance(void)
{
    int distance = get_mouse_moving_distance();
    
    switch(distance)
    {
        case  1:
            distance =  3;
            break;
        case  3:
            distance = 10;
            break;
        case 10:
            distance = 30;
            break;
        case 30:
            distance =  1;
            break;
        default:
            distance =  1;
            break;
    }
    Serial1.println(distance);
    set_mouse_moving_distance(distance);
}

void mouse_single_click(int button)
{
    Mouse.press(button); 
    delay(ANTI_CHATTER);
    Mouse.release(button); 
    delay(ANTI_CHATTER);
}
void mouse_double_click(int button)
{
    mouse_single_click(button);
    mouse_single_click(button);
}

/*
 * val    : 0(0x0000)～1023(0x03FF)の整数
 * return : valに応じた移動量
 */
signed char get_mouse_cursor_movement_amount_by_joystick(int val)
{
    signed char amount[16] = { -8, -4, -2, -2, -2, -1, -1,  0,
                                0,  1,  1,  2,  2,  2,  4,  8 };
    unsigned int range = ((unsigned int)val&0x03ff) / 64 ;
    return amount[range];
}

void move_mouse_cursor_with_joystick(void)
{
    unsigned long response_delay = 10;
    int x_pin = 1;
    int y_pin = 2;
    int x_reading = 0;        /* analogRead()の戻り値を格納する */
    int y_reading = 0;        /* analogRead()の戻り値を格納する */
    signed char x_amount = 0; /* Mouse.move()の xの値を格納する */
    signed char y_amount = 0; /* Mouse.move()の yの値を格納する */

    x_reading = analogRead(x_pin);
    y_reading = analogRead(y_pin);
    x_amount = get_mouse_cursor_movement_amount_by_joystick(x_reading);
    y_amount = get_mouse_cursor_movement_amount_by_joystick(y_reading);
    if( x_amount || y_amount )
    {
        Mouse.move(x_amount, y_amount, 0);
        delay(response_delay);
    }
}

/*
  Arduino Leonardoにジョイスティックを接続しないで起動したときにひとりでにマウスカーソルが移動しないよう、
  ジョイスティックによるマウスカーソル移動機能はジョイスティックを上下左右に動かして有効化する。
  get_joystick_status()の戻り値が0x0fの場合、ジョイスティックが上下左右に動かされたと判定する。
 */  
static char joystick_status = 0;
char get_joystick_status(void)
{
    int x_pin = 1;
    int y_pin = 2;
    int x_reading = 0;        /* analogRead()の戻り値を格納する */
    int y_reading = 0;        /* analogRead()の戻り値を格納する */

    x_reading = analogRead(x_pin);
    y_reading = analogRead(y_pin);

    if( x_reading <         0x400/8 )joystick_status |= 0x01;
    if( x_reading > 0x400 - 0x400/8 )joystick_status |= 0x02;
    if( y_reading <         0x400/8 )joystick_status |= 0x04;
    if( y_reading > 0x400 - 0x400/8 )joystick_status |= 0x08;

   return joystick_status; 
}


/***********************************************************************
  GPIO
 ***********************************************************************/
void gpio_init(void)
{
    pinMode(EXT_INPUT,     INPUT_PULLUP);
    pinMode(MODE_SELECT,   INPUT_PULLUP);
    pinMode(OUTPUT_1,      OUTPUT);
    pinMode(OUTPUT_2,      OUTPUT);
    pinMode(OUTPUT_3,      OUTPUT);
    pinMode(OUTPUT_4,      OUTPUT);
    digitalWrite(OUTPUT_1, LOW); 
    digitalWrite(OUTPUT_2, LOW); 
    digitalWrite(OUTPUT_3, LOW); 
    digitalWrite(OUTPUT_4, LOW); 
}


/***********************************************************************
  キーパッドモード
 ***********************************************************************/
void keypad_init(void)
{
    pinMode(KEY_X,      OUTPUT);
    pinMode(KEY_Y,      OUTPUT);
    pinMode(KEY_Z,      OUTPUT);
    pinMode(KEY_A,      INPUT);
    pinMode(KEY_B,      INPUT);
    pinMode(KEY_C,      INPUT);
    digitalWrite(KEY_X, HIGH); 
    digitalWrite(KEY_Y, HIGH); 
    digitalWrite(KEY_Z, HIGH); 
}
int keypad_get_key(void)
{
    int keypad_val = 0;

    digitalWrite(KEY_X, 0);
    digitalWrite(KEY_Y, 1);
    digitalWrite(KEY_Z, 1);
    keypad_val = keypad_val|((digitalRead(KEY_C)<<6)|(digitalRead(KEY_B)<<3)|(digitalRead(KEY_A)<<0));
    
    digitalWrite(KEY_X, 1); 
    digitalWrite(KEY_Y, 0); 
    digitalWrite(KEY_Z, 1); 
    keypad_val = keypad_val|((digitalRead(KEY_C)<<7)|(digitalRead(KEY_B)<<4)|(digitalRead(KEY_A)<<1));
    
    digitalWrite(KEY_X, 1); 
    digitalWrite(KEY_Y, 1); 
    digitalWrite(KEY_Z, 0); 
    keypad_val = keypad_val|((digitalRead(KEY_C)<<8)|(digitalRead(KEY_B)<<5)|(digitalRead(KEY_A)<<2));
    
    return (~keypad_val) & 0x01ff; //押されているキーの値を1にしてreturnする
}
void keypad_execute(int keypad_val)
{
    if(digitalRead(MODE_SELECT)) // Mode Selectスイッチがオープン:1、クローズ:0
    {
        if (keypad_val & 0x0001)
        {
            Serial1.println("A1");
            print_hello_world_and_L_blink();
        }
        if (keypad_val & 0x0002)
        {
            Serial1.println("A2");
            win_copy();
        }
        if (keypad_val & 0x0004)
        {
            Serial1.println("A3");
            excel_row_col_swap_paste();
        }
        if (keypad_val & 0x0008)
        {
            Serial1.println("A4");
            mouse_drawing();
        }
        if (keypad_val & 0x0010)
        {
//            Serial1.println("A5");
            generate_two_phase_pulse(OUTPUT_1, OUTPUT_2);
        }
        if (keypad_val & 0x0020)
        {
//            Serial1.println("A6");
            generate_two_phase_pulse(OUTPUT_2, OUTPUT_1);
        }
        if (keypad_val & 0x0040)
        {
            Serial1.println("A7");
            digitalWrite(OUTPUT_3, HIGH);
            delay(500);
            digitalWrite(OUTPUT_3, LOW);
            delay(500);
        }
        if (keypad_val & 0x0080)
        {
            Serial1.println("A8");
            digitalWrite(OUTPUT_4, HIGH);
            delay(500);
            digitalWrite(OUTPUT_4, LOW);
            delay(500);
        }
        if (keypad_val & 0x0100)
        {
            Serial1.println("A9");
            win_screen_lock();
        }
    }
    else
    {
        int mouse_d = get_mouse_moving_distance();
        
        if (keypad_val == 0x0010) // ボタン5押下
        {
            change_mouse_moving_distance();
        }
        
        if (keypad_val == 0x0004) // ボタン3押下
        {
            if(!Mouse.isPressed(MOUSE_LEFT))
            {
                Serial1.println("PRS L");
                Mouse.press(MOUSE_LEFT);
            }
            else
            {
                Serial1.println("REL L");
                Mouse.release(MOUSE_LEFT);
            }
        }
        
        if (keypad_val == 0x0040) // ボタン7押下
        {
            Serial1.println("CLK L");
            mouse_single_click(MOUSE_LEFT);
        }
        if (keypad_val == 0x0100) // ボタン9押下
        {
            Serial1.println("CLK R");
            mouse_single_click(MOUSE_RIGHT);
        }
        
        if (keypad_val == 0x0008) // ボタン4押下
        {
            Serial1.println("L");
            mouse_d = -1*mouse_d;
            Mouse.move(mouse_d, 0, 0);
        }
        if (keypad_val == 0x0020) // ボタン6押下
        {
            Serial1.println("R");
            Mouse.move(mouse_d, 0, 0);
        
        }
        if (keypad_val == 0x0080) //ボタン8押下
        {
            Serial1.println("U");
            mouse_d = -1*mouse_d;
            Mouse.move(0, mouse_d, 0);
        }
        if (keypad_val == 0x0002) //ボタン2押下
        {
            Serial1.println("D");
            Mouse.move(0, mouse_d, 0);
        }
        
        if (keypad_val == 0x0081) //ボタン8+1押下
        {
            Serial1.println("SCR U");
            Mouse.move(0, 0, mouse_d);
        }
        if (keypad_val == 0x0003) //ボタン2+1押下
        {
            Serial1.println("SCR D");
            mouse_d = -1*mouse_d;
            Mouse.move(0, 0, mouse_d);
        }
        delay(MOUSE_HOLD);
    }
}


/***********************************************************************
  コマンドモード
 ***********************************************************************/
#define CMD_QUIT          1
#define CMD_OK            0
#define CMD_INVALID      -1
#define CMD_MALLOC_ERR   -2
#define CMD_BUF_LENGTH   64 // 63+1
#define CMD_MAX_LENGTH   64 // 63+1
#define ARG_MAX_LENGTH   64 // 63+1

static bool cmd_echoback;
void cmd_set_echoback(bool val)
{
    if(val)
    {
        cmd_echoback = true;
    }
    else
    {
        cmd_echoback = false;
    }
}
bool cmd_get_echoback(void)
{
    return cmd_echoback;
}

int cmd_print_msg(const char *msg_addr, byte msg_size)
{
    int i;
    char *msg_array = NULL;
    msg_array = (char *)malloc(msg_size);
    if (NULL == msg_array)
    {
        return CMD_MALLOC_ERR;
    }
    
    memcpy_P(msg_array, msg_addr, msg_size);
    for(i=0; i<msg_size; i++)
    {
        Serial1.print(msg_array[i]);
    }
    
    free(msg_array);
    return CMD_OK;
}

void cmd_print_help(void)
{
    Serial1.print( F("Available Command:\r\n") );
    Serial1.print( F("help, ?\r\n") );
    Serial1.print( F("  Print Help Messages\r\n") );
    Serial1.print( F("ver\r\n") );
    Serial1.print( F("  Print Version Information\r\n") );
    Serial1.print( F("quit, exit\r\n") );
    Serial1.print( F("  Quit Command Control Mode\r\n") );
    Serial1.print( F("echo <on or off>\r\n") );
    Serial1.print( F("  echo back on or off\r\n") );
    Serial1.print( F("port <port(1-4)> <low or high>\r\n") );
    Serial1.print( F("  OUTPUT_1 - OUTPUT_4 low or high\r\n") );
    Serial1.print( F("toneon <Frequency[Hz]>\r\n") );
    Serial1.print( F("  Frequency: 32Hz - 22500Hz, digital pin #10\r\n") );
    Serial1.print( F("toneoff\r\n") );
    Serial1.print( F("  stop tone\r\n") );
    Serial1.print( F("scrlock\r\n") );
    Serial1.print( F("  Screen Lock (Windows)\r\n") );
    Serial1.print( F("str\r\n") );
    Serial1.print( F("  issue strings by keyboard keycode\r\n") );
    Serial1.print( F("mouse sc\r\n") );
    Serial1.print( F("  mouse L-button single click\r\n") );
    Serial1.print( F("mouse wc\r\n") );
    Serial1.print( F("  mouse L-button double click\r\n") );
    Serial1.print( F("mouse rc\r\n") );
    Serial1.print( F("  mouse R-button single click\r\n") );
    Serial1.print( F("mouse p\r\n") );
    Serial1.print( F("  mouse L-button press\r\n") );
    Serial1.print( F("mouse r\r\n") );
    Serial1.print( F("  mouse L-button release\r\n") );
    Serial1.print( F("mouse scr z\r\n") );
    Serial1.print( F("  mouse wheel scroll\r\n") );
    Serial1.print( F("mouse x y\r\n") );
    Serial1.print( F("  mouse cursor move\r\n") );
}

void cmd_print_ver(void)
{
    Serial1.print( F("This is ") );
    Serial1.print( F(__FILE__) );
    Serial1.print( F(" ") );
    Serial1.print( F("Build at ") );
    Serial1.print( F(__DATE__) );
    Serial1.print( F(" ") );
    Serial1.print( F(__TIME__) );
    Serial1.print( F("\r\n") );
}

int cmd_str(char *str)
{
    int i;
    int len;
    len = strlen(str);
    
    for(i=0; i<len; i++)
    {
        if(str[i]<0x20)
        {
            return CMD_INVALID;
        }
    }
    
    for(i=0; i<len; i++){ keyboard_key(KEY_NUL, KEY_NUL, str[i]); }
    return CMD_OK;
}

int cmd_str_to_char(char *str)
{
    int keycode;
    char buf[2];
    
    if     (strcmp(str, "ent")==0){ keycode = KEY_RETURN; }
    else if(strcmp(str, "sp" )==0){ keycode = 0x20; }
    else if(strcmp(str, "tab")==0){ keycode = KEY_TAB; }
    else if(strcmp(str, "bs" )==0){ keycode = KEY_BACKSPACE; }
    else if(strcmp(str, "del")==0){ keycode = KEY_DELETE; }
    else if(strcmp(str, "esc")==0){ keycode = KEY_ESC; }
    else if(strcmp(str, "ps" )==0){ keycode = KEY_PRINTSCREEN; }
    else if(strcmp(str, "ua" )==0){ keycode = KEY_UP_ARROW; }
    else if(strcmp(str, "da" )==0){ keycode = KEY_DOWN_ARROW; }
    else if(strcmp(str, "la" )==0){ keycode = KEY_LEFT_ARROW; }
    else if(strcmp(str, "ra" )==0){ keycode = KEY_RIGHT_ARROW; }
    else
    {
        if(strlen(str)>1) { return CMD_INVALID; }
        sscanf(str, "%c", buf);
        keycode = buf[0];
        if((keycode<0x20) || (keycode>0x7f)){ return CMD_INVALID; }
    }
    return keycode;
}

int cmd_key(int primary_key, int secondary_key, char *str)
{
    int keycode;
    keycode = cmd_str_to_char(str);
    if(keycode<0)
    {
        return CMD_INVALID;
    }
    keyboard_key(primary_key, secondary_key, keycode);
    return CMD_OK;
}

int cmd_execute(char *buf)
{
    int i, x, y;
    unsigned int ux;
    int return_val = CMD_OK;
    char cmd[CMD_MAX_LENGTH];
    char arg1[ARG_MAX_LENGTH];
    char arg2[ARG_MAX_LENGTH];
    
    strcpy(cmd, "");
    strcpy(arg1, "");
    strcpy(arg2, "");
    sscanf(buf, "%s %s %s", &cmd, &arg1, &arg2);
    
    if     (strcmp(cmd, "help")==0){ cmd_print_help();}
    else if(strcmp(cmd, "?"   )==0){ cmd_print_help();}
    else if(strcmp(cmd, "ver" )==0){ cmd_print_ver(); }
    
    else if((strcmp(cmd, "quit")==0) or (strcmp(cmd, "exit")==0))
    {
        return CMD_QUIT;
    }
    
    else if(strcmp(cmd, "scrlock")==0)
    {
        win_screen_lock();
    }
    
    else if(strcmp(cmd, "port")==0)
    {
        i = atoi(arg1);
        switch (i)
        {
            case 1:
                x = OUTPUT_1;
                break;
            case 2:
                x = OUTPUT_2;
                break;
            case 3:
                x = OUTPUT_3;
                break;
            case 4:
                x = OUTPUT_4;
                break;
            default:
                return CMD_INVALID;
        }
        
        if     (strcmp(arg2, "low" )==0){ y = LOW;  }
        else if(strcmp(arg2, "high")==0){ y = HIGH; }
        else
        {
            return CMD_INVALID;
        }
        
        digitalWrite(x, y);
    }
    
    else if(strcmp(cmd, "str")==0){ return_val = cmd_str(&arg1[0]); }
    else if(strcmp(cmd, "key")==0)
    {
        if     (strcmp(arg1, "alt"      )==0){ return_val = cmd_key(KEY_LEFT_ALT,   KEY_NUL,        &arg2[0]); }
        else if(strcmp(arg1, "ctrl"     )==0){ return_val = cmd_key(KEY_LEFT_CTRL,  KEY_NUL,        &arg2[0]); }
        else if(strcmp(arg1, "shift"    )==0){ return_val = cmd_key(KEY_LEFT_SHIFT, KEY_NUL,        &arg2[0]); }
        else if(strcmp(arg1, "win"      )==0){ return_val = cmd_key(KEY_LEFT_GUI,   KEY_NUL,        &arg2[0]); }
        else if(strcmp(arg1, "ctrlalt"  )==0){ return_val = cmd_key(KEY_LEFT_CTRL,  KEY_LEFT_ALT,   &arg2[0]); }
        else if(strlen(arg1) != 0           ){ return_val = cmd_key(KEY_NUL,        KEY_NUL,        &arg1[0]); }
        else
        {
            return CMD_INVALID;
        }
    }
    else if(strcmp(cmd, "mouse")==0)
    {
        if     (strcmp(arg1, "sc" )==0){ mouse_single_click(MOUSE_LEFT ); }
        else if(strcmp(arg1, "wc" )==0){ mouse_double_click(MOUSE_LEFT ); }
        else if(strcmp(arg1, "rc" )==0){ mouse_single_click(MOUSE_RIGHT); }
        else if(strcmp(arg1, "p"  )==0){ if(!Mouse.isPressed(MOUSE_LEFT)){ Mouse.press  (MOUSE_LEFT); } }
        else if(strcmp(arg1, "r"  )==0){ if( Mouse.isPressed(MOUSE_LEFT)){ Mouse.release(MOUSE_LEFT); } }
        else if(strcmp(arg1, "scr")==0)
        {
            x = atoi(arg2);
            if(!x){ return CMD_INVALID; }
            Mouse.move(0, 0, x);
        }
        else
        {
            x = atoi(arg1);
            y = atoi(arg2);
            Mouse.move(x, y, 0);
        }
    }

    else if(strcmp(cmd, "echo")==0)
    {
        if     (strcmp(arg1, "on" )==0){ cmd_set_echoback(true); }
        else if(strcmp(arg1, "off")==0){ cmd_set_echoback(false);}
        else
        {
            return CMD_INVALID;
        }
    }
    
    else if(strcmp(cmd, "toneon")==0)
    {
        ux = (unsigned int)atoi(arg1);
        if( 32 <= ux && ux <= 22500)
        {
            tone(10, ux);
        }
        else
        {
            return CMD_INVALID;
        }
    }
    else if(strcmp(cmd, "toneoff")==0)
    {
        noTone(10);
    }
    
    else if(strcmp(cmd, "typesize")==0)
    {
        Serial1.print( F("char: ") );
        Serial1.print( sizeof(char) );

        Serial1.print( F("\r\nint: ") );
        Serial1.print( sizeof(int) );

        Serial1.print( F("\r\nshort: ") );
        Serial1.print( sizeof(short) );

        Serial1.print( F("\r\nlong: ") );
        Serial1.print( sizeof(long) );

        Serial1.print( F("\r\nlong long: ") );
        Serial1.print( sizeof(long long) );

        Serial1.print( F("\r\n") );
    }
    
    else
    {
        return CMD_INVALID;
    }
    return return_val;
}

void cmd_rx_data(void)
{
    int i;
    int return_val = CMD_OK;
    char buf[CMD_BUF_LENGTH];
    
    /* モード切替時の "Hit any key" のキー操作を捨てる */
    while(Serial1.available()){ Serial1.read(); }
    
    Serial1.print( F("### Command Mode. ###\r\n") );
    Serial1.print( F("### Hit ? to help.###\r\n") );
    Serial1.print( F("$") );
    
    i=0;
    while(1)
    {
        if(Serial1.available())
        {
            buf[i] = Serial1.read();
            if( cmd_get_echoback() )
            {
                Serial1.print(buf[i]); //echo-back
            }
            
            if ( (buf[i] == 0x08) or (buf[i] == 0x7f) ) //BackSpace, Delete
            {
                buf[i] = '\0';
                if(i) i--;
            }
            else if( (buf[i] == '\r') or (buf[i] == '\n') )
            {
                Serial1.print( F("\r\n") );
                buf[i] = '\0';
                return_val = cmd_execute(&buf[0]);
                for(i=0; i<CMD_BUF_LENGTH; i++) buf[i] = '\0';
                i=0;
                
                if(return_val == CMD_QUIT)
                {
                    Serial1.print( F("### Quit Command Mode. ###\r\n") );
                    return;
                }
                else if(return_val == CMD_INVALID)
                {
                    Serial1.print( F("?\r\n") );
                    Serial1.print( F("$") );
                }
                else
                {
                    Serial1.print( F("OK\r\n$") );
                }
            }
            else
            {
                i++;
                if(i>=CMD_BUF_LENGTH)
                {
                    Serial1.print( F("### CMD BUFFER FULL, CLEAR. ###\r\n") );
                    for(i=0; i<CMD_BUF_LENGTH; i++) buf[i] = '\0';
                    i=0;
                }
            }
        }
    }// while
}


/***********************************************************************
  setup, loop
 ***********************************************************************/
const char msg_setup_01[] PROGMEM = "Arduino Leonardo Tools Ver.2\r\n";
const char msg_setup_02[] PROGMEM = "Hit any key to Command Mode.\r\n";
void setup()
{
    Keyboard.begin();
    Mouse.begin();
    Serial1.begin(9600);
    
    gpio_init();
    keypad_init();
    set_periodic_time(DEFAULT_PERIODIC_TIME); // 二相パルスの周期初期化
    set_mouse_moving_distance(1);
    cmd_set_echoback(true);
    cmd_print_msg(&msg_setup_01[0], sizeof(msg_setup_01));
    cmd_print_msg(&msg_setup_02[0], sizeof(msg_setup_02));
}
void loop()
{
    int keypad_val;
    keypad_val = keypad_get_key();
    
    digitalWrite(OUTPUT_1, !digitalRead(MODE_SELECT)); // B面の時に点灯する
    if (keypad_val & 0x001ff)
    {
        keypad_execute(keypad_val);
    }
    if( !digitalRead(MODE_SELECT) )
    {
        if( 0x0f == get_joystick_status() )
        {
            move_mouse_cursor_with_joystick();
        }
    }
    if(Serial1.available())
    {
        digitalWrite(OUTPUT_2, HIGH);
        cmd_rx_data();
        digitalWrite(OUTPUT_2, LOW);
    }
    
    if(digitalRead(EXT_INPUT) == SW_PRESSED)
    {
        set_periodic_time_by_analog_in_a0();
    }
}
```