lishen2 / isotp-c

An implementation of the ISO-TP (ISO15765-2) CAN protocol in C
MIT License
213 stars 79 forks source link

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

Build Status

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

License

Licensed under the MIT license.