Files
dmc/app/lib/transport/msp/msp_commands.dart
T
Constantin Leue 5fd5d0117a Show the drone's locked home point on the fly-mode map
Adds MSP_WP (118) support to query the flightcontroller's stored home
point (WP#0 is a special case for this in iNAV's getWaypoint(), per
navigation.c) - the FC reports (0,0,0) rather than an error before one
is set, so parseMspHomePoint() treats that pair as "unset". Rendered
with a small pentagon house icon (design/homepoint-pentagon.svg,
ported to a CustomPainter like the other map icons).

Deliberately not polled continuously: a new home_point_provider.dart
refreshes it only at the three moments the FC's home point can
actually change - connect/reconnect, GPS fix acquired, and arming
(iNAV's default reset_home_type=FIRST_ARM only freezes it at the first
arm; before that it continuously follows the aircraft while disarmed).

Mock's implementation offsets the point 25m from the anchor so it
doesn't sit exactly under the drone marker during UI testing.
2026-08-09 21:10:00 +02:00

162 lines
8.6 KiB
Dart

/// MSP-Befehlsnummern, geprueft gegen den tatsaechlichen iNAV-9.1.0-Quellcode
/// statt aus dem Gedaechtnis uebernommen (Doku Kommunikationsschicht
/// Abschnitt 4):
/// https://github.com/iNavFlight/inav/blob/9.1.0/src/main/msp/msp_protocol.h
/// https://github.com/iNavFlight/inav/blob/9.1.0/src/main/msp/msp_protocol_v2_inav.h
///
/// `MSP_STATUS` und `MSP_ANALOG` sind laut `msp_protocol.h` seit iNAV 9.1
/// als "DEPRECATED ... use MSP2_INAV_STATUS/MSP2_INAV_ANALOG instead. Will
/// be removed in INAV 10.0" markiert - deshalb wird hier bewusst die
/// MSP2_INAV_-Variante verwendet, nicht die MSPv1-Klassiker.
abstract final class MspCommands {
/// Response: u8 fixType, u8 numSat, i32 lat(1e-7 deg), i32 lon(1e-7 deg),
/// u16 alt(m), u16 groundSpeed(cm/s), u16 groundCourse(0.1 deg), u16 hdop.
/// (`fc_msp.c`, `case MSP_RAW_GPS`)
static const int rawGps = 106;
/// Response (4 Byte): u8 reserved, u8 maxWaypoints (NAV_MAX_WAYPOINTS),
/// u8 isWaypointListValid (`posControl.waypointListValid`), u8
/// waypointCount (`getWaypointCount()`) - Grundlage der Upload-
/// Verifikation (Doku 2.2/4.5: "Upload + verifiziert"), siehe
/// msp_waypoint_codec.dart. (`fc_msp.c`, `case MSP_WP_GETINFO`)
static const int wpGetInfo = 20;
/// Request-Payload (1 Byte): u8 wp_no. Response (21 Byte): u8 wp_no, u8
/// action, i32 lat(1e-7 deg), i32 lon(1e-7 deg), i32 alt(cm), i16 p1, i16
/// p2, i16 p3, u8 flag (`fc_msp.c`, `mspFcWaypointOutCommand()`, `case
/// MSP_WP`). Kein Batch-Kommando - ein Request pro Wegpunktnummer, analog
/// zu [setWp]. `wp_no` kennt laut `navigation.c` `getWaypoint()` zwei
/// Sonderwerte neben echten Missions-Wegpunktnummern: 0 = Homepoint
/// (`GPS_home.lat/lon/alt`, nur gueltig wenn `STATE(GPS_FIX_HOME)` -
/// sonst liefert der FC (0,0,0) statt eines Fehlers, siehe
/// msp_waypoint_codec.dart `parseMspHomePoint`), 255 = aktuelle
/// Ist-Position. Der Homepoint selbst wird vom FC NICHT beim ersten
/// GPS-Fix eingefroren, sondern folgt der Drohne kontinuierlich, solange
/// sie disarmed ist und noch nie armed war (`updateHomePosition()`) -
/// gesperrt wird er erst beim ersten Armen (iNAV-Default
/// `reset_home_type = FIRST_ARM`, `settings.yaml`).
static const int getWp = 118;
/// Request-Payload (21 Byte, siehe msp_waypoint_codec.dart
/// `encodeMspSetWaypoint`): u8 wp_no, u8 action, i32 lat(1e-7 deg),
/// i32 lon(1e-7 deg), i32 alt(cm), i16 p1, i16 p2, i16 p3, u8 flag. Nur
/// WP#1..NAV_MAX_WAYPOINTS gueltig; WP#1 setzt die Missionsliste des FC
/// zurueck (neue Mission), jede weitere WP# muss exakt die naechste sein
/// (`fc_msp.c` `case MSP_SET_WP`, `navigation.c` `setWaypoint()` - kein
/// Batch-Kommando, ein Aufruf pro Wegpunkt).
static const int setWp = 209;
/// Response: i32 estAlt(cm), i16 vario(cm/s), i32 baroAlt(cm).
/// `vario` ist `getEstimatedActualVelocity(Z)` - dieselbe Achse/dasselbe
/// Vorzeichen wie `estAlt` (`getEstimatedActualPosition(Z)`, beide aus
/// `navGetCurrentActualPositionAndVelocity()`, `navigation.c`), also
/// positiv = Steigen, negativ = Sinken.
/// (`fc_msp.c`, `case MSP_ALTITUDE`)
static const int altitude = 109;
/// Response: u8 mode, u8 state, u8 activeWpAction, u8 activeWpNumber,
/// u8 error, u16 headingHoldTarget. (`fc_msp.c`, `case MSP_NAV_STATUS`)
static const int navStatus = 121;
/// Response: u16 cycleTime, u16 i2cErrors, u16 sensorStatus,
/// u16 avgSystemLoad%, u8 (batteryProfile<<4|configProfile),
/// u32 armingFlags, 8 byte boxModeFlags, u8 mixerProfile.
/// (`fc_msp.c`, `case MSP2_INAV_STATUS`; ID aus
/// `msp_protocol_v2_inav.h`: `#define MSP2_INAV_STATUS 0x2000`)
///
/// Bit-Layout `sensorStatus` (`packSensorStatus()` in `fc_msp_box.c`) -
/// siehe [MspSensorStatusBits].
///
/// Bit-Layout `armingFlags` (`armingFlag_e` in `fc/runtime_config.h`) -
/// siehe [MspArmingFlags].
///
/// `boxModeFlags` (8 Byte = 64 Bit) ist KEIN fest verdrahtetes Bit-pro-
/// Modus-Mapping (`packBoxModeFlags()` in `fc_msp_box.c`): Bit `i` sagt
/// nur "ist der i-te Eintrag der aktuell aktiven Box-Liste gerade aktiv"
/// - welcher Modus das ist, hängt vom konkreten Firmware-Build/-Setup ab
/// (nur kompilierte/aktivierte Boxen landen in der Liste,
/// `initActiveBoxIds()`). Um ein Bit einem Modus zuzuordnen, muss der
/// Client `MSP_BOXIDS` (ID 119, `case MSP_BOXIDS: serializeBoxReply()`)
/// EINMAL beim Verbindungsaufbau abfragen: die Antwort ist ein
/// Array von `u8 permanentId` in exakt der gleichen Reihenfolge wie die
/// Bits in `boxModeFlags` - erst über diese `permanentId` lässt sich der
/// Modusname aus der festen `boxes[]`-Tabelle in `fc_msp_box.c` ablesen
/// (`permanentId` ist stabil über Firmware-Versionen, die Bit-Position
/// nicht). Für Fixed-Wing/iNAV-Missionsbetrieb relevante `permanentId`s:
/// 0 ARM, 1 ANGLE, 2 HORIZON, 10 NAV RTH, 11 NAV POSHOLD, 12 MANUAL,
/// 27 FAILSAFE, 28 NAV WP, 30 HOME RESET, 34 FLAPERON, 35 TURN ASSIST,
/// 36 NAV LAUNCH, 37 SERVO AUTOTRIM, 45 NAV COURSE HOLD, 51 PREARM,
/// 53 NAV CRUISE, 55 WP PLANNER, 56 SOARING, 59 MISSION CHANGE, 64
/// ANGLE HOLD (vollständige Tabelle: `boxes[]` in `fc_msp_box.c`, iNAV
/// 9.1.0 - noch nicht in `MspCommands` aufgenommen, da bisher ungenutzt).
static const int inavStatus = 0x2000;
/// Response (24 Byte): u8 flags, u16 batteryVoltage(0.01V),
/// u16 amperage(0.01A), u32 power, u32 mAhDrawn, u32 mWhDrawn,
/// u32 remainingCapacity, u8 batteryPercentage(0-100, bereits fertig
/// berechnet ueber `calculateBatteryPercentage()`), u16 legacyRssi
/// (0-1023, veraltete Skala - siehe stattdessen [inavLinkStats]).
/// (`fc_msp.c`, `case MSP2_INAV_ANALOG`; ID aus
/// `msp_protocol_v2_inav.h`: `#define MSP2_INAV_ANALOG 0x2002`)
static const int inavAnalog = 0x2002;
/// Response (3 Byte): u8 uplinkRssiDbm (negiert gespeichert als
/// `-rxLinkStatistics.uplinkRSSI`), u8 uplinkLinkQuality (0-100%,
/// `rxLinkStatistics.uplinkLQ` - das ist die "Receiver Link Quality"),
/// i8 uplinkSnr. (`fc_msp.c`, `case MSP2_INAV_GET_LINK_STATS`; ID aus
/// `msp_protocol_v2_inav.h`: `#define MSP2_INAV_GET_LINK_STATS 0x2103`)
static const int inavLinkStats = 0x2103;
/// Response (`MAX_TEMP_SENSORS` = 8 Slots, je i16, 0.1°C-Schritte,
/// `sensors/temperature.h`: "Temperature is returned in degC*10"):
/// ungueltiger/nicht konfigurierter Sensor liefert `-1000`
/// (`fc_msp.c`, `case MSP2_INAV_TEMPERATURES`: `sbufWriteU16(dst, valid
/// ? temperature : -1000)`, braucht `USE_TEMPERATURE_SENSOR` +
/// konfigurierte Sensoren, sonst dauerhaft `-1000` fuer alle 8 Slots).
/// ID aus `msp_protocol_v2_inav.h`:
/// `#define MSP2_INAV_TEMPERATURES 0x201E`.
static const int inavTemperatures = 0x201E;
}
/// Bits von `armingFlags` (`armingFlag_e` in `fc/runtime_config.h`, iNAV
/// 9.1.0). Nur das fuer die Telemetrie benoetigte ARMED-Bit ist hier als
/// Konstante abgebildet - der Rest der Aufzaehlung ist unten als Referenz
/// dokumentiert, falls spaeter z.B. eine "warum laesst sich nicht armen"-
/// Anzeige gebraucht wird (aktuell ungenutzt, deshalb keine eigenen
/// Konstanten dafuer):
///
/// Bit 2 `ARMED`, Bit 3 `WAS_EVER_ARMED`, Bit 4/5 Simulator-Modus
/// (HITL/SITL), danach ausschliesslich Arm-Sperrgruende
/// (`ARMING_DISABLED_*`, jeweils "warum kann/darf gerade nicht armiert
/// werden"): Bit 6 GEOZONE, 7 FAILSAFE_SYSTEM, 8 NOT_LEVEL, 9
/// SENSORS_CALIBRATING, 10 SYSTEM_OVERLOADED, 11 NAVIGATION_UNSAFE, 12
/// COMPASS_NOT_CALIBRATED, 13 ACCELEROMETER_NOT_CALIBRATED, 14 ARM_SWITCH,
/// 15 HARDWARE_FAILURE, 16 BOXFAILSAFE, 18 RC_LINK, 19 THROTTLE, 20 CLI,
/// 21 CMS_MENU, 22 OSD_MENU, 23 ROLLPITCH_NOT_CENTERED, 24 SERVO_AUTOTRIM,
/// 25 OOM, 26 INVALID_SETTING, 27 PWM_OUTPUT_ERROR, 28 NO_PREARM, 29
/// DSHOT_BEEPER, 30 LANDING_DETECTED (Bit 17 unbenutzt).
abstract final class MspArmingFlags {
static const int armed = 1 << 2;
}
/// Bits von `sensorStatus` (`packSensorStatus()` in `fc_msp_box.c`, Teil
/// der `MSP2_INAV_STATUS`-Antwort, siehe [MspCommands.inavStatus]) - Bits
/// 0-7 melden je, ob der Sensor als vorhanden/aktiviert erkannt wurde
/// (`sensors(SENSOR_*)`), unabhaengig von einer eigenen Kalibrierung/einem
/// gueltigen Messwert. Bits 8-14 unbenutzt.
abstract final class MspSensorStatusBits {
static const int acc = 1 << 0;
static const int baro = 1 << 1;
static const int mag = 1 << 2;
static const int gps = 1 << 3;
static const int rangefinder = 1 << 4;
static const int opflow = 1 << 5;
static const int pitot = 1 << 6;
static const int temp = 1 << 7;
/// `!isHardwareHealthy()` - mind. ein Sensor meldet einen Hardware-Defekt
/// (I2C-Fehler etc.), unabhaengig davon, welcher der Bits 0-7 betroffen
/// ist.
static const int hardwareFailure = 1 << 15;
}