本記事はFIXERが提供する「cloud.config Tech Blog」に掲載された「車両の情報をAzureに飛ばしてプチIoT化してみた」を再編集したものです。
お久しぶりです。石川です。ブログ書くのは3ヶ月ぶりくらいでしょうか… 気付けばもう12月も半分終わってしまいました。早すぎる。
はじめに
この記事はFIXER Advent Calendar 2021 14日目の記事です。
久しぶりのブログに何を書こうか考えたんですが、最近戯れているWindows Server絡みの記事かなぁ〜とか思ってたんですがもっと寝かせていた趣味の方に突っ走ろうと思います。
ということで今回は、
車のデータを取得してAzureに送信する
というIoTみたいなことをやってみます。
東京から四日市に移住してそろそろ1年という時、ふと実家で眺めていた車が欲しくなり、車を買っていました。
車というのは実は大量のセンサーとそれらが生み出すデータで溢れています。それらを制御/処理するため、モノによってはその辺りに転がっているようなパソコンよりも高性能なシステムを積んでいたりもします。自動運転などの技術もそういった技術を突き詰めていた結果できているのかなぁとか思ったり。
まずは前提となる技術を軽く説明しましょう。
OBD(On Board Diagnosis)
自動車にはOn Board Diagnosisと呼ばれる自己診断機能が組み込まれています。エンジンチェックランプとかが点灯するアレです。あれは車がエンジン回りでエラーが起きたと自己診断を行いメーターパネルへの表示を行なっています。
歴史など詳しいことはWikipediaあたりを調べてみてください。
運転席付近に車載コンピュータと通信を行なうための端子が用意してあります。最近(10年前〜今くらい)の車だとOBD-IIと呼ばれる規格で統一されています。
そちらに通信用の端子などぶっ込んであげることで車両の情報を取得することができちゃう、という感じです。今回はOBD2での通信を楽にしてくれるELM327というチップを積んだBluetooth経由で通信ができるドングルを入手してたのでそれを使います。
アーキテクチャ?
今回作るものの全体像は
こんな感じです。雑にPower BIで見れるようにしたらおもしろいかな〜と思ったので可視化まで含めてます。
実装
※以下に、車載システムと通信を行なうコードを記載しています。車載システムは通常ユーザーが通信を行なわない想定で制作されており、想定外の故障の原因となりうる可能性があります。同様のことを行う場合は自己責任にてお願いします。
持っていたドングルは車に刺して電源をオンにし、Bluetoothでペアリングを行なうとWindowsからはシリアルポートとして見えます。
ありがたいことに、シリアル通信のインターフェースでOBD2通信をラップしているライブラリがNugetに公開してあったので先人の知恵に感謝しつつ使わせていただきます。
一番壁になるのは車両との接続なので、書くコード自体は少ないです(夜中の2時間くらいで作ったのでdynamicでHTTPリクエストを組み立てているところなどはスルーしてください…)。
サンプルを参考にします。
コンソールアプリ
program.cs
using System;
using System.Dynamic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using OBD.NET.Common.Devices;
using OBD.NET.Common.Logging;
using OBD.NET.Common.OBDData;
using OBD.NET.Desktop.Communication;
using OBD.NET.Desktop.Logging;
namespace CarObd.Console
{
static class Program
{
private static HttpClient _client;
static async Task Main()
{
_client = new HttpClient();
var comPort = "COM3"; // bluetoothのシリアルポート
using (SerialConnection connection = new SerialConnection(comPort))
using (ELM327 dev = new ELM327(connection, new OBDConsoleLogger(OBDLogLevel.None)))
{
dev.Initialize();
while (true) // 無限に送りたい
{
dynamic req = new ExpandoObject();
var rpm = await dev.RequestDataAsync();
if (rpm != null) req.rpm = rpm.Rpm.Value;
var speed = await dev.RequestDataAsync();
if (speed != null) req.speed = speed.Speed.Value;
var oilTemp = await dev.RequestDataAsync();
if (oilTemp != null) req.oilTemp = oilTemp.Temperature.Value;
var fuelLevel = await dev.RequestDataAsync();
if (fuelLevel != null) req.fuel = fuelLevel.Level.Value;
req.carName = "BMW 320i"; // 識別できるように決め打ち
var reqStr = JsonConvert.SerializeObject(req);
await _client.PostAsync("", new StringContent(reqStr, Encoding.UTF8, "application/json"));
Console.WriteLine("Sent!");
}
}
}
}
}
イニシャライズしてデータを取得してHTTP POSTで送信するだけの単純なプログラムです。
ライブラリのサンプルコードで接続確認ができたらちょっと書き換えるだけです。
ロジックアプリ
以下のようなjsonをコンソールアプリから送信するのでそれに噛み合うように組み立てます。
request.json
{
"rpm": 671.5,
"speed": 0,
"oilTemp": 90,
"fuel": 8.627450980392158,
"carName": "BMW 320i"
}
各項目の意味は
・rpm:エンジン回転数(rpm)
・speed:車速(km/h)
・oilTemp:エンジンオイル温度(摂氏)
・fuel:残燃料(%)
・carName:車両名(識別用なので何でもいい)
といった感じです。
同じようにSQL Databaseのテーブルを組んで型を噛み合うように変換し、Insertするアクションを作っておきます。
出来上がったものが こちら
APIを書くのが手間なときはLogic Appで受けちゃえるの楽すぎて感動しました。推しです。
結果を確認
出来上がったら実際に動かして確認します。ちょっとドライブ。
可視化
家に帰ったらSQL Databaseに接続してみてみましょう。
無事データが入っているのを確認できたので雑にPower BIに食わせます。コード上で待ち時間をかけてないのでデータは秒間1〜2レコードくらい飛んできます。
2軸の折れ線グラフを選択し、お試しで値に車速とエンジン回転数をセットしてみます。軸はもちろん時間です。
その結果がこちら!
値の動きが激しすぎる。
2本とも0に落ちているのは信号待ちのアイドリングストップです。エンジン止まってもデータは取れるんですね。
うーん。なんか相関あるのかなこれ。
加速時のデータが極端に崖みたいになってたら運転荒いよってことなんでしょうか。街乗りは加減速が多くてグラフガッタガタになることはわかりました。高速でも試してみたい。
オイル温度は時間に応じて綺麗に右肩上がりになっていますね。
アイドリングストップ時に下がっているのも特徴的です。
おわりに
ということでお手軽? に車をIoT化? してみました。
お手軽じゃないとかIoTじゃないとかいう意見は受け止めます。
もっと色々なデータが取れそうですが、取ったデータの使い道があまり思いつかないのでこれっきりでお蔵入りになるかもしれません。
PCがないと動かせないの結構面倒というかボトルネックなのでRaspberry Piとかで動くように改良したいですね。もっとも、半導体不足の煽りを受けてるのか国内在庫がスッカラカンな気がしますが。
次は Windows Server ネタでもやるかな…
それでは!
参考
・オン・ボード・ダイアグノーシス – Wikipedia
・DarthAffe/OBD.NET: C#-Library to read/write data from/to a car through an ELM327-/STN1170-Adapter
石川 順平/FIXER
九州のとある高専卒のFIXER3年目。すき間時間でbotを作ったりAZ系の資格の勉強をしたりしています。まだまだAzure初心者です。趣味はゲームやったりバイクで出かけたりすること。