◆micro:bitの弱点?文字入力
micro:bitはセンサー、ディスプレイ、GPIO、無線が完備していて非常にコストパフォーマンスが高いデバイスなのですが、折角文字列の表示機能があるのに、文字入力ができないという弱点?を抱えています。
文字入力ができると、コマンドで動作を変えたり、無線で文字通信ができたり、バーコードが使えたり、応用が拡がりそうな気がします。
既存の製品ではM5Stack用のI2Cのキーボードが使えるのですが、金属製のタクトスイッチを使った簡易的なものなので、文字列を打ち込むのは少々つらいところです。
普通に売られているUSBキーボードが接続できると便利なのですが、ブレッドボードで使えるスイッチサイエンス委託の製品「かんたんUSBホスト」があるくらいで、簡単にmicro:bitに接続できるものがありません。ないものは作ってみることにしました。
◆USB HIDーシリアル変換チップCH9350L
USBキーボード、マウス等はUSB HIDクラスのプロトコルでデータを転送します。これを汎用プロセッサだけで実現するにはUSBホスト、HIDプロトコルの実装などかなりのレイヤーの開発が必要になります。
上記の「かんたんUSBホスト」はWCHのCH559というUSBホスト内蔵のチップを使っているのですが、8051という馴染みのないコアでファームウェアを実装するので敷居が高そうです。
格安USBホストマイコン CH559をいじってみた(大盛)
調べたところ、同じWCHからCH9350Lという、HIDプロトコルのデータを非同期シリアルに変換するという、おあつらえ向きのチップが出ているのがわかりました。
USBマウス・キーボード用シリアル通信制御IC CH9350L
本来の用途は2個をペアにして一方をUSBデバイス、一方をホストに接続し、2つの間をシリアル通信で結んでキーボード・マウス等を延長するためのもののようです。そのためシリアルは独自のプロトコルになっていて、USB HID Usage IDで転送されます。
この文字情報をmicro:bitのシリアル入力用にASCIIコードに変換するため、二度手間になりますが変換用に別のチップを挟むことにしました。
大した処理ではないので、当初は手軽なATtiny85で試したのですが、ATtinyはハードウェアシリアルがなく、CH9350Lからの入力、micro:bitへの出力を同時に処理するのが困難で、少々オーバースペックですがATmega328Pを使うことにしました。
◆実装と基板化
回路はCH9350LとATmega328Pをシリアルで繋ぐだけですが、これらが5V系でmicro:bitが3.3V系のため、micro:bitへの電源供給用にレギュレーターを入れています。ATmega328からmicro:bitへ出力するシリアルのラインは抵抗分圧で電圧を合わせてあります。
まずバラック組でテスト。対象がmicro:bitだけだとつまらないので、汎用のUART出力とGroveコネクタもつけました。
基板化でパーツを表面実装にしたので、オンボードでATmega328P-AUにArduinoブートローダを書き込むためにICSPのピンヘッダをつけてあります。
◆ファームウェア
CH9350Lのデータシートにあるプロトコルに従って、余計なヘッダ情報を無視して、文字情報部分をUsageIDに対応するASCIIコードに変換するだけです。変換は配列usage_idに定義しています。
現状はアルファベット大文字と数字、スペースのみ対応しています。
#include <SoftwareSerial.h>
#include <Wire.h>
SoftwareSerial mySerial(9, 8); // RX, TX
#define KB_ADDR 0x5F
byte usage_id[45] = {0,0,0,0,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,49,50,51,52,53,54,55,56,57,48,13,37,8,9,32};
byte rcv_byte;
byte rcv_buff[8];
byte rcv_cnt;
byte i2c_send;
boolean key_pressed;
void setup() {
Serial.begin(115200); // ハードウェアシリアルを準備
Serial.println("Ready");
mySerial.begin(115200); // ソフトウェアシリアルの初期化
mySerial.listen();
Wire.begin(KB_ADDR);
Wire.onRequest(i2c_write);
key_pressed = false;
}
void i2c_write(){
if (key_pressed){
Wire.write(i2c_send);
key_pressed = false;
}
}
byte get_byte(){
if (mySerial.available()){
return mySerial.read();
}
}
void loop() {
rcv_byte = get_byte();
if (rcv_byte == 0x57){
rcv_byte = get_byte();
if (rcv_byte == 0xAB){
for(rcv_cnt =0; rcv_cnt <=8; rcv_cnt++){
rcv_buff[rcv_cnt] = get_byte();
}
if((rcv_buff[0] == 0x88) && (rcv_buff[5] != 0x00)){
if (usage_id[rcv_buff[5]] != 13){
Serial.write(usage_id[rcv_buff[5]]);
key_pressed = true;
i2c_send = usage_id[rcv_buff[5]];
} else {
//改行をLF+CRにする処理
//Serial.write(10);
key_pressed = true;
i2c_send = 10;
Serial.write(13);
key_pressed = true;
i2c_send = 13;
}
}
}
}
}
◆応用例
・キーボード入力した文字列を無線でもう一台のmicro:bitに送信し、受信したmicro:bitでプリンタに印刷するテレタイプ
・バーコードスキャナでバーコードを読み取って印刷します
【メインのサイトはこちらです】 ネコマ製作所
以上