FastBond4挑战部分-增强版ESP32C3无线485串口&电台实战开发
该项目使用了esp32c3,实现了无线485串口的设计,它的主要功能为:近距离无线数据透传,支持AT指令配置串口。 该项目使用了KiCAD,实现了ESP32C3无线485串口电台的设计,它的主要功能为:绘制原理图和PCB。 该项目使用了拓竹H3D,实现了数传电台的外观的设计,它的主要功能为:保护PCB电路板,实现产品级外观设计。
标签
esp32c3
FastBond4
数传电台
485串口
2345vor
更新2026-03-30
大连海事大学
16
KiCad文件
全屏





前言

随着物联网技术的快速发展,ESP32系列芯片因其强大的性能和丰富的功能,成为了物联网开发的热门选择。而ESPNOW作为ESP32的一种高速无线通信协议,无需WiFi网络即可实现设备间的点对点通讯,非常适合传感器数据传输、远程控制等场景。


1.png


本文将详细介绍如何利用Trae开发环境结合豆包大模型,现在都直接用trae进行esp32的开发,完全可以代替arduino和vs code编译下载全代码AI,快速实现ESP32C3之间的ESPNOW无线串口&电台系统。Trae作为新一代AI辅助开发工具,能大幅提升开发效率,让代码编写更加智能高效。

🔧 ** https://www.trae.cn/ **:使用豆包大模型辅助开发,让ESP32开发事半功倍!

请大家点击豆包火山注册地址,不注册是不能完成下面的实验哦: https://t.vncps.com/5LOve

谢谢啦大家的支持💖💖💖



2.png

一、豆包大模型与Trae开发环境

1.1 豆包大模型在ESP32开发中的应用



豆包大模型可以帮助我们:

  • 快速生成代码:根据需求描述自动生成ESP32相关代码
  • 解决技术问题:解答开发过程中遇到的技术难题
  • 优化代码结构:提供代码优化建议,提高代码质量
  • 学习新技术:快速了解ESP32的新功能和使用方法

1.2 Trae开发环境优势



Trae作为新一代AI辅助开发工具,相比传统VSCode+PlatformIO有以下优势:

  • AI智能补全:基于豆包大模型的智能代码补全,大幅提升编码效率
  • 上下文理解:能够理解整个项目的上下文,提供更准确的开发建议
  • 无缝集成:内置PlatformIO支持,无需额外配置
  • 实时协作:支持多人实时协作开发
  • 云端同步:代码自动云端备份,安全可靠

1.3 注册火山引擎并使用Trae

  1. 访问火山引擎注册地址: https://t.vncps.com/5LOve
  2. 完成注册并登录
  3. 订阅火山CodePlan,畅享Trae中所有模型

    请大家点击豆包火山注册地址:https://t.vncps.com/5LOve

    方舟 Coding Plan 支持 Doubao、GLM、DeepSeek、Kimi 等模型,工具不限,现在订阅折上9折,低至8.9元,订阅越多越划算!立即订阅:https://volcengine.com/L/2p_L1OTLQZw/ 邀请码:TVGNH4JT

    3.png



    访问火山方舟 Coding Plan 新用户特惠活动,按需订阅套餐。套餐介绍参见套餐概览https://www.volcengine.com/docs/82379/1925114?lang=zh4.png





    在开通管理页面https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&advancedActiveKey=subscribe选择或切换目标模型,无需在工具中额外变更模型配置。


    5.png


  4. 下载并安装Trae客户端

    官网地址:https://www.trae.cn/

    6.png
  5. 开发方式和vscode一样,安装相关插件即可,在Trae中Platformio创建ESP32项目

    7.png


二、项目概述



本项目实现了一个基于ESP32C3的ESPNOW无线串口&电台系统,具有以下功能:

  • 设备自动配对与连接管理
  • 双向数据透明传输
  • 配置信息持久化存储
  • AT指令配置界面
  • 连接状态实时监测

2.1 系统框架图


8.png

2.2 配置流程图


9.png


三、硬件准备

