diff --git a/make/source.mk b/make/source.mk
index f3dec33c25..2001dbd440 100644
--- a/make/source.mk
+++ b/make/source.mk
@@ -170,6 +170,7 @@ FC_SRC = \
io/displayport_oled.c \
io/displayport_rcdevice.c \
io/displayport_srxl.c \
+ io/displayport_crsf.c \
io/rcdevice_cam.c \
io/rcdevice.c \
io/rcdevice_osd.c \
diff --git a/src/main/cms/cms.c b/src/main/cms/cms.c
index a3080cc5ec..74a85c2b40 100644
--- a/src/main/cms/cms.c
+++ b/src/main/cms/cms.c
@@ -108,6 +108,19 @@ static displayPort_t *cmsDisplayPortSelectNext(void)
return cmsDisplayPorts[cmsCurrentDevice];
}
+bool cmsDisplayPortSelect(displayPort_t *instance)
+{
+ if (cmsDeviceCount == 0) {
+ return false;
+ }
+ for (int i = 0; i < cmsDeviceCount; i++) {
+ if (cmsDisplayPortSelectNext() == instance) {
+ return true;
+ }
+ }
+ return false;
+}
+
#define CMS_UPDATE_INTERVAL_US 50000 // Interval of key scans (microsec)
#define CMS_POLL_INTERVAL_US 100000 // Interval of polling dynamic values (microsec)
@@ -638,7 +651,7 @@ STATIC_UNIT_TESTED long cmsMenuBack(displayPort_t *pDisplay)
return 0;
}
-STATIC_UNIT_TESTED void cmsMenuOpen(void)
+void cmsMenuOpen(void)
{
if (!cmsInMenu) {
// New open
diff --git a/src/main/cms/cms.h b/src/main/cms/cms.h
index 6efd689912..610424d557 100644
--- a/src/main/cms/cms.h
+++ b/src/main/cms/cms.h
@@ -14,6 +14,8 @@ displayPort_t *pCurrentDisplay;
void cmsInit(void);
void cmsHandler(timeUs_t currentTimeUs);
+bool cmsDisplayPortSelect(displayPort_t *instance);
+void cmsMenuOpen(void);
long cmsMenuChange(displayPort_t *pPort, const void *ptr);
long cmsMenuExit(displayPort_t *pPort, const void *ptr);
void cmsUpdate(uint32_t currentTimeUs);
diff --git a/src/main/drivers/display.h b/src/main/drivers/display.h
index 29747da575..8bf27be081 100644
--- a/src/main/drivers/display.h
+++ b/src/main/drivers/display.h
@@ -58,6 +58,8 @@ typedef struct displayPortProfile_s {
uint8_t whiteBrightness;
} displayPortProfile_t;
+// Note: displayPortProfile_t used as a parameter group for CMS over CRSF (io/displayport_crsf)
+
void displayGrab(displayPort_t *instance);
void displayRelease(displayPort_t *instance);
void displayReleaseAll(displayPort_t *instance);
diff --git a/src/main/fc/fc_init.c b/src/main/fc/fc_init.c
index 476412c5be..d534dd257e 100644
--- a/src/main/fc/fc_init.c
+++ b/src/main/fc/fc_init.c
@@ -106,6 +106,7 @@
#include "io/displayport_max7456.h"
#include "io/displayport_rcdevice.h"
#include "io/displayport_srxl.h"
+#include "io/displayport_crsf.h"
#include "io/serial.h"
#include "io/flashfs.h"
#include "io/gps.h"
@@ -643,6 +644,10 @@ void init(void)
cmsDisplayPortRegister(displayPortSrxlInit());
#endif
+#if defined(USE_CMS) && defined(USE_CRSF_CMS_TELEMETRY) && defined(USE_TELEMETRY)
+ cmsDisplayPortRegister(displayPortCrsfInit());
+#endif
+
#ifdef USE_GPS
if (feature(FEATURE_GPS)) {
gpsInit();
diff --git a/src/main/interface/crsf_protocol.h b/src/main/interface/crsf_protocol.h
index 0594f2fd18..be1c1175d3 100644
--- a/src/main/interface/crsf_protocol.h
+++ b/src/main/interface/crsf_protocol.h
@@ -31,9 +31,18 @@ typedef enum {
// MSP commands
CRSF_FRAMETYPE_MSP_REQ = 0x7A, // response request using msp sequence as command
CRSF_FRAMETYPE_MSP_RESP = 0x7B, // reply with 58 byte chunked binary
- CRSF_FRAMETYPE_MSP_WRITE = 0x7C // write with 8 byte chunked binary (OpenTX outbound telemetry buffer limit)
+ CRSF_FRAMETYPE_MSP_WRITE = 0x7C, // write with 8 byte chunked binary (OpenTX outbound telemetry buffer limit)
+ CRSF_FRAMETYPE_DISPLAYPORT_UPDATE = 0x7D, // transmit displayport buffer to remote
+ CRSF_FRAMETYPE_DISPLAYPORT_CLEAR = 0x7E, // clear remote
+ CRSF_FRAMETYPE_DISPLAYPORT_CMD = 0x7F, // client request
} crsfFrameType_e;
+enum {
+ CRSF_DISPLAYPORT_SUBCMD_OPEN = 0x01, // client request to open cms menu
+ CRSF_DISPLAYPORT_SUBCMD_CLOSE = 0x02, // client request to close cms menu
+ CRSF_DISPLAYPORT_SUBCMD_POLL = 0x03, // client request to poll/refresh cms menu
+};
+
enum {
CRSF_FRAME_GPS_PAYLOAD_SIZE = 15,
CRSF_FRAME_BATTERY_SENSOR_PAYLOAD_SIZE = 8,
diff --git a/src/main/interface/settings.c b/src/main/interface/settings.c
index b2b7d61385..4b2e0650c0 100644
--- a/src/main/interface/settings.c
+++ b/src/main/interface/settings.c
@@ -928,6 +928,12 @@ const clivalue_t valueTable[] = {
{ "displayport_msp_row_adjust", VAR_INT8 | MASTER_VALUE, .config.minmax = { -3, 0 }, PG_DISPLAY_PORT_MSP_CONFIG, offsetof(displayPortProfile_t, rowAdjust) },
#endif
+// PG_DISPLAY_PORT_CRSF_CONFIG
+#if defined(USE_CRSF_CMS_TELEMETRY)
+ { "displayport_crsf_col_adjust", VAR_INT8 | MASTER_VALUE, .config.minmax = { -8, 0 }, PG_DISPLAY_PORT_CRSF_CONFIG, offsetof(displayPortProfile_t, colAdjust) },
+ { "displayport_crsf_row_adjust", VAR_INT8 | MASTER_VALUE, .config.minmax = { -3, 0 }, PG_DISPLAY_PORT_CRSF_CONFIG, offsetof(displayPortProfile_t, rowAdjust) },
+#endif
+
// PG_DISPLAY_PORT_MSP_CONFIG
#ifdef USE_MAX7456
{ "displayport_max7456_col_adjust", VAR_INT8| MASTER_VALUE, .config.minmax = { -6, 0 }, PG_DISPLAY_PORT_MAX7456_CONFIG, offsetof(displayPortProfile_t, colAdjust) },
diff --git a/src/main/io/displayport_crsf.c b/src/main/io/displayport_crsf.c
new file mode 100644
index 0000000000..a9f263e673
--- /dev/null
+++ b/src/main/io/displayport_crsf.c
@@ -0,0 +1,214 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Cleanflight is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Cleanflight. If not, see .
+ */
+
+#include
+#include
+
+#include "platform.h"
+
+#if defined(USE_CRSF_CMS_TELEMETRY)
+
+#include "cms/cms.h"
+#include "common/maths.h"
+#include "common/printf.h"
+#include "common/time.h"
+#include "drivers/display.h"
+#include "drivers/time.h"
+#include "io/displayport_crsf.h"
+#include "pg/pg_ids.h"
+
+#define CRSF_DISPLAY_PORT_OPEN_DELAY_MS 400
+#define CRSF_DISPLAY_PORT_CLEAR_DELAY_MS 38
+
+static crsfDisplayPortScreen_t crsfScreen;
+static timeMs_t delayTransportUntilMs = 0;
+
+PG_REGISTER(displayPortProfile_t, displayPortProfileCrsf, PG_DISPLAY_PORT_CRSF_CONFIG, 0);
+
+displayPort_t crsfDisplayPort;
+
+static int crsfGrab(displayPort_t *displayPort)
+{
+ return displayPort->grabCount = 1;
+}
+
+static int crsfClearScreen(displayPort_t *displayPort)
+{
+ UNUSED(displayPort);
+ crsfDisplayPortRow_t *screenRow;
+ for (int row=0; rowpendingTransport = false;
+ for (int col=0; coldata[col]=' ';
+ }
+ }
+ crsfScreen.reset = true;
+ delayTransportUntilMs = millis() + CRSF_DISPLAY_PORT_CLEAR_DELAY_MS;
+ return 0;
+}
+
+static int crsfRelease(displayPort_t *displayPort)
+{
+ displayPort->grabCount = 0;
+ return crsfClearScreen(displayPort);
+}
+
+static int crsfDrawScreen(displayPort_t *displayPort)
+{
+ UNUSED(displayPort);
+ return 0;
+}
+
+static int crsfScreenSize(const displayPort_t *displayPort)
+{
+ return displayPort->rows * displayPort->cols;
+}
+
+
+static int crsfWriteString(displayPort_t *displayPort, uint8_t col, uint8_t row, const char *s)
+{
+ UNUSED(displayPort);
+ if (row >= CRSF_DISPLAY_PORT_ROWS_MAX || col >= CRSF_DISPLAY_PORT_COLS_MAX) {
+ return 0;
+ }
+ const size_t truncLen = MIN((int)strlen(s), CRSF_DISPLAY_PORT_COLS_MAX-col); // truncate at CRSF_DISPLAY_PORT_COLS_MAX
+ crsfDisplayPortRow_t *screenRow = &crsfScreen.rows[row];
+ screenRow->pendingTransport = memcmp(&screenRow->data[col], s, truncLen);
+ if (screenRow->pendingTransport) {
+ memcpy(&screenRow->data[col], s, truncLen);
+ }
+ return 0;
+}
+
+static int crsfWriteChar(displayPort_t *displayPort, uint8_t col, uint8_t row, uint8_t c)
+{
+ char s[1];
+ tfp_sprintf(s, "%c", c);
+ return crsfWriteString(displayPort, col, row, s);
+}
+
+static bool crsfIsTransferInProgress(const displayPort_t *displayPort)
+{
+ UNUSED(displayPort);
+ return false;
+}
+
+static bool crsfIsSynced(const displayPort_t *displayPort)
+{
+ UNUSED(displayPort);
+ return true;
+}
+
+static int crsfHeartbeat(displayPort_t *displayPort)
+{
+ UNUSED(displayPort);
+ return 0;
+}
+
+static void crsfResync(displayPort_t *displayPort)
+{
+ displayPort->rows = CRSF_DISPLAY_PORT_ROWS_MAX + displayPortProfileCrsf()->rowAdjust;
+ displayPort->cols = CRSF_DISPLAY_PORT_COLS_MAX + displayPortProfileCrsf()->colAdjust;
+ crsfClearScreen(displayPort);
+}
+
+static uint32_t crsfTxBytesFree(const displayPort_t *displayPort)
+{
+ UNUSED(displayPort);
+ return UINT32_MAX;
+}
+
+static const displayPortVTable_t crsfDisplayPortVTable = {
+ .grab = crsfGrab,
+ .release = crsfRelease,
+ .clearScreen = crsfClearScreen,
+ .drawScreen = crsfDrawScreen,
+ .screenSize = crsfScreenSize,
+ .writeString = crsfWriteString,
+ .writeChar = crsfWriteChar,
+ .isTransferInProgress = crsfIsTransferInProgress,
+ .heartbeat = crsfHeartbeat,
+ .resync = crsfResync,
+ .isSynced = crsfIsSynced,
+ .txBytesFree = crsfTxBytesFree
+};
+
+displayPort_t *displayPortCrsfInit()
+{
+ displayInit(&crsfDisplayPort, &crsfDisplayPortVTable);
+ crsfResync(&crsfDisplayPort);
+ return &crsfDisplayPort;
+}
+
+crsfDisplayPortScreen_t *crsfDisplayPortScreen(void)
+{
+ return &crsfScreen;
+}
+
+void crsfDisplayPortMenuOpen(void)
+{
+ if (cmsInMenu) {
+ return;
+ }
+ if (cmsDisplayPortSelect(&crsfDisplayPort)) {
+ cmsMenuOpen();
+ delayTransportUntilMs = millis() + CRSF_DISPLAY_PORT_OPEN_DELAY_MS;
+ }
+}
+
+void crsfDisplayPortMenuExit(void)
+{
+ if (!cmsInMenu) {
+ return;
+ }
+ uint8_t exitMenu = CMS_EXIT;
+ cmsMenuExit(&crsfDisplayPort, &exitMenu);
+}
+
+
+void crsfDisplayPortRefresh(void)
+{
+ if (!cmsInMenu) {
+ crsfDisplayPortMenuOpen();
+ return;
+ }
+ crsfDisplayPortRow_t *screenRow;
+ for (int row=0; rowpendingTransport = true;
+ }
+ crsfScreen.reset = true;
+}
+
+int crsfDisplayPortNextRow(void)
+{
+ const timeMs_t currentTimeMs = millis();
+ if (currentTimeMs < delayTransportUntilMs) {
+ return -1;
+ }
+ crsfDisplayPortRow_t *screenRow;
+ for(int i=0; ipendingTransport) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+#endif
diff --git a/src/main/io/displayport_crsf.h b/src/main/io/displayport_crsf.h
new file mode 100644
index 0000000000..2c9070ebb2
--- /dev/null
+++ b/src/main/io/displayport_crsf.h
@@ -0,0 +1,44 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Cleanflight is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Cleanflight. If not, see .
+ */
+
+#pragma once
+
+#include "pg/pg.h"
+#include "drivers/display.h"
+
+#define CRSF_DISPLAY_PORT_ROWS_MAX 8
+#define CRSF_DISPLAY_PORT_COLS_MAX 32
+
+typedef struct crsfDisplayPortRow_s {
+ char data[CRSF_DISPLAY_PORT_COLS_MAX];
+ bool pendingTransport;
+} crsfDisplayPortRow_t;
+
+typedef struct crsfDisplayPortScreen_s {
+ crsfDisplayPortRow_t rows[CRSF_DISPLAY_PORT_ROWS_MAX];
+ bool reset;
+} crsfDisplayPortScreen_t;
+
+PG_DECLARE(displayPortProfile_t, displayPortProfileCrsf);
+
+struct displayPort_s;
+struct displayPort_s *displayPortCrsfInit(void);
+crsfDisplayPortScreen_t *crsfDisplayPortScreen(void);
+void crsfDisplayPortMenuOpen(void);
+void crsfDisplayPortMenuExit(void);
+void crsfDisplayPortRefresh(void);
+int crsfDisplayPortNextRow(void);
diff --git a/src/main/pg/pg_ids.h b/src/main/pg/pg_ids.h
index 884ef55d27..2ebaf75681 100644
--- a/src/main/pg/pg_ids.h
+++ b/src/main/pg/pg_ids.h
@@ -123,7 +123,8 @@
#define PG_PINIOBOX_CONFIG 530
#define PG_USB_CONFIG 531
#define PG_SDIO_CONFIG 532
-#define PG_BETAFLIGHT_END 532
+#define PG_DISPLAY_PORT_CRSF_CONFIG 533
+#define PG_BETAFLIGHT_END 533
// OSD configuration (subject to change)
diff --git a/src/main/rx/crsf.c b/src/main/rx/crsf.c
index a9cbe2201e..2b58c7d530 100644
--- a/src/main/rx/crsf.c
+++ b/src/main/rx/crsf.c
@@ -166,6 +166,12 @@ STATIC_UNIT_TESTED void crsfDataReceive(uint16_t c, void *data)
case CRSF_FRAMETYPE_DEVICE_PING:
crsfScheduleDeviceInfoResponse();
break;
+#if defined(USE_CRSF_CMS_TELEMETRY)
+ case CRSF_FRAMETYPE_DISPLAYPORT_CMD: ;
+ uint8_t *cmd = (uint8_t *)&crsfFrame.frame.payload + CRSF_FRAME_ORIGIN_DEST_SIZE;
+ crsfProcessDisplayPortCmd(*cmd);
+ break;
+#endif
default:
break;
}
diff --git a/src/main/target/MIDELICF3/target.h b/src/main/target/MIDELICF3/target.h
index 4db60465e5..40a471f9ba 100644
--- a/src/main/target/MIDELICF3/target.h
+++ b/src/main/target/MIDELICF3/target.h
@@ -19,6 +19,9 @@
#define TARGET_BOARD_IDENTIFIER "MIF3"
+// prevent flash overflow
+#undef USE_CRSF_CMS_TELEMETRY
+
#define LED0_PIN PB5
#define USE_BEEPER
diff --git a/src/main/target/SPRACINGF3EVO/target.h b/src/main/target/SPRACINGF3EVO/target.h
index 33a757964e..4f83e627ca 100644
--- a/src/main/target/SPRACINGF3EVO/target.h
+++ b/src/main/target/SPRACINGF3EVO/target.h
@@ -30,6 +30,7 @@
#undef USE_COPY_PROFILE_CMS_MENU
#undef USE_RX_MSP
#undef USE_ESC_SENSOR_INFO
+#undef USE_CRSF_CMS_TELEMETRY
#if !defined(AIORACERF3)
diff --git a/src/main/target/SPRACINGF3NEO/target.h b/src/main/target/SPRACINGF3NEO/target.h
index a5f22d41ac..42050982dc 100644
--- a/src/main/target/SPRACINGF3NEO/target.h
+++ b/src/main/target/SPRACINGF3NEO/target.h
@@ -25,6 +25,7 @@
#undef USE_COPY_PROFILE_CMS_MENU
#undef USE_RX_MSP
#undef USE_ESC_SENSOR_INFO
+#undef USE_CRSF_CMS_TELEMETRY
#undef USE_COPY_PROFILE_CMS_MENU
diff --git a/src/main/target/common_fc_pre.h b/src/main/target/common_fc_pre.h
index 18bb48788d..fe86c466f3 100644
--- a/src/main/target/common_fc_pre.h
+++ b/src/main/target/common_fc_pre.h
@@ -171,6 +171,7 @@
#define USE_GYRO_LPF2
#define USE_ESC_SENSOR
#define USE_ESC_SENSOR_INFO
+#define USE_CRSF_CMS_TELEMETRY
#ifdef USE_SERIALRX_SPEKTRUM
#define USE_SPEKTRUM_BIND
diff --git a/src/main/telemetry/crsf.c b/src/main/telemetry/crsf.c
index 22bb295de0..00ff9af43e 100644
--- a/src/main/telemetry/crsf.c
+++ b/src/main/telemetry/crsf.c
@@ -47,6 +47,7 @@
#include "interface/crsf_protocol.h"
+#include "io/displayport_crsf.h"
#include "io/gps.h"
#include "io/serial.h"
@@ -59,7 +60,6 @@
#include "telemetry/crsf.h"
#include "telemetry/msp_shared.h"
-
#define CRSF_CYCLETIME_US 100000 // 100ms, 10 Hz
#define CRSF_DEVICEINFO_VERSION 0x01
#define CRSF_DEVICEINFO_PARAMETER_COUNT 0
@@ -306,6 +306,34 @@ void crsfFrameDeviceInfo(sbuf_t *dst) {
*lengthPtr = sbufPtr(dst) - lengthPtr;
}
+#if defined(USE_CRSF_CMS_TELEMETRY)
+
+static void crsfFrameDisplayPortRow(sbuf_t *dst, uint8_t row, const char *str)
+{
+ uint8_t *lengthPtr = sbufPtr(dst);
+ const uint8_t bufLen = CRSF_DISPLAY_PORT_COLS_MAX + displayPortProfileCrsf()->colAdjust;
+ const uint8_t frameLength = CRSF_FRAME_LENGTH_EXT_TYPE_CRC + bufLen;
+ sbufWriteU8(dst, frameLength);
+ sbufWriteU8(dst, CRSF_FRAMETYPE_DISPLAYPORT_UPDATE);
+ sbufWriteU8(dst, CRSF_ADDRESS_RADIO_TRANSMITTER);
+ sbufWriteU8(dst, CRSF_ADDRESS_FLIGHT_CONTROLLER);
+ sbufWriteU8(dst, row);
+ sbufWriteData(dst, str, bufLen);
+ *lengthPtr = sbufPtr(dst) - lengthPtr;
+}
+
+static void crsfFrameDisplayPortClear(sbuf_t *dst)
+{
+ uint8_t *lengthPtr = sbufPtr(dst);
+ sbufWriteU8(dst, CRSF_DISPLAY_PORT_COLS_MAX + CRSF_FRAME_LENGTH_EXT_TYPE_CRC);
+ sbufWriteU8(dst, CRSF_FRAMETYPE_DISPLAYPORT_CLEAR);
+ sbufWriteU8(dst, CRSF_ADDRESS_RADIO_TRANSMITTER);
+ sbufWriteU8(dst, CRSF_ADDRESS_FLIGHT_CONTROLLER);
+ *lengthPtr = sbufPtr(dst) - lengthPtr;
+}
+
+#endif
+
#define BV(x) (1 << (x)) // bit value
// schedule array to decide how often each type of frame is sent
@@ -348,6 +376,7 @@ void crsfSendMspResponse(uint8_t *payload)
static void processCrsf(void)
{
static uint8_t crsfScheduleIndex = 0;
+
const uint8_t currentSchedule = crsfSchedule[crsfScheduleIndex];
sbuf_t crsfPayloadBuf;
@@ -384,6 +413,7 @@ void crsfScheduleDeviceInfoResponse(void)
deviceInfoReplyPending = true;
}
+
void initCrsfTelemetry(void)
{
// check if there is a serial port open for CRSF telemetry (ie opened by the CRSF RX)
@@ -415,6 +445,28 @@ bool checkCrsfTelemetryState(void)
return crsfTelemetryEnabled;
}
+#if defined(USE_CRSF_CMS_TELEMETRY)
+void crsfProcessDisplayPortCmd(uint8_t cmd)
+{
+ switch (cmd) {
+ case CRSF_DISPLAYPORT_SUBCMD_OPEN:
+ crsfDisplayPortMenuOpen();
+ break;
+ case CRSF_DISPLAYPORT_SUBCMD_CLOSE:
+ crsfDisplayPortMenuExit();
+ break;
+ case CRSF_DISPLAYPORT_SUBCMD_POLL:
+ crsfDisplayPortRefresh();
+ crsfDisplayPortScreen()->reset = true;
+ break;
+ default:
+ break;
+ }
+
+}
+
+#endif
+
/*
* Called periodically by the scheduler
*/
@@ -450,6 +502,31 @@ void handleCrsfTelemetry(timeUs_t currentTimeUs)
return;
}
+#if defined(USE_CRSF_CMS_TELEMETRY)
+ if (crsfDisplayPortScreen()->reset) {
+ crsfDisplayPortScreen()->reset = false;
+ sbuf_t crsfDisplayPortBuf;
+ sbuf_t *dst = &crsfDisplayPortBuf;
+ crsfInitializeFrame(dst);
+ crsfFrameDisplayPortClear(dst);
+ crsfFinalize(dst);
+ crsfLastCycleTime = currentTimeUs;
+ return;
+ }
+ const int nextRow = crsfDisplayPortNextRow();
+ if (nextRow >= 0) {
+ crsfDisplayPortRow_t *row = &crsfDisplayPortScreen()->rows[nextRow];
+ sbuf_t crsfDisplayPortBuf;
+ sbuf_t *dst = &crsfDisplayPortBuf;
+ crsfInitializeFrame(dst);
+ crsfFrameDisplayPortRow(dst, nextRow, row->data);
+ crsfFinalize(dst);
+ row->pendingTransport = false;
+ crsfLastCycleTime = currentTimeUs;
+ return;
+ }
+#endif
+
// Actual telemetry data only needs to be sent at a low frequency, ie 10Hz
// Spread out scheduled frames evenly so each frame is sent at the same frequency.
if (currentTimeUs >= crsfLastCycleTime + (CRSF_CYCLETIME_US / crsfScheduleCount)) {
diff --git a/src/main/telemetry/crsf.h b/src/main/telemetry/crsf.h
index 83fe327ae3..6f283d1148 100644
--- a/src/main/telemetry/crsf.h
+++ b/src/main/telemetry/crsf.h
@@ -26,13 +26,15 @@
#define CRSF_MSP_RX_BUF_SIZE 128
#define CRSF_MSP_TX_BUF_SIZE 128
-
void initCrsfTelemetry(void);
bool checkCrsfTelemetryState(void);
void handleCrsfTelemetry(timeUs_t currentTimeUs);
void crsfScheduleDeviceInfoResponse(void);
void crsfScheduleMspResponse(void);
int getCrsfFrame(uint8_t *frame, crsfFrameType_e frameType);
+#if defined(USE_CRSF_CMS_TELEMETRY)
+void crsfProcessDisplayPortCmd(uint8_t cmd);
+#endif
#if defined(USE_MSP_OVER_TELEMETRY)
void initCrsfMspBuffer(void);
bool bufferCrsfMspFrame(uint8_t *frameStart, int frameLength);