客户服务

Windows上的应用软件如何接收诺为翻页笔的APP通道数据

2024-12-18 11:43:51 诺为

Windows上的应用软件如何接收诺为翻页笔的APP通道数据

诺为翻页笔对电脑来说,就是一个和鼠标、键盘一样的USB HID设备(Human Interface Device)。其所用的技术,和无线键盘、无线鼠标是一样的。

目前,如果想在电脑上获得翻页笔的按键信息,或者说想用翻页笔来控制电脑上的软件,有两种方法。一种是将翻页笔的按键功能自定义为电脑中的应用软件的快捷键,应用软件通过获得USB HID通道传来的快捷键,来触发相应的功能。这种自定义按键的方法,请参见诺为官方网站上的Norwii Presenter软件的说明书,有比较详细的介绍。另一种是先自定义翻页笔按键的APP通道的数据,应用软件通过获取APP通道的数据来接受控制。本文讲述的是Windows上的应用软件如何自定义按键的APP通道数据、读取诺为翻页笔的按键的APP通道数据,主要是程序实现。

这里的APP通道指的是一个用来和APP通信的诺为定义的HID通道。

可以把按键的短按、长按、双击自定义为APP通道的数据。本文所述的短按是按键抬起时触发,只要没有超过长按计时器的按键抬起,都是短按。长按是持续按住按键超过长按计时器时触发。长按计时器一般为1秒,不同的产品或按键可能会有差异。

一、为设备使用Norwii Presenter软件设置APP通道

1. 在Norwii Presenter软件的【功能自定义】页面,点击软件界面上显示的翻页笔按键图标或这个图标对应的蓝色文字,会打开按键自定义窗口。翻页笔的按键模式有3种或者4种,翻页笔的按键模式通过同时长按翻页笔的上、下翻页键进行切换,下图中有4种模式。用户可以选择按键模式中的一种进行自定义,自定义的结果也只在这种模式下有效。

诺为 翻页笔扩音器 18年专业品牌 连续6年销量领先!

2. 点击“短按功能”处“下拉框箭头”-“APP通道”进入APP通道窗口,在编辑框输入十进制数字1~255的一个数值作为APP通道的值,点击“确定”后,在上个窗口点击“保存”。

诺为 翻页笔扩音器 18年专业品牌 连续6年销量领先!

诺为 翻页笔扩音器 18年专业品牌 连续6年销量领先!

诺为 翻页笔扩音器 18年专业品牌 连续6年销量领先!

诺为 翻页笔扩音器 18年专业品牌 连续6年销量领先!

3. 如何确定将翻页笔按键的值自定义为APP通道的数据是成功的?

在【对码】页面,短按已经把短按功能自定义为“APP通道”的按键,窗口右下方白色框中出现“APP通道+数字”,如“APP通道 1”,说明翻页笔按键的APP通道的自定义设置成功了。按照这种方法,也可能对按键的长按功能进行自定义。

诺为 翻页笔扩音器 18年专业品牌 连续6年销量领先!

4. 如何获取诺为翻页笔的VID和PID。

在【关于】页面,找到显示“固件信息”的行,右侧显示诺为翻页笔的VID和PID,这两个值在USB HID通信中会使用到。页面显示的这两个值是省略了前缀0x的16进制数值,在程序中使用的时候记得加上前缀0x,下图中VID是16进制数值0x3243,PID是16进制数值 0x0341,诺为翻页笔的VID都是相同的,不同型号的产品的PID是不同的,同一型号的产品的PID一般是相同的,但同一型号的产品、不同的主控芯片也会有不同的PID。

诺为 翻页笔扩音器 18年专业品牌 连续6年销量领先!

二、USB HID通信

1. 引用到的文件和库。

1. #include <windows.h>

2. #include <vector>

3. #include <string>     // Includes the string functions.

4. #include <setupapi.h>   // Includes the SetupAPI.

5. #include "hidapi.h"

6. using namespace std;

7. #pragma comment (lib, "setupapi.lib")

2. 通过VID、PID获取设备的数据通道的HID地址关键词;如VID:0x3243,PID:0x0341,它的HID地址关键词是“#vid_3243&pid_0341&mi_02&col01#”。

1. string GetNorwiiKeyPath(const int vid, const int pid)

