USB 手柄转无线

这是一个能够将USB 手柄转为无线手柄的设备。这样,你可以将有线的 USB 手柄变为无线的手柄进行游戏。之所以做这个项目是因为我发现Linux 系统在支持多HID设备上和 Windows上有所不同。之前设计的,能够在 Windows上工作正常的USB转蓝牙设备无法在 Linux 系统上正常工作。

这次带来的方案是有两部分:发射端和接收端。

发射端是 ESP32 C3 配合 CH9350实现的,它能够实现USB手柄数据的读取和无线信号的发送。其中 CH9350负责USB手柄数据的解析;解析后的数据由ESP32 C3通过ESP-Now发送出去。CH9350是 WCH (就是出品 Ch340 的那个公司)推出的USB HID 转串口通讯控制芯片。就是说USB 手柄连接到这个芯片之后,数据会转化为串口输出。关于这个芯片的功能介绍如下:

  • 支持12Mbps全速USB传输和1.5Mbps低速USB传输,兼容USB V2.0。
  • 上位机端USB端口符合标准HID类协议,不需要额外安装驱动程序,支持内置HID类设备驱动的Windows、Linux、macOS等操作系统。
  • 同一芯片可配置为上位机模式和下位机模式,分别连接USB-Host主机和USB键盘、鼠标。
  • 支持USB键盘鼠标在BIOS界面使用,支持多媒体功能键,支持不同分辨率USB鼠标。
  • 支持各种品牌的USB键盘鼠标、USB无线键盘鼠标、USB转PS2线等。
  • 上位机端和下位机端支持热插拔。
  • 提供发送状态引脚,支持485通讯。
  • 串口支持115200/57600/38400串口通信波特率。
  • 内置晶振和上电复位电路,外围电路简单。
  • 支持5V、3.3V电源电压。
  • 提供LQFP-48无铅封装,兼容RoHS。

发射端方案的优点是:成本比较低,体积比较小,容易DIY焊接(ESP32 C3 是我用过的最容易焊接的ESP32芯片)。能够同时支持2个USB设备,就是说你可以同时使用2个USB手柄,同时转化为无线给主机使用。

接收器使用的是ESP32-S2 ,它带有USB Device ,能够方便的将自身模拟为一个USB手柄。因此,无论 Windows 还是 Linux 只要支持USB 手柄,在操作系统端看起来插入的就是一个USB手柄,完全不会碰到兼容性问题。这里使用淘宝直接购买的Mini ESP32-S2 开发板,体积小,价格便宜。

接下来首先进行电路的设计。

  1. 主控 ESP32 C3 部分。这款主控内置了 USB 下载电路,我们设计一个 USB接口即可工作。此外,复位与下载按钮是必须的,当出现问题无法下载时,这两个按钮随时可以帮助恢复。

2.接下来时CH9350部分,它外部电路非常简单,只需要一个 3.3uf和一个 0.1uf电容即可工作。外部的 LED1和 LED2用来指示USB工作状态,没有有效数据时会亮,有数据传输时会熄灭。其中的USB3 是一个双层USB座子,这样我们可以同时使用两个USB手柄。

3.我们使用 TLV1117将5V 转为3.3V,同时还预留一个假负载,用来避免在使用充电宝供电,输出小于50ma 一段时间后自动关机的问题。

电路比较简单PCB设计也相对简单:

预览如下:

上面就是硬件设计,接下来着手软件的设计。

同样,分成发送端和接收端分别介绍。

发送端的主要工作是:USB 手柄数据的获取,获得数据的发送。

对于USB 手柄数据的获取和之前的并没有多少差别,只需要解析来自串口的数据即可;特别注意代码加入了判断,只有收到和之前数据不同的时候才会发送;

我们使用 ESP-Now 进行数据发送。对于 Arduino 开发来说,这个非常简单。创建 ESPNow 对象,然后指定发送的地址即可:

// 创建esp_now_peer_info_t类型变量存储有关peer方的信息。
esp_now_peer_info_t peerInfo0;
esp_now_peer_info_t peerInfo1;

//  ESP32 接收器 MAC 地址
uint8_t broadcastAddress0[] = {'L', 'A', 'B', 'Z', '-', '0'};
uint8_t broadcastAddress1[] = {'L', 'A', 'B', 'Z', '-', '1'};

之后每次收到改变后的USB手柄数据,可以使用下面的函数进行发送

// Send message via ESP-NOW
                esp_err_t result = esp_now_send(broadcastAddress1, (uint8_t *) &Data[i + 6], 7);

对应的接收端的设计如下:

  1. 程序开始处,设置当前ESP32 的MAC地址,作为接收数据的地址。发送端同时支持2个USB手柄,因此我们通过GPIO 判断设置2个Mac。这样,我们的程序刷到2个ESP32 S2主板上之后,根据外部跳线可以选择不同的 Mac 非常方便。
  if (digitalRead(ADDRESSPIN2) == HIGH) {
    esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress0[0]);
  } else {
    esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress1[0]);
  }
  if (DEBUGMODE) {
    Serial.print("[NEW] ESP32 Board MAC Address:  ");
    Serial.println(WiFi.macAddress());
  }

2.为了实现USB 手柄,我们使用USB 手柄相同的 Report 描述符,这样在操作系统端看起来插入的就是一个 HID 手柄。

    CustomHIDDevice(void) {
      static bool initialized = false;
      if (!initialized) {
        initialized = true;
        HID.addDevice(this, sizeof(report_descriptor));
      }
}

3.接收到的来自ESP NOW 的数据会出现在 OnDataRecv() 这个回调函数中,我们接收之后无需额外处理直接作为USB数据发送给主机即可。

// 创建一个回调函数,当 ESP32 通过 ESP-NOW 接收到数据时将被调用
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  if (DEBUGMODE) {
    // 收到的数字,例如:
    // 128 128 128    128 31 0
    // 发过来的数据直接就是 RAW 格式
    for (uint8_t i = 0; i < len; i++) {
      printf("%02x  ", incomingData[i]);
    }
    printf("\n");
  }

  if (HID.ready()) {
    Device.send((uint8_t *)&incomingData[0]);
  }
}

工作的视频在B站可以看到:

https://www.bilibili.com/video/BV168411r7r3/?share_source=copy_web&vd_source=5ca375392c3dd819bfc37d4672cb6d54

对应的 Arduino 代码在这里下载:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注