diff --git a/src/main/cli/settings.c b/src/main/cli/settings.c index 9143ca8adb..766991e626 100644 --- a/src/main/cli/settings.c +++ b/src/main/cli/settings.c @@ -1336,6 +1336,7 @@ const clivalue_t valueTable[] = { #endif { "osd_rcchannels_pos", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_RC_CHANNELS]) }, + { "osd_camera_frame_pos", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_CAMERA_FRAME]) }, // OSD stats enabled flags are stored as bitmapped values inside a 32bit parameter // It is recommended to keep the settings order the same as the enumeration. This way the settings are displayed in the cli in the same order making it easier on the users @@ -1381,6 +1382,8 @@ const clivalue_t valueTable[] = { #endif { "osd_rcchannels", VAR_INT8 | MASTER_VALUE | MODE_ARRAY, .config.array.length = OSD_RCCHANNELS_COUNT, PG_OSD_CONFIG, offsetof(osdConfig_t, rcChannels) }, + { "osd_camera_frame_width", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { OSD_CAMERA_FRAME_MIN_WIDTH, OSD_CAMERA_FRAME_MAX_WIDTH }, PG_OSD_CONFIG, offsetof(osdConfig_t, camera_frame_width) }, + { "osd_camera_frame_height", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { OSD_CAMERA_FRAME_MIN_HEIGHT, OSD_CAMERA_FRAME_MAX_HEIGHT }, PG_OSD_CONFIG, offsetof(osdConfig_t, camera_frame_height) }, // PG_SYSTEM_CONFIG #if defined(STM32F4) diff --git a/src/main/msp/msp.c b/src/main/msp/msp.c index bfbeae2ece..8a4beb7e3b 100644 --- a/src/main/msp/msp.c +++ b/src/main/msp/msp.c @@ -878,6 +878,11 @@ static bool mspCommonProcessOutCommand(uint8_t cmdMSP, sbuf_t *dst, mspPostProce sbufWriteU8(dst, 0); #endif // USE_OSD_STICK_OVERLAY + // API >= 1.43 + // Add the camera frame element width/height + sbufWriteU8(dst, osdConfig()->camera_frame_width); + sbufWriteU8(dst, osdConfig()->camera_frame_height); + #endif // USE_OSD break; } @@ -3259,6 +3264,13 @@ static mspResult_e mspCommonProcessInCommand(mspDescriptor_t srcDesc, uint8_t cm #endif // USE_OSD_STICK_OVERLAY } + + if (sbufBytesRemaining(src) >= 2) { + // API >= 1.43 + // OSD camera frame element width/height + osdConfigMutable()->camera_frame_width = sbufReadU8(src); + osdConfigMutable()->camera_frame_height = sbufReadU8(src); + } #endif } else if ((int8_t)addr == -2) { #if defined(USE_OSD) diff --git a/src/main/osd/osd.c b/src/main/osd/osd.c index 9a89b8b9f7..eed3f6a4ef 100644 --- a/src/main/osd/osd.c +++ b/src/main/osd/osd.c @@ -286,6 +286,7 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig) osdConfig->item_pos[OSD_CROSSHAIRS] = OSD_POS(13, 6); osdConfig->item_pos[OSD_ARTIFICIAL_HORIZON] = OSD_POS(14, 2); osdConfig->item_pos[OSD_HORIZON_SIDEBARS] = OSD_POS(14, 6); + osdConfig->item_pos[OSD_CAMERA_FRAME] = OSD_POS(3, 1); // Enable the default stats osdConfig->enabled_stats = 0; // reset all to off and enable only a few initially @@ -343,6 +344,9 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig) osdConfig->distance_alarm = 0; osdConfig->logo_on_arming = OSD_LOGO_ARMING_OFF; osdConfig->logo_on_arming_duration = 5; // 0.5 seconds + + osdConfig->camera_frame_width = 24; + osdConfig->camera_frame_height = 11; } static void osdDrawLogo(int x, int y) diff --git a/src/main/osd/osd.h b/src/main/osd/osd.h index f242ed3a9c..2ab546a851 100644 --- a/src/main/osd/osd.h +++ b/src/main/osd/osd.h @@ -41,6 +41,11 @@ extern const char * const osdTimerSourceNames[OSD_NUM_TIMER_TYPES]; #define OSD_RCCHANNELS_COUNT 4 +#define OSD_CAMERA_FRAME_MIN_WIDTH 2 +#define OSD_CAMERA_FRAME_MAX_WIDTH 30 // Characters per row supportes by MAX7456 +#define OSD_CAMERA_FRAME_MIN_HEIGHT 2 +#define OSD_CAMERA_FRAME_MAX_HEIGHT 16 // Rows supported by MAX7456 (PAL) + #define OSD_PROFILE_BITS_POS 11 #define OSD_PROFILE_MASK (((1 << OSD_PROFILE_COUNT) - 1) << OSD_PROFILE_BITS_POS) #define OSD_POS_MAX 0x3FF @@ -135,6 +140,7 @@ typedef enum { OSD_PROFILE_NAME, OSD_RSSI_DBM_VALUE, OSD_RC_CHANNELS, + OSD_CAMERA_FRAME, OSD_ITEM_COUNT // MUST BE LAST } osd_items_e; @@ -274,6 +280,8 @@ typedef struct osdConfig_s { uint16_t distance_alarm; uint8_t logo_on_arming; // show the logo on arming uint8_t logo_on_arming_duration; // display duration in 0.1s units + uint8_t camera_frame_width; // The width of the box for the camera frame element + uint8_t camera_frame_height; // The height of the box for the camera frame element } osdConfig_t; PG_DECLARE(osdConfig_t, osdConfig); diff --git a/src/main/osd/osd_elements.c b/src/main/osd/osd_elements.c index dc19954583..ebd50102e8 100644 --- a/src/main/osd/osd_elements.c +++ b/src/main/osd/osd_elements.c @@ -596,6 +596,30 @@ static void osdElementCoreTemperature(osdElementParms_t *element) } #endif // USE_ADC_INTERNAL +static void osdBackgroundCameraFrame(osdElementParms_t *element) +{ + const uint8_t xpos = element->elemPosX; + const uint8_t ypos = element->elemPosY; + const uint8_t width = constrain(osdConfig()->camera_frame_width, OSD_CAMERA_FRAME_MIN_WIDTH, OSD_CAMERA_FRAME_MAX_WIDTH); + const uint8_t height = constrain(osdConfig()->camera_frame_height, OSD_CAMERA_FRAME_MIN_HEIGHT, OSD_CAMERA_FRAME_MAX_HEIGHT); + + element->buff[0] = SYM_STICK_OVERLAY_CENTER; + for (int i = 1; i < (width - 1); i++) { + element->buff[i] = SYM_STICK_OVERLAY_HORIZONTAL; + } + element->buff[width - 1] = SYM_STICK_OVERLAY_CENTER; + element->buff[width] = 0; // string terminator + + displayWrite(element->osdDisplayPort, xpos, ypos, DISPLAYPORT_ATTR_NONE, element->buff); + for (int i = 1; i < (height - 1); i++) { + displayWriteChar(element->osdDisplayPort, xpos, ypos + i, DISPLAYPORT_ATTR_NONE, SYM_STICK_OVERLAY_VERTICAL); + displayWriteChar(element->osdDisplayPort, xpos + width - 1, ypos + i, DISPLAYPORT_ATTR_NONE, SYM_STICK_OVERLAY_VERTICAL); + } + displayWrite(element->osdDisplayPort, xpos, ypos + height - 1, DISPLAYPORT_ATTR_NONE, element->buff); + + element->drawElement = false; // element already drawn +} + static void osdBackgroundCraftName(osdElementParms_t *element) { if (strlen(pilotConfig()->name) == 0) { @@ -1535,11 +1559,13 @@ static const uint8_t osdElementDisplayOrder[] = { OSD_PROFILE_NAME, #endif OSD_RC_CHANNELS, + OSD_CAMERA_FRAME, }; // Define the mapping between the OSD element id and the function to draw it const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = { + [OSD_CAMERA_FRAME] = NULL, // only has background. Added first so it's the lowest "layer" and doesn't cover other elements [OSD_RSSI_VALUE] = osdElementRssi, [OSD_MAIN_BATT_VOLTAGE] = osdElementMainBatteryVoltage, [OSD_CROSSHAIRS] = NULL, // only has background @@ -1647,6 +1673,7 @@ const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = { // Only necessary to define the entries that actually have a background function const osdElementDrawFn osdElementBackgroundFunction[OSD_ITEM_COUNT] = { + [OSD_CAMERA_FRAME] = osdBackgroundCameraFrame, [OSD_CROSSHAIRS] = osdBackgroundCrosshairs, [OSD_HORIZON_SIDEBARS] = osdBackgroundHorizonSidebars, [OSD_CRAFT_NAME] = osdBackgroundCraftName,