Эх сурвалжийг харах

Merge branch 'ripplebiz:main' into faketec-support

oltaco 1 жил өмнө
parent
commit
c8104563a0

+ 103 - 24
examples/companion_radio/main.cpp

@@ -46,6 +46,10 @@
   #define OFFLINE_QUEUE_SIZE  16
 #endif
 
+#ifndef BLE_NAME_PREFIX
+  #define BLE_NAME_PREFIX  "MeshCore-"
+#endif
+
 #include <helpers/BaseChatMesh.h>
 
 #define SEND_TIMEOUT_BASE_MILLIS          500
@@ -124,11 +128,11 @@ static uint32_t _atoi(const char* sp) {
 #define FIRMWARE_VER_CODE    2
 
 #ifndef FIRMWARE_BUILD_DATE
-  #define FIRMWARE_BUILD_DATE   "9 Mar 2025"
+  #define FIRMWARE_BUILD_DATE   "13 Mar 2025"
 #endif
 
 #ifndef FIRMWARE_VERSION
-  #define FIRMWARE_VERSION   "v1.2.2"
+  #define FIRMWARE_VERSION   "v1.3.0"
 #endif
 
 #define CMD_APP_START              1
@@ -161,6 +165,8 @@ static uint32_t _atoi(const char* sp) {
 #define CMD_HAS_CONNECTION        28
 #define CMD_LOGOUT                29   // 'Disconnect'
 #define CMD_GET_CONTACT_BY_KEY    30
+#define CMD_GET_CHANNEL           31
+#define CMD_SET_CHANNEL           32
 
 #define RESP_CODE_OK                0
 #define RESP_CODE_ERR               1
@@ -178,6 +184,8 @@ static uint32_t _atoi(const char* sp) {
 #define RESP_CODE_DEVICE_INFO      13   // a reply to CMD_DEVICE_QEURY
 #define RESP_CODE_PRIVATE_KEY      14   // a reply to CMD_EXPORT_PRIVATE_KEY
 #define RESP_CODE_DISABLED         15
+//  ... _V3 stuff in here
+#define RESP_CODE_CHANNEL_INFO     18   // a reply to CMD_GET_CHANNEL
 
 // these are _pushed_ to client app at any time
 #define PUSH_CODE_ADVERT            0x80
@@ -216,7 +224,6 @@ class MyMesh : public BaseChatMesh {
   uint32_t expected_ack_crc;  // TODO: keep table of expected ACKs
   uint32_t pending_login;
   uint32_t pending_status;
-  mesh::GroupChannel* _public;
   BaseSerialInterface* _serial;
   unsigned long last_msg_sent;
   ContactsIterator _iter;
@@ -311,6 +318,58 @@ class MyMesh : public BaseChatMesh {
     }
   }
 
+  void loadChannels() {
+    if (_fs->exists("/channels2")) {
+      File file = _fs->open("/channels2");
+      if (file) {
+        bool full = false;
+        uint8_t channel_idx = 0;
+        while (!full) {
+          ChannelDetails ch;
+          uint8_t unused[4];
+
+          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);
+
+          if (!success) break;  // EOF
+
+          if (setChannel(channel_idx, ch)) {
+            channel_idx++;
+          } else {
+            full = true;
+          }
+        }
+        file.close();
+      }
+    }
+  }
+
+  void saveChannels() {
+  #if defined(NRF52_PLATFORM)
+    File file = _fs->open("/channels2", FILE_O_WRITE);
+    if (file) { file.seek(0); file.truncate(); }
+  #else
+    File file = _fs->open("/channels2", "w", true);
+  #endif
+    if (file) {
+      uint8_t channel_idx = 0;
+      ChannelDetails ch;
+      uint8_t unused[4];
+      memset(unused, 0, 4);
+    
+      while (getChannel(channel_idx, ch)) {
+        bool success = (file.write(unused, 4) == 4);
+        success = success && (file.write((uint8_t *) ch.name, 32) == 32);
+        success = success && (file.write((uint8_t *) ch.channel.secret, 32) == 32);
+    
+        if (!success) break;  // write failed
+        channel_idx++;
+      }
+      file.close();
+    }
+  }
+    
   int  getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override {
     char path[64];
     char fname[18];
@@ -536,7 +595,7 @@ protected:
   void onChannelMessageRecv(const mesh::GroupChannel& channel, int in_path_len, uint32_t timestamp, const char *text) override {
     int i = 0;
     out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV;
-    out_frame[i++] = 0;  // FUTURE: channel_idx (will just be 'public' for now)
+    out_frame[i++] = findChannelIdx(channel);
     out_frame[i++] = in_path_len < 0 ? 0xFF : in_path_len;
     out_frame[i++] = TXT_TYPE_PLAIN;
     memcpy(&out_frame[i], &timestamp, 4); i += 4;
@@ -713,7 +772,8 @@ public:
     _fs->mkdir("/bl");
 
     loadContacts();
-    _public = addChannel(PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
+    addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
+    loadChannels();
 
     _phy->setFrequency(_prefs.freq);
     _phy->setSpreadingFactor(_prefs.sf);
@@ -769,7 +829,9 @@ public:
       int i = 0;
       out_frame[i++] = RESP_CODE_DEVICE_INFO;
       out_frame[i++] = FIRMWARE_VER_CODE;
-      memset(&out_frame[i], 0, 6); i += 6;  // reserved
+      out_frame[i++] = MAX_CONTACTS / 2;        // v3+
+      out_frame[i++] = MAX_GROUP_CHANNELS;      // v3+
+      memset(&out_frame[i], 0, 4); i += 4;  // reserved
       memset(&out_frame[i], 0, 12);
       strcpy((char *) &out_frame[i], FIRMWARE_BUILD_DATE); i += 12;
       StrHelper::strzcpy((char *) &out_frame[i], board.getManufacturerName(), 40); i += 40;
@@ -844,12 +906,14 @@ public:
     } else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) {  // send GroupChannel msg
       int i = 1;
       uint8_t txt_type = cmd_frame[i++];  // should be TXT_TYPE_PLAIN
-      uint8_t channel_idx = cmd_frame[i++];   // reserved future
+      uint8_t channel_idx = cmd_frame[i++];
       uint32_t msg_timestamp;
       memcpy(&msg_timestamp, &cmd_frame[i], 4); i += 4;
       const char *text = (char *) &cmd_frame[i];
 
-      if (txt_type == TXT_TYPE_PLAIN && sendGroupMessage(msg_timestamp, *_public, _prefs.node_name, text, len - i)) {   // hard-coded to 'public' channel for now
+      ChannelDetails channel;
+      bool success = getChannel(channel_idx, channel);
+      if (success && txt_type == TXT_TYPE_PLAIN && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) {
         writeOKFrame();
       } else {
         writeErrFrame();
@@ -1161,6 +1225,33 @@ public:
       uint8_t* pub_key = &cmd_frame[1];
       stopConnection(pub_key);
       writeOKFrame();
+    } else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) {
+      uint8_t channel_idx = cmd_frame[1];
+      ChannelDetails channel;
+      if (getChannel(channel_idx, channel)) {
+        int i = 0;
+        out_frame[i++] = RESP_CODE_CHANNEL_INFO;
+        out_frame[i++] = channel_idx;
+        strcpy((char *)&out_frame[i], channel.name); i += 32;
+        memcpy(&out_frame[i], channel.channel.secret, 16); i += 16;   // NOTE: only 128-bit supported
+        _serial->writeFrame(out_frame, i);
+      } else {
+        writeErrFrame();
+      }
+    } else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2+32+32) {
+      writeErrFrame();  // not supported (yet)
+    } else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2+32+16) {
+      uint8_t channel_idx = cmd_frame[1];
+      ChannelDetails channel;
+      StrHelper::strncpy(channel.name, (char *) &cmd_frame[2], 32);
+      memset(channel.channel.secret, 0, sizeof(channel.channel.secret));
+      memcpy(channel.channel.secret, &cmd_frame[2+32], 16);   // NOTE: only 128-bit supported
+      if (setChannel(channel_idx, channel)) {
+        saveChannels();
+        writeOKFrame();
+      } else {
+        writeErrFrame();
+      }
     } else {
       writeErrFrame();
       MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]);
@@ -1297,14 +1388,8 @@ void setup() {
   the_mesh.begin(InternalFS, trng);
 
 #ifdef BLE_PIN_CODE
-  char dev_name[32+10];
-  const char* prefix = 
-  #ifdef BLE_NAME_PREFIX
-      BLE_NAME_PREFIX;
-  #else
-      "MeshCore-";
-  #endif
-  sprintf(dev_name, "%s%s", prefix, the_mesh.getNodeName());
+  char dev_name[32+16];
+  sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName());
   serial_interface.begin(dev_name, the_mesh.getBLEPin());
 #else
 #ifdef RAK_4631
@@ -1321,14 +1406,8 @@ void setup() {
   WiFi.begin(WIFI_SSID, WIFI_PWD);
   serial_interface.begin(TCP_PORT);
 #elif defined(BLE_PIN_CODE)
-  char dev_name[32+10];
-  const char* prefix = 
-  #ifdef BLE_NAME_PREFIX
-      BLE_NAME_PREFIX;
-  #else
-      "MeshCore-";
-  #endif
-  sprintf(dev_name, "%s%s", prefix, the_mesh.getNodeName());
+  char dev_name[32+16];
+  sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName());
   serial_interface.begin(dev_name, the_mesh.getBLEPin());
 #else
   serial_interface.begin(Serial);

+ 6 - 3
examples/simple_repeater/main.cpp

@@ -22,11 +22,11 @@
 /* ------------------------------ Config -------------------------------- */
 
 #ifndef FIRMWARE_BUILD_DATE
-  #define FIRMWARE_BUILD_DATE   "9 Mar 2025"
+  #define FIRMWARE_BUILD_DATE   "13 Mar 2025"
 #endif
 
 #ifndef FIRMWARE_VERSION
-  #define FIRMWARE_VERSION   "v1.2.2"
+  #define FIRMWARE_VERSION   "v1.3.0"
 #endif
 
 #ifndef LORA_FREQ
@@ -239,7 +239,9 @@ protected:
   }
 
   bool allowPacketForward(const mesh::Packet* packet) override {
-    return !_prefs.disable_fwd;
+    if (_prefs.disable_fwd) return false;
+    if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false;
+    return true;
   }
 
   const char* getLogDateTime() override {
@@ -540,6 +542,7 @@ public:
     _prefs.cr = LORA_CR;
     _prefs.tx_power_dbm = LORA_TX_POWER;
     _prefs.advert_interval = 1;  // default to 2 minutes for NEW installs
+    _prefs.flood_max = 64;
   }
 
   CommonCLI* getCLI() { return &_cli; }

