教程:使用 Arduino UNO 为 OSOLINK WiFi/BLE I/O Shield v1.0 烧录 ESP-AT 固件

适用产品:OSOLINK WiFi/BLE I/O Shield v1.0(Espressif ESP32-C3-MINI-1)

烧录接口:4 针 ESP UART0 排针(丝印:3V3 / ESP_RX / ESP_TX / GND)

所需工具:Arduino UNO(用作 USB 转串口适配器)+ 乐鑫 Flash Download Tool

目录

1前言 – 什么情况下需要烧录?

OSOLINK WiFi/BLE I/O Shield v1.0 出厂时已预烧录乐鑫 ESP-AT 固件,正常使用时无需自行烧录,只需从 Arduino 通过 UART 发送 AT 指令即可。

遇到以下情况时,您可能需要重新烧录 ESP-AT 固件:

  • 无线模组对 AT 指令无响应(无回复或乱码)— 极少数模组出厂时可能未预烧录固件。
  • 误将 AT 固件覆盖为自己的程序或其他二进制文件。
  • 需要升级到更新版本的 ESP-AT 固件。

本教程演示仅使用一块 Arduino UNO 作为 USB 转串口适配器来重新烧录固件的方法 — 无需额外的 USB 转 TTL 模块。我们将 Arduino UNO 板载的 USB 芯片用作电脑与扩展板 ESP UART0 烧录接口之间的透明串口桥。

提示:如果您手头有专用的 3.3 V USB 转 TTL 模块,也可以直接连接到 ESP UART0 接口(见第 4 节说明)。本教程介绍的 Arduino UNO 方法,适合没有独立 USB 转 TTL 适配器的用户。

2准备工作

  • Windows 电脑 1 台(乐鑫 Flash Download Tool 仅支持 Windows)。
  • Arduino UNO 开发板 1 块 + USB 数据线(仅用作 USB 转串口适配器,无需上传任何程序)。
  • 待烧录的 OSOLINK WiFi/BLE I/O Shield v1.0 若干块。
  • 若干杜邦线(母对母或母对公),若 ESP UART0 接口未焊排针,可额外准备 4P 测试夹 1 个。
  • 乐鑫官方 Flash Download Tool 软件。
  • ESP32-C3 ESP-AT 固件压缩包(下载链接见第 5 节)。
OSOLINK WiFi/BLE I/O Shield v1.0 - ESP UART0、BOOT、EN 位置
图1 – 扩展板实物。4 针 ESP UART0 接口(3V3 / ESP_RX / ESP_TX / GND)位于模组旁;BOOTEN 按键在右上方。

3将 Arduino UNO 变成 USB 转串口适配器

Arduino UNO 板载已经带有一颗 USB 转串口芯片,它连接到主控芯片的 D0(RX)和 D1(TX)引脚。如果我们让主控芯片停止工作,这颗 USB 转串口芯片就会变成电脑与 D0/D1 之间的透明桥梁 — 这正是我们与扩展板通信所需要的。

只需用一根杜邦线将 Arduino UNO 的 RESET 引脚与 GND 引脚短接,并在整个烧录过程中保持连接:

Arduino UNO 的 RESET → Arduino UNO 的 GND(杜邦线,全程保持连接)。

这样会让主控芯片一直处于复位状态,从而不再驱动串口线路。该 Arduino UNO 无需上传任何程序,也不要把扩展板插在这块 Arduino 上。UNO 单独使用,仅作为 USB 转串口适配器,通过杜邦线与扩展板相连。

重要:请勿将扩展板插在这块 Arduino UNO 上。两者保持分离,只连接第 4 节所述的 4 根线。若叠插,会把扩展板的电源与 I/O 引脚连到已被复位的 Arduino 上,导致无法烧录。

4硬件接线(ESP UART0 接口)

在扩展板上找到丝印为 ESP UART0 的 4 针接口(丝印引脚:3V3ESP_RXESP_TXGND)。若该接口未焊排针,可焊接 2.54 mm 排针,或使用 4P 测试夹夹住焊盘。

按下表将 Arduino UNO 连接到扩展板的 ESP UART0 接口。由于主控芯片已被复位,此时接线为直连(RX → RX,TX → TX):

Arduino UNO 扩展板 – ESP UART0(丝印)
3.3V 3V3
D0 (RX0) ESP_RX
D1 (TX1) ESP_TX
GND GND

