diff --git a/radio/src/gui/128x64/menus.h b/radio/src/gui/128x64/menus.h index a1e980874..1dfa1d24d 100644 --- a/radio/src/gui/128x64/menus.h +++ b/radio/src/gui/128x64/menus.h @@ -74,6 +74,7 @@ void menuRadioTools(event_t event); void menuRadioSpectrumAnalyser(event_t event); void menuRadioPowerMeter(event_t event); void menuRadioCalibration(event_t event); +void menuGhostModuleConfig(event_t event); static const MenuHandlerFunc menuTabGeneral[MENU_RADIO_PAGES_COUNT] = { #if defined(RADIO_TOOLS) diff --git a/radio/src/gui/212x64/menus.h b/radio/src/gui/212x64/menus.h index bdfb8f591..f39cdc71b 100644 --- a/radio/src/gui/212x64/menus.h +++ b/radio/src/gui/212x64/menus.h @@ -76,6 +76,7 @@ void menuRadioTools(event_t event); void menuRadioCalibration(event_t event); void menuRadioSpectrumAnalyser(event_t event); void menuRadioPowerMeter(event_t event); +void menuGhostModuleConfig(event_t event); extern const MenuHandlerFunc menuTabGeneral[MENU_RADIO_PAGES_COUNT]; diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt index 74d74b9b7..3151e92d6 100644 --- a/radio/src/gui/colorlcd/CMakeLists.txt +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -79,6 +79,10 @@ if(PXX2 OR MULTIMODULE) add_gui_src(radio_spectrum_analyser.cpp) endif() +if(GHOST) + add_gui_src(radio_ghost_module_config.cpp) +endif() + if(LUA STREQUAL YES) set(SRC ${SRC} lua/widgets.cpp) # TODO set(GUI_SRC ${GUI_SRC} model_custom_scripts.cpp) diff --git a/radio/src/gui/colorlcd/menus.h b/radio/src/gui/colorlcd/menus.h index 544fce039..650abec8e 100644 --- a/radio/src/gui/colorlcd/menus.h +++ b/radio/src/gui/colorlcd/menus.h @@ -251,7 +251,7 @@ bool menuAboutView(event_t event); bool menuMainViewChannelsMonitor(event_t event); bool menuTextView(event_t event); bool menuScreensTheme(event_t event); -bool menuRadioSpectrumAnalyser(event_t event); +bool menuRadioGhostModuleConfig(event_t event); bool menuRadioPowerMeter(event_t event); extern int8_t checkIncDec_Ret; // global helper vars diff --git a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp new file mode 100644 index 000000000..e97a211c5 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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. + */ + +#include "opentx.h" +#include "radio_ghost_module_config.h" + +class GhostModuleConfigWindow: public Window +{ + public: + GhostModuleConfigWindow(Window * parent, const rect_t & rect) : + Window(parent, rect, FORWARD_SCROLL | FORM_FORWARD_FOCUS | REFRESH_ALWAYS) + { + setFocus(SET_FOCUS_DEFAULT); + } + + void paint(BitmapBuffer * dc) override + { + constexpr coord_t xOffset = 140; + constexpr coord_t xOffset2 = 260; + constexpr coord_t yOffset = 20; + constexpr coord_t lineSpacing = 25; + + for (uint8_t line = 0; line < GHST_MENU_LINES; line++) { + if (reusableBuffer.ghostMenu.line[line].splitLine) { + if (reusableBuffer.ghostMenu.line[line].lineFlags & GHST_LINE_FLAGS_LABEL_SELECT) { + dc->drawSolidFilledRect(xOffset, yOffset + line * lineSpacing, getTextWidth(reusableBuffer.ghostMenu.line[line].menuText, 0,FONT(L)), getFontHeight(FONT(L)), FONT(L) | FOCUS_BGCOLOR); + dc->drawText(xOffset, yOffset + line * lineSpacing, reusableBuffer.ghostMenu.line[line].menuText, FONT(L) | DEFAULT_BGCOLOR); + } + else { + dc->drawText(xOffset, yOffset + line * lineSpacing, reusableBuffer.ghostMenu.line[line].menuText, FONT(L)); + } + + if (reusableBuffer.ghostMenu.line[line].lineFlags & GHST_LINE_FLAGS_VALUE_SELECT) { + dc->drawSolidFilledRect(xOffset, yOffset + line * lineSpacing, getTextWidth( &reusableBuffer.ghostMenu.line[line].menuText[reusableBuffer.ghostMenu.line[line].splitLine], 0,FONT(L)), getFontHeight(0), FOCUS_BGCOLOR); + dc->drawText(xOffset, yOffset + line * lineSpacing, &reusableBuffer.ghostMenu.line[line].menuText[reusableBuffer.ghostMenu.line[line].splitLine], FONT(L) | DEFAULT_BGCOLOR); + } + else { + dc->drawText(xOffset2, yOffset + line * lineSpacing, &reusableBuffer.ghostMenu.line[line].menuText[reusableBuffer.ghostMenu.line[line].splitLine], FONT(L) | DEFAULT_COLOR); + } + } + else { + if (reusableBuffer.ghostMenu.line[line].lineFlags & GHST_LINE_FLAGS_LABEL_SELECT) { + dc->drawSolidFilledRect(xOffset, yOffset + line * lineSpacing, getTextWidth(reusableBuffer.ghostMenu.line[line].menuText, 0, FONT(L)), getFontHeight(FONT(L)), FOCUS_BGCOLOR); + dc->drawText(xOffset, yOffset + line * lineSpacing, reusableBuffer.ghostMenu.line[line].menuText, FONT(L) | DEFAULT_BGCOLOR); + } + else if (reusableBuffer.ghostMenu.line[line].lineFlags & GHST_LINE_FLAGS_VALUE_EDIT) { + if (BLINK_ON_PHASE) { + dc->drawText(xOffset, yOffset + line * lineSpacing, reusableBuffer.ghostMenu.line[line].menuText, FONT(L)); + } + } + else { + dc->drawText(xOffset, yOffset + line * lineSpacing, reusableBuffer.ghostMenu.line[line].menuText, FONT(L) | DEFAULT_COLOR); + } + } + } + } + protected: +}; + +RadioGhostModuleConfig::RadioGhostModuleConfig(uint8_t moduleIdx) : + Page(ICON_RADIO_TOOLS), + moduleIdx(moduleIdx) +{ + init(); + buildHeader(&header); + buildBody(&body); + setFocus(SET_FOCUS_DEFAULT); +} + +void RadioGhostModuleConfig::buildHeader(Window * window) +{ + new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + 10, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, "GHOST MODULE", 0, MENU_COLOR); +} + +void RadioGhostModuleConfig::buildBody(FormWindow * window) +{ + new GhostModuleConfigWindow(window, {0, 0, LCD_W, LCD_H - MENU_HEADER_HEIGHT - 5}); +} + +#if defined(HARDWARE_KEYS) +void RadioGhostModuleConfig::onEvent(event_t event) +{ + switch (event) { +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_LEFT: + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_JOYUP; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_NONE; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + break; +#endif + +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_RIGHT: + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_JOYDOWN; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_NONE; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + break; +#endif + + case EVT_KEY_FIRST(KEY_ENTER): + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_JOYPRESS; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_NONE; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + break; + + case EVT_KEY_BREAK(KEY_EXIT): + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_JOYLEFT; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_NONE; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + break; + + case EVT_KEY_LONG(KEY_EXIT): + menuVerticalOffset = 0; + memclear(&reusableBuffer.ghostMenu, sizeof(reusableBuffer.ghostMenu)); + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_NONE; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_CLOSE; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + RTOS_WAIT_MS(10); + deleteLater(); + break; + } + Page::onEvent(event); +} +#endif + +void RadioGhostModuleConfig::init() +{ + memclear(&reusableBuffer.ghostMenu, sizeof(reusableBuffer.ghostMenu)); + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_NONE; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_OPEN; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; +} + diff --git a/radio/src/gui/colorlcd/radio_ghost_module_config.h b/radio/src/gui/colorlcd/radio_ghost_module_config.h new file mode 100644 index 000000000..6dc8b85e7 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_ghost_module_config.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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. + */ + +#pragma once + +#include "page.h" + +class RadioGhostModuleConfig: public Page +{ + public: + explicit RadioGhostModuleConfig(uint8_t moduleIdx); + +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif + + protected: + uint8_t moduleIdx; + + void buildHeader(Window * window); + void buildBody(FormWindow * window); + void init(); +}; + diff --git a/radio/src/gui/colorlcd/radio_tools.cpp b/radio/src/gui/colorlcd/radio_tools.cpp index bcaff6f3d..e968d1c4d 100644 --- a/radio/src/gui/colorlcd/radio_tools.cpp +++ b/radio/src/gui/colorlcd/radio_tools.cpp @@ -21,6 +21,7 @@ #include #include "radio_tools.h" #include "radio_spectrum_analyser.h" +#include "radio_ghost_module_config.h" #include "opentx.h" #include "libopenui.h" #include "lua/lua_api.h" @@ -174,5 +175,16 @@ void RadioToolsPage::rebuild(FormWindow * window) } #endif +#if defined(GHOST) + if (isModuleGhost(EXTERNAL_MODULE)) { + new StaticText(window, grid.getLabelSlot(), "ghost", BUTTON_BACKGROUND, CENTERED); + new TextButton(window, grid.getFieldSlot(1), "Ghost module config", [=]() -> uint8_t { + new RadioGhostModuleConfig(EXTERNAL_MODULE); + return 0; + }, 0); + grid.nextLine(); + } +#endif + window->setInnerHeight(grid.getWindowHeight()); } \ No newline at end of file diff --git a/radio/src/gui/common/stdlcd/CMakeLists.txt b/radio/src/gui/common/stdlcd/CMakeLists.txt index f3b021c37..a453932f9 100644 --- a/radio/src/gui/common/stdlcd/CMakeLists.txt +++ b/radio/src/gui/common/stdlcd/CMakeLists.txt @@ -42,6 +42,13 @@ if(PXX2) ) endif() +if(GHOST) + set(GUI_SRC + ${GUI_SRC} + ../common/stdlcd/radio_ghost_menu.cpp + ) +endif() + if(PXX2 OR MULTIMODULE) set(GUI_SRC ${GUI_SRC} diff --git a/radio/src/gui/common/stdlcd/radio_ghost_menu.cpp b/radio/src/gui/common/stdlcd/radio_ghost_menu.cpp new file mode 100644 index 000000000..490e9f021 --- /dev/null +++ b/radio/src/gui/common/stdlcd/radio_ghost_menu.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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. + */ + +#include "opentx.h" + +void menuGhostModuleConfig(event_t event) +{ + switch (event) { + case EVT_ENTRY: + memclear(&reusableBuffer.ghostMenu, sizeof(reusableBuffer.ghostMenu)); + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_NONE; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_OPEN; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + break; + +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_LEFT: +#elif defined(KEYS_GPIO_REG_UP) + case EVT_KEY_BREAK(KEY_UP): +#elif defined(KEYS_GPIO_REG_PLUS) + case EVT_KEY_BREAK(KEY_PLUS): +#endif + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_JOYUP; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_NONE; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + break; + +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_RIGHT: +#elif defined(KEYS_GPIO_REG_DOWN) + case EVT_KEY_BREAK(KEY_DOWN): +#elif defined(KEYS_GPIO_REG_MINUS) + case EVT_KEY_BREAK(KEY_MINUS): +#endif + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_JOYDOWN; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_NONE; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + break; + + + case EVT_KEY_FIRST(KEY_ENTER): + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_JOYPRESS; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_NONE; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + break; + + case EVT_KEY_BREAK(KEY_EXIT): + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_JOYLEFT; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_NONE; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + break; + + case EVT_KEY_LONG(KEY_EXIT): + menuVerticalOffset = 0; + memclear(&reusableBuffer.ghostMenu, sizeof(reusableBuffer.ghostMenu)); + reusableBuffer.ghostMenu.buttonAction = GHST_BTN_NONE; + reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_CLOSE; + moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; + RTOS_WAIT_MS(10); + popMenu(); + break; + } + + if (reusableBuffer.ghostMenu.menuAction == GHST_MENU_CTRL_CLOSE) { + popMenu(); + } + + constexpr coord_t xOffset = LCD_W / 3 - 15; + constexpr coord_t xOffset2 = LCD_W / 3 + LCD_W / 4; + constexpr coord_t yOffset = 6; + LcdFlags flags = 0; + + for (uint8_t line = 0; line < GHST_MENU_LINES; line++) { + flags = 0; + if (reusableBuffer.ghostMenu.line[line].splitLine) { + if (reusableBuffer.ghostMenu.line[line].lineFlags & GHST_LINE_FLAGS_LABEL_SELECT) + flags = INVERS; + lcdDrawText(xOffset, yOffset + line * FH, reusableBuffer.ghostMenu.line[line].menuText, flags); + + flags = 0; + if (reusableBuffer.ghostMenu.line[line].lineFlags & GHST_LINE_FLAGS_VALUE_SELECT) + flags |= INVERS; + if (reusableBuffer.ghostMenu.line[line].lineFlags & GHST_LINE_FLAGS_VALUE_EDIT) + flags |= BLINK; + lcdDrawText(xOffset2, yOffset + line * FH, &reusableBuffer.ghostMenu.line[line].menuText[reusableBuffer.ghostMenu.line[line].splitLine], flags); + } + else { + if (reusableBuffer.ghostMenu.line[line].lineFlags & GHST_LINE_FLAGS_LABEL_SELECT) + flags = INVERS; + lcdDrawText(xOffset, yOffset + line * FH, reusableBuffer.ghostMenu.line[line].menuText, flags); + } + } +} diff --git a/radio/src/gui/common/stdlcd/radio_tools.cpp b/radio/src/gui/common/stdlcd/radio_tools.cpp index 8a1cd4498..ab881a1d6 100644 --- a/radio/src/gui/common/stdlcd/radio_tools.cpp +++ b/radio/src/gui/common/stdlcd/radio_tools.cpp @@ -129,6 +129,10 @@ void menuRadioTools(event_t event) addRadioModuleTool(index++, STR_POWER_METER_EXT, menuRadioPowerMeter, EXTERNAL_MODULE); #endif +#if defined(GHOST) + if (isModuleGhost(EXTERNAL_MODULE)) + addRadioModuleTool(index++, "Ghost Menu", menuGhostModuleConfig, EXTERNAL_MODULE); +#endif if (index == 0) { lcdDrawCenteredText(LCD_H/2, STR_NO_TOOLS); } diff --git a/radio/src/opentx.h b/radio/src/opentx.h index fe84d0bbd..b2dfb6753 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -1112,6 +1112,14 @@ union ReusableBuffer uint8_t moduleOFF; } spectrumAnalyser; +#if defined(GHOST) + struct { + GhostMenuData line[GHST_MENU_LINES + 1]; + uint8_t menuAction; + uint8_t buttonAction; + } ghostMenu; +#endif + struct { uint32_t freq; int16_t power; diff --git a/radio/src/pulses/ghost.cpp b/radio/src/pulses/ghost.cpp index 03f098fe2..40cf551db 100755 --- a/radio/src/pulses/ghost.cpp +++ b/radio/src/pulses/ghost.cpp @@ -20,6 +20,31 @@ #include "opentx.h" +uint8_t createGhostMenuControlFrame(uint8_t * frame, int16_t * pulses) +{ + moduleState[EXTERNAL_MODULE].counter = GHST_FRAME_CHANNEL; + + uint8_t * buf = frame; +#if SPORT_MAX_BAUDRATE < 400000 + *buf++ = g_eeGeneral.telemetryBaudrate == GHST_TELEMETRY_RATE_400K ? GHST_ADDR_MODULE_SYM : GHST_ADDR_MODULE_ASYM; +#else + *buf++ = GHST_ADDR_MODULE_SYM; +#endif + + *buf++ = GHST_UL_RC_CHANS_SIZE; + uint8_t * crc_start = buf; + *buf++ = GHST_UL_MENU_CTRL; + *buf++ = reusableBuffer.ghostMenu.buttonAction; // Joystick states, Up, Down, Left, Right, Press + *buf++ = reusableBuffer.ghostMenu.menuAction; // menu control, open, close, etc. + + for (uint8_t i = 0; i < 8; i++) + *buf++ = 0; // padding to make this the same size as the pulses packet + + *buf++ = crc8(crc_start, GHST_UL_RC_CHANS_SIZE - 1); + + return buf - frame; +} + // Range for pulses (channels output) is [-1024:+1024] uint8_t createGhostChannelsFrame(uint8_t * frame, int16_t * pulses) { @@ -89,6 +114,9 @@ void setupPulsesGhost() { if (telemetryProtocol == PROTOCOL_TELEMETRY_GHOST) { uint8_t * pulses = extmodulePulsesData.ghost.pulses; - extmodulePulsesData.ghost.length = createGhostChannelsFrame(pulses, &channelOutputs[g_model.moduleData[EXTERNAL_MODULE].channelsStart]); + if (moduleState[EXTERNAL_MODULE].counter == GHST_MENU_CONTROL) + extmodulePulsesData.ghost.length = createGhostMenuControlFrame(pulses, &channelOutputs[g_model.moduleData[EXTERNAL_MODULE].channelsStart]); + else + extmodulePulsesData.ghost.length = createGhostChannelsFrame(pulses, &channelOutputs[g_model.moduleData[EXTERNAL_MODULE].channelsStart]); } -} +} \ No newline at end of file diff --git a/radio/src/telemetry/ghost.cpp b/radio/src/telemetry/ghost.cpp index 406db73d0..c96ba3d35 100755 --- a/radio/src/telemetry/ghost.cpp +++ b/radio/src/telemetry/ghost.cpp @@ -175,6 +175,31 @@ void processGhostTelemetryFrame() processGhostTelemetryValueString(sensor, vtxBandString); break; } + + case GHST_DL_MENU_DESC: + { + GhostMenuFrame * packet; + GhostMenuData * lineData; + + packet = (GhostMenuFrame * ) telemetryRxBuffer; + lineData = (GhostMenuData *) &reusableBuffer.ghostMenu.line[packet->lineIndex]; + lineData->splitLine = 0; + reusableBuffer.ghostMenu.menuAction = packet->menuFlags; + lineData->lineFlags = packet->lineFlags; + for (uint8_t i = 0; i < GHST_MENU_CHARS; i++) { + if (packet->menuText[i] == 0x7C) { + lineData->menuText[i] = '\0'; + lineData->splitLine = i + 1; + } + else { + lineData->menuText[i] = packet->menuText[i]; + } + } + lineData->menuText[GHST_MENU_CHARS] = '\0'; + if (packet->lineIndex == GHST_MENU_LINES - 1) + lineData->menuUpdateNeeded = true; + break; + } } } diff --git a/radio/src/telemetry/ghost.h b/radio/src/telemetry/ghost.h index 9fcd54120..4f7fdbb37 100755 --- a/radio/src/telemetry/ghost.h +++ b/radio/src/telemetry/ghost.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _GHOST_H_ -#define _GHOST_H_ +#pragma once #include #include "dataconstants.h" @@ -38,10 +37,12 @@ #define GHST_UL_RC_CHANS_HS4_9TO12 0x11 // High Speed 4 channel (12 bits), plus CH9-12 (8 bits) #define GHST_UL_RC_CHANS_HS4_13TO16 0x12 // High Speed 4 channel (12 bits), plus CH13-16 (8 bits) #define GHST_UL_RC_CHANS_SIZE 12 // 1 (type) + 10 (data) + 1 (crc) +#define GHST_UL_MENU_CTRL 0x13 #define GHST_DL_OPENTX_SYNC 0x20 #define GHST_DL_LINK_STAT 0x21 #define GHST_DL_VTX_STAT 0x22 +#define GHST_DL_MENU_DESC 0x24 #define GHST_RC_CTR_VAL_12BIT 0x7C0 // 0x3e0 << 1 #define GHST_RC_CTR_VAL_8BIT 0x7C @@ -113,4 +114,60 @@ enum GhostTelemetryBaudrates #define GHOST_BAUDRATE 400000 #define GHOST_PERIOD 4 -#endif // _GHOST_H_ +enum GhostLineFlags +{ + GHST_LINE_FLAGS_NONE = 0x00, + GHST_LINE_FLAGS_LABEL_SELECT = 0x01, + GHST_LINE_FLAGS_VALUE_SELECT = 0x02, + GHST_LINE_FLAGS_VALUE_EDIT = 0x04, +}; + +enum GhostButtons +{ + GHST_BTN_NONE = 0x00, + GHST_BTN_JOYPRESS = 0X01, + GHST_BTN_JOYUP = 0X02, + GHST_BTN_JOYDOWN = 0X04, + GHST_BTN_JOYLEFT = 0X08, + GHST_BTN_JOYRIGHT = 0X10, + GHST_BTN_BIND = 0X20 // future, for no-UI Ghost +}; + +enum GhostMenuControl +{ + GHST_MENU_CTRL_NONE = 0X00, + GHST_MENU_CTRL_OPEN = 0X01, + GHST_MENU_CTRL_CLOSE = 0X02, + GHST_MENU_CTRL_REDRAW = 0X04, +}; + +enum GhostFrames +{ + GHST_FRAME_CHANNEL, + GHST_MENU_CONTROL +}; + +constexpr uint8_t GHST_MENU_LINES = 6; +constexpr uint8_t GHST_MENU_CHARS = 20; + +// GHST_DL_MENU_DESC (27 bytes) +struct GhostMenuFrame +{ + uint8_t address; + uint8_t length ; + uint8_t packetId; + uint8_t menuFlags; // GHST_MENU_CTRL + uint8_t lineFlags; // Carat states, Inverse, Bold for each of Menu Label, and Value + uint8_t lineIndex; // 0 = first line + unsigned char menuText[GHST_MENU_CHARS]; + uint8_t crc; +}; + +struct GhostMenuData +{ + uint8_t menuFlags; // Update Line, Clear Menu, etc. + uint8_t lineFlags; // Carat states, Inverse, Bold for each of Menu Label, and Value + uint8_t splitLine; // Store beginning of Value substring + char menuText[GHST_MENU_CHARS + 1]; + uint8_t menuUpdateNeeded; +};