samcrow / canadensis

A Rust implementation of Cyphal
Apache License 2.0
44 stars 5 forks source link

CAN loopback frames #11

Closed chemicstry closed 1 year ago

chemicstry commented 2 years ago

The library works great, however, I would like to use time synchronisation over canbus. The time synchronisation master requires loopback frames to know the precise transmission timestamp of its messages, but they don't seem to be implemented in canadensis. I would gladly make a PR, but I'm not sure what would be the best way to approach this.

The simlest solution without modifying canadensis internals would be to loopback everything as regular received messages and then check if source id matches current node id in TransferHandler::handle_message. But I'm not sure if this is the best approach, because it could cause weird bugs in other handlers if source id is not checked. It would also act weird in case of a node id conflict.

Another solution would be to add loopback flag to frames (maybe in Transfer and also in canadensis_can::Frame) and then have an additional TransferHandler::handle_loopback(&mut self, node: &mut N, transfer: &Transfer) method. This is similar to how pycyphal does. This way loopback frames can be separated and don't interfere with regular frames.

Any thoughts?

samcrow commented 2 years ago

Adding a loopback flag sounds like a good idea.

The only problem I can think of is that some CAN controllers (bxCAN, MCP2515, maybe others) have loopback mode as a global switch and can't loopback specific frames only. The best solution might be documentation saying that some drivers may ignore the loopback flag.

Out of curiosity, what CAN controller are you using that can do loopback for specific frames?

chemicstry commented 2 years ago

I'm using bxCAN and it supports loopback only in debug mode (RX is disconnected). However, I'm aiming for a similar implementation as in the old libuavcan, where loopback is implemented in software and TX timestamp is captured in TX mailbox empty interrupt. This is not ideal, but if critical sections in software are short, you can get microsecond precision time synchronisation.

pavel-kirienko commented 2 years ago

It is reasonable to enable loopback in the driver for all frames and then silently drop all loopback frames except those few that were requested by the application to be reported back. It was implemented this way in the original libuavcan driver for SocketCAN, for example.

samcrow commented 2 years ago

Now that I understand the idea of using the interrupt to get a timestamp and do software loopback, this makes more sense. The driver part (canadensis_bxcan or similar) might be slightly challenging, but the rest of the changes seem simple.