OBD-II に車載PCを繋いで追加メーターを作ってみた

車載PC/サーバーを試みた時の記事でスティックPCを車に積んでPC/サーバーとして動くようにし、純正ナビの画面に映像を映せるようにしていた。今回はこれを OBD-II に接続し、追加メーターを作ってみた。

OBD-II +車載PCで作ってみた追加メーター

OBD-II

OBD-II は自動車の診断用の端子として普及したり国や場合によって義務付けられたりしている端子のようだ。

ただし、正確に理解しているわけではないが、どうやら診断のためのリクエストを待ち受けてくれている何かがいるわけではなく、CAN 等の組み合わせで車両の制御系のネットワークに繋いで ECU から直接返事を得るようなもののようだ。なので車種ごとの実装によると思うが、そもそも診断用の OBD-II プロトコルではないようなものも OBD-II 端子から ECU に流すことができると理解している。

また、 OBD-II の拡張なのかそうではないのかわからないが、 FORscan を用いて OBD-II に繋いだ後述の ELM327 互換機器から、例えばアイドリングストップを無効化する設定変更を行っている例もある(FORScanによるi-stop無効化(マツダ CX-5・KE)by da2525 – みんカラマツダ ロードスターRF (NDERC) のアズビルト値を変更し、外気温警告灯を有効にした上でアイドリングストップ(i-stop)を無効にした – fnoji.com)ので、少なくとも後述の ELM327 互換機器でできる範囲でも read-only の端子ということではないらしい。OBD-II を一切拡張しない標準と思われる範疇でも、 Service 04 で Diagnostic Trouble Codes をクリアすることができる。

また、ウェブで調べると、眉唾な情報も多いが OBD-II と関連して車自体にトラブルが生じたという事例が報告がされてはいるからこれこれの条件ではやめておけという主張をしている人もいる(が、これは眉唾すぎるのでリンクを張らないでおく)。

少なくとも車載のインフォテインメント用の USB 端子等と異なりそもそもユーザーが使用することは想定されていない端子であるし、仕組みとしても診断しかできないわけではなく ECU と直接通信するようなつくりになっているようであるから、適切に情報収集したうえでどの程度リスクがあってどこに気を付ければそのリスクを回避できるかを判断する必要がありそうだ。

なお、それとは別途に、 OBD-II は通常 ACC ではなく常時電源が来ているようだ。僕の車でも、エンジンオフ状態でも OBD-II 端子に接続して電圧を観測できた(”01 00″ 等のコマンドに対しては後述の ELM327 互換機器は “NO DATA” をレスポンスした)。

ELM327 互換デバイス

ELM327 は ELM Electronics が製造していたチップで、 OBD-II の複数あるようなプロトコルの面倒なところを吸収してシリアル経由で簡単なコマンドを打つだけで OBD-II の機能を使うことができるようにしてくれる。これ自体は手前に何かまた別の何かを噛ませて、 USB であったり Bluetooth であったりを通して使わせられるもののようだ。ただし、 ELM Electronics は事業を終了したので、今は本家 ELM327 を手に入れることは基本的にはできない。

幸いにして互換デバイスが多く売られているようだ。ただ、 AliExpress 等でも多数販売されているが、互換デバイスというより偽物と呼びたくなるような見た目や商品紹介で売られているものも多く、偽物と呼ぶかどうかはともかくとしても前述のとおり OBD-II 端子は結構色々なことができる端子であるからあまり低品質なものを掴まされるのにも注意は必要ではある。とにかく ELM327 採用品と同じように扱える品は、見定める方法はわからないが市場に存在はしそうだ。

データシートはかつて ELM Electronics が公開されていたものが 2025/05/20 時点ではまだ Internet Archive で参照できる: https://web.archive.org/web/20221207221529/https://www.elmelectronics.com/wp-content/uploads/2016/07/ELM327DSH.pdf

今回は Vgate vLinker FS USB を用いた。このページでは一応 $39.9 が定価っぽく書いてあるがしょっちゅうセールをやっているのか、買った時は $32.99 だったし今見ても $32.99 となっている(※ FedEx で送ってくるので別途送料が結構かかり、買った時は送料 $20.00、初回割引的なものが $1.00 で合計 $51.99 だったようだ)。

