Skip to content

Commit c595afb

Browse files
author
b14ckyy
committed
Add MSP2_INAV_SET_AUX_RC (0x2230)
1 parent 76809d7 commit c595afb

File tree

5 files changed

+154
-0
lines changed

5 files changed

+154
-0
lines changed

docs/development/msp/msp_messages.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10943,6 +10943,32 @@
1094310943
"notes": "All attitude angles are in deci-degrees.",
1094410944
"description": "Provides estimates of current attitude, local NEU position, and velocity."
1094510945
},
10946+
"MSP2_INAV_SET_AUX_RC": {
10947+
"code": 8752,
10948+
"mspv": 2,
10949+
"request": {
10950+
"payload": [
10951+
{
10952+
"name": "definitionByte",
10953+
"ctype": "uint8_t",
10954+
"desc": "Packed start channel and resolution. Bits 7-3: start channel index (valid range 8-31 for CH9-CH32; 0-7 rejected as error). Bits 2-0: resolution mode (0=2-bit, 1=4-bit, 2=8-bit, 3=16-bit; 4-7 reserved/error).",
10955+
"units": ""
10956+
},
10957+
{
10958+
"name": "channelData",
10959+
"ctype": "uint8_t",
10960+
"desc": "Packed channel values, sequential from start channel. Number of channels is derived from data size and resolution. Value 0 means skip (no update). Sub-byte modes (2-bit, 4-bit) are packed MSB-first. 2-bit values 1-3 map to 1000/1500/2000us. 4-bit values 1-15 map to 1000 + (val-1)*1000/14 us. 8-bit values 1-255 map to 1000 + (val-1)*1000/254 us. 16-bit values are direct PWM.",
10961+
"units": "PWM (encoded)",
10962+
"array": true,
10963+
"array_size": 0
10964+
}
10965+
]
10966+
},
10967+
"reply": null,
10968+
"variable_len": true,
10969+
"notes": "CH1-CH8 (index 0-7) are protected and will return `MSP_RESULT_ERROR`. Constraint: `startChannel + channelCount <= 32`. Values persist until overwritten; no timeout. Applied as a post-RX overlay in `calculateRxChannelsAndUpdateFailsafe()` after MSP RC Override but before failsafe. Does not require `USE_RX_MSP` or MSP-RC-OVERRIDE flight mode. Does not affect failsafe detection. Recommended to send with `MSP_FLAG_DONT_REPLY` (flags=0x01) to save bandwidth on telemetry passthrough links. 16-bit mode requires even number of data bytes.",
10970+
"description": "Bandwidth-efficient auxiliary RC channel update. Sets CH9-CH32 with configurable resolution (2/4/8/16-bit) without affecting primary flight controls. Designed for extending channel count beyond native RC link capacity via MSP passthrough."
10971+
},
1094610972
"MSP2_BETAFLIGHT_BIND": {
1094710973
"code": 12288,
1094810974
"mspv": 2,

src/main/fc/fc_msp.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2412,6 +2412,110 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
24122412
}
24132413
break;
24142414
#endif
2415+
2416+
case MSP2_INAV_SET_AUX_RC:
2417+
{
2418+
if (dataSize < 2) {
2419+
return MSP_RESULT_ERROR;
2420+
}
2421+
2422+
const uint8_t defByte = sbufReadU8(src);
2423+
const uint8_t startChannel = defByte >> 3; // Bits 7-3: start channel index (0-31)
2424+
const uint8_t resolutionMode = defByte & 0x07; // Bits 2-0: resolution
2425+
2426+
// Safety: CH1-CH8 (index 0-7) are protected
2427+
if (startChannel < 8) {
2428+
return MSP_RESULT_ERROR;
2429+
}
2430+
2431+
const uint8_t dataBytes = dataSize - 1;
2432+
uint8_t channelCount;
2433+
uint8_t bitsPerChannel;
2434+
2435+
switch (resolutionMode) {
2436+
case 0: // 2-bit
2437+
bitsPerChannel = 2;
2438+
channelCount = dataBytes * 4;
2439+
break;
2440+
case 1: // 4-bit
2441+
bitsPerChannel = 4;
2442+
channelCount = dataBytes * 2;
2443+
break;
2444+
case 2: // 8-bit
2445+
bitsPerChannel = 8;
2446+
channelCount = dataBytes;
2447+
break;
2448+
case 3: // 16-bit
2449+
bitsPerChannel = 16;
2450+
if (dataBytes % 2 != 0) {
2451+
return MSP_RESULT_ERROR;
2452+
}
2453+
channelCount = dataBytes / 2;
2454+
break;
2455+
default:
2456+
return MSP_RESULT_ERROR;
2457+
}
2458+
2459+
if (channelCount == 0 || startChannel + channelCount > 32) {
2460+
return MSP_RESULT_ERROR;
2461+
}
2462+
2463+
// Decode and apply channel values
2464+
if (bitsPerChannel >= 8) {
2465+
// Byte-aligned modes: 8-bit and 16-bit
2466+
for (int i = 0; i < channelCount; i++) {
2467+
uint16_t rawValue;
2468+
if (bitsPerChannel == 16) {
2469+
rawValue = sbufReadU16(src);
2470+
} else {
2471+
rawValue = sbufReadU8(src);
2472+
}
2473+
2474+
if (rawValue == 0) {
2475+
continue; // skip: no update
2476+
}
2477+
2478+
uint16_t pwmValue;
2479+
if (bitsPerChannel == 16) {
2480+
pwmValue = rawValue;
2481+
} else {
2482+
// 8-bit: 1-255 → 1000-2000
2483+
pwmValue = 1000 + ((uint32_t)(rawValue - 1) * 1000) / 254;
2484+
}
2485+
2486+
rxMspAuxOverlaySet(startChannel + i, pwmValue);
2487+
}
2488+
} else {
2489+
// Sub-byte modes: 2-bit and 4-bit
2490+
const uint8_t mask = (1 << bitsPerChannel) - 1;
2491+
const uint8_t channelsPerByte = 8 / bitsPerChannel;
2492+
int ch = 0;
2493+
2494+
for (int byteIdx = 0; byteIdx < (int)dataBytes && ch < channelCount; byteIdx++) {
2495+
const uint8_t dataByte = sbufReadU8(src);
2496+
for (int sub = channelsPerByte - 1; sub >= 0 && ch < channelCount; sub--, ch++) {
2497+
const uint8_t rawValue = (dataByte >> (sub * bitsPerChannel)) & mask;
2498+
2499+
if (rawValue == 0) {
2500+
continue; // skip: no update
2501+
}
2502+
2503+
uint16_t pwmValue;
2504+
if (bitsPerChannel == 2) {
2505+
// 2-bit: 1→1000, 2→1500, 3→2000
2506+
pwmValue = 1000 + (rawValue - 1) * 500;
2507+
} else {
2508+
// 4-bit: 1-15 → 1000-2000
2509+
pwmValue = 1000 + ((uint32_t)(rawValue - 1) * 1000) / 14;
2510+
}
2511+
2512+
rxMspAuxOverlaySet(startChannel + ch, pwmValue);
2513+
}
2514+
}
2515+
}
2516+
}
2517+
break;
2518+
24152519
case MSP2_COMMON_SET_MOTOR_MIXER:
24162520
sbufReadU8Safe(&tmp_u8, src);
24172521
if ((dataSize == 9) && (tmp_u8 < MAX_SUPPORTED_MOTORS)) {
@@ -4486,6 +4590,7 @@ mspResult_e mspFcProcessCommand(mspPacket_t *cmd, mspPacket_t *reply, mspPostPro
44864590
sbuf_t *dst = &reply->buf;
44874591
sbuf_t *src = &cmd->buf;
44884592
const uint16_t cmdMSP = cmd->cmd;
4593+
44894594
// initialize reply by default
44904595
reply->cmd = cmd->cmd;
44914596

src/main/msp/msp_protocol_v2_inav.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,5 @@
126126
#define MSP2_INAV_SET_GVAR 0x2214
127127

128128
#define MSP2_INAV_FULL_LOCAL_POSE 0x2220
129+
130+
#define MSP2_INAV_SET_AUX_RC 0x2230

src/main/rx/rx.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ static bool isRxSuspended = false;
9494

9595
static rcChannel_t rcChannels[MAX_SUPPORTED_RC_CHANNEL_COUNT];
9696

97+
// MSP aux channel overlay: non-zero values override rcChannels[].data for CH9-CH32
98+
static uint16_t mspAuxOverlay[MAX_SUPPORTED_RC_CHANNEL_COUNT];
99+
97100
rxLinkStatistics_t rxLinkStatistics;
98101
rxRuntimeConfig_t rxRuntimeConfig;
99102
static uint8_t rcSampleIndex = 0;
@@ -512,6 +515,13 @@ bool calculateRxChannelsAndUpdateFailsafe(timeUs_t currentTimeUs)
512515
}
513516
#endif
514517

518+
// Apply MSP aux channel overlay (CH9-CH32)
519+
for (int i = 8; i < 32; i++) {
520+
if (mspAuxOverlay[i] > 0) {
521+
rcChannels[i].data = mspAuxOverlay[i];
522+
}
523+
}
524+
515525
// Update failsafe
516526
if (rxFlightChannelsValid && rxSignalReceived) {
517527
failsafeOnValidDataReceived();
@@ -663,6 +673,13 @@ int16_t rxGetChannelValue(unsigned channelNumber)
663673
}
664674
}
665675

676+
void rxMspAuxOverlaySet(uint8_t channelIndex, uint16_t value)
677+
{
678+
if (channelIndex >= 8 && channelIndex < 32) {
679+
mspAuxOverlay[channelIndex] = value;
680+
}
681+
}
682+
666683
void lqTrackerReset(rxLinkQualityTracker_e * lqTracker)
667684
{
668685
lqTracker->lastUpdatedMs = millis();

src/main/rx/rx.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,7 @@ void resumeRxSignal(void);
232232
// filtering and some extra processing like value holding
233233
// during failsafe.
234234
int16_t rxGetChannelValue(unsigned channelNumber);
235+
236+
// MSP aux channel overlay (CH9-CH32). Sets a channel value that persists
237+
// across RX update cycles. value=0 clears the overlay for that channel.
238+
void rxMspAuxOverlaySet(uint8_t channelIndex, uint16_t value);

0 commit comments

Comments
 (0)