
#第六届立创电赛#ESP32 一对多无线键盘鼠标扩展器
简介
简介:开源协议
:GPL 3.0
描述
注:* 为必填项
请在报名阶段填写 ↓
* 1、项目功能介绍
很多时候出于空间和便捷性考虑,我们希望一套键盘鼠标来控制多个PC。通常情况下可以通过 KVM实现这个功能。显而易见,他们使用有线方式来进行连接,仍然会占用大量空间。这次使用 ESP32来实现无线的键盘鼠标扩展功能。
*2、项目属性
原创项目首次公开
* 3、开源协议
GPL3.0开源协议
请在竞赛阶段填写 ↓
*4、硬件部分
为了完成这个目标,选择眼下最流行的 Arduino 开发环境,配合ESP32 来实现。这次使用到了 ESP32 和 ESP32 S2两种型号的主控芯片。
首先要实现解析USB键盘鼠标的功能,通常情况下可以使用 Arduino USB Host Shield,但是因为我们需要同时解析USB键盘和鼠标,这样的方案会显得比较麻烦,另外一个原因是这个Shield的主要芯片是 Max3421e 现在价格较高(立创商城报价人民币42-65元)。因此,选择了CH9350(这是一颗国产芯片,是南京沁恒WCH出品的, 现在非常流行的 USB转串口芯片CH340 也是他们家的产品)。CH9350能够将 USB 键盘鼠标的有效信息转为串口数据。芯片特点如下:
• 支持12Mbps全速USB传输和1.5Mbps低速USB传输,兼容USB V2.0。
• 上位机端USB端口符合标准HID类协议,不需要额外安装驱动程序,支持内置HID类设备驱动的Windows、Linux、MAC等操作系统。
• 同一芯片可配置为上位机模式和下位机模式,分别连接USB-Host主机和USB键盘、鼠标。
• 支持USB键盘鼠标在BIOS界面使用,支持多媒体功能键,支持不同分辨率USB鼠标。
• 支持各种品牌的USB键盘鼠标、USB无线键盘鼠标、USB转PS2线等。
• 上位机端和下位机端支持热插拔。
• 提供发送状态引脚,支持485通讯。
• 串口支持115200/57600/38400串口通信波特率。
• 内置晶振和上电复位电路,外围电路简单。
• 支持5V、3.3V电源电压。
• 提供LQFP-48无铅封装,兼容RoHS。
同时这个款芯片立创商城报价是 25元,比Max3421便宜一大截。
然后围绕这个芯片,给DFRobot 的 FireBeetle 主控板(芯片是ESP32D32)设计一个转接板,能够将USB 键盘鼠标信号解析为串口数据。
核心是 CH9350芯片,它能够一次性支持2个 USB Host接口,下图中的 USB1 和 USB2。LED1 和 LED2 是通讯指示灯,对应的 USB1 和 USB2 如果有正常的通讯,对应的 LED会熄灭。此外,还有一个USB_Power 是USB公头,用于从外部取电,避免 FireBeetle 供电不足的情况。
PCB 设计如下:
3D 预览如下:
做出来就是这样(美中不足的因为2个USB 母头的存在,这个板子稍微大一些。另外,这个芯片引脚比较密集,焊接费了一些功夫,如果你对自己焊接技术不放心,推荐直接 SMT 避免手工焊接):
接下来我们还需要设计和用户交互的部分,这一部分电路比较简单,有四个按钮,用于选择当前工作的接收器,另外有四个指示灯用于指示当前的工作接收器。
PCB设计如下,比较有意思的是:LED 是反贴(发光部分朝向PCB方向)在PCB背面的,然后这里使用了阻焊层,这样光线能够透过PCB照亮正面的字迹。
很遗憾 3D 预览无法展现这种效果
实物拍摄:
于是我们有了三个卡:FireBeetle,USB 键盘鼠标Shield和上面的用户交互界面卡。将三个插接在一起即可正常工作。上述就是发送端。
我们同样使用 ESP32 作为接收端,只是这里必须使用 ESP32 S2 主控,因为这款支持 USB Device ,可以方便的实现一个 USB 设备。你可以使用下面这种直接引出 USB Device的 ESP32 S2 开发板(上面一个是 CH340 下载端口,一个是 USB Device 端口):
除此之外,还可以直接购买ESP32 S2模块自己设计引出 USB Device 引脚。
*5、软件部分
同样的,软件也有2个,一个是 键盘鼠标信息发送端,另外一个是数据接收端,还要负责模拟键盘鼠标设备。
前面提到了我们使用 CH9350进行键盘鼠标数据的解析,具体数据格式在 Data Sheet 上有描述:
解析后的数据会出现在 FireBeetle 的 Serial2 上。
首先解说发送端的关键代码:
1.初始化时,使用下面的代码对于 CH9350 发送数据,这样 Ch9350只会发送有效的信息(就是说鼠标键盘有动作才会有数据出来)。
char SwitchToMode1[] = {0x57, 0xAB, 0x12, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x20};
for (int i = 0; i < sizeof(SwitchToMode1); i++) {
Serial2.write(SwitchToMode1[i]);
}
2.ESP32 自带了一个称作 ESP-NOW 的功能,只需要简单的代码就能让两块 ESP32 轻松实现无线互联。
// 初始化 ESP-NOW
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
// 设置发送数据回调函数
//esp_now_register_send_cb(OnDataSent);
// 绑定数据接收端,这里需要首先知道接收端的MAC,在文件开始处,使用下面的代码来给定 MAC
// 接收设备的 MAC 地址
// 红色 PCB 7C:DF:A1:06:70:EC
// 双TypeC 开发板 7C:DF:A1:06:5D:18
// 双TypeC 开发板 7C:DF:A1:06:77:C0
// 大绿色开发板 7C:DF:A1:06:70:A4
uint8_t Receiver1[] = {0x7C, 0xDF, 0xA1, 0x06, 0x70, 0xEC};
uint8_t Receiver2[] = {0x7C, 0xDF, 0xA1, 0x06, 0x5D, 0x18};
uint8_t Receiver3[] = {0x7C, 0xDF, 0xA1, 0x06, 0x70, 0xA4};
uint8_t Receiver4[] = {0x7C, 0xDF, 0xA1, 0x06, 0x77, 0xC0};
// 接下来绑定接收端的MAC
esp_now_peer_info_t peerInfo;
memcpy(peerInfo.peer_addr, Receiver1, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
3.使用 CurrentDevice 变量来记录当前接收端的动作(一些时候我们不会希望在多台机器上进行相同的键盘鼠标动作)
if (digitalRead(BUTTON1) == LOW) {
delay(100);
if (digitalRead(BUTTON1) == LOW) {
if ((CurrentDevice & BIT0) == 0) {
CurrentDevice |= BIT0;
} else {
CurrentDevice &= (~BIT0);
}
}
}
同样的,根据当前的CurrentDevice值设定LED
if (CurrentDevice != CurrentDeviceLast) {
digitalWrite(LED1, (CurrentDevice & BIT0 ? 1 : 0));
digitalWrite(LED2, (CurrentDevice & BIT1 ? 1 : 0));
digitalWrite(LED3, (CurrentDevice & BIT2 ? 1 : 0));
digitalWrite(LED4, (CurrentDevice & BIT3 ? 1 : 0));
CurrentDeviceLast = CurrentDevice;
Serial.println(CurrentDevice, HEX);
}
4.接收USB键盘鼠标的动作之后,根据前面的CurrentDevice状态,发送给不同的接收端,只需要esp_now_send()函数即可将数据送出:
if ((CurrentDevice & BIT0) != 0) {
// 发送数据
result = esp_now_send(Receiver1, Data, Counter);
// 检查数据是否发送成功
if (result == ESP_OK) {
// Serial.println("Sent success Receiver1");
}
else {
// Serial.println("Error sending the data to Receiver1");
}
}
上述就是发送端的关键代码,有兴趣的朋友可以直接阅读完整的代码实现。特别注意,因为我使用 DFRobot 的 FireBeetle, 所以编译时需要安装对应的 FireBeetle 支持包。
接下来介绍接收端的代码,相比发送,接收端要简单很多。同样只挑出来关键部分进行解说:
1. 无线接收的 ESP-NOW代码同样很简单,初始化之后注册回调函数即可,当收到无线信号后会自动进入esp_now_register_recv_cb()给定的函数中进行处理
// 初始化 ESP-NOW
WiFi.mode(WIFI_STA);
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// 设置接收数据回调函数
esp_now_register_recv_cb(OnDataRecv);
2. 收到数据后的处理代码中会使用verifyData()函数对接收到的数据进行校验,之后再根据数据类型使用device.directMS() 和device.directKB分别处理
// 数据接收回调函数
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
char *Starter = (char*)incomingData;
if (len>72) {return;}
while (Starter < (char *)(incomingData+len)) {
if (verifyData((char*)Starter, Starter[3]+4)) {
for (int i = 0; i < Starter[3]+4; i++) {
Serial.print(Starter[i], HEX);
Serial.print(" ");
}
Serial.println(" ");
// 如果是鼠标
if (((Starter[4] >> 4) & 0x3) == 0x2) {
device.directMS((char *)&Starter[5]);
}
//如果是键盘
if ((((Starter[4] >> 4) & 0x3) == 0x3)||(((Starter[4] >> 4) & 0x3) == 0x1)) {
device.directKB((char *)&Starter[5]);
}
}
Starter=&Starter[Starter[3]+4];
}//while (Start < (incomingDatalen))
}
3. 为了模拟USB键盘鼠标,我使用了 TinyUSB 库,这个库中自带一个KB MS复合设备的代码(hidcomposite.cpp) ,美中不足的是代码没有支持RAW 格式发送鼠标键盘数据。为此,在代码中添加如下函数支持这个功能,即将收到的数据原封不动的发送为鼠标或者键盘的动作:
//LABZ_Debug_Start
void HIDcomposite::directKB(char *data)
{
uint8_t keycode[6]={data[2],data[3],data[4],data[5],data[6],data[7]};
if (tud_hid_ready())
{
// KEYBOARD: convenient helper to send keyboard report if application
// use template layout report as defined by hid_keyboard_report_t
tud_hid_keyboard_report(report_keyboard,data[0], keycode);
}
}
//LABZ_Debug_End
//LABZ_Debug_Start
void HIDcomposite::directMS(char *data)
{
if (tud_hid_ready())
{
// uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal
tud_hid_mouse_report(report_mouse, data[0], data[1], data[2], data[3], 0);
}
}
//LABZ_Debug_End
*6、BOM清单
用户界面板
ID |
Name |
Designator |
Footprint |
Quantity |
Manufacturer Part |
Manufacturer |
Supplier |
Supplier Part |
Price |
1 |
KT-1206Red LED |
LED1,LED2,LED3,LED4 |
LED1206-R-RD |
4 |
KT-1206Red LED |
KENTO |
LCSC |
C49018 |
0.0738 |
2 |
10K |
R1,R2 |
R1206 |
2 |
CR1206F10K7P05 |
Ever Ohms Tech |
LCSC |
C245447 |
0.0391 |
3 |
FireBeelte ESP32 |
U1 |
DFROBOT FIREBEETLE |
1 |
DFRobot |
||||
4 |
10K |
R3,R4 |
R1206 |
2 |
CR1206F10K7P05 |
Ever Ohms Tech |
LCSC |
C245447 |
0.0391 |
5 |
1TS005A-2700-5001 |
DEV1,DEV2,DEV3,DEV4 |
SW-TH_4P-L6.0-W6.0-P4.50-LS6.5 |
4 |
1TS005A-2700-5001 |
HYP |
LCSC |
C319408 |
0.161
|
CH9350 Shield
ID |
Name |
Designator |
Footprint |
Quantity |
Manufacturer Part |
Manufacturer |
Supplier |
Supplier Part |
Price |
1 |
3.3uf |
C2 |
C1206 |
1 |
TCC1206X7R561K501DT |
CCTC |
LCSC |
C377112 |
0.1299 |
2 |
0.1uf |
C3 |
C1206 |
1 |
TCC1206X7R561K501DT |
CCTC |
LCSC |
C377112 |
0.1299 |
3 |
100uf |
C5,C4 |
C1206 |
2 |
TCC1206X7R561K501DT |
CCTC |
LCSC |
C377112 |
0.1299 |
4 |
2.54mm 1*2P |
DPOWER1 |
HDR-TH_2P-P2.54-V-M |
1 |
2.54mm 1*2P |
ReliaPro |
LCSC |
C36717 |
0.0372 |
5 |
短接电阻 |
JP2 |
短接焊盘 |
1 |
TTOOAADD |
||||
6 |
LED |
LED2,LED1 |
LED1206-R-RD |
2 |
KT-1206Red LED |
KENTO |
LCSC |
C49018 |
0.0738 |
7 |
1K |
R3,R4 |
R1206 |
2 |
R1206RXX102XJ04LTC |
Shenzhen Eyang Tech Development |
LCSC |
C267247 |
0.0201 |
8 |
CH9350L |
U1 |
LQFP-48_L7.0-W7.0-P0.50-LS9.0-BL |
1 |
CH9350L |
WCH |
LCSC |
C109472 |
25.69 |
9 |
FireBeelte ESP32 |
U2 |
DFROBOT FIREBEETLE |
1 |
DFRobot |
||||
10 |
U2 |
USB1,USB2 |
USB-A-TH_U-A-24DD-Y-1 |
2 |
U-A-24DD-Y-1 |
Rectangular Connectors - Contacts |
LCSC |
C193133 |
0.8234 |
11 |
USB-212-BCW |
USB-POWER |
USB-A-SMD_USB-212-BCW |
1 |
USB-212-BCW |
XUNPU |
LCSC |
C720521 |
0.4174 |
*7、大赛LOGO验证
* 8、演示您的项目并录制成视频上传
视频要求:请横屏拍摄,分辨率不低于1280×720,格式Mp4/Mov,单个视频大小限100M内;
视频标题:立创电赛:{项目名称}-{视频模块名称};如立创电赛:《自动驾驶》-团队介绍。
更多详情:https://diy.szlcsc.com/posts/06c94d90c2c447dfbd9ed7339ff4a5b1
设计图

BOM


评论