From 5c3b8c6eb62f23eabddaa84f049c927d5894ce61 Mon Sep 17 00:00:00 2001 From: Bruce Luckcuck Date: Fri, 1 Nov 2019 17:30:02 -0400 Subject: [PATCH] Add camera frame OSD element Adds an adjustable outline element designed to represent the field of view of the pilot's HD camera for visual framing. The width, height and position of the frame are adjustable. --- src/main/cli/settings.c | 3 +++ src/main/msp/msp.c | 12 ++++++++++++ src/main/osd/osd.c | 4 ++++ src/main/osd/osd.h | 8 ++++++++ src/main/osd/osd_elements.c | 27 +++++++++++++++++++++++++++ 5 files changed, 54 insertions(+) diff --git a/src/main/cli/settings.c b/src/main/cli/settings.c index 9143ca8ad..766991e62 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 bfbeae2ec..8a4beb7e3 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 9a89b8b9f..eed3f6a4e 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 f242ed3a9..2ab546a85 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 dc1995458..ebd50102e 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,