保持第 3 节中 RESETGND 的短接线不变。全部接好后,将 Arduino UNO 的 USB 线插入电脑。此时扩展板的电源指示灯(PWR LED)应点亮,由 3.3 V 线供电。

警告:烧录时仅能通过 Arduino UNO 的 3.3 V 引脚为扩展板供电。请勿同时用其他电源(USB、VIN 电池或 ON/OFF 开关)给扩展板供电 — 双电源同时接入可能损坏板卡。
另一种方式(专用 USB 转 TTL 模块):若改用 3.3 V USB 转 TTL 适配器,则 RX/TX 需交叉连接:适配器 3.3V → 3V3TXD → ESP_RXRXD → ESP_TXGND → GND

5下载烧录工具与 AT 固件

  • Flash Download Tool – 乐鑫官方下载(直链):dl.espressif.com/public/flash_download_tool.zip。解压后即可使用,无需安装。
  • ESP32-C3 ESP-AT 固件 V3.2.0.0 – 预编译 ESP32-C3-MINI-1 AT 固件下载(直链):ESP32-C3-MINI-1-AT-V3.2.0.0.zip。解压压缩包后,需要用到的文件路径为:
    ESP32-C3-MINI-1-AT-V3.2.0.0\ESP32-C3-MINI-1-V3.2.0.0\factory\factory_MINI-1.bin
注意:请务必使用与本扩展板 ESP32-C3-MINI-1 模组相匹配的 MINI-1 factory 固件。该 factory_MINI-1.bin 已将 bootloader、分区表和 AT 应用合并在一起,因此只需一次性烧录到地址 0x0

6配置 Flash Download Tool

6.1  运行 flash_download_tool.exe。在弹出的第一个对话框中设置:

  • ChipType:ESP32-C3
  • WorkMode:Factory
  • LoadMode:UART

然后点击 OK

6.2  进入主界面后,按下表进行设置:

步骤 设置项 值 / 操作
a LockSettings 先取消勾选,使下方选项可编辑。
b SPI Flash Config 保持默认:40MHzDIO
c 固件行复选框 勾选第一行固件前的复选框以启用该行。
d 固件文件 点击 ...,从解压后的 AT 固件包中选择 ...\factory\factory_MINI-1.bin
e 烧录地址 在固件文件旁的地址框中填入 0x0
f LockSettings 重新勾选,锁定设置。
g COM 选择 Arduino UNO 对应的 COM 端口(不确定时可在 Windows 设备管理器查看)。
h BAUD Arduino UNO 方法建议设为 115200(更稳定)。使用专用 USB 转 TTL 模块时可选更高波特率以加快烧录。

7进入下载模式并烧录

需要使用扩展板自身的 BOOTEN 按键让 ESP32-C3 进入下载(bootloader)模式。按以下顺序操作:

  1. 点击 Flash Download Tool 上的 START 按钮。
  2. 长按扩展板上的 BOOT 按键。
  3. 保持长按 BOOT 的同时,按一下 EN 按键后松开。
  4. 松开 BOOT 按键。此时 ESP32-C3 已进入下载模式,工具底部会出现进度条。
  5. 等待烧录完成。完成后工具会显示 FINISH 标志。

烧录完成后,再按一下 EN 按键(或拔插 USB)让模组重启,即可运行新的 AT 固件。

批量烧录:用同一套配置可逐块连续烧录多块板子 — 只需在每次更换板子后确认选择了正确的 COM 端口。

8验证 AT 固件(功能测试)

重要 – 两个接口是分开的:上文使用的 ESP UART0 接口(3V3 / ESP_RX / ESP_TX / GND)仅用于固件烧录。Arduino 主机与模组通信所用的 AT 指令接口另一个端口,从扩展板丝印 ATRX / ATTX 引出(经 UART 选择器 连到 Arduino 的 D5 / D4,此为出厂默认设置)。因此不要通过烧录口去测试 AT 指令,而应将扩展板插到 Arduino UNO 上,按下面的方法测试。

先断开所有烧录连线,并拆掉第 3 节中 RESETGND 的短接线。

8.1  上电测试。将扩展板安装到 Arduino UNO 上 — 注意排针对齐并完整插入,不要将排针弄歪。用 USB 线将 UNO 连接到电脑。此时应能看到扩展板板载电源指示灯常亮。若常亮,则该项测试通过。

