From 1e751a85d46a8541a9e47e48f4d9a827cd5f5aff Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Oct 2026 10:32:27 +0300 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD=20?= =?UTF-8?q?=D0=B1=D0=B0=D1=82=D0=B0=D1=80=D0=B5=D0=B8=20=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B1=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20ADC=20?= =?UTF-8?q?=D0=B2=20=D0=BC=D0=B0=D1=81=D1=82=D0=B5=D1=80=D0=B5=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UITask: новые пункты меню Battery и ADC Battery — напряжение, процент заряда, полоска и текущий множитель ADC; короткое нажатие показывает «сырое» значение АЦП в милливольтах, длинное/двойное — выход в меню ADC — поразрядная правка множителя в формате x.xx (как полоса), под цифрами показывается измеренное напряжение, поэтому калибровать можно прямо на экране - процент заряда берётся у платы (getBatteryPercent), а если плата его не считает — линейно по getBatteryMinMilliVolts()/getBatteryMaxMilliVolts() - множитель применяется командой set adc.multiplier, значение 0.00 возвращает заводской расчёт платы; проверка и сохранение остаются в CommonCLI - поразрядный редактор SCREEN_DIGITS теперь обслуживает и полосу, и ADC - документация: posadmesh-docs/device-setup.md (таблица меню, раздел «Батарея и калибровка ADC», два новых макета экранов), README — журнал изменений Проверено сборкой: heltec_v4_repeater_mqtt, Meshadventurer_sx1262_repeater_mqtt, Generic_E22_sx1262_repeater_mqtt (без дисплея — код исключается препроцессором) --- README.md | 14 +- examples/simple_repeater/UITask.cpp | 135 ++++++++++++++++++- examples/simple_repeater/UITask.h | 7 +- posadmesh-docs/_assets/wizard/13-battery.png | Bin 0 -> 4954 bytes posadmesh-docs/_assets/wizard/14-adc.png | Bin 0 -> 4825 bytes posadmesh-docs/device-setup.md | 19 +++ 6 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 posadmesh-docs/_assets/wizard/13-battery.png create mode 100644 posadmesh-docs/_assets/wizard/14-adc.png diff --git a/README.md b/README.md index a2c284dc..efb2c5ec 100644 --- a/README.md +++ b/README.md @@ -379,10 +379,22 @@ MQTT-репитер использует: - ввод координат в формате `xx.xxxx yy.yyyy`: длинное нажатие — правка текущей цифры, короткое — `+1`, двойное — `−1`, длинное — применить, короткое после применения — следующая цифра; после всех 12 цифр — экран сохранения; - радиопараметры настраиваются по отдельности: `Frequency` (значение `868.731 MHz`: целые МГц шагом ±1, кГц по разрядам), `Bandwidth` (`062.50 kHz`, поразрядно), `CR` и `SF` (шагом); внизу экранов частоты и полосы — пункт `Save & exit`, после сохранения возврат в меню; любой из четырёх пунктов отправляет `set radio <частота>,<полоса>,,`, поэтому остальные значения сохраняются; - остальные пункты: мощность TX, пересылка пакетов, размер хэша пути, интервалы advert и flood advert, лимит flood; внизу меню — `Save & exit`; +- добавлены экран батареи и калибровка ADC: пункт `Battery` показывает напряжение, процент заряда (от платы, иначе линейная оценка по её границам) и полоску, короткое нажатие выводит «сырое» значение АЦП в милливольтах; пункт `ADC` правит множитель ADC поразрядно в формате `x.xx` (как полоса) и показывает измеренное напряжение прямо под цифрами, поэтому калибровать можно на экране; значение применяется командой `set adc.multiplier`, а `0.00` возвращает заводской расчёт платы; - значения применяются через тот же `CommonCLI`, что и последовательный порт, а ответ устройства (`OK` или текст ошибки) выводится на экран; -- документация с иллюстрациями: страница [Настройка с дисплея и кнопки](./posadmesh-docs/device-setup.md) дополнена схемой управления и изображениями всех экранов (состояние, меню, частота и правка разряда, полоса, CR, SF, координаты, подтверждение, ответ устройства); та же документация опубликована в вики проекта — ; +- документация с иллюстрациями: страница [Настройка с дисплея и кнопки](./posadmesh-docs/device-setup.md) дополнена схемой управления и изображениями всех экранов (состояние, меню, частота и правка разряда, полоса, CR, SF, координаты, подтверждение, ответ устройства, батарея, ADC); та же документация опубликована в вики проекта — ; - сборка проверена на `heltec_v4_repeater_mqtt`, `Meshadventurer_sx1262_repeater_mqtt`, `Generic_E22_sx1262_repeater_mqtt` (без дисплея) и `RAK_3112_repeater_mqtt`. +**Обновление до upstream MeshCore v1.18.0** + +- база проекта поднята с `v1.17.1` до `v1.18.0`: влит релизный тег `repeater-v1.18.0` (266 коммитов upstream, 201 файл, +7185/−1010) через интеграционную ветку `integrate/upstream-1.18.0`; страховочный тег `pre-1.18.0` и `git bundle` лежат в истории репозитория; +- слияние дало 12 конфликтных файлов. Companion-часть (4 файла) взята из upstream целиком — в этом форке она не собирается. В `simple_repeater` сохранены наши возможности, а интеграция вентилятора T-Beam 1W заменена на upstream-реализацию: у него задача FreeRTOS, чтение NTC, гистерезис, TX-кулдаун, свои настройки в NVS и CLI (`get fan`, `set fan auto|on|off`, `set fan.lo/hi`); наша команда `set fan timeout ` убрана вместе с ручным опросом из `loop()`; +- время на ESP32 восстанавливается в два слоя: сначала из NVS (наша надстройка, переживает отключение питания), затем из RTC-памяти upstream, иначе опорная дата; deep sleep по-прежнему не трогаем; +- в менеджере датчиков взята логика upstream (карта `detected[]`, один драйвер на тип), поверх возвращено определение адреса платой; попутно восстановлен потерянный слиянием helper `scanI2CBus` с нашим таймаутом 20 мс; +- наша сигнатура `format*StatsReply(char*, size_t)` и наши префы (`FanPrefs`, `WebSensorSnapshot`, `BlacklistEntry`, `ChanNameFilter`, `getCLI()`) сохранены; из upstream добавлены `isHiddenNode()`, `DynamicConfigSerializer custom`, `attachDynamicPrefs`; +- добавлена плата **Waveshare ESP32-S3 LR1121** из 1.18.0 — три MQTT-окружения (`Waveshare_S3_LR1121_repeater_mqtt`, `_espnow`, `_bridge`); остальные новые платы 1.18.0 (ThinkNode M8 и M4, Wio Tracker L1 1W, Muzi Base) выполнены на nRF52, WiFi у них нет, поэтому MQTT-сборки для них невозможны; +- MQTT-целей стало 112 (42 uplink, 34 espnow, 36 bridge); после слияния проверены сборкой `heltec_v4_repeater_mqtt`, `LilyGo_TBeam_1W_repeater_mqtt`, `Meshadventurer_sx1262_repeater_mqtt`, `RAK_3112_repeater_mqtt`, `heltec_v4_repeater_mqtt_espnow`, `heltec_v4_repeater_mqtt_bridge`, `LilyGo_Tlora_C6_repeater_mqtt_espnow`, `LilyGo_TBeam_1W_repeater_mqtt_espnow`, `Waveshare_S3_LR1121_repeater_mqtt`, `Waveshare_S3_LR1121_repeater_mqtt_bridge`; +- в `AGENTS.md` обновлён процесс: работаем в `main` (ветка `develop` — устаревшая, от эпохи EastMesh), upstream тянем релизным тегом, а не веткой `dev`, версия upstream — `v1.18.0`. + **Исправлено** - `set mqtt.meshcoretel on|off` не срабатывал: обработчик сравнивался со строкой `set mqtt.mqtt.meshcoretel `. Алиас `set mqtt.meshcoretel.ru on|off` читает значение по правильному смещению; diff --git a/examples/simple_repeater/UITask.cpp b/examples/simple_repeater/UITask.cpp index c3f2b363..b35fab1f 100644 --- a/examples/simple_repeater/UITask.cpp +++ b/examples/simple_repeater/UITask.cpp @@ -31,6 +31,8 @@ enum { MENU_CR, MENU_SF, MENU_TX, + MENU_BATTERY, + MENU_ADC, MENU_REPEAT, MENU_PATHHASH, MENU_ADVERT, @@ -47,6 +49,8 @@ static const char* const MENU_NAMES[MENU_COUNT] = { "CR", "SF", "TX power", + "Battery", + "ADC", "Repeat", "Path hash", "Advert int", @@ -224,6 +228,26 @@ void UITask::beginBwEdit() { _screen = SCREEN_DIGITS; } +void UITask::beginAdcEdit() { + // Множитель ADC: x.xx, где 0.00 означает «использовать заводское значение платы» + _digit_int = 1; + _digit_count = 3; + for (int i = 0; i < _digit_count; i++) _digit_applied[i] = false; + _digit_index = 0; + _digit_editing = false; + + float v = _node_prefs ? _node_prefs->adc_multiplier : 0.0f; + if (v < 0) v = 0; + if (v > 9.99f) v = 9.99f; + int ip = (int)v; + int fp = (int)((v - ip) * 100.0f + 0.5f); + if (fp > 99) { fp = 0; ip++; } + char buf[8]; + snprintf(buf, sizeof(buf), "%d%02d", ip % 10, fp); + memcpy(_digit_digits, buf, _digit_count); + _screen = SCREEN_DIGITS; +} + double UITask::digitValue() const { int ip = 0, fp = 0; for (int i = 0; i < _digit_int; i++) ip = ip * 10 + (_digit_digits[i] - '0'); @@ -233,6 +257,35 @@ double UITask::digitValue() const { return ip + fp / scale; } +// Применение поразрядного редактора: полоса (set radio) или множитель ADC +void UITask::applyDigits() { + if (_menu_index == MENU_ADC) { + char cmd[48]; + snprintf(cmd, sizeof(cmd), "set adc.multiplier %d.%02d", + _digit_digits[0] - '0', + (_digit_digits[1] - '0') * 10 + (_digit_digits[2] - '0')); + runCommand(cmd); + return; + } + _radio_bw = digitValue(); + applyRadio(); +} + +// Процент заряда: значение платы, а если его нет — линейная оценка по напряжению +int UITask::batteryPercent() const { + if (_board == nullptr) return -1; + int pct = _board->getBatteryPercent(); + if (pct >= 0) return pct > 100 ? 100 : pct; + + const uint16_t mv = _board->getBattMilliVolts(); + const uint16_t lo = _board->getBatteryMinMilliVolts(); + const uint16_t hi = _board->getBatteryMaxMilliVolts(); + if (hi <= lo) return -1; + if (mv <= lo) return 0; + if (mv >= hi) return 100; + return (int)(((uint32_t)(mv - lo) * 100U) / (uint32_t)(hi - lo)); +} + // --- меню ------------------------------------------------------------------- void UITask::openField(uint8_t index) { @@ -261,6 +314,12 @@ void UITask::openField(uint8_t index) { case MENU_TX: _value = _node_prefs ? _node_prefs->tx_power_dbm : 0; break; + case MENU_BATTERY: + _screen = SCREEN_BATTERY; + return; + case MENU_ADC: + beginAdcEdit(); + return; case MENU_REPEAT: _value = (_node_prefs && _node_prefs->disable_fwd) ? 0 : 1; break; @@ -451,8 +510,7 @@ void UITask::handleButton(int ev) { } } else if (ev == BUTTON_EVENT_LONG_PRESS) { if (_digit_index >= _digit_count) { // Save & exit - _radio_bw = digitValue(); - applyRadio(); + applyDigits(); _screen = SCREEN_MENU; } else { _digit_editing = true; @@ -465,6 +523,17 @@ void UITask::handleButton(int ev) { break; } + case SCREEN_BATTERY: { // заряд аккумулятора: только просмотр + if (ev == BUTTON_EVENT_CLICK) { + char msg[32]; + snprintf(msg, sizeof(msg), "raw %u mV", (unsigned)(_board ? _board->getBattMilliVolts() : 0)); + setToast(msg); + } else if (ev == BUTTON_EVENT_LONG_PRESS || ev == BUTTON_EVENT_DOUBLE_CLICK) { + _screen = SCREEN_MENU; + } + break; + } + case SCREEN_FREQ: { // частота: МГц шагом 1, кГц по разрядам, затем Save & exit if (_freq_editing) { if (_freq_index == 0) { @@ -690,9 +759,10 @@ void UITask::renderValue() { renderToast(); } -// поразрядный ввод значения с точкой (полоса): "062.50" + пункт Save & exit +// поразрядный ввод значения с точкой: полоса ("062.50") или множитель ADC ("0.00") void UITask::renderDigits() { - renderTitleBar("Bandwidth"); + const bool adc = (_menu_index == MENU_ADC); + renderTitleBar(adc ? "ADC" : "Bandwidth"); _display->setTextSize(1); int y = 18; int cx = 2; @@ -718,7 +788,17 @@ void UITask::renderDigits() { } _display->setColor(UIColor::primary_txt); _display->setCursor(cx + CHAR_W, y); - _display->print("kHz"); + _display->print(adc ? "x" : "kHz"); + + // На экране ADC показываем измеренное напряжение: по нему видно результат калибровки + if (adc) { + const unsigned mv = _board ? _board->getBattMilliVolts() : 0; + char volts[24]; + snprintf(volts, sizeof(volts), "%u.%02u V", mv / 1000U, (mv % 1000U) / 10U); + _display->setColor(UIColor::primary_txt); + _display->setCursor(2, 45); + _display->print(volts); + } renderRow(_display, 34, "Save & exit", _digit_index >= _digit_count); @@ -732,6 +812,50 @@ void UITask::renderDigits() { renderToast(); } +// заряд аккумулятора: напряжение, процент, полоска и текущий множитель ADC +void UITask::renderBattery() { + renderTitleBar("Battery"); + _display->setTextSize(1); + _display->setColor(UIColor::primary_txt); + + const unsigned mv = _board ? _board->getBattMilliVolts() : 0; + char line[32]; + snprintf(line, sizeof(line), "%u.%02u V", mv / 1000U, (mv % 1000U) / 10U); + _display->setCursor(2, 15); + _display->print(line); + + const int pct = batteryPercent(); + if (pct >= 0) { + snprintf(line, sizeof(line), "%d %%", pct); + int x = _display->width() - (int)strlen(line) * CHAR_W - 2; + if (x < 2) x = 2; + _display->setCursor(x, 15); + _display->print(line); + } + + // полоска заряда + const int bar_w = _display->width() - 4; + _display->setColor(UIColor::primary_txt); + _display->drawRect(2, 27, bar_w, 9); + if (pct > 0) { + int fill = (bar_w - 4) * (pct > 100 ? 100 : pct) / 100; + if (fill > 0) _display->fillRect(4, 29, fill, 5); + } + + const float adc = _node_prefs ? _node_prefs->adc_multiplier : 0.0f; + if (adc > 0.0f) { + snprintf(line, sizeof(line), "ADC %.3f", (double)adc); + } else { + snprintf(line, sizeof(line), "ADC default"); + } + _display->setColor(UIColor::primary_txt); + _display->setCursor(2, 40); + _display->print(line); + + renderHint("hold=menu clk=raw"); + renderToast(); +} + // частота: целые МГц выделяются целиком (шаг 1), кГц — по разрядам, затем Save & exit void UITask::renderFreq() { renderTitleBar("Frequency"); @@ -921,6 +1045,7 @@ void UITask::renderCurrScreen() { case SCREEN_VALUE: renderValue(); break; case SCREEN_DIGITS: renderDigits(); break; case SCREEN_FREQ: renderFreq(); break; + case SCREEN_BATTERY: renderBattery(); break; case SCREEN_COORDS: renderCoords(); break; case SCREEN_COORDS_SAVE: renderCoordsSave(); break; default: renderStatus(); break; diff --git a/examples/simple_repeater/UITask.h b/examples/simple_repeater/UITask.h index bdf3c6a1..c8f35da1 100644 --- a/examples/simple_repeater/UITask.h +++ b/examples/simple_repeater/UITask.h @@ -16,8 +16,9 @@ public: SCREEN_STATUS = 0, SCREEN_MENU, SCREEN_VALUE, // числа и переключатели: SF, CR, TX, repeat, path hash, advert, flood - SCREEN_DIGITS, // поразрядный ввод значения с точкой (полоса) + SCREEN_DIGITS, // поразрядный ввод значения с точкой (полоса, ADC) SCREEN_FREQ, // частота: МГц шагом 1, кГц по разрядам + SCREEN_BATTERY, // заряд аккумулятора: напряжение, процент, множитель ADC SCREEN_COORDS, SCREEN_COORDS_SAVE }; @@ -74,9 +75,12 @@ private: void loadRadio(); void applyRadio(); void beginBwEdit(); + void beginAdcEdit(); void beginFreqEdit(); + void applyDigits(); double digitValue() const; double freqValue() const; + int batteryPercent() const; void coordFromPrefs(); void coordSave(); double coordAxisValue(uint8_t axis) const; // axis 0 = lat, 1 = lon @@ -91,6 +95,7 @@ private: void renderValue(); void renderDigits(); void renderFreq(); + void renderBattery(); void renderCoords(); void renderCoordsSave(); void renderTitleBar(const char* title); diff --git a/posadmesh-docs/_assets/wizard/13-battery.png b/posadmesh-docs/_assets/wizard/13-battery.png new file mode 100644 index 0000000000000000000000000000000000000000..674d4f7ec75988ef5e1fe1cb7bc0fad953c2f438 GIT binary patch literal 4954 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 TZ)|GPuo=JIZCl|FN6!5phVe(( literal 0 HcmV?d00001 diff --git a/posadmesh-docs/_assets/wizard/14-adc.png b/posadmesh-docs/_assets/wizard/14-adc.png new file mode 100644 index 0000000000000000000000000000000000000000..87edb44186b3bbb29270feddc8b28cd95d17bf30 GIT binary patch literal 4825 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 literal 0 HcmV?d00001 diff --git a/posadmesh-docs/device-setup.md b/posadmesh-docs/device-setup.md index cc25cfd7..2e8a685d 100644 --- a/posadmesh-docs/device-setup.md +++ b/posadmesh-docs/device-setup.md @@ -32,6 +32,8 @@ | `CR` | coding rate, 5–8 | `set radio` | | `SF` | spreading factor, 5–12 | `set radio` | | `TX power` | мощность передатчика, 1–30 дБм | `set tx` | +| `Battery` | напряжение, процент заряда и текущий множитель ADC — только просмотр | `get battery`, `get adc.multiplier` | +| `ADC` | множитель ADC для калибровки измерения батареи (0.00 — заводское значение платы) | `set adc.multiplier` | | `Repeat` | пересылка пакетов включена или выключена | `set repeat` | | `Path hash` | размер хэша пути: 0, 1 или 2 байта | `set path.hash.mode` | | `Advert int` | интервал advert в минутах (0 — выключено, иначе 60–240) | `set advert.interval` | @@ -109,6 +111,23 @@ Любой из четырёх пунктов отправляет команду целиком — `set radio <частота>,<полоса>,,`, поэтому остальные три параметра сохраняют свои значения. Устройство отвечает `OK - reboot to apply`: изменения радио вступают в силу после перезагрузки. +## Батарея и калибровка ADC + +Пункт `Battery` показывает напряжение в вольтах, процент заряда и полоску, а также текущий множитель ADC. Значения обновляются раз в секунду. + +| Экран батареи | Правка множителя ADC | +| --- | --- | +| ![Батарея](_assets/wizard/13-battery.png) | ![ADC](_assets/wizard/14-adc.png) | + +- **короткое** нажатие — показать «сырое» значение АЦП в милливольтах (удобно сверять с мультиметром); +- **длинное** или **двойное** — вернуться в меню. + +Процент берётся у платы, если она умеет его считать (например T-Beam с PMU). Если не умеет — считается линейно по напряжению между `getBatteryMinMilliVolts()` и `getBatteryMaxMilliVolts()` платы; на большинстве плат это 3000–4200 мВ. + +Пункт `ADC` правит множитель ADC поразрядно, формат `x.xx` — так же, как полоса: длинное нажатие входит в правку разряда, короткое `+1`, двойное `−1`, длинное применяет разряд, короткое после применения переходит к следующему разряду, а на пункте `Save & exit` длинное нажатие сохраняет значение и возвращает в меню. Под цифрами видно измеренное напряжение, поэтому калибровать удобно прямо на экране: подстроили множитель — сразу видите новое напряжение. + +`0.00` означает «использовать заводское значение платы»: команда `set adc.multiplier 0` возвращает расчёт по умолчанию. Значение применяется командой `set adc.multiplier ` — той же, что и в последовательном CLI. + ## Ограничения - На экранах без кириллицы подписи меню выводятся латиницей.