• IoT

娘に渡すIoTデバイス(Wio LTE+GPSセンサー)

図4

技術開発部の原田です。普段は業務ハッカーへの道を記載しています。
今回は趣向を変えて原田の自宅で構築した、娘に渡すIoTデバイスについて記載します。

TL;DR ※まとめです

  • SIMカードを挿せるIoTデバイス(Wio LTE)を娘に渡して、GPSセンサーで娘の居場所を確認した。
  • Firebase (Realtime Database)はやっぱり便利。Pub/Sub形式のデータブローカーとして使いやすかった。
  • 追加開発でGroveコネクターのボタンをつけて、娘から「帰るよ」という連絡を可能にした。

はじめに

唐突ですが、私の娘は2018年現在小学1年生です。
ある時私は、娘の居場所を把握するためのシステムが欲しいと考えました。

きっかけは、娘が友達とだけで近所の公園に遊びに行きたいと言い出したことです。
※ 親(とくにパパ)には付いてきてほしくないとのこと。これは悲しい!w

ともあれ、友達とだけで行くのは居場所もわからないためNG。
(娘はUbuntu PCはあれどスマートフォンやタブレットを持ってない。他の子供が欲しがるからスマートフォンはNG)
少なくとも外出先の居場所が把握出来たらな、ということで機材を用意し、仕組みを作りました。

用意した機材

  • Wio LTE JP Version(赤色の基板)
  • Soracom SIM(赤色の基板の裏に設置済み)
  • Grove GPSモジュール(中央の緑色ランプが点灯しているもの)
  • モバイルバッテリー(白色のもの。Anker製。)

IMG_20180711_165848

また、以前Firebaseを絡めた、任意のタイミングで任意の発言をGoogle Homeにさせる
という仕組みを組んでいたため、それも一部流用しました。

既存の機材

  • Google Home mini
  • Raspberry pi zero W (Node.jsがFirebaseの変化を検知している)

左:ラズパイとセンサー、右GoogleHome

娘の持つIoTデバイスの位置が変わったら、「娘の現在地が変わりましたよ!」とGoogle Homeが喋ってくれる形になります。

構成図

早速ですが、構成図です。
娘に渡すデバイスが、クラウド上のデータベースに位置情報を書き込んでいます。デバイスは5分おきにデータベースに書き込んでいます。

図3

娘にはWio LTEというArduino互換デバイスを渡します。
このデバイスにはSIMカードが挿せるため、SIMカードを挿すだけで携帯回線を使用可能です。これで外出先でも通信が可能になります。
また、Wio LTEにはGroveという形式のコネクタが6つ用意されています。
このコネクタにGPSセンサーを挿し込むことで、GPS機能を使用可能にします。
また、定時タイミングで位置情報を取得して、Firebase (Realtime Database)に向けてHTTPS/POSTで送信しています。
Firebaseに書き込むためにCloud Functionsを一部使っていますが、非常に細かい話になるのでこちらは補足で。
位置情報の値が変化したときのみ、Node.jsが反応し、娘の位置情報をGoogle Homeが話してくれます。
Node.jsが反応して~Google Homeが話してくれる、まではこちらの記事へどうぞ。(記載省略)

ここでは主にIoTデバイスの中でのソースコードを解説します。

解説

Wio LTEでは、通常のArduinoと同様にC++で開発が可能です。
ソースはこちらです。
※ GPSデータを読みやすくするため、MicroNMEAライブラリを用いています。

#include <MicroNMEA.h>
#include <WioLTEforArduino.h>
 
#define APN               "xxxxxx.jp"
#define USERNAME          "xxxx"
#define PASSWORD          "xxxx"
#define WEBHOOK_URL       "https://xxxxxxxxxxxxxxxxxxx.cloudfunctions.net/gpsDataListener"
#define NG_INTERVAL       1000
#define OK_INTERVAL       300000
#define RED               200,50,50
#define GREEN             50,200,50
 
WioLTE Wio;
HardwareSerial& gps = Serial;
char nmeaBuffer[100];
MicroNMEA nmea(nmeaBuffer, sizeof(nmeaBuffer));
bool ledState = LOW;
 
// Set LED OK:GREEN, NG:RED
void setLED(bool state)
{
  if(state){
    Wio.LedSetRGB(GREEN);
  } else {
    Wio.LedSetRGB(RED);
  }
}
 
// Reset Hardware
void gpsHardwareReset()
{
  // Skip Data.
  while (gps.available()){
    gps.read();
  }
 
  // Reset Grove Power
  Wio.PowerSupplyGrove(false);
  delay(500);
  Wio.PowerSupplyGrove(true);
 
  while (1) {
    while (gps.available()) {
      char c = gps.read();
      if (nmea.process(c)){
        return;
      }
    }
  }
}
 
