1.目的

 USBキーボードのキー変換をする変換器を作成しました。

 (USB Keyboard) → (変換器) → (Host PC)

 

2.利用する基盤

 (1) Pro micro (8MHz, 3.3V) (写真上段:変換器の表側)

 (2) mini USB Host Shield(写真下段:変換器の裏側)

  (配線/組み立てはこちらを参照にしました)

 

   今回作成した変換器

 

 

3.利用するプログラム

  プログラミングにはarduino(C/C++)の統合開発基盤を利用しました。

 (1) 開発環境

   PC:Lenovo IdeaPad Flex10

   OS:Linux  Ubuntu 20.04.1 TLS

   SW:Arduino 1.8.13

 (2) ライブラリ

   Sketch > Include Library > Manage Library から「HID-project」、「USB 

   Host Shield Library 2.0」をインストールする。

 (3) プログラム概要

   Keyboardのキーが押される度にKeyboardReportParser が呼び出される。

  *bufに入力されたキー情報をoutbufにCOPYし、oldbufと比較することでキーの

  On/Offを判定し、BootKeyboard.add/removeにより出力キーをセットし、

  BootKeyboard.sendでHost PCに送信する。

   HID-projectによりBIOS設定時でも利用可能でした。

 

       outufにCOPYしたキー情報(USB Scancodeです)

 

キー情報を単に透過するプログラム

 

#include <hid-project.h>
#include <hidboot.h>

uint8_t i, outbuf[8], oldbuf[8];
uint8_t modp[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
uint8_t modo[8]={0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7};

class HIDKeyboardParser : public KeyboardReportParser {
    virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};

void HIDKeyboardParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
  memcpy(outbuf,buf,8); 
 /* 必要に応じてoutbufをキー変換する*/
  for(i=0;i<8;i++){
    if(((outbuf[0]&modp[i])!=0)&&((oldbuf[0]&modp[i])==0)) BootKeyboard.add(KeyboardKeycode(modo[i]));
    if(((outbuf[0]&modp[i])==0)&&((oldbuf[0]&modp[i])!=0)) BootKeyboard.remove(KeyboardKeycode(modo[i]));
  }
  for(i=2;i<8;i++){
    if((outbuf[i]!=0)&&(oldbuf[i]==0)) BootKeyboard.add(KeyboardKeycode(outbuf[i]));
    if((outbuf[i]==0)&&(oldbuf[i]!=0)) BootKeyboard.remove(KeyboardKeycode(oldbuf[i]));
    if((outbuf[i]!=oldbuf[i])&&(outbuf[i]!=0)) BootKeyboard.remove(KeyboardKeycode(oldbuf[i]));
  }
  BootKeyboard.send();
  memcpy(oldbuf,outbuf,8);
}

USB usb;
HIDBoot<hid_protocol_keyboard> HidKeyboard(&usb);
HIDKeyboardParser parser;

void setup() {
  Serial.begin(115200);
  BootKeyboard.begin();
  if (usb.Init() == -1) Serial.println("OSC did not start.");
  delay(200);
  HidKeyboard.SetReportParser(0, (HIDKeyboardParser*)&parser);
}

void loop() {
  usb.Task();
}

 

 

【利用例】Sampleプログラムの主な機能を下記に示す。

US配列キーボードのままJapanese Keyboard・US keyboardを切り替えて使います。

 (Fn+BackspaceでJP/USを切り替え。)

SpaceキーをTap(Space)、Hold(Fn)にする。

Japanese Keyboardの場合は、CapsLockキーをE/Jキーにする。

 (他にShift+CapsLock ⇨ 変換、Fn+CapsLock⇨無変換)

スクリーンセーバー抑止で1分毎に自動キー出力する。

 (Windows10連続キー条件からCtrlとShiftを交互に出力する。)

 

入力されたScancodeで直接デーブルの行検索するため0x00から0x39のキーを定義した。

(Tableにないキーはそのまま入力⇨出力で転送されます)

Layer情報で直接テーブルの列検索をします。

追加のArduinoライブラリ:「MsTimer2」

 

Sampleのキーマップ(下図の黄色はキー変換した部分)

Keymap (US to JP scancode)

keymap (US to US scancode)

 

Sampleプログラム

#include <hid-project.h>
#include <hidboot.h>
#include <mstimer2.h>

uint8_t i, t=0, outbuf[8], oldbuf[8], layer=0, atc[58][6];
uint8_t modp[4]={0x11,0x22,0x44,0x88}, modo[4]={0xe0,0xe1,0xe2,0xe3};

