CardKBとM5StickCで、普通に使えるスマホ用のbluetoothキーボードを作る

CardKB は、M5STACK社が発売しているカードサイズのフルキーボードです。

https://docs.m5stack.com/en/unit/cardkb

前々から気になっていたのですがやっと購入しました。
ほんとに名刺サイズで薄いですねー。

私は**blackberryのようなキーボード付きスマホにちょっとあこがれを持つ者なので**、今回はこれをスマホに繋ぐことを前提に、bluetoothキーボードにしていきたいと思います。

# 参考にしたサイト
http://asamomiji.jp/contents/modifying-cardkb-firmware
https://tomono.tokyo/2021/01/26/9195/
https://gomazm.xyz/post-782/

# まずはCardKBのサンプルを見てみる
Arduino IDEのスケッチ例の中にサンプルがあります。
[スケッチ例]>[M5StickC]>[Unit]>[CardKB]
<details><summary>サンプルコード</summary>

```
#include <M5StickC.h>
#include <Wire.h>

#define CARDKB_ADDR 0x5F

void setup() {
    M5.begin();
    Wire.begin(32, 33);
    M5.Lcd.setRotation(3);
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(1, 10);
    M5.Lcd.setTextColor(YELLOW);
    M5.Lcd.setTextSize(2);
    M5.Lcd.printf("IIC Address: 0x5F\n");
    M5.Lcd.printf(">>");
}
void loop() {
    Wire.requestFrom(CARDKB_ADDR, 1);
    while (Wire.available()) {
        char c = Wire.read();  // receive a byte as characterif
        if (c != 0) {
            M5.Lcd.printf("%c", c);
            Serial.println(c, HEX);
            // M5.Speaker.beep();
        }
    }
    // delay(10);
}
```
</details>

コードは短くて簡単ですね。
CardKBから送られてきたキャラクタコードをLCDに表示してるだけ。