void setup()
{
  SerialUSB.println("Settings start.");
  gps.begin(9600);
 
  Wio.Init();
  Wio.PowerSupplyGrove(true);
  Wio.PowerSupplyLTE(true);
  Wio.TurnOnOrReset();
  Wio.Activate(APN, USERNAME, PASSWORD);
 
  setLED(ledState);
  gpsHardwareReset();
 
  MicroNMEA::sendSentence(gps, "$PORZB");
  MicroNMEA::sendSentence(gps, "$PORZB,RMC,1,GGA,1");
  MicroNMEA::sendSentence(gps, "$PNVGNME,2,9,1");
  SerialUSB.println("Settings done.");
 
}
 
void loop()
{  
 
  ledState = false;
  if(nmea.isValid()) {
    ledState = true;
 
    // Get GPS Data.
    long latitude_mdeg = nmea.getLatitude();
    long longitude_mdeg = nmea.getLongitude();
    SerialUSB.print("Latitude (deg): ");
    SerialUSB.println((latitude_mdeg / 1000 + 0.5)/1000., 4);
    SerialUSB.print("Longitude (deg): ");
    SerialUSB.println((longitude_mdeg / 1000 + 0.5)/1000., 4);
    nmea.clear();
 
    // Mod Payload Data.
    int status;
    String payloadStr = "{\"result\":{\"latitude\":" + String((latitude_mdeg / 1000 + 0.5)/1000., 4) + ",\"longitude\":" + String((longitude_mdeg / 1000 + 0.5)/1000., 4) + "}}";
    char payload[200];
    strcpy(payload, payloadStr.c_str());
 
    // Send data to Cloud Functions.
    if(!Wio.HttpPost(WEBHOOK_URL,payload,&status)){
      SerialUSB.println("### HTTP POST ERROR! ###");
    }
  }
 
  // Set LED Color
  setLED(ledState);
 
  while (gps.available()) {
    char c = gps.read();
    if(!ledState) SerialUSB.print(c);
    nmea.process(c);
  }
 
  // Wait.
  if(ledState){
    delay(OK_INTERVAL);
  } else {
    delay(NG_INTERVAL);
  }
}

中身は少し長く感じるかもしれませんが、Arduinoのソースコードは非常にシンプルです。
起動時に一回だけ実行されるsetup関数と、その後繰り返し実行されるloop関数しかありません。
APN, USERNAME, PASSWORDについてはSIMの提供会社から知らされます。
WEBHOOK_URLはCloud FunctionsへのURLになります。

GPSモジュールを用いて位置情報を取得した後にFirebase Cloud Functionsに対してHTTPS/POST通信しています。5分おきに動作してほしいため、適宜delayを入れています。
SIMの使用についても、setupのPowerSupplyLTE,TurnOnOrReset,Activate関数を使うだけでセットアップが完了です。
「GPSモジュールで位置情報を取得する」というのは多少手間がかかっていますが、
「SIMを使う」、「HTTPS/POST送信を行う」という操作は、比較的簡単に記載できる印象です。

まとめ

ここではWioLTEという携帯回線を使用可能なArduino互換IoTデバイスを扱い、位置の情報を発信させることを行いました。
WioLTEにはGrove形式のコネクタが用意されていることを冒頭で記載しましたが、その点ハードウェアとしてもよく出来ている印象です。
手っ取り早くセンサーを使ったIoTデバイスを作りたい場合、Groveのコネクタがハードウェア面での苦労を最小になるようにしてくれています。
ソフトウェアの知識さえあれば、電子工作の知識がなくてもセンサーを扱えるのは大きいですね。

Arduinoはオープンソースハードウェアで、基板を誰でも組み立てることができます。
Arduino互換機である8pinoなどに至っては、親指の先程度の大きさしかありません(その分単目的かつ軽量な処理しか行なえませんが)

取り付けるセンサー部分の省スペース化は難しくとも、接続するマイコン部分を小さくすることは可能です。
非常に限られたスペースにおいてセンサーを用いて何かをしたい場合Arduinoは有効な手段となりえます。(用途に応じた最小限の基板を購入するか作れば良い)

省サイズで言えばIoTデバイスの中でArduinoと双璧をなす、Raspberry Piの方も捨てがたいです。
Raspberry Pi Zero W であれば、おおよそ 人差し指+中指の2本分のサイズになります。
センサーを付けてひとまず動作を試したいという場合であれば、Raspberry Piの方もオススメです。
GPIOのピンにセンサーを繋げるだけで扱えます。
(Arduinoは基板種別が多々あることで、情報を見つけることがなかなか難しい印象です。Raspberry Piの方が基板種別が少なく、情報が多く扱いやすい印象。)

基板が指2本分であれば、モバイルバッテリーを合わせてもポケットに収まりますね。

オチ

この後、デバイスを娘に渡して公園に出掛けさせたところ(流石に子供たちだけではなく妻が付いていきましたが笑)、
確かに移動した際に位置情報がFirebaseに書き込まれてGoogle Homeが音声で教えてくれました。

自宅に居るだけで娘の情報が音声でプッシュ通知してくれるようになり、親としては安心感が増しました。
娘がもう少し大きくなったときに、この仕組がしっかり使えるようになると嬉しいですね。

