キーボード #2 Advent Calendar 2020 に参加しています。

3日目 Arduino

やりたいこと

Arduino用のキーボードを作ってみる。

正しくは、Arduino で利用できるキーボードをQMKで作る。

USB(HID)のキーボードは、 su120キットのおかげで作れるようになったので、それを改造する。

special thanks to @e3w2q さん

完成図

Arduino 側は、ATOM Matrix で作成。今後もテスターとして利用できるようにする。

通信はをシリアル接続(8N1)で、ATOM Matrixには押されたキーの位置をLEDで表示する。

ATOM Matrix は、ESP32 Pico に 5x5 のフルカラーLEDを実装したかわいいやつ。

I/F仕様

シリアル通信の仕様(Network層)

  • 電圧 5V
  • bitrate ... QMXの最低である20Kbps
  • bit数 ... 8bits
  • parity ... なし
  • stop bit ... 1bit

キーのMatrixデータ仕様 (Transport層)

1回の送信で7bytes。

bytebits値説明
11-50 or 1キーボード1行目の押し込み状態
21-50 or 1キーボード2行目の押し込み状態
31-50 or 1キーボード3行目の押し込み状態
41-50 or 1
51-80-127左側のロータリーエンコーダーの値
61-80-127右側のロータリーエンコーダーの値
71-80xffデータの最後EOF

ex

押したキーMatrixデータ
なし00:00:00:00:00:00:ff
1行目1列80:00:00:00:00:00:ff
2行目1列00:80:00:00:00:00:ff
2行目1列と2列00:c0:00:00:00:00:ff
2列目全部40:40:40:40:00:00:ff
3行目のキー5個全部00:00:f8:00:00:00:ff

ATOM Matrix でテスターを作成

QMK の改造をする前に、正しい動作を確認するためのテスターを作る。

シリアル通信部分

だれでも簡単。Serial のread でデータを読める。

例えば、こんな感じ。

c
void setup() {
...

  // Serial1 の設定
  Serial1.begin(20000, SERIAL_8N1, PIN_RX, PIN_TX);
}

void loop() {
  if (Serial1.available()) {
    int inByte = Serial1.read()
    if (inByte == 0xff) {
       // stop data

テスターの実装

テスター仕様は、ATOM Matrix の5x5のLEDのうち、上位4行でキーボードの押した位置を点灯。

5行目の1,2カラムは、左のエンコーダ値。 5行目の3,4カラムは、右のエンコーダ値。 5行目の5カラムは、serialでデータを受け取ると、点滅する。

keyboard_checker.c · Gistc
#include "M5Atom.h"

//#define PIN_RX 22   // G0
#define PIN_RX 32   // GROVE
#define PIN_TX 26

#define PIXEL_PER_ROW 5

// Matrix Display Buffer
uint8_t DisBuff[2 + 5 * 5 * 3];

#define ORANGE              0xe07020
#define GRAY                0x686868
#define RED                 0x701212
#define BLUE                0x000070
#define GREEN               0x00ff00

uint32_t DisColormap[] = {
    // keyboard color map
    GRAY,   ORANGE, GRAY,   ORANGE, GRAY,
    ORANGE, GRAY,   ORANGE, GRAY,   ORANGE,
    GRAY,   ORANGE, GRAY,   ORANGE, GRAY,
    GRAY,   GRAY,   GRAY,   GRAY,   GRAY
};

void setup() {

    // serial console init
    Serial.begin(115200);

    // ATOM Matrix init
    M5.begin(true, false, true);
    delay(10);

    Serial1.begin(20000, SERIAL_8N1, PIN_RX, PIN_TX); // EXT_IO
    Serial.println("start");

    M5.dis.drawpix(24, GRAY);
}

uint8_t SerialBuff[128];
uint16_t SeirialBuffSize = 0;

void draw_matrix() {

    // keycode
    //     1    2    3    4    5 : 1byte(1-5)
    //     6    7    8    9   10 : 1byes(6-8) 2bytes(1-2)
    //    11   12   13   14   15 : 2byte(3-7)
    //         17        19   20 : 3byte(1, 3-4)
    // encoder
    // LEFT  ENCODE VALUE(0-127) : 5byte(1-8)
    // RIGHT ENCODE VALUE(0-127) : 6byte(1-8)
    
    DisBuff[0] = 0x05;
    DisBuff[1] = 0x05;

    // keyboard matrix
    for (int row = 0; row < 4; row++) {
        uint8_t val = SerialBuff[row];
        for (int col = 0; col < PIXEL_PER_ROW; col++) {
            uint32_t color = 0;

            uint32_t pixel = row*PIXEL_PER_ROW + col;
            uint32_t buf_offset = pixel * 3;
            
            if (val & 1<<col) {
                color = DisColormap[pixel];
            }

            uint8_t red = (color &0xff0000)>>16;
              uint8_t green = (color &0xff00)>>8;
            uint8_t blue = (color &0xff);

            DisBuff[2 + buf_offset + 0] = red;
            DisBuff[2 + buf_offset + 1] = green;
            DisBuff[2 + buf_offset + 2] = blue;
            
            // M5.dis.drawpix(row*5+col, 0x707070);
        }
    }

    // encoder
    for (uint8_t encoder_hand = 0; encoder_hand < 2; encoder_hand++) {
          
        uint8_t pixel_row = 4;
        uint8_t data_offset = 4/*enconder byte*/ + encoder_hand;
        uint8_t encoder_val = SerialBuff[data_offset];
     
        uint8_t color[2];
        color[0] = ((encoder_val & 0x0f)<<1) | 0x01;         // val < 128
        color[1] = (((encoder_val & 0xf0)>>4)<<1) | 0x01;    // val >= 128
        
        for (uint8_t col = 0; col < 2; col++) {

            uint32_t pixel = pixel_row*PIXEL_PER_ROW + encoder_hand * 2 + col;
            uint32_t buf_offset = pixel * 3;

            uint8_t red = 0;
            uint8_t green = 0;
            uint8_t blue = 0;
            
            if (encoder_hand == 0) {
                // left hand
                green = color[col]<<3;   
            } else {
                // right hand
                red = color[col]<<3;
            }

            DisBuff[2 + buf_offset + 0] = red;
            DisBuff[2 + buf_offset + 1] = green;
            DisBuff[2 + buf_offset + 2] = blue;

            // DisBuff[2 + buf_offset + 0] = 0x70;
        }
    }

    M5.dis.displaybuff(DisBuff);
}

void loop() {

  if (Serial1.available()) {
      int inByte = Serial1.read();
      M5.dis.drawpix(24, 0x000070);
      if (inByte == 0xff) {
          // stop data
          for (int i =0; i < SeirialBuffSize; i++) {
              Serial.printf("%02x", SerialBuff[i]);
          }
          Serial.println("");
          draw_matrix();
          SeirialBuffSize = 0;

      } else {

          // matrics data
          SerialBuff[SeirialBuffSize] = inByte;
          if (SeirialBuffSize++ >= sizeof(SerialBuff)) {
              Serial.println("error"); 
              SeirialBuffSize = 0;
          }
    }

  } else {
      delay(50);
  }

  M5.update();
}

全部のキーを押すとこんな表示になる。

続き