LoginSignup
2
0

ちっこいキーボードを愛でた話

Last updated at Posted at 2024-05-23

ちっ○いはステータスだ!!

これは、ちっこいキーボードです
スクリーンショット 2024-05-22 20.58.57.jpg

はじめに

  • ラズパイのヘッドレス化作業をしているとき、有線キーボードとマウスがないと不便だと思ってたところ、M5Stackがこんなキーボードを作っていたのを思い出して買ってみた。
  • なんとか、有線キーボードおよび有線マウスに化けさせたいと思う。
  • USBHIDKeyboard.hUSBHIDMouse.hというライブラリを使えば余裕らしいから、1日で出来るかな?

サンプルスケッチ(M5AtomS3用に改変)

  • サンプルスケッチはこんな感じ。動かすと、AtomS3の画面とシリアルモニタに打ち込んだ文字が表示された。
//M5AtomS3用に、若干改変
#include <M5Unified.h> // 変更:M5Stack.h ⇒ M5Unified.h
#include <Wire.h> // 追加
#define CARDKB_ADDR 0x5F

void setup() {
    M5.begin();             
    // M5.Power.begin();    // 削除
    Wire.begin(); //追加
    M5.Lcd.setTextSize(2);
    M5.Lcd.printf("CardKBtest\n"); //スペースを削って10文字にした
    M5.Lcd.printf(">>");
}
void loop() {
    Wire.requestFrom(CARDKB_ADDR,1);
    while (Wire.available())
    {
        char c = Wire.read();
        if (c != 0) {
            M5.Lcd.printf("%c", c);
            Serial.printf("%c <-HEX:", c); // 追加            
            Serial.println(c, HEX);
        }
    }
}
  • キーボードのキーとvalue(上記コードのc)の対応表は以下の通り。なお、「a」のvalueは0x61となっているが、これは文字のasciiコードであり、キーボードのスキャンコードではないことに注意。
    image.png
  • 大文字のAを打つときは、「Shift + a」の同時押しでなく、「Shift」⇒(LEDが赤く点滅)⇒「a」の順番で打つ。「Sym」(緑点滅)、「Fn」(青点滅)も同じ。

usbキーボード化

  • USB.hUSBHIDKeyboard.hを導入して、USBHIDKeyboard Keyboard;のKeyboardを出力先にするだけでよさそう。
  • つまり、サンプルスケッチのM5.Lcd.printf("%c", c);の「M5.Lcd」を「Keyboard」に入れ替えればOKだ!余裕〜〜
//M5AtomS3の場合
#include <M5Unified.h>       // 変更:M5Stack.h ⇒ M5Unified.h
#include <Wire.h>            // 追加
#include <USB.h>             // さらに追加
#include <USBHIDKeyboard.h>  // さらに追加
USBHIDKeyboard Keyboard;     // さらに追加

#define CARDKB_ADDR 0x5F

void setup() {
  M5.begin();
  // M5.Power.begin();    // 削除
  Wire.begin();      //追加
  USB.begin();       //さらに追加
  Keyboard.begin();  //さらに追加
  M5.Lcd.setTextSize(2);
  M5.Lcd.printf("CardKBtest\n");  //スペースを削って10文字にした
  M5.Lcd.printf(">>");
}
void loop() {
  Wire.requestFrom(CARDKB_ADDR, 1);
  while (Wire.available()) {
    char c = Wire.read();
    if (c != 0) {
      M5.Lcd.printf("%c", c);
      M5.Lcd.printf("(%x) ", c);  // 変更
      // Serial.println(c, HEX); // 削除
      Keyboard.print(c);  // さらに追加
    }
  }
}
  • よし、macに接続されて、キーボードとして認識された!
  • 「abc」と入力すると反応する!バックスペースで文字が消える!成功だ!
  • と思ったら、enterキーを押しても改行しない...左矢印キーを押しても、カーソルが右に行く...ボスケテ...
  • 調べてみると、対応表のEnterの0x0dはascii制御文字「CR(\r)」に相当するけど、macの改行は「LF(\n)」(0x0a)であるため、それが悪さをしてるんだろうか。でも、左矢印なんて、そもそもascii制御文字にそのようなものはないし、ascii制御文字は関係ないのかな?
  • 基本に立ち返って、USBHIDKeyboard.hのコードを見てみると、Returnの割り当ては0xB0でascii制御文字とは全然関係ないね。それに、上下左右キーの割り当ても設定されている。これを使えばいいのかな?
