Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 44 additions & 1 deletion docs/development/msp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,9 @@ When the MSP JSON specification changes, bump `msp_messages.json` version:
[8744 - MSP2_INAV_TIMESYNC](#msp2_inav_timesync)
[8752 - MSP2_INAV_SET_AUX_RC](#msp2_inav_set_aux_rc)
[8753 - MSP2_INAV_WIND](#msp2_inav_wind)
[8754 - MSP2_INAV_MAG_UNALIGNED](#msp2_inav_mag_unaligned)
[8755 - MSP2_INAV_ESC_SRXL2_STATUS](#msp2_inav_esc_srxl2_status)
[8756 - MSP2_INAV_ESC_SRXL2_CALIBRATE](#msp2_inav_esc_srxl2_calibrate)
[12288 - MSP2_BETAFLIGHT_BIND](#msp2_betaflight_bind)
[12289 - MSP2_RX_BIND](#msp2_rx_bind)

Expand Down Expand Up @@ -2695,7 +2698,7 @@ When the MSP JSON specification changes, bump `msp_messages.json` version:
|---|---|---|---|
| `status` | `uint8_t` | 1 | 1 if passthrough started successfully, 0 on error (e.g., port not found). For 4way, returns number of ESCs found |

**Notes:** Accepts 0 bytes (defaults to ESC 4-way) or up to 2 bytes for mode/argument. If successful, sets `mspPostProcessFn` to the appropriate handler (`mspSerialPassthroughFn` or `esc4wayProcess`). This handler takes over the serial port after the reply is sent. Requires `USE_SERIAL_4WAY_BLHELI_INTERFACE` for ESC passthrough.
**Notes:** Accepts 0 bytes (defaults to ESC 4-way) or up to 2 bytes for mode/argument. If successful, sets `mspPostProcessFn` to the appropriate handler (`mspSerialPassthroughFn` or `esc4wayProcess`). This handler takes over the serial port after the reply is sent. Requires `USE_SERIAL_4WAY_BLHELI_INTERFACE` for ESC passthrough. ESC 4-way passthrough will fail if the craft is armed: the reply carries `MSP_RESULT_ERROR` and the serial port is not handed over. Serial passthrough to another device is unaffected.

## <a id="msp_rtc"></a>`MSP_RTC (246 / 0xf6)`
**Description:** Retrieves the current Real-Time Clock time.
Expand Down Expand Up @@ -4878,6 +4881,46 @@ When the MSP JSON specification changes, bump `msp_messages.json` version:

**Notes:** Requires `USE_WIND_ESTIMATOR`; returns zeroes when wind estimation is not compiled in or not yet valid. Check bit 0 of `flags` before using speed/angle values.

## <a id="msp2_inav_mag_unaligned"></a>`MSP2_INAV_MAG_UNALIGNED (8754 / 0x2232)`
**Description:** Reads the unaligned magnetometer vector.

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

**Reply Payload:**
|Field|C Type|Size (Bytes)|Description|
|---|---|---|---|
| `magADCUnaligned` | `int16_t[3]` | 6 | X, Y and Z components, rounded to signed 16-bit values. |

**Notes:** Returns rounded mag.magADCUnaligned values before board alignment. Returns three zeroes without USE_MAG.

## <a id="msp2_inav_esc_srxl2_status"></a>`MSP2_INAV_ESC_SRXL2_STATUS (8755 / 0x2233)`
**Description:** Reads SRXL2 ESC calibration and connection status.

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

**Reply Payload:**
|Field|C Type|Size (Bytes)|Description|
|---|---|---|---|
| `phase` | `uint8_t` | 1 | Calibration phase (srxl2CalPhase_e). |
| `connected` | `uint8_t` | 1 | 1 when every opened ESC is connected; otherwise 0. |
| `lastResult` | `uint8_t` | 1 | Last calibration start result (srxl2CalResult_e). |
| `portCount` | `uint8_t` | 1 | Number of opened SRXL2 motor ports. |
| `motorCount` | `uint8_t` | 1 | Number of motors in the current mixer. |

**Notes:** Requires USE_MOTOR_SRXL2. Counts report opened motor ports and the current mixer motor count, not hardware capacity.

## <a id="msp2_inav_esc_srxl2_calibrate"></a>`MSP2_INAV_ESC_SRXL2_CALIBRATE (8756 / 0x2234)`
**Description:** Controls SRXL2 ESC throttle-range calibration.

**Request Payload:**
|Field|C Type|Size (Bytes)|Description|
|---|---|---|---|
| `phase` | `uint8_t` | 1 | Requested calibration action (srxl2CalPhase_e): 0, 1, 4 or 5. |

**Reply Payload:** **None**

**Notes:** Requires USE_MOTOR_SRXL2 and at least one request byte. Accepted commands: 0 abort, 1 automatic start, 4 manual high, 5 manual low. Driver safety checks can reject start requests; read MSP2_INAV_ESC_SRXL2_STATUS for the reason. Other command values return an MSP error. Remove propellers before calibration.

## <a id="msp2_betaflight_bind"></a>`MSP2_BETAFLIGHT_BIND (12288 / 0x3000)`
**Description:** Initiates the receiver binding procedure for supported serial protocols (CRSF, SRXL2).

Expand Down
79 changes: 78 additions & 1 deletion docs/development/msp/msp_messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -5737,7 +5737,7 @@
}
]
},
"notes": "Accepts 0 bytes (defaults to ESC 4-way) or up to 2 bytes for mode/argument. If successful, sets `mspPostProcessFn` to the appropriate handler (`mspSerialPassthroughFn` or `esc4wayProcess`). This handler takes over the serial port after the reply is sent. Requires `USE_SERIAL_4WAY_BLHELI_INTERFACE` for ESC passthrough.",
"notes": "Accepts 0 bytes (defaults to ESC 4-way) or up to 2 bytes for mode/argument. If successful, sets `mspPostProcessFn` to the appropriate handler (`mspSerialPassthroughFn` or `esc4wayProcess`). This handler takes over the serial port after the reply is sent. Requires `USE_SERIAL_4WAY_BLHELI_INTERFACE` for ESC passthrough. ESC 4-way passthrough will fail if the craft is armed: the reply carries `MSP_RESULT_ERROR` and the serial port is not handed over. Serial passthrough to another device is unaffected.",
"description": "Enables serial passthrough mode to peripherals like ESCs (BLHeli 4-way) or other serial devices."
},
"MSP_RTC": {
Expand Down Expand Up @@ -11674,6 +11674,83 @@
},
"notes": "Requires a receiver using MSP as the protocol, sends MSP2_RX_BIND to the receiver.",
"description": "Initiates binding for MSP receivers (mLRS)."
},
"MSP2_INAV_MAG_UNALIGNED": {
"code": 8754,
"mspv": 2,
"request": null,
"reply": {
"payload": [
{
"name": "magADCUnaligned",
"ctype": "int16_t",
"desc": "X, Y and Z components, rounded to signed 16-bit values.",
"units": "",
"array": true,
"array_size": 3
}
]
},
"notes": "Returns rounded mag.magADCUnaligned values before board alignment. Returns three zeroes without USE_MAG.",
"description": "Reads the unaligned magnetometer vector."
},
"MSP2_INAV_ESC_SRXL2_STATUS": {
"code": 8755,
"mspv": 2,
"request": null,
"reply": {
"payload": [
{
"name": "phase",
"ctype": "uint8_t",
"desc": "Calibration phase (srxl2CalPhase_e).",
"units": ""
},
{
"name": "connected",
"ctype": "uint8_t",
"desc": "1 when every opened ESC is connected; otherwise 0.",
"units": ""
},
{
"name": "lastResult",
"ctype": "uint8_t",
"desc": "Last calibration start result (srxl2CalResult_e).",
"units": ""
},
{
"name": "portCount",
"ctype": "uint8_t",
"desc": "Number of opened SRXL2 motor ports.",
"units": ""
},
{
"name": "motorCount",
"ctype": "uint8_t",
"desc": "Number of motors in the current mixer.",
"units": ""
}
]
},
"notes": "Requires USE_MOTOR_SRXL2. Counts report opened motor ports and the current mixer motor count, not hardware capacity.",
"description": "Reads SRXL2 ESC calibration and connection status."
},
"MSP2_INAV_ESC_SRXL2_CALIBRATE": {
"code": 8756,
"mspv": 2,
"request": {
"payload": [
{
"name": "phase",
"ctype": "uint8_t",
"desc": "Requested calibration action (srxl2CalPhase_e): 0, 1, 4 or 5.",
"units": ""
}
]
},
"reply": null,
"notes": "Requires USE_MOTOR_SRXL2 and at least one request byte. Accepted commands: 0 abort, 1 automatic start, 4 manual high, 5 manual low. Driver safety checks can reject start requests; read MSP2_INAV_ESC_SRXL2_STATUS for the reason. Other command values return an MSP error. Remove propellers before calibration.",
"description": "Controls SRXL2 ESC throttle-range calibration."
}
}
}
25 changes: 18 additions & 7 deletions src/main/fc/fc_msp.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ static void mspSerialPassthroughFn(serialPort_t *serialPort)
}
}

