別再用很難接線的 ESP-01 了!以正規 NodeMCU/D1 mini(ESP8266)替任何無 WiFi 的開發板打造物聯網應用──燒錄 AT firmware 並傳送 AT 指令

Alan Wang
19 min readAug 13, 2020

ESP-01 這塊極其迷你的 ESP8266 開發板,曾經風靡一時,因為它讓 Arduino Uno 這類簡單的開發板突然多了能連上網路的能力。事實上,這種使用外部 WiFi 晶片的架構仍沿用在許多產品中,例如 Arduino MKR 1010 和 Nano 33 IoT,或是設計給 Arduino 的 ESP 擴充板。

ESP-01(以及記憶體加大的 ESP-01S)出廠時會安裝安可信(Espressif)的 AT 韌體,這韌體讓外部裝置能以序列埠指令(或 AT 指令)來控制此模組的行為,如連上 WiFi、傳送資料等等。

但問題就在於,ESP-01 模組的接線和供電有點麻煩,想燒錄新韌體就更麻煩了。此外,若想在電腦上用終端機監看 ESP-01 的回應訊息,不僅得買個 USB 轉 TTL 模組,接線複雜度也會進一步增加。對初學者而言,光是對硬體除錯可能就把耐心磨光了。

外接 ESP-01 模組的方式(包含外部電源,但不含除錯用的 USB-TTL 模組)

相較之下,直接使用正規 ESP8266 開發板(如 NodeMCU 或 WeMos D1 mini)來做 IoT 專案不僅夠便宜,也有 Arduino C++ 或 MicroPython 語言可選。可是回過頭來,BBC micro:bit 這類板子還是很麻煩,因為它非得倚賴外部 WiFi 晶片不可 — — 若不是外接一個 ESP-01,就是去購買某些廠商推出的 ESP-01 擴充板。

筆者以前曾針對 BBC micro:bit 和 ESP-01 模組寫過一些 IoT 應用,近來發現越來越多人詢問,但對於對方遇到的軟硬體問題總是愛莫能助。ESP-01 用起來就是麻煩,軟硬體會出錯的地方多得是,若沒有實際監看其序列埠回應,大概也很難判斷問題究竟在哪。

有鑑於此,筆者在這系列文章提出一個新解法──使用全尺寸、已經內建有 USB 介面晶片的 ESP8266 開發板來燒錄 AT 韌體,並把它當成其他開發板的外部 WiFi 晶片。這麼做有以下好處:

  1. 現在 ESP8266 開發板容易取得且仍相對便宜;
  2. 接線和供電只要 1 至 2 條 USB 線和 3 條杜邦線;
  3. 如果 ESP8266 的 USB 線接在電腦上的話,還可順便監看 ESP8266 AT 韌體的回應。

只要安裝好 AT 韌體,這塊板子也能重複使用。當然由於 AT 指令內容相當多,這邊就只會講最簡單的基礎。

讓我們開始吧。

註:當然囉,本篇的方法只適用於 Windows 環境。

Photo by Sabri Tuzcu on Unsplash

購買 ESP8266 開發板

首先,你需要一塊 NodeMCU 或 D1 mini,或者其他 ESP8266 開發板。不管造型怎樣,功能都是一樣的。

最容易購買和使用的是 NodeMCU,這又分成 V2 和 V3 兩種:

比較大的是 V3,較小的是 V2。

V2 和 V3 的差異只在尺寸和 USB 晶片不同而已(前者使用方形的 CP2102,後者則是長方形的 CH340)。有些 V2 或 V3 在網路上的價格僅有台幣百元出頭,這不會比 ESP-01S 貴上多少,而且又已經焊有針腳,買來就立即可用。

至於 WeMos 的 D1 mini 以及 D1 mini Pro 是因尺寸小所以頗受歡迎的產品(其實你能買到的說不定還是仿製版,但功能沒有差異),缺點是你得自己焊接針腳:

或者你可買像這樣做成 Arduino Uno 造型的 WeMos D1/D1 R2(同樣的這也有仿製版存在):

本篇則不討論 ESP-32 開發板,這是 ESP-8266 的升級版,規格上有明顯差異,價格也貴得多。

