ESP32でBLEのデモを動かす 補足

ESP32にBLEのデモの認証方法についての補足

概要

ESP32でBLEのデモを動かす では認証方法(IO capability なので入出能力と言うべきか? あまり直感的でないのでここでは認証方法と呼んでおこう)
ESP_IO_CAP_NONE(NoInputNoOutput; 入出力なし)に設定していたが、これを別の認証方法に変更する場合についてのメモ。

[!NOTE] 自作の範囲だとESP_IO_CAP_NONEしか使わないと思うけど、使いたくなった時に備えて。

ESP_IO_CAP_NONE (NoInputNoOutput)

入出力両方なし。
passkey(PINcode)入力などはなし。
接続すると自動的に鍵交換して接続される。
接続動作以降のログ例は以下。

V (92315) BLE_HEART_RATE: - GATT_EVT: ESP_GATTS_CONNECT_EVT(14)
I (92315) BLE_HEART_RATE:     connection start
V (92975) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (92975) BLE_HEART_RATE:     event nor handled
V (93245) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (93245) BLE_HEART_RATE:     event nor handled
E (97855) BT_BTM: btm_ble_remove_resolving_list_entry_complete remove resolving list error 0x2
V (98285) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (98285) BLE_HEART_RATE:     event nor handled
W (98335) BT_SMP: FOR LE SC LTK IS USED INSTEAD OF STK
V (98375) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (98375) BLE_HEART_RATE:     key type = ESP_LE_KEY_LENC
V (98375) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (98375) BLE_HEART_RATE:     key type = ESP_LE_KEY_PENC
V (98385) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (98385) BLE_HEART_RATE:     key type = ESP_LE_KEY_LID
V (98405) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (98405) BLE_HEART_RATE:     key type = ESP_LE_KEY_PID
D (98405) nvs: nvs_open_from_partition bt_config.conf 1
D (98415) nvs: nvs_set_blob bt_cfg_key0 475
D (98425) nvs: nvs_close 4
D (98425) nvs: nvs_open_from_partition bt_config.conf 1
D (98425) nvs: nvs_set_blob bt_cfg_key0 475
D (98485) nvs: nvs_close 5
D (98485) nvs: nvs_open_from_partition bt_config.conf 1
D (98485) nvs: nvs_set_blob bt_cfg_key0 475
D (98495) nvs: nvs_close 6
D (98495) nvs: nvs_open_from_partition bt_config.conf 1
D (98495) nvs: nvs_set_blob bt_cfg_key0 475
D (98515) nvs: nvs_close 7
D (98515) nvs: nvs_open_from_partition bt_config.conf 1
D (98515) nvs: nvs_set_blob bt_cfg_key0 475
D (98515) nvs: nvs_close 8
V (98515) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_AUTH_CMPL_EVT(8)
I (98525) BLE_HEART_RATE:     remote BD_ADDR: 5d5308bb0efd
I (98525) BLE_HEART_RATE:     address type = 1
I (98535) BLE_HEART_RATE:     pair status = success
I (98535) BLE_HEART_RATE:     auth mode = ESP_LE_AUTH_REQ_SC_BOND
I (98545) BLE_HEART_RATE:     Bonded devices number : 1
I (98545) BLE_HEART_RATE:     Bonded devices list :
I (98555) BLE_HEART_RATE:        0 :   5d:53:08:bb:0e:fd
V (99025) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (99025) BLE_HEART_RATE:     event nor handled
V (99545) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (99545) BLE_HEART_RATE:     event nor handled
V (99755) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (99755) BLE_HEART_RATE:     event nor handled```

ESP_IO_CAP_OUT (DisplayOnly)

表示機器のみ必要。
自分が指定するpasskeyをコンソール等に表示し、相手側(ホスト)にpasskey(PINcode)入力させる。

passkeyはesp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, ~);で設定するので、
必ず表示しなくてはいけない、ということはない(BTヘッドフォンなどでマニュアルに「0000を入力」などと書かれているのと同じ)。

相手側には入力手段が必須。

GAP(Generic Access Profile)のコールバック(gap_event_handler())の以下の部分で行う。

      case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:               // passkey通知要求イベント
        // 自身がDisplayOnly(ESP_IO_CAP_OUT)のときに発生する
        ESP_LOGI(APP_TAG, "    ==== ESP_GAP_BLE_PASSKEY_NOTIF_EVT ====");
        // passkeyの表示
        printf("**** The passkey Notify number:%06d\n", param->ble_security.key_notif.passkey);
        break;

相手側で入力されたpasskeyが正しければ鍵交換して接続される。
接続動作以降のログ例は以下。

V (17105) BLE_HEART_RATE: - GATT_EVT: ESP_GATTS_CONNECT_EVT(14)
I (17105) BLE_HEART_RATE:     connection start
V (17775) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (17775) BLE_HEART_RATE:     event nor handled
V (18025) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (18025) BLE_HEART_RATE:     event nor handled
V (21935) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_PASSKEY_NOTIF_EVT(11)
I (21935) BLE_HEART_RATE:     ==== ESP_GAP_BLE_PASSKEY_NOTIF_EVT ====
**** The passkey Notify number:123456                     ← 相手側にこのpasskeyを入力
V (22185) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (22185) BLE_HEART_RATE:     event nor handled
W (33505) BT_SMP: FOR LE SC LTK IS USED INSTEAD OF STK
V (33535) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (33535) BLE_HEART_RATE:     key type = ESP_LE_KEY_LENC
V (33535) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (33535) BLE_HEART_RATE:     key type = ESP_LE_KEY_PENC
V (33545) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (33545) BLE_HEART_RATE:     key type = ESP_LE_KEY_LID
V (33575) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (33575) BLE_HEART_RATE:     key type = ESP_LE_KEY_PID
D (33575) nvs: nvs_open_from_partition bt_config.conf 1
D (33575) nvs: nvs_set_blob bt_cfg_key0 250
D (33595) nvs: nvs_close 4
D (33595) nvs: nvs_open_from_partition bt_config.conf 1
D (33595) nvs: nvs_set_blob bt_cfg_key0 264
D (33605) nvs: nvs_close 5
D (33605) nvs: nvs_open_from_partition bt_config.conf 1
D (33605) nvs: nvs_set_blob bt_cfg_key0 347
D (33625) nvs: nvs_close 6
D (33625) nvs: nvs_open_from_partition bt_config.conf 1
D (33625) nvs: nvs_set_blob bt_cfg_key0 407
D (33625) nvs: nvs_close 7
D (33625) nvs: nvs_open_from_partition bt_config.conf 1
D (33635) nvs: nvs_set_blob bt_cfg_key0 462
D (33635) nvs: nvs_close 8
D (33645) nvs: nvs_open_from_partition bt_config.conf 1
D (33645) nvs: nvs_set_blob bt_cfg_key0 476
D (33655) nvs: nvs_close 9
V (33655) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_AUTH_CMPL_EVT(8)
I (33655) BLE_HEART_RATE:     remote BD_ADDR: 786562f732fc
I (33655) BLE_HEART_RATE:     address type = 1
I (33665) BLE_HEART_RATE:     pair status = success
I (33665) BLE_HEART_RATE:     auth mode = ESP_LE_AUTH_REQ_SC_MITM_BOND
I (33675) BLE_HEART_RATE:     Bonded devices number : 1
I (33675) BLE_HEART_RATE:     Bonded devices list :
I (33685) BLE_HEART_RATE:        0 :   78:65:62:f7:32:fc
V (34185) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (34185) BLE_HEART_RATE:     event nor handled
V (34695) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (34695) BLE_HEART_RATE:     event nor handled
V (34885) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (34885) BLE_HEART_RATE:     event nor handled

passkeyを間違えると当然接続されない。
接続が成功したかどうかは、ログの以下の部分で判別できる。

V (27095) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_AUTH_CMPL_EVT(8)
I (27095) BLE_HEART_RATE:     remote BD_ADDR: 67c015dc4505
I (27105) BLE_HEART_RATE:     address type = 1
I (27105) BLE_HEART_RATE:     pair status = fail            ← failしている
I (27115) BLE_HEART_RATE:     reason = 0x51                 ← 失敗した原因だけど、ドキュメントがない...
I (27115) BLE_HEART_RATE:     Bonded devices number : 0
I (27125) BLE_HEART_RATE:     Bonded devices list :

ESP_IO_CAP_IN (KeyboardOnly)

入力機器のみ必要。
相手側に表示されたpasskeyをコンソール等から入力する。

passkeyは相手側から指定される値なので、どのような値かは相手の仕様による。
相手側には表示手段が必須。

GAP(Generic Access Profile)のコールバック(gap_event_handler())の以下の部分で行う。

      case ESP_GAP_BLE_PASSKEY_REQ_EVT:                 // passkey 要求
        // KeyboardOnly(ESP_IO_CAP_IN)のときに発生する
        ESP_LOGI(APP_TAG, "    ==== ESP_GAP_BLE_PASSKEY_REQ_EVT ====");
        // 以下の関数で相手側に表示されたパスキーを返す
        uint32_t    passkey;
        char        passkey_buff[16];
        int         passkey_len;
        do {
            printf("**** input paskey : ");
            fflush(stdout);
            passkey_len = uart_gets(passkey_buff, sizeof(passkey_buff));
        } while (passkey_len == 0);
        passkey = (uint32_t)strtol(passkey_buff, NULL, 10);
        esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, passkey);
        break;

passkeyに使用しているuart_gets()関数については後述。

入力したpasskeyが正しければ鍵交換して接続される。
接続動作以降のログ例は以下。

V (16805) BLE_HEART_RATE: - GATT_EVT: ESP_GATTS_CONNECT_EVT(14)
I (16805) BLE_HEART_RATE:     connection start
V (17425) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (17425) BLE_HEART_RATE:     event nor handled
V (17675) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (17685) BLE_HEART_RATE:     event nor handled
E (20765) BT_BTM: btm_ble_remove_resolving_list_entry_complete remove resolving list error 0x2
V (20955) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_PASSKEY_REQ_EVT(12)
I (20955) BLE_HEART_RATE:     ==== ESP_GAP_BLE_PASSKEY_REQ_EVT ====
**** input paskey : 047528                                  ← 相手側で表示されているpasskeyを入力
V (36455) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (36455) BLE_HEART_RATE:     event nor handled
W (37335) BT_SMP: FOR LE SC LTK IS USED INSTEAD OF STK
V (37375) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (37375) BLE_HEART_RATE:     key type = ESP_LE_KEY_LENC
V (37375) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (37375) BLE_HEART_RATE:     key type = ESP_LE_KEY_PENC
V (37385) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (37385) BLE_HEART_RATE:     key type = ESP_LE_KEY_LID
V (37405) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (37405) BLE_HEART_RATE:     key type = ESP_LE_KEY_PID
D (37415) nvs: nvs_open_from_partition bt_config.conf 1
D (37415) nvs: nvs_set_blob bt_cfg_key0 476
D (37425) nvs: nvs_close 4
D (37425) nvs: nvs_open_from_partition bt_config.conf 1
D (37425) nvs: nvs_set_blob bt_cfg_key0 476
D (37495) nvs: nvs_close 5
D (37495) nvs: nvs_open_from_partition bt_config.conf 1
D (37495) nvs: nvs_set_blob bt_cfg_key0 476
D (37505) nvs: nvs_close 6
D (37505) nvs: nvs_open_from_partition bt_config.conf 1
D (37505) nvs: nvs_set_blob bt_cfg_key0 476
D (37525) nvs: nvs_close 7
D (37525) nvs: nvs_open_from_partition bt_config.conf 1
D (37525) nvs: nvs_set_blob bt_cfg_key0 476
D (37525) nvs: nvs_close 8
V (37525) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_AUTH_CMPL_EVT(8)
I (37535) BLE_HEART_RATE:     remote BD_ADDR: 786562f732fc
I (37535) BLE_HEART_RATE:     address type = 1
I (37545) BLE_HEART_RATE:     pair status = success
I (37545) BLE_HEART_RATE:     auth mode = ESP_LE_AUTH_REQ_SC_MITM_BOND
I (37555) BLE_HEART_RATE:     Bonded devices number : 1
I (37555) BLE_HEART_RATE:     Bonded devices list :
I (37565) BLE_HEART_RATE:        0 :   78:65:62:f7:32:fc
V (38255) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (38255) BLE_HEART_RATE:     event nor handled

passkeyを間違えたときの動作は ESP_IO_CAP_OUT(DisplayOnly)の場合と同様。

ESP_IO_CAP_IO (DisplayYesNo)

入力機器/表示機器 両方必要。

相手側に表示されたpasskeyとコンソールに表示されたpasskeyを目視確認して等しければyを入力し、相手側でも接続許可をタップする。 確認せずにYesを選択しても良いけど、それならこのモードを指定する必要はないでしょう。

相手側にも入力/表示手段が必須。
GAP(Generic Access Profile)のコールバック(gap_event_handler())の以下の部分で行う。

      case ESP_GAP_BLE_NC_REQ_EVT:                      // 数値比較リクエスト イベント
        // DisplayYesNo(ESP_IO_CAP_IO)のときに発生する
        ESP_LOGI(APP_TAG, "    ==== ESP_GAP_BLE_NC_REQ_EVT ====");
        printf("**** the passkey Notify number:%d\n", param->ble_security.key_notif.passkey);
        printf("**** Accept? (y/n) : ");
        fflush(stdout);
        int kb_key = uart_getchar();
        printf("%c\n", kb_key);
        if (kb_key == 'y' || kb_key == 'Y') {
            // 接続受け入れ
            esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, true);
        }
        else {
            // 接続拒否
            esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, false);
        }
        break;

両方でyesを選択すれば鍵交換して接続される。
接続動作以降のログ例は以下。

V (14185) BLE_HEART_RATE: - GATT_EVT: ESP_GATTS_CONNECT_EVT(14)
I (14185) BLE_HEART_RATE:     connection start
V (14855) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (14855) BLE_HEART_RATE:     event nor handled
V (15145) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (15145) BLE_HEART_RATE:     event nor handled
E (18775) BT_SMP: Value for numeric comparison = 260195
V (18775) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_NC_REQ_EVT(16)
I (18775) BLE_HEART_RATE:     ==== ESP_GAP_BLE_NC_REQ_EVT ====
**** the passkey Notify number:260195                                               ← 相手側で表示されている数値と目視比較
**** Accept? (y/n) : y                                                              ← y 入力、相手側でもyesクリック
V (26785) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (26785) BLE_HEART_RATE:     event nor handled
W (28245) BT_SMP: FOR LE SC LTK IS USED INSTEAD OF STK
V (28275) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (28275) BLE_HEART_RATE:     key type = ESP_LE_KEY_LENC
V (28275) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (28285) BLE_HEART_RATE:     key type = ESP_LE_KEY_PENC
V (28285) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (28295) BLE_HEART_RATE:     key type = ESP_LE_KEY_LID
V (28305) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_KEY_EVT(9)
I (28305) BLE_HEART_RATE:     key type = ESP_LE_KEY_PID
D (28315) nvs: nvs_open_from_partition bt_config.conf 1
D (28315) nvs: nvs_set_blob bt_cfg_key0 250
D (28325) nvs: nvs_close 4
D (28325) nvs: nvs_open_from_partition bt_config.conf 1
D (28325) nvs: nvs_set_blob bt_cfg_key0 264
D (28335) nvs: nvs_close 5
D (28335) nvs: nvs_open_from_partition bt_config.conf 1
D (28335) nvs: nvs_set_blob bt_cfg_key0 347
D (28355) nvs: nvs_close 6
D (28355) nvs: nvs_open_from_partition bt_config.conf 1
D (28355) nvs: nvs_set_blob bt_cfg_key0 407
D (28365) nvs: nvs_close 7
D (28365) nvs: nvs_open_from_partition bt_config.conf 1
D (28365) nvs: nvs_set_blob bt_cfg_key0 462
D (28385) nvs: nvs_close 8
D (28385) nvs: nvs_open_from_partition bt_config.conf 1
D (28385) nvs: nvs_set_blob bt_cfg_key0 476
D (28395) nvs: nvs_close 9
V (28395) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_AUTH_CMPL_EVT(8)
I (28395) BLE_HEART_RATE:     remote BD_ADDR: 612610f26701
I (28395) BLE_HEART_RATE:     address type = 1
I (28405) BLE_HEART_RATE:     pair status = success
I (28405) BLE_HEART_RATE:     auth mode = ESP_LE_AUTH_REQ_SC_MITM_BOND
I (28415) BLE_HEART_RATE:     Bonded devices number : 1
I (28415) BLE_HEART_RATE:     Bonded devices list :
I (28425) BLE_HEART_RATE:        0 :   61:26:10:f2:67:01
V (28905) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (28905) BLE_HEART_RATE:     event nor handled
V (29435) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (29435) BLE_HEART_RATE:     event nor handled
V (29655) BLE_HEART_RATE: * GAP_EVT: ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT(20)
I (29655) BLE_HEART_RATE:     event nor handled

ESP_IO_CAP_KBDISP (Keyboard display)

ESP_IO_CAP_OUT(DisplayOnly) と ESP_IO_CAP_IN(KeyboardOnly) の合わせ技かと思いきや、
ESP_IO_CAP_IO(DisplayYesNo)と同じ動作。
たしかにキーボードと表示両方必要だわな。。。

コンソール入力ルーチン

passkeyの入力など、コンソールからの入力が必要な場合、gets()などを使用するとキー入力待ちの間タスク切り替えが行われずに他のタスクが動作できない。
(esp-idfはFreeRTOSを使用したマルチタスク構成)。 そこで、gets()/getchar()の代わりとなる関数を用意した。
動作としては、キー入力待ちの少しの間、vTaskDelay()でCPUを解放して他のタスクの動作を阻害しないようにしている。

uart_checkkey()は今回使ってないけど、〇秒以内に入力がなければデフォルトで動作、のような動作を実現するのに使用する関数。

printf()もあちこちのタスクから出力する場合はセマフォで排他処理した方が良いけど、今回は特に問題になりそうにないのでそのまま使用。

Buildは これらのファイルをsrcディレクトリにぶち込むだけでOK。

uart_console.h

#include    <stdio.h>
#include    <stdint.h>
#include    <stdbool.h>

extern bool uart_checkkey(int loop_num);
extern int uart_getchar(void);
extern int uart_gets(char* buf, int max);

uart_console.c

#include    <stdio.h>
#include    <stdint.h>
#include    <stdbool.h>
#include    <string.h>
#include    <stdarg.h>

#include    "freertos/FreeRTOS.h"
#include    "freertos/task.h"
#include    "esp_system.h"
#include    "rom/uart.h"

#include    "uart_console.h"

// ========= UARTからの入力待ち ===============================================
// param    loop_num: 待ち時間(単位100msec)
// return   true: キー入力があった     false: キー入力はなかった
bool uart_checkkey(int loop_num)
{
    bool    ret = false;
    uint8_t ch;
     for (int loop_cnt = 0; loop_cnt < loop_num; loop_cnt++) {
        // UARTから1文字取得
        if (uart_rx_one_char(&ch) == OK) {
            // 入力あり
            ret = true;
            break;
        }
        if ((loop_cnt % 5)== 0) {
            // たくさん出ると鬱陶しいので5回毎に
            putchar('.');
            fflush(stdout);
        }
        // 100ms待つ
        vTaskDelay(100 / portTICK_RATE_MS);
    }
    putchar('\n');

    // バッファにたまっているデータを読み捨てる
    while (uart_rx_one_char(&ch) == OK);
    return ret;
}


// ========= UARTから1文字取得 ================================================
// param    なし
// return   文字コード
// note     CRは無視するので注意
int uart_getchar(void)
{
    uint8_t ch;
    while (1) {
        // UARTから1文字取得
        if (uart_rx_one_char(&ch) == OK) {
            // 入力あり
            if (ch == '\r') {
                // CRなら次の値を取得
                continue;
            }
            // 入力された値を返す
            return ch;
        }
        // 100ms待つ(CPUを握りっぱなしにしないように)
        vTaskDelay(100 / portTICK_RATE_MS);
    }
}

// ========= UARTから1行取得 ===================================================
// param    buf: 文字列格納領域へのポインタ
//          max: 最大文字列長
// return   入力された文字列長
int uart_gets(char* buf, int max)
{
    int     i = 0;
    while (i < (max - 1)) {     // null terminate の分を空けておくので max - 1
        char ch = uart_getchar();
        if (ch == '\n') {
            // LFで終了
            putchar(ch);
            fflush(stdout);
            break;
        }
        if (ch == '\b' || ch == 0x7f) {     // BackSpace or DEL
            if (i > 0) {        // 先頭でない
                i--;            // ポインタを一つ前に
                putchar('\b');  // 前の文字の表示を削除
                putchar(' ');
                putchar('\b');
                fflush(stdout);
            }
        }
        else {
            // それ以外の文字はバッファに格納
            buf[i] = ch;
            i++;
            putchar(ch);        // 表示
            fflush(stdout);
        }
    }
    buf[i] = '\0';          // null terminate
    return i;
}