【python-can】ISO-TPの勉強!シミュレーションで動作確認するぞ!!

みなさま、お疲れ様です。今回はpythonを使ってISO-TPのシミュレーションをしてみようと思います。ISO-TPは自動車のCAN通信を理解しようとしたら避けては通れないっすね。

あ??ISO-TP???

と思った方、むずこいですね。この記事で勉強しましょう。

ちなみに筆者が書いたCAN関連記事が以下の通りです。気になる方は是非読んでみて下さい↓。

今回は4回目。ISO-TPについての勉強と動作確認のシミュレーションをやってみます。誰かの参考になれば幸いです。

スポンサーリンク

ISO-TPとは?

まずISO-TPってなに?を簡単に理解しましょう。

wikiを読んだだけで分かった気になっているが実は何もわかってない人

ISO 15765-2[1]、またはISO-TP(Transport Layer)は、CANバスを介してデータパケットを送信するための国際規格である。このプロトコルは、CANフレームの8バイトの最大ペイロード長を超えるメッセージの転送を可能にする。ISO-TPは長いメッセージを複数のフレーム(マルチフレーム)に分割し、個々のフレームの解釈と受信者による完全なメッセージパケットへの再構成を可能にするメタデータを付与する。一つのメッセージパケットについて最大4095バイトのペイロードを搬送できる。(wikipedia「ISO 15765-2」より引用)

いや、そーゆーことが聞きてぇんじゃねぇよ!!!ってね。こんな説明に限らず、フワついた説明で煙に巻く人にはドロップキックしましょう。簡単に説明します。

まずCAN通信で送信できるデータは最大8byte(=64bit)ですよね。↓のデータフレームのデータフィールドです。

はじめてのCAN/CAN FD 図12より引用

それを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のデータを送信できるようにしています。

何かの参考になれば幸いです。最後までお読みいただきありがとうございました!