8.2  上传测试代码。使用 OSOYOO 功能测试代码(如下所示),让 Arduino 通过 AT 接口(ATRX / ATTX → D5 / D4)与 ESP32-C3 通信,执行一系列 AT 指令并扫描附近 WiFi:

  1. 打开 Arduino IDE,并打开测试代码。
  2. 选择正确的端口号,板子类型选择 Arduino UNO
  3. 点击上传,等待上传完成。
  4. 打开串口监视器(Serial Monitor),波特率选择 9600(行结束符:换行 NL)。

测试代码 – 将下方完整代码复制到 Arduino IDE 新建窗口后上传:

/*
 * ESP32-C3 WiFi/BLE Shield —— 产线测试 + 出厂恢复(供同事测试,测完可直接出货)
 * 硬件:Arduino UNO + ESP32-C3 Shield(AT 固件,SoftwareSerial D4/D5)
 *
 * 流程(全部在 setup 自动完成,按 UNO 复位键即可测下一片):
 *   STEP A  AT+RESTORE 恢复出厂(覆盖 115200/9600 两种当前波特率,测完模块回到出厂 115200)
 *   STEP B  临时把波特率降到 9600(AT+UART_CUR,不写 flash,仅本次有效)以便软串口稳定通信
 *   STEP 1~5  基本AT / 固件版本 / Station模式 / 扫描配置 / 扫描WiFi —— 每步 PASS/FAIL
 *   STEP D  再次 AT+RESTORE,抹掉临时配置,模块以【出厂默认 115200】结束,可直接出货
 *
 * 串口监视器:波特率 9600,行结束符「换行 NL」。
 */

#include <SoftwareSerial.h>
SoftwareSerial esp(4, 5);   // D4=RX, D5=TX

// 前向声明
bool   testAT(const char* title, String cmd, String expect, int timeout);
String readFor(int timeout);
void   scanWiFi();
void   printAp(int n, String line);
String ecnToStr(int ecn);
void   factoryRestore();
void   flushEsp();

int passCount = 0, failCount = 0;

void setup() {
  Serial.begin(9600);
  delay(300);
  Serial.println(F("\n=========================================="));
  Serial.println(F("   ESP32-C3 Shield 产线测试 + 出厂恢复"));
  Serial.println(F("=========================================="));

  // ---------- STEP A: 恢复出厂 ----------
  Serial.println(F("\nSTEP A: 恢复出厂状态 (AT+RESTORE) ..."));
  factoryRestore();     // 处理未知波特率;结束后模块为出厂默认 115200
  Serial.println(F("  已恢复出厂,模块当前应为 115200"));

  // ---------- STEP B: 临时降速到 9600(不写 flash)----------
  Serial.println(F("\nSTEP B: 临时切到 9600 用于测试 (AT+UART_CUR) ..."));
  esp.begin(115200);
  delay(400);
  esp.println("AT+UART_CUR=9600,8,1,0,0");   // 仅本次有效,掉电/RESTORE 后失效
  delay(800);
  esp.end();
  esp.begin(9600);
  delay(400);
  flushEsp();

  // ---------- STEP 1~4 ----------
  bool ok1 = testAT("STEP 1: 基本 AT 通信", "AT", "OK", 1500);
  if (!ok1) {
    Serial.println(F("  !! AT 无响应,请检查:"));
    Serial.println(F("     - UART 选择器跳线是否在 D4/D5"));
    Serial.println(F("     - 模块供电/是否插好"));
    Serial.println(F("     - 若 STEP B 的降速指令没送达,按 UNO 复位重试一次"));
  }
  testAT("STEP 2: 读取固件版本 (AT+GMR)",   "AT+GMR",           "OK", 2500);
  testAT("STEP 3: 设置 Station 模式",       "AT+CWMODE=1",      "OK", 1500);
  testAT("STEP 4: 配置扫描输出(排序/精简)",  "AT+CWLAPOPT=1,23", "OK", 1000);

  // ---------- STEP 5: 扫描 WiFi ----------
  Serial.println(F("\nSTEP 5: 扫描附近 WiFi (AT+CWLAP) ..."));
  scanWiFi();

  // ---------- 汇总 ----------
  Serial.println(F("\n------------------------------------------"));
  Serial.print(F("测试结果:PASS ")); Serial.print(passCount);
  Serial.print(F(" 项,FAIL "));      Serial.print(failCount);
  Serial.println(F(" 项"));
  if (failCount == 0) Serial.println(F("★★★ 测试通过 ★★★"));
  else                Serial.println(F("!!! 有项目未通过,请排查 !!!"));
  Serial.println(F("------------------------------------------"));

  // ---------- STEP D: 测完再次恢复出厂,出货前保持纯净 ----------
  Serial.println(F("\nSTEP D: 恢复出厂状态以便直接出货 ..."));
  flushEsp();
  esp.println("AT+RESTORE");    // 当前 9600,抹掉临时配置并重启到出厂 115200
  delay(3000);
  Serial.println(F("★ 模块已恢复出厂默认(115200、无残留配置)"));
  Serial.println(F("★ 可直接出货,无需再次烧录。"));
  Serial.println(F("=========================================="));
  Serial.println(F("(测下一片:换上新 Shield,按一下 UNO 复位键即可)"));
}

