这段时间因为项目需求,接触到了罗技G29方向盘的SDK开发,能够参考的资料比较有限,一路磕磕碰碰遇见不少问题,硬着头走了下去,不过最后还是成功了,写下这篇笔记来记录下我的开发过程,也给有需要的人参考,少走点弯路

一.开发环境和开发工具
开发环境:win10
开发工具:vs2017
方向盘型号:罗技G29

开发前的准备
(1).去罗技官网上下载罗技方向盘SDK
https://www.logitechg.com.cn/zh-cn/innovation/developer-lab.html
文件中有相关的.h和.lib文件,以及三个mfc例程以及相关的说明文档

(2).下载罗技游戏软件
这里要说一下,在SDK文档里提出了方向盘得在罗技游戏软件运行的情况下才能进行相关的SDK开发,所以这个软件在开发中需要全程运行,注意下载后它会提醒你下载新的罗技 G support,别理它就是了,罗技 G support根本就识别不出来罗技G29方向盘.
下载链接:https://support.logi.com/hc/zh-cn/articles/360025298053

(3).检测方向盘是否正常工作
正常情况下,在方向盘上电并接入电脑后,方向盘会自动旋转几圈然后拨正,打开罗技游戏软件的时候也会有这个现象,接好方向盘并且打开罗技游戏软件,我们来通过官方提供的demo来检测一下方向盘是否能够正常工作.
先介绍一下这几个demo
第一个是个比较完善的demo,打开后的界面大概如下
正常情况下,在点击INIT按钮后,界面上就会出现方向盘的相关数据,这里我没有接入方向盘,所以就没有显示,我们需要记录下一些数据,首先是确认下数据是出现在哪块区域的,就是divice 0 还是divice 1,这关系到后面相关api的调用参数,还有就是按下方向盘上的十字按键后,是在POV 0 还是POV 1显示数据的理由同上。

第二个demo我也没搞懂究竟是干什么用的,不过我这边的开发也用不到这个demo,感兴趣的可以去自己研究

第三个demo比较有意思,这个demo是罗技官方提供给开发者用来测试SDK中提供的相关API是否能够正常工作的MFC程序

具体的代码逻辑就是你点击相关的API按钮,它就会调用相关的API,然后将返回结果显示在下方的信息栏里面,不过这个demo没有写完整,很多按钮的功能都还没有实现,你点击的话它会提示这个功能还没有写完(???,官方拖更),不过并不影响我们正常获取方向盘数据

二.阅读SDK文档,弄清相关的API调用顺序

(1).这里我就简单介绍一下几个关键的函数,首先是两个初始化函数
bool LogiSteeringInitialize(CONST bool ignoreXInputControllers)
这个初始化函数会自动寻找当前处于最前端的窗口句柄并传入给这个函数进行方向盘的初始化,不过我在实际使用中这个函数经常抽风,返回值一直不稳定,所以我在后面的程序开发中舍弃了使用这个初始化的打算

bool LogiSteeringInitializeWithWindow(CONST bool ignoreXInputControllers, HWND hwnd)

这个函数的效果和上一个函数是一样的,第一个参数是是否忽略X-IPU的参数,第二个参数是当前程序的窗口句柄,这个函数的不同之处在于我们需要手动将你写的程序界面的句柄传入到这个函数里面去,那么什么是句柄呢?
我也看了不少资料,我是把它理解为界面程序的类似pid号的一个东西,让windows系统能够找到你的界面程序,这是我遇见的第一个坑,当初我想如果用windows编程创建一个窗口的话太麻烦了,底层的代码又臭又长,想直接通过获取win32控制台程序的句柄传入到这个函数里面去,但初始化结果一直都是失败的,折腾了很久一直都没有搞定,最好还是老老实实写了一个空界面来初始化,值得一提的是,这个初始化函数要求当前界面处于所有应用的最顶端,意思就是如果你把这个界面缩小,获取点击其他界面后,这个程序就拉跨了,所以在使用的时候要确保这一点

(2).相关API的调用顺序
这里贴一张图,是来自于其他博主的,使用的C#的sdk,不过流程都是一致的
在C/C++使用的时候直接把前面的类名去掉即可,然后将初始化的函数换成
LogiSteeringInitializeWithWindow,值得一提的是,如果方向盘初始化成功的话,方向盘的旋转阻力会变成0,所以可以通过这个方法来判断方向盘是否初始化成功.

