Help us understand the problem. What is going on with this article?

MCP2515を使った自作基板とRaspberry Piで自動車のECUにOBDリクエストを送る

前回の記事 では、作成した基板で車両から流れてくるデータを受け取る所まで確認しましたが、今回はOBDのリクエストを送って、それに対するレスポンスを受け取るまでを行います

OBD-2 の6番ピンと14番ピンにはCAN High-Speed(CAN-C)のデータが流れています。CAN High-Speed(以下CANと略す)は車載ネットワークの種類の一つで、エンジンを制御する電子制御ユニット(以下ECUと表記)など主に走行に関わる機器との通信に使われています

CANの概要

ここでは、実験に必要な説明にまでに留めています。詳しい説明は別のサイトや本などで確認してください

  • 全てのデータがブロードキャストされ、ID(CAN-ID)によって機器が自分にとって必要なデータなのかを判断している
  • 規格上の最大通信速度: 1MBps (車両では CAN High-Speed で 500kbps、 CAN Middle-Speed で 250kbps が使われる)
  • 一度に送信できるデータ量: 8Byte

プロトコル

CANには「データフレーム」「リモートフレーム」「エラーフレーム」「オーバーロードフレーム」の4種類のフレームがあり、ECUからの情報取得にはデータフレームを使用します

データフレームには標準フォーマット(11bit CAN)と拡張フォーマット(29bit CAN)があります。乗用車では主に標準フォーマットが使用されており、拡張フォーマットはトラックやバスを中心に使われています。ただし、ホンダの乗用車では拡張フォーマットが採用されているようです

どちらのフォーマットでも送るデータには変わりはありません。後述するCAN-IDが変わるくらいです。ただし、標準フォーマットにしか対応していない車に拡張フォーマットのデータを流してしまうと、エラー扱いになりトラブルの原因になることがありますので注意が必要です。(逆に拡張フォーマット対応の車に標準フォーマットのデータを流すことは問題ないはず)

標準フォーマットのフレーム構造

1 2 3 4 5 6 ・・・
名前 SOF ID RTR 予約領域 DLC Data ・・・
Bit数 (1) (11) (1) (2) (4) (0~64)
  • SOF: データの開始位置を示す(Start Of Frame)。0が入る
  • ID: 送信先を識別する (Identifier)
  • RTR: データフレームでは0になる。リモートフレームのリクエストの時に1になる (Remote Transmission Request)
  • 予約領域: 0が入る
  • DLC: データの長さを示す (Data Length Code)
  • Data: 0~8バイトのデータ

Dataの後にはCRCフィールド等が続きますが、ソフトの方では意識することなくCANコントローラーの方で処理されるので省略します

拡張フォーマットのフレーム構造

1 2 3 4 5 6 7 8 9 ・・・
名前 SOF ベースID SRR IDE 拡張ID RTR 予約領域 DLC Data ・・・
Bit数 (1) (11) (1) (1) (18) (1) (2) (4) (0~64)

標準フォーマットとほぼ同じで、IDがベースIDと拡張IDを使って表されるだけの違いです

IDの後に拡張フォーマットを示すためのSRRとIDEが続きます。これらは固定で1がセットされています

  • SRR(Substitute Remote Request Bit)
  • IDE(Identifier Extension Bit)

※フレーム構造の説明もかなり省いています。他のサイトではマイコンを使ってCAN通信を使っている例が多くあり、それらは、フレームのデータをシリアルで直接送っている物です。それに対し、この記事はLinuxアプリの can-utils を使うケースで、データ部のみを指定する物のため、それらの違いを説明するために記載しました

送信先 (CAN-ID)

送信先をCAN-IDで指定します。これはLANでいうIPアドレスに相当します。

ECUのCAN-IDは ISO15765-4 の規格で定められており、7e0(11bit CANの場合) か 18da00f1(29bit CANの場合) になっています。ECUが複数ある場合は7e1, 7e2, 7e3 ...(11bit CANの場合) か 18da01f1, 18da02f1, 18da03f1 ...(29bit CANの場合) になりますが、どのECUに送るか不明な場合は 7df(11bit CANの場合) や 18db33f1(29bit CANの場合) で送れば、応答できるECUの全てからレスポンスが返ってきます。しかし、一つのデータを取るために複数のECUがレスポンスを返してしまうと、無駄な通信になりますので、可能な限りECUを特定して送信先を決める方が良いでしょう