2. {

3. if (vid == 0x023243 & pid == 0x0352)

4.   return "_dev_vid&023243_pid&0352_rev&0001_";

5. if (vid == 0x023243 & pid == 0x0361)

6.   return "_dev_vid&023243_pid&0361_rev&0001_";

7. std::vector<string> vtHidPathKey;

8. vtHidPathKey.push_back("vid_3243&pid_0111&mi_00&col03");

9. vtHidPathKey.push_back("vid_3243&pid_0121&mi_01&col02");

10. vtHidPathKey.push_back("vid_3243&pid_0221&mi_01&col02");

11. vtHidPathKey.push_back("#vid_3243&pid_0131&mi_00&col03#");

12. vtHidPathKey.push_back("#vid_3243&pid_1234&mi_00&col03#");

13. vtHidPathKey.push_back("#vid_3243&pid_0112&mi_00&col03#");

14. vtHidPathKey.push_back("#vid_3243&pid_0212&mi_00&col03#");

15. vtHidPathKey.push_back("#vid_3243&pid_0122&mi_01&col03#");

16. vtHidPathKey.push_back("#vid_3243&pid_0122&mi_02#");

17. vtHidPathKey.push_back("#vid_3243&pid_0222&mi_01&col03#");

18. vtHidPathKey.push_back("#vid_3243&pid_0341&mi_02&col01#");

19. vtHidPathKey.push_back("#vid_3243&pid_0342&mi_02&col01#");

20. vtHidPathKey.push_back("#vid_3243&pid_0381&mi_02&col01#");

21. vtHidPathKey.push_back("#vid_3243&pid_0382&mi_02&col01#");

22.

23. string sKeyPath;

24. char temp_char[20] = { 0 };

25. sprintf(temp_char, "&pid_%04x&", pid);

26. std::vector<string>::iterator iter = vtHidPathKey.begin();

27. for (; iter != vtHidPathKey.end(); iter++)

28. {

29.   string sTempKeyPath = *iter;

30.   if (sTempKeyPath.find(temp_char) != string::npos)

31.   {

32.    sKeyPath = sTempKeyPath;

33.    break;

34.   }

35. }

36. return sKeyPath;

37. }

3. 遍历Windows系统所有USB和蓝牙设备的HID设备接口的路径,来收集所有的设备接口路径。

1. string GetDevicePath(HDEVINFO deviceInfoSet, SP_DEVICE_INTERFACE_DATA deviceInterfaceData)

2. {

3. DWORD bufferSize = 0;

4. SetupDiGetDeviceInterfaceDetailA(deviceInfoSet, &deviceInterfaceData, NULL, 0, &bufferSize, NULL);

5.

6. SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;

7. device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*)malloc(bufferSize);

8. device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);

9.

10. string sDevicePath =

11.   SetupDiGetDeviceInterfaceDetailA(deviceInfoSet, &deviceInterfaceData, device_interface_detail_data, bufferSize, &bufferSize, NULL) ?

12.   device_interface_detail_data->DevicePath : "";

13. free(device_interface_detail_data);

14. return sDevicePath;

15. }

16.

17. vector<string> EnumeratePath()

18. {

19. vector<string> devices;

20. GUID InterfaceClassGuid = { 0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };

21. //GUID InterfaceClassGuid = { 0xa5dcbf10L, 0x6530, 0x11d2,{0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed} };

22. HDEVINFO device_info_set = INVALID_HANDLE_VALUE;

23. //Get information for all the devices belonging to the HID class. 

24. device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

25. if (device_info_set != INVALID_HANDLE_VALUE)

26. {

27.   int device_index = 0;

28.   SP_DEVINFO_DATA devinfo_data;

29.   memset(&devinfo_data, 0x0, sizeof(devinfo_data));

30.   devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);

31.

32.   while (SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data))

33.   {

34.    device_index += 1;

35.

36.    SP_DEVICE_INTERFACE_DATA device_interface_data;

37.    device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

38.    int deviceInterfaceIndex = 0;

39.

40.    while (SetupDiEnumDeviceInterfaces(device_info_set, &devinfo_data, &InterfaceClassGuid, deviceInterfaceIndex, &device_interface_data))

41.    {

42.     deviceInterfaceIndex++;

43.     string devicePath = GetDevicePath(device_info_set, device_interface_data);

44.

45.     devices.push_back(devicePath);

46.    }

47.   }

48.   SetupDiDestroyDeviceInfoList(device_info_set);

49. }

50. return devices;

51. }

4. 获取诺为翻页笔的VID、PID通信的HID地址。

