Public Access
Stop IRC by hand; remove mDNS
IrcService::disconnect() stops the session from any state: QUIT if connected, then no more retries. /quit goes through it (before, it only stopped a connected session; while waiting for Wi-Fi or retrying it did nothing), and so does the new `irc stop` command. Stopped by hand, opening the IRC App no longer reconnects; typing a line does. mDNS is gone: it never crossed the dev box's routed network, and it cost about 7.5 KB of RAM. Pushes go to the IP shown in Settings -> Firmware, which drops its Name row. OTA Q54 records the change. On the device, Debug Build: 105.6 KB free with Wi-Fi (was 98); with IRC on TLS 54 KB free (was 46); after `irc stop`, back to 99 KB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
+1
-1
@@ -61,7 +61,7 @@ A Wi-Fi network the device may join on its own (name, password). When several ar
|
||||
_Avoid_: profile, known network
|
||||
|
||||
**IRC Service**:
|
||||
The Service that keeps the IRC connection alive in the background once the IRC App has started it, until the user disconnects. It reconnects after drops, and pauses while the Wi-Fi Service is Monitoring. It does not start by itself after a reboot.
|
||||
The Service that keeps the IRC connection alive in the background once the IRC App has started it, until the user stops it (`/quit`, or `irc stop` on the console). Once stopped by hand, opening the App again doesn't reconnect; typing a line does. It reconnects after drops, and pauses while the Wi-Fi Service is Monitoring. It does not start by itself after a reboot.
|
||||
_Avoid_: IRC client (that's the App)
|
||||
|
||||
**Buffer**:
|
||||
|
||||
@@ -67,6 +67,7 @@ To install from the SD card instead, copy the `.ota` file from `.pio/build/cardp
|
||||
| `sd list` | Lists the files of each Storage Clean-up category |
|
||||
| `cat <path>` | Prints the first ~1.2 KB of a file on the SD card |
|
||||
| `irc start` | Starts the IRC Service (normally done by opening the IRC App) |
|
||||
| `irc stop` | Stops it, as `/quit` does: QUIT if connected, no more retries, and the App stays disconnected until you type |
|
||||
| `irc say <buffer> <text>` | Types into a Buffer, commands included (`irc say 0 /join #test`) |
|
||||
| `irc dump` | Prints IRC status, memory, and the last lines of each Buffer |
|
||||
| `wifi status` | Prints Wi-Fi state, network, signal, clock and free heap |
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|---|---|
|
||||
| Q52 | Two sources: **push over Wi-Fi** from the PC, and **from the SD card**. Pulling from Gitea releases is deferred. |
|
||||
| Q53 | **Signed Update Files** (ECDSA P-256 over SHA-256). The private key stays in `~/.config/roro9stack/`, and the firmware embeds the public key (ADR 0003). |
|
||||
| Q54 | The device **always listens** for pushes on the LAN while Wi-Fi is Connected, announced as `roro9stack-<id>.local`. |
|
||||
| Q54 | The device **always listens** for pushes on the LAN while Wi-Fi is Connected. *Revised in M2:* it was announced over mDNS as `roro9stack-<id>.local`; mDNS was removed to save RAM (it never crossed the dev box's routed network anyway). Pushes go to the IP shown in Settings → Firmware. |
|
||||
| Q55 | New firmware runs on **Probation**. It's confirmed once booted, UI drawn, Services started, 30 s without a crash, and Wi-Fi connected (if configured). Otherwise **Rollback**. A Toast reports either outcome. |
|
||||
| Q56 | **Downgrades are allowed**, with "older than the installed version" shown. |
|
||||
| Q57 | A valid push **installs right away**: progress screen, then reboot. The reboot waits for Text Entry to end, 60 s at most. |
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"""Pushes a signed Update File to a Cardputer over Wi-Fi (TCP 3232) and reports the result.
|
||||
|
||||
Usage: scripts/ota_push.py <file.ota> <host>
|
||||
<host> is the device's IP (shown in Settings > About), or its name when mDNS works on your network.
|
||||
<host> is the device's IP (shown in Settings > Firmware).
|
||||
"""
|
||||
import socket
|
||||
import sys
|
||||
|
||||
@@ -78,7 +78,6 @@ void FirmwarePage::draw(Canvas& c) {
|
||||
case kVersion: return "Version";
|
||||
case kStatus: return "Status";
|
||||
case kAddress: return "Push to";
|
||||
case kName: return "Name";
|
||||
case kSdHeader: return listed ? (found.empty() ? "No .ota files in /updates" : "On the SD card:") : "Looking on the SD card...";
|
||||
default: return " " + found[i - kFixed].substr(std::string(kUpdatesFolder).size() + 1);
|
||||
}
|
||||
@@ -88,7 +87,6 @@ void FirmwarePage::draw(Canvas& c) {
|
||||
case kVersion: return versionString();
|
||||
case kStatus: return update_.onProbation() ? "on probation" : "confirmed";
|
||||
case kAddress: return ip.empty() ? "Wi-Fi not connected" : ip + ":" + std::to_string(UpdateService::kPort);
|
||||
case kName: return update_.hostname() + ".local";
|
||||
case kSdHeader: return "";
|
||||
default: return "install >";
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class FirmwarePage {
|
||||
void draw(Canvas& c);
|
||||
|
||||
private:
|
||||
enum Row { kVersion, kStatus, kAddress, kName, kSdHeader, kFixed };
|
||||
enum Row { kVersion, kStatus, kAddress, kSdHeader, kFixed };
|
||||
|
||||
std::vector<std::string> files();
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ std::string lowered(std::string s) {
|
||||
void IrcApp::onEnter() {
|
||||
page_ = Page::Chat;
|
||||
editing_ = false;
|
||||
irc_.connect();
|
||||
if (!irc_.stoppedByUser()) irc_.connect(); // stopped by hand: stay stopped, typing reconnects
|
||||
view(current_);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -347,7 +347,7 @@ static const char* const kHelp =
|
||||
"coredump erase forget the core dump in flash\n"
|
||||
"key <name|char> press a key: up down left right select back home del tab, or one character\n"
|
||||
"wifi status | wifi add <ssid><TAB><password>\n"
|
||||
"irc start | irc dump | irc say <buffer> <text>\n"
|
||||
"irc start | irc stop | irc dump | irc say <buffer> <text>\n"
|
||||
"ls [folder] | rm <path> | install <path.ota> (Update from SD)\n"
|
||||
"sd list | cat <path> | log <text> | burst | sound on|off | short | normal\n"
|
||||
#ifdef RORO_DEBUG
|
||||
@@ -463,6 +463,7 @@ static void runCommand(String line) {
|
||||
listingWanted = true;
|
||||
}
|
||||
if (line == "irc start") irc->connect();
|
||||
if (line == "irc stop") irc->disconnect();
|
||||
if (line.startsWith("irc say ")) { // irc say <buffer index> <text>
|
||||
String rest = line.substring(8);
|
||||
int space = rest.indexOf(' ');
|
||||
|
||||
@@ -37,12 +37,19 @@ void IrcService::start() {
|
||||
}
|
||||
|
||||
void IrcService::connect() {
|
||||
stoppedByUser_ = false;
|
||||
if (wanted_) return;
|
||||
retryAtMs_ = millis();
|
||||
backoff_.reset();
|
||||
wanted_ = true;
|
||||
}
|
||||
|
||||
void IrcService::disconnect() {
|
||||
stoppedByUser_ = true;
|
||||
stopRequested_ = true; // the IRC task says QUIT and closes; nothing to do if it never started
|
||||
if (!task_) wanted_ = false;
|
||||
}
|
||||
|
||||
int IrcService::totalUnread() {
|
||||
Lock l(lock_);
|
||||
return session_->totalUnread();
|
||||
@@ -176,13 +183,10 @@ void IrcService::flushEffects() {
|
||||
status_ = Status::Online;
|
||||
backoff_.reset();
|
||||
}
|
||||
if (quit && open_) {
|
||||
delay(300); // let QUIT reach the server
|
||||
close("");
|
||||
wanted_ = false;
|
||||
status_ = Status::Stopped;
|
||||
Lock l(lock_);
|
||||
session_->disconnected(clock_.utcNow(), "disconnected");
|
||||
if (quit) { // /quit: QUIT is already on its way if connected; stop in any state
|
||||
stoppedByUser_ = true;
|
||||
stopRequested_ = true;
|
||||
quitSent_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +196,19 @@ void IrcService::loop() {
|
||||
bool wifiUp = wifi_.state() == WifiController::State::Connected;
|
||||
bool monitoring = wifi_.state() == WifiController::State::Monitoring;
|
||||
|
||||
if (stopRequested_) {
|
||||
stopRequested_ = false;
|
||||
if (open_) {
|
||||
if (!quitSent_) conn_->print("QUIT :roro9stack\r\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(300)); // let QUIT reach the server
|
||||
close("");
|
||||
}
|
||||
quitSent_ = false;
|
||||
wanted_ = false;
|
||||
status_ = Status::Stopped;
|
||||
Lock l(lock_);
|
||||
session_->disconnected(clock_.utcNow(), "disconnected (Enter reconnects)");
|
||||
}
|
||||
if (restart_) {
|
||||
restart_ = false;
|
||||
if (open_) {
|
||||
|
||||
@@ -31,9 +31,12 @@ class IrcService : public Service {
|
||||
const char* name() const override { return "irc"; }
|
||||
void start() override;
|
||||
|
||||
// Starts the session (the IRC App does this when opened); /quit stops it.
|
||||
// Starts the session (the IRC App does this when opened, unless you stopped it).
|
||||
void connect();
|
||||
// Stops it, from any state: says QUIT if connected, and stops retrying. /quit, `irc stop`.
|
||||
void disconnect();
|
||||
bool running() const { return wanted_; }
|
||||
bool stoppedByUser() const { return stoppedByUser_; }
|
||||
Status status() const { return status_; }
|
||||
|
||||
// Read or act on the session while holding its lock: withSession([](IrcSession& s) { ... }).
|
||||
@@ -78,6 +81,9 @@ class IrcService : public Service {
|
||||
ReconnectPolicy backoff_;
|
||||
std::string partial_;
|
||||
volatile bool wanted_ = false;
|
||||
volatile bool stoppedByUser_ = false; // a manual stop: opening the App doesn't reconnect
|
||||
volatile bool stopRequested_ = false;
|
||||
bool quitSent_ = false; // /quit already queued its QUIT
|
||||
volatile bool restart_ = false;
|
||||
volatile Status status_ = Status::Stopped;
|
||||
bool open_ = false;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#include "update_service.h"
|
||||
|
||||
#include <ESPmDNS.h>
|
||||
#include <SD.h>
|
||||
#include <WiFi.h>
|
||||
#include <esp_ota_ops.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "platform/identity.h"
|
||||
#include "platform/ota_device.h"
|
||||
#include "platform/system_info.h"
|
||||
#include "probation.h"
|
||||
@@ -66,7 +64,6 @@ class FileSource : public UpdateSource {
|
||||
UpdateService::UpdateService(KeyValueStore& store, WifiService& wifi, SavedNetworks& saved, StorageService& storage,
|
||||
EventBus& bus, const Settings& settings)
|
||||
: store_(store), wifi_(wifi), saved_(saved), storage_(storage), bus_(bus), settings_(settings) {
|
||||
hostname_ = "roro9stack-" + identity::defaultShortName();
|
||||
}
|
||||
|
||||
void UpdateService::notify(const std::string& text, NotificationLevel level) {
|
||||
@@ -196,11 +193,9 @@ void UpdateService::listen() {
|
||||
bool connected = wifi_.state() == WifiController::State::Connected;
|
||||
if (connected && !listening) {
|
||||
server.begin();
|
||||
if (MDNS.begin(hostname_.c_str())) MDNS.addService("roro9stack", "tcp", kPort);
|
||||
listening = true;
|
||||
} else if (!connected && listening) {
|
||||
server.end();
|
||||
MDNS.end();
|
||||
listening = false;
|
||||
}
|
||||
// The main loop restarts into an installed update when it's safe. If it never does (stuck,
|
||||
|
||||
@@ -35,7 +35,6 @@ class UpdateService : public Service {
|
||||
int percent() const { return percent_; }
|
||||
std::string incomingVersion() const;
|
||||
bool onProbation() const { return probation_; }
|
||||
const std::string& hostname() const { return hostname_; }
|
||||
|
||||
// Installs an Update File from the SD card (runs on the storage task).
|
||||
void installFromSd(const std::string& path);
|
||||
@@ -58,7 +57,6 @@ class UpdateService : public Service {
|
||||
EventBus& bus_;
|
||||
const Settings& settings_;
|
||||
TaskHandle_t task_ = nullptr;
|
||||
std::string hostname_;
|
||||
volatile Phase phase_ = Phase::Idle;
|
||||
volatile int percent_ = 0;
|
||||
std::string incoming_;
|
||||
|
||||
Reference in New Issue
Block a user