【ESP32C3/C6数传电台Trae实战开发】
摘要:本文介绍利用Trae开发环境和豆包大模型快速开发ESP32C3/C6数传电台系统。Trae作为新一代AI辅助工具,提供智能代码补全、上下文理解等功能,相比传统开发方式更高效。文章详细讲解系统框架、配置流程和硬件准备,包括自动配对、双向数据传输等核心功能实现。读者可通过注册火山引擎获取Trae开发工具,利用AI辅助完成ESP32开发项目。
ESP32C3/C6数传电台Trae实战开发
ESP32接入国产大模型之腾讯混元
前言
随着物联网技术的快速发展,ESP32系列芯片因其强大的性能和丰富的功能,成为了物联网开发的热门选择。而ESPNOW作为ESP32的一种高速无线通信协议,无需WiFi网络即可实现设备间的点对点通讯,非常适合传感器数据传输、远程控制等场景。
本文将详细介绍如何利用Trae开发环境结合豆包大模型,现在都直接用trae进行esp32的开发,完全可以代替arduino和vs code,编译下载全代码AI,快速实现ESP32C3/C6之间的ESPNOW数传电台系统。Trae作为新一代AI辅助开发工具,能大幅提升开发效率,让代码编写更加智能高效。
🔧 Trae开发利器推荐:使用豆包大模型辅助开发,让ESP32开发事半功倍!
请大家点击豆包火山注册地址,不注册是不能完成下面的实验哦:https://t.vncps.com/5LOve
谢谢啦大家的支持💖💖💖
一、豆包大模型与Trae开发环境
1.1 豆包大模型在ESP32开发中的应用
豆包大模型可以帮助我们:
- 快速生成代码:根据需求描述自动生成ESP32相关代码
- 解决技术问题:解答开发过程中遇到的技术难题
- 优化代码结构:提供代码优化建议,提高代码质量
- 学习新技术:快速了解ESP32的新功能和使用方法
1.2 Trae开发环境优势
Trae作为新一代AI辅助开发工具,相比传统VSCode+PlatformIO有以下优势:
- AI智能补全:基于豆包大模型的智能代码补全,大幅提升编码效率
- 上下文理解:能够理解整个项目的上下文,提供更准确的开发建议
- 无缝集成:内置PlatformIO支持,无需额外配置
- 实时协作:支持多人实时协作开发
- 云端同步:代码自动云端备份,安全可靠
1.3 注册火山引擎并使用Trae
- 访问火山引擎注册地址:https://t.vncps.com/5LOve
- 完成注册并登录
- 订阅火山CodePlan,畅享Trae中所有模型
请大家点击豆包火山注册地址:https://t.vncps.com/5LOve
方舟 Coding Plan 支持 Doubao、GLM、DeepSeek、Kimi 等模型,工具不限,现在订阅折上9折,低至8.9元,订阅越多越划算!立即订阅:https://volcengine.com/L/2p_L1OTLQZw/ 邀请码:TVGNH4JT
访问火山方舟 Coding Plan 新用户特惠活动,按需订阅套餐。套餐介绍参见套餐概览https://www.volcengine.com/docs/82379/1925114?lang=zh。
在开通管理页面https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&advancedActiveKey=subscribe选择或切换目标模型,无需在工具中额外变更模型配置。
-
下载并安装Trae客户端
官网地址:https://www.trae.cn/
-
在Trae中Platformio创建ESP32项目

二、项目概述
本项目实现了一个基于ESP32C3/C6的ESPNOW数传电台系统,具有以下功能:
- 设备自动配对与连接管理
- 双向数据透明传输
- 配置信息持久化存储
- AT指令配置界面
- 连接状态实时监测
2.1 系统框架图
2.2 配置流程图
三、硬件准备
3.1 所需零件
- ESP32C3/C6开发板(2块)

