manvaのエンジニアリング魂

エンジニアリング・ものづくり・DIYをもっと身近にするためのブログ。インスピレーションを刺激します。

マウス内蔵キーボードの作り方(ver.0.4) シリアル通信 右から来たものを左で受け止める

左右分割キーボードで、右手デバイスからの情報を左手デバイスで受取る部分ができた。
シリアル通信の配線はこんな感じ。2台のPro MicroのTXとRXをクロスして繋ぐ。
f:id:manva:20210203184732p:plain
左手デバイスはまだ作ってなくて,Pro MicroにTRRSケーブルのジャックだけをつないだもの。左手用Pro MicroがUSBケーブルでパソコンにつながっていて,右から来たもの(情報)をパソコンに受け流している。電源もパソコンからUSBで供給され,TRRSケーブルで右手デバイスにもおすそ分けしている。

右手デバイスで、キーを押した時と離した時に、左手デバイスに情報を送信する。マウスシフトキーを押している間は、マウス情報も送信する。
キーボード部分に関しては、ほぼ↓の記事と同じ。
オリジナルキーボードを作ってみる その9「シリアル通信の実装」 - ゆかりメモeucalyn.hatenadiary.jp

マウスの情報を送る部分を自分で作らなければいけない。
送りたい情報は、

  1. マウスのイメージセンサから読んだ移動量 MouseDeltaX
  2. マウスのイメージセンサから読んだ移動量 MouseDeltaY
  3. マウスホイールエンコーダのカウント値 WheelCount

の3つで、それぞれ8ビットの情報なのだが、Arduino(Pro Micro)のシリアル通信は1回に8ビットしか送れない。キーボードの情報も送るので、何の情報か、も載せないといけないので、8ビット全部をデータに使うこともできない。以下のような仕様にした。

  • 先頭のビットが0ならキーボード情報、1ならマウス情報(上記記事を書いた方も、マウスを載せることも考えていたようで、これと同じ仕様が書かれている。)
  • キーボード情報の場合、上記記事と同じ。2ビット目は、キーを押したか離したかを示すビット。押した時1,離した時0。3,4,5ビット目が行の番号、最後の6,7,8ビット目が列の番号。
  • マウス情報の場合、上記3つのマウス情報を4回に分けて送る。( 8ビット x 3個 = 6ビット x 4回 = 24ビット )
  • 2ビット目は、4回に分けて送るデータの先頭を表すビット、後ろの6ビットをデータとする。

つまりこんな感じ。
f:id:manva:20210203183305p:plain


通信部分のプログラムは以下のようにした。
TX, RXを使ったシリアル通信は,Arduino IDEでは,Serial1という名前で使えるように用意してくれているので,setup( )の中で,

Serial1.begin( 9600 );

などと書いておくだけで使える。

送る側 (右手デバイス)

void SendMouseMove( byte MouseDeltaX, byte MouseDeltaY, byte WheelCount ){
  byte sendData;

  sendData = 0b11000000 | ( MouseDeltaX >> 2 );
  Serial1.write( sendData );
  sendData = 0b10000000 | ( ( MouseDeltaX & 0b00000011 ) << 4 | MouseDeltaY >> 4 );
  Serial1.write( sendData );
  sendData = 0b10000000 | ( ( MouseDeltaY & 0b00001111 ) << 2 | WheelCount >> 6 );
  Serial1.write( sendData );
  sendData = 0b10000000 | ( WheelCount & 0b00111111 );
  Serial1.write( sendData );
}

受取る側 (左手デバイス)

typedef  struct  {
  int row;
  int col;
} str_keypos;

void loop() {
  str_keypos keypos;
  byte readData;
  bool isMouseShiftKey, isPress;
 ︙
  //左手キーボードの処理
 ︙
  //右手キーボード&マウスの処理
  if( Serial1.available() ){
    readData = Serial1.read();
    if ( readData & 0b10000000 ){ // マウスデータ
      SetMouseMove( readData );
    }else{ // キーボードキー状態データ
      isPress    = (readData & 0b01000000) >> 6;
      keypos.row = (readData & 0b00111000) >> 3;
      keypos.col =  readData & 0b00000111;
      isMouseShiftKey = CheckSpecialKey( &KEYPOS_MOUSE_SHIFT, &keypos );
      SetFlgMouseMode( isPress, isMouseShiftKey );
      if( flgMouseMode ){ // マウスモード
        MousePressRelease( isPress, &keypos );
      }else{ // キーボードモード
        KeyboardPressRelease( isPress, &keypos );
      }
    }
  }
}
//-----------------------------------------------------------------
void SetMouseMove( byte readData ){
  if ( readData & 0b01000000 ){ // 先頭データ
    readMouseDataCount = 0;
  }else{
    readMouseDataCount++;
  }
  readMouseData[readMouseDataCount] = readData & 0b00111111; // 上位2ビット消去
  if( readMouseDataCount==3 ){ // データが揃ったら
    MouseDeltaX = (readMouseData[0]<<2) | (readMouseData[1]>>4);
    MouseDeltaY = (readMouseData[1]<<4) | (readMouseData[2]>>2);
    WheelCount  = (signed char)(readMouseData[2]<<6) |  readMouseData[3];
    Mouse.move( MouseDeltaX, MouseDeltaY, WheelCount ); // カーソルとホイール
  }
}

マウスシフトキーを押している間だけマウスの機能が動作するようにしてみたのだが、使ってみると、マウスポインタは常時動いてくれた方が使いやすそう。マウスシフトキーはキーを左右クリックに切り替えるためだけに使うように修正しよう。