安裝 USB 驅動程式

取決你的 ESP8266 使用哪種 USB 晶片,你得在 Windows 上安裝 CP2102 或 CH340 的驅動程式,這樣電腦才能跟板子溝通:

  1. CP2102https://cn.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers
  2. CH340http://www.wch.cn/download/ch341ser_exe.html
Photo by Shelbey Miller on Unsplash

下載 AT 韌體及燒錄

你買來的開發板上可能已經裝有某個版本的 AT 韌體,但現在我們要把最新版的燒錄上去。這個辦法適用於已經燒錄過其他程式語言(如 C++ 或 MicroPython)的 ESP8266。筆者目前測試過 NodeMCU V2 和 WeMos D1 mini 都是可行的。

首先,下載下面這兩樣東西:

  1. 最新的 ESP8266_NONOS_SDK:https://github.com/espressif/ESP8266_NONOS_SDK/releases。本篇以 3.0.4 為例。解壓後丟到電腦內某處。
  2. 安可信的 Flash Download Tools:https://www.espressif.com/en/support/download/other-tools。本篇以 v3.8.5 為例。同樣解壓後丟到電腦內。

執行 Flash Download Tools 資料夾中的 flash_download_tool_3.8.5.exe,點選 Developer mode 然後選 ESP8266。這樣就會帶出下面的燒錄畫面。

設定燒錄選項與檔案

在燒錄畫面中,你得做如下的設定:

首先你要指定需寫入的檔案及位址:(所有檔案在 ESP8266_NONOS_SDK-3.0.4 底下)

  1. \bin\boot_v1.7.bin0x000000
  2. \bin\at\1024+1024\user1.2048.new.5.bin0x01000
  3. \bin\esp_init_data_default_v08.bin0x1FC000
  4. \bin\blank.bin0x7E000
  5. \bin\blank.bin0x1FB000
  6. \bin\blank.bin0x1FE000

此外是其餘設定:

  1. SPI SPEED -> 40MHz
  2. SPI MODE -> QIO
  3. FLASH SIZE -> 16Mbit(但 16Mbit-C1 似乎也可以)
  4. COM -> 板子所在的 COM port
  5. BAUD -> 115200(有時接上板子會跳到 1152000,記得改掉)

按下 START 就能啟動燒錄了。耐心等它跑完。

以上參數其實在 AT 指令集文件內也有寫。如果是 ESP-01,要改選 8Mbit 以及 512+512\user1.2048.new.5.bin。

Photo by Chinh Le Duc on Unsplash

測試 AT 韌體回應

燒錄完成後,我們可以用終端機軟體來測試 AT 韌體是否正常運作。

