
先日よりTTのOBDⅡが吐き出すデータで遊んでいます。「
自作・iPhone表示のOBD2メーターを作る【実験編】」
今回はOBDⅡの規格の中でもCAN通信のデータのやり取りで遊んでみました。
何とか理解ができてきたので備忘録です。
以下のCAN通信用のコントローラを解析中に、「みんカラ」の先人のブログなどが非常に役に立ちました。この先遊んでみたいと思う人のためにちょっとやり方を残しておきます。
でも、興味のない人には全く面白くない記事ですw
今回作るシステムの概要として…
①OBDⅡより
CAN通信によって車体のデータを吸い取る。
②CAN通信の処理・制御は
マイコンで行う。
③データはBLEモジュールに送り、
Bluetooth無線で送信を行う。
④Bluetooth無線のデータは
自作iPhoneアプリにて受信し画面にデータを表示させる。
OBDⅡとは自動車の自己診断機能を表します。Wikiによるとヨーロッパでは2001年より、日本では2008年10月以降に製造された乗用車および小型トラックに搭載を義務付けられています。
この自己診断機能を使って車体情報データを取ってやろうという目論見です。
今回の実験は標準的に取得できる「モード01」の標準的に取得できるデータのみを考えています。もうちょっと踏み入れば色々なデータが取れそうですが、まずは簡単なところから…。
OBDⅡのコネクタはこんな形状です。

コネクタは16ピンで構成されており、CAN通信では6ピンの「CAN High」と14ピンの「CAN LOW」を使用します。
ちなみに16ピンの「バッテリー電源」は常時電源となり常にバッテリー電圧が付加されています。通電しっぱなので電子回路にはちょっと使いにくい電源です。
今回は「CAN High」と「CAN Low」の2本のみを使用してみます。
CAN通信の送受信されるデータは「
アービトレーション」「
コントロール」「
データ」の大きく3つのフィールドに分かれています。
「
アービトレーションフィールド」はデータの識別子IDです。標準フォーマットで11ビット分のフィールド値が設定でき、拡張フォーマットは29ビット分のフィールド値が設定できます。
「
コントロールフィールド」にはデータフィールドの長さがバイト単位で入力されています。
「
データフィールド」には送受信したいデータが入力されています。
車両の診断機能ではデータ要求に標準フォーマット「
0x7E0」(=0b11111100000)の識別子IDとほしいデータを要求送信します。すると、「
0x7E8」(=0b11111101000)の識別子IDと結果データが付いて返答として帰ってきます。
(※プログラム言語で書きなれているので「0b」は2進数・「0x」は16進数と理解ください…)
例として「
エンジンの回転数」がデータとしてほしいとすると…
(※以下概要ですので厳密には違います)
0x7E0 8 02010C0000000000のデータをCAN通信上に流します。
「
0x7E0」はデータ要求の標準識別子IDを表す「アービトレーションフィールド」。
「
0x8」はデータの長さ(バイト単位)を表す「コントロールフィールド」。
それ以降の「02 01 0C 00 00 00 00 00」は「データフィールド」であり、「コントロールフィールド」で宣言した8バイト分のデータを送信しています。
「
0x02」はデータに入っている個数をバイト単位で表します。
「
0x01」はモードで「01:車両の制御状態を得る」を意味します。
「
0x0C」は「エンジン回転数」を表すPID(パラメーターID)でほしいデータをここに入力し送信します。
するとCAN通信上に…
0x7E8 8 04410C1678000000のデータが返信されてきます。
「
0x7E8」はデータ返答の標準識別子IDを表す「アービトレーションフィールド」。
「
0x8」はデータの長さ(バイト単位)を表す「コントロールフィールド」。
それ以降の「04 01 0C 16 78 00 00 00」は「データフィールド」であり、「コントロールフィールド」で宣言した8バイト分のデータを送信してきます。
「
0x04」はデータに入っている個数をバイト単位で表します。
「
0x41」はサービスIDの返信データIDを表します。
「
0x0C」は返答しているPID。
「
0x16 78」はPIDの実データとなります。
最後の「0x16」と「0x78」がエンジン回転数を表すデータであり、次の計算式が成立します。(256*0x16+0x78)/4が成立して、10進数に直すと(256*22+120)/4 = 1438rpmという「エンジン回転数」が導き出されます。
これらのPID等の詳しい情報は、
Wikiに詳しく記載されています。英語ですけど…。
上記の例は、CAN通信を簡略化して書きました。実際のCAN通信はもうちょっと複雑なことをやっています。しかし、以下のCAN用のコントローラーを使うとこのくらいの知識くらいあれば、後はコントローラー任せで通信できてしまいます。
今回実際にOBDⅡのCAN通信用に考えた回路は以下です。