static const uint8_t jpc[][6] = {       // US Keycap -> JP Scancode
  {0,0x00,0,0x00,0,0x00},    // 0x00 dumy
  {0,0x00,0,0x00,0,0x00},    // 0x01 dumy
  {0,0x00,0,0x00,0,0x00},    // 0x02 dumy
  {0,0x00,0,0x00,0,0x00},    // 0x03 dumy   layer0  layer2  layer4
  {0,0x04,1,0x04,0,0x4A},    // 0x04 'a'   -> 'a'  , 'A'  , 'home'
  {0,0x05,1,0x05,0,0x00},    // 0x05 'b'   -> 'b'  , 'B'  , ''
  {0,0x06,1,0x06,0,0x00},    // 0x06 'c'   -> 'c'  , 'C'  , ''
  {0,0x07,1,0x07,0,0x51},    // 0x07 'd'   -> 'd'  , 'D'  , '↓'
  {0,0x08,1,0x08,0,0x52},    // 0x08 'e'   -> 'e'  , 'E'  , '↑'
  {0,0x09,1,0x09,0,0x4F},    // 0x09 'f'   -> 'f'  , 'F'  , '→'
  {0,0x0A,1,0x0A,0,0x4D},    // 0x0A 'g'   -> 'g'  , 'G'  , 'End'
  {0,0x0B,1,0x0B,0,0x2A},    // 0x0B 'h'   -> 'h'  , 'H'  , 'BS'
  {0,0x0C,1,0x0C,0,0x00},    // 0x0C 'i'   -> 'i'  , 'I'  , ''
  {0,0x0D,1,0x0D,0,0x28},    // 0x0D 'j'   -> 'j'  , 'J'  , 'RT'
  {0,0x0E,1,0x0E,0,0x00},    // 0x0E 'k'   -> 'k'  , 'K'  , ''
  {0,0x0F,1,0x0F,0,0x00},    // 0x0F 'l'   -> 'l'  , 'L'  , ''
  {0,0x10,1,0x10,0,0x00},    // 0x10 'm'   -> 'm'  , 'M'  , ''
  {0,0x11,1,0x11,0,0x00},    // 0x11 'n'   -> 'n'  , 'N'  , ''
  {0,0x12,1,0x12,0,0x00},    // 0x12 'o'   -> 'o'  , 'O'  , ''
  {0,0x13,1,0x13,0,0x00},    // 0x13 'p'   -> 'p'  , 'P'  , ''
  {0,0x14,1,0x14,0,0x00},    // 0x14 'q'   -> 'q'  , 'Q'  , ''
  {0,0x15,1,0x15,0,0x4E},    // 0x15 'r'   -> 'r'  , 'R'  , 'PgDn'
  {0,0x16,1,0x16,0,0x50},    // 0x16 's'   -> 's'  , 'S'  , '←'
  {0,0x17,1,0x17,0,0x00},    // 0x17 't'   -> 't'  , 'T'  , ''
  {0,0x18,1,0x18,0,0x00},    // 0x18 'u'   -> 'u'  , 'U'  , ''
  {0,0x19,1,0x19,0,0x00},    // 0x19 'v'   -> 'v'  , 'V'  , ''
  {0,0x1A,1,0x1A,0,0x4B},    // 0x1A 'w'   -> 'w'  , 'W'  , 'PgUp'
  {0,0x1B,1,0x1B,0,0x00},    // 0x1B 'x'   -> 'x'  , 'X'  , ''
  {0,0x1C,1,0x1C,0,0x00},    // 0x1C 'y'   -> 'y'  , 'Y'  , ''
  {0,0x1D,1,0x1D,0,0x00},    // 0x1D 'z'   -> 'z'  , 'Z'  , ''
  {0,0x1E,1,0x1E,0,0x3A},    // 0x1E '1'   -> '1'  , '!'  , 'F1'
  {0,0x1F,0,0x2F,0,0x3B},    // 0x1F '2'   -> '2'  , '@'  , 'F2'
  {0,0x20,1,0x20,0,0x3C},    // 0x20 '3'   -> '3'  , '#'  , 'F3'
  {0,0x21,1,0x21,0,0x3D},    // 0x21 '4'   -> '4'  , '$'  , 'F4'
  {0,0x22,1,0x22,0,0x3E},    // 0x22 '5'   -> '5'  , '%'  , 'F5'
  {0,0x23,0,0x2E,0,0x3F},    // 0x23 '6'   -> '6'  , '^'  , 'F6'
  {0,0x24,1,0x23,0,0x40},    // 0x24 '7'   -> '7'  , '&'  , 'F7'
  {0,0x25,1,0x34,0,0x41},    // 0x25 '8'   -> '8'  , '*'  , 'F8'
  {0,0x26,1,0x25,0,0x42},    // 0x26 '9'   -> '9'  , '('  , 'F9'
  {0,0x27,1,0x26,0,0x43},    // 0x27 '0'   -> '0'  , ')'  , 'F10'
  {0,0x28,0,0x00,2,0x28},    // 0x28 'RT'  -> 'RT' , ''   ,  ''
  {0,0x29,0,0x00,0,0x35},    // 0x29 'esc' -> 'esc', ''   , 'E/J'
  {0,0x2A,0,0x00,0,0x00},    // 0x2A 'bs'  -> 'bs' , ''   , 'jp flag'
  {0,0x2B,0,0x00,0,0x3B},    // 0x2B 'tab' -> 'tab', ''   , 'F2'
  {0,0x2C,1,0x2C,0,0x00},    // 0x2C 'spc' -> 'spc', 'spc', ''
  {0,0x2D,1,0x87,0,0x44},    // 0x2D '-'   -> '-'  , '_'  , 'F11'
  {1,0x2D,1,0x33,0,0x45},    // 0x2E '='   -> '='  , '+'  , 'F12'
  {0,0x30,1,0x30,0,0x00},    // 0x2F '['   -> '['  , '{'  , ''
  {0,0x31,1,0x31,0,0x00},    // 0x30 ']'   -> ']'  , '}'  , ''
  {0,0x89,1,0x89,0,0x00},    // 0x31 '¥'   -> '¥'  , '|'  , ''
  {0,0x00,0,0x00,0,0x00},    // 0x32 ''    -> ''   , ''   , ''
  {0,0x33,0,0x34,0,0x00},    // 0x33 ';'   -> ';'  , ':'  , ''
  {1,0x24,1,0x1F,0,0x00},    // 0x34 '''   -> '''  , '"'  , ''
  {0,0x00,1,0x2E,0,0x00},    // 0x35 '''   -> '`'  , '~'  , '`'
  {0,0x36,1,0x36,1,0x2F},    // 0x36 ','   -> ','  , '<'  , '`'
  {0,0x37,1,0x37,0,0x00},    // 0x37 '.'   -> '.'  , '>'  , ''
  {0,0x38,1,0x38,0,0x00},    // 0x38 '/'   -> '/'  , '?'  , ''
  {0,0x35,0,0x8A,0,0x8B}     // 0x39 'cps' -> 'E/J', 'Cnv', 'NCnv'
};

