網頁最後修改時間:2018/08/17
現在還缺的,就是知道怎麼上傳感測數據 ?
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
接下來的內容若是有看不懂或是疑惑的地方,建議先行依序閱讀 [1], [2], [3], [4] 這幾篇網頁。
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
*********************************************************************************
此網頁所用的材料可自行準備,或選用新版本的升級套件
- V2.1 版 { 萬物皆聯網-ESP8266 IoT(Internet of Things)入門學習套件 }
- Arduino UNO + ESP8266 (Wi-Fi) 二合一開發板 ( 可選擇獨立與偕同開發 )
*********************************************************************************
建立一個新專案:
方便起見,這裡再重新建立一個新的 CHT IoT SP 專案;只有一個設備,設備包含兩個感測器,分別用來記錄與顯示 SHT31 的溫溼度值 ( 不懂專案建立,請參閱前面幾篇的說明 )。
專案建立後,可得類似下面的設備管理頁面;剛建立的感測器沒有數值,不需要先手動設定,下一節再直接用 Postman 指定即可
專案、設備和感測器建置完成之後的設備管理頁面 |
上傳數據的 RESTful API 協定很簡單,都是使用 HTTP POST 且採用相同的 URI
https://iot.cht.com.tw/iot/v1/device/${device_id}/rawdata
RESTful API HTTP POST 資料儲存協定列表 |
body 中的幾個 key 欄位:time 和 save 兩個欄位可省略不寫,使用系統時間設定與永久儲存作為預設值;其他欄位值則一定要寫不可省略。body 所使用字元數目,將是 headers Content-Length 的欄位值。
headers 在 API 文件網頁上的格式範例可以直接用,但是實際在撰寫程式時只會留下真正需要的,其他的都會在不影響數據儲存功能的狀況下省略掉。
由於在此範例下只有兩個感測器,所以能夠一次上傳溫度與濕度兩個數據最好!但為了測試,我會先上傳 SHT31 的溫度值,然後再接著一次同時上傳溫度與濕度值,分別演示協定 "依平台接收時間儲存資料" 和 "儲存感測資料" ( 敏感的資料已經先遮掉;感測器數據只是演示用,故只暫時儲存 ( "save":false ),會被之後的資料取代掉 )。
不管是使用上面哪一個協定儲存感測器數據,Request 的 headers 使用的部分都是相同 (當然 ${PROJECT_KEY} 和 body 的字串長度是不同的,要因資料而異做更改),不過現在不著墨在此上面,請將重點花在 body 裡面的 JSON 字串要怎麼寫!
下面需要填的部分就是粉色底線上面的 ${device_id} 和 ${PROJECT_KEY};填上這兩個值之後,就可以切換到 Body 的視窗
Postman - Headers 設定視窗 |
JSON 字串格式,可以直接由 API 文件網頁中的 "依平台接收時間儲存資料" 協定裡的範例複製過來再做修改。
完成修改之後按下 "SEND" 按鈕,若格式正確則會馬上收到回傳 (由於回傳的 Response 格式裡只有 headers 的部分有東西 (只需要注意 Status 欄位), body 沒有內容)。
"依平台接收時間儲存資料" 操作範例與結果 |
JSON 字串格式,可以直接由 API 文件網頁中的 "儲存感測資料" 協定裡的範例複製過來再做修改。
完成修改之後按下 "SEND" 按鈕,若格式正確則會馬上收到回傳 (由於回傳的 Response 格式裡只有 headers 的部分有東西 (只需要注意 Status 欄位), body 沒有內容)
"儲存感測資料" 操作範例與結果 |
再一次提醒,Postman 所做的測試,只有 Body 頁面中的 JSON 字串能直接使用在程式,Headers 頁面裡 (甚至是 "Code" 按下出現的視窗內容) 都不能直接用。
ESP8266 AT 指令測試:
上面做完 Postman 的測試之後,熟悉 HTTP 格式的用戶,應該就可以直接跳到撰寫程式的步驟;想了解或是不熟的用戶,就繼續往下看!
這裡要做的,就是使用 AT 指令連線到 CHT IoT SP ,並直接發送 headers 加上 body 這兩個部分的 Request 字串出去。所以只要這裡發送的 Request 字串是正確的,那麼程式就不容易出錯!
HTTP POST Request = Request headers + Request body(JSON 格式字串)
一旦 Request 字串成功發送之後,隨即而來就是接收 Response 回應的部分,而 Response 接收的部分有兩個選擇:
- 不理會
不理會的前提是 Request 發送是正確的,能確保感測資料無誤的儲存到 CHT IoT SP 上去,否則數據儲存不成功,可能無法得知! - 正常接收
只需要接收與處理 headers 的部分,確認 Status 欄位回傳 200 無誤,即是正常處理完成儲存數據的動作
HTTP POST Response = Response headers + Response body("")
因為數據儲存協定回傳的 Response body 部分是空白的,所以接收的只有 headers 的部分;這部分在撰寫程式時,處不處理看個人選擇。
下圖,建立與 CHT IoT SP 的 TCP 透傳通訊後 (1),把要發送的 Request 字串組合到下方輸入欄中按下 "Send" (2),在成功發送 Request 格式字串之後,就會得到 CHT IoT SP 的 Response 回應字串 (3)
ESP8266 AT 指令測試結果 |
還有一點,上面漏掉說明了!就是 headers 的 Content-Length 欄位值:這個欄位在 Postman 的 "Code" 按下的視窗中不會有,但是在上面與 API 文件中有,這代表 body 內容 (JSON 字串) 的字元數目,可能每次都不一樣,但是一定要有,否則會出現錯誤! ( 對照 3 的輸出內容,可知道其 body 是沒有資料的 )
到此,數據儲存的 Request 和 Response 的格式已都說明清楚,現在可以進入實際程式寫作的部分了!
參考電路圖:
參考電路圖如下圖所示。
其中,中間部分也可以使用 Arduino 開發板外接 ESP8266 無線模組來取代,其餘的接線不變。
參考電路圖 |
程式一開始會連線至 CHT IoT SP 並與其建立 TCP 透傳通訊,接著讀取 SHT31 溫溼度有效值,建立 Request 的 body 內容 (JSON 字串) 和 headers 然後發送等待 Response;Response 處不處理都可以,不影響結果!
提供的程式碼對於 Response 是採不處理的方式,但範例測試 ( 定時上傳數據;最下面有測試結果 ) 是有做處理以作為判斷數據傳成不成功的依據。需要加入 Response 處理的用戶可參考上一篇的程式碼自己加進去。
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
環境設置:
- Software
- Arduino IDE v1.8.5
- Library
- ArduinoJSON v5.13.2
- Hardware (下面兩種型式皆可)
- Arduino UNO + ESP8266 (WiFi) 二合一開發板**
- Arduino 開發板外接 ESP8266 無線模組**
** ESP8266 AT 韌體版本 AT: 1.2.0.0, SDK: v1.5.4.1
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*下面的程式碼移除一些冗長的註解並採分段說明,使用之前請自行整理;由於有些函式與前一篇重複,故只列出不說明!
先看一下主程式的部分;由於 loop() 裡面不需要程式碼,所以只說明 setup() 的部分。
setup
- line 9: I2C 函式庫初始化
- line 10: SHT31 初始化
- line 13 ~ 14: 退出 ESP8266 透傳模式並確認 ESP8266 是否連接正確;若發生錯誤,LED 每秒閃爍1 次!
- line 15: 關閉 TCP 連線*
- line 16: 斷開與無線路由器 (AP) 的連接*
- line 19 ~ 20: 建立與 CHT IoT SP 的 TCP Client 透傳通訊;若連線的 AT 指令出現錯誤,LED 每秒閃爍 2 次!
- line 21 ~ 24: 主要執行發送 HTTP GET Request 格式字串;若出現錯誤,關閉與 AP 的連線,LED 每秒閃爍 4 次!
- line 26: LED 常亮,表示完成 SHT31 溫溼度值上傳儲存的動作
SHT31TCPPost_Demo02.ino, (01/06), setup()+loop()
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 | void setup() { Serial.begin( 115200 ); swSerialOutput.begin( 115200 ); delay( 3000 ); pinMode( _builtin_led, OUTPUT ); swSerialOutput.print( F("SoftwareSerial Ready!\r\n") ); Wire.begin(); softResetSHT31(); swSerialOutput.print( F("SHT31 Ready!\r\n") ); if( !exitPassthroughMode() ) ledErrorIndicator( 1 ); closeTCPConnection(); disconnectFromAP(); swSerialOutput.print( F("\r\n>>>>>>>> START <<<<<<<<\r\n") ); if( !station_createTCPPassthrough() ) ledErrorIndicator( 2 ); if( !postSHT31ToCHTIoT() ) { disconnectFromAP(); ledErrorIndicator( 4 ); } swSerialOutput.print( F("\r\n>>>>>>>> END <<<<<<<<\r\n") ); digitalWrite( _builtin_led, HIGH ); } void loop() {} |
程式用到的標頭檔與變數宣告和定義
- line 22 ~ 25: 儲存 SHT31 溫度與濕度值的結構
SHT31TCPPost_Demo02.ino, (02/06)
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 | #include <SoftwareSerial.h> #include <Wire.h> //--** Software Serial **// #define _rxpin 2 #define _txpin 3 SoftwareSerial swSerialOutput( _rxpin, _txpin ); // RX, TX // 如果要輸出一些除錯訊息的話 #define DEBUG #ifdef DEBUG #define dbgOutput( str ) (swSerialOutput.print( str )) #else #define dbgOutput( str ) #endif //--** SHT31 預設的 IIC 地址 **--// #define SHT31_IIC_ADDRESS 0x44 // 常亮表示 AT 指令執行正常,閃爍就是出現錯誤! #define _builtin_led 13 struct sht31_t{ float temperature; float humidity; } sht31 = { 0.0, 0.0 }; |
其他下面幾個一般輔助函式的程式碼,請參考前幾篇網頁中的說明。
SHT31TCPPost_Demo02.ino, (03/06), functions()
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 | bool checkOK( ) { uint8_t i = 0; while( !Serial.find( "OK" ) ) { if( ++i >= 6 ) return false; } return true; } bool exitPassthroughMode() { Serial.println( "AT" ); if( !checkOK() ) { Serial.print( "+++" ); delay( 1000 ); Serial.println( "AT" ); if( !checkOK() ) return false; } return true; } void closeTCPConnection() { Serial.println( "AT+CIPCLOSE" ); checkOK(); } void disconnectFromAP() { Serial.println( "AT+CWQAP" ); checkOK(); } void stopRunning() { while(1) delay( 1000 ); } void ledErrorIndicator( uint8_t times ) { static uint16_t offms[] = {900, 400, 250, 150, 100}; while(1) { for( int i = 0; i < times; i++ ) { digitalWrite( _builtin_led, HIGH ); delay( 100 ); digitalWrite( _builtin_led, LOW ); delay( offms[times - 1] ); } delay( 1000 ); } } |
請參考前幾篇網頁中的說明。
SHT31TCPPost_Demo02.ino, (04/06), station_createTCPPassthrough()
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 | bool station_createTCPPassthrough() { // station swSerialOutput.print( F("Connecting to CHT IoT SP...") ); dbgOutput( "\r\n" ); // 建立 station Serial.println( "AT+CWMODE_CUR=1" ); // station mode dbgOutput( F("AT+CWMODE_CUR=1\r\n") ); if( !checkOK() ) return false; // 連線到 ROUTER Serial.println( "AT+CWJAP_CUR=\"Proteus-WiFi\",\"asdfghjkl\"" ); dbgOutput( "AT+CWJAP_CUR=\"Proteus-WiFi\",\"asdfghjkl\"\r\n" ); if( !checkOK() ) return false; // 指定 station 的 IP、Gateway 和 Netmask Serial.println( "AT+CIPSTA_CUR=\"192.168.11.11\",\"192.168.11.1\",\"255.255.255.0\"" ); dbgOutput( "AT+CIPSTA_CUR=\"192.168.11.11\",\"192.168.11.1\",\"255.255.255.0\"\r\n" ); if( !checkOK() ) return false; // Establish TCP Transmission // <type>,<remote IP>,<remote port> Serial.println( "AT+CIPSTART=\"TCP\",\"iot.cht.com.tw\",80"); dbgOutput( "AT+CIPSTART=\"TCP\",\"iot.cht.com.tw\",80\r\n"); if( !checkOK() ) return false; // 設定傳輸模式為透傳模式 Serial.println( "AT+CIPMODE=1" ); dbgOutput( "AT+CIPMODE=1\r\n" ); if( !checkOK() ) return false; // 開始傳送資料 Serial.println( "AT+CIPSEND" ); dbgOutput( "AT+CIPSEND\r\n" ); if( !Serial.find( ">" ) ) return false; swSerialOutput.print( "done.\r\n\r\n" ); return true; } |
下面兩個函式負責 SHT31 的軟體重啟以及讀取溫溼度值的工作;成功讀取的溫溼度值會儲存到 sht31 結構成員中。相關程式碼的說明已列在其中,不再贅述!
SHT31TCPPost_Demo02.ino, (05/06), SHT31()
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 | void softResetSHT31() { // 確認此 I2C 地址是否存在 SHT31 Wire.beginTransmission( SHT31_IIC_ADDRESS ); if( Wire.endTransmission() != 0 ) { swSerialOutput.print( F("SHT31-D not found!\r\n") ); ledErrorIndicator(3); } // soft reset 0x30A2 Wire.beginTransmission( SHT31_IIC_ADDRESS ); Wire.write( 0x30 ); Wire.write( 0xA2 ); Wire.endTransmission(); delay(10); } bool readTempHumSHT31() { Wire.beginTransmission( SHT31_IIC_ADDRESS ); // 16-bit command byte // 0x2C06: high repeatability measurement with clock stretching enabled Wire.write( 0x2C ); Wire.write( 0x06 ); Wire.endTransmission(); delay(300); // for clock stretching enabled Wire.beginTransmission( SHT31_IIC_ADDRESS ); Wire.endTransmission(); // 取得 6-byte 的資料 // temp msb, temp lsb, temp crc, humi msb, humi lsb, humi crc Wire.requestFrom( SHT31_IIC_ADDRESS, 6 ); if( Wire.available() == 6 ) { uint8_t rawdata[6] = {0}; rawdata[0] = Wire.read(); rawdata[1] = Wire.read(); rawdata[2] = Wire.read(); rawdata[3] = Wire.read(); rawdata[4] = Wire.read(); rawdata[5] = Wire.read(); int temp = (rawdata[0] << 8) + rawdata[1]; sht31.temperature = -45.0 + (175.0 * temp / 65535.0); //float fTemp = (cTemp * 1.8) + 32.0; sht31.humidity = (100.0 * ((rawdata[3] * 256.0) + rawdata[4])) / 65535.0; return true; } return false; } |
整個程式最主要的部分,負責呼叫取得 SHT31 的溫溼度、合成 headers 和 body 兩部分欄位資料形成 HTTP POST Request 格式字串發送給 CHT IoT SP,結束時離開透傳並關閉 TCP 連線。
postSHT31ToCHTIoT
- line 2: 讀取 SHT31 溫溼度值;成功返回 true,失敗返回 false
- line 4 ~ 8: 建立 Request body 所需要的內容 ( JSON 字串)
- line 10 ~ 14: 建立 Request headers 所需要的欄位資料 (字串);裡面缺少兩個資料,請依實際專案中所提供的值填進去
- line 16 ~ 17: 輸出 Request headers 和 body 的實際內容到 SoftwareSerial
- line 18 ~ 19: 輸出 Request headers 和 body 的實際內容到 ESP8266 轉發到 CHT IoT SP
- line 21 ~ 24: 若需要檢查 Response,程式碼可加在此處
SHT31TCPPost_Demo02.ino, (06/06), postSHT31ToCHTIoT()
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 | bool postSHT31ToCHTIoT() { if( readTempHumSHT31() ) { swSerialOutput.println( F("Request ->:") ); String BODY = "[{\"id\":\"SHT31_Temperature\",\"save\":true,\"value\":[\""; BODY += String( sht31.temperature ); // Temperature value of the SHT31 BODY += "\"]},{\"id\":\"SHT31_Humidity\",\"save\":true,\"value\":[\""; BODY += String( sht31.humidity ); // Humidity value the the SHT31 BODY += "\"]}]"; String HEADERS = "POST /iot/v1/device/##########/rawdata HTTP/1.1\r\nHost:iot.cht.com.tw\r\nContent-Type: application/json\r\nContent-Length:"; // 填入設備編號 ^^^^^^^^^^ HEADERS += String( BODY.length() ); HEADERS += "\r\nCK:******************\r\n"; // ^^^^^^^^^^^^^^^^^^ 填入 ${PROJECT_KEY} 或 ${DEVICE_KEY} swSerialOutput.println( HEADERS ); swSerialOutput.print( BODY); Serial.println( HEADERS ); Serial.print( BODY ); //--** Check HTTP Response **--// // // code here // exitPassthroughMode(); // +++ closeTCPConnection(); // AT+CIPCLOSE return true; } // if return false; } |
完成編譯之後上傳程式執行,可得到類似下面的輸出與結果 (點擊可放大看原圖)
程式碼測試結果 |
不過使用上面所提供的函式,很容易就能實現定時上傳數據的功能,在此不再贅述!
一旦加入了 Response 確認與定時上傳數據的功能之後,可得到類似下面的輸出結果
定時數據儲存與 Response 確認 |
CHT IoT SP / 應用服務 / 儀表板 |
CHT IoT SP / 應用服務 / 儀表板 / 新增小工具 |
CHT IoT SP / 應用服務 / 儀表板 |
經過幾篇網頁撰寫過程中與中華電信 IoT 智慧聯網大平台實際的相處下,發現它的功能還不少!像是能源、交通、安防和建築/家庭領域服務,和 AI、大數據和區塊鍊的智慧服務,值得花一些時間去發掘!
看一下硬體也已經支援了 NB-IoT ( 公司行號比較好申請 SIM 卡,個人不知道什麼時可以申請 ? ) ... 等,所以一個平台就包含了幾乎現在物聯網會用到技術服務,只要它不要虎頭蛇尾,持續不斷的改善與更新,就會是未來一個物聯網學習、練功和實務應用的好選擇!
<< 部落格相關文章 >>
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 07 } - 結合 Arduino + ESP8266 實現 MQTT 主題訂閱與接收
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 06 } - 了解 MQTT 協議,學習如何訂閱 MQTT 主題與接收 MQTT 發佈消息
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 05 } - 結合 ESP8266 發佈 MQTT 消息
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 04 } - 了解 MQTT 協議,學習如何發佈 MQTT 消息
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 02 } - 設備感測數據讀取與 (JSON) 解析
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 01 } - 如何使用 ESP8266 利用 AT 指令取回中華電信 IoT 智慧聯網大平台上的設備感測器數據
- 如何使用 MCU 建立與其他 ESP8266 的 UDP 透傳通訊
- ESP8266 AT 指令下的透傳模式
- 如何使用 AT 指令讓同在 AP+STA 模式下的 ESP8266 互相通訊 ?
- 操控 ESP8266 無線模組 - 經由 AP、STA 和 AP+STA 三種模式,學習 ESP8266 AT 指令
沒有留言:
張貼留言
留言屬名為"Unknown"或"不明"的用戶,大多這樣的留言都會直接被刪除掉,不會得到任何回覆!
發問問題,請描述清楚你(妳)的問題,別人回答前不會想去 "猜" 問題是什麼?
不知道怎麼發問,請看 [公告] 部落格提問須知 - 如何問問題 !