Python可以這樣玩(27):ESP8266 and IoT
接下來我們的焦點就要回到 ESP8266上面,要讓 ESP8266連上 Arduino UNO,其實還有很長的路要走,我看了很多網路的分享,有些人上手的測試大概就花了兩個月,中間可能燒壞了好幾個 ESP8266,還好我不是先驅,所以我會整理出最佳方式,讓初學者可以輕鬆上手。
ESP8266是深圳樂鑫資訊(信息)科技公司開發的Wi-Fi晶片(芯片),只有32個接腳(引腳),而深圳安信可科技用這顆晶片開發出12種小型電路板,分別為ESP-01到ESP-12。如下圖:
不只安信可科技一家公司用ESP8266晶片做Wi-Fi小卡,也有其他板卡商拿這顆晶片去做小卡,例如北京多奧雲智科技做成DWA8,或位在紐約市的創客知名公司Adafruit也有做。
甚至現在已經出現了 ESP8266與Arduino 整合的板子,例如 ESP-12E 與 ESP-13(光華商場就可以買到,連說明書800多塊)都是。但我所使用的,就是目前大家討論最多的 ESP-01(新版是黑色板)。
ESP-01雖然便宜,但是卻是個設計上,與
Arduino和麵包板格格不入的板子,沒有說它不好,就是格格不入。這麼說是讓大家先有心理準備,不過它真的很便宜,我們就用吧。
不相容的腳位設計
下圖是 ESP-01的腳位,它的腳位設計蠻奇怪的,無法直接插入麵包板:
所以可以看到網路上有人分享,自己做一個轉接板,接插座跟接頭弄成適合麵包板插的形式,如下圖,當然,市面上也可以買到現成的,但是我去光華商場問過了,沒賣,上網找的話可以找到:
因此,最方便的解決之道就是買一包公對母的杜比接頭線,公頭就可以直接插到麵包板或者
Arduino 上,母頭則是接 ESP-01。
3.3V電壓與不相容的電流
ESP8266吃的是 3.3V的電壓,還可以高興一下,因為 Arduino UNO可以提供 3.3V的電壓,不幸的是Arduino官方規格寫 3.3V 只能提供 50mA 的電流,而 ESP8266
會吃超過 100mA 的電,理論上要外接電源,但是我覺得歐洲人在寫規格的時候會比較保守,雖說只提供
50mA的電流,但是超過也可以運作,一般來說,開
STA 模式傳資料時,ESP-01最多吃 100mA,沒出什麼問題,所以為了方便玩家入門,不用另外供電。
可是如果是要燒錄,也就是進入韌體更新模式,就會用到 200 – 300 mA的電流,這是後 Arduino 供電就會有問題,板子就不穩定,因此必須使用獨立的 3.3V電源。目前來說,建議不要急著測試燒錄,很多玩家在網路上都建議更新韌體,要忍住,我的原則還是選擇最簡易的方式來連線 IoT。
占用 Arduino RX/TX 腳位
真是一波未平一波又起,ESP8266的 RX/TX腳位必須接在 Arduino Uno 的D0/D1腳位 (板子上標記的:RX <- 0、TX -> 1),但是這樣接了之後,ESP8266 就佔據了電腦跟 Arduino 的 RX/TX,如下圖:
這樣一來,首先,你會無法上傳韌體,要先拔掉才能上傳,其次,上傳完畢插進去之後,又會佔據 Arduino 的 Serial Port,你就看不到 Serial.print 的結果,這對程式除錯來說,是個大麻煩。
還好這個問題我們可以用軟體來解決,我們先避開 D0/D1、一般可以使用D10/D11或是 D2/D3,如下圖,就把ESP的 TX接在D2,RX接在D3 (這個順序就不重要,必須看後面程式如何定義,我通常會反過來接):
然後,在 Arduino 的程式中,使用SoftwareSerial 程式庫,在程式中重新定義ESP8266 的RX/TX 到D3/D2,不過有一點要注意,
SoftwareSerial 支援 Buad rate 不超過 19200,如果韌體預設不是 9600 的就要改 buad rate。
這裡有個概念要提,RX/TX是相對應的,ESP的RX要接到 Arduino 的 TX,而ESP的TX則是要接到Arduino 的RX。在前面接D0/D1的圖中,可以看到 Arduino 板子上的標記:RX <- 0、TX -> 1,這是表示,D0是接其他設備的 RX腳位,所以其實 D0是 Arduino 的 TX,同理,D1則是 Arduino 的 RX。
要講這個是在後面會談到的程式碼中會出現 SoftwareSerial ESP(2,3)
這樣的寫法,它的語法如下:SoftwareSerial ESP(RX,TX),這表示把 Arduino 的 D2 定義為Arduino 的 RX,D3定義為TX,那麼在接法上,D2就必須接 ESP的 TX,D3就必須接 ESP的RX。弄清楚這個概念之後,無論後面你接哪個PIN,程式碼都不會弄錯。如果你把
ESP的RX接在D2,TX接在D3,程式就會寫成:SoftwareSerial
ESP(3,2); //RX, TX(註解中的RX, TX指的是Arduino的)。
ESP RX無法承受5V
最後的大問題來了,就是電壓問題。前面有關於供電問題,我們勉強用 Arduino 上面的 3.3V來克服了,但是接下來的問題是,我們把 ESP的TX接上D2,ESP的RX接上D3,當ESP 的 TX 傳一個 3.3V的高電位給Arduino的時候,不會有問題,因為 Arduino 雖然是 5V,但是它會把 >
2.0V的電位當成
1,< 0.8V的電位當成0,不會有錯。但是當 Arduino 傳 5V的高電位給 ESP的時候,由於ESP 是 3.3V,可能就會把ESP燒掉。
解法有三個,第一就是不管它,很多人在網路上面分享,即使不管它,ESP還是可以正常運作,的確,很多書上也這樣做,但是,使用時間一長,我們很難保證ESP不會被燒壞,所以即使這是最方便的方法,我也不贊成如此做。
第二種解法是利用電位轉換器(Level Shifter)的方式來做,如下圖:
D3與RX之前不可以直接連,必須多接一個 1K歐姆電阻,這樣還沒完,還要多接一個 2K歐姆電阻再接地。利用公式就可以算出 5V/(1K+2K) = x/2K,x = 3.3V,這樣就可以保護ESP不被 5V的電壓衝擊。
第三種方法是參考 “超圖解物聯網IoT實作入門” 這本書,我引用該書的插圖如下:
圖中的板子是ESP-12E的整合開發板,但是道理是一樣的。Arduino UNO透過SoftwareSerial程式庫,將數位2和3腳模擬成UART序列埠。由於Arduino的數位輸出高電位是5V,為了避免損壞ESP8266,Arduino的TX和ESP8266的RX之間用一個1N4148開關二極體串連,如下圖,1N4148的原理是,當Arduino 傳低電位0,可以通過,但是如果傳高電位5,則被擋住,這是ESP因為有上拉電阻而產生 3.3V的電位,視同接收到高電位:
經過PK,我決定採用 “超圖解物聯網IoT實作入門” 這本書的作法,至於接法我也比照,但是上面的接線圖用的是 ESP-12E,我必須把它轉換成 Arduino UNO + ESP-01,完成圖如下:
最左邊的橘線接 3.3V到麵包板下方正極,左邊第二條藍線則是將GND接到麵包板負極,D2的綠線接出來到麵包板連接
1N4148 (我連到左邊有黑環那頭),1N4148的右側則是連到 ESP的RX。剩下的就是黃線接ESP TX,右邊的紅線橘線接麵包板正極,咖啡線接麵包板負極。
我是故意沒有另外繪製線路圖的,因為這篇文章是心得分享,我希望看文章的人能夠真正懂我所要表達的東西,所以必須自我測試一下,由上面的說明成功的把線路接出來(真的不行請留言)。
測試程式碼
連接好了之後,我們就要準備一個簡單的程式碼,來測試儀下ESP8266是否可以正常運作:
#include
<SoftwareSerial.h>
SoftwareSerial
ESP8266(3,2); // Arduino 的
RX, TX
void
setup()
{
Serial.begin(115200);
ESP8266.begin(115200);
}
void
loop()
{
if(ESP8266.available())
Serial.write(ESP8266.read());
else if(Serial.available())
ESP8266.write(Serial.read());
}
這個程式就是透過Serial 監視器,與ESP8266溝通,首先要注意的是速率,ESP8266 內定的鮑率有兩種,一是 115200、另一是 9600,我假設新的板子都是使用
115200,所以我的程式透過 115200 進行溝通,請參考下圖:
程式上傳應該沒有問題,上傳後請打開 Serial Monitor,要正確執行,必須修改狀態列下面的兩個下拉選單,請按照上圖改成 NL & CR 與 115200 baud,修改之後,請在上方輸入
“AT”,按下 [傳送]就可以看見有一個OK的回應。這樣就可以確定正常了。
執行的結果也在上圖,有回應,可是有些BUG,就是當下達 “AT+GMR” 指令的時候,傳回的資料不完整。這也許跟 115200的鮑率有關係。所以,我們馬上下指令把鮑率改成 9600,請輸入 “AT+UART_DEF=9600,8,1,0,0”,然後按下 [傳送]。接著您就會看到OK的回應,但是就再也無法通訊了,如果要通訊,請回到程式碼,將裡面的 115200改成 9600,再上傳一次即可,如下圖,現在再輸入 “AT+GMR” 命令的時候,就會回應正確的字串了
加入AP
確定我們的Arduino 與 ESP 通訊沒有問題之後,我們就要進一步,讓我們的設備連上AP。這個概念很簡單,就把它想成我們讓手機連上家裡的AP,然後就可以連到外面的網路,也就是說我們就可以連上 IoT 平台了。
要連接AP之前,我們先將 ESP 設定成 STA 模式,也就是Client的模式,請輸入指令:
AT+CWMODE=1
看到OK之後,再輸入下面的指令列出所有的AP:
AT+CWLAP
這時候就會列出所有可以搜尋到的AP,如下圖:
最後請透過CWJAP指令來連結AP:
AT+CWJAP="Great-Fortune_as","***********"
成功連線之後會得到下面的訊息:
WIFI
CONNECTED
WIFI
GOT IP
很好,成功之後,我們就要開始製作溫濕度的IoT監控了。
DHT11 溫溼度上傳IoT
還記得第21課中DHT的程式碼吧,如下:
#include <dht.h>
dht
DHT;
#define
DHT11_PIN 7
void
setup(){
Serial.begin(9600);
}
void
loop()
{
int chk = DHT.read11(DHT11_PIN);
Serial.print("Temperature = ");
Serial.println(DHT.temperature);
Serial.print("Humidity = ");
Serial.println(DHT.humidity);
delay(1000);
}
當然,也要將DHT11安裝在我們的麵包板上面,接5V的電源,並連接到 PIN7 (跟21課相同),如下圖:
最後,將這段DHT程式碼與ESP程式碼整合在一起,然後再將前一個單元談到的 ThingSpeak GET字串,就可以將溫溼度每五分鐘 Update
一次(ThingSpeak 好像接受每兩分鐘上傳一次,設定五分鐘就好,不要太接近,以免浪費網路資源),完整的程式碼如下:
#include
<SoftwareSerial.h>
#include
<dht.h>
dht
DHT;
#define
DHTPIN 7
#define
SSID "Great-Fortune_as"
#define PASSWD "**********" //輸入自己的
#define
IP "184.106.153.149"
//thingspeak.com
SoftwareSerial
ESP8266(3,2); //RX, TX
const
int WIFIled=13;
boolean
FAIL_8266 = false;
String
cmd;
int
i;
unsigned
long realtime=0;
void
setup()
{
pinMode(WIFIled,OUTPUT);
digitalWrite(WIFIled,LOW);
ESP8266.begin(9600);
Serial.begin(9600); //把訊息顯示在
Serial
for(i=0;i<3;i++)
{
digitalWrite(WIFIled,HIGH);
delay(200);
digitalWrite(WIFIled,LOW);
delay(200);
}
do
{
sendESP8266cmd("AT+RST",2000);
Serial.println("reset
8266...");
if(ESP8266.find("OK"))
{
Serial.println("OK");
if(connectWiFi(10)) //連線AP
{
FAIL_8266=false;
Serial.println("connect
success");
}
else
{
FAIL_8266=true;
Serial.println("connect
fail");
}
}
else
{
delay(500);
FAIL_8266=true;
Serial.println("no response");
}
}while(FAIL_8266);
digitalWrite(WIFIled,HIGH);
}
void
loop()
{
if((millis()-realtime)>=300000) //300秒 Update一次
{
realtime=millis();
if(!FAIL_8266)
{
int chk = DHT.read11(DHTPIN);
updateDHT(String(DHT.temperature),String(DHT.humidity));
}
}
}
void
updateDHT(String T,String H)
{
String
cmd="AT+CIPSTART=\"TCP\",\"";
cmd += IP;
cmd += "\",80";
sendESP8266cmd(cmd,2000);
if(ESP8266.find("OK"))
{
Serial.println("TCP OK");
cmd="GET /update?api_key=MSFPC7MCUNU9***N";
//輸入自己的
cmd+="&field1=" + T +
"&field2=" + H + "\r\n";
ESP8266.print("AT+CIPSEND=");
ESP8266.println(cmd.length());
if(ESP8266.find(">"))
{
ESP8266.print(cmd);
Serial.println(cmd); //把命令顯示出來
if(ESP8266.find("OK"))
{
Serial.println("update OK");
}
else
{
Serial.println("update
error");
}
}
}
else
{
Serial.println("TCP error");
sendESP8266cmd("AT+CIPCLOSE",1000);
}
}
boolean
connectWiFi(int timeout)
{
sendESP8266cmd("AT+CWMODE=1",2000); //Set station MODE
Serial.println("WiFi
mode:STA");
do
{
String cmd="AT+CWJAP=\"";
cmd+=SSID;
cmd+="\",\"";
cmd+=PASSWD;
cmd+="\"";
sendESP8266cmd(cmd,1000);
Serial.println("join AP...");
if(ESP8266.find("OK"))
{
Serial.println("OK");
sendESP8266cmd("AT+CIPMUX=0",1000);
return true;
}
}while((timeout--)>0);
return false;
}
void
sendESP8266cmd(String cmd, int waitTime)
{
Serial.println(cmd);
ESP8266.println(cmd);
delay(waitTime);
}
上傳之後,就開始上傳資料了,進入自己的 ThingSpeak 首頁,結果如下:
上面的圖形可能看起來很奇怪,因為前面幾筆資料是我在前一個單元裡面自行輸入的,最後一筆則是透過 ESP8266 上傳的。任何時候,只要進入 ThingSpeak.com 網站,Sign In 自己的帳號,就可以看到溫度記錄的結果,如下圖:
前面比較密集的點,是我兩分鐘上傳一次,後面我改成了每五分鐘上傳一次,建議每五分鐘上傳一次就好,這樣資料點看得比較清楚。在上傳的過程中,我使用 Serial Monitor 去觀察結果,發現上傳的成功率是二分之一,請看下面紅色的部分:
AT+CIPSTART="TCP","184.106.153.149",80
TCP OK
GET
/update?api_key=MSFPC7MCUNU9K2KN&field1=22.00&field2=66.00
update
OK
AT+CIPSTART="TCP","184.106.153.149",80
TCP error
AT+CIPCLOSE
AT+CIPSTART="TCP","184.106.153.149",80
TCP OK
GET
/update?api_key=MSFPC7MCUNU9K2KN&field1=22.00&field2=65.00
update
OK
AT+CIPSTART="TCP","184.106.153.149",80
TCP error
AT+CIPCLOSE
我個人是認為應該是 Server 的問題,很可能是我們用的是免費帳號,一律接受一次拒絕一次,因為實在是太規律了。關於這點,要等到我買了付費的帳號之後才能驗證了。
到這裡,Arduino 先暫時告一段落,接下來我們就會進入 Raspberry Pi + Python 的世界。
到這裡,Arduino 先暫時告一段落,接下來我們就會進入 Raspberry Pi + Python 的世界。
留言
張貼留言