ちっ○いはステータスだ!!
はじめに
- ラズパイのヘッドレス化作業をしているとき、有線キーボードとマウスがないと不便だと思ってたところ、M5Stackがこんなキーボードを作っていたのを思い出して買ってみた。
- なんとか、有線キーボードおよび有線マウスに化けさせたいと思う。
-
USBHIDKeyboard.h
とUSBHIDMouse.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コードであり、キーボードのスキャンコードではないことに注意。
- 大文字のAを打つときは、「Shift + a」の同時押しでなく、「Shift」⇒(LEDが赤く点滅)⇒「a」の順番で打つ。「Sym」(緑点滅)、「Fn」(青点滅)も同じ。
usbキーボード化
-
USB.h
とUSBHIDKeyboard.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設定を行えた。目的は達成された。
おわりに
- 会社から帰って夜に作業を始めてから、翌日の午前中に完成できて一安心。
- それにしても、ちっこいキーボードで作業すると、指が痛くなるな。
- 次は、旧atomをusb-cでラズパイと接続して、PCとはbluetoothシリアル接続させることで、普通のキーボードからラズパイを操作させようかな。
おまけ
- 後日、さらにコンパクトにしようとして、atomS3のかわりにnanoC6を使おうと思ったら、nanoC6に使われるESPC6はUSB-OTG機能(必要に応じてUSBホストにもUSBペリフェラルにもなれる)がないらしく、nanoC6はUSBHIDとしてのキーボードやマウスにはなれなかった。残念。