2017年1月4日 星期三

初遇 IoT ( Internet of Thing, 物聯網 ) 加強版 - 使用 ESP8266 Arduino AT 函式庫上傳溫溼度資料到 Thingspeak (Ai-Mod, AT v1.2.0.0 based on SDK v1.5.4.1 )

網頁最後修改時間:2017/01/07 
接續上一篇 "給我一個可操作 AT 指令的 Arduino 函式庫 - 解決 AT 指令處理的蛋疼問題 (親測 AT v1.2.0.0 韌體)" 網頁提到的 ESP8266 Arduino AT 函式庫,這一篇將使用這個函式庫改寫 "初遇 IoT ( Internet of Thing, 物聯網 ) - 使用 Arduino 控制 ESP8266 無線模組上傳資料到 IoT Server ( ThingSpeak )" 網頁中的例子,說明函式庫使用方法。
*********************************************************************************
ESP8266 IoT 入門學習套件可至露天賣場訂購:
*********************************************************************************
這程式的線路跟之前的範例都一樣,不過因為更新網頁的關係將線路修了一下,與之前的線路相差不多 (原本的網頁已經更新)
IoT_Demo 電路圖 V0.2 - 包括 UART 除錯
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
有購買商品的使用者,網頁中所需相關資料已放置於雲端硬碟,請自行下載使用!
其餘的使用者,請自行依照提供之連結下載相關資料,程式碼複製貼上使用!
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*

程式的執行結果與之前的範例一樣,所不同的是使用 ESP8266 Arduino AT 函式庫來操作 AT 指令。所有 AT 指令的操作都會在背景執行,在程式中只會看到處理的函式名稱,所以撰寫上會比較直覺,但會不會變的比較簡單,就見仁見智了!

要提醒各位的是:使用之前請一定要熟悉 AT 指令 !

軟 / 硬體設置:
  • ESP8266
    AT v1.2.0.0;UART baudrate: 115200 bps
  • ESP8266 與 Arduino 通訊使用硬體 UART (@ 115200 bps)
  • 除錯使用軟體 UART (@ 9600 bps)
使用 ESP8266 Arduino AT 函式庫有一些限制!預設的函式庫使用軟體 (SoftwareSerial) UART 與 ESP8266 通訊,而且寫死使用的接腳 ( <D8> <D9>)。若深入看一下 SparkFunESP8266WiFi.h  最上面的程式碼,會發現到接線的時候 <D8> 就是電壓準位轉換後接 ESP8266 的 <UTXD><D9> 就是電壓準位轉換之後接 <URXD>,不需要自行跳線!

/////////////////////
// Pin Definitions //
/////////////////////
#define ESP8266_SW_RX 9 // ESP8266 UART0 RXI goes to Arduino pin 9
#define ESP8266_SW_TX 8 // ESP8266 UART0 TXO goes to Arduino pin 8

//... <中間程式碼省略>

static SoftwareSerial swSerial(ESP8266_SW_TX, ESP8266_SW_RX);

為了不去變更 AT 韌體預設的 115200 bps 通訊速率,所以與 ESP8266 的通訊使用硬體 UART;除錯訊息的輸出則是使用 9600 bps。所以接好線之後,就只剩下程式裡面的參數設定 !

首先,一定要設定的就是無線基地台的名稱 (SSID) 與密碼 (PASS)
//*-- 無線 AP 的 SSID 和 PASS
#define SSID "無線網路的名稱"
#define PASS "無線網路的密碼"

另一個一定要設定的就是 THINGSPEAK_KEY,這對應到 ThingSpeak channel 的 API_KEY
String GET = "GET /update?key=THINGSPEAK_KEY";

如果沒有修改線路和通訊速率,則下面不需要修改;否則根據硬體再自行變更
/** UART 通訊速率設定 */
#define  esp8266_baudrate 115200
#define    debug_baudrate 9600

/** SoftwareSerial */
#define  _rxpin   2
#define  _txpin   3
SoftwareSerial debug( _rxpin, _txpin );   

因為有一些部分跟原本的網頁相同,所以重複的部分就不需要多提,有問題就請自行跳轉一下再跳回來繼續看。

