温度や明るさ,ドアが開いた回数,人が通った回数などのデータをセンサーで取得し,モノの状態をリアルタイムにインターネットを通じてどこからでも知ることができるようにすることは,IoTの代表的な例だといえます.
これをobnizを使えば,環境構築不要,ハードウェアの配線も一瞬,ソフトウェアもほぼコピペで,簡単に実現できるので,その方法をまとめました.
この開発手法のメリット
後に説明するように,今回はobnizとGoogle Apps Script, Googleスプレッドシートを使って開発しました.これにより,
- 環境構築不要…デバイスもサーバー側のプログラムも,全てブラウザ上で開発可能
- ハードウェアの配線が一瞬…obnizはセンサーやモーターなど様々なペリフェラルを直接接続できる
- デバイスのプログラムがシンプル & ほぼコピペ…obniz公式に多様なパーツのライブラリが用意されている
- デバイスへのプログラム書き込み不要,無線で即動作開始…obnizはクラウドを介して操作できる
- サーバー側のプログラムもほぼコピペ…GoogleスプレッドシートをIoT向けロガーとして使えるよう汎用なGASプログラムを掲載
- リアルタイムにデータ閲覧可能…Googleスプレッドシートにアクセスすればどこからでも最新のデータやグラフが見られる
- 更にPythonでのデータ解析もシームレスに実行可能…Google Colabでスプレッドシートの最新データをインポートできるのでいちいちCSVにしてダウンロードする必要もない
といったメリットがあります!!
obnizとは
obniz(オブナイズ)は日本のCambrianRobotics社が開発したマイコンボードで,Wi-Fiに接続してインターネット経由で操作します.JavaScriptやPython, ブロックプログラミングで記述することができます.
スマートフォンやPCのブラウザ上でJavaScriptから操作できるので,スマートフォンの加速度センサやカメラと組み合せることもできますし,TensorFlow.jsを使った機械学習とも相性が良いです.
さらに「最大1Aまで利用でき,5v/3v切り替えやオープンドレインも利用できる12のIO」が搭載されているため,モータードライバ不要で,ハードウェアは非常にシンプルに,例えば多機能なラジコンを作ることもできます.
Google Apps Scriptとは
Google Apps Scriptとは,クラウドで動き,Gmail, カレンダー,ドライブなどGoogleの様々なサービスと連携できる,JavaScriptで開発可能な,Googleの迅速なアプリケーション開発プラットフォームです.インストール不要ですぐに使い始めることができます.略してGAS(ガス)と呼ばれます.
構成
obniz側とGAS側の2つのプログラムを書きます.
obnizでセンサー値を取得すると,GASで作成したウェブアプリケーションにその値がPOSTされ,Googleスプレッドシートの最下行に追記されるようにします.
実際には,スマートフォンやPCなどからobniz Cloudに接続し,クラウド上のAPIを操作することで,obnizを操作できます.またはobniz Cloudのサーバーレスイベント機能を利用することで,Webhookをきっかけとしたり,定期的に実行することもできます.
obniz側のプログラムでは,
- obnizでセンサー値を取得
- センサー値をGAS側ウェブアプリケーションにPOST
します.
GAS側のプログラムでは,
- POSTされたデータをスプレッドシートに追記
- GETされたら溜まったデータを返す
ようにします.
これにより,obnizで取得したセンサー値をスプレッドシートに表として保存することができ,リアルタイムに最新のデータを閲覧したり,グラフ化したり,CSVでダウンロードしたりと汎用できます.
サーバー側の準備
Googleドライブを開き,センサーデータを保存したい場所に新しくGoogleスプレッドシートを作ります.
セルA1にuuid
, B1にdate
と入力し,その右には送信予定の各データのラベルを付けてください.このラベルは,obniz側のプログラムで送信するオブジェクトのキーと合わせておく必要があります.また,シート名はlog
としました.
次に,GASのスクリプトを作成します.「ツール」→「スクリプトエディタ」を開きます.
そして既存のコードを,以下のコードで上書きしてください.
このプログラムは,Google SpreadSheet にデータを追加、更新、削除する Web API をサクッと作るを参考にして一部改変しました.
function createRow(sheet, parameter) {
var keys = sheet.getDataRange().getValues()[0];
var row = [];
keys.map(function(key) {
var value = parameter[key];
if (value) {
row.push(value);
}else{
row.push(null);
}
});
return row;
}
function appendRow(sheet, parameter) {
parameter['uuid'] = Utilities.getUuid();
parameter['date'] = new Date();
var row = createRow(sheet, parameter);
sheet.appendRow(row);
return parameter;
}
function updateRow(sheet, parameter) {
var row = createRow(sheet, parameter);
var values = sheet.getDataRange().getValues();
for (var i = 0; i < values.length; i++) {
if (values[i][0] === row[0]) {
sheet.getRange(i + 1, 1, 1, values[i].length).setValues([row]);
}
}
}
function deleteRow(sheet, parameter) {
var row = createRow(sheet, parameter);
var values = sheet.getDataRange().getValues();
for (var i = 0; i < values.length; i++) {
if (values[i][0] === row[0]) {
sheet.deleteRow(i + 1);
}
}
}
function findRow(sheet,val,col){
var dat = sheet.getDataRange().getValues();
for(var i=1;i<dat.length;i++){
if(dat[i][col-1] === val){
return i+1;
}
}
return 0;
}
function obj2txtout(obj){
var output = ContentService.createTextOutput();
output.setMimeType(ContentService.MimeType.JSON);
output.setContent(JSON.stringify(obj));
return output;
}
function doPost(e) {
var SHEET_ID = PropertiesService.getScriptProperties().getProperty("SHEET_ID");
var ss = SpreadsheetApp.openById(SHEET_ID);
if (e.parameter.sheet) {
var sheet = ss.getSheetByName(e.parameter.sheet);
}else{
var sheet = ss.getSheetByName("シート1");
}
if (e.parameter.action) {
var action = e.parameter.action.toLowerCase();
if (action === 'update') {
// updateRow(sheet, e.parameter);
} else if (action === 'delete') {
// deleteRow(sheet, e.parameter);
}
} else {
var param=appendRow(sheet, e.parameter);
return obj2txtout({ message: "success!", });
}
}
function doGet(e){
var SHEET_ID = PropertiesService.getScriptProperties().getProperty("SHEET_ID");
var ss = SpreadsheetApp.openById(SHEET_ID);
if (e.parameter.sheet) {
var sheet = ss.getSheetByName(e.parameter.sheet);
}else{
var sheet = ss.getSheetByName("シート1");
}
var read_enabled_sheet_names=["log","シート1"];
if(read_enabled_sheet_names.indexOf(sheet.getName()) == -1){
return obj2txtout({message: "failed. Do not have read permission.",});
}
return obj2txtout({message: "success!", data: sheet.getDataRange().getValues(), });
}
「スクリプトのプロパティ」に,プロパティにはSHEET_ID
, 値には,スプレッドシートの編集画面のURLhttps://docs.google.com/spreadsheets/d/YOUR-SPREADSHEET-ID/edit#gid=0
のYOUR-SPREADSHEET-ID
に当たる部分の文字列を指定してください.
保存したら,「公開」→「ウェブアプリケーションとして導入…」から,「プロジェクトバージョン」はNew,「アプリケーションにアクセスできるユーザー」は全員(匿名ユーザーを含む)を選択して更新してください.
obniz側のプログラムを作る
obniz SDKパーツライブラリにあるパーツであれば,ほぼコピペで完成します.今回は,LM60という温度センサがあったのでそれを使いました.
obniz公式サイトにアクセスし,右上の「開発者コンソール」で,左側のメニューから「リポジトリ」を選択します.右上の「新規作成」を押して適当なファイル名を付けて「作成」すると,エディタ画面に移ります.
<body>
~</body>
の中身を,以下のコードに書き換えます.
パーツライブラリのサンプルをコピペしてコードを作成しました.
<div id="obniz-debug"></div>
<h1>obniz Sensor Logger</h1>
<button id="post">送信</button>
<button id="get">取得</button>
<script>
var obniz = new Obniz("XXXX-XXXX");//obnizのIDを指定してください
const url="";//GASのウェブアプリケーションのURLを指定してください
var sensor;
obniz.onconnect = async function () {
sensor = obniz.wired("LM60", { gnd:0 , output:1, vcc:2});
var temp = await sensor.getWait();
console.log(temp);
var param={sheet: "log", obniz_id: obniz.id, temperature: temp };
console.log(param);
$.post(url, param)
.done(function (data) {console.dir(data);});
}
$("#post").click(async function(e) {
var temp = await sensor.getWait();
$.post(url, {sheet: "log", obniz_id: obniz.id, temperature: temp })
.done(function (data) {console.dir(data);});
});
$("#get").click(function(e) {
$.get(url,{sheet: "log"},function(data){console.dir(data);});
});
</script>
この中で書き換える場所は,まずnew Obniz("XXXX-XXXX");
にobnizのIDを指定してください.そしてconst url="";
には,GASの「現在のウェブアプリケーションのURL」を貼り付けてください.
また使用するセンサが異なる場合,そのパーツがobniz SDKパーツライブラリにあればそのサンプルを参考に書き換える必要があります.サンプルがない場合でも,AD変換,I2CやSPI,シリアル通信のサンプルはあるので,それを参考にプログラムを書けばセンサを扱うことができます.
また今回は送信するデータのキーをtemperature
としていますが,送信するデータに合わせて,変更,また追加してください.
ハードウェアの配線
使用するセンサとプログラムに合わせて,配線してください.といってもLM60 - obnizパーツライブラリの通りにLM60を表向きにobnizに挿すだけでした.
そしてUSB電源に繋ぐと画面が点くので,表示にしたがってWi-Fiに接続します.既に接続されていれば,プログラム実行の待機中QRコードが表示されます.
写真はプログラム実行中の様子です.
実行
obnizのエディタ画面右上の実行ボタンを押すと,以下のように表示されます.
今回載せたobniz側プログラムでは,画面を開いたときに1回センサー値を取得して送信し,また「送信」ボタンを押したときにもセンサー値を取得して送信します.また「取得」ボタンを押すと,スプレッドシートに溜まったデータを全て取得することができます.ブラウザのコンソールに出力しているため,画面に表示はされません.
この画面を開いた時点で,スプレッドシートを見てみると,ちゃんとデータが記録されています!
obniz側の実行画面を再読み込みすることで再実行したり,「送信」ボタンを押すことで,スプレッドシートにさらに追記されていくのを見ることができます.
さらに,date
の列とtemperature
の列を選択し,「挿入」→「グラフ」から折れ線グラフを挿入するだけで,図のように温度変化を可視化することができました!
定期的に実行する
obnizの開発コンソール左側のメニューから「イベント」を選び,「新規作成」します.名前を入力し,「実行するWebApp」として先ほど作成したプログラムを選びます.「きっかけ」として「繰り返し」を選び,実行間隔を入力します.
これでもう,いちいちobnizの実行画面を開いたり,送信ボタンを押す必要さえありません!
センサーが感知した時刻を記録する
ドアの開閉時刻と回数とか,人の通過人数などを記録したい場合があると思います.
例えばPIRセンサーのHC-SR505では,赤外線で人や動物が近くにいるかを検出できます.
HC-SR505 - obnizパーツライブラリを使えば,onchange
にセンサーデータをPOST
する関数を入れることで,センサーの値が変化したときにデータを送信することができます.今回の例では$.post()
が,jQueryによるPOST
する関数でした.
集まったデータを解析する
Google Colabを使えば,環境構築不要で,ブラウザ上でPython, Jupyter Notebookのプログラムを編集・実行することができます.Pythonにはデータ解析やAIのライブラリが豊富であり,Google ColabではGoogleスプレッドシートのデータを読み込むことも容易にできるので,今回溜まったセンサーデータの解析にも非常に適しています.
以下のようなプログラムで,集まったデータをPandas DataFrameの形に収めることができます.
!pip install --upgrade --quiet gspread
from google.colab import auth
auth.authenticate_user()
import gspread
from oauth2client.client import GoogleCredentials
gc = gspread.authorize(GoogleCredentials.get_application_default())
import pandas as pd
workbook = gc.open_by_key('YOUR-SHEET-ID')
worksheet = workbook.worksheet('log')
data = worksheet.get_all_values()
df = pd.DataFrame.from_records(data)
備考
キーにハイフンを入れないほうがいい
jsonのキーにハイフンをいれないほうがいいの記事に書いてあるとおり,obnizからPOSTするオブジェクトのキーにはハイフンを入れないほうが良さそうでした.
スプレッドシートに溜まったセンサーデータをGETして解析するプログラムを作成する場合,JavaScriptでの取得時に戸惑う可能性があります.
API呼び出し制限
- obniz Cloudのサーバーレスイベント機能: 120 run/day, 1 runあたり30秒以内
- GAS: Triggers total runtime 90min/day
というAPI呼び出し制限があります.
より高頻度でデータを保存したい場合は,obniz側のプログラムでsetInterval
を用いて書き,node.js等で常に実行しておくという方法があります.しかし,GASの利用制限も超えてしまう場合は,Googleスプレッドシートを使うことはできないので,他のIoT向けサービスを検討する必要があります.IoTに特化したプラットフォームとして,Google Cloud IoT, Microsoft Azure IoT, AWS IoT等があり,IoT向けの通信にはMQTTという通信方式が使われます.
最後に
obnizとGoogle Apps Script, Googleスプレッドシートを使って,環境構築不要,ほぼコピペでできるIoT遠隔センサーロガーを紹介しました.
集まったデータの解析もGoogle Colabを使えば,すぐにPythonで解析できます.
obnizがあれば,個人でも非常に簡単にいろんなものをIoT化して,さらにはデータ解析など好きなように応用することができそうです!これを応用していろんなデータを取得して解析してみたいと思います.