大きく「
CAN通信部分」「
マイコンの制御部分」「
Bluetooth通信モジュール」に分けられています。
まずは…
①OBDⅡよりCAN通信によって車体のデータを吸い取る。の部分として…。
CAN通信のIC部分にはCANコントローラ「
MCP2515」とCANトランシーバ「
MCP2551」を使用しています。
CANトランシーバの「
MCP2551」は入力のCAN信号と出力のCAN信号のレベルを合わせてくれるもので実際には何をやっているのか今もわかっていません…w
でも要るものらしいので回路に組み込んでいます。特に接続に難しい箇所はなく「RS」ピンをGNDに取り付けることを忘れないようにするくらいです。
実際のCAN通信はCANトランシーバ「
MCP2515」のほうが肝となりここでCAN通信のすべてがコントロールされます。
このコントローラーのレジスタは以下のようにあって、このレジストリを設定しながらコントローラーの動きを決めていきます。
「
CANCTRL」レジストリは、主に動作を設定するレジストリです。
「
CNFx」の3つのレジストリはコンフィギュレーションで通信の速度やピン設定などを行ないます。
「
CANINTE」「
CANINTF」は割込み許可の設定や割込みフラグの監視に使用します。
コントローラには「フィルタ」「マスク」の設定ができて、受信されたデータにはIDを持っています。膨大な量のデータが受信された場合には、その処理にCPU(今回はマイコン)に負荷がかかってしまいます。それを避けるために「
RXFx~」「
RXMx~」の設定で受信したIDに関所を設け必要なものだけを処理に回すことができます。
大体ここまでのレジスタを初期化処理で設定しています。
MCP2515にはいくつかのコマンドを持っていてレジストリの読取・書込作業はマイコンよりSPI通信によってコマンドを実行していきます。
この、SPI通信とは同期式の通信でIC間の通信によく使用されています。今回使用のマイコン「PIC16F1705」にも標準でMSSPってSPIが搭載されているけど通信方法はいたって簡単なので自分でプログラミングしたほうが柔軟性が利いたりします。
SPI通信によるレジストリの書込みコマンドは「0b00000010」であり、その後にレジストリの「アドレス」と「データ」を続けて送信することで、その「アドレス」に指定した「データ」を書込むことができます。
例えば、「
CANCTRL」のレジストリに「
0b01000000」のデータを設定したいとすると…
SPI通信でマイコンからコマンド「0b00000010」を送信して、上記の表よりCANCTRLのアドレス「0b00001111」を送信。続けてデータの「0b01000000」でSPI通信を終わらせるとレジストリが書き換えられます。
要は、「0x02 0x0F 0x40」をSPIで送信する。理解できれば簡単です。
このように、レジストリに書込みを行ないながらコントローラMCP2515の設定をしていきます。
初期設定が終わったら、実際に通信を実行してみます。
コントローラーには動作モード設定があります。電源直後は「
コンフィギュレーションモード」となっており、初期化を行なう際にはこのモードにする必要があります。
通常の通信動作にするときには「
ノーマルモード」に変更する必要があります。
動作モード切替は「
CANCTRL」(0x0F)の「
REQOP」(5~7)にて変更を行ないます。「
0b000」が「
ノーマルモード」で「
0b100」が「
コンフィギュレーションモード」となっています。ビット変更については後述の「
ビットモデファイ」が変更しやすいです(0x05 0x0F 0xE0 0x00をSPI送信で「ノーマルモード」)。
「
ノーマルモード」に切り替えたら通信することが可能となります。
このコントローラは2つの受信バファ「RXB0」と「RXB1」を持ち3つの送信バファ「TXB0」「TXB1」「TXB2」を持っています。
表のRXBx~とTXBx~で表されているレジストリがそれです。
受信バッファはアドレス「0x60~0x6E」「0x70~0x7E」にあり、送信バッファは「0x30~0x3E」「0x40~0x4E」「0x50~0x5E」に位置します。
今回のプログラムでは1つずつの送受信バファのみを使いました。
先の例のように「エンジン回転数」のデータを取得するためにMCP2515を設定してみます。
まずはMCP2515よりほしいデータを要求送信をしてみます。
データ要求のID「
0x7E0」を送るために、送信用の標準フォーマットIDを設定するレジストリである「
TXB0SIDH」と「
TXB0SIDL」を設定します。標準フォーマットは11ビットに対し使用するマイコンは8ビットマイコンであるのでレジストリが2つに分かれています。
「
TXB0SIDH」の8ビットと「
TXB0SIDL」の上位3ビットに「0x7E0」(=0b11111100 000)を設定するには「0x7E0」のIDを左に3ビットシフトして上位8ビット分の「
0xFC」を「
TXB0SIDH」書込み。残りの3ビット「
0b000」を「
TXB0SIDL」の上位詰め3ビットに書き込みます。
先のSPI通信のMCP2515書込みコマンド「0b00000010」を使ってマイコンより上記レジスタのアドレス「
TXB0SIDH(0x31)」=0xFC・「
TXB0SIDL(0x32)」=0x00にそれぞれのIDデータを書き込みます。
マイコンからSPIでMCP2515に向かって…
「
0x02 0x31 0xFC」
「
0x02 0x32 0x00」
と命令すればOK。
次に「データフィールド」の長さを設定します。対応するレジストリは「
TBX0DLC」となります。CAN通信での送信バイト数は常に8バイトで設定しています。ちなみに、このレジストリには6ビット目に転送要求の種類を設定する「RTR」がありますが、送信データはデータフレームであるので常に「0」となり、「
TBX0DLC」にはダイレクトにデータフィールドのバイト数を入力しても構いません。
なので、「
TBX0DLC(0x35)」=0x08をこのレジストリに書込みます。
マイコンからSPIでMCP2515に向かって…
「
0x02 0x35 0x08」
と命令すればOK。
最後に「データフィールド」の中身を設定します。「TXB0D0」~「TXB0D7」の8つのレジストリに送信するデータを直接入力します。レジストリのアドレスは
0x36~0x3Dです。
例ではデータとして必要な3バイト分のデータを書込み、残りの不要な5バイト分を0x00で埋めて「
0x02 0x01 0x0C 0x00 0x00 0x00 0x00 0x00」のデータをレジストリに書き込みます。
「
0x02 0x36 0x02」
「
0x02 0x37 0x01」
「
0x02 0x38 0x0C」
「
0x02 0x39 0x00」
「
0x02 0x3A 0x00」
「
0x02 0x3B 0x00」
「
0x02 0x3C 0x00」
「
0x02 0x3D 0x00」
を命令すればOK。
ここまで設定できれば実際に送信開始の命令を出してやります。「
TXB0CTRL」(0x30)レジスタ「
TXREQ」(3)の「メッセージ送信要求」のフラグを立ててあげれば送信が始まります。
あるアドレスのあるビットのみをいじる方法はやり方はいろいろあるけど「ビットモデファイ」ってコマンドを使うと1つの命令でこれが実現できます(前述の「CANCTRL」でも使用)。
コマンド「0x05」の後に「アドレス」・「マスク値」・「データ」を続けて送信します。
ここでは、アドレス0x30の3ビットを「1」にするので…
「
0x05 0x30 0x08 0xFF」
を送信してやります。ちなみに同じところを「0」にしてあげたければ…
「
0x05 0x30 0x08 0x00」
で実現できます。
以上のようにTXBのデータをロードして送信リクエスト「
TXREQ」のフラグを立てればデータがCAN上に流れて行きます。
すると、「
0x7E8」のIDと返信情報を持ったデータがCAN上へ流れて来ますのでそれを拾ってやります。
受信のバッファは「RXB0~」のみを今回は使用しました。
先の初期設定で行なった「フィルタ」「マスク」の設定を行なうと必要なIDだけを取り出すことができます。
受信のタイミングは「
CANSTAT」(0x0E)レジスタの「
ICOD」(1~3)が0b110になっているかポーリングして受信を確認できますが、今回は「
INTピン割込み」を使って受信をMCP2515からマイコンに知らせてやります。
「RXB0~」のバッファがいっぱいになると(受信されると)MCP2515の「INT」ピンがLowレベルとなりそれをマイコンで割り込みをかけて受信を確認することができます。
先の「
CANINTE」(0x2B)の「
RX0IE」(0)のフラグを初期設定で立てていれば割り込みが行なわれます(「
0x05 0x2B 0x01 0xFF」を初期設定で行なっておく)。
割込み確認後は「
CANINTF」(0x2C)の「
RX0IF」(0)にフラグが立っているのでソフトウェアリセットが必要です。(「
0x05 0x2C 0x01 0x00」)
受信データは「
RXB0SIDH」「
RXB0SIDL」「
RXB0DLC」「
RXB0D0」~「
RXB0D7」のアドレスに入ってきます。
「
RXB0SIDH」「
RXB0SIDL」「
RXB0DLC」のアドレスについては初期設定の「フィルタ」「マスク」で「0x7E8」のみを受信する設定をしていれば、標準フォーマットID(0x7E8)と受信データ長8バイトが入ってきます。
実際の受信データについては、「
RXB0D0」~「
RXB0D7」(0x66~0x6D)にセーブされているので、このアドレスのデータをとってあげればデータ取得できます。
SPI通信のMCP2515読込みコマンド「0b00000011」を使います。コマンド「0b00000011」を送って、ほしいアドレスを送信するとすぐにそのデータがSPI通信上に流れてくるのでそれを拾ってやる感じです。
例えば、「RXB0D1」~「RXB0D4」のデータを取得してみます。
「0x03 0x66」をSPIに流すと…「0x04」(受信データのバイト数)
「0x03 0x67」をSPIに流すと…「0x41」(サービスIDを表す返信データID)
「0x03 0x68」をSPIに流すと…「0x0C」(エンジン回転数のPID)
「0x03 0x69」をSPIに流すと…「0x16」(エンジン回転数データ1)
「0x03 0x6A」をSPIに流すと…「0x78」(エンジン回転数データ2)
がそれぞれSPI上に流れてくるのでそれを取得して計算してやります。
以上のように、マイコンとMCP2515コントローラーとのSPI通信のやり取りでCAN通信のデータを送受信ができてしまいます。
コントローラーとの通信が理解できれば、実際のCANの細かい通信方法についてはあまり考える必要もなく簡単にCAN通信ができます。
説明下手だから文章じゃわからないか…w
じゃ一体何のデータが取れるのか…?
取得できるデータ(PID)は
Wikiに詳しく記載されており、196種類のデータがあります。
でも、すべてのデータ取れるわけでもなく車両・メーカーによって異なります。
車両によりサポートされるデータは調査できるようになっています。
データ中のPID(パラメーターID)「0x00」「0x20」「0x40」「0x60」をそれぞれ指定してデータを取得するとサポートされるPIDの調査ができるようになっています。
例えば、「0x00」をPIDとしてデータを取得すると、4バイトのデータが帰ってきます。
私の年式のAudi TTの場合は、「0xBE 0x1F 0xA8 0x13」ってデータが返信されました。これを2進数に直すと…
「0b 1011 1110 0001 1111 1010 1000 0001 0011」
となります。
この数字は「1」がサポートされているPIDで「0」がサポートされていないPIDを表しています。
取得したPID(0x00)の次PID(0x01)からのサポートの有無を表す羅列です。「0x20」までのサポート状況を確認できます。
2009年式TTは、PID「0x00~0x20」まででは以下のデータが取れそうです。
「水温計」「エンジン回転数」「「車速」等々…。一般的な情報が並んでいます。
PID「0x21~0x40」までのデータはこんな感じ…。
「大気圧」がある。それ以外は欲しそうなデータはないか…。
PID「0x41~0x60」までのデータはこんな感じ…。
「電圧」が取れそう? それ以外はちょっとわからない…。
「0x60」に1が立たなかったので「0x60」以降のPIDはサポートされていないみたいです。
ざっくり、見た感じ「あっこれ!」って情報は取得できないみたいです。
でも、取れる情報もわかったし、簡単にデータが取れそうなので何かのモニターは作ってみたいと思います。
コントローラの実装方法でえらく長文になったので、それ以降の「マイコン」関係・「Bluetoothモジュール」関係・「自作iPhoneアプリ」関係は、(その2)としてまた後日書こうと思います。