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:
2026-10-04 23:17:48 +02:00
co-authored by Claude Opus 5.5
parent db265fb757
commit 4e439410cd
12 changed files with 39 additions and 23 deletions
-2
View File
@@ -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 >";
}
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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
View File
@@ -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(' ');
+24 -7
View File
@@ -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_) {
+7 -1
View File
@@ -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;
-5
View File
@@ -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,
-2
View File
@@ -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_;