Teensy 打造手机游戏辅助器
最近我在玩一个安卓的射击游戏。 偶然发现可以使用鼠标进行游戏,但是使用鼠标有下面两条:1. 鼠标右键会导致游戏闪退2. 不同武器射击方式有差别。例如:狙击枪长按鼠标左键打开瞄准镜,速成“开镜”,瞄准目标后抬起左键立即射击;机关枪最好用三连发点射。为此,我使用 Teensy 搭配 USB Host Mini 做了一个转接器,将USB鼠标操作解析过滤之后再送入手机。
我使用的是微软 InteliMouse Optical1.1A 鼠标。这款鼠标有5个按键,使用到的只有左键和右键。在设计上,可以其中前者用来发射,后者用来切换不同的射击模式。
首先需要解析这个鼠标的数据。使用之前介绍过很多次的 USBlyzer进行分析,第一步查看描述符了解鼠标数据的格式:Microsoft USBIntelliMouse Optical
Connection StatusDevice connected
Current Configuration1
SpeedLow (1.5 Mbit/s)
Device Address5
Number Of Open Pipes1
Device Descriptor Microsoft5-Button Mouse with IntelliEye(TM)
OffsetFieldSizeValueDescription
0bLength112h
1bDescriptorType101hDevice
2bcdUSB20110hUSB Spec 1.1
4bDeviceClass100hClass info in Ifc Descriptors
5bDeviceSubClass100h
6bDeviceProtocol100h
7bMaxPacketSize0108h8 bytes
8idVendor2045Eh
10idProduct20039h
12bcdDevice20300h3.00
14iManufacturer101h"Microsoft"
15iProduct103h"Microsoft 5-Button Mousewith IntelliEye(TM)"
16iSerialNumber100h
17bNumConfigurations101h
Configuration Descriptor1 Bus Powered, 100 mA
OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType102hConfiguration
2wTotalLength20022h
4bNumInterfaces101h
5bConfigurationValue101h
6iConfiguration100h
7bmAttributes1A0hBus Powered, Remote Wakeup
4..0: Reserved...00000
5: Remote Wakeup..1..... Yes
6: Self Powered.0...... No, Bus Powered
7: Reserved (set to one)
(bus-powered for 1.0)1.......
8bMaxPower132h100 mA
Interface Descriptor 0/0 HID,1 Endpoint
OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType104hInterface
2bInterfaceNumber100h
3bAlternateSetting100h
4bNumEndpoints101h
5bInterfaceClass103hHID
6bInterfaceSubClass101hBoot Interface
7bInterfaceProtocol102hMouse
8iInterface100h
HID Descriptor
OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType121hHID
2bcdHID20110h1.10
4bCountryCode100h
5bNumDescriptors101h
6bDescriptorType122hReport
7wDescriptorLength20048h72 bytes
Endpoint Descriptor 81 1In, Interrupt, 10 ms
OffsetFieldSizeValueDescription
0bLength107h
1bDescriptorType105hEndpoint
2bEndpointAddress181h1 In
3bmAttributes103hInterrupt
1..0: Transfer Type......11 Interrupt
7..2: Reserved000000..
4wMaxPacketSize20004h4 bytes
6bInterval10Ah10 ms
Interface 0 HID ReportDescriptor Mouse
Item Tag (Value)Raw Data
Usage Page (Generic Desktop)05 01
Usage (Mouse)09 02
Collection (Application)A1 01
Usage(Pointer)09 01
Collection(Physical)A1 00
UsagePage (Button)05 09
UsageMinimum (Button 1)19 01
UsageMaximum (Button 5)29 05
LogicalMinimum (0)15 00
LogicalMaximum (1)25 01
ReportSize (1)75 01
ReportCount (5)95 05
Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit)81 02
ReportSize (3)75 03
ReportCount (1)95 01
Input (Cnst,Ary,Abs)81 01
UsagePage (Generic Desktop)05 01
Usage(X)09 30
Usage(Y)09 31
Usage(Wheel)09 38
LogicalMinimum (-127)15 81
LogicalMaximum (127)25 7F
ReportSize (8)75 08
ReportCount (3)95 03
Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit)81 06
End CollectionC0
Usage Page05 FF
Usage09 02
LogicalMinimum (0)15 00
LogicalMaximum (1)25 01
ReportSize (1)75 01
ReportCount (1)95 01
Feature (Data,Var,Abs,NWrp,Lin,NPrf,NNul,NVol,Bit)B1 22
ReportSize (7)75 07
ReportCount (1)95 01
Feature (Cnst,Ary,Abs,NWrp,Lin,Pref,NNul,NVol,Bit)B1 01
End CollectionC0
This report was generated by USBlyzer关注点在 HID Descriptor上。同时,移动鼠标按下按键结合抓取数据的功能:
最终确定鼠标发出来的数据格式如下:
偏移长度(Byte)功能
01Report ID
11按键信息BIT0:左键BIT1:右键BIT2:中键BIT3:左下键BIT4:右下键
21X 方向移动距离,范围 -127~+127
41Y 方向移动距离,范围 -127~+127
61滚轮,范围 -127~+127
有了上面的数据格式,配合USB Host Shield可以得知鼠标的操作,比如:移动多少距离,是否按下鼠标左键。和之前的解析方式类似,这次选择的是USBHost Mini,它和之前使用的USBHOST Shield都是同一个作者设计的,使用相同的核心芯片(MAX3421E),因此使用的库和调用方法是相同的。具体的 rework 方法可以在之前的帖子中看到。
相比键盘鼠标对于时间非常敏感,高速的读取和解析是必要的,为了保证游戏体验这次使用 Teensy3.2。这是一款Arduino 兼容板,和Arduino相比,它速度更快(Teensy3.2,72Mhz,64K 内存),同时和Arduino代码100%兼容,无需专门修改 Arduino 代码即可烧写运行。 此外,Teensy3.2 引脚都是3.3V 的,因此,可以直接将它 USB Host 相连。为了方便使用笔者又设计了一块不包含任何元件的转接板,使用时只要将Teensy和 Shield一同插到转接板即可工作。
拿到PCB之后焊接排插,将Teensy 3.2 和 USBHOST MINI 插接在一起即可工作: 此外,使用 D2/D3/D4 Pin连接3个LED,分别表示:正常射击,三连发射击和模拟长按射击,不同模式使用鼠标右键顺序切换。正常射击模式
完整代码
msmouse.ino#include <SPI.h>
#include "msParser.h"
USB Usb;
MSPARSER msparser(&Usb);
bool printTilt;
void setup() {
Serial.begin(115200);
//3个状态指示灯用来指示状态
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
if (Usb.Init() == -1) {
Serial.print(F("\r\nOSC did not start"));
while (1); // Halt
}
Serial.println(F("\r\nSteelSeries SRW-S1 Steering Wheel example started"));
msparser.SetMode(0);
}
void loop() {
Usb.Task();
if (msparser.connected()) {
if (printTilt) { // Show tilt angle using the LEDs
}
}
}
msParser.h
#ifndef __srws1_h__
#define __srws1_h__
#include <hiduniversal.h>
//鼠标的PID和VID
#define STEELSERIES_VID 0x045E
#define STEELSERIES_SRWS1_PID 0x0039
class MSPARSER : public HIDUniversal {
public:
MSPARSER(USB*p) : HIDUniversal(p) {};
voidSetMode(int Current);
intGetMode();
boolconnected() {
returnHIDUniversal::isReady() && HIDUniversal::VID == STEELSERIES_VID&& HIDUniversal::PID == STEELSERIES_SRWS1_PID;
};
private:
voidParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); // Calledby the HIDUniversal library
uint8_tOnInitSuccessful() { // Called by the HIDUniversal library on success
if(HIDUniversal::VID != STEELSERIES_VID || HIDUniversal::PID !=STEELSERIES_SRWS1_PID) // Make sure the right device is actually connected
return 1;
return0;
};
int Mode=0;
};
#endif
这是完成主要工作的文件msParser.cpp#include "msParser.h"
#include <Mouse.h>
//设置当前的射击模式
void MSPARSER::SetMode(int Current)
{
digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
Serial.print("Current");
Serial.println(Current);
switch (Current) {
case 0: //Mode 0 正常模式,鼠标信息只是Bypass
digitalWrite(2,HIGH);
break;
case 1: //Mode 1 速射模式,模拟快速按键
digitalWrite(3,HIGH);
break;
case 2: //Mode 2 狙击枪模式,模拟按下一段时间设计
digitalWrite(4,HIGH);
break;
default:
break;
}
Mode=Current;
}
//返回当前的射击模式
int MSPARSER::GetMode()
{
return Mode;
}
//解析USB鼠标的数据
void MSPARSER::ParseHIDData(USBHID *hid, bool is_rpt_id,uint8_t len, uint8_t *buf) {
if(HIDUniversal::VID != STEELSERIES_VID || HIDUniversal::PID !=STEELSERIES_SRWS1_PID)
return;
if (len&& buf){
//输出收到的数据
for(uint8_t i = 0; i < len; i++) {
if (buf<0x10) {Serial.print("0");}
Serial.print(buf,HEX);
Serial.print(" ");
}
Serial.println();
//如果鼠标移动了,那么也让模拟鼠标相同的移动
if((buf!=0)||(buf!=0)) {
Mouse.move(buf,buf);
}
if(buf&1!=0) {//如果按下鼠标左键
if (GetMode()==0) { //正产模式
Mouse.press(MOUSE_LEFT);
}
if(GetMode()==1) { //速射模式,打三发
Mouse.press(MOUSE_LEFT);
delay(10);
Mouse.release(MOUSE_LEFT);
delay(10);
Mouse.press(MOUSE_LEFT);
delay(10);
Mouse.release(MOUSE_LEFT);
delay(10);
Mouse.press(MOUSE_LEFT);
delay(10);
Mouse.release(MOUSE_LEFT);
}
if(GetMode()==2) { //狙击模式,长按然后马上发射
Mouse.press(MOUSE_LEFT);
delay(500);
Mouse.release(MOUSE_LEFT);
}
}
elseif (Mouse.isPressed(MOUSE_LEFT)){
Mouse.release(MOUSE_LEFT);
}
if((buf&2)!=0) {
Serial.println("Fired");
SetMode((GetMode()+1)%3);
}
}
}
最后,将鼠标接在USB Host Mini 上,然后再将 Teensy 连接到手机上就可以快乐的进行游戏了。除了打得准,我相信鼠标能让你的手更轻松………游戏视频可以在 https://zhuanlan.zhihu.com/p/43195205 看到 https://blog.csdn.net/skdev/article/details/48528293 我之前也做过类似的 不过是用stm32实现的 在stm32cubeMX构建的时候改一下描述符就可以了 只是不能直接插鼠标啊 看来hostusb这块板用处大大哟 哈哈 游戏是死亡之屋吧 我在wii上和ps3上都玩过 “它速度更快(Teensy3.2,72Mhz,64K 内存),同时和Arduino代码100%兼容,无需专门修改 Arduino 代码即可烧写运行。”能具体讲讲吗,teensy家族只有这款100%兼容arduino代码吗?其他的像teensy2.0 teensy2.0++呢?这么问是因为这两块现在比较偏移。 我还想知道teensy可以干哪些arduino不可以干的事,感觉arduino家族里也有可以直接被电脑认到的,像leonardo promicro 甚至用nicohoodHID也可以,不懂为什么teensy能入你的法眼哈,有空的话希望听你聊聊 之前看过Arduino for Musicians: A Complete Guide to Arduino and Teensy Microcontrollers这本,但里面只是很少一部分提到了teensy,不知道你有没有什么资料可以推荐 impking 发表于 2019-2-23 22:28
之前看过Arduino for Musicians: A Complete Guide to Arduino and Teensy Microcontrollers这本,但里面只 ...
直接看 teensy 制造商网站
页:
[1]