另外測試過程中發現, 在下 "AT+CWJAP=ssid,pwd" 指令後, 不要接著下 "AT+CWMODE=1" 修改為模式 1, 因為這會造成 SoftAP 馬上消失, 使得伺服器被關閉, 導致無法回應 "Wifi setup OK!" 給前端手機瀏覽器, 因此工作模式的切換應該放在 setup() 函數中才對.
為了要切換 wifi 設定模式與工作模式, 我使用了Arduino 的 D4 接腳與一個滑動開關來作為模式偵測之用, D4 先串接一個 10K 電阻接到滑動開關的中間接腳 (最好不要將 Arduino 的輸入接腳直接接地, 應該串個 10k 電阻), 而左方接腳則接 5V, 右方接腳接 GND, 如下圖所示 :
圖中滑動開關切在右邊, 使得 D4 經 10K 電阻接地, 這是在 Wifi 設定模式; 若切到左邊就是工作模式. 我們在程式中要偵測 D4 的位準, 若為 LOW 表示是在 Wifi 設定模式, 反之為工作模式.
在此實驗中我使用了之前製作的 ESP8266 轉接板, 直接從 Arduino 分別接 +5V 與 GND 到轉接板的左邊算來第四腳 VCC (紅線) 與第六腳 GND (黑線). 另外, D11 腳被定義為軟體序列埠的 TX, 它被接到轉接板的左邊算來第三腳 (綠線), 即 ESP8266 的 RX; 而 D10 腳被定義為軟體序列埠的 RX, 它被接到轉接板的左邊算來第一腳 (白線), 即 ESP8266 的 TX. 關於轉接板, 參考 :
# 製作 ESP8266 轉接板
# 撰寫 Arduino 的 ESP8266 WiFi 函式
如果沒有製作轉接板也是可以參考下面的電路圖自行接線 (使用 Upverter 繪製) :
# 線上電子設計平台 Upverter
# Upverter 匯出與匯入專案
完整的的程式如下 :
#include <SoftwareSerial.h>
#define DEBUG true
SoftwareSerial esp8266(10,11); //(RX,TX)
const int SW_PIN=4; //Pin to switch configuration or working mode
const int MAX_PAGE_NAME_LEN=48; //buffer size
char buffer[MAX_PAGE_NAME_LEN + 1]; //store page_name/ssid/pwd
int mode; //store current mode(LOW=configuration, HIGH=working)
void setup() {
Serial.begin(9600);
esp8266.begin(9600);
sendData("AT+RST\r\n",2000,DEBUG); // reset ESP8266
pinMode(SW_PIN, INPUT);
mode=digitalRead(SW_PIN);
if (mode==LOW) { //wifi configuration mode :
sendData("AT+CWMODE=3\r\n",1000,DEBUG); //configure as access point
sendData("AT+CIPMUX=1\r\n",1000,DEBUG); //enable multiple connections
sendData("AT+CIPSERVER=1,80\r\n",1000,DEBUG); //turn on server 80 port
sendData("AT+CIFSR\r\n",1000,DEBUG); //get ip address
}
else { //working mode
sendData("AT+CWMODE=1\r\n",1000,DEBUG); // configure as station
}
}
void loop() {
if (mode==LOW) {setupWifi();}
else { //working mode : application codes are here
sendData("AT+CIFSR\r\n",1000,DEBUG);
delay(2000);
}
}
void setupWifi() {
if (esp8266.available()) { // check if the esp is sending a message
if (esp8266.find("+IPD,")) {
delay(1000);
//esp8266 link response : +IPD,0,498:GET / HTTP/1.1
//retrieve connection ID from response (0~4, after "+IPD,")
int connectionId=esp8266.read()-48; //from ASCII to number
//subtract 48 because read() returns ASCII decimal value
//and in ASCII, "0" (the first decimal number) starts at 48
if (esp8266.find("GET /")) { //retrieve page name (router)
memset(buffer, 0, sizeof(buffer)); //clear buffer (all set to 0)
if (esp8266.readBytesUntil('/', buffer, sizeof(buffer))) {
if (strcmp(buffer, "update") == 0) { //update wifi
//"?ssid=aaa&pwd=bbb HTTP/1.1"
esp8266.find("?ssid="); //skip ssid token
memset(buffer, 0, sizeof(buffer)); //clear buffer (all set to 0)
esp8266.readBytesUntil('&', buffer, sizeof(buffer)); //retrieve ssid
String ssid=buffer;
esp8266.find("pwd="); //skip pwd token
memset(buffer, 0, sizeof(buffer)); //clear buffer (all set to 0)
esp8266.readBytesUntil(' ', buffer, sizeof(buffer)); //retrieve pwd
String pwd=buffer;
//configure as a station
String res=sendData("AT+CWJAP=\"" + ssid + "\",\"" + pwd + "\"\r\n",6000,DEBUG);
//show setup result
String webpage="<html>Wifi setup ";
if (res.indexOf("OK") != -1) {webpage += "OK!</html>";}
else {webpage += "Failed!</html>";}
String cipSend = "AT+CIPSEND=";
cipSend += connectionId;
cipSend += ",";
cipSend +=webpage.length();
cipSend +="\r\n";
sendData(cipSend,1000,DEBUG);
sendData(webpage,2000,DEBUG);
String closeCommand = "AT+CIPCLOSE=";
closeCommand+=connectionId; // append connection id
closeCommand+="\r\n";
sendData(closeCommand,3000,DEBUG);
}
else { //show setup page
String webpage="<html><form method=get action='/update/'>SSID ";
webpage += "<input name=ssid type=text><br>";
String cipSend = "AT+CIPSEND=";
cipSend = "AT+CIPSEND=";
cipSend += connectionId;
cipSend += ",";
cipSend +=webpage.length();
cipSend +="\r\n";
sendData(cipSend,1000,DEBUG);
sendData(webpage,2000,DEBUG);
webpage="PWD <input name=pwd type=text> ";
cipSend = "AT+CIPSEND=";
cipSend += connectionId;
cipSend += ",";
cipSend +=webpage.length();
cipSend +="\r\n";
sendData(cipSend,1000,DEBUG);
sendData(webpage,2000,DEBUG);
webpage="<input type=submit value=Connect></form></html>";
cipSend = "AT+CIPSEND=";
cipSend += connectionId;
cipSend += ",";
cipSend +=webpage.length();
cipSend +="\r\n";
sendData(cipSend,1000,DEBUG);
sendData(webpage,2000,DEBUG);
String closeCommand = "AT+CIPCLOSE=";
closeCommand+=connectionId; // append connection id
closeCommand+="\r\n";
sendData(closeCommand,3000,DEBUG);
}
}
}
}
}
}
String sendData(String command, const int timeout, boolean debug) {
String response="";
esp8266.print(command); // send the read character to the esp8266
long int time=millis();
while ((time+timeout) > millis()) {
while(esp8266.available()) {
// The esp has data so display its output to the serial window
char c=esp8266.read(); // read the next character.
response += c;
}
}
if (debug) {Serial.print(response);}
return response;
}
在 loop() 函數中, 首先判斷工作模式, 若在 wifi 設定模式 (mode==LOW), 就呼叫 setupWifi() 函數來處理; 否則就執行 else{} 中的工作模式 (mode==HIGH) 程式碼, 這裡只是簡單的用 AT+CIFSR 指令查詢 IP 而已.
在 wifi 設定模式中, 當擷取出使用者傳送的 ssid 與 pwd 後, 就呼叫 sendDat() 函數以 "AT+CWJAP=" 指令將其寫入 ESP8266 中, 然後從其回傳字串是否含有 "OK" 判斷是否設定成功, 成功的話就回應瀏覽器 "Wifi setup OK!".
程式上傳後, 先拔掉 Nano 的電源, 把滑動開關切到右邊的 wifi 設定模式, 再接上 Nano 的電源, 這時因為 D4 腳偵測到 LOW, 所以會進入設定模式, 在 setup() 函數中將 ESP8266 設在模式 3, 並開啟多重連線與伺服器之 80 埠.
這時打開手機的 wifi, 連線 "ESP_" 開頭的無線基地台, 這是 ESP8266 在模式 3 下開啟的 SoftAP.
然後啟動手機瀏覽器, 連線 192.168.4.1 (SoftAP 的固定網址), ESP8266 收到 Request 後會回應如下標頭訊息 :
+IPD,0,362:GET / HTTP/1.1
Arduino 程式在偵測到 +IPD 連線回應後, 擷取 "GET /" 與 "HTTP" 間的網頁路徑, 發現不是 "update", 就利用 ESP8266 的伺服器功能向瀏覽器回應 wifi 設定表單網頁.
輸入家中聯外的無線基地台 ssid 與 pwd, 按 Connect, 這時 ESP8266 會回應如下標題訊息 :
+IPD,0,426:GET /update/?ssid=EDIMAX-tony&pwd=1234567890 HTTP/1.1
這時 Arduino 分析 "GET /" 與 "/"之間的網頁名稱為 update, 就繼續擷取後面的 ssid 與 pwd 參數, 再用 "AT+CWJAP=" 指令設定要連線的 AP. 成功的話就回應瀏覽器 "Wifi setup OK!" 即表示設定成功.
這時就可以拔掉電源, 將滑動開關切到左邊的工作模式, 再重新插電開機.
序列埠監視視窗所擷取之訊息如下 :
AT+RST
OK
bB�鑭b禔S��"愃L�侒��餾�
[System Ready, Vendor:www.ai-thinker.com]
AT+CWMODE=3
no change
AT+CIPMUX=1
OK
AT+CIPSERVER=1,80
OK
AT+CIFSR
192.168.4.1
0.0.0.0
OK
1.1
Host: 192.168.4.1
Connection: keep-aliveAT+CIPSEND=0,77
> <html><form method=get action='/update/'>SSID <input name=ssid
SEND OK
AT+CIPSEND=0,31
> PWD <input name=pwd type=text>
SEND OK
AT+CIPSEND=0,47
> <input type=submit value=Connect></form></html>
SEND OK
AT+CIPCLOSE=0
Link
+IPD,1,355:GET /favicon.ico HTTP/1.1
Host: 192.168.4.1
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Linux; Android 4.4.2; H30-L02 Build/HonorH30-L02) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36
Accept-Encoding: gzip,deflate
Accept-Language: zh-TW,en-US;q=0.8
X-Requested-With: com.android.browser
OK
OK
HTTP/1.1
AT+CWJAP="EDIMAX-tony","1234567890"
OK
AT+CIPSEND=0,27
> <html>Wifi setup OK!</html>
SEND OK
AT+CIPCLOSE=0
OK
AT+RST
OK
bB�鑭b禔S��"愃L�侂��餾�
[System Ready, Vendor:www.ai-thinker.com]
AT+CWMODE=1
no change
AT+CIFSR
0.0.0.0
OK
AT+CIFSR
0.0.0.0
OK
AT+CIFSR
192.168.2.114
OK
AT+CIFSR
192.168.2.114
OK
AT+CIFSR
192.168.2.114
........
上面程式的工作模式僅僅是向 ESP8266 每隔 2 秒送出 "AT+CIFSR", 因此它會不斷地回應所獲得的區網 IP, ESP8266 板子上的藍燈也會每 2 秒閃一次. 用在物聯網應用時 (例如溫溼度測候紀錄), 程式碼就寫在 loop() 函數的 else{} 區塊內即可.
草稿碼使用了 8,996 bytes (29%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 722 bytes (35%) 的動態記憶體,剩餘 1,326 bytes 供局部變數。最大值為 2,048 bytes 。
很不錯, 只用掉了約 3 成的記憶體, 比之前用 wifi 函式庫還省, 這得感謝 AllAboutEE 程式碼的啟發, 這告訴我們 : 凡事要化繁為簡. 我們常為了完美追求形式化, 把整個系統越搞越大, 而效能卻越來越低.
好了, 終於在集中火力猛攻下, 完成了學習 ESP8266 以來始終想要搞定的事. 接下來我要把上面的電路製作成一個 Arduino+ESP8266 模組, 方便之後的應用實驗. 例如以前做過的 DHT11 物聯網實驗, 前陣子有網友問我為何其 DHT11 數值很奇怪, 那我就用新模組來重作看看唄!
# ESP8266 WiFi 模組與 Arduino 連線測試
已經好久沒拿烙鐵囉!
# Ameba Arduino: UART – 使用UART與電腦溝通
# ESP8266 WiFi Control Relay
# ESP8266 WiFi Control Device ( Relay ) 無線控制設備(繼電器)
# Arduino WiFi Control with ESP8266 Module#
# 58 ESP8266 Sensor runs 17 days on a coin cell/transmits data to sparkfun.com and ubidots.com
# Arduino/libraries/
# WiFi Web Server
# ESP8266 Temperature / Humidity Webserver
# Arduino/libraries (ESP8266WiFi)
# Webserver for Arduino ESP8266
# https://github.com/itead/ITEADLIB_Arduino_WeeESP8266
# ESP8266 WiFi 模組 AT command 測試
# espressif/ESP8266_AT
# 認識Arduino與C語言的函式指標以及函式指標陣列
# ESP8266 wiring with Arduino
您好,我用Android手機透過WIFI連上SoftAP之後,開啟192.168.4.1可以看到輸入SSID&PASSWD的網頁,但是用iPhone7透過WIFI連上SoftAP之後,用Safari開啟192.168.4.1則一直是空白,沒有顯示任何東西,請問是否有遇過這樣的問題? 感謝分享!!
回覆刪除因我無 Apple 產品, 都在 Android 上測試, 所以不知在 iPhone 有此問題. 但此為很單純網頁, 奇怪 iPhone 為何無法顯示, 有時間借手機來試試看.
回覆刪除有可能是 Apple 的瀏覽器對 HTML 檢查較嚴, 因我的 input 元件沒有用 form 包起來.
回覆刪除Sorry, 我看錯了, 有 form 哩! 看來可能還有其他格式問題也說不定. 手機瀏覽器似乎沒有偵錯工具.
回覆刪除版主好~~看到版主您做伺服機測試,我就突發奇想想做客戶端測試而且要傳rfid的值,可是在見客戶端時卻卡關了,請版主解惑感恩~~,如果版主需要我的程式的話,請版主告知您的gmail~~感恩~~
回覆刪除不太了解, 我沒做過 RFID 實驗. 您是說您的客戶端要讀取 RFID 然後傳給伺服器嗎?
回覆刪除是的,而且也要把QR code值傳給伺服器
回覆刪除客戶端是用 ESP8266 嗎? 我 mailbox : tony1966@ms5.hinet.net
回覆刪除是的
回覆刪除已寄給版主,不知版主的寫法如何,可以傳授一下嗎??謝謝~~
回覆刪除太久沒用 Arduino, 生疏了. 請問你是用 ESP8266 Stand alone 還是 Arduino+ESP8266? 我手上沒有 RFID, 要等添購 RFID 模組才能測試.
回覆刪除想請問如果想把上述的程式碼與Blynk整合
回覆刪除在Blynk.begin(auth,wifi,ssid,psw)的部分要如何取得儲存在ESP8266的SSID與密碼呢?
Blynk 韌體與這不能共存, 應該是無法整合. 這需要 Blynk 提供 api 才行. 太久沒用 blynk 了, 不知是否有增加輸入基地台之功能.
回覆刪除