2017/6/2

以ESP8266為基礎的IoT控制中心--溫濕度感測數據傳輸

延續前一個案例「ESP8266AT命令建立HTTP服務」中, 我們很容易透過ESP-01模組的AT命令來建立HTTP伺服器, 也知道web browserESP-01模組背後是如何溝通運作的. 本案例電路部份以「 ESP8266 的第一次接觸」為基礎, 在所有接線都不變的前提下再追加一個「溫濕度感測器」來展示以ESP-01模組為IoT控制中心的方法, 概念如下圖.






溫濕度感測器測試
Maker喜歡用「溫濕度感測器」來展示以IoT控制的應用, 因為它非常簡單又適合達到展示物聯網的效果. 筆者的溫濕度感測器型號是DHT11, 只有三隻腳分別為DataVddGnd, 如下圖所示.



每當遇到這種排針, 筆者還是習慣使用於前例「 ESP8266 的第一次接觸」中預備好的排線先將其接腳全部引出.




依照規格Vdd可接受3V~5V的電壓輸入, 而本例直接引用前例Arduino3.3V輸出(紅色線). 圖中黑色為Gnd, 而白色為Data. 整個系統看起來如下圖, ESP-01模組後面多了另一位主角(DHT11).




細部接線
整個系統接法相當簡單, Vcc(3.3V)Gnd直接share之前給ESP-01模組的麵包板排線, Data我們任意指定一根Arduino上還未使用的數位腳即可, 例如本例為Pin5(下圖藍色接線).


   


DHT11模組測試
我們需要先測試一下DHT11模組是否能正常工作, 測試程式相當簡單. 本例我們使用現成的DHT11模組函式庫, 可以至https://github.com/adafruit/DHT-sensor-library下載原始檔並將其複製到Arduino IDE路徑下的libraries目錄即可. 例如, $IDE_ROOT\arduino-1.8.2\libraries\DHT-sensor-library-master (其中DHT_U.*我們用不到, 請將其刪除). 下面範例程式每隔10秒透過DHT11讀取溫濕度並透過串列埠監控窗顯示.

#include "DHT.h" // package required from https://github.com/adafruit/DHT-sensor-library

DHT dht(5, DHT11); // digital pin5 connected to the DHT11

void setup() {
  Serial.begin(115200);
  Serial.println("DHT11 start ...");
  dht.begin();
}

void loop() {
    delay(10000); // measure humidity and temperature per 10 seconds
    float h = dht.readHumidity();   // Reading temperature or humidity takes about 250 milliseconds
    float t = dht.readTemperature(); // Read temperature as Celsius (the default)

    Serial.print("Humidity: ");
    Serial.print(h);
    Serial.println(" %\t");
 
    Serial.print("Temperature: ");
    Serial.print(t);
    Serial.println(" *C");
}




Arduino程式
筆者的Arduino IDE開發環境1.8.2 ZIP免安裝版. 程式部份我們以前一個案例「ESP8266AT命令建立HTTP服務」為基礎, 並將上述DHT11的程式碼整合在一起. 在啟動ESP-01AP模式前, 我們先啟動DHT11模組並等待約1~2. 我們主要修改了sendHTML函式, 於讀取溫度與濕度內容之後將兩筆值打包成HTML格式並回傳給clien(ESP-01), 其它大部分程式內容不變. HTML部份我們透過<meta http-equiv="refresh" content="10">標籤讓client端每隔10秒對我們的HTTP伺服器重新提出更新需求, 因此也同時更新了溫度與濕度的內容.

#include <SoftwareSerial.h>
#include "DHT.h" // package required from https://github.com/adafruit/DHT-sensor-library

DHT dht(5, DHT11); // digital pin5 connected to the DHT11
SoftwareSerial ESP(3, 2); // ESP8266 ESP-01: Tx, Rx

void setup() {
    Serial.begin(115200);
    dht.begin();
    delay(2000); // Sensor readings may be up to 2 seconds
    Serial.println("DHT11 start ...");

    ESP.begin(115200);
    Serial.println("\nInitialize ESP-01 ...");
    sendATcmd("AT+RST\r\n",3000);       // reset ESP-01
    sendATcmd("AT+CWMODE=2\r\n",2000);  // config ESP-01 as AP mode
    //sendATcmd("AT+CWMODE?\r\n",1000); // check AP mode
    //sendATcmd("AT+GMR\r\n",1000);     // check firmware version
    sendATcmd("AT+CIFSR\r\n",2000);     // check IP
    sendATcmd("AT+CIPMUX=1\r\n",2000);  // allow multiple access
    sendATcmd("AT+CIPSERVER=1,80\r\n",2000); // start server at port:80
    Serial.println("\nServer started at port 80 ...");
}

void setDelay(unsigned int delay) {
    unsigned long timeout = delay+millis();
    while( millis()<timeout ) {} // NOP
}

void sendATcmd(char* cmd, unsigned int delay) {
    ESP.print( cmd ); // send AT command to ESP-01
    setDelay(delay);
}

