2016年8月14日 星期日

使用 Xively 物聯網服務

去年曾經接觸過 Xively 這個物聯網服務網站, 較早期的 Arduino 書上會提到 Pachube 與 Cosm 物聯網服務, 它原先是免費的, 後來被 LogMeIn 併購改名為 Xively, 轉型為收費服務. 參考 :

# 關於物聯網服務平台 Pachube 與 Cosm

我以為它完全不再提供免費服務, 所以就轉而使用開源的 ThingSpeak. 但今天在翻閱本周要還的這本碁峰的 "實戰數位家庭自動化" :


在第 4, 5 章介紹利用 Xively 所提供的物聯網服務來將設備所取得的感測資料儲存到雲端, 以達到全球存取目的. 我自己買的博碩出版 "Arduino 物聯網專案實作" 這本的第五章也有介紹結合 Xively 與 Nearbus 服務以紀錄太陽能板的電壓變化 :


我在想這兩本書都還蠻新的, 都沒提到說 Xively 要付費才能用. 於是我用 "xively free" 去搜尋, 發現 Xively 有保留給個人開發者免費使用體驗的登入畫面如下 (官網似乎找不到入口, 只找到 Free Trial), 以前都不知道 :

https://personal.xively.com


但這是登入畫面, 必須先申請帳號才行, 其申請網址如下 (注意, 此頁面有時會出現 Page not found, 只要重新整理幾次就會顯示了) :

https://personal.xively.com/signup


填寫帳號, email, 以及密碼, 以及國家, 郵區等資料後, 按 Sign Up 鈕即可. 接著必須去收信, 按信裡面的帳號啟動連結即完成帳號啟用 :



啟用同時即登入, 首先按 "+ Add Device" 新增設備 :

這裡我加入一個 Arduino+ESP8266 的溫溼度與亮度的測候站, 輸入 Device Name 與簡單描述, Privacy 勾選 Private Device (除非是要公開讓人讀取才勾 Public), 按 "Add Device" 即新增 :



新增設備後, 頁面右上方便會出現此設備的 Feed ID, 右下方也會有自動產生的金鑰, 這兩項資料是 Arduino 程式與 Xively 網站連線饋送資料所必須, 所以要複製下來備用 :



最後是按 "+Add Channel" 為此 WeatherStation 設備新增資料通道 (Channel), 也就是要傳送的變數 :


 我新增了 Temperature, Humidity, 以及 Luminance 三個變數 :


以上便完成 Xively 的設定了. 關於 Xively 的使用方式可參考 :

Xively Basics Tutorial

接下來便可撰寫 Arduino 程式來向 Xively 饋送感測資料, 可參考 2016/7/8 這篇最後面的程式來修改 :

以 Arduino+ESP8266 物聯網模組重作 DHT11 溫溼度實驗

關於光敏電阻則可參考 :

# Arduino 光敏電阻測試

以下測試程式是在我自己用洞洞板焊接的 Arduino Nano+ESP8266 IOT 模組上進行實驗, 電路圖參考 :

# 製作 Arduino Nano + ESP8266 物聯網模組 (四) : 完結篇



向 Xively 物聯網伺服器饋送資料比 ThingSpeak 稍微麻煩一點, 它採用 HTTP 的 PUT 方法, 將要傳送的參數以 JSON 格式呈現; 而 ThingSpeak 則是用 GET 字串來傳送. 在碁峰的 "實戰數位家庭自動化" 這本書第四章 "用 Xively 建置雲端氣象站" 有範例程式, 不過那是以 CC3000 Wifi 晶片函式庫寫的, 不能直接用在 ESP8266, 但是可以從中了解與 Xively 互動的步驟, 參考 :

https://github.com/openhomeautomation/arduino-home-automation/blob/master/chapter4/weather_xively/weather_xively.ino

此 PUT 方法須向 Xively 傳送如下的 HTTP 標頭 :

PUT /v2/feeds/321056143.json HTTP/1.1
Host: api.xively.com
X-ApiKey: cnSMGOi2HMVV5kaHxq6QKSJ9JgvqIBVblxqWZxxxxxxxxksY
Content-Length: 171

第一行中的 json 主檔名就是上面新增設備時所獲得的 FEED ID, 代表此一氣象站設備; 而 X-ApiKey 屬性值為認證金鑰; 最後一行 Content-Length 則為後面要傳送的 HTTP 本體資料的長度 (byte 數), 也就是 JSON 字串的長度, 其內容如下 :

{"version":"1.0.0","datastreams" : [{"id" : "Temperature","current_value" : "26"},
                                    {"id" : "Luminance","current_value" : "58"},
                                    {"id" : "Humidity","current_value" : "72"}]}

可見其資料結構由 version 與 datastream 兩個屬性組成, 其中 datastream 就是我們要傳送的參數集合, 參數名稱寫在 id 屬性, 而參數值則寫在 current_value 屬性中. 完整程式如下 :

#include <SoftwareSerial.h>
#define DEBUG true
#include "DHT.h"
#define DHTPIN 6
#define DHTTYPE DHT11

SoftwareSerial esp8266(7,8); //(RX,TX)
DHT dht(DHTPIN, DHTTYPE); // Initialize DHT sensor
String feed_id="321056143"; //Xively WeatherStation feed ID
String api_key="cnSMGOi2HMVV5kaHxq6QKSJ9JgvqIBVblxqWZxxxxxxxxksY"; //Xively API Write Key

