macOS上的应用软件如何接收诺为翻页笔的API通道数据
诺为翻页笔对电脑来说,就是一个和鼠标、键盘一样的HID(Human Interface Device)设备。其所用的技术,和无线键盘、无线鼠标是一样的。
目前,如果在电脑上想获得翻页笔的按键信息,或者说想用翻页笔来控制电脑上的软件,有两种方法。一种是设置快捷键来获得翻页笔的按键信息,另一种是使用翻页笔的APP通道来获得数据。本文讲述的是macOS上的应用软件如何接收诺为翻页笔的APP通道数据,主要是程序实现。
本文所述的短按是按键抬起时触发,只要没有超过长按计时器的按键抬起,都是短按。本文所述的长按是持续按住按键超过长按计时器时触发。长按计时器一般为1秒,不同的产品可能会有差异。
一、诺为设备的设置和信息
1. 打开Norwii Presenter软件,【功能自定义】页面,在按键的短按或者长按的自定义界面,选择“特殊键” – “APP通道”,然后输入APP通道的值(这个值是十进制的,范围是1-255),点击“确定”和“保存”。
2. 在对码页面,按下已经定义为“APP通道”数据的按键,右侧的窗口中出现“APP通道”的键值,说明APP通道的自定义设置成功。
3. 在【关于】页面,获取诺为翻页笔的VID和PID。这两个值在USB HID通信中会使用到。页面显示的这两个值是16进制,使用的时候前面加上0x,下图中VID就是0x3243,PID就是 0x0341。不同的产品,VID相同,PID不同。
二、USB HID通信
1. Frameworks:IOKit.framework
2. 导入头文件
#import <IOKit/hid/IOHIDLib.h>
3. 初始化IOHIDManager
IOHIDManagerRef managerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
4. 进行配对设置,可以过滤其他USB设备
1) 无配对设备
IOHIDManagerSetDeviceMatching(managerRef, NULL);
2)单类设备配对(其中“pid”和“vid”分别替换为Presenter软件中查到的值)
NSMutableDictionary* dict= [NSMutableDictionary dictionary];
[dict setValue:[NSNumber numberWithLong:pid] forKey:[NSString stringWithCString:kIOHIDProductIDKey encoding:NSUTF8StringEncoding]];
[dict setValue:[NSNumber numberWithLong:vid] forKey:[NSString stringWithCString:kIOHIDVendorIDKey encoding:NSUTF8StringEncoding]];
IOHIDManagerSetDeviceMatching(managerRef, (__bridge CFMutableDictionaryRef)dict);
5. 注册插拔设备的callback
IOHIDManagerRegisterDeviceMatchingCallback(managerRef, &HandleDeviceMatchingCallback, NULL);
IOHIDManagerRegisterDeviceRemovalCallback(managerRef, &HandleDeviceRemovalCallback, NULL);
6. 加入RunLoop
IOHIDManagerScheduleWithRunLoop(managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
7. 打开IOHIDManager
IOReturn IOReturn = IOHIDManagerOpen(managerRef, kIOHIDOptionsTypeNone);
if(kIOReturnSuccess != IOReturn) puts("IOHIDManagerOpen failed.");
8. 实现插拔callback
void Handle_DeviceMatchingCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
CFTypeRef ref;
int32_t nVendorID, nProductID;
ref = IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDKey));
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &nVendorID);
}
ref = IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductIDKey));
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &nProductID);
}
NSLog(@"insert hid, vid is:%d, pid is:%d",nVendorID, nProductID);
CFRelease(ref);
}
void Handle_DeviceRemovalCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
CFTypeRef ref;
int32_t nVendorID, nProductID;
ref = IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDKey));
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &nVendorID);
}
ref = IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductIDKey));
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &nProductID);
}
NSLog(@"remove hid, vid is:%d, pid is:%d",nVendorID, nProductID);
CFRelease(ref);
}
9. 插入设备获取到IOHIDDeviceRef inIOHIDDeviceRef后,打开IOHIDDeviceRef
IOReturn ret = IOHIDDeviceOpen(inIOHIDDeviceRef,options);
if (ret != kIOReturnSuccess)
{
printf("Open device failed!\n");return;
}
10. 注册接收数据方法,即可接收数据
unsigned char InputBuffer[64];
IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), MyInputCallback, NULL);
11. 接收数据callback,当操作定义为“APP通道”数据的按键会收到设备发送来的数据
int appChannel = 0;
void MyInputCallback(void* context, IOReturn result, void* sender, IOHIDReportType type, uint32_t reportID, uint8_t *report,CFIndex reportLength)
{
memcpy(ReceiveData,report,sizeof(ReceiveData));
if (report[0] == 0x03 && report[1] == 0x31 && report[2] == 0x00 &&
report[3] == 0x00 && report[4] == 0x02) {
appChannel = report[5];
}
}
12. 向USB设备发送指令
IOReturn ret = IOHIDDeviceSetReport(inIOHIDDeviceRef, kIOHIDReportTypeOutput, 0, (uint8_t*)outbuffer, size);
if (ret != kIOReturnSuccess) {
PRINTMSG("发送hid数据失败,error number: %x\r\n",ret);
return ret;
}
13. 断开设备
IOReturn ret = IOHIDDeviceClose(inIOHIDDeviceRef,0L);
if (ret == kIOReturnSuccess) {
NSLog(@"断开成功");
}
14. 可能出现的问题
1)出现如下报错:
TCC deny IOHIDDeviceOpen
Open device failed!
需要赋予输入监听权限
2)赋予权限后依然不能打开hid时,需要在sandbox页赋予USB权限。