void sendHTML(byte connID,char* msg) {
    extern int connCount; 
    String html;
    char cipSend[128];
    char cipClose[128];
    float h = dht.readHumidity();   // Reading temperature or humidity takes about 250 milliseconds
    float t = dht.readTemperature(); // Read temperature as Celsius (the default)
   
    html += "<html><head>";
    html += "<meta http-equiv=\"refresh\" content=\"10\">";
    html +="<title>From ESP-01</title></head><body>\n\r";
    html += "<p>ClientMsg: ";
    html += msg;
    html += "</p>\n\r";
    html += "<p>Humidity:";
    html += h;
    html += "%</p>\n\r";
    html += "<p>Temperature:";
    html += t;
    html += "*C</p>\n\r";
    html += "</body></html>";
    Serial.println(html);
    sprintf(cipSend,"AT+CIPSEND=%d,%d\r\n",connID,html.length());
    sprintf(cipClose,"AT+CIPCLOSE=%d\r\n",connID);
    sendATcmd(cipSend,1000);
    sendATcmd(html.c_str(),1000);
    sendATcmd(cipClose,1000);
}

void loop() {
    // send AT command to ESP-01 form console (serial)
    if ( Serial.available() ) {
        ESP.write( Serial.read() );
    }
    if ( ESP.available() ) { // receive message from ESP-01
        if ( ESP.find("+IPD,") ) { // detect the client's request
            String msg="";
            byte connID = ESP.read()-48; // client's connection ID
            while( ESP.available() ) { // collect client's request from the web browser
                msg += (char)ESP.read();
                delay(20); // the delay will let the message become more stable
            }
            sendHTML(connID,msg.c_str()); // send HTML message to client
            Serial.flush();
        }
    }
}




同場加映LED燈控制
我們於前面的例子中已經知道, 客戶端(client)web browser可以透過GET方式順便傳送簡單的指令到ESP-01模組所扮演的HTTP server. 因此上面的程式只要稍作修改, 我們就能看到透過客戶端的web browser做遠程控制LED燈的效果. 概念上如下圖所示, 透過電腦或手機的web browser下指令給ESP-01並點亮/熄滅LED.





為了不增加任何硬體線路, 我們選擇Arduino內建的LED燈做示範. setup步驟, 我們將built-in LED燈腳重新規劃為output. 追加的程式碼以橙色文字表示, 主要是透過sendHTML呼叫時, 在將原本客戶端傳送進來的訊息打包成HTML之前判斷是否有「GET /led=?」開頭的字串. 若有則判斷下一個字元為01的值(字元減去48), 並將該值寫到built-in LED的腳位(1為點亮, 0為熄滅).

void setup() {
    pinMode(LED_BUILTIN, OUTPUT); // to demonstrate the argument passed in GET method

    Serial.begin(115200);
    dht.begin();
    delay(2000); // Sensor readings may be up to 2 seconds
    Serial.println("DHT11 start ...");

    ESP.begin(115200);
    Serial.println("\nInitialize ESP-01 ...");
    sendATcmd("AT+RST\r\n",3000);       // reset ESP-01
    sendATcmd("AT+CWMODE=2\r\n",2000);  // config ESP-01 as AP mode
    //sendATcmd("AT+CWMODE?\r\n",1000); // check AP mode
    //sendATcmd("AT+GMR\r\n",1000);     // check firmware version
    sendATcmd("AT+CIFSR\r\n",2000);     // check IP
    sendATcmd("AT+CIPMUX=1\r\n",2000);  // allow multiple access
    sendATcmd("AT+CIPSERVER=1,80\r\n",2000); // start server at port:80
    Serial.println("\nServer started at port 80 ...");
}

void sendHTML(byte connID,char* msg) {
    extern int connCount; 
    String html;
    char cipSend[128];
    char cipClose[128];
    float h = dht.readHumidity();   // Reading temperature or humidity takes about 250 milliseconds
    float t = dht.readTemperature(); // Read temperature as Celsius (the default)
    char* p;
    int led = 0; // built-in LED value
    if ( (p=strstr(msg,"GET /led="))!=NULL ) { // receive LED control form client with GET method
        *(p+10) = '\0'; // remove the rest message
        led = (int)p[9]-48; // get LED value
        digitalWrite(LED_BUILTIN, led);
        delay(1000);
    }
    html += "<html><head>";
    html += "<meta http-equiv=\"refresh\" content=\"10\">";
    html +="<title>From ESP-01</title></head><body>\n\r";
    html += "<p>Loop:";
    html += (++connCount);
    html += "</p>\n\r";
    html += "<p>ClientMsg: ";
    html += msg;
    html += "</p>\n\r";
    html += "<p>Humidity:";
    html += h;
    html += "%</p>\n\r";
    html += "<p>Temperature:";
    html += t;
    html += "*C</p>\n\r";
    html += "<p>LED: ";
    html += led;
    html += "</p>\n\r";
    html += "</body></html>";
    Serial.println(html);
    sprintf(cipSend,"AT+CIPSEND=%d,%d\r\n",connID,html.length());
    sprintf(cipClose,"AT+CIPCLOSE=%d\r\n",connID);
    sendATcmd(cipSend,1000);
    sendATcmd(html.c_str(),1000);
    sendATcmd(cipClose,1000);
}