一、项目简介
本项目设计并实现了一款基于ESP32-C3-MINI-1U模块的遥控飞机控制板,专门用于将36/48手抛飞机改装为双发差速航模遥控飞机。控制板采用1S锂电池供电,集成2路大电流直流电机驱动(最大4.2A/路),支持双发空心杯电机的差速转向控制,并通过油门大小直接控制飞机的升降运动。通信方面采用ESP-NOW协议,具有低延迟、高可靠性的特点,配合预留的4路舵机驱动接口,为后续功能扩展提供了可能。整个项目基于PlatformIO平台开发,充分发挥了ESP32-C3的高性能和低功耗优势。
二、项目介绍
2.1 硬件介绍
控制板核心硬件组成如下:
- 主控模块:ESP32-C3-MINI-1U,集成RISC-V 32位单核处理器,主频160MHz,内置4MB Flash,支持Wi-Fi和BLE 5.0,功耗低、响应快,非常适合遥控模型应用。
- 电机驱动:2路独立直流电机驱动电路,每路最大持续电流4.2A,峰值电流可达5A以上,采用低内阻MOSFET搭建,支持PWM调速,专门适配空心杯电机。
- 舵机驱动:4路PWM信号输出接口(本项目中未实际使用舵机,但电路已预留,可驱动标准航模舵机)。
- 电源管理:1S锂电池输入(3.7V~4.2V),集成欠压保护、过流保护,并为ESP32-C3及电机驱动提供稳定电压。
- 通信接口:ESP-NOW无线协议,工作在2.4GHz频段,无需路由器即可实现点对点或多对一通信。
- 其他接口:电机接线端子、电池接头、烧录调试接口(UART)。
2.2 功能概览
功能模块 | 描述 |
|---|---|
双发差速控制 | 通过左右空心杯电机的转速差实现转向,无需方向舵 |
油门控制升降 | 油门推杆控制电机总功率,速度增加时机头仰起,实现爬升;收油则下降 |
无线遥控 | 基于ESP-NOW,遥控器(另一ESP32设备)发送油门、转向等指令 |
电池管理 | 1S ~ 2S 锂电池供电 |
扩展预留 | 4路舵机接口,可用于副翼、升降舵、方向舵等传统舵面控制 |
2.3 设计思路
- 差速转向原理:固定翼飞机在无副翼/方向舵的情况下,通过左右发动机推力差产生偏航力矩,实现转向。本设计左电机转速高于右电机时,飞机右转(反扭效应),反之左转。
- 油门与俯仰关系:加大油门时,飞机空速增加,升力增大,水平尾翼(原手抛机固定升降舵)产生正俯仰力矩,机头上仰爬升;收油时机头下沉。利用这一特性无需额外舵机即可控制升降。
- ESP-NOW优势:相比传统PPM/PWM遥控,ESP-NOW延迟更低(<10ms),抗干扰能力强,支持双向通信(可回传电池电压等遥测数据),且开发简单。
- 驱动能力设计:空心杯电机启动瞬间电流较大(可达3-5A),因此选择4.2A驱动能力,并采用大电流走线和散热设计,确保稳定工作。
- 模块化软件:将电机控制、通信协议、电压监测等功能封装为独立模块,便于调试和后续扩展。
三、功能实现
3.1 硬件设计
3.1.1 硬件框图

3.1.2 关键电路说明
- 电机驱动电路:采用双N沟道MOSFET(如AO4612或SI2302组合)组成H桥,每个H桥由4个MOSFET构成,通过ESP32-C3的两路互补PWM(带死区)控制正反转和调速。实际差速控制中仅需单向驱动(前进),故可简化为单侧PWM+使能脚。
- 电源分配:1S电池直接为电机供电(经电流采样电阻),同时通过LDO(如ME6211C33M5G)降压至3.3V为ESP32-C3和舵机逻辑供电。
- 舵机接口:GPIO输出50Hz PWM信号,脉宽500us~2500us对应0~180度。
3.2 软件设计
3.2.1 软件流程图