#define KEY_UP_ARROW     0xDA
#define KEY_DOWN_ARROW   0xD9
#define KEY_LEFT_ARROW   0xD8
#define KEY_RIGHT_ARROW  0xD7
#define KEY_MENU         0xFE
#define KEY_SPACE        0x20
#define KEY_BACKSPACE    0xB2
#define KEY_TAB          0xB3
#define KEY_RETURN       0xB0
#define KEY_ESC          0xB1
  • よって、試しに、
  •   Keyboard.print(c);
    

 を

  •     if (c == 0x0d) {
          Keyboard.print(KEY_RETURN);
        } else {
          Keyboard.print(c);
        }
    
    

  に書き換えて、リターンを押したところ、「176」(=0xb0)が表示された。

  • 改めてUSBHIDKeyboard.hをみると、そもそも標準のメソッド名はwriteなので、printをwriteに書き換えたところ、うまく改行できた。これを踏まえると、abcのような単純な文字以外は、USBHIDKeyboard.hの割り当て表に従って、変換すべきと分かる。
  • よって、制御系のコードを変換することとした。
      char c_key;
      switch (c) {
        case 0x0d: c_key = KEY_RETURN; break;
        case 0xb5: c_key = KEY_UP_ARROW; break;
        case 0xb6: c_key = KEY_DOWN_ARROW; break;
        case 0xb4: c_key = KEY_LEFT_ARROW; break;
        case 0xb7: c_key = KEY_RIGHT_ARROW; break;
        case 0x09: c_key = KEY_TAB; break;
        case 0x1b: c_key = KEY_ESC; break;
        default: c_key = c;
      }
      Keyboard.write(c_key);
  • 上記により、上下左右の動きと、リターン、tab、escのキーを割り当てることが出来た。
  • とりあえず、緊急避難的な使用目的なら、これらのキーで十分でしょ。

usbマウス化

  • 次は、usbマウス化を探る。
  • ライブラリUSBHIDMouse.hや、これを使ったサンプルスケッチを見てみると、ボタンは、press,release,clickの動作を使い分けることが出来るみたい。緊急避難的な使用としては、clickだけで十分かな。デフォは左ボタンだけど、右ボタンと中央ボタンの指定も出来る。
  • 動作としては、4つの引数を持つmoveメソッドがあり、(x軸移動、y軸移動、ホイール、パン)に割り当てられている。
  • こっちのサンプルスケッチをみると、とりあえずの移動は20刻みで様子をみたほうが良さそう。
  • で、作ってみたコードがこれ。
#include <M5Unified.h>
#include <Wire.h>
#include <USB.h>
#include <USBHIDMouse.h>
USBHIDMouse Mouse;

#define CARDKB_ADDR 0x5F

void setup() {
  M5.begin();
  Wire.begin();
  Mouse.begin();
  USB.begin();
  M5.Lcd.setTextSize(2);
  M5.Lcd.print("USB_mouse");
}

int8_t x = 0;
int8_t y = 0;
int8_t dx = 1;
int8_t dy = 1;