vLinker FS USB は USB 端子が接続されていれば OBD-II 側は接続されていなくとも、できることはほとんどないが少なくとも機能はしているようで、電源ランプもつくし、 OBD-II が何もレスポンスしてこないし電圧を測れと言われても電圧らしきものがみえないという範疇で可能な範疇では PC からのリクエストに対してレスポンスは返してくれた。対して OBD-II だけ繋いでも電源ランプもつかないので、電源は USB 側から取っていると考えられる。ただし、今のところ僕の車で自然に常設できる場所がなく、またこのメーターに関しては作って満足してそんなに頻繁には使わない気もしているので、 vLinker FS USB は常時接続はしておらず普段は取り外してあり、エンジンオフのまま繋ぎっぱなしにして車のバッテリーが大丈夫かどうかの検証はしていない。

ELM327 互換デバイスと CLI で接続

次のようなコマンドで vLinker FS USB とシリアル接続できた。minicom コマンドの使い方自体はウェブ検索すれば出てくるが、オプション画面の Screen メニューから Linefeed を追加するよう変更しないと結果が見えなかった。

# vLinker FS USB 同士でも環境ごとに違いそうな部分を X に置き換えている
# baudrate 115200 は製品や設定によって異なる可能性がある
minicom -D /dev/serial/by-id/usb-www.vgatemall.com_vLinker_FS_XXXXXXXX-ifXX-portX -b 115200

なお、 hex を入力することもあるがバイナリを入力する必要はなく、テキストで入力でき、テキストで出力された(CLI ではなくプログラムからシリアル通信する場合も同様)。例えば “AT RV” と打って Enter を押せば電圧が返ってくるし、 “01 00” と打って Enter を押せば hex でレスポンスが返ってくる。コマンド体系は先ほど張った ELM Electronics によるデータシート参照。

なお CLI ではないが、製品説明で謳われている通り Windows から FORScan を用いて接続することも可能だった。

Python でアプリケーションを作る

パラメーターに関しては OBD-II PIDs – Wikipedia を参照した(書かれている通り全てがサポートされているわけではないが、 “01 00” や “01 20” などサポートされているパラメータの取得方法もある)。また、Mazda 3 PIDs (Rough List) でマツダ車用の拡張コードをいくつか知ることができた(ただしおそらく年式等の差で使えないものも多かった)他、 Bluetooth ELM327 互換デバイス用のスマートフォンアプリで使うための情報共有がなされているものもあるので、参考にできた。

シリアル通信が使えるので好きな言語で開発できるが、今回は Python で WebUI を書いて localhost で LISTEN することにした。pyserial というモジュールを用いて以下のような感じのコードで動作している(WebAPI からの呼び出しのため、この外側でこのようなコードが同時に走らないような排他制御はしている)。

import serial

device_path = '/dev/serial/by-id/usb-www.vgatemall.com_vLinker_FS_XXXXXXXX-ifXX-portX'
baudrate = 115200
command = '01 0D' # 速度を取得する場合

with serial.Serial(device_path, baudrate) as ser:
    ser.write((command + '\r').encode())
    response = ser.read_until(b'>')
    if response[-3:] != b'\r\r>':
        raise Exception("Unexpected response: " + response.decode().replace('\r', '\n'))
    return response[:-3].decode()

なお、プログラム起動後の初回は、まず “AT Z” を実行した上で万一バッファに何か貯まっていたとしても invalid な hex で捨てられることを想定して結果は見ず進め、二回目の “AT Z” でリセットと “AT E0” でエコー状態を戻すところまで実施し、更に “AT I” で想定通りの文字列が返ってくることを確認してから処理に進むことにした。”AT I” の結果は手元の vLinker FS USB では “ELM327 v2.3” だった。

なお、開発にあたっては、 ELM327-emulator を用いることで /dev/pts/ にそれらしいデバイスを作ってくれるので、夜間の住宅街で車のエンジンをかけなくともある程度それっぽいデバイスをエミュレーションして開発できた。