11bit CAN

CAN-ID 説明
7df リクエストを理解できる全ての ECU へリクエスト
7e0 ECU#1 (ECM: エンジン制御モジュール) へのリクエスト
7e8 ECU#1 からのレスポンス
7e1 ECU#2 (TCM: トランスミッション制御モジュール) へのリクエスト
7e9 ECU#2 からのレスポンス

29bit CAN

CAN-ID 説明
18 db 33 f1 リクエストを理解できる全ての ECU へリクエスト
18 da xx f1 ECU#xx へのリクエスト
18 da f1 xx ECU#xx からのレスポンス

※ xx には 00 からのhex値が入ります

モード

目的に応じて以下のモードがあります。モードはサービスと呼ぶこともあります

01:現在の車両コンピューターの制御情報を得る
02:故障した時の車両コンピューターの制御情報を得る
03:現在発生している故障コードを得る
04:故障コード、故障履歴を消す
05:排気ガスセンサーの動作を検査する
06:時々しか作動しない装置(例:燃料タンク蒸発ガス吸収装置)の動作を検査する
07:過去に一度でも発生した故障コードを得る
08:外部テスター入力値でエンジン車両部品を動かす(例:ガソリン噴射量を増やす)
09:車両情報を得る(例:車の形式)

PID

取得したいデータをPIDで指定します。PIDはwikipedia(英語)にまとめられています

https://en.wikipedia.org/wiki/OBD-II_PIDs

その中からよく見かけるPIDを取り上げてみました

Mode1

PID 内容 単位 数式
04 エンジン負荷 % 100 / 255 * A
05 冷却水温度 A - 40
06 短期燃調(STFT) % 100 / 128 * A - 100
07 長期燃調(LTFT) % 100 / 128 * A - 100
0B 吸気マニホールド内気圧 kPa A
0C エンジン回転数 RPM (256 * A + B) / 4
0D 速度 km/h A
0E 点火進角 deg A / 2 - 64
0F 吸気温度 A - 40
11 アクセル開放率 % 100 / 255 * A
2F 燃料タンク残量 % 100 / 255 * A
46 外気温 A - 40

※各内容は勝手な意訳です。専門用語は英語のままの方がしっくりくるかもしれません

データ

リクエスト

リクエストデータの書式は次の通りです

1 2 3 4 5 6 7 8
説明 データ長さ モード PID 0 0 0 0 0
桁数 2 2 2 2 2 2 2 2
  • それぞれの値は16進数の2桁で表現します
  • 長さはバイト数(16進数2桁で1バイト)です。ここではモードとPIDの2つのデータを送るので常に2になります
  • 4桁目以降の0はデータを8バイトにするために埋めるダミーです

例: モード1のエンジン回転数を得るには以下のリクエストになります

02010c0000000000

レスポンス

車両から得られるデータの書式は次の通りです

1 2 3 4 5 6 7 8
説明 データ長さ モード PID データA データB データC データD データE
桁数 2 2 2 2 2 2 2 2
  • それぞれの値は16進数の2桁で表現します
  • 長さはバイト数(16進数2桁で1バイト)です。8に満たない余った部分は0で埋められます
  • モードはリクエストのモードに対して0x40が加算されます

例: エンジン回転数のレスポンスは次のように返ってきます

04410C0C7D000000

can-utilsのコマンド

CANのデータについて説明しましたが、次に実際にRaspberry Piからコマンドを送信する手順を説明します

前回の手順にあった、can-utilsのディレクトリに移ってからコマンドを実行してください

$ cd can-utils-master

送信

リクエストを送るコマンドは cansend を使います

コマンド書式

cansend ${インタフェース} ${CAN-ID}#${データ}

CAN-IDとデータの間に # を挟みます

例: エンジン回転数を取得する

11bit CAN の場合
$ ./cansend can0 7e0#02010c0000000000

29bit CAN の場合
$ ./cansend can0 18da01f1#02010c0000000000

受信

レスポンスを受け取るには candump を使います

コマンド書式

candump ${インタフェース},${CAN-ID}:${bitmask}
  • CAN-IDはリクエストとレスポンスでそれぞれ違います
  • bitmaskは取得するデータを振り分けるために指定します。ビット演算で受信したデータのCAN-ID & bitmask = 引数で指定したCAN-ID になった時に取得します

