manvaのエンジニアリング魂

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

マウス内蔵キーボードver.0.4 爆誕

できた。

使ってみた。

ダメだ。

もちろん、前に作ったものよりは格段に良くなったのだが,残念ながらこれを普段使いにしたいとは思えない。慣れれば使えないことはない,というレベル。

一番の問題は、キーをすり鉢状に傾けすぎたせいで,押したときにデバイスが逃げるように動いてしまう点。
f:id:manva:20210425232957p:plain
前に作ったときそれが嫌だったので,手のひらで押さえながら指だけで打てるようにする,というコンセプトで作ったが,やはり手のひらでずっと押さえているというのは使っていて気持ち良くない。最初のキー配置決める時は粘土だったから、ずっしりしてて問題なかったんだよな。おもりつけたらまだいいかも。


前回の記事の後でやったことを書いておく。

もう少しで完成,と思っていたが,最後にまあまあの難関が残っていた。

Arduino IDEが日本語キーボードに対応していない問題

Arduino IDEの標準機能では,日本語キーボードには対応していないのである。すでに解決してくれている人がいるので助かるが,まだいろいろうまく行かず,キーボードの仕様がよくわからなくなって,結局,まあまあちゃんと勉強してしまったので、別記事にまとめておいた。
manva.hatenablog.com

