Files
MeshCore/examples/companion_radio/NodePrefs.h
T
shade 03c54bdcc4 Merge upstream 1.18.0 (81dbae59): перенос изменений оригинала с сохранением доработок форка
Сохранено: страница POWER (батарея/ADC/reboot), настройка радио с разрядами частоты,
темы и кириллица на дисплеях, меню уведомлений (тихие часы, режимы каналов),
стартовый экран MESHINFO, свои варианты плат.

Адаптировано под новый API 1.18.0: Listener (onMessageRecv/onChannelMessageRecv/
onQueueSizeChanged) вместо msgRead/newMsg, фильтрация уведомлений переехала в UITask,
в ChannelDetails добавлен индекс канала.

Отклонения от upstream: убран пин toolchain-gccarmnoneeabi@^1.140201.0 (такой версии
нет в реестре PlatformIO, сборка nRF52 висела) и не добавлен NRF52_POWER_MANAGEMENT
(upstream отключил его как ломающий не-Li-Ion батареи).
2026-10-15 20:00:58 +03:00

296 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <cstdint> // For uint8_t, uint32_t
#include <helpers/ConfigSerializer.h>
#include <helpers/CommonRadioPrefs.h>
#include <helpers/DynamicConfigSerializer.h>
#define TELEM_MODE_DENY 0
#define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags
#define TELEM_MODE_ALLOW_ALL 2
#define ADVERT_LOC_NONE 0
#define ADVERT_LOC_SHARE 1
// Режимы уведомлений по каналам (по 2 бита на канал)
#define CH_MODE_POPUP 0 // звук + экран переключается на сообщение
#define CH_MODE_SILENT 1 // в непрочитанные, но без звука и без всплытия
#define CH_MODE_OFF 2 // на устройстве не уведомляем вообще
// Что показывать рядом с иконкой батареи в статус-баре
#define BATT_DISPLAY_VOLT 0 // напряжение, например 4.05V
#define BATT_DISPLAY_PERC 1 // процент заряда
#define BATT_DISPLAY_OFF 2 // индикатор батареи скрыт
// Сколько каналов покрываем настройками. Совпадает с MAX_GROUP_CHANNELS у companion-сборок,
// но этот заголовок его не включает, поэтому задаём свой предел.
#ifndef NOTIFY_MAX_CHANNELS
#define NOTIFY_MAX_CHANNELS 40
#endif
#define CH_MODE_BYTES ((NOTIFY_MAX_CHANNELS * 2 + 7) / 8)
class NodePrefs : public ConfigSerializer { // persisted to file
public:
float airtime_factor = 0;
char node_name[32];
double node_lat = 0, node_lon = 0;
float freq = 0;
uint8_t sf = 0;
uint8_t cr = 0;
uint8_t multi_acks = 0;
uint8_t manual_add_contacts = 0;
float bw = 0;
int8_t tx_power_dbm = 0;
uint8_t telemetry_mode_base = 0;
uint8_t telemetry_mode_loc = 0;
uint8_t telemetry_mode_env = 0;
float rx_delay_base = 0;
float tx_delay_factor = 0;
float direct_tx_delay_factor = 0;
uint32_t ble_pin = 0;
uint8_t advert_loc_policy = 0;
uint8_t buzzer_quiet = 0;
uint8_t vibe_quiet = 0;
uint8_t gps_enabled = 0; // GPS enabled flag (0=disabled, 1=enabled)
uint32_t gps_interval = 0; // GPS read interval in seconds
uint8_t autoadd_config = 0; // bitmask for auto-add contacts config
uint8_t rx_boosted_gain = 0; // SX126x RX boosted gain mode (0=power saving, 1=boosted)
uint8_t radio_fem_rxgain = 0; // external LoRa FEM RX gain (LNA)
uint8_t radio_fem_txgain = 0; // external LoRa FEM TX gain (low by default)
uint8_t _client_repeat = 0; // DEPRECATED -> use repeat.disable_fwd
uint8_t path_hash_mode = 0; // which path mode to use when sending
uint8_t autoadd_max_hops = 0; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64)
uint8_t cad_enabled = 0;
uint8_t interference_threshold = 0;
uint8_t agc_reset_interval = 0; // secs / 4
char default_scope_name[31];
uint8_t default_scope_key[16];
int8_t tz_offset = 0;
char ui_theme[12]; // имя темы дисплея; пусто = тема по умолчанию для платы
// Уведомления о входящих (меню на первом экране)
uint8_t msg_popup = 1; // переключать экран на входящее сообщение
uint8_t msg_wake_display = 1; // зажигать дисплей при входящем сообщении
uint8_t quiet_en = 0; // часы тишины включены
uint8_t quiet_from = 23; // час начала (локальный)
uint8_t quiet_to = 7; // час окончания (локальный)
uint8_t ch_mode[CH_MODE_BYTES]; // режимы по каналам (CH_MODE_*), по 2 бита
uint8_t batt_display = BATT_DISPLAY_PERC; // что показывать рядом с батареей (BATT_DISPLAY_*)
float adc_multiplier = 0; // коррекция АЦП батареи (0 = как считает плата)
#ifdef ENABLE_WIFI_INTERFACE
#ifndef WIFI_SSID
#define WIFI_SSID ""
#endif
char wifi_ssid[33] = {0}; // if empty, the compile-time WIFI_SSID is used
char wifi_pwd[64] = {0};
uint8_t wifi_enabled = 1; // enabled by default to allow wifi only builds to work. wifi won't be started if ssid is empty
// use ssid from prefs, or fallback to ssid from build flags
const char* getWifiSSID() const { return wifi_ssid[0] ? wifi_ssid : WIFI_SSID; }
#endif
private:
class RadioPrefs : public CommonRadioPrefs {
NodePrefs* _parent;
protected:
void structure() override {
def("freq", _parent->freq);
def("bw", _parent->bw);
def("sf", _parent->sf);
def("cr", _parent->cr);
def("cad", _parent->cad_enabled);
def("int_thr", _parent->interference_threshold);
def("rxgain", _parent->rx_boosted_gain);
def("fem_rxgain", _parent->radio_fem_rxgain); // fem_rxgain WAS mapped to wrong JSON property previously
def("fem_txgain", _parent->radio_fem_txgain);
def("tx", _parent->tx_power_dbm);
def("af", _parent->airtime_factor);
def("adc_mult", _parent->adc_multiplier); // коррекция АЦП батареи (как в CommonCLI)
def("rxdelay", _parent->rx_delay_base);
def("f_txdelay", _parent->tx_delay_factor);
def("d_txdelay", _parent->direct_tx_delay_factor);
def("agc_int", _parent->agc_reset_interval);
def("hash_mode", _parent->path_hash_mode);
def("multi_ack", _parent->multi_acks);
}
public:
RadioPrefs(NodePrefs* parent) : _parent(parent) { }
// CommonRadioPrefs interface
float getFreq() const override { return _parent->freq; }
void setFreq(float f) override { _parent->freq = f; markDirty(); }
float getBandwidth() const override { return _parent->bw; }
void setBandwidth(float bw) override { _parent->bw = bw; markDirty(); }
uint8_t getSpreadFactor() const override { return _parent->sf; }
void setSpreadFactor(uint8_t sf) override { _parent->sf = sf; markDirty(); }
uint8_t getCodingRate() const override { return _parent->cr; }
void setCodingRate(uint8_t cr) override { _parent->cr = cr; markDirty(); }
float getAirtimeFactor() const override { return _parent->airtime_factor; }
void setAirtimeFactor(float af) override { _parent->airtime_factor = af; markDirty(); }
bool isCadEnabled() const override { return _parent->cad_enabled; }
void setCadEnabled(bool en) override { _parent->cad_enabled = en; markDirty(); }
uint8_t getIntThresh() const override { return _parent->interference_threshold; }
void setIntThresh(uint8_t t) override { _parent->interference_threshold = t; markDirty(); }
uint8_t getRxGain() const override { return _parent->rx_boosted_gain; }
void setRxGain(uint8_t g) override { _parent->rx_boosted_gain = g; markDirty(); }
uint8_t getTxPower() const override { return _parent->tx_power_dbm; }
void setTxPower(uint8_t dbm) override { _parent->tx_power_dbm = dbm; markDirty(); }
float getRxDelay() const override { return _parent->rx_delay_base; }
void setRxDelay(float d) override { _parent->rx_delay_base = d; markDirty(); }
uint16_t getAgcResetInt() const override { return _parent->agc_reset_interval * 4; }
void setAgcResetInt(uint16_t secs) override { _parent->agc_reset_interval = secs / 4; markDirty(); }
uint8_t getHashMode() const override { return _parent->path_hash_mode; }
void setHashMode(uint8_t m) override { _parent->path_hash_mode = m; markDirty(); }
uint8_t getMultiAcks() const override { return _parent->multi_acks; }
void setMultiAcks(uint8_t m) override { _parent->multi_acks = m; markDirty(); }
float getFloodTxDelay() const override { return _parent->tx_delay_factor; }
void setFloodTxDelay(float d) override { _parent->tx_delay_factor = d; markDirty(); }
float getDirectTxDelay() const override { return _parent->direct_tx_delay_factor; }
void setDirectTxDelay(float d) override { _parent->direct_tx_delay_factor = d; markDirty(); }
uint8_t getFEMRxGain() const override { return _parent->radio_fem_rxgain; }
void setFEMRxGain(uint8_t g) override { _parent->radio_fem_rxgain = g; markDirty(); }
uint8_t getFEMTxGain() const override { return _parent->radio_fem_txgain; }
void setFEMTxGain(uint8_t g) override { _parent->radio_fem_txgain = g; markDirty(); }
};
RadioPrefs radio;
class GPSPrefs : public ConfigSerializer { // COPIED from CommonCLI (for now)
NodePrefs* _parent;
protected:
void structure() override {
def("en", _parent->gps_enabled); // boolean
def("int", _parent->gps_interval); // interval in seconds
def("adv_loc", _parent->advert_loc_policy);
}
public:
GPSPrefs(NodePrefs* parent) : _parent(parent) { }
};
GPSPrefs gps;
class RepeatPrefs : public ConfigSerializer { // COPIED from CommonCLI (for now)
public:
uint8_t disable_fwd = 1;
protected:
void structure() override {
def("disable", disable_fwd);
//def("f_max", flood_max);
//def("f_max_uns", flood_max_unscoped);
//def("f_max_adv", flood_max_advert);
//def("loop", loop_detect);
}
};
RepeatPrefs repeat;
class CompanionPrefs : public ConfigSerializer {
NodePrefs* _parent;
protected:
void structure() override {
def("auto_max", _parent->autoadd_max_hops); // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64)
def("defs_nm", _parent->default_scope_name, sizeof(_parent->default_scope_name));
def("defs_key", (void *) _parent->default_scope_key, sizeof(_parent->default_scope_key));
def("pin", _parent->ble_pin);
def("buzz_q", _parent->buzzer_quiet);
def("vibe_q", _parent->vibe_quiet);
def("auto_add", _parent->autoadd_config); // bitmask for auto-add contacts config
def("man_add", _parent->manual_add_contacts);
def("theme", _parent->ui_theme, sizeof(_parent->ui_theme)); // имя темы дисплея
// уведомления
def("msg_popup", _parent->msg_popup);
def("msg_wake", _parent->msg_wake_display);
def("quiet_en", _parent->quiet_en);
def("quiet_from", _parent->quiet_from);
def("quiet_to", _parent->quiet_to);
def("ch_mode", (void *)_parent->ch_mode, sizeof(_parent->ch_mode)); // hex-блоб
def("batt_disp", _parent->batt_display); // что показывать у батареи
def("tel_base", _parent->telemetry_mode_base);
def("tel_loc", _parent->telemetry_mode_loc);
def("tel_env", _parent->telemetry_mode_env);
def("tz_offset", _parent->tz_offset);
}
public:
CompanionPrefs(NodePrefs* parent) : _parent(parent) { }
};
CompanionPrefs companion;
DynamicConfigSerializer custom;
#ifdef ENABLE_WIFI_INTERFACE
class WiFiPrefs : public ConfigSerializer {
NodePrefs* _parent;
protected:
void structure() override {
def("ssid", _parent->wifi_ssid, sizeof(_parent->wifi_ssid));
def("pwd", _parent->wifi_pwd, sizeof(_parent->wifi_pwd));
def("enabled", _parent->wifi_enabled);
}
public:
WiFiPrefs(NodePrefs* parent) : _parent(parent) { }
};
WiFiPrefs wifi;
#endif
protected:
void structure() override {
def("name", node_name, sizeof(node_name));
//def("adv_int", advert_interval);
//def("f_adv_int", flood_advert_interval);
def("lat", node_lat);
def("lon", node_lon);
def("radio", radio);
def("gps", gps);
def("repeat", repeat);
def("comp", companion);
def("custom", custom);
#ifdef ENABLE_WIFI_INTERFACE
def("wifi", wifi);
#endif
}
public:
NodePrefs() : radio(this), gps(this), companion(this), custom(&radio)
#ifdef ENABLE_WIFI_INTERFACE
, wifi(this)
#endif
{
node_name[0] = 0;
default_scope_name[0] = 0;
ui_theme[0] = 0;
memset(ch_mode, 0, sizeof(ch_mode)); // все каналы — CH_MODE_POPUP
memset(default_scope_key, 0, sizeof(default_scope_key));
}
// new accessor methods
bool isRepeatEn() const { return repeat.disable_fwd == 0; }
void setRepeatEn(bool en) { repeat.disable_fwd = en ? 0 : 1; }
// Настройки уведомлений по каналам
uint8_t getChannelMode(uint8_t idx) const {
if (idx >= NOTIFY_MAX_CHANNELS) return CH_MODE_POPUP;
uint8_t sh = (uint8_t)((idx & 3) * 2);
return (uint8_t)((ch_mode[idx >> 2] >> sh) & 0x03);
}
void setChannelMode(uint8_t idx, uint8_t mode) {
if (idx >= NOTIFY_MAX_CHANNELS) return;
uint8_t sh = (uint8_t)((idx & 3) * 2);
ch_mode[idx >> 2] = (uint8_t)((ch_mode[idx >> 2] & ~(0x03 << sh)) | ((mode & 0x03) << sh));
}
// Локальный час суток по unix-времени RTC и смещению часового пояса
uint8_t localHour(uint32_t utc) const {
int32_t h = (int32_t)((utc / 3600) % 24) + tz_offset;
h %= 24;
if (h < 0) h += 24;
return (uint8_t)h;
}
// Тихие часы: окно может пересекать полночь (например, 23..7)
bool isQuietNow(uint32_t utc) const {
if (!quiet_en) return false;
if (quiet_from == quiet_to) return false;
uint8_t h = localHour(utc);
if (quiet_from < quiet_to) return h >= quiet_from && h < quiet_to;
return h >= quiet_from || h < quiet_to;
}
CommonRadioPrefs* getRadioPrefs() { return &radio; }
KeyValueStore* getCustom() { return &custom; }
bool isDirty() const override { return ConfigSerializer::isDirty() || radio.isDirty() || custom.isDirty(); }
void clearDirty() override { ConfigSerializer::clearDirty(); radio.clearDirty(); custom.clearDirty(); }
};