Cyphal/Serial and Yakut

Hello everybody,

I am prototyping an application that uses Cyphal via the serial interface. My setup consists of a sender embedded board with a Cyphal application. The board is connected via UART-Serial with a Linux PC. A connection check with picocom to analyze the bytes received was successful. In my Cyphal application I have build everything needed to recreate the example message of the specification 4.4.5 and used in upon a simple heartbeat.
The receiving side is prepared with the export of the appropriate variable UAVCAN__SERIAL__IFACE="/dev/ttyUSB0". I listen for the example message or a heartbeat with the following commands of an installed Cyphal project (it works via Cyphal/Zubax/CAN, python files are present):

yakut sub 1234:uavcan.primitive.String.1 - example spec 4.4.5
yakut sub uavcan.node.Heartbeat or yakut mon

There is no recognition of any correct messages. However, yakut recognizes wrong serial frames if the frame delimiters are set from or if a 0x00 is send on error.

I also reversed the situation. Yakut mon sends a heartbeat in of itself. I let my application print it out for me. What i recognized was that compared with the specification the crc32 of the payload is missing (24 bytes for header, 11 bytes for heartbeat - frame ends).

So there are a few questions:

  • Is Yakut fully implemented for Cyphal/Serial?
  • Am I missing some set ups/commands to let Yakut work properly with Serial?
  • Is there an example like libcanard_Linux_example?

Thank you!

Please share serial port dumps from your application and from Yakut, and also let us know the version number of PyCyphal in your installation.

An example log from the board (yakut mon → board) is (all in hex):
[00][04][01][04][10][05][ff][ff][55][1d][01][01][01][01][01][01][01][01][01][01][02][80][01][03][5b][20][01][01][01][01][01][06][39][10][f8][6f][f3][00][04][01][07][10][05][ff][ff][56][1d][01][01][01][01][01][01][01][01][01][01][02][80][01][04][dd][56][06][01][01][08][01][02][55][1d][56][1d][01][01][01][03][02][40][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][02][40][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][01][02][03][01][01][01][02][40][01][04][01][04][10][06][ff][ff][55][1d][03][01][01][01][01][01][01][01][01][01][02][80][01][04][d6][83][03][01][01][01][01][06][39][48][8d][6a][4b][00][04][01][04][10][06][ff][ff][55][1d][04][01][01][01][01][01][01][01][01][01][02][80][01][04][a6][85][04][01][01][01][01][06][39][c1][3d][8f][56][00][00][04][01][04][10][06][ff][ff][55][1d][05][01][01][01][01][01][01][01][01][01][02][80][01][04][dd][e4][05][01][01][01][01][06][39][11][8c][3e][00][04][01][04][10][06][ff][ff][55][1d][06][01][01][01][01][01][01][01][01][01][02][80][01][04][50][47][06][01][01][01][01][06][39][51][64][89][86][00][04][01][04][10][06][ff][ff][55][1d][07][01][01][01][01][01][01][01][01][01][02][80][01][04][2b][26][07][01][01][01][01][06][39][99][48][8a][ee][00][04][01][04][10][06][ff][ff][55][1d][08][01][01][01][01][01][01][01][01][01][02][80][01][04][b0][4b][08][01][01][01][01][06][39][43][05][42][bd][00][04][01][04][10][06][ff][ff][55][1d][01][01][01][01][01][01][01][01][01][02][80][01][04][cb][2a][01][01][01][01][06][39][8b][29][41][d5][00][04][01][04][10][06][ff][ff][55][1d][01][01][01][01][01][01][01][01][01][02][80][01][04][46][89][01][01][01][01][06][39][d3][5c][44][6d][00][04][01][07][10][06][ff][ff][56][1d][01][01][01][01][01][01][01][01][01][01][02][80][01][04][a6][37][06][01][01][08][01][02][55][1d][56][1d][01][01][01][03][02][40][01][01][01][01][01][01][01][01][01][01][01][01][01][01][ee][0c][01][01][01][01][06][39][92][c0][a2][18][00][04][01][04][10][06][ff][ff][55][1d][01][01][01][01][01][01][01][01][01][02][80][01][04][36][8f][01][01][01][01][06][39][5a][ec][a1][70][00]
single message: [00][04][01][04][10][06][ff][ff][55][1d][06][01][01][01][01][01][01][01][01][01][02][80][01][04][50][47][06][01][01][01][01][06][39][51][64][89][86][00]

The other way around (heartbeat board → notebook):
[00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][3c][48][10][01][01][00][00][00][00][00][00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][1c][4c][10][01][01][00][00][00][00][00][00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][fc][4f][10][01][01][00][00][00][00][00][00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][dc][53][10][01][01][00][00][00][00][00][00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][dc][57][10][01][01][00][00][00][00][00][00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][bc][5b][10][01][01][00][00][00][00][00][00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][9c][5f][10][01][01][00][00][00][00][00][00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][7c][63][10][01][01][00][00][00][00][00][00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][7c][67][10][01][01][00][00][00][00][00][00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][5c][6b][10][01][01][00][00][00][00][00]
single message: [00][04][01][04][3c][05][ff][ff][d2][04][01][01][01][01][01][01][01][01][01][01][02][80][01][06][41][2e][3c][48][10][01][01][00]

