LoginSignup
2
3

More than 3 years have passed since last update.

Arduinoでキー変換器を作る

Last updated at Posted at 2021-04-22

2021.4.30: ソースコードを修正(PC側でシリアル接続しないと動作しない問題修正)

IMG_0719.png

参考にしたもの

[https://okiraku-camera.tokyo/blog/?p=8193]

作り方

  • キーボード: Thinkpad keyboard with Tracpoint
  • ハード:参考と一緒
  • ソフト:参考にしたものはkeyboardだけだったのでArduinoの標準ライブラリのUSBHIDBootKbdAndMouseのサンプルをもとに実装(tracpointが使いたい!!!)
  • 注意点:Aruduinoの標準ライブラリのKeyboardのsendReport()privateからpublicに変更した。
keybord_converter.ino
/** 
 * KeyCodeConvert
 * original Keyboard Library must be modified.
 * in Keyboard.h, function "sendReport()" should be change to "public" from "private".
 */

#include <hidboot.h>
#include <usbhub.h>

#include "Keyboard.h"
#include "Mouse.h"

// Satisfy IDE, which only needs to see the include statment in the ino.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>

// KeyReportのmodifiersのビット構成
static const uint8_t MODIFIRE_LEFT_CONTROL = 0x01;
static const uint8_t MODIFIRE_LEFT_SHIFT = 0x02;
static const uint8_t MODIFIRE_LEFT_ALT = 0x04;
static const uint8_t MODIFIRE_LEFT_GUI= 0x08;
static const uint8_t MODIFIRE_RIGHT_CONTROL = 0x10;
static const uint8_t MODIFIRE_RIGHT_SHIFT = 0x20;
static const uint8_t MODIFIRE_RIGHT_ALT = 0x40;
static const uint8_t MODIFIRE_RIGHT_GUI= 0x80;

//#define DEBUG_PRINT

class MouseRptParser : public MouseReportParser
{
  protected:
    void OnMouseMove(MOUSEINFO *mi);
    void OnLeftButtonUp(MOUSEINFO *mi);
    void OnLeftButtonDown(MOUSEINFO *mi);
    void OnRightButtonUp(MOUSEINFO *mi);
    void OnRightButtonDown(MOUSEINFO *mi);
    void OnMiddleButtonUp(MOUSEINFO *mi);
    void OnMiddleButtonDown(MOUSEINFO *mi);
};
void MouseRptParser::OnMouseMove(MOUSEINFO *mi)
{
#ifdef DEBUG_PRINT
  Serial.print("dx=");
  Serial.print(mi->dX, DEC);
  Serial.print(" dy=");
  Serial.println(mi->dY, DEC);
#else
  Mouse.move(mi->dX, mi->dY);
#endif
};
void MouseRptParser::OnLeftButtonUp (MOUSEINFO *mi)
{
#ifdef DEBUG_PRINT
  Serial.println("L Butt Up");
#else
  Mouse.release(MOUSE_LEFT);
#endif
};
void MouseRptParser::OnLeftButtonDown   (MOUSEINFO *mi)
{
#ifdef DEBUG_PRINT
  Serial.println("L Butt Dn");
#else
  Mouse.press(MOUSE_LEFT);
#endif
};
void MouseRptParser::OnRightButtonUp    (MOUSEINFO *mi)
{
#ifdef DEBUG_PRINT
  Serial.println("R Butt Up");
#else
  Mouse.release(MOUSE_RIGHT);
#endif
};
void MouseRptParser::OnRightButtonDown  (MOUSEINFO *mi)
{
#ifdef DEBUG_PRINT
  Serial.println("R Butt Dn");
#else
  Mouse.press(MOUSE_RIGHT);
#endif
};
void MouseRptParser::OnMiddleButtonUp   (MOUSEINFO *mi)
{
#ifdef DEBUG_PRINT
  Serial.println("M Butt Up");
#else
  Mouse.release(MOUSE_MIDDLE);
#endif
};
void MouseRptParser::OnMiddleButtonDown (MOUSEINFO *mi)
{
#ifdef DEBUG_PRINT
  Serial.println("M Butt Dn");
#else
  Mouse.press(MOUSE_MIDDLE);
#endif
};

bool modifyKey(uint8_t* pKey, bool bDown);
bool modifyModifier(uint8_t before, uint8_t after);
void sendReport(KeyReport* report);

class KbdRptParser : public KeyboardReportParser
{
    void PrintKey(uint8_t mod, uint8_t key);

  protected:
    void OnControlKeysChanged(uint8_t before, uint8_t after);
    void OnKeyDown  (uint8_t mod, uint8_t key);
    void OnKeyUp    (uint8_t mod, uint8_t key);
//    void OnKeyPressed(uint8_t key);
};

KeyReport keyReport;

void sendReport(KeyReport* report)
{
  Serial.print("== ");
  Serial.print(report->modifiers, HEX);
  Serial.print(":");
  Serial.print(report->keys[0], HEX);
  Serial.println(" ==");

  Keyboard.sendReport(report);
}

/**
 * 修飾キー変換
 * @note Controlの場合はCapsLockに変更
 * @param [in] MODIFIERKEYS before: 前回の修飾キー状態 
 * @param [in] MODIFIERKEYS after: 今回の修飾キー状態
 * @return bool : 修飾キー変換したかどうか
 */
bool modifyModifier(MODIFIERKEYS before, MODIFIERKEYS after)
{
  // Control->CAPSLOCK(0x39)
  if (before.bmLeftCtrl != after.bmLeftCtrl)
  {
    if (after.bmLeftCtrl)
    {
      Serial.println("CAPSLOCK DOWN");
      keyReport.keys[0] = 0x39;      
    }
    else
    {
      Serial.println("CAPSLOCK UP");
      keyReport.keys[0] = 0;      
    }
    sendReport(&keyReport);
    return true;
  }

  return false;
}

/**
 * キー変換
 * @param [in/out] uint8_t* pKey: キーコード
 * @param [in] bool bDown: 押下
 * @return bool : true キー変換実施, false modifierとして処理
 */
bool modifyKey(uint8_t* pKey, bool bDown)
{
  switch(*pKey) {
    case 0x29: // ESC(0x29)->半角/全角(0x35)
      *pKey = 0x35;
      break;
    case 0x35: // 半角/全角(0x35)->ESC(0x29)
      *pKey = 0x29;
      break;
    case 0x8A: // 変換(0x8A)->半角/全角(0x35)
      *pKey = 0x35;
      break;
    case 0x8B: // 無変換(0x8B)->半角/全角(0x35)
      *pKey = 0x35;
        break;
    case 0x39: // CAPSLOCK(0x39)->Control
    {
      KBDINFO* pKbdInfo;
      pKbdInfo = (KBDINFO*)&keyReport;
      if (bDown) 
      {
        pKbdInfo->bmLeftCtrl = 1;
        Serial.println("LeftCtrl down");      
      }
      else
      {
        pKbdInfo->bmLeftCtrl = 0;
        Serial.println("LeftCtrl up");      
      }
      return false;
    }
    default:
      break;
  }

  Serial.print("changed to ");
  Serial.println(*pKey, HEX);

  return true;  
}

void KbdRptParser::PrintKey(uint8_t m, uint8_t key)
{
  MODIFIERKEYS mod;
  *((uint8_t*)&mod) = m;
  Serial.print((mod.bmLeftCtrl   == 1) ? "C" : " ");
  Serial.print((mod.bmLeftShift  == 1) ? "S" : " ");
  Serial.print((mod.bmLeftAlt    == 1) ? "A" : " ");
  Serial.print((mod.bmLeftGUI    == 1) ? "G" : " ");

  Serial.print(" >");
  PrintHex<uint8_t>(key, 0x80);
  Serial.print("< ");

  Serial.print((mod.bmRightCtrl   == 1) ? "C" : " ");
  Serial.print((mod.bmRightShift  == 1) ? "S" : " ");
  Serial.print((mod.bmRightAlt    == 1) ? "A" : " ");
  Serial.println((mod.bmRightGUI    == 1) ? "G" : " ");
};

void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key)
{
  // キー変換
  if (modifyKey(&key, true))
  {
    Serial.print("DN ");
    keyReport.keys[0] = key;
    PrintKey(keyReport.modifiers, key);
    sendReport(&keyReport);

//  uint8_t c = OemToAscii(mod, key);
//  if (c)
//    OnKeyPressed(c);    
  }
}