3.1 所需零件

  • ESP32C3开发板(2块)




    10.png









    【下单链接】https://s.click.taobao.com/FUJ3xfn,ESP32C3 PRO MINI开发板板载ESP32-C3FH4芯片模块wifi 蓝牙开发板也是可以的,我采用这个小巧模块自带陶瓷天线

  1. WIN10/WIN11电脑:编写代码调试功能
  2. USB数据线x2:用于烧录代码和串口通信。

  • 天线(推荐使用外置天线,增强通信距离)
  • USB数据线(2条)
  • 面包板及杜邦线(可选,用于扩展)

3.2 硬件连接

  • LED指示灯:连接到GPIO8(ESP32C3)
  • 天线:连接到开发板的天线接口
  • 串口连接:
  • Serial:USB口,用于配置和调试
  • MySerial0:普通串口,用于数据传输
  • MySerial1:485口,用于工业设备通信



    采用KiCAD绘制原理图,导入到嘉立创下单PCB



    image.png
    PCB板图
    局部截取_20260328_234826.png
    3d效果图
  • 局部截取_20260328_234848.png




    我用solidworks2024设计了一个外壳



    13.png
    然后发给拓竹H3D打印切片



    14.png


    PCB通过嘉立创生产后得到了屎黄色黄色板子🤣🤣🤣


    15.jpeg


    人工手动贴片两块


    16.jpeg


    这里我们为了提高esp32c3的天线信号增益,手动调整PCB的板载天线改为外置棒杆天线


    17.png


    修改完毕后,我们就将3D外壳、PCB、杜邦线还有天线一起组装后,大约需要1天反复调整后得到小巧的数传电台啦



    18.png
    这里我把我常用的电台都放在这里,给大家欣赏一下




    19.jpeg


    突然发现我的小电台超好看😘😘😘

四、软件配置



首先需要VScode安装Platformio,然后Trae安装Platformio插件调用即可


image.png


全程交给Trae进行编程外加写博客


image.png

4.1 PlatformIO配置(platformio.ini)



下面是esp32c3配置代码:



; 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
monitor_filters = esp32_exception_decoder
lib_deps =
SPIFFS
board_build.partitions = partitions.csv
build_flags = -std=gnu++17
upload_protocol = esptool
upload_resetmethod = nodemcu





配置说明

  • 支持ESP32C3开发板
  • 使用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)





#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>
#include <esp_wifi.h>
#include <string.h>
#include <SPIFFS.h> // 添加SPIFFS支持
#include <HardwareSerial.h>
#define LED_PIN 8 // esp32c3 GPIO8 连接到LED,esp32c6 GPIO15
// #define LED_signal 9
#define LED_connect 10
#define C485 2
// 使用更安全的GPIO引脚(避免使用GPIO0/2等有特殊功能的引脚)
// const int RX_PIN = 1; // 推荐使用的RX引脚
// const int TX_PIN = 0; // 推荐使用的TX引脚
// SoftwareSerial MySerial1(RX_PIN, TX_PIN); // RX, TX
HardwareSerial MySerial0(0);
HardwareSerial MySerial1(1);
// #define LED_PIN 15 //esp32c3 GPIO8 连接到LED,esp32c6 GPIO15
#define CONFIG_FILE "/espnow_config.json" // SPIFFS配置文件路径
#define CURRENT_VERSION "1.0" // 当前版本号

// 默认波特率
#define DEFAULT_BAUD_RATE 115200

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;
bool data = false;

// 波特率设置
int baudRate = DEFAULT_BAUD_RATE; // 当前波特率

// 连接状态更新时间
unsigned long lastConnectionUpdate = 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);
}

void print(String s)
{
Serial.println(s);
MySerial1.println(s);
}

