Soracom

Users

スタートガイド
Home スタートガイド IoT デザインパターン デバイスや環境に依存する設定情報を SORACOM から取得する 実装ガイド

実装例

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 で動作するサンプルスケッチです。

このサンプルスケッチでは、以下のライブラリを利用します。

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.