3.2.2 软件代码实现
基于PlatformIO,框架为Arduino for ESP32-C3。核心代码片段如下:
platformio.ini
[platformio]
description = 遥控飞机控制板
[env:esp32-c3-Receiver]
platform = espressif32
board = seeed_xiao_esp32c3
framework = arduino
主程序 receiver.cpp
void init() {
Serial.begin(115200);
// 初始化输入引脚
uint8_t pwm_channel = 0;
for(int i = 0; i < sizeof(pins) / sizeof(PinConfig); i ++) {
if(pins[i].mode == PIN_MOD_LEDC) {
// 坦克模型PWM频率设定为 1 / 10 ,也就是 1 KHz
ledcSetup(pwm_channel, PWM_FREQUENCY, PWM_RESOLUTION_BITS);
ledcAttachPin(pins[i].pin, pwm_channel);
ledcWrite(pwm_channel++, 0);
} else {
pinMode(pins[i].pin, pins[i].mode);
}
}
// 初始化 ESP-NOW
Serial.println("Initializing ESP-NOW ...");
delay(100);
WiFi.mode(WIFI_STA);
// This is the mac address of the Slave in AP Mode
Serial.print("STA MAC: "); Serial.println(WiFi.macAddress());
// Init ESPNow with a fallback logic
InitESPNow();
// 设置接收数据回调函数
esp_now_register_recv_cb(OnDataRecv);
resetData();
}
void run() {
unsigned long currentMillis = millis();
if(!lockState) { // 已解锁,根据遥控器信号输出
// 油门最大档位
maxThrottle = 0 < data.leftPotentiometer ? data.leftPotentiometer : 255;
// 转向最大档位
maxRudder = 0 < data.rightPotentiometer ? data.rightPotentiometer : 255;
// 油门位置
throttlePosition = map(data.leftJoystickY, 0, 255, 0, maxThrottle);
// 转向档位
rudderPosition = map(data.rightJoystickX, 0, 255, 0, maxRudder);
// 归一化处理
throttle = throttlePosition / 255.0;
rudder = (rudderPosition - maxRudder / 2) / 127.0;
// 转向增加转向微调值
rudder = rudder + config.rudderFineTuningValue;
// 计算差速
baseSpeed = throttle;
leftSpeed = baseSpeed * (1 + config.k * rudder);
rightSpeed = baseSpeed * (1 - config.k * rudder);
// 限幅输出
leftSpeed = constrain(leftSpeed, 0, 1);
rightSpeed = constrain(rightSpeed, 0, 1);
// 转换为PWM信号
leftSpeedPwm = leftSpeed * ((1 << PWM_RESOLUTION_BITS) - 1);
rightSpeedPwm = rightSpeed * ((1 << PWM_RESOLUTION_BITS) - 1);
// 输出占比
ledcWrite(0, leftSpeedPwm);
ledcWrite(1, rightSpeedPwm);
} else { // 锁定状态,关闭输出
// 左右摇杆拉低并保持3秒 解锁 start -----------------------------------------------------
if(0 == data.leftJoystickY && 0 == data.rightJoystickY && !unlockStart) {
unlockStart = true;
unlockStartMillis = currentMillis;
}
if(0 == data.leftJoystickY && 0 == data.rightJoystickY) {
if(currentMillis - unlockStartMillis >= unlockHoldTime) { // 解锁成功
unlockStart = false;
lockState = false;
}
}
// 左右摇杆拉低并保持3秒 解锁 end -------------------------------------------------------
}
// 转向微调 start -----------------------------------------------------
if(rudderFineTuningState) { // 转向微调状态
if(0 == data.rightJoystickBtn) {
rudderFineTuningStart = true;
} else if(rudderFineTuningStart) {
rudderFineTuningState = false;
rudderFineTuningStartMillis = 0;
}
if(1 == data.leftBtnRight && (currentMillis - rudderFineTuningKeyCoolingMillis) >= rudderFineTuningKeyCoolingTime) { // 微调状态下,右边左键按下,左微调,值 -0.01
rudderFineTuningKeyCoolingMillis = currentMillis;
config.rudderFineTuningValue -= 0.01;
}
if(1 == data.rightBtnRight && (currentMillis - rudderFineTuningKeyCoolingMillis) >= rudderFineTuningKeyCoolingTime) { // 微调状态下,右边右键按下,右微调,值 +0.01
rudderFineTuningKeyCoolingMillis = currentMillis;
config.rudderFineTuningValue += 0.01;
}
} else {
if(1 == data.rightJoystickBtn) {
if(0 == rudderFineTuningStartMillis) {
rudderFineTuningStartMillis = currentMillis;
} else if(currentMillis - rudderFineTuningStartMillis >= rudderFineTuningHoldTime) {
rudderFineTuningState = true;
rudderFineTuningStart = false;
}
} else {
rudderFineTuningStartMillis = 0;
}
}
// 转向微调 end -------------------------------------------------------
}
四、功能展示
4.1 硬件展示
原理图

PCB 3D预览

PCB实物

4.2 实际测试场景
将控制板安装在改装后的手抛飞机上(36cm翼展EPP材质),替换原配重块位置,固定两个720空心杯电机于机翼后缘两侧,正反桨各一。遥控端使用另一个ESP32-S2开发板加摇杆模块,同样运行ESP-NOW发送程序。
焊接好的控制板只有4克,非常适合用于手抛飞机的改造,实际测试36或48mm的手抛飞机均可使用该控制板进行改造。这里我改造了一架36mm的手抛飞机,仅使用了2个720空心杯电机,使用油门控制升降,差速控制转向,改造完成后仅43克左右,轻轻松松爽飞。

图:改装后的手抛飞机及控制板安装位置
4.3 测试结果
测试项目 | 结果 | 说明 |
|---|---|---|
通信距离 | >150米(开阔地) | ESP-NOW稳定,无丢包 |
电机差速响应 | 良好 | 转向灵敏,地面滑行可完成U型转弯 |
油门控制升降 | 成功 | 全油门时机头明显上仰,收油则平飘 |
续航时间 | 约8分钟 | 搭配300mAh 1S电池,连续飞行 |
4.3 演示视频截图
五、总结
本项目成功设计并实现了一款基于ESP32-C3的高性能遥控飞机控制板,具备以下特点:
- 低成本高性能:ESP32-C3模块价格低廉,但处理能力和无线性能远超传统航模接收机,且支持二次开发。
- 动力强劲:2路4.2A驱动可轻松驱动8520或1020空心杯电机,满足手抛机改装需求。
- 差速控制简单有效:无需复杂舵面即可实现转向和升降,极大简化了改装复杂度。
- 通信可靠:ESP-NOW协议提供毫秒级响应,实测抗干扰能力强于普通2.4G玩具遥控器。
- 扩展性强:预留4路舵机接口,后续可升级为全功能航模(副翼、升降舵、方向舵)。
待改进方向:
- 增加姿态传感器(MPU6050),实现增稳模式,降低飞行难度。
- 增加蓝牙或Wi-Fi配置页面,方便修改遥控器配对信息。
- 设计专用PCB并优化布局,减小体积和重量。
本控制板为手抛机电动化改装提供了完整的解决方案,同时也可作为学习ESP32-C3电机控制、ESP-NOW无线通信的优秀案例。项目所有硬件设计文件及源代码均已开源,可供爱好者参考和二次开发。
最后,感谢硬禾学堂联合 DigiKey 推出的这次活动,使我的设计能力进一步提升!