ただし妻に言わせればこれは「ストーカーシステム」らしいです。失礼な!

補足

位置情報はGPSでしか取得できないか?

精緻な位置情報であれば、GPSが必要です。一方で「だいたい」で良いのであれば、SIMカードを挿すだけで十分です。
SIMを用いて通信するだけで、基地局の簡易位置情報を拾えるためです。
500~600m程度の誤差が生じるものの、「だいたい」わかれば良いのであればこれで事足ります。
Wioの標準ライブラリを使えば、Wio.getLocation()だけで基地局の位置が取得可能です。驚きのお手軽さ。

GPSモジュールの注意点は?

ロストのしやすさかと思います。
簡易位置情報と比べて、こちらの場合、少し建物に入っただけでロストしやすいです。
みちびき衛星があっても難しいのか、GPSモジュールの精度のせいなのかは不明です。
参考までに携帯会社はGPSと簡易位置情報を組み合わせて精度の高い位置情報を提供しています。
それこそそのロジックを真似ればよいのですが、そこまで頑張らなくてもいいと判断しました。
ソースコードも込み入るし、C++のソースコード書くのが若干面倒というのもあり・・・
GPSが取得できない場合はそちらで代替することは可能です。

Firebase(Realtime Database)へのHTTPS/POST通信をDBの更新に変換する?

そもそも、FirebaseのRealtime Databaseに対してHTTPS/POST通信を行うと、データが追加されます。更新ではなく、追加です。
その際Firebaseがランダム文字列によるキーを自動生成し、そのキーに対する値がPOST通信の中身(payload)に設定されてしまいます。これは仕様です。(RealtimeDatabaseはKVSキーバリューストア型でデータを保持するデータベースです。)
ここでは追加されてレコードがどんどん増えていくようなことはしたくありませんでした。
データベース側で「娘の位置 : “緯度,経度”」といった具合に、固定のキーに対して値を更新(追加ではなく)していく形を取りたかったのです。
そこで、FirebaseのCloud Functions機能を使い、一旦POSTを受けるFaaSの口(AWSで言うところのLambda)を用意し、そこでデータベースの特定のキーを更新しました。
HTTPS/PUTが出来ればよかったのですが、Wio LTEの標準ライブラリでPUTがなかったためにとった手段でした。
参考までにCloud Functionsのコードを載せます。
※ なお別解として、HTTPS/PUTをすることのできるライブラリをArduino側に用意する、でも良いですね。

const functions = require('firebase-functions');
const admin = require("firebase-admin");
 
admin.initializeApp(functions.config().firebase);
 
// 位置情報を受け取って処理する関数
exports.positionDataListener = functions.https.onRequest((request, response) => {
 
  //リクエスト元からのパラメータ取得
  const latitude  = request.body.result.latitude;
  const longitude = request.body.result.longitude;
 
  //Database更新先と更新内容を指定
  var path = "/daughter/position";
  var gpsValue = longitude + "," + latitude;
 
  //Databaseを更新
  admin.database().ref(path).set(gpsValue);
 
  //リクエスト元へ`speech`と`displayText`の情報を返す
  response.setHeader("Content-Type", "application/json");
  response.send(
    JSON.stringify({
      "speech": " ", "displayText": gpsValue
    })
  );
 
});

デバイス側から「今から帰るよ!」という発信はできる?

追加開発した部分です。できます。
上のGPSモジュールを付属する場所でも少し触れていますが、幸いなことに、Wio LTEにはGrove形式のボタンやセンサーを簡単に取り付けることができます。

IMG_20180711_165558

このようになります。ケーブルをつなげるだけでそのセンサーを扱うことができるため、ハードウェアの電子工作が不要です。
非常に便利ですし、電子工作の知識に乏しい私には嬉しいデバイスでした。
あとは上で述べたArduinoのソフトウェアを改変し、ボタンが押されたらというロジックを追加することで、クラウド側に通知することが可能でした。
Wio LTEには最大6個までGroveモジュールをつけられるため、様々なセンサーを組み合わせれば、できることが広がりそうです。
IoTデバイスが範囲外に出たらGoogle Homeや親の携帯に通知が来たり、IoTデバイス側でブザー音がなるのも有効かと。

関連記事

  1. RFIDタグでホテルのカードキーシステムを再現しよう(RFIDリーダR…

  2. 観葉植物とお話しよう!(Wio NodeとGrove土壌水分センサ)

  3. RPAによるゲームの自動化 ~とあるギルドマスターの挑戦

  4. M5StickC(マイコン)で格ゲーをつくる

  5. SORACOM LTE-M Buttonで飲み会に行くことを妻に通知す…

  6. IoT x 料理(Raspberry Pi+Groveリレー+1wir…

  7. Google Homeの機能紹介 ~ブロードキャスト機能、メモ機能~

  8. Nature Remoを使って憧れのスマートホーム化!

PAGE TOP