程式碼:
下面就是使用 ESP8266 Arduino AT 函式庫所改寫的上傳 DHT11 到 Thingspeak 的程式碼。為了減少程式長度,所以一些壟長的註解與說明已經拿掉 (雲端硬碟是包含完整註解與說明的),但這不影響程式的執行結果。

假設家中無線網路名稱 (SSID) 與密碼 (PASS) 分別是:IamRoutter ThisisaPassword
Thingspeak 的 API_KEY 為:THINGSPEAK_KEY

填入程式碼中,就如下面所示:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#include <SoftwareSerial.h>    // 這個標頭一定要加入,即便不用 SoftwareSerial
#include <SparkFunESP8266WiFi.h>  // v1.0.0
#include <DHT.h>    // v1.0.0, 要用部落格上面的下載連接中的函式庫檔案

#define DEBUG     // 如果要使用 DEBUG 功能輸出除錯訊息

//*-- 無線 AP 的 SSID 和 PASS
#define SSID "無線網路的名稱"
#define PASS "無線網路的密碼"
//
// Thingspeak 連線的網址
#define IP "184.106.153.149"    // ThingSpeak IP Address: 184.106.153.149
//
// 使用 GET 傳送資料的格式
// GET /update?key=[THINGSPEAK_KEY]&field1=[data 1]&filed2=[data 2]...;
String GET = "GET /update?key=THINGSPEAK_KEY";

/** UART 通訊速率設定 */
#define  esp8266_baudrate 115200
#define    debug_baudrate 9600

/** SoftwareSerial */
#define  _rxpin    2
#define  _txpin    3
SoftwareSerial debug( _rxpin, _txpin );  // RX, TX

// 每次傳送資料的時候 LED 會亮起,傳送完畢就會滅
#define  _ledpin    13

//*-- DHT11
#define  _dhtpin    8
#define  _dhttype   DHT11

DHT dht11( _dhtpin, _dhttype );
uint8_t dhtbuf[2];

void setup() {

 debug.begin( debug_baudrate );

 /** 初始化 ESP8266 */
 int test = esp8266.begin( esp8266_baudrate, ESP8266_HARDWARE_SERIAL );
 if( !test ) {
  debug.println( "Unable to communication with the ESP8266." );
  errorLoop( test );
 } else {
  debug.println( "ESP8266 Ready to go!" ); // 成功連線
 }

 connectESP8266();

 displayConnectInfo();

 // DHT11
 dht11.begin();

 // LED
 pinMode( _ledpin, OUTPUT );
 digitalWrite( _ledpin, LOW );
}

void loop() {
 // DHT11 溫濕度讀取時間不要小於 1 秒
 // DHT11 溫溼度不需要小數位,也沒有小數位
 dhtbuf[0] = dht11.readHumidity();
 dhtbuf[1] = dht11.readTemperature();

 // 確認取回的溫溼度數據可用
 if( isnan(dhtbuf[0]) || isnan(dhtbuf[1]) )
 {
  debug.println( "Failed to read form DHT11" );
 }
 else
 {
  digitalWrite( _ledpin, HIGH );
  char buf[3];
  String HH, TT;
  // 轉換為 10 進位,並轉換為字串
  buf[0] = 0x30 + dhtbuf[1] / 10;
  buf[1] = 0x30 + dhtbuf[1] % 10;
  TT = (String(buf)).substring( 0, 2 );
  buf[0] = 0x30 + dhtbuf[0] / 10;
  buf[1] = 0x30 + dhtbuf[0] % 10;
  HH = (String(buf)).substring( 0, 2 );

    // DHT11 溫度與濕度傳送
  updateDHT11( TT, HH );
  #ifdef DEBUG
   debug.print("Humidity: "); 
      debug.print( HH );
      debug.print(" %\t");
      debug.print("Temperature: "); 
      debug.print( TT );
      debug.println(" *C\t");
     #endif
  digitalWrite( _ledpin, LOW );
 }

 // 每隔多久傳送一次資料
 delay(60000); // 60 second
}

