Arduino Pro Micro + ADNS-5050によるトラックボールのコントロール

大分時間が空いてしまいまして、すみません。本業が忙しかったもので。
 
さて、今回は、Arduino Pro Micro  +  Arduino IDEを使用した場合のADNS-5050の使い方について簡単に触れてみたい。つまり、QMK Firmware でなく、Arduino のライブラリを使ってADNS-5050というマウスセンサをPro Microでコントロールして、USBマウスを作る方法だ。
 
Pro MicroとADNS-5050の物理的な接続はSPIという規格のインタフェースを用いて行う。(例えば、https://www.analog.com/jp/analog-dialogue/articles/introduction-to-spi-interface.html  )ちょっと難しそうだけれど、幸い、 https://github.com/okhiroyuki/ADNS5050 とのライブラリを見つけた。これを使わせていただくと、そんなに難しくはなかった。
また、Arduinoのライブラリには、USBマウスのためのMouse.hなどというものがあるので、単にUSBトラックボールを作るだけなら、比較的簡単にできることが分かった。また、多少苦労したものの、Fusion 360 + 3Dプリンタでボールカップを作成し、Mouse.hを使ってマウスカーソルを動かすことができた。

さて、具体的なやり方を見ていこう。
まず、ハードウェア。ビットトレードワンの通販ページからデータシート https://btoshop.jp/wp-content/uploads/sites/3/2019/04/ADNS-5050.pdf を入手する。Fig. 8 にサンプルの回路図があるので、それに従って、Pro Micro と ADNS-5050を接続する。基本的に、ADNS-5050にLEDとコンデンサを繋ぎ、Pro MicroとSCLK, SDIO, NCSを接続すればOK。回路としてはそんな難しくはない。(実装は面倒かとは思う。ADNS-5050はピン間が2mmなので、ユニバーサル基板にはうまく収まらない。私は、100円ショップで買ったマウスを分解してプリント基板を加工して流用した。)
ソフトウェアは、
 
1. https://github.com/okhiroyuki/ADNS5050 のCode -> Download ZIP から、ライブラリをダウンロード。
2. Arduino IDE のスケッチ->ライブラリをインクルード->.ZIP形式のライブラリをインストール、でライブラリをインストール。
3. サンプルプログラムは以下のとおり。10番ピンがローだとUSB HIDとして動き、ロー以外ならI2Cで動作する。I2CのピンアサインはSDIO: 4, SCLK: 5, NCS: 6とした。

/* ADNS-5050 Controller

 for USB HID and QMK Firmware (I2C)
 Default I2C Address: 0x10(7bit) 0x20(8bit, QMK Firmware)
*/

#include <Wire.h>
#include <Mouse.h>

// ADNS-5050 ライブラリのインクルードが必要です。
// https://github.com/okhiroyuki/ADNS5050
#include <adns5050.h>

#define USB_HID_MODE_PIN 10 // LOW: USB_HID is active. Not LOW: i2c is available.
#define I2C_SLAVE_2_ADDRESS 0x10 // (7 bit version of I2C addresses)
#define LED_PIN  13

// The register map of ADNS5050
#define MOTION          0x02  //R   0x00
#define DELTA_X         0x03  //R   any
#define DELTA_Y         0x04  //R   any

// Mouse sensor object
ADNS5050 mouse(4, 5, 6); // (SDIO, SCLK, NCS)

unsigned long last_time; 
char mouse_dx = 0;
char mouse_dy = 0;

// I2C応答イベント
void receiveEvent(int s) {
  char c;

  while (Wire.available()) 
  {
    c = Wire.read(); // 1バイト(文字)読み出し
    if (c == 'a') digitalWrite(13, HIGH); // LED on
    if (c == 'z') digitalWrite(13, LOW);  // LED off
#ifdef DEBUG
    Serial.println(c, HEX);
#endif
  }
}

// I2C要求イベント
void requestEvent() {
  if (digitalRead(USB_HID_MODE_PIN) == HIGH) { // HIGH: I2C is active. USB HID is inactive.
#ifndef DEBUG
    Wire.write(-mouse_dx);
    Wire.write(-mouse_dy);
    Wire.write(0); // ハードウェアスイッチ用のデータフィールド(予約。現在未使用。)
    Wire.write(0); // タッチスイッチ用のデータフィールド(予約。現在未使用。)
    #endif
#ifdef DEBUG
    Serial.print(mouse_dx);
    Serial.print(", ");
    Serial.print(mouse_dy);
    Serial.print("; ");
#endif
    mouse_dx = mouse_dy = 0;
  }
}

void setup() {
  // シリアル通信のセットアップ
  Serial.begin(9600);

  // I2Cのセットアップ
  Wire.begin(I2C_SLAVE_2_ADDRESS);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
  pinMode(USB_HID_MODE_PIN, INPUT_PULLUP);

  // マウスセンサのセットアップ
  mouse.begin();
  delay(100);
  mouse.sync();
  if (digitalRead(USB_HID_MODE_PIN) == LOW) {
    Mouse.begin();
  }
  last_time = millis();
}

void loop() {
  unsigned long curr_time = 0;
  unsigned long interval = 10;
  byte prev_button_state, prev_touch_button_state;

  curr_time = millis();

  // read the mouse sensor.
  if ((abs(curr_time - last_time) > interval) && (mouse.read(MOTION) != 0) ) {
    mouse_dx = mouse_dx + (char)mouse.read(DELTA_X);
    mouse_dy = mouse_dy + (char)mouse.read(DELTA_Y);
    last_time = curr_time;
  }

  if (digitalRead(USB_HID_MODE_PIN) == LOW) { // USB HID is active
    Mouse.move(mouse_dx, mouse_dy);
    mouse_dx = mouse_dy = 0;
  }
}

これで、マウスとしては動作すると思う。I2CについてはQMK Firmware側の対応もちょっと面倒なので、次回にでも。