mirror of
https://github.com/ratspeak/ratdeck.git
synced 2026-08-28 05:24:32 +00:00
Performance and stability pass (v1.5.9)
- Tighten transport announce rate limit from 5/sec to 2/sec - Reduce RNS loop frequency from 200Hz to 100Hz - Throttle LVGL rendering to 30 FPS (was unthrottled) - Time-box TCP frame loop to 3 frames / 8ms per client - Add 12ms global TCP budget across all clients - Replace blocking WiFi announce delay(1500) with deferred non-blocking check - Rotate persistData() across 3 cycles to spread file I/O - Reduce LvNodesScreen rebuild frequency (2s → 5s, skip small transient changes) - Bump version to 1.5.9
This commit is contained in:
+3
-3
@@ -6,8 +6,8 @@
|
||||
|
||||
#define RATDECK_VERSION_MAJOR 1
|
||||
#define RATDECK_VERSION_MINOR 5
|
||||
#define RATDECK_VERSION_PATCH 8
|
||||
#define RATDECK_VERSION_STRING "1.5.8"
|
||||
#define RATDECK_VERSION_PATCH 9
|
||||
#define RATDECK_VERSION_STRING "1.5.9"
|
||||
|
||||
// --- Feature Flags ---
|
||||
#define HAS_DISPLAY true
|
||||
@@ -47,7 +47,7 @@
|
||||
#define TCP_CONNECT_TIMEOUT_MS 5000
|
||||
|
||||
// --- Announce Flood Defense ---
|
||||
#define RATDECK_MAX_ANNOUNCES_PER_SEC 5 // Transport-level rate limit (before Ed25519 verify)
|
||||
#define RATDECK_MAX_ANNOUNCES_PER_SEC 2 // Transport-level rate limit (before Ed25519 verify)
|
||||
|
||||
// --- Limits ---
|
||||
#define RATDECK_MAX_NODES 200 // PSRAM allows more
|
||||
|
||||
+42
-23
@@ -120,6 +120,11 @@ unsigned long lastHeartbeat = 0;
|
||||
constexpr unsigned long HEARTBEAT_INTERVAL_MS = 5000;
|
||||
unsigned long loopCycleStart = 0;
|
||||
unsigned long maxLoopTime = 0;
|
||||
unsigned long lastLvglTime = 0;
|
||||
constexpr unsigned long LVGL_INTERVAL_MS = 33; // ~30 FPS
|
||||
constexpr unsigned long TCP_GLOBAL_BUDGET_MS = 12; // Max cumulative TCP time per loop
|
||||
bool wifiDeferredAnnounce = false;
|
||||
unsigned long wifiConnectedAt = 0;
|
||||
|
||||
// =============================================================================
|
||||
// Announce with display name (MessagePack-encoded app_data)
|
||||
@@ -888,16 +893,20 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. LVGL timer handler — run FIRST after input for responsive UI
|
||||
if (powerMgr.isScreenOn()) {
|
||||
lv_timer_handler();
|
||||
// 3. LVGL timer handler — throttled to ~30 FPS
|
||||
{
|
||||
unsigned long now = millis();
|
||||
if (powerMgr.isScreenOn() && now - lastLvglTime >= LVGL_INTERVAL_MS) {
|
||||
lastLvglTime = now;
|
||||
lv_timer_handler();
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Reticulum loop (radio RX via LoRaInterface) — throttle to ~200Hz
|
||||
// 4. Reticulum loop (radio RX via LoRaInterface) — throttle to ~100Hz
|
||||
{
|
||||
static unsigned long lastRNS = 0;
|
||||
unsigned long now = millis();
|
||||
if (now - lastRNS >= 5) {
|
||||
if (now - lastRNS >= 10) {
|
||||
lastRNS = now;
|
||||
rns.loop();
|
||||
}
|
||||
@@ -932,20 +941,9 @@ void loop() {
|
||||
|
||||
// Recreate TCP clients on every WiFi connect (old clients may have stale sockets)
|
||||
reloadTCPClients();
|
||||
// Announce over TCP now that it's available — delay to let VPS register the connection
|
||||
bool anyTcpConnected = false;
|
||||
for (auto* tcp : tcpClients) {
|
||||
if (tcp->isConnected()) { anyTcpConnected = true; break; }
|
||||
}
|
||||
if (anyTcpConnected) {
|
||||
delay(1500); // Let VPS Reticulum register the new interface
|
||||
Serial.println("[TCP] Sending announce over new TCP connection...");
|
||||
RNS::Bytes appData = encodeAnnounceName(userConfig.settings().displayName);
|
||||
rns.announce(appData);
|
||||
lastAutoAnnounce = millis();
|
||||
} else {
|
||||
Serial.println("[TCP] No TCP clients connected, skipping announce");
|
||||
}
|
||||
// Defer announce to let VPS register the connection (non-blocking)
|
||||
wifiDeferredAnnounce = true;
|
||||
wifiConnectedAt = millis();
|
||||
} else if (!connected && wifiSTAConnected) {
|
||||
wifiSTAConnected = false;
|
||||
ui.statusBar().setWiFiActive(false);
|
||||
@@ -962,11 +960,32 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// 8. WiFi + TCP loops
|
||||
// 7.5. Deferred WiFi announce (non-blocking replacement for delay(1500))
|
||||
if (wifiDeferredAnnounce && millis() - wifiConnectedAt >= 1500) {
|
||||
wifiDeferredAnnounce = false;
|
||||
bool anyTcpConnected = false;
|
||||
for (auto* tcp : tcpClients) {
|
||||
if (tcp->isConnected()) { anyTcpConnected = true; break; }
|
||||
}
|
||||
if (anyTcpConnected) {
|
||||
Serial.println("[TCP] Sending announce over new TCP connection...");
|
||||
RNS::Bytes appData = encodeAnnounceName(userConfig.settings().displayName);
|
||||
rns.announce(appData);
|
||||
lastAutoAnnounce = millis();
|
||||
} else {
|
||||
Serial.println("[TCP] No TCP clients connected, skipping announce");
|
||||
}
|
||||
}
|
||||
|
||||
// 8. WiFi + TCP loops (with global budget)
|
||||
if (wifiImpl) wifiImpl->loop();
|
||||
for (auto* tcp : tcpClients) {
|
||||
tcp->loop();
|
||||
yield();
|
||||
{
|
||||
unsigned long tcpBudgetStart = millis();
|
||||
for (auto* tcp : tcpClients) {
|
||||
if (millis() - tcpBudgetStart >= TCP_GLOBAL_BUDGET_MS) break;
|
||||
tcp->loop();
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
// 9. BLE loops
|
||||
|
||||
@@ -232,28 +232,38 @@ void ReticulumManager::loop() {
|
||||
}
|
||||
|
||||
void ReticulumManager::persistData() {
|
||||
RNS::Transport::persist_data();
|
||||
RNS::Identity::persist_data();
|
||||
// Backup routing tables and known destinations to SD
|
||||
if (_sd && _sd->isReady()) {
|
||||
static const char* files[] = {"/destination_table", "/packet_hashlist", "/known_destinations"};
|
||||
for (const char* name : files) {
|
||||
File f = LittleFS.open(name, "r");
|
||||
if (f && f.size() > 0) {
|
||||
size_t sz = f.size();
|
||||
uint8_t* buf = (uint8_t*)malloc(sz);
|
||||
if (buf) {
|
||||
f.readBytes((char*)buf, sz);
|
||||
char sdPath[64];
|
||||
snprintf(sdPath, sizeof(sdPath), "/ratputer/transport%s", name);
|
||||
_sd->ensureDir("/ratputer/transport");
|
||||
_sd->writeSimple(sdPath, buf, sz);
|
||||
free(buf);
|
||||
// Rotate through persist steps to spread file I/O across cycles
|
||||
switch (_persistCycle) {
|
||||
case 0:
|
||||
RNS::Transport::persist_data();
|
||||
break;
|
||||
case 1:
|
||||
RNS::Identity::persist_data();
|
||||
break;
|
||||
case 2:
|
||||
// Backup routing tables and known destinations to SD
|
||||
if (_sd && _sd->isReady()) {
|
||||
static const char* files[] = {"/destination_table", "/packet_hashlist", "/known_destinations"};
|
||||
for (const char* name : files) {
|
||||
File f = LittleFS.open(name, "r");
|
||||
if (f && f.size() > 0) {
|
||||
size_t sz = f.size();
|
||||
uint8_t* buf = (uint8_t*)malloc(sz);
|
||||
if (buf) {
|
||||
f.readBytes((char*)buf, sz);
|
||||
char sdPath[64];
|
||||
snprintf(sdPath, sizeof(sdPath), "/ratputer/transport%s", name);
|
||||
_sd->ensureDir("/ratputer/transport");
|
||||
_sd->writeSimple(sdPath, buf, sz);
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
if (f) f.close();
|
||||
}
|
||||
}
|
||||
if (f) f.close();
|
||||
}
|
||||
break;
|
||||
}
|
||||
_persistCycle = (_persistCycle + 1) % 3;
|
||||
}
|
||||
|
||||
String ReticulumManager::identityHash() const {
|
||||
|
||||
@@ -73,4 +73,5 @@ private:
|
||||
bool _transportActive = false;
|
||||
unsigned long _lastPersist = 0;
|
||||
unsigned long _lastAnnounceTime = 0;
|
||||
uint8_t _persistCycle = 0; // Rotating: 0=Transport, 1=Identity, 2=SD backup
|
||||
};
|
||||
|
||||
@@ -71,9 +71,9 @@ void TCPClientInterface::loop() {
|
||||
return; // Will reconnect on next loop iteration
|
||||
}
|
||||
|
||||
// Drain multiple incoming frames per loop (up to 10, time-boxed)
|
||||
// Drain multiple incoming frames per loop (up to 3, time-boxed)
|
||||
unsigned long tcpStart = millis();
|
||||
for (int i = 0; i < 10 && _client.available() && (millis() - tcpStart < TCP_LOOP_BUDGET_MS); i++) {
|
||||
for (int i = 0; i < 3 && _client.available() && (millis() - tcpStart < TCP_LOOP_BUDGET_MS); i++) {
|
||||
unsigned long rxStart = millis();
|
||||
int len = readFrame();
|
||||
if (len > 0) {
|
||||
|
||||
@@ -45,7 +45,7 @@ private:
|
||||
static constexpr uint8_t FRAME_ESC = 0x7D;
|
||||
static constexpr uint8_t FRAME_XOR = 0x20;
|
||||
static constexpr unsigned long TCP_KEEPALIVE_TIMEOUT_MS = 300000; // 5 min
|
||||
static constexpr unsigned long TCP_LOOP_BUDGET_MS = 15;
|
||||
static constexpr unsigned long TCP_LOOP_BUDGET_MS = 8;
|
||||
|
||||
public:
|
||||
unsigned long lastRxTime() const { return _lastRxTime; }
|
||||
|
||||
@@ -50,9 +50,15 @@ void LvNodesScreen::refreshUI() {
|
||||
if (now - _lastRebuild < REBUILD_INTERVAL_MS) return;
|
||||
int contacts = 0;
|
||||
for (const auto& n : _am->nodes()) { if (n.saved) contacts++; }
|
||||
if (_am->nodeCount() != _lastNodeCount || contacts != _lastContactCount) {
|
||||
int countDelta = abs(_am->nodeCount() - _lastNodeCount);
|
||||
int contactDelta = abs(contacts - _lastContactCount);
|
||||
if (countDelta > 0 || contactDelta > 0) {
|
||||
_lastRebuild = now;
|
||||
rebuildList();
|
||||
// Skip rebuild for tiny transient count changes (≤3 nodes, no contact changes)
|
||||
// They'll be picked up on the next interval
|
||||
if (countDelta > 3 || contactDelta > 0) {
|
||||
rebuildList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ private:
|
||||
std::vector<int> _rowToNodeIdx; // Maps row index -> node index in _am->nodes(), -1 for headers
|
||||
|
||||
unsigned long _lastRebuild = 0;
|
||||
static constexpr unsigned long REBUILD_INTERVAL_MS = 2000;
|
||||
static constexpr unsigned long REBUILD_INTERVAL_MS = 5000;
|
||||
|
||||
lv_obj_t* _list = nullptr;
|
||||
lv_obj_t* _lblEmpty = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user