Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
2d7e92b
dronecan/h7: reduce FDCAN SJW from 8 to 3
daijoubu May 23, 2026
be17792
dronecan/h7: extend PLL2 block to cover USE_DRONECAN, fix PLL2Q for F…
daijoubu May 28, 2026
98d76dc
dronecan/h7: use system PLL2 clock for FDCAN; add KAKUTEH7WING CAN su…
daijoubu May 29, 2026
b165d39
dronecan/h7: remove BusOff/ErrorPassive LOG_DEBUG spam from GetProtoc…
daijoubu May 29, 2026
d3cc8e4
dronecan/h7: fix FDCAN timing clock source and restore SJW=3
daijoubu May 29, 2026
f294f3f
dronecan/h7: remove LOG_DEBUG spam and fix PLL2 VCI range
daijoubu May 29, 2026
a1f4c99
dronecan: remove LOG_DEBUG spam from H7/F7 drivers and dronecan.c
daijoubu May 29, 2026
c21c7a2
dronecan/h7: set SJW=1 for ISO 11898-1 conformance and verified 1Mbps…
daijoubu May 29, 2026
2ea077e
dronecan: set SJW=1 in F7 driver; clean up stale comments in H7/F7 dr…
daijoubu May 29, 2026
5e07d1d
dronecan: gate GPS fix handlers on GPS_DRONECAN provider
daijoubu May 29, 2026
173b909
dronecan/f7: restore SJW=3 (hardware 4 tq); document register encodin…
daijoubu May 29, 2026
dbe3ace
dronecan: add STATE_DRONECAN_FAILED; set on CAN peripheral init failure
daijoubu May 29, 2026
5f55bec
dronecan: add FAILED to CLI state name array
daijoubu May 29, 2026
a814040
dronecan/h7: flush TX FIFO before clearing CCCR.INIT on bus-off recovery
daijoubu May 29, 2026
3865228
dronecan: disable AutoRetransmission on H7/F7 to prevent TX FIFO stall
daijoubu May 29, 2026
57f8536
dronecan/f7: check canardSTM32ComputeTimings return value
daijoubu May 29, 2026
8fa8233
dronecan: increase bus-off recovery delay from 1ms to 20ms
daijoubu May 29, 2026
af897d8
dronecan: move GPS provider guard to dispatch layer in dronecan.c
daijoubu May 29, 2026
5390248
dronecan: rate-limit protocol status check to 1Hz in NORMAL state
daijoubu May 29, 2026
da72c03
dronecan/f7: implement RecoverFromBusOff to clear sticky ESR.BOFF flag
daijoubu May 29, 2026
f1fa4c7
dronecan/h7: fix static_assert comment for PLL2M HSE divisibility check
daijoubu May 29, 2026
ef0fe3a
dronecan: rate-limit protocol status check in BUS_OFF state to 20ms c…
daijoubu May 29, 2026
cca23b3
dronecan/f7: revert RecoverFromBusOff HAL_CAN_Stop/Start — causes lockup
daijoubu May 29, 2026
5a47b87
dronecan: only log CAN status when BusOff or ErrorPassive is non-zero
daijoubu May 29, 2026
fe8d859
dronecan/f7: remove TX failure log spam
daijoubu May 29, 2026
69f6570
dronecan/gps: guard GNSSAuxiliary DOP fields against NaN and overflow
daijoubu May 29, 2026
e6e2c7c
dronecan/gps: remove lastVDOP — no vdop field in gpsSol and not EPV-c…
daijoubu May 29, 2026
3aa922f
dronecan: add STATE_DRONECAN_COUNT sentinel and assert dronecanStateN…
daijoubu May 29, 2026
d2be273
dronecan: handle STATE_DRONECAN_COUNT in switch to satisfy -Werror=sw…
daijoubu May 29, 2026
9747c8e
dronecan: fix ARRAYLEN macro name in dronecanStateNames assert
daijoubu May 30, 2026
97c939b
dronecan: clamp state index before dronecanStateNames lookup in CLI
daijoubu May 30, 2026
03b1614
dronecan/f7: document why RecoverFromBusOff is a no-op
daijoubu May 31, 2026
f916fc1
dronecan: fix sign-compare in CLI state name clamp
daijoubu May 31, 2026
48c1d7c
dronecan: fix %lu format specifier for uint32_t fields
daijoubu May 31, 2026
60b8f45
fix: use PRIu32 for uint32_t format specifiers in dronecan LOG_DEBUG
daijoubu May 31, 2026
9c1b352
fix(dronecan): send elapsed ms since last seen in MSP2_INAV_DRONECAN_…
daijoubu Jun 1, 2026
1d53383
fix(dronecan): send elapsed ms since last seen in MSP2_INAV_DRONECAN_…
daijoubu Jun 1, 2026
f52241c
dronecan/h7: TX queue mode, depth 3, ISR pump, NVIC masking
daijoubu Jun 12, 2026
8d34897
dronecan: F7 ISR-driven TX, NVIC masking, TEC/REC/LEC counters, cliDr…
daijoubu Jun 12, 2026
da08d0d
dronecan: fix H7 ISR pump, ECR TEC field, narrow TX mask window
daijoubu Jun 12, 2026
3c889c0
dronecan: reorder driver files to public API before private helpers
daijoubu Jun 12, 2026
b24900d
dronecan: address code review findings
daijoubu Jun 12, 2026
5da300c
dronecan: address third code review findings
daijoubu Jun 12, 2026
484e46f
dronecan: address fourth code review findings
daijoubu Jun 12, 2026
9c038c4
dronecan: address fifth code review findings
daijoubu Jun 12, 2026
85183b3
dronecan: address sixth code review findings
daijoubu Jun 12, 2026
9dd06ae
dronecan: fix H7 FDCAN filter type to accept all extended IDs
daijoubu Jun 12, 2026
e0b5f82
Enforce FIFO ordering to prevent spin errors in multi frame messages …
daijoubu Jun 13, 2026
cf3f98b
Allow FDCAN targets to use 18 quanta per bit solution as well at 1 MBps.
daijoubu Jun 13, 2026
6079e86
test(dronecan): add bxCAN timing unit tests for max_quanta=18 change
daijoubu Jun 13, 2026
c1b52a5
dronecan: reduce flash waste, improve GPIO config, add diagnostics
daijoubu Jun 15, 2026
08c4768
dronecan: guard STM32 CAN drivers with USE_DRONECAN
daijoubu Jun 15, 2026
37ec2ba
Set max_quanta_per_bit back to 10 for H7 targets as well
daijoubu Jun 16, 2026
1139492
dronecan: use ATOMIC_BLOCK for TX/RX critical sections, guard RX reas…
daijoubu Aug 5, 2026
0ba0114
dronecan: extract shared CAN bit-timing solver, fix stale unit test
daijoubu Aug 5, 2026
3bfbebb
test(dronecan): assert a real known value in CRC_KnownValues
daijoubu Aug 5, 2026
3433bcc
Merge branch 'maintenance-10.x' into fix/h7-dronecan-driver
daijoubu Aug 10, 2026
75f4bae
feat(dronecan): request and accept GetNodeInfo from a target node
daijoubu Aug 17, 2026
738f9ea
feat(dronecan): on-demand GetSet, ExecuteOpcode, RestartNode via shar…
daijoubu Aug 17, 2026
74a9d21
refactor(dronecan): extract async param/service client into its own m…
daijoubu Aug 16, 2026
6b6a5c9
feat(dronecan): add Dynamic Node Allocation (DNA) server
daijoubu Aug 17, 2026
acfa632
fix(dronecan): accept single-frame full-UID stage-1 delivery for DNA …
daijoubu Aug 17, 2026
a795ead
refactor(dronecan): extract NodeStatus tracking module
daijoubu Aug 16, 2026
97a0368
feat(dronecan): add GPS node-ID and battery-ID filtering with health …
daijoubu Aug 17, 2026
ca6f3d7
feat(dronecan): log node health-state transitions
daijoubu Aug 17, 2026
c64221a
fix(dronecan): correct GNSS Fix2 covariance parsing, add GNSS time pa…
daijoubu Aug 17, 2026
6ab50f8
refactor(dronecan): drop legacy GNSS Fix support and dead RTCMStream …
daijoubu Aug 18, 2026
03a1ae9
docs(dronecan): add missing dronecan_battery_id/dronecan_gps_node_id …
daijoubu Aug 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmake/stm32f7.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ main_sources(STM32F7_SRC
drivers/serial_uart_hal.c
drivers/sdcard/sdmmc_sdio_hal.c
drivers/dronecan/libcanard/canard_stm32f7xx_driver.c
drivers/dronecan/libcanard/canard_stm32_timing.c

)

