初探物聯網 - 基於Arduino的氣象站和View and Data API的結合例項

峻祁連發表於2015-12-04

如果你參加了上個月在北京的Autodesk 開發者日,你應該看到了我做的關於Arduino的物聯網例項演示,如果你沒看到,歡迎參加14號在上海的開發者日,到時候我會再演(xian)示(bai)一下。

這是個基於這樣一個場景的簡單演示。我的一個建築物上面安裝了這樣一個溫度感測器,隨時把當前環境溫度上傳到雲端,在瀏覽器端可以顯示這個建築物的三維模型和溫度變化曲線圖。如果溫度到達一定的高溫,比如大於40度,那可能是起火了,就需要發出高溫報警,在三維模型中定位出報警的溫度感測器的位置,併發出火警警報。

演示地址 : http://arduiview.herokuapp.com/ 

 

繼續之前你可能需要閱讀一下我前面的兩篇文章:

邁出物聯網的第一步,玩兒一下Arduino

使用 Arduino 和 LM35 溫度感測器監測溫度

 

下面簡單介紹一下這個例項系統的實現。下圖為系統的架構圖, Arduino 和 Viewer都通過REST 的方式和雲端的伺服器進行通訊。Arduino 定時把當前溫度通過REST的方式上傳,Viewer定時取得溫度資訊並繪製曲線圖,如遇高溫則報警。貌似相當簡單,這種基於HTTP的REST API方式的一個缺點就是實時性不好,需要進行輪詢。後面我做了改進,通過WebSocket和MQTT協議,可以實現更好的實時傳輸,這個我們後面再說。

Screen Shot 2015-12-03 at 2.50.53 PM

 

下圖就是Arduino 和LM 35溫度感測器的連結情況。Arduino 本身並沒有聯網功能,所以還需要一個額外的裝置,我採用了CC3000 WiFi Shield模組,在淘寶上也可以買到。把Arduino 和CC3000兩個套在一起,然後按照前面文章中提到的方式把溫度感測器連線起來即可。

arduino-lm35

然後我們需要寫些程式碼驅動CC3300 WiFi模組聯網。我們可以使用Adafruite CC3000 Library 。 在Arduino IDE裡面,“Project” –> “Include Libraries” –> “Manage Libraries”, 搜尋“CC3000”,找到這個類庫安裝。然後你可以閱讀一下自帶的例子。這一點Arduino 做的非常好,每個類庫都有完備的例項,拿過來改一下就可以了。

 Screen Shot 2015-12-03 at 1.25.34 PM

 

下面我們需要建立雲端的伺服器,我用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 

相關文章