// ================== SPIFFS ==================
// 保存配对MAC地址和版本信息到SPIFFS
bool saveConfig()
{
if (!SPIFFS.begin(true))
{
print("❌ An error occurred while mounting SPIFFS");
return false;
}

File configFile = SPIFFS.open(CONFIG_FILE, "w");
if (!configFile)
{
print("❌ Failed to open config file for writing");
return false;
}

// 创建JSON格式的数据 - 包含波特率配置
String configJson = "{";
configJson += "\"version\":\"" + String(CURRENT_VERSION) + "\",";
configJson += "\"peer_mac\":\"" + formatMac(peerMac) + "\",";
configJson += "\"baud_rate\":" + String(baudRate);
configJson += "}";

configFile.print(configJson);
configFile.close();
print("✅ 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))
{
print("❌ An error occurred while mounting SPIFFS");
return false;
}

File configFile = SPIFFS.open(CONFIG_FILE, "r");
if (!configFile)
{
print("❌ 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);
int baudStart = json.indexOf("\"baud_rate\":") + 12;
int baudEnd = json.indexOf(",", baudStart);

if (baudStart > 12) { // 检查是否有波特率配置
String baudStr = json.substring(baudStart, baudEnd);
baudRate = baudStr.toInt();

// 验证波特率是否为支持的值
if (baudRate != 9600 && baudRate != 38400 && baudRate != 115200) {
baudRate = DEFAULT_BAUD_RATE; // 使用默认值
}
} else {
baudRate = DEFAULT_BAUD_RATE; // 没有保存的波特率配置时使用默认值
}

if (versionStart > 11 && macStart > 12) // 检查是否找到字段
{
String loadedVersion = json.substring(versionStart, versionEnd);
String macStr = json.substring(macStart, macEnd);

print("Loaded version: " + loadedVersion);
print("Loaded MAC: " + macStr);
print("Loaded baud rate: " + String(baudRate));

// 尝试解析MAC地址
return parseMacAddress(macStr, peerMac);
}

return false;
}

// 清除SPIFFS中的配置
void clearConfig()
{
if (!SPIFFS.begin(true))
{
print("❌ An error occurred while mounting SPIFFS");
return;
}

if (SPIFFS.exists(CONFIG_FILE))
{
SPIFFS.remove(CONFIG_FILE);
print("✅ Config file cleared from SPIFFS");
baudRate = DEFAULT_BAUD_RATE; // 重置为默认波特率
}
}
// ========================================

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;
lastConnectionUpdate = millis(); // 更新连接时间
print("✅ Reconnected!");
digitalWrite(LED_PIN, LOW); // 点亮
}
else
{
isConnected = false; // 修正:断开连接时更新状态
lastConnectionUpdate = millis(); // 更新连接时间
// print("❌ Connection lost!");
digitalWrite(LED_PIN, HIGH); // 熄灭
}
vTaskDelay(pdMS_TO_TICKS(CONNECTION_TIMEOUT)); // 增加检测间隔到5秒
}
}

void printhelp()
{
print("\nESP32-C3 ESP-NOW Enhanced (with Debug Log)");
print("Enter config mode with '+++', then use AT commands:");
print(" AT+HELP Show this help");
print(" AT+PAIR=<MAC> Pair with device");
print(" AT+CLEAR Clear paired device");
print(" AT+MAC Get own MAC");
print(" AT+FRIEND Get paired device MAC");
print(" AT+TEST Test communication");
print(" AT+RESTART Restart device");
print(" AT+BAUD=<rate> Set baud rate (9600/38400/115200)");
print(" AT+GETBAUD Get current baud rate");
print(" AT+SYSINFO Print system parameters");
print(" ATO Exit config mode");
}

// 打印系统参数
void printSystemInfo()
{
// 更新连接状态 - 基于配对状态和最后接收数据时间
unsigned long now = millis();


print("=== ESP32c3 Radio System Parameters by @2345vor ===");
print("Firmware Version: " + String(CURRENT_VERSION));
print("My MAC Address: " + formatMac(selfMac));
if (isPaired) {
print("Peer MAC Address: " + formatMac(peerMac));
} else {
print("Peer MAC Address: Not Paired");
}
print("Current Baud Rate: " + String(baudRate));
print("Connection Status: " + String(isConnected ? "Connected" : "Disconnected"));
print("Pairing Status: " + String(isPaired ? "Paired" : "Not Paired"));
print("Last Received: " + String((now - lastReceived) / 1000.0) + "s ago");
print("Last Connection Update: " + String((now - lastConnectionUpdate) / 1000.0) + "s ago");
print("=========================");
}

