FIWARE IoT Agent for Ultralight 2.0 対応デバイス
以前の記事では、FIWARE Orion に直接アクセスして、エンティティ情報を更新しました。FIWARE には、IoT Agent と呼ばれる、既存の IoT プロトコルと NGSI の橋渡しをする Generic Enabler があります。これを利用すると、既存 IoT プロトコルを使用して、エンティティ情報を更新することができます。各種 IoT プロトコルに対応した、複数の IoT Agent があり、IDAS (IoT Device Agent Suites) と呼ばれています。
本記事では、IoT Agent for Ultralight 2.0 を使用して、温度、湿度、気圧の情報を定期的に、FIWARE Orion にプッシュし、コンテキスト情報を更新する、IoT デバイスを作成します。Ultralight 2.0 は、帯域幅やデバイスのメモリが制限された、制約のあるデバイスとの通信を目的とした、軽量のテキスト・ベースのプロトコルです。
サーバ側には、Orion と IoT Agent for Ultralight 2.0 をデプロイします。デバイスには、IoT マイコンの ESP32 を使用し、Ultralight 2.0 に対応したデバイスを作成します。
サーバ環境の作成
Orion と IoT Agent for Ultralight 2.0 は、Docker コンテナを利用して、サーバ上にデプロイします。docker-compose ファイルは次のとおりです。
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 |
version: "3" services: mongo: container_name: mongo restart: always image: mongo:3.4 command: --nojournal ports: - 127.0.0.1:27017:27017 orion: container_name: orion restart: always image: fiware/orion:2.0.0 depends_on: - mongo links: - mongo ports: - "1026:1026" command: -dbhost mongo iot-agent: container_name: fiware-iot-agent restart: always image: fiware/iotagent-ul:1.7.0 hostname: iot-agent depends_on: - mongo expose: - "4041" - "7896" ports: - "4041:4041" - "7896:7896" environment: - "IOTA_CB_HOST=orion" - "IOTA_CB_PORT=1026" - "IOTA_NORTH_PORT=4041" - "IOTA_REGISTRY_TYPE=mongodb" - "IOTA_LOG_LEVEL=DEBUG" - "IOTA_TIMESTAMP=true" - "IOTA_MONGO_HOST=mongo" - "IOTA_MONGO_PORT=27017" - "IOTA_MONGO_DB=iotagentul" - "IOTA_HTTP_PORT=7896" - "IOTA_PROVIDER_URL=http://iot-agent:4041" |
正常性の確認
Orion のバージョン取得や、IoT Agent に次のクエリを実行して、正しくデプロイされたことを確認してください。(サーバの IP アドレスを 192.168.1.1 として説明しています)
次のようなレスポンスが返ってくることを確認してください。
1 2 3 4 5 6 |
{ "libVersion": "2.7.0", "port": "4041", "baseRoot": "/", "version": "1.7.0" } |
Ultralight 2.0 対応デバイスの作成
ESP32 を利用して、Ultralight 2.0 対応デバイスの作成します。プログラムは次のとおりです。このプログラムのポイントは、loop() 関数のなかで、センサからデータを取得し、Ultralight 2.0 のペイロードを作成し、IoT Agent に送信している部分です。これで一定間隔で、温湿度、気圧の情報を IoT Agent に送信されます。
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 |
/** * iot-device-sensor.ino * * IoT Device for Ultralight 2.0 * * Copyright 2018 Kazuhito Suda * * Created on: December 8, 2018 * */ #include <WiFi.h> #include <HTTPClient.h> #include <time.h> #include <Wire.h> #include <SPI.h> #include <Adafruit_Sensor.h> // Adafruit Unified Sensor #include <Adafruit_BME280.h> // Adafruit BME280 libary #define SEALEVELPRESSURE_HPA (1013.25) #define I2C_SDA 4 // BME280 SDA <---> ESP32 GPIO4 #define I2C_SCL 5 // BME280 SCL <---> ESP32 GPIO5 Adafruit_BME280 bme; // I2C #define JST (3600*9) const char essid[] = "your essid"; const char passphrase[] = "your passphrase"; const char iotagent[] = "http://192.168.11.1:7896/iot/d"; const char key[] = "x3knfo18xf28ekqbs93zmroz9f"; const char device[] = "sensor001"; void setup() { Serial.begin(115200); Serial.println("Start"); connectWifi(); configTime(JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp"); setupBME280(); } void loop() { HTTPClient http; char url[128]; char postData[128]; time_t t = time(NULL); struct tm *tm = localtime(&t); sprintf(url, "%s?k=%s&i=%s", iotagent, key, device); http.begin(url); sprintf(postData, "d|%04d-%02d-%02dT%02d:%02d:%02d+0900|t|%2.2f|h|%2.2f|p|%4.2f", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, bme.readTemperature(), bme.readHumidity(), bme.readPressure() / 100.0F); Serial.printf("POST %s\n", url); Serial.println(postData); int httpCode = http.POST(postData); if(httpCode > 0) Serial.printf("httpCode: %d\n", httpCode); else Serial.printf("POST failed, error: %s\n", http.errorToString(httpCode).c_str()); http.end(); delay(5000); } void connectWifi() { WiFi.mode(WIFI_STA); WiFi.begin(essid, passphrase); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(100); } Serial.println(""); Serial.println("Connected"); Serial.print("IP address : "); Serial.println(WiFi.localIP()); } void setupBME280() { Wire.begin(I2C_SDA, I2C_SCL); bool status = bme.begin(); if (!status) { Serial.println("Could not find a valid BME280 sensor, check wiring!"); while (1); } } |
IoT デバイスの接続
IoT デバイスにプロビジョニングします。最初にサービス・グループをプロビジョニングし、次にデバイスをプロビジョニングします。
サービス・グループのプロビジョニング
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/bin/sh curl -iX POST \ 'http://192.168.1.1:4041/iot/services' \ -H 'Content-Type: application/json' \ -H 'fiware-service: openiot' \ -H 'fiware-servicepath: /' \ -d '{ "services": [ { "apikey": "x3knfo18xf28ekqbs93zmroz9f", "cbroker": "http://192.168.1.1:1026", "entity_type": "Thing", "resource": "/iot/d" } ] }' |
デバイスのプロビジョニング
動的な属性値として、温度、湿度、気圧、取得時刻を、静的な属性値として位置情報をもつデバイスとしてプロビジョニングします。
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 |
#!/bin/sh curl -iX POST \ 'http://192.168.1.1:4041/iot/devices' \ -H 'Content-Type: application/json' \ -H 'fiware-service: openiot' \ -H 'fiware-servicepath: /' \ -d '{ "devices": [ { "device_id": "sensor001", "entity_name": "urn:ngsd-ld:Sensor:001", "entity_type": "Sensor", "timezone": "Asia/Tokyo", "attributes": [ { "object_id": "d", "name": "dateObserved", "type": "DateTime" }, { "object_id": "t", "name": "temperature", "type": "Number" }, { "object_id": "h", "name": "relativeHumidity", "type": "Number" }, { "object_id": "p", "name": "atmosphericPressure", "type": "Number" } ], "static_attributes": [ { "name":"location", "type": "geo:json", "value" : { "type": "Point", "coordinates" : [ 139.7671, 35.68117 ] } } ] } ] } ' |
センサデータの送信
サーバ環境の作成、デバイスのプロビジョニングが完了したら、デバイスの電源をオンにします。ESP32 のシリアルから次のような出力があれば、IoT Agent に正しくデータが送信できています。
1 2 3 4 5 6 7 8 9 10 11 12 |
Start ...................................................................... Connected IP address : 192.168.1.2 POST http://192.168.1.1:7896/iot/d?k=x3knfo18xf28ekqbs93zmroz9f&i=sensor001 d|1970-01-01T09:00:07+0900|t|22.95|h|30.12|p|1019.56 httpCode: 200 POST http://192.168.1.1:7896/iot/d?k=x3knfo18xf28ekqbs93zmroz9f&i=sensor001 d|2018-12-08T16:40:03+0900|t|23.01|h|30.39|p|1019.62 httpCode: 200 POST http://192.168.1.1:7896/iot/d?k=x3knfo18xf28ekqbs93zmroz9f&i=sensor001 d|2018-12-08T16:40:13+0900|t|22.99|h|30.37|p|1019.58 |
コンテキス情報の確認
Orion に対してクエリを実行して、コンテキスト情報が正しく更新されているかを確認します。
クエリ
1 2 3 4 5 |
#!/bin/sh curl -X GET \ 'http://192.168.1.1:1026/v2/entities/urn:ngsd-ld:Sensor:001?type=Sensor' \ -H 'fiware-service: openiot' \ -H 'fiware-servicepath: /' |
実行結果
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 |
{ "id": "urn:ngsd-ld:Sensor:001", "type": "Sensor", "TimeInstant": { "type": "ISO8601", "value": "2018-12-08T07:43:07.00Z", "metadata": {} }, "atmosphericPressure": { "type": "Number", "value": 1019.62, "metadata": { "TimeInstant": { "type": "ISO8601", "value": "2018-12-08T07:43:07.682Z" } } }, "dateObserved": { "type": "DateTime", "value": "2018-12-15T07:42:38.00Z", "metadata": { "TimeInstant": { "type": "ISO8601", "value": "2018-12-08T07:43:07.682Z" } } }, "location": { "type": "geo:json", "value": { "type": "Point", "coordinates": [ "139.7671", "35.68117" ] }, "metadata": { "TimeInstant": { "type": "ISO8601", "value": "2018-12-08T07:43:07.682Z" } } }, "relativeHumidity": { "type": "Number", "value": 30.02, "metadata": { "TimeInstant": { "type": "ISO8601", "value": "2018-12-08T07:43:07.682Z" } } }, "temperature": { "type": "Number", "value": 23.09, "metadata": { "TimeInstant": { "type": "ISO8601", "value": "2018-12-08T07:43:07.682Z" } } } } |
実行結果 (keyValues)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "id": "urn:ngsd-ld:Sensor:001", "type": "Sensor", "TimeInstant": "2018-12-08T07:43:33.00Z", "atmosphericPressure": 1019.6, "dateObserved": "2018-12-08T07:43:03.00Z", "location": { "type": "Point", "coordinates": [ "139.7671", "35.68117" ] }, "relativeHumidity": 29.95, "temperature": 22.93 } |