void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key)
{
  if (modifyKey(&key, false))
  {
    Serial.print("UP ");
    keyReport.keys[0] = 0;
    PrintKey(keyReport.modifiers, key);
    sendReport(&keyReport);  
  }
}

void KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) {

  MODIFIERKEYS beforeMod;
  *((uint8_t*)&beforeMod) = before;

  MODIFIERKEYS afterMod;
  *((uint8_t*)&afterMod) = after;

  if (before != after) {
    if (modifyModifier(beforeMod, afterMod))
    {
      return;
    }
    Serial.print("Modifer changed:");
    Serial.println(after);
  }
  keyReport.modifiers = after;

  if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl) {
    Serial.println("LeftCtrl changed");
  }
  if (beforeMod.bmLeftShift != afterMod.bmLeftShift) {
    Serial.println("LeftShift changed");
  }
  if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt) {
    Serial.println("LeftAlt changed");
  }
  if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI) {
    Serial.println("LeftGUI changed");
  }

  if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl) {
    Serial.println("RightCtrl changed");
  }
  if (beforeMod.bmRightShift != afterMod.bmRightShift) {
    Serial.println("RightShift changed");
  }
  if (beforeMod.bmRightAlt != afterMod.bmRightAlt) {
    Serial.println("RightAlt changed");
  }
  if (beforeMod.bmRightGUI != afterMod.bmRightGUI) {
    Serial.println("RightGUI changed");
  }

}

//void KbdRptParser::OnKeyPressed(uint8_t key)
//{
//  Serial.print("ASCII: ");
//  Serial.println((char)key);
//  Keyboard.write(key);
//};

USB     Usb;
USBHub     Hub(&Usb);

HIDBoot < USB_HID_PROTOCOL_KEYBOARD | USB_HID_PROTOCOL_MOUSE > HidComposite(&Usb);
HIDBoot<USB_HID_PROTOCOL_KEYBOARD>    HidKeyboard(&Usb);
HIDBoot<USB_HID_PROTOCOL_MOUSE>    HidMouse(&Usb);

KbdRptParser KbdPrs;
MouseRptParser MousePrs;

void setup()
{
  Serial.begin( 9600 );
#if 0 // シリアル接続待ちするので無効化 #if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  Serial.println("Start");

  if (Usb.Init() == -1)
    Serial.println("OSC did not start.");

  delay( 200 );

  HidComposite.SetReportParser(0, &KbdPrs);
  HidComposite.SetReportParser(1, &MousePrs);
  HidKeyboard.SetReportParser(0, &KbdPrs);
  HidMouse.SetReportParser(0, &MousePrs);

  Mouse.begin();
  Keyboard.begin();
}

void loop()
{
  Usb.Task();
}
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3