XIAO ESP32C6のBLEでログインボタンを作る

# はじめに
最近、ハマっている [Seeed Studio XIAO ESP32C6](https://akizukidenshi.com/catalog/g/g129481/)でBluetooth Low Energy（BLE）のテストを兼ねて、無線１ボタンキーボードを作ってみた。１ボタン動作は色々用途があると思いますが、今回は、Windowsのログインを自動化するボタンにしてみた。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203949/f47bb220-c5f0-4e2e-83b8-151121ee3dde.png" width=30%>

<iframe width="560" height="315" src="https://www.youtube.com/embed/28FG4bl_ojg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


以前の「Seeed Studio XIAO ESP32C6」の記事
- [XIAO ESP32C6のLチカ](https://qiita.com/nori-dev-akg/items/e9d2ff175b7d7384498f)
- [XIAO ESP32C6のリポバッテリー運用について](https://qiita.com/nori-dev-akg/items/d3dc43fccd8dff622926)
- [春なので冬眠中のESP32とTFTでフォトカレンダーを作ろう](https://qiita.com/nori-dev-akg/items/579ebf86c1163ea38c2f)

# ハード
- Windows10
- XIAO ESP32C6
- ブレッドボード
- タクトスイッチx1
- ジャンプワイヤー
- リポバッテリー3.7v 210mAh

## XIAO ESP32C6
使用したXIAO ESP32C6。
- [Seeed Studio XIAO ESP32C6](https://akizukidenshi.com/catalog/g/g129481/)
- [オフィシャルサイト](https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/)

## 配線
HIGH検知のボタン。10KΩでプルダウンさせておく。
DeepSleepからの復帰に使うので XIAO ESP32C6 の場合は、[GPIO0,1,2](https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/#demo1-deep-sleep-with-external-wake-up)のいずれかにする必要がある。
```
ESP32C6 GPIO1 -+- BUTTON --- 3.3v
               |
               +-- 10KΩ --- GND
```

## Li-Poバッテリ駆動
リチウムポリマー電池を使ってワイヤレス。
背面のBAT±にLi-Poバッテリを繋ぐだけ。※要はんだ付け

# ソフト
- ArduinoIDE 2.3.6(Arduino core3.2.0)
- ESP32-BLE-Keyboard 0.3.2
 

## ESP32-BLE-Keyboard
ESP32には、BLE が使えるので無線キーボード化できる。Arduino用ライブラリは色々あるが今回は下記を使用。

https://github.com/T-vK/ESP32-BLE-Keyboard

XIAO ESP32C6ではコンパイルエラーが出るのでbegin関数の中を以下のように修正した。

```cpp:Arduino\libraries\ESP32-BLE-Keyboard\BleKeyboard.cpp
void BleKeyboard::begin(void)
{
#if defined(ARDUINO_XIAO_ESP32C6)
  BLEDevice::init(deviceName.c_str());
#else
  BLEDevice::init(deviceName);
#endif
      :
      :
#if defined(ARDUINO_XIAO_ESP32C6)
  hid->manufacturer()->setValue(deviceManufacturer.c_str());
#else
  hid->manufacturer()->setValue(deviceManufacturer);
#endif
      :
      :
#if defined(ARDUINO_XIAO_ESP32C6)
  pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND);
#else
  pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND);
#endif

```

# 仕様
ログインパスワードをソース埋め込みにすればもっと簡単なロジックになる。今回はNVS書き込みにしたので少々手間。プロコンで良ければごっそり外してくださいな。

| 手順 | 説明（初回のみ） |  |
|:-:|:-|:-:|
| 0 |リポバッテリーは外しておく |  |
| 1 |PC ⇔ ESP32C6 USB接続後 コンパイル＆アップロード|   |
| 2 |ペアリングモード（内臓LED点滅）|  |
| 3 |WindowsのBluetoothデバイスの追加で「ESP32BLEButton」を追加<br>(30秒以内→ボタン押下で2から)|  |
| 4 |BLE接続完了（内臓LED点灯）|  |
| 5 |シリアルモニタでログインパスワード入力<br>「Enter new password:」|  |
| 6 |パスワードがNVS(不揮発性メモリ)に保存される|  |
| 7 |ESP32C6をPCから取り外す |  |
| 8 |リポバッテリー接続 |  |
| 9 |BLE接続開始（内臓LED点滅）|  |
| 10|BLE接続完了（内臓LED点灯）|  |
| 11|10秒後 BLE切断（内臓LED消灯）|  |
| 12|DeepSleep|  |
<br>

| 手順 | 説明(2回目以降ここから) |  |
|:-:|:-|:-:|
| 0 |DeepSleep（内臓LED消灯） |  |
| 1 |１回ボタン押下（復帰） |  |
| 2 |BLE接続開始（内臓LED点滅） |  |
| 3 |BLE接続完了（内臓LED点灯） |  |
| 4 |２回ボタン押下（ダブルクリック） |  |
| 5 |Windowsへログインパスワード送信 |  |
| 6 |10秒後 BLE切断（内臓LED消灯）|  |
| 7 |DeepSleep|  |
<br>

| 手順 | 説明(パスワード変更) |  |
|:-:|:-|:-:|
| 0 |リポバッテリーは外す |  |
| 1 |PC ⇔ ESP32C6 USB接続| |
| 2 |ArduinoIDE起動 シリアルモニタ COM接続確認|  |
| 3 |ボタン長押し（3秒） |  |
| 4 |NVS(不揮発性メモリ)クリア|  |
| 5 |初回手順5へ|  |
<br>

# ソース
githubに置きました。
https://github.com/nori-dev-akg/BLE-Button-login

以下、ちょっと説明。

## BLE接続
以下の ```bleKeyboard.begin()``` がBLE接続開始。そのあと接続確認ループ。タイムアウト```CONNECT_TIMEOUT```するまで内臓LEDを点滅させる処理。タイムアウトしたらDeepSleepになる。

```cpp
  bleKeyboard.begin(); // Start BLE keyboard
  Serial.print("BLE Keyboard started. Waiting for connection...");
  unsigned long startTime = millis();
  while (!bleKeyboard.isConnected()) {
    if (millis() - startTime > CONNECT_TIMEOUT * 1000) {
      Serial.println("Connection timeout. Please try again.");
      gotoSleep();
      return;
    }
    digitalWrite(LED_PIN, LOW);
    delay(100);
    digitalWrite(LED_PIN, HIGH);
    delay(100);
    Serial.print(".");
  }
  Serial.println("connected!");
```

## ログインパスワード送信
Windowsロック画面で、最初にコントロールキー```KEY_LEFT_CTRL```を送信して、パスワード入力フォームを表示させている。表示されるまでディレイ```WAIT_LOGIN```して、その後に ```pass```を送信して、```KEY_RETURN```を送信。

```cpp
  Serial.println("Double Pressed");
  // On double click, send Ctrl key
  bleKeyboard.press(KEY_LEFT_CTRL);
  delay(100);
  bleKeyboard.releaseAll();
  delay(WAIT_LOGIN); // Wait for login screen

  bleKeyboard.write((const uint8_t*)pass, strlen(pass)); // Send password
  bleKeyboard.write(KEY_RETURN);
```

## DeepSleep
DeepSleepでは、```esp_sleep_enable_ext1_wakeup()```関数を使う。指定したピンの状態を検知してWakeUpすることができるDeepleep。```esp_sleep_enable_ext0_wakeup()``` は ESP32C6には無い模様。さらに以下の ```BUTTON_PIN```のところで使えるピンは0,1,2だけ。

```cpp
void gotoSleep() {
  Serial.println("Going to sleep...");
  bleKeyboard.end(); // End BLE connection
  digitalWrite(LED_PIN, HIGH); // Turn off LED
  delay(500);
  
  esp_sleep_enable_ext1_wakeup(1ULL << BUTTON_PIN, ESP_EXT1_WAKEUP_ANY_HIGH); // Wakeup on HIGH
  esp_deep_sleep_start(); // Start deep sleep
}
```

# 消費電流

BLE接続中が40mA越えです。内臓LEDは数mAだとして素XIAO-ESP32C6が20mAぐらいだったから結構BLEも喰うね。1日数回接続で殆どDeepSleep状態なので結構持つと思うけど。

| BLE接続中 | DeepSleep |
|:-:|:-:|
| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203949/19d7be4c-b349-43fa-afd4-579f7968f5af.png" width=50%>  | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203949/89a4a231-ae21-461e-bf25-25571d7059cb.png" width=50%>  |
| 40.3mA | 23.8μA |


# おわりに
ハマりポイントは ESP32-BLE-Keyboard ライブラリの修正が必要なところぐらいでしょう。
Windows起動直後の一発目のBLEの接続に10秒ほど掛かるのが気持ち悪いぐらいですが、ロック状態からのログインの時は直ぐにつながる。

3Dプリンターがあればケースなんか用意できるんだけど、そんなもん置く場所ない。広い家に住みたいよ。