static const uint8_t usc[][6] = {       // US Keycap -> US Scancode
  {0,0x00,0,0x00,0,0x00},    // 0x00 dumy
  {0,0x00,0,0x00,0,0x00},    // 0x01 dumy
  {0,0x00,0,0x00,0,0x00},    // 0x02 dumy
  {0,0x00,0,0x00,0,0x00},    // 0x03 dumy   layer0  layer2  layer4
  {0,0x04,1,0x04,0,0x00},    // 0x04 'a'   -> 'a'  , 'A'  , ''
  {0,0x05,1,0x05,0,0x00},    // 0x05 'b'   -> 'b'  , 'B'  , ''
  {0,0x06,1,0x06,0,0x00},    // 0x06 'c'   -> 'c'  , 'C'  , ''
  {0,0x07,1,0x07,0,0x00},    // 0x07 'd'   -> 'd'  , 'D'  , ''
  {0,0x08,1,0x08,0,0x00},    // 0x08 'e'   -> 'e'  , 'E'  , ''
  {0,0x09,1,0x09,0,0x00},    // 0x09 'f'   -> 'f'  , 'F'  , ''
  {0,0x0A,1,0x0A,0,0x00},    // 0x0A 'g'   -> 'g'  , 'G'  , ''
  {0,0x0B,1,0x0B,0,0x2A},    // 0x0B 'h'   -> 'h'  , 'H'  , 'BS'
  {0,0x0C,1,0x0C,0,0x00},    // 0x0C 'i'   -> 'i'  , 'I'  , ''
  {0,0x0D,1,0x0D,0,0x28},    // 0x0D 'j'   -> 'j'  , 'J'  , 'RT'
  {0,0x0E,1,0x0E,0,0x00},    // 0x0E 'k'   -> 'k'  , 'K'  , ''
  {0,0x0F,1,0x0F,0,0x00},    // 0x0F 'l'   -> 'l'  , 'L'  , ''
  {0,0x10,1,0x10,0,0x00},    // 0x10 'm'   -> 'm'  , 'M'  , ''
  {0,0x11,1,0x11,0,0x00},    // 0x11 'n'   -> 'n'  , 'N'  , ''
  {0,0x12,1,0x12,0,0x00},    // 0x12 'o'   -> 'o'  , 'O'  , ''
  {0,0x13,1,0x13,0,0x00},    // 0x13 'p'   -> 'p'  , 'P'  , ''
  {0,0x14,1,0x14,0,0x00},    // 0x14 'q'   -> 'q'  , 'Q'  , ''
  {0,0x15,1,0x15,0,0x00},    // 0x15 'r'   -> 'r'  , 'R'  , ''
  {0,0x16,1,0x16,0,0x00},    // 0x16 's'   -> 's'  , 'S'  , ''
  {0,0x17,1,0x17,0,0x00},    // 0x17 't'   -> 't'  , 'T'  , ''
  {0,0x18,1,0x18,0,0x00},    // 0x18 'u'   -> 'u'  , 'U'  , ''
  {0,0x19,1,0x19,0,0x00},    // 0x19 'v'   -> 'v'  , 'V'  , ''
  {0,0x1A,1,0x1A,0,0x00},    // 0x1A 'w'   -> 'w'  , 'W'  , ''
  {0,0x1B,1,0x1B,0,0x00},    // 0x1B 'x'   -> 'x'  , 'X'  , ''
  {0,0x1C,1,0x1C,0,0x00},    // 0x1C 'y'   -> 'y'  , 'Y'  , ''
  {0,0x1D,1,0x1D,0,0x00},    // 0x1D 'z'   -> 'z'  , 'Z'  , ''
  {0,0x1E,1,0x1E,0,0x3A},    // 0x1E '1'   -> '1'  , '!'  , 'F1'
  {0,0x1F,1,0x1F,0,0x3B},    // 0x1F '2'   -> '2'  , '@'  , 'F2'
  {0,0x20,1,0x20,0,0x3C},    // 0x20 '3'   -> '3'  , '#'  , 'F3'
  {0,0x21,1,0x21,0,0x3D},    // 0x21 '4'   -> '4'  , '$'  , 'F4'
  {0,0x22,1,0x22,0,0x3E},    // 0x22 '5'   -> '5'  , '%'  , 'F5'
  {0,0x23,1,0x23,0,0x3F},    // 0x23 '6'   -> '6'  , '^'  , 'F6'
  {0,0x24,1,0x24,0,0x40},    // 0x24 '7'   -> '7'  , '&'  , 'F7'
  {0,0x25,1,0x25,0,0x41},    // 0x25 '8'   -> '8'  , '*'  , 'F8'
  {0,0x26,1,0x26,0,0x42},    // 0x26 '9'   -> '9'  , '('  , 'F9'
  {0,0x27,1,0x27,0,0x43},    // 0x27 '0'   -> '0'  , ')'  , 'F10'
  {0,0x28,0,0x00,0,0x00},    // 0x28 'RT'  -> 'RT' , ''   , ''
  {0,0x29,0,0x00,0,0x35},    // 0x29 'esc' -> 'esc', ''   , '`'
  {0,0x2A,0,0x00,1,0x00},    // 0x2A 'bs'  -> 'bs' , ''   , 'us flag'
  {0,0x2B,0,0x00,0,0x00},    // 0x2B 'tab' -> 'tab', ''   , ''
  {0,0x2C,1,0x2C,0,0x00},    // 0x2C 'spc' -> 'spc', 'spc', ''
  {0,0x2D,1,0x2D,0,0x44},    // 0x2D '-'   -> '-'  , '_'  , 'F11'
  {0,0x2E,1,0x2E,0,0x45},    // 0x2E '='   -> '='  , '+'  , 'F12'
  {0,0x2F,1,0x2F,0,0x00},    // 0x2F '['   -> '['  , '{'  , ''
  {0,0x30,1,0x30,0,0x00},    // 0x30 ']'   -> ']'  , '}'  , ''
  {0,0x31,1,0x31,0,0x00},    // 0x31 '¥'   -> '¥'  , '|'  , ''
  {0,0x32,1,0x32,0,0x00},    // 0x32 ''    -> ''   , ''   , ''
  {0,0x33,1,0x33,0,0x2A},    // 0x33 ';'   -> ';'  , ':'  , ''
  {0,0x34,1,0x34,0,0x28},    // 0x34 '''   -> '''  , '"'  , ''
  {0,0x00,1,0x35,0,0x00},    // 0x35 '''   -> ''   , '~'  , ''
  {0,0x36,1,0x36,0,0x00},    // 0x36 ','   -> ','  , '<'  , ''
  {0,0x37,1,0x37,0,0x00},    // 0x37 '.'   -> '.'  , '>'  , ''
  {0,0x38,1,0x38,0,0x00},    // 0x38 '/'   -> '/'  , '?'  , ''
  {0,0x39,0,0x00,0,0x00}     // 0x39 'cps' -> 'cps', ''   , ''
};

