From 24750e5c8a39f8ca5c066457c2d437aa9133e4b8 Mon Sep 17 00:00:00 2001 From: Matt Swann <3lemenopy@gmail.com> Date: Sat, 1 Nov 2025 13:55:07 -0700 Subject: [PATCH 1/4] Add support for channel flags Add a "no store" flag for channels --- examples/companion_radio/DataStore.cpp | 15 +++++++++------ examples/companion_radio/DataStore.h | 10 ++++++++++ examples/companion_radio/MyMesh.cpp | 7 ++++++- src/Mesh.h | 6 ++++++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index eac027b89..6db730383 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -324,9 +324,11 @@ void DataStore::loadChannels(DataStoreHost* host) { uint8_t channel_idx = 0; while (!full) { ChannelDetails ch; - uint8_t unused[4]; + ChannelHeader header; + + bool success = (file.read((uint8_t*)&header, sizeof(ChannelHeader)) == sizeof(ChannelHeader)); + ch.channel.flags.noStore = header.flags.noStore; - bool success = (file.read(unused, 4) == 4); success = success && (file.read((uint8_t *)ch.name, 32) == 32); success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32); @@ -347,11 +349,12 @@ void DataStore::saveChannels(DataStoreHost* host) { if (file) { uint8_t channel_idx = 0; ChannelDetails ch; - uint8_t unused[4]; - memset(unused, 0, 4); - + while (host->getChannelForSave(channel_idx, ch)) { - bool success = (file.write(unused, 4) == 4); + ChannelHeader header{}; + header.flags.noStore = ch.channel.flags.noStore; + + bool success = (file.write((uint8_t *)&header, sizeof(ChannelHeader)) == sizeof(ChannelHeader)); success = success && (file.write((uint8_t *)ch.name, 32) == 32); success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32); diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h index 625809429..e7233eaba 100644 --- a/examples/companion_radio/DataStore.h +++ b/examples/companion_radio/DataStore.h @@ -5,6 +5,16 @@ #include #include "NodePrefs.h" +struct ChannelFlags { + bool noStore : 1; + uint8_t reserved : 7; // remaining 7 bits unused +}; + +struct ChannelHeader { + ChannelFlags flags; + uint8_t unused[3]; +}; + class DataStoreHost { public: virtual bool onContactLoaded(const ContactInfo& contact) =0; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 02f1a21de..483b97834 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -423,7 +423,12 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe } memcpy(&out_frame[i], text, tlen); i += tlen; - addToOfflineQueue(out_frame, i); + + if (channel.flags.noStore && !_serial->isConnected()) { + MESH_DEBUG_PRINTLN("INFO: not storing message for noStore channel"); + } else { + addToOfflineQueue(out_frame, i); + } if (_serial->isConnected()) { uint8_t frame[1]; diff --git a/src/Mesh.h b/src/Mesh.h index cbf1c9cf0..57fb93dd5 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -4,10 +4,16 @@ namespace mesh { +struct ChannelFlags { + bool noStore : 1; + uint8_t reserved : 7; // remaining 7 bits unused +}; + class GroupChannel { public: uint8_t hash[PATH_HASH_SIZE]; uint8_t secret[PUB_KEY_SIZE]; + ChannelFlags flags; }; /** From b302f8a8965b13ac57ae07ba3e178c5e27675371 Mon Sep 17 00:00:00 2001 From: Matt Swann <3lemenopy@gmail.com> Date: Sat, 1 Nov 2025 15:55:47 -0700 Subject: [PATCH 2/4] Code review feedback --- examples/companion_radio/DataStore.cpp | 4 ++-- examples/companion_radio/DataStore.h | 8 ++------ src/helpers/ChannelDetails.h | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 6db730383..a404a7084 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -327,7 +327,7 @@ void DataStore::loadChannels(DataStoreHost* host) { ChannelHeader header; bool success = (file.read((uint8_t*)&header, sizeof(ChannelHeader)) == sizeof(ChannelHeader)); - ch.channel.flags.noStore = header.flags.noStore; + ch.channel.flags = header.flags; success = success && (file.read((uint8_t *)ch.name, 32) == 32); success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32); @@ -352,7 +352,7 @@ void DataStore::saveChannels(DataStoreHost* host) { while (host->getChannelForSave(channel_idx, ch)) { ChannelHeader header{}; - header.flags.noStore = ch.channel.flags.noStore; + header.flags = ch.channel.flags; bool success = (file.write((uint8_t *)&header, sizeof(ChannelHeader)) == sizeof(ChannelHeader)); success = success && (file.write((uint8_t *)ch.name, 32) == 32); diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h index e7233eaba..4fb1524d8 100644 --- a/examples/companion_radio/DataStore.h +++ b/examples/companion_radio/DataStore.h @@ -5,15 +5,11 @@ #include #include "NodePrefs.h" -struct ChannelFlags { - bool noStore : 1; - uint8_t reserved : 7; // remaining 7 bits unused -}; - struct ChannelHeader { - ChannelFlags flags; + mesh::ChannelFlags flags; uint8_t unused[3]; }; +static_assert(sizeof(ChannelHeader) == 4, "ChannelHeader must be 4 bytes"); class DataStoreHost { public: diff --git a/src/helpers/ChannelDetails.h b/src/helpers/ChannelDetails.h index b9d38d4fc..b64812feb 100644 --- a/src/helpers/ChannelDetails.h +++ b/src/helpers/ChannelDetails.h @@ -6,4 +6,4 @@ struct ChannelDetails { mesh::GroupChannel channel; char name[32]; -}; +}; \ No newline at end of file From 63587ae149343bcc0af78cdbb773c14f87fc5be4 Mon Sep 17 00:00:00 2001 From: Matt Swann <3lemenopy@gmail.com> Date: Sun, 9 Nov 2025 10:06:52 -0800 Subject: [PATCH 3/4] Code review feedback --- examples/companion_radio/DataStore.cpp | 4 ++-- examples/companion_radio/DataStore.h | 2 +- examples/companion_radio/MyMesh.cpp | 7 ++++--- src/Mesh.h | 6 ------ src/helpers/ChannelDetails.h | 6 ++++++ 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index a404a7084..62e24febc 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -327,7 +327,7 @@ void DataStore::loadChannels(DataStoreHost* host) { ChannelHeader header; bool success = (file.read((uint8_t*)&header, sizeof(ChannelHeader)) == sizeof(ChannelHeader)); - ch.channel.flags = header.flags; + ch.flags.noStore = header.flags.noStore; success = success && (file.read((uint8_t *)ch.name, 32) == 32); success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32); @@ -352,7 +352,7 @@ void DataStore::saveChannels(DataStoreHost* host) { while (host->getChannelForSave(channel_idx, ch)) { ChannelHeader header{}; - header.flags = ch.channel.flags; + header.flags.noStore = ch.flags.noStore; bool success = (file.write((uint8_t *)&header, sizeof(ChannelHeader)) == sizeof(ChannelHeader)); success = success && (file.write((uint8_t *)ch.name, 32) == 32); diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h index 4fb1524d8..e90110b0f 100644 --- a/examples/companion_radio/DataStore.h +++ b/examples/companion_radio/DataStore.h @@ -6,7 +6,7 @@ #include "NodePrefs.h" struct ChannelHeader { - mesh::ChannelFlags flags; + ChannelFlags flags; uint8_t unused[3]; }; static_assert(sizeof(ChannelHeader) == 4, "ChannelHeader must be 4 bytes"); diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index fc71e390d..89cacdf11 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -456,7 +456,9 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe memcpy(&out_frame[i], text, tlen); i += tlen; - if (channel.flags.noStore && !_serial->isConnected()) { + ChannelDetails channel_details; + bool foundDetails = getChannel(channel_idx, channel_details); + if (foundDetails && channel_details.flags.noStore && !_serial->isConnected()) { MESH_DEBUG_PRINTLN("INFO: not storing message for noStore channel"); } else { addToOfflineQueue(out_frame, i); @@ -474,8 +476,7 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe #ifdef DISPLAY_CLASS // Get the channel name from the channel index const char *channel_name = "Unknown"; - ChannelDetails channel_details; - if (getChannel(channel_idx, channel_details)) { + if (foundDetails) { channel_name = channel_details.name; } if (_ui) _ui->newMsg(path_len, channel_name, text, offline_queue_len); diff --git a/src/Mesh.h b/src/Mesh.h index b1077df4e..00f7ed00f 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -4,16 +4,10 @@ namespace mesh { -struct ChannelFlags { - bool noStore : 1; - uint8_t reserved : 7; // remaining 7 bits unused -}; - class GroupChannel { public: uint8_t hash[PATH_HASH_SIZE]; uint8_t secret[PUB_KEY_SIZE]; - ChannelFlags flags; }; /** diff --git a/src/helpers/ChannelDetails.h b/src/helpers/ChannelDetails.h index b64812feb..1933cc181 100644 --- a/src/helpers/ChannelDetails.h +++ b/src/helpers/ChannelDetails.h @@ -3,7 +3,13 @@ #include #include +struct ChannelFlags { + bool noStore : 1; + uint8_t reserved : 7; // remaining 7 bits unused +}; + struct ChannelDetails { mesh::GroupChannel channel; char name[32]; + ChannelFlags flags; }; \ No newline at end of file From d4d0db42ed43ed14a6aa5e0777afbec07b77d2d3 Mon Sep 17 00:00:00 2001 From: Matt Swann <3lemenopy@gmail.com> Date: Sat, 15 Nov 2025 11:39:02 -0800 Subject: [PATCH 4/4] Add get and set commands for the nostore channel flag --- examples/companion_radio/MyMesh.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index ebf4a7785..c148a38e6 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -52,6 +52,8 @@ #define CMD_SEND_PATH_DISCOVERY_REQ 52 #define CMD_SET_FLOOD_SCOPE 54 // v8+ #define CMD_SEND_CONTROL_DATA 55 // v8+ +#define CMD_GET_CHANNEL_FLAG_NOSTORE 56 +#define CMD_SET_CHANNEL_FLAG_NOSTORE 57 #define RESP_CODE_OK 0 #define RESP_CODE_ERR 1 @@ -77,6 +79,7 @@ #define RESP_CODE_CUSTOM_VARS 21 #define RESP_CODE_ADVERT_PATH 22 #define RESP_CODE_TUNING_PARAMS 23 +#define RESP_CODE_CHANNEL_FLAG_NOSTORE 24 // a reply to CMD_GET_CHANNEL_FLAG_NOSTORE #define SEND_TIMEOUT_BASE_MILLIS 500 #define FLOOD_SEND_TIMEOUT_FACTOR 16.0f @@ -1559,6 +1562,30 @@ void MyMesh::handleCmdFrame(size_t len) { } else { writeErrFrame(ERR_CODE_TABLE_FULL); } + } else if (cmd_frame[0] == CMD_GET_CHANNEL_FLAG_NOSTORE && len >= 2) { + uint8_t channel_idx = cmd_frame[1]; + ChannelDetails channel; + if (getChannel(channel_idx, channel)) { + out_frame[0] = RESP_CODE_CHANNEL_FLAG_NOSTORE; + out_frame[1] = static_cast(channel.flags.noStore); + _serial->writeFrame(out_frame, 2); + } else { + writeErrFrame(ERR_CODE_NOT_FOUND); + } + } else if (cmd_frame[0] == CMD_SET_CHANNEL_FLAG_NOSTORE && len >= 3) { + uint8_t channel_idx = cmd_frame[1]; + ChannelDetails channel; + if (getChannel(channel_idx, channel)) { + channel.flags.noStore = static_cast(cmd_frame[2]); + if (setChannel(channel_idx, channel)) { + saveChannels(); + writeOKFrame(); + } else { + writeErrFrame(ERR_CODE_NOT_FOUND); + } + } else { + writeErrFrame(ERR_CODE_NOT_FOUND); + } } else { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]);