feat(ui): экран батареи и калибровка ADC в мастере настройки
- 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 (без дисплея — код исключается препроцессором)
This commit is contained in:
1 parent
8fa0bfe1d3
commit
1e751a85d4
6 files changed
+168
-7
No files matched your search
@@ -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 <частота>,<полоса>,<sf>,<cr>`, поэтому остальные значения сохраняются;
|
||||
- остальные пункты: мощность 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, координаты, подтверждение, ответ устройства); та же документация опубликована в вики проекта — <https://git.meshinfo.ru/shade/MeshCore-Posadmesh/wiki/Device-Setup>;
|
||||
- документация с иллюстрациями: страница [Настройка с дисплея и кнопки](./posadmesh-docs/device-setup.md) дополнена схемой управления и изображениями всех экранов (состояние, меню, частота и правка разряда, полоса, CR, SF, координаты, подтверждение, ответ устройства, батарея, ADC); та же документация опубликована в вики проекта — <https://git.meshinfo.ru/shade/MeshCore-Posadmesh/wiki/Device-Setup>;
|
||||
- сборка проверена на `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 <Ns>` убрана вместе с ручным опросом из `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` читает значение по правильному смещению;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
@@ -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 <частота>,<полоса>,<sf>,<cr>`, поэтому остальные три параметра сохраняют свои значения. Устройство отвечает `OK - reboot to apply`: изменения радио вступают в силу после перезагрузки.
|
||||
|
||||
## Батарея и калибровка ADC
|
||||
|
||||
Пункт `Battery` показывает напряжение в вольтах, процент заряда и полоску, а также текущий множитель ADC. Значения обновляются раз в секунду.
|
||||
|
||||
| Экран батареи | Правка множителя ADC |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
- **короткое** нажатие — показать «сырое» значение АЦП в милливольтах (удобно сверять с мультиметром);
|
||||
- **длинное** или **двойное** — вернуться в меню.
|
||||
|
||||
Процент берётся у платы, если она умеет его считать (например T-Beam с PMU). Если не умеет — считается линейно по напряжению между `getBatteryMinMilliVolts()` и `getBatteryMaxMilliVolts()` платы; на большинстве плат это 3000–4200 мВ.
|
||||
|
||||
Пункт `ADC` правит множитель ADC поразрядно, формат `x.xx` — так же, как полоса: длинное нажатие входит в правку разряда, короткое `+1`, двойное `−1`, длинное применяет разряд, короткое после применения переходит к следующему разряду, а на пункте `Save & exit` длинное нажатие сохраняет значение и возвращает в меню. Под цифрами видно измеренное напряжение, поэтому калибровать удобно прямо на экране: подстроили множитель — сразу видите новое напряжение.
|
||||
|
||||
`0.00` означает «использовать заводское значение платы»: команда `set adc.multiplier 0` возвращает расчёт по умолчанию. Значение применяется командой `set adc.multiplier <x.xx>` — той же, что и в последовательном CLI.
|
||||
|
||||
## Ограничения
|
||||
|
||||
- На экранах без кириллицы подписи меню выводятся латиницей.
|
||||
|
||||
Reference in new issue
Block a user