diff --git a/docs/development/msp/inav_enums_ref.md b/docs/development/msp/inav_enums_ref.md
index 2d60f270183..b0941d90897 100644
--- a/docs/development/msp/inav_enums_ref.md
+++ b/docs/development/msp/inav_enums_ref.md
@@ -1994,6 +1994,7 @@
|---|---:|---|
| `NAV_WP_TAKEOFF_DATUM` | 0 | |
| `NAV_WP_MSL_DATUM` | 1 | |
+| `NAV_WP_TERRAIN_DATUM` | 2 | |
---
## `geoOriginResetMode_e`
@@ -2909,7 +2910,8 @@
| `LOGIC_CONDITION_RESET_MAG_CALIBRATION` | 54 | |
| `LOGIC_CONDITION_SET_GIMBAL_SENSITIVITY` | 55 | |
| `LOGIC_CONDITION_OVERRIDE_MIN_GROUND_SPEED` | 56 | |
-| `LOGIC_CONDITION_LAST` | 57 | |
+| `LOGIC_CONDITION_SET_ALTITUDE_TARGET` | 57 | |
+| `LOGIC_CONDITION_LAST` | 58 | |
---
## `logicWaypointOperands_e`
diff --git a/docs/development/msp/msp_messages.checksum b/docs/development/msp/msp_messages.checksum
index 98dc134e990..f90eaa7b1bf 100644
--- a/docs/development/msp/msp_messages.checksum
+++ b/docs/development/msp/msp_messages.checksum
@@ -1 +1 @@
-7db80f38dda2265704e7852630a02a83
+7601652331e47d41941de8efdcb0c1e7
diff --git a/docs/development/msp/msp_ref.md b/docs/development/msp/msp_ref.md
index 5a413a1506c..f0f3b800357 100644
--- a/docs/development/msp/msp_ref.md
+++ b/docs/development/msp/msp_ref.md
@@ -413,6 +413,7 @@ For current generation code, see [documentation project](https://github.com/xznh
[8722 - MSP2_INAV_GEOZONE_VERTEX](#msp2_inav_geozone_vertex)
[8723 - MSP2_INAV_SET_GEOZONE_VERTEX](#msp2_inav_set_geozone_vertex)
[8724 - MSP2_INAV_SET_GVAR](#msp2_inav_set_gvar)
+[8725 - MSP2_INAV_ALT_TARGET](#msp2_inav_alt_target)
[8736 - MSP2_INAV_FULL_LOCAL_POSE](#msp2_inav_full_local_pose)
[12288 - MSP2_BETAFLIGHT_BIND](#msp2_betaflight_bind)
@@ -4494,12 +4495,36 @@ For current generation code, see [documentation project](https://github.com/xznh
**Notes:** Requires `USE_GEOZONE`. Expects 10 bytes (Polygon) or 14 bytes (Circular). Returns error if indexes invalid or if trying to set vertex beyond `vertexCount` defined in `MSP2_INAV_SET_GEOZONE`. Calls `geozoneSetVertex()`. For circular zones, sets center (vertex 0) and radius (vertex 1's latitude).
+## `MSP2_INAV_ALT_TARGET (8725 / 0x2215)`
+**Description:** Get or set the active altitude hold target using updateClimbRateToAltitudeController.
+#### Variant: `get`
+
+**Description:** Get current altitude target
+
+**Request Payload:** **None**
+
+**Reply Payload:**
+|Field|C Type|Size (Bytes)|Units|Description|
+|---|---|---|---|---|
+| `altitudeDatum` | `uint8_t` | 1 | [geoAltitudeDatumFlag_e](https://github.com/iNavFlight/inav/wiki/Enums-reference#enum-geoaltitudedatumflag_e) | Default internal reference altitude datum `NAV_WP_TAKEOFF_DATUM` |
+| `altitudeTarget` | `int32_t` | 4 | cm | Current altitude target (`posControl.desiredState.pos.z`) |
+
+#### Variant: `set`
+
+**Description:** Set new altitude target
## `MSP2_INAV_SET_GVAR (8724 / 0x2214)`
**Description:** Sets the specified Global Variable (GVAR) to the provided value.
**Request Payload:**
|Field|C Type|Size (Bytes)|Units|Description|
|---|---|---|---|---|
+| `altitudeDatum` | `uint8_t` | 1 | [geoAltitudeDatumFlag_e](https://github.com/iNavFlight/inav/wiki/Enums-reference#enum-geoaltitudedatumflag_e) | Altitude reference datum flag (`geoAltitudeDatumFlag_e`): `NAV_WP_TAKEOFF_DATUM`, `NAV_WP_MSL_DATUM`, `NAV_WP_TERRAIN_DATUM` (not implemented yet) |
+| `altitudeTarget` | `int32_t` | 4 | cm | Desired altitude target according to reference datum |
+
+**Reply Payload:** **None**
+
+
+**Notes:** Empty request payload returns the current altitude target with datum. Sending a 5-byte payload sets a new target: 1 byte datum, 4 bytes altitude. Command is rejected unless altitude control is active, not landing/emergency landing, altitude estimation is valid, and datum is supported (MSL requires valid GPS origin; TERRAIN is reserved and rejected).
| `gvarIndex` | `uint8_t` | 1 | Index | Index of the Global Variable to set |
| `value` | `int32_t` | 4 | - | New value to store (clamped to configured min/max by `gvSet()`) |
diff --git a/docs/development/msp/original_msp_ref.md b/docs/development/msp/original_msp_ref.md
index d635bbc6843..209ca629481 100644
--- a/docs/development/msp/original_msp_ref.md
+++ b/docs/development/msp/original_msp_ref.md
@@ -2,7 +2,6 @@
# WARNING: DEPRECATED, OBSOLETE, FULL OF ERRORS, DO NOT USE AS REFERENCE!!!
# (OBSOLETE) INAV MSP Messages reference
-**Warning: Work in progress**\
**Generated with Gemini 2.5 Pro Preview O3-25 on source code files**\
**Verification needed, exercise caution until completely verified for accuracy and cleared**\
**Refer to source for absolute certainty**
diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c
index 936c708030d..c5a3dfb6222 100644
--- a/src/main/fc/fc_msp.c
+++ b/src/main/fc/fc_msp.c
@@ -4128,6 +4128,35 @@ bool mspFCProcessInOutCommand(uint16_t cmdMSP, sbuf_t *dst, sbuf_t *src, mspResu
*ret = mspFcGeozoneVerteciesOutCommand(dst, src);
break;
#endif
+
+#ifdef USE_BARO
+ case MSP2_INAV_ALT_TARGET:
+ {
+ if (dataSize == 0) {
+ sbufWriteU8(dst, NAV_WP_TAKEOFF_DATUM);
+ sbufWriteU32(dst, (uint32_t)lrintf(posControl.desiredState.pos.z));
+ *ret = MSP_RESULT_ACK;
+ break;
+ }
+
+ if (dataSize != (sizeof(int32_t) + sizeof(uint8_t))) {
+ *ret = MSP_RESULT_ERROR;
+ break;
+ }
+
+ const uint8_t datumFlag = sbufReadU8(src);
+ const int32_t targetAltitudeCm = (int32_t)sbufReadU32(src);
+
+ if (!navigationSetAltitudeTargetWithDatum((geoAltitudeDatumFlag_e)datumFlag, targetAltitudeCm)) {
+ *ret = MSP_RESULT_ERROR;
+ break;
+ }
+
+ *ret = MSP_RESULT_ACK;
+ break;
+ }
+#endif
+
#ifdef USE_SIMULATOR
case MSP_SIMULATOR:
tmp_u8 = sbufReadU8(src); // Get the Simulator MSP version
diff --git a/src/main/msp/msp_protocol_v2_inav.h b/src/main/msp/msp_protocol_v2_inav.h
index 696d426cd78..e0827c4392b 100755
--- a/src/main/msp/msp_protocol_v2_inav.h
+++ b/src/main/msp/msp_protocol_v2_inav.h
@@ -124,4 +124,6 @@
#define MSP2_INAV_SET_GEOZONE_VERTEX 0x2213
#define MSP2_INAV_SET_GVAR 0x2214
+#define MSP2_INAV_ALT_TARGET 0x2215
+
#define MSP2_INAV_FULL_LOCAL_POSE 0x2220
diff --git a/src/main/navigation/navigation.c b/src/main/navigation/navigation.c
index e7f0774925e..a3752233149 100644
--- a/src/main/navigation/navigation.c
+++ b/src/main/navigation/navigation.c
@@ -5201,6 +5201,33 @@ bool navigationIsControllingAltitude(void) {
return (stateFlags & NAV_CTL_ALT);
}
+bool navigationSetAltitudeTargetWithDatum(geoAltitudeDatumFlag_e datumFlag, int32_t targetAltitudeCm)
+{
+ const navigationFSMStateFlags_t stateFlags = navGetCurrentStateFlags();
+ if (!(stateFlags & NAV_CTL_ALT) || (stateFlags & NAV_CTL_LAND) || navigationIsExecutingAnEmergencyLanding() || posControl.flags.estAltStatus == EST_NONE) {
+ return false;
+ }
+
+ float targetAltitudeLocalCm;
+ switch (datumFlag) {
+ case NAV_WP_TAKEOFF_DATUM:
+ targetAltitudeLocalCm = (float)targetAltitudeCm;
+ break;
+ case NAV_WP_MSL_DATUM:
+ if (!posControl.gpsOrigin.valid) {
+ return false;
+ }
+ targetAltitudeLocalCm = (float)(targetAltitudeCm - posControl.gpsOrigin.alt);
+ break;
+ case NAV_WP_TERRAIN_DATUM:
+ default:
+ return false;
+ }
+
+ updateClimbRateToAltitudeController(0.0f, targetAltitudeLocalCm, ROC_TO_ALT_TARGET);
+ return true;
+}
+
bool navigationIsFlyingAutonomousMode(void)
{
navigationFSMStateFlags_t stateFlags = navGetCurrentStateFlags();
diff --git a/src/main/navigation/navigation.h b/src/main/navigation/navigation.h
index ddc7e23e76d..810fdc26ef8 100644
--- a/src/main/navigation/navigation.h
+++ b/src/main/navigation/navigation.h
@@ -729,7 +729,8 @@ typedef enum {
typedef enum {
NAV_WP_TAKEOFF_DATUM,
- NAV_WP_MSL_DATUM
+ NAV_WP_MSL_DATUM,
+ NAV_WP_TERRAIN_DATUM
} geoAltitudeDatumFlag_e;
// geoSetOrigin stores the location provided in llh as a GPS origin in the
@@ -779,6 +780,7 @@ bool isFixedWingAutoThrottleManuallyIncreased(void);
bool navigationIsFlyingAutonomousMode(void);
bool navigationIsExecutingAnEmergencyLanding(void);
bool navigationIsControllingAltitude(void);
+bool navigationSetAltitudeTargetWithDatum(geoAltitudeDatumFlag_e datumFlag, int32_t targetAltitudeCm);
/* Returns true if navConfig()->general.flags.rth_allow_landing is NAV_RTH_ALLOW_LANDING_ALWAYS
* or if it's NAV_RTH_ALLOW_LANDING_FAILSAFE and failsafe mode is active.
*/
diff --git a/src/main/programming/logic_condition.c b/src/main/programming/logic_condition.c
index edba68b8e94..fa1712e1f7f 100644
--- a/src/main/programming/logic_condition.c
+++ b/src/main/programming/logic_condition.c
@@ -471,6 +471,10 @@ static int logicConditionCompute(
return true;
break;
+ case LOGIC_CONDITION_SET_ALTITUDE_TARGET:
+ return navigationSetAltitudeTargetWithDatum((geoAltitudeDatumFlag_e)operandA, operandB);
+ break;
+
case LOGIC_CONDITION_FLIGHT_AXIS_ANGLE_OVERRIDE:
if (operandA >= 0 && operandA <= 2) {
diff --git a/src/main/programming/logic_condition.h b/src/main/programming/logic_condition.h
index 74ea96c98ee..e9d8eced5eb 100644
--- a/src/main/programming/logic_condition.h
+++ b/src/main/programming/logic_condition.h
@@ -86,6 +86,7 @@ typedef enum {
LOGIC_CONDITION_RESET_MAG_CALIBRATION = 54,
LOGIC_CONDITION_SET_GIMBAL_SENSITIVITY = 55,
LOGIC_CONDITION_OVERRIDE_MIN_GROUND_SPEED = 56,
+ LOGIC_CONDITION_SET_ALTITUDE_TARGET = 57,
LOGIC_CONDITION_LAST
} logicOperation_e;
diff --git a/src/main/telemetry/mavlink.c b/src/main/telemetry/mavlink.c
index d5084b32a62..13e6c0344b7 100644
--- a/src/main/telemetry/mavlink.c
+++ b/src/main/telemetry/mavlink.c
@@ -1157,7 +1157,6 @@ static bool handleIncoming_MISSION_REQUEST(void)
return false;
}
-
static bool handleIncoming_COMMAND_INT(void)
{
mavlink_command_int_t msg;
@@ -1226,6 +1225,45 @@ static bool handleIncoming_COMMAND_INT(void)
mavlinkSendMessage();
}
return true;
+#ifdef USE_BARO
+ } else if (msg.command == MAV_CMD_DO_CHANGE_ALTITUDE) {
+ const float altitudeMeters = msg.param1;
+ const uint8_t frame = (uint8_t)msg.frame;
+
+ geoAltitudeDatumFlag_e datum;
+ switch (frame) {
+ case MAV_FRAME_GLOBAL:
+ case MAV_FRAME_GLOBAL_INT:
+ datum = NAV_WP_MSL_DATUM;
+ break;
+ case MAV_FRAME_GLOBAL_RELATIVE_ALT:
+ case MAV_FRAME_GLOBAL_RELATIVE_ALT_INT:
+ datum = NAV_WP_TAKEOFF_DATUM;
+ break;
+ default:
+ mavlink_msg_command_ack_pack(mavSystemId, mavComponentId, &mavSendMsg,
+ msg.command,
+ MAV_RESULT_UNSUPPORTED,
+ 0,
+ 0,
+ mavRecvMsg.sysid,
+ mavRecvMsg.compid);
+ mavlinkSendMessage();
+ return true;
+ }
+
+ const int32_t targetAltitudeCm = (int32_t)lrintf(altitudeMeters * 100.0f);
+ const bool accepted = navigationSetAltitudeTargetWithDatum(datum, targetAltitudeCm);
+ mavlink_msg_command_ack_pack(mavSystemId, mavComponentId, &mavSendMsg,
+ msg.command,
+ accepted ? MAV_RESULT_ACCEPTED : MAV_RESULT_DENIED,
+ 0,
+ 0,
+ mavRecvMsg.sysid,
+ mavRecvMsg.compid);
+ mavlinkSendMessage();
+ return true;
+#endif
}
return false;
}
@@ -1355,7 +1393,7 @@ static bool processMAVLinkIncomingTelemetry(void)
//TODO:
//case MAVLINK_MSG_ID_COMMAND_LONG; //up to 7 float parameters
//return handleIncoming_COMMAND_LONG();
-
+
case MAVLINK_MSG_ID_COMMAND_INT: //7 parameters: parameters 1-4, 7 are floats, and parameters 5,6 are scaled integers
return handleIncoming_COMMAND_INT();
case MAVLINK_MSG_ID_MISSION_REQUEST: