一、项目介绍
本项目设计并实现了一款基于 ESP32-S2-WROVER-N4R2 模块的RC遥控器,专用于坦克模型或其它多通道遥控设备。该遥控器支持最多 16个控制通道,采用 ESP-NOW 无线通信协议,具备低延迟、高响应、多输入源等特点。硬件上集成了摇杆、电位器、按键等多种输入方式,并预留了SPI/I2C扩展接口,便于后续功能升级。项目基于 PlatformIO 平台开发,代码结构清晰,易于维护与二次开发。
1.1 硬件介绍
本遥控器硬件核心为 ESP32-S2-WROVER-N4R2 模组,其主要外围电路包括:
- 2路摇杆输入:每路摇杆提供 X、Y 两个模拟轴,共实现 4通道模拟输入。
- 2路电位器输入:独立线性电位器,用于调节额外模拟量(如油门微调、舵量比例)。
- 2路摇杆按键:按压摇杆触发,可作为数字开关。
- 8路独立按键:用于模式切换、功能触发等。
- 供电系统:默认支持 1S锂电池(3.7V~4.2V);若更换LDO芯片(如从3.3V LDO换为宽压型),可支持2S电池(7.4V)。
- 预留接口:
- SPI 2.4G模拟接口(可外接NRF24L01等)
- SPI显示模块接口
- I2C OLED接口(如SSD1306)
1.2 功能概览
功能项 | 说明 |
|---|---|
通道数 | 最大支持16通道数据发送,当前硬件提供6个模拟通道 + 10个数字通道(可配置为开关通道) |
无线协议 | ESP-NOW(点对点或广播,低功耗、低延迟) |
输入设备 | 2×摇杆(4轴)、2×电位器、2×摇杆按键、8×独立按键 |
状态指示 | (可选)通过预留I2C接口连接OLED显示通道值及连接状态 |
开发平台 | PlatformIO + Arduino 框架 |
供电方式 | 1S锂电池(未集成则外部充电) |
1.3 设计思路
- 模块化输入采集:每个输入设备(摇杆、电位器、按键)独立读取,软件中统一转换为0~255或0~100%的标准化数值。
- 通道映射:物理输入可灵活映射到16个逻辑通道中,例如摇杆左X→通道1,左Y→通道2,电位器1→通道5等。
- 低延迟通信:ESP-NOW无需连接路由器,直接MAC层通信,延迟<10ms,适合实时遥控。
- 扩展性:预留SPI/I2C接口,未来可增加显示屏、2.4G备份链路、舵机反馈等。
二、功能实现
2.1 硬件设计
硬件框图

2.2 软件流程图

