LoginSignup
4
4

More than 5 years have passed since last update.

Arduino+USBホストシールドでUSBマウスを扱う(続き)

Last updated at Posted at 2019-02-25

前回は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にさらに手を加えました。

USBHIDMouse.ino
#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();
}
hidmouserptparser.h
#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__
hidmouserptparser.cpp
#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マウスと同等につかうことができるようになりました。
他のマウスもレポートを解析すれば同様に使えるようになるでしょう。
今回はただのマウスにしてしまいましたが、キーアサインを変えたり、キーボード入力を行ったりもできます。
私はトラックボールを左手デバイスにしようかと考えています。

4
4
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
4
4