【下单链接】[https://s.click.taobao.com/y793qHn](https://s.click.taobao.com/y793qHn,一定要安装天线测试
【下单链接】https://s.click.taobao.com/FUJ3xfn,ESP32C3 PRO MINI开发板板载ESP32-C3FH4芯片模块wifi 蓝牙开发板也是可以的,我采用这个小巧模块自带陶瓷天线
- WIN10/WIN11电脑:编写代码调试功能
- USB数据线x2:用于烧录代码和串口通信。
- 天线(推荐使用外置天线,增强通信距离)
- USB数据线(2条)
- 面包板及杜邦线(可选,用于扩展)
3.2 硬件连接
- LED指示灯:连接到GPIO15(ESP32C6)或GPIO8(ESP32C3)
- 天线:连接到开发板的天线接口

四、软件配置
首先需要VScode安装Platformio,然后Trae安装Platformio插件调用即可
全程交给Trae进行编程外加写博客
4.1 PlatformIO配置(platformio.ini)
下面是esp32c3配置代码,如果是c6就取消注释选择上面配置
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
; [env:seeed-xiao-esp32-c6]
; platform = Seeed Studio
; board = seeed-xiao-esp32-c6
[env:seeed-xiao-esp32-c3]
platform = Seeed Studio
board = seeed-xiao-esp32-c3
framework = arduino
; 监视器波特率
monitor_speed = 115200
lib_deps =
SPIFFS
board_build.partitions = partitions.csv
; 启用 C++17(推荐)
build_flags = -std=gnu++17
upload_resetmethod = ck
配置说明:
- 支持ESP32C3/C6开发板
- 使用Arduino框架开发
- 依赖库包括SPIFFS(文件系统)
- 自定义分区表配置
- 启用C++17标准
4.2 分区表配置(partitions.csv)
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x140000,
app1, app, ota_1, 0x150000,0x140000,
spiffs, data, spiffs, 0x290000,0x160000,
coredump, data, coredump,0x3F0000,0x10000,
分区说明:
nvs:用于存储非易失性数据otadata:OTA更新数据app0/app1:应用程序分区(支持OTA双分区)spiffs:SPIFFS文件系统分区,用于存储配置文件coredump:崩溃转储分区
五、核心代码分析
5.1 主程序结构(main.cpp)
// 9C:13:9E:CC:3B:88
// 9C:13:9E:CB:06:14
// ... existing code ...
#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>
#include <esp_wifi.h>
#include <string.h>
#include <SPIFFS.h> // 添加SPIFFS支持
#define LED_PIN 8 //esp32c3 GPIO8 连接到LED,esp32c6 GPIO15
// #define LED_PIN 15 //esp32c3 GPIO8 连接到LED,esp32c6 GPIO15
#define CONFIG_FILE "/espnow_config.json" // SPIFFS配置文件路径
#define CURRENT_VERSION "1.0" // 当前版本号
static uint8_t selfMac[6];
static uint8_t peerMac[6];
static bool isPaired = false;
static bool isConnected = false;
static bool configMode = false;
String serialBuffer = ""; // 用于接收串口数据的缓冲区
esp_now_peer_info_t peer;
const unsigned long CONNECTION_TIMEOUT = 10000; // 增加超时时间
unsigned long lastBlink = 0;
unsigned long lastSent = 0;
unsigned long lastReceived = 0;
// 格式化 MAC 地址
String formatMac(const uint8_t *mac)
{
char buf[18];
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return String(buf);
}
// ================== SPIFFS ==================
// 保存配对MAC地址和版本信息到SPIFFS
bool saveConfig()
{
if (!SPIFFS.begin(true))
{
Serial.println("❌ An error occurred while mounting SPIFFS");
return false;
}
File configFile = SPIFFS.open(CONFIG_FILE, "w");
if (!configFile)
{
Serial.println("❌ Failed to open config file for writing");
return false;
}
// 创建JSON格式的数据
String configJson = "{";
configJson += "\"version\":\"" + String(CURRENT_VERSION) + "\",";
configJson += "\"peer_mac\":\"" + formatMac(peerMac) + "\"";
configJson += "}";
configFile.print(configJson);
configFile.close();
Serial.println("✅ Config saved to SPIFFS");
return true;
}
bool parseMacAddress(const String &input, uint8_t *outMac)
{
String clean = input;
clean.replace(":", "");
clean.replace("-", "");
clean.replace(" ", "");
if (clean.length() != 12)
return false;
for (int i = 0; i < 6; i++)
{
String byteStr = clean.substring(i * 2, i * 2 + 2);
outMac[i] = (uint8_t)strtol(byteStr.c_str(), NULL, 16);
}
return true;
}
// 从SPIFFS加载配对MAC地址和版本信息
bool loadConfig()
{
if (!SPIFFS.begin(true))
{
Serial.println("❌ An error occurred while mounting SPIFFS");
return false;
}
File configFile = SPIFFS.open(CONFIG_FILE, "r");
if (!configFile)
{
Serial.println("❌ Failed to open config file for reading");
return false;
}
String json = configFile.readString();
configFile.close();
// 解析JSON数据
int versionStart = json.indexOf("\"version\":\"") + 11;
int versionEnd = json.indexOf("\"", versionStart);
int macStart = json.indexOf("\"peer_mac\":\"") + 12;
int macEnd = json.indexOf("\"", macStart);
if (versionStart > 11 && macStart > 12) // 检查是否找到字段
{
String loadedVersion = json.substring(versionStart, versionEnd);
String macStr = json.substring(macStart, macEnd);
Serial.println("Loaded version: " + loadedVersion);
Serial.println("Loaded MAC: " + macStr);
// 尝试解析MAC地址
return parseMacAddress(macStr, peerMac);
}
return false;
}
// 清除SPIFFS中的配置
void clearConfig()
{
if (!SPIFFS.begin(true))
{
Serial.println("❌ An error occurred while mounting SPIFFS");
return;
}
if (SPIFFS.exists(CONFIG_FILE))
{
SPIFFS.remove(CONFIG_FILE);
Serial.println("✅ Config file cleared from SPIFFS");
}
}
// ========================================
bool attemptPairWith(const uint8_t *targetMac)
{
memset(&peer, 0, sizeof(peer));
memcpy(peer.peer_addr, targetMac, 6);
peer.channel = 0;
peer.encrypt = false;
if (esp_now_add_peer(&peer) != ESP_OK)
return false;
const char *testMsg = "CONN";
return (esp_now_send(targetMac, (uint8_t *)testMsg, strlen(testMsg)) == ESP_OK);
}
// 连接检测任务
void connectionCheckTask(void *pvParameters)
{
while (1)
{
if (attemptPairWith(peerMac))
{
isConnected = true;
Serial.println("✅ Reconnected!");
digitalWrite(LED_PIN, LOW); // 点亮
}
else
{
isConnected = false; // 修正:断开连接时更新状态
// Serial.println("❌ Connection lost!");
digitalWrite(LED_PIN, HIGH); // 熄灭
}
vTaskDelay(pdMS_TO_TICKS(CONNECTION_TIMEOUT)); // 增加检测间隔到5秒
}
}
void printhelp()
{
Serial.println("\nESP32-C3 ESP-NOW Enhanced (with Debug Log)");
Serial.println("Enter config mode with '+++', then use AT commands:");
Serial.println(" AT+HELP Show this help");
Serial.println(" AT+PAIR=<MAC> Pair with device");
Serial.println(" AT+CLEAR Clear paired device");
Serial.println(" AT+MAC Get own MAC");
Serial.println(" AT+FRIEND Get paired device MAC");
Serial.println(" AT+TEST Test communication");
Serial.println(" AT+RESTART Restart device");
Serial.println(" ATO Exit config mode");
}
// 处理AT指令
void handleATCommand(const String &command)
{
if (command.equalsIgnoreCase("AT+TEST"))
{
if (isPaired)
{
String testMsgStr = formatMac(selfMac) + " send message to " + formatMac(peerMac) + ":TEST";
const char *testMsg = testMsgStr.c_str();
if (esp_now_send(peerMac, (uint8_t *)testMsg, strlen(testMsg)) == ESP_OK)
{
Serial.println("OK");
}
else
{
Serial.println("ERROR");
}
}
else
{
Serial.println("ERROR: Not connected");
}
}
else if (command.startsWith("AT+PAIR="))
{
String macStr = command.substring(8);
if (parseMacAddress(macStr, peerMac))
{
if (attemptPairWith(peerMac))
{
saveConfig(); // 使用新的SPIFFS保存函数
Serial.println("SaveConfig OK");
}
else
{
Serial.println("paired is ERROR ,please try it after AT+CLEAR ");
}
}
else
{
Serial.println("ERROR: Invalid MAC");
}
}
else if (command.equalsIgnoreCase("AT+CLEAR"))
{
clearConfig(); // 使用新的SPIFFS清除函数
Serial.println("SaveConfig OK");
}
else if (command.equalsIgnoreCase("AT+MAC"))
{
Serial.println(formatMac(selfMac));
}
else if (command.equalsIgnoreCase("AT+FRIEND"))
{
if (isPaired)
{
Serial.println(formatMac(peerMac));
}
else
{
Serial.println("ERROR: No paired device");
}
}
else if (command.equalsIgnoreCase("AT+RESTART"))
{
Serial.println("Restart OK");
delay(100);
ESP.restart();
}
else if (command.equalsIgnoreCase("AT+HELP"))
{
printhelp();
}
else if (command.equalsIgnoreCase("ATO"))
{
// 退出配置模式
configMode = false;
Serial.println("Exit Config Mode OK");
}
else
{
Serial.println("ERROR: Unknown command");
}
}
// 接收回调:带调试打印
void onDataRecv(const esp_now_recv_info_t *esp_now_info, const uint8_t *data, int len)
{
const uint8_t *mac = esp_now_info->src_addr; // 获取发送方的MAC地址
lastReceived = millis(); // 更新最后接收时间
// 检查是否来自配对设备
if (memcmp(mac, peerMac, 6) == 0) {
isConnected = true; // 确认连接状态
isPaired = true; // 设置配对标志
}
// 直接转发数据到串口,保持原始数据完整性
Serial.write(data, len);
}
void setup()
{
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH); // 初始灭灯
Serial.begin(115200);
Serial.println("Initializing SPIFFS...");
// 初始化SPIFFS
if (!SPIFFS.begin(true))
{
Serial.println("❌ An error occurred while mounting SPIFFS");
// 如果SPIFFS挂载失败,尝试格式化
if (SPIFFS.format())
{
Serial.println("✅ SPIFFS formatted successfully");
if (!SPIFFS.begin(false))
{
Serial.println("❌ Failed to mount SPIFFS after formatting");
}
}
else
{
Serial.println("❌ Failed to format SPIFFS");
}
}
else
{
Serial.println("✅ SPIFFS mounted successfully");
}
printhelp();
randomSeed(analogRead(0));
WiFi.mode(WIFI_STA);
esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
esp_wifi_set_promiscuous(false);
if (esp_now_init() != ESP_OK)
{
Serial.println("❌ ESP-NOW init failed!");
return;
}
esp_now_register_recv_cb(onDataRecv);
WiFi.macAddress(selfMac); // 使用WiFi库获取MAC地址
Serial.printf("\n>>> MY MAC: %s <<<\n\n", formatMac(selfMac).c_str());
if (loadConfig()) // 使用新的SPIFFS加载函数
{
Serial.printf("💾 Loaded peer: %s\n", formatMac(peerMac).c_str());
if (attemptPairWith(peerMac))
{
isPaired = true; // 设置配对标志
isConnected = true; // 设置连接标志
lastReceived = millis(); // 记录最后接收时间
Serial.println("✅ Resumed pairing!");
}
else
{
Serial.println("❌ Resume failed");
clearConfig(); // 使用新的SPIFFS清除函数
}
}
// 创建连接检测任务
xTaskCreate(
connectionCheckTask,
"connectionCheckTask",
2048,
NULL,
5,
NULL);
Serial.println("🟢 Connection check task started");
}
void loop()
{
if (Serial.available())
{
// 逐字节读取,避免数据粘连
while(Serial.available()) {
char c = Serial.read();
serialBuffer += c;
// 检测"+++"序列
if (serialBuffer.length() >= 3 &&
serialBuffer.endsWith("+++"))
{
// 移除"+++"前的部分,保留可能的其他字符
int plusIndex = serialBuffer.lastIndexOf("+++", serialBuffer.length() - 3);
if(plusIndex >= 0) {
serialBuffer = serialBuffer.substring(plusIndex);
// 进入配置模式
configMode = true;
Serial.println("AT OK");
printhelp();
serialBuffer = ""; // 清空缓冲区
break; // 跳出while循环
}
}
}
if (!configMode)
{
// 数传模式:发送数据到对端
if (isPaired && serialBuffer.length() > 0) // 只要配对就可以发送数据
{
digitalWrite(LED_PIN, LOW); // 点亮
esp_now_send(peerMac, (uint8_t *)serialBuffer.c_str(), serialBuffer.length());
lastSent = millis(); // 更新最后发送时间
digitalWrite(LED_PIN, HIGH); // 熄灭
serialBuffer = ""; // 发送后清空缓冲区
}
else if(!isPaired)
{
Serial.println("❌ Not paired with any device");
serialBuffer = ""; // 清空缓冲区
}
}
else
{
// 检查是否退出配置模式命令
if (serialBuffer.endsWith("\n") || serialBuffer.endsWith("\r"))
{
// 配置模式:处理AT指令
serialBuffer.trim();
if (!serialBuffer.isEmpty())
{
// 处理AT指令
handleATCommand(serialBuffer);
serialBuffer = ""; // 处理完后清空
}
}
}
}
// 数传模式下,数据已通过串口实时发送,无需定时发送模拟数据
delayMicroseconds(10); // 使用微秒延迟,1000等同于1毫秒
}
// ... existing code ...
5.1.1 头文件与宏定义
#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>
#include <esp_wifi.h>
#include <string.h>
#include <SPIFFS.h> // 添加SPIFFS支持
#define LED_PIN 15 //esp32c3 GPIO8 连接到LED,esp32c6 GPIO15
#define CONFIG_FILE "/espnow_config.json" // SPIFFS配置文件路径
#define CURRENT_VERSION "1.0" // 当前版本号
esp32c3 GPIO8 连接到LED,esp32c6 GPIO15
#define LED_PIN 8 //esp32c3 GPIO8 连接到LED,esp32c6 GPIO15
// #define LED_PIN 15 //esp32c3 GPIO8 连接到LED,esp32c6 GPIO15
如果不修改会报错,大家看情况注释对应一行
5.1.2 全局变量
static uint8_t selfMac[6];
static uint8_t peerMac[6];
static bool isPaired = false;
static bool isConnected = false;
static bool configMode = false;
String serialBuffer = ""; // 用于接收串口数据的缓冲区
esp_now_peer_info_t peer;
const unsigned long CONNECTION_TIMEOUT = 10000; // 连接超时时间
5.1.3 SPIFFS配置管理
// ================== SPIFFS ==================
// 保存配对MAC地址和版本信息到SPIFFS
bool saveConfig()
{
if (!SPIFFS.begin(true))
{
Serial.println("❌ An error occurred while mounting SPIFFS");
return false;
}
File configFile = SPIFFS.open(CONFIG_FILE, "w");
if (!configFile)
{
Serial.println("❌ Failed to open config file for writing");
return false;
}
// 创建JSON格式的数据
String configJson = "{";
configJson += "\"version\":\"" + String(CURRENT_VERSION) + "\",";
configJson += "\"peer_mac\":\"" + formatMac(peerMac) + "\"";
configJson += "}";
configFile.print(configJson);
configFile.close();
Serial.println("✅ Config saved to SPIFFS");
return true;
}
bool parseMacAddress(const String &input, uint8_t *outMac)
{
String clean = input;
clean.replace(":", "");
clean.replace("-", "");
clean.replace(" ", "");
if (clean.length() != 12)
return false;
for (int i = 0; i < 6; i++)
{
String byteStr = clean.substring(i * 2, i * 2 + 2);
outMac[i] = (uint8_t)strtol(byteStr.c_str(), NULL, 16);
}
return true;
}
// 从SPIFFS加载配对MAC地址和版本信息
bool loadConfig()
{
if (!SPIFFS.begin(true))
{
Serial.println("❌ An error occurred while mounting SPIFFS");
return false;
}
File configFile = SPIFFS.open(CONFIG_FILE, "r");
if (!configFile)
{
Serial.println("❌ Failed to open config file for reading");
return false;
}
String json = configFile.readString();
configFile.close();
// 解析JSON数据
int versionStart = json.indexOf("\"version\":\"") + 11;
int versionEnd = json.indexOf("\"", versionStart);
int macStart = json.indexOf("\"peer_mac\":\"") + 12;
int macEnd = json.indexOf("\"", macStart);
if (versionStart > 11 && macStart > 12) // 检查是否找到字段
{
String loadedVersion = json.substring(versionStart, versionEnd);
String macStr = json.substring(macStart, macEnd);
Serial.println("Loaded version: " + loadedVersion);
Serial.println("Loaded MAC: " + macStr);
// 尝试解析MAC地址
return parseMacAddress(macStr, peerMac);
}
return false;
}
// 清除SPIFFS中的配置
void clearConfig()
{
if (!SPIFFS.begin(true))
{
Serial.println("❌ An error occurred while mounting SPIFFS");
return;
}
if (SPIFFS.exists(CONFIG_FILE))
{
SPIFFS.remove(CONFIG_FILE);
Serial.println("✅ Config file cleared from SPIFFS");
}
}
// ========================================
功能说明:
- 使用SPIFFS文件系统持久化存储设备配对信息
- 支持MAC地址的解析和格式化
- 配置文件采用JSON格式,便于扩展
5.1.4 设备配对与连接管理
bool attemptPairWith(const uint8_t *targetMac)
{
memset(&peer, 0, sizeof(peer));
memcpy(peer.peer_addr, targetMac, 6);
peer.channel = 0;
peer.encrypt = false;
if (esp_now_add_peer(&peer) != ESP_OK)
return false;
const char *testMsg = "CONN";
return (esp_now_send(targetMac, (uint8_t *)testMsg, strlen(testMsg)) == ESP_OK);
}
// 连接检测任务
void connectionCheckTask(void *pvParameters)
{
while (1)
{
if (attemptPairWith(peerMac))
{
isConnected = true;
Serial.println("✅ Reconnected!");
digitalWrite(LED_PIN, LOW); // 点亮
}
else
{
isConnected = false;
digitalWrite(LED_PIN, HIGH); // 熄灭
}
vTaskDelay(pdMS_TO_TICKS(CONNECTION_TIMEOUT));
}
}
功能说明:
- 实现设备配对逻辑,自动添加ESPNOW对等节点
- 定期检测连接状态,实现自动重连
- 使用FreeRTOS任务实现非阻塞连接检测
5.1.5 AT指令处理
void printhelp()
{
Serial.println("\nESP32-C3 ESP-NOW Enhanced (with Debug Log)");
Serial.println("Enter config mode with '+++', then use AT commands:");
Serial.println(" AT+HELP Show this help");
Serial.println(" AT+PAIR=<MAC> Pair with device");
Serial.println(" AT+CLEAR Clear paired device");
Serial.println(" AT+MAC Get own MAC");
Serial.println(" AT+FRIEND Get paired device MAC");
Serial.println(" AT+TEST Test communication");
Serial.println(" AT+RESTART Restart device");
Serial.println(" ATO Exit config mode");
}
// 处理AT指令
void handleATCommand(const String &command)
{
if (command.equalsIgnoreCase("AT+TEST"))
{
if (isPaired)
{
String testMsgStr = formatMac(selfMac) + " send message to " + formatMac(peerMac) + ":TEST";
const char *testMsg = testMsgStr.c_str();
if (esp_now_send(peerMac, (uint8_t *)testMsg, strlen(testMsg)) == ESP_OK)
{
Serial.println("OK");
}
else
{
Serial.println("ERROR");
}
}
else
{
Serial.println("ERROR: Not connected");
}
}
else if (command.startsWith("AT+PAIR="))
{
String macStr = command.substring(8);
if (parseMacAddress(macStr, peerMac))
{
if (attemptPairWith(peerMac))
{
saveConfig(); // 使用新的SPIFFS保存函数
Serial.println("SaveConfig OK");
}
else
{
Serial.println("paired is ERROR ,please try it after AT+CLEAR ");
}
}
else
{
Serial.println("ERROR: Invalid MAC");
}
}
else if (command.equalsIgnoreCase("AT+CLEAR"))
{
clearConfig(); // 使用新的SPIFFS清除函数
Serial.println("SaveConfig OK");
}
else if (command.equalsIgnoreCase("AT+MAC"))
{
Serial.println(formatMac(selfMac));
}
else if (command.equalsIgnoreCase("AT+FRIEND"))
{
if (isPaired)
{
Serial.println(formatMac(peerMac));
}
else
{
Serial.println("ERROR: No paired device");
}
}
else if (command.equalsIgnoreCase("AT+RESTART"))
{
Serial.println("Restart OK");
delay(100);
ESP.restart();
}
else if (command.equalsIgnoreCase("AT+HELP"))
{
printhelp();
}
else if (command.equalsIgnoreCase("ATO"))
{
// 退出配置模式
configMode = false;
Serial.println("Exit Config Mode OK");
}
else
{
Serial.println("ERROR: Unknown command");
}
}
功能说明:
- 提供AT指令界面,便于设备配置
- 支持设备配对、清除配对、获取MAC地址等功能
- 类似传统数传电台的操作方式,降低使用门槛
5.1.6 ESPNOW数据接收回调
// 接收回调:带调试打印
void onDataRecv(const esp_now_recv_info_t *esp_now_info, const uint8_t *data, int len)
{
const uint8_t *mac = esp_now_info->src_addr; // 获取发送方的MAC地址
lastReceived = millis(); // 更新最后接收时间
// 检查是否来自配对设备
if (memcmp(mac, peerMac, 6) == 0) {
isConnected = true; // 确认连接状态
isPaired = true; // 设置配对标志
}
// 直接转发数据到串口,保持原始数据完整性
Serial.write(data, len);
}
功能说明:
- 接收ESPNOW数据并转发到串口
- 自动更新连接状态
- 保持数据的原始完整性
5.1.7 初始化与主循环
void setup()
{
// 硬件初始化
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH); // 初始灭灯
Serial.begin(115200);
// 初始化SPIFFS
if (!SPIFFS.begin(true))
{
Serial.println("❌ An error occurred while mounting SPIFFS");
// 如果SPIFFS挂载失败,尝试格式化
if (SPIFFS.format())
{
Serial.println("✅ SPIFFS formatted successfully");
if (!SPIFFS.begin(false))
{
Serial.println("❌ Failed to mount SPIFFS after formatting");
}
}
else
{
Serial.println("❌ Failed to format SPIFFS");
}
}
else
{
Serial.println("✅ SPIFFS mounted successfully");
}
printhelp();
randomSeed(analogRead(0));
// WiFi与ESPNOW初始化
WiFi.mode(WIFI_STA);
esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
esp_wifi_set_promiscuous(false);
if (esp_now_init() != ESP_OK)
{
Serial.println("❌ ESP-NOW init failed!");
return;
}
esp_now_register_recv_cb(onDataRecv);
// 加载配置
if (loadConfig()) // 使用新的SPIFFS加载函数
{
Serial.printf("💾 Loaded peer: %s\n", formatMac(peerMac).c_str());
if (attemptPairWith(peerMac))
{
isPaired = true; // 设置配对标志
isConnected = true; // 设置连接标志
lastReceived = millis(); // 记录最后接收时间
Serial.println("✅ Resumed pairing!");
}
else
{
Serial.println("❌ Resume failed");
clearConfig(); // 使用新的SPIFFS清除函数
}
}
// 创建连接检测任务
xTaskCreate(
connectionCheckTask,
"connectionCheckTask",
2048,
NULL,
5,
NULL);
}
void loop()
{
if (Serial.available())
{
// 逐字节读取,避免数据粘连
while(Serial.available()) {
char c = Serial.read();
serialBuffer += c;
// 检测"+++"序列进入配置模式
if (serialBuffer.length() >= 3 &&
serialBuffer.endsWith("+++"))
{
// 处理配置模式进入...
}
}
if (!configMode)
{
// 数传模式:发送数据到对端
if (isPaired && serialBuffer.length() > 0)
{
digitalWrite(LED_PIN, LOW); // 点亮
esp_now_send(peerMac, (uint8_t *)serialBuffer.c_str(), serialBuffer.length());
lastSent = millis(); // 更新最后发送时间
digitalWrite(LED_PIN, HIGH); // 熄灭
serialBuffer = ""; // 发送后清空缓冲区
}
}
else
{
// 配置模式:处理AT指令
// 实现代码...
}
}
delayMicroseconds(10); // 使用微秒延迟,降低CPU占用
}
六、功能测试
6.1 测试准备
- 准备两块ESP32C3/C6开发板
- 分别烧录相同的固件
- 连接串口调试助手

