diff --git a/docs/Mavlink.md b/docs/Mavlink.md index 1e27cb6a3aa..3d57c310440 100644 --- a/docs/Mavlink.md +++ b/docs/Mavlink.md @@ -136,7 +136,7 @@ When `mavlink_port{1-4}_radio_type = MLRS`, INAV uses native receiver-emitted ML Messages are organized into MAVLink datastream groups. Each group sends one message per trigger at the configured rate. -- `SYS_STATUS`: advertises detected sensors (gyro/accel/compass, baro, pitot, GPS, optical flow, rangefinder, RC, blackbox) and whether they are healthy. Includes main loop load, battery voltage/current/percentage, and logging capability. +- `SYS_STATUS`: advertises detected sensors (gyro/accel/compass, baro, pitot, GPS, optical flow, rangefinder, RC, blackbox) and whether they are healthy. Includes main loop load, battery voltage/current/percentage, and logging capability. With no battery detected, or with `FEATURE_VBAT` off, voltage is sent as `UINT16_MAX` and remaining percentage as `-1`, the MAVLink unknown values, so a ground station shows no reading instead of 0 V or a full battery. - `RC_CHANNELS_RAW` (v1) / `RC_CHANNELS` (v2): up to 18 input channels plus RSSI mapped to MAVLink units. - `GPS_RAW_INT`: GNSS fix quality, HDOP/VDOP, velocity, satellite count, and receiver-native ellipsoid altitude and accuracy extensions when available. - `GLOBAL_POSITION_INT`: GPS position plus INAV altitude and velocity estimates. @@ -145,7 +145,7 @@ Messages are organized into MAVLink datastream groups. Each group sends one mess - `VFR_HUD`: airspeed (if a healthy pitot is available), ground speed, throttle, altitude, and climb rate. - `HEARTBEAT`: arming state plus ArduPilot-style `custom_mode` mapping from INAV flight modes. - `EXTENDED_SYS_STATE`: landed-state reporting. -- `BATTERY_STATUS`: per-cell voltages (cells 11-14 in `voltages_ext`), current draw, consumed mAh/Wh, and remaining percentage when available. +- `BATTERY_STATUS`: per-cell voltages (cells 11-14 in `voltages_ext`), current draw, consumed mAh/Wh, and remaining percentage when available. Without a detected battery the cell voltages stay at `UINT16_MAX` and remaining percentage at `-1`. - `SCALED_PRESSURE`: baro pressure and temperature data. - `SYSTEM_TIME`: boot time plus RTC Unix time when the RTC is valid. - `STATUSTEXT`: pending OSD/system messages, changed arming-disable reasons, and flight-mode transitions. OSD/system messages carry a mapped severity (NOTICE by default, WARNING when the source OSD element is inverted, CRITICAL when it blinks); arming-disable and mode notices are always sent at NOTICE severity. Repeated OSD-derived status text is suppressed per MAVLink port; changed text is sent immediately, while unchanged notice/warning/critical messages are re-announced at progressively shorter severity-based intervals (30 s / 10 s / 5 s). Changed arming-disable reasons are broadcast to every enabled port as `Arming disabled: `. INAV flight-mode transitions are broadcast as notices in the form `Notice: INAV: Entering `, with explicit `Notice: INAV: Entering GCS NAV mode` / `Notice: INAV: Exiting GCS NAV mode` transitions while GCS-assisted navigation engages or ends. See [Reconnect and status re-announcement](#reconnect-and-status-re-announcement) for how these are re-sent when a peer (re)connects. diff --git a/src/main/mavlink/mavlink_streams.c b/src/main/mavlink/mavlink_streams.c index aeea48e60e1..746cecaa5f2 100644 --- a/src/main/mavlink/mavlink_streams.c +++ b/src/main/mavlink/mavlink_streams.c @@ -412,6 +412,16 @@ void mavlinkSendExtendedSysState(void) mavlinkSendMessage(); } +// Running from USB without a battery leaves the battery state at +// BATTERY_NOT_PRESENT, where voltage and remaining capacity are meaningless. +// Reporting them as 0V/0% makes a GCS or radio announce a critical battery, +// so they are reported as unknown instead. This matches the way the OSD and +// HIGH_LATENCY2 only raise a battery warning for BATTERY_WARNING/CRITICAL. +static bool mavlinkBatteryIsPresent(void) +{ + return feature(FEATURE_VBAT) && getBatteryState() != BATTERY_NOT_PRESENT; +} + void mavlinkSendSystemStatus(void) { // Receiver is assumed to be always present @@ -529,9 +539,9 @@ void mavlinkSendSystemStatus(void) onboard_control_sensors_enabled, onboard_control_sensors_health, constrain(averageSystemLoadPercent * 10, 0, 1000), - feature(FEATURE_VBAT) ? getBatteryVoltage() * 10 : 0, + mavlinkBatteryIsPresent() ? getBatteryVoltage() * 10 : UINT16_MAX, isAmperageConfigured() ? getAmperage() : -1, - feature(FEATURE_VBAT) ? calculateBatteryPercentage() : 100, + mavlinkBatteryIsPresent() ? calculateBatteryPercentage() : -1, 0, 0, 0, @@ -821,7 +831,7 @@ void mavlinkSendBatteryStatus(void) uint16_t batteryVoltagesExt[MAVLINK_MSG_BATTERY_STATUS_FIELD_VOLTAGES_EXT_LEN]; memset(batteryVoltages, UINT16_MAX, sizeof(batteryVoltages)); memset(batteryVoltagesExt, 0, sizeof(batteryVoltagesExt)); - if (feature(FEATURE_VBAT)) { + if (mavlinkBatteryIsPresent()) { uint8_t batteryCellCount = getBatteryCellCount(); if (batteryCellCount > 0) { for (int cell = 0; cell < batteryCellCount && cell < MAVLINK_MSG_BATTERY_STATUS_FIELD_VOLTAGES_LEN + MAVLINK_MSG_BATTERY_STATUS_FIELD_VOLTAGES_EXT_LEN; cell++) { @@ -836,9 +846,6 @@ void mavlinkSendBatteryStatus(void) batteryVoltages[0] = getBatteryVoltage() * 10; } } - else { - batteryVoltages[0] = 0; - } mavlink_msg_battery_status_pack(mavSystemId, mavComponentId, &mavSendMsg, 0, @@ -849,7 +856,7 @@ void mavlinkSendBatteryStatus(void) isAmperageConfigured() ? getAmperage() : -1, isAmperageConfigured() ? getMAhDrawn() : -1, isAmperageConfigured() ? getMWhDrawn() * 36 : -1, - feature(FEATURE_VBAT) ? calculateBatteryPercentage() : -1, + mavlinkBatteryIsPresent() ? calculateBatteryPercentage() : -1, 0, 0, batteryVoltagesExt, @@ -1041,7 +1048,7 @@ void mavlinkSendHighLatency2(timeUs_t currentTimeUs) uint8_t epv = UINT8_MAX; int8_t temperatureAir = 0; int8_t climbRate = (int8_t)constrain(lrintf(getEstimatedActualVelocity(Z) / 10.0f), INT8_MIN, INT8_MAX); - int8_t battery = feature(FEATURE_VBAT) ? (int8_t)calculateBatteryPercentage() : -1; + int8_t battery = mavlinkBatteryIsPresent() ? (int8_t)calculateBatteryPercentage() : -1; #if defined(USE_GPS) if (sensors(SENSOR_GPS)