// 处理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)
{
print("TEST OK");
}
else
{
print("TEST FAIL");
}
}
else
{
print("ERROR: Not paired");
}
}
else if (command.startsWith("AT+PAIR="))
{
String macStr = command.substring(8);
if (parseMacAddress(macStr, peerMac))
{
if (attemptPairWith(peerMac))
{
saveConfig(); // 使用新的SPIFFS保存函数
print("SaveConfig OK");
}
else
{
print("ERROR: Pair failed");
}
}
else
{
print("ERROR: Invalid MAC");
}
}
else if (command.equalsIgnoreCase("AT+CLEAR"))
{
clearConfig(); // 使用新的SPIFFS清除函数
print("ClearConfig OK");
}
else if (command.equalsIgnoreCase("AT+MAC"))
{
print(formatMac(selfMac));
}
else if (command.equalsIgnoreCase("AT+FRIEND"))
{
if (isPaired)
{
print(formatMac(peerMac));
}
else
{
print("ERROR: Not paired device");
}
}
else if (command.equalsIgnoreCase("AT+RESTART"))
{
print("Restarting...");
delay(100);
ESP.restart();
}
else if (command.equalsIgnoreCase("AT+HELP"))
{
printhelp();
}
else if (command.startsWith("AT+BAUD="))
{
String baudStr = command.substring(8);
int newBaud = baudStr.toInt();

// 检查是否是支持的波特率
if (newBaud == 9600 || newBaud == 38400 || newBaud == 115200) {
baudRate = newBaud;

// 重新初始化串口
MySerial0.updateBaudRate(baudRate);
MySerial1.updateBaudRate(baudRate);

saveConfig(); // 保存新波特率到配置文件
print("Baud rate set to: " + String(baudRate));
} else {
print("ERROR: Unsupported baud rate. Use 9600, 38400, or 115200");
}
}
else if (command.equalsIgnoreCase("AT+GETBAUD"))
{
print("Current baud rate: " + String(baudRate));
}
else if (command.equalsIgnoreCase("AT+SYSINFO"))
{
printSystemInfo();
}
else if (command.equalsIgnoreCase("ATO"))
{
// 退出配置模式
configMode = false;
print("Exit config mode");
}
else
{
print("ERROR: Invalid 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; // 确认连接状态
lastConnectionUpdate = millis(); // 更新连接状态时间
isPaired = true; // 设置配对标志


// 特殊处理 CONN 消息,只用于连接测试,不转发
if (len == 4 && memcmp(data, "CONN", 4) == 0) {
// 这是一个连接测试请求,不需要转发
return;
}
}

// 直接转发数据到两个串口,保持原始数据完整性
Serial.write(data, len);
MySerial0.write(data, len);
MySerial1.write(data, len);
}

void setup()
{
pinMode(LED_PIN, OUTPUT);
// pinMode(LED_signal, OUTPUT);
pinMode(LED_connect, OUTPUT);
pinMode(C485, OUTPUT);
digitalWrite(C485, HIGH); // 初始灭灯
digitalWrite(LED_PIN, HIGH); // 初始灭灯
// digitalWrite(LED_signal, HIGH); // 初始灭灯
digitalWrite(LED_connect, HIGH); // 初始灭灯

Serial.begin(DEFAULT_BAUD_RATE); // 使用默认波特率初始化
MySerial0.begin(DEFAULT_BAUD_RATE, SERIAL_8N1, 20, 21);
// And configure MySerial1 on pins RX=D9, TX=D0
MySerial1.begin(DEFAULT_BAUD_RATE, SERIAL_8N1, 1, 0);

print("Initializing SPIFFS...");

// 初始化SPIFFS
if (!SPIFFS.begin(true))
{
print("❌ An error occurred while mounting SPIFFS");
// 如果SPIFFS挂载失败,尝试格式化
if (SPIFFS.format())
{
print("✅ SPIFFS formatted successfully");
if (!SPIFFS.begin(false))
{
print("❌ Failed to mount SPIFFS after formatting");
}
}
else
{
print("❌ Failed to format SPIFFS");
}
}
else
{
print("✅ SPIFFS mounted successfully");
}

printhelp();

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)
{
print("❌ ESP-NOW init failed!");
return;
}
esp_now_register_recv_cb(onDataRecv);

WiFi.macAddress(selfMac); // 使用WiFi库获取MAC地址
print(">>> MY MAC: " + formatMac(selfMac) + " <<<");