void updateDHT11( String T, String H )
{
 String getrequest;

 ESP8266Client client;

 // ESP8266Client connect([server], [port]) 
 // Returns: 1 on success, 2 on already connected,
 // negative on fail (-1=TIMEOUT, -3=FAIL).
 int retVal = client.connect( IP, 80 );
 if (retVal <= 0)
 {
  debug.println(F("Failed to connect to server."));
  return;
 }

 // 準備上傳到 ThingSpeak IoT Server 的資料
 // 已經預先設定好 ThingSpeak IoT Channel 的欄位
 // field1:溫度;field2:濕度
 getrequest = GET + "&field1=" + T + "&field2=" + H +"\r\n";
 // print and write can be used to send data to a connected client connection.
 client.print( getrequest );

 while( client.available() )
  debug.write( client.read() );

 if ( client.connected() )
  client.stop();
}

void connectESP8266()
{
 int retVal = esp8266.getMode();
 if ( retVal != ESP8266_MODE_STA ) { 
  // 如果現在不是在 STA 模式則使用 
  // esp8266.setMode([mode]) 設定指定的模式
  retVal = esp8266.setMode( ESP8266_MODE_STA );
  if( retVal < 0 ) {
   debug.println( F( "Error setting mode." ) );
   errorLoop( retVal );
  }
 }
 debug.println( F( "Mode set to station" ) );

 retVal = esp8266.status();
 if ( retVal <= 0 ) {
  debug.print( F( "Connecting to " ) );
  debug.println( SSID );
  retVal = esp8266.connect( SSID, PASS );
  if (retVal < 0) {
   debug.println( F( "Error connecting") );
   errorLoop( retVal );
  }
 }
}

void displayConnectInfo()
{
 char connectedSSID[24];
 memset( connectedSSID, 0, 24 );
 int retVal = esp8266.getAP( connectedSSID );
 if (retVal > 0) {
  debug.print( F( "Connected to: " ) );
  debug.println( connectedSSID );
 }

 IPAddress myIP = esp8266.localIP();
 debug.print( F( "My IP: " ) ); debug.println( myIP );
}

void errorLoop( int error )
{
 debug.print( F( "Error: " ) ); debug.println( error );
 debug.println( F( "Looping forever." ) );
 for (;;)
 ;
}

/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 執行輸出與結果:
下圖是在 2017/01/04 執行程式連續幾個小時之後的溫濕度變化。前幾筆是在室溫下,之後的數據都是在接近筆電散熱孔附近所測量得到的值

下面是程式執行時的輸出。有些溫度值的上方會出現一些數字或是文字,那是因為我們在送出資料之後,Thingspeak 會回傳一些東西回來,因為會傳在緩衝區裡面,所以必須將它清出來。
程式執行輸出
結論:
這篇網頁使用 ESP8266 AT 函式庫改寫了 "初遇 IoT ( Internet of Thing, 物聯網 ) - 使用 Arduino 控制 ESP8266 無線模組上傳資料到 IoT Server ( ThingSpeak )" 網頁中的範例程式,雖然得到的都是相同結果,但是提供了另一個同樣使用 AT 指令在 Arduino IDE 的方法,有興趣的話可以再深入去研究 !

<< 部落格相關文章 >>

6 則留言:

  1. 請問我跟圖片差法依樣可是功能不能超做
    阿監控地方只跑出 AT 就停止了
    ESPlorer 按了馬上閃退不知道為甚麼 可以幫忙解決嗎

    回覆刪除
    回覆
    1. 如果是 AT 指令操作的問題,直接看 AT 指令的網頁,裡面有詳細介紹! ESPlorer 閃退 ??? 要不就更換其他版本試試。

      刪除
  2. 請問罩上圖插完 ESP8266是否會亮 阿+5V 要重拿裡接出來還是不用呢

    回覆刪除
    回覆
    1. 單顆 LED 的只會在開機時後閃幾下就滅掉 ! 電源直接從 Arduino 上面取電!

      刪除
  3. 請問如何新增其他感測器數值到THINGSPENK

    回覆刪除
    回覆
    1. 看"部落格相關文章",裡面有你要的!

      刪除