+ 6 - 3
examples/simple_room_server/main.cpp

@@ -22,11 +22,11 @@
 /* ------------------------------ Config -------------------------------- */
 
 #ifndef FIRMWARE_BUILD_DATE
-  #define FIRMWARE_BUILD_DATE   "9 Mar 2025"
+  #define FIRMWARE_BUILD_DATE   "13 Mar 2025"
 #endif
 
 #ifndef FIRMWARE_VERSION
-  #define FIRMWARE_VERSION   "v1.2.2"
+  #define FIRMWARE_VERSION   "v1.3.0"
 #endif
 
 #ifndef LORA_FREQ
@@ -308,7 +308,9 @@ protected:
   }
 
   bool allowPacketForward(const mesh::Packet* packet) override {
-    return !_prefs.disable_fwd;
+    if (_prefs.disable_fwd) return false;
+    if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false;
+    return true;
   }
 
   void onAnonDataRecv(mesh::Packet* packet, uint8_t type, const mesh::Identity& sender, uint8_t* data, size_t len) override {
@@ -572,6 +574,7 @@ public:
     _prefs.tx_power_dbm = LORA_TX_POWER;
     _prefs.disable_fwd = 1;
     _prefs.advert_interval = 1;  // default to 2 minutes for NEW installs
+    _prefs.flood_max = 64;
   #ifdef ROOM_PASSWORD
     StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
   #endif

+ 3 - 3
examples/simple_secure_chat/main.cpp

@@ -111,7 +111,7 @@ class MyMesh : public BaseChatMesh, ContactVisitor {
   FILESYSTEM* _fs;
   NodePrefs _prefs;
   uint32_t expected_ack_crc;
-  mesh::GroupChannel* _public;
+  ChannelDetails* _public;
   unsigned long last_msg_sent;
   ContactInfo* curr_recipient;
   char command[512+10];
@@ -341,7 +341,7 @@ public:
     }
 
     loadContacts();
-    _public = addChannel(PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
+    _public = addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
   }
 
   void savePrefs() {
@@ -409,7 +409,7 @@ public:
       temp[5 + MAX_TEXT_LEN] = 0;  // truncate if too long
 
       int len = strlen((char *) &temp[5]);
-      auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, *_public, temp, 5 + len);
+      auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, _public->channel, temp, 5 + len);
       if (pkt) {
         sendFlood(pkt);
         Serial.println("   Sent.");

+ 75 - 67
platformio.ini

@@ -52,10 +52,10 @@ build_src_filter = ${esp32_base.build_src_filter}
 extends = Heltec_lora32_v2
 build_flags =
   ${Heltec_lora32_v2.build_flags}
-  -D ADVERT_NAME="\"Heltec Repeater\""
+  -D ADVERT_NAME='"Heltec Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 build_src_filter = ${Heltec_lora32_v2.build_src_filter}
@@ -80,7 +80,7 @@ extends = Heltec_lora32_v2
 build_flags =
   ${Heltec_lora32_v2.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
 ; NOTE: DO NOT ENABLE -->  -D MESH_PACKET_LOGGING=1
 ; NOTE: DO NOT ENABLE -->  -D MESH_DEBUG=1
 build_src_filter = ${Heltec_lora32_v2.build_src_filter}
@@ -95,7 +95,7 @@ extends = Heltec_lora32_v2
 build_flags =
   ${Heltec_lora32_v2.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456
   -D BLE_DEBUG_LOGGING=1
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
@@ -109,7 +109,6 @@ lib_deps =
   ${Heltec_lora32_v2.lib_deps}
   densaugeo/base64 @ ~1.4.0
 
-
 ; ================
 [Heltec_lora32_v3]
 extends = esp32_base
@@ -127,7 +126,7 @@ build_flags =
   -D SX126X_DIO2_AS_RF_SWITCH=true
   -D SX126X_DIO3_TCXO_VOLTAGE=1.8
   -D SX126X_CURRENT_LIMIT=130.0f  ; for best TX power!
-  -D SX126X_RX_BOOSTED_GAIN=true
+  -D SX126X_RX_BOOSTED_GAIN=1
 build_src_filter = ${esp32_base.build_src_filter}
 lib_deps =
   ${esp32_base.lib_deps}
@@ -138,10 +137,10 @@ extends = Heltec_lora32_v3
 build_flags =
   ${Heltec_lora32_v3.build_flags}
   -D DISPLAY_CLASS=SSD1306Display
-  -D ADVERT_NAME="\"Heltec Repeater\""
+  -D ADVERT_NAME='"Heltec Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
   -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 build_src_filter = ${Heltec_lora32_v3.build_src_filter}
@@ -156,12 +155,12 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter}
 build_flags =
   ${Heltec_lora32_v3.build_flags}
   -D DISPLAY_CLASS=SSD1306Display
-  -D ADVERT_NAME="\"Heltec Room\""
+  -D ADVERT_NAME='"Heltec Room"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
-  -D ROOM_PASSWORD="\"hello\""
-  -D MESH_PACKET_LOGGING=1
+  -D ADMIN_PASSWORD='"password"'
+  -D ROOM_PASSWORD='"hello"'
+;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
 [env:Heltec_v3_terminal_chat]
@@ -183,7 +182,7 @@ extends = Heltec_lora32_v3
 build_flags =
   ${Heltec_lora32_v3.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D DISPLAY_CLASS=SSD1306Display
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
 ;  -D ENABLE_PRIVATE_KEY_EXPORT=1
@@ -201,7 +200,7 @@ extends = Heltec_lora32_v3
 build_flags =
   ${Heltec_lora32_v3.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D DISPLAY_CLASS=SSD1306Display
   -D BLE_PIN_CODE=0   ; dynamic, random PIN
   -D BLE_DEBUG_LOGGING=1
@@ -222,11 +221,11 @@ extends = Heltec_lora32_v3
 build_flags =
   ${Heltec_lora32_v3.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D DISPLAY_CLASS=SSD1306Display
   -D WIFI_DEBUG_LOGGING=1
-  -D WIFI_SSID="\"myssid\""
-  -D WIFI_PWD="\"mypwd\""
+  -D WIFI_SSID='"myssid"'
+  -D WIFI_PWD='"mypwd"'
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
 ;  -D ENABLE_PRIVATE_KEY_EXPORT=1
 ;  -D MESH_PACKET_LOGGING=1
@@ -243,10 +242,10 @@ lib_deps =
 extends = Heltec_lora32_v3
 build_flags =
   ${Heltec_lora32_v3.build_flags}
-  -D ADVERT_NAME="\"Heltec Repeater\""
+  -D ADVERT_NAME='"Heltec Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
   -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 build_src_filter = ${Heltec_lora32_v3.build_src_filter}
@@ -258,11 +257,11 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter}
   +<../examples/simple_room_server>
 build_flags =
   ${Heltec_lora32_v3.build_flags}
-  -D ADVERT_NAME="\"Heltec Room\""
+  -D ADVERT_NAME='"Heltec Room"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
-  -D ROOM_PASSWORD="\"hello\""
+  -D ADMIN_PASSWORD='"password"'
+  -D ROOM_PASSWORD='"hello"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -271,7 +270,7 @@ extends = Heltec_lora32_v3
 build_flags =
   ${Heltec_lora32_v3.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456
   -D BLE_DEBUG_LOGGING=1
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
@@ -312,11 +311,12 @@ build_flags =
   ${Xiao_esp32_C3.build_flags}
   -D RADIO_CLASS=CustomSX1262
   -D WRAPPER_CLASS=CustomSX1262Wrapper
+  -D SX126X_RX_BOOSTED_GAIN=1
   -D LORA_TX_POWER=22
-  -D ADVERT_NAME="\"Xiao Repeater\""
+  -D ADVERT_NAME='"Xiao Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -329,10 +329,10 @@ build_flags =
   -D RADIO_CLASS=CustomSX1268
   -D WRAPPER_CLASS=CustomSX1268Wrapper
   -D LORA_TX_POWER=22
-  -D ADVERT_NAME="\"Xiao Repeater\""
+  -D ADVERT_NAME='"Xiao Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
  ; -D MESH_PACKET_LOGGING=1
  ; -D MESH_DEBUG=1
 
@@ -357,6 +357,7 @@ build_flags = ${esp32_base.build_flags}
   -D RADIO_CLASS=CustomSX1262
   -D WRAPPER_CLASS=CustomSX1262Wrapper
   -D LORA_TX_POWER=22
+  -D SX126X_RX_BOOSTED_GAIN=1
 
 [env:Xiao_S3_WIO_Repeater]
 extends = Xiao_S3_WIO
@@ -364,10 +365,10 @@ build_src_filter = ${Xiao_S3_WIO.build_src_filter}
   +<../examples/simple_repeater/main.cpp>
 build_flags =
   ${Xiao_S3_WIO.build_flags}
-  -D ADVERT_NAME="\"XiaoS3 Repeater\""
+  -D ADVERT_NAME='"XiaoS3 Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -376,7 +377,7 @@ extends = Xiao_S3_WIO
 build_flags =
   ${Xiao_S3_WIO.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 build_src_filter = ${Xiao_S3_WIO.build_src_filter}
@@ -390,7 +391,7 @@ extends = Xiao_S3_WIO
 build_flags =
   ${Xiao_S3_WIO.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456
 ;  -D BLE_DEBUG_LOGGING=1
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
@@ -442,10 +443,10 @@ build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
   +<../examples/simple_repeater>
 build_flags =
   ${LilyGo_TLora_V2_1_1_6.build_flags}
-  -D ADVERT_NAME="\"TLora-V2.1-1.6 Repeater\""
+  -D ADVERT_NAME='"TLora-V2.1-1.6 Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 ;  -D CORE_DEBUG_LEVEL=3
@@ -455,7 +456,7 @@ extends = LilyGo_TLora_V2_1_1_6
 build_flags =
   ${LilyGo_TLora_V2_1_1_6.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
@@ -470,7 +471,7 @@ extends = LilyGo_TLora_V2_1_1_6
 build_flags =
   ${LilyGo_TLora_V2_1_1_6.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
 ;  -D ENABLE_PRIVATE_KEY_EXPORT=1
 ; NOTE: DO NOT ENABLE -->  -D MESH_PACKET_LOGGING=1
@@ -487,7 +488,7 @@ extends = LilyGo_TLora_V2_1_1_6
 build_flags =
   ${LilyGo_TLora_V2_1_1_6.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456
 ;  -D BLE_DEBUG_LOGGING=1
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
@@ -509,11 +510,11 @@ build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
   +<../examples/simple_room_server>
 build_flags =
   ${LilyGo_TLora_V2_1_1_6.build_flags}
-  -D ADVERT_NAME="\"TLora-V2.1-1.6 Room\""
+  -D ADVERT_NAME='"TLora-V2.1-1.6 Room"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
-  -D ROOM_PASSWORD="\"hello\""
+  -D ADMIN_PASSWORD='"password"'
+  -D ROOM_PASSWORD='"hello"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -538,6 +539,7 @@ build_flags = ${esp32_base.build_flags}
   -D RADIO_CLASS=CustomSX1262
   -D WRAPPER_CLASS=CustomSX1262Wrapper
   -D LORA_TX_POWER=22
+  -D SX126X_RX_BOOSTED_GAIN=1
 
 ; === LilyGo T3S3 with SX1262 environments ===
 [env:LilyGo_T3S3_sx1262_Repeater]
@@ -546,10 +548,10 @@ build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
   +<../examples/simple_repeater/main.cpp>
 build_flags =
   ${LilyGo_T3S3_sx1262.build_flags}
-  -D ADVERT_NAME="\"T3S3-1262 Repeater\""
+  -D ADVERT_NAME='"T3S3-1262 Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -572,7 +574,7 @@ extends = LilyGo_T3S3_sx1262
 build_flags =
   ${LilyGo_T3S3_sx1262.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
 ;  -D ENABLE_PRIVATE_KEY_EXPORT=1
 ; NOTE: DO NOT ENABLE -->  -D MESH_PACKET_LOGGING=1
@@ -588,7 +590,7 @@ extends = LilyGo_T3S3_sx1262
 build_flags =
   ${LilyGo_T3S3_sx1262.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456
   -D BLE_DEBUG_LOGGING=1
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
@@ -618,6 +620,9 @@ build_flags =
   -D SX126X_DIO2_AS_RF_SWITCH=true
   -D SX126X_DIO3_TCXO_VOLTAGE=1.8
   -D SX126X_CURRENT_LIMIT=130.0f  ; for best TX power!
+;  -D SX126X_RX_BOOSTED_GAIN=1 - DO NOT ENABLE THIS!
+;  https://wiki.uniteng.com/en/meshtastic/station-g2#impact-of-lora-node-dense-areashigh-noise-environments-on-rf-performance
+
 build_src_filter = ${esp32_base.build_src_filter}
 lib_deps =
   ${esp32_base.lib_deps}
@@ -626,10 +631,10 @@ lib_deps =
 extends = Station_G2
 build_flags =
   ${Station_G2.build_flags}
-  -D ADVERT_NAME="\"Station G2 Repeater\""
+  -D ADVERT_NAME='"Station G2 Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
   -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 build_src_filter = ${Station_G2.build_src_filter}
@@ -641,11 +646,11 @@ build_src_filter = ${Station_G2.build_src_filter}
   +<../examples/simple_room_server>
 build_flags =
   ${Station_G2.build_flags}
-  -D ADVERT_NAME="\"Station G2 Room\""
+  -D ADVERT_NAME='"Station G2 Room"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
-  -D ROOM_PASSWORD="\"hello\""
+  -D ADMIN_PASSWORD='"password"'
+  -D ROOM_PASSWORD='"hello"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -677,6 +682,7 @@ build_flags = ${nrf52840_base.build_flags}
   -D WRAPPER_CLASS=CustomSX1262Wrapper
   -D LORA_TX_POWER=22
   -D SX126X_CURRENT_LIMIT=130
+  -D SX126X_RX_BOOSTED_GAIN=1
 
 [env:RAK_4631_Repeater]
 extends = rak4631
@@ -684,10 +690,10 @@ build_src_filter = ${rak4631.build_src_filter}
   +<../examples/simple_repeater/main.cpp>
 build_flags =
   ${rak4631.build_flags}
-  -D ADVERT_NAME="\"RAK4631 Repeater\""
+  -D ADVERT_NAME='"RAK4631 Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -697,11 +703,11 @@ build_src_filter = ${rak4631.build_src_filter}
   +<../examples/simple_room_server/main.cpp>
 build_flags =
   ${rak4631.build_flags}
-  -D ADVERT_NAME="\"Test Room\""
+  -D ADVERT_NAME='"Test Room"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
-  -D ROOM_PASSWORD="\"hello\""
+  -D ADMIN_PASSWORD='"password"'
+  -D ROOM_PASSWORD='"hello"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -724,7 +730,7 @@ extends = rak4631
 build_flags =
   ${rak4631.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
 ;  -D ENABLE_PRIVATE_KEY_EXPORT=1
 ; NOTE: DO NOT ENABLE -->  -D MESH_PACKET_LOGGING=1
@@ -740,7 +746,7 @@ extends = rak4631
 build_flags =
   ${rak4631.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456
   -D BLE_DEBUG_LOGGING=1
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
@@ -792,7 +798,7 @@ upload_protocol = nrfutil
 extends = t1000-e
 build_flags = ${t1000-e.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456
   -D BLE_DEBUG_LOGGING=1
   -D MESH_PACKET_LOGGING=1
@@ -826,6 +832,7 @@ build_flags = ${nrf52840_t114.build_flags}
   -D WRAPPER_CLASS=CustomSX1262Wrapper
   -D LORA_TX_POWER=22
   -D SX126X_CURRENT_LIMIT=130
+  -D SX126X_RX_BOOSTED_GAIN=1
 build_src_filter = ${nrf52840_t114.build_src_filter}
   +<helpers/*.cpp>
   +<helpers/nrf52/T114Board.cpp>
@@ -839,10 +846,10 @@ build_src_filter = ${Heltec_t114.build_src_filter}
   +<../examples/simple_repeater/main.cpp>
 build_flags =
   ${Heltec_t114.build_flags}
-  -D ADVERT_NAME="\"Heltec_T114 Repeater\""
+  -D ADVERT_NAME='"Heltec_T114 Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -852,11 +859,11 @@ build_src_filter = ${Heltec_t114.build_src_filter}
   +<../examples/simple_room_server>
 build_flags =
   ${Heltec_t114.build_flags}
-  -D ADVERT_NAME="\"Heltec_T114 Room\""
+  -D ADVERT_NAME='"Heltec_T114 Room"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
-  -D ROOM_PASSWORD="\"hello\""
+  -D ADMIN_PASSWORD='"password"'
+  -D ROOM_PASSWORD='"hello"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -865,7 +872,7 @@ extends = Heltec_t114
 build_flags =
   ${Heltec_t114.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456
   -D BLE_DEBUG_LOGGING=1
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1
@@ -901,6 +908,7 @@ build_flags = ${nrf52840_techo.build_flags}
   -D WRAPPER_CLASS=CustomSX1262Wrapper
   -D LORA_TX_POWER=22
   -D SX126X_CURRENT_LIMIT=130
+  -D SX126X_RX_BOOSTED_GAIN=1
 build_src_filter = ${nrf52840_techo.build_src_filter}
   +<helpers/*.cpp>
   +<helpers/nrf52/TechoBoard.cpp>
@@ -913,10 +921,10 @@ extends = LilyGo_Techo
 build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_repeater/main.cpp>
 build_flags =
   ${LilyGo_Techo.build_flags}
-  -D ADVERT_NAME="\"T-Echo Repeater\""
+  -D ADVERT_NAME='"T-Echo Repeater"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -925,10 +933,10 @@ extends = LilyGo_Techo
 build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_room_server/main.cpp>
 build_flags =
   ${LilyGo_Techo.build_flags}
-  -D ADVERT_NAME="\"T-Echo Room\""
+  -D ADVERT_NAME='"T-Echo Room"'
   -D ADVERT_LAT=-37.0
   -D ADVERT_LON=145.0
-  -D ADMIN_PASSWORD="\"password\""
+  -D ADMIN_PASSWORD='"password"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
@@ -937,7 +945,7 @@ extends = LilyGo_Techo
 build_flags =
   ${LilyGo_Techo.build_flags}
   -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=1
+  -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456
   -D BLE_DEBUG_LOGGING=1
 ;  -D ENABLE_PRIVATE_KEY_IMPORT=1

+ 45 - 8
src/helpers/BaseChatMesh.cpp

@@ -203,9 +203,9 @@ void BaseChatMesh::onAckRecv(mesh::Packet* packet, uint32_t ack_crc) {
 #ifdef MAX_GROUP_CHANNELS
 int BaseChatMesh::searchChannelsByHash(const uint8_t* hash, mesh::GroupChannel dest[], int max_matches) {
   int n = 0;
-  for (int i = 0; i < num_channels && n < max_matches; i++) {
-    if (channels[i].hash[0] == hash[0]) {
-      dest[n++] = channels[i];
+  for (int i = 0; i < MAX_GROUP_CHANNELS && n < max_matches; i++) {
+    if (channels[i].channel.hash[0] == hash[0]) {
+      dest[n++] = channels[i].channel;
     }
   }
   return n;
@@ -588,24 +588,61 @@ bool BaseChatMesh::removeContact(ContactInfo& contact) {
 #ifdef MAX_GROUP_CHANNELS
 #include <base64.hpp>
 
-mesh::GroupChannel* BaseChatMesh::addChannel(const char* psk_base64) {
+ChannelDetails* BaseChatMesh::addChannel(const char* name, const char* psk_base64) {
   if (num_channels < MAX_GROUP_CHANNELS) {
     auto dest = &channels[num_channels];
 
-    memset(dest->secret, 0, sizeof(dest->secret));
-    int len = decode_base64((unsigned char *) psk_base64, strlen(psk_base64), dest->secret);
+    memset(dest->channel.secret, 0, sizeof(dest->channel.secret));
+    int len = decode_base64((unsigned char *) psk_base64, strlen(psk_base64), dest->channel.secret);
     if (len == 32 || len == 16) {
-      mesh::Utils::sha256(dest->hash, sizeof(dest->hash), dest->secret, len);
+      mesh::Utils::sha256(dest->channel.hash, sizeof(dest->channel.hash), dest->channel.secret, len);
+      StrHelper::strncpy(dest->name, name, sizeof(dest->name));
       num_channels++;
       return dest;
     }
   }
   return NULL;
 }
+bool BaseChatMesh::getChannel(int idx, ChannelDetails& dest) {
+  if (idx >= 0 && idx < MAX_GROUP_CHANNELS) {
+    dest = channels[idx];
+    return true;
+  }
+  return false;
+}
+bool BaseChatMesh::setChannel(int idx, const ChannelDetails& src) {
+  static uint8_t zeroes[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+
+  if (idx >= 0 && idx < MAX_GROUP_CHANNELS) {
+    channels[idx] = src;
+    if (memcmp(&src.channel.secret[16], zeroes, 16) == 0) {
+      mesh::Utils::sha256(channels[idx].channel.hash, sizeof(channels[idx].channel.hash), src.channel.secret, 16);  // 128-bit key
+    } else {
+      mesh::Utils::sha256(channels[idx].channel.hash, sizeof(channels[idx].channel.hash), src.channel.secret, 32);  // 256-bit key
+    }
+    return true;
+  }
+  return false;
+}
+int BaseChatMesh::findChannelIdx(const mesh::GroupChannel& ch) {
+  for (int i = 0; i < MAX_GROUP_CHANNELS; i++) {
+    if (memcmp(ch.secret, channels[i].channel.secret, sizeof(ch.secret)) == 0) return i;
+  }
+  return -1;  // not found
+}
 #else
-mesh::GroupChannel* BaseChatMesh::addChannel(const char* psk_base64) {
+ChannelDetails* BaseChatMesh::addChannel(const char* name, const char* psk_base64) {
   return NULL;  // not supported
 }
+bool BaseChatMesh::getChannel(int idx, ChannelDetails& dest) {
+  return false;
+}
+bool BaseChatMesh::setChannel(int idx, const ChannelDetails& src) {
+  return false;
+}
+int BaseChatMesh::findChannelIdx(const mesh::GroupChannel& ch) {
+  return -1;  // not found
+}
 #endif
 
 ContactsIterator BaseChatMesh::startContactsIterator() {

+ 12 - 3
src/helpers/BaseChatMesh.h

@@ -61,6 +61,11 @@ struct ConnectionInfo {
   uint32_t expected_ack;
 };
 
+struct ChannelDetails {
+  mesh::GroupChannel channel;
+  char name[32];
+};
+
 /**
  *  \brief  abstract Mesh class for common 'chat' client
  */
@@ -74,8 +79,8 @@ class BaseChatMesh : public mesh::Mesh {
   int matching_peer_indexes[MAX_SEARCH_RESULTS];
   unsigned long txt_send_timeout;
 #ifdef MAX_GROUP_CHANNELS
-  mesh::GroupChannel channels[MAX_GROUP_CHANNELS];
-  int num_channels;
+  ChannelDetails channels[MAX_GROUP_CHANNELS];
+  int num_channels;  // only for addChannel()
 #endif
   mesh::Packet* _pendingLoopback;
   uint8_t temp_buf[MAX_TRANS_UNIT];
@@ -89,6 +94,7 @@ protected:
   { 
     num_contacts = 0;
   #ifdef MAX_GROUP_CHANNELS
+    memset(channels, 0, sizeof(channels));
     num_channels = 0;
   #endif
     txt_send_timeout = 0;
@@ -151,7 +157,10 @@ public:
   bool  addContact(const ContactInfo& contact);
   int getNumContacts() const { return num_contacts; }
   ContactsIterator startContactsIterator();
-  mesh::GroupChannel* addChannel(const char* psk_base64);
+  ChannelDetails* addChannel(const char* name, const char* psk_base64);
+  bool getChannel(int idx, ChannelDetails& dest);
+  bool setChannel(int idx, const ChannelDetails& src);
+  int findChannelIdx(const mesh::GroupChannel& ch);
 
   void loop();
 };

+ 13 - 0
src/helpers/CommonCLI.cpp

@@ -41,6 +41,7 @@ void CommonCLI::loadPrefs(FILESYSTEM* fs) {
       file.read((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2));  // 115
       file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw));  // 116
       file.read(pad, 4);   // 120
+      file.read((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max));   // 124
 
       // sanitise bad pref values
       _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
@@ -91,6 +92,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
     file.write((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2));  // 115
     file.write((uint8_t *) &_prefs->bw, sizeof(_prefs->bw));  // 116
     file.write(pad, 4);   // 120
+    file.write((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max));   // 124
 
     file.close();
   }
@@ -176,6 +178,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
         sprintf(reply, "> %s", StrHelper::ftoa(_prefs->rx_delay_base));
       } else if (memcmp(config, "txdelay", 7) == 0) {
         sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor));
+      } else if (memcmp(config, "flood.max", 9) == 0) {
+        sprintf(reply, "> %d", (uint32_t)_prefs->flood_max);
       } else if (memcmp(config, "direct.txdelay", 14) == 0) {
         sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor));
       } else if (memcmp(config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) {
@@ -262,6 +266,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
         } else {
           strcpy(reply, "Error, cannot be negative");
         }
+      } else if (memcmp(config, "flood.max ", 10) == 0) {
+        uint8_t m = atoi(&config[10]);
+        if (m <= 64) {
+          _prefs->flood_max = m;
+          savePrefs();
+          strcpy(reply, "OK");
+        } else {
+          strcpy(reply, "Error, max 64");
+        }
       } else if (memcmp(config, "direct.txdelay ", 15) == 0) {
         float f = atof(&config[15]);
         if (f >= 0) {

+ 1 - 0
src/helpers/CommonCLI.h

@@ -23,6 +23,7 @@ struct NodePrefs {  // persisted to file
     uint8_t reserved1;
     uint8_t reserved2;
     float bw;
+    uint8_t flood_max;
 };
 
 class CommonCLICallbacks {