目录
0 摘 要 :简单说一下这篇文章在搞啥
1 背 景 :什么需求以及对开发的背景调查
2 环境配置 :具体需要什么环境,对软件层面的需求
3 硬件测试 :测试遥感器硬件本身好坏以及是否正常连接
4 Debug :软件debug并放出相关代码
6 后 续 :完结撒花
0摘要
本文主要介绍了一个简单的demo。在windows下通过DirectInput开发套件读取Thrustmaster Hotas Warthog飞行操作杆数据,要求后续可能会用C++进行更加深入的二次开发。
1背景
今天上午,同事给我布置了一个任务,让我读取一个遥感的数据,要求后面用C++进行二次开发,看了一下设备,是:A10C疣猪双手摇杆 – Thrustmaster(图马思特)
首先我去了官网,大致浏览了一番,发现官网没有说到相关素材的。只有适配什么软件啊,说明书啊,没有提到怎么进行二次开发,可惜...... 有幸在CSDN上找到几篇相关的内容,但是都不能直接解决我的二次开发问题,有的虽然有重合,但是需要收费。这里一次放出:
首先是python的,参考博客:Thrustmaster(图马思特) HOTAS Warthog 疣猪杆 读取按钮数据-CSDN博客
然后是介绍如何在Windows下通过Directinput来开发(收费):
Thrustmaster Hotas Warthog/猪杆/疣猪飞行操作杆开发_疣猪摇杆-CSDN博客
还有一个是使用XInput开发的,本人试过了可以使用,但是这个开发工具存在问题,简单的调试可以,但是如果是游戏那种对实时性要求高的就不行了:
Thrustmaster HOTAS Warthog 开发指南-CSDN博客
别人的实例代码:
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib,"Winmm.lib")
int main()
{
UINT inum = joyGetNumDevs();
printf("%d\n", inum);
while (getchar() != 'q')
{
UINT uJoyID = 0;
LPJOYINFO pji = new joyinfo_tag;
joyGetPos(uJoyID, pji);
printf("pji->wXpos: %d\n", pji->wXpos);
printf("pji->wYpos: %d\n", pji->wYpos);
printf("pji->wZpos: %d\n", pji->wZpos);
printf("pji->wButtons: %d\n", pji->wButtons);
}
return 0;
}
2环境配置
那就麻烦万能的GPT了
我:有一个Thrustmaster hotas warthog摇杆,怎么做二次开发?
ChatGPT 说:
要对Thrustmaster HOTAS Warthog摇杆进行二次开发,可以从以下几个方面入手:
1. 了解设备接口
HOTAS Warthog通常通过USB接口与PC连接,符合HID(Human Interface Device)标准。
可使用HID协议或现成的库读取和控制设备。
2. 开发工具与库
- DirectInput/XInput:Windows平台上读取输入设备的API。适合处理复杂输入设备。
- vJoy:虚拟摇杆驱动,适合自定义输入信号。
- HIDAPI:跨平台库,可直接与HID设备通信。
- Joystick Gremlin:用于自定义摇杆输入和映射(适合无代码修改)。
- Python:利用pyvjoy或pyhidapi库开发自定义功能。
3. 固件/硬件层开发
- HOTAS Warthog固件不可直接修改,但可以通过外接设备(如Arduino或STM32)中转信号,实现高级功能。
- 如果想进行深度硬件改造,可以拆解摇杆并改装开关或传感器。
4. 软件集成
- DCS World或Microsoft Flight Simulator等模拟飞行游戏支持自定义插件开发,可与摇杆联动。
- 使用Lua脚本或SDK对接游戏,实现更复杂的控制逻辑。
5. 案例开发方向
- 自定义按键映射(复杂组合键、宏命令)。
- 模拟仪表或开关反馈(LED、震动反馈)。
- 多设备联动(与脚蹬、推杆、仪表盘集成)。
- 飞行模拟之外的用途(如工业设备控制、机械臂操控)。
5. 案例开发方向
- 自定义按键映射(复杂组合键、宏命令)。
- 模拟仪表或开关反馈(LED、震动反馈)。
- 多设备联动(与脚蹬、推杆、仪表盘集成)。
- 飞行模拟之外的用途(如工业设备控制、机械臂操控)。
我的需求是在window下进行C++开发,所以就需要DirectInput/XInput。 后面gpt就对这两种开发工具做了比较,简单来说就是对DirectInput和XInput开发工具进行比较。
您说:
DirectInput/XInput是什么软件?还是第三方库?
ChatGPT 说:
DirectInput和XInput都不是独立的软件或第三方库,而是微软提供的API(应用程序接口),用于在Windows系统上与输入设备(如游戏手柄、摇杆、方向盘)进行交互。
1. DirectInput
简介:DirectInput是微软DirectX的一部分,专门用于与各种输入设备(键盘、鼠标、摇杆、游戏手柄等)交互。
特点:
- 支持多种设备,尤其是复杂的飞行摇杆和赛车方向盘。
- 支持多按键、轴、旋钮、力反馈(Force Feedback)。
- 不支持Xbox 360及更新设备(XInput替代)。
- 适用场景:适合读取复杂的输入设备,如Thrustmaster HOTAS Warthog这种多轴、多按钮设备。
- 开发语言支持:C/C++(DirectX SDK),C#(通过SharpDX或SlimDX封装)。
2. XInput
简介:XInput是微软专门为Xbox 360及更新型号的控制器设计的API。
特点:
- 仅支持Xbox 360、Xbox One等标准手柄。
- 不支持复杂的摇杆设备和力反馈功能(功能较简化)。
- 简单易用,适合一般的游戏手柄开发。
- 适用场景:主要用于开发支持Xbox手柄的PC游戏,不适合HOTAS Warthog这种设备。
- 开发语言支持:C/C++,也有C#封装(如XInput.NET)。
然后就是根据这个开发工具进行环境配置与软件部署
您说:
DirectInput怎么用于摇杆做第三方开发?
ChatGPT 说:
使用 DirectInput 与 Thrustmaster HOTAS Warthog 摇杆进行第三方开发,主要涉及设备初始化、输入读取和数据解析等步骤。:
1.开发工具:
- 推荐使用 Visual Studio(Windows 环境)。
- C++是DirectInput最常用的开发语言。
2.环境准备:
安装 DirectX SDK(适用于 DirectInput):
https://www.microsoft.com/zh-cn/download/details.aspx?id=8109
安装后,新建Visual Studio项目,并且配置头文件和库文件(这里路径很怪,在:C:\Program Files (x86)\Windows Kits\10\Lib\10.0.16299.0\um\x64目录下)。
链接库:
- 右键项目 → 属性 → 链接器 → 输入 → 附加依赖项
- 添加:dinput8.lib; dxguid.lib; d3d8.lib; winmm.lib
- 注意: 我的该目录下只有 d3d9.lib;
- 包含目录:
- C/C++ → 常规 → 附加包含目录 → 添加 DirectX SDK 的 Include 目录。
- 链接器 → 常规 → 附加库目录 → 添加 DirectX SDK 的 Lib 目录。
3. 注意VS的debug模式是x64
3硬件测试
安装了官网的驱动程序和测试软件,确定硬件方面还有连接是正常的:
下载后驱动程序直接安装:
经过硬件测试软件确定硬件还有连接,动遥感(准心在动)还有按钮(亮灯)是没有问题的!:
4Debug
中间花了三个小时与GPT对战,互相debug,在一个阴暗的小角落里面通过手机+电脑飞快的查询问题。其中最严重的bug是获取设备的这个函数报错:
// 获取设备时打印详细错误码
hr = g_pJoystickDevice->Acquire();
if (FAILED(hr)) {
std::cerr << "设备获取失败! 错误码: " << std::hex << hr << std::endl;
return false;
}
一直提示没有管理员权限(返回0x80070005):
0x88780096 (DIERR_OTHERAPPHASPRIO):其他程序占用设备。
0x8878000C (DIERR_NOTACQUIRED):设备没有被获取。
0x8878000F (DIERR_INPUTLOST):输入设备丢失,需重新获取。
0x80070005 (E_ACCESSDENIED):权限不足(需要管理员权限)。
后面不知道Gpt给的啥代码,这个问题不提示了。最后的代码见下:
#include <windows.h>
#include <dinput.h>
#include <iostream>
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")
LPDIRECTINPUT8 g_pDirectInput = nullptr;
LPDIRECTINPUTDEVICE8 g_pJoystickDevice = nullptr;
bool InitDirectInput(HINSTANCE hInstance, HWND hWnd) {
// 初始化 DirectInput
if (FAILED(DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&g_pDirectInput, NULL))) {
std::cerr << "DirectInput 初始化失败!" << std::endl;
return false;
}
// 创建设备(默认获取第一个手柄)
if (FAILED(g_pDirectInput->CreateDevice(GUID_Joystick, &g_pJoystickDevice, NULL))) {
std::cerr << "无法创建摇杆设备!" << std::endl;
return false;
}
// 设置数据格式
if (FAILED(g_pJoystickDevice->SetDataFormat(&c_dfDIJoystick))) {
std::cerr << "设置数据格式失败!" << std::endl;
return false;
}
// 设置后台非独占模式
if (FAILED(g_pJoystickDevice->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND))) {
std::cerr << "设置协作级别失败!" << std::endl;
return false;
}
// 获取设备控制权
if (FAILED(g_pJoystickDevice->Acquire())) {
std::cerr << "设备获取失败!" << std::endl;
return false;
}
std::cout << "DirectInput 初始化成功,正在监听摇杆输入..." << std::endl;
return true;
}
void ReadJoystickState() {
DIJOYSTATE js;
HRESULT hr = g_pJoystickDevice->Poll(); // 轮询设备状态
if (FAILED(hr)) {
// 设备可能丢失,尝试重新获取
g_pJoystickDevice->Acquire();
return;
}
// 获取设备状态
if (FAILED(g_pJoystickDevice->GetDeviceState(sizeof(DIJOYSTATE), &js))) {
std::cerr << "读取设备状态失败!" << std::endl;
return;
}
// 打印摇杆信息
std::cout << "X轴: " << js.lX << " Y轴: " << js.lY << " Z轴: " << js.lZ << std::endl;
}
void CleanUp() {
if (g_pJoystickDevice) {
g_pJoystickDevice->Unacquire();
g_pJoystickDevice->Release();
g_pJoystickDevice = nullptr;
}
if (g_pDirectInput) {
g_pDirectInput->Release();
g_pDirectInput = nullptr;
}
}
int main() {
HINSTANCE hInstance = GetModuleHandle(NULL);
HWND hWnd = GetConsoleWindow(); // 使用控制台窗口句柄
if (!InitDirectInput(hInstance, hWnd)) {
std::cerr << "DirectInput 初始化失败,程序终止!" << std::endl;
return -1;
}
// 持续读取摇杆数据
while (true) {
ReadJoystickState();
Sleep(2000); // 每100ms读取一次
}
CleanUp();
return 0;
}
最后效果展示如下
6后续
可惜只要求我写个demo,具体还没要求我干啥,能读遥感数据就行,按钮后面有需求再写demo吧。
完结,撒花