DFRobot ESP32C3 USB HID Shield

这次带来的作品是 DFRobot Beetle ESP32-C3的扩展版,通过这个扩展版能够让 Beetle ESP32-C3 取得 USB 键盘鼠标这种 HID 设备的输入数据。

这个扩展版的核心是 WCH 出品的CH9350芯片,CH9350是USB键盘鼠标转串口通讯控制芯片。就是说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。

电路图设计如下:

从DataSheet可以知道,芯片支持 3.3V h和5V供电,为了方便电路设计这里我们直接使用5V供电。PCB 设计如下:

3D渲染结果如下:

黑色PCB 风格非常接近DFRobot

为了Beetle 配合板子只使用了一个 USB 母头,实际芯片同时支持2个USB接口,让客户可以同时使用键盘和鼠标。

下面带来一个USB键盘转蓝牙例子,展示这个板子的能力。

从原理上来说,首先使用 CH9350取得鼠标数据,之后通过 Beetle ESP32-C3 的蓝牙功能将这个数据发送出去。

#include <Arduino.h>
#include <BleKeyboard.h>

BleKeyboard bleKeyboard;

#define DEBUGMODE 1

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, RX, TX);

  bleKeyboard.begin();

}

void loop() {
  while (Serial.available()) {
    char c = Serial.read();
    if (c == '1') {
      Serial.println("get1");
    }
    if (c == '3') {
      ESP.restart();
    }
  }

  //根据 CH9350 Spec 每次最多输出 72Bytes
  byte Data[72];
  unsigned int CounterLast = Serial1.available();
  unsigned int CounterCurrent = 0;


  // 如果当前串口有数据
  if (CounterLast != 0) {
    // 进行简单测试,如果当前还在传输数据那么持续接收
    while (CounterCurrent != CounterLast) {
      CounterLast = Serial1.available();
      delayMicroseconds(500);
      CounterCurrent = Serial1.available();
    }
  }

  if (CounterCurrent > 0) {
    // 一次性将数据收取下来
    Serial1.readBytes(Data, CounterCurrent);
    unsigned int i = 0;
    unsigned int Length;
    while (i < CounterCurrent) {
      // 识别帧头
      if ((Data[i] == 0x57) && (Data[i + 1] == 0xAB)) {
        // 有效键值帧
        if (Data[i + 2] == 0x88) {
          // 获得数据长度
          Length = Data[i + 3];
          if (DEBUGMODE) {
            //Serial.print("Ln:");Serial.print(Length);
            for (int j = 1; j < Length + 1; j++) {
              if (Data[i + 3  + j] < 16) {
                Serial.print("0");
              }
              Serial.print(Data[i + 3  + j], HEX);
              Serial.print(" ");
            }
            Serial.println(" ");
          }

          //如果是键盘
          if (Data[i + 4] == 0x10) {
            if (DEBUGMODE) {
              Serial.print("Key");
              for (int j = 1; j < Length + 1; j++) {
                Serial.print(Data[i + 3  + j], HEX);
                Serial.print(" ");
              }
              Serial.println(" ");
            }
            
            //判断为Dostyle键盘
            if (Data[i + 3  + 1] == 0x10) { 
              if (bleKeyboard.isConnected() == true) {
                bleKeyboard.sendReport((KeyReport*)(&Data[i + 3  + 2]));
              }
            }
          }
          i = i + 3 + Length;
        } else if (Data[i + 2] == 0x82) {
          i = i + 3; // 跳过
        }
      }
      i++;
    } // while (i < Counter)
  }

}

首先,调用了 ESP32 的 BLE 键盘库(第三方),创建一个蓝牙键盘;之后就是分析串口数据。CH9350 能够获得USB 键盘鼠标数据,但是获得这个数据每家会有差别。正确的做法是使用工具(USBlyzer)读取键盘鼠标数据,然后编写分析代码。或者是使用工具读取这个设备的 HID Report Descriptor。再进一步解释就是,例如:市面上有2中鼠标,他们发送出来的数据格式可能是:

A鼠标:

Byte0:  Button

Byte1: X

Byte2: Y

Byte3: Wheel

B鼠标:

Byte0:  Button

Byte1: X 低8位

Byte2: X 高8位

Byte3: X 低8位

Byte4: X 高8位

Byte5: Wheel

具体的格式数据是设备通过HID Report Descriptor报告给系统的,而对于我们来说只能通过人工识别然后在代码中分析的方式来实现解析。相比USB鼠标,键盘的情况要好得多,通常都是使用8 Bytes 来报告按键信息。我这次测试使用的是一款机械键盘,使用的同样是 8 Bytes的格式,因此代码中直接将对应的数据以KeyReport结构体直接发送出去。

if (bleKeyboard.isConnected() == true) {
                bleKeyboard.sendReport((KeyReport*)(&Data[i + 3  + 2]));
 }

本文提到的电路图和PCB:

本文提到的完整代码:

本文使用的库:

发表回复

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