Homepage GitHub

Generic tunnelling of data

I am new to UAVCAN and i would like to use UAVCAN to tunnel data to I2C bus, SPI bus and UART.

I2C device <-> CAN node (I2C bridge) <-> CAN node (connected to a host PC)
SPI device <-> CAN node (SPI bridge) <-> CAN node (connected to a host PC)
UART device <-> CAN node (UART bridge) <-> CAN node (connected to a host PC)

Any advice on how to implement the respective CAN node with good interoperability (compliance to standards) would be much appreciated.

UAVCAN is an application-layer standard. While you can certainly use it for tunneling data, it is not exactly the kind of thing it was designed for. If using the raw CAN is for some reason undesirable, you can do the following: pick a subject-ID for your data (let’s say, 100 for I2C, 101 for SPI, 102 for UART, the numbers are chosen arbitrarily); either define your own types or use the standard uavcan.primitive.Unstructured. Make your I2C/SPI/UART node subscribe to these subjects and at the same time publsh the received data to them.

You can use PyUAVCAN to implement the logic on the host side (in Python) and libcanard to implement it on the device side (in C11/C99).

You will find practical examples in the documentation for PyUAVCAN and Libcanard.

The goal is to emulate a I2C, SPI or UART interface on the host. The idea is that this is useful in the development stage of a system by allowing to extend and test already known and supported sensors in the (UAV) system. As the sensor usage matures and proves itself to the system integrator, a node shall dedicate itself to that sensor by implementing the related messages.

From reading the docs, here is how I see this implementation.
The emulation driver on the host (master) can use uavcan.register API to configure the I2C, SPI or UART by modifying “registers” such a “baudrate”.
The available i2c devices or SPI chip selects can be seen with the uavcan.file API
Reading and modification of the remote SPI or I2C device can be done by uavcan.file.Read and uavcan.file.Modify API.
UART is somewhat different as it is an asynchronous interface, It would hence need a subject id with which any data received on the uart can be published. Writing to the UART by the host may require some specific service implementation.

This is all just early thought process as I start to read the docs. Any pointing in the right direction is much appreciated.

PS. “uavcan.primitive.Unstructured” is the datatype. What would be the subject ID / service ID? Does this use case require the creation of new ID’s?

What you described sounds okay and it will work, but you may benefit from applying some principles of service-orientation early in the design process. I expect that your host doesn’t need to be aware of which exact node is providing the tunneling service and the exact mapping of tunnels to the nodes available on the bus. You can decouple these specifics from the service definition by relying on pub/sub interactions instead of RPC (remote procedure call). For instance, the host may command an SPI transfer by publishing a message and expect a matching response transfer from the SPI bridge node. This is just a guess, though, since I am not familiar with your system.

Data types are not related to subject/service-IDs (excepting some well-defined standard use cases like the register or file services). In virtually any practical application you have to define your own subject-IDs; it helps to think of them as topics in ROS or DDS. The background is explained in https://uavcan.org/guide.

The idea here is the make the CAN node dumb and unaware about the sensor/ device connected to it. It only understands the interface that it extends (I2C / SPI / UART). The host has the complete device driver for the sensor that is connected to the CAN node. It is responsible to initializing the sensor and reading data at intervals. Of course, limitations will exist. It wouldn’t be possible to read from inertial sensors at 1khz but should be possible to read pressure sensor data at 10Hz.

For example, if I wish to add an I2C pitot pressure sensor that is already supported by ardupilot, the sensor may be connected to such a tunnel node and let ardupilot deal with the pressure sensor just as it would have if the sensor were directly connected to it’s own i2c bus. Wouldn’t such a use case be more like the RPC than pub/sub interactions?
The tunnel node driver in ardupilot presents itself an i2c bus and the reset of the code that interacts with the sensors work seamlessly so long as no assumptions (most probably, timing related) are made that are incompatible with the fact that an RPC is being performed.

You certainly can do this, but this is not what UAVCAN is for. UAVCAN is designed for distributed vehicular computing, where each participant is responsible for its own functionality, providing a clear and abstract interface for its collaborators. Please read the Interface Design Guidelines from The UAVCAN Guide (I linked it earlier).

The architecturally endorsed approach here would be to use the ArduPilot drivers directly on your node and let it implement the correct air data sensor (or whatever) network service as defined by the DS-015 standard.

Agreed. I wound consider such a CAN node only for the purpose of development, testing or making an investigation on a system. Certainly not something for a production system.

Even in such a use case, I would like the device to be standards compliant. Would that be possible?

There is currently no domain-specific standard for data tunneling over UAVCAN. If there is any demand for that, such a standard might appear one day, but it is unlikely to be driven by any of the core team members. You are welcome to try, though, if your application depends on it.

The immediate solution for you is to consult with the guidelines and build an interface yourself.