Fix screen wake on idle, add regional frequency presets and Hz input

Screen idle:
- Remove powerMgr.activity() from radio yield callback — screen no
  longer wakes on LoRa RX, auto-announce, or RSSI polls
- Add [POWER] state transition logging for on-device diagnostics

Radio regions:
- Add 4-region system: Americas (915), Europe (868), Australia (915),
  Asia (923) with timezone-to-region mapping
- Region picker in Radio settings (always visible)
- Presets now apply region-appropriate frequency + modulation params
- Timezone change warns if it suggests a different radio region

Frequency input (developer mode):
- Hz-precision input (9 digits) displayed as MHz with trimmed decimals
- Left/right arrows step by 125 kHz, digit keys for direct entry
- Inspired by @strijar in https://github.com/ratspeak/ratdeck/pull/12
This commit is contained in:
DeFiDude committed 2026-03-28 14:06:31 -06:00
1 parent b2fcfe526e
commit a77fb594a2
7 files changed
+94 -28

No files matched your search

+17
View File
@@ -61,6 +61,23 @@
#define SCREEN_OFF_TIMEOUT_MS 60000
#define SCREEN_DIM_BRIGHTNESS 64
// --- Radio Regions ---
enum RadioRegion : uint8_t {
REGION_AMERICAS = 0, // 915 MHz (902-928 ISM)
REGION_EUROPE = 1, // 868 MHz (863-870)
REGION_AUSTRALIA = 2, // 915 MHz (915-928)
REGION_ASIA = 3, // 923 MHz (AS923)
REGION_COUNT = 4
};
static constexpr uint32_t REGION_FREQ[REGION_COUNT] = {
915000000, 868000000, 915000000, 923000000
};
static const char* const REGION_LABELS[REGION_COUNT] = {
"Americas (915)", "Europe (868)", "Australia (915)", "Asia (923)"
};
// --- Serial Debug ---
#define SERIAL_BAUD 115200
+2
View File
@@ -11,6 +11,7 @@ bool UserConfig::parseJson(const String& json) {
return false;
}
_settings.radioRegion = constrain((int)(doc["radio_region"] | 0), 0, REGION_COUNT - 1);
_settings.loraFrequency = doc["lora_freq"] | (long)LORA_DEFAULT_FREQ;
_settings.loraSF = doc["lora_sf"] | (int)LORA_DEFAULT_SF;
_settings.loraBW = doc["lora_bw"] | (long)LORA_DEFAULT_BW;
@@ -75,6 +76,7 @@ bool UserConfig::parseJson(const String& json) {
String UserConfig::serializeToJson() const {
JsonDocument doc;
doc["radio_region"] = _settings.radioRegion;
doc["lora_freq"] = _settings.loraFrequency;
doc["lora_sf"] = _settings.loraSF;
doc["lora_bw"] = _settings.loraBW;
+1
View File
@@ -18,6 +18,7 @@ struct TCPEndpoint {
struct UserSettings {
// Radio
uint8_t radioRegion = REGION_AMERICAS;
uint32_t loraFrequency = LORA_DEFAULT_FREQ;
uint8_t loraSF = LORA_DEFAULT_SF;
uint32_t loraBW = LORA_DEFAULT_BW;
+2
View File
@@ -91,6 +91,8 @@ void Power::loop() {
void Power::setState(State newState) {
if (newState == _state) return;
const char* names[] = {"ACTIVE", "DIMMED", "SCREEN_OFF"};
Serial.printf("[POWER] %s -> %s\n", names[_state], names[newState]);
State oldState = _state;
_state = newState;
+10 -2
View File
@@ -683,13 +683,12 @@ void setup() {
ui.statusBar().setTransportMode("RatDeck");
ui.lvStatusBar().setTransportMode("RatDeck");
// Keep UI alive during blocking radio TX (endPacket wait loop)
// Keep LVGL responsive during blocking radio operations (if screen is on)
// Re-entrancy guard prevents nested lv_timer_handler() calls
radio.setYieldCallback([]() {
static bool inYield = false;
if (inYield) return;
inYield = true;
powerMgr.activity(); // Keep screen alive during TX
if (powerMgr.isScreenOn()) {
lv_timer_handler();
}
@@ -847,6 +846,15 @@ void setup() {
gps.setPosixTZ(tz);
}
#endif
// Warn if timezone suggests a different radio region
uint8_t tzRegion = TIMEZONE_TABLE[tzIdx].radioRegion;
if (tzRegion != userConfig.settings().radioRegion) {
char msg[64];
snprintf(msg, sizeof(msg), "TZ suggests %s region", REGION_LABELS[tzRegion]);
ui.lvStatusBar().showToast(msg, 3000);
Serial.printf("[REGION] Timezone suggests %s, current is %s\n",
REGION_LABELS[tzRegion], REGION_LABELS[userConfig.settings().radioRegion]);
}
goHome();
});
+39 -5
View File
@@ -47,6 +47,7 @@ void LvSettingsScreen::applyPreset(int presetIdx) {
const auto& p = LV_PRESETS[presetIdx];
s.loraSF = p.sf; s.loraBW = p.bw; s.loraCR = p.cr;
s.loraTxPower = p.txPower; s.loraPreamble = p.preamble;
s.loraFrequency = REGION_FREQ[constrain(s.radioRegion, 0, REGION_COUNT - 1)];
}
bool LvSettingsScreen::isEditable(int idx) const {
@@ -230,6 +231,20 @@ void LvSettingsScreen::buildItems() {
// Radio
int radioStart = idx;
{
// Region picker — always visible
SettingItem regionItem;
regionItem.label = "Region";
regionItem.type = SettingType::ENUM_CHOICE;
regionItem.getter = [&s]() { return (int)s.radioRegion; };
regionItem.setter = [this, &s](int v) {
s.radioRegion = constrain(v, 0, REGION_COUNT - 1);
if (_ui) _ui->lvStatusBar().showToast("Select a preset to apply frequency", 2500);
};
regionItem.minVal = 0; regionItem.maxVal = REGION_COUNT - 1; regionItem.step = 1;
regionItem.enumLabels = {REGION_LABELS[0], REGION_LABELS[1], REGION_LABELS[2], REGION_LABELS[3]};
_items.push_back(regionItem);
idx++;
SettingItem presetItem;
presetItem.label = "Preset";
presetItem.type = SettingType::ENUM_CHOICE;
@@ -243,13 +258,25 @@ void LvSettingsScreen::buildItems() {
// Custom radio parameters — only visible in Developer Mode
if (s.devMode) {
_items.push_back({"Frequency", SettingType::INTEGER,
[&s]() { return (int)(s.loraFrequency / 1000); },
[&s](int v) { s.loraFrequency = (uint32_t)v * 1000; },
[&s]() { return (int)(s.loraFrequency); },
[&s](int v) { s.loraFrequency = (uint32_t)v; },
[](int v) -> String {
char buf[16]; snprintf(buf, sizeof(buf), "%d.%03d MHz", v / 1000, v % 1000);
char buf[20];
int mhz = v / 1000000;
int rem = v % 1000000;
if (rem == 0) {
snprintf(buf, sizeof(buf), "%d MHz", mhz);
} else {
char frac[8];
snprintf(frac, sizeof(frac), "%06d", rem);
int len = 6;
while (len > 0 && frac[len - 1] == '0') len--;
frac[len] = '\0';
snprintf(buf, sizeof(buf), "%d.%s MHz", mhz, frac);
}
return String(buf);
},
137000, 1020000, 125});
137000000, 1020000000, 125000});
idx++;
_items.push_back({"TX Power", SettingType::INTEGER,
[&s]() { return s.loraTxPower; }, [&s](int v) { s.loraTxPower = v; },
@@ -282,7 +309,14 @@ void LvSettingsScreen::buildItems() {
idx++;
}
_categories.push_back({"Radio", radioStart, idx - radioStart,
[this]() { int p = detectPreset(); return (p >= 0) ? String(LV_PRESETS[p].name) : String("Custom"); }});
[this]() {
int p = detectPreset();
auto& s = _cfg->settings();
String label = (p >= 0) ? String(LV_PRESETS[p].name) : String("Custom");
label += " ";
label += REGION_LABELS[constrain(s.radioRegion, 0, REGION_COUNT - 1)];
return label;
}});
// Network
int netStart = idx;
+23 -21
View File
@@ -1,37 +1,39 @@
#pragma once
#include "ui/UIManager.h"
#include "config/Config.h"
#include <functional>
struct TimezoneEntry {
const char* label; // Display name (e.g., "New York (EST/EDT)")
const char* posixTZ; // POSIX TZ string for setenv("TZ", ...)
int8_t baseOffset; // Base UTC offset for display (e.g., -5 for EST)
uint8_t radioRegion; // Mapped RadioRegion for ISM band defaults
};
// Major world timezones with DST rules where applicable
static const TimezoneEntry TIMEZONE_TABLE[] = {
{"Honolulu", "HST10", -10},
{"Anchorage", "AKST9AKDT,M3.2.0,M11.1.0", -9},
{"Los Angeles", "PST8PDT,M3.2.0,M11.1.0", -8},
{"Denver", "MST7MDT,M3.2.0,M11.1.0", -7},
{"Phoenix", "MST7", -7},
{"Chicago", "CST6CDT,M3.2.0,M11.1.0", -6},
{"New York", "EST5EDT,M3.2.0,M11.1.0", -5},
{"San Juan", "AST4", -4},
{"Sao Paulo", "<-03>3", -3},
{"London", "GMT0BST,M3.5.0/1,M10.5.0", 0},
{"Paris", "CET-1CEST,M3.5.0,M10.5.0/3", 1},
{"Cairo", "EET-2", 2},
{"Moscow", "MSK-3", 3},
{"Dubai", "<+04>-4", 4},
{"Karachi", "PKT-5", 5},
{"Kolkata", "IST-5:30", 5},
{"Bangkok", "<+07>-7", 7},
{"Singapore", "<+08>-8", 8},
{"Tokyo", "JST-9", 9},
{"Sydney", "AEST-10AEDT,M10.1.0,M4.1.0/3", 10},
{"Auckland", "NZST-12NZDT,M9.5.0,M4.1.0/3", 12},
{"Honolulu", "HST10", -10, REGION_AMERICAS},
{"Anchorage", "AKST9AKDT,M3.2.0,M11.1.0", -9, REGION_AMERICAS},
{"Los Angeles", "PST8PDT,M3.2.0,M11.1.0", -8, REGION_AMERICAS},
{"Denver", "MST7MDT,M3.2.0,M11.1.0", -7, REGION_AMERICAS},
{"Phoenix", "MST7", -7, REGION_AMERICAS},
{"Chicago", "CST6CDT,M3.2.0,M11.1.0", -6, REGION_AMERICAS},
{"New York", "EST5EDT,M3.2.0,M11.1.0", -5, REGION_AMERICAS},
{"San Juan", "AST4", -4, REGION_AMERICAS},
{"Sao Paulo", "<-03>3", -3, REGION_AMERICAS},
{"London", "GMT0BST,M3.5.0/1,M10.5.0", 0, REGION_EUROPE},
{"Paris", "CET-1CEST,M3.5.0,M10.5.0/3", 1, REGION_EUROPE},
{"Cairo", "EET-2", 2, REGION_EUROPE},
{"Moscow", "MSK-3", 3, REGION_EUROPE},
{"Dubai", "<+04>-4", 4, REGION_EUROPE},
{"Karachi", "PKT-5", 5, REGION_EUROPE},
{"Kolkata", "IST-5:30", 5, REGION_EUROPE},
{"Bangkok", "<+07>-7", 7, REGION_ASIA},
{"Singapore", "<+08>-8", 8, REGION_ASIA},
{"Tokyo", "JST-9", 9, REGION_ASIA},
{"Sydney", "AEST-10AEDT,M10.1.0,M4.1.0/3", 10, REGION_AUSTRALIA},
{"Auckland", "NZST-12NZDT,M9.5.0,M4.1.0/3", 12, REGION_AUSTRALIA},
};
static constexpr int TIMEZONE_COUNT = sizeof(TIMEZONE_TABLE) / sizeof(TIMEZONE_TABLE[0]);