遍历Windows系统所有USB和蓝牙设备HID设备接口的路径,检查是否有一条地址含有前面介绍的HID设备接口的路径关键词。如果有,该条HID设备接口的路径就是诺为翻页笔的VID、PID的HID设备接口的路径。

如果有这样一条HID设备接口的路径\\?\hid#vid_3243&pid_0341&mi_02&col01#7&34f72933&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030},它里面含有HID设备接口的路径关键词“#vid_3243&pid_0341&mi_02&col01#”,这个HID设备接口的路径就是诺为翻页笔的VID、PID的HID通信地址。

1. string GetNorwiiHidPath(const string &sKeyPath)

2. {

3. string sCustomizedPath;

4. vector<string> vtAllPath = EnumeratePath();

5. for (int k = 0; k < vtAllPath.size(); k++)

6. {

7.   string rString = vtAllPath[k];

8.   if (rString.find(sKeyPath) != string::npos)

9.   {

10.    if (rString.find("_dev_vid&023243_pid&0352_rev&0001_") != string::npos ||

11.     rString.find("_dev_vid&023243_pid&0361_rev&0001_") != string::npos)

12.    {

13.     if (rString.find("&col03#") == string::npos)

14.      continue;

15.    }

16.    sCustomizedPath = rString;

17.    break;

18.   }

19. }

20. return sCustomizedPath;

21. }

5. 打开HID设备接口的路径进行通信,要调用hidapi.h文件中的类库函数hid_open_path

1. string szHidPath = "\\?\hid#vid_3243&pid_0341&mi_02&col01#7&34f72933&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";

2. hid_device *hidHandle = hid_open_path(szHidPath.c_str());

6. 关闭已打开的HID设备接口的路径,要调用hidapi.h文件中的类库函数hid_close。

1. if (hidHandle)

2. {

3.   hid_close(hidHandle);

4. }

7.接收HID数据,判断收到APP通道数据,要调用hidapi.h文件中的类库函数hid_read_timeout。

1. size_t length = 8;

2. unsigned char data[8] = { 0 };

3. int res = hid_read_timeout(hidHandle, data, length, 5);

4. if (res > 0 && data[0] > 0x00)

5. {

6.   if (data[0] == 0x03 && data[1] == 0x31 && data[2] == 0x00 && \

7.    data[3] == 0x00 && data[4] == 0x02)

8.   {

9.    int appPath = data[5];//获取到APP通道值

10.    std::cout << "APP Path" << appPath << "\n";

11.   }

12. }

8. 下面代码为整体通信代码。

1. struct hid_device_ {

2. HANDLE device_handle;

3. BOOL blocking;

4. USHORT output_report_length;

5. size_t input_report_length;

6. void *last_error_str;

7. DWORD last_error_num;

8. BOOL read_pending;

9. char *read_buf;

10. OVERLAPPED ol;

11. };

12.

13. int main()

14. {

15. string szKeyPath = GetNorwiiKeyPath(0x3243, 0x341);

16. if (!szKeyPath.empty()) 

17. {

18.   string szHidPath = GetNorwiiHidPath(szKeyPath);

19.   if (!szHidPath.empty()) 

20.   {

21.    hid_device *hidHandle = hid_open_path(szHidPath.c_str());

22.    if (hidHandle) 

23.    {

24.     std::cout << "connect success!\n";

25.     int nPendingCount = 0;

26.     size_t length = 8;

27.     unsigned char data[8] = { 0 };

28.     while (true)

29.     {

30.      int res = hid_read_timeout(hidHandle, data, length, 5);

31.      if (res > 0 && data[0] > 0x00)

32.      {

33.       nPendingCount = 0;

34.       if (data[0] == 0x03 && data[1] == 0x31 && data[2] == 0x00 && \

35.        data[3] == 0x00 && data[4] == 0x02)

36.       {

37.        int appPath = data[5];//获取到APP通道值

38.        std::cout << "APP Path" << appPath << "\n";

39.       }

40.       else if (!hidHandle->read_pending)

41.       {

42.        nPendingCount++;

43.        if (nPendingCount > 100)

44.         break;

45.       }

46.      }

47.     }

48.     hid_close(hidHandle);

49.    }

50.   }

51. }

52.  

53.     std::cout << "Hello World!\n"; 

54. system("pause");

55. }

2.9. 参考代码地址:

https://www.norwii.com/downloads/presenter/windows/app_path.zip