Expand Down
1 change: 1 addition & 0 deletions cmake/stm32h7.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ main_sources(STM32H7_SRC
drivers/sdio.h
drivers/sdcard/sdmmc_sdio_hal.c
drivers/dronecan/libcanard/canard_stm32h7xx_driver.c
drivers/dronecan/libcanard/canard_stm32_timing.c

)

Expand Down
79 changes: 68 additions & 11 deletions docs/DroneCAN-Driver.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# DroneCAN Driver Documentation

**Last Updated:** 2026-02-16
**Last Updated:** 2026-06-06
**Status:** Complete
**Branch:** feature-dronecan-sitl
**Branch:** feature/dronecan-dna-server

---

Expand Down Expand Up @@ -115,6 +115,7 @@ CAN Bus ──[CAN interrupt]──> canardSTM32Receive() ──> shouldAcceptTr
| `UAVCAN_EQUIPMENT_GNSS_RTCMSTREAM_ID` | `handle_GNSSRCTMStream()` | RTCM Stream | Broadcast | Variable | RTK correction data |
| `UAVCAN_EQUIPMENT_POWER_BATTERYINFO_ID` | `handle_BatteryInfo()` | Battery Info | Broadcast | 1-10 Hz | Integrated with battery system |
| `UAVCAN_PROTOCOL_GETNODEINFO_ID` | `handle_GetNodeInfo()` | GetNodeInfo | Request/Response | On demand | Responds with FC firmware version |
| `UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_ID` | `dronecanDnaHandleAllocation()` | Dynamic Node Allocation | Broadcast | On connect | Assigns node IDs to anonymous peripherals via 3-stage UID handshake |

