🐭

M5AtomS3LiteとジョイスティックユニットでUSB HIDポインティングデバイスを作る

2024/08/28に公開

はじめに

  • M5AtomS3LiteM5Stack用ジョイスティックユニットを使用してポインティングデバイスを製作します。
  • PCで動画等を楽な姿勢で見ていると、次の動画を選択するときにマウスに手を伸ばすのも億劫、マウスは地面が必要なのでジョイスティックが良いのではないかと思ったのがきっかけです。
  • 選択したデバイスは、何かに使えそうでとりあえず買ったけど使っていなかったもので、特に大きな選択理由はありません。せっかく買ったのに使ってなかったから使ってみる、それだけです。

ファーストタッチ

公式のドキュメントを読みながら、ジョイスティックユニットから値をもらってみましょう。
AtomS3LiteとユニットはGROVE接続です。

UnitJoyStickFirstTouch.ino
#include <Wire.h>
#include <M5Unified.h>

#define JOY_ADDR 0x52 //ジョイスティックユニットのI2Cアドレス

void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);

  Serial.begin(115200);
  delay(1000);
  Serial.println("start");

  Wire.begin(M5.Ex_I2C.getSDA(), M5.Ex_I2C.getSCL()/*, 400000*/);
}

void loop() {
  delay(500);

  static uint8_t x_data,y_data,button_data;
  Wire.requestFrom(JOY_ADDR, 3); 
  if (Wire.available()) {
    x_data = Wire.read();
    y_data = Wire.read();
    button_data = Wire.read();
    Serial.printf("x:%d y:%d button:%d\n", x_data, y_data, button_data);
  }
}

I2C通信で、0.5秒に1回、ユニットから値を取得します。
GROVEコネクタを下にして持ったとき、
xは、左に倒すと255、右に倒すと0、ニュートラルで約128でした。
yは、下に倒すと255、上に倒すと0、ニュートラルで約128でした。
buttonは、押していないとき0、押しているとき1でした。

値を変換

ポインティングデバイスとして使用する場合、ニュートラルが0でプラスマイナスに振れる方が使いやすいように思いますので、ユニットから取得した値を変換します。
0~255の取得値から128引いて-128~127とすればよいでしょう。
ついでに取得処理を関数化します。

UnitJoyStickFirstTouch_kai.ino
#include <Wire.h>
#include <M5Unified.h>

#define JOY_ADDR 0x52 //ジョイスティックユニットのI2Cアドレス
#define JOY_DATA_NUM 3 //ジョイスティックユニットのデータ数

void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);

  Serial.begin(115200);
  delay(1000);
  Serial.println("start");

  Wire.begin(M5.Ex_I2C.getSDA(), M5.Ex_I2C.getSCL());
}

void loop() {
  delay(500);

  static int8_t joy_x, joy_y
  static uint8_t joy_button;
  Wire.requestFrom(JOY_ADDR, JOY_DATA_NUM); 
  if (getJoyStick(&joy_x, &joy_y, &joy_button)) {
    Serial.printf("x:%d y:%d button:%d\n", joy_x, joy_y, joy_button);
  }
}

bool getJoyStick(int8_t* x, int8_t* y, uint8_t button){
  bool ret;
  Wire.requestFrom(JOY_ADDR, JOY_DATA_NUM);
  ret = Wire.available(); 
  if (ret) {
    *x = Wire.read() - 128;
    *y = Wire.read() - 128;
    *button = Wire.read();
  }
  return ret;
}

いざポインティングデバイスへ

AtomS3用スケッチ例のUSB→ButtonMouseControlを参考に,ユニットからの取得値で動くマウスを作ります。

UsbJoyStickPointingDevice.ino
#include <Wire.h>
#include <M5Unified.h>
#include "USB.h"
#include "USBHIDMouse.h"

#define JOY_ADDR 0x52 //ジョイスティックユニットのI2Cアドレス
#define JOY_DATA_NUM 3 //ジョイスティックユニットのデータ数

USBHIDMouse Mouse;

void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);

  Wire.begin(M5.Ex_I2C.getSDA(), M5.Ex_I2C.getSCL());

  Mouse.begin();
  USB.begin();
}

void loop() {
  const uint32_t loop_cycle = 5;
  const int8_t joy_step = 16;
  const int8_t mouse_step = 1;

  delay(loop_cycle);

  static int8_t joy_x, joy_y;
  static uint8_t joy_button;
  Wire.requestFrom(JOY_ADDR, JOY_DATA_NUM); 
  if (getJoyStick(&joy_x, &joy_y, &joy_button)) {
    int8_t move_x = -(joy_x / joy_step) * mouse_step; //x方向の動きがマウスと反対だったので符号反転
    int8_t move_y = (joy_y / joy_step) * mouse_step;
    Mouse.move(move_x, move_y);

    if(joy_button == 1) {
      if(!Mouse.isPressed(MOUSE_LEFT)) {
        Mouse.press(MOUSE_LEFT);
      }
    }
    else {
      if(Mouse.isPressed(MOUSE_LEFT)) {
        Mouse.release(MOUSE_LEFT);
      }
    }
  }
}

bool getJoyStick(int8_t* x, int8_t* y, uint8_t* button){
  bool ret;
  Wire.requestFrom(JOY_ADDR, JOY_DATA_NUM);
  ret = Wire.available(); 
  if (ret) {
    *x = Wire.read() - 128;
    *y = Wire.read() - 128;
    *button = Wire.read();
  }
  return ret;
}

USB.hUSBHIDMouse.hをインクルードして、USBHIDMouseのインスタンスを使用します。
beginで開始、moveにxyの移動量を渡してマウスカーソルを移動します。
pressでクリックしたことになり、releaseでクリックを離したことになり、現在、押した状態か離した状態かはisPressedで知ることができます。
コンパイル時のポイントとして、USB-OTGという仕組みを利用するので、ツール→USB Mode:"USB-OTG(TinyUSB)"を選択する必要があります。

余談

  • AtomS3のCPUクロックを最高の240MHzで使用すると結構熱を持つので、クロックを可能な限り落としたかったのですが、10MHz,20MHzではPCがAtomS3をUSBデバイスとして認識してくれませんでした。
    40MHzは必要なようです。
  • 一度、USBデバイスにしてしまうと、PCのAtomS3に対する認識が変わり、プログラムを書き込めなくなります。AtomS3のサイドのボタンを、緑LEDが光るまで(2秒程度)長押しすると、再び書き込めるようになります。

感想

できましたやったー

Discussion