Demo_app.py utilizing can as transport layer

Hi all,

I am fairly new to the game of CAN and Cyphal. I did the pycyphal.com demo, which works like a charm for UDP as transport layer. Now, I would like to utilize CAN as transport layer, instead of UDP. To do this I changed the code accordingly:

export UAVCAN__NODE__ID=42                           # Set the local node-ID 42 (anonymous by default)
export UAVCAN__CAN__IFACE="socketcan:can0"           # Use CAN can0 as transport layer
export UAVCAN__CAN__BITRATE="500000 5000000"         # 500kbits
export UAVCAN__CAN__MTU=16                           # Maximum Transmission Unit (largest package size for a single frame)
export UAVCAN__SUB__TEMPERATURE_SETPOINT__ID=2345    # Subject "temperature_setpoint"    on ID 2345
export UAVCAN__SUB__TEMPERATURE_MEASUREMENT__ID=2346 # Subject "temperature_measurement" on ID 2346
export UAVCAN__PUB__HEATER_VOLTAGE__ID=2347          # Subject "heater_voltage"          on ID 2347
export UAVCAN__SRV__LEAST_SQUARES__ID=123            # Service "least_squares"           on ID 123
export UAVCAN__DIAGNOSTIC__SEVERITY=2 

based on scarcely scattered information on this forum. can0 of course exists, hence

sudo ip link set can0 up type can bitrate 500000
ifconfig

returns

can0: flags=193<UP,RUNNING,NOARP>  mtu 16
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 10  (UNSPEC)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

and then when running python3 demo_app.py simply returns Running. Press Ctrl+C to stop., as expected. But when aborting, it prints

