ISO-TP (ISO 15765-2) Support Library in C
This project is inspired by openxc isotp-c, but the code has been completely re-written.
This is a platform agnostic C library that implements the ISO 15765-2 (also known as ISO-TP) protocol, which runs over a CAN bus. Quoting Wikipedia:
ISO 15765-2, or ISO-TP, is an international standard for sending data packets over a CAN-Bus. The protocol allows for the transport of messages that exceed the eight byte maximum payload of CAN frames. ISO-TP segments longer messages into multiple frames, adding metadata that allows the interpretation of individual frames and reassembly into a complete message packet by the recipient. It can carry up to 4095 bytes of payload per message packet.
This library doesn't assume anything about the source of the ISO-TP messages or the underlying interface to CAN. It uses dependency injection to give you complete control.
The current version supports ISO-15765-2 single and multiple frame transmition, and works in Full-duplex mode.
Builds
Master Build
Usage
First, create some shim functions to let this library use your lower level system:
/* required, this must send a single CAN message with the given arbitration
* ID (i.e. the CAN message ID) and data. The size will never be more than 8
* bytes. */
int isotp_user_send_can(const uint32_t arbitration_id,
const uint8_t* data, const uint8_t size) {
// ...
}
/* required, return system tick, unit is millisecond */
uint32_t isotp_user_get_ms(void) {
// ...
}
/* optional, provide to receive debugging log messages */
void isotp_user_debug(const char* message, ...) {
// ...
}
API
You can use isotp-c in the following way:
/* Alloc IsoTpLink statically in RAM */
static IsoTpLink g_link;
/* Alloc send and receive buffer statically in RAM */
static uint8_t g_isotpRecvBuf[ISOTP_BUFSIZE];
static uint8_t g_isotpSendBuf[ISOTP_BUFSIZE];
int main(void) {
/* Initialize CAN and other peripherals */
/* Initialize link, 0x7TT is the CAN ID you send with */
isotp_init_link(&g_link, 0x7TT,
g_isotpSendBuf, sizeof(g_isotpSendBuf),
g_isotpRecvBuf, sizeof(g_isotpRecvBuf));
while(1) {
/* If receive any interested can message, call isotp_on_can_message to handle message */
ret = can_receive(&id, &data, &len);
/* 0x7RR is CAN ID you want to receive */
if (RET_OK == ret && 0x7RR == id) {
isotp_on_can_message(&g_link, data, len);
}
/* Poll link to handle multiple frame transmition */
isotp_poll(&g_link);
/* You can receive message with isotp_receive.
payload is upper layer message buffer, usually UDS;
payload_size is payload buffer size;
out_size is the actuall read size;
*/
ret = isotp_receive(&g_link, payload, payload_size, &out_size);
if (ISOTP_RET_OK == ret) {
/* Handle received message */
}
/* And send message with isotp_send */
ret = isotp_send(&g_link, payload, payload_size);
if (ISOTP_RET_OK == ret) {
/* Send ok */
} else {
/* An error occured */
}
/* In case you want to send data w/ functional addressing, use isotp_send_with_id */
ret = isotp_send_with_id(&g_link, 0x7df, payload, payload_size);
if (ISOTP_RET_OK == ret) {
/* Send ok */
} else {
/* Error occur */
}
}
return;
}
You can call isotp_poll as frequently as you want, as it internally uses isotp_user_get_ms to measure timeout occurences. If you need handle functional addressing, you must use two separate links, one for each.
/* Alloc IsoTpLink statically in RAM */
static IsoTpLink g_phylink;
static IsoTpLink g_funclink;
/* Allocate send and receive buffer statically in RAM */
static uint8_t g_isotpPhyRecvBuf[512];
static uint8_t g_isotpPhySendBuf[512];
/* currently functional addressing is not supported with multi-frame messages */
static uint8_t g_isotpFuncRecvBuf[8];
static uint8_t g_isotpFuncSendBuf[8];
int main(void) {
/* Initialize CAN and other peripherals */
/* Initialize link, 0x7TT is the CAN ID you send with */
isotp_init_link(&g_phylink, 0x7TT,
g_isotpPhySendBuf, sizeof(g_isotpPhySendBuf),
g_isotpPhyRecvBuf, sizeof(g_isotpPhyRecvBuf));
isotp_init_link(&g_funclink, 0x7TT,
g_isotpFuncSendBuf, sizeof(g_isotpFuncSendBuf),
g_isotpFuncRecvBuf, sizeof(g_isotpFuncRecvBuf));
while(1) {
/* If any CAN messages are received, which are of interest, call isotp_on_can_message to handle the message */
ret = can_receive(&id, &data, &len);
/* 0x7RR is CAN ID you want to receive */
if (RET_OK == ret) {
if (0x7RR == id) {
isotp_on_can_message(&g_phylink, data, len);
} else if (0x7df == id) {
isotp_on_can_message(&g_funclink, data, len);
}
}
/* Poll link to handle multiple frame transmition */
isotp_poll(&g_phylink);
isotp_poll(&g_funclink);
/* You can receive message with isotp_receive.
payload is upper layer message buffer, usually UDS;
payload_size is payload buffer size;
out_size is the actuall read size;
*/
ret = isotp_receive(&g_phylink, payload, payload_size, &out_size);
if (ISOTP_RET_OK == ret) {
/* Handle physical addressing message */
}
ret = isotp_receive(&g_funclink, payload, payload_size, &out_size);
if (ISOTP_RET_OK == ret) {
/* Handle functional addressing message */
}
/* And send message with isotp_send */
ret = isotp_send(&g_phylink, payload, payload_size);
if (ISOTP_RET_OK == ret) {
/* Send ok */
} else {
/* An error occured */
}
}
return;
}
Authors
- shen.li [email protected] (Original author!)
- Simon Cahill [email protected] (or [email protected])
License
Licensed under the MIT license.