From 302f2d3454f326d22a0a5feec9ff605389d56811 Mon Sep 17 00:00:00 2001 From: Emiliyan Martinov Date: Sat, 20 Jun 2026 01:49:16 +0300 Subject: [PATCH 1/2] msp: add MSP2_ADSB_VEHICLE (per-index getter) and MSP2_ADSB_VEHICLE_COUNT MSP2_ADSB_VEHICLE_LIST omits horVelocity and downscales heading to whole degrees. A controller computing its own CPA/collision prediction needs the exact ground speed and full-resolution heading of nearby traffic. Add two small commands instead of widening the bulk list (which is broadcast over telemetry radio, so every added byte is multiplied across all vehicles): - MSP2_ADSB_VEHICLE (0x2091): request = 1-byte index; returns that vehicle's full record incl. horVelocity (cm/s) and full-res heading (centideg). O(1) slot read via findVehicle(); MSP_RESULT_ERROR for out-of-range index. - MSP2_ADSB_VEHICLE_COUNT (0x2092): returns MAX_ADSB_VEHICLES so a client knows the iteration bound without hardcoding it. Purely additive; existing commands unchanged. --- src/main/fc/fc_msp.c | 37 +++++++++++++++++++++++++++++ src/main/msp/msp_protocol_v2_inav.h | 2 ++ 2 files changed, 39 insertions(+) diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c index 3ec3f89f3f0..506f1097d96 100644 --- a/src/main/fc/fc_msp.c +++ b/src/main/fc/fc_msp.c @@ -1051,6 +1051,15 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF sbufWriteU32(dst, 0); #endif break; + + case MSP2_ADSB_VEHICLE_COUNT: +#ifdef USE_ADSB + sbufWriteU8(dst, MAX_ADSB_VEHICLES); // iteration bound for the client +#else + sbufWriteU8(dst, 0); +#endif + break; + case MSP_DEBUG: // output some useful QA statistics // debug[x] = ((hse_value / 1000000) * 1000) + (SystemCoreClock / 1000000); // XX0YY [crystal clock : core clock] @@ -4449,6 +4458,34 @@ bool mspFCProcessInOutCommand(uint16_t cmdMSP, sbuf_t *dst, sbuf_t *src, mspResu *ret = MSP_RESULT_ACK; break; +#ifdef USE_ADSB + case MSP2_ADSB_VEHICLE: + if (sbufBytesRemaining(src) >= 1) { + adsbVehicle_t *vehicle = findVehicle(sbufReadU8(src)); + if (vehicle == NULL) { // index past MAX_ADSB_VEHICLES + *ret = MSP_RESULT_ERROR; + break; + } + sbufWriteU32(dst, vehicle->vehicleValues.icao); + sbufWriteU32(dst, vehicle->vehicleValues.gps.lat); + sbufWriteU32(dst, vehicle->vehicleValues.gps.lon); + sbufWriteU32(dst, vehicle->vehicleValues.alt); + sbufWriteU16(dst, vehicle->vehicleValues.heading); // centideg, full-res + sbufWriteU16(dst, vehicle->vehicleValues.horVelocity); // cm/s - omitted by the bulk list + sbufWriteU8(dst, vehicle->vehicleValues.tslc); + sbufWriteU8(dst, vehicle->vehicleValues.emitterType); + sbufWriteU8(dst, vehicle->ttl); + for (uint8_t i = 0; i < ADSB_CALL_SIGN_MAX_LENGTH; i++) { + sbufWriteU8(dst, vehicle->vehicleValues.callsign[i]); + } + } else { + *ret = MSP_RESULT_ERROR; // no index supplied + break; + } + *ret = MSP_RESULT_ACK; + break; +#endif + #if defined(USE_FLASHFS) case MSP_DATAFLASH_READ: mspFcDataFlashReadCommand(dst, src); diff --git a/src/main/msp/msp_protocol_v2_inav.h b/src/main/msp/msp_protocol_v2_inav.h index e50115d99ed..6d09ed2a13c 100755 --- a/src/main/msp/msp_protocol_v2_inav.h +++ b/src/main/msp/msp_protocol_v2_inav.h @@ -111,6 +111,8 @@ #define MSP2_INAV_SELECT_MIXER_PROFILE 0x2080 #define MSP2_ADSB_VEHICLE_LIST 0x2090 +#define MSP2_ADSB_VEHICLE 0x2091 +#define MSP2_ADSB_VEHICLE_COUNT 0x2092 #define MSP2_INAV_CUSTOM_OSD_ELEMENTS 0x2100 #define MSP2_INAV_CUSTOM_OSD_ELEMENT 0x2101 From d088081174cb188a5c2c25df10cecd191139c0c5 Mon Sep 17 00:00:00 2001 From: Emiliyan Martinov Date: Sun, 21 Jun 2026 00:12:28 +0300 Subject: [PATCH 2/2] msp: address review for MSP2_ADSB_VEHICLE - write callsign with sbufWriteData() instead of a per-byte loop - document MSP2_ADSB_VEHICLE / MSP2_ADSB_VEHICLE_COUNT in msp_messages.json, stressing the index is an iteration cursor, not a stable handle: correlate aircraft by ICAO, never by index --- docs/development/msp/msp_messages.json | 100 +++++++++++++++++++++++++ src/main/fc/fc_msp.c | 4 +- 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/docs/development/msp/msp_messages.json b/docs/development/msp/msp_messages.json index e1b128f305d..8368373bcc1 100644 --- a/docs/development/msp/msp_messages.json +++ b/docs/development/msp/msp_messages.json @@ -10371,6 +10371,106 @@ "notes": "Requires `USE_ADSB`. Only a subset of `adsbVehicle_t` is transmitted (callsign, core values, heading in whole degrees, TSLC, emitter type, TTL).", "description": "Retrieves the list of currently tracked ADSB (Automatic Dependent Surveillance–Broadcast) vehicles. See `adsbVehicle_t` and `adsbVehicleValues_t` in `io/adsb.h` for the exact structure fields." }, + "MSP2_ADSB_VEHICLE": { + "code": 8337, + "mspv": 2, + "request": { + "payload": [ + { + "name": "index", + "ctype": "uint8_t", + "desc": "Slot index to read, `0 .. (MSP2_ADSB_VEHICLE_COUNT - 1)`. WARNING: this is an iteration cursor over fixed slots, NOT a stable identifier. The same index may return a different aircraft (or an empty slot) on a later poll. Always identify the aircraft by the `icao` field in the reply; never cache or correlate data by index. Returns an error result if the index is out of range.", + "units": "" + } + ] + }, + "reply": { + "payload": [ + { + "name": "icao", + "ctype": "uint32_t", + "desc": "ICAO 24-bit address (`vehicleValues.icao`). This is the stable per-aircraft identifier; use it to correlate replies, not the request index. An empty slot reports `icao == 0` and `ttl == 0`.", + "units": "" + }, + { + "name": "lat", + "ctype": "int32_t", + "desc": "Latitude (`vehicleValues.gps.lat`).", + "units": "1e-7 deg" + }, + { + "name": "lon", + "ctype": "int32_t", + "desc": "Longitude (`vehicleValues.gps.lon`).", + "units": "1e-7 deg" + }, + { + "name": "alt", + "ctype": "int32_t", + "desc": "Altitude above sea level (`vehicleValues.alt`).", + "units": "cm" + }, + { + "name": "heading", + "ctype": "uint16_t", + "desc": "Course over ground at full resolution (`vehicleValues.heading`). Unlike `MSP2_ADSB_VEHICLE_LIST`, this is in centidegrees, not whole degrees.", + "units": "1e-2 deg" + }, + { + "name": "horVelocity", + "ctype": "uint16_t", + "desc": "Horizontal (ground) speed (`vehicleValues.horVelocity`). Not present in `MSP2_ADSB_VEHICLE_LIST`.", + "units": "cm/s" + }, + { + "name": "tslc", + "ctype": "uint8_t", + "desc": "Time since last communication (`vehicleValues.tslc`).", + "units": "s" + }, + { + "name": "emitterType", + "ctype": "uint8_t", + "desc": "Emitter category (`vehicleValues.emitterType`).", + "units": "" + }, + { + "name": "ttl", + "ctype": "uint8_t", + "desc": "Remaining time-to-live for this slot (`adsbVehicle->ttl`). `ttl == 0` means the slot is empty/expired and its contents are stale; skip such entries.", + "units": "s" + }, + { + "name": "callsign", + "desc": "Fixed-length callsign (`vehicleValues.callsign`), padded with NULs if shorter.", + "ctype": "char", + "array": true, + "array_size": 9, + "array_size_define": "ADSB_CALL_SIGN_MAX_LENGTH", + "units": "" + } + ] + }, + "notes": "Requires `USE_ADSB`. Reads a single ADSB vehicle slot by index. THE INDEX IS NOT A STABLE HANDLE: slots are reused, so a given index may hold a different aircraft (or be empty, `ttl == 0`) between polls. Correlate aircraft by the `icao` field in the reply, never by index. Compared with the bulk `MSP2_ADSB_VEHICLE_LIST`, this message adds horizontal velocity and reports heading at full (centidegree) resolution, and orders the callsign last. Returns an error result for an out-of-range index.", + "description": "Retrieves a single tracked ADSB (Automatic Dependent Surveillance-Broadcast) vehicle by slot index. Intended for polling one slot at a time: query `MSP2_ADSB_VEHICLE_COUNT` for the iteration bound, then request indices `0 .. count-1`, skipping slots with `ttl == 0`, and identify each aircraft by its `icao`. See `adsbVehicle_t` / `adsbVehicleValues_t` in `io/adsb.h`." + }, + "MSP2_ADSB_VEHICLE_COUNT": { + "code": 8338, + "mspv": 2, + "request": null, + "reply": { + "payload": [ + { + "name": "count", + "ctype": "uint8_t", + "desc": "Number of vehicle slots to iterate (`MAX_ADSB_VEHICLES`). This is the slot capacity / iteration bound, not the number of currently active aircraft - some slots may be empty (`ttl == 0`). 0 if `USE_ADSB` is disabled.", + "units": "" + } + ] + }, + "notes": "Requires `USE_ADSB`. Returns the iteration bound for `MSP2_ADSB_VEHICLE`: request indices `0 .. count-1` and skip any slot whose `ttl == 0`.", + "description": "Returns the number of ADSB vehicle slots available to iterate with `MSP2_ADSB_VEHICLE`." + }, "MSP2_INAV_CUSTOM_OSD_ELEMENTS": { "code": 8448, "mspv": 2, diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c index 506f1097d96..eb50427ea81 100644 --- a/src/main/fc/fc_msp.c +++ b/src/main/fc/fc_msp.c @@ -4475,9 +4475,7 @@ bool mspFCProcessInOutCommand(uint16_t cmdMSP, sbuf_t *dst, sbuf_t *src, mspResu sbufWriteU8(dst, vehicle->vehicleValues.tslc); sbufWriteU8(dst, vehicle->vehicleValues.emitterType); sbufWriteU8(dst, vehicle->ttl); - for (uint8_t i = 0; i < ADSB_CALL_SIGN_MAX_LENGTH; i++) { - sbufWriteU8(dst, vehicle->vehicleValues.callsign[i]); - } + sbufWriteData(dst, vehicle->vehicleValues.callsign, ADSB_CALL_SIGN_MAX_LENGTH); } else { *ret = MSP_RESULT_ERROR; // no index supplied break;