如果你參加了上個月在北京的Autodesk 開發者日,你應該看到了我做的關於Arduino的物聯網例項演示,如果你沒看到,歡迎參加14號在上海的開發者日,到時候我會再演(xian)示(bai)一下。
這是個基於這樣一個場景的簡單演示。我的一個建築物上面安裝了這樣一個溫度感測器,隨時把當前環境溫度上傳到雲端,在瀏覽器端可以顯示這個建築物的三維模型和溫度變化曲線圖。如果溫度到達一定的高溫,比如大於40度,那可能是起火了,就需要發出高溫報警,在三維模型中定位出報警的溫度感測器的位置,併發出火警警報。
演示地址 : http://arduiview.herokuapp.com/
繼續之前你可能需要閱讀一下我前面的兩篇文章:
下面簡單介紹一下這個例項系統的實現。下圖為系統的架構圖, Arduino 和 Viewer都通過REST 的方式和雲端的伺服器進行通訊。Arduino 定時把當前溫度通過REST的方式上傳,Viewer定時取得溫度資訊並繪製曲線圖,如遇高溫則報警。貌似相當簡單,這種基於HTTP的REST API方式的一個缺點就是實時性不好,需要進行輪詢。後面我做了改進,通過WebSocket和MQTT協議,可以實現更好的實時傳輸,這個我們後面再說。
下圖就是Arduino 和LM 35溫度感測器的連結情況。Arduino 本身並沒有聯網功能,所以還需要一個額外的裝置,我採用了CC3000 WiFi Shield模組,在淘寶上也可以買到。把Arduino 和CC3000兩個套在一起,然後按照前面文章中提到的方式把溫度感測器連線起來即可。
然後我們需要寫些程式碼驅動CC3300 WiFi模組聯網。我們可以使用Adafruite CC3000 Library 。 在Arduino IDE裡面,“Project” –> “Include Libraries” –> “Manage Libraries”, 搜尋“CC3000”,找到這個類庫安裝。然後你可以閱讀一下自帶的例子。這一點Arduino 做的非常好,每個類庫都有完備的例項,拿過來改一下就可以了。
下面我們需要建立雲端的伺服器,我用node.js來建立並且暴露了一些REST API。其中一個就是用來解釋Arduino上傳的溫度資料的REST API。如下所示:
PUT /sensors/somesensorId/values
body:
{
value : 22
}
node.js中路由部分的程式碼實現為:
router.route('/sensors/:sensorId/values')
.get(sensorController.getSensorValues)
.put(sensorController.appendSensorValues);
下面是sensorController控制器的實現程式碼。這裡沒有列出的是,其實後端我還使用的mongoose和mongoDb以便把上傳的溫度資料儲存起來,這樣以後就可以做大資料分析了。不過這個例子只是為了演示,我也沒儲存全部資料,只是儲存了最近50多個。
exports.appendSensorValues = function(req,res){ //append //we just save 50 + 1 values items to save db spaces var MAX_VAULE_ITEM_COUNT = 50; var sensorId = req.params.sensorId; Sensor.findById(sensorId, function(err, sensor){ if(err) res.json(err); var sensorValueItem = {}; sensorValueItem.timestamp = new Date().getTime(); sensorValueItem.value = req.body.value; //console.log(sensorValueItem); var len = sensor.values.length; sensor.values = sensor.values.slice(len - MAX_VAULE_ITEM_COUNT ); sensor.values = sensor.values.concat(sensorValueItem); sensor.save(function(err){ if(err) res.send(err); res.json(sensorValueItem); }) }); }
這裡的程式碼還是掛一漏萬,如果大家感興趣還是在github上看完整程式碼.https://github.com/duchangyu/project-arduivew/tree/v0.1,
下面來實現Arduino的部分,獲取溫度並通過REST的方式上傳。前面提到的CC3000提供的例項已經演示了怎麼連線到WiFi並上網,這裡略過,如果你感興趣可以看我的完整程式碼。這裡我們只說說Arduino怎麼傳送REST請求的部分。在Arduino裡面,我沒有找到好的REST 的client庫,不過說起來也不復雜,就是按照HTTP的協議傳送原生字串即可。按照我們的REST介面的定義,上傳的REST協議應該是這樣的:
PUT /api/sensors/somesensorid/value HTTP/1.1
HOST: arduiview.heroku.com
content-type : application/json
Content-Length : 19
{
value : 22
}
下面就是構建這樣的字串,然後通過CC3000客戶端傳送出去即可,程式碼片段如下:
void postTemperatureToCloudServer() { //connectToCloudServer Serial.println(F("trying to connect to cloud server.....")); //client.close(); client = cc3000.connectTCP(ip, 80); Serial.println(F("connected to cloud server - ")); Serial.println(WEBSITE ); Serial.println(F("begin uploading...")); float temp = 0.0; // get the current temperature from sensor int reading = analogRead(0); temp = reading * 0.0048828125 * 100; Serial.print(F("Current temp")); Serial.println(temp); int length; char sTemp[5] = ""; //convert float to char*, dtostrf(temp, 2, 2, sTemp); //val, integer part width, precise, result char array //itoa(temp, sTemp,10); Serial.println(sTemp); char sLength[3]; //prepare the http body // //{ // "value" : 55.23 //} // char httpPackage[20] = ""; strcat(httpPackage, "{\"value\": \""); strcat(httpPackage, sTemp); strcat(httpPackage, "\" }"); // get the length of data package length = strlen(httpPackage); // convert int to char array for posting itoa(length, sLength, 10); Serial.print(F("body lenght=")); Serial.println(sLength); //prepare the http header Serial.println(F("Sending headers...")); client.fastrprint(F("PUT /api/sensors/")); char *sensorId = SENSOR_ID; client.fastrprint(sensorId); //client.fastrprint(SENSOR_ID); client.fastrprint(F("/values")); client.fastrprintln(F(" HTTP/1.1")); Serial.print(F(".")); client.fastrprint(F("Host: ")); client.fastrprintln(WEBSITE); Serial.print(F(".")); client.fastrprint(F("content-type: ")); client.fastrprintln(F("application/json")); Serial.print(F(".")); client.fastrprint(F("Content-Length: ")); client.fastrprintln(sLength); client.fastrprintln(F("")); Serial.print(F(".")); Serial.println(F("header done.")); //send data Serial.println(F("Sending data")); client.fastrprintln(httpPackage); Serial.println(F("===upload completed.")); // Get the http page feedback unsigned long rTimer = millis(); Serial.println(F("Reading Cloud Response!!!\r\n")); while (millis() - rTimer < 2000) { while (client.connected() && client.available()) { char c = client.read(); Serial.print(c); } } delay(1000); // Wait for 1s to finish posting the data stream client.close(); // Close the service connection Serial.println(F("upload completed\n")); }
感興趣還是看一下完整程式碼,在這裡:
https://github.com/duchangyu/project-arduivew/blob/v0.1/arduino/arduiview-lm35-2/arduiview-lm35-2.ino