dronecan / DSDL

DSDL Protocol Description files for DroneCAN
MIT License
27 stars 63 forks source link

Proposal: Add control reception support #21

Open David-OConnor opened 1 year ago

David-OConnor commented 1 year ago

I propose adding control channel data reception to the DroneCAN protocol. This post is a collaboration between Hydra and myself.

This is a common use case, and would be a good fit for various vehicle types that use DroneCAN. I’m also interested in Cyphal support, but will focus on DroneCAN for now. I have concerns about Cyphal’s complexity that may delay or preclude using it.

Requirements:

I'm unclear on the exact capabilities of DroneCAN and Cyphal (especially the latter), and use cases that may arise. Below are two starting example implementations, that represent different ends of a flexibility spectrum. They would both be sufficient for the canonical use case described above, but different in applications beyond it:

#1: A simple, partially hard-coded standard, analogous to the CRSF protocol. I imagine this will integrate in a straightforward way with DroneCAN. I’ve put this together by following examples on the DroneCAN List of standard data types. Of note, it sends all control channel data in the same packet, without distinguishing priority.

Example packet, using the DroneCAN List of standard data types:

uint16 [<=10] channel_high_precision     # High precision channel data. For example, throttle or pitch

uint8 [<=10] channel_low_precision        # Low precision channel data. For example, arm status or flight mode

Of note, I’m not sure how to make precision flexible using the example standard data types; in this example, I’ve hard-coded 2 precision levels. Compared to existing standards that use 11-13 bits of data for control channel data, this is higher precision, so uses more bandwidth/larger packet size than required. How would you handle this, using the DroneCAN spec?

Example link stats packet, sent at a slower rate, hard-coded values and precision; this is what CRSF uses. I am not proposing it specifically, but it’s an example of hard-coding, using a current standard. Of note, it is inflexible:

uint32 timestamp
uint8 uplink_rssi_1
uint8 uplink_rssi_2
uint7 uplink_link_quality
int8 uplink_snr
uint8 active_antenna
int8 rf_mode
int4 uplink_tx_power
uint8 download rssi
uint8 downlink_link_quality
int8 downlink_snr

#2: An ideal, flexible standard. I would prefer something like this, but am not sure if it fits the model of DroneCAN and/or Cyphal:

By default, the node is silent. The entity receiving data (eg flight controller) submits a discovery request. The Rx responds with a list of information it can supply, including channel indices, available resolutions, and available data rates for each channel. Given this info, the FC can choose which info it wants to receive based on the Rx’s capabilities, which it knows due to the response to discovery. The subscribe request will always succeed, since the flight controller has the information it needs to make it.

Example of available information: #1: The channel number (to be interpreted by the Rx node). #2: Data rate. #3: resolution. The node responds with a success or error message, per its ability to serve this request. If success, the node broadcasts the data as requested until it receives a cancellation request. Example subscription request:

[
    Subscription index/reference = 1,
    Rate: 100Hz,
    Channel 1, 12 bits,
    Channel 2, 12 bits
]

[
    Subscription index/reference = 2,
    Rate: 50Hz,
    Channel 5, 2 bits,
]

[
    Subscription index/reference = 3,
    Rate: 10Hz,
    Channel 6, 2 bits,
    Channel 17, 1 bit,
    Channel 18, 1 bit,
]

Example responses here. E.g.

[
  Subscription index/reference = 1,
  <12 bits of data for channel 1>,
  <12 bits of data for channel 2>,
]

[
  Subscription index/reference = 2,
  <2 bits of data for channel 5>,
]

[
  Subscription index/reference = 3,
  <2 bits of data for channel 6>,
  <1 bits of data for channel 17>,
  <1 bits of data for channel 18>,
]

The sequence of packets will be: 1,1,1,1…,2,1,1,1…,2,1,1,1… eventually 3,1,1,1…, 2,1,1,1…

The flexible approach would be robust to changing requirements for different use cases, and allow the subscriber (eg flight controller) to only receive the data it needs, at the rate it's capable of receiving. It would minimize the amount of data sent, saving bandwidth. Of note, the FC could make multiple subscriptions, each with a reference that is included in the subscribe request; the same reference is returned when the Rx sends data, so the FC knows what data, and the format of it for decoding purposes.

I think some combination of the approaches may be viable. For example, a flexible subscription setup, but restricting the number of data rates to 2.

Of note, I think for many cases of channel data, 2+ frames may be required on FDCAN. If this addition is made available for basic CAN, multi-frame packets would be required in all cases. This begs the question: Should this be backwards compatible with basic CAN, or should it be FDCAN only? Suggested approach: A discovery agreement between the Rx and FC, allowing either to be used. The subscribe can choose the frame format (CAN/FDCAN) too.

Relevant discussion on the TBS github; about improving the CRSF spec, but is immediately applicable here: https://github.com/tbs-fpv/freedomtx/issues/26#issuecomment-1435738854

Would appreciate any and all input on establishing a standard that’s simple and flexible. Of note, I already have a working CAN receiver, using an ExpressLRS circuit integrated with an STM32 MCU that acts as the FDCAN node, and a CAN transceiver. Using this for specific hardware where both Rx and flight controller are cooperative is easy; my intent here is establishing a common API that will allow CAN Rxes to be swapped arbitrarily, with no change to flight-controller code.