6.2 设备配对
- 在第一块设备的串口发送
+++进入配置模式 - 发送
AT+MAC获取设备MAC地址(如:9C:13:9E:CC:3B:88) - 在第二块设备的串口发送
+++进入配置模式 - 发送
AT+PAIR=9C:13:9E:CC:3B:88配对第一块设备 - 同样操作将第一块设备配对第二块设备

6.3 数据传输测试
- 退出配置模式(发送
ATO) - 在任意一块设备的串口发送数据,另一块设备的串口会接收到相同的数据
- 观察LED指示灯,发送数据时LED会短暂点亮
6.4 连接稳定性测试
- 测试设备在不同距离下的通信稳定性
- 测试设备重启后是否能自动恢复连接
- 测试设备在遮挡环境下的通信情况
七、总结与扩展
7.1 项目总结
本项目成功实现了基于ESP32C3/C6的ESPNOW数传电台系统,具有以下特点:
- 设备自动配对与连接管理
- 双向透明数据传输
- 配置信息持久化存储
- 友好的AT指令配置界面
- 稳定的连接状态监测
7.2 扩展功能
- 增加加密通信:使用ESPNOW的加密功能,提高数据安全性
- 支持多设备通信:扩展为一对多或多对多的通信模式
- 增加传感器接口:集成温湿度、GPS等传感器,实现数据采集与传输一体化
- 支持低功耗模式:优化电源管理,延长电池供电时间
- 增加WiFi备份通道:在ESPNOW通信失败时自动切换到WiFi通信
7.3 Trae开发体验
使用Trae开发环境结合豆包大模型,相比传统开发方式有以下提升:
- 开发效率提升30%以上
- 代码质量更高,bug更少
- 学习成本降低,新手也能快速上手
- 智能代码补全,减少重复劳动
🌟 推荐阅读:
想了解更多ESP32开发技巧,请持续关注我的CSDN博客
别忘了注册火山引擎,体验Trae的强大功能:https://t.vncps.com/5LOve
常见问题解答
Q1:设备无法配对怎么办?
A:请检查以下几点:
- MAC地址格式是否正确
- 两个设备是否在同一个WiFi信道
- 距离是否过远或有遮挡
Q2:数据传输不稳定怎么办?
A:建议:
- 使用外置天线增强信号
- 降低数据传输速率
- 增加重传机制
Q3:如何修改通信信道?
A:在setup()函数中修改esp_wifi_set_channel()的参数
感谢您阅读本文!如果您有任何问题或建议,欢迎在评论区留言讨论。
💡 技术交流:
加入ESP32开发交流群:123456789
关注我的CSDN博客:https://blog.csdn.net/VOR234
通过我的邀请连接《火山引擎注册地址:https://t.vncps.com/5LOve》注册火山引擎或者使用Trae的来找私信我抽奖哦,两块esp32c3,截止日期2026年3月18日。我包邮哦,如果超过50人,送5块esp32c3🤣🤣🤣
更多推荐




所有评论(0)