安裝 Tera Term(https://osdn.net/projects/ttssh2/releases/),並確保板子仍接在電腦上。執行 Tera Term 後選擇板子所在的 COM port(若選單沒出現,點「文件」→「建立新連線」):

這張圖中連的是 D1 mini,可見它使用 CH340 USB 晶片

點「確定」就能連線。按一下板子的 RST 或 RESET 鈕重開它,你可能會看到畫面中跑一些垃圾訊息出來,最後一行可能是 Ready:

點選單的「設定」→「終端機」,調整設定如下:

至少「發送」和「本地回顧」這兩個要調

接著到「設定」→「連接埠」,將位元速率(baud rate)調為 115200:

現在,在畫面中用鍵盤輸入 AT 然後按 Enter,你應該會看到下面的字出現:

AATTOK

看到這樣的結果就代表 AT 韌體正在運作,且能回應。

之所以看到 AT 的字有重複,是因為 AT 韌體會將我們輸入的東西重傳一次回來,屬於正常現象。關閉這個功能的指令如下:

ATE0

接著來試些東西。我們可以輸入

AT+GMR

來檢查韌體版本:

AT version:1.7.4.0(May 11 2020 19:13:04)
SDK version:3.0.4(9532ceb)
compile time:May 27 2020 10:12:22
Bin version(Wroom 02):1.7.4
OK

如何?很不錯吧。若是在 ESP-01 上,韌體版本會比這舊多了(可能源自 2013 或 2014 年)。

Photo by Markus Winkler on Unsplash

再來試另一個指令。首先輸入

AT+CWMODE=1

這會讓板子進入 WiFi station 模式。然後

AT+CWLAP

這會顯示出附近所有的 WiFi 熱點名稱、位址和強度等資訊:

+CWLAP:(3,"FLAG-N007-B",-67,"d8:b0:4c:f2:21:64",1,-24,0,3,3,7,0)
+CWLAP:(3,"FLAG-N007-60",-73,"d8:b0:4c:f2:22:50",1,-74,0,3,3,7,0)
+CWLAP:(0,"FLAG-N007-99",-56,"d8:b0:4c:f2:22:80",1,-14,0,0,0,7,0)
+CWLAP:(0,"FLAG-XXXX",-80,"be:dd:c2:82:43:8f",1,-7,0,0,0,3,0)
+CWLAP:(4,"MicroPython-4e73b8",-82,"52:02:91:4e:73:b8",1,-9,0,5,3,3,0)
+CWLAP:(4,"MicroPython-6c81c2",-86,"be:dd:c2:6c:81:c2",1,8,0,5,3,3,0)
+CWLAP:(4,"AAM-B",-85,"e4:6f:13:2f:73:74",1,-29,0,5,3,7,1)
+CWLAP:(4,"MicroPython-205064",-84,"2e:f4:32:20:50:64",5,-7,0,5,3,3,0)
+CWLAP:(3,"flaginfo",-79,"cc:5d:4e:45:07:10",1,-37,0,4,4,7,0)
+CWLAP:(3,"FLAG",-41,"e8:26:89:bb:2a:64",6,5,0,4,4,7,0)
+CWLAP:(4,"CHT_I040GW1",-68,"1c:49:7b:9b:61:50",7,-26,0,5,3,7,0)
+CWLAP:(0,"CSD_Employee",-91,"24:de:c6:86:1b:90",1,0,0,0,0,7,0)
+CWLAP:(0,"CSD_Guest",-91,"24:de:c6:86:1b:91",1,0,0,0,0,7,0)
+CWLAP:(3,"TN-ASUS-AP01",-93,"54:a0:50:e1:f7:84",9,-32,0,4,4,7,1)
+CWLAP:(3,"",-66,"00:0e:8e:b9:70:56",10,-39,0,4,4,3,0)
+CWLAP:(4,"oh4648",-76,"c8:5b:a0:3f:7b:57",11,-16,0,4,4,7,0)
+CWLAP:(0,"BUFFALO-E0E204-1",-73,"10:6f:3f:e0:e2:04",11,-32,0,0,0,7,1)
+CWLAP:(3,"FLAG-N007-test",-75,"d8:b0:4c:e0:74:ac",11,5,0,4,4,7,0)
+CWLAP:(0,"FlagPub",-73,"10:6f:3f:e0:e2:06",11,-32,0,0,0,7,0)
+CWLAP:(3,"DSL-7740C",-82,"ac:f1:df:01:1f:7f",11,1,0,4,4,7,0)
+CWLAP:(0,"Tainan-user",-83,"1c:3a:60:24:81:d8",11,-6,0,0,0,7,0)
+CWLAP:(3,"Tainan-guest",-85,"1c:3a:60:64:81:d8",11,-6,0,4,4,7,0)

最後將板子重設,好清除上述設定:

AT+RESTORE
Photo by Annie Spratt on Unsplash

讓開發板以序列埠對 ESP8266 傳送 AT 訊息

只要你懂 AT 指令,你可以直接在終端機軟體內控制 ESP8266 連上網路、發送 HTTP 請求等。可想而知,若把這個過程寫在程式中、由開發板透過序列埠傳給 ESP8266,就能做到相同的結果了。對於 micro:bit 這類天生缺乏網路功能的板子來說,這無疑是一大福音。

那麼,燒錄好的 ESP8266 板子要如何跟其他開發板連接呢?

有趣的地方就在這裡:現在 ESP8266 板子除了會透過 USB 交換 AT 訊息,也會透過其 Tx/Rx 腳位做一樣的事。也就是說,你能將其 Tx 和 Rx 腳位接到其他開發板,同時透過 ESP8266 的 USB 頭來供電/讀取序列埠資料。你總共只需要 2 條 USB 線和 3 條杜邦線。

以 Uno 簡單測試序列埠 AT 訊息交換

在本篇,我們先用 Arduino Uno 做個示範,畢竟 Uno 可以在硬體序列埠外建立軟體序列埠,我們能藉此「透過」Uno 傳 AT 指令給 ESP8266,並看到後者傳回的回應。

下面來連接 Uno 和 ESP8266,兩塊板子各自用 USB 線供電(電腦的 USB 槽電力是夠的):

  1. GND → GND
  2. Tx → 12
  3. Rx → 11
這裡的圖用的是 NodeMCU V3,沒為什麼,只因 fritzing 裡這板子的圖最好看。
#include <SoftwareSerial.h>SoftwareSerial softSerial(11, 12); // 軟體序列埠 (Rx, Tx)void setup() {

Serial.begin(115200);
softSerial.begin(115200);

}
void loop() {

if (Serial.available()) { // 將序列埠輸入的字串傳給軟體序列埠
softSerial.write(Serial.read());
}

if (softSerial.available()) { // 將軟體序列埠輸入的字串傳給序列埠
Serial.write(softSerial.read());
}

}

