Microsoft Azure 活用事例

IoT Hub、Stream Analytics、Power BIを用いた獣害対策ソリューション

  • 作成日:2017年3月
  • 作成者:株式会社ナチュラルスタイル
  • 協 力:PCN勝山クラブ 谷川一男さま

1. 課題

福井県勝山市内の山中に設置されている約60台の獣害対策用の箱罠(イノシシ用)の動作状況を把握したい。
具体的には、

2. ソリューションの概要

「IchigoJam」と「さくらのIoT Platform β」を使用して通信機能を持つ箱罠を作成し、「Microsoft Azure」に箱罠の動作イベントを送信します。
「Microsoft Azure」からはレポート生成ツール「Power BI」への連携やメール送信を行ないます。
また、「Table Storage」にログを保存します。

3. 「IchigoJam」と「さくらの通信モジュール(LTE)-β版」を使った箱罠

箱罠に「IchigoJam」と接続された距離センサーとソレノイドを設置し、害獣が箱罠に入ったとき、とびらを閉じるとともに「さくらの通信モジュール(LTE)-β版」を経由して「さくらのIoT Platform β」とその先の「Microsoft Azure」に動作イベントを通知します。
「IchigoJam」と「さくらの通信モジュール(LTE)-β版」は、信号レベルの変換(3.3V <->1.8V)を施した上でI2Cで通信します。
IchigoJam BASIC の POKE を使用して、メモリ上に「さくらの通信モジュール(LTE)-β版」の仕様に則ったバイト列を作り、I2CWで送信します。


箱罠

箱罠制御装置

IchigoJam

IchigoJam BASICのソースコード(箱罠ID:1011の場合)

1 'INOSHISHI VER:1.2
8 C=0:N=0
9 OUT 4,-1
10 V=(ANA(8)*19/12)+70
11 D=1024-ANA(2)+C
15 ?V;"   ";D
20 IF D<V N=N+1 ELSE N=0
25 IF 3<=N OUT1:WAIT30:OUT0:LED1:GOSUB10010:END
30 GOTO10

5 I=1011
6 H=#00

