sakura.io + Arduino + GROVE でGPSロガー (6)

さくらインターネットsakura.io を使って電子工作初心者がIoTなGPSロガーを作るメモ。その6
[その1] [その2] [その3] [その4] [その5] [その6]


仕様

前回「Outgoing Webhook」を使いPHPで受信したデータをファイル毎に完全保存する方法を書きました。

今回はもっとシンプルに直近の10,000件の座標データを1つのファイルに書き込み、GoogleMapにリアルタイムに表示するシステムを作ってみます。


「Outgoing Webhook」をPHPで受信する

index.php の内容

<?php

//---PHP設定用のファイル
require_once("./config.inc.php");

//---Secret(コンパネに入力した文字列)
$secret = 'hoge';

//---POSTデータを受信する
$json = file_get_contents('php://input');

//---JSONをPHP連想配列に変換
$json_ary = json_decode($json , true);

//---すべてのレスポンスヘッダを取得(Apacheのみ)
$header_ary = getallheaders();

//---------------------------------------------
//   +JsonデータとSecretとヘッダーの署名が必須
//---------------------------------------------

if($json && $secret && !empty($header_ary['X-Sakura-Signature'])){

    //---受信した署名と比較
    if($header_ary['X-Sakura-Signature'] === hash_hmac('sha1', $json, $secret)){

        //---座標データをファイルに追記する
        dataAryAdd($json_ary);
    }
}

///=======================================================================
//   +座標データをファイルに追記する
///=======================================================================
function dataAryAdd($p_json_ary){

    $path = "../../_data/simple_gps/last_location.psrv";

    $index = 0;
    $data_ary = array();

    //---必要なデータが含まれていたら
    if(!empty($p_json_ary['payload']['channels'][0])){

        //---既存のファイルがある場合には読み込み
        if(is_file($path)){

            $data_ary = unserialize(file_get_contents($path));
        }

        //---配列を展開する
        foreach ($p_json_ary['payload']['channels'] as $value_ary){

            $channel = $value_ary['channel'];
            $value = $value_ary['value'];

            //---datetimeを変換する
            $time = datetimeToTime($value_ary['datetime']);

            //---緯度
            if($channel == 0){

                //---channel[0]の時間をキーとする。
                $index = $time;

                $data_ary[$index]['lat'] = $value;

                //---経度
            }elseif($channel == 1){

                $data_ary[$index]['lng'] = $value;
            }

            //---最大10000件を超えたら古いデータを削除する
            if(count($data_ary) > 10000){

                $keys = array_keys($data_ary);

                unset($data_ary[$keys[0]]);
            }
        }

        //---ファイルに書き込む
        file_put_contents($path, serialize($data_ary));
    }
}

///=======================================================================
//   +datetimeをUNIXタイムスタンプに変換する
///=======================================================================
function datetimeToTime($p_datetime){

    $result = 0;

    //---マイクロタイム7桁以上でエラーとなるので無視する
    $p_datetime = mb_ereg_replace(".[0-9]{1,}Z$" , ".000Z" , $p_datetime);

    $d = new DateTime($p_datetime);
    $d->setTimeZone(new DateTimeZone('Asia/Tokyo'));

    $result = $d->getTimestamp();

    //---------------------
    //   +返り値
    //---------------------

    return $result;
}

?>

※config.inc.phpは各自お好きな内容を記載してください。

config.inc.php の内容

<?php

//---タイムゾーン
date_default_timezone_set('Asia/Tokyo');

//---文字コード
ini_set('default_charset', 'UTF-8');

?>

※手元の環境ではdatetimeをUNIXタイムスタンプに変換する際にマイクロタイム7桁以上でエラーとなるので無視するようにしています。PHPのバージョンの問題なのかもしれません。


GoogleMap APIで表示する

index.phpで出力したlast_location.psrvを読み込んでGoogleMap APIを使い地図上に表示します。
$apikey = xxxxxxxxxxxxxxxxxxxxxxxx;の部分は各自で取得したKEYに書き換えてください。

location.php の内容

<?php

//---PHP設定用のファイル
require_once("./config.inc.php");

//---GoogleMapのAPIキー
$apikey = 'xxxxxxxxxxxxxxxxxxxxxxxx';

$path = "../../_data/simple_gps/last_location.psrv";