三.搭建开发环境

创建一个空的windows桌面应用程序,并且将SDK包中提供的头文件和库文件复制到项目目录下面,并且配置好,具体的配置过程可以参考下面
然后在你的main.cpp里面添加

#pragma comment(lib, "LogitechSteeringWheelLib.lib")

先贴一下完整的代码,我这个程序主要是获取方向盘的基础数据,转向,油门,刹车等信息后上传到局域网内的服务器上

#include <Windows.h>
#include <thread>
#include <stdio.h>
#include <iostream>
#include<winsock.h>#include "LogitechSteeringWheelLib.h"#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib, "LogitechSteeringWheelLib.lib")
#pragma comment( linker, "/subsystem:\"console\" /entry:\"WinMainCRTStartup\"")using namespace std;void initialization() {//初始化套接字库WORD w_req = MAKEWORD(2, 2);//版本号WSADATA wsadata;int err;err = WSAStartup(w_req, &wsadata);if (err != 0) {cout << "初始化套接字库失败!" << endl;}else {//cout << "初始化套接字库成功!" << endl;}//检测版本号if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {cout << "套接字库版本号不符!" << endl;WSACleanup();}else {//cout << "套接字库版本正确!" << endl;}//填充服务端地址信息}void SendMessage(int x, int y, int z)
{//定义长度变量int send_len = 0;int recv_len = 0;//定义发送缓冲区和接受缓冲区char send_buf[100];char recv_buf[100];//定义服务端套接字,接受请求套接字SOCKET s_server;//服务端地址客户端地址SOCKADDR_IN server_addr;initialization();//填充服务端信息server_addr.sin_family = AF_INET;server_addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.103");//server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//server_addr.sin_port = htons(2020);server_addr.sin_port = htons(1888);//创建套接字s_server = socket(AF_INET, SOCK_STREAM, 0);if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {//cout << "服务器连接失败!" << endl;WSACleanup();}else {//cout << "服务器连接成功!" << endl;}//发送,接收数据//cout << "请输入发送信息:";//cin >> send_buf;sprintf_s(send_buf, "%d,%d,%d", x, y, z);send_len = send(s_server, send_buf, strlen(send_buf), 0);if (send_len < 0) {cout << "发送失败!" << endl;}printf("x = %d,y = %d,z = %d\n", x, y, z);Sleep(500);/*recv_len = recv(s_server, recv_buf, 100, 0);if (recv_len < 0) {cout << "接受失败!" << endl;break;}else {cout << "服务端信息:" << recv_buf << endl;}*///关闭套接字closesocket(s_server);//释放DLL资源WSACleanup();}void WheelInit(HWND hwnd)
{//LogiSteeringInitialize(true);if (LogiSteeringInitializeWithWindow(true, hwnd)){printf("init secuss\n");//printf("%d\n", hwnd);while(LogiUpdate() && LogiIsConnected(1)){//printf("connect secuss\n");Sleep(100);DIJOYSTATE2 * wheel = LogiGetState(1);//输出角度,油门,刹车信息//printf("Angle = %d  Accelerator = %d  Brake = %d\n", wheel->lX, wheel->lY,wheel->lRz);SendMessage(wheel->lX, wheel->lY, wheel->lRz);//printf(wheel->rgdwPOV)//std::cout << wheel->rgdwPOV[0] << endl;switch (wheel->rgdwPOV[0]){case(0):cout << "D-pad Up Button Pressed" << endl; break;case(18000):cout << "D-pad Down Button Pressed" << endl; break;case(27000):cout << "D-pad Left Button Pressed" << endl; break;case(9000):cout << "D-pad Right Button Pressed" << endl; break;case(31500):cout << "D-pad Left-up Button Pressed" << endl; break;case(4500):cout << "D-pad Right-up Button Pressed" << endl; break;case(22500):cout << "D-pad Left-down Button Pressed" << endl; break;case(13500):cout << "D-pad Right-down Button Pressed" << endl; break;}if (LogiButtonTriggered(1, 19)) {printf("-----------------------\n");printf("Button 19 Pressed\n");printf("-----------------------\n");}if (LogiButtonTriggered(1, 20)){printf("-----------------------\n");printf("Button 20 Pressed\n");printf("-----------------------\n");}if (LogiButtonTriggered(1, 23)){printf("-----------------------\n");printf("Button 23 Pressed\n");printf("-----------------------\n");}}}else{printf("init faild");}}// 必须要进行前导声明
LRESULT CALLBACK WindowProc(_In_  HWND hwnd,_In_  UINT uMsg,_In_  WPARAM wParam,_In_  LPARAM lParam
);int CALLBACK WinMain(_In_  HINSTANCE hInstance,_In_  HINSTANCE hPrevInstance,_In_  LPSTR lpCmdLine,_In_  int nCmdShow
)
{// 类名  TCHAR cls_Name[] = L"My Class";// 设计窗口类  WNDCLASS wc = { sizeof(WNDCLASS) };wc.hbrBackground = (HBRUSH)COLOR_WINDOW;wc.lpfnWndProc = WindowProc;wc.lpszClassName = cls_Name;wc.hInstance = hInstance;wc.style = CS_HREDRAW | CS_VREDRAW;// 注册窗口类  RegisterClass(&wc);// 创建窗口HWND hwnd = CreateWindow(cls_Name,           //类名,要和刚才注册的一致  L"方向盘Demo",          //窗口标题文字  WS_OVERLAPPEDWINDOW,        //窗口外观样式  38,             //窗口相对于父级的X坐标  20,             //窗口相对于父级的Y坐标  500,                //窗口的宽度  500,                //窗口的高度  NULL,               //没有父窗口,为NULL  NULL,               //没有菜单,为NULL  hInstance,          //当前应用程序的实例句柄  NULL);              //没有附加数据,为NULL  if (hwnd == NULL)                //检查窗口是否创建成功  return 0;// 显示窗口  ShowWindow(hwnd, SW_SHOW);// 更新窗口  UpdateWindow(hwnd);SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); //设置窗口最前端thread GetWheelData(WheelInit, hwnd);GetWheelData.detach();//MessageBox(0, "调用了WinMain函数", "测试:", 0);// 消息循环  MSG msg;while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0;
}// 在WinMain后实现
LRESULT CALLBACK WindowProc(_In_  HWND hwnd,_In_  UINT uMsg,_In_  WPARAM wParam,_In_  LPARAM lParam
)
{switch (uMsg){case WM_DESTROY:{PostQuitMessage(0);return 0;}case WM_PAINT:{/*PAINTSTRUCT     ps;HDC hdc = BeginPaint(hwnd, &ps);int length;TCHAR buff[1024];length = wsprintf(buff, TEXT("the angle is : %d"), x);TextOut(hdc, 20, 20, buff, length);EndPaint(hwnd, &ps);break;*/}default:break;}return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

方向盘的基础信息是整合在一个 DIJOYSTATE2的结构体里面,然后通过

DIJOYSTATE2* LogiGetState(const int index);

方法来返回该结构体变量,然后输出变量成员来获取数据,这个函数的传入参数是设备的ID号,就是我在前面提到的divice 0 还是divice 1

提一下方向盘按键数据的获取,主要是通过

bool LogiButtonTriggered(const int index, const int buttonNbr);

获取的,第一个参数的设备ID,第二个参数是按键编号,具体的编号可以打开罗技游戏软件,然后看方向盘上的编号,不过要注意一下,实际上调用的编号是软件上显示的编号-1。

最后推荐一篇其他人的笔记,写的比我详细多了,想要深入研究的可以参考一下这篇文档

G29开发笔记


8.11日更新

有同学反应只下载罗技游戏软件的话运行demo会识别不到方向盘,我看了下好像是软件版本的问题,如果使用的是最新版的罗技游戏软件的话就去官网上下载最新的罗技G hub,运行demo的时候把两个软件都打开就可以解决了

罗技方向盘SDK开发笔记相关推荐

  1. android大疆飞控界面,DJI Android SDK 开发笔记(入门篇)

    ##大疆SDK开发笔记## #1.文档相关 2.Android SDK文档 接入Android的SDK都在这部分,飞控相关的接口. 3.Android UX SDK文档 大疆自定义的组件,已经跟飞机关 ...

  2. IOS在线听书 喜马拉雅SDK开发笔记(部分)

    喜马拉雅IOS开发笔记 由于我的课程设计做的比较简单至少是现在做得计较简单, 现在我已经接入成功了,所以SDK接入教程可能会在后续做笔记 在官方给出的SDK中有一个demo使用OC写的,我们在demo ...

  3. Android支付宝SDK开发笔记

    一.准备工作 〉1.下载开发包 https://b.alipay.com/order/productDetail.htm?productId=2014110308141993&tabId=4# ...

  4. X2000 SDK 开发笔记

    SDK编译 # 1.设置环境变量 #在工程目录下执行命令: source build/envsetup.sh # 2.选择 device #在工程目录下执行命令: lunch # 根据硬件底板版本选择 ...

  5. SLM328美格4G模组SDK开发笔记

    一.设置日志通过USB串口输出 AT^TRACECTRL=1,1,5 //CP Log开启并输出到USB AT^TRACECTRL=0,1,2 //AP Log开启并输出到USB AT+TRB//通过 ...

  6. Pico Unity XR SDK开发笔记(1)

    20221107更新 这玩意更新太快了,新的SDK使用比之前的有一些不同,不过大体一致.新的SDK配置官网上也有详细的教程了https://developer-cn.pico-interactive. ...

  7. Pico Unity XR SDK开发笔记(2)-- 乒乓

    1,素材 在unity的asset store上下载一个免费的素材"Low-Poly Table Tennis Set",先"Add to My Assets" ...

  8. 海康摄像头开发笔记(一):连接防爆摄像头、配置摄像头网段、设置rtsp码流、播放rtsp流、获取rtsp流、调优rtsp流播放延迟以及录像存储

    文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/131679108 红胖子(红模仿)的博文大全:开发技术集合( ...

  9. 《ArcGIS Runtime SDK for Android开发笔记》——离在线一体化技术:概述

    1.前言 数据生产和数据展示是常见的两大专业级移动GIS应用场景,这里我们针对数据生产环节的ArcGIS的离在线一体化技术给大家做一个基本的介绍和梳理. 使用ArcGIS离在线一体化技术首先需要以下基 ...

最新文章

  1. opencv补全边缘_为什么OpenCV中绘制的轮廓不能填充图像边缘的轮廓?
  2. Hibernate连接池配置实例
  3. 2018年第九届蓝桥杯C/C++ C组国赛 —— 第三题:全排列
  4. odata协议里filter操作自带的函数 - endswith
  5. 计算机视觉招聘_INDEMIND|SLAM、计算机视觉、深度学习算法招聘(社招实习)
  6. python progressbar print_python print 进度条的例子
  7. csdn的blog后台程序的导航菜单的实现
  8. 谷歌浏览器实现直接打印效果
  9. Anguar 使用interceptor拦截器设置请求头传入jwt token
  10. phtread条件变量pthread_cond_t初始化方式
  11. 云计算摆摊的可行性分析 | 凌云时刻
  12. Flash cs4 for mac 序列号。
  13. 微信订阅号改回列表显示
  14. ClientToScreen ()与 ScreenToClient()
  15. javascript调用php函数_JavaScript怎么调用PHP函数?
  16. 易语言单窗口单ip软件源码_游戏搬砖怎样换IP防封?有几种处理方法?
  17. oreo授权系统V1.1开源版
  18. 欢迎来访个人网站——阳光日志
  19. 网站 下载 整个_如何下载整个网站?
  20. 4位数字排列组合(所有组合与出现一次)

热门文章

  1. 金蝶K3wise、金蝶KIS旗舰版数据同步到金蝶云星空
  2. 数据结构——用户登陆系统
  3. java三大特性之多态的认识,以及多态的实际应用(一)
  4. PGP加密,良好隐私密码法
  5. android小问题-------------android模拟器没有SD卡,SD卡状态为removed的解决方式
  6. pydicom处理dicom序列的tag
  7. 程序员开发的常用21个开发工具(总结篇)
  8. windows 11远程桌面连接无法使用已保存的凭据密码,每次连接都要求输入的解决方案
  9. 滚动轴承故障诊断MATLAB程序:快速谱峭度、谱峭度+包络谱分析
  10. 如何通过C语言代码表白女程序媛 (直男必看)