void loop() {
  Wire.requestFrom(CARDKB_ADDR, 1);
  while (Wire.available()) {
    char c = Wire.read();
    if (c != 0) {

      //クリック動作
      switch (c) {
        case 0x2c:  //, 左クリック
          Mouse.click();
          x = y = 0;
          break;
        case 0x2e:  //. 右クリック
          Mouse.click(MOUSE_RIGHT);
          x = y = 0;
          break;
        case 0x0d:  //returnキーでダブルクリック
          Mouse.click();
          delay(50);
          Mouse.click();
          x = y = 0;
          break;
      }

      //距離設定
      if (c > 0x30 && c < 0x40)  //数字キー 0-9の場合
      {
        dx = dy = 2 * (c - 0x30);
        x = y = 0;
      }

      //移動動作
      switch (c) {
        //可変距離
        case 0xb5: y = max(min(y - 20,-20), -120); break;  //up(画面座標は左上が原点だから、yを減少させる方向)
        case 0xb6: y = min(max(y + 20,+20), +120); break;  //down
        case 0xb4: x = max(min(x - 20,-20), -120); break;  //left
        case 0xb7: x = min(max(x + 20,+20), +120); break;  //right
        case 0x20: x = y = 0                     ; break;  //spaceキーでリセット

        //固定距離(sボタンを中心の上下左右) -- 微調整用に距離短め
        case 0x65: x =   0 ; y = -dy ; break;  //e 上
        case 0x78: x =   0 ; y = +dy ; break;  //x 下
        case 0x61: x = -dx ; y =   0 ; break;  //a 左
        case 0x64: x = +dx ; y =   0 ; break;  //d 右
        case 0x77: x = -dx ; y = -dy ; break;  //w 左上
        case 0x7a: x = -dx ; y = +dy ; break;  //z 左下
        case 0x72: x = +dx ; y = -dy ; break;  //r 右上
        case 0x63: x = +dx ; y = +dy ; break;  //c 右下
      }
      Mouse.move(x, y);

      M5.Lcd.clearDisplay();
      M5.Lcd.setCursor(0, 0);
      M5.Lcd.printf("(x,y)=\n (%d,%d)\n\n", x, y);
      M5.Lcd.printf("(dx,dy)=\n (%d,%d)", dx, dy);
    }
  }
}
  • 使い勝手は改善の余地がありそうだけど、取りあえず動いた。
  • 上下左右のキーを押すと、一度の押下で移動する距離が20→40→...→100→120と増えていく。
  • sの上下左右に有るキー(w,e,r,a,d,z,x,c)を押すと、常に固定幅の距離で動く。移動距離は数字キー(1-9)で設定可能(初期値:1)。9に設定すると移動距離は18(数字キーの倍)。

usbキーボードマウス

  • 二つを合体させるけど、当然同時に使用は出来ないから、モード切替ボタン操作を決める。「fn + return」(=0xa3)でいいかな。
  • で、合体させたのがこれ。
#include <M5Unified.h>
#include <Wire.h>
#include <USB.h>
#include <USBHIDMouse.h>
#include <USBHIDKeyboard.h>
USBHIDKeyboard Keyboard;
USBHIDMouse Mouse;

#define CARDKB_ADDR 0x5F


enum mskbmode { MS,
                KB };  //マウス[MS]とキーボード[KB]の2モード設定
mskbmode mode = KB;    //初期値はキーボードモード(緊急避難的にはキーボードの方が使用頻度高そう)

void setup() {
  M5.begin();
  Wire.begin();
  Mouse.begin();
  Keyboard.begin();
  USB.begin();
  M5.Lcd.setTextSize(2);
  M5.Lcd.print("USBmouseKB");
}

int8_t x = 0;
int8_t y = 0;
int8_t dx = 1;
int8_t dy = 1;