例: ECUからのリクエストを待ち受ける

11bit CAN の場合
$ ./candump can0,7e8:7fe

29bit CAN の場合
$ ./can-utils-master/candump can0,98daf100:ffffff00

実験

  1. 車両のOBD-2コネクタに自作ケーブルを繋ぎ、エンジンをかけます
  2. Raspberry Piを起動し、ターミナルを2つ立ち上げます
  3. canインタフェースを有効にします
    # ip link set can0 type can bitrate 500000
    # ip link set can0 up
    
  4. 1つのターミナルで candump を実行してECUからのリクエストを待ち受け状態にします
  5. もう1つのターミナルでcansendを実行します
  6. candumpを実行したターミナルで取得したデータが表示されます
    取得イメージ image ※ recv_11.sh は上のcandumpコマンドの例を実行している物です

取得したデータの見方

04 41 0C 0C 7D 00 00 00

最初の 04 はデータの長さ、41 はモード1のリクエストに対するレスポンスのモードで、続く 0C はエンジン回転数を示すPIDです。

0C と 7D がデータです。7Dより後の 00 は余りのデータなので無視します。このレスポンスはエンジンの回転数ですが、hexでは小数点やマイナスを表現できないので、PID毎に決められた数式にかけることで、値が求められます。

エンジン回転数の場合は以下の数式を使います

(256 * A + B) / 4

ここで、A には1桁目の 0C が入り、B には2桁目の 7D が入ります

分子の 256 * A + B は一見難しそうに見えますが、256 は hex で 0x100 なので、0x0C * 0x100 + 0x7D → 0x0C00 + 0x7D → 0x0C7D となり、単にhexを繋げて4桁にすれば良いだけです

これを10進数になおして計算すると 3197 / 4 = 799.25 となり、
エンジン回転数 799.25 rpm が取得できました

補足

bootパラメーターについて

前回の手順で /boot/config.txt にSPI周りの設定を書きましたが、まれに受信エラーが出ていましたので、spi-dma-overlay を追加した方が良いかもしれません。

dtoverlay=mcp2515-can0-overlay,oscillator=16000000,interrupt=25
dtoverlay=spi-bcm2835-overlay
dtoverlay=spi-dma-overlay  ★ この行を追加 ★

エラーとの関係は分かっていませんし、基板やケーブルの接触不良もあるかもしれませんが、何となく良くなったような気がしてますので、これで様子見してます

OBD-2のデータを取ることについて

このように車両のデータを取ることで、何か悪い事をしているような後ろめたい気持ちが沸いてくるかもしれません。それについて、個人的な見解になりますが、全然気にする必要は無いと考えています。以下のように国土交通省も指針を出しています。

今般、自動車に備え付けらている排ガス装置について、ユーザーが円滑に点検整備を行い、自動車の性能を適切に維持することにより、自動車の環境の保全等を図ることを目的に、J―OBD2(排気に係る装置の車載式故障診断装置:OBDとはOn Board Diagnosisのこと。)の装備が義務付けられた自動車(ガソリン又はLPGを燃料とする乗車定員10人以下又は車両総重量3.5t以下の自動車等)を対象として、自動車製作者等が提供する情報の内容や方法を指針に定めました。

http://www.mlit.go.jp/report/press/jidosha09_hh_000041.html

つまり、OBD-2から得られる情報はユーザーにとっても有用なものであるから、自動車メーカーは提供すべきだということです。

注意と自己責任のお願い

Mode1のリクエストを送るくらいではトラブルになることは無いと思いますが、くれぐれも、結果の分からないPIDのリクエストや、不必要なリクエストの再送は止めた方良いでしょう。車種によってはECUの負担になってエンジンの制御がおろそかになるものもあるようです。

検査モードの実行など、ECUからの値を取得する以外のリクエストについては、この記事では想定していません

初めて自作基板を接続した場合は、予期せぬトラブルが起きても対処出来るように、今まで以上に安全に注意して運転してください。もし運転中に車の動作に異常を感じた場合は、安全な場所に停車してOBD-2ケーブルを外してください。

この記事を参考にして、機器や車両の故障、事故等の被害が起きたとしても責任は負えませんので、ご了承願います。

この記事は、自分が所有する車で行うことを想定しています

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした