SIM に割り当てられた config
というキーのタグから値を読み出し、解析するところまでをそれぞれ実装しています。
値は JSON で以下のように書かれているとしています。
{ "device_id": "IOT1", "interval": 5000, "sensors": { "led": 1, "temp": 0, "humi": 0, "gps": 1 } }
ここからはデバイスごとの実装例を解説します。
Raspberry Pi (Linux の systemd + Python3)
Raspberry Pi OS 起動時に実行するように systemd を利用しています。実際の取得を担当するのは Python スクリプトです。
/usr/local/sbin/get_metadata.py
# Device DI pattern using SORACOM Air metadata service for Linux
#
# Copyright (c) 2020 SORACOM, INC.
# This software is released under the MIT License.
# http://opensource.org/licenses/mit-license.php
import urllib.request
def get_tag_value_of(tag_key_name):
"""Get value of tag from SORACOM metadata service.
Args:
tag_key_name (str): The name of the tag to retrieve.
Returns:
str: Raw value in tag.
Examples:
>>> get_tag_value_of("config")
b'{"device_id": "IOT1", "interval": 5000, "sensors": {"led": 1, "temp": 0, "humi": 0, "gps": 1}}\n'
Note:
An environment that can access the SORACOM Air metadata service is required.
For example, there is a way to use a USB dongle + SORACOM IoT SIM.
"""
req = urllib.request.Request("http://metadata.soracom.io/v1/subscriber.tags.{}".format(tag_key_name))
with urllib.request.urlopen(req) as res:
body = res.read()
return body
if __name__ == '__main__':
import json
# Fetch value of metadata from SORACOM metadata service
body = get_tag_value_of("config")
print(body)
# Parse JSON
config = json.loads(body)
print(config["device_id"])
print(config["interval"])
print(config["sensors"]["gps"])
/etc/systemd/system/get_metadata.service
[Unit]
Description = Fetch and Apply configuration from SORACOM Metadata service
After = network.target
[Service]
Type = simple
RemainAfterExit = yes
ExecStart = /usr/bin/python3 /usr/local/sbin/get_metadata.py
[Install]
WantedBy = multi-user.target
インストール
$ sudo systemctl daemon-reload
$ sudo systemctl enable get_metadata.service
動作の様子
起動後に以下を確認してみます。
$ sudo systemctl status get_metadata.service
● get_metadata.service - Fetch and Apply configuration from SORACOM Metadata service
Loaded: loaded (/etc/systemd/system/get_metadata.service; enabled; vendor preset: enabled)
Active: active (exited) since Fri 2020-12-11 00:59:14 JST; 6s ago
Process: 705 ExecStart=/usr/bin/python3 /usr/local/sbin/get_metadata.py (code=exited, status=0/SUCCESS)
Main PID: 705 (code=exited, status=0/SUCCESS)
Dec 11 00:59:14 rpi4 systemd[1]: Started Fetch and Apply configuration from SORACOM Metadata service.
Dec 11 00:59:15 rpi4 python3[705]: b'{"device_id": "IOT1", "interval": 5000, "sensors": {"led": 1, "temp": 0, "humi": 0, "gps": 1}}\n'
Dec 11 00:59:15 rpi4 python3[705]: "IOT1"
Dec 11 00:59:15 rpi4 python3[705]: 5000
Dec 11 00:59:15 rpi4 python3[705]: 1
Wio LTE JP Version
Wio LTE JP Version での実装例です。 HTTP アクセスには Wio LTE JP Version のライブラリを、JSON 解析には ArduinoJson を利用しています。
/*
* Device DI pattern using SORACOM Air metadata service for Wio LTE JP Version
*
* Copyright (c) 2020 SORACOM, INC.
* This software is released under the MIT License.
* http://opensource.org/licenses/mit-license.php
*/
#define SerialMon SerialUSB
#include <WioLTEforArduino.h>
WioLTE Wio;
#include <ArduinoJson.h>
DynamicJsonDocument doc(192); // Generated by https://arduinojson.org/v6/assistant/
/**
* @brief Get value of tag from SORACOM metadata service.
* @param[in] tag_key_name The name of the tag to retrieve.
* @param[in] Wio The instance of Wio.
* @param[in] default_value Value to return in case of 404 or 403.
* @return Raw value in tag or default_value.
* @par Examples:
* String body = get_tag_value_of("config", &Wio);
* @note An environment that can access the SORACOM Air metadata service is required.
* For example, there is a way to use a USB dongle + SORACOM IoT SIM.
*/
String get_tag_value_of(const char *tag_key_name, WioLTE *wio, const char *default_value = "") {
char url[255];
sprintf(url, "http://metadata.soracom.io/v1/subscriber.tags.%s", tag_key_name);
char buf[1024];
wio->HttpGet(url, buf, sizeof(buf));
String body = String(buf);
// Wio.HttpGet cannot read header.
if (body == "Specified key does not exist." || /* == 404 */
body == "You Ware not allowed to access Metadata Server.") { /* == 403 */
body = String(default_value);
}
return body;
}
void setup() {
delay(1000);
SerialMon.println("");
SerialMon.println("--- START ---------------------------------------------------");
SerialMon.println("### I/O Initialize.");
Wio.Init();
SerialMon.println("### Power supply ON.");
Wio.PowerSupplyLTE(true);
delay(500);
SerialMon.println("### Turn on or reset.");
if (!Wio.TurnOnOrReset()) {
SerialMon.println("### ERROR! ###");
return;
}
SerialMon.println("### Connecting...");
if (!Wio.Activate("soracom.io", "sora", "sora")) {
SerialMon.println("### ERROR! ###");
return;
}
SerialMon.println("### Setup completed.");
// Fetch value of metadata from SORACOM metadata service
String body = get_tag_value_of("config", &Wio);
SerialMon.println(body);
// Parse JSON
DeserializationError err = deserializeJson(doc, body);
if (err) {
SerialMon.print(F("deserializeJson() failed: "));
SerialMon.println(err.c_str());
return;
}
String c1 = doc["device_id"];
SerialMon.println(c1);
long n2 = doc["interval"];
SerialMon.println(n2);
long n3 = doc["sensors"]["gps"];
SerialMon.println(n3);
SerialMon.println("### done.");
}
void loop() {
// Your impl.
}
動作の様子
--- START ---------------------------------------------------
### I/O Initialize.
### Power supply ON.
### Turn on or reset.
### Connecting...
### Setup completed.
{"device_id": "IOT1", "interval": 5000, "sensors": {"led": 1, "temp": 0, "humi": 0, "gps": 1}}
IOT1
5000
1
### done.
M5Stack や Arduino UNO
M5Stack 用 3G 拡張ボード や LTE-M Shield for Arduino で動作するサンプルスケッチです。
このサンプルスケッチでは、以下のライブラリを利用します。
- 3G/LTE 通信: TinyGSM。詳しくは、TinyGSM をインストールする を参照してください。
- HTTP アクセス: ArduinoHttpClient
- JSON 解析: ArduinoJson
Arduino UNO + LTE-M Shield for Arduino においては、実行時メモリ不足になるため ArduinoJson による JSON 解析は行わないようにしています。
/*
* Device DI pattern using SORACOM Air metadata service for M5Stack(with 3G ext. board)/Arduino UNO(with LTE-M Shield for Arduino)
*
* Copyright (c) 2020 SORACOM, INC.
* This software is released under the MIT License.
* http://opensource.org/licenses/mit-license.php
*/
#define SerialMon Serial
#ifdef ARDUINO_M5Stack_Core_ESP32
#include <M5Stack.h>
#include <HTTPClient.h> /* Why? see https://qiita.com/ma2shita/items/97bf1a0c3158b848019a */
#endif
#ifdef ARDUINO_M5Stack_Core_ESP32
#define SerialAT Serial2 // `Serial2` is 3G Extension board for M5Stack Basic/Gray
#define TINY_GSM_MODEM_UBLOX
#elif defined(ARDUINO_AVR_UNO)
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(10, 11); // for LTE-M Shield for Arduino with Arduino UNO
#define TINY_GSM_MODEM_BG96
#endif
#include <TinyGsmClient.h>
TinyGsm modem(SerialAT);
TinyGsmClient socket(modem);
#include <ArduinoHttpClient.h>
#ifdef ARDUINO_M5Stack_Core_ESP32
#include <ArduinoJson.h>
DynamicJsonDocument doc(192); // Generated by https://arduinojson.org/v6/assistant/
#endif
/**
* @brief Get value of tag from SORACOM metadata service.
* @param[in] tag_key_name The name of the tag to retrieve.
* @param[in] socket The instance of TinyGsmClient.
* @param[in] default_value Value to return in case of 404 or 403.
* @return Raw value in tag or default_value.
* @par Examples:
* String body = get_tag_value_of("config", &Wio);
* @note An environment that can access the SORACOM Air metadata service is required.
* For example, there is a way to use a USB dongle + SORACOM IoT SIM.
*/
String get_tag_value_of(const char *tag_key_name, TinyGsmClient *socket, const char *default_value = "") {
char path[255];
sprintf_P(path, PSTR("/v1/subscriber.tags.%s"), tag_key_name);
HttpClient http = HttpClient(*socket, "metadata.soracom.io", 80);
http.get(path);
int rc = http.responseStatusCode();
String body = http.responseBody();
http.stop();
if (rc != 200) body = String(default_value);
return body;
}
void setup() {
delay(1000);
SerialMon.begin(115200);
SerialMon.println("");
SerialMon.println("--- START ---------------------------------------------------");
#ifdef ARDUINO_M5Stack_Core_ESP32
M5.begin();
SerialAT.begin(115200, SERIAL_8N1, 16, 17);
#elif defined(ARDUINO_AVR_UNO)
SerialAT.begin(9600);
#endif
SerialMon.println(F("modem.restart()"));
modem.restart();
SerialMon.println(F("waitForNetwork()"));
while (!modem.waitForNetwork()) SerialMon.print(".");
SerialMon.println(F("gprsConnect(soracom.io)"));
modem.gprsConnect("soracom.io", "sora", "sora");
SerialMon.println("### Setup completed.");
// Fetch value of metadata from SORACOM metadata service
String body = get_tag_value_of("config", &socket);
SerialMon.println(body);
#ifdef ARDUINO_M5Stack_Core_ESP32
// Parse JSON
DeserializationError err = deserializeJson(doc, body);
if (err) {
SerialMon.print(F("deserializeJson() failed: "));
SerialMon.println(err.c_str());
return;
}
String c1 = doc["device_id"];
SerialMon.println(c1);
long n2 = doc["interval"];
SerialMon.println(n2);
long n3 = doc["sensors"]["gps"];
SerialMon.println(n3);
#endif
SerialMon.println("### done.");
}
void loop() {
// Your impl.
}
動作の様子 (M5Stack Basic + M5Stack 用 3G 拡張ボード)
--- START ---------------------------------------------------
modem.restart()
waitForNetwork()
gprsConnect(soracom.io)
### Setup completed.
{"device_id": "IOT1", "interval": 5000, "sensors": {"led": 1, "temp": 0, "humi": 0, "gps": 1}}
IOT1
5000
1
### done.