void loop() {
  Wire.requestFrom(CARDKB_ADDR, 1);
  while (Wire.available()) {
    char c = Wire.read();

    if (c == 0xa3) {  //モード切替
      switch (mode) {
        case MS:
          mode = KB;
          c = 0;
          M5.Lcd.clearDisplay();
          M5.Lcd.setCursor(0, 0);
          M5.Lcd.println("KEYBOARD");
          break;
        case KB:
          mode = MS;
          c = 0;
          M5.Lcd.clearDisplay();
          M5.Lcd.setCursor(0, 0);
          M5.Lcd.println("MOUSE");
          break;
      }
    }

    // キーボードモード
    if (c != 0 && mode == KB) {

      char c_key;
      switch (c) {
        case 0x0d: c_key = KEY_RETURN; break;
        case 0xb5: c_key = KEY_UP_ARROW; break;
        case 0xb6: c_key = KEY_DOWN_ARROW; break;
        case 0xb4: c_key = KEY_LEFT_ARROW; break;
        case 0xb7: c_key = KEY_RIGHT_ARROW; break;
        case 0x09: c_key = KEY_TAB; break;
        case 0x1b: c_key = KEY_ESC; break;
        default: c_key = c;
      }
      Keyboard.write(c_key);

      M5.Lcd.clearDisplay();
      M5.Lcd.setCursor(0, 0);
      M5.Lcd.println("KEYBOARD");
      M5.Lcd.printf("%c", c);
      M5.Lcd.printf("(%x) ", c);
    }

    // マウスモード
    if (c != 0 && mode == MS) {

      //クリック動作
      switch (c) {
        case 0x2c:  //, 左クリック
          Mouse.click();
          x = y = 0;
          break;
        case 0x2e:  //. 右クリック
          Mouse.click(MOUSE_RIGHT);
          x = y = 0;
          break;
        case 0x0d:  //returnキーでダブルクリック
          Mouse.click();
          delay(50);
          Mouse.click();
          x = y = 0;
          break;

        //追加
        case 0x6b:  //k 左ボタンpress
          Mouse.press();
          x = y = 0;
          break;
        case 0x6c:  //l 左ボタンrelease
          Mouse.release();
          x = y = 0;
          break;
      }

      //距離設定
      if (c > 0x30 && c < 0x40)  //数字キー 0-9の場合
      {
        dx = dy = 2 * (c - 0x30);
        x = y = 0;
      }

      //移動動作
      switch (c) {
        //可変距離
        case 0xb5: y = max(min(y - 20,-20), -120); break;  //up(画面座標は左上が原点だから、yを減少させる方向)
        case 0xb6: y = min(max(y + 20,+20), +120); break;  //down
        case 0xb4: x = max(min(x - 20,-20), -120); break;  //left
        case 0xb7: x = min(max(x + 20,+20), +120); break;  //right
        case 0x20: x = y = 0                     ; break;  //spaceキーでリセット

        //固定距離(sボタンを中心の上下左右) -- 微調整用に距離短め
        case 0x65: x =   0 ; y = -dy ; break;  //e 上
        case 0x78: x =   0 ; y = +dy ; break;  //x 下
        case 0x61: x = -dx ; y =   0 ; break;  //a 左
        case 0x64: x = +dx ; y =   0 ; break;  //d 右
        case 0x77: x = -dx ; y = -dy ; break;  //w 左上
        case 0x7a: x = -dx ; y = +dy ; break;  //z 左下
        case 0x72: x = +dx ; y = -dy ; break;  //r 右上
        case 0x63: x = +dx ; y = +dy ; break;  //c 右下
      }
      Mouse.move(x, y);

      M5.Lcd.clearDisplay();
      M5.Lcd.setCursor(0, 0);
      M5.Lcd.println("MOUSE");
      M5.Lcd.printf("(x,y)=\n (%d,%d)\n", x, y);
      M5.Lcd.printf("(dx,dy)=\n (%d,%d)\n", dx, dy);
      M5.Lcd.print(Mouse.isPressed() ? "pressed" : "released");
    }
  }
}
  • (後日、追記)実際に使ってみて、press()release()が有った方が便利だったので、合体版で追加した。写真のsbcはradxa_zero3wで、ラズパイzeroと違ってusb-cが使えるのが良い。メモリ2g+eMMc16gbの40pin付きで8千円。とりあえず、出先でusbキーボードマウスを使ってsbcのbluetooth設定を行えた。目的は達成された。
    スクリーンショット 2024-05-25 10.13.18.jpg

おわりに

  • 会社から帰って夜に作業を始めてから、翌日の午前中に完成できて一安心。
  • それにしても、ちっこいキーボードで作業すると、指が痛くなるな。
  • 次は、旧atomをusb-cでラズパイと接続して、PCとはbluetoothシリアル接続させることで、普通のキーボードからラズパイを操作させようかな。

おまけ

  • 後日、さらにコンパクトにしようとして、atomS3のかわりにnanoC6を使おうと思ったら、nanoC6に使われるESPC6はUSB-OTG機能(必要に応じてUSBホストにもUSBペリフェラルにもなれる)がないらしく、nanoC6はUSBHIDとしてのキーボードやマウスにはなれなかった。残念。
2
0
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
2
0