Implementation

This sections explains the python implementation of the IsoTP protocol.

Transport layer

Depending on your constraints, you may want to have the IsoTP protocol layer to run in Python (in the user space). For example, if you want to rely on python-can for the support of your CAN interface, you will need to run the IsoTP layer in Python.

In such case, the isotp.TransportLayer will be the right tool. One must first define functions to access the hardware and provide them to the isotp.TransportLayer as parameters named rxfn and txfn.

class isotp.TransportLayer(rxfn, txfn, address=None, error_handler=None, params=None)[source]

The IsoTP transport layer implementation

Parameters:
  • rxfn (Callable) – Function to be called by the transport layer to read the CAN layer. Must return a isotp.CanMessage or None if no message has been received.

  • txfn (Callable) – Function to be called by the transport layer to send a message on the CAN layer. This function should receive a isotp.CanMessage

  • address (isotp.Address) – The address information of CAN messages. Includes the addressing mode, txid/rxid, source/target address and address extension. See isotp.Address for more details.

  • error_handler (Callable) – A function to be called when an error has been detected. An isotp.IsoTpError (inheriting Exception class) will be given as sole parameter. See the Error section

  • params (dict) – List of parameters for the transport layer

If python-can must be used as CAN layer, one can use the isotp.CanStack which extends the TransportLayer object with predefined functions that calls python-can.

class isotp.CanStack(bus, timeout=0.0, *args, **kwargs)[source]

The IsoTP transport using python-can as CAN layer. python-can must be installed in order to use this class. All parameters except the bus parameter will be given to the TransportLayer constructor

Parameters:
  • bus (BusABC) – A python-can bus object implementing recv and send

  • address (isotp.Address) – The address information of CAN messages. Includes the addressing mode, txid/rxid, source/target address and address extension. See isotp.Address for more details.

  • error_handler (Callable) – A function to be called when an error has been detected. An isotp.protocol.IsoTpError (inheriting Exception class) will be given as sole parameter

  • params (dict) – List of parameters for the transport layer

The CAN messages going in and out from the transport layer are defined with isotp.CanMessage.

class isotp.CanMessage(arbitration_id=None, dlc=None, data=None, extended_id=False, is_fd=False, bitrate_switch=False)[source]

Represent a CAN message (ISO-11898)

Parameters:
  • arbitration_id (int) – The CAN arbitration ID. Must be a 11 bits value or a 29 bits value if extended_id is True

  • dlc (int) – The Data Length Code representing the number of bytes in the data field

  • data (bytearray) – The 8 bytes payload of the message

  • extended_id (bool) – When True, the arbitration ID stands on 29 bits. 11 bits when False

  • is_fd – When True, message has to be transmitted or has been received in a CAN FD frame. CAN frame when set to False


Parameters

The transport layer params parameter must be a dictionary with the following keys.

stmin (int)

default: 0

The single-byte Separation Time to include in the flow control message that the layer will send when receiving data. Refer to ISO-15765-2 for specific values. From 1 to 127, represents milliseconds. From 0xF1 to 0xF9, represents hundreds of microseconds (100us, 200us, …, 900us). 0 Means no timing requirements

blocksize (int)

default: 8

The single-byte Block Size to include in the flow control message that the layer will send when receiving data. Represents the number of consecutive frames that a sender should send before expecting the layer to send a flow control message. 0 means infinitely large block size (implying no flow control message)

tx_data_length (int)

default: 8

The maximum number of bytes that the Link Layer (CAN layer) can transport. In other words, the biggest number of data bytes possible in a single CAN message. Valid values are : 8, 12, 16, 20, 24, 32, 48, 64.

Large frames will be transmitted in small CAN messages of this size except for the last CAN message that will be as small as possible, unless padding is used.

This parameter was formely named ll_data_length but has been renamed to explicitly indicate that it affects transmitted messages only.

tx_data_min_length (int)

default: None

Sets the minimum length of CAN messages. Message with less data than this value will be padded using tx_padding byte or 0xCC if tx_padding=None.

When set to None, CAN messages will be as small as possible unless tx_data_length=8 and tx_padding != None; in that case, all CAN messages will be padded up to 8 bytes to be compliant with ISO-15765.

Valid values are : 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64.

squash_stmin_requirement (bool)

default: False

Indicates if the layer should override the receiver separation time (stmin) when sending and try sending as fast as possible instead. This can be useful when the layer is running on an operating system giving low priority to your application; such as Windows that has a thread resolution of 16ms.

rx_flowcontrol_timeout

default: 1000

The number of milliseconds to wait for a flow control frame before stopping reception and triggering a FlowControlTimeoutError. Defined as N_BS bs ISO-15765-2

rx_consecutive_frame_timeout (int)

default: 1000

The number of milliseconds to wait for a consecutive frame before stopping reception and triggering a ConsecutiveFrameTimeoutError. Defined as N_CS by ISO-15765-2