void loop() {
  // 出厂恢复后模块已回到 115200,本程序不再与其通信,空转即可。
}

// ---- 恢复出厂:覆盖 115200 与 9600 两种可能的当前波特率 ----
void factoryRestore() {
  long bauds[2] = {115200, 9600};
  for (int i = 0; i < 2; i++) {
    esp.end();
    esp.begin(bauds[i]);
    delay(300);
    esp.print("+++");            // 退出可能的透传模式(无换行,前后留白)
    delay(1000);
    esp.println("AT+RESTORE");   // 恢复出厂并重启
    delay(2500);
    Serial.print(F("  已在 "));
    Serial.print(bauds[i]);
    Serial.println(F(" 下发送 +++ / AT+RESTORE"));
  }
  // RESTORE 后模块统一回到出厂默认 115200
}

// ---- 发送 AT 指令并判断是否包含期望关键字,打印 PASS/FAIL ----
bool testAT(const char* title, String cmd, String expect, int timeout) {
  Serial.println();
  Serial.println(title);
  Serial.println("  > " + cmd);
  flushEsp();
  esp.println(cmd);
  String resp = readFor(timeout);

  resp.trim();
  if (resp.length()) {
    Serial.print("  < ");
    for (unsigned int i = 0; i < resp.length(); i++) {
      char c = resp[i];
      Serial.write(c);
      if (c == '\n') Serial.print("    ");
    }
    Serial.println();
  } else {
    Serial.println("  < (无返回)");
  }

  bool ok = resp.indexOf(expect) != -1;
  Serial.println(ok ? "  [PASS]" : "  [FAIL]");
  if (ok) passCount++; else failCount++;
  return ok;
}

// ---- 在 timeout 内收集模块返回(收到数据就刷新超时)----
String readFor(int timeout) {
  String r = "";
  unsigned long t = millis();
  while (millis() - t < (unsigned long)timeout) {
    while (esp.available()) {
      r += (char)esp.read();
      t = millis();
      if (r.length() > 250) return r;   // UNO 内存有限,防止 String 过大
    }
  }
  return r;
}

// ---- 扫描 WiFi:边收边解析并逐条打印,超时也照常统计 ----
void scanWiFi() {
  flushEsp();
  esp.println("AT+CWLAP");

  char buf[90];
  byte idx = 0;
  int count = 0;
  bool done = false;
  unsigned long t = millis();

  while (millis() - t < 9000 && !done) {
    while (esp.available()) {
      char c = esp.read();
      t = millis();
      if (c == '\r') continue;
      if (c == '\n') {
        buf[idx] = '\0';
        String line = String(buf);
        idx = 0;
        if (line.indexOf("+CWLAP:") != -1)      { count++; printAp(count, line); }
        else if (line.indexOf("OK") != -1)      { done = true; break; }
        else if (line.indexOf("ERROR") != -1)   { Serial.println(F("  返回 ERROR")); done = true; break; }
      } else if (idx < sizeof(buf) - 1) {
        buf[idx++] = c;
      }
    }
  }

  Serial.print(F("  共发现 "));
  Serial.print(count);
  Serial.println(F(" 个热点"));
  if (count > 0) { Serial.println(F("  [PASS] 射频与扫描功能正常")); passCount++; }
  else {
    Serial.println(F("  [FAIL] 未扫到热点(检查模式/接线/天线)"));
    failCount++;
  }
}