2.3 软件代码实现
以下为核心代码片段(基于PlatformIO + Arduino框架)。
2.3.1. 数据结构定义
定义控制数据包结构体(与接收端约定一致):
struct Data_Package {
uint8_t leftJoystickX; // 左摇杆 X
uint8_t leftJoystickY; // 左摇杆 Y
uint8_t rightJoystickX; // 右摇杆 X
uint8_t rightJoystickY; // 右摇杆 Y
uint8_t leftJoystickBtn:1; // 左摇杆按键
uint8_t rightJoystickBtn:1; // 右摇杆按键
uint8_t leftSwitch1:1; // 左按键开关1
uint8_t leftSwitch2:1; // 左按键开关2 和 左按键开关1 反相
uint8_t rightSwitch1:1; // 右按键开关1
uint8_t rightSwitch2:1; // 右按键开关2 和 右按键开关1 反相
uint8_t leftPotentiometer; // 左电位器
uint8_t rightPotentiometer; // 右电位器
};
2.3.2. 初始化ESP-NOW
// 初始化 ESP-NOW
Serial.println("Initializing ESP-NOW...");
delay(100);
WiFi.mode(WIFI_STA);
Serial.println("InitESPNow");
// Init ESPNow with a fallback logic
InitESPNow();
// 设置发送数据回调函数
esp_now_register_send_cb(OnDataSent);
2.3.3. 模拟输入读取(ADC)
int potValues[NUMBER_OF_POT_SAMPLES];
int potValues2[NUMBER_OF_POT_SAMPLES];
int potValues3[NUMBER_OF_POT_SAMPLES];
int potValues4[NUMBER_OF_POT_SAMPLES];
// 连续采样 NUMBER_OF_POT_SAMPLES 个数据,间隔 DELAY_BETWEEN_SAMPLES 毫秒。这里不应该这样做,虽然没有什么大问题,但会使的采样慢,导致整个运行效率过低 TODO 待优化
for (int i = 0 ; i < NUMBER_OF_POT_SAMPLES ; i++) {
potValues[i] = analogRead(LEFT_JOYSTICK_X_PIN);
potValues2[i] = analogRead(LEFT_JOYSTICK_Y_PIN);
potValues3[i] = analogRead(RIGHT_JOYSTICK_X_PIN);
potValues4[i] = analogRead(RIGHT_JOYSTICK_Y_PIN);
delay(DELAY_BETWEEN_SAMPLES);
}
// 计算 左 摇杆 X 轴采样均值。
int potValue = 0;
for (int i = 0 ; i < NUMBER_OF_POT_SAMPLES ; i++) {
potValue += potValues[i];
}
potValue = potValue / NUMBER_OF_POT_SAMPLES;
LX_read = map(potValue, 0, 4095, 0, 255);
// 计算 左 摇杆 Y 轴采样均值。
int potValue2 = 0;
for (int i = 0 ; i < NUMBER_OF_POT_SAMPLES ; i++) {
potValue2 += potValues2[i];
}
potValue2 = potValue2 / NUMBER_OF_POT_SAMPLES;
LY_read = map(potValue2, 0, 4095, 255, 0);
// 计算 右 摇杆 X 轴采样均值。
int potValue3 = 0;
for (int i = 0 ; i < NUMBER_OF_POT_SAMPLES ; i++) {
potValue3 += potValues3[i];
}
potValue3 = potValue3 / NUMBER_OF_POT_SAMPLES;
RX_read = map(potValue3, 0, 4095, 255, 0);
// 计算 右 摇杆 Y 轴采样均值。
int potValue4 = 0;
for (int i = 0 ; i < NUMBER_OF_POT_SAMPLES ; i++) {
potValue4 += potValues4[i];
}
potValue4 = potValue4 / NUMBER_OF_POT_SAMPLES;
RY_read = map(potValue4, 0, 4095, 255, 0);
2.3.4. 通道映射与发送
data.leftJoystickX = LX_to_send;
data.leftJoystickY = LY_to_send;
data.rightJoystickX = RX_to_send;
data.rightJoystickY = RY_to_send;
data.leftJoystickBtn = !digitalRead(LEFT_JOYSTICK_BUTTON_PIN);
data.rightJoystickBtn = !digitalRead(RIGHT_JOYSTICK_BUTTON_PIN);
data.upBtnLeft = !digitalRead(LEFT_UP_BUTTON_PIN);
data.downBtnLeft = !digitalRead(LEFT_DOWN_BUTTON_PIN);
data.leftBtnLeft = !digitalRead(LEFT_LEFT_BUTTON_PIN);
data.rightBtnLeft = !digitalRead(LEFT_RIGHT_BUTTON_PIN);
data.upBtnRight = !digitalRead(RIGHT_UP_BUTTON_PIN);
data.downBtnRight = !digitalRead(RIGHT_DOWN_BUTTON_PIN);
data.leftBtnRight = !digitalRead(RIGHT_LEFT_BUTTON_PIN);
data.rightBtnRight = !digitalRead(RIGHT_RIGHT_BUTTON_PIN);
data.leftPotentiometer = 255 - map(lp, 120, 4095, 0, 255);
data.rightPotentiometer = map(rp, 120, 4095, 0, 255);
// 发送数据 send data
void sendData() { //uint8_t data
esp_now_send(broadcastAddress, (uint8_t *) &data, sizeof(data));
}
三、功能展示
硬件展示
原理图

PCB 3D预览

PCB实物

实测效果
- 模拟通道响应:摇杆和电位器线性度良好,ADC 12位分辨率,经映射后输出0~255连续变化。
- 数字按键:8个独立按键及摇杆按键无抖动误触发(软件已做简单去抖)。
- 无线通信:ESP-NOW在开阔地有效距离约100米,数据丢包率低于1%(每1000包重传小于10次)。
- 通道数验证:通过串口监视器打印发送的16个通道值,全部可正确接收并解析。
测试环境使用一块 Seeed Studio XIAO ESP32C3 开发板作为接收机,接收遥控器发送的 ESPNOW 数据包并通过串口发送至上位机,上位机以图形方式展示各通道的数值。
四、总结
遇到的问题
- 忘记增加板载充电电路(TP4056等),无法实现Type-C直充还是有些不太方便的。
- 摇杆的质量比较差,每过一段时间就会出现飘移的现象。
- 右电位器在旋转到最小位置时会出现跳变,导致采集到的数值0偶尔会跳变成为255。
心得体会
本项目成功基于ESP32-S2设计并实现了一款功能完备、扩展性强的RC遥控器。6个模拟输入 + 10个数字输入,满足大多数坦克、机器人、航模控制需求。 ESP-NOW协议在保证低延迟的同时,具备较好的抗干扰能力。通道映射、数据比例均可通过修改代码灵活调整,便于适配不同接收机。本项目代码已开源,所有原理图及PlatformIO工程文件均可提供,可作为DIY遥控器或教学实验平台使用。
最后,感谢硬禾学堂联合 DigiKey 推出的这次活动!此次活动带给我许多宝贵实践经验和机会,在此表示感谢!