LoginSignup
4
6

More than 5 years have passed since last update.

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

Posted at

arduinoLeonardoToolsV2_00.jpg arduinoLeonardoToolsV2_01.jpg

1. はじめに

LチカHello, World.の同時実行のような小ネタからExcelやパソコン作業の省力化、さらには自動測定の治具までこなすArduino Leonardoを使用した多目的ツールです。

機能例:

  • スイッチひと押しでWindowsをスクリーンロック。離れたところのPCもコマンドでスクリーンロック。

    • Windowsのスクリーンロックはキーボード操作だと「Windowsロゴキー+L」と2手順(Windowsロゴキーのないキーボードだと「Ctrl+Alt+Del, Alt+k」の5手順)かかるのをスイッチひと押しで。 image.png
    • 離れたところのPCはコマンドで。 image.png
  • マウスでは難しい微調整をスムーズに。
    Excel方眼紙の強い味方。セルの大きさをあと1ピクセル調整したいとき十字に並べたスイッチで簡単に。
    arduinoLeonardoToolsV2_12.png

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

2. 技術的な目標

  1. キーパッドをキーマトリクス化してGPIOを節約しそのぶん汎用的に使えるGPIOを増やす
  2. Serial1のUART通信でArduinoを制御する

3. ハードウェア

作成したハードウェアはこちら。
arduinoLeonardoToolsV2_30.jpg

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

表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とジョイスティックでマウスカーソルを動かす

3.1 パーツ

Arduino
USB HID(Human Interface Device)機能を備えるArduino Leonardoを使用します。
基板
aitendoUBD-ARD53X68(SOIC28-1.27)を使用しています。
※後継(?)のUBD-ARD53x68-HLはねじ穴付近の穴の配置が異なるようです。
ピンソケット
ブレッドボードへ信号を引き出せるよう足の長い(10mm)ピンソケットを使用しています。
ブレッドボード、シャーシ
aitendoで売られているミニブレッドボードベース板を使用しています。
Arduino Leonardoは両面クションテープで留めています。
また、ベース板の裏にクッションゴムを貼っています。
キーパッド
キーパッドは秋月電子4x3キーパッド作成キットを参考にXYZxABCの3x3マトリクスで組んでいます。
これはマトリクス回路とソフトウェアの工夫で9個のスイッチを6本のGPIOでスキャンしOFF/ON判定するもので貴重なGPIOの節約になります。従来は9個のタクトスイッチをそのままGPIOへ接続していたのですが汎用出力を増やすため基板化に併せてマトリクス化しました。
なお、キットは専用基板、ピンヘッダ、タクトスイッチ、抵抗、ダイオードがセットになっていてブレッドボードで使いやすそうです。
汎用入力#1
特に用途を決めていない入力端子です。スイッチが押されたらループをbreakしたり。
汎用出力#1、#2、#3、#4
特に用途を決めていない出力端子です。2.2kΩの抵抗を介してLEDを接続しています。
動作のインジケートや2つセットで使って二相パルス、4つセットで使って四相パルスの生成など。
#3、#4はジャンパ端子を短絡することでリレーの駆動回路と接続します。
リレー
秋月電子で売られている941H-2C-5Dを2個載せています。これ1個で2回路のスイッチ切替を同時に行え、例えば音声信号(L/R)の入力切替ができます。
Arduino Leonardo(というかATmega32U4)の絶対最大定格はDigital Outputはピン1つあたり40mA、チップ全体で200mAなので2SD2012で駆動回路を組んでいます。R=33kΩ、Diは1N4007です。
10kΩ可変抵抗
分圧した電圧をA/D変換しループ内のsleep()の時間調整に使うのを想定して載せています。
USBシリアル変換器
秋月電子のAE-TTL-232Rです。はんだ面に印刷された端子のシルクが見えるよう部品面にヘッダピンをはんだ付けしています。

4. ソフトウェア

作成したスケッチはこのページの下の方にあります。無保証、無サポートです。

4.1 操作モード

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

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

4.2 キーパッドモード

キーパッドは以下のように並べています。1~6と9は抵抗のカラーコードに合わせましたが紫、灰色のタクトスイッチがなかったので7、8は白で代用しています。
arduinoLeonardoToolsV2_40.jpg

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 なし なし 型のサイズをバイト数で表示する

表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を使用します。

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()を実行し シリアルポートにデータがあればcmd_rx_data()へジャンプします。
  • 汎用入力#1に接続したスイッチが押下されたらset_periodic_time_by_analog_in_a0()を実行します。
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にビットフィールドで格納し戻り値で返します。digitalRead()の値はキー押下で0、開放で1ですが戻り値を返す際に押下で1、開放で0としています。

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が立っていたらキーが押下されていると判定します。

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)でループし続けます。


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()で分解しstrcmp()で文字列比較します。比較して一致したら該当するコマンドを実行します。

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マクロが用意されていますがPROGMEM修飾子とconst修飾子、Flash ROMのデータにアクセスする関数を使用して実装してみました。

/* データを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チカともHello, World.とも言われますがArduino Leonardoならどちらもできるので一つにまとめました。キーボードのキー操作、UARTへの文字出力、Lチカを行います。

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

6.1.2 Lチカ

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

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

6.3 Excelやパソコン作業の省力化

6.3.1 Excelでコピー

ExcelでHello,とWorld.のセルを選択しキーパッドA面でスイッチ2を押下するとコピーされます。
arduinoLeonardoToolsV2_61.png

6.3.2 Excelで行と列を入替えて値で貼付け

手順の多い操作もスイッチ一発で。キーパッドA面でスイッチ3を押下すると以下のダイアログを開いて、値(V)を選んで、行列を入れ替える(E)にチェックを入れてOKボタンを押してくれます。
arduinoLeonardoToolsV2_62.png

arduinoLeonardoToolsV2_63.png

6.3.3 Excel方眼紙の微調整

arduinoLeonardoToolsV2_12.png

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

6.3.4 スクリーンロック

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

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をハードウェア操作のコントローラとして使用し測定を行う:

7. スケッチ

筆者が使用しているArduino IDEはVer. 1.6.4です。

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();
    }
}
4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6