static void mspFcSetPassthroughCommand(sbuf_t *dst, sbuf_t *src, mspPostProcessFnPtr *mspPostProcessFn)
static mspResult_e mspFcSetPassthroughCommand(sbuf_t *dst, sbuf_t *src, mspPostProcessFnPtr *mspPostProcessFn)
{
const unsigned int dataSize = sbufBytesRemaining(src); /* Payload size in Bytes */

Expand Down Expand Up @@ -254,6 +254,11 @@ static void mspFcSetPassthroughCommand(sbuf_t *dst, sbuf_t *src, mspPostProcessF
break;
#ifdef USE_SERIAL_4WAY_BLHELI_INTERFACE
case MSP_PASSTHROUGH_ESC_4WAY:
// entering the 4way interface stops the motor outputs, refuse while armed
if (ARMING_FLAG(ARMED)) {
return MSP_RESULT_ERROR;
}

// get channel number
// switch all motor lines HI
// reply with the count of ESC found
Expand All @@ -267,6 +272,8 @@ static void mspFcSetPassthroughCommand(sbuf_t *dst, sbuf_t *src, mspPostProcessF
default:
sbufWriteU8(dst, 0);
}

return MSP_RESULT_ACK;
}

static void mspRebootNormalFn(serialPort_t *serialPort)
Expand Down Expand Up @@ -377,17 +384,22 @@ static void serializeDataflashReadReply(sbuf_t *dst, uint32_t address, uint16_t

// size will be lower than that requested if we reach end of volume
const uint32_t flashfsSize = flashfsGetSize();
if (readLen > flashfsSize - address) {
if (address >= flashfsSize) {
// nothing left to read from this address
readLen = 0;
} else if (readLen > flashfsSize - address) {
// truncate the request
readLen = flashfsSize - address;
}

// Write address
sbufWriteU32(dst, address);

// Read into streambuf directly
const int bytesRead = flashfsReadAbs(address, sbufPtr(dst), readLen);
sbufAdvance(dst, bytesRead);
if (readLen > 0) {
// Read into streambuf directly
const int bytesRead = flashfsReadAbs(address, sbufPtr(dst), readLen);
sbufAdvance(dst, bytesRead);
}
}
#endif

Expand Down Expand Up @@ -5157,8 +5169,7 @@ mspResult_e mspFcProcessCommand(mspPacket_t *cmd, mspPacket_t *reply, mspPostPro
} else if (mspFcProcessOutCommand(cmdMSP, dst, mspPostProcessFn)) {
ret = MSP_RESULT_ACK;
} else if (cmdMSP == MSP_SET_PASSTHROUGH) {
mspFcSetPassthroughCommand(dst, src, mspPostProcessFn);
ret = MSP_RESULT_ACK;
ret = mspFcSetPassthroughCommand(dst, src, mspPostProcessFn);
} else if (cmdMSP == MSP_REBOOT) {
if (!ARMING_FLAG(ARMED)) {
ret = mspFcRebootCommand(src, mspPostProcessFn);
Expand Down
7 changes: 7 additions & 0 deletions src/main/telemetry/msp_shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ bool handleMspFrame(uint8_t *const frameStart, const int payloadLength)
}

if (payloadLength < MIN_LENGTH_CHUNK) {
mspStarted = 0;
return false; // prevent analyzing garbage data
}

Expand All @@ -135,14 +136,20 @@ bool handleMspFrame(uint8_t *const frameStart, const int payloadLength)
lastRequestVersion = (status & TELEMETRY_MSP_VER_MASK) >> TELEMETRY_MSP_VER_SHIFT;

if (lastRequestVersion > TELEMETRY_MSP_VERSION) {
mspStarted = 0;
sendMspErrorResponse(TELEMETRY_MSP_VER_MISMATCH, 0);
return true;
}

if (status & TELEMETRY_MSP_START_MASK) { // first packet in sequence
// A new start supersedes any partial request, even if its own header is malformed.
mspStarted = 0;
uint16_t mspPayloadSize;

if (lastRequestVersion == 1) { // MSPv1
if (payloadLength < MIN_LENGTH_REQUEST_V1) {
return false; // prevent analyzing garbage data
}
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.

mspPayloadSize = frameStart[MSP_INDEX_SIZE_V1];
requestPacket->cmd = frameStart[MSP_INDEX_ID_V1];
Expand Down
1 change: 1 addition & 0 deletions src/main/telemetry/smartport.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ void checkSmartPortTelemetryState(void)
#if defined(USE_MSP_OVER_TELEMETRY)
static void smartPortSendMspResponse(uint8_t *data, const uint8_t dataSize) {
smartPortPayload_t payload;
memset(&payload, 0, sizeof(payload)); // don't leak stack content in the unused bytes
payload.frameId = FSSP_MSPS_FRAME;
memcpy(&payload.valueId, data, MIN(dataSize,SMARTPORT_MSP_PAYLOAD_SIZE));

Expand Down
Loading