[こちらのサイト](http://asamomiji.jp/contents/modifying-cardkb-firmware)によれば、買ってきたばかりのCardKBにはファームウェアによる制限があって、キーを押す/キーを離す というイベントを拾う事ができないそうです。
キーを押して離した瞬間に、対応するキーコードが送信される、と。
となると、**矢印キー押しっぱなしでカーソル移動とかはできない**という事ですね。

http://asamomiji.jp/contents/modifying-cardkb-firmware

アクションゲームの操作には使えなさそう、という事が分かって多少へこむ。
でもまあ他のことには使えそうですし、改造ファームウェアへの野望は胸に秘めてこのまま進みます。

# ESP32 BLE Keyboard のサンプルを見てみる
続いて、M5StickCをbluetoothキーボードにするサンプルを見ていきます。

こちらもArduino IDEのスケッチ例の中にサンプルがあります。
[スケッチ例]>[ESP32 BLE Keyboard]>[SendKeyStrokes]

<details><summary>サンプルコード</summary>

```
/**
 * This example turns the ESP32 into a Bluetooth LE keyboard that writes the words, presses Enter, presses a media key and then Ctrl+Alt+Delete
 */
#include <BleKeyboard.h>

BleKeyboard bleKeyboard;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");
  bleKeyboard.begin();
}

void loop() {
  if(bleKeyboard.isConnected()) {
    Serial.println("Sending 'Hello world'...");
    bleKeyboard.print("Hello world");

    delay(5000);

    Serial.println("Sending Enter key...");
    bleKeyboard.write(KEY_RETURN);

    delay(1000);

    Serial.println("Sending Play/Pause media key...");
    bleKeyboard.write(KEY_MEDIA_PLAY_PAUSE);

    delay(1000);

    Serial.println("Sending Ctrl+Alt+Delete...");
    bleKeyboard.press(KEY_LEFT_CTRL);
    bleKeyboard.press(KEY_LEFT_ALT);
    bleKeyboard.press(KEY_DELETE);
    delay(100);
    bleKeyboard.releaseAll();
  }

  Serial.println("Waiting 5 seconds...");
  delay(5000);
}
```
</details>

こちらの記事を参考にしました。

https://tomono.tokyo/2021/01/26/9195/

ご存じと思いますがM5StackシリーズはプロセッサにESP32を使っているため、ESP32向けのサンプルがほとんどそのまま使えたりします。

で、これによるとKEY_RETURNとかKEY_DELETEとかあるんですね。という事はKEY_UPとかKEY_ESCとかもあるのでは？
そこら辺のことはBleKeyboard.hにまとめて書いてありました。一部抜粋してみます。


```
const uint8_t KEY_UP_ARROW = 0xDA;
const uint8_t KEY_DOWN_ARROW = 0xD9;
const uint8_t KEY_LEFT_ARROW = 0xD8;
const uint8_t KEY_RIGHT_ARROW = 0xD7;
const uint8_t KEY_BACKSPACE = 0xB2;
const uint8_t KEY_TAB = 0xB3;
const uint8_t KEY_RETURN = 0xB0;
const uint8_t KEY_ESC = 0xB1;
const uint8_t KEY_INSERT = 0xD1;
const uint8_t KEY_PRTSC = 0xCE;
const uint8_t KEY_DELETE = 0xD4;
const uint8_t KEY_PAGE_UP = 0xD3;
const uint8_t KEY_PAGE_DOWN = 0xD6;
const uint8_t KEY_HOME = 0xD2;
const uint8_t KEY_END = 0xD5;
const uint8_t KEY_CAPS_LOCK = 0xC1;
```

やっぱりあった。
bleKeyboardを初期化したら後はbleKeyboard.write()やbleKeyboard.press()でキーコードを送信すればいいみたいですね。そんなに難しくないな。

それじゃ、二つのサンプルを組み合わせていきましょう。

# というわけで

作っておいたものがこちらになります。

```
//CardKBをbluetoothキーボードに
// fn + space で kana

// 参照
// ESP32-BLE-Keyboard
// https://github.com/T-vK/ESP32-BLE-Keyboard
// CardKBBorad
// https://github.com/Tamakichi/CardKeyBoard_PS2


#include <M5StickCPlus.h>
#include <Wire.h>
#include <BleKeyboard.h>

BleKeyboard bleKeyboard("CardKB");

#define CARDKB_ADDR 0x5F


void setup() {
    M5.begin();
    Wire.begin(32, 33);
    M5.Lcd.setRotation(3);
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(1, 10);
    M5.Lcd.setTextColor(YELLOW);
    M5.Lcd.setTextSize(2);
    M5.Lcd.println("Starting BLE CardKB");
    M5.Lcd.printf(">>");
    bleKeyboard.begin();
}
void loop() {
    Wire.requestFrom(CARDKB_ADDR, 1);
    while (Wire.available()) {
        char c = Wire.read();  // receive a byte as characterif
        if (c != 0) {
          switch(c){
            case 13: // Enter
              M5.Lcd.print("/n");
              Serial.println("KEY_RETURN");
              bleKeyboard.write(KEY_RETURN);
              break;
            case 27: // ESC
              Serial.println("KEY_ESC");
              bleKeyboard.write(KEY_ESC);
              break;
            case 139: // fn + BS 戻る(for android)
              Serial.println("Modoru");
              bleKeyboard.press(KEY_RIGHT_GUI);
              bleKeyboard.write(KEY_BACKSPACE);
              bleKeyboard.release(KEY_RIGHT_GUI);
              break;
            case 175: // fn + space 全角半角切り替え
              Serial.println("ZenHan");
              bleKeyboard.press(KEY_LEFT_SHIFT);
              bleKeyboard.write(' ');
              bleKeyboard.release(KEY_LEFT_SHIFT);
              break;
            case 180: // left
              Serial.println("KEY_LEFT_ARROW");
              bleKeyboard.write(KEY_LEFT_ARROW);            
              break;
            case 181: // up
              Serial.println("KEY_UP_ARROW");
              bleKeyboard.write(KEY_UP_ARROW);            
              break;
            case 182: // down
              Serial.println("KEY_DOWN_ARROW");
              bleKeyboard.write(KEY_DOWN_ARROW);            
             break;
            case 183: // right
              Serial.println("KEY_RIGHT_ARROW");
              bleKeyboard.write(KEY_RIGHT_ARROW);            
              break;
            default:
              M5.Lcd.printf("%c", c);
              Serial.println(c, HEX);
              bleKeyboard.write(c);
              break;
            
          }
            // M5.Speaker.beep();
        }
    }
    // delay(10);
}
```

## 改良点
M5StickCの画面が小さすぎたのでPLUSに変更しました。

**特殊キー対応**
[ESC][BackSpace][Enter]および矢印キーのための処理を追加。

**android対応**
androidではshift+spaceで「全角半角の切り替え」ができるため、その処理を追加。キーは Fn + Space。
おなじくandroid用の処理として「戻る」機能を追加。キーは Fn + BS。
(iPadに繋ぎたい方はこの辺を変更する必要があると思います)

こちらの記事が参考になりました。

https://gomazm.xyz/post-782/

お好みでPageUP/PageDownの処理を追加したりとかするといいんじゃないでしょうか。


# 最後に
前述のファームウェア由来の制限をどうするかなのですが、とりあえずこのまま使って不都合があれば対応を考えることにしたいと思います。



あとこれは蛇足なのですが、CardKBはボタンが小さくて指が痛くなるので、何らかのケースかカバーに入れたほうが使いやすいと思います。
3Dプリンターでケースを作っている方もおられるみたいですね。


**基盤むき出しの方が武骨でカッコいいのですが**、私のおススメは100円ショップで売っているクリアケースです。
紙を何枚か挟んでおく透明なアレです。
私はこれを適当な大きさに切って端を接着してケースに使っています。

https://twitter.com/sullblue/status/1611627443823206403?s=20&t=0HFWGICN7xBh8HeBPOAoRg

これだけでずいぶん指の負担が減って押しやすくなります。
CardKBのボタンに苦労している方は試してみてはいかがでしょうか。