diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index 88f2b0e65..59a18cd25 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -24,18 +24,200 @@ #include "model_telemetry.h" #include "opentx.h" #include "libopenui.h" +#include "lvgl_widgets/input_mix_line.h" #define SET_DIRTY() storageDirty(EE_MODEL) -static constexpr coord_t SENSOR_LABEL_WIDTH = 80; -static constexpr coord_t SENSOR_COL1 = 10; -static constexpr coord_t SENSOR_COL2 = SENSOR_COL1 + 70; -static constexpr coord_t SENSOR_COL3 = LCD_W - SENSOR_LABEL_WIDTH - 38; +std::string getSensorCustomValue(uint8_t sensor, int32_t value, LcdFlags flags); + +// Overview grid variants +static const lv_coord_t col_dsc[] = {LV_GRID_FR(14), LV_GRID_FR(6), + LV_GRID_TEMPLATE_LAST}; +#if LCD_W > LCD_H +static const lv_coord_t col_dsc2[] = {LV_GRID_FR(3), LV_GRID_FR(3), 8, LV_GRID_FR(8), LV_GRID_FR(2), + LV_GRID_TEMPLATE_LAST}; +#else +static const lv_coord_t col_dsc2[] = {28, 56, 8, LV_GRID_FR(1), 32, + LV_GRID_TEMPLATE_LAST}; +#endif +static const lv_coord_t col_dsc3[] = {LCD_W - 12, + LV_GRID_TEMPLATE_LAST}; +#if LCD_W > LCD_H +static const lv_coord_t col_dsc4[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST}; +#else +static const lv_coord_t col_dsc4[] = {LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST}; +#endif +static const lv_coord_t col_dsc5[] = {LV_GRID_FR(5), LV_GRID_FR(4), LV_GRID_FR(4), LV_GRID_FR(4), + LV_GRID_TEMPLATE_LAST}; + +// Edit grid variants +static const lv_coord_t e_col_dsc[] = {LV_GRID_FR(2), LV_GRID_FR(3), + LV_GRID_TEMPLATE_LAST}; +static const lv_coord_t e_col_dsc2[] = {LV_GRID_FR(4), LV_GRID_FR(3), LV_GRID_FR(3), + LV_GRID_TEMPLATE_LAST}; + +static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, + LV_GRID_TEMPLATE_LAST}; + +// Sensor value label that updates current value display +class SensorValueLabel : public StaticText { + public: + SensorValueLabel(Window * parent, const rect_t &rect, uint8_t index) : + StaticText(parent, rect, ""), + index(index) + { + padLeft(10); + } + + void checkEvents() override + { + uint32_t now = RTOS_GET_MS(); + TelemetryItem & telemetryItem = telemetryItems[index]; + + if ((now - lastRefresh >= 200) || telemetryItem.isFresh()) { + // update at least every 200ms + lastRefresh = now; + + std::string s; + LcdFlags color = COLOR_THEME_SECONDARY1; + + if (telemetryItem.isAvailable()) { + color = telemetryItem.isOld() ? COLOR_THEME_WARNING : color; + s = getSensorCustomValue(index, getValue(MIXSRC_FIRST_TELEM + 3 * index), LEFT); + } else { + s = "---"; + } + +#if LCD_H > LCD_W + if (s.size() >= 20) { + setFont(FONT(XS)); + } else { + setFont(FONT(STD)); + } +#endif + lv_obj_set_style_text_color(lvobj, makeLvColor(color), 0); + setText(s); + } + } + + void paint(BitmapBuffer * dc) override + { + TelemetryItem &telemetryItem = telemetryItems[index]; + + // Draw a 'fresh' marker + if (telemetryItem.isFresh()) + dc->drawFilledCircle(5, 10, 4, COLOR_THEME_SECONDARY1); + } + + protected: + uint8_t index; + uint32_t lastRefresh = 0; +}; + +class SensorButton : public Button { + public: + SensorButton(Window * parent, const rect_t &rect, uint8_t index) : + Button(parent, rect, nullptr, 0, 0, input_mix_line_create), + index(index) + { + padAll(0); + + lv_obj_set_layout(lvobj, LV_LAYOUT_GRID); + lv_obj_set_grid_dsc_array(lvobj, col_dsc2, row_dsc); + lv_obj_set_style_pad_row(lvobj, 0, 0); + lv_obj_set_style_pad_column(lvobj, 4, 0); + + check(isActive()); + + lv_obj_update_layout(parent->getLvObj()); + if(lv_obj_is_visible(lvobj)) delayed_init(nullptr); + + lv_obj_add_event_cb(lvobj, SensorButton::on_draw, LV_EVENT_DRAW_MAIN_BEGIN, nullptr); + } + + protected: + bool init = false; + uint8_t index; + uint32_t lastRefresh = 0; + + static void on_draw(lv_event_t * e) + { + lv_obj_t* target = lv_event_get_target(e); + auto line = (SensorButton*)lv_obj_get_user_data(target); + if (line) { + if (!line->init) + line->delayed_init(e); + else + line->refresh(); + } + } + + bool isActive() const + { + TelemetryItem & telemetryItem = telemetryItems[index]; + return telemetryItem.isAvailable(); + } + + void checkEvents() override + { + Button::checkEvents(); + check(isActive()); + } + + void delayed_init(lv_event_t* e) + { + char s[20]; + + auto lbl = lv_label_create(lvobj); + lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_grid_cell(lbl, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1); + + lv_label_set_text(lbl, std::to_string(index).c_str()); + + lbl = lv_label_create(lvobj); + lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lbl, LV_GRID_ALIGN_START, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1); + + strAppend(s, g_model.telemetrySensors[index].label, TELEM_LABEL_LEN); + lv_label_set_text(lbl, s); + + auto v = new SensorValueLabel(this, rect_t{}, index); + lv_obj_set_grid_cell(v->getLvObj(), LV_GRID_ALIGN_STRETCH, 2, 2, LV_GRID_ALIGN_CENTER, 0, 1); + + TelemetrySensor * sensor = & g_model.telemetrySensors[index]; + if (IS_SPEKTRUM_PROTOCOL()) { + sprintf(s, "%x", sensor->id); + } else if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) { + sprintf(s, "%d", sensor->instance); + } + + lbl = lv_label_create(lvobj); + lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lbl, LV_GRID_ALIGN_START, 4, 1, LV_GRID_ALIGN_CENTER, 0, 1); + + lv_label_set_text(lbl, s); + + init = true; + refresh(); + lv_obj_update_layout(lvobj); + + if(e) { + auto param = lv_event_get_param(e); + lv_event_send(lvobj, LV_EVENT_DRAW_MAIN, param); + } + } + + void refresh() + { + } +}; class SensorSourceChoice : public SourceChoice { public: - SensorSourceChoice(FormGroup *window, const rect_t &rect, uint8_t *source, + SensorSourceChoice(Window *window, const rect_t &rect, uint8_t *source, IsValueAvailable isValueAvailable) : SourceChoice(window, rect, MIXSRC_NONE, MIXSRC_LAST_TELEM, GET_DEFAULT(*source ? MIXSRC_FIRST_TELEM + 3 * (*source - 1) @@ -55,116 +237,6 @@ class SensorSourceChoice : public SourceChoice } }; -class SensorButton : public Button { - public: - SensorButton(FormGroup * parent, const rect_t &rect, uint8_t index, uint8_t number) : - Button(parent, rect), - index(index), - number(number) - { - } - - static constexpr coord_t line1 = 1; - - void checkEvents() override - { - uint32_t now = RTOS_GET_MS(); - if (now - lastRefresh >= 200) { - // update at least every 200ms - invalidate(); - } - - TelemetryItem & telemetryItem = telemetryItems[index]; - if (telemetryItem.isFresh()) { - invalidate(); - } - - Button::checkEvents(); - } - - void paint(BitmapBuffer * dc) override - { - TelemetryItem &telemetryItem = telemetryItems[index]; - - if (telemetryItem.isAvailable()) - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, COLOR_THEME_ACTIVE); - else - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, COLOR_THEME_PRIMARY2); - - if (telemetryItem.isFresh()) - dc->drawFilledCircle(SENSOR_COL2 - 10, (2 + rect.h - 4)/2, 4, COLOR_THEME_SECONDARY1); - - dc->drawSizedText(SENSOR_COL1, line1, g_model.telemetrySensors[index].label, TELEM_LABEL_LEN); - - if (telemetryItem.isAvailable()) { - LcdFlags color = telemetryItem.isOld() ? COLOR_THEME_WARNING : COLOR_THEME_SECONDARY1; - drawSensorCustomValue(dc, SENSOR_COL2, line1, index, getValue(MIXSRC_FIRST_TELEM + 3 * index), LEFT | color); - } - else { - dc->drawText(SENSOR_COL2, line1, "---", COLOR_THEME_SECONDARY1); - } - - TelemetrySensor * sensor = & g_model.telemetrySensors[index]; - if (IS_SPEKTRUM_PROTOCOL()) { - drawHexNumber(dc, SENSOR_COL3, line1, sensor->id, LEFT); - } - else if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) { - dc->drawNumber(SENSOR_COL3, line1, sensor->instance, LEFT); - } - - if (hasFocus()) - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); - else - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); - } - - protected: - uint8_t index; - uint8_t number; - uint32_t lastRefresh = 0; -}; - -class SensorLiveValue: public Window { - public: - explicit SensorLiveValue(Window * parent, const rect_t & rect, int index): - Window(parent, rect, OPAQUE), - index(index) - { - } - - void checkEvents() override - { - uint32_t now = RTOS_GET_MS(); - if (now - lastRefresh >= 200) { - // update at least every 200ms - invalidate(); - } - - TelemetryItem & telemetryItem = telemetryItems[index]; - if (telemetryItem.isFresh()) { - invalidate(); - } - - Window::checkEvents(); - } - - void paint(BitmapBuffer * dc) override - { - TelemetryItem &telemetryItem = telemetryItems[index]; - - dc->drawSolidFilledRect(0, 0, width(), height(), COLOR_THEME_SECONDARY1); - if (telemetryItem.isAvailable()) { - LcdFlags color = telemetryItem.isOld() ? COLOR_THEME_WARNING : COLOR_THEME_PRIMARY2; - drawSensorCustomValue(dc, 0, 1, index, getValue(MIXSRC_FIRST_TELEM + 3 * index), LEFT | color); - } else { - dc->drawText(0, 1, "---", COLOR_THEME_PRIMARY2); - } - } - protected: - uint8_t index; - uint32_t lastRefresh = 0; -}; - class SensorEditWindow : public Page { public: explicit SensorEditWindow(uint8_t index) : @@ -177,28 +249,61 @@ class SensorEditWindow : public Page { protected: uint8_t index; - FormGroup * sensorParametersWindow = nullptr; + uint32_t lastRefresh = 0; + FormWindow * sensorParametersWindow = nullptr; + StaticText * headerValue = nullptr; void buildHeader(Window * window) { header.setTitle(STR_SENSOR + std::to_string(index + 1)); - new SensorLiveValue(window, + headerValue = new StaticText( + window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, - LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, index); + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + "", 0, COLOR_THEME_PRIMARY2); + + lv_obj_set_style_text_color(headerValue->getLvObj(), makeLvColor(COLOR_THEME_WARNING), LV_STATE_USER_1); + } + + void checkEvents() override + { + uint32_t now = RTOS_GET_MS(); + TelemetryItem & telemetryItem = telemetryItems[index]; + + if ((now - lastRefresh >= 200) || telemetryItem.isFresh()) { + // update at least every 200ms + lastRefresh = now; + + lv_obj_clear_state(headerValue->getLvObj(), LV_STATE_USER_1); + + if (telemetryItem.isAvailable()) { + if (telemetryItem.isOld()) + lv_obj_add_state(headerValue->getLvObj(), LV_STATE_USER_1); + headerValue->setText(getSensorCustomValue(index, getValue(MIXSRC_FIRST_TELEM + 3 * index), LEFT)); + } else { + headerValue->setText("---"); + } + } } void updateSensorParametersWindow() { // Sensor variable part - FormGridLayout grid; sensorParametersWindow->clear(); + sensorParametersWindow->setFlexLayout(); + FlexGridLayout grid(e_col_dsc, row_dsc, 2); + FlexGridLayout grid2(e_col_dsc2, row_dsc, 2); + TelemetrySensor * sensor = &g_model.telemetrySensors[index]; + FormWindow::Line* line; + if (sensor->type == TELEM_TYPE_CALCULATED) { // Formula - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_FORMULA, 0, COLOR_THEME_PRIMARY1); - new Choice(sensorParametersWindow, grid.getFieldSlot(), STR_VFORMULAS, 0, TELEM_FORMULA_LAST, GET_DEFAULT(sensor->formula), + line = sensorParametersWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_FORMULA, 0, COLOR_THEME_PRIMARY1); + new Choice(line, rect_t{}, STR_VFORMULAS, 0, TELEM_FORMULA_LAST, GET_DEFAULT(sensor->formula), [=](uint8_t newValue) { sensor->formula = newValue; sensor->param = 0; @@ -218,24 +323,24 @@ class SensorEditWindow : public Page { telemetryItems[index].clear(); updateSensorParametersWindow(); }); - grid.nextLine(); } else { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_ID, 0, COLOR_THEME_PRIMARY1); - auto hex = new NumberEdit(sensorParametersWindow, grid.getFieldSlot(2, 0), 0, 0xFFFF, GET_SET_DEFAULT(sensor->id)); + line = sensorParametersWindow->newLine(&grid2); + new StaticText(line, rect_t{}, STR_ID, 0, COLOR_THEME_PRIMARY1); + auto hex = new NumberEdit(line, rect_t{}, 0, 0xFFFF, GET_SET_DEFAULT(sensor->id)); hex->setDisplayHandler([](int32_t value) { std::stringstream stream; stream << std::hex << value; return stream.str(); }); - new NumberEdit(sensorParametersWindow, grid.getFieldSlot(2, 1), 0, 0xff, GET_SET_DEFAULT(sensor->instance)); - grid.nextLine(); + new NumberEdit(line, rect_t{}, 0, 0xff, GET_SET_DEFAULT(sensor->instance)); } // Unit if ((sensor->type == TELEM_TYPE_CALCULATED && (sensor->formula == TELEM_FORMULA_DIST)) || sensor->isConfigurable()) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_UNIT, 0, COLOR_THEME_PRIMARY1); - new Choice(sensorParametersWindow, grid.getFieldSlot(), STR_VTELEMUNIT, 0, UNIT_MAX, GET_DEFAULT(sensor->unit), + line = sensorParametersWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_UNIT, 0, COLOR_THEME_PRIMARY1); + new Choice(line, rect_t{}, STR_VTELEMUNIT, 0, UNIT_MAX, GET_DEFAULT(sensor->unit), [=](uint8_t newValue) { sensor->unit = newValue; if (sensor->unit == UNIT_FAHRENHEIT) { @@ -245,154 +350,156 @@ class SensorEditWindow : public Page { telemetryItems[index].clear(); updateSensorParametersWindow(); }); - grid.nextLine(); } // Precision if (sensor->isPrecConfigurable() && sensor->unit != UNIT_FAHRENHEIT) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_PRECISION, 0, COLOR_THEME_PRIMARY1); - new Choice(sensorParametersWindow, grid.getFieldSlot(), STR_VPREC, 0, 2, GET_DEFAULT(sensor->prec), + line = sensorParametersWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_PRECISION, 0, COLOR_THEME_PRIMARY1); + new Choice(line, rect_t{}, STR_VPREC, 0, 2, GET_DEFAULT(sensor->prec), [=](uint8_t newValue) { sensor->prec = newValue; SET_DIRTY(); telemetryItems[index].clear(); updateSensorParametersWindow(); }); - grid.nextLine(); } // Params if (sensor->unit < UNIT_FIRST_VIRTUAL) { + line = sensorParametersWindow->newLine(&grid); if (sensor->type == TELEM_TYPE_CALCULATED) { if (sensor->formula == TELEM_FORMULA_CELL) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_CELLSENSOR, 0, COLOR_THEME_PRIMARY1); - new SensorSourceChoice(sensorParametersWindow, grid.getFieldSlot(), &sensor->cell.source, isCellsSensor); + new StaticText(line, rect_t{}, STR_CELLSENSOR, 0, COLOR_THEME_PRIMARY1); + new SensorSourceChoice(line, rect_t{}, &sensor->cell.source, isCellsSensor); } else if (sensor->formula == TELEM_FORMULA_DIST) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_GPSSENSOR, 0, COLOR_THEME_PRIMARY1); - new SensorSourceChoice(sensorParametersWindow, grid.getFieldSlot(), &sensor->dist.gps, isGPSSensor); + new StaticText(line, rect_t{}, STR_GPSSENSOR, 0, COLOR_THEME_PRIMARY1); + new SensorSourceChoice(line, rect_t{}, &sensor->dist.gps, isGPSSensor); } else if (sensor->formula == TELEM_FORMULA_CONSUMPTION) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_CURRENTSENSOR, 0, COLOR_THEME_PRIMARY1); - new SensorSourceChoice(sensorParametersWindow, grid.getFieldSlot(), &sensor->consumption.source, isSensorAvailable); + new StaticText(line, rect_t{}, STR_CURRENTSENSOR, 0, COLOR_THEME_PRIMARY1); + new SensorSourceChoice(line, rect_t{}, &sensor->consumption.source, isSensorAvailable); } else if (sensor->formula == TELEM_FORMULA_TOTALIZE) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_SOURCE, 0, COLOR_THEME_PRIMARY1); - new SensorSourceChoice(sensorParametersWindow, grid.getFieldSlot(), &sensor->consumption.source, isSensorAvailable); + new StaticText(line, rect_t{}, STR_SOURCE, 0, COLOR_THEME_PRIMARY1); + new SensorSourceChoice(line, rect_t{}, &sensor->consumption.source, isSensorAvailable); } else { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_SOURCE + std::to_string(1), 0, COLOR_THEME_PRIMARY1); - new SensorSourceChoice(sensorParametersWindow, grid.getFieldSlot(), (uint8_t *) &sensor->calc.sources[0], isSensorAvailable); + new StaticText(line, rect_t{}, STR_SOURCE + std::to_string(1), 0, COLOR_THEME_PRIMARY1); + new SensorSourceChoice(line, rect_t{}, (uint8_t *) &sensor->calc.sources[0], isSensorAvailable); } } else { if (sensor->unit == UNIT_RPMS) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_BLADES, 0, COLOR_THEME_PRIMARY1); - new NumberEdit(sensorParametersWindow, grid.getFieldSlot(), 1, 30000, GET_SET_DEFAULT(sensor->custom.ratio)); + new StaticText(line, rect_t{}, STR_BLADES, 0, COLOR_THEME_PRIMARY1); + new NumberEdit(line, rect_t{}, 1, 30000, GET_SET_DEFAULT(sensor->custom.ratio)); } else { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_RATIO, 0, COLOR_THEME_PRIMARY1); - auto edit = new NumberEdit(sensorParametersWindow, grid.getFieldSlot(), 0, 30000, GET_SET_DEFAULT(sensor->custom.ratio)); + new StaticText(line, rect_t{}, STR_RATIO, 0, COLOR_THEME_PRIMARY1); + auto edit = new NumberEdit(line, rect_t{}, 0, 30000, GET_SET_DEFAULT(sensor->custom.ratio)); edit->setZeroText("-"); } } - grid.nextLine(); } if (!(sensor->unit == UNIT_GPS || sensor->unit == UNIT_DATETIME || sensor->unit == UNIT_CELLS || (sensor->type == TELEM_TYPE_CALCULATED && (sensor->formula == TELEM_FORMULA_CONSUMPTION || sensor->formula == TELEM_FORMULA_TOTALIZE)))) { + line = sensorParametersWindow->newLine(&grid); if (sensor->type == TELEM_TYPE_CALCULATED) { if (sensor->formula == TELEM_FORMULA_CELL) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_CELLINDEX, 0, COLOR_THEME_PRIMARY1); - new Choice(sensorParametersWindow, grid.getFieldSlot(), STR_VCELLINDEX, TELEM_CELL_INDEX_LOWEST, TELEM_CELL_INDEX_LAST, GET_SET_DEFAULT(sensor->cell.index)); + new StaticText(line, rect_t{}, STR_CELLINDEX, 0, COLOR_THEME_PRIMARY1); + new Choice(line, rect_t{}, STR_VCELLINDEX, TELEM_CELL_INDEX_LOWEST, TELEM_CELL_INDEX_LAST, GET_SET_DEFAULT(sensor->cell.index)); } else if (sensor->formula == TELEM_FORMULA_DIST) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_ALTSENSOR, 0, COLOR_THEME_PRIMARY1); - new SensorSourceChoice(sensorParametersWindow, grid.getFieldSlot(), &sensor->dist.alt, isAltSensor); + new StaticText(line, rect_t{}, STR_ALTSENSOR, 0, COLOR_THEME_PRIMARY1); + new SensorSourceChoice(line, rect_t{}, &sensor->dist.alt, isAltSensor); } else { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_SOURCE + std::to_string(2), 0, COLOR_THEME_PRIMARY1); - new SensorSourceChoice(sensorParametersWindow, grid.getFieldSlot(), (uint8_t *) &sensor->calc.sources[1], isSensorAvailable); + new StaticText(line, rect_t{}, STR_SOURCE + std::to_string(2), 0, COLOR_THEME_PRIMARY1); + new SensorSourceChoice(line, rect_t{}, (uint8_t *) &sensor->calc.sources[1], isSensorAvailable); } } else if (sensor->unit == UNIT_RPMS) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_MULTIPLIER, 0, COLOR_THEME_PRIMARY1); - new NumberEdit(sensorParametersWindow, grid.getFieldSlot(), 1, 30000, GET_SET_DEFAULT(sensor->custom.offset)); + new StaticText(line, rect_t{}, STR_MULTIPLIER, 0, COLOR_THEME_PRIMARY1); + new NumberEdit(line, rect_t{}, 1, 30000, GET_SET_DEFAULT(sensor->custom.offset)); } else { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_OFFSET, 0, COLOR_THEME_PRIMARY1); - new NumberEdit(sensorParametersWindow, grid.getFieldSlot(), -30000, 30000, GET_SET_DEFAULT(sensor->custom.offset), + new StaticText(line, rect_t{}, STR_OFFSET, 0, COLOR_THEME_PRIMARY1); + new NumberEdit(line, rect_t{}, -30000, 30000, GET_SET_DEFAULT(sensor->custom.offset), 0, (sensor->prec > 0) ? (sensor->prec == 2 ? PREC2 : PREC1) : 0); } - grid.nextLine(); } if ((sensor->type == TELEM_TYPE_CALCULATED && sensor->formula < TELEM_FORMULA_MULTIPLY)) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_SOURCE + std::to_string(3), 0, COLOR_THEME_PRIMARY1); - new SensorSourceChoice(sensorParametersWindow, grid.getFieldSlot(), (uint8_t *) &sensor->calc.sources[2], isSensorAvailable); - grid.nextLine(); + line = sensorParametersWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_SOURCE + std::to_string(3), 0, COLOR_THEME_PRIMARY1); + new SensorSourceChoice(line, rect_t{}, (uint8_t *) &sensor->calc.sources[2], isSensorAvailable); - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_SOURCE + std::to_string(4), 0, COLOR_THEME_PRIMARY1); - new SensorSourceChoice(sensorParametersWindow, grid.getFieldSlot(), (uint8_t *) &sensor->calc.sources[3], isSensorAvailable); - grid.nextLine(); + line = sensorParametersWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_SOURCE + std::to_string(4), 0, COLOR_THEME_PRIMARY1); + new SensorSourceChoice(line, rect_t{}, (uint8_t *) &sensor->calc.sources[3], isSensorAvailable); } // Auto Offset if (sensor->unit != UNIT_RPMS && sensor->isConfigurable()) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_AUTOOFFSET, 0, COLOR_THEME_PRIMARY1); - new CheckBox(sensorParametersWindow, grid.getFieldSlot(), GET_SET_DEFAULT(sensor->autoOffset)); - grid.nextLine(); + line = sensorParametersWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_AUTOOFFSET, 0, COLOR_THEME_PRIMARY1); + new CheckBox(line, rect_t{}, GET_SET_DEFAULT(sensor->autoOffset)); } if (sensor->isConfigurable()) { // Only positive - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_ONLYPOSITIVE, 0, COLOR_THEME_PRIMARY1); - new CheckBox(sensorParametersWindow, grid.getFieldSlot(), GET_SET_DEFAULT(sensor->onlyPositive)); - grid.nextLine(); + line = sensorParametersWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_ONLYPOSITIVE, 0, COLOR_THEME_PRIMARY1); + new CheckBox(line, rect_t{}, GET_SET_DEFAULT(sensor->onlyPositive)); // Filter - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_FILTER, 0, COLOR_THEME_PRIMARY1); - new CheckBox(sensorParametersWindow, grid.getFieldSlot(), GET_SET_DEFAULT(sensor->filter)); - grid.nextLine(); + line = sensorParametersWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_FILTER, 0, COLOR_THEME_PRIMARY1); + new CheckBox(line, rect_t{}, GET_SET_DEFAULT(sensor->filter)); } if (sensor->type == TELEM_TYPE_CALCULATED) { - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_PERSISTENT, 0, COLOR_THEME_PRIMARY1); - new CheckBox(sensorParametersWindow, grid.getFieldSlot(), GET_DEFAULT(sensor->persistent), [=](int32_t newValue) { + line = sensorParametersWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_PERSISTENT, 0, COLOR_THEME_PRIMARY1); + new CheckBox(line, rect_t{}, GET_DEFAULT(sensor->persistent), [=](int32_t newValue) { sensor->persistent = newValue; if (!sensor->persistent) sensor->persistentValue = 0; SET_DIRTY(); }); - grid.nextLine(); } - new StaticText(sensorParametersWindow, grid.getLabelSlot(), STR_LOGS, 0, COLOR_THEME_PRIMARY1); - new CheckBox(sensorParametersWindow, grid.getFieldSlot(), GET_DEFAULT(sensor->logs), [=](int32_t newValue) { + line = sensorParametersWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_LOGS, 0, COLOR_THEME_PRIMARY1); + new CheckBox(line, rect_t{}, GET_DEFAULT(sensor->logs), [=](int32_t newValue) { sensor->logs = newValue; logsClose(); SET_DIRTY(); }); - - sensorParametersWindow->adjustHeight(); } void buildBody(FormWindow * window) { // Sensor one - FormGridLayout grid; - grid.spacer(PAGE_PADDING); + auto form = new FormWindow(window, rect_t{}); + form->setFlexLayout(); + form->padAll(4); + + FlexGridLayout grid(e_col_dsc, row_dsc, 2); TelemetrySensor * sensor = &g_model.telemetrySensors[index]; // Sensor name - new StaticText(window, grid.getLabelSlot(), STR_NAME, 0, COLOR_THEME_PRIMARY1); - new ModelTextEdit(window, grid.getFieldSlot(), sensor->label, sizeof(sensor->label)); - grid.nextLine(); + auto line = form->newLine(&grid); + new StaticText(line, rect_t{}, STR_NAME, 0, COLOR_THEME_PRIMARY1); + new ModelTextEdit(line, rect_t{}, sensor->label, sizeof(sensor->label)); // Type - new StaticText(window, grid.getLabelSlot(), STR_TYPE, 0, COLOR_THEME_PRIMARY1); - new Choice(window, grid.getFieldSlot(), STR_VSENSORTYPES, 0, 1, GET_DEFAULT(sensor->type), + line = form->newLine(&grid); + new StaticText(line, rect_t{}, STR_TYPE, 0, COLOR_THEME_PRIMARY1); + new Choice(line, rect_t{}, STR_VSENSORTYPES, 0, 1, GET_DEFAULT(sensor->type), [=](uint8_t newValue) { sensor->type = newValue; sensor->instance = 0; @@ -404,15 +511,9 @@ class SensorEditWindow : public Page { SET_DIRTY(); updateSensorParametersWindow(); }); - grid.nextLine(); - sensorParametersWindow = new FormGroup(window, {0, grid.getWindowHeight(), LCD_W, 0}, - // FORM_FORWARD_FOCUS | - FORM_NO_BORDER - | FORWARD_SCROLL); + sensorParametersWindow = new FormWindow(form, rect_t{}); updateSensorParametersWindow(); - grid.addWindow(sensorParametersWindow); - } }; @@ -449,58 +550,58 @@ void ModelTelemetryPage::editSensor(FormWindow * window, uint8_t index) }); } +#if LCD_W > LCD_H +#define B_HEIGHT 32 +#else +#define B_HEIGHT 44 +#endif + void ModelTelemetryPage::build(FormWindow * window, int8_t focusSensorIndex) { - FormGridLayout grid; - grid.spacer(PAGE_PADDING); - window->padAll(0); + window->padAll(4); this->window = window; - // RSSI - new Subtitle(window, grid.getLineSlot(), getRssiLabel(), 0, COLOR_THEME_PRIMARY1); - grid.nextLine(); + auto form = new FormWindow(window, rect_t{}); + form->setFlexLayout(); + form->padAll(0); - new StaticText(window, grid.getLabelSlot(true), STR_LOWALARM, 0, COLOR_THEME_PRIMARY1); - new NumberEdit(window, grid.getFieldSlot(), 0, 100, - GET_SET_DEFAULT(g_model.rfAlarms.warning)); - grid.nextLine(); - - new StaticText(window, grid.getLabelSlot(true), STR_CRITICALALARM, 0, COLOR_THEME_PRIMARY1); - new NumberEdit(window, grid.getFieldSlot(), 0, 100, - GET_SET_DEFAULT(g_model.rfAlarms.critical)); - grid.nextLine(); - - new StaticText(window, grid.getLabelSlot(true), STR_DISABLE_ALARM, 0, COLOR_THEME_PRIMARY1); - new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.disableTelemetryWarning)); - grid.nextLine(); - - if (LCD_W < LCD_H) grid.nextLine(); + FlexGridLayout grid(col_dsc, row_dsc, 2); + FlexGridLayout grid2(col_dsc2, row_dsc, 2); // Sensors - grid.setLabelWidth(140); - new Subtitle(window, grid.getLineSlot(), STR_TELEMETRY_SENSORS, 0, COLOR_THEME_PRIMARY1); + auto line = form->newLine(&grid2); + lv_obj_set_style_pad_row(line->getLvObj(), 0, 0); + lv_obj_set_style_pad_column(line->getLvObj(), 4, 0); + auto subttl = new Subtitle(line, rect_t{}, STR_TELEMETRY_SENSORS, 0, COLOR_THEME_PRIMARY1); + +#if LCD_H > LCD_W + lv_obj_set_grid_cell(subttl->getLvObj(), LV_GRID_ALIGN_START, 0, 3, LV_GRID_ALIGN_CENTER, 0, 1); + line = form->newLine(&grid2); + lv_obj_set_style_pad_row(line->getLvObj(), 0, 0); + lv_obj_set_style_pad_column(line->getLvObj(), 4, 0); + grid2.nextCell(); +#endif // Sensors columns titles uint8_t sensorsCount = getTelemetrySensorsCount(); if (sensorsCount > 0) { - // put in the +14 and +17 to align the text correctly. Not sure why we need this - new StaticText(window, { SENSOR_LABEL_WIDTH + SENSOR_COL1, grid.getWindowHeight() + 3, SENSOR_COL2 - SENSOR_COL1, PAGE_LINE_HEIGHT}, STR_NAME, 0, FONT(XS) | COLOR_THEME_PRIMARY1); - new StaticText(window, { SENSOR_LABEL_WIDTH + SENSOR_COL2, grid.getWindowHeight() + 3, SENSOR_COL3 - SENSOR_COL2, PAGE_LINE_HEIGHT}, STR_VALUE, 0, FONT(XS) | COLOR_THEME_PRIMARY1); + new StaticText(line, rect_t{}, STR_NAME, 0, FONT(XS) | COLOR_THEME_PRIMARY1); + grid2.nextCell(); + new StaticText(line, rect_t{}, STR_VALUE, 0, FONT(XS) | COLOR_THEME_PRIMARY1); if (!g_model.ignoreSensorIds && !IS_SPEKTRUM_PROTOCOL()) { - new StaticText(window, { SENSOR_LABEL_WIDTH + SENSOR_COL3, grid.getWindowHeight() + 3, LCD_W - SENSOR_COL3, PAGE_LINE_HEIGHT}, STR_ID, 0, FONT(XS) | COLOR_THEME_PRIMARY1); + new StaticText(line, rect_t{}, STR_ID, 0, FONT(XS) | COLOR_THEME_PRIMARY1); } } - grid.nextLine(); - grid.setLabelWidth(SENSOR_LABEL_WIDTH); - - for (uint8_t idx = 0, count = 0; idx < MAX_TELEMETRY_SENSORS; idx++) { + FlexGridLayout grid3(col_dsc3, row_dsc, 2); + + for (uint8_t idx = 0; idx < MAX_TELEMETRY_SENSORS; idx++) { if (g_model.telemetrySensors[idx].isAvailable()) { - auto txt = new StaticText(window, grid.getLabelSlot(), std::to_string(idx), BUTTON_BACKGROUND, CENTERED | COLOR_THEME_PRIMARY1); - Button * button = new SensorButton(window, grid.getFieldSlot(), idx, ++count); + line = form->newLine(&grid3); + auto button = new SensorButton(line, rect_t{0, 0, LCD_W-12, 25}, idx); + button->setPressHandler([=]() -> uint8_t { - button->bringToTop(); Menu * menu = new Menu(window); menu->addLine(STR_EDIT, [=]() { editSensor(window, idx); @@ -527,41 +628,32 @@ void ModelTelemetryPage::build(FormWindow * window, int8_t focusSensorIndex) }); return 0; }); - button->setFocusHandler([=](bool focus) { - if (focus) { - txt->setBackgroundColor(COLOR_THEME_FOCUS); - txt->setTextFlags(COLOR_THEME_PRIMARY2 | CENTERED); - } else { - txt->setBackgroundColor(COLOR_THEME_SECONDARY2); - txt->setTextFlags(COLOR_THEME_PRIMARY1 | CENTERED); - } - txt->invalidate(); - }); - - - // if (focusSensorIndex == idx) { - // button->setFocus(SET_FOCUS_DEFAULT); - // } - grid.nextLine(); + if (focusSensorIndex == idx) { + lv_group_focus_obj(button->getLvObj()); + } } } + FlexGridLayout grid4(col_dsc4, row_dsc, 4); + // Autodiscover button - auto discover = new TextButton(window, grid.getFieldSlot(2, 0), (allowNewSensors) ? STR_STOP_DISCOVER_SENSORS : STR_DISCOVER_SENSORS); + line = form->newLine(&grid4); + auto discover = new TextButton(line, rect_t{}, (allowNewSensors) ? STR_STOP_DISCOVER_SENSORS : STR_DISCOVER_SENSORS); discover->setPressHandler([=]() { allowNewSensors = !allowNewSensors; if (allowNewSensors) { discover->setText(STR_STOP_DISCOVER_SENSORS); return 1; - } - else { + } else { discover->setText(STR_DISCOVER_SENSORS); return 0; } }); + lv_obj_set_grid_cell(discover->getLvObj(), LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1); + discover->check(allowNewSensors); // New sensor button - new TextButton(window, grid.getFieldSlot(2, 1), STR_TELEMETRY_NEWSENSOR, + auto b = new TextButton(line, rect_t{}, STR_TELEMETRY_NEWSENSOR, [=]() -> uint8_t { int idx = availableTelemetryIndex(); if (idx >= 0) @@ -570,11 +662,14 @@ void ModelTelemetryPage::build(FormWindow * window, int8_t focusSensorIndex) new FullScreenDialog(WARNING_TYPE_ALERT, "", STR_TELEMETRYFULL); return 0; }); - grid.nextLine(); + lv_obj_set_grid_cell(b->getLvObj(), LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1); if (sensorsCount > 0) { +#if LCD_H > LCD_W + line = form->newLine(&grid4); +#endif // Delete all sensors button - new TextButton(window, grid.getFieldSlot(), STR_DELETE_ALL_SENSORS, + b = new TextButton(line, rect_t{}, STR_DELETE_ALL_SENSORS, []() -> uint8_t { new FullScreenDialog(WARNING_TYPE_CONFIRM, STR_CONFIRMDELETE, "", "", [=]() { for (int i = 0; i < MAX_TELEMETRY_SENSORS; i++) { @@ -583,22 +678,49 @@ void ModelTelemetryPage::build(FormWindow * window, int8_t focusSensorIndex) }); return 0; }); - grid.nextLine(); +#if LCD_H > LCD_W + b->setWidth((LCD_W - 16) / 2); + lv_obj_set_grid_cell(b->getLvObj(), LV_GRID_ALIGN_CENTER, 0, 2, LV_GRID_ALIGN_CENTER, 0, 1); +#else + lv_obj_set_grid_cell(b->getLvObj(), LV_GRID_ALIGN_STRETCH, 2, 1, LV_GRID_ALIGN_CENTER, 0, 1); +#endif } - // restore aligned layout - grid.setLabelWidth(PAGE_LABEL_WIDTH); - // Ignore instance button - new StaticText(window, grid.getLabelSlot(true), STR_IGNORE_INSTANCE, 0, COLOR_THEME_PRIMARY1); - new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.ignoreSensorIds)); - grid.nextLine(); + line = form->newLine(&grid); + line->padLeft(10); + new StaticText(line, rect_t{}, STR_IGNORE_INSTANCE, 0, COLOR_THEME_PRIMARY1); + new CheckBox(line, rect_t{}, GET_SET_DEFAULT(g_model.ignoreSensorIds)); + + // RSSI + line = form->newLine(&grid); + new Subtitle(line, rect_t{}, getRssiLabel(), 0, COLOR_THEME_PRIMARY1); + + line = form->newLine(&grid); + line->padLeft(10); + new StaticText(line, rect_t{}, STR_LOWALARM, 0, COLOR_THEME_PRIMARY1); + new NumberEdit(line, rect_t{}, 0, 100, GET_SET_DEFAULT(g_model.rfAlarms.warning)); + + line = form->newLine(&grid); + line->padLeft(10); + new StaticText(line, rect_t{}, STR_CRITICALALARM, 0, COLOR_THEME_PRIMARY1); + new NumberEdit(line, rect_t{}, 0, 100, GET_SET_DEFAULT(g_model.rfAlarms.critical)); + + line = form->newLine(&grid); + line->padLeft(10); + new StaticText(line, rect_t{}, STR_DISABLE_ALARM, 0, COLOR_THEME_PRIMARY1); + new CheckBox(line, rect_t{}, GET_SET_DEFAULT(g_model.disableTelemetryWarning)); // Vario - new Subtitle(window, grid.getLineSlot(), STR_VARIO, 0, COLOR_THEME_PRIMARY1); - grid.nextLine(); - new StaticText(window, grid.getLabelSlot(true), STR_SOURCE, 0, COLOR_THEME_PRIMARY1); - auto choice = new SourceChoice(window, grid.getFieldSlot(), MIXSRC_NONE, MIXSRC_LAST_TELEM, + line = form->newLine(&grid); + new Subtitle(line, rect_t{}, STR_VARIO, 0, COLOR_THEME_PRIMARY1); + + FlexGridLayout grid5(col_dsc5, row_dsc, 4); + + line = form->newLine(&grid5); + line->padLeft(10); + new StaticText(line, rect_t{}, STR_SOURCE, 0, COLOR_THEME_PRIMARY1); + auto choice = new SourceChoice(line, rect_t{}, MIXSRC_NONE, MIXSRC_LAST_TELEM, GET_DEFAULT(g_model.varioData.source ? MIXSRC_FIRST_TELEM + 3 * (g_model.varioData.source - 1) : MIXSRC_NONE), SET_VALUE(g_model.varioData.source, newValue == MIXSRC_NONE ? 0 : (newValue - MIXSRC_FIRST_TELEM) / 3 + 1)); choice->setAvailableHandler([=](int16_t value) { @@ -609,36 +731,150 @@ void ModelTelemetryPage::build(FormWindow * window, int8_t focusSensorIndex) auto qr = div(value - MIXSRC_FIRST_TELEM, 3); return qr.rem == 0 && isSensorAvailable(qr.quot + 1); }); - grid.nextLine(); - new StaticText(window, grid.getLabelSlot(true), STR_RANGE, 0, COLOR_THEME_PRIMARY1); - auto vMin = new NumberEdit(window, grid.getFieldSlot(2, 0), -17, 17, - GET_SET_WITH_OFFSET(g_model.varioData.min, -10)); - vMin->setAvailableHandler( - [](int val) { return val < g_model.varioData.max + 10; }); + line = form->newLine(&grid5); + line->padLeft(10); + new StaticText(line, rect_t{}, STR_RANGE, 0, COLOR_THEME_PRIMARY1); - auto vMax = new NumberEdit(window, grid.getFieldSlot(2, 1), -17, 17, - GET_SET_WITH_OFFSET(g_model.varioData.max, 10)); - vMax->setAvailableHandler( - [](int val) { return g_model.varioData.min - 10 < val; }); + auto vMin = new NumberEdit(line, rect_t{}, -17, 17, GET_SET_WITH_OFFSET(g_model.varioData.min, -10)); + vMin->setAvailableHandler([](int val) { return val < g_model.varioData.max + 10; }); - grid.nextLine(); - new StaticText(window, grid.getLabelSlot(true), STR_CENTER, 0, COLOR_THEME_PRIMARY1); + auto vMax = new NumberEdit(line, rect_t{}, -17, 17, GET_SET_WITH_OFFSET(g_model.varioData.max, 10)); + vMax->setAvailableHandler([](int val) { return g_model.varioData.min - 10 < val; }); - auto cMin = new NumberEdit( - window, grid.getFieldSlot(3, 0), -15, 15, - GET_SET_WITH_OFFSET(g_model.varioData.centerMin, -5), 0, PREC1); - cMin->setAvailableHandler( - [](int val) { return val < g_model.varioData.centerMax + 5; }); + line = form->newLine(&grid5); + line->padLeft(10); + new StaticText(line, rect_t{}, STR_CENTER, 0, COLOR_THEME_PRIMARY1); - auto cMax = new NumberEdit( - window, grid.getFieldSlot(3, 1), -15, 15, - GET_SET_WITH_OFFSET(g_model.varioData.centerMax, 5), 0, PREC1); - cMax->setAvailableHandler( - [](int val) { return g_model.varioData.centerMin - 5 < val; }); + auto cMin = new NumberEdit(line, rect_t{}, -15, 15, GET_SET_WITH_OFFSET(g_model.varioData.centerMin, -5), 0, PREC1); + cMin->setAvailableHandler([](int val) { return val < g_model.varioData.centerMax + 5; }); - new Choice(window, grid.getFieldSlot(3, 2), STR_VVARIOCENTER, 0, 1, - GET_SET_DEFAULT(g_model.varioData.centerSilent)); - grid.nextLine(); + auto cMax = new NumberEdit(line, rect_t{}, -15, 15, GET_SET_WITH_OFFSET(g_model.varioData.centerMax, 5), 0, PREC1); + cMax->setAvailableHandler([](int val) { return g_model.varioData.centerMin - 5 < val; }); + new Choice(line, rect_t{}, STR_VVARIOCENTER, 0, 1, GET_SET_DEFAULT(g_model.varioData.centerSilent)); +} + + +// Version of 'drawSensorCustomValue' and related functions that returns strings instead of drawing to screen +// TODO: should this be moved somewhere else? + +std::string getDate(TelemetryItem & telemetryItem) +{ + return + formatNumberAsString(telemetryItem.datetime.day, LEADING0|LEFT, 2) + "-" + + formatNumberAsString(telemetryItem.datetime.month, LEFT, 2) + "-" + + formatNumberAsString(telemetryItem.datetime.year-2000, LEFT) + " " + + formatNumberAsString(telemetryItem.datetime.hour, LEADING0|LEFT, 2) + ":" + + formatNumberAsString(telemetryItem.datetime.min, LEADING0|LEFT, 2) + ":" + + formatNumberAsString(telemetryItem.datetime.sec, LEADING0|LEFT, 2); +} + +std::string getGPSCoord(int32_t value, const char * direction, bool seconds=true) +{ + char s[32] = {}; + uint32_t absvalue = abs(value); + char * tmp = strAppendUnsigned(s, absvalue / 1000000); + tmp = strAppend(tmp, "°"); + absvalue = absvalue % 1000000; + absvalue *= 60; + if (g_eeGeneral.gpsFormat == 0 || !seconds) { + tmp = strAppendUnsigned(tmp, absvalue / 1000000, 2); + *tmp++ = '\''; + if (seconds) { + absvalue %= 1000000; + absvalue *= 60; + absvalue /= 100000; + tmp = strAppendUnsigned(tmp, absvalue / 10); + *tmp++ = '.'; + tmp = strAppendUnsigned(tmp, absvalue % 10); + *tmp++ = '"'; + } + } + else { + tmp = strAppendUnsigned(tmp, absvalue / 1000000, 2); + *tmp++ = '.'; + absvalue /= 1000; + tmp = strAppendUnsigned(tmp, absvalue, 3); + } + *tmp++ = direction[value>=0 ? 0 : 1]; + *tmp = '\0'; + return std::string(s); +} + +std::string getGPSSensorValue(TelemetryItem & telemetryItem, LcdFlags flags) +{ + if (flags & RIGHT) + return getGPSCoord(telemetryItem.gps.longitude, "EW", true) + " " + getGPSCoord(telemetryItem.gps.latitude, "NS", true); + + return getGPSCoord(telemetryItem.gps.latitude, "NS", true) + " " + getGPSCoord(telemetryItem.gps.longitude, "EW", true); +} + +std::string getSensorCustomValue(uint8_t sensor, int32_t value, LcdFlags flags) +{ + TelemetryItem & telemetryItem = telemetryItems[sensor]; + TelemetrySensor & telemetrySensor = g_model.telemetrySensors[sensor]; + + if (telemetrySensor.unit == UNIT_DATETIME) { + return getDate(telemetryItem); + } + else if (telemetrySensor.unit == UNIT_GPS) { + return getGPSSensorValue(telemetryItem, flags); + } + else if (telemetrySensor.unit == UNIT_BITFIELD) { + if (IS_FRSKY_SPORT_PROTOCOL()) { + if (telemetrySensor.id >= RBOX_STATE_FIRST_ID && telemetrySensor.id <= RBOX_STATE_LAST_ID) { + if (telemetrySensor.subId == 0) { + if (value == 0) { + return std::string("OK"); + } + else { + for (uint8_t i=0; i<16; i++) { + if (value & (1 << i)) { + char s[] = "CH__ KO"; + strAppendUnsigned(&s[2], i+1, 2); + return std::string(s); + } + } + } + } + else { + if (value == 0) { + return std::string("Rx OK"); + } + else { + static const char * const RXS_STATUS[] = { + "Rx1 Ovl", + "Rx2 Ovl", + "SBUS Ovl", + "Rx1 FS", + "Rx1 LF", + "Rx2 FS", + "Rx2 LF", + "Rx1 Lost", + "Rx2 Lost", + "Rx1 NS", + "Rx2 NS", + }; + for (uint8_t i=0; i 0) { + flags |= (telemetrySensor.prec==1 ? PREC1 : PREC2); + } + return getValueWithUnit(value, telemetrySensor.unit == UNIT_CELLS ? UNIT_VOLTS : telemetrySensor.unit, flags); + } + + return std::string(""); } diff --git a/radio/src/gui/colorlcd/model_telemetry.h b/radio/src/gui/colorlcd/model_telemetry.h index d5f239a76..8938f9b93 100644 --- a/radio/src/gui/colorlcd/model_telemetry.h +++ b/radio/src/gui/colorlcd/model_telemetry.h @@ -36,9 +36,10 @@ class ModelTelemetryPage: public PageTab { void checkEvents() override; protected: - FormWindow * window = nullptr; - void editSensor(FormWindow * window, uint8_t index); int lastKnownIndex = 0; + FormWindow * window = nullptr; + + void editSensor(FormWindow * window, uint8_t index); void build(FormWindow * window, int8_t focusSensorIndex=-1); void rebuild(FormWindow * window, int8_t focusSensorIndex=-1); };