настройка радио с экрана RADIO: freq/bw/sf/cr/tx, сохранение и автовыход из меню
Run Unit Tests / test (push) Canceled after 0s
Build and deploy Docs site to GitHub Pages / github-pages (push) Canceled after 0s
PR Build Check / build (Heltec_v3_companion_radio_ble) (push) Canceled after 0s
PR Build Check / build (Heltec_v3_repeater) (push) Canceled after 0s
PR Build Check / build (Heltec_v3_room_server) (push) Canceled after 0s
PR Build Check / build (LilyGo_Tlora_C6_repeater_) (push) Canceled after 0s
PR Build Check / build (PicoW_repeater) (push) Canceled after 0s
PR Build Check / build (RAK_4631_companion_radio_ble) (push) Canceled after 0s
PR Build Check / build (RAK_4631_companion_radio_ethernet) (push) Canceled after 0s
PR Build Check / build (RAK_4631_repeater) (push) Canceled after 0s
PR Build Check / build (RAK_4631_repeater_ethernet) (push) Canceled after 0s
PR Build Check / build (RAK_4631_room_server) (push) Canceled after 0s
PR Build Check / build (RAK_4631_room_server_ethernet) (push) Canceled after 0s
PR Build Check / build (Tbeam_SX1276_repeater) (push) Canceled after 0s
PR Build Check / build (wio-e5-mini_repeater) (push) Canceled after 0s
PR Build Check / build (wio_wm1110_repeater) (push) Canceled after 0s
Run Unit Tests / test (push) Canceled after 0s
Build and deploy Docs site to GitHub Pages / github-pages (push) Canceled after 0s
PR Build Check / build (Heltec_v3_companion_radio_ble) (push) Canceled after 0s
PR Build Check / build (Heltec_v3_repeater) (push) Canceled after 0s
PR Build Check / build (Heltec_v3_room_server) (push) Canceled after 0s
PR Build Check / build (LilyGo_Tlora_C6_repeater_) (push) Canceled after 0s
PR Build Check / build (PicoW_repeater) (push) Canceled after 0s
PR Build Check / build (RAK_4631_companion_radio_ble) (push) Canceled after 0s
PR Build Check / build (RAK_4631_companion_radio_ethernet) (push) Canceled after 0s
PR Build Check / build (RAK_4631_repeater) (push) Canceled after 0s
PR Build Check / build (RAK_4631_repeater_ethernet) (push) Canceled after 0s
PR Build Check / build (RAK_4631_room_server) (push) Canceled after 0s
PR Build Check / build (RAK_4631_room_server_ethernet) (push) Canceled after 0s
PR Build Check / build (Tbeam_SX1276_repeater) (push) Canceled after 0s
PR Build Check / build (wio-e5-mini_repeater) (push) Canceled after 0s
PR Build Check / build (wio_wm1110_repeater) (push) Canceled after 0s
This commit is contained in:
@@ -992,6 +992,32 @@ uint32_t MyMesh::getBLEPin() {
|
||||
return _active_ble_pin;
|
||||
}
|
||||
|
||||
bool MyMesh::setRadioParams(float freq, float bw, uint8_t sf, uint8_t cr) {
|
||||
if (freq < 150.0f || freq > 2500.0f) return false;
|
||||
if (bw < 7.0f || bw > 500.0f) return false;
|
||||
if (sf < 5 || sf > 12) return false;
|
||||
if (cr < 5 || cr > 8) return false;
|
||||
if (_prefs.isRepeatEn() && !isValidClientRepeatFreq((uint32_t)(freq * 1000.0f + 0.5f))) return false;
|
||||
|
||||
_prefs.freq = freq;
|
||||
_prefs.bw = bw;
|
||||
_prefs.sf = sf;
|
||||
_prefs.cr = cr;
|
||||
|
||||
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
||||
// Сеттеры RadioLib уводят чип в standby, а враппер при этом может считать, что
|
||||
// он всё ещё в RX (state не меняется) — перезапускаем приём с новыми параметрами.
|
||||
radio_driver.restartRecv();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MyMesh::setTxPowerDbm(int8_t dbm) {
|
||||
if (dbm < -9) dbm = -9;
|
||||
if (dbm > MAX_LORA_TX_POWER) dbm = MAX_LORA_TX_POWER;
|
||||
_prefs.tx_power_dbm = dbm;
|
||||
radio_driver.setTxPower(_prefs.tx_power_dbm);
|
||||
}
|
||||
|
||||
struct FreqRange {
|
||||
uint32_t lower_freq, upper_freq;
|
||||
};
|
||||
|
||||
@@ -171,6 +171,12 @@ public:
|
||||
_store->savePrefs(_prefs);
|
||||
}
|
||||
|
||||
// Ручная настройка радио (экран RADIO в ui-new). Проверяет допустимые
|
||||
// диапазоны, применяет параметры к радио и обновляет prefs в памяти;
|
||||
// запись в файл — только через savePrefs().
|
||||
bool setRadioParams(float freq, float bw, uint8_t sf, uint8_t cr);
|
||||
void setTxPowerDbm(int8_t dbm);
|
||||
|
||||
#if ENV_INCLUDE_GPS == 1
|
||||
void applyGpsPrefs() {
|
||||
sensors.setSettingValue("gps", _prefs.gps_enabled ? "1" : "0");
|
||||
|
||||
@@ -30,6 +30,25 @@
|
||||
#endif
|
||||
|
||||
#include "icons.h"
|
||||
#include <math.h>
|
||||
|
||||
// --- Ручная настройка радио (страница RADIO) ---
|
||||
// Границы частоты по умолчанию — для SX126x (150..960 МГц). Для других модулей
|
||||
// можно переопределить в env варианта.
|
||||
#ifndef UI_RADIO_FREQ_MIN
|
||||
#define UI_RADIO_FREQ_MIN 150.0f
|
||||
#endif
|
||||
#ifndef UI_RADIO_FREQ_MAX
|
||||
#define UI_RADIO_FREQ_MAX 960.0f
|
||||
#endif
|
||||
#define UI_RADIO_FREQ_STEP_KHZ 25 // шаг перестройки частоты
|
||||
#define UI_RADIO_MENU_TIMEOUT_MILLIS 300000UL // 5 минут простоя — выйти из меню без сохранения
|
||||
|
||||
// BW перебирается по списку допустимых значений, а не шагом
|
||||
static const float UI_RADIO_BW_STEPS[] = {
|
||||
7.8f, 10.4f, 15.6f, 20.8f, 31.25f, 41.7f, 62.5f, 125.0f, 250.0f, 500.0f
|
||||
};
|
||||
#define UI_RADIO_BW_STEPS_N (sizeof(UI_RADIO_BW_STEPS) / sizeof(UI_RADIO_BW_STEPS[0]))
|
||||
|
||||
class SplashScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
@@ -114,6 +133,35 @@ class HomeScreen : public UIScreen {
|
||||
bool _shutdown_init;
|
||||
AdvertPath recent[UI_RECENT_LIST_SIZE];
|
||||
|
||||
// режим ручной настройки радио (страница RADIO)
|
||||
enum RadioRow { ROW_FQ, ROW_BW, ROW_SF, ROW_CR, ROW_TX, ROW_SAVE, ROW_COUNT };
|
||||
bool _radio_edit;
|
||||
bool _radio_val_edit;
|
||||
uint8_t _radio_sel;
|
||||
unsigned long _radio_last_activity; // для автовыхода из меню по простою
|
||||
|
||||
void radioEditEnter() {
|
||||
_task->clearAlert(); // висящий попап (например, от входящего сообщения) не должен перекрывать меню
|
||||
_radio_edit = true;
|
||||
_radio_val_edit = false;
|
||||
_radio_sel = ROW_FQ;
|
||||
_radio_last_activity = millis();
|
||||
}
|
||||
void radioEditLeave(bool save) {
|
||||
_radio_edit = false;
|
||||
_radio_val_edit = false;
|
||||
if (save) {
|
||||
the_mesh.savePrefs();
|
||||
_task->showAlert("Saved", 1000);
|
||||
}
|
||||
_task->showPendingMessages(); // сообщения, пришедшие во время настройки
|
||||
}
|
||||
void radioRowStep(int8_t dir) { _radio_sel = (_radio_sel + ROW_COUNT + dir) % ROW_COUNT; }
|
||||
void radioValueStep(int8_t dir);
|
||||
void radioRowValue(uint8_t row, char* buf, size_t len);
|
||||
const char* radioRowName(uint8_t row);
|
||||
void renderRadioEditor(DisplayDriver& display);
|
||||
|
||||
|
||||
void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) {
|
||||
// Convert millivolts to percentage
|
||||
@@ -185,15 +233,33 @@ 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), sensors_lpp(200) { }
|
||||
_shutdown_init(false), _radio_edit(false), _radio_val_edit(false), _radio_sel(0),
|
||||
_radio_last_activity(0), sensors_lpp(200) { }
|
||||
|
||||
// в меню настройки радио входящие сообщения не всплывают поверх экрана
|
||||
bool suppressAlerts() override { return _radio_edit; }
|
||||
|
||||
void poll() override {
|
||||
if (_radio_edit) {
|
||||
if (millis() - _radio_last_activity >= UI_RADIO_MENU_TIMEOUT_MILLIS) {
|
||||
radioEditLeave(false); // 5 минут без действий: выходим без сохранения
|
||||
_task->blankDisplay(); // и гасим экран
|
||||
} else {
|
||||
_task->keepDisplayOn(); // пока работаем — экран не гасим по таймауту
|
||||
}
|
||||
}
|
||||
if (_shutdown_init && !_task->isButtonPressed()) { // must wait for USR button to be released
|
||||
_task->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
int render(DisplayDriver& display) override {
|
||||
if (_radio_edit) {
|
||||
// режим настройки радио: без статус-бара (имя ноды / заряд) и точек страниц —
|
||||
// всё место отдано параметрам
|
||||
renderRadioEditor(display);
|
||||
return 5000;
|
||||
}
|
||||
display.setColor(UIColor::title_bkg);
|
||||
display.fillRect(0, 0, display.width(), 12);
|
||||
char tmp[80];
|
||||
@@ -279,17 +345,19 @@ public:
|
||||
sprintf(tmp, "FQ: %06.3f SF: %d", _node_prefs->freq, _node_prefs->sf);
|
||||
display.print(tmp);
|
||||
|
||||
display.setCursor(0, 31);
|
||||
display.setCursor(0, 29);
|
||||
sprintf(tmp, "BW: %03.2f CR: %d", _node_prefs->bw, _node_prefs->cr);
|
||||
display.print(tmp);
|
||||
|
||||
// tx power, noise floor
|
||||
display.setCursor(0, 42);
|
||||
display.setCursor(0, 38);
|
||||
sprintf(tmp, "TX: %ddBm", _node_prefs->tx_power_dbm);
|
||||
display.print(tmp);
|
||||
display.setCursor(0, 53);
|
||||
display.setCursor(0, 47);
|
||||
sprintf(tmp, "Noise floor: %d", radio_driver.getNoiseFloor());
|
||||
display.print(tmp);
|
||||
display.setColor(UIColor::secondary_txt);
|
||||
display.drawTextCentered(display.width() / 2, 56, "setup: " PRESS_LABEL);
|
||||
} else if (_page == HomePage::BLUETOOTH) {
|
||||
display.setColor(UIColor::corp_blue);
|
||||
display.drawXbm((display.width() - 32) / 2, 18,
|
||||
@@ -441,6 +509,28 @@ public:
|
||||
}
|
||||
|
||||
bool handleInput(char c) override {
|
||||
if (_radio_edit) { // режим настройки радио: страницы не листаются
|
||||
_radio_last_activity = millis(); // любое нажатие продлевает жизнь меню
|
||||
if (c == KEY_NEXT || c == KEY_RIGHT) {
|
||||
if (_radio_val_edit) radioValueStep(+1); else radioRowStep(+1);
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_PREV || c == KEY_LEFT) {
|
||||
if (_radio_val_edit) radioValueStep(-1); else radioRowStep(-1);
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_ENTER) {
|
||||
if (_radio_val_edit) {
|
||||
_radio_val_edit = false; // назад к списку
|
||||
} else if (_radio_sel == ROW_SAVE) {
|
||||
radioEditLeave(true); // сохранить в память и выйти
|
||||
} else {
|
||||
_radio_val_edit = true; // править значение
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true; // остальные события в этом режиме игнорируем
|
||||
}
|
||||
if (c == KEY_LEFT || c == KEY_PREV) {
|
||||
_page = (_page + HomePage::Count - 1) % HomePage::Count;
|
||||
return true;
|
||||
@@ -452,6 +542,12 @@ public:
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_ENTER && _page == HomePage::RADIO) {
|
||||
radioEditEnter();
|
||||
// предупреждение: остальные узлы сети должны иметь те же параметры
|
||||
_task->showAlert("Nodes must match", 1500);
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_ENTER && _page == HomePage::BLUETOOTH) {
|
||||
if (_task->isBluetoothEnabled()) { // toggle Bluetooth on/off
|
||||
_task->disableBluetooth();
|
||||
@@ -490,6 +586,125 @@ 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;
|
||||
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;
|
||||
if (!the_mesh.setRadioParams((float)khz / 1000.0f, _node_prefs->bw, _node_prefs->sf, _node_prefs->cr)) {
|
||||
_task->showAlert("Freq not allowed", 1200);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ROW_BW: {
|
||||
// ищем ближайшее допустимое значение; если текущее не из списка —
|
||||
// первый шаг приводит к ближайшему, дальше идём по списку
|
||||
int idx = 0;
|
||||
for (int i = 1; i < (int)UI_RADIO_BW_STEPS_N; i++) {
|
||||
if (fabsf(UI_RADIO_BW_STEPS[i] - _node_prefs->bw) < fabsf(UI_RADIO_BW_STEPS[idx] - _node_prefs->bw)) idx = i;
|
||||
}
|
||||
int next = idx;
|
||||
if (fabsf(UI_RADIO_BW_STEPS[idx] - _node_prefs->bw) < 0.01f) {
|
||||
next = idx + dir;
|
||||
if (next < 0) next = 0;
|
||||
if (next >= (int)UI_RADIO_BW_STEPS_N) next = (int)UI_RADIO_BW_STEPS_N - 1;
|
||||
}
|
||||
the_mesh.setRadioParams(_node_prefs->freq, UI_RADIO_BW_STEPS[next], _node_prefs->sf, _node_prefs->cr);
|
||||
break;
|
||||
}
|
||||
case ROW_SF: {
|
||||
int v = (int)_node_prefs->sf + dir;
|
||||
if (v < 5) v = 5;
|
||||
if (v > 12) v = 12;
|
||||
the_mesh.setRadioParams(_node_prefs->freq, _node_prefs->bw, (uint8_t)v, _node_prefs->cr);
|
||||
break;
|
||||
}
|
||||
case ROW_CR: {
|
||||
int v = (int)_node_prefs->cr + dir;
|
||||
if (v < 5) v = 5;
|
||||
if (v > 8) v = 8;
|
||||
the_mesh.setRadioParams(_node_prefs->freq, _node_prefs->bw, _node_prefs->sf, (uint8_t)v);
|
||||
break;
|
||||
}
|
||||
case ROW_TX: {
|
||||
the_mesh.setTxPowerDbm((int8_t)((int)_node_prefs->tx_power_dbm + dir));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HomeScreen::radioRowValue(uint8_t row, char* buf, size_t len) {
|
||||
switch (row) {
|
||||
case ROW_FQ: snprintf(buf, len, "%.3f MHz", _node_prefs->freq); break;
|
||||
case ROW_BW: snprintf(buf, len, "%.2f kHz", _node_prefs->bw); break;
|
||||
case ROW_SF: snprintf(buf, len, "%u", (unsigned)_node_prefs->sf); break;
|
||||
case ROW_CR: snprintf(buf, len, "%u", (unsigned)_node_prefs->cr); break;
|
||||
case ROW_TX: snprintf(buf, len, "%d dBm", (int)_node_prefs->tx_power_dbm); break;
|
||||
default: buf[0] = 0; break;
|
||||
}
|
||||
}
|
||||
|
||||
const char* HomeScreen::radioRowName(uint8_t row) {
|
||||
switch (row) {
|
||||
case ROW_FQ: return "FQ";
|
||||
case ROW_BW: return "BW";
|
||||
case ROW_SF: return "SF";
|
||||
case ROW_CR: return "CR";
|
||||
case ROW_TX: return "TX";
|
||||
default: return "Save & exit";
|
||||
}
|
||||
}
|
||||
|
||||
void HomeScreen::renderRadioEditor(DisplayDriver& display) {
|
||||
char buf[24];
|
||||
char head[32];
|
||||
|
||||
if (_radio_val_edit) { // фаза правки значения
|
||||
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;
|
||||
default: snprintf(head, sizeof(head), "TX -9-%d dBm", MAX_LORA_TX_POWER); break;
|
||||
}
|
||||
display.setColor(UIColor::secondary_txt);
|
||||
display.setTextSize(1);
|
||||
display.drawTextLeftAlign(0, 2, head);
|
||||
|
||||
radioRowValue(_radio_sel, buf, sizeof(buf));
|
||||
display.setColor(UIColor::warning_txt);
|
||||
display.setTextSize(2);
|
||||
display.drawTextCentered(display.width() / 2, 20, buf);
|
||||
|
||||
display.setTextSize(1);
|
||||
display.drawTextCentered(display.width() / 2, 64 - 10, "click+ dbl- hold=back");
|
||||
return;
|
||||
}
|
||||
|
||||
// фаза выбора: выбранная строка инвертирована
|
||||
display.setTextSize(1);
|
||||
int y = 2;
|
||||
for (uint8_t i = 0; i < ROW_COUNT; i++, y += 10) {
|
||||
radioRowValue(i, buf, sizeof(buf));
|
||||
if (i == _radio_sel) {
|
||||
display.setColor(UIColor::primary_txt);
|
||||
display.fillRect(0, y - 1, display.width(), 10);
|
||||
display.setColor(UIColor::window_bkg);
|
||||
display.drawTextLeftAlign(1, y, radioRowName(i));
|
||||
if (buf[0]) display.drawTextRightAlign(display.width() - 2, y, buf);
|
||||
} else {
|
||||
display.setColor(UIColor::primary_txt);
|
||||
display.drawTextLeftAlign(1, y, radioRowName(i));
|
||||
if (buf[0]) {
|
||||
display.setColor(UIColor::secondary_txt);
|
||||
display.drawTextRightAlign(display.width() - 2, y, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MsgPreviewScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
mesh::RTCClock* _rtc;
|
||||
@@ -611,6 +826,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
|
||||
ui_started_at = millis();
|
||||
_alert_expiry = 0;
|
||||
_msg_pending = false;
|
||||
|
||||
splash = new SplashScreen(this);
|
||||
home = new HomeScreen(this, &rtc_clock, sensors, node_prefs);
|
||||
@@ -623,6 +839,29 @@ void UITask::showAlert(const char* text, int duration_millis) {
|
||||
_alert_expiry = millis() + duration_millis;
|
||||
}
|
||||
|
||||
void UITask::keepDisplayOn() {
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS; // экран не гаснет, пока идёт работа с меню
|
||||
}
|
||||
|
||||
void UITask::blankDisplay() {
|
||||
if (_display != NULL && _display->isOn()) {
|
||||
_display->turnOff();
|
||||
}
|
||||
}
|
||||
|
||||
void UITask::clearAlert() {
|
||||
_alert_expiry = 0;
|
||||
}
|
||||
|
||||
void UITask::showPendingMessages() {
|
||||
if (_msg_pending) {
|
||||
_msg_pending = false;
|
||||
if (_msgcount > 0) {
|
||||
setCurrScreen(msg_preview);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UITask::notify(UIEventType t) {
|
||||
#if defined(PIN_BUZZER)
|
||||
switch(t){
|
||||
@@ -664,6 +903,14 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i
|
||||
_msgcount = msgcount;
|
||||
|
||||
((MsgPreviewScreen *) msg_preview)->addPreview(path_len, from_name, text);
|
||||
|
||||
if (curr != NULL && curr->suppressAlerts()) {
|
||||
// Экран попросил не перекрывать его (меню настройки радио): сообщение уже
|
||||
// в очереди непрочитанных, покажем его после выхода из меню.
|
||||
_msg_pending = true;
|
||||
_next_refresh = 100;
|
||||
return;
|
||||
}
|
||||
setCurrScreen(msg_preview);
|
||||
|
||||
if (_display != NULL) {
|
||||
|
||||
@@ -35,6 +35,7 @@ class UITask : public AbstractUITask {
|
||||
NodePrefs* _node_prefs;
|
||||
char _alert[80];
|
||||
unsigned long _alert_expiry;
|
||||
bool _msg_pending; // сообщение пришло, пока экран просил не перекрывать его
|
||||
int _msgcount;
|
||||
unsigned long ui_started_at, next_batt_chck;
|
||||
int next_backlight_btn_check = 0;
|
||||
@@ -74,6 +75,10 @@ public:
|
||||
|
||||
void gotoHomeScreen() { setCurrScreen(home); }
|
||||
void showAlert(const char* text, int duration_millis);
|
||||
void keepDisplayOn(); // продлить таймаут автогашения (например, пока открыто меню)
|
||||
void blankDisplay(); // погасить экран немедленно
|
||||
void clearAlert(); // снять текущий попап
|
||||
void showPendingMessages(); // открыть предпросмотр, если сообщение пришло «в фоне»
|
||||
int getMsgCount() const { return _msgcount; }
|
||||
bool hasDisplay() const { return _display != NULL; }
|
||||
bool isButtonPressed() const;
|
||||
|
||||
@@ -51,6 +51,11 @@ public:
|
||||
uint32_t getRngSeed();
|
||||
void setTxPower(int8_t dbm);
|
||||
|
||||
// Перезапуск приёма после смены радио-параметров: сеттеры RadioLib уводят чип
|
||||
// в standby, а state обёртки остаётся STATE_RX, поэтому recvRaw() сам приём не
|
||||
// поднимет. Нужен после setParams()/setTxPower() при настройке на ходу.
|
||||
void restartRecv() { idle(); startRecv(); }
|
||||
|
||||
virtual float getCurrentRSSI() =0;
|
||||
virtual uint8_t getSpreadingFactor() const { return LORA_SF; }
|
||||
static uint16_t preambleLengthForSF(uint8_t sf) { return sf <= 8 ? 32 : 16; }
|
||||
|
||||
@@ -21,5 +21,10 @@ public:
|
||||
virtual int render(DisplayDriver& display) =0; // return value is number of millis until next render
|
||||
virtual bool handleInput(char c) { return false; }
|
||||
virtual void poll() { }
|
||||
|
||||
// Экран может попросить не перекрывать его всплывающими уведомлениями
|
||||
// (например, меню настройки радио): входящие сообщения копятся в очереди,
|
||||
// но экран не подменяется.
|
||||
virtual bool suppressAlerts() { return false; }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user