### Handler-Based Architecture

Expand Down Expand Up @@ -282,15 +283,9 @@ The driver uses the following settings from `settings.yaml`:

| Setting | Description | Type | Valid Range | Default |
|---|---|---|---|---|
| `dronecan_mode` | Enable/disable DroneCAN | bool | 0/1 | 0 |
| `dronecan_node_id` | This FC's CAN node ID | uint8 | 1-125 | 0 |
| `dronecan_baudrate` | CAN bus bitrate | enum | 0-3 | 2 (500kbps) |

**Bitrate Mapping:**
- 0 = 125 kbps
- 1 = 250 kbps
- 2 = 500 kbps (default)
- 3 = 1000 kbps
| `dronecan_node_id` | This FC's CAN node ID | uint8 | 1–127 | 1 |
| `dronecan_bitrate_kbps` | CAN bus bitrate in kbps | enum | 125, 250, 500, 1000 | 1000 |
| `dronecan_use_dna_server` | Enable automatic node ID assignment | bool | ON/OFF | ON |

#### Accessing Configuration

Expand Down Expand Up @@ -549,6 +544,67 @@ Total: 1 + nodeCount × 30 bytes.

---

## DNA Server

The DNA server (`dronecan_dna_server.c`) implements a non-redundant (single-master) Dynamic Node Allocation allocator per UAVCAN Specification §6.4.9. It is guarded by `dronecanConfig()->dronecanUseDNAServer` and can be disabled via `set dronecan_use_dna_server = OFF`.

### Three-stage handshake

A peripheral that has no node ID broadcasts anonymous Allocation messages carrying up to 6 bytes of its 16-byte hardware unique ID per stage:

| Stage | `first_part_of_unique_id` | Bytes sent |
|-------|--------------------------|------------|
| 1 | `true` | bytes 0–5 (+ optional preferred node ID) |
| 2 | `false` | bytes 6–11 |
| 3 | `false` | bytes 12–15 |

Each stage must arrive within 500 ms (`FOLLOWUP_TIMEOUT_MS`) of the previous one, or the accumulator resets and the peripheral must start over from Stage 1.

A transport capable of a larger single frame (e.g. CAN-FD) may instead send the full 16-byte UID in one message with `first_part_of_unique_id = true` and `unique_id.len = 16`; the server accepts this as Stage 1 and completes the allocation immediately without waiting for Stage 2/3.

After the full 16-byte UID has been assembled — whether over three stages or in one single-frame message — the server calls `dnaLookupOrAssignNode()`.

### Node ID assignment

`dnaLookupOrAssignNode()` applies the following priority order:

1. **Existing entry** — if the UID is already in the allocation table and the stored node ID isn't currently claimed by another live node, return the previously assigned node ID. If it *is* claimed by someone else, fall through to reassignment below.
2. **Preferred ID** — if the peripheral included a preferred node ID in Stage 1 (range 1–125; 126–127 are reserved for network maintenance tools), search upward from that ID first, then downward from `preferred - 1`, assigning the first available ID found either way.
3. **Top-down fallback** — if there's no preference, or the preferred-ID search found nothing free, scan from node ID 125 downward and assign the first available ID.

The FC's own node ID is never assigned to a peripheral. If all 32 table slots are full and no existing entry matches, the allocation fails silently.

### Allocation table persistence

The allocation table (`dnaServerData_t`) is stored in a Parameter Group (`PG_DRONECAN_DNA_SERVER`) backed by flash. A new entry calls `saveConfig()` (deferred, silent, only while disarmed) so the same peripheral receives the same node ID on every boot without re-negotiating.

The table holds up to `DRONECAN_MAX_NODES` (32) entries, each storing:

```c
typedef struct {
uint8_t uniqueId[16]; // Full 16-byte hardware UID
uint8_t nodeId; // Assigned CAN node ID (1-127)
} dnaAllocationEntry_t;
```

### Entry point

`dronecanDnaHandleAllocation()` is called from `onTransferReceived()` when a `UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_ID` broadcast arrives, but only while `dronecanUseDNAServer` is true:

```c
case UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_ID:
if (dronecanConfig()->dronecanUseDNAServer) {
dronecanDnaHandleAllocation(ins, transfer);
}
break;
```

### Unit tests

Sixteen tests in `src/test/unit/dronecan_dna_server_unittest.cc` cover the full handshake, UID re-use, table-full rejection, non-broadcast source rejection, stage ordering, timeout reset, FC node ID exclusion, preferred node ID honouring, top-down sequential assignment, reserved-range and taken-preferred-ID fallback, live-network reassignment of a stored ID, single-frame (e.g. CAN-FD) full-UID delivery, and rejection of a malformed 4-byte stage-1 message without poisoning a legitimate follow-up handshake (DNA-1 through DNA-16).

---

## Adding New Message Types