class HIDKeyboardParser : public KeyboardReportParser {
    virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};

void HIDKeyboardParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
  memcpy(outbuf,buf,8); 
  switch(layer){
    case 0:                                     // Normal layer
      if((*buf&0x22)!=0) layer=2;    // shift key on to layer 2
      if((*(buf+2)==0x2C)&&(*(buf+3)==0x00)){   // space key on to layer 3
        layer=3;
        return 0;
      }
      break;
    case 2:                                     // shift layer
      if((*buf&0x22)==0) layer=0;   // shift key off to layer 0
      break;
    case 3:                                     // space key on
      if(*(buf+2)==0x00){                // taped space key off
        BootKeyboard.write(KeyboardKeycode(0x2c)); // Push & release space key
        layer=0;
        return 0;
      } else if(*(buf+3)!=0x00) layer=4;        // Fanctional space key off
    case 4:                                     // Function layer
      if((*(buf+2)==0x00)){                     //  space off to layer 0
        layer=0;
        return 0;
      }
      if(*(buf+3)==0x2A){                       // Fn + BS
        if(atc[0x2A][4]==0) memcpy(atc,usc,348);   // to us keycode
        else memcpy(atc,jpc,348);               // to jp keycode
      }
  }

  for(i=2;i<8;i++){                             // change key
    if((*(buf+i)>0x03)&&(*(buf+i)<0x40)){       // 0x04('1') <= input key <= 0x39('CapsLock')
      if(atc[*(buf+i)][layer]==0) outbuf[0]=outbuf[0]&0xdd;  // Set OFF shift key
      if(atc[*(buf+i)][layer]==1) outbuf[0]=outbuf[0]|0x02;  // Set On shift key
      if(atc[*(buf+i)][layer]==2) outbuf[0]=outbuf[0]|0x04;  // Set On ALT key
      outbuf[i]=atc[*(buf+i)][layer+1];         // Set key changed
    }else outbuf[i]=*(buf+i);               // Set key over 0x39 (Poker keyboard)
  }
  for(i=0;i<4;i++){                             // Set modifers keys for bootkeyboard
    if(((outbuf[0]&modp[i])!=0)&&((oldbuf[0]&modp[i])==0)) BootKeyboard.add(KeyboardKeycode(modo[i]));
    if(((outbuf[0]&modp[i])==0)&&((oldbuf[0]&modp[i])!=0)) BootKeyboard.remove(KeyboardKeycode(modo[i]));
  }
  for(i=2;i<8;i++){                             // Set normal kes for bootkeyboard
    if((outbuf[i]!=0)&&(oldbuf[i]==0)) BootKeyboard.add(KeyboardKeycode(outbuf[i]));
    if((outbuf[i]==0)&&(oldbuf[i]!=0)) BootKeyboard.remove(KeyboardKeycode(oldbuf[i]));
    if((outbuf[i]!=oldbuf[i])&&(outbuf[i]!=0)) BootKeyboard.remove(KeyboardKeycode(oldbuf[i]));
  }
  BootKeyboard.send();                   // send krycode to Host PC
  memcpy(oldbuf,outbuf,8);             // copy to oldbuf from outbuf
  t=0;                                                // Timer clear
}

void timerFire() {
  if((t%2)==0) BootKeyboard.write(KeyboardKeycode(0xe1));  //  Push & release Shift key 60s interval
  else BootKeyboard.write(KeyboardKeycode(0xe0));          //  Push & release Ctrl key 60s interval
  t++;
}

USB usb;
HIDBoot<hid_protocol_keyboard> HidKeyboard(&usb);
HIDKeyboardParser parser;

void setup() {
  Serial.begin(115200);
  BootKeyboard.begin();                         // Init HID on promicro
  if (usb.Init() == -1) Serial.println("OSC did not start.");
  delay(200);
  memcpy(atc,jpc,348);                          // set JP keycode on default
  HidKeyboard.SetReportParser(0, (HIDKeyboardParser*)&parser);    // Init host shield
  MsTimer2::set(60000, timerFire);              // set timer interval 60s
  MsTimer2::start();
}

void loop() {
  usb.Task();
}

 

<謝辞>

「東京お気楽カメラ」様 ハードウェア組み立ての情報は大変助かりました。

「森山 将之のホームページへようこそ!」様 Scancode一覧は大変助かりました。

 ありがとうございました。