みなさま、お疲れ様です。今回はpythonを使ってISO-TPのシミュレーションをしてみようと思います。ISO-TPは自動車のCAN通信を理解しようとしたら避けては通れないっすね。
あ??ISO-TP???
と思った方、むずこいですね。この記事で勉強しましょう。
ちなみに筆者が書いたCAN関連記事が以下の通りです。気になる方は是非読んでみて下さい↓。
0回目:「CAN通信って何?」という人向けにCAN通信の解説をしてみる
1回目:【Vector Virtual CAN BUS & Python-can】CAN通信のシミュレーション環境構築
2回目:【python-can】CANを定期的に送信する!!
3回目:【Python-can】受信したら返答するCANプログラム
4回目:【python-can】ISO-TPの勉強!シミュレーションで動作確認するぞ!!(本記事)
5回目:【python-can】ISO-TPのリクエストとレスポンスをシミュレーションするぞ!
今回は4回目。ISO-TPについての勉強と動作確認のシミュレーションをやってみます。誰かの参考になれば幸いです。
ISO-TPとは?
まずISO-TPってなに?を簡単に理解しましょう。
いや、そーゆーことが聞きてぇんじゃねぇよ!!!ってね。こんな説明に限らず、フワついた説明で煙に巻く人にはドロップキックしましょう。簡単に説明します。
まずCAN通信で送信できるデータは最大8byte(=64bit)ですよね。↓のデータフレームのデータフィールドです。
それをCAN通信でデータを4095byteまで送れるようにするっている工夫がISO-TPですね。あ?そんなん無理やんけ?どうするんな?ってね。思いますよね。
どうやって4095byteも送るんだよ
結論は何回かに分けて送信します。簡単ですね。例えば、10byteのデータなら2回に分けて送れば、送れますよね。
でも、ただ分けて送っただけだと、受信した側は「ん?これは10byteデータの続きのデータなのか?それとも単純に8byte送ってきただけなのか?」とわからなくなります。なので8byte中の一部は「何回かに分けて送りますよ。」の印をつけておきましょうね。ってします。そうすれば、送受信側も誤認識なく、何回にも分けて送信できます。
んで、その印のつけ方にルールがあって、そのルールがISO-TPでそのルールに則って送ると最大で4095byteまで送れますよって話です。
さっさと図で解説
送受信の関係を図にすると以下の通りです。
↑よく見ますよね、これ。イメージはこんなんです↓。
First Frame
「これはマルチメッセージの最初です。ごにょごにょごにょごにょ。」
Flow Control Frame
「はいはい。分けて送信してくるのね。了解。早口だと困るから送信間隔はこんくらい空けてね(=STmin)。あと連続で喋るのは●回までな(=BS)。」
Consecutive Frame
「これはマルチメッセージの2回目です。ごにょごにょごにょごにょ。」
Consecutive Frame
「これはマルチメッセージの3回目です。ごにょごにょごにょごにょ。」
Consecutive Frame
「これはマルチメッセージの4回目です。ごにょごにょごにょごにょ。」
Flow Control Frame
「はいはい。同じように送ってね。」
Consecutive Frame
「これはマルチメッセージの5回目、、、、」
以下同じように続いていきます。
そんでFF, FC, CFそれぞれにデータ構造が決まってます。まぁそれはここのAddressing formatを読むべし↓。FFは先頭に1,CFは先頭に2,FCは先頭に3を付けるって感じですね。
おっしゃシミュレーションすっぞ!!
ふんわりisotpについては理解できたので、pythonでシミュレーションしてみます。やっぱり動かしてみないと分からないです。
まずはISO-TPでCAN通信が使えるようにcan-isotpというライブラリをインストールします。
> pip install can-isotp
Collecting can-isotp
Downloading can_isotp-1.8-py3-none-any.whl (21 kB)
Installing collected packages: can-isotp
Successfully installed can-isotp-1.8
オッケーです。次ぃ!
送信側と受信側のプログラムを書きました。
送信側:マルチフレームのデータを送信する。(コードの中身はここを超参照した。)
import isotp
import time
import can
print("start send")
#バス接続
bus = can.interface.Bus(bustype='vector',
channel=0,
bitrate=500000,
app_name='python-can')
#受信IDは0xF1、送信IDは0x10、Normalフレーム
addr = isotp.Address(isotp.AddressingMode.Normal_11bits,
rxid=0xF1,
txid=0x10)
stack = isotp.CanStack(bus, address=addr)
#送信データ11byte
stack.send(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b')
time.sleep(2)
while stack.transmitting():
stack.process()
time.sleep(stack.sleep_time())
bus.shutdown()
受信側:First Frame(先頭が”0x10″)が来たらFlow Control Frame(“0x30”)を返答する。
import can
import time
#バス接続
bus = can.interface.Bus(bustype='vector', channel=0, bitrate=500000, app_name='python-can')
#受信
start_time = time.time()
while time.time() - start_time < 15 :#15sec間モニター
recv_msg = bus.recv(timeout=1)
if recv_msg != None:
if hex(recv_msg.data[0]) == "0x10":#data[0]が10のときは返答する。
#返答メッセージ生成
msg = can.Message(arbitration_id = 0xF1,
data= [0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00],#Flow Control
is_extended_id = False)
bus.send(msg)
print("end")
これを実行します。そしてBUSMASTERでCAN BUSをモニターすると以下の通りです。
まずIDを見るとID10を送信して->IDF1を返答->そして10を送信しています。オッケーです。
次にデータの先頭を見てみるとFFで1,次にFCで3,CFで2が先頭についてますね。オッケーです。
FFのDLCは0Bと11個のデータを送るよと言ってます。オッケーです。
CFを見ると21となってます。1がシーケンスナンバーです。「CF1回目ですよ。」って送信していますね。オッケーです。
よしよし。ちゃんと出来ていますね。今日はここまでです。
まとめ
ISO-TPでデータを送るときはまずFirst Frameを送信して、受信側からFlow Controlが帰ってきて、送信側が続きをConsective Frameでペチャクチャ喋ります。
これによってCANの8byteのデータ長を何回も送信して4095byteのデータを送信できるようにしています。
何かの参考になれば幸いです。最後までお読みいただきありがとうございました!