設定変更やAPIキー保管の課題と「SORACOMサービス」
ここまでの作業でセルラー通信を活用し「いつでも・どこでも・だれでも使える雨雲レーダー表示デバイス」ができ上がったわけですが、実は「変更に弱い」ことと「セキュリティ上のリスク」という2つの課題があります。
この2つの課題は「スケッチ内に直接情報を埋め込んでいる」ことに起因するものです。
例えば大阪を中心に表示する設定変更をしたい場合、スケッチのlatやlonを書き換えてM5Stackに書き込むことで実現できます。今回のように1台のみを対象とした場合は作業負担は少なくて済みますが、これが10台、100台となった時はどうなるでしょうか? また、4台は東京、3台は大阪、3台は福岡と別々の設定をすることになった場合の作業量はあまり想像したくありません。
柔軟に変更に対応できるように、設定情報をmicroSDに保管しておいて必要時に読み込むといった方法が考えられますが、今度は「microSDからの情報漏洩」という問題があります。設定情報ならまだしも、クレデンシャル(認証情報)が流出してしまったら、その情報を使ってクラウド上のリソースを使われてしまうリスクを抱えることになるわけです。
SORACOMサービスのアプローチ
3GやLTEといったセルラー通信に使われるSIMは「内部情報の読み出しと複製が極めて困難」という特性を持つ(耐タンパー性が高い)デバイスです。
SORACOMではこの特性を活かして、SIMを鍵(Root of Trust;信頼性を確保するための基点)として利用できるサービスを提供しています。「鍵」の利用といっても難しいプログラムや操作は不要です。通信が確立した時点でSORACOMでは「どのSIMの通信なのか」を識別することができるため、例えばこの後紹介するデータ転送サービス、SORACOM Beamでは、SIMごとに転送先URLの設定を行うことができます。その際、「鍵」の存在を意識することなく安全にご利用いただけます。
データ転送サービス「SORACOM Beam」
SORACOM Beamは、本来であればデバイス(スケッチ)内に保管されるクラウドやサービスの接続先設定をSORACOM上にオフロードできるサービスです。
SORACOM IoT SIMで通信しているデバイスからは、beam.soracom.io(もしくはuni.soracom.io)というアドレスにアクセスできるようになります。このアドレスを介して、SORACOM上に保管された転送設定に応じたURLにアクセスします。これによりデバイス上ではbeam.soracom.ioにアクセスするスケッチで済みます。先ほどの「大阪」「福岡」の例であれば、スケッチを変更するのではなく「SORACOMユーザーコンソール上のSORACOM Beamの設定変更」で対応できるわけです。
また、M5StackとSORACOM Beam間はセルラー通信によって暗号化されているため、HTTPSのような上位での暗号化が不要となります。そのため、暗号化通信オーバーヘッドがなくなることで通信量を削減することができます。メリットとしては通信料金を抑えることはもちろん、通信時間を短くできることでデバイスの電力消費の削減も可能となります。
注
SORACOM Beamは従量課金サービスです。費用についてはSORACOM Beam のご利用料金でご確認ください。また、SORACOM Beamは気軽にお試しいただくための「無料枠」がございます。1アカウントあたり月間10万リクエストまで毎月無料です(執筆時点)。
SORACOM Beamの設定
SORACOM Beam 上に「/rainradarのアクセスをmap.yahooapis.jp/map/V1/static?....に転送する」設定を行います。
まず、SORACOM ユーザーコンソール>[(左上の) Menu]>[SIM 管理]に進みます(すでにSIM一覧が表示されている場合は遷移しないので、左上の[SORACOMロゴ]をクリックしてください)。
SIM一覧から、M5Stackに取り付けているSIMの「チェックボックス」にチェックを付けた後、[操作]>[所属グループ変更]に進みます。
「SIM の所属グループ変更」ダイアログでは、新しい所属グループのプルダウンをクリックし、[新しいグループを作成]に進みます。
「グループ作成」ダイアログでは、グループ名にyahooapis-for-m5stack(任意の文字列でも可)を入力して[グループ作成]に進みます。
すると「SIM の所属グループ変更」ダイアログに戻ります。新しい所属グループにyahooapis-for-m5stackが設定されていることを確認して[グループ変更]に進みます。
SIM一覧のグループ列に表示されているyahooapis-for-m5stackのリンクをクリックします。
「SIM グループ」で[SORACOM Beam 設定]をクリックしてから[+]>[HTTP エントリポイント]に進みます。
「SORACOM Beam - HTTP 設定」ダイアログでは、以下のように入力してから[保存]に進みます。
-
パス:
/rainradar - プロトコル:HTTPS
-
ホスト名:
map.yahooapis.jp - ポート番号:(なにも入力しません)
-
パス:
/map/V1/static?appid=<YOUR_CLIENT_ID>&output=jpg&quality=50&width=320&height=208&overlay=type:rainfall%7Cdatelabel:off&mode=map&style=base:simple&z=8&lat=35.6313456&lon=139.7312189-
注意:パスの
<YOUR_CLIENT_ID>をClient IDに置き換えてください。
-
注意:パスの
以上でSORACOM Beamの設定は終了です。
SORACOM Beam対応スケッチへの変更
次はスケッチの変更です。今までのスケッチをもとにSORACOM Beamへ対応させてみます。変更点は3つです。
これから紹介する変更点が適用済みのSORACOM Beam対応版スケッチはGist / m5stack_rainradar_with_soracom_beam.inoからもダウンロードいただけます。
1点目:9行目
SORACOM BeamへはHTTPでアクセスするため、HTTPSからHTTPに変更します。
// 前: TinyGsmClientSecure ctx(modem); // 後: TinyGsmClient ctx(modem);
2点目:87、88行目
SORACOM Beamのアドレスを指定します。
// 前: const char *host = "map.yahooapis.jp"; const int port = 443; // 後: const char *host = "beam.soracom.io"; const int port = 8888;
3点目:98行目
URLのパス部分を変更します。
// 前 (途中省略しています): const char *path = "GET /map/V1/static?appid=<YOUR_CLIENT_ID>&output=jpg&quality=50&...&lon=139.7312189 HTTP/1.0"; // 後: const char *path = "GET /rainradar HTTP/1.0";
変更が済んだら、M5Stackに書き込みます。書き込みが正常に完了すれば自動的に再起動して書き込まれたスケッチが実行されます。見た目や挙動は前回と同様ですが、表示位置やズームの変更は SORACOM Beam上の転送先URLの変更で済みます。例えば、SORACOM Beam上のURL設定でlon=139.7312189からlon=136.7312189として保存した後に、左ボタンを押して雨雲レーダーの再取得を行うと、以下のように岐阜を中心とした表示に変更されていることが確認できます。
Gistで配布しているソースコード
Gistで配布しているソースコードを、こちらにも掲載します。
/*
* Copyright (c) 2019 Kohei "Max" MATSUSHITA
* Released under the MIT license
* https://opensource.org/licenses/mit-license.php
*/
#include <M5Stack.h>
#define CONSOLE Serial
#define MODEM Serial2 /* Serial2 is Modem of 3G Module */
#include <string.h>
#define TINY_GSM_MODEM_UBLOX
#include <TinyGsmClient.h>
TinyGsm modem(MODEM);
TinyGsmClient ctx(modem);
void modem_enabler();
void render_rain_radar();
void print_top();
void print_bottom();
uint16_t getColor(uint8_t red, uint8_t green, uint8_t blue);
void setup() {
M5.begin();
CONSOLE.begin(115200);
M5.Lcd.clear(BLACK);
M5.Lcd.setTextSize(2);
print_top();
print_bottom();
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setCursor(0, 16);
M5.Lcd.println("## Congrats,");
M5.Lcd.println("## Boot successfully!");
}
void loop() {
if (M5.BtnA.wasReleased()) {
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setCursor(218, 0);
M5.Lcd.print(F("(prog..."));
modem_enabler();
get_and_render_rain_radar();
print_top();
print_bottom();
modem.gprsDisconnect();
}
M5.update();
}
/* ------------------------------------------------------------*/
void modem_enabler() {
M5.Lcd.setTextColor(WHITE);
MODEM.begin(115200, SERIAL_8N1, 16, 17);
M5.Lcd.fillRect(0, 224, 320, 16, BLACK); /* clean up */
M5.Lcd.setCursor(0, 224);
M5.Lcd.println(F("modem.restart()"));
CONSOLE.println(F("modem.restart()"));
modem.restart();
M5.Lcd.fillRect(0, 224, 320, 16, BLACK); /* clean up */
M5.Lcd.setCursor(0, 224);
M5.Lcd.println(F("waitForNetwork()"));
CONSOLE.println(F("waitForNetwork()"));
while (!modem.waitForNetwork()) M5.Lcd.print(".");
M5.Lcd.fillRect(0, 224, 320, 16, BLACK); /* clean up */
M5.Lcd.setCursor(0, 224);
M5.Lcd.println(F("gprsConnect(soracom.io)"));
CONSOLE.println(F("gprsConnect(soracom.io)"));
modem.gprsConnect("soracom.io", "sora", "sora");
M5.Lcd.fillRect(0, 224, 320, 16, BLACK); /* clean up */
M5.Lcd.setCursor(0, 224);
M5.Lcd.println(F("isNetworkConnected()"));
CONSOLE.println(F("isNetworkConnected()"));
while (!modem.isNetworkConnected()) M5.Lcd.print(".");
M5.Lcd.fillRect(0, 224, 320, 16, BLACK); /* clean up */
M5.Lcd.setCursor(0, 224);
M5.Lcd.print(F("MyIP:"));
CONSOLE.print(F("MyIP: "));
IPAddress ipaddr = modem.localIP();
M5.Lcd.println(ipaddr);
CONSOLE.println(ipaddr);
}
char _buf[32*1024] = {0}; /* store for JPG image data */
void get_and_render_rain_radar() {
/* connect to host */
const char *host = "beam.soracom.io";
const int port = 8888;
int timeout_s = 60;
CONSOLE.print("Connecting:");
CONSOLE.println(host);
if (!ctx.connect(host, port, timeout_s)) {
CONSOLE.println("fail");
return;
}
/* send request */
const char *path = "GET /rainradar HTTP/1.0";
CONSOLE.println(path);
ctx.println(path);
ctx.print("Host: ");
ctx.println(host);
ctx.println();
Serial.println("sent.");
/* receive response */
while (ctx.connected()) {
String line = ctx.readStringUntil('\n');
CONSOLE.println(line);
if (line == "\n") {
CONSOLE.println("headers received.");
break;
}
}
ctx.readBytes(_buf, sizeof(_buf)); /* body */
ctx.stop();
/* rendering */
size_t _buf_s = strlen(_buf);
CONSOLE.println(_buf_s);
CONSOLE.println(_buf);
M5.Lcd.drawJpg((const unsigned char *)_buf, _buf_s, 0, 16);
_buf[0] = '\0'; /* cleanup */
CONSOLE.println("done.");
}
void print_top() {
M5.Lcd.fillRect(0, 0, 320, 16, getColor(51, 244, 204));
M5.Lcd.setTextColor(BLACK);
M5.Lcd.setCursor(0, 0);
M5.Lcd.print(F("RainRadar by YOLP"));
}
void print_bottom() {
M5.Lcd.fillRect(0, 224, 320, 16, BLACK); /* clean up */
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setCursor(0, 224);
M5.Lcd.print(F("Btn => Retreve RainRadar"));
}
uint16_t getColor(uint8_t red, uint8_t green, uint8_t blue) {
return ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3);
}
おわりに
M5StackとM5Stack用3G拡張ボードによる「雨雲レーダー表示デバイス」の製作を通じて、セルラー通信の活用方法を紹介しました。
一見すると同じことはタブレットやスマートデバイスでも実現できます。しかしながら機能を「特化」することでだれでも使うことができるシンプルな製品に仕上げることができ、そこへ「セルラー通信」を加えることでいつでも・どこでもという特徴を製品に与えることが可能となります。このような「特化&セルラー通信」でいつでも・どこでも・だれでも使えるようにした製品の例が、ソースネクスト社の「POCKETALK」というクラウド型翻訳機や、SORACOMのボタン型デバイス「SORACOM LTE-M Button」です。
こうした製品をいきなり作るのではなく、M5StackとM5Stack用3G拡張ボードを利用することでプロトタイピングを素早く行って顧客フィードバックを得るといった「アジャイル開発」を適用していくことが、これからのハードウェア開発に必要とされます。
その際に本稿の後半で紹介した、SORACOM BeamをはじめするSORACOMのさまざまなサービスを活用することで、機能変更の手間やセキュリティリスクの低減、通信量の削減といった「IoTを本格的に、継続的に進めるためのメリット」を感じていただけたら幸いです。
