2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 /* Created by phobos- */
32 #if defined(USE_VTX_MSP) && defined(USE_VTX_CONTROL) && defined(USE_VTX_COMMON)
34 #include "build/debug.h"
36 //#include "cms/cms_menu_vtx_msp.h"
37 #include "common/crc.h"
38 #include "common/log.h"
39 #include "config/feature.h"
41 #include "drivers/vtx_common.h"
42 //#include "drivers/vtx_table.h"
44 #include "fc/runtime_config.h"
45 #include "flight/failsafe.h"
47 #include "io/serial.h"
48 #include "io/vtx_msp.h"
49 #include "io/vtx_control.h"
50 #include "io/vtx_string.h"
51 #include "io/vtx_smartaudio.h"
53 #include "io/displayport_msp_osd.h"
55 #include "msp/msp_protocol.h"
56 #include "msp/msp_serial.h"
59 //#include "pg/vtx_table.h"
60 #include "fc/settings.h"
63 //#include "rx/crsf_protocol.h"
66 #include "telemetry/msp_shared.h"
68 //static uint16_t mspConfFreq = 0;
69 static uint8_t mspConfBand
= SETTING_VTX_BAND_DEFAULT
;
70 static uint8_t mspConfChannel
= SETTING_VTX_CHANNEL_DEFAULT
;
71 //static uint16_t mspConfPower = 0;
72 static uint16_t mspConfPowerIndex
= SETTING_VTX_POWER_DEFAULT
;
73 static uint8_t mspConfPitMode
= 0;
74 static bool mspVtxConfigChanged
= false;
75 static timeUs_t mspVtxLastTimeUs
= 0;
76 static bool prevLowPowerDisarmedState
= false;
78 static const vtxVTable_t mspVTable
; // forward
79 static vtxDevice_t vtxMsp
= {
81 .capability
.bandCount
= VTX_MSP_TABLE_MAX_BANDS
,
82 .capability
.channelCount
= VTX_MSP_TABLE_MAX_CHANNELS
,
83 .capability
.powerCount
= VTX_MSP_TABLE_MAX_POWER_LEVELS
,
84 .capability
.bandNames
= (char **)vtx58BandNames
,
85 .capability
.channelNames
= (char **)vtx58ChannelNames
,
86 .capability
.powerNames
= (char**)saPowerNames
90 STATIC_UNIT_TESTED mspVtxStatus_e mspVtxStatus
= MSP_VTX_STATUS_OFFLINE
;
91 static uint8_t mspVtxPortIdentifier
= 255;
93 #define MSP_VTX_REQUEST_PERIOD_US (200 * 1000) // 200ms
95 static bool isCrsfPortConfig(const serialPortConfig_t
*portConfig
)
97 return portConfig
->functionMask
& FUNCTION_RX_SERIAL
&& portConfig
->functionMask
& FUNCTION_VTX_MSP
&& rxConfig()->serialrx_provider
== SERIALRX_CRSF
;
100 static bool isLowPowerDisarmed(void)
102 return (!ARMING_FLAG(ARMED
) && !failsafeIsActive() &&
103 (vtxSettingsConfig()->lowPowerDisarm
== VTX_LOW_POWER_DISARM_ALWAYS
||
104 (vtxSettingsConfig()->lowPowerDisarm
== VTX_LOW_POWER_DISARM_UNTIL_FIRST_ARM
&& !ARMING_FLAG(WAS_EVER_ARMED
))));
107 bool isVtxConfigValid(const vtxConfig_t
*cfg
)
109 LOG_DEBUG(VTX
, "msp isVtxConfigValid\r\n");
110 for (int i
= 0; i
< MAX_CHANNEL_ACTIVATION_CONDITION_COUNT
; ++i
) {
112 if (cfg
->vtxChannelActivationConditions
[i
].band
||
113 (cfg
->vtxChannelActivationConditions
[i
].range
.startStep
&& cfg
->vtxChannelActivationConditions
[i
].range
.endStep
) ||
114 cfg
->vtxChannelActivationConditions
[i
].auxChannelIndex
||
115 cfg
->vtxChannelActivationConditions
[i
].channel
) {
120 LOG_DEBUG(VTX
, "msp Invalid Config!\r\n");
125 void setMspVtxDeviceStatusReady(const int descriptor
)
127 LOG_DEBUG(VTX
, "msp setMspVtxDeviceStatusReady\r\n");
130 mspVtxStatus
= MSP_VTX_STATUS_READY
;
131 mspVtxConfigChanged
= true;
135 void prepareMspFrame(uint8_t *mspFrame
)
137 LOG_DEBUG(VTX
, "msp PrepareMspFrame\r\n");
140 fc_band_rx = msp_rx_buf[1];
141 fc_channel_rx = msp_rx_buf[2];
142 fc_pwr_rx = msp_rx_buf[3];
143 fc_pit_rx = msp_rx_buf[4];
144 fc_lp_rx = msp_rx_buf[8];
148 vtxCommonGetPitMode(&vtxMsp
, &pitmode
);
150 mspFrame
[0] = VTXDEV_MSP
,
151 mspFrame
[1] = vtxSettingsConfig()->band
;
152 mspFrame
[2] = vtxSettingsConfig()->channel
;
153 mspFrame
[3] = isLowPowerDisarmed() ? 1 : vtxSettingsConfig()->power
; // index based
154 mspFrame
[4] = pitmode
;
155 mspFrame
[5] = 0; // Freq_L
156 mspFrame
[6] = 0; // Freq_H
157 mspFrame
[7] = (mspVtxStatus
== MSP_VTX_STATUS_READY
) ? 1 : 0;
158 mspFrame
[8] = isLowPowerDisarmed();
159 mspFrame
[9] = 0; // Pitmode freq Low
160 mspFrame
[10] = 0; // pitmod freq High
161 mspFrame
[11] = 0; // 1 if using vtx table
162 mspFrame
[12] = 0; // vtx table bands or 0
163 mspFrame
[13] = 0; // vtx table channels or 0
164 mspFrame
[14] = 0; // vtx table power levels or 0
167 static void mspCrsfPush(const uint8_t mspCommand
, const uint8_t *mspFrame
, const uint8_t mspFrameSize
)
170 LOG_DEBUG(VTX
, "msp CrsfPush\r\n");
171 #ifndef USE_TELEMETRY_CRSF
174 UNUSED(mspFrameSize
);
176 sbuf_t crsfPayloadBuf
;
177 sbuf_t
*dst
= &crsfPayloadBuf
;
179 uint8_t mspHeader
[6] = {0x50, 0, mspCommand
& 0xFF, (mspCommand
>> 8) & 0xFF, mspFrameSize
& 0xFF, (mspFrameSize
>> 8) & 0xFF }; // MSP V2 over CRSF header
182 mspChecksum
= crc8_dvb_s2_update(0, &mspHeader
[1], 5); // first character is not checksummable
183 mspChecksum
= crc8_dvb_s2_update(mspChecksum
, mspFrame
, mspFrameSize
);
185 uint8_t fullMspFrameSize
= mspFrameSize
+ sizeof(mspHeader
) + 1; // add 1 for msp checksum
186 uint8_t crsfFrameSize
= CRSF_FRAME_LENGTH_EXT_TYPE_CRC
+ CRSF_FRAME_LENGTH_TYPE_CRC
+ fullMspFrameSize
;
188 uint8_t crsfFrame
[crsfFrameSize
];
190 dst
->ptr
= crsfFrame
;
191 dst
->end
= ARRAYEND(crsfFrame
);
193 sbufWriteU8(dst
, CRSF_SYNC_BYTE
);
194 sbufWriteU8(dst
, fullMspFrameSize
+ CRSF_FRAME_LENGTH_EXT_TYPE_CRC
); // size of CRSF frame (everything except sync and size itself)
195 sbufWriteU8(dst
, CRSF_FRAMETYPE_MSP_RESP
); // CRSF type
196 sbufWriteU8(dst
, CRSF_ADDRESS_CRSF_RECEIVER
); // response destination is the receiver the vtx connection
197 sbufWriteU8(dst
, CRSF_ADDRESS_FLIGHT_CONTROLLER
); // origin is always this device
198 sbufWriteData(dst
, mspHeader
, sizeof(mspHeader
));
199 sbufWriteData(dst
, mspFrame
, mspFrameSize
);
200 sbufWriteU8(dst
, mspChecksum
);
201 crc8_dvb_s2_sbuf_append(dst
, &crsfFrame
[2]); // start at byte 2, since CRC does not include device address and frame length
202 sbufSwitchToReader(dst
, crsfFrame
);
204 crsfRxSendTelemetryData(); //give the FC a chance to send outstanding telemetry
205 crsfRxWriteTelemetryData(sbufPtr(dst
), sbufBytesRemaining(dst
));
206 crsfRxSendTelemetryData();
210 static uint16_t packetCounter
= 0;
212 static bool isVtxConfigChanged(void)
214 if(mspVtxStatus
== MSP_VTX_STATUS_READY
) {
215 if (mspVtxConfigChanged
== true)
218 if (isLowPowerDisarmed() != prevLowPowerDisarmedState
) {
219 LOG_DEBUG(VTX
, "msp vtx config changed (lower power disarm 2)\r\n");
220 mspVtxConfigChanged
= true;
221 prevLowPowerDisarmedState
= isLowPowerDisarmed();
224 if (mspConfPowerIndex
!= vtxSettingsConfig()->power
) {
225 LOG_DEBUG(VTX
, "msp vtx config changed (power 2)\r\n");
226 mspVtxConfigChanged
= true;
229 if (mspConfBand
!= vtxSettingsConfig()->band
|| mspConfChannel
!= vtxSettingsConfig()->channel
) {
230 LOG_DEBUG(VTX
, "msp vtx config changed (band and channel 2)\r\n");
231 mspVtxConfigChanged
= true;
234 return mspVtxConfigChanged
;
240 static void vtxMspProcess(vtxDevice_t
*vtxDevice
, timeUs_t currentTimeUs
)
244 const serialPortConfig_t
*portConfig
= findSerialPortConfig(FUNCTION_MSP_OSD
);
247 switch (mspVtxStatus
) {
248 case MSP_VTX_STATUS_OFFLINE
:
249 LOG_DEBUG(VTX
, "msp MspProcess: OFFLINE\r\n");
250 // wait for MSP communication from the VTX
252 //mspCmsUpdateStatusString();
255 case MSP_VTX_STATUS_READY
:
256 LOG_DEBUG(VTX
, "msp MspProcess: READY\r\n");
257 // send an update if stuff has changed with 200ms period
258 if ((isVtxConfigChanged()) && cmp32(currentTimeUs
, mspVtxLastTimeUs
) >= MSP_VTX_REQUEST_PERIOD_US
) {
260 LOG_DEBUG(VTX
, "msp-vtx: vtxInfo Changed\r\n");
261 prepareMspFrame(frame
);
263 if (isCrsfPortConfig(portConfig
)) {
264 mspCrsfPush(MSP_VTX_CONFIG
, frame
, sizeof(frame
));
266 mspPort_t
*port
= getMspOsdPort();
267 if(port
!= NULL
&& port
->port
) {
268 LOG_DEBUG(VTX
, "msp-vtx: mspSerialPushPort\r\n");
269 int sent
= mspSerialPushPort(MSP_VTX_CONFIG
, frame
, sizeof(frame
), port
, MSP_V2_NATIVE
);
276 mspVtxLastTimeUs
= currentTimeUs
;
277 mspVtxConfigChanged
= false;
280 //mspCmsUpdateStatusString();
285 mspVtxStatus
= MSP_VTX_STATUS_OFFLINE
;
290 DEBUG_SET(DEBUG_VTX_MSP
, 0, packetCounter
);
291 DEBUG_SET(DEBUG_VTX_MSP
, 1, isCrsfPortConfig(portConfig
));
292 DEBUG_SET(DEBUG_VTX_MSP
, 2, isLowPowerDisarmed());
293 #if defined(USE_MSP_OVER_TELEMETRY)
294 DEBUG_SET(DEBUG_VTX_MSP
, 3, isCrsfPortConfig(portConfig
) ? getMspTelemetryDescriptor() : getMspSerialPortDescriptor(mspVtxPortIdentifier
));
296 DEBUG_SET(DEBUG_VTX_MSP
, 3, getMspSerialPortDescriptor(mspVtxPortIdentifier
));
301 static vtxDevType_e
vtxMspGetDeviceType(const vtxDevice_t
*vtxDevice
)
303 //LOG_DEBUG(VTX, "msp GetDeviceType\r\n");
308 static bool vtxMspIsReady(const vtxDevice_t
*vtxDevice
)
310 //LOG_DEBUG(VTX, "msp vtxIsReady: %s\r\n", (vtxDevice != NULL && mspVtxStatus == MSP_VTX_STATUS_READY) ? "Y": "N");
311 return vtxDevice
!= NULL
&& mspVtxStatus
== MSP_VTX_STATUS_READY
;
314 static void vtxMspSetBandAndChannel(vtxDevice_t
*vtxDevice
, uint8_t band
, uint8_t channel
)
316 LOG_DEBUG(VTX
, "msp SetBandAndChannel\r\n");
318 if (band
!= mspConfBand
|| channel
!= mspConfChannel
) {
319 LOG_DEBUG(VTX
, "msp vtx config changed (band and channel)\r\n");
320 mspVtxConfigChanged
= true;
323 mspConfChannel
= channel
;
326 static void vtxMspSetPowerByIndex(vtxDevice_t
*vtxDevice
, uint8_t index
)
328 LOG_DEBUG(VTX
, "msp SetPowerByIndex\r\n");
331 if (index
> 0 && (index
< VTX_MSP_TABLE_MAX_POWER_LEVELS
))
333 if (index
!= mspConfPowerIndex
)
335 LOG_DEBUG(VTX
, "msp vtx config changed (power by index)\r\n");
336 mspVtxConfigChanged
= true;
338 mspConfPowerIndex
= index
;
342 static void vtxMspSetPitMode(vtxDevice_t
*vtxDevice
, uint8_t onoff
)
344 LOG_DEBUG(VTX
, "msp SetPitMode\r\n");
346 if (onoff
!= mspConfPitMode
) {
347 LOG_DEBUG(VTX
, "msp vtx config changed (pitmode)\r\n");
348 mspVtxConfigChanged
= true;
350 mspConfPitMode
= onoff
;
354 static void vtxMspSetFreq(vtxDevice_t
*vtxDevice
, uint16_t freq
)
357 if (freq
!= mspConfFreq
) {
358 mspVtxConfigChanged
= true;
367 static bool vtxMspGetBandAndChannel(const vtxDevice_t
*vtxDevice
, uint8_t *pBand
, uint8_t *pChannel
)
369 if (!vtxMspIsReady(vtxDevice
)) {
373 *pBand
= vtxSettingsConfig()->band
;
374 *pChannel
= vtxSettingsConfig()->channel
;
376 //LOG_DEBUG(VTX, "msp GetBandAndChannel: %02x:%02x\r\n", vtxSettingsConfig()->band, vtxSettingsConfig()->channel);
381 static bool vtxMspGetPowerIndex(const vtxDevice_t
*vtxDevice
, uint8_t *pIndex
)
383 if (!vtxMspIsReady(vtxDevice
)) {
387 uint8_t power
= isLowPowerDisarmed() ? 1 : vtxSettingsConfig()->power
;
388 // Special case, power not set
389 if (power
> VTX_MSP_TABLE_MAX_POWER_LEVELS
) {
391 //LOG_DEBUG(VTX, "msp GetPowerIndex: %u\r\n", *pIndex);
397 //LOG_DEBUG(VTX, "msp GetPowerIndex: %u\r\n", *pIndex);
401 static bool vtxMspGetFreq(const vtxDevice_t
*vtxDevice
, uint16_t *pFreq
)
403 LOG_DEBUG(VTX
, "msp GetFreq\r\n");
404 if (!vtxMspIsReady(vtxDevice
)) {
412 static bool vtxMspGetPower(const vtxDevice_t
*vtxDevice
, uint8_t *pIndex
, uint16_t *pPowerMw
)
414 LOG_DEBUG(VTX
, "msp GetPower\r\n");
417 if (!vtxMspGetPowerIndex(vtxDevice
, &powerIndex
)) {
422 *pIndex
= powerIndex
;
427 static bool vtxMspGetOsdInfo(const vtxDevice_t
*vtxDevice
, vtxDeviceOsdInfo_t
* pOsdInfo
)
429 LOG_DEBUG(VTX
, "msp GetOsdInfo\r\n");
433 uint8_t band
, channel
;
435 if (!vtxMspGetBandAndChannel(vtxDevice
, &band
, &channel
)) {
439 if (!vtxMspGetFreq(vtxDevice
, &freq
)) {
443 if (!vtxMspGetPower(vtxDevice
, &powerIndex
, &powerMw
)) {
447 pOsdInfo
->band
= band
;
448 pOsdInfo
->channel
= channel
;
449 pOsdInfo
->frequency
= freq
;
450 pOsdInfo
->powerIndex
= powerIndex
;
451 pOsdInfo
->powerMilliwatt
= powerMw
;
452 pOsdInfo
->bandLetter
= vtx58BandNames
[band
][0];
453 pOsdInfo
->bandName
= vtx58BandNames
[band
];
454 pOsdInfo
->channelName
= vtx58ChannelNames
[channel
];
455 pOsdInfo
->powerIndexLetter
= '0' + powerIndex
;
460 bool vtxMspInit(void)
462 LOG_DEBUG(VTX
, "msp %s\r\n", __FUNCTION__
);
463 // don't bother setting up this device if we don't have MSP vtx enabled
464 // Port is shared with MSP_OSD
465 const serialPortConfig_t
*portConfig
= findSerialPortConfig(FUNCTION_MSP_OSD
);
470 mspVtxPortIdentifier
= portConfig
->identifier
;
472 // XXX Effect of USE_VTX_COMMON should be reviewed, as following call to vtxInit will do nothing if vtxCommonSetDevice is not called.
473 #if defined(USE_VTX_COMMON)
474 vtxCommonSetDevice(&vtxMsp
);
477 mspConfBand
= vtxSettingsConfig()->band
;
478 mspConfChannel
= vtxSettingsConfig()->channel
;
479 mspConfPowerIndex
= isLowPowerDisarmed() ? 1 : vtxSettingsConfig()->power
; // index based
480 vtxCommonGetPitMode(&vtxMsp
, &mspConfPitMode
);
487 static const vtxVTable_t mspVTable
= {
488 .process
= vtxMspProcess
,
489 .getDeviceType
= vtxMspGetDeviceType
,
490 .isReady
= vtxMspIsReady
,
491 .setBandAndChannel
= vtxMspSetBandAndChannel
,
492 .setPowerByIndex
= vtxMspSetPowerByIndex
,
493 .setPitMode
= vtxMspSetPitMode
,
494 //.setFrequency = vtxMspSetFreq,
495 .getBandAndChannel
= vtxMspGetBandAndChannel
,
496 .getPowerIndex
= vtxMspGetPowerIndex
,
497 .getFrequency
= vtxMspGetFreq
,
498 //.getStatus = vtxMspGetStatus,
499 .getPower
= vtxMspGetPower
,
500 //.serializeCustomDeviceStatus = NULL,
501 .getOsdInfo
= vtxMspGetOsdInfo
,
504 static mspResult_e
mspVtxProcessMspCommand(mspPacket_t
*cmd
, mspPacket_t
*reply
, mspPostProcessFnPtr
*mspPostProcessFn
)
506 //LOG_DEBUG(VTX, "msp VTX_MSP_PROCESS\r\n");
507 UNUSED(mspPostProcessFn
);
509 sbuf_t
*dst
= &reply
->buf
;
510 sbuf_t
*src
= &cmd
->buf
;
512 const unsigned int dataSize
= sbufBytesRemaining(src
);
516 // Start initializing the reply message
517 reply
->cmd
= cmd
->cmd
;
518 reply
->result
= MSP_RESULT_ACK
;
520 vtxDevice_t
*vtxDevice
= vtxCommonDevice();
521 if (!vtxDevice
|| vtxCommonGetDeviceType(vtxDevice
) != VTXDEV_MSP
) {
522 LOG_DEBUG(VTX
, "msp wrong vtx\r\n");
523 return MSP_RESULT_ERROR
;
528 case MSP_VTXTABLE_BAND
:
530 LOG_DEBUG(VTX
, "msp MSP_VTXTABLE_BAND\r\n");
531 uint8_t deviceType
= vtxCommonGetDeviceType(vtxDevice
);
532 if (deviceType
== VTXDEV_MSP
)
535 char bandName[MSP_VTX_TABLE_BAND_NAME_LENGTH + 1];
536 memset(bandName, 0, MSP_VTX_TABLE_BAND_NAME_LENGTH + 1);
537 uint16_t frequencies[MSP_VTX_TABLE_MAX_CHANNELS];
538 const uint8_t band = sbufReadU8(src);
539 const uint8_t bandNameLength = sbufReadU8(src);
540 for (int i = 0; i < bandNameLength; i++) {
541 const char nameChar = sbufReadU8(src);
542 if (i < MSP_VTX_TABLE_BAND_NAME_LENGTH) {
543 bandName[i] = toupper(nameChar);
546 const char bandLetter = toupper(sbufReadU8(src));
547 const bool isFactoryBand = (bool)sbufReadU8(src);
548 const uint8_t channelCount = sbufReadU8(src);
549 for (int i = 0; i < channelCount; i++)
551 const uint16_t frequency = sbufReadU16(src);
552 if (i < vtxTableConfig()->channels)
554 frequencies[i] = frequency;
559 setMspVtxDeviceStatusReady(1);
563 case MSP_VTXTABLE_POWERLEVEL
:
565 LOG_DEBUG(VTX
, "msp MSP_VTXTABLE_POWERLEVEL\r\n");
568 char powerLevelLabel[VTX_TABLE_POWER_LABEL_LENGTH + 1];
569 memset(powerLevelLabel, 0, VTX_TABLE_POWER_LABEL_LENGTH + 1);
570 const uint8_t powerLevel = sbufReadU8(src);
571 const uint16_t powerValue = sbufReadU16(src);
572 const uint8_t powerLevelLabelLength = sbufReadU8(src);
573 for (int i = 0; i < powerLevelLabelLength; i++)
575 const char labelChar = sbufReadU8(src);
576 if (i < VTX_TABLE_POWER_LABEL_LENGTH)
578 powerLevelLabel[i] = toupper(labelChar);
582 setMspVtxDeviceStatusReady(1);
587 LOG_DEBUG(VTX
, "msp MSP_VTX_CONFIG received\r\n");
588 LOG_DEBUG(VTX
, "msp MSP_VTX_CONFIG VTXDEV_MSP\r\n");
590 vtxCommonGetPitMode(vtxDevice
, &pitmode
);
593 sbufWriteU8(dst
, VTXDEV_MSP
);
595 sbufWriteU8(dst
, vtxSettingsConfig()->band
);
597 sbufWriteU8(dst
, vtxSettingsConfig()->channel
);
598 // power; // index based
599 sbufWriteU8(dst
, vtxSettingsConfig()->power
);
609 sbufWriteU8(dst
, vtxSettingsConfig()->lowPowerDisarm
);
614 // 1 if using vtx table
616 // vtx table bands or 0
618 // vtx table channels or 0
621 setMspVtxDeviceStatusReady(1);
624 case MSP_SET_VTX_CONFIG
:
625 LOG_DEBUG(VTX
, "msp MSP_SET_VTX_CONFIG\r\n");
628 if (vtxCommonGetDeviceType(vtxDevice
) != VTXDEV_UNKNOWN
)
630 for (int i
= 0; i
< 15; ++i
)
632 uint8_t data
= sbufReadU8(src
);
636 vtxSettingsConfigMutable()->band
= data
;
639 vtxSettingsConfigMutable()->channel
= data
;
642 vtxSettingsConfigMutable()->power
= data
;
645 vtxCommonSetPitMode(vtxDevice
, data
);
651 vtxSettingsConfigMutable()->lowPowerDisarm
= data
;
657 setMspVtxDeviceStatusReady(1);
660 LOG_DEBUG(VTX
, "msp MSP_RESULT_ERROR\r\n");
661 return MSP_RESULT_ERROR
;
664 // debug[2] = cmd->cmd;
665 if(cmd
->cmd
!= MSP_STATUS
&& cmd
->cmd
!= MSP_STATUS_EX
&& cmd
->cmd
!= MSP_RC
) {
666 LOG_DEBUG(VTX
, "msp default: %02x\r\n", cmd
->cmd
);
668 reply
->result
= MSP_RESULT_ERROR
;
672 // Process DONT_REPLY flag
673 if (cmd
->flags
& MSP_FLAG_DONT_REPLY
)
675 reply
->result
= MSP_RESULT_NO_REPLY
;
678 return reply
->result
;
681 void mspVtxSerialProcess(mspProcessCommandFnPtr mspProcessCommandFn
)
683 UNUSED(mspProcessCommandFn
);
684 // Check if VTX is ready
686 if (mspVtxStatus != MSP_VTX_STATUS_READY) {
687 LOG_DEBUG(VTX, "msp VTX NOT READY, skipping\r\n");
692 mspPort_t
*port
= getMspOsdPort();
695 mspSerialProcessOnePort(port
, MSP_SKIP_NON_MSP_DATA
, mspVtxProcessMspCommand
);