CDiagnosticPublisher(Publisher(dtype=uavcan.diagnostic.Record.1.1, transport_session=BroadcastCANOutputSession(OutputSessionSpecifier
(data_specifier=MessageDataSpecifier(subject_id=8184), remote_node_id=None), PayloadMetadata(extent_bytes=300)))) TIMEOUT 
uavcan.diagnostic.Record.1.1(timestamp=uavcan.time.SynchronizedTimestamp.1.0(microsecond=0), 
severity=uavcan.diagnostic.Severity.1.0(value=2), text="pycyphal.transport.can.media.socketcan._socketcan: 
SocketCANMedia('can0', mtu=16) FIXME: acceptance filter configuration is not yet implemented; please submit patches! 
Requested configuration: ext:xxx1x0xxxxxxxxx0101010xxxxxxx, ext:xxxxx0xxxxxxxxxxxxxxxx0")

Traceback (most recent call last):
  File "$mypath/demo_app.py", line 159, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 636, in run_until_complete
    self.run_forever()
  File "/usr/lib/python3.10/asyncio/base_events.py", line 603, in run_forever
    self._run_once()
  File "/usr/lib/python3.10/asyncio/base_events.py", line 1871, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.10/selectors.py", line 469, in select
    fd_event_list = self._selector.poll(timeout, max_ev)
KeyboardInterrupt

tells me this has not yet been implemented (FIXME: acceptance filter configuration is not yet implemented; please submit patches!), and yakut cannot echo the heartbeat as intended, doing:

export UAVCAN__CAN__IFACE="socketcan:can0"
yakut sub --with-metadata uavcan.node.heartbeat uavcan.diagnostic.record

returns nothing. ifconfig also tells me there’s nothing happening on the BUS (aka RX/TX-packets are 0). Any advice?

— Nice to Know—
When switching between UDP and CAN as transport layer, one must delete the demo_app.db, otherwise things go boomboom.
— Nice to Know —

The MTU is set incorrectly. According to ifconfig, your can0 is a Classic CAN, because its kernel MTU is 16 bytes (8 bytes of payload plus 8 bytes of overhead); whereas you’re setting the transport MTU to 16 bytes, which requires CAN FD. Set the MTU to 8 bytes to fix the problem:

export UAVCAN__CAN__MTU=8

I regularly see much confusion caused by the fact that the MTU reported by the Linux tools includes the 8 bytes of overhead.

This message is irrelevant.

1 Like

Hi @pavel.kirienko

this worked, thank you. New config is now

export UAVCAN__NODE__ID=42                           # Set the local node-ID 42 (anonymous by default)
export UAVCAN__CAN__IFACE="socketcan:can0"           # Use CAN can0 as transport layer
export UAVCAN__CAN__BITRATE="500000 5000000"         # 500kbits, 500kbits arbitration 
export UAVCAN__CAN__MTU=8                           # Maximum Transmission Unit (largest package size for a single frame)
export UAVCAN__SUB__TEMPERATURE_SETPOINT__ID=2345    # Subject "temperature_setpoint"    on ID 2345
export UAVCAN__SUB__TEMPERATURE_MEASUREMENT__ID=2346 # Subject "temperature_measurement" on ID 2346
export UAVCAN__PUB__HEATER_VOLTAGE__ID=2347          # Subject "heater_voltage"          on ID 2347
export UAVCAN__SRV__LEAST_SQUARES__ID=123            # Service "least_squares"           on ID 123
export UAVCAN__DIAGNOSTIC__SEVERITY=2 

and ifconfig now replied with

can0: flags=193<UP,RUNNING,NOARP>  mtu 16
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 10  (UNSPEC)
        RX packets 165121  bytes 1320968 (1.3 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 3  bytes 24 (24.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

i.e. there’s traffic on the CAN bus now. Every once in a while demo_app.py prints

DiagnosticPublisher(Publisher(dtype=uavcan.diagnostic.Record.1.1, 
transport_session=BroadcastCANOutputSession(OutputSessionSpecifier(data_specifier=
MessageDataSpecifier(subject_id=8184), remote_node_id=None), PayloadMetadata(extent_bytes=300)))) 
TIMEOUT uavcan.diagnostic.Record.1.1(timestamp=uavcan.time.SynchronizedTimestamp.1.0(microsecond=0), 
severity=uavcan.diagnostic.Severity.1.0(value=2), text="pycyphal.transport.can._can: 
CANTransport(SocketCANMedia('can0', mtu=8), local_node_id=42): 1 frames of 1 total with CAN ID 0x107d552a could 
not be sent before the deadline")

which I will ignore (for now). But sourcing the data types

export YAKUT_PATH="$mypath/uavcan"
export YAKUT_PATH="$mypath/sirius_cyber_corp"
# Setting up the Cyphal/UAVCAN parameters, in this case we are using Cyphal via CAN
export UAVCAN__CAN__IFACE="socketcan:can0"
yakut sub --with-metadata uavcan.node.heartbeat uavcan.diagnostic.record

still won’t print the heartbeat as expected.

demo_app.py problem —
Also, restarting demo_app.py responds with

DiagnosticPublisher(Publisher(dtype=uavcan.diagnostic.Record.1.1, transport_session=BroadcastCANOutputSession(OutputSessionSpecifier
(data_specifier=MessageDataSpecifier(subject_id=8184), remote_node_id=None), 
PayloadMetadata(extent_bytes=300)))) ERROR OSError [Errno 105] No buffer space available

Fixed by restarting can0, i.e. sudo ip link set can0 down, sudo ip link set can0 up type can bitrate 500000
demo_app.py problem —

If your can0 is a physical bus, ensure there is at least one other node available for the interface to function. Alternatively, use vcan.

1 Like

Yes can0 is a physical bus, something like CAN USB adapter, which registers as a Geschwister Schneider GmbH / Candlelight CAN USB adapter as used for Unix Kernel (or here).

Meaning for the UDP tutorial, since its’s looped(?) to localhost/127.0.0.1 it works even without a second node? I am note sure I tried yakut in UDP with or without the second node up.

Please elaborate.

The requirement to have at least one node follows from the design of the CAN bus itself and it has no relation to Cyphal. A CAN controller will keep retransmitting the frame until at least one node has confirmed its reception. Since you have no nodes on the physical bus, the frames cannot be confirmed, so transmission cannot make progress. As a result, the frames you’re attempting to transmit just get stuck in the TX queue until it’s full; this is where you get the warnings about timed out transmissions.

Virtual CAN ifaces do not require TX frames to be confirmed so they can work without physical nodes (and they can’t have physical nodes indeed since they are virtual).

UDP/IP does not require frame transmission confirmation since it is built on completely different principles.

1 Like

Thank you for the elaborate answer. I wrongly assumed yakut is just another node on the bus.