void setup() {
  Serial.begin(9600);
  esp8266.begin(9600);
  sendData("AT+RST\r\n",2000,DEBUG); // reset ESP8266
  sendData("AT+GMR\r\n",1000,DEBUG);
  delay(3000); //wait for wifi connection to get local ip
  sendData("AT+CIFSR\r\n",1000,DEBUG); //get ip address
  }

void loop() {
  float Humidity=dht.readHumidity(); //get humidity from DHT11
  float Temperature=dht.readTemperature(); //get temperature (C) from DHT11
  int cds=analogRead(A0); //get cds voltage (0~5V to 0~1023 dark)
  int Luminance=100-map(cds,0,1023,0,100); //map 0~1023(dark) to 0(dark)~100%
  Serial.print("Temperature=");
  Serial.println(Temperature);
  Serial.print("Humidity=");
  Serial.println(Humidity);
  Serial.print("Luminance=");
  Serial.println(Luminance);
  //create data string to Xively            
  String json=String("") + "{\"version\":\"1.0.0\",\"datastreams\" : " +
              "[ {\"id\" : \"Temperature\",\"current_value\" : \"" +
              String((int)Temperature) + "\"}," +
              "{\"id\" : \"Luminance\",\"current_value\" : \"" +
              String(Luminance) + "\"}," +
              "{\"id\" : \"Humidity\",\"current_value\" : \"" +
              String((int)Humidity) + "\"}]}";
  String head="PUT /v2/feeds/" + String(feed_id) + ".json HTTP/1.1\r\n" +
              "Host: api.xively.com\r\n" +
              "X-ApiKey: " + String(api_key) + "\r\n" +
              "Content-Length: " + json.length() + "\r\n\r\n";
  Serial.println(head);
  String cmd="AT+CIPSTART=\"TCP\",\"api.xively.com\",80\r\n";
  sendData(cmd, 1000, DEBUG);
  cmd="AT+CIPSEND=" + (String)(head.length() + json.length()) + "\r\n";
  sendData(cmd,1000,DEBUG);
  sendData(head, 1000, DEBUG);
  sendData(json, 1000, DEBUG);
  sendData("AT+CIPCLOSE\r\n",1000,DEBUG); //close session
  delay(5000);
  }

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;
  }

在這個程式中, 我們從 D0 腳讀取 DHT11 所送出的溫溼度資料; 從 A0 腳讀入光敏電阻的電壓, 然後換算成 0~100% 的光度值. 在 HTTP 協定部分, 我們將 PUT 方法的 HTTP 格式字串分成標頭 (head) 與本體 (json), 先用 AT+CIPSEND 告訴 ESP8266 總共要傳遞多少字元資料 (head 加 json 長度), 然後分兩次先後將head 與 json 傳送出去. JSON 資料的屬性與值必須用雙引號括起來, 所以都用倒斜線來跳脫 (Escape).注意, 在製作 json 字串時, 必須先用 String() 函數產生字串物件, 以避免產生 invalid operands of type 錯誤, 參考 :

Error: invalid operands of types ‘const char [35]’ and ‘const char [2]’ to binary ‘operator+’

此程式編譯後記憶體耗用情形 :

草稿碼使用了 9,896 bytes (32%) 的程式儲存空間。上限為 30,720 bytes。
全域變數使用了 799 bytes (39%) 的動態記憶體,剩餘 1,249 bytes 給區域變數。上限為 2,048 bytes 。

程式上傳後登入 Xively 網站便可看到各通道資料隨時間變化的圖形 :

https://personal.xively.com

或在下列網址填入此設備的 PRODUCT ID:

# https://personal.xively.com/develop/PRODUCT_ID


每個通道圖形底下有一個時鐘圖形, 點一下可以更改時間軸單位. Arduino 每提出一次要求, 右邊的 Request Log 新增一筆 :


點一下 feed 就會顯示該次要求的內容 :


序列埠監控視窗擷取訊息如下 :

Temperature=28.00
Humidity=88.00
Luminance=81
PUT /v2/feeds/321056143.json HTTP/1.1
Host: api.xively.com
X-ApiKey: cnSMGOi2HMVV5kaHxq6QKSJ9JgvqIBVblxqWZbPebWRnLksY
Content-Length: 171


AT+CIPSTART="TCP","api.xively.com",80


OK
Linked
AT+CIPSEND=315

< PUT /v2/feeds/321056143.json HTTP/1.1
Host: api.xively.com
X-Ap{"version":"1.0.0","datastreams" : [ {"id" : "Temperature","cur
SEND OK

+IPD,251:HTTP/1.1 200 OK
Date: Sun, 14 Aug 2016 07:01:52 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 0
Connection: keep-alive
X-Request-Id: fab66cbb2e205fcd63e2ee50150edefc40ea5151
Cache-Control:AT+CIPCLOSE


OK
Unlink

雖然用過 ThingSpeak 後再來用 Xively 感覺有點麻煩, 但終究是可以用啦! 當作是體驗各家物聯網服務的不同作法也好. 或許 Xively 有其他特異功能也說不定, 有待慢慢發掘.

參考 :

vindolin/wifisensor.ino (ESP8266 範例程式)
# https://github.com/openhomeautomation/arduino-home-automation/tree/master/chapter4
https://personal.xively.com/testdrive
# Python x Arduino物聯網整合開發實戰


沒有留言:

張貼留言