tx_padding (int or None)

default: None

When not None represents the byte used for padding messages sent. No padding applied when None unless tx_data_min_length is set or CAN FD mandatory padding is required.

wftmax (int)

default: 0

The single-byte Wait Frame Max to include in the flow control message that the layer will send when receiving data. When this limits is reached, reception will stop and trigger a MaximumWaitFrameReachedError

A value of 0 means that wait frames are not supported and none shall be sent.

max_frame_size (int)

default: 4095

The maximum frame length that the stack will accept to receive. ISO-15765-2:2016 allows frames as long as 2^32-1 (4294967295 bytes). When a FirstFrame is sent with a length longer than max_frame_size, the message will be ignored, a FlowControl message with FlowStaus=2 (Overflow) will be sent and a FrameTooLongError will be triggered.

This parameter mainly is a protection to avoid crashes due to lack of memory (caused by an external device).

can_fd (bool)

default: False

When set to True, transmitted messages will be CAN FD. CAN 2.0 when False.

Setting this parameter to True does not change the behaviour of the TransportLayer except that outputted message will have their is_fd property set to True. This parameter is just a convenience to integrate more easily with python-can

bitrate_switch (bool)

default: False

When set to True, tx message will have a flag bitrate_switch marked as True, meaning that the underlying layer shall performe a CAN FD bitrate switch after arbitration phase.

Setting this parameter to True does not change the behaviour of the TransportLayer except that outputted message will have their bitrate_switch property set to True. This parameter is just a convenience to integrate more easily with python-can

default_target_address_type (int)

default: Physical (0)

When using the TransportLayer.send method without specifying target_address_type, the value in this field will be used. The purpose of this parameter is to easily switch the address type if your program is not calling send directly; for example, if you use a library that interact with the TransportLayer object (such as a UDS client).

Can either be Physical (0) or Functional (1)

rate_limit_enable (bool)

default: False

Enable or disable the rate limiter. When disabled, no throttling is done on the output rate. When enabled, extra wait states are added in between CAN message tranmission to meet rate_limit_max_bitrate

Refer to Rate Limiter Section for more details

rate_limit_max_bitrate (int)

default: 10000000 b/s

Defines the target bitrate in Bits/seconds that the TranportLayer object should try to respect. This rate limiter only apply to the data of the output messages.

Refer to Rate Limiter Section for more details

rate_limit_window_size (float)

default: 0.2 sec

Time window used to compute the rate limit. The rate limiter algorithm works with a sliding time window. This parameter defines the width of the window. The rate limiter ensure that no more than N bits is sent within the moving window where N=(rate_limit_max_bitrate*rate_limit_window_size).

This value should be at least 50 msec for reliable behavior.

Refer to Rate Limiter Section for more details

listen_mode (bool)

default: False

When Listen Mode is enabled, the TransportLayer will correctly receive and transmit ISO-TP Frame, but will not send Flow Control message when receiving a frame. This mode of operation is usefull to listen to a transmission between two third-party devices without interferring.


Rate Limiter

The isotp.TransportLayer transmission rate limiter is a feature that allows to do some throttling on the output data rate. It works with a simple sliding window and keeps the total amount of bits sent during that time window below the maximum allowed.

../_images/rate_limiter.png

The maximum of bits allowed during the moving time window is defined by the product of rate_limit_max_bitrate and rate_limit_window_size. For example, if the target bitrate is 1000b/s and the window size is 0.1sec, then the rate limiter will keep to total amount of bits during a window of 0.1 sec below 100bits.