PyCyphal: 1.15.4
Yakut: 0.11.1

Take the message sent by Yakut:

000401041006ffff551d06010101010101010101028001045047060101010106395164898600

Decode automatically using PyCyphal:

>>> img='000401041006ffff551d06010101010101010101028001045047060101010106395164898600'
>>> SerialFrame.parse_from_cobs_image(bytes.fromhex(img))
SerialFrame(priority=NOMINAL, transfer_id=6, index=0, end_of_transfer=True, payload=0600000000003951648986, source_node_id=16, destination_node_id=None, data_specifier=MessageDataSpecifier(subject_id=7509), user_data=0)

The message appears to be valid; however, it could be that the PyCyphal is parsing it incorrectly. To check that, parse it manually:

>>> import cobs
>>> cobs.cobs.decode(bytes.fromhex(img).strip(b'\0')).hex()
'01041000ffff551d060000000000000000000080000050470600000000003951648986'

Decompose by hand:

01                  -- version
04                  -- priority
1000                -- source node-ID = 16
ffff                -- destination node-ID = broadcast
551d                -- message via subject-ID 7509
0600000000000000    -- transfer-ID = 6
00000080            -- frame #0, end of transfer
0000                -- user data
5047                -- header CRC
06000000000039      -- payload
51648986            -- payload CRC

The message is up to spec.

Take your message:

>>> img = "000401043c05ffffd2040101010101010101010102800106412e3c4810010100"
>>> SerialFrame.parse_from_cobs_image(bytes.fromhex(img))
SerialFrame(priority=NOMINAL, transfer_id=0, index=0, end_of_transfer=True, payload=3c48100000, source_node_id=60, destination_node_id=None, data_specifier=MessageDataSpecifier(subject_id=1234), user_data=0)

The message itself is valid, but the payload CRC is invalid:

>>> from pycyphal.transport.commons.crc import CRC32C
>>> CRC32C.new(bytes.fromhex('3c48100000')).check_residue()
False

Whereas the message emitted by Yakut contains a valid CRC:

>>> CRC32C.new(bytes.fromhex('0600000000003951648986')).check_residue()
True

tl;dr your board is emitting incorrect transfer CRCs.

Thank you for analyzing the messages. The problem was an erroneous copying of the cobs buffer to the frame to be send and the crc32c algorithm itself.

The board now generates frames which return “True” upon calling “check_residue()”.

Yakut does not recognize the heartbeats (yakut mon/sub uavcan.node.Heartbeat). An example of the picocom log is given:

port is        : /dev/ttyUSB0
flowcontrol    : none
baudrate is    : 115200
parity is      : none
databits are   : 8
stopbits are   : 1

[00][04][01][04][3c][05][ff][ff][55][1d][01][01][01][01][01][01][01][01][01][01][02][80][01][06][e6][f3][9c][8a][12][01][01][01][05][3c][17][44][61][00] // a heartbeat from the board

An analysis with your method (SerialFrame.parse_from_cobs_image(bytes.fromhex(img))) returns the following:

SerialFrame(priority=NOMINAL, transfer_id=0, index=0, end_of_transfer=True, payload=9c8a12000000003c174461, source_node_id=60, destination_node_id=None, data_specifier=MessageDataSpecifier(subject_id=7509), user_data=0)

The CRC32C for this seems correct:

>>> crc32c.new(bytes.fromhex('9c8a12000000003c174461')).check_residue()
True

The notebook is set up with the following:

export UAVCAN__SERIAL__IFACE="/dev/ttyUSB0"
export UAVCAN__NODE__ID="16" // the node id found the previous post

I use the same folder for analyzing CAN which works. All public regulated data types are present.

Are there further configurations for Yakut w/ Serial or am I missing something?

There must be a lower-level issue somewhere in your stack then.

Your dumps contain brackets, where do they come from? This is suspicious.

Run Yakut with --verbose --verbose logging level to see what’s happening on the wire.

I have analyzed the serial frames with another tool. The brackets are a feature of picocom for showing hex numbers.

If the baud rate is changed to 9600 in the board’s application Yakut is able to receive heartbeats and the specification message (4.4.5 primitive String).

I noticed that approximately every 4th to 6th heartbeat/String is received. The application was changed to only send the specification message for testing. Of 20 messages around 5 will be recognised by “yakut sub …”. If another tool is used all messages appear and are exactly the same (with the delimiters in between).

I will record the situation with “yakut -v -v” on receiving the spec messages.

You clearly have a low-level problem somewhere in your setup. I recommend focusing on that rather than Yakut.

I solved the issue. I needed to export the following variable for yakut:
$ export UAVCAN__SERIAL__BAUDRATE="115200"
Nothing was changed on the microcontroller part.

Then it worked. Thank you for your support.

1 Like