前回はArduino+USBホストシールドにUSBマウスを接続し、マウスから送られるレポートを解析しました。
今回はレポートの情報をもとにArduinoに接続したUSBマウスからの入力でPCを操作できるようにします。
#用意するもの
-
Arduino Leonardo
-
USBホストシールド
-
LOGICOOL M500 マウス
-
USB_Host_Shield_2.0 ライブラリ
ライブラリは下記で入手しました。
https://github.com/felis/USB_Host_Shield_2.0 -
hiduniversal.cpp の 300~400行のコメントアウト(前回の最後の問題点と解決案)
#Arduino の Mouseライブラリの改変
Arduinoには標準でMouseライブラリがあり、ArduinoをUSBマウスと見せかけてPCに入力を送ることができます。
ただしこのライブラリはマウスのX1、X2ボタンとチルトホイールの入力がサポートされていません。
今回はM500マウスのすべてのボタンを使えるようにしたいので、Mouseライブラリを改変します。
改変のやり方はこちらのブログで紹介されていました。
Arduino でチルト付き 5 ボタンマウスを試す
ライブラリの改変するファイルは、WIndowsであれば
C:\Program Files (x86)\Arduino\libraries\Mouse\src
にある Mouse.cpp と Mouse.h です
私は元のファイルに手を加えたくないのでリネームしたMouse2.cppとMouse2.hを用意しライブラリに保存しました。
#USBマウス入力プログラムの作成
今回のArduino用プログラムの本体です。
前回作成した3つのファイルUSBHIDMouse.ino、hidmouserptparser.h、hidmouserptparser.cppにさらに手を加えました。
#include <usbhid.h>
#include <hiduniversal.h>
//#include <usbhub.h>
#include <Mouse2.h>
// Satisfy IDE, which only needs to see the include statment in the ino.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>
#include "hidmouserptparser.h"
USB Usb;
//USBHub Hub(&Usb);
HIDUniversal Hid(&Usb);
HIDMouseEvents MouEvents;
HIDMouseReportParser Mou(&MouEvents);
void setup() {
Mouse.begin();
Serial.begin(9600);
#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);
if (!Hid.SetReportParser(0, &Mou))
ErrorMessage<uint8_t > (PSTR("SetReportParser"), 1);
}
void loop() {
Usb.Task();
}
#if !defined(__HIDMOUSERPTPARSER_H__)
#define __HIDMOUSERPTPARSER_H__
#include <usbhid.h>
class HIDMouseEvents {
public:
virtual void OnButtonDn(uint8_t but_id);
virtual void OnButtonUp(uint8_t but_id);
virtual void Move(int16_t xm, int16_t ym, int8_t scr, int8_t tilt);
};
#define RPT_LEN 8
class HIDMouseReportParser : public HIDReportParser {
HIDMouseEvents *mouEvents;
uint8_t oldButtons;
public:
HIDMouseReportParser(HIDMouseEvents *evt);
virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};
#endif // __HIDMOUSERPTPARSER_H__
#include <Mouse2.h>
#include "hidmouserptparser.h"
HIDMouseReportParser::HIDMouseReportParser(HIDMouseEvents *evt) :
mouEvents(evt),
oldButtons(0){}
void HIDMouseReportParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
/*
for (uint8_t i = 0; i < RPT_LEN; i++) {
Serial.print(buf[i], BIN);
Serial.print(", ");
}
Serial.println("");
//*/
//but_id 1,2,4,8,16
for (uint8_t but_id = 1; but_id <= 16; but_id <<= 1){
if (buf[0] & but_id) {
if (!(oldButtons & but_id)) {
mouEvents->OnButtonDn(but_id);
}
}else {
if (oldButtons & but_id) {
mouEvents->OnButtonUp(but_id);
}
}
}
oldButtons = buf[0];
int16_t xm = buf[2];
xm <<= 8;
xm |= buf[1];
xm <<= 4;
xm >>= 4;
if (xm > 127) {
xm = 127;
}else if (xm < -127){
xm = -127;
}
int16_t ym = buf[3];
ym <<= 8;
ym |= buf[2];
ym >>= 4;
if (ym > 127) {
ym = 127;
}else if (ym < -127){
ym = -127;
}
int8_t scr = buf[4];
int8_t tilt = buf[5];
if ((xm != 0) || (ym != 0) || (scr != 0) || (tilt != 0)) {
mouEvents->Move(xm, ym, scr, tilt);
}
}
void HIDMouseEvents::OnButtonDn(uint8_t but_id) {
/*
Serial.print(but_id);
Serial.println(":Dn");
//*/
Mouse.press(but_id);
}
void HIDMouseEvents::OnButtonUp(uint8_t but_id) {
/*
Serial.print(but_id);
Serial.println(":Up");
//*/
Mouse.release(but_id);
}
void HIDMouseEvents::Move(int16_t xm, int16_t ym, int8_t scr, int8_t tilt) {
/*
Serial.print("move:");
Serial.print(xm);
Serial.print(",");
Serial.print(ym);
Serial.print(",");
Serial.print(scr);
Serial.print(",");
Serial.println(tilt);
//*/
Mouse.move(xm, ym, scr, tilt);
}
hidmouserptparser.cppのParseメソッドにマウスのレポートを読み、マウスライブラリの入力へ切り替える部分が書かれています。
M500マウスのレポートの1バイト目とArduinoのマウスライブラリのInput Reportは同じ構造なので、レポートのフラグのたったビットの数値をそのままMouse.pressに送ることが可能でした。
マウスのXY移動量はM500では符号付12ビットでしたが、マウスライブラリは符号付8ビットなので、移動量が127を超えたときは127に補正するようにしました。
#終わり
これでArduinoにつないだM500マウスを普通のUSBマウスと同等につかうことができるようになりました。
他のマウスもレポートを解析すれば同様に使えるようになるでしょう。
今回はただのマウスにしてしまいましたが、キーアサインを変えたり、キーボード入力を行ったりもできます。
私はトラックボールを左手デバイスにしようかと考えています。