冒頭のスクリーンショットの項目でいえば、エンジンオイルの温度とギヤポジションは Mazda 3 PIDs (Rough List) に記載されたマツダ拡張である(※ 特にエンジンオイル温度は標準でも 01 5C でセルシウス単位整数で取得できそうになっているのに、こちらはサポートされていなかった)が、他は標準の Service 01 から取得した。時計の左にあるパーセンテージは燃料残量である。バキューム圧は Intake manifold absolute pressure (01 0B)から大気圧(01 33)を減じたもの(基本的に負になるが高負荷時やアイドリングストップ時にゼロとなる)とした 。トルクは Service 01 の PID 0x62 (Actual engine – percent torque)と 0x63 (Engine reference torque)の乗算とし、出力(馬力)はトルクに1ラジアン(次元なし)を乗じた値が1ラジアン回転した際のエネルギーであるという想定から換算した。この辺りがあっているかどうか、特に出力(馬力)の計算がこれでいいのかはよくわからない。

観測したこと

これを表示したまま走って観測できたことがいくつかあった。

アイドリングストップ中、バッテリー電圧がみるみる下がり、 12V を下回ったあたりでアイドリングストップが終了してエンジンが起動することがあった。

燃料計については、 Fuel Tank Level Input (01 2F) に関し、上り坂と下り坂で表示に差があることがわかり、自動車の燃料計はそのあたりをいい感じに吸収しているんだなぁと思った。

エンジン回転数に対し取得される変速比と車両の仕様書に記された最終減速比で除し、指定タイヤの直径に円周率を乗じたものを乗じることで、クラッチ等があるので速度と必ずしも一致しないがそれなりに整合する値となることもわかった。

ある程度以上の速度で走っている間にアクセルをオフにすると、取得されるトルクが 0 となり、おそらく燃料をカットしているのだろうと思われる様子も観測できた。

観測した車両の挙動

今回、いくつかのコマンドに対し、2つのレスポンスが返ってきた。そもそも、 “01 00” 等のサポートされたパラメーター一覧にすら2つのレスポンスがあり、しかも異なる内容だった。二つの ECU が独立してレスポンスを返しており、両方がサポートしている場合は両方から返ってくるということのようだ。

ここで、 “09 0A” (ECU name) に対して、次のようなレスポンスが返ってきた。混ざっているが、 “ECM\0-EngineControl” と “TCM\0-TransmisCtrl” の二人から返事が返ってきていそうだ。どちらも追加メーターのためにレスポンスを返すほど暇な子であるイメージはないが、つまり僕の車の場合 OBD-II で診断情報を問い合わせたら ECM と TCM から返事が返ってくるということらしい。

>09 0a
017
0: 49 0A 01 45 43 4D
017
0: 49 0A 01 54 43 4D
1: 00 2D 45 6E 67 69 6E
2: 65 43 6F 6E 74 72 6F
1: 00 2D 54 72 61 6E 73
3: 6C 00 00 00 00 00 00
2: 6D 69 73 43 74 72 6C
3: 00 00 00 00 00 00 00

電圧は双方から返事が返ってきたが、 0.5-1V 程度差があった。vLinker FS USB が返した電圧は高い方より少しだけ低いという感じなので、高い方がバッテリー電圧に近いと推測される。

車両速度(コマンド 01 0D、単位 km/h、整数)とエンジン回転数(コマンド 01 0C、単位 rpm、0.25 の倍数)に関して二つのレスポンスがあった。エンジン回転数に関してはタイミングの範疇であろう範囲を超えた差が見られなかったが、車両速度に関しては2つのレスポンスでおおよそ 3% 程度の差が見られた(せいぜい0.1秒とか0.2秒で両レスポンスが返ってきていそうなので加減速の認識がない状態で 2km/h 以上の差が出ることはそうそうなさそうだし、そういう場面では差が0になることもありそうなのに、ある程度以上の速度だと定速のつもりで走ってもずっと差があって0にならなかった)。
従って、 EngineControl と TransmisCtrl で認識している速度が 3% ほど違い、それぞれが独立してレスポンスしているということになりそうだ。どちらの方が高い数字を返しているのかを手持ちの機器で調べる方法は思いつかない(※ どちらが必ず先にレスポンスを返すというほどでもないのと、その上で CAN レイヤーのアドレスがわかれば判別できるのだろうが、それを dump する方法は思いつかない)ので調べていないが、車両速度は差があるということらしい。
なお、停止中はゼロしか表示されないし停止中以外は注視するわけにもいかないので確かなことは言えないが、速度計はレスポンスのうち高い方の速度より更に少しだけ高い値を指していそうだった。