//---変数の初期化
$last_lat = $last_lng = $last_time = 0;
$list_html = $lastupdate = "";

//---ファイルがあれば読み込む
if(is_file($path)){

    $data_ary = unserialize(file_get_contents($path));

    $list_ary = array();

    foreach ($data_ary as $key_time => $value_ary){

        $last_time = $key_time;
        $lat = $last_lat = $value_ary['lat'];
        $lng = $last_lng = $value_ary['lng'];

        $list = "new google.maps.LatLng({$lat}, {$lng})";
        $list_ary[] = $list;
    }

    $list_html = implode(",ntt" , $list_ary);

    //---最終更新日
    $lastupdate = date("Y/m/d H:i:s" , $last_time);
}

//---自動リロード用の記述
if(!empty($_GET['reload']) && $_GET['reload'] === 'on'){

    $reload_html = '<meta http-equiv="refresh" content="20">';

} else{

    $reload_html = '';
}


///=======================================================================
//   +HTML表示部分
///=======================================================================
echo <<<HERE

<html lang="ja">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
{$reload_html}

<meta name="format-detection" content="telephone=no">
<meta name="viewport" content="width=700">

<title>Simple GPS</title>

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true&key={$apikey}"></script>
<script type="text/javascript">

function initialize() {

    //---地図の中心位置を指定
    var map = new google.maps.LatLng({$last_lat}, {$last_lng});

    //---地図オプション
    var map_option = {
        zoom: 16,
        center: map,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };

    //---地図の生成
    var map = new google.maps.Map(document.getElementById("map_canvas"),map_option);

    //---座標一覧
    var lists = [
        {$list_html}
    ];

    //---ポリラインオプション
    var line_option = {
        path: lists,
        strokeColor: "#FF0000",
        strokeOpacity: 1.0,
        strokeWeight: 5
    };  

    //---ポリラインの生成
    var paths = new google.maps.Polyline(line_option);

    //---地図にポリラインをセット
    paths.setMap(map);
}


</script>

<style type="text/css">
<!--

/*************************
   +HTML 
*************************/

html {  
    overflow-y:scroll;  
}  

body {
    font-family:HelveticaNeue;
    margin: 0;
    padding: 0;
    background: #FFB845;
    color: #000000; 
}

#map_canvas {
    width: 100%;
    height: 90%;
}

-->
</style>

</head>
<body onload="initialize();">

<div id="map_canvas">
     
</div>

<div style="margin:10px;">
最終更新:{$lastupdate} [<a href="./location.php?reload=off">自動更新停止</a>] [<a href="./location.php?reload=on">再開</a>]
</div>

</body>
</html>

HERE;


?>

動作確認

location.phpをブラウザから確認してみます。

データ保存数が10,000件なので数百キロ分の軌跡が表示されます。
一筆書きになってしまうので表示を工夫して日付毎に表示を切り替えたり線の色を変えたりした方が良さそうです。

最後の位置情報だけが必要であれば1件のデータだけ保存しておけばよいですね。

GPSの精度はなかなか良い感じで瀬戸大橋を渡って与島パーキングに駐車した事もわかります。

空港に車を置いて旅行に行っても旅先からスマホを見ると自分の車がちゃんと駐車されているのがわかります!

モジュール毎に処理を分ければ何台増えても対応出来ますよね。


クラウドGPSロガーまとめ

6回にわたって長々と書いてきましたがこれにて完結です。

GPSモジュールで受信したデータをsakura.ioを使って簡単にサーバーに受け渡すことが出来ました。
考えてみてください。これってパトカーやらタクシーに着いている位置情報のシステムと同じですよね?

100台の車の位置情報を管理するシステムがあるとして、sakura.ioが無い時代にこのシステムを作るといくらだろう?
通信費の原価を考えても1台あたりの月額1万円、1台あたりの機材費で初期費用で10万円、システムの構築費で300万ってところか。(※ものすごーく適当な見積もりです)

sakura.ioならネットで部品注文して(1台あたり約2.2万円)ハンダ付けもせずにパチっとはめ込むだけで簡単に組み立て出来てネットで拾ってきたサンプルコードをつかったら簡単に同じようなものが作れるよ。
100台で月額6,000円だよ。なにこれスゴイ。

…と感動したので、自作したプログラムも全部公開してみました。
今後もsakura.ioを使ったおもしろいシステムを開発していきます!