10010 E=#21: S=#0A: T=#49
10020 L = I % #100
10030 M = I / #100
10040 P= E ^ S ^ H ^ T ^ L ^ M
10050 POKE #700,E,S,H,T,L,M,#00,#00,#00,#00,#00,#00,P
10060 R=I2CW(#4F,#700,1,#701,12)
10070 RETURN
		  

4. Microsoft Azure と Power BI の活用

箱罠から送信された動作イベントは、「IoT Hub」に格納されます。

「Stream Analytics」に設定したクエリがこの「IoT Hub」を常に監視しており、吸い上げた動作イベントをレポート生成ツール「Power BI」と、管理者にメールを送信するために設置した「Event Hub」に転送します。

また、「Function App」で作成したアプリケーションが「Event Hub」を常に監視しており、吸い上げた動作イベントからメールを作成し、「SendGrid」に渡すことでメールが送信されます。

HTTPとIoT HubのGatewayとなる Function App

#r "Newtonsoft.Json"

using System.Net;
using Microsoft.Azure.Devices.Client;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text;

public static async Task Run(HttpRequestMessage req, TraceWriter log)
{
    string postData = await req.Content.ReadAsStringAsync();
    dynamic json = JsonConvert.DeserializeObject<dynamic>(postData);

    string deviceId;
    try {
        deviceId = json.payload.channels[0].value;        
    }catch(Exception ex){
        // device status message
        // do nothing
        return;
    }

    var connStr = Environment.GetEnvironmentVariable("IOTHUB_BROWSER_CONNSTR");
    var manager = Microsoft.Azure.Devices.RegistryManager.CreateFromConnectionString(connStr);
    var device = await manager.GetDeviceAsync(deviceId);
    if (device != null)
    {
        var twin = await manager.GetTwinAsync(deviceId);

        double la = 0.0;
        double lo = 0.0;
        if(twin.Tags.Contains("naturalstyle"))
        {
            dynamic props = twin.Tags["naturalstyle"];
            la = props.location.latitude;
            lo = props.location.longitude;
        }
        log.Info($"la:{la},lo:{lo}");

        var ns = new {
            location = new {
                latitude = la,
                longitude = lo
            }
        };
        json.naturalstyle = JToken.FromObject(ns);

        string uri = Environment.GetEnvironmentVariable("IOTHUB_URI");
        string deviceConnStr = "HostName=" + uri
                             + ";DeviceId=" + deviceId
                             + ";SharedAccessKey=" + device.Authentication.SymmetricKey.PrimaryKey;
        var client = DeviceClient.CreateFromConnectionString(deviceConnStr);
        await client.OpenAsync();
        await client.SendEventAsync(new Message(Encoding.UTF8.GetBytes(json.ToString())));
        await client.CloseAsync();
    }
}
  

Stream Analytics に設定したクエリ

WITH
temp AS (
    SELECT
     GetRecordPropertyValue(GetArrayElement(payload.channels,0),'value') AS cageid,
     DATEADD(hour,9,GetRecordPropertyValue(GetArrayElement(payload.channels,0),'datetime')) AS eventdt,
     naturalstyle.location.latitude AS latitude,
     naturalstyle.location.longitude AS longitude
    FROM fromIotHub
),
events AS (
    SELECT
        cageid,
        eventdt,
        latitude,
        longitude,
        DATETIMEFROMPARTS(YEAR(eventdt),MONTH(eventdt),DAY(eventdt),0,0,0,0) AS eventdate,
        DATEPART(hour,eventdt) AS eventhour
    FROM temp
)
SELECT * INTO toPowerBI FROM events
SELECT * INTO toSendGrid FROM events
SELECT * INTO toTableStorage FROM events

Event Hub を監視して SendGrid にメールを送る Function App

#r "Newtonsoft.Json"
#r "SendGrid"

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SendGrid.Helpers.Mail;

const string mailTo = "admin@example.com";

public static Mail Run(string message, TraceWriter log)
{
    log.Info($"message: {message}");
    
    dynamic json = JsonConvert.DeserializeObject<object>(message);

    // JSONから必要なパラメータを取り出す
    int numb = json.cageid;
    DateTime ltime = json.eventdt.ToObject<DateTime>();
    string time = ltime.ToString("yyyy/MM/dd HH:mm:ss"); // 日本風フォーマット
    double la = json.latitude;
    double lo = json.longitude;

    // メール作成
    string subject = $"[箱罠監視] 箱罠:{numb} が閉じました!";
    string mapurl  = "http://maps.google.com/maps";
    string body    = $"箱罠が閉じました!\n箱罠番号: {numb}\n日時: {time}\n場所: {mapurl}?q={la},{lo}";

    log.Info(subject);
    log.Info(body);

    var mail = new Mail(null, subject, new Email(mailTo), new Content("text/plain", body));
    mail.TrackingSettings = NewTrackingSettings();
    return mail;
}

public static TrackingSettings NewTrackingSettings()
{
  TrackingSettings trackingSettings = new TrackingSettings();
  ClickTracking clickTracking = new ClickTracking();
  clickTracking.Enable = false;
  clickTracking.EnableText = false;
  trackingSettings.ClickTracking = clickTracking;
  return trackingSettings;
}

Power BIで生成した 動作タイムライン

Power BIで生成した 箱罠別動作件数グラフ

Power BIで生成した 時間帯別動作件数グラフ

Power BIで生成した 地理別動作件数グラフ

5. まとめと今後の課題

まとめ

今後の課題

  • 山中では電源が限られているため、「IchigoJam」や「さくらの通信モジュール(LTE)-β版」の電源を必要な時だけONにする工夫が必要。
  • 誤動作を防止するため、距離センサーの指向性を高める工夫が必要。

▲ PageTop