It is important to understand that this product also defines the maximum burst size that the isotp.TransportLayer object will output, and this is actually the original problem the rate limiter is intended to fix (See issue #61). Consider the case where a big payload of 10000 bytes must be transmitted, after the transmission of the FirstFrame, the receiving party sends a FlowControl message with BlockSize=0 and STMin=0. In that situation, the whole payload can be sent immediately but writing 10000 bytes in a single burst might be too much for the CAN driver to handle and may overflow its internal buffer. In this situation, it is useful to use the rate limiter to reduces the strain on the driver internal buffer.

In the above scenario, having a bitrate of 80000 bps and a window size of 0.1 sec would make the isotp.TransportLayer output a burst of 8000 bits (1000 bytes) every 0.1 seconds.

Warning

The bitrate defined by rate_limit_max_bitrate represent the bitrate of the CAN payload that goes out of the isotp.TransportLayer object only, the CAN layer overhead is exluded. Knowing the a classical CAN message with 11bits ID and a payload of 64 bits usually have 111 bits, the extra 47 bits of overhead will not be considered by the rate limiter. This means that even if the rate limiter is requested to keep a steady 10kbps, depending on the CAN layer configuration, the effective hardware bitrate measured might be much more significant, from 1 to 1.5x more.

Warning

Bitrate is achieved by adding extra wait states which normally translate into OS calls to Sleep(). Because an OS scheduler has a time resolution, bitrate accuracy will be poor if the specified bitrate is very low or if the window size is very small.


Usage

The isotp.TransportLayer object has the following methods

TransportLayer.send(data, target_address_type=None)[source]

Enqueue an IsoTP frame to be sent over CAN network

Parameters:
  • data (bytearray) – The data to be sent

  • target_address_type (int) – Optional parameter that can be Physical (0) for 1-to-1 communication or Functional (1) for 1-to-n. See isotp.TargetAddressType. If not provided, parameter default_target_address_type will be used (default to Physical)

Raises:
  • ValueError – Input parameter is not a bytearray or not convertible to bytearray

  • RuntimeError – Transmit queue is full

TransportLayer.recv()[source]

Dequeue an IsoTP frame from the reception queue if available.

Returns:

The next available IsoTP frame

Return type:

bytearray or None

TransportLayer.available()[source]

Returns True if an IsoTP frame is awaiting in the reception queue. False otherwise

TransportLayer.transmitting()[source]

Returns True if an IsoTP frame is being transmitted. False otherwise

TransportLayer.set_address(address)[source]

Sets the layer Address. Can be set after initialization if needed.

TransportLayer.reset()[source]

Reset the layer: Empty all buffers, set the internal state machines to Idle

TransportLayer.process()[source]

Function to be called periodically, as fast as possible. This function is non-blocking.

TransportLayer.sleep_time()[source]

Returns a value in seconds that can be passed to time.sleep() when the stack is processed in a different thread.

The value will change according to the internal state machine state, sleeping longer while idle and shorter when active.


Errors

When calling TransportLayer.process, no exception should raise. Still, errors are possible and are given to an error handler provided by the user. An error handler should be a callable function that expects an Exception as first parameter.

my_error_handler(error)
Parameters:

error (isotp.IsoTpError) – The error

All errors inherit isotp.IsoTpError which itself inherits Exception

class isotp.FlowControlTimeoutError(*args, **kwargs)[source]

Happens when the senders fails to sends a Flow Control message in time. Refer to TransportLayer parameter rx_flowcontrol_timeout

class isotp.ConsecutiveFrameTimeoutError(*args, **kwargs)[source]

Happens when the senders fails to sends a Consecutive Frame message in time. Refer to TransportLayer parameter rx_consecutive_frame_timeout

class isotp.InvalidCanDataError(*args, **kwargs)[source]

Happens when a CAN message that cannot be decoded as valid First Frame, Consecutive Frame, Single Frame or Flow Control PDU is received.

class isotp.UnexpectedFlowControlError(*args, **kwargs)[source]

Happens when a Flow Control message is received and was not expected

class isotp.UnexpectedConsecutiveFrameError(*args, **kwargs)[source]

Happens when a Consecutive Frame message is received and was not expected

class isotp.ReceptionInterruptedWithSingleFrameError(*args, **kwargs)[source]

Happens when the reception of a multi packet message reception is interrupted with a new Single Frame PDU.

class isotp.ReceptionInterruptedWithFirstFrameError(*args, **kwargs)[source]

Happens when the reception of a multi packet message reception is interrupted with a new First Frame PDU.

class isotp.WrongSequenceNumberError(*args, **kwargs)[source]

Happens when a consecutive frame is received with a wrong sequence number.

class isotp.UnsuportedWaitFrameError(*args, **kwargs)[source]

Happens when a Flow Control PDU with FlowStatus=Wait is received and wftmax is set to 0

class isotp.MaximumWaitFrameReachedError(*args, **kwargs)[source]

Happens when too much Flow Control PDU with FlowStatus=Wait is received. Refer to wftmax

class isotp.FrameTooLongError(*args, **kwargs)[source]

Happens when a FirstFrame with a length (FF_DL) longer than max_frame_size is received.

class isotp.ChangingInvalidRXDLError(*args, **kwargs)[source]

Happens when a ConsecutiveFrame is received with a length smaller than RX_DL (size of first frame) without being the last message of the IsoTP frame.

class isotp.MissingEscapeSequenceError(*args, **kwargs)[source]

Happens when a SingleFrame with length (CAN_DL) greater than 8 bytes is received and the length of the payload (SF_DL) is encoded in the first byte, which is forbidden by ISO-15765-2

class isotp.InvalidCanFdFirstFrameRXDL(*args, **kwargs)[source]

Happens when a FirstFrame is received with missing data; In other words when CAN_DL is smaller than the deduced RX_DL. The sender did not optimized the capacity usage of the CAN message.

class isotp.OverflowError(*args, **kwargs)[source]

Happens when the TransportLayer receive a FlowControl PDU with a FlowStatus=Overflow (2). In this event, the transmission is stopped.