To add support for a new DroneCAN message type:
Expand Down Expand Up @@ -909,6 +965,7 @@ void broadcastNodeStatus(void) {

| Date | Version | Changes |
|---|---|---|
| 2026-06-06 | 1.3 | Added DNA server section; corrected settings table (`dronecan_bitrate_kbps`, `dronecan_use_dna_server`); added DNA allocation to message type table |
| 2026-04-30 | 1.2 | Added node table (`dronecanNodeInfo_t`), accessor functions, CLI status output, and MSP commands (0x2042/0x2043) |
| 2026-02-18 | 1.1 | Added error recovery, graceful disable behavior, and safe initialization documentation |
| 2026-02-16 | 1.0 | Initial version - handler-based architecture documentation |
Expand Down
63 changes: 51 additions & 12 deletions docs/DroneCAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ DroneCAN (formerly UAVCAN v0) is a lightweight protocol designed for reliable co
| Battery Current | Supported | Current sensing from DroneCAN battery monitors |
| Parameter Get/Set | Planned | Remote parameter configuration |
| ESC Control | Planned | Motor control via DroneCAN ESCs |
| Dynamic Node Assignment | Planned | Manage node IDs dynamically to minimize first time configuration |
| Dynamic Node Assignment | Supported | Automatically assign CAN node IDs to plug-and-play DroneCAN peripherals |

## Supported Hardware

Expand All @@ -38,10 +38,11 @@ save

| Setting | Values | Default | Description |
|---------|--------|---------|-------------|
| `dronecan_node_id` | 1-127 | 10 | CAN node ID for the flight controller |
| `dronecan_bitrate` | 125KBPS, 250KBPS, 500KBPS, 1000KBPS | 1000KBPS | CAN bus bitrate |
| `dronecan_node_id` | 1-127 | 1 | CAN node ID for the flight controller |
| `dronecan_bitrate_kbps` | 125, 250, 500, 1000 | 1000 | CAN bus bitrate in kbps |
| `dronecan_use_dna_server` | ON, OFF | ON | Enable automatic node ID assignment for plug-and-play peripherals |

All peripherals need to have the node ID and the bitrate set manually through the dronecan_gui for now. You can use your flight controller as a CAN interface by loading an Ardupilot image on it. Once the set up is complete, you can reflash it to Inav.
With `dronecan_use_dna_server = ON` (the default), peripherals that support Dynamic Node Allocation and have their node ID set to 0 (anonymous/unset) negotiate their node IDs automatically at power-up. Only the CAN bitrate needs to match across the bus. Peripherals that do not support DNA, or that already have a static node ID configured, will not send Allocation requests and must have their node ID set manually via a tool such as dronecan_gui. You can use your flight controller as a CAN interface by loading an ArduPilot image on it; once configuration is complete, reflash to INAV.

### GPS via DroneCAN

Expand Down Expand Up @@ -154,24 +155,28 @@ Setting up multiple DroneCAN peripherals on a single CAN bus:

```
# Flight Controller Configuration
set dronecan_node_id = 10 # Flight controller = node 10
set dronecan_bitrate = 1000KBPS
set dronecan_node_id = 1 # Flight controller = node 1
set dronecan_bitrate_kbps = 1000
set dronecan_use_dna_server = ON # Enable plug-and-play node assignment (default)

# Configure GPS (from node 1)
# Configure GPS
set gps_provider = DRONECAN

# Configure battery monitor (from node 2)
# Configure battery monitor
set bat_voltage_src = CAN
feature CURRENT_METER
set current_meter_type = CAN

save
```

**Peripheral Configuration (using dronecan_gui or similar tool):**
- **GPS Receiver:** Node ID = 1, Bitrate = 1000 KBPS
- **Battery Monitor:** Node ID = 2, Bitrate = 1000 KBPS
- **Potential Future Peripheral:** Node ID = 3, etc.
**Peripheral Configuration:**

With `dronecan_use_dna_server = ON`, peripherals that support Dynamic Node Allocation negotiate their node IDs automatically — no manual per-device configuration needed. Just set the CAN bitrate to match on every device.

For peripherals that do not support DNA, use dronecan_gui to assign a static node ID:
- **GPS Receiver:** Node ID = 2, Bitrate = 1000 KBPS
- **Battery Monitor:** Node ID = 3, Bitrate = 1000 KBPS

**CAN Bus Layout:**
```
Expand Down Expand Up @@ -295,6 +300,40 @@ bat_voltage_src = CAN
current_meter_type = CAN
```

## Dynamic Node Allocation (Plug and Play)

DroneCAN supports a three-stage handshake protocol (defined in UAVCAN Specification §6.4.9) that lets peripherals negotiate a unique node ID at power-up without any manual configuration. INAV implements a non-redundant (single-master) DNA server.

### How it works

1. The peripheral powers on without a node ID (anonymous mode).
2. It broadcasts an Allocation request containing the first 6 bytes of its 16-byte hardware unique ID, optionally including a preferred node ID.
3. INAV responds with an echo of the bytes received so far, prompting the peripheral to send the next 6 bytes.
4. After three stages (6 + 6 + 4 bytes), INAV has the full UID and assigns a node ID.
5. INAV stores the UID→node ID mapping in flash so the same peripheral receives the same ID on every power cycle.

All three stages must arrive within 500 ms of each other, or the handshake resets.

### Preferred node IDs

A peripheral may include a preferred node ID in its Stage 1 request. INAV honours the request if the ID is in the valid range (1–127) and not already assigned. If the requested ID is unavailable, INAV falls back to the next free ID in sequence.

### Allocation table

INAV stores up to 32 UID→node ID mappings in persistent flash storage. The table survives power cycles, so peripherals receive consistent node IDs across reboots. The table is managed automatically and is not directly user-configurable.

The flight controller's own node ID is never assigned to a peripheral.

### Enabling / disabling

```
set dronecan_use_dna_server = ON # default — plug-and-play enabled
set dronecan_use_dna_server = OFF # static node IDs only
save
```

---

## Hardware Setup

### Wiring
Expand Down
30 changes: 30 additions & 0 deletions docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,16 @@ Re-purpose the craft name field for messages.

---

### dronecan_battery_id

Only accept BatteryInfo messages whose battery_id field (battery slot) matches this value. Every value 0-255 is a valid battery_id on the wire, so there is no wildcard; the default (0) matches the conventional primary-battery ID used by most peripherals.

| Default | Min | Max |
| --- | --- | --- |
| 0 | 0 | 255 |

---

### dronecan_bitrate_kbps

The speed of the CANbus network in kbps. Set all devices to the same speed.
Expand All @@ -895,6 +905,16 @@ The speed of the CANbus network in kbps. Set all devices to the same speed.

---

### dronecan_gps_node_id

Filter GPS messages by source Node ID. 0 = use any node.

| Default | Min | Max |
| --- | --- | --- |
| 0 | 0 | 127 |

---

### dronecan_node_id

Unique identifier for this device. Valid values are 1 to 127. 126 and 127 are reserved for diagnostic tools.
Expand All @@ -905,6 +925,16 @@ Unique identifier for this device. Valid values are 1 to 127. 126 and 127 are re

---

### dronecan_use_dna_server

Enable the DNA server to manage plug and play dronecan devices

| Default | Min | Max |
| --- | --- | --- |
| ON | OFF | ON |

---

### dshot_beeper_enabled

Whether using DShot motors as beepers is enabled
Expand Down
56 changes: 36 additions & 20 deletions docs/development/msp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,8 @@ When the MSP JSON specification changes, bump `msp_messages.json` version:
[8256 - MSP2_INAV_ESC_RPM](#msp2_inav_esc_rpm)
[8257 - MSP2_INAV_ESC_TELEM](#msp2_inav_esc_telem)
[8258 - MSP2_INAV_DRONECAN_NODES](#msp2_inav_dronecan_nodes)
[8259 - MSP2_INAV_DRONECAN_NODE_INFO](#msp2_inav_dronecan_node_info)
[8259 - MSP2_INAV_DRONECAN_ASYNC_REQUEST](#msp2_inav_dronecan_async_request)
[8260 - MSP2_INAV_DRONECAN_ASYNC_RESULT](#msp2_inav_dronecan_async_result)
[8264 - MSP2_INAV_LED_STRIP_CONFIG_EX](#msp2_inav_led_strip_config_ex)
[8265 - MSP2_INAV_SET_LED_STRIP_CONFIG_EX](#msp2_inav_set_led_strip_config_ex)
[8266 - MSP2_INAV_FW_APPROACH](#msp2_inav_fw_approach)
Expand Down Expand Up @@ -4165,34 +4166,49 @@ When the MSP JSON specification changes, bump `msp_messages.json` version:
**Request Payload:** **None**

**Reply Payload:**
|Field|C Type|Size (Bytes)|Description|
|---|---|---|---|
| `nodeCount` | `uint8_t` | 1 | Number of detected DroneCAN nodes |
| `nodeData` | `dronecanNodeStatus_t[]` | array | Array of per-node status records, one per detected node. Each record: nodeID(1)+health(1)+mode(1)+last_seen_ms(4) = 7 bytes. Full detail available via MSP2_INAV_DRONECAN_NODE_INFO. |
|Field|C Type|Size (Bytes)|Units|Description|
|---|---|---|---|---|
| `nodeCount` | `uint8_t` | 1 | - | Number of detected DroneCAN nodes |
| `nodeID` | `uint8_t[]` | array | - | [per node] DroneCAN node ID (1-127) |
| `health` | `uint8_t` | 1 | - | [per node] Node health: 0=OK, 1=WARNING, 2=ERROR, 3=CRITICAL |
| `mode` | `uint8_t` | 1 | - | [per node] Node mode: 0=OPERATIONAL, 1=INITIALIZATION, 2=MAINTENANCE, 3=SOFTWARE_UPDATE, 7=OFFLINE |
| `last_seen_ms` | `uint32_t` | 4 | ms | [per node] Milliseconds since this node was last seen (FC-local timestamp delta) |
| `uptime_sec` | `uint32_t` | 4 | s | [per node] Node uptime in seconds (from NodeStatus broadcast) |
| `vendor_status_code` | `uint16_t` | 2 | - | [per node] Vendor-specific status code |

**Notes:** Requires `USE_DRONECAN`. Response is `nodeCount` followed by `nodeCount` records of 7 bytes each: nodeID(1)+health(1)+mode(1)+last_seen_ms(4). Maximum payload 1 + (DRONECAN_MAX_NODES * 7) = 225 bytes. Full node detail including uptime, vendor status, and name is available via MSP2_INAV_DRONECAN_NODE_INFO.
**Notes:** Requires `USE_DRONECAN`. Response is `nodeCount` followed by `nodeCount` records of 13 bytes each: nodeID(1)+health(1)+mode(1)+last_seen_ms(4)+uptime_sec(4)+vendor_status_code(2). Maximum payload 1 + (DRONECAN_MAX_NODES * 13) = 417 bytes. For full node detail (name, SW/HW version, unique ID) use MSP2_INAV_DRONECAN_ASYNC_REQUEST with service_id=DRONECAN_SERVICE_GETNODEINFO(1).

## <a id="msp2_inav_dronecan_node_info"></a>`MSP2_INAV_DRONECAN_NODE_INFO (8259 / 0x2043)`
**Description:** Returns full status detail for a single DroneCAN node by ID.
## <a id="msp2_inav_dronecan_async_request"></a>`MSP2_INAV_DRONECAN_ASYNC_REQUEST (8259 / 0x2043)`
**Description:** Initiates an asynchronous DroneCAN service request (GetNodeInfo, ParamGetSet, ExecuteOpcode, RestartNode) to a specific node. Result retrieved via MSP2_INAV_DRONECAN_ASYNC_RESULT.

**Request Payload:**
|Field|C Type|Size (Bytes)|Description|
|---|---|---|---|
| `nodeID` | `uint8_t` | 1 | DroneCAN node ID to query (1-127) |
| `service_id` | `uint16_t` | 2 | Service to invoke: 1=GETNODEINFO, 5=RESTART_NODE, 10=EXECUTE_OPCODE, 11=PARAM_GETSET. Transmitted as u16 for MSP alignment; only low 8 bits used. |
| `nodeID` | `uint8_t` | 1 | Target DroneCAN node ID (1-127) |

**Reply Payload:**
|Field|C Type|Size (Bytes)|Units|Description|
|---|---|---|---|---|
| `nodeID` | `uint8_t` | 1 | - | DroneCAN node ID |
| `health` | `uint8_t` | 1 | - | Node health: 0=OK, 1=WARNING, 2=ERROR, 3=CRITICAL |
| `mode` | `uint8_t` | 1 | - | Node mode: 0=OPERATIONAL, 1=INITIALIZATION, 2=MAINTENANCE, 3=SOFTWARE_UPDATE, 7=OFFLINE |
| `uptime_sec` | `uint32_t` | 4 | s | Node uptime in seconds |
| `vendor_status_code` | `uint16_t` | 2 | - | Vendor-specific status code |
| `last_seen_ms` | `uint32_t` | 4 | ms | FC millisecond timestamp when this node was last seen |
| `name_len` | `uint8_t` | 1 | - | Length of node name string (0 if unknown) |
| `name` | `char[32]` | 32 | - | Node name up to 32 bytes, zero-padded |
|Field|C Type|Size (Bytes)|Description|
|---|---|---|---|
| `accepted` | `uint8_t` | 1 | 0=request accepted; 1=busy (slot in use) or unrecognised service_id; 0xFF=bus not in STATE_DRONECAN_NORMAL (not ready) |
| `seq` | `uint8_t` | 1 | Sequence number; correlate with MSP2_INAV_DRONECAN_ASYNC_RESULT to verify the result belongs to this request |

**Notes:** Requires `USE_DRONECAN`. Initiates an async DroneCAN service request; poll MSP2_INAV_DRONECAN_ASYNC_RESULT at ~100ms intervals until state=READY(2) or ERROR(3). Only one request in-flight at a time. Service-specific request fields follow the common header in the request payload: EXECUTE_OPCODE appends opcode(u8); PARAM_GETSET appends index(u16)+is_write(u8) and optionally value_type(u8)+value(variable) for writes, then req_name_len(u8)+req_name(bytes) for named lookup. Param value encoding: INT=lo(u32)+hi(u32), FLOAT=raw(u32), BOOL=u8, STRING=len(u8)+data. Requests time out after DRONECAN_ASYNC_TIMEOUT_MS (2000ms). If bus is not in STATE_DRONECAN_NORMAL, returns accepted=0xFF without dispatching.

## <a id="msp2_inav_dronecan_async_result"></a>`MSP2_INAV_DRONECAN_ASYNC_RESULT (8260 / 0x2044)`
**Description:** Polls the result of the most recent MSP2_INAV_DRONECAN_ASYNC_REQUEST. Poll at ~100ms intervals until state is READY(2) or ERROR(3).

**Request Payload:** **None**

**Reply Payload:**
|Field|C Type|Size (Bytes)|Description|
|---|---|---|---|
| `state` | `uint8_t` | 1 | Async slot state: 0=IDLE, 1=PENDING, 2=READY, 3=ERROR |
| `seq` | `uint8_t` | 1 | Sequence number matching the originating MSP2_INAV_DRONECAN_ASYNC_REQUEST reply |
| `service_id` | `uint16_t` | 2 | Service ID of the in-flight or just-completed request |
| `node_id` | `uint8_t` | 1 | Node ID of the target |

**Notes:** Requires `USE_DRONECAN`. Returns `MSP_RESULT_ERROR` if the requested node ID is not in the node table.
**Notes:** Requires `USE_DRONECAN`. When state=READY(2), service-specific result fields follow the 5-byte common header. GETNODEINFO: name_len(u8)+name(bytes)+sw_major(u8)+sw_minor(u8)+sw_optional_field_flags(u8)+sw_vcs_commit(u32)+hw_major(u8)+hw_minor(u8)+hw_unique_id(u8[16]). PARAM_GETSET: name_len(u8)+name(bytes)+type(u8)+value(variable)+min_type(u8)+min(variable)+max_type(u8)+max(variable); value/min/max encoding: INT=lo(u32)+hi(u32), FLOAT=raw(u32), BOOL=u8, STRING=len(u8)+data; EMPTY(0) min/max type means no bound is present. EXECUTE_OPCODE and RESTART_NODE: ok(u8) where 1=success. Reading result when state=READY transitions slot back to IDLE.

## <a id="msp2_inav_led_strip_config_ex"></a>`MSP2_INAV_LED_STRIP_CONFIG_EX (8264 / 0x2048)`
**Description:** Retrieves the full configuration for each LED on the strip using the `ledConfig_t` structure. Supersedes `MSP_LED_STRIP_CONFIG`.
Expand Down
Loading
Loading