if (loadConfig()) // 使用新的SPIFFS加载函数
{
print("💾 Loaded peer: " + formatMac(peerMac));
if (attemptPairWith(peerMac))
{
isPaired = true; // 设置配对标志
isConnected = true; // 设置连接标志
lastReceived = millis(); // 记录最后接收时间
lastConnectionUpdate = millis(); // 记录连接更新时间
print("✅ Resumed pairing!");
}
else
{
print("❌ Resume failed");
clearConfig(); // 使用新的SPIFFS清除函数
}
}
// 创建连接检测任务
xTaskCreate(
connectionCheckTask,
"connectionCheckTask",
2048,
NULL,
5,
NULL);

print("🟢 Connection check task started");
// digitalWrite(LED_signal, LOW); // 初始灭灯
digitalWrite(LED_connect, LOW); // 初始灭灯

// 重新初始化串口使用加载的波特率
if (baudRate != DEFAULT_BAUD_RATE) {
Serial.begin(baudRate);
MySerial0.updateBaudRate(baudRate);
MySerial1.updateBaudRate(baudRate);
}

// 启动时打印系统参数
printSystemInfo();
}

void loop()
{

delayMicroseconds(10); // 使用微秒延迟,1000等同于1毫秒
// 处理软件串口(串口0)- 仅数传
if (MySerial0.available())
{
while (MySerial0.available())
{
char c = MySerial0.read();
serialBuffer += c;

if (c == '\n' || c == '\r')
{
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)
{
MySerial0.println("❌ Not paired with any device");
serialBuffer = "";
}
}
}
data = true;
}
// 处理软件串口(串口1)- 仅数传
if (MySerial1.available())
{
while (MySerial1.available())
{
char c = MySerial1.read();
serialBuffer += c;

if (c == '\n' || c == '\r')
{
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)
{
MySerial1.println("❌ Not paired with any device");
serialBuffer = "";
}
}
}
data = true;
}

// 处理硬件串口(串口0)- 配置+数传
if (!data && 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)
{
print("❌ Not paired with any device");
serialBuffer = ""; // 清空缓冲区
}
}
else
{
// 检查是否退出配置模式命令
if (serialBuffer.endsWith("\n") || serialBuffer.endsWith("\r"))
{
// 配置模式:处理AT指令
serialBuffer.trim();
if (!serialBuffer.isEmpty())
{
// 处理AT指令
handleATCommand(serialBuffer);
serialBuffer = ""; // 处理完后清空
}
}
}
}
data = false;
}






5.2 串口功能分配



本项目使用了三个串口,各自功能如下:

  1. Serial:USB口,用于配置和调试

  • 连接到电脑的USB接口
  • 用于发送AT指令进行设备配置
  • 用于查看系统状态和调试信息
  1. MySerial0:普通串口,用于数据传输

  • 连接到GPIO20和GPIO21
  • 用于与普通串口设备通信
  • 支持透明数据传输
  1. MySerial1:485口,用于工业设备通信

  • 连接到GPIO1和GPIO0
  • 用于与RS-485工业设备通信
  • 通过C485引脚(GPIO2)控制485芯片的收发状态

5.3 核心功能模块

  1. 设备配对与连接管理

  • 通过AT+PAIR=<MAC>指令配对目标设备
  • 自动重连机制确保连接稳定
  • 连接状态实时监测与LED指示
  1. 数据传输

  • 支持双向透明数据传输
  • 三个串口的数据都可以通过ESPNOW无线传输
  • 数据传输时LED指示灯闪烁提示
  1. 配置管理

  • 通过SPIFFS文件系统持久化存储配置
  • 支持波特率配置(9600/38400/115200)
  • 配置自动加载与验证
  1. AT指令系统

  • 丰富的AT指令集,支持设备配置和状态查询
  • 配置模式与数传模式无缝切换
  • 详细的帮助信息和错误提示

六、使用方法

6.1 设备配对

  1. 连接两个ESP32C3开发板到电脑
  2. 打开串口监视器,波特率设置为115200
  3. 在串口输入 +++进入配置模式
  4. 输入 AT+MAC获取本机MAC地址
  5. 在另一个设备上输入 AT+PAIR=<MAC>,其中 <MAC>为第一个设备的MAC地址
  6. 配对成功后,设备会自动保存配置并进入数传模式

6.2 数据传输

  • 从电脑发送数据:通过Serial(USB口)发送数据,会自动通过ESPNOW传输到配对设备
  • 从普通串口发送数据:通过MySerial0发送数据,会自动通过ESPNOW传输到配对设备
  • 从485口发送数据:通过MySerial1发送数据,会自动通过ESPNOW传输到配对设备
  • 接收数据:ESPNOW接收到的数据会同时转发到三个串口

