mirror of
https://github.com/Colorado-Mesh/meshcore-bot-firmware.git
synced 2026-08-11 16:20:29 +00:00
Update MeshCore base to v1.16.0; add sig/air/coin; ship 139 boards
- Bump vendor/MeshCore pin 910b1bee -> bbb58cce (upstream v1.16.0,
272 commits) and rebase the 14-patch bot queue onto it; one trivial
conflict in the MyMesh constructor.
- New patch 15: sig/air/coin commands plus registry-derived help and
cmd listings ('cmd diag' for the diagnostic set). BOT_PREFS_VERSION
bumped to 7. Host tests cover the new commands and assert every
discoverable registry entry appears in a listing.
- Re-include six boards fixed upstream (Pico W, RAK 11310, Waveshare
RP2040 LoRa, Xiao RP2040, Nibble USB+BLE) after local test builds;
remaining exclusions are BLE flash-size only.
- Fix release env enumeration to strip CR line endings so envs in CRLF
variant files (Minewsemi ME25LS01, Wio WM1110, Nibble) are counted
and built; verified all three build locally.
- Refresh README/CHANGELOG/RELEASE/CONTRIBUTING for the new base and
command set.
Verified: host tests, safety checks, 4 representative builds, and 9
additional env builds all pass; the 15-patch queue applies cleanly to
pristine bbb58cce.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Fri, 15 May 2026 11:44:21 -0600
|
||||
Subject: [PATCH 01/14] Add companion radio firmware bot command parity
|
||||
Subject: [PATCH 01/15] Add companion radio firmware bot command parity
|
||||
|
||||
---
|
||||
.../companion_radio/BotCommandRegistry.cpp | 121 ++
|
||||
@@ -1428,7 +1428,7 @@ index 00000000..5c7430a9
|
||||
+static_assert(sizeof(BotPrefs) <= 320, "BotPrefs RAM budget exceeded");
|
||||
+static_assert(sizeof(BotStats) <= 64, "BotStats RAM budget exceeded");
|
||||
diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp
|
||||
index c7988bb3..9a134389 100644
|
||||
index bf2f36c3..e6f52e09 100644
|
||||
--- a/examples/companion_radio/DataStore.cpp
|
||||
+++ b/examples/companion_radio/DataStore.cpp
|
||||
@@ -1,6 +1,10 @@
|
||||
@@ -1482,7 +1482,7 @@ index c7988bb3..9a134389 100644
|
||||
File file = openRead(_fs, filename);
|
||||
if (file) {
|
||||
diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h
|
||||
index 58b4d5d2..187cd771 100644
|
||||
index af5ee7af..702316cf 100644
|
||||
--- a/examples/companion_radio/DataStore.h
|
||||
+++ b/examples/companion_radio/DataStore.h
|
||||
@@ -5,6 +5,13 @@
|
||||
@@ -1508,7 +1508,7 @@ index 58b4d5d2..187cd771 100644
|
||||
+ bool saveBotPrefs(const BotPrefs& prefs);
|
||||
+#endif
|
||||
void loadContacts(DataStoreHost* host);
|
||||
void saveContacts(DataStoreHost* host);
|
||||
void saveContacts(DataStoreHost* host, bool (*filter)(const ContactInfo& c) = NULL);
|
||||
void loadChannels(DataStoreHost* host);
|
||||
diff --git a/examples/companion_radio/EmergencyForwarder.cpp b/examples/companion_radio/EmergencyForwarder.cpp
|
||||
new file mode 100644
|
||||
@@ -2072,7 +2072,7 @@ index 00000000..5e93dad0
|
||||
+
|
||||
+}
|
||||
diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp
|
||||
index e8c1914b..44fcd1ca 100644
|
||||
index 6fbb0f74..19963f38 100644
|
||||
--- a/examples/companion_radio/MyMesh.cpp
|
||||
+++ b/examples/companion_radio/MyMesh.cpp
|
||||
@@ -2,6 +2,21 @@
|
||||
@@ -2097,7 +2097,7 @@ index e8c1914b..44fcd1ca 100644
|
||||
|
||||
#define CMD_APP_START 1
|
||||
#define CMD_SEND_TXT_MSG 2
|
||||
@@ -105,8 +120,244 @@
|
||||
@@ -106,8 +121,244 @@
|
||||
#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250
|
||||
#define LAZY_CONTACTS_WRITE_DELAY 5000
|
||||
|
||||
@@ -2342,7 +2342,7 @@ index e8c1914b..44fcd1ca 100644
|
||||
// these are _pushed_ to client app at any time
|
||||
#define PUSH_CODE_ADVERT 0x80
|
||||
#define PUSH_CODE_PATH_UPDATED 0x81
|
||||
@@ -514,6 +765,9 @@ void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t
|
||||
@@ -523,6 +774,9 @@ void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t
|
||||
const char *text) {
|
||||
markConnectionActive(from); // in case this is from a server, and we have a connection
|
||||
queueMessage(from, TXT_TYPE_PLAIN, pkt, sender_timestamp, NULL, 0, text);
|
||||
@@ -2352,7 +2352,7 @@ index e8c1914b..44fcd1ca 100644
|
||||
}
|
||||
|
||||
void MyMesh::onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp,
|
||||
@@ -528,8 +782,840 @@ void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uin
|
||||
@@ -537,8 +791,840 @@ void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uin
|
||||
// from.sync_since change needs to be persisted
|
||||
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
|
||||
queueMessage(from, TXT_TYPE_SIGNED_PLAIN, pkt, sender_timestamp, sender_prefix, 4, text);
|
||||
@@ -2399,8 +2399,8 @@ index e8c1914b..44fcd1ca 100644
|
||||
+ (unsigned)bot_prefs.normal_jitter_ms);
|
||||
+ Serial.printf(" > advert local=%lu flood=%lu\n", (unsigned long)bot_prefs.local_advert_interval_ms,
|
||||
+ (unsigned long)bot_prefs.flood_advert_interval_ms);
|
||||
+}
|
||||
+
|
||||
}
|
||||
|
||||
+bool MyMesh::handleBotCLI(const char *args) {
|
||||
+ if (!args) return false;
|
||||
+ botSkipSpaces(&args);
|
||||
@@ -2705,8 +2705,8 @@ index e8c1914b..44fcd1ca 100644
|
||||
+ pending->text[pending->text_len] = 0;
|
||||
+ pending->active = true;
|
||||
+ return true;
|
||||
}
|
||||
|
||||
+}
|
||||
+
|
||||
+bool MyMesh::findBotChannel(BotChannelKind kind, uint8_t &channel_idx) {
|
||||
+ for (uint8_t i = 0; i < MAX_GROUP_CHANNELS; i++) {
|
||||
+ ChannelDetails channel;
|
||||
@@ -3193,7 +3193,7 @@ index e8c1914b..44fcd1ca 100644
|
||||
void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp,
|
||||
const char *text) {
|
||||
int i = 0;
|
||||
@@ -566,15 +1652,17 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe
|
||||
@@ -575,15 +1661,17 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe
|
||||
if (_ui) _ui->notify(UIEventType::channelMessage);
|
||||
#endif
|
||||
}
|
||||
@@ -3213,7 +3213,7 @@ index e8c1914b..44fcd1ca 100644
|
||||
}
|
||||
|
||||
void MyMesh::onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint16_t data_type,
|
||||
@@ -829,6 +1917,23 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code,
|
||||
@@ -838,6 +1926,23 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code,
|
||||
} else {
|
||||
MESH_DEBUG_PRINTLN("onTraceRecv(), data received while app offline");
|
||||
}
|
||||
@@ -3237,10 +3237,10 @@ index e8c1914b..44fcd1ca 100644
|
||||
}
|
||||
|
||||
uint32_t MyMesh::calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const {
|
||||
@@ -856,6 +1961,19 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
dirty_contacts_expiry = 0;
|
||||
@@ -866,6 +1971,19 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
memset(advert_paths, 0, sizeof(advert_paths));
|
||||
memset(send_scope.key, 0, sizeof(send_scope.key));
|
||||
send_unscoped = false;
|
||||
+#if CMESH_BOT_ENABLED
|
||||
+ BotPrefsCodec::defaults(bot_prefs);
|
||||
+ memset(&bot_stats, 0, sizeof(bot_stats));
|
||||
@@ -3257,7 +3257,7 @@ index e8c1914b..44fcd1ca 100644
|
||||
|
||||
// defaults
|
||||
memset(&_prefs, 0, sizeof(_prefs));
|
||||
@@ -868,6 +1986,9 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
@@ -878,6 +1996,9 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
_prefs.tx_power_dbm = LORA_TX_POWER;
|
||||
_prefs.gps_enabled = 0; // GPS disabled by default
|
||||
_prefs.gps_interval = 0; // No automatic GPS updates by default
|
||||
@@ -3267,7 +3267,7 @@ index e8c1914b..44fcd1ca 100644
|
||||
//_prefs.rx_delay_base = 10.0f; enable once new algo fixed
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268)
|
||||
#ifdef SX126X_RX_BOOSTED_GAIN
|
||||
@@ -956,6 +2077,11 @@ void MyMesh::begin(bool has_display) {
|
||||
@@ -966,6 +2087,11 @@ void MyMesh::begin(bool has_display) {
|
||||
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
|
||||
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
|
||||
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
|
||||
@@ -3279,7 +3279,7 @@ index e8c1914b..44fcd1ca 100644
|
||||
}
|
||||
|
||||
const char *MyMesh::getNodeName() {
|
||||
@@ -1975,7 +3101,13 @@ void MyMesh::checkCLIRescueCmd() {
|
||||
@@ -2023,7 +3149,13 @@ void MyMesh::checkCLIRescueCmd() {
|
||||
if (len > 0 && cli_command[len - 1] == '\r') { // received complete line
|
||||
cli_command[len - 1] = 0; // replace newline with C string null terminator
|
||||
|
||||
@@ -3294,7 +3294,7 @@ index e8c1914b..44fcd1ca 100644
|
||||
const char* config = &cli_command[4];
|
||||
if (memcmp(config, "pin ", 4) == 0) {
|
||||
_prefs.ble_pin = atoi(&config[4]);
|
||||
@@ -2172,6 +3304,10 @@ void MyMesh::loop() {
|
||||
@@ -2228,6 +3360,10 @@ void MyMesh::loop() {
|
||||
dirty_contacts_expiry = 0;
|
||||
}
|
||||
|
||||
@@ -3306,7 +3306,7 @@ index e8c1914b..44fcd1ca 100644
|
||||
if (_ui) _ui->setHasConnection(_serial->isConnected());
|
||||
#endif
|
||||
diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h
|
||||
index aeff591c..8d848fef 100644
|
||||
index 43d3950b..c1159a31 100644
|
||||
--- a/examples/companion_radio/MyMesh.h
|
||||
+++ b/examples/companion_radio/MyMesh.h
|
||||
@@ -4,6 +4,15 @@
|
||||
@@ -3323,9 +3323,9 @@ index aeff591c..8d848fef 100644
|
||||
+#endif
|
||||
+
|
||||
/*------------ Frame Protocol --------------*/
|
||||
#define FIRMWARE_VER_CODE 11
|
||||
#define FIRMWARE_VER_CODE 13
|
||||
|
||||
@@ -192,6 +201,43 @@ private:
|
||||
@@ -195,6 +204,43 @@ private:
|
||||
return _store->putBlobByKey(key, key_len, src_buf, len);
|
||||
}
|
||||
|
||||
@@ -3369,7 +3369,7 @@ index aeff591c..8d848fef 100644
|
||||
void checkCLIRescueCmd();
|
||||
void checkSerialInterface();
|
||||
bool isValidClientRepeatFreq(uint32_t f) const;
|
||||
@@ -223,6 +269,53 @@ private:
|
||||
@@ -227,6 +273,53 @@ private:
|
||||
|
||||
TransportKey send_scope;
|
||||
|
||||
@@ -3679,7 +3679,7 @@ index 00000000..7717bee7
|
||||
+
|
||||
+}
|
||||
diff --git a/platformio.ini b/platformio.ini
|
||||
index 864e5e1f..dfed7dbd 100644
|
||||
index e16f7b83..e96147b9 100644
|
||||
--- a/platformio.ini
|
||||
+++ b/platformio.ini
|
||||
@@ -54,6 +54,14 @@ build_src_filter =
|
||||
@@ -3698,7 +3698,7 @@ index 864e5e1f..dfed7dbd 100644
|
||||
extends = arduino_base
|
||||
platform = platformio/espressif32@6.11.0
|
||||
diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini
|
||||
index 803ee683..ca9cf00d 100644
|
||||
index a70a93a5..8164bc78 100644
|
||||
--- a/variants/heltec_v3/platformio.ini
|
||||
+++ b/variants/heltec_v3/platformio.ini
|
||||
@@ -144,6 +144,7 @@ build_flags =
|
||||
@@ -3718,10 +3718,10 @@ index 803ee683..ca9cf00d 100644
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3400
|
||||
-D BLE_DEBUG_LOGGING=1
|
||||
diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini
|
||||
index ea7e49c3..d6610098 100644
|
||||
index 2bbba314..a1226fe3 100644
|
||||
--- a/variants/rak4631/platformio.ini
|
||||
+++ b/variants/rak4631/platformio.ini
|
||||
@@ -122,6 +122,7 @@ build_flags =
|
||||
@@ -127,6 +127,7 @@ build_flags =
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
@@ -3729,7 +3729,7 @@ index ea7e49c3..d6610098 100644
|
||||
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
|
||||
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
|
||||
build_src_filter = ${rak4631.build_src_filter}
|
||||
@@ -143,6 +144,7 @@ build_flags =
|
||||
@@ -148,6 +149,7 @@ build_flags =
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Fri, 15 May 2026 19:58:42 -0600
|
||||
Subject: [PATCH 02/14] Harden firmware bot response coordination
|
||||
Subject: [PATCH 02/15] Harden firmware bot response coordination
|
||||
|
||||
---
|
||||
examples/companion_radio/BotCommands.cpp | 2 +-
|
||||
@@ -153,10 +153,10 @@ index e248adc1..38d4416d 100644
|
||||
BotFingerprint responseFingerprintFor(const BotMessage& message, const char* response_text, size_t response_text_len);
|
||||
BotCommandId commandIdForName(const char* name, size_t len);
|
||||
diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp
|
||||
index 44fcd1ca..2ee5139b 100644
|
||||
index 19963f38..cb557f5d 100644
|
||||
--- a/examples/companion_radio/MyMesh.cpp
|
||||
+++ b/examples/companion_radio/MyMesh.cpp
|
||||
@@ -139,6 +139,18 @@ static void botCopyString(char *dest, size_t dest_len, const char *src) {
|
||||
@@ -140,6 +140,18 @@ static void botCopyString(char *dest, size_t dest_len, const char *src) {
|
||||
dest[len] = 0;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
static bool botParseU32(const char *text, uint32_t *value, const char **end_out) {
|
||||
if (!text || !value || !isdigit((unsigned char)text[0])) return false;
|
||||
uint32_t parsed = 0;
|
||||
@@ -598,6 +610,10 @@ void MyMesh::onContactsFull() {
|
||||
@@ -599,6 +611,10 @@ void MyMesh::onContactsFull() {
|
||||
}
|
||||
|
||||
void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) {
|
||||
@@ -186,7 +186,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
if (_serial->isConnected()) {
|
||||
if (is_new) {
|
||||
writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact);
|
||||
@@ -1040,11 +1056,9 @@ void MyMesh::observeBotChannelMessage(uint8_t channel_idx, const char *channel_n
|
||||
@@ -1049,11 +1065,9 @@ void MyMesh::observeBotChannelMessage(uint8_t channel_idx, const char *channel_n
|
||||
message.path = packet->path;
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
recordBotObservation(message, NULL, channel_idx);
|
||||
}
|
||||
|
||||
@@ -1100,6 +1114,21 @@ void MyMesh::buildBotCommandContext(BotCommandContext &context, BotCommandId com
|
||||
@@ -1109,6 +1123,21 @@ void MyMesh::buildBotCommandContext(BotCommandContext &context, BotCommandId com
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
bool MyMesh::enqueueBotResponse(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx,
|
||||
const char *text, size_t text_len, BotFingerprint request_fingerprint,
|
||||
BotFingerprint response_fingerprint) {
|
||||
@@ -1124,11 +1153,9 @@ bool MyMesh::enqueueBotResponse(const BotMessage &message, const ContactInfo *di
|
||||
@@ -1133,11 +1162,9 @@ bool MyMesh::enqueueBotResponse(const BotMessage &message, const ContactInfo *di
|
||||
pending->channel_idx = channel_idx;
|
||||
pending->request_fingerprint = request_fingerprint;
|
||||
pending->response_fingerprint = response_fingerprint;
|
||||
@@ -238,7 +238,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
pending->active = true;
|
||||
return true;
|
||||
}
|
||||
@@ -1204,7 +1231,14 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
@@ -1213,7 +1240,14 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
BotFingerprint request_fingerprint = FirmwareBot::fingerprintFor(message);
|
||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
size_t response_len = botFormatTraceSent(response, sizeof(response));
|
||||
@@ -254,7 +254,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
BotFingerprint fingerprint;
|
||||
uint32_t due_at_millis = 0;
|
||||
uint32_t now = _ms->getMillis();
|
||||
@@ -1264,6 +1298,10 @@ bool MyMesh::enqueueBotTrace(const BotMessage &message, const ContactInfo *direc
|
||||
@@ -1273,6 +1307,10 @@ bool MyMesh::enqueueBotTrace(const BotMessage &message, const ContactInfo *direc
|
||||
memcpy(pending->recipient_pub_key, direct_recipient->id.pub_key, sizeof(pending->recipient_pub_key));
|
||||
}
|
||||
pending->channel_idx = channel_idx;
|
||||
@@ -265,7 +265,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
pending->request_fingerprint = request_fingerprint;
|
||||
pending->response_fingerprint = response_fingerprint;
|
||||
pending->tag = tag;
|
||||
@@ -1275,6 +1313,16 @@ bool MyMesh::enqueueBotTrace(const BotMessage &message, const ContactInfo *direc
|
||||
@@ -1284,6 +1322,16 @@ bool MyMesh::enqueueBotTrace(const BotMessage &message, const ContactInfo *direc
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
bool MyMesh::sendBotTraceText(const PendingBotTrace &pending, const char *text, size_t text_len,
|
||||
BotFingerprint response_fingerprint, uint32_t now_millis) {
|
||||
bool success = false;
|
||||
@@ -1325,7 +1373,13 @@ bool MyMesh::sendPendingBotTrace(PendingBotTrace &pending, uint32_t now_millis)
|
||||
@@ -1334,7 +1382,13 @@ bool MyMesh::sendPendingBotTrace(PendingBotTrace &pending, uint32_t now_millis)
|
||||
|
||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
size_t response_len = botFormatTraceSent(response, sizeof(response));
|
||||
@@ -297,7 +297,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
}
|
||||
|
||||
void MyMesh::expirePendingBotTraces(uint32_t now_millis) {
|
||||
@@ -1334,10 +1388,13 @@ void MyMesh::expirePendingBotTraces(uint32_t now_millis) {
|
||||
@@ -1343,10 +1397,13 @@ void MyMesh::expirePendingBotTraces(uint32_t now_millis) {
|
||||
if (!pending->active || !pending->sent || (int32_t)(now_millis - pending->expires_at_millis) < 0) continue;
|
||||
|
||||
const char *response = "Trace timed out";
|
||||
@@ -315,7 +315,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
pending->active = false;
|
||||
}
|
||||
}
|
||||
@@ -1384,6 +1441,18 @@ bool MyMesh::observeKnownBotResponse(const BotMessage &message, bool authoritati
|
||||
@@ -1393,6 +1450,18 @@ bool MyMesh::observeKnownBotResponse(const BotMessage &message, bool authoritati
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx) {
|
||||
bot_stats.observed_messages++;
|
||||
BotPolicyDecision decision = BotPolicy::decide(message.channel_kind);
|
||||
@@ -1403,6 +1472,7 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
@@ -1412,6 +1481,7 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
}
|
||||
|
||||
if (observeKnownBotResponse(message, direct_recipient != NULL)) return;
|
||||
@@ -342,7 +342,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
sendQueuedBotResponses();
|
||||
|
||||
BotCommand command;
|
||||
@@ -1439,8 +1509,15 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
@@ -1448,8 +1518,15 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
BotFingerprint fingerprint;
|
||||
uint32_t due_at_millis = 0;
|
||||
uint32_t bot_identity_seed;
|
||||
@@ -1462,7 +1539,7 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
@@ -1471,7 +1548,7 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
bot_stats.pending_responses++;
|
||||
FirmwareBot::recordCommandCooldown(bot_command_cooldowns, BOT_COMMAND_COOLDOWN_SLOTS, command.id, _ms->getMillis(),
|
||||
BOT_COMMAND_COOLDOWN_MILLIS);
|
||||
@@ -368,7 +368,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
ResponseCoordinator::cancel(bot_coordinator_pending, BOT_COORDINATOR_PENDING_SLOTS, fingerprint);
|
||||
bot_stats.send_failures++;
|
||||
}
|
||||
@@ -1572,10 +1649,12 @@ void MyMesh::sendQueuedEmergencyForwards() {
|
||||
@@ -1581,10 +1658,12 @@ void MyMesh::sendQueuedEmergencyForwards() {
|
||||
|
||||
bool MyMesh::sendBotSelfAdvert(bool flood) {
|
||||
mesh::Packet* pkt;
|
||||
@@ -383,7 +383,7 @@ index 44fcd1ca..2ee5139b 100644
|
||||
}
|
||||
if (!pkt) return false;
|
||||
|
||||
@@ -1927,9 +2006,13 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code,
|
||||
@@ -1936,9 +2015,13 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code,
|
||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
size_t response_len = botFormatTraceResult(response, sizeof(response), tag, flags, path_hashes, path_len,
|
||||
(int8_t)(packet->getSNR() * 4));
|
||||
@@ -401,10 +401,10 @@ index 44fcd1ca..2ee5139b 100644
|
||||
break;
|
||||
}
|
||||
diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h
|
||||
index 8d848fef..67bdc010 100644
|
||||
index c1159a31..1197caff 100644
|
||||
--- a/examples/companion_radio/MyMesh.h
|
||||
+++ b/examples/companion_radio/MyMesh.h
|
||||
@@ -214,7 +214,9 @@ private:
|
||||
@@ -217,7 +217,9 @@ private:
|
||||
uint32_t sender_timestamp, const mesh::Packet *packet);
|
||||
void recordBotObservation(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx);
|
||||
bool observeKnownBotResponse(const BotMessage &message, bool authoritative_sender);
|
||||
@@ -414,7 +414,7 @@ index 8d848fef..67bdc010 100644
|
||||
bool enqueueBotResponse(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx,
|
||||
const char *text, size_t text_len, BotFingerprint request_fingerprint,
|
||||
BotFingerprint response_fingerprint);
|
||||
@@ -226,6 +228,7 @@ private:
|
||||
@@ -229,6 +231,7 @@ private:
|
||||
BotFingerprint response_fingerprint, uint32_t tag, uint32_t auth_code);
|
||||
bool sendBotTraceText(const PendingBotTrace &pending, const char *text, size_t text_len,
|
||||
BotFingerprint response_fingerprint, uint32_t now_millis);
|
||||
@@ -422,7 +422,7 @@ index 8d848fef..67bdc010 100644
|
||||
bool sendPendingBotTrace(PendingBotTrace &pending, uint32_t now_millis);
|
||||
void expirePendingBotTraces(uint32_t now_millis);
|
||||
bool enqueueEmergencyForward(const BotMessage &message);
|
||||
@@ -291,8 +294,12 @@ private:
|
||||
@@ -295,8 +298,12 @@ private:
|
||||
bool active;
|
||||
bool direct;
|
||||
bool sent;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Fri, 15 May 2026 20:09:58 -0600
|
||||
Subject: [PATCH 03/14] Allow prefixless firmware bot DMs
|
||||
Subject: [PATCH 03/15] Allow prefixless firmware bot DMs
|
||||
|
||||
---
|
||||
examples/companion_radio/BotPolicy.cpp | 2 +-
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Sat, 16 May 2026 04:12:02 +0000
|
||||
Subject: [PATCH 04/14] Align firmware bot commands with upstream behavior
|
||||
Subject: [PATCH 04/15] Align firmware bot commands with upstream behavior
|
||||
|
||||
---
|
||||
.../companion_radio/BotCommandRegistry.cpp | 25 +-
|
||||
@@ -816,10 +816,10 @@ index 38d4416d..9eff340b 100644
|
||||
bool isBotAdvertName(const char* name, size_t name_len);
|
||||
BotFingerprint fingerprintFor(const BotMessage& message);
|
||||
diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp
|
||||
index 2ee5139b..7b87f14f 100644
|
||||
index cb557f5d..e84b3a52 100644
|
||||
--- a/examples/companion_radio/MyMesh.cpp
|
||||
+++ b/examples/companion_radio/MyMesh.cpp
|
||||
@@ -223,7 +223,7 @@ static uint8_t botTraceHashSize(uint8_t flags) {
|
||||
@@ -224,7 +224,7 @@ static uint8_t botTraceHashSize(uint8_t flags) {
|
||||
|
||||
static bool botTracePathShapeValid(uint8_t path_len, uint8_t flags) {
|
||||
uint8_t hash_size = botTraceHashSize(flags);
|
||||
@@ -828,7 +828,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
(path_len % hash_size) == 0 && (path_len / hash_size) <= MAX_PATH_SIZE;
|
||||
}
|
||||
|
||||
@@ -236,17 +236,42 @@ static void botCopyReversedPath(uint8_t *dest, const uint8_t *src, uint8_t hop_c
|
||||
@@ -237,17 +237,42 @@ static void botCopyReversedPath(uint8_t *dest, const uint8_t *src, uint8_t hop_c
|
||||
static bool botParseTraceHexPath(const BotCommand &command, uint8_t flags, uint8_t path[BOT_MAX_PATH_BYTES],
|
||||
uint8_t *path_len) {
|
||||
if (!path || !path_len || command.args_len == 0) return false;
|
||||
@@ -880,7 +880,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
*path_len = parsed_len;
|
||||
return true;
|
||||
}
|
||||
@@ -284,7 +309,7 @@ static BotCommandResult botWriteFormatted(char *output, size_t output_len, const
|
||||
@@ -285,7 +310,7 @@ static BotCommandResult botWriteFormatted(char *output, size_t output_len, const
|
||||
static bool botParsePubKeyPrefixHex(const BotCommand &command, uint8_t prefix[PUB_KEY_SIZE], uint8_t *prefix_len) {
|
||||
if (!prefix || !prefix_len || command.args_len == 0 || (command.args_len & 1) != 0) return false;
|
||||
size_t byte_len = command.args_len / 2;
|
||||
@@ -889,7 +889,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
for (size_t i = 0; i < byte_len; i++) {
|
||||
int high = botHexValue(command.args[i * 2]);
|
||||
int low = botHexValue(command.args[i * 2 + 1]);
|
||||
@@ -341,14 +366,32 @@ static void botAppendHex(char *output, size_t output_len, size_t *pos, const uin
|
||||
@@ -342,14 +367,32 @@ static void botAppendHex(char *output, size_t output_len, size_t *pos, const uin
|
||||
if (output_len > 0) output[*pos < output_len ? *pos : output_len - 1] = 0;
|
||||
}
|
||||
|
||||
@@ -929,7 +929,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
}
|
||||
|
||||
static size_t botFormatTraceResult(char *output, size_t output_len, uint32_t tag, uint8_t flags,
|
||||
@@ -358,14 +401,22 @@ static size_t botFormatTraceResult(char *output, size_t output_len, uint32_t tag
|
||||
@@ -359,14 +402,22 @@ static size_t botFormatTraceResult(char *output, size_t output_len, uint32_t tag
|
||||
uint8_t hop_count = path_len / hash_size;
|
||||
char snr[8];
|
||||
botFormatQuarters(snr_quarters, snr, sizeof(snr));
|
||||
@@ -955,7 +955,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
return botBoundedStrLen(output, output_len);
|
||||
}
|
||||
#endif
|
||||
@@ -1032,6 +1083,19 @@ void MyMesh::observeBotDirectMessage(const ContactInfo &from, uint32_t sender_ti
|
||||
@@ -1041,6 +1092,19 @@ void MyMesh::observeBotDirectMessage(const ContactInfo &from, uint32_t sender_ti
|
||||
if (sender_prefix && prefix_len > 0) memcpy(message.sender_key_prefix, sender_prefix, prefix_len);
|
||||
message.sender_key_prefix_len = prefix_len;
|
||||
message.sender_timestamp = sender_timestamp;
|
||||
@@ -975,7 +975,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
message.text_truncated = FirmwareBot::normalizeText(text, botBoundedStrLen(text, BOT_MAX_TEXT_LEN + 1), message.text,
|
||||
sizeof(message.text), &message.text_len) == BOT_WRITE_TRUNCATED;
|
||||
recordBotObservation(message, &from, 0xFF);
|
||||
@@ -1048,6 +1112,7 @@ void MyMesh::observeBotChannelMessage(uint8_t channel_idx, const char *channel_n
|
||||
@@ -1057,6 +1121,7 @@ void MyMesh::observeBotChannelMessage(uint8_t channel_idx, const char *channel_n
|
||||
message.channel_name[channel_len] = 0;
|
||||
}
|
||||
message.sender_timestamp = sender_timestamp;
|
||||
@@ -983,7 +983,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
if (packet && packet->isRouteFlood() && packet->path_len <= 0xFF && mesh::Packet::isValidPathLen((uint8_t)packet->path_len)) {
|
||||
message.path_len = (uint8_t)packet->path_len;
|
||||
message.path_hash_size = packet->getPathHashSize();
|
||||
@@ -1178,7 +1243,7 @@ BotCommandResult MyMesh::executeBotPrefixCommand(const BotCommand &command, char
|
||||
@@ -1187,7 +1252,7 @@ BotCommandResult MyMesh::executeBotPrefixCommand(const BotCommand &command, char
|
||||
uint8_t prefix[PUB_KEY_SIZE];
|
||||
uint8_t prefix_len = 0;
|
||||
if (!botParsePubKeyPrefixHex(command, prefix, &prefix_len)) {
|
||||
@@ -992,7 +992,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
}
|
||||
|
||||
ContactInfo match;
|
||||
@@ -1217,10 +1282,17 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
@@ -1226,10 +1291,17 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
bool have_path = false;
|
||||
if (command.args_len > 0) {
|
||||
have_path = botParseTraceHexPath(command, flags, path, &path_len);
|
||||
@@ -1012,7 +1012,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
path_len = raw_len;
|
||||
have_path = true;
|
||||
}
|
||||
@@ -1230,7 +1302,7 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
@@ -1239,7 +1311,7 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
|
||||
BotFingerprint request_fingerprint = FirmwareBot::fingerprintFor(message);
|
||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
@@ -1021,7 +1021,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
char final_response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
size_t final_response_len = 0;
|
||||
if (!botFormatResponseForChannel(message, response, response_len, final_response, sizeof(final_response),
|
||||
@@ -1247,7 +1319,7 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
@@ -1256,7 +1328,7 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
uint32_t jitter_seed = request_fingerprint.value ? (uint32_t)request_fingerprint.value : 1;
|
||||
uint8_t queue_depth = (uint8_t)_mgr->getOutboundTotal();
|
||||
BotCoordinatorScheduleResult schedule = ResponseCoordinator::schedule(bot_coordinator_pending, BOT_COORDINATOR_PENDING_SLOTS,
|
||||
@@ -1030,7 +1030,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
response_fingerprint, now, jitter_seed,
|
||||
bot_identity_seed, queue_depth, bot_prefs.normal_delay_ms,
|
||||
bot_prefs.normal_jitter_ms, &fingerprint,
|
||||
@@ -1264,7 +1336,7 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
@@ -1273,7 +1345,7 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
|
||||
bot_stats.eligible_messages++;
|
||||
bot_stats.pending_responses++;
|
||||
@@ -1039,7 +1039,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
BOT_TRACE_COOLDOWN_MILLIS);
|
||||
if (!enqueueBotTrace(message, direct_recipient, channel_idx, path, path_len, flags, fingerprint, response_fingerprint, tag,
|
||||
auth_code)) {
|
||||
@@ -1278,7 +1350,7 @@ bool MyMesh::enqueueBotTrace(const BotMessage &message, const ContactInfo *direc
|
||||
@@ -1287,7 +1359,7 @@ bool MyMesh::enqueueBotTrace(const BotMessage &message, const ContactInfo *direc
|
||||
const uint8_t *path, uint8_t path_len, uint8_t flags,
|
||||
BotFingerprint request_fingerprint, BotFingerprint response_fingerprint, uint32_t tag,
|
||||
uint32_t auth_code) {
|
||||
@@ -1048,7 +1048,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
|
||||
size_t slot = BOT_PENDING_TRACE_SLOTS;
|
||||
for (size_t i = 0; i < BOT_PENDING_TRACE_SLOTS; i++) {
|
||||
@@ -1308,7 +1380,7 @@ bool MyMesh::enqueueBotTrace(const BotMessage &message, const ContactInfo *direc
|
||||
@@ -1317,7 +1389,7 @@ bool MyMesh::enqueueBotTrace(const BotMessage &message, const ContactInfo *direc
|
||||
pending->auth_code = auth_code;
|
||||
pending->flags = flags;
|
||||
pending->path_len = path_len;
|
||||
@@ -1057,7 +1057,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
pending->active = true;
|
||||
return true;
|
||||
}
|
||||
@@ -1372,7 +1444,7 @@ bool MyMesh::sendPendingBotTrace(PendingBotTrace &pending, uint32_t now_millis)
|
||||
@@ -1381,7 +1453,7 @@ bool MyMesh::sendPendingBotTrace(PendingBotTrace &pending, uint32_t now_millis)
|
||||
pending.expires_at_millis = now_millis + BOT_TRACE_TIMEOUT_MILLIS;
|
||||
|
||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
@@ -1066,7 +1066,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
char final_response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
size_t final_response_len = 0;
|
||||
if (!botFormatResponseForChannelKind(pending.channel_kind, response, response_len, final_response, sizeof(final_response),
|
||||
@@ -1387,7 +1459,9 @@ void MyMesh::expirePendingBotTraces(uint32_t now_millis) {
|
||||
@@ -1396,7 +1468,9 @@ void MyMesh::expirePendingBotTraces(uint32_t now_millis) {
|
||||
PendingBotTrace *pending = &pending_bot_traces[i];
|
||||
if (!pending->active || !pending->sent || (int32_t)(now_millis - pending->expires_at_millis) < 0) continue;
|
||||
|
||||
@@ -1077,7 +1077,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
char final_response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
size_t final_response_len = 0;
|
||||
if (botFormatResponseForChannelKind(pending->channel_kind, response, botBoundedStrLen(response, BOT_MAX_RESPONSE_LEN + 1),
|
||||
@@ -1491,19 +1565,43 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
@@ -1500,19 +1574,43 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
|
||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
BotCommandResult result;
|
||||
@@ -1127,7 +1127,7 @@ index 2ee5139b..7b87f14f 100644
|
||||
bot_stats.parse_errors++;
|
||||
return;
|
||||
diff --git a/platformio.ini b/platformio.ini
|
||||
index dfed7dbd..e3e4b30f 100644
|
||||
index e96147b9..e724ad35 100644
|
||||
--- a/platformio.ini
|
||||
+++ b/platformio.ini
|
||||
@@ -57,6 +57,7 @@ build_src_filter =
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Sat, 16 May 2026 12:38:13 -0600
|
||||
Subject: [PATCH 05/14] Add hop-aware bot coordination, utility commands, and
|
||||
Subject: [PATCH 05/15] Add hop-aware bot coordination, utility commands, and
|
||||
bounded delays
|
||||
|
||||
Adds new utility commands (TIME, LORA, ID, NEIGHBORS), neighbor tracking,
|
||||
@@ -551,10 +551,10 @@ index de6dd92f..6aac8a1b 100644
|
||||
size_t actual = boundedStrLen(output, output_len);
|
||||
if (written) *written = actual;
|
||||
diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp
|
||||
index 7b87f14f..34517c44 100644
|
||||
index e84b3a52..27ff37c1 100644
|
||||
--- a/examples/companion_radio/MyMesh.cpp
|
||||
+++ b/examples/companion_radio/MyMesh.cpp
|
||||
@@ -394,31 +394,6 @@ static size_t botFormatTraceSent(char *output, size_t output_len, uint8_t hop_co
|
||||
@@ -395,31 +395,6 @@ static size_t botFormatTraceSent(char *output, size_t output_len, uint8_t hop_co
|
||||
return botBoundedStrLen(output, output_len);
|
||||
}
|
||||
|
||||
@@ -586,7 +586,7 @@ index 7b87f14f..34517c44 100644
|
||||
#endif
|
||||
|
||||
// these are _pushed_ to client app at any time
|
||||
@@ -663,6 +638,13 @@ void MyMesh::onContactsFull() {
|
||||
@@ -664,6 +639,13 @@ void MyMesh::onContactsFull() {
|
||||
void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) {
|
||||
#if CMESH_BOT_ENABLED
|
||||
autoLearnKnownBotAdvert(contact);
|
||||
@@ -600,7 +600,7 @@ index 7b87f14f..34517c44 100644
|
||||
#endif
|
||||
|
||||
if (_serial->isConnected()) {
|
||||
@@ -833,7 +815,7 @@ void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t
|
||||
@@ -842,7 +824,7 @@ void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t
|
||||
markConnectionActive(from); // in case this is from a server, and we have a connection
|
||||
queueMessage(from, TXT_TYPE_PLAIN, pkt, sender_timestamp, NULL, 0, text);
|
||||
#if CMESH_BOT_ENABLED
|
||||
@@ -609,7 +609,7 @@ index 7b87f14f..34517c44 100644
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -850,7 +832,7 @@ void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uin
|
||||
@@ -859,7 +841,7 @@ void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uin
|
||||
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
|
||||
queueMessage(from, TXT_TYPE_SIGNED_PLAIN, pkt, sender_timestamp, sender_prefix, 4, text);
|
||||
#if CMESH_BOT_ENABLED
|
||||
@@ -618,7 +618,7 @@ index 7b87f14f..34517c44 100644
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1073,7 +1055,7 @@ bool MyMesh::handleBotCLI(const char *args) {
|
||||
@@ -1082,7 +1064,7 @@ bool MyMesh::handleBotCLI(const char *args) {
|
||||
}
|
||||
|
||||
void MyMesh::observeBotDirectMessage(const ContactInfo &from, uint32_t sender_timestamp, const uint8_t *sender_prefix,
|
||||
@@ -627,7 +627,7 @@ index 7b87f14f..34517c44 100644
|
||||
BotMessage message;
|
||||
memset(&message, 0, sizeof(message));
|
||||
message.channel_kind = BotPolicy::classifyChannel(NULL, 0, true, bot_prefs);
|
||||
@@ -1084,7 +1066,13 @@ void MyMesh::observeBotDirectMessage(const ContactInfo &from, uint32_t sender_ti
|
||||
@@ -1093,7 +1075,13 @@ void MyMesh::observeBotDirectMessage(const ContactInfo &from, uint32_t sender_ti
|
||||
message.sender_key_prefix_len = prefix_len;
|
||||
message.sender_timestamp = sender_timestamp;
|
||||
message.received_at_timestamp = getRTCClock()->getCurrentTime();
|
||||
@@ -642,7 +642,7 @@ index 7b87f14f..34517c44 100644
|
||||
if (from.out_path_len != OUT_PATH_UNKNOWN && mesh::Packet::isValidPathLen(from.out_path_len)) {
|
||||
message.path_len = from.out_path_len;
|
||||
message.path_hash_size = (from.out_path_len >> 6) + 1;
|
||||
@@ -1270,6 +1258,220 @@ BotCommandResult MyMesh::executeBotPrefixCommand(const BotCommand &command, char
|
||||
@@ -1279,6 +1267,220 @@ BotCommandResult MyMesh::executeBotPrefixCommand(const BotCommand &command, char
|
||||
return botWriteFormatted(output, output_len, "Prefix %s %s", key_hex, name);
|
||||
}
|
||||
|
||||
@@ -863,7 +863,7 @@ index 7b87f14f..34517c44 100644
|
||||
bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx,
|
||||
const BotCommand &command) {
|
||||
uint8_t hash_size = command.args_len > 0 ? botConfiguredTraceHashSize(_prefs.path_hash_mode) : message.path_hash_size;
|
||||
@@ -1277,9 +1479,14 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
@@ -1286,9 +1488,14 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
uint8_t flags = 0;
|
||||
if (!botTraceFlagForHashSize(hash_size, &flags)) return false;
|
||||
|
||||
@@ -878,7 +878,7 @@ index 7b87f14f..34517c44 100644
|
||||
if (command.args_len > 0) {
|
||||
have_path = botParseTraceHexPath(command, flags, path, &path_len);
|
||||
} else if (message.path_hash_size == hash_size) {
|
||||
@@ -1292,13 +1499,20 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
@@ -1301,13 +1508,20 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
} else {
|
||||
botCopyReversedPath(path, message.path, message.path_hash_count, hash_size);
|
||||
}
|
||||
@@ -900,7 +900,7 @@ index 7b87f14f..34517c44 100644
|
||||
|
||||
BotFingerprint request_fingerprint = FirmwareBot::fingerprintFor(message);
|
||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
@@ -1316,14 +1530,15 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
@@ -1325,14 +1539,15 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
uint32_t now = _ms->getMillis();
|
||||
uint32_t bot_identity_seed;
|
||||
memcpy(&bot_identity_seed, self_id.pub_key, sizeof(bot_identity_seed));
|
||||
@@ -919,7 +919,7 @@ index 7b87f14f..34517c44 100644
|
||||
if (schedule == BOT_COORDINATOR_NO_SPACE || schedule == BOT_COORDINATOR_NOT_NORMAL) {
|
||||
bot_stats.send_failures++;
|
||||
return true;
|
||||
@@ -1372,6 +1587,7 @@ bool MyMesh::enqueueBotTrace(const BotMessage &message, const ContactInfo *direc
|
||||
@@ -1381,6 +1596,7 @@ bool MyMesh::enqueueBotTrace(const BotMessage &message, const ContactInfo *direc
|
||||
pending->channel_idx = channel_idx;
|
||||
pending->channel_kind = message.channel_kind;
|
||||
StrHelper::strzcpy(pending->channel_name, message.channel_name, sizeof(pending->channel_name));
|
||||
@@ -927,7 +927,7 @@ index 7b87f14f..34517c44 100644
|
||||
memcpy(pending->sender_key_prefix, message.sender_key_prefix, sizeof(pending->sender_key_prefix));
|
||||
pending->sender_key_prefix_len = message.sender_key_prefix_len;
|
||||
pending->request_fingerprint = request_fingerprint;
|
||||
@@ -1442,16 +1658,9 @@ bool MyMesh::sendPendingBotTrace(PendingBotTrace &pending, uint32_t now_millis)
|
||||
@@ -1451,16 +1667,9 @@ bool MyMesh::sendPendingBotTrace(PendingBotTrace &pending, uint32_t now_millis)
|
||||
sendDirect(pkt, pending.path, pending.path_len);
|
||||
pending.sent = true;
|
||||
pending.expires_at_millis = now_millis + BOT_TRACE_TIMEOUT_MILLIS;
|
||||
@@ -947,7 +947,7 @@ index 7b87f14f..34517c44 100644
|
||||
}
|
||||
|
||||
void MyMesh::expirePendingBotTraces(uint32_t now_millis) {
|
||||
@@ -1460,8 +1669,26 @@ void MyMesh::expirePendingBotTraces(uint32_t now_millis) {
|
||||
@@ -1469,8 +1678,26 @@ void MyMesh::expirePendingBotTraces(uint32_t now_millis) {
|
||||
if (!pending->active || !pending->sent || (int32_t)(now_millis - pending->expires_at_millis) < 0) continue;
|
||||
|
||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
@@ -976,7 +976,7 @@ index 7b87f14f..34517c44 100644
|
||||
char final_response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
size_t final_response_len = 0;
|
||||
if (botFormatResponseForChannelKind(pending->channel_kind, response, botBoundedStrLen(response, BOT_MAX_RESPONSE_LEN + 1),
|
||||
@@ -1579,27 +1806,46 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
@@ -1588,27 +1815,46 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
|
||||
BotCommandContext context;
|
||||
buildBotCommandContext(context, command.id);
|
||||
@@ -1036,7 +1036,7 @@ index 7b87f14f..34517c44 100644
|
||||
}
|
||||
}
|
||||
if (result.code == BOT_COMMAND_RESULT_NOT_HANDLED || result.code == BOT_COMMAND_RESULT_NO_SPACE || result.text_len == 0) {
|
||||
@@ -1620,14 +1866,16 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
@@ -1629,14 +1875,16 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
uint32_t due_at_millis = 0;
|
||||
uint32_t bot_identity_seed;
|
||||
memcpy(&bot_identity_seed, self_id.pub_key, sizeof(bot_identity_seed));
|
||||
@@ -1056,7 +1056,7 @@ index 7b87f14f..34517c44 100644
|
||||
if (schedule == BOT_COORDINATOR_NO_SPACE || schedule == BOT_COORDINATOR_NOT_NORMAL) {
|
||||
bot_stats.send_failures++;
|
||||
return;
|
||||
@@ -2102,8 +2350,11 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code,
|
||||
@@ -2111,8 +2359,11 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code,
|
||||
if (!pending->active || !pending->sent || pending->tag != tag || pending->auth_code != auth_code) continue;
|
||||
|
||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
@@ -1070,7 +1070,7 @@ index 7b87f14f..34517c44 100644
|
||||
char final_response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
size_t final_response_len = 0;
|
||||
if (botFormatResponseForChannelKind(pending->channel_kind, response, response_len, final_response,
|
||||
@@ -2149,6 +2400,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
@@ -2159,6 +2410,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
memset(pending_bot_traces, 0, sizeof(pending_bot_traces));
|
||||
memset(pending_emergency_forwards, 0, sizeof(pending_emergency_forwards));
|
||||
memset(bot_command_cooldowns, 0, sizeof(bot_command_cooldowns));
|
||||
@@ -1079,10 +1079,10 @@ index 7b87f14f..34517c44 100644
|
||||
ResponseCoordinator::clearRecent(bot_coordinator_recent, BOT_COORDINATOR_RECENT_SLOTS);
|
||||
KnownBotRegistry::clear(known_bot_entries, BOT_KNOWN_BOT_SLOTS);
|
||||
diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h
|
||||
index 67bdc010..9685e89a 100644
|
||||
index 1197caff..488987c0 100644
|
||||
--- a/examples/companion_radio/MyMesh.h
|
||||
+++ b/examples/companion_radio/MyMesh.h
|
||||
@@ -209,7 +209,7 @@ private:
|
||||
@@ -212,7 +212,7 @@ private:
|
||||
void printBotPrefs();
|
||||
bool handleBotCLI(const char *args);
|
||||
void observeBotDirectMessage(const ContactInfo &from, uint32_t sender_timestamp, const uint8_t *sender_prefix,
|
||||
@@ -1091,7 +1091,7 @@ index 67bdc010..9685e89a 100644
|
||||
void observeBotChannelMessage(uint8_t channel_idx, const char *channel_name, const char *text,
|
||||
uint32_t sender_timestamp, const mesh::Packet *packet);
|
||||
void recordBotObservation(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx);
|
||||
@@ -222,7 +222,14 @@ private:
|
||||
@@ -225,7 +225,14 @@ private:
|
||||
BotFingerprint response_fingerprint);
|
||||
bool handleBotTraceCommand(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx,
|
||||
const BotCommand &command);
|
||||
@@ -1106,7 +1106,7 @@ index 67bdc010..9685e89a 100644
|
||||
bool enqueueBotTrace(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx,
|
||||
const uint8_t *path, uint8_t path_len, uint8_t flags, BotFingerprint request_fingerprint,
|
||||
BotFingerprint response_fingerprint, uint32_t tag, uint32_t auth_code);
|
||||
@@ -298,6 +305,7 @@ private:
|
||||
@@ -302,6 +309,7 @@ private:
|
||||
uint8_t recipient_pub_key[PUB_KEY_SIZE];
|
||||
uint8_t channel_idx;
|
||||
char channel_name[BOT_MAX_CHANNEL_NAME_LEN + 1];
|
||||
@@ -1114,7 +1114,7 @@ index 67bdc010..9685e89a 100644
|
||||
uint8_t sender_key_prefix[BOT_SENDER_KEY_PREFIX_LEN];
|
||||
uint8_t sender_key_prefix_len;
|
||||
BotFingerprint request_fingerprint;
|
||||
@@ -319,6 +327,7 @@ private:
|
||||
@@ -323,6 +331,7 @@ private:
|
||||
BotCoordinatorPending bot_coordinator_pending[BOT_COORDINATOR_PENDING_SLOTS];
|
||||
BotCoordinatorRecent bot_coordinator_recent[BOT_COORDINATOR_RECENT_SLOTS];
|
||||
BotKnownBotEntry known_bot_entries[BOT_KNOWN_BOT_SLOTS];
|
||||
@@ -1246,11 +1246,11 @@ index 7717bee7..f549b887 100644
|
||||
bool cancel(BotCoordinatorPending pending[], size_t pending_count, BotFingerprint request_fingerprint);
|
||||
BotCoordinatorReady poll(BotCoordinatorPending pending[], size_t pending_count, uint32_t now_millis);
|
||||
diff --git a/src/Mesh.cpp b/src/Mesh.cpp
|
||||
index 57fee140..9c512b20 100644
|
||||
index 87ad61af..4a3cc567 100644
|
||||
--- a/src/Mesh.cpp
|
||||
+++ b/src/Mesh.cpp
|
||||
@@ -53,12 +53,22 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
|
||||
uint8_t offset = pkt->path_len << path_sz;
|
||||
@@ -55,12 +55,22 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
|
||||
uint16_t offset = (uint16_t)pkt->path_len << path_sz;
|
||||
if (offset >= len) { // TRACE has reached end of given path
|
||||
onTraceRecv(pkt, trace_tag, auth_code, flags, pkt->path, &pkt->payload[i], len);
|
||||
- } else if (self_id.isHashMatch(&pkt->payload[i + offset], 1 << path_sz) && allowPacketForward(pkt) && !_tables->hasSeen(pkt)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Sat, 16 May 2026 18:58:28 -0600
|
||||
Subject: [PATCH 06/14] Mark bot adverts, speed up initial advert, default
|
||||
Subject: [PATCH 06/15] Mark bot adverts, speed up initial advert, default
|
||||
contacts auto-overwrite
|
||||
|
||||
Three coordination/discoverability fixes:
|
||||
@@ -42,10 +42,10 @@ index 4fdd4bdd..d30fb7f2 100644
|
||||
#define BOT_PREFS_MAX_ADVERT_MILLIS (7UL * 24UL * 60UL * 60UL * 1000UL)
|
||||
#define BOT_PREFS_SERIALIZED_SIZE 296
|
||||
diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp
|
||||
index 34517c44..9209dd86 100644
|
||||
index 27ff37c1..b2c47df4 100644
|
||||
--- a/examples/companion_radio/MyMesh.cpp
|
||||
+++ b/examples/companion_radio/MyMesh.cpp
|
||||
@@ -848,7 +848,7 @@ void MyMesh::applyBotPrefs() {
|
||||
@@ -857,7 +857,7 @@ void MyMesh::applyBotPrefs() {
|
||||
}
|
||||
if (bot_prefs.enabled) {
|
||||
scheduleBotLocalAdvert(bot_prefs.local_advert_interval_ms ? BOT_PREFS_INITIAL_LOCAL_ADVERT_MILLIS : 0);
|
||||
@@ -54,7 +54,7 @@ index 34517c44..9209dd86 100644
|
||||
} else {
|
||||
scheduleBotLocalAdvert(0);
|
||||
scheduleBotFloodAdvert(0);
|
||||
@@ -1996,11 +1996,11 @@ void MyMesh::sendQueuedEmergencyForwards() {
|
||||
@@ -2005,11 +2005,11 @@ void MyMesh::sendQueuedEmergencyForwards() {
|
||||
bool MyMesh::sendBotSelfAdvert(bool flood) {
|
||||
mesh::Packet* pkt;
|
||||
char bot_advert_name[sizeof(((ContactInfo*)0)->name)];
|
||||
@@ -69,7 +69,7 @@ index 34517c44..9209dd86 100644
|
||||
}
|
||||
if (!pkt) return false;
|
||||
|
||||
@@ -2041,6 +2041,18 @@ void MyMesh::tickBot() {
|
||||
@@ -2050,6 +2050,18 @@ void MyMesh::tickBot() {
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -88,7 +88,7 @@ index 34517c44..9209dd86 100644
|
||||
void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp,
|
||||
const char *text) {
|
||||
int i = 0;
|
||||
@@ -2479,6 +2491,9 @@ void MyMesh::begin(bool has_display) {
|
||||
@@ -2489,6 +2501,9 @@ void MyMesh::begin(bool has_display) {
|
||||
_prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER);
|
||||
_prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1
|
||||
_prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours
|
||||
@@ -98,7 +98,7 @@ index 34517c44..9209dd86 100644
|
||||
|
||||
#ifdef BLE_PIN_CODE // 123456 by default
|
||||
if (_prefs.ble_pin == 0) {
|
||||
@@ -2780,10 +2795,12 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
@@ -2794,10 +2809,12 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
}
|
||||
} else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) {
|
||||
mesh::Packet* pkt;
|
||||
@@ -113,7 +113,7 @@ index 34517c44..9209dd86 100644
|
||||
}
|
||||
if (pkt) {
|
||||
if (len >= 2 && cmd_frame[1] == 1) { // optional param (1 = flood, 0 = zero hop)
|
||||
@@ -2864,10 +2881,12 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
@@ -2878,10 +2895,12 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
if (len < 1 + PUB_KEY_SIZE) {
|
||||
// export SELF
|
||||
mesh::Packet* pkt;
|
||||
@@ -128,7 +128,7 @@ index 34517c44..9209dd86 100644
|
||||
}
|
||||
if (pkt) {
|
||||
pkt->header |= ROUTE_TYPE_FLOOD; // would normally be sent in this mode
|
||||
@@ -3748,10 +3767,12 @@ void MyMesh::loop() {
|
||||
@@ -3804,10 +3823,12 @@ void MyMesh::loop() {
|
||||
|
||||
bool MyMesh::advert() {
|
||||
mesh::Packet* pkt;
|
||||
@@ -144,10 +144,10 @@ index 34517c44..9209dd86 100644
|
||||
if (pkt) {
|
||||
sendZeroHop(pkt);
|
||||
diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h
|
||||
index 9685e89a..711bb69f 100644
|
||||
index 488987c0..efc5240a 100644
|
||||
--- a/examples/companion_radio/MyMesh.h
|
||||
+++ b/examples/companion_radio/MyMesh.h
|
||||
@@ -248,6 +248,9 @@ private:
|
||||
@@ -251,6 +251,9 @@ private:
|
||||
bool sendBotSelfAdvert(bool flood);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Sat, 16 May 2026 19:33:04 -0600
|
||||
Subject: [PATCH 07/14] Prefix bot responses with request token for inter-bot
|
||||
Subject: [PATCH 07/15] Prefix bot responses with request token for inter-bot
|
||||
suppression
|
||||
|
||||
Adds a 4-hex request token (low 16 bits of the request fingerprint) as a
|
||||
@@ -131,10 +131,10 @@ index 9eff340b..6e5cfd9a 100644
|
||||
size_t maxResponseLenForChannel(BotChannelKind channel_kind);
|
||||
bool isCommandOnCooldown(const BotCommandCooldown* cooldowns, size_t cooldown_count, BotCommandId command_id,
|
||||
diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp
|
||||
index 9209dd86..02339124 100644
|
||||
index b2c47df4..d4bca6ce 100644
|
||||
--- a/examples/companion_radio/MyMesh.cpp
|
||||
+++ b/examples/companion_radio/MyMesh.cpp
|
||||
@@ -1209,6 +1209,11 @@ bool MyMesh::enqueueBotResponse(const BotMessage &message, const ContactInfo *di
|
||||
@@ -1218,6 +1218,11 @@ bool MyMesh::enqueueBotResponse(const BotMessage &message, const ContactInfo *di
|
||||
if (!botFormatResponseForChannel(message, text, text_len, pending->text, sizeof(pending->text), &pending->text_len)) {
|
||||
return false;
|
||||
}
|
||||
@@ -146,7 +146,7 @@ index 9209dd86..02339124 100644
|
||||
pending->active = true;
|
||||
return true;
|
||||
}
|
||||
@@ -1613,6 +1618,17 @@ BotFingerprint MyMesh::traceResponseFingerprintFor(const PendingBotTrace &pendin
|
||||
@@ -1622,6 +1627,17 @@ BotFingerprint MyMesh::traceResponseFingerprintFor(const PendingBotTrace &pendin
|
||||
|
||||
bool MyMesh::sendBotTraceText(const PendingBotTrace &pending, const char *text, size_t text_len,
|
||||
BotFingerprint response_fingerprint, uint32_t now_millis) {
|
||||
@@ -164,7 +164,7 @@ index 9209dd86..02339124 100644
|
||||
bool success = false;
|
||||
if (pending.direct) {
|
||||
ContactInfo *recipient = lookupContactByPubKey(pending.recipient_pub_key, PUB_KEY_SIZE);
|
||||
@@ -1620,12 +1636,7 @@ bool MyMesh::sendBotTraceText(const PendingBotTrace &pending, const char *text,
|
||||
@@ -1629,12 +1645,7 @@ bool MyMesh::sendBotTraceText(const PendingBotTrace &pending, const char *text,
|
||||
uint32_t expected_ack = 0;
|
||||
uint32_t est_timeout = 0;
|
||||
uint32_t timestamp = getRTCClock()->getCurrentTimeUnique();
|
||||
@@ -178,7 +178,7 @@ index 9209dd86..02339124 100644
|
||||
success = result != MSG_SEND_FAILED;
|
||||
if (success && expected_ack) {
|
||||
expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis();
|
||||
@@ -1638,7 +1649,7 @@ bool MyMesh::sendBotTraceText(const PendingBotTrace &pending, const char *text,
|
||||
@@ -1647,7 +1658,7 @@ bool MyMesh::sendBotTraceText(const PendingBotTrace &pending, const char *text,
|
||||
ChannelDetails channel;
|
||||
uint32_t timestamp = getRTCClock()->getCurrentTimeUnique();
|
||||
success = getChannel(pending.channel_idx, channel) &&
|
||||
@@ -187,7 +187,7 @@ index 9209dd86..02339124 100644
|
||||
}
|
||||
|
||||
if (success) {
|
||||
@@ -1744,6 +1755,19 @@ bool MyMesh::observeKnownBotResponse(const BotMessage &message, bool authoritati
|
||||
@@ -1753,6 +1764,19 @@ bool MyMesh::observeKnownBotResponse(const BotMessage &message, bool authoritati
|
||||
|
||||
bool MyMesh::observeBotGroupResponse(const BotMessage &message) {
|
||||
if (!BotPolicy::isPrefixlessCommandAllowed(message.channel_kind)) return false;
|
||||
@@ -207,7 +207,7 @@ index 9209dd86..02339124 100644
|
||||
BotFingerprint fingerprint = FirmwareBot::responseFingerprintFor(message, message.text, message.text_len);
|
||||
if (ResponseCoordinator::recentlySent(bot_coordinator_recent, BOT_COORDINATOR_RECENT_SLOTS, fingerprint, _ms->getMillis())) {
|
||||
return true;
|
||||
@@ -1751,7 +1775,7 @@ bool MyMesh::observeBotGroupResponse(const BotMessage &message) {
|
||||
@@ -1760,7 +1784,7 @@ bool MyMesh::observeBotGroupResponse(const BotMessage &message) {
|
||||
if (ResponseCoordinator::suppress(bot_coordinator_pending, BOT_COORDINATOR_PENDING_SLOTS, fingerprint)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Sat, 16 May 2026 19:55:36 -0600
|
||||
Subject: [PATCH 08/14] Drop [MCBOT] advert suffix and auto-learn known-bot
|
||||
Subject: [PATCH 08/15] Drop [MCBOT] advert suffix and auto-learn known-bot
|
||||
path
|
||||
|
||||
Removes BOT_ADVERT_MARKER / writeBotAdvertName / isBotAdvertName from
|
||||
@@ -93,10 +93,10 @@ index 6e5cfd9a..708432a2 100644
|
||||
BotFingerprint responseFingerprintFor(const BotMessage& message, const char* response_text, size_t response_text_len);
|
||||
uint16_t requestToken(BotFingerprint request_fingerprint);
|
||||
diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp
|
||||
index 02339124..9121d42c 100644
|
||||
index d4bca6ce..a8b4680b 100644
|
||||
--- a/examples/companion_radio/MyMesh.cpp
|
||||
+++ b/examples/companion_radio/MyMesh.cpp
|
||||
@@ -637,7 +637,6 @@ void MyMesh::onContactsFull() {
|
||||
@@ -638,7 +638,6 @@ void MyMesh::onContactsFull() {
|
||||
|
||||
void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) {
|
||||
#if CMESH_BOT_ENABLED
|
||||
@@ -104,7 +104,7 @@ index 02339124..9121d42c 100644
|
||||
if ((path_len & 63) == 0) {
|
||||
float rssi = radio_driver.getLastRSSI();
|
||||
if (rssi > 32767.0f) rssi = 32767.0f;
|
||||
@@ -1167,21 +1166,6 @@ void MyMesh::buildBotCommandContext(BotCommandContext &context, BotCommandId com
|
||||
@@ -1176,21 +1175,6 @@ void MyMesh::buildBotCommandContext(BotCommandContext &context, BotCommandId com
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ index 02339124..9121d42c 100644
|
||||
bool MyMesh::enqueueBotResponse(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx,
|
||||
const char *text, size_t text_len, BotFingerprint request_fingerprint,
|
||||
BotFingerprint response_fingerprint) {
|
||||
@@ -2019,12 +2003,10 @@ void MyMesh::sendQueuedEmergencyForwards() {
|
||||
@@ -2028,12 +2012,10 @@ void MyMesh::sendQueuedEmergencyForwards() {
|
||||
|
||||
bool MyMesh::sendBotSelfAdvert(bool flood) {
|
||||
mesh::Packet* pkt;
|
||||
@@ -141,7 +141,7 @@ index 02339124..9121d42c 100644
|
||||
}
|
||||
if (!pkt) return false;
|
||||
|
||||
@@ -2065,17 +2047,6 @@ void MyMesh::tickBot() {
|
||||
@@ -2074,17 +2056,6 @@ void MyMesh::tickBot() {
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -159,7 +159,7 @@ index 02339124..9121d42c 100644
|
||||
|
||||
void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp,
|
||||
const char *text) {
|
||||
@@ -2819,12 +2790,10 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
@@ -2833,12 +2804,10 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
}
|
||||
} else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) {
|
||||
mesh::Packet* pkt;
|
||||
@@ -174,7 +174,7 @@ index 02339124..9121d42c 100644
|
||||
}
|
||||
if (pkt) {
|
||||
if (len >= 2 && cmd_frame[1] == 1) { // optional param (1 = flood, 0 = zero hop)
|
||||
@@ -2905,12 +2874,10 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
@@ -2919,12 +2888,10 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
if (len < 1 + PUB_KEY_SIZE) {
|
||||
// export SELF
|
||||
mesh::Packet* pkt;
|
||||
@@ -189,7 +189,7 @@ index 02339124..9121d42c 100644
|
||||
}
|
||||
if (pkt) {
|
||||
pkt->header |= ROUTE_TYPE_FLOOD; // would normally be sent in this mode
|
||||
@@ -3791,12 +3758,10 @@ void MyMesh::loop() {
|
||||
@@ -3847,12 +3814,10 @@ void MyMesh::loop() {
|
||||
|
||||
bool MyMesh::advert() {
|
||||
mesh::Packet* pkt;
|
||||
@@ -205,10 +205,10 @@ index 02339124..9121d42c 100644
|
||||
if (pkt) {
|
||||
sendZeroHop(pkt);
|
||||
diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h
|
||||
index 711bb69f..46c3ff3c 100644
|
||||
index efc5240a..7aa56326 100644
|
||||
--- a/examples/companion_radio/MyMesh.h
|
||||
+++ b/examples/companion_radio/MyMesh.h
|
||||
@@ -216,7 +216,6 @@ private:
|
||||
@@ -219,7 +219,6 @@ private:
|
||||
bool observeKnownBotResponse(const BotMessage &message, bool authoritative_sender);
|
||||
bool observeBotGroupResponse(const BotMessage &message);
|
||||
void buildBotCommandContext(BotCommandContext &context, BotCommandId command_id);
|
||||
@@ -216,7 +216,7 @@ index 711bb69f..46c3ff3c 100644
|
||||
bool enqueueBotResponse(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx,
|
||||
const char *text, size_t text_len, BotFingerprint request_fingerprint,
|
||||
BotFingerprint response_fingerprint);
|
||||
@@ -248,9 +247,6 @@ private:
|
||||
@@ -251,9 +250,6 @@ private:
|
||||
bool sendBotSelfAdvert(bool flood);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Sat, 16 May 2026 20:05:19 -0600
|
||||
Subject: [PATCH 09/14] Widen hop-step + drop "ack " prefix from test response
|
||||
Subject: [PATCH 09/15] Widen hop-step + drop "ack " prefix from test response
|
||||
|
||||
Two changes:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Sat, 16 May 2026 20:31:12 -0600
|
||||
Subject: [PATCH 10/14] Widen coordinator delays a tiny bit more
|
||||
Subject: [PATCH 10/15] Widen coordinator delays a tiny bit more
|
||||
|
||||
~25% bump across the main coordination knobs:
|
||||
- BOT_RESPONSE_DELAY_BASE_MILLIS 1200 -> 1500
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Sat, 16 May 2026 20:49:51 -0600
|
||||
Subject: [PATCH 11/14] Auto-advert on startup (initial flood at 5s instead of
|
||||
Subject: [PATCH 11/15] Auto-advert on startup (initial flood at 5s instead of
|
||||
120s)
|
||||
|
||||
BOT_PREFS_INITIAL_FLOOD_ADVERT_MILLIS 120000 -> 5000. The bot now sends
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Sat, 16 May 2026 21:32:04 -0600
|
||||
Subject: [PATCH 12/14] Spell out hops/byte in test+path; drop channel name
|
||||
Subject: [PATCH 12/15] Spell out hops/byte in test+path; drop channel name
|
||||
from test ack
|
||||
|
||||
Test response (writeAckResponse):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Sat, 16 May 2026 22:18:02 -0600
|
||||
Subject: [PATCH 13/14] =?UTF-8?q?Hop=20delay=20grows=20quadratically=20?=
|
||||
Subject: [PATCH 13/15] =?UTF-8?q?Hop=20delay=20grows=20quadratically=20?=
|
||||
=?UTF-8?q?=E2=80=94=20wider=20gaps=20at=20higher=20hop=20counts?=
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: cj-vana <cj@depth23.online>
|
||||
Date: Mon, 18 May 2026 08:02:27 -0600
|
||||
Subject: [PATCH] Stabilize bot diagnostics and duplicate suppression
|
||||
Subject: [PATCH 14/15] Stabilize bot diagnostics and duplicate suppression
|
||||
|
||||
---
|
||||
examples/companion_radio/BotTypes.h | 9 ++-
|
||||
@@ -128,10 +128,10 @@ index afd44d41..d256e61a 100644
|
||||
size_t* new_len) {
|
||||
if (new_len) *new_len = text_len;
|
||||
diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp
|
||||
index 9121d42c..5d333328 100644
|
||||
index a8b4680b..5e00c9d2 100644
|
||||
--- a/examples/companion_radio/MyMesh.cpp
|
||||
+++ b/examples/companion_radio/MyMesh.cpp
|
||||
@@ -120,9 +120,6 @@
|
||||
@@ -121,9 +121,6 @@
|
||||
#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250
|
||||
#define LAZY_CONTACTS_WRITE_DELAY 5000
|
||||
|
||||
@@ -141,7 +141,7 @@ index 9121d42c..5d333328 100644
|
||||
#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg=="
|
||||
|
||||
#if CMESH_BOT_ENABLED
|
||||
@@ -1072,7 +1069,12 @@ void MyMesh::observeBotDirectMessage(const ContactInfo &from, uint32_t sender_ti
|
||||
@@ -1081,7 +1078,12 @@ void MyMesh::observeBotDirectMessage(const ContactInfo &from, uint32_t sender_ti
|
||||
if (rssi < -32768.0f) rssi = -32768.0f;
|
||||
recordBotNeighbor(from.id.pub_key, (int16_t)rssi, (int8_t)(packet->getSNR() * 4));
|
||||
}
|
||||
@@ -155,7 +155,7 @@ index 9121d42c..5d333328 100644
|
||||
message.path_len = from.out_path_len;
|
||||
message.path_hash_size = (from.out_path_len >> 6) + 1;
|
||||
message.path_hash_count = from.out_path_len & 63;
|
||||
@@ -1430,11 +1432,16 @@ bool MyMesh::dispatchBotTraceDirectLink(const BotMessage &message, const Contact
|
||||
@@ -1439,11 +1441,16 @@ bool MyMesh::dispatchBotTraceDirectLink(const BotMessage &message, const Contact
|
||||
}
|
||||
|
||||
BotFingerprint request_fingerprint = FirmwareBot::fingerprintFor(message);
|
||||
@@ -173,7 +173,7 @@ index 9121d42c..5d333328 100644
|
||||
uint32_t bot_identity_seed;
|
||||
memcpy(&bot_identity_seed, self_id.pub_key, sizeof(bot_identity_seed));
|
||||
uint32_t jitter_seed = (uint32_t)request_fingerprint.value ^ bot_identity_seed;
|
||||
@@ -1504,6 +1511,11 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
@@ -1513,6 +1520,11 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||
if (!have_path || !botTracePathShapeValid(path_len, flags)) return false;
|
||||
|
||||
BotFingerprint request_fingerprint = FirmwareBot::fingerprintFor(message);
|
||||
@@ -185,7 +185,7 @@ index 9121d42c..5d333328 100644
|
||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
size_t response_len = botFormatTraceSent(response, sizeof(response), (uint8_t)(path_len / hash_size));
|
||||
char final_response[BOT_MAX_RESPONSE_LEN + 1];
|
||||
@@ -1640,6 +1652,10 @@ bool MyMesh::sendBotTraceText(const PendingBotTrace &pending, const char *text,
|
||||
@@ -1649,6 +1661,10 @@ bool MyMesh::sendBotTraceText(const PendingBotTrace &pending, const char *text,
|
||||
bot_stats.sent_messages++;
|
||||
ResponseCoordinator::recordRecent(bot_coordinator_recent, BOT_COORDINATOR_RECENT_SLOTS, response_fingerprint,
|
||||
now_millis);
|
||||
@@ -196,7 +196,7 @@ index 9121d42c..5d333328 100644
|
||||
} else {
|
||||
bot_stats.send_failures++;
|
||||
}
|
||||
@@ -1747,6 +1763,7 @@ bool MyMesh::observeBotGroupResponse(const BotMessage &message) {
|
||||
@@ -1756,6 +1772,7 @@ bool MyMesh::observeBotGroupResponse(const BotMessage &message) {
|
||||
size_t token_prefix_len = 0;
|
||||
bool token_suppressed = false;
|
||||
if (FirmwareBot::parseRequestTokenPrefix(message.text, message.text_len, &token, &token_prefix_len)) {
|
||||
@@ -204,7 +204,7 @@ index 9121d42c..5d333328 100644
|
||||
if (ResponseCoordinator::suppressByRequestToken(bot_coordinator_pending, BOT_COORDINATOR_PENDING_SLOTS, token)) {
|
||||
token_suppressed = true;
|
||||
}
|
||||
@@ -1790,7 +1807,6 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
@@ -1799,7 +1816,6 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
if (message.text_len > 0 && (message.text[0] == '!' || message.text[0] == '/')) bot_stats.parse_errors++;
|
||||
return;
|
||||
}
|
||||
@@ -212,7 +212,7 @@ index 9121d42c..5d333328 100644
|
||||
if ((command.id != BOT_COMMAND_UNKNOWN && command.id != BOT_COMMAND_UNSUPPORTED &&
|
||||
!BotPrefsCodec::commandEnabled(bot_prefs, command.id)) ||
|
||||
FirmwareBot::isCommandOnCooldown(bot_command_cooldowns, BOT_COMMAND_COOLDOWN_SLOTS, command.id, _ms->getMillis())) {
|
||||
@@ -1869,6 +1885,11 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
@@ -1878,6 +1894,11 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
}
|
||||
|
||||
BotFingerprint request_fingerprint = FirmwareBot::fingerprintFor(message);
|
||||
@@ -224,7 +224,7 @@ index 9121d42c..5d333328 100644
|
||||
BotFingerprint response_fingerprint = FirmwareBot::responseFingerprintFor(message, final_response, final_response_len);
|
||||
BotFingerprint fingerprint;
|
||||
uint32_t due_at_millis = 0;
|
||||
@@ -1904,7 +1925,6 @@ void MyMesh::sendQueuedBotResponses() {
|
||||
@@ -1913,7 +1934,6 @@ void MyMesh::sendQueuedBotResponses() {
|
||||
while (true) {
|
||||
BotCoordinatorReady ready = ResponseCoordinator::poll(bot_coordinator_pending, BOT_COORDINATOR_PENDING_SLOTS, now);
|
||||
if (ready.result == BOT_COORDINATOR_READY_NONE) return;
|
||||
@@ -232,7 +232,7 @@ index 9121d42c..5d333328 100644
|
||||
if (ready.result == BOT_COORDINATOR_READY_SUPPRESSED) {
|
||||
bot_stats.suppressed_responses++;
|
||||
} else if (ready.result == BOT_COORDINATOR_READY_EXPIRED) {
|
||||
@@ -1941,6 +1961,12 @@ void MyMesh::sendQueuedBotResponses() {
|
||||
@@ -1950,6 +1970,12 @@ void MyMesh::sendQueuedBotResponses() {
|
||||
pending->active = false;
|
||||
continue;
|
||||
}
|
||||
@@ -245,7 +245,7 @@ index 9121d42c..5d333328 100644
|
||||
|
||||
bool success = false;
|
||||
if (pending->direct) {
|
||||
@@ -1960,15 +1986,19 @@ void MyMesh::sendQueuedBotResponses() {
|
||||
@@ -1969,15 +1995,19 @@ void MyMesh::sendQueuedBotResponses() {
|
||||
}
|
||||
} else if (pending->channel_idx != 0xFF) {
|
||||
ChannelDetails channel;
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: CJ Vana <cj@depth23.online>
|
||||
Date: Fri, 10 Jul 2026 15:19:21 -0600
|
||||
Subject: [PATCH 15/15] Add sig/air/coin commands; derive help listings from
|
||||
the registry
|
||||
|
||||
- sig (aliases snr/rssi/signal): reports the SNR the bot heard the
|
||||
request at, plus local last-RSSI and noise floor - range testing aid
|
||||
- air (alias airtime): TX/RX airtime and flood/direct packet counters
|
||||
- coin (aliases flip/coinflip): coin flip alongside roll/dice/magic8
|
||||
- help and cmd listings now walk the command registry filtered by
|
||||
context class (chat vs 'cmd diag') instead of hand-picked id arrays
|
||||
that had drifted (magic8 was missing from both)
|
||||
- BOT_PREFS_VERSION 6 -> 7 so deployed bots re-default command_mask
|
||||
and pick up the mask bits for the added commands
|
||||
---
|
||||
.../companion_radio/BotCommandRegistry.cpp | 12 +++
|
||||
examples/companion_radio/BotCommands.cpp | 81 ++++++++++++++-----
|
||||
examples/companion_radio/BotTypes.h | 15 +++-
|
||||
examples/companion_radio/MyMesh.cpp | 8 +-
|
||||
4 files changed, 89 insertions(+), 27 deletions(-)
|
||||
|
||||
diff --git a/examples/companion_radio/BotCommandRegistry.cpp b/examples/companion_radio/BotCommandRegistry.cpp
|
||||
index c429259c..65fed686 100644
|
||||
--- a/examples/companion_radio/BotCommandRegistry.cpp
|
||||
+++ b/examples/companion_radio/BotCommandRegistry.cpp
|
||||
@@ -14,6 +14,9 @@ const char* const kChannelsAliases[] = { "channel" };
|
||||
const char* const kPathAliases[] = { "p", "decode", "route" };
|
||||
const char* const kPrefixAliases[] = { "lookup" };
|
||||
const char* const kNeighborsAliases[] = { "near" };
|
||||
+const char* const kSigAliases[] = { "snr", "rssi", "signal" };
|
||||
+const char* const kAirAliases[] = { "airtime" };
|
||||
+const char* const kCoinAliases[] = { "flip", "coinflip" };
|
||||
|
||||
size_t boundedStrLen(const char* value, size_t max_len) {
|
||||
size_t len = 0;
|
||||
@@ -74,6 +77,15 @@ const BotCommandMetadata kCommands[] = {
|
||||
{ BOT_COMMAND_NEIGHBORS, "neighbors", kNeighborsAliases, 1, BOT_COMMAND_MASK_NEIGHBORS, BOT_COMMAND_VISIBILITY_DISCOVERABLE,
|
||||
BOT_COMMAND_CONTEXT_DIAGNOSTIC, "Show recent direct neighbors", "neighbors",
|
||||
"Show nodes heard directly within the last hour with RSSI and SNR." },
|
||||
+ { BOT_COMMAND_SIG, "sig", kSigAliases, 3, BOT_COMMAND_MASK_SIG, BOT_COMMAND_VISIBILITY_DISCOVERABLE,
|
||||
+ BOT_COMMAND_CONTEXT_DIAGNOSTIC, "Report received signal", "sig",
|
||||
+ "Show how the bot heard your request: SNR plus local RSSI and noise floor." },
|
||||
+ { BOT_COMMAND_AIR, "air", kAirAliases, 1, BOT_COMMAND_MASK_AIR, BOT_COMMAND_VISIBILITY_DISCOVERABLE,
|
||||
+ BOT_COMMAND_CONTEXT_DIAGNOSTIC, "Show radio airtime", "air",
|
||||
+ "Show local TX/RX airtime and flood/direct packet counters." },
|
||||
+ { BOT_COMMAND_COIN, "coin", kCoinAliases, 2, BOT_COMMAND_MASK_COIN, BOT_COMMAND_VISIBILITY_DISCOVERABLE,
|
||||
+ BOT_COMMAND_CONTEXT_NORMAL, "Flip a coin", "coin",
|
||||
+ "Flip a coin and report heads or tails." },
|
||||
{ BOT_COMMAND_UNKNOWN, "unknown", NULL, 0, 0, BOT_COMMAND_VISIBILITY_INTERNAL,
|
||||
BOT_COMMAND_CONTEXT_INTERNAL, "Unknown command", "unknown", "Internal unknown-command handler." }
|
||||
};
|
||||
diff --git a/examples/companion_radio/BotCommands.cpp b/examples/companion_radio/BotCommands.cpp
|
||||
index 198dc32f..4fbbbee0 100644
|
||||
--- a/examples/companion_radio/BotCommands.cpp
|
||||
+++ b/examples/companion_radio/BotCommands.cpp
|
||||
@@ -313,45 +313,55 @@ void formatQuarters(int8_t quarters, char* output, size_t output_len) {
|
||||
snprintf(output, output_len, "%s%d.%02d", sign, value / 4, (value % 4) * 25);
|
||||
}
|
||||
|
||||
-void appendCommandList(char* output, size_t output_len, size_t* pos, const BotCommandId* ids, size_t id_count) {
|
||||
+bool commandInClasses(const BotCommandMetadata* command, const BotCommandContextClass* classes, size_t class_count) {
|
||||
+ for (size_t i = 0; i < class_count; i++) {
|
||||
+ if (command->context_class == classes[i]) return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+// Listings are derived from the registry so an added command can never
|
||||
+// be missed; classes split the list because all commands do not fit one
|
||||
+// group-channel message.
|
||||
+void appendCommandsByClass(char* output, size_t output_len, size_t* pos, const BotCommandContextClass* classes,
|
||||
+ size_t class_count) {
|
||||
bool first = true;
|
||||
- for (size_t i = 0; i < id_count; i++) {
|
||||
- const BotCommandMetadata* command = BotCommandRegistry::findById(ids[i]);
|
||||
+ for (size_t i = 0; i < BotCommandRegistry::commandCount(); i++) {
|
||||
+ const BotCommandMetadata* command = BotCommandRegistry::commandAt(i);
|
||||
if (!command || command->visibility != BOT_COMMAND_VISIBILITY_DISCOVERABLE) continue;
|
||||
- if (!first) appendText(output, output_len, pos, ", ");
|
||||
+ if (!commandInClasses(command, classes, class_count)) continue;
|
||||
+ if (!first) appendText(output, output_len, pos, " ");
|
||||
appendText(output, output_len, pos, command->name);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
-BotCommandResult executeCmd(char* output, size_t output_len) {
|
||||
- static const BotCommandId ids[] = {
|
||||
- BOT_COMMAND_TEST, BOT_COMMAND_PING, BOT_COMMAND_HELP, BOT_COMMAND_HELLO, BOT_COMMAND_ABOUT,
|
||||
- BOT_COMMAND_ROLL, BOT_COMMAND_DICE, BOT_COMMAND_STATUS, BOT_COMMAND_CHANNELS, BOT_COMMAND_VERSION,
|
||||
- BOT_COMMAND_STATS, BOT_COMMAND_PATH, BOT_COMMAND_TRACE, BOT_COMMAND_TRACER, BOT_COMMAND_PREFIX,
|
||||
- BOT_COMMAND_TIME, BOT_COMMAND_LORA, BOT_COMMAND_ID, BOT_COMMAND_NEIGHBORS
|
||||
- };
|
||||
+const BotCommandContextClass kChatClasses[] = { BOT_COMMAND_CONTEXT_NORMAL, BOT_COMMAND_CONTEXT_TRACE };
|
||||
+const BotCommandContextClass kDiagClasses[] = { BOT_COMMAND_CONTEXT_DIAGNOSTIC, BOT_COMMAND_CONTEXT_LOCAL_CONTACT };
|
||||
+
|
||||
+BotCommandResult executeCmd(const BotCommand& command, char* output, size_t output_len) {
|
||||
if (!output || output_len == 0) return makeResult(BOT_COMMAND_RESULT_NO_SPACE, 0);
|
||||
output[0] = 0;
|
||||
size_t pos = 0;
|
||||
+ if (textEqualsIgnoreCase(command.args, command.args_len, "diag")) {
|
||||
+ appendText(output, output_len, &pos, "Diag: ");
|
||||
+ appendCommandsByClass(output, output_len, &pos, kDiagClasses, 2);
|
||||
+ return resultForAppend(output, output_len, pos);
|
||||
+ }
|
||||
appendText(output, output_len, &pos, "Commands: ");
|
||||
- appendCommandList(output, output_len, &pos, ids, sizeof(ids) / sizeof(ids[0]));
|
||||
+ appendCommandsByClass(output, output_len, &pos, kChatClasses, 2);
|
||||
+ appendText(output, output_len, &pos, " | cmd diag");
|
||||
return resultForAppend(output, output_len, pos);
|
||||
}
|
||||
|
||||
BotCommandResult executeHelp(const BotCommand& command, char* output, size_t output_len) {
|
||||
if (command.args_len == 0) {
|
||||
- static const BotCommandId ids[] = {
|
||||
- BOT_COMMAND_HELP, BOT_COMMAND_CMD, BOT_COMMAND_PING, BOT_COMMAND_TEST, BOT_COMMAND_HELLO, BOT_COMMAND_ROLL,
|
||||
- BOT_COMMAND_DICE, BOT_COMMAND_TRACE, BOT_COMMAND_TRACER, BOT_COMMAND_PREFIX, BOT_COMMAND_TIME,
|
||||
- BOT_COMMAND_LORA, BOT_COMMAND_ID, BOT_COMMAND_NEIGHBORS
|
||||
- };
|
||||
if (!output || output_len == 0) return makeResult(BOT_COMMAND_RESULT_NO_SPACE, 0);
|
||||
output[0] = 0;
|
||||
size_t pos = 0;
|
||||
appendText(output, output_len, &pos, "Commands: ");
|
||||
- appendCommandList(output, output_len, &pos, ids, sizeof(ids) / sizeof(ids[0]));
|
||||
- appendText(output, output_len, &pos, "; help <command>");
|
||||
+ appendCommandsByClass(output, output_len, &pos, kChatClasses, 2);
|
||||
+ appendText(output, output_len, &pos, " | cmd diag | help <command>");
|
||||
return resultForAppend(output, output_len, pos);
|
||||
}
|
||||
|
||||
@@ -531,6 +541,31 @@ uint8_t batteryPercentFromMillivolts(uint16_t mv) {
|
||||
return (uint8_t)(((uint32_t)(mv - kEmpty) * 100UL) / (uint32_t)(kFull - kEmpty));
|
||||
}
|
||||
|
||||
+BotCommandResult executeSig(const BotCommandContext& context, char* output, size_t output_len) {
|
||||
+ char snr[8];
|
||||
+ formatQuarters(context.path_snr_quarters, snr, sizeof(snr));
|
||||
+ const char* target = context.response_target[0] ? context.response_target : NULL;
|
||||
+ if (target) {
|
||||
+ return writeFormatted(output, output_len, "Sig @[%s]: heard you at SNR %s | last RSSI %d dBm, noise %d dBm", target,
|
||||
+ snr, (int)context.last_rssi, (int)context.noise_floor);
|
||||
+ }
|
||||
+ return writeFormatted(output, output_len, "Sig: heard you at SNR %s | last RSSI %d dBm, noise %d dBm", snr,
|
||||
+ (int)context.last_rssi, (int)context.noise_floor);
|
||||
+}
|
||||
+
|
||||
+BotCommandResult executeAir(const BotCommandContext& context, char* output, size_t output_len) {
|
||||
+ return writeFormatted(output, output_len, "Air: tx %lus rx %lus | rx flood %lu direct %lu | tx flood %lu direct %lu",
|
||||
+ (unsigned long)context.tx_airtime_seconds, (unsigned long)context.rx_airtime_seconds,
|
||||
+ (unsigned long)context.flood_recv, (unsigned long)context.direct_recv,
|
||||
+ (unsigned long)context.flood_sent, (unsigned long)context.direct_sent);
|
||||
+}
|
||||
+
|
||||
+BotCommandResult executeCoin(const BotCommandContext& context, char* output, size_t output_len) {
|
||||
+ uint32_t state = context.random_seed ^ 0x636F696EUL;
|
||||
+ if (state == 0) state = 1;
|
||||
+ return writeFormatted(output, output_len, "Coin: %s", rollOnce(&state, 2) == 1 ? "Heads" : "Tails");
|
||||
+}
|
||||
+
|
||||
BotCommandResult executeTest(const BotCommand& command, const BotCommandContext& context, char* output, size_t output_len) {
|
||||
const char* name = context.node_name[0] ? context.node_name : "local";
|
||||
char received_at[9];
|
||||
@@ -591,7 +626,7 @@ BotCommandResult executeCommand(const BotCommand& command, const BotCommandConte
|
||||
case BOT_COMMAND_HELP:
|
||||
return executeHelp(command, output, output_len);
|
||||
case BOT_COMMAND_CMD:
|
||||
- return executeCmd(output, output_len);
|
||||
+ return executeCmd(command, output, output_len);
|
||||
case BOT_COMMAND_PING:
|
||||
return writeText(output, output_len, "Pong!");
|
||||
case BOT_COMMAND_TEST:
|
||||
@@ -642,6 +677,12 @@ BotCommandResult executeCommand(const BotCommand& command, const BotCommandConte
|
||||
(unsigned long)context.packets_recv_errors, (unsigned)context.queue_depth);
|
||||
case BOT_COMMAND_MAGIC8:
|
||||
return executeMagic8(context, output, output_len);
|
||||
+ case BOT_COMMAND_SIG:
|
||||
+ return executeSig(context, output, output_len);
|
||||
+ case BOT_COMMAND_AIR:
|
||||
+ return executeAir(context, output, output_len);
|
||||
+ case BOT_COMMAND_COIN:
|
||||
+ return executeCoin(context, output, output_len);
|
||||
case BOT_COMMAND_PATH:
|
||||
return executePath(command, context, output, output_len);
|
||||
case BOT_COMMAND_TRACE:
|
||||
diff --git a/examples/companion_radio/BotTypes.h b/examples/companion_radio/BotTypes.h
|
||||
index a54949d9..b91b291d 100644
|
||||
--- a/examples/companion_radio/BotTypes.h
|
||||
+++ b/examples/companion_radio/BotTypes.h
|
||||
@@ -39,7 +39,7 @@
|
||||
#define BOT_MIN_AUTH_SENDER_KEY_PREFIX_LEN 4
|
||||
#define BOT_KNOWN_BOT_LABEL_LEN 12
|
||||
#define BOT_PREFS_MAGIC 0x31504642UL
|
||||
-#define BOT_PREFS_VERSION 6
|
||||
+#define BOT_PREFS_VERSION 7
|
||||
#define BOT_PREFS_DEFAULT_LOCAL_ADVERT_MILLIS (24UL * 60UL * 60UL * 1000UL)
|
||||
#define BOT_PREFS_DEFAULT_FLOOD_ADVERT_MILLIS (24UL * 60UL * 60UL * 1000UL)
|
||||
#define BOT_PREFS_INITIAL_LOCAL_ADVERT_MILLIS 60000UL
|
||||
@@ -90,8 +90,11 @@ enum BotCommandId : uint8_t {
|
||||
BOT_COMMAND_LORA = 19,
|
||||
BOT_COMMAND_ID = 20,
|
||||
BOT_COMMAND_NEIGHBORS = 21,
|
||||
- BOT_COMMAND_UNSUPPORTED = 22,
|
||||
- BOT_COMMAND_UNKNOWN = 23
|
||||
+ BOT_COMMAND_SIG = 22,
|
||||
+ BOT_COMMAND_AIR = 23,
|
||||
+ BOT_COMMAND_COIN = 24,
|
||||
+ BOT_COMMAND_UNSUPPORTED = 25,
|
||||
+ BOT_COMMAND_UNKNOWN = 26
|
||||
};
|
||||
|
||||
enum BotCommandVisibility : uint8_t {
|
||||
@@ -143,13 +146,17 @@ struct BotCommandMetadata {
|
||||
#define BOT_COMMAND_MASK_LORA (1UL << BOT_COMMAND_LORA)
|
||||
#define BOT_COMMAND_MASK_ID (1UL << BOT_COMMAND_ID)
|
||||
#define BOT_COMMAND_MASK_NEIGHBORS (1UL << BOT_COMMAND_NEIGHBORS)
|
||||
+#define BOT_COMMAND_MASK_SIG (1UL << BOT_COMMAND_SIG)
|
||||
+#define BOT_COMMAND_MASK_AIR (1UL << BOT_COMMAND_AIR)
|
||||
+#define BOT_COMMAND_MASK_COIN (1UL << BOT_COMMAND_COIN)
|
||||
#define BOT_COMMAND_MASK_ALL (BOT_COMMAND_MASK_HELP | BOT_COMMAND_MASK_CMD | BOT_COMMAND_MASK_PING | \
|
||||
BOT_COMMAND_MASK_TEST | BOT_COMMAND_MASK_HELLO | BOT_COMMAND_MASK_ABOUT | \
|
||||
BOT_COMMAND_MASK_ROLL | BOT_COMMAND_MASK_DICE | BOT_COMMAND_MASK_STATUS | \
|
||||
BOT_COMMAND_MASK_CHANNELS | BOT_COMMAND_MASK_VERSION | BOT_COMMAND_MASK_STATS | \
|
||||
BOT_COMMAND_MASK_MAGIC8 | BOT_COMMAND_MASK_PATH | BOT_COMMAND_MASK_TRACE | \
|
||||
BOT_COMMAND_MASK_TRACER | BOT_COMMAND_MASK_PREFIX | BOT_COMMAND_MASK_TIME | \
|
||||
- BOT_COMMAND_MASK_LORA | BOT_COMMAND_MASK_ID | BOT_COMMAND_MASK_NEIGHBORS)
|
||||
+ BOT_COMMAND_MASK_LORA | BOT_COMMAND_MASK_ID | BOT_COMMAND_MASK_NEIGHBORS | \
|
||||
+ BOT_COMMAND_MASK_SIG | BOT_COMMAND_MASK_AIR | BOT_COMMAND_MASK_COIN)
|
||||
|
||||
enum BotCommandResultCode : uint8_t {
|
||||
BOT_COMMAND_RESULT_NOT_HANDLED = 0,
|
||||
diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp
|
||||
index 5e00c9d2..fc37823c 100644
|
||||
--- a/examples/companion_radio/MyMesh.cpp
|
||||
+++ b/examples/companion_radio/MyMesh.cpp
|
||||
@@ -1142,7 +1142,8 @@ void MyMesh::buildBotCommandContext(BotCommandContext &context, BotCommandId com
|
||||
context.pending_responses = bot_stats.pending_responses;
|
||||
context.emergency_forwards = bot_stats.emergency_forwards;
|
||||
context.emergency_forward_failures = bot_stats.emergency_forward_failures;
|
||||
- if (command_id == BOT_COMMAND_ROLL || command_id == BOT_COMMAND_DICE || command_id == BOT_COMMAND_MAGIC8) {
|
||||
+ if (command_id == BOT_COMMAND_ROLL || command_id == BOT_COMMAND_DICE || command_id == BOT_COMMAND_MAGIC8 ||
|
||||
+ command_id == BOT_COMMAND_COIN) {
|
||||
getRNG()->random((uint8_t *)&context.random_seed, sizeof(context.random_seed));
|
||||
}
|
||||
if (command_id == BOT_COMMAND_STATUS || command_id == BOT_COMMAND_STATS) {
|
||||
@@ -1160,7 +1161,7 @@ void MyMesh::buildBotCommandContext(BotCommandContext &context, BotCommandId com
|
||||
StrHelper::strzcpy(context.firmware_version, FIRMWARE_VERSION, sizeof(context.firmware_version));
|
||||
StrHelper::strzcpy(context.firmware_build_date, FIRMWARE_BUILD_DATE, sizeof(context.firmware_build_date));
|
||||
}
|
||||
- if (command_id == BOT_COMMAND_STATS) {
|
||||
+ if (command_id == BOT_COMMAND_STATS || command_id == BOT_COMMAND_SIG || command_id == BOT_COMMAND_AIR) {
|
||||
context.queue_depth = (uint8_t)_mgr->getOutboundTotal();
|
||||
context.noise_floor = (int16_t)_radio->getNoiseFloor();
|
||||
context.last_rssi = (int8_t)radio_driver.getLastRSSI();
|
||||
@@ -1842,7 +1843,8 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||
if (message.sender_name[0]) {
|
||||
StrHelper::strzcpy(context.response_target, message.sender_name, sizeof(context.response_target));
|
||||
}
|
||||
- if (command.id == BOT_COMMAND_PATH || command.id == BOT_COMMAND_TRACE || command.id == BOT_COMMAND_TRACER) {
|
||||
+ if (command.id == BOT_COMMAND_PATH || command.id == BOT_COMMAND_TRACE || command.id == BOT_COMMAND_TRACER ||
|
||||
+ command.id == BOT_COMMAND_SIG) {
|
||||
context.path_len = message.path_len;
|
||||
context.path_hash_size = command.args_len > 0 ? botConfiguredTraceHashSize(_prefs.path_hash_mode) : message.path_hash_size;
|
||||
context.path_hash_count = message.path_hash_count;
|
||||
Reference in New Issue
Block a user