// ---- 解析并打印一条 +CWLAP 行 ----
void printAp(int n, String line) {
  int lp = line.indexOf('(');
  if (lp == -1) return;

  int c1  = line.indexOf(',', lp);
  int ecn = line.substring(lp + 1, c1).toInt();

  int q1 = line.indexOf('"', c1);
  int q2 = line.indexOf('"', q1 + 1);
  String ssid = (q1 != -1 && q2 != -1) ? line.substring(q1 + 1, q2) : "";
  if (ssid.length() == 0) ssid = "(隐藏 SSID)";

  int c2   = line.indexOf(',', q2);
  int c3   = line.indexOf(',', c2 + 1);
  int rssi = line.substring(c2 + 1, c3).toInt();

  int rp = line.lastIndexOf(')');
  int lc = line.lastIndexOf(',');
  int ch = line.substring(lc + 1, rp).toInt();

  Serial.print(F("  "));   Serial.print(n);   Serial.print(F(". SSID="));
  Serial.print(ssid);
  Serial.print(F("  信号=")); Serial.print(rssi); Serial.print(F("dBm"));
  Serial.print(F("  信道=")); Serial.print(ch);
  Serial.print(F("  加密=")); Serial.println(ecnToStr(ecn));
}

String ecnToStr(int ecn) {
  switch (ecn) {
    case 0: return "OPEN";
    case 1: return "WEP";
    case 2: return "WPA-PSK";
    case 3: return "WPA2-PSK";
    case 4: return "WPA/WPA2-PSK";
    case 5: return "WPA2-Enterprise";
    case 6: return "WPA3-PSK";
    case 7: return "WPA2/WPA3-PSK";
    default: return "?";
  }
}

void flushEsp() {
  while (esp.available()) esp.read();
}
说明:AT 接口出厂默认位于 D5 / D4(而非 D0 / D1),因此通过 USB 上传代码与和 ESP32-C3 通信互不冲突 — 扩展板插在 UNO 上时两者可同时工作,无需改动跳线。

8.3  预期结果。若固件正常,串口监视器会打印 AT 固件版本、各 AT 步骤的结果以及附近 WiFi 热点列表,最后将模块恢复出厂默认。典型输出如下:

STEP 1: 基本 AT 通信
> AT
< OK  [PASS]
STEP 2: 读取固件版本 (AT+GMR)
AT version:3.2.0.0 …  Bin version:v3.2.0.0(MINI-1)
[PASS]STEP 3: 设置 Station 模式 (AT+CWMODE=1)  [PASS]
STEP 4: 配置扫描输出 (AT+CWLAPOPT)  [PASS]STEP 5: 扫描附近 WiFi (AT+CWLAP) …
1. SSID=TP-LINK*****  信号=-40dBm  信道=4
2. SSID=NVDA  信号=-43dBm  信道=6
…  共发现 15 个热点  [PASS]

——————————————
测试结果:PASS 5 项,FAIL 0 项   ★★★ 测试通过 ★★★
★ 模块已恢复出厂默认(115200、无残留配置)
★ 可直接出货,无需再次烧录。

若能看到固件版本、附近 WiFi SSID 列表以及 PASS 汇总,即表示 ESP-AT 固件已正确烧录并正常工作。测下一片时,换上新扩展板,按一下 UNO 的复位键即可。

9故障排查与注意事项

1. 工具无法连接 / 提示 “Failed to connect”。
重新执行下载时序:先点 START,再长按 BOOT,点一下 EN,松开 BOOT。检查 Arduino UNO 上的 RESETGND 短接线是否到位(主控芯片是否已被复位),并确认 D0→ESP_RX、D1→ESP_TX 没有接反。

2. 看不到 COM 端口。
安装 Arduino UNO 的 USB 转串口驱动,并确认端口出现在 Windows 设备管理器中。更换 USB 线或 USB 口再试 — 部分数据线仅供电、不传数据。

3. 烧录中途失败。
将 BAUD 降到 115200,杜邦线尽量短,并确保扩展板仅由 Arduino UNO 的 3.3 V 引脚供电。杜邦线或测试夹接触不良是中途报错最常见的原因。

4. 功能测试时 AT 无响应。
请记住 AT 接口在 ATRX / ATTX(D5 / D4),而非烧录口。确认扩展板已完整插入 Arduino UNO、UART 选择器处于出厂默认的 D5 / D4 位置、串口监视器波特率设为 9600,且没有其他程序占用该 COM 端口。按一下 UNO 复位键可重新运行测试。

提醒:本扩展板请使用 MINI-1 factory 固件,并烧录到地址 0x0。使用其他型号变体的固件会导致模组无响应。

© 2026 OSOYOO | www.osoyoo.com