From e59a5f17592d44b031bc8e293cb8b75ceff60ad5 Mon Sep 17 00:00:00 2001 From: Roman Terekhov Date: Mon, 12 Oct 2026 09:03:02 +0300 Subject: [PATCH] =?UTF-8?q?ui-new:=20=D1=87=D0=B0=D1=81=D1=82=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=20=D0=B2=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9?= =?UTF-8?q?=D0=BA=D0=B0=D1=85=20=D1=80=D0=B0=D0=B4=D0=B8=D0=BE=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B2=D0=B8=D1=82=D1=81=D1=8F=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D1=80=D1=8F=D0=B4=D0=B0=D0=BC=20(=D0=9C?= =?UTF-8?q?=D0=93=D1=86=20=D1=88=D0=B0=D0=B3=D0=BE=D0=BC=201,=20=D0=BA?= =?UTF-8?q?=D0=93=D1=86=20=D0=BF=D0=BE=20=D1=81=D0=B8=D0=BC=D0=B2=D0=BE?= =?UTF-8?q?=D0=BB=D0=B0=D0=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 +++-- examples/companion_radio/ui-new/UITask.cpp | 68 ++++++++++++++++++++-- 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2b5927f7..a74a96ad 100644 --- a/README.md +++ b/README.md @@ -60,11 +60,14 @@ git merge upstream/main # правки для upstream отправляю ### Интерфейс (companion_radio / ui-new) -- **Настройка радио прямо с устройства** — страница RADIO, длинное нажатие: частота - (шаг 25 кГц), ширина полосы, SF, CR и мощность передатчика. Значения применяются к радио - сразу, в память пишутся по пункту `Save & exit`; через 5 минут простоя меню закрывается - без сохранения и экран гаснет, а пока меню открыто — входящие сообщения не всплывают - поверх него. +- **Настройка радио прямо с устройства** — страница RADIO, длинное нажатие: частота, + ширина полосы, SF, CR и мощность передатчика. Частота правится **по разрядам** (как в + PosadMesh): целые МГц меняются шагом 1 МГц, а килогерцы — по символам (сотни, десятки, + единицы). Короткое и двойное нажатие меняют активный разряд на ±1, длинное — переводит + на следующий разряд, а после единиц кГц возвращает к списку; активный разряд показан + инверсией. Значения применяются к радио сразу, в память пишутся по пункту `Save & exit`; + через 5 минут простоя меню закрывается без сохранения и экран гаснет, а пока меню открыто — + входящие сообщения не всплывают поверх него. - **Меню уведомлений** (длинное нажатие на первом экране): `Popups`, `Wake screen` (запретить автовключение дисплея по входящему сообщению; по кнопке экран работает), `Alerts` (звук+вибрация / только звук / только вибрация / выключено), `Channels` diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index bdedd60c..6a224962 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -41,7 +41,9 @@ #ifndef UI_RADIO_FREQ_MAX #define UI_RADIO_FREQ_MAX 960.0f #endif -#define UI_RADIO_FREQ_STEP_KHZ 25 // шаг перестройки частоты +// Частота правится по разрядам, как в PosadMesh: целые МГц меняются шагом 1 МГц, +// а килогерцы — по символам (сотни, десятки, единицы). Разряд 0 — МГц, 1..3 — кГц. +#define UI_RADIO_FREQ_KHZ_DIGITS 3 #define UI_RADIO_MENU_TIMEOUT_MILLIS 300000UL // 5 минут простоя — выйти из меню без сохранения #define NOTIFY_MAIN_ROWS 7 // строк в меню уведомлений @@ -163,6 +165,7 @@ class HomeScreen : public UIScreen { bool _radio_edit; bool _radio_val_edit; uint8_t _radio_sel; + uint8_t _radio_fq_pos; // активный разряд частоты: 0 — МГц, 1..3 — сотни/десятки/единицы кГц unsigned long _radio_last_activity; // для автовыхода из меню по простою void radioEditEnter() { @@ -170,6 +173,7 @@ class HomeScreen : public UIScreen { _radio_edit = true; _radio_val_edit = false; _radio_sel = ROW_FQ; + _radio_fq_pos = 0; _radio_last_activity = millis(); } void radioEditLeave(bool save) { @@ -339,7 +343,7 @@ class HomeScreen : public UIScreen { public: HomeScreen(UITask* task, mesh::RTCClock* rtc, SensorManager* sensors, NodePrefs* node_prefs) : _task(task), _rtc(rtc), _sensors(sensors), _node_prefs(node_prefs), _page(0), - _shutdown_init(false), _radio_edit(false), _radio_val_edit(false), _radio_sel(0), + _shutdown_init(false), _radio_edit(false), _radio_val_edit(false), _radio_sel(0), _radio_fq_pos(0), _radio_last_activity(0), _theme_edit(false), _theme_sel(0), _theme_last_activity(0), _notify_screen(NOTIFY_OFF), _notify_sel(0), _notify_val_edit(false), _notify_last_activity(0), sensors_lpp(200) { } @@ -655,10 +659,16 @@ public: } if (c == KEY_ENTER) { if (_radio_val_edit) { - _radio_val_edit = false; // назад к списку + // у частоты долгое нажатие перебирает разряды, на последнем — возврат к списку + if (_radio_sel == ROW_FQ && _radio_fq_pos < UI_RADIO_FREQ_KHZ_DIGITS) { + _radio_fq_pos++; + } else { + _radio_val_edit = false; // назад к списку + } } else if (_radio_sel == ROW_SAVE) { radioEditLeave(true); // сохранить в память и выйти } else { + _radio_fq_pos = 0; _radio_val_edit = true; // править значение } return true; @@ -773,7 +783,16 @@ public: void HomeScreen::radioValueStep(int8_t dir) { switch (_radio_sel) { case ROW_FQ: { - int32_t khz = (int32_t)(_node_prefs->freq * 1000.0f + 0.5f) + dir * UI_RADIO_FREQ_STEP_KHZ; + // разрядное редактирование: целые МГц (±1 МГц), килогерцы — по символам + int32_t khz = (int32_t)(_node_prefs->freq * 1000.0f + 0.5f); + if (_radio_fq_pos == 0) { + khz += dir * 1000; + } else { + int step = 1; + for (uint8_t i = _radio_fq_pos; i < UI_RADIO_FREQ_KHZ_DIGITS; i++) step *= 10; // 100, 10, 1 + int digit = (khz / step) % 10; + khz += ((digit + dir + 10) % 10 - digit) * step; // по кругу: 9 + 1 -> 0, 0 - 1 -> 9 + } int32_t lo = (int32_t)(UI_RADIO_FREQ_MIN * 1000.0f), hi = (int32_t)(UI_RADIO_FREQ_MAX * 1000.0f); if (khz < lo) khz = lo; if (khz > hi) khz = hi; @@ -846,8 +865,47 @@ void HomeScreen::renderRadioEditor(DisplayDriver& display) { char head[32]; if (_radio_val_edit) { // фаза правки значения + if (_radio_sel == ROW_FQ) { + // частота правится по разрядам: активный разряд инвертируем, чтобы было видно, + // что именно меняется (целые МГц или один из разрядов кГц) + int32_t khz = (int32_t)(_node_prefs->freq * 1000.0f + 0.5f); + int mhz = khz / 1000, rem = khz % 1000; + snprintf(buf, sizeof(buf), "%d.%03d", mhz, rem); + + snprintf(head, sizeof(head), "FQ %.0f-%.0f MHz", UI_RADIO_FREQ_MIN, UI_RADIO_FREQ_MAX); + display.setColor(UIColor::secondary_txt); + display.setTextSize(1); + display.drawTextLeftAlign(0, 2, head); + + display.setColor(UIColor::warning_txt); + display.setTextSize(2); + int w = display.getTextWidth(buf); + int x = (display.width() - w) / 2; + const int y = 20; + display.drawTextLeftAlign(x, y, buf); + + char mhz_txt[8]; + snprintf(mhz_txt, sizeof(mhz_txt), "%d", mhz); + int sel_len = _radio_fq_pos == 0 ? (int)strlen(mhz_txt) : 1; + int sel_start = _radio_fq_pos == 0 ? 0 : (int)strlen(mhz_txt) + _radio_fq_pos; // +1 — точка + char sel[10]; + if (sel_len > (int)sizeof(sel) - 1) sel_len = (int)sizeof(sel) - 1; + memcpy(sel, buf + sel_start, sel_len); + sel[sel_len] = 0; + + const int char_w = 6 * 2; // шрифт моноширинный: 6 px на символ при размере 2 + display.setColor(UIColor::primary_txt); + display.fillRect(x + sel_start * char_w - 1, y - 2, sel_len * char_w + 2, 20); + display.setColor(UIColor::window_bkg); + display.drawTextLeftAlign(x + sel_start * char_w, y, sel); + + display.setColor(UIColor::secondary_txt); + display.setTextSize(1); + display.drawTextCentered(display.width() / 2, 64 - 10, "clk+ dbl- hold=next"); + return; + } + switch (_radio_sel) { - case ROW_FQ: snprintf(head, sizeof(head), "FQ %.0f-%.0f MHz", UI_RADIO_FREQ_MIN, UI_RADIO_FREQ_MAX); break; case ROW_BW: strcpy(head, "BW 7.8-500 kHz"); break; case ROW_SF: strcpy(head, "SF 5-12"); break; case ROW_CR: strcpy(head, "CR 5-8"); break;