ESP-01/ESP-01S是市面上最小最便宜的ESP8266 Wifi模組之一,也是許多人學習物聯網(IoT)應用的入門管道。ESP8266本身其實也是個開發板,不過出廠時預載的韌體允許使用者以外部開發板和它溝通──透過所謂的「AT指令」。
筆者購買的ESP-01S,不過較舊的ESP-01也適用 |
接線方式如下:
- Pin 8/Pin 4 → 3.3V
- Pin 1 → GND
- Pin 2(Tx)→ micro:bit的Rx(在本例是P16)
- Pin 7(Rx)→ micro:bit的Tx(在本例是P15)
為了示範方便,這張圖的Tx和Rx接在P0和P1。 當然,由於使用Nexus:bit擴充板的關係,我們實際上改用P15/P16。 當然,你也能在下面的程式中修改Tx_pin和Rx_pin常數。 |
要注意ESP8266需要至少400mA的供電量才能正常運作。有時ESP-01可能在通電後未能正確啟動,這時就得手動重新接電。
此外,為了讀取ESP-01丟出的序列回應,我們也接上一個USB轉TTL模組,好在電腦上監看ESP-01的運作狀況。
下面是完整程式碼。我們之所以使用MakeCode編輯器的JavaScript模式,主要是為了能輸入AT指令結尾要求的CR+LF,也就是\u000D\u000A。此外,這兒需要處理不少字串,用MakeCode的積木做起來不太方便。
程式開頭的常數WIFI_MODE可用來設定採用STA(站點)或AP(熱點)模式,前者會連上你所在環境的Wifi分享器,後者則會讓自己變成Wifi分享器。下面的常數SSID_x和PASSWORD_x是兩個模式要使用的連線名稱與密碼。
注意:若使用STA模式,不要把含有連線名稱及密碼的程式碼隨意分享在網路上。
完整的AT指令與用法可參考這裡。
程式開頭的常數WIFI_MODE可用來設定採用STA(站點)或AP(熱點)模式,前者會連上你所在環境的Wifi分享器,後者則會讓自己變成Wifi分享器。下面的常數SSID_x和PASSWORD_x是兩個模式要使用的連線名稱與密碼。
注意:若使用STA模式,不要把含有連線名稱及密碼的程式碼隨意分享在網路上。
完整的AT指令與用法可參考這裡。
const WIFI_MODE: number = 2 // 1=STA模式; 2=AP模式
const Tx_pin: SerialPin = SerialPin.P15 // 接ESP-01的Rx pin
const Rx_pin: SerialPin = SerialPin.P16 // 接ESP-01的Tx pin
const LED_pin: DigitalPin = DigitalPin.P2 // LED控制腳位
const SSID_1: string = "your_wifi_ssid" // STA模式要連的wifi名稱
const PASSWORD_1: string = "your_wifi_password" // STA模式要連的wifi密碼
const SSID_2: string = "ESP8266" // AP模式熱點名稱
const PASSWORD_2: string = "microbit" // AP模式熱點密碼
let LED_status: number = 0
let serial_str: string = ""
pins.digitalWritePin(LED_pin, 0)
serial.redirect(Tx_pin, Rx_pin, 115200)
// 設定ESP-01
sendAT("AT+RESTORE", 1000)
sendAT("AT+RST", 1000)
sendAT("AT+CWMODE=" + WIFI_MODE)
if (WIFI_MODE == 1) { // STA模式, 等待連上再繼續
sendAT("AT+CWJAP=\"" + SSID_1 + "\",\"" + PASSWORD_1 + "\"")
let result: boolean = wait_for_response("OK")
if (!result) control.reset()
} else if (WIFI_MODE == 2) { // AP模式
// 開放1個連線頻道, 驗證模式=4 (WPA_WPA2_PSK)
sendAT("AT+CWSAP=\"" + SSID_2 + "\",\"" + PASSWORD_2 + "\",1,4", 1000)
}
// 設定伺服器
sendAT("AT+CIPMUX=1")
sendAT("AT+CIPSERVER=1,80")
// 顯示IP (在AP模式預設為192.168.4.1)
sendAT("AT+CIFSR")
// 顯示打勾圖案, 伺服器完成開啟
basic.showIcon(IconNames.Yes)
// 處理HTTP請求
while (true) {
// 儲存最長200字的序列資料, 以免loss掉
serial_str += serial.readString()
if (serial_str.length > 200) {
serial_str = serial_str.substr(serial_str.length - 200)
}
if (serial_str.includes("+IPD") && serial_str.includes("HTTP")) {
// 有連線請求
let client_ID: string = serial_str.substr(serial_str.indexOf("IPD") + 4, 1)
let GET_pos: number = serial_str.indexOf("GET")
let HTTP_pos: number = serial_str.indexOf("HTTP")
let GET_command: string = serial_str.substr(GET_pos + 5, (HTTP_pos - 1) - (GET_pos + 5))
let GET_success: boolean = false
// 判斷GET指令
switch (GET_command) {
case "": // request 192.168.x.x/
GET_success = true
break
case "LED": // request 192.168.x.x/LED
GET_success = true
LED_status = 1 - LED_status
pins.digitalWritePin(LED_pin, LED_status)
break
}
// 產生HTML並傳給使用者
let HTML_str: string = getHTML(GET_success)
sendAT("AT+CIPSEND=" + client_ID + "," + (HTML_str.length + 2))
sendAT(HTML_str, 1000)
// 關閉連線
sendAT("AT+CIPCLOSE=" + client_ID)
serial_str = ""
}
}
// 寫入AT指令並加上CR+LF
function sendAT(command: string, waitTime: number = 100) {
serial.writeString(command + "\u000D\u000A")
basic.pause(waitTime)
}
// 產生客戶端HTML
function getHTML(normal: boolean): string {
let LED_statusString: string = ""
let LED_buttonString: string = ""
let web_title: string = "ESP8266 (ESP-01) Wifi on BBC micro:bit"
let html: string = ""
html += "HTTP/1.1 200 OK\r\n"
html += "Content-Type: text/html\r\n"
html += "Connection: close\r\n\r\n"
html += "<!DOCTYPE html>"
html += "<html>"
html += "<head>"
html += "<link rel=\"icon\" href=\"data:,\">"
html += "<title>" + web_title + "</title>"
html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" // mobile view
html += "</head>"
html += "<body>"
html += "<div style=\"text-align:center\">"
html += "<h1>" + web_title + "</h1>"
html += "<br>"
if (normal) {
if (LED_status) {
LED_statusString = "ON"
LED_buttonString = "TURN IT OFF"
} else {
LED_statusString = "OFF"
LED_buttonString = "TURN IT ON"
}
html += "<h3>LED STATUS: " + LED_statusString + "</h3>"
html += "<br>"
html += "<input type=\"button\" onClick=\"window.location.href=\'LED\'\" value=\"" + LED_buttonString + "\">"
html += "<br>"
} else {
html += "<h3>ERROR: REQUEST NOT FOUND</h3>"
}
html += "<br>"
html += "<input type=\"button\" onClick=\"window.location.href=\'/'\" value=\"Home\">"
html += "</div>"
html += "</body>"
html += "</html>"
return html
}
// 等待wifi連線用
function wait_for_response(str: string): boolean {
let result: boolean = false
let time: number = input.runningTime()
while (true) {
serial_str += serial.readString()
if (serial_str.length > 200) {
serial_str = serial_str.substr(serial_str.length - 200)
}
if (serial_str.includes(str)) {
result = true
break
}
if (input.runningTime() - time > 300000) break
}
return result
}
網頁伺服器開啟後,使用者只要用手機連上名為ESP8266的熱點,然後在瀏覽器打開192.168.4.1就行了。micro:bit收到請求後,會把一個動態產生的HTML傳給使用者,顯示在手機上。
在這個簡單範例中,我們可以透過Wifi來遙控接在micro:bit上的一個LED燈。
若使用者想要增加其他功能或按鈕,只要修改判斷GET指令以及產生HTML的程式碼即可。
沒有留言:
張貼留言