在 Arduino IDE 中燒錄以上程式,這會使 Uno 變成一個序列埠「橋梁」 — — 你在序列埠監控視窗裡(記得 baud rate 要設為 115200)輸入 AT 指令,就會被 Uno 傳給 ESP8266,然後其回應會透過 Uno 傳回。不過,若是要在程式中送出 AT 指令,結尾必須記得加上換行符號(\r\n 或 \u000d\u000a)就是,不然 AT 韌體不會回應。

當然,這個程式寫得很簡單,而基於 Uno 的序列埠緩衝區大小限制,你會發現有不少資料遺失,讀到的 AT 回應不完整。某方面而言這是可以改善的,比如不斷把回應讀進字串、在某個時候再放出來。但這裡就暫時不討論了。

Photo by Jim Sung on Unsplash

使用 ESP8266 上傳資料到 ThingSpeak 物聯網網站

第二個範例是將 BBC micro:bit 接上 ESP8266,這其實也是筆者過去以 ESP-01 做過的應用。為了簡化起見,在此我們完全忽略 ESP8266 的 AT 回應,畢竟傳送資料到 ThinkSpeak,只需要用 HTTP GET 送出一個網址就行了。

這裡我們不介紹如何申請 ThinkSpeak 帳號並設定一個「頻道」──網路上應能找到許多教學文。

在此 micro:bit 會改用 P15/P16 腳位來傳送 UART 訊息。由於 micro:bit 是雙晶片架構,所以它不需要透過序列埠燒錄程式,但改變 UART 腳位後電腦就收不到 UART 訊息了。因此,你得監看 ESP8266 本身的序列埠才能知道程式跑到哪裡。

如果把 ESP8266 和 micro:bit 的 3.3V 電源接在一起,那麼 micro:bit 燒好程式後,用一條 USB 線直接從 ESP8266 供電就能兩個一起運作了 。據了解 CH340G 在 3.3V 可供 50 mA 的電,這對 micro:bit 來說是夠的。(許多 ESP8266 的 5V 腳位則是直接從 USB 吃電,但要注意 micro:bit 的運作電壓不能超過 3.6V。)

當然,ESP8266 在使用 WiFi 時,尖峰耗電量可能達 300–400 mA。所以有時對 ESP8266 獨立供電可能是更好的主意。

下面我們以 micro:bit 版的 MicroPython 為例,你可以使用官方線上編輯器Mu editor。一開始記得先用編輯器燒錄一個空程式進去,這樣板子上才會有 Python 直譯器。