全角/半角キーで日本語に切替えられず,「`」になる問題で苦労したが,これはキーボード側のソフトの問題ではなく,Windows側で英語キーボードとして認識されているせいだった。最近,X-Bows↓という英語キーボードを使っていたので,英語キーボードの設定になっていた。
x-bows.com

Windowsでは,複数のキーボードを接続した場合,日本語と英語の切換えは基本的にキーボードごとの設定ではなく,全てのキーボードが同じ設定になるようだ。レジストリをいじったら日本語キーボードとして使えるようになったのだが,今度はX-Bowsの方まで日本語キーボードとして扱われてしまい、いくつかのキーが刻印と一致しなくなった。この問題はまだ解決できていない。

 
その他は細々した問題。

チャタリングの問題

キーを押したとき,何回かに1回くらい,チャタリングで同じ文字が2個入力される問題があった。

まずは,QMKではどうしているか勉強↓。
Debounce API - QMK

タイマーで実時間で設定できた方が良いのだろうが,自分の作る処理の演算時間がそんなに大幅に変わる予定もないので,普通にループでカウントダウンするだけにしよう。

 QMKでは,Eager(押したらすぐ反応し,しばらくは無視)か,Defer(しばらく押されていたら反応)かを選べるようだが,Eagerの実装にしよう。

 

キーを押したままレイヤー切換えると離しても押しっぱなしになる問題

これは単純にレイヤー切替えたときReleaseAll()することで解決。

void SetLayerTo( int layernum ){
  if( layer!=layernum ){
    layer = layernum;
    Keyboard.releaseAll(); //押したままレイヤー切換えたら押しっぱなしになるので全部離す
  }  
}

 

レイヤーをどうするか

QMK firmwareのLAYOUTマクロをパクってキー配置をビジュアル的に見やすくした。キーの定義もQMKっぽく名前をつけた。

#define LAYOUT( \
  L31, L41, L32, L42, L33, L43,  L24, L34,             R45, R55,  R36, R46, R37, R47, R38, R48, \
  L21, L51, L22, L52, L23, L53,  L14,                       R65,  R26, R56, R27, R57, R28, R58, \
  L11, L61, L12, L62, L13, L63,  L64, L54, L44,   R35, R25, R15,  R16, R66, R17, R67, R18, R68 \
  ) \
  { \
    { L11, L12, L13, L14, R15, R16, R17, R18 }, \
    { L21, L22, L23, L24, R25, R26, R27, R28 }, \
    { L31, L32, L33, L34, R35, R36, R37, R38 }, \
    { L41, L42, L43, L44, R45, R46, R47, R48 }, \
    { L51, L52, L53, L54, R55, R56, R57, R58 }, \
    { L61, L62, L63, L64, R65, R66, R67, R68 } \
  }

レイヤーは、数字シフトキーというのを用意し、押している間は数字レイヤ。数字レイヤでは,ホームポジションの行を数字、その上の行をシフトキー+数字で入力される記号とした。下の行は,キーを減らしたせいではみ出た記号のキーを並べた。もう1つ,ファンクションシフトキーを押している間はファンクションレイヤ。ファンクションキーとかカーソルキーとかを集めた。
f:id:manva:20210425232319p:plain
シフトキーと親指のキーはレイヤーによらず共通。エンターキーは,小指でターン!と叩くのが好きそうな人をよく見かけるので,小指のキーを減らしてもその快感を奪わないよう,親指の端にして手首逆回転でターンとできるようにした。

const byte keymaps[NUM_LAYER][NUM_ROW][NUM_COL_ALL]  = {
LAYOUT( \
  KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,     _______, KC_LGUI,                  KC_RALT, KC_RCTL,  KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    KC_AT,   \
  KC_CAPS, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,     _______,                                    _______,  KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_COLN, \
  KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,     KC_BSPC, KC_SPC, KC_DEL,   KC_ENT, KC_SPC,  KC_ZKHK,  KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT ),
LAYOUT( \
  KC_ESC,  KC_EXLM, KC_DQOT, KC_HASH, KC_DLR,  KC_PERC,  _______, KC_LGUI,                  KC_RALT, KC_RCTL,  KC_AMPR, KC_QUOT, KC_LPRN, KC_RPRN, KC_P,    KC_EQL,  \
  KC_CAPS, KC_1,    KC_2,    KC_3,    KC_4,    KC_5,     _______,                                    _______,  KC_6,    KC_7,    KC_8,    KC_9,    KC_0,    KC_MINS, \
  KC_LSFT, KC_BSLS, KC_CIRC, KC_YEN,  KC_TILD, KC_PIPE,  KC_BSPC, KC_SPC, KC_DEL,   KC_ENT, KC_SPC,  KC_ZKHK,  KC_LBRC, KC_RBRC, KC_LLBR, KC_RLBR, KC_UNDS, KC_RSFT ),
LAYOUT( \
  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,    _______, KC_LGUI,                  KC_RALT, KC_RCTL,  KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  \
  KC_CAPS, _______, KC_LEFT, KC_UP,   KC_RGHT, _______,  _______,                                    _______,  _______, KC_HOME, KC_PGUP, KC_END,  KC_PSCR, _______, \
  KC_LSFT, _______, _______, KC_DOWN, _______, _______,  KC_BSPC, KC_SPC, KC_DEL,   KC_ENT, KC_SPC,  KC_ZKHK,  _______, _______, KC_PGDN, _______, _______, KC_RSFT )
};

 

通信速度

左右デバイス間の通信速度(ボーレート)を上げた。ArduinoのMax 115200bpsに上げたら動かなかったので,19200bpsにしておいた。元々の設定9600bpsから2倍にしているが,体感では違いがわからない。

 

マウスを動かしたときのザラザラ感

右手用の裏にマウスソール↓(滑りを良くするやつ)を貼った。

f:id:manva:20210425164816j:plain

 

とにかく、これで完成。やりたかったことは大体やったので,これ以上やって完璧を目指してもあまり大幅な改善は期待できない。
結論:マウスはキーボードに内蔵しない方が良い
しばらく別のことしよう。


参考までにソースコードを載せておく。
右手用
KeybouseR.ino

// 右手用
#include <SPI.h>
#include <Mouse.h>
#include <Keyboard.h>
#include "ADNS3050.h"
#define PIN_NUM_PHASE_A 2 //pin2->int1 phaseA
#define PIN_NUM_PHASE_B 3 //pin3->int0 phaseB
#define INT_NUM_PHASE_A 1
#define INT_NUM_PHASE_B 0
#define NUM_ROW 6
#define NUM_COL 4
typedef  struct  {
  int row;
  int col;
} str_keypos;
const int PIN_NUM_ROW[NUM_ROW] = { 4, 5, 6, 7, 8, 9 };
const int PIN_NUM_COL[NUM_COL] = { A3, A2, A1, A0 };
const str_keypos KEYPOS_MOUSE_SHIFT = {5,4};
bool PreviousKeyState[NUM_ROW][NUM_COL];
byte MouseDeltaX = 0;
byte MouseDeltaY = 0;
byte WheelCount = 0;
volatile bool PreviousA = 0;
volatile bool PreviousB = 0;
bool flgMouseMode;

//-----------------------------------------------------------------
void setup(){
  int row, col;
  
  startupImageSensor();
  Mouse.begin();
  Keyboard.begin();
  Serial1.begin(19200); //左右通信用
  pinMode( PIN_NUM_PHASE_A, INPUT_PULLUP );
  pinMode( PIN_NUM_PHASE_B, INPUT_PULLUP );
  attachInterrupt( INT_NUM_PHASE_A, EncoderA_Change, CHANGE );
  attachInterrupt( INT_NUM_PHASE_B, EncoderB_Change, CHANGE );

  for( row = 0; row < NUM_ROW; row++ ){
    pinMode( PIN_NUM_ROW[row], OUTPUT );
  }
  for( row = 0; row < NUM_COL; row++ ){
    pinMode( PIN_NUM_COL[row], INPUT_PULLUP );
  }
  for( row = 0; row < NUM_ROW; row++){
    for( col = 0; col < NUM_COL; col++){
      PreviousKeyState[row][col] = HIGH;
    }
    digitalWrite( PIN_NUM_ROW[row], HIGH );
  }
}
//-----------------------------------------------------------------
void EncoderA_Change(){
  int CurrentA, CurrentB;
  CurrentA = digitalRead( PIN_NUM_PHASE_A );
  CurrentB = digitalRead( PIN_NUM_PHASE_B );
  //※本来,値が変化したときにだけ割込が入るはずなので,Previousを残しておく必要はないし,else ifの条件も要らないはずなのだが,
  //なぜか割込が呼ばれているのにAが変わっていない時がある。チャタリングしていて,割込が入ってから値を読むまでの間に戻った?
  if( ((PreviousA==0)&&(CurrentA==1)&&(CurrentB==0))||((PreviousA==1)&&(CurrentA==0)&&(CurrentB==1)) ){
    WheelCount++;
  }else if( ((PreviousA==0)&&(CurrentA==1)&&(CurrentB==1))||((PreviousA==1)&&(CurrentA==0)&&(CurrentB==0)) ){
    WheelCount--;
  }
  PreviousA = CurrentA;
}
//-----------------------------------------------------------------
void EncoderB_Change(){
  int CurrentA, CurrentB;
  CurrentA = digitalRead( PIN_NUM_PHASE_A );
  CurrentB = digitalRead( PIN_NUM_PHASE_B );
  if( ((PreviousB==0)&&(CurrentB==1)&&(CurrentA==1))||((PreviousB==1)&&(CurrentB==0)&&(CurrentA==0)) ){
    WheelCount++;
  }else if( ((PreviousB==0)&&(CurrentB==1)&&(CurrentA==0))||((PreviousB==1)&&(CurrentB==0)&&(CurrentA==1)) ){
    WheelCount--;
  }
  PreviousB = CurrentB;
}
//-----------------------------------------------------------------
void SendMouseMove( byte MouseDeltaX, byte MouseDeltaY, byte WheelCount ){
  byte sendData;
  // データフォーマット
  // 先頭bit: マウスデータフラグ,2番目bit: 同期フラグ
  // 後ろ 6bit x 4 で 8bitデータ3つを送信
  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 );
}
//-----------------------------------------------------------------
bool CheckSpecialKey( str_keypos* keypos1, str_keypos* keypos2 ){
  if( ( keypos1->row==keypos2->row ) &&( keypos1->col==keypos2->col ) ){
    return true;
  }else{
    return false;
  }
}
//-----------------------------------------------------------------
void SendKeyPress( str_keypos* keypos ){
  Serial1.write( 1<<6 | keypos->row<<3 | keypos->col );
}
//-----------------------------------------------------------------
void SendKeyRelease( str_keypos* keypos ){
  Serial1.write( keypos->row<<3 | keypos->col );
}
//-----------------------------------------------------------------
void ClearImageSensorDeltaXY( void ){
    MouseDeltaX = 0;
    MouseDeltaY = 0;
    GetImageSensorDeltaX(); // 読取ったら消える
    GetImageSensorDeltaY();
}
//-----------------------------------------------------------------
void loop(){
  int CurrentKeyState, row, col;
  str_keypos keypos;
  bool isMouseShiftKey;
   
  for( row=0; row<NUM_ROW; row++ ){ // キーボードキー状態送信
    digitalWrite( PIN_NUM_ROW[row], LOW );
    for( col=0; col<NUM_COL; col++ ){
      CurrentKeyState = digitalRead( PIN_NUM_COL[col] );
      keypos.row = row;
      keypos.col = col + 4;
      isMouseShiftKey = CheckSpecialKey( &KEYPOS_MOUSE_SHIFT, &keypos );
      if( CurrentKeyState == LOW ){ //押しているとき
        if( PreviousKeyState[row][col] == HIGH ){ //今押した
          SendKeyPress( &keypos );
          if( isMouseShiftKey ){
            ClearImageSensorDeltaXY();
          }
        }
        if( isMouseShiftKey ){
          flgMouseMode = 1;
        }
      }else{ //離しているとき
        if( PreviousKeyState[row][col] == LOW ){ //今離した
          SendKeyRelease( &keypos );
        }
        if( isMouseShiftKey ){
          flgMouseMode = 0;
        }
      }
      PreviousKeyState[row][col] = CurrentKeyState;
    }
    digitalWrite( PIN_NUM_ROW[row], HIGH );
  }
  MouseDeltaX = GetImageSensorDeltaX();
  MouseDeltaY = -GetImageSensorDeltaY();
  //if( flgMouseMode ){ // マウスモード
    SendMouseMove( MouseDeltaX, MouseDeltaY, WheelCount );
    WheelCount = 0; //↑とセット。使った直後にリセット
  //}
}

ADNS3050.h
これは↓のソースを少し書き換えたもの。
Interfacing an Arduino With a Mouse Sensor (ADNS-3050) : 3 Steps - Instructables

#include <SPI.h>
// SPI and misc pins for the ADNS
#define PIN_SCLK   SCK
#define PIN_MISO   MISO
#define PIN_MOSI   MOSI
#define PIN_NCS    10
// レジスタ
#define DELTA_X               0x03
#define DELTA_Y               0x04
#define MOUSE_CTRL            0x0d
#define RESET                 0x3a
#define MOTION_CTRL           0x41
byte ImageSensorRead( byte reg_addr ){
  digitalWrite( PIN_NCS, LOW );//begin communication
  // send address of the register, with MSBit = 0 to say it's reading
  SPI.transfer( reg_addr & 0x7f );
  delayMicroseconds(100);
  // read data
  byte data = SPI.transfer(0);
  delayMicroseconds(30);
  digitalWrite( PIN_NCS, HIGH );//end communication
  delayMicroseconds(30);
  return data;
}

void ImageSensorWrite( byte reg_addr, byte SPIdata ){
  digitalWrite( PIN_NCS, LOW );
  //send address of the register, with MSBit = 1 to say it's writing
  SPI.transfer( reg_addr | 0x80 );
  //send data
  SPI.transfer( SPIdata );
  delayMicroseconds(30);
  digitalWrite( PIN_NCS, HIGH );//end communication
  delayMicroseconds(30);
}

void ImageSensorComStart(){
  digitalWrite( PIN_NCS, HIGH );
  delay(20);
  digitalWrite( PIN_NCS, LOW );
}

void startupImageSensor(){
  //--------Setup SPI Communication---------
  byte out = 0;
  byte read = 0;
  byte bit = 0;
  pinMode( PIN_MISO, INPUT );
  pinMode( PIN_NCS, OUTPUT );
  SPI.begin();
  // set the details of the communication
  SPI.setBitOrder( MSBFIRST ); // transimission order of bits
  SPI.setDataMode( SPI_MODE3 ); // sampling on rising edge
  SPI.setClockDivider( SPI_CLOCK_DIV16 ); // 16MHz/16 = 1MHz
  delay(10);
  //----------------- Power Up and config ---------------
  ImageSensorComStart();
  ImageSensorWrite( RESET, 0x5a ); // force reset
  delay(100); // wait for it to reboot
  ImageSensorWrite( MOUSE_CTRL,  0x20 ); //Setup Mouse Control
  ImageSensorWrite( MOTION_CTRL, 0x00 ); //Clear Motion Control register
  delay(100);
}

byte GetImageSensorDeltaX(){//returns the X acceleration value
  return( ImageSensorRead( DELTA_X ) );
}
byte GetImageSensorDeltaY(){//returns the Y acceleration value
  return( ImageSensorRead( DELTA_Y ) );
}

左手用
KeybouseL.ino

// 左手用
#include <Mouse.h>
#include <Keyboard_jp.h>
#include "KeyDefine.h"
#define NUM_ROW 6
#define NUM_COL 4
#define NUM_COL_ALL 8
#define NUM_LAYER 3 //レイヤの数

#define PRESS   0  //押した(アクティブロー)
#define RELEASE 1 //離した

#define LAYER_MAIN 0 //メインレイヤのレイヤ番号
#define LAYER_NUM  1 //数字レイヤのレイヤ番号
#define LAYER_FN   2 //ファンクションレイヤのレイヤ番号

typedef  struct  {
  int row;
  int col;
} str_keypos;
byte MouseDeltaX = 0;
byte MouseDeltaY = 0;
byte WheelCount = 0;
byte readMouseData[4];
int readMouseDataCount = 0;
int layer = 0;
int PreviousKeyState[NUM_ROW][NUM_COL];
bool flgMouseMode = false;
const int PIN_NUM_ROW[NUM_ROW] = { 9, 8, 7, 6, 5, 4 };
const int PIN_NUM_COL[NUM_COL] = { A0, A1, A2, A3 };
const int debounce = 100;

const byte keymaps[NUM_LAYER][NUM_ROW][NUM_COL_ALL]  = {
LAYOUT( \
  KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,     _______, KC_LGUI,                  KC_RALT, KC_RCTL,  KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    KC_AT,   \
  KC_CAPS, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,     _______,                                    _______,  KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_COLN, \
  KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,     KC_BSPC, KC_SPC, KC_DEL,   KC_ENT, KC_SPC,  KC_ZKHK,  KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT ),
LAYOUT( \
  KC_ESC,  KC_EXLM, KC_DQOT, KC_HASH, KC_DLR,  KC_PERC,  _______, KC_LGUI,                  KC_RALT, KC_RCTL,  KC_AMPR, KC_QUOT, KC_LPRN, KC_RPRN, KC_P,    KC_EQL,  \
  KC_CAPS, KC_1,    KC_2,    KC_3,    KC_4,    KC_5,     _______,                                    _______,  KC_6,    KC_7,    KC_8,    KC_9,    KC_0,    KC_MINS, \
  KC_LSFT, KC_BSLS, KC_CIRC, KC_YEN,  KC_TILD, KC_PIPE,  KC_BSPC, KC_SPC, KC_DEL,   KC_ENT, KC_SPC,  KC_ZKHK,  KC_LBRC, KC_RBRC, KC_LLBR, KC_RLBR, KC_UNDS, KC_RSFT ),
LAYOUT( \
  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,    _______, KC_LGUI,                  KC_RALT, KC_RCTL,  KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  \
  KC_CAPS, _______, KC_LEFT, KC_UP,   KC_RGHT, _______,  _______,                                    _______,  _______, KC_HOME, KC_PGUP, KC_END,  KC_PSCR, _______, \
  KC_LSFT, _______, _______, KC_DOWN, _______, _______,  KC_BSPC, KC_SPC, KC_DEL,   KC_ENT, KC_SPC,  KC_ZKHK,  _______, _______, KC_PGDN, _______, _______, KC_RSFT )
};

const str_keypos KEYPOS_MOUSE_SHIFT = {5,4};
const str_keypos KEYPOS_LEFT_CLICK  = {4,5};
const str_keypos KEYPOS_RIGHT_CLICK = {1,6};

const str_keypos KEYPOS_NUM_SHIFT = {0,3};
const str_keypos KEYPOS_FN_SHIFT  = {1,3};

//-----------------------------------------------------------------
void setup(){
  int row, col;
  
  Mouse.begin(); //start mouse emulation
  Keyboard.begin();
  Serial1.begin( 19200 ); //左右通信用
  for( row = 0; row < NUM_ROW; row++ ){
    pinMode( PIN_NUM_ROW[row], OUTPUT );
  }
  for( col = 0; col < NUM_COL; col++ ){
    pinMode( PIN_NUM_COL[col], INPUT_PULLUP );
  }
  for( row = 0; row < NUM_ROW; row++){
    for( col = 0; col < NUM_COL; col++){
      PreviousKeyState[row][col] = HIGH; // 全てのキーの前回値を「押してない」にしておく
    }
    digitalWrite( PIN_NUM_ROW[row], HIGH ); // 全てのキーを無効にしておく
  }
}
//-----------------------------------------------------------------
void MousePressRelease( bool isPress, str_keypos* keypos ){
  if( CheckSpecialKey( &KEYPOS_LEFT_CLICK, keypos ) ){
    if( isPress ){
      Mouse.press( MOUSE_LEFT );
    }else{
      Mouse.release( MOUSE_LEFT );
    }
  }
  if( CheckSpecialKey( &KEYPOS_RIGHT_CLICK, keypos ) ){
    if( isPress ){
      Mouse.press( MOUSE_RIGHT );
    }else{
      Mouse.release( MOUSE_RIGHT );
    }
  }
}
//-----------------------------------------------------------------
void KeyboardPress( str_keypos* keypos ){
  Keyboard.press( keymaps[layer][keypos->row][keypos->col] );
}
//-----------------------------------------------------------------
void KeyboardRelease( str_keypos* keypos ){
  Keyboard.release( keymaps[layer][keypos->row][keypos->col] );
}
//-----------------------------------------------------------------
void KeyboardPressRelease( bool isPress, str_keypos* keypos ){
  if( isPress ){
    KeyboardPress( keypos );
  }else{
    KeyboardRelease( keypos );
  }
}
//-----------------------------------------------------------------
bool CheckSpecialKey( str_keypos* keypos1, str_keypos* keypos2 ){
  if( ( keypos1->row==keypos2->row ) &&( keypos1->col==keypos2->col ) ){
    return true;
  }else{
    return false;
  }
}
//-----------------------------------------------------------------
void SetLayer( void ){
  if( PreviousKeyState[KEYPOS_NUM_SHIFT.row][KEYPOS_NUM_SHIFT.col] == PRESS ){
    SetLayerTo( LAYER_NUM );
  }else if( PreviousKeyState[KEYPOS_FN_SHIFT.row][KEYPOS_FN_SHIFT.col] == PRESS ){
    SetLayerTo( LAYER_FN );
  }else{
    SetLayerTo( LAYER_MAIN );
  }
}
//-----------------------------------------------------------------
void SetLayerTo( int layernum ){
  if( layer!=layernum ){
    layer = layernum;
    Keyboard.releaseAll(); //押したままレイヤー切換えたら押しっぱなしになるので全部離す
  }  
}  
//-----------------------------------------------------------------
void CheckMouseModeKey( bool isPress, str_keypos* keypos ){
  if( CheckSpecialKey( &KEYPOS_MOUSE_SHIFT, keypos ) ){
    if( isPress ){
      flgMouseMode = true;
    }else{
      flgMouseMode = false;
    }
  }
}
//-----------------------------------------------------------------
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 ); // カーソルとホイール
  }
}
//-----------------------------------------------------------------
void loop() {
  int CurrentKeyState;
  str_keypos keypos;
  byte readData;
  bool isPress;
    
  // 左手キーボード
  SetLayer();
  for( keypos.row=0; keypos.row<NUM_ROW; keypos.row++ ){
    digitalWrite( PIN_NUM_ROW[keypos.row], LOW ); // keypos.row列目のキー入力を有効にする
    for( keypos.col=0; keypos.col<NUM_COL; keypos.col++ ){
      CurrentKeyState = digitalRead( PIN_NUM_COL[keypos.col] ); // keypos.col行目の値を読む
      if( PreviousKeyState[keypos.row][keypos.col]>1 ){              // 離したばかり
        PreviousKeyState[keypos.row][keypos.col]--;     // チャタリング対策のカウントダウン
      }else if( PreviousKeyState[keypos.row][keypos.col]<0 ){        // 押したばかり
        PreviousKeyState[keypos.row][keypos.col]++;     // チャタリング対策のカウントダウン
      }else if( PreviousKeyState[keypos.row][keypos.col]==RELEASE ){ // 離してから一定時間経過
        if( CurrentKeyState==PRESS ){   // 押した
          KeyboardPress( &keypos );
          PreviousKeyState[keypos.row][keypos.col] = -debounce;
        }
      }else if( PreviousKeyState[keypos.row][keypos.col]==PRESS ){   // 押してから一定時間経過
        if( CurrentKeyState==RELEASE ){ // 離した
          KeyboardRelease( &keypos );
          PreviousKeyState[keypos.row][keypos.col] = debounce;
        }
      }
    }
    digitalWrite( PIN_NUM_ROW[keypos.row], HIGH ); // keypos.row列目のキー入力を無効に戻す
  }
  //右手キーボード&マウス
  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;
      CheckMouseModeKey( isPress, &keypos );
      if( flgMouseMode ){ // マウスモード
        MousePressRelease( isPress, &keypos );
      }else{ // キーボードモード
        KeyboardPressRelease( isPress, &keypos );
      }
    }
  }
}

KeyDefine.h
ASCIIベースで作ってしまったが,記事に書いたとおり,Usage IDベースの方が良かった。名前はできるだけQMK firmwareと同じにした。

#define NONE       0x00
#define _______    0x00
#define KC_SPC     32
#define KC_EXLM    33 // !
#define KC_DQOT    34 // "
#define KC_HASH    35 // # 
#define KC_DLR     36 // $
#define KC_PERC    37 // %
#define KC_AMPR    38 // &
#define KC_QUOT    39 // '
#define KC_LPRN    40 // (
#define KC_RPRN    41 // )
#define KC_ASTR    42 // *
#define KC_PLS     43 // +
#define KC_COMM    44 // ,
#define KC_MINS    45 // -
#define KC_DOT     46 // .
#define KC_SLSH    47 // /
#define KC_0       0x30
#define KC_1       0x31
#define KC_2       0x32
#define KC_3       0x33
#define KC_4       0x34
#define KC_5       0x35
#define KC_6       0x36
#define KC_7       0x37
#define KC_8       0x38
#define KC_9       0x39
#define KC_COLN    58 // :
#define KC_SCLN    59 // ;
#define KC_LESS    60 // <
#define KC_EQL     61 // =
#define KC_GRAT    62 // >
#define KC_QSTN    63 // ?
#define KC_AT      64 // @
#define KC_LLBR    91 // [
#define KC_YEN     92 // z
#define KC_RLBR    93 // ]
#define KC_CIRC    94 // ^ CIRCUMFLEX
#define KC_UNDS    95 // _
#define KC_A       0x61
#define KC_B       0x62
#define KC_C       0x63
#define KC_D       0x64
#define KC_E       0x65
#define KC_F       0x66
#define KC_G       0x67
#define KC_H       0x68
#define KC_I       0x69
#define KC_J       0x6A
#define KC_K       0x6B
#define KC_L       0x6C
#define KC_M       0x6D
#define KC_N       0x6E
#define KC_O       0x6F
#define KC_P       0x70
#define KC_Q       0x71
#define KC_R       0x72
#define KC_S       0x73
#define KC_T       0x74
#define KC_U       0x75
#define KC_V       0x76
#define KC_W       0x77
#define KC_X       0x78
#define KC_Y       0x79
#define KC_Z       0x7A
#define KC_LBRC   123 // {
#define KC_PIPE   124 // |
#define KC_RBRC   125 // }
#define KC_TILD   126 // ~

//USB の Usage ID に 0x58 を足したキーコード
                         // Usage ID
#define KC_ENT     0x80  // 0x28
#define KC_ESC     0x81  // 0x29
#define KC_BSPC    0x82  // 0x2a
#define KC_TAB     0x83  // 0x2b
#define KC_ZKHK    0x8d  //全角半角
#define KC_CAPS    0x91  // 0x39
#define KC_F1      0x92  // 0x3a
#define KC_F2      0x93  // 0x3b
#define KC_F3      0x94  // 0x3c
#define KC_F4      0x95  // 0x3d
#define KC_F5      0x96  // 0x3e
#define KC_F6      0x97  // 0x3f
#define KC_F7      0x98  // 0x40
#define KC_F8      0x99  // 0x41
#define KC_F9      0x9a  // 0x42
#define KC_F10     0x9b  // 0x43
#define KC_F11     0x9c  // 0x44
#define KC_F12     0x9d  // 0x45
#define KC_PSCR    0x9e  // 0x46
#define KC_INS     0xa1  // 0x49
#define KC_HOME    0xa2  // 0x4a
#define KC_PGUP    0xa3  // 0x4b
#define KC_DEL     0xa4  // 0x4c
#define KC_END     0xa5  // 0x4d
#define KC_PGDN    0xa6  // 0x4e
#define KC_RGHT    0xa7  // 0x4f
#define KC_LEFT    0xa8  // 0x50
#define KC_DOWN    0xa9  // 0x51
#define KC_UP      0xaa  // 0x52
#define KC_BSLS   223 // \ backslash
#define KC_LCTL    0xf8  // 0xe0
#define KC_LSFT    0xf9  // 0xe1
#define KC_LALT    0xfa  // 0xe2
#define KC_LGUI    0xfb  // 0xe3
#define KC_RCTL    0xfc  // 0xe4
#define KC_RSFT    0xfd  // 0xe5
#define KC_RALT    0xfe  // 0xe6
#define KC_RGUI    0xff  // 0xe7

#define LAYOUT( \
  L31, L41, L32, L42, L33, L43,  L24, L34,             R45, R55,  R36, R46, R37, R47, R38, R48, \
  L21, L51, L22, L52, L23, L53,  L14,                       R65,  R26, R56, R27, R57, R28, R58, \
  L11, L61, L12, L62, L13, L63,  L64, L54, L44,   R35, R25, R15,  R16, R66, R17, R67, R18, R68 \
  ) \
  { \
    { L11, L12, L13, L14, R15, R16, R17, R18 }, \
    { L21, L22, L23, L24, R25, R26, R27, R28 }, \
    { L31, L32, L33, L34, R35, R36, R37, R38 }, \
    { L41, L42, L43, L44, R45, R46, R47, R48 }, \
    { L51, L52, L53, L54, R55, R56, R57, R58 }, \
    { L61, L62, L63, L64, R65, R66, R67, R68 } \
  }