6.3 配置命令

命令

功能

示例

AT+HELP

显示帮助信息

AT+HELP

AT+PAIR=<MAC>

配对目标设备

AT+PAIR=9C:13:9E:CC:3B:88

AT+CLEAR

清除配对信息

AT+CLEAR

AT+MAC

获取本机MAC地址

AT+MAC

AT+FRIEND

获取配对设备MAC

AT+FRIEND

AT+TEST

测试通信

AT+TEST

AT+RESTART

重启设备

AT+RESTART

AT+BAUD=<rate>

设置波特率9600/38400/115200

AT+BAUD=115200

AT+GETBAUD

获取当前波特率

AT+GETBAUD

AT+SYSINFO

打印系统参数

AT+SYSINFO

ATO

退出配置模式

ATO

七、测试与调试

7.1 测试步骤

  1. 准备两个ESP32C3开发板,烧录相同的代码
  2. 按照6.1节的步骤进行设备配对
  3. 验证LED指示灯状态:配对成功后LED常亮
  4. 测试数据传输:从一个设备的任意串口发送数据,在另一个设备的所有串口查看是否接收到数据
  5. 测试连接稳定性:断开一个设备的电源,重新上电后观察是否自动重连

7.1.1 USB互发测试


20.png


可以正常收发数据2345vor和123


7.1.2 串口互发测试



记得手动添加 \r\n结束符哦👌👌👌

21.png



22.png



这里的串口0和串口1 485接口都已测试通过


7.2 常见问题与解决方案

问题

可能原因

解决方案

配对失败

MAC地址格式错误

确保MAC地址格式正确,不含空格或其他字符

连接不稳定

距离过远或干扰严重

靠近设备或减少干扰源,使用外置天线

数据传输失败

未配对或连接断开

重新配对设备,确保连接状态正常

串口无响应

波特率设置错误

使用AT+GETBAUD查看当前波特率,确保与串口监视器设置一致

八、总结



本项目成功实现了基于ESP32C3的增强版无线串口&电台系统,具有以下特点:

  1. 多串口支持:明确区分了三个串口的功能,Serial作为USB口用于配置和调试,MySerial0作为普通串口用于数据传输,MySerial1作为485口用于工业设备通信。
  2. 稳定可靠:采用ESPNOW协议实现高速无线通信,配合自动重连机制,确保连接稳定可靠。
  3. 易于配置:通过AT指令系统,用户可以方便地进行设备配对、波特率设置等操作。
  4. 灵活扩展:系统架构清晰,易于扩展其他功能,如传感器数据采集、远程控制等。
  5. AI辅助开发:使用Trae结合豆包大模型,大幅提升了开发效率,让代码编写更加智能高效。



    通过本项目,我们展示了如何利用ESP32C3和ESPNOW协议构建一个功能强大的无线串口&电台系统,为物联网设备间的通信提供了一种高效、可靠的解决方案。

🔧 ** https://www.trae.cn/ **:使用豆包大模型辅助开发,让ESP32开发事半功倍!

请大家点击豆包火山注册地址,不注册是不能完成下面的实验哦: https://t.vncps.com/5LOve

谢谢啦大家的支持💖💖💖

九、扩展与展望

  1. 增加传感器支持:可以扩展系统,添加温湿度、光照等传感器,实现无线传感器网络。
  2. 实现OTA更新:通过ESPNOW实现设备固件的无线更新,方便系统维护和功能升级。
  3. 增加加密功能:对传输数据进行加密,提高系统安全性。
  4. 支持多设备网络:扩展为星型或 mesh 网络,支持更多设备的通信。
  5. 优化功耗:通过深度睡眠等技术,降低系统功耗,延长电池寿命。



    通过不断完善和扩展,本系统可以应用于更多场景,如工业自动化、智能家居、环境监测等领域,为物联网应用提供更加灵活、可靠的通信解决方案。





附件下载
esp32c3_radio.zip
3D打印外壳源文件打印文件
esp_station.zip
源代码
esp_radio_PCB.zip
Kicad原理图和PCB文件
团队介绍
好好学习天天向上
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号