我們需要使用的 AT 指令如下:

  1. AT+RESTORE(恢復預設設定)
  2. AT+CWMODE=1(設為 STA 或 station 模式)
  3. AT+RST(重置)
  4. AT+CWJAP=”wifi名稱”,”wifi密碼”(連上 WiFi)
  5. AT+CIPSTART=”TCP”,”server路徑”,80(連上伺服器)
  6. AT+CIPSEND=要傳送的字串長度(含換行字元)
    (然後緊接著傳送該字串,也就是 HTTP GET 請求)
SSID    = 'xxxxx'   # WiFi 名稱
PWD = 'ooooo' # WiFi 密碼
API_KEY = '你的API_KEY' # ThinkSpeak api key
URL = 'api.thingspeak.com' # 伺服器位址
REQUEST = '/update?api_key={}&field1={}' # 在伺服器要 GET 的網址

# 匯入所需模組
from microbit import uart, sleep, pin15, pin16, display
# 重新指定 UART 腳位為 P15, P16
uart.init(baudrate=115200, tx=pin15, rx=pin16)

# 用來傳 AT 指令的 function (包含傳送後的等待時間)
def sendAT(cmd, delay=100):
uart.write(str(cmd) + '\r\n')
sleep(delay)
sendAT('AT+RESTORE', 1000) # 恢復預設設定
sendAT('AT+CWMODE=1') # 設為 station 模式
sendAT('AT+RST', 1000) # 重置
# 連到 WiFi (建議等久一點)
sendAT('AT+CWJAP=\"{}\",\"{}\"'.format(SSID, PWD), 12000)

while True:

# 連到伺服器
sendAT('AT+CIPSTART=\"TCP\",\"{}\",80'.format(URL), 1000)

# 產生要送出的 GET 字串 (包含亮度感測值)
sendStr = 'GET ' + REQUEST.format(API_KEY, display.read_light_level())

# 先指定要傳送的字數 (含 \r\n)
sendAT('AT+CIPSEND={}'.format(len(sendStr) + 2))

# 傳送 GET 字串
sendAT(sendStr, 0)

# 等待下次傳送
sleep(9000)

WiFi 連線資料會被記錄在 ESP8266 上,再次通電時就會自動連上那個 WiFi。當然程式每次執行時還是會強制重設 + 重置 ESP8266 就是。

既然沒有讀取 AT 回應,有些指令就必須加上合適的延遲了。比如,有時使用 AT+CWJAP 連上 WiFi 的時間會格外的長,若來不及在時限內連上,後面的指令就會錯到底。所以若你遇到問題,可以試著將延遲時間加多一點。

你可以把 ESP8266 接在電腦上,這麼一來你就可以用 Tera Term 讀它的 AT 回應:

AT+CWJAP="xxxxx","ooooo"
WIFI CONNECTED
WIFI GOT IP
OK
AT+CIPSTART="TCP","api.thingspeak.com",80
CONNECT
OK
AT+CIPSEND=48
OK
>
Recv 48 bytes
SEND OK+IPD,2:11CLOSED

你能看到 ThinkSpeak 上確實收到資料並顯示出來。

Photo by Franki Chamaki on Unsplash

結語

本篇簡介了如何在正規 ESP8266 開發板上燒錄最新的 AT firmware,並用它當成其他開發板的 WiFi 晶片。儘管這樣做比不上直接使用 ESP8266 做 IoT 開發,但這樣能給某些無 WiFi 能力的板子朝網路延伸的機會。此外,這樣在供電與接線上,也比使用較舊的 ESP-01 模組容易許多。

當然,如果你想做到更複雜的網路控制,就必須研讀安可信的 AT 指令集。此外,接收資料的難度會更高,因為一不小心就可能錯過回應訊息(如 HTTP GET 的整個傳回結果)。所以,單純傳送資料到外部網站仍是比較容易做的應用。

另一個小問題是 ESP8266 的連線位址必須拆開成伺服器和後續的網域,這使得像是 IFTTT 的 Webhook 服務是沒法以這種方式呼叫的(我想大概是要 GET 整句才能正確轉換 DNS)。因此 ThinkSpeak 大概仍是這類板子上傳感測器資料時最好的對象吧。

Photo by Benjamin Davies on Unsplash

--

--

Alan Wang

Technical writer, former translator and IT editor.