Kaynağa Gözat

Merge remote-tracking branch 'upstream/dev' into jbrazio/2025_3f11ad35

João Brázio 11 ay önce
ebeveyn
işleme
7fca20475a
100 değiştirilmiş dosya ile 2300 ekleme ve 899 silme
  1. 0 0
      boards/heltec_e213.json
  2. 0 0
      boards/heltec_e290.json
  3. 53 0
      boards/heltec_mesh_pocket.json
  4. 0 0
      boards/heltec_t190.json
  5. 187 28
      examples/companion_radio/DataStore.cpp
  6. 10 0
      examples/companion_radio/DataStore.h
  7. 70 18
      examples/companion_radio/MyMesh.cpp
  8. 24 1
      examples/companion_radio/main.cpp
  9. 86 34
      examples/companion_radio/ui-new/UITask.cpp
  10. 12 0
      examples/companion_radio/ui-new/UITask.h
  11. 1 0
      examples/simple_repeater/main.cpp
  12. 1 0
      examples/simple_room_server/main.cpp
  13. 4 1
      platformio.ini
  14. 1 2
      src/helpers/HeltecV3Board.h
  15. 2 2
      src/helpers/IdentityStore.h
  16. 2 2
      src/helpers/nrf52/SerialBLEInterface.cpp
  17. 4 0
      src/helpers/nrf52/SerialBLEInterface.h
  18. 18 16
      src/helpers/sensors/EnvironmentSensorManager.cpp
  19. 38 16
      src/helpers/ui/GxEPDDisplay.cpp
  20. 21 5
      src/helpers/ui/GxEPDDisplay.h
  21. 58 4
      src/helpers/ui/MomentaryButton.cpp
  22. 6 0
      src/helpers/ui/MomentaryButton.h
  23. 0 0
      src/helpers/ui/NullDisplayDriver.h
  24. 11 7
      src/helpers/ui/UIScreen.h
  25. 5 37
      variants/generic-e22/target.cpp
  26. 2 4
      variants/heltec_e213/HeltecE213Board.cpp
  27. 0 10
      variants/heltec_e213/HeltecE213Board.h
  28. 0 0
      variants/heltec_e213/pins_arduino.h
  29. 47 22
      variants/heltec_e213/platformio.ini
  30. 0 0
      variants/heltec_e213/target.cpp
  31. 0 0
      variants/heltec_e213/target.h
  32. 2 4
      variants/heltec_e290/HeltecE290Board.cpp
  33. 0 9
      variants/heltec_e290/HeltecE290Board.h
  34. 0 0
      variants/heltec_e290/pins_arduino.h
  35. 48 21
      variants/heltec_e290/platformio.ini
  36. 0 0
      variants/heltec_e290/target.cpp
  37. 0 0
      variants/heltec_e290/target.h
  38. 4 4
      variants/heltec_mesh_solar/platformio.ini
  39. 7 9
      variants/heltec_t114/T114Board.cpp
  40. 0 13
      variants/heltec_t114/T114Board.h
  41. 196 0
      variants/heltec_t114/platformio.ini
  42. 0 0
      variants/heltec_t114/target.cpp
  43. 8 3
      variants/heltec_t114/target.h
  44. 0 0
      variants/heltec_t114/variant.cpp
  45. 0 0
      variants/heltec_t114/variant.h
  46. 2 4
      variants/heltec_t190/HeltecT190Board.cpp
  47. 0 9
      variants/heltec_t190/HeltecT190Board.h
  48. 0 0
      variants/heltec_t190/pins_arduino.h
  49. 42 19
      variants/heltec_t190/platformio.ini
  50. 0 0
      variants/heltec_t190/target.cpp
  51. 0 0
      variants/heltec_t190/target.h
  52. 1 1
      variants/heltec_v3/platformio.ini
  53. 4 4
      variants/ikoka_stick_nrf/platformio.ini
  54. 0 0
      variants/lilygo_techo/TechoBoard.cpp
  55. 0 13
      variants/lilygo_techo/TechoBoard.h
  56. 118 0
      variants/lilygo_techo/platformio.ini
  57. 53 0
      variants/lilygo_techo/target.cpp
  58. 3 19
      variants/lilygo_techo/target.h
  59. 0 0
      variants/lilygo_techo/variant.cpp
  60. 13 0
      variants/lilygo_techo/variant.h
  61. 3 37
      variants/lilygo_tlora_c6/target.cpp
  62. 72 0
      variants/mesh_pocket/MeshPocket.cpp
  63. 45 0
      variants/mesh_pocket/MeshPocket.h
  64. 46 47
      variants/mesh_pocket/platformio.ini
  65. 44 0
      variants/mesh_pocket/target.cpp
  66. 33 0
      variants/mesh_pocket/target.h
  67. 15 0
      variants/mesh_pocket/variant.cpp
  68. 124 0
      variants/mesh_pocket/variant.h
  69. 4 4
      variants/minewsemi_me25ls01/platformio.ini
  70. 5 4
      variants/nano_g2_ultra/platformio.ini
  71. 4 0
      variants/promicro/PromicroBoard.h
  72. 4 4
      variants/promicro/platformio.ini
  73. 4 4
      variants/rak4631/platformio.ini
  74. 81 0
      variants/rak_wismesh_tag/RAKWismeshTagBoard.cpp
  75. 80 0
      variants/rak_wismesh_tag/RAKWismeshTagBoard.h
  76. 116 0
      variants/rak_wismesh_tag/platformio.ini
  77. 54 0
      variants/rak_wismesh_tag/target.cpp
  78. 27 0
      variants/rak_wismesh_tag/target.h
  79. 21 0
      variants/rak_wismesh_tag/variant.cpp
  80. 121 0
      variants/rak_wismesh_tag/variant.h
  81. 0 0
      variants/rpi_picow/PicoWBoard.cpp
  82. 0 13
      variants/rpi_picow/PicoWBoard.h
  83. 37 28
      variants/rpi_picow/platformio.ini
  84. 0 0
      variants/rpi_picow/target.cpp
  85. 1 1
      variants/rpi_picow/target.h
  86. 4 4
      variants/sensecap_solar/platformio.ini
  87. 3 0
      variants/t1000-e/T1000eBoard.cpp
  88. 11 19
      variants/t1000-e/T1000eBoard.h
  89. 19 26
      variants/t1000-e/platformio.ini
  90. 87 107
      variants/t1000-e/t1000e_sensors.cpp
  91. 2 1
      variants/t1000-e/t1000e_sensors.h
  92. 0 9
      variants/t1000-e/target.cpp
  93. 2 1
      variants/t1000-e/variant.cpp
  94. 0 85
      variants/techo/platformio.ini
  95. 0 123
      variants/techo/target.cpp
  96. 8 8
      variants/thinknode_m1/ThinkNodeM1Board.cpp
  97. 0 13
      variants/thinknode_m1/ThinkNodeM1Board.h
  98. 49 19
      variants/thinknode_m1/platformio.ini
  99. 1 1
      variants/thinknode_m1/target.h
  100. 9 0
      variants/thinknode_m1/variant.h

+ 0 - 0
boards/heltec_vision_master_e213.json → boards/heltec_e213.json


+ 0 - 0
boards/heltec_vision_master_e290.json → boards/heltec_e290.json


+ 53 - 0
boards/heltec_mesh_pocket.json

@@ -0,0 +1,53 @@
+{
+    "build": {
+      "arduino": {
+        "ldscript": "nrf52840_s140_v6.ld"
+      },
+      "core": "nRF5",
+      "cpu": "cortex-m4",
+      "extra_flags": "-DNRF52840_XXAA",
+      "f_cpu": "64000000L",
+      "hwids": [
+        ["0x239A", "0x4405"],
+        ["0x239A", "0x0029"],
+        ["0x239A", "0x002A"]
+      ],
+      "usb_product": "HT-n5262",
+      "mcu": "nrf52840",
+      "variant": "heltec_mesh_pocket",
+      "variants_dir": "variants",
+      "bsp": {
+        "name": "adafruit"
+      },
+      "softdevice": {
+        "sd_flags": "-DS140",
+        "sd_name": "s140",
+        "sd_version": "6.1.1",
+        "sd_fwid": "0x00B6"
+      },
+      "bootloader": {
+        "settings_addr": "0xFF000"
+      }
+    },
+    "connectivity": ["bluetooth"],
+    "debug": {
+      "jlink_device": "nRF52840_xxAA",
+      "onboard_tools": ["jlink"],
+      "svd_path": "nrf52840.svd",
+      "openocd_target": "nrf52840-mdk-rs"
+    },
+    "frameworks": ["arduino"],
+    "name": "Heltec nrf (Adafruit BSP)",
+    "upload": {
+      "maximum_ram_size": 248832,
+      "maximum_size": 815104,
+      "speed": 115200,
+      "protocol": "nrfutil",
+      "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
+      "use_1200bps_touch": true,
+      "require_upload_port": true,
+      "wait_for_upload_port": true
+    },
+    "url": "https://heltec.org/project/meshpocket/",
+    "vendor": "Heltec"
+  }

+ 0 - 0
boards/heltec_vision_master_t190.json → boards/heltec_t190.json


+ 187 - 28
examples/companion_radio/DataStore.cpp

@@ -1,7 +1,25 @@
 #include <Arduino.h>
 #include "DataStore.h"
 
-DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _clock(&clock),
+#if defined(EXTRAFS) || defined(QSPIFLASH)
+  #define MAX_BLOBRECS 100
+#else
+  #define MAX_BLOBRECS 20
+#endif
+
+DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _fsExtra(nullptr), _clock(&clock),
+#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
+    identity_store(fs, "")
+#elif defined(RP2040_PLATFORM)
+    identity_store(fs, "/identity")
+#else
+    identity_store(fs, "/identity")
+#endif
+{
+}
+
+#if defined(EXTRAFS) || defined(QSPIFLASH)
+DataStore::DataStore(FILESYSTEM& fs, FILESYSTEM& fsExtra, mesh::RTCClock& clock) : _fs(&fs), _fsExtra(&fsExtra), _clock(&clock),
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
     identity_store(fs, "")
 #elif defined(RP2040_PLATFORM)
@@ -11,15 +29,16 @@ DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _clock(&
 #endif
 {
 }
+#endif
 
-static File openWrite(FILESYSTEM* _fs, const char* filename) {
+static File openWrite(FILESYSTEM* fs, const char* filename) {
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
-  _fs->remove(filename);
-  return _fs->open(filename, FILE_O_WRITE);
+  fs->remove(filename);
+  return fs->open(filename, FILE_O_WRITE);
 #elif defined(RP2040_PLATFORM)
-  return _fs->open(filename, "w");
+  return fs->open(filename, "w");
 #else
-  return _fs->open(filename, "w", true);
+  return fs->open(filename, "w", true);
 #endif
 }
 
@@ -30,6 +49,9 @@ void DataStore::begin() {
 
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
   checkAdvBlobFile();
+  #if defined(EXTRAFS) || defined(QSPIFLASH)
+  migrateToSecondaryFS();
+  #endif
 #else
   // init 'blob store' support
   _fs->mkdir("/bl");
@@ -41,7 +63,13 @@ void DataStore::begin() {
 #elif defined(RP2040_PLATFORM)
   #include <LittleFS.h>
 #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
-  #include <InternalFileSystem.h>
+  #if defined(QSPIFLASH)
+    #include <CustomLFS_QSPIFlash.h>
+  #elif defined(EXTRAFS)
+    #include <CustomLFS.h>
+  #else 
+    #include <InternalFileSystem.h>
+  #endif
 #endif
 
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
@@ -51,9 +79,9 @@ int _countLfsBlock(void *p, lfs_block_t block){
   return 0;
 }
 
-lfs_ssize_t _getLfsUsedBlockCount() {
+lfs_ssize_t _getLfsUsedBlockCount(FILESYSTEM* fs) {
   lfs_size_t size = 0;
-  lfs_traverse(InternalFS._getFS(), _countLfsBlock, &size);
+  lfs_traverse(fs->_getFS(), _countLfsBlock, &size);
   return size;
 }
 #endif
@@ -67,8 +95,8 @@ uint32_t DataStore::getStorageUsedKb() const {
   _fs->info(info);
   return info.usedBytes / 1024;
 #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
-  const lfs_config* config = InternalFS._getFS()->cfg;
-  int usedBlockCount = _getLfsUsedBlockCount();
+  const lfs_config* config = _getContactsChannelsFS()->_getFS()->cfg;
+  int usedBlockCount = _getLfsUsedBlockCount(_getContactsChannelsFS());
   int usedBytes = config->block_size * usedBlockCount;
   return usedBytes / 1024;
 #else
@@ -85,7 +113,7 @@ uint32_t DataStore::getStorageTotalKb() const {
   _fs->info(info);
   return info.totalBytes / 1024;
 #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
-  const lfs_config* config = InternalFS._getFS()->cfg;
+  const lfs_config* config = _getContactsChannelsFS()->_getFS()->cfg;
   int totalBytes = config->block_size * config->block_count;
   return totalBytes / 1024;
 #else
@@ -103,13 +131,31 @@ File DataStore::openRead(const char* filename) {
 #endif
 }
 
+File DataStore::openRead(FILESYSTEM* fs, const char* filename) {
+#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
+  return fs->open(filename, FILE_O_READ);
+#elif defined(RP2040_PLATFORM)
+  return fs->open(filename, "r");
+#else
+  return fs->open(filename, "r", false);
+#endif
+}
+
 bool DataStore::removeFile(const char* filename) {
   return _fs->remove(filename);
 }
 
+bool DataStore::removeFile(FILESYSTEM* fs, const char* filename) {
+  return fs->remove(filename);
+}
+
 bool DataStore::formatFileSystem() {
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
-  return _fs->format();
+  if (_fsExtra == nullptr) {
+    return _fs->format();
+  } else {
+    return _fs->format() && _fsExtra->format();
+  }
 #elif defined(RP2040_PLATFORM)
   return LittleFS.format();
 #elif defined(ESP32)
@@ -203,11 +249,15 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_
 }
 
 void DataStore::loadContacts(DataStoreHost* host) {
-  if (_fs->exists("/contacts3")) {
-#if defined(RP2040_PLATFORM)
+#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
+  if (_getContactsChannelsFS()->exists("/contacts3")) {
+    File file = _getContactsChannelsFS()->open("/contacts3");
+#elif defined(RP2040_PLATFORM)
+    if (_fs->exists("/contacts3")) {
     File file = _fs->open("/contacts3", "r");
 #else
-    File file = _fs->open("/contacts3");
+    if (_fs->exists("/contacts3")) {
+      File file = _fs->open("/contacts3", "r", false);
 #endif
     if (file) {
       bool full = false;
@@ -240,7 +290,7 @@ void DataStore::loadContacts(DataStoreHost* host) {
 }
 
 void DataStore::saveContacts(DataStoreHost* host) {
-  File file = openWrite(_fs, "/contacts3");
+  File file = openWrite(_getContactsChannelsFS(), "/contacts3");
   if (file) {
     uint32_t idx = 0;
     ContactInfo c;
@@ -269,11 +319,15 @@ void DataStore::saveContacts(DataStoreHost* host) {
 }
 
 void DataStore::loadChannels(DataStoreHost* host) {
+#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
+  if (_getContactsChannelsFS()->exists("/channels2")) {
+    File file = _getContactsChannelsFS()->open("/channels2");
+#elif defined(RP2040_PLATFORM)
   if (_fs->exists("/channels2")) {
-#if defined(RP2040_PLATFORM)
     File file = _fs->open("/channels2", "r");
 #else
-    File file = _fs->open("/channels2");
+  if (_fs->exists("/channels2")) {
+    File file = _fs->open("/channels2", "r", false);
 #endif
     if (file) {
       bool full = false;
@@ -300,7 +354,7 @@ void DataStore::loadChannels(DataStoreHost* host) {
 }
 
 void DataStore::saveChannels(DataStoreHost* host) {
-  File file = openWrite(_fs, "/channels2");
+  File file = openWrite(_getContactsChannelsFS(), "/channels2");
   if (file) {
     uint8_t channel_idx = 0;
     ChannelDetails ch;
@@ -331,12 +385,12 @@ struct BlobRec {
 };
 
 void DataStore::checkAdvBlobFile() {
-  if (!_fs->exists("/adv_blobs")) {
-    File file = openWrite(_fs, "/adv_blobs");
+  if (!_getContactsChannelsFS()->exists("/adv_blobs")) {
+    File file = openWrite(_getContactsChannelsFS(), "/adv_blobs");
     if (file) {
       BlobRec zeroes;
       memset(&zeroes, 0, sizeof(zeroes));
-      for (int i = 0; i < 20; i++) {     // pre-allocate to fixed size
+      for (int i = 0; i < MAX_BLOBRECS; i++) {     // pre-allocate to fixed size
         file.write((uint8_t *) &zeroes, sizeof(zeroes));
       }
       file.close();
@@ -344,10 +398,117 @@ void DataStore::checkAdvBlobFile() {
   }
 }
 
+void DataStore::migrateToSecondaryFS() {
+  // migrate old adv_blobs, contacts3 and channels2 files to secondary FS if they don't already exist
+  if (!_fsExtra->exists("/adv_blobs")) {
+    if (_fs->exists("/adv_blobs")) {
+    File oldAdvBlobs = openRead(_fs, "/adv_blobs");
+    File newAdvBlobs = openWrite(_fsExtra, "/adv_blobs");
+
+    if (oldAdvBlobs && newAdvBlobs) {
+      BlobRec rec;
+      size_t count = 0;
+
+      // Copy 20 BlobRecs from old to new
+      while (count < 20 && oldAdvBlobs.read((uint8_t *)&rec, sizeof(rec)) == sizeof(rec)) {
+        newAdvBlobs.seek(count * sizeof(BlobRec));
+        newAdvBlobs.write((uint8_t *)&rec, sizeof(rec));
+        count++;
+      }
+    }
+    if (oldAdvBlobs) oldAdvBlobs.close();
+    if (newAdvBlobs) newAdvBlobs.close();
+    _fs->remove("/adv_blobs");
+    }
+  }
+  if (!_fsExtra->exists("/contacts3")) {
+    if (_fs->exists("/contacts3")) {
+      File oldFile = openRead(_fs, "/contacts3");
+      File newFile = openWrite(_fsExtra, "/contacts3");
+
+      if (oldFile && newFile) {
+        uint8_t buf[64];
+        int n;
+        while ((n = oldFile.read(buf, sizeof(buf))) > 0) {
+          newFile.write(buf, n);
+        }
+      }
+      if (oldFile) oldFile.close();
+      if (newFile) newFile.close();
+      _fs->remove("/contacts3");
+    }
+  }
+  if (!_fsExtra->exists("/channels2")) {
+    if (_fs->exists("/channels2")) {
+      File oldFile = openRead(_fs, "/channels2");
+      File newFile = openWrite(_fsExtra, "/channels2");
+
+      if (oldFile && newFile) {
+        uint8_t buf[64];
+        int n;
+        while ((n = oldFile.read(buf, sizeof(buf))) > 0) {
+          newFile.write(buf, n);
+        }
+      }
+      if (oldFile) oldFile.close();
+      if (newFile) newFile.close();
+      _fs->remove("/channels2");
+    }
+  }
+  // cleanup nodes which have been testing the extra fs, copy _main.id and new_prefs back to primary
+  if (_fsExtra->exists("/_main.id")) {
+      if (_fs->exists("/_main.id")) {_fs->remove("/_main.id");}
+      File oldFile = openRead(_fsExtra, "/_main.id");
+      File newFile = openWrite(_fs, "/_main.id");
+
+      if (oldFile && newFile) {
+        uint8_t buf[64];
+        int n;
+        while ((n = oldFile.read(buf, sizeof(buf))) > 0) {
+          newFile.write(buf, n);
+        }
+      }
+      if (oldFile) oldFile.close();
+      if (newFile) newFile.close();
+      _fsExtra->remove("/_main.id");
+  }
+  if (_fsExtra->exists("/new_prefs")) {
+    if (_fs->exists("/new_prefs")) {_fs->remove("/new_prefs");}
+      File oldFile = openRead(_fsExtra, "/new_prefs");
+      File newFile = openWrite(_fs, "/new_prefs");
+
+      if (oldFile && newFile) {
+        uint8_t buf[64];
+        int n;
+        while ((n = oldFile.read(buf, sizeof(buf))) > 0) {
+          newFile.write(buf, n);
+        }
+      }
+      if (oldFile) oldFile.close();
+      if (newFile) newFile.close();
+      _fsExtra->remove("/new_prefs");
+  }
+  // remove files from where they should not be anymore
+  if (_fs->exists("/adv_blobs")) {
+    _fs->remove("/adv_blobs");
+  }
+  if (_fs->exists("/contacts3")) {
+    _fs->remove("/contacts3");
+  }
+  if (_fs->exists("/channels2")) {
+    _fs->remove("/channels2");
+  }
+  if (_fsExtra->exists("/_main.id")) {
+    _fsExtra->remove("/_main.id");
+  }
+  if (_fsExtra->exists("/new_prefs")) {
+    _fsExtra->remove("/new_prefs");
+  }
+}
+
 uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) {
-  File file = _fs->open("/adv_blobs");
+  File file = _getContactsChannelsFS()->open("/adv_blobs");
   uint8_t len = 0;  // 0 = not found
-
   if (file) {
     BlobRec tmp;
     while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) {
@@ -364,10 +525,8 @@ uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_b
 
 bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) {
   if (len < PUB_KEY_SIZE+4+SIGNATURE_SIZE || len > MAX_ADVERT_PKT_LEN) return false;
-
   checkAdvBlobFile();
-
-  File file = _fs->open("/adv_blobs", FILE_O_WRITE);
+  File file = _getContactsChannelsFS()->open("/adv_blobs", FILE_O_WRITE);
   if (file) {
     uint32_t pos = 0, found_pos = 0;
     uint32_t min_timestamp = 0xFFFFFFFF;

+ 10 - 0
examples/companion_radio/DataStore.h

@@ -15,6 +15,7 @@ public:
 
 class DataStore {
   FILESYSTEM* _fs;
+  FILESYSTEM* _fsExtra;
   mesh::RTCClock* _clock;
   IdentityStore identity_store;
 
@@ -25,8 +26,11 @@ class DataStore {
 
 public:
   DataStore(FILESYSTEM& fs, mesh::RTCClock& clock);
+  DataStore(FILESYSTEM& fs, FILESYSTEM& fsExtra, mesh::RTCClock& clock);
   void begin();
   bool formatFileSystem();
+  FILESYSTEM* getPrimaryFS() const { return _fs; }
+  FILESYSTEM* getSecondaryFS() const { return _fsExtra; }
   bool loadMainIdentity(mesh::LocalIdentity &identity);
   bool saveMainIdentity(const mesh::LocalIdentity &identity);
   void loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon);
@@ -35,10 +39,16 @@ public:
   void saveContacts(DataStoreHost* host);
   void loadChannels(DataStoreHost* host);
   void saveChannels(DataStoreHost* host);
+  void migrateToSecondaryFS();
   uint8_t getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]);
   bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len);
   File openRead(const char* filename);
+  File openRead(FILESYSTEM* fs, const char* filename);
   bool removeFile(const char* filename);
+  bool removeFile(FILESYSTEM* fs, const char* filename);
   uint32_t getStorageUsedKb() const;
   uint32_t getStorageTotalKb() const;
+
+private:
+  FILESYSTEM* _getContactsChannelsFS() const { if (_fsExtra) return _fsExtra; return _fs;};
 };

+ 70 - 18
examples/companion_radio/MyMesh.cpp

@@ -1524,33 +1524,72 @@ void MyMesh::checkCLIRescueCmd() {
 
       // get path from command e.g: "ls /adafruit"
       const char *path = &cli_command[3];
-      
+
+      bool is_fs2 = false;
+      if (memcmp(path, "UserData/", 9) == 0) {
+        path += 8; // skip "UserData"
+      } else if (memcmp(path, "ExtraFS/", 8) == 0) {
+        path += 7; // skip "ExtraFS"
+        is_fs2 = true;
+      }
+      Serial.printf("Listing files in %s\n", path);
+
       // log each file and directory
       File root = _store->openRead(path);
-      if(root){
-        File file = root.openNextFile();
-        while (file) {
-
-          if (file.isDirectory()) {
-            Serial.printf("[dir] %s\n", file.name());
-          } else {
-            Serial.printf("[file] %s (%d bytes)\n", file.name(), file.size());
+      if (is_fs2 == false) {
+        if (root) {
+          File file = root.openNextFile();
+          while (file) {
+            if (file.isDirectory()) {
+              Serial.printf("[dir]  UserData%s/%s\n", path, file.name());
+            } else {
+              Serial.printf("[file] UserData%s/%s (%d bytes)\n", path, file.name(), file.size());
+            }
+            // move to next file
+            file = root.openNextFile();
           }
-
-          // move to next file
-          file = root.openNextFile();
-
+          root.close();
         }
-        root.close();
       }
 
+      if (is_fs2 == true || strlen(path) == 0 || strcmp(path, "/") == 0) {
+        if (_store->getSecondaryFS() != nullptr) {
+          File root2 = _store->openRead(_store->getSecondaryFS(), path);
+          File file = root2.openNextFile();
+          while (file) {
+            if (file.isDirectory()) {
+              Serial.printf("[dir]  ExtraFS%s/%s\n", path, file.name());
+            } else {
+              Serial.printf("[file] ExtraFS%s/%s (%d bytes)\n", path, file.name(), file.size());
+            }
+            // move to next file
+            file = root2.openNextFile();
+          }
+          root2.close();
+        }
+      }
     } else if (memcmp(cli_command, "cat", 3) == 0) {
 
       // get path from command e.g: "cat /contacts3"
       const char *path = &cli_command[4];
       
+      bool is_fs2 = false;
+      if (memcmp(path, "UserData/", 9) == 0) {
+        path += 8; // skip "UserData"
+      } else if (memcmp(path, "ExtraFS/", 8) == 0) {
+        path += 7; // skip "ExtraFS"
+        is_fs2 = true;
+      } else {
+        Serial.println("Invalid path provided, must start with UserData/ or ExtraFS/");
+        cli_command[0] = 0;
+        return;
+      }
+
       // log file content as hex
       File file = _store->openRead(path);
+      if (is_fs2 == true) {
+        file = _store->openRead(_store->getSecondaryFS(), path);
+      }
       if(file){
 
         // get file content
@@ -1567,17 +1606,30 @@ void MyMesh::checkCLIRescueCmd() {
       }
 
     } else if (memcmp(cli_command, "rm ", 3) == 0) {
-
       // get path from command e.g: "rm /adv_blobs"
-      const char *path = &cli_command[4];
-
+      const char *path = &cli_command[3];
+      MESH_DEBUG_PRINTLN("Removing file: %s", path);
       // ensure path is not empty, or root dir
       if(!path || strlen(path) == 0 || strcmp(path, "/") == 0){
         Serial.println("Invalid path provided");
       } else {
+      bool is_fs2 = false;
+      if (memcmp(path, "UserData/", 9) == 0) {
+        path += 8; // skip "UserData"
+      } else if (memcmp(path, "ExtraFS/", 8) == 0) {
+        path += 7; // skip "ExtraFS"
+        is_fs2 = true;
+      }
 
         // remove file
-        bool removed = _store->removeFile(path);
+        bool removed;
+        if (is_fs2) {
+          MESH_DEBUG_PRINTLN("Removing file from ExtraFS: %s", path);
+          removed = _store->removeFile(_store->getSecondaryFS(), path);
+        } else {
+          MESH_DEBUG_PRINTLN("Removing file from UserData: %s", path);
+          removed = _store->removeFile(path);
+        }
         if(removed){
           Serial.println("File removed");
         } else {

+ 24 - 1
examples/companion_radio/main.cpp

@@ -14,7 +14,18 @@ static uint32_t _atoi(const char* sp) {
 
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
   #include <InternalFileSystem.h>
-  DataStore store(InternalFS, rtc_clock);
+  #if defined(QSPIFLASH)
+    #include <CustomLFS_QSPIFlash.h>
+    DataStore store(InternalFS, QSPIFlash, rtc_clock);
+  #else
+  #if defined(EXTRAFS)
+    #include <CustomLFS.h>
+    CustomLFS ExtraFS(0xD4000, 0x19000, 128);
+    DataStore store(InternalFS, ExtraFS, rtc_clock);
+  #else
+    DataStore store(InternalFS, rtc_clock);
+  #endif
+  #endif
 #elif defined(RP2040_PLATFORM)
   #include <LittleFS.h>
   DataStore store(LittleFS, rtc_clock);
@@ -118,6 +129,18 @@ void setup() {
 
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
   InternalFS.begin();
+  #if defined(QSPIFLASH)
+    if (!QSPIFlash.begin()) {
+      // debug output might not be available at this point, might be too early. maybe should fall back to InternalFS here?
+      MESH_DEBUG_PRINTLN("CustomLFS_QSPIFlash: failed to initialize");
+    } else {
+      MESH_DEBUG_PRINTLN("CustomLFS_QSPIFlash: initialized successfully");
+    }
+  #else
+  #if defined(EXTRAFS)
+      ExtraFS.begin();
+  #endif
+  #endif
   store.begin();
   the_mesh.begin(
     #ifdef DISPLAY_CLASS

+ 86 - 34
examples/companion_radio/ui-new/UITask.cpp

@@ -3,7 +3,9 @@
 #include "../MyMesh.h"
 #include "target.h"
 
-#define AUTO_OFF_MILLIS     15000   // 15 seconds
+#ifndef AUTO_OFF_MILLIS
+  #define AUTO_OFF_MILLIS     15000   // 15 seconds
+#endif
 #define BOOT_SCREEN_MILLIS   3000   // 3 seconds
 
 #ifdef PIN_STATUS_LED
@@ -200,8 +202,8 @@ public:
       display.print(tmp);
     } else if (_page == HomePage::BLUETOOTH) {
       display.setColor(DisplayDriver::GREEN);
-      display.drawXbm((display.width() - 32) / 2, 18, 
-          _task->isSerialEnabled() ? bluetooth_on : bluetooth_off, 
+      display.drawXbm((display.width() - 32) / 2, 18,
+          _task->isSerialEnabled() ? bluetooth_on : bluetooth_off,
           32, 32);
       display.setTextSize(1);
       display.drawTextCentered(display.width() / 2, 64 - 11, "toggle: " PRESS_LABEL);
@@ -223,11 +225,11 @@ public:
   }
 
   bool handleInput(char c) override {
-    if (c == KEY_LEFT) {
+    if (c == KEY_LEFT || c == KEY_PREV) {
       _page = (_page + HomePage::Count - 1) % HomePage::Count;
       return true;
     }
-    if (c == KEY_RIGHT || c == KEY_SELECT) {
+    if (c == KEY_NEXT || c == KEY_RIGHT) {
       _page = (_page + 1) % HomePage::Count;
       if (_page == HomePage::RECENT) {
         _task->showAlert("Recent adverts", 800);
@@ -321,11 +323,15 @@ public:
     display.setColor(DisplayDriver::LIGHT);
     display.printWordWrap(p->msg, display.width());
 
+#if AUTO_OFF_MILLIS==0 // probably e-ink
+    return 10000; // 10 s
+#else
     return 1000;  // next render after 1000 ms
+#endif
   }
 
   bool handleInput(char c) override {
-    if (c == KEY_SELECT || c == KEY_RIGHT) {
+    if (c == KEY_NEXT || c == KEY_RIGHT) {
       num_unread--;
       if (num_unread == 0) {
         _task->gotoHomeScreen();
@@ -419,42 +425,38 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i
   if (_display != NULL) {
     if (!_display->isOn()) _display->turnOn();
     _auto_off = millis() + AUTO_OFF_MILLIS;  // extend the auto-off timer
-    _next_refresh = 0;  // trigger refresh
+    _next_refresh = 100;  // trigger refresh
   }
 }
 
 void UITask::userLedHandler() {
 #ifdef PIN_STATUS_LED
-  static int state = 0;
-  static int next_change = 0;
-  static int last_increment = 0;
-
   int cur_time = millis();
-  if (cur_time > next_change) {
-    if (state == 0) {
-      state = 1;
+  if (cur_time > next_led_change) {
+    if (led_state == 0) {
+      led_state = 1;
       if (_msgcount > 0) {
-        last_increment = LED_ON_MSG_MILLIS;
+        last_led_increment = LED_ON_MSG_MILLIS;
       } else {
-        last_increment = LED_ON_MILLIS;
+        last_led_increment = LED_ON_MILLIS;
       }
-      next_change = cur_time + last_increment;
+      next_led_change = cur_time + last_led_increment;
     } else {
-      state = 0;
-      next_change = cur_time + LED_CYCLE_MILLIS - last_increment;
+      led_state = 0;
+      next_led_change = cur_time + LED_CYCLE_MILLIS - last_led_increment;
     }
-    digitalWrite(PIN_STATUS_LED, state);
+    digitalWrite(PIN_STATUS_LED, led_state);
   }
 #endif
 }
 
 void UITask::setCurrScreen(UIScreen* c) {
   curr = c;
-  _next_refresh = 0;
+  _next_refresh = 100;
 }
 
-/* 
-  hardware-agnostic pre-shutdown activity should be done here 
+/*
+  hardware-agnostic pre-shutdown activity should be done here
 */
 void UITask::shutdown(bool restart){
 
@@ -492,9 +494,13 @@ void UITask::loop() {
 #if defined(PIN_USER_BTN)
   int ev = user_btn.check();
   if (ev == BUTTON_EVENT_CLICK) {
-    c = checkDisplayOn(KEY_SELECT);
+    c = checkDisplayOn(KEY_NEXT);
   } else if (ev == BUTTON_EVENT_LONG_PRESS) {
     c = handleLongPress(KEY_ENTER);
+  } else if (ev == BUTTON_EVENT_DOUBLE_CLICK) {
+    c = handleDoubleClick(KEY_PREV);
+  } else if (ev == BUTTON_EVENT_TRIPLE_CLICK) {
+    c = handleTripleClick(KEY_SELECT);
   }
 #endif
 #if defined(WIO_TRACKER_L1)
@@ -514,16 +520,27 @@ void UITask::loop() {
 #if defined(PIN_USER_BTN_ANA)
   ev = analog_btn.check();
   if (ev == BUTTON_EVENT_CLICK) {
-    c = checkDisplayOn(KEY_SELECT);
+    c = checkDisplayOn(KEY_NEXT);
   } else if (ev == BUTTON_EVENT_LONG_PRESS) {
     c = handleLongPress(KEY_ENTER);
+  } else if (ev == BUTTON_EVENT_DOUBLE_CLICK) {
+    c = handleDoubleClick(KEY_PREV);
+  } else if (ev == BUTTON_EVENT_TRIPLE_CLICK) {
+    c = handleTripleClick(KEY_SELECT);
+  }
+#endif
+#if defined(DISP_BACKLIGHT) && defined(BACKLIGHT_BTN)
+  if (millis() > next_backlight_btn_check) {
+    bool touch_state = digitalRead(PIN_BUTTON2);
+    digitalWrite(DISP_BACKLIGHT, !touch_state);
+    next_backlight_btn_check = millis() + 300;
   }
 #endif
 
   if (c != 0 && curr) {
     curr->handleInput(c);
     _auto_off = millis() + AUTO_OFF_MILLIS;   // extend auto-off timer
-    _next_refresh = 0;  // trigger refresh
+    _next_refresh = 100;  // trigger refresh
   }
 
   userLedHandler();
@@ -553,9 +570,11 @@ void UITask::loop() {
       }
       _display->endFrame();
     }
+#if AUTO_OFF_MILLIS > 0
     if (millis() > _auto_off) {
       _display->turnOff();
     }
+#endif
   }
 
 #ifdef AUTO_SHUTDOWN_MILLIVOLTS
@@ -565,7 +584,7 @@ void UITask::loop() {
 
       // show low battery shutdown alert
       // we should only do this for eink displays, which will persist after power loss
-      #ifdef THINKNODE_M1
+      #if defined(THINKNODE_M1) || defined(LILYGO_TECHO)
       if (_display != NULL) {
         _display->startFrame();
         _display->setTextSize(2);
@@ -604,20 +623,53 @@ char UITask::handleLongPress(char c) {
   return c;
 }
 
-/*
-void UITask::handleButtonTriplePress() {
-  MESH_DEBUG_PRINTLN("UITask: triple press triggered");
-  // Toggle buzzer quiet mode
+char UITask::handleDoubleClick(char c) {
+  MESH_DEBUG_PRINTLN("UITask: double click triggered");
+  checkDisplayOn(c);
+  return c;
+}
+
+char UITask::handleTripleClick(char c) {
+  MESH_DEBUG_PRINTLN("UITask: triple click triggered");
+  checkDisplayOn(c);
+  toggleBuzzer();
+  c = 0;
+  return c;
+}
+
+void UITask::toggleGPS() {
+    if (_sensors != NULL) {
+    // toggle GPS on/off
+    int num = _sensors->getNumSettings();
+    for (int i = 0; i < num; i++) {
+      if (strcmp(_sensors->getSettingName(i), "gps") == 0) {
+        if (strcmp(_sensors->getSettingValue(i), "1") == 0) {
+          _sensors->setSettingValue("gps", "0");
+          soundBuzzer(UIEventType::ack);
+          showAlert("GPS: Disabled", 800);
+        } else {
+          _sensors->setSettingValue("gps", "1");
+          soundBuzzer(UIEventType::ack);
+          showAlert("GPS: Enabled", 800);
+        }
+        _next_refresh = 0;
+        break;
+      }
+    }
+  }
+}
+
+void UITask::toggleBuzzer() {
+    // Toggle buzzer quiet mode
   #ifdef PIN_BUZZER
     if (buzzer.isQuiet()) {
       buzzer.quiet(false);
       soundBuzzer(UIEventType::ack);
-      showAlert("Buzzer: ON", 600);
+      showAlert("Buzzer: ON", 800);
     } else {
       buzzer.quiet(true);
-      showAlert("Buzzer: OFF", 600);
+      showAlert("Buzzer: OFF", 800);
     }
     _next_refresh = 0;  // trigger refresh
   #endif
 }
-*/

+ 12 - 0
examples/companion_radio/ui-new/UITask.h

@@ -26,6 +26,12 @@ class UITask : public AbstractUITask {
   unsigned long _alert_expiry;
   int _msgcount;
   unsigned long ui_started_at, next_batt_chck;
+  int next_backlight_btn_check = 0;
+#ifdef PIN_STATUS_LED
+  int led_state = 0;
+  int next_led_change = 0;
+  int last_led_increment = 0;
+#endif
 
   UIScreen* splash;
   UIScreen* home;
@@ -37,6 +43,8 @@ class UITask : public AbstractUITask {
   // Button action handlers
   char checkDisplayOn(char c);
   char handleLongPress(char c);
+  char handleDoubleClick(char c);
+  char handleTripleClick(char c);
 
   void setCurrScreen(UIScreen* c);
 
@@ -55,6 +63,10 @@ public:
   bool hasDisplay() const { return _display != NULL; }
   bool isButtonPressed() const;
 
+  void toggleBuzzer();
+  void toggleGPS();
+
+
   // from AbstractUITask
   void msgRead(int msgcount) override;
   void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override;

+ 1 - 0
examples/simple_repeater/main.cpp

@@ -852,6 +852,7 @@ void setup() {
 #ifdef DISPLAY_CLASS
   if (display.begin()) {
     display.startFrame();
+    display.setCursor(0, 0);
     display.print("Please wait...");
     display.endFrame();
   }

+ 1 - 0
examples/simple_room_server/main.cpp

@@ -993,6 +993,7 @@ void setup() {
 #ifdef DISPLAY_CLASS
   if (display.begin()) {
     display.startFrame();
+    display.setCursor(0, 0);
     display.print("Please wait...");
     display.endFrame();
   }

+ 4 - 1
platformio.ini

@@ -79,7 +79,10 @@ platform = nordicnrf52
 build_flags = ${arduino_base.build_flags}
   -D NRF52_PLATFORM
   -D LFS_NO_ASSERT=1
-
+  -D EXTRAFS=1
+lib_deps =
+  ${arduino_base.lib_deps}
+  https://github.com/oltaco/CustomLFS @ 0.2.1
 ; ----------------- RP2040 ---------------------
 
 [rp2040_base]

+ 1 - 2
src/helpers/HeltecV3Board.h

@@ -85,8 +85,7 @@ public:
   }
 
   void powerOff() override {
-    // TODO: re-enable this when there is a definite wake-up source pin:
-    //  enterDeepSleep(0);
+    enterDeepSleep(0);
   }
 
   uint16_t getBattMilliVolts() override {

+ 2 - 2
src/helpers/IdentityStore.h

@@ -9,7 +9,6 @@
 
   using namespace Adafruit_LittleFS_Namespace;
 #endif
-
 #include <Identity.h>
 
 class IdentityStore {
@@ -18,7 +17,8 @@ class IdentityStore {
 public:
   IdentityStore(FILESYSTEM& fs, const char* dir): _fs(&fs), _dir(dir) { }
 
-  void begin() { if (_dir && _dir[0] == '/') { _fs->mkdir(_dir); } }
+  void begin() {
+     if (_dir && _dir[0] == '/') { _fs->mkdir(_dir); } }
   bool load(const char *name, mesh::LocalIdentity& id);
   bool load(const char *name, mesh::LocalIdentity& id, char display_name[], int max_name_sz);
   bool save(const char *name, const mesh::LocalIdentity& id);

+ 2 - 2
src/helpers/nrf52/SerialBLEInterface.cpp

@@ -27,8 +27,8 @@ void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) {
 
   Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
   Bluefruit.configPrphConn(250, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);  // increase MTU
+  Bluefruit.setTxPower(BLE_TX_POWER);
   Bluefruit.begin();
-  Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values
   Bluefruit.setName(device_name);
 
   Bluefruit.Security.setMITM(true);
@@ -80,7 +80,7 @@ void SerialBLEInterface::startAdv() {
    * https://developer.apple.com/library/content/qa/qa1931/_index.html   
    */
   Bluefruit.Advertising.restartOnDisconnect(false); // don't restart automatically as we handle it in onDisconnect
-  Bluefruit.Advertising.setInterval(32, 244);    // in unit of 0.625 ms
+  Bluefruit.Advertising.setInterval(32, 1600);
   Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
   Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds
 

+ 4 - 0
src/helpers/nrf52/SerialBLEInterface.h

@@ -3,6 +3,10 @@
 #include "../BaseSerialInterface.h"
 #include <bluefruit.h>
 
+#ifndef BLE_TX_POWER
+#define BLE_TX_POWER 4
+#endif
+
 class SerialBLEInterface : public BaseSerialInterface {
   BLEUart bleuart;
   bool _isEnabled;

+ 18 - 16
src/helpers/sensors/EnvironmentSensorManager.cpp

@@ -66,8 +66,8 @@ static Adafruit_INA260 INA260;
 #endif
 
 #if ENV_INCLUDE_INA226
-#define TELEM_INA226_ADDRESS    0x44 
-#define TELEM_INA226_SHUNT_VALUE 0.100 
+#define TELEM_INA226_ADDRESS    0x44
+#define TELEM_INA226_SHUNT_VALUE 0.100
 #define TELEM_INA226_MAX_AMP 0.8
 #include <INA226.h>
 static INA226 INA226(TELEM_INA226_ADDRESS);
@@ -85,7 +85,11 @@ static Adafruit_MLX90614 MLX90614;
 static Adafruit_VL53L0X VL53L0X;
 #endif
 
-#if ENV_INCLUDE_GPS && RAK_BOARD
+#if ENV_INCLUDE_GPS && defined(RAK_BOARD) && !defined(RAK_WISMESH_TAG)
+#define RAK_WISBLOCK_GPS
+#endif
+
+#ifdef RAK_WISBLOCK_GPS
 static uint32_t gpsResetPin = 0;
 static bool i2cGPSFlag = false;
 static bool serialGPSFlag = false;
@@ -96,7 +100,7 @@ static SFE_UBLOX_GNSS ublox_GNSS;
 
 bool EnvironmentSensorManager::begin() {
   #if ENV_INCLUDE_GPS
-  #if RAK_BOARD
+  #ifdef RAK_WISBLOCK_GPS
   rakGPSInit();   //probe base board/sockets for GPS
   #else
   initBasicGPS();
@@ -457,7 +461,7 @@ void EnvironmentSensorManager::initBasicGPS() {
   gps_active = false; //Set GPS visibility off until setting is changed
 }
 
-#ifdef RAK_BOARD
+#ifdef RAK_WISBLOCK_GPS
 void EnvironmentSensorManager::rakGPSInit(){
 
   Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX);
@@ -531,7 +535,7 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){
 
 void EnvironmentSensorManager::start_gps() {
   gps_active = true;
-  #ifdef RAK_BOARD
+  #ifdef RAK_WISBLOCK_GPS
     pinMode(gpsResetPin, OUTPUT);
     digitalWrite(gpsResetPin, HIGH);
     return;
@@ -547,7 +551,7 @@ void EnvironmentSensorManager::start_gps() {
 
 void EnvironmentSensorManager::stop_gps() {
   gps_active = false;
-  #ifdef RAK_BOARD
+  #ifdef RAK_WISBLOCK_GPS
     pinMode(gpsResetPin, OUTPUT);
     digitalWrite(gpsResetPin, LOW);
     return;
@@ -568,13 +572,7 @@ void EnvironmentSensorManager::loop() {
 
   if (millis() > next_gps_update) {
     if(gps_active){
-    #ifndef RAK_BOARD
-    if (_location->isValid()) {
-      node_lat = ((double)_location->getLatitude())/1000000.;
-      node_lon = ((double)_location->getLongitude())/1000000.;
-      MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon);
-    }
-    #else
+    #ifdef RAK_WISBLOCK_GPS
     if(i2cGPSFlag){
       node_lat = ((double)ublox_GNSS.getLatitude())/10000000.;
       node_lon = ((double)ublox_GNSS.getLongitude())/10000000.;
@@ -585,8 +583,12 @@ void EnvironmentSensorManager::loop() {
       node_lon = ((double)_location->getLongitude())/1000000.;
       MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon);
     }
-    //else
-    //MESH_DEBUG_PRINTLN("No valid GPS data");
+    #else
+    if (_location->isValid()) {
+      node_lat = ((double)_location->getLatitude())/1000000.;
+      node_lon = ((double)_location->getLongitude())/1000000.;
+      MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon);
+    }
     #endif
     }
     next_gps_update = millis() + 1000;

+ 38 - 16
src/helpers/ui/GxEPDDisplay.cpp

@@ -5,9 +5,6 @@
   #define DISPLAY_ROTATION 3
 #endif
 
-#define SCALE_X   1.5625f   //  200 / 128
-#define SCALE_Y   1.5625f   //  200 / 128
-
 bool GxEPDDisplay::begin() {
   display.epd2.selectSPI(SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
   SPI1.begin();
@@ -19,6 +16,7 @@ bool GxEPDDisplay::begin() {
   display.fillScreen(GxEPD_WHITE);
   display.display(true);
   #if DISP_BACKLIGHT
+  digitalWrite(DISP_BACKLIGHT, LOW);
   pinMode(DISP_BACKLIGHT, OUTPUT);
   #endif
   _init = true;
@@ -27,14 +25,14 @@ bool GxEPDDisplay::begin() {
 
 void GxEPDDisplay::turnOn() {
   if (!_init) begin();
-#if DISP_BACKLIGHT
+#if defined(DISP_BACKLIGHT) && !defined(BACKLIGHT_BTN)
   digitalWrite(DISP_BACKLIGHT, HIGH);
 #endif
   _isOn = true;
 }
 
 void GxEPDDisplay::turnOff() {
-#if DISP_BACKLIGHT
+#if defined(DISP_BACKLIGHT) && !defined(BACKLIGHT_BTN)
   digitalWrite(DISP_BACKLIGHT, LOW);
 #endif
   _isOn = false;
@@ -43,14 +41,17 @@ void GxEPDDisplay::turnOff() {
 void GxEPDDisplay::clear() {
   display.fillScreen(GxEPD_WHITE);
   display.setTextColor(GxEPD_BLACK);
+  display_crc.reset();
 }
 
 void GxEPDDisplay::startFrame(Color bkg) {
   display.fillScreen(GxEPD_WHITE);
   display.setTextColor(_curr_color = GxEPD_BLACK);
+  display_crc.reset();
 }
 
 void GxEPDDisplay::setTextSize(int sz) {
+  display_crc.update<int>(sz);
   switch(sz) {
     case 1:  // Small
       display.setFont(&FreeSans9pt7b);
@@ -68,6 +69,7 @@ void GxEPDDisplay::setTextSize(int sz) {
 }
 
 void GxEPDDisplay::setColor(Color c) {
+  display_crc.update<Color> (c);
   // colours need to be inverted for epaper displays
   if (c == DARK) {
     display.setTextColor(_curr_color = GxEPD_WHITE);
@@ -77,25 +79,41 @@ void GxEPDDisplay::setColor(Color c) {
 }
 
 void GxEPDDisplay::setCursor(int x, int y) {
-  display.setCursor(x*SCALE_X, (y+10)*SCALE_Y);
+  display_crc.update<int>(x);
+  display_crc.update<int>(y);
+  display.setCursor((x+offset_x)*scale_x, (y+offset_y)*scale_y);
 }
 
 void GxEPDDisplay::print(const char* str) {
+  display_crc.update<char>(str, strlen(str));
   display.print(str);
 }
 
 void GxEPDDisplay::fillRect(int x, int y, int w, int h) {
-  display.fillRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, _curr_color);
+  display_crc.update<int>(x);
+  display_crc.update<int>(y);
+  display_crc.update<int>(w);
+  display_crc.update<int>(h);
+  display.fillRect(x*scale_x, y*scale_y, w*scale_x, h*scale_y, _curr_color);
 }
 
 void GxEPDDisplay::drawRect(int x, int y, int w, int h) {
-  display.drawRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, _curr_color);
+  display_crc.update<int>(x);
+  display_crc.update<int>(y);
+  display_crc.update<int>(w);
+  display_crc.update<int>(h);
+  display.drawRect(x*scale_x, y*scale_y, w*scale_x, h*scale_y, _curr_color);
 }
 
 void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
+  display_crc.update<int>(x);
+  display_crc.update<int>(y);
+  display_crc.update<int>(w);
+  display_crc.update<int>(h);
+  display_crc.update<uint8_t>(bits, w * h / 8);
   // Calculate the base position in display coordinates
-  uint16_t startX = x * SCALE_X;
-  uint16_t startY = y * SCALE_Y;
+  uint16_t startX = x * scale_x;
+  uint16_t startY = y * scale_y;
   
   // Width in bytes for bitmap processing
   uint16_t widthInBytes = (w + 7) / 8;
@@ -103,15 +121,15 @@ void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
   // Process the bitmap row by row
   for (uint16_t by = 0; by < h; by++) {
     // Calculate the target y-coordinates for this logical row
-    int y1 = startY + (int)(by * SCALE_Y);
-    int y2 = startY + (int)((by + 1) * SCALE_Y);
+    int y1 = startY + (int)(by * scale_y);
+    int y2 = startY + (int)((by + 1) * scale_y);
     int block_h = y2 - y1;
     
     // Scan across the row bit by bit
     for (uint16_t bx = 0; bx < w; bx++) {
       // Calculate the target x-coordinates for this logical column
-      int x1 = startX + (int)(bx * SCALE_X);
-      int x2 = startX + (int)((bx + 1) * SCALE_X);
+      int x1 = startX + (int)(bx * scale_x);
+      int x2 = startX + (int)((bx + 1) * scale_x);
       int block_w = x2 - x1;
       
       // Get the current bit
@@ -132,9 +150,13 @@ uint16_t GxEPDDisplay::getTextWidth(const char* str) {
   int16_t x1, y1;
   uint16_t w, h;
   display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
-  return ceil((w + 1) / SCALE_X);
+  return ceil((w + 1) / scale_x);
 }
 
 void GxEPDDisplay::endFrame() {
-  display.display(true);
+  uint32_t crc = display_crc.finalize();
+  if (crc != last_display_crc_value) {
+    display.display(true);
+    last_display_crc_value = crc;
+  }
 }

+ 21 - 5
src/helpers/ui/GxEPDDisplay.h

@@ -13,10 +13,9 @@
 #include <Fonts/FreeSansBold12pt7b.h>
 #include <Fonts/FreeSans18pt7b.h>
 
-#define GxEPD2_DISPLAY_CLASS GxEPD2_BW
-#define GxEPD2_DRIVER_CLASS GxEPD2_150_BN  // DEPG0150BN 200x200, SSD1681, (FPC8101), TTGO T5 V2.4.1
-
 #include <epd/GxEPD2_150_BN.h>  // 1.54" b/w
+#include <epd/GxEPD2_213_B74.h> // 2.13" b/w
+#include <CRC32.h>
 
 #include "DisplayDriver.h"
 
@@ -25,15 +24,32 @@
 
 class GxEPDDisplay : public DisplayDriver {
 
+#if defined(HELTEC_MESH_POCKET)
+  GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> display;
+  const float scale_x  = EINK_SCALE_X; 
+  const float scale_y  = EINK_SCALE_Y;
+  const float offset_x = EINK_X_OFFSET;
+  const float offset_y = EINK_Y_OFFSET;
+#else
   GxEPD2_BW<GxEPD2_150_BN, 200> display;
+  const float scale_x  = 1.5625f;
+  const float scale_y  = 1.5625f;
+  const float offset_x = 0;
+  const float offset_y = 10;
+#endif
   bool _init = false;
   bool _isOn = false;
   uint16_t _curr_color;
+  CRC32 display_crc;
+  int last_display_crc_value = 0;
 
 public:
   // there is a margin in y...
-  GxEPDDisplay() : DisplayDriver(128, 128), display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)) {
-  }
+#if defined(HELTEC_MESH_POCKET)
+  GxEPDDisplay() : DisplayDriver(128, 128), display(EINK_DISPLAY_MODEL(PIN_DISPLAY_CS, PIN_DISPLAY_DC, PIN_DISPLAY_RST, PIN_DISPLAY_BUSY)) {}
+#else
+  GxEPDDisplay() : DisplayDriver(128, 128), display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)) {}
+#endif
 
   bool begin();
 

+ 58 - 4
src/helpers/ui/MomentaryButton.cpp

@@ -1,5 +1,7 @@
 #include "MomentaryButton.h"
 
+#define MULTI_CLICK_WINDOW_MS  280
+
 MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse, bool pulldownup) { 
   _pin = pin;
   _reverse = reverse;
@@ -9,6 +11,10 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse
   cancel = 0;
   _long_millis = long_press_millis;
   _threshold = 0;
+  _click_count = 0;
+  _last_click_time = 0;
+  _multi_click_window = MULTI_CLICK_WINDOW_MS;
+  _pending_click = false;
 }
 
 MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, int analog_threshold) {
@@ -20,6 +26,10 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, int analog_t
   cancel = 0;
   _long_millis = long_press_millis;
   _threshold = analog_threshold;
+  _click_count = 0;
+  _last_click_time = 0;
+  _multi_click_window = MULTI_CLICK_WINDOW_MS;
+  _pending_click = false;
 }
 
 void MomentaryButton::begin() {
@@ -35,6 +45,10 @@ bool  MomentaryButton::isPressed() const {
 
 void MomentaryButton::cancelClick() {
   cancel = 1;
+  down_at = 0;
+  _click_count = 0;
+  _last_click_time = 0;
+  _pending_click = false;
 }
 
 bool MomentaryButton::isPressed(int level) const {
@@ -60,13 +74,20 @@ int MomentaryButton::check(bool repeat_click) {
       // button UP
       if (_long_millis > 0) {
         if (down_at > 0 && (unsigned long)(millis() - down_at) < _long_millis) {  // only a CLICK if still within the long_press millis
-          event = BUTTON_EVENT_CLICK;
+            _click_count++;
+            _last_click_time = millis();
+            _pending_click = true;
         }
       } else {
-        event = BUTTON_EVENT_CLICK;  // any UP results in CLICK event when NOT using long_press feature
+          _click_count++;
+          _last_click_time = millis();
+          _pending_click = true;
       }
       if (event == BUTTON_EVENT_CLICK && cancel) {
         event = BUTTON_EVENT_NONE;
+        _click_count = 0;
+        _last_click_time = 0;
+        _pending_click = false;
       }
       down_at = 0;
     }
@@ -77,8 +98,16 @@ int MomentaryButton::check(bool repeat_click) {
   }
 
   if (_long_millis > 0 && down_at > 0 && (unsigned long)(millis() - down_at) >= _long_millis) {
-    event = BUTTON_EVENT_LONG_PRESS;
-    down_at = 0;
+    if (_pending_click) {
+      // long press during multi-click detection - cancel pending clicks
+      cancelClick();
+    } else {
+      event = BUTTON_EVENT_LONG_PRESS;
+      down_at = 0;
+      _click_count = 0;
+      _last_click_time = 0;
+      _pending_click = false;
+    }
   }
   if (down_at > 0 && repeat_click) {
     unsigned long diff = (unsigned long)(millis() - down_at);
@@ -87,5 +116,30 @@ int MomentaryButton::check(bool repeat_click) {
     }
   }
 
+  if (_pending_click && (millis() - _last_click_time) >= _multi_click_window) {
+    if (down_at > 0) {
+      // still pressed - wait for button release before processing clicks
+      return event;
+    }
+    switch (_click_count) {
+      case 1:
+        event = BUTTON_EVENT_CLICK;
+        break;
+      case 2:
+        event = BUTTON_EVENT_DOUBLE_CLICK;
+        break;
+      case 3:
+        event = BUTTON_EVENT_TRIPLE_CLICK;
+        break;
+      default:
+        // For 4+ clicks, treat as triple click?
+        event = BUTTON_EVENT_TRIPLE_CLICK;
+        break;
+    }
+    _click_count = 0;
+    _last_click_time = 0;
+    _pending_click = false;
+  }
+
   return event;
 }

+ 6 - 0
src/helpers/ui/MomentaryButton.h

@@ -5,6 +5,8 @@
 #define BUTTON_EVENT_NONE        0
 #define BUTTON_EVENT_CLICK       1
 #define BUTTON_EVENT_LONG_PRESS  2
+#define BUTTON_EVENT_DOUBLE_CLICK 3
+#define BUTTON_EVENT_TRIPLE_CLICK 4
 
 class MomentaryButton {
   int8_t _pin;
@@ -13,6 +15,10 @@ class MomentaryButton {
   int _long_millis;
   int _threshold;  // analog mode
   unsigned long down_at;
+  uint8_t _click_count;
+  unsigned long _last_click_time;
+  int _multi_click_window;
+  bool _pending_click;
 
   bool isPressed(int level) const;
 

+ 0 - 0
variants/t1000-e/NullDisplayDriver.h → src/helpers/ui/NullDisplayDriver.h


+ 11 - 7
src/helpers/ui/UIScreen.h

@@ -2,13 +2,17 @@
 
 #include "DisplayDriver.h"
 
-#define KEY_LEFT     0xB4
-#define KEY_UP       0xB5
-#define KEY_DOWN     0xB6
-#define KEY_RIGHT    0xB7
-#define KEY_SELECT     10
-#define KEY_ENTER      13
-#define KEY_BACK       27   // Esc
+#define KEY_LEFT           0xB4
+#define KEY_UP             0xB5
+#define KEY_DOWN           0xB6
+#define KEY_RIGHT          0xB7
+#define KEY_SELECT           10
+#define KEY_ENTER            13
+#define KEY_CANCEL           27   // Esc
+#define KEY_HOME           0xF0
+#define KEY_NEXT           0xF1
+#define KEY_PREV           0xF2
+#define KEY_CONTEXT_MENU   0xF3
 
 class UIScreen {
 protected:

+ 5 - 37
variants/generic-e22/target.cpp

@@ -16,47 +16,15 @@ ESP32RTCClock fallback_clock;
 AutoDiscoverRTCClock rtc_clock(fallback_clock);
 SensorManager sensors;
 
-#ifndef LORA_CR
-  #define LORA_CR      5
-#endif
-
 bool radio_init() {
   fallback_clock.begin();
   rtc_clock.begin(Wire);
-  
-#ifdef SX126X_DIO3_TCXO_VOLTAGE
-  float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
-#else
-  float tcxo = 1.6f;
-#endif
-
-#if defined(P_LORA_SCLK)
-  spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
-#endif
-  int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
-  if (status != RADIOLIB_ERR_NONE) {
-    Serial.print("ERROR: radio init failed: ");
-    Serial.println(status);
-    return false;  // fail
-  }
-  
-  radio.setCRC(1);
-  
-#if defined(SX126X_RXEN) && defined(SX126X_TXEN)
-  radio.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
-#endif
-
-#ifdef SX126X_CURRENT_LIMIT
-  radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
-#endif
-#ifdef SX126X_DIO2_AS_RF_SWITCH
-  radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
-#endif
-#ifdef SX126X_RX_BOOSTED_GAIN
-  radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
-#endif
 
-  return true;  // success
+  #if defined(P_LORA_SCLK)
+    return radio.std_init(&spi);
+  #else
+    return radio.std_init();
+  #endif
 }
 
 uint32_t radio_get_rng_seed() {

+ 2 - 4
variants/heltec_vision_master_e213/HeltecE213Board.cpp → variants/heltec_e213/HeltecE213Board.cpp

@@ -23,7 +23,7 @@ void HeltecE213Board::begin() {
   void HeltecE213Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) {
     esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
 
-    // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep 
+    // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
     rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
     rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
 
@@ -44,8 +44,7 @@ void HeltecE213Board::begin() {
   }
 
   void HeltecE213Board::powerOff()  {
-    // TODO: re-enable this when there is a definite wake-up source pin:
-    //  enterDeepSleep(0);
+    enterDeepSleep(0);
   }
 
   uint16_t HeltecE213Board::getBattMilliVolts()  {
@@ -66,4 +65,3 @@ void HeltecE213Board::begin() {
   const char* HeltecE213Board::getManufacturerName() const {
     return "Heltec E213";
   }
-

+ 0 - 10
variants/heltec_vision_master_e213/HeltecE213Board.h → variants/heltec_e213/HeltecE213Board.h

@@ -5,15 +5,6 @@
 #include <helpers/ESP32Board.h>
 #include <driver/rtc_io.h>
 
-// LoRa radio module pins for heltec_vision_master_e213 
-#define  P_LORA_DIO_1   14
-#define  P_LORA_NSS      8
-#define  P_LORA_RESET   12
-#define  P_LORA_BUSY    13
-#define  P_LORA_SCLK     9
-#define  P_LORA_MISO    11
-#define  P_LORA_MOSI    10
-
 class HeltecE213Board : public ESP32Board {
 
 public:
@@ -26,5 +17,4 @@ public:
   void powerOff() override;
   uint16_t getBattMilliVolts() override;
   const char* getManufacturerName() const override ;
-
 };

+ 0 - 0
variants/heltec_vision_master_e213/pins_arduino.h → variants/heltec_e213/pins_arduino.h


+ 47 - 22
variants/heltec_vision_master_e213/platformio.ini → variants/heltec_e213/platformio.ini

@@ -1,14 +1,22 @@
-[Heltec_Vision_Master_E213_base]
+[Heltec_E213_base]
 extends = esp32_base
-board = heltec_vision_master_e213
+board = heltec_e213
 build_flags =
   ${esp32_base.build_flags}
-  -I variants/heltec_vision_master_e213
-  -D HELTEC_VISION_MASTER_E213
+  -I variants/heltec_e213
+  -D Vision_Master_E213
+  -D ARDUINO_USB_CDC_ON_BOOT=1
   -D RADIO_CLASS=CustomSX1262
   -D WRAPPER_CLASS=CustomSX1262Wrapper
-  -D LORA_TX_POWER=22
+  -D P_LORA_DIO_1=14
+  -D P_LORA_NSS=8
+  -D P_LORA_RESET=12
+  -D P_LORA_BUSY=13
+  -D P_LORA_SCLK=9
+  -D P_LORA_MISO=11
+  -D P_LORA_MOSI=10
   -D P_LORA_TX_LED=45
+  -D LORA_TX_POWER=22
   -D PIN_USER_BTN=0
   -D PIN_VEXT_EN=18
   -D PIN_VEXT_EN_ACTIVE=HIGH
@@ -26,17 +34,16 @@ build_flags =
   -D DISP_RST=3
   -D DISP_SCLK=4
   -D DISP_MOSI=6
-  -D Vision_Master_E213
 build_src_filter = ${esp32_base.build_src_filter}
-  +<../variants/heltec_vision_master_e213>
+  +<../variants/heltec_e213>
 lib_deps =
   ${esp32_base.lib_deps}
   https://github.com/Quency-D/heltec-eink-modules/archive/563dd41fd850a1bc3039b8723da4f3a20fe1c800.zip
 
-[env:Heltec_Vision_Master_E213_radio_ble]
-extends = Heltec_Vision_Master_E213_base
+[env:Heltec_E213_companion_radio_ble]
+extends = Heltec_E213_base
 build_flags =
-  ${Heltec_Vision_Master_E213_base.build_flags}
+  ${Heltec_E213_base.build_flags}
   -I examples/companion_radio/ui-new
   -D MAX_CONTACTS=300
   -D MAX_GROUP_CHANNELS=8
@@ -44,43 +51,61 @@ build_flags =
   -D BLE_PIN_CODE=123456   ; dynamic, random PIN
   -D BLE_DEBUG_LOGGING=1
   -D OFFLINE_QUEUE_SIZE=256
-build_src_filter = ${Heltec_Vision_Master_E213_base.build_src_filter}
+build_src_filter = ${Heltec_E213_base.build_src_filter}
+  +<helpers/ui/E213Display.cpp>
+  +<helpers/esp32/*.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${Heltec_E213_base.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+
+[env:Heltec_E213_companion_radio_usb]
+extends = Heltec_E213_base
+build_flags =
+  ${Heltec_E213_base.build_flags}
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=300
+  -D MAX_GROUP_CHANNELS=8
+  -D DISPLAY_CLASS=E213Display
+  -D OFFLINE_QUEUE_SIZE=256
+build_src_filter = ${Heltec_E213_base.build_src_filter}
   +<helpers/ui/E213Display.cpp>
   +<helpers/esp32/*.cpp>
   +<../examples/companion_radio/*.cpp>
   +<../examples/companion_radio/ui-new/*.cpp>
 lib_deps =
-  ${Heltec_Vision_Master_E213_base.lib_deps}
+  ${Heltec_E213_base.lib_deps}
   densaugeo/base64 @ ~1.4.0
 
-[env:Heltec_Vision_Master_E213_repeater]
-extends = Heltec_Vision_Master_E213_base
+[env:Heltec_E213_repeater]
+extends = Heltec_E213_base
 build_flags =
-  ${Heltec_Vision_Master_E213_base.build_flags}
+  ${Heltec_E213_base.build_flags}
   -D DISPLAY_CLASS=E213Display
   -D ADVERT_NAME='"Heltec E213 Repeater"'
   -D ADVERT_LAT=0.0
   -D ADVERT_LON=0.0
-build_src_filter = ${Heltec_Vision_Master_E213_base.build_src_filter}
+build_src_filter = ${Heltec_E213_base.build_src_filter}
   +<helpers/ui/E213Display.cpp>
   +<../examples/simple_repeater>
 lib_deps =
-  ${Heltec_Vision_Master_E213_base.lib_deps}
+  ${Heltec_E213_base.lib_deps}
   ${esp32_ota.lib_deps}
 
-[env:Heltec_Vision_Master_E213_room_server]
-extends = Heltec_Vision_Master_E213_base
+[env:Heltec_E213_room_server]
+extends = Heltec_E213_base
 build_flags =
-  ${Heltec_Vision_Master_E213_base.build_flags}
+  ${Heltec_E213_base.build_flags}
   -D DISPLAY_CLASS=E213Display
   -D ADVERT_NAME='"Heltec E213 Room"'
   -D ADVERT_LAT=0.0
   -D ADVERT_LON=0.0
   -D ADMIN_PASSWORD='"password"'
   -D ROOM_PASSWORD='"hello"'
-build_src_filter = ${Heltec_Vision_Master_E213_base.build_src_filter}
+build_src_filter = ${Heltec_E213_base.build_src_filter}
   +<helpers/ui/E213Display.cpp>
   +<../examples/simple_room_server>
 lib_deps =
-  ${Heltec_Vision_Master_E213_base.lib_deps}
+  ${Heltec_E213_base.lib_deps}
   ${esp32_ota.lib_deps}

+ 0 - 0
variants/heltec_vision_master_e213/target.cpp → variants/heltec_e213/target.cpp


+ 0 - 0
variants/heltec_vision_master_e213/target.h → variants/heltec_e213/target.h


+ 2 - 4
variants/heltec_vision_master_e290/HeltecE290Board.cpp → variants/heltec_e290/HeltecE290Board.cpp

@@ -23,7 +23,7 @@ void HeltecE290Board::begin() {
   void HeltecE290Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) {
     esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
 
-    // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep 
+    // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
     rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
     rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
 
@@ -44,8 +44,7 @@ void HeltecE290Board::begin() {
   }
 
   void HeltecE290Board::powerOff()  {
-    // TODO: re-enable this when there is a definite wake-up source pin:
-    //  enterDeepSleep(0);
+    enterDeepSleep(0);
   }
 
   uint16_t HeltecE290Board::getBattMilliVolts()  {
@@ -66,4 +65,3 @@ void HeltecE290Board::begin() {
   const char* HeltecE290Board::getManufacturerName() const {
     return "Heltec E290";
   }
-

+ 0 - 9
variants/heltec_vision_master_e290/HeltecE290Board.h → variants/heltec_e290/HeltecE290Board.h

@@ -5,15 +5,6 @@
 #include <helpers/ESP32Board.h>
 #include <driver/rtc_io.h>
 
-// LoRa radio module pins for heltec_vision_master_e290 
-#define  P_LORA_DIO_1   14
-#define  P_LORA_NSS      8
-#define  P_LORA_RESET   12
-#define  P_LORA_BUSY    13
-#define  P_LORA_SCLK     9
-#define  P_LORA_MISO    11
-#define  P_LORA_MOSI    10
-
 class HeltecE290Board : public ESP32Board {
 
 public:

+ 0 - 0
variants/heltec_vision_master_e290/pins_arduino.h → variants/heltec_e290/pins_arduino.h


+ 48 - 21
variants/heltec_vision_master_e290/platformio.ini → variants/heltec_e290/platformio.ini

@@ -1,13 +1,21 @@
-[Heltec_Vision_Master_E290_base]
+[Heltec_E290_base]
 extends = esp32_base
-board = heltec_vision_master_e290
+board = heltec_e290
 build_flags =
   ${esp32_base.build_flags}
-  -I variants/heltec_vision_master_e290
-  -D HELTEC_VISION_MASTER_E290
+  -I variants/heltec_e290
+  -D Vision_Master_E290
+  -D ARDUINO_USB_CDC_ON_BOOT=1
   -D RADIO_CLASS=CustomSX1262
   -D WRAPPER_CLASS=CustomSX1262Wrapper
   -D LORA_TX_POWER=22
+  -D P_LORA_DIO_1=14
+  -D P_LORA_NSS=8
+  -D P_LORA_RESET=12
+  -D P_LORA_BUSY=13
+  -D P_LORA_SCLK=9
+  -D P_LORA_MISO=11
+  -D P_LORA_MOSI=10
   -D P_LORA_TX_LED=45
   -D PIN_USER_BTN=0
   -D PIN_VEXT_EN=18
@@ -20,17 +28,36 @@ build_flags =
   -D SX126X_RX_BOOSTED_GAIN=1
   -D PIN_BOARD_SDA=39
   -D PIN_BOARD_SCL=38
-  -D Vision_Master_E290
 build_src_filter = ${esp32_base.build_src_filter}
-  +<../variants/heltec_vision_master_e290>
+  +<../variants/heltec_e290>
 lib_deps =
   ${esp32_base.lib_deps}
   https://github.com/Quency-D/heltec-eink-modules/archive/563dd41fd850a1bc3039b8723da4f3a20fe1c800.zip
 
-[env:Heltec_Vision_Master_E290_radio_ble]
-extends = Heltec_Vision_Master_E290_base
+[env:Heltec_E290_companion_radio_ble]
+extends = Heltec_E290_base
+build_flags =
+  ${Heltec_E290_base.build_flags}
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=300
+  -D MAX_GROUP_CHANNELS=8
+  -D DISPLAY_CLASS=E290Display
+  -D BLE_PIN_CODE=123456   ; dynamic, random PIN
+  -D BLE_DEBUG_LOGGING=1
+  -D OFFLINE_QUEUE_SIZE=256
+build_src_filter = ${Heltec_E290_base.build_src_filter}
+  +<helpers/ui/E290Display.cpp>
+  +<helpers/esp32/*.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${Heltec_E290_base.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+
+[env:Heltec_E290_companion_radio_usb]
+extends = Heltec_E290_base
 build_flags =
-  ${Heltec_Vision_Master_E290_base.build_flags}
+  ${Heltec_E290_base.build_flags}
   -I examples/companion_radio/ui-new
   -D MAX_CONTACTS=300
   -D MAX_GROUP_CHANNELS=8
@@ -38,43 +65,43 @@ build_flags =
   -D BLE_PIN_CODE=123456   ; dynamic, random PIN
   -D BLE_DEBUG_LOGGING=1
   -D OFFLINE_QUEUE_SIZE=256
-build_src_filter = ${Heltec_Vision_Master_E290_base.build_src_filter}
+build_src_filter = ${Heltec_E290_base.build_src_filter}
   +<helpers/ui/E290Display.cpp>
   +<helpers/esp32/*.cpp>
   +<../examples/companion_radio/*.cpp>
   +<../examples/companion_radio/ui-new/*.cpp>
 lib_deps =
-  ${Heltec_Vision_Master_E290_base.lib_deps}
+  ${Heltec_E290_base.lib_deps}
   densaugeo/base64 @ ~1.4.0
 
-[env:Heltec_Vision_Master_E290_repeater]
-extends = Heltec_Vision_Master_E290_base
+[env:Heltec_E290_repeater]
+extends = Heltec_E290_base
 build_flags =
-  ${Heltec_Vision_Master_E290_base.build_flags}
+  ${Heltec_E290_base.build_flags}
   -D DISPLAY_CLASS=E290Display
   -D ADVERT_NAME='"Heltec E290 Repeater"'
   -D ADVERT_LAT=0.0
   -D ADVERT_LON=0.0
-build_src_filter = ${Heltec_Vision_Master_E290_base.build_src_filter}
+build_src_filter = ${Heltec_E290_base.build_src_filter}
   +<helpers/ui/E290Display.cpp>
   +<../examples/simple_repeater>
 lib_deps =
-  ${Heltec_Vision_Master_E290_base.lib_deps}
+  ${Heltec_E290_base.lib_deps}
   ${esp32_ota.lib_deps}
 
-[env:Heltec_Vision_Master_E290_room_server]
-extends = Heltec_Vision_Master_E290_base
+[env:Heltec_E290_room_server]
+extends = Heltec_E290_base
 build_flags =
-  ${Heltec_Vision_Master_E290_base.build_flags}
+  ${Heltec_E290_base.build_flags}
   -D DISPLAY_CLASS=E290Display
   -D ADVERT_NAME='"Heltec E290 Room"'
   -D ADVERT_LAT=0.0
   -D ADVERT_LON=0.0
   -D ADMIN_PASSWORD='"password"'
   -D ROOM_PASSWORD='"hello"'
-build_src_filter = ${Heltec_Vision_Master_E290_base.build_src_filter}
+build_src_filter = ${Heltec_E290_base.build_src_filter}
   +<helpers/ui/E290Display.cpp>
   +<../examples/simple_room_server>
 lib_deps =
-  ${Heltec_Vision_Master_E290_base.lib_deps}
+  ${Heltec_E290_base.lib_deps}
   ${esp32_ota.lib_deps}

+ 0 - 0
variants/heltec_vision_master_e290/target.cpp → variants/heltec_e290/target.cpp


+ 0 - 0
variants/heltec_vision_master_e290/target.h → variants/heltec_e290/target.h


+ 4 - 4
variants/heltec_mesh_solar/platformio.ini

@@ -59,8 +59,8 @@ build_flags =
 extends = Heltec_mesh_solar
 build_flags =
   ${Heltec_mesh_solar.build_flags}
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D BLE_PIN_CODE=123456
 ;  -D BLE_DEBUG_LOGGING=1
   -D OFFLINE_QUEUE_SIZE=256
@@ -77,8 +77,8 @@ lib_deps =
 extends = Heltec_mesh_solar
 build_flags =
   ${Heltec_mesh_solar.build_flags}
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
 ;  -D BLE_PIN_CODE=123456
 ;  -D BLE_DEBUG_LOGGING=1
 ;  -D MESH_PACKET_LOGGING=1

+ 7 - 9
src/helpers/nrf52/T114Board.cpp → variants/heltec_t114/T114Board.cpp

@@ -1,19 +1,17 @@
-#include <Arduino.h>
 #include "T114Board.h"
 
-#include <bluefruit.h>
+#include <Arduino.h>
 #include <Wire.h>
+#include <bluefruit.h>
 
 static BLEDfu bledfu;
 
-static void connect_callback(uint16_t conn_handle)
-{
+static void connect_callback(uint16_t conn_handle) {
   (void)conn_handle;
   MESH_DEBUG_PRINTLN("BLE client connected");
 }
 
-static void disconnect_callback(uint16_t conn_handle, uint8_t reason)
-{
+static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
   (void)conn_handle;
   (void)reason;
 
@@ -60,7 +58,7 @@ void T114Board::begin() {
 
   // Disable unused analog peripherals
   // SAADC channels - only keep what's needed for battery monitoring
-  NRF_SAADC->ENABLE = 0;  // Re-enable only when needed for measurements
+  NRF_SAADC->ENABLE = 0; // Re-enable only when needed for measurements
 
   // COMP - Comparator not used
   NRF_COMP->ENABLE = 0;
@@ -78,10 +76,10 @@ void T114Board::begin() {
 
   pinMode(SX126X_POWER_EN, OUTPUT);
   digitalWrite(SX126X_POWER_EN, HIGH);
-  delay(10);   // give sx1262 some time to power up
+  delay(10); // give sx1262 some time to power up
 }
 
-bool T114Board::startOTAUpdate(const char* id, char reply[]) {
+bool T114Board::startOTAUpdate(const char *id, char reply[]) {
   // Config the peripheral connection with maximum bandwidth
   // more SRAM required by SoftDevice
   // Note: All config***() function must be called before begin()

+ 0 - 13
src/helpers/nrf52/T114Board.h → variants/heltec_t114/T114Board.h

@@ -3,19 +3,6 @@
 #include <MeshCore.h>
 #include <Arduino.h>
 
-// LoRa radio module pins for Heltec T114
-#define  P_LORA_DIO_1     20
-#define  P_LORA_NSS       24
-#define  P_LORA_RESET     25
-#define  P_LORA_BUSY      17
-#define  P_LORA_SCLK      19
-#define  P_LORA_MISO      23
-#define  P_LORA_MOSI      22
-#define  SX126X_POWER_EN  37
-
-#define SX126X_DIO2_AS_RF_SWITCH  true
-#define SX126X_DIO3_TCXO_VOLTAGE   1.8
-
 // built-ins
 #define  PIN_VBAT_READ    4
 #define  PIN_BAT_CTL      6

+ 196 - 0
variants/heltec_t114/platformio.ini

@@ -0,0 +1,196 @@
+;
+; Heltec T114 without display
+;
+[Heltec_t114]
+extends = nrf52_base
+board = heltec_t114
+board_build.ldscript = boards/nrf52840_s140_v6.ld
+build_flags = ${nrf52_base.build_flags}
+  -I lib/nrf52/s140_nrf52_6.1.1_API/include
+  -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
+  -I variants/heltec_t114
+  -I src/helpers/ui
+  -D HELTEC_T114
+  -D P_LORA_DIO_1=20
+  -D P_LORA_NSS=24
+  -D P_LORA_RESET=25
+  -D P_LORA_BUSY=17
+  -D P_LORA_SCLK=19
+  -D P_LORA_MISO=23
+  -D P_LORA_MOSI=22
+  -D P_LORA_TX_LED=35
+  -D RADIO_CLASS=CustomSX1262
+  -D WRAPPER_CLASS=CustomSX1262Wrapper
+  -D LORA_TX_POWER=22
+  -D SX126X_POWER_EN=37
+  -D SX126X_DIO2_AS_RF_SWITCH=true
+  -D SX126X_DIO3_TCXO_VOLTAGE=1.8
+  -D SX126X_CURRENT_LIMIT=140
+  -D SX126X_RX_BOOSTED_GAIN=1
+  -D DISPLAY_CLASS=NullDisplayDriver
+  -D ST7789
+build_src_filter = ${nrf52_base.build_src_filter}
+  +<helpers/*.cpp>
+  +<../variants/heltec_t114>
+lib_deps =
+  ${nrf52_base.lib_deps}
+  stevemarple/MicroNMEA @ ^2.0.6
+  adafruit/Adafruit GFX Library @ ^1.12.1
+debug_tool = jlink
+upload_protocol = nrfutil
+
+[env:Heltec_t114_without_display_repeater]
+extends = Heltec_t114
+build_src_filter = ${Heltec_t114.build_src_filter}
+  +<../examples/simple_repeater>
+
+build_flags =
+  ${Heltec_t114.build_flags}
+  -D ADVERT_NAME='"Heltec_T114 Repeater"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+  -D MAX_NEIGHBOURS=8
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+
+[env:Heltec_t114_without_display_room_server]
+extends = Heltec_t114
+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_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+  -D ROOM_PASSWORD='"hello"'
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+
+[env:Heltec_t114_without_display_companion_radio_ble]
+extends = Heltec_t114
+build_flags =
+  ${Heltec_t114.build_flags}
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
+  -D BLE_PIN_CODE=123456
+;  -D BLE_DEBUG_LOGGING=1
+  -D OFFLINE_QUEUE_SIZE=256
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+build_src_filter = ${Heltec_t114.build_src_filter}
+  +<helpers/nrf52/SerialBLEInterface.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${Heltec_t114.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+
+[env:Heltec_t114_without_display_companion_radio_usb]
+extends = Heltec_t114
+build_flags =
+  ${Heltec_t114.build_flags}
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
+;  -D BLE_PIN_CODE=123456
+;  -D BLE_DEBUG_LOGGING=1
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+build_src_filter = ${Heltec_t114.build_src_filter}
+  +<helpers/nrf52/*.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${Heltec_t114.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+
+;
+; Heltec T114 with ST7789 display
+;
+[Heltec_t114_with_display]
+extends = Heltec_t114
+board = heltec_t114
+board_build.ldscript = boards/nrf52840_s140_v6.ld
+build_flags = ${Heltec_t114.build_flags}
+  -D HELTEC_T114_WITH_DISPLAY
+  -D DISPLAY_CLASS=ST7789Display
+build_src_filter = ${Heltec_t114.build_src_filter}
+  +<helpers/ui/ST7789Display.cpp>
+  +<helpers/ui/MomentaryButton.cpp>
+  +<helpers/ui/OLEDDisplay.cpp>
+  +<helpers/ui/OLEDDisplayFonts.cpp>
+lib_deps =
+  ${Heltec_t114.lib_deps}
+debug_tool = jlink
+upload_protocol = nrfutil
+
+[env:Heltec_t114_repeater]
+extends = Heltec_t114_with_display
+build_src_filter = ${Heltec_t114_with_display.build_src_filter}
+  +<../examples/simple_repeater>
+
+build_flags =
+  ${Heltec_t114_with_display.build_flags}
+  -D ADVERT_NAME='"Heltec_T114 Repeater"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+  -D MAX_NEIGHBOURS=8
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+
+[env:Heltec_t114_room_server]
+extends = Heltec_t114_with_display
+build_src_filter = ${Heltec_t114_with_display.build_src_filter}
+  +<../examples/simple_room_server>
+build_flags =
+  ${Heltec_t114_with_display.build_flags}
+  -D ADVERT_NAME='"Heltec_T114 Room"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+  -D ROOM_PASSWORD='"hello"'
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+
+[env:Heltec_t114_companion_radio_ble]
+extends = Heltec_t114_with_display
+build_flags =
+  ${Heltec_t114_with_display.build_flags}
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=100
+  -D MAX_GROUP_CHANNELS=8
+  -D BLE_PIN_CODE=123456
+;  -D BLE_DEBUG_LOGGING=1
+  -D OFFLINE_QUEUE_SIZE=256
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+build_src_filter = ${Heltec_t114_with_display.build_src_filter}
+  +<helpers/nrf52/SerialBLEInterface.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${Heltec_t114_with_display.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+
+[env:Heltec_t114_companion_radio_usb]
+extends = Heltec_t114_with_display
+build_flags =
+  ${Heltec_t114_with_display.build_flags}
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=100
+  -D MAX_GROUP_CHANNELS=8
+;  -D BLE_PIN_CODE=123456
+;  -D BLE_DEBUG_LOGGING=1
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+build_src_filter = ${Heltec_t114_with_display.build_src_filter}
+  +<helpers/nrf52/*.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${Heltec_t114_with_display.lib_deps}
+  densaugeo/base64 @ ~1.4.0

+ 0 - 0
variants/t114/target.cpp → variants/heltec_t114/target.cpp


+ 8 - 3
variants/t114/target.h → variants/heltec_t114/target.h

@@ -3,14 +3,19 @@
 #define RADIOLIB_STATIC_ONLY 1
 #include <RadioLib.h>
 #include <helpers/radiolib/RadioLibWrappers.h>
-#include <helpers/nrf52/T114Board.h>
+#include <T114Board.h>
 #include <helpers/radiolib/CustomSX1262Wrapper.h>
 #include <helpers/AutoDiscoverRTCClock.h>
 #include <helpers/SensorManager.h>
 #include <helpers/sensors/LocationProvider.h>
+
 #ifdef DISPLAY_CLASS
-  #include <helpers/ui/ST7789Display.h>
-  #include <helpers/ui/MomentaryButton.h>
+#include <helpers/ui/MomentaryButton.h>
+#ifdef HELTEC_T114_WITH_DISPLAY
+#include <helpers/ui/ST7789Display.h>
+#else
+#include "helpers/ui/NullDisplayDriver.h"
+#endif
 #endif
 
 class T114SensorManager : public SensorManager {

+ 0 - 0
variants/t114/variant.cpp → variants/heltec_t114/variant.cpp


+ 0 - 0
variants/t114/variant.h → variants/heltec_t114/variant.h


+ 2 - 4
variants/heltec_vision_master_t190/HeltecT190Board.cpp → variants/heltec_t190/HeltecT190Board.cpp

@@ -23,7 +23,7 @@ void HeltecT190Board::begin() {
   void HeltecT190Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) {
     esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
 
-    // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep 
+    // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
     rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
     rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
 
@@ -44,8 +44,7 @@ void HeltecT190Board::begin() {
   }
 
   void HeltecT190Board::powerOff()  {
-    // TODO: re-enable this when there is a definite wake-up source pin:
-    //  enterDeepSleep(0);
+    enterDeepSleep(0);
   }
 
   uint16_t HeltecT190Board::getBattMilliVolts()  {
@@ -66,4 +65,3 @@ void HeltecT190Board::begin() {
   const char* HeltecT190Board::getManufacturerName() const {
     return "Heltec T190";
   }
-

+ 0 - 9
variants/heltec_vision_master_t190/HeltecT190Board.h → variants/heltec_t190/HeltecT190Board.h

@@ -5,15 +5,6 @@
 #include <helpers/ESP32Board.h>
 #include <driver/rtc_io.h>
 
-// LoRa radio module pins for heltec_vision_master_e290 
-#define  P_LORA_DIO_1   14
-#define  P_LORA_NSS      8
-#define  P_LORA_RESET   12
-#define  P_LORA_BUSY    13
-#define  P_LORA_SCLK     9
-#define  P_LORA_MISO    11
-#define  P_LORA_MOSI    10
-
 class HeltecT190Board : public ESP32Board {
 
 public:

+ 0 - 0
variants/heltec_vision_master_t190/pins_arduino.h → variants/heltec_t190/pins_arduino.h


+ 42 - 19
variants/heltec_vision_master_t190/platformio.ini → variants/heltec_t190/platformio.ini

@@ -1,13 +1,20 @@
-[Heltec_Vision_Master_T190_base]
+[Heltec_T190_base]
 extends = esp32_base
-board = heltec_vision_master_t190
+board = heltec_t190
 build_flags =
   ${esp32_base.build_flags}
-  -I variants/heltec_vision_master_t190
+  -I variants/heltec_t190
   -I src/helpers/ui
   -D HELTEC_VISION_MASTER_T190
   -D RADIO_CLASS=CustomSX1262
   -D WRAPPER_CLASS=CustomSX1262Wrapper
+  -D P_LORA_DIO_1=14
+  -D P_LORA_NSS=8
+  -D P_LORA_RESET=12
+  -D P_LORA_BUSY=13
+  -D P_LORA_SCLK=9
+  -D P_LORA_MISO=11
+  -D P_LORA_MOSI=10
   -D LORA_TX_POWER=22
   -D PIN_USER_BTN=0
   -D PIN_VEXT_EN=5
@@ -31,7 +38,7 @@ build_flags =
   -D ST7789
   -D DISPLAY_CLASS=ST7789Display
 build_src_filter = ${esp32_base.build_src_filter}
-  +<../variants/heltec_vision_master_t190>
+  +<../variants/heltec_t190>
   +<helpers/*.cpp>
   +<helpers/ui/ST7789Display.cpp>
   +<helpers/ui/OLEDDisplay.cpp>
@@ -40,48 +47,64 @@ lib_deps =
   ${esp32_base.lib_deps}
   adafruit/Adafruit GFX Library @ ^1.12.1
 
-[env:Heltec_Vision_Master_T190_radio_ble]
-extends = Heltec_Vision_Master_T190_base
+[env:Heltec_T190_companion_radio_ble]
+extends = Heltec_T190_base
 build_flags =
-  ${Heltec_Vision_Master_T190_base.build_flags}
+  ${Heltec_T190_base.build_flags}
   -I examples/companion_radio/ui-new
   -D MAX_CONTACTS=300
   -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456   ; dynamic, random PIN
   -D BLE_DEBUG_LOGGING=1
   -D OFFLINE_QUEUE_SIZE=256
-build_src_filter = ${Heltec_Vision_Master_T190_base.build_src_filter}
+build_src_filter = ${Heltec_T190_base.build_src_filter}
   +<helpers/esp32/*.cpp>
   +<../examples/companion_radio/*.cpp>
   +<../examples/companion_radio/ui-new/*.cpp>
 lib_deps =
-  ${Heltec_Vision_Master_T190_base.lib_deps}
+  ${Heltec_T190_base.lib_deps}
   densaugeo/base64 @ ~1.4.0
 
-[env:Heltec_Vision_Master_T190_repeater]
-extends = Heltec_Vision_Master_T190_base
+[env:Heltec_T190_companion_radio_usb]
+extends = Heltec_T190_base
 build_flags =
-  ${Heltec_Vision_Master_T190_base.build_flags}
+  ${Heltec_T190_base.build_flags}
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=300
+  -D MAX_GROUP_CHANNELS=8
+  -D OFFLINE_QUEUE_SIZE=256
+build_src_filter = ${Heltec_T190_base.build_src_filter}
+  +<helpers/esp32/*.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${Heltec_T190_base.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+
+[env:Heltec_T190_repeater]
+extends = Heltec_T190_base
+build_flags =
+  ${Heltec_T190_base.build_flags}
   -D ADVERT_NAME='"Heltec T190 Repeater"'
   -D ADVERT_LAT=0.0
   -D ADVERT_LON=0.0
-build_src_filter = ${Heltec_Vision_Master_T190_base.build_src_filter}
+build_src_filter = ${Heltec_T190_base.build_src_filter}
   +<../examples/simple_repeater>
 lib_deps =
-  ${Heltec_Vision_Master_T190_base.lib_deps}
+  ${Heltec_T190_base.lib_deps}
   ${esp32_ota.lib_deps}
 
-[env:Heltec_Vision_Master_T190_room_server]
-extends = Heltec_Vision_Master_T190_base
+[env:Heltec_T190_room_server]
+extends = Heltec_T190_base
 build_flags =
-  ${Heltec_Vision_Master_T190_base.build_flags}
+  ${Heltec_T190_base.build_flags}
   -D ADVERT_NAME='"Heltec T190 Room"'
   -D ADVERT_LAT=0.0
   -D ADVERT_LON=0.0
   -D ADMIN_PASSWORD='"password"'
   -D ROOM_PASSWORD='"hello"'
-build_src_filter = ${Heltec_Vision_Master_T190_base.build_src_filter}
+build_src_filter = ${Heltec_T190_base.build_src_filter}
   +<../examples/simple_room_server>
 lib_deps =
-  ${Heltec_Vision_Master_T190_base.lib_deps}
+  ${Heltec_T190_base.lib_deps}
   ${esp32_ota.lib_deps}

+ 0 - 0
variants/heltec_vision_master_t190/target.cpp → variants/heltec_t190/target.cpp


+ 0 - 0
variants/heltec_vision_master_t190/target.h → variants/heltec_t190/target.h


+ 1 - 1
variants/heltec_v3/platformio.ini

@@ -151,7 +151,7 @@ extends = Heltec_lora32_v3
 build_flags =
   ${Heltec_lora32_v3.build_flags}
   -I examples/companion_radio/ui-new
-  -D MAX_CONTACTS=160
+  -D MAX_CONTACTS=300
   -D MAX_GROUP_CHANNELS=8
   -D DISPLAY_CLASS=SSD1306Display
   -D BLE_PIN_CODE=123456   ; dynamic, random PIN

+ 4 - 4
variants/ikoka_stick_nrf/platformio.ini

@@ -103,8 +103,8 @@ build_src_filter = ${nrf52840_xiao.build_src_filter}
 extends = ikoka_stick_nrf_baseboard
 build_flags =
   ${ikoka_stick_nrf_baseboard.build_flags}
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D BLE_PIN_CODE=123456
   -D OFFLINE_QUEUE_SIZE=256
   -I examples/companion_radio/ui-new
@@ -123,8 +123,8 @@ lib_deps =
 extends = ikoka_stick_nrf_baseboard
 build_flags =
   ${ikoka_stick_nrf_baseboard.build_flags}
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -I examples/companion_radio/ui-new
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1

+ 0 - 0
src/helpers/nrf52/TechoBoard.cpp → variants/lilygo_techo/TechoBoard.cpp


+ 0 - 13
src/helpers/nrf52/TechoBoard.h → variants/lilygo_techo/TechoBoard.h

@@ -3,19 +3,6 @@
 #include <MeshCore.h>
 #include <Arduino.h>
 
-// LoRa radio module pins for LilyGo T-Echo
-#define  P_LORA_DIO_1     20
-#define  P_LORA_NSS       24
-#define  P_LORA_RESET     25
-#define  P_LORA_BUSY      17
-#define  P_LORA_SCLK      19
-#define  P_LORA_MISO      23
-#define  P_LORA_MOSI      22
-#define  SX126X_POWER_EN  37
-
-#define SX126X_DIO2_AS_RF_SWITCH  true
-#define SX126X_DIO3_TCXO_VOLTAGE   1.8
-
 // built-ins
 #define VBAT_MV_PER_LSB   (0.73242188F)   // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096
 

+ 118 - 0
variants/lilygo_techo/platformio.ini

@@ -0,0 +1,118 @@
+[LilyGo_T-Echo]
+extends = nrf52_base
+board = t-echo
+board_build.ldscript = boards/nrf52840_s140_v6.ld
+build_flags = ${nrf52_base.build_flags}
+  -I variants/lilygo_techo
+  -I src/helpers/nrf52
+  -I lib/nrf52/s140_nrf52_6.1.1_API/include
+  -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
+  -D LILYGO_TECHO
+  -D RADIO_CLASS=CustomSX1262
+  -D WRAPPER_CLASS=CustomSX1262Wrapper
+  -D LORA_TX_POWER=22
+  -D P_LORA_DIO_1=20
+  -D P_LORA_NSS=24
+  -D P_LORA_RESET=25
+  -D P_LORA_BUSY=17
+  -D P_LORA_SCLK=19
+  -D P_LORA_MISO=23
+  -D P_LORA_MOSI=22
+  -D SX126X_POWER_EN=37
+  -D SX126X_DIO2_AS_RF_SWITCH=true
+  -D SX126X_DIO3_TCXO_VOLTAGE=1.8
+  -D SX126X_CURRENT_LIMIT=140
+  -D SX126X_RX_BOOSTED_GAIN=1
+  -D P_LORA_TX_LED=LED_GREEN
+  -D DISABLE_DIAGNOSTIC_OUTPUT
+  -D ENV_INCLUDE_GPS=1
+  -D ENV_INCLUDE_BME280=1
+  -D GPS_BAUD_RATE=9600
+  -D PIN_GPS_EN=GPS_EN
+  -D TELEM_BME280_ADDRESS=0x77
+  -D DISPLAY_CLASS=GxEPDDisplay
+  -D BACKLIGHT_BTN=PIN_BUTTON2
+  -D AUTO_OFF_MILLIS=0
+build_src_filter = ${nrf52_base.build_src_filter}
+  +<helpers/*.cpp>
+  +<TechoBoard.cpp>
+  +<helpers/sensors/EnvironmentSensorManager.cpp>
+  +<helpers/ui/GxEPDDisplay.cpp>
+  +<helpers/ui/MomentaryButton.cpp>
+  +<../variants/lilygo_techo>
+lib_deps =
+  ${nrf52_base.lib_deps}
+  stevemarple/MicroNMEA @ ^2.0.6
+  adafruit/Adafruit BME280 Library @ ^2.3.0
+  zinggjm/GxEPD2 @ 1.6.2
+  bakercp/CRC32 @ ^2.0.0
+debug_tool = jlink
+upload_protocol = nrfutil
+
+[env:LilyGo_T-Echo_repeater]
+extends = LilyGo_T-Echo
+build_src_filter = ${LilyGo_T-Echo.build_src_filter}
+  +<../examples/simple_repeater>
+build_flags =
+  ${LilyGo_T-Echo.build_flags}
+  -D ADVERT_NAME='"T-Echo Repeater"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+  -D MAX_NEIGHBOURS=8
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+
+[env:LilyGo_T-Echo_room_server]
+extends = LilyGo_T-Echo
+build_src_filter = ${LilyGo_T-Echo.build_src_filter}
+  +<../examples/simple_room_server>
+build_flags =
+  ${LilyGo_T-Echo.build_flags}
+  -D ADVERT_NAME='"T-Echo Room"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+
+[env:LilyGo_T-Echo_companion_radio_ble]
+extends = LilyGo_T-Echo
+build_flags =
+  ${LilyGo_T-Echo.build_flags}
+  -I src/helpers/ui
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
+  -D QSPIFLASH=1
+  -D BLE_PIN_CODE=123456
+;  -D BLE_DEBUG_LOGGING=1
+  -D OFFLINE_QUEUE_SIZE=256
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+  -D AUTO_SHUTDOWN_MILLIVOLTS=3300
+build_src_filter = ${LilyGo_T-Echo.build_src_filter}
+  +<helpers/nrf52/SerialBLEInterface.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${LilyGo_T-Echo.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+
+[env:LilyGo_T-Echo_companion_radio_usb]
+extends = LilyGo_T-Echo
+build_flags =
+  ${LilyGo_T-Echo.build_flags}
+  -I src/helpers/ui
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=100
+  -D MAX_GROUP_CHANNELS=8
+  -D OFFLINE_QUEUE_SIZE=256
+  -D UI_RECENT_LIST_SIZE=9
+  -D AUTO_SHUTDOWN_MILLIVOLTS=3300
+build_src_filter = ${LilyGo_T-Echo.build_src_filter}
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${LilyGo_T-Echo.lib_deps}
+  densaugeo/base64 @ ~1.4.0

+ 53 - 0
variants/lilygo_techo/target.cpp

@@ -0,0 +1,53 @@
+#include <Arduino.h>
+#include "target.h"
+#include <helpers/ArduinoHelpers.h>
+#include <helpers/sensors/MicroNMEALocationProvider.h>
+
+TechoBoard board;
+
+RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
+
+WRAPPER_CLASS radio_driver(radio, board);
+
+VolatileRTCClock fallback_clock;
+AutoDiscoverRTCClock rtc_clock(fallback_clock);
+
+#ifdef ENV_INCLUDE_GPS
+MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock);
+EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea);
+#else
+EnvironmentSensorManager sensors = EnvironmentSensorManager();
+#endif
+
+
+#ifdef DISPLAY_CLASS
+  DISPLAY_CLASS display;
+  MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
+#endif
+
+bool radio_init() {
+  rtc_clock.begin(Wire);
+  
+  return radio.std_init(&SPI);
+}
+
+uint32_t radio_get_rng_seed() {
+  return radio.random(0x7FFFFFFF);
+}
+
+void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
+  radio.setFrequency(freq);
+  radio.setSpreadingFactor(sf);
+  radio.setBandwidth(bw);
+  radio.setCodingRate(cr);
+}
+
+void radio_set_tx_power(uint8_t dbm) {
+  radio.setOutputPower(dbm);
+}
+
+mesh::LocalIdentity radio_new_identity() {
+  RadioNoiseListener rng(radio);
+  return mesh::LocalIdentity(&rng);  // create new random identity
+}
+

+ 3 - 19
variants/techo/target.h → variants/lilygo_techo/target.h

@@ -3,37 +3,21 @@
 #define RADIOLIB_STATIC_ONLY 1
 #include <RadioLib.h>
 #include <helpers/radiolib/RadioLibWrappers.h>
-#include <helpers/nrf52/TechoBoard.h>
+#include <TechoBoard.h>
 #include <helpers/radiolib/CustomSX1262Wrapper.h>
 #include <helpers/AutoDiscoverRTCClock.h>
 #include <helpers/SensorManager.h>
+#include <helpers/sensors/EnvironmentSensorManager.h>
 #include <helpers/sensors/LocationProvider.h>
 #ifdef DISPLAY_CLASS
   #include <helpers/ui/GxEPDDisplay.h>
   #include <helpers/ui/MomentaryButton.h>
 #endif
 
-class TechoSensorManager : public SensorManager {
-  bool gps_active = false;
-  LocationProvider* _location;
-
-  void start_gps();
-  void stop_gps();
-public:
-  TechoSensorManager(LocationProvider &location): _location(&location) { }
-  bool begin() override;
-  bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override;
-  void loop() override;
-  int getNumSettings() const override;
-  const char* getSettingName(int i) const override;
-  const char* getSettingValue(int i) const override;
-  bool setSettingValue(const char* name, const char* value) override;
-};
-
 extern TechoBoard board;
 extern WRAPPER_CLASS radio_driver;
 extern AutoDiscoverRTCClock rtc_clock;
-extern TechoSensorManager sensors;
+extern EnvironmentSensorManager sensors;
 
 #ifdef DISPLAY_CLASS
   extern DISPLAY_CLASS display;

+ 0 - 0
variants/techo/variant.cpp → variants/lilygo_techo/variant.cpp


+ 13 - 0
variants/techo/variant.h → variants/lilygo_techo/variant.h

@@ -58,6 +58,19 @@
 #define PIN_SPI_SCK             (19)
 #define PIN_SPI_NSS             (24)
 
+////////////////////////////////////////////////////////////////////////////////
+// QSPI FLASH
+
+#define PIN_QSPI_SCK            (46)
+#define PIN_QSPI_CS             (47)
+#define PIN_QSPI_IO0            (44)
+#define PIN_QSPI_IO1            (45)
+#define PIN_QSPI_IO2            (7)
+#define PIN_QSPI_IO3            (5)
+
+#define EXTERNAL_FLASH_DEVICES ZD25WQ16BUIGR
+#define EXTERNAL_FLASH_USE_QSPI
+
 ////////////////////////////////////////////////////////////////////////////////
 // Builtin LEDs
 

+ 3 - 37
variants/lilygo_tlora_c6/target.cpp

@@ -16,49 +16,15 @@ ESP32RTCClock fallback_clock;
 AutoDiscoverRTCClock rtc_clock(fallback_clock);
 SensorManager sensors;
 
-#ifndef LORA_CR
-  #define LORA_CR      5
-#endif
-
 bool radio_init() {
   fallback_clock.begin();
   rtc_clock.begin(Wire);
 
-#ifdef SX126X_DIO3_TCXO_VOLTAGE
-  float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
-#else
-  float tcxo = 1.6f;
-#endif
-
 #if defined(P_LORA_SCLK)
-  spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
-#endif
-  int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
-  if (status != RADIOLIB_ERR_NONE) {
-    Serial.print("ERROR: radio init failed: ");
-    Serial.println(status);
-    return false;  // fail
-  }
-
-  radio.setCRC(1);
-
-#if defined(SX126X_RXEN) && defined(SX126X_TXEN)
-  radio.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
-#endif
-
-#ifdef SX126X_CURRENT_LIMIT
-  radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
-#endif
-
-#ifdef SX126X_DIO2_AS_RF_SWITCH
-  radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
-#endif
-
-#ifdef SX126X_RX_BOOSTED_GAIN
-  radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
+  return radio.std_init(&spi);
+#else
+  return radio.std_init();
 #endif
-
-  return true;  // success
 }
 
 uint32_t radio_get_rng_seed() {

+ 72 - 0
variants/mesh_pocket/MeshPocket.cpp

@@ -0,0 +1,72 @@
+#include <Arduino.h>
+#include "MeshPocket.h"
+#include <bluefruit.h>
+#include <Wire.h>
+
+static BLEDfu bledfu;
+
+static void connect_callback(uint16_t conn_handle)
+{
+  (void)conn_handle;
+  MESH_DEBUG_PRINTLN("BLE client connected");
+}
+
+static void disconnect_callback(uint16_t conn_handle, uint8_t reason)
+{
+  (void)conn_handle;
+  (void)reason;
+
+  MESH_DEBUG_PRINTLN("BLE client disconnected");
+}
+
+void HeltecMeshPocket::begin() {
+  // for future use, sub-classes SHOULD call this from their begin()
+  startup_reason = BD_STARTUP_NORMAL;
+  Serial.begin(115200);
+  pinMode(PIN_VBAT_READ, INPUT);
+
+  pinMode(PIN_USER_BTN, INPUT);
+}
+
+bool HeltecMeshPocket::startOTAUpdate(const char* id, char reply[]) {
+  // Config the peripheral connection with maximum bandwidth
+  // more SRAM required by SoftDevice
+  // Note: All config***() function must be called before begin()
+  Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
+  Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
+
+  Bluefruit.begin(1, 0);
+  // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
+  Bluefruit.setTxPower(4);
+  // Set the BLE device name
+  Bluefruit.setName("MESH_POCKET_OTA");
+
+  Bluefruit.Periph.setConnectCallback(connect_callback);
+  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
+
+  // To be consistent OTA DFU should be added first if it exists
+  bledfu.begin();
+
+  // Set up and start advertising
+  // Advertising packet
+  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
+  Bluefruit.Advertising.addTxPower();
+  Bluefruit.Advertising.addName();
+
+  /* Start Advertising
+    - Enable auto advertising if disconnected
+    - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
+    - Timeout for fast mode is 30 seconds
+    - Start(timeout) with timeout = 0 will advertise forever (until connected)
+
+    For recommended advertising interval
+    https://developer.apple.com/library/content/qa/qa1931/_index.html
+  */
+  Bluefruit.Advertising.restartOnDisconnect(true);
+  Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
+  Bluefruit.Advertising.setFastTimeout(30);   // number of seconds in fast mode
+  Bluefruit.Advertising.start(0);             // 0 = Don't stop advertising after n seconds
+
+  strcpy(reply, "OK - started");
+  return true;
+}

+ 45 - 0
variants/mesh_pocket/MeshPocket.h

@@ -0,0 +1,45 @@
+#pragma once
+
+#include <Arduino.h>
+#include <MeshCore.h>
+
+// built-ins
+#define  PIN_VBAT_READ    29
+#define  PIN_BAT_CTL      34
+#define  MV_LSB   (3000.0F / 4096.0F) // 12-bit ADC with 3.0V input range
+
+class HeltecMeshPocket : public mesh::MainBoard {
+protected:
+  uint8_t startup_reason;
+
+public:
+  void begin();
+  uint8_t getStartupReason() const override { return startup_reason; }
+
+
+
+  uint16_t getBattMilliVolts() override {
+    int adcvalue = 0;
+    analogReadResolution(12);
+    analogReference(AR_INTERNAL_3_0);
+    pinMode(PIN_BAT_CTL, OUTPUT);          // battery adc can be read only ctrl pin set to high
+    pinMode(PIN_VBAT_READ, INPUT);
+    digitalWrite(PIN_BAT_CTL, HIGH);
+
+    delay(10);
+    adcvalue = analogRead(PIN_VBAT_READ);
+    digitalWrite(PIN_BAT_CTL, LOW);
+
+    return (uint16_t)((float)adcvalue * MV_LSB * 4.9);
+  }
+
+  const char* getManufacturerName() const override {
+    return "Heltec MeshPocket";
+  }
+
+  void reboot() override {
+    NVIC_SystemReset();
+  }
+
+  bool startOTAUpdate(const char* id, char reply[]) override;
+};

+ 46 - 47
variants/t114/platformio.ini → variants/mesh_pocket/platformio.ini

@@ -1,53 +1,49 @@
-[nrf52840_t114]
+[Mesh_pocket]
 extends = nrf52_base
+board = heltec_mesh_pocket
 platform_packages = framework-arduinoadafruitnrf52
-build_flags = ${nrf52_base.build_flags}
+board_build.ldscript = boards/nrf52840_s140_v6.ld
+build_flags =  ${nrf52_base.build_flags}
   -I src/helpers/nrf52
   -I lib/nrf52/s140_nrf52_6.1.1_API/include
   -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
-lib_deps =
-  ${nrf52_base.lib_deps}
-  rweather/Crypto @ ^0.4.0
-
-[Heltec_t114]
-extends = nrf52840_t114
-board = heltec_t114
-board_build.ldscript = boards/nrf52840_s140_v6.ld
-build_flags = ${nrf52840_t114.build_flags}
-  -I variants/t114
-  -I src/helpers/ui
-  -DHELTEC_T114
-  -D P_LORA_TX_LED=35
+  -I variants/mesh_pocket
+  -D HELTEC_MESH_POCKET
   -D RADIO_CLASS=CustomSX1262
   -D WRAPPER_CLASS=CustomSX1262Wrapper
   -D LORA_TX_POWER=22
   -D SX126X_CURRENT_LIMIT=140
   -D SX126X_RX_BOOSTED_GAIN=1
-  -D ST7789
-  -D DISPLAY_CLASS=ST7789Display
-build_src_filter = ${nrf52840_t114.build_src_filter}
+  -D EINK_DISPLAY_MODEL=GxEPD2_213_B74
+  -D EINK_SCALE_X=1.953125f
+  -D EINK_SCALE_Y=1.28f
+  -D EINK_X_OFFSET=0
+  -D EINK_Y_OFFSET=10
+  -D DISPLAY_CLASS=GxEPDDisplay
+  -D DISABLE_DIAGNOSTIC_OUTPUT
+build_src_filter = ${nrf52_base.build_src_filter}
   +<helpers/*.cpp>
-  +<helpers/nrf52/T114Board.cpp>
-  +<../variants/t114>
-  +<helpers/ui/ST7789Display.cpp>
-  +<helpers/ui/MomentaryButton.cpp>
-  +<helpers/ui/OLEDDisplay.cpp>
-  +<helpers/ui/OLEDDisplayFonts.cpp>
+  +<../variants/mesh_pocket>
+  +<helpers/ui/GxEPDDisplay.cpp>
 lib_deps =
-  ${nrf52840_t114.lib_deps}
+  ${nrf52_base.lib_deps}
+  adafruit/Adafruit EPD @ 4.6.1
+  rweather/Crypto @ ^0.4.0
   stevemarple/MicroNMEA @ ^2.0.6
-  adafruit/Adafruit GFX Library @ ^1.12.1
+  zinggjm/GxEPD2 @ 1.6.2
+  bakercp/CRC32 @ ^2.0.0
+
 debug_tool = jlink
 upload_protocol = nrfutil
 
-[env:Heltec_t114_repeater]
-extends = Heltec_t114
-build_src_filter = ${Heltec_t114.build_src_filter}
+[env:Mesh_pocket_repeater]
+extends = Mesh_pocket
+build_src_filter = ${Mesh_pocket.build_src_filter}
   +<../examples/simple_repeater>
 
 build_flags =
-  ${Heltec_t114.build_flags}
-  -D ADVERT_NAME='"Heltec_T114 Repeater"'
+  ${Mesh_pocket.build_flags}
+  -D ADVERT_NAME='"Heltec_Mesh_Pocket Repeater"'
   -D ADVERT_LAT=0.0
   -D ADVERT_LON=0.0
   -D ADMIN_PASSWORD='"password"'
@@ -55,13 +51,13 @@ build_flags =
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
-[env:Heltec_t114_room_server]
-extends = Heltec_t114
-build_src_filter = ${Heltec_t114.build_src_filter}
+[env:Mesh_pocket_room_server]
+extends = Mesh_pocket
+build_src_filter = ${Mesh_pocket.build_src_filter}
   +<../examples/simple_room_server>
 build_flags =
-  ${Heltec_t114.build_flags}
-  -D ADVERT_NAME='"Heltec_T114 Room"'
+  ${Mesh_pocket.build_flags}
+  -D ADVERT_NAME='"Heltec_Mesh_Pocket Room"'
   -D ADVERT_LAT=0.0
   -D ADVERT_LON=0.0
   -D ADMIN_PASSWORD='"password"'
@@ -69,41 +65,44 @@ build_flags =
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 
-[env:Heltec_t114_companion_radio_ble]
-extends = Heltec_t114
+[env:Mesh_pocket_companion_radio_ble]
+extends = Mesh_pocket
 build_flags =
-  ${Heltec_t114.build_flags}
+  ${Mesh_pocket.build_flags}
   -I examples/companion_radio/ui-new
   -D MAX_CONTACTS=100
   -D MAX_GROUP_CHANNELS=8
   -D BLE_PIN_CODE=123456
-;  -D BLE_DEBUG_LOGGING=1
   -D OFFLINE_QUEUE_SIZE=256
+  -D AUTO_OFF_MILLIS=0
+;  -D BLE_DEBUG_LOGGING=1
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
-build_src_filter = ${Heltec_t114.build_src_filter}
+
+build_src_filter = ${Mesh_pocket.build_src_filter}
   +<helpers/nrf52/SerialBLEInterface.cpp>
   +<../examples/companion_radio/*.cpp>
   +<../examples/companion_radio/ui-new/*.cpp>
 lib_deps =
-  ${Heltec_t114.lib_deps}
+  ${Mesh_pocket.lib_deps}
   densaugeo/base64 @ ~1.4.0
 
-[env:Heltec_t114_companion_radio_usb]
-extends = Heltec_t114
+[env:Mesh_pocket_companion_radio_usb]
+extends = Mesh_pocket
 build_flags =
-  ${Heltec_t114.build_flags}
+  ${Mesh_pocket.build_flags}
   -I examples/companion_radio/ui-new
   -D MAX_CONTACTS=100
   -D MAX_GROUP_CHANNELS=8
+  -D AUTO_OFF_MILLIS=0
 ;  -D BLE_PIN_CODE=123456
 ;  -D BLE_DEBUG_LOGGING=1
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
-build_src_filter = ${Heltec_t114.build_src_filter}
+build_src_filter = ${Mesh_pocket.build_src_filter}
   +<helpers/nrf52/*.cpp>
   +<../examples/companion_radio/*.cpp>
   +<../examples/companion_radio/ui-new/*.cpp>
 lib_deps =
-  ${Heltec_t114.lib_deps}
+  ${Mesh_pocket.lib_deps}
   densaugeo/base64 @ ~1.4.0

+ 44 - 0
variants/mesh_pocket/target.cpp

@@ -0,0 +1,44 @@
+#include <Arduino.h>
+#include "target.h"
+#include <helpers/ArduinoHelpers.h>
+#include <helpers/sensors/MicroNMEALocationProvider.h>
+
+HeltecMeshPocket board;
+
+RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
+
+WRAPPER_CLASS radio_driver(radio, board);
+
+SensorManager sensors = SensorManager();
+
+VolatileRTCClock fallback_clock;
+AutoDiscoverRTCClock rtc_clock(fallback_clock);
+
+#ifdef DISPLAY_CLASS
+  DISPLAY_CLASS display;
+  MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
+#endif
+
+bool radio_init() {
+  return radio.std_init(&SPI);
+}
+
+uint32_t radio_get_rng_seed() {
+  return radio.random(0x7FFFFFFF);
+}
+
+void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
+  radio.setFrequency(freq);
+  radio.setSpreadingFactor(sf);
+  radio.setBandwidth(bw);
+  radio.setCodingRate(cr);
+}
+
+void radio_set_tx_power(uint8_t dbm) {
+  radio.setOutputPower(dbm);
+}
+
+mesh::LocalIdentity radio_new_identity() {
+  RadioNoiseListener rng(radio);
+  return mesh::LocalIdentity(&rng);  // create new random identity
+}

+ 33 - 0
variants/mesh_pocket/target.h

@@ -0,0 +1,33 @@
+#pragma once
+
+#define RADIOLIB_STATIC_ONLY 1
+#include <RadioLib.h>
+#include <helpers/radiolib/RadioLibWrappers.h>
+#include <helpers/radiolib/CustomSX1262Wrapper.h>
+#include <helpers/AutoDiscoverRTCClock.h>
+#include <helpers/SensorManager.h>
+#include <helpers/sensors/LocationProvider.h>
+#include "MeshPocket.h"
+
+#ifdef DISPLAY_CLASS
+#include <helpers/ui/GxEPDDisplay.h>
+#include <helpers/ui/MomentaryButton.h>
+#endif
+
+extern HeltecMeshPocket board;
+extern WRAPPER_CLASS radio_driver;
+extern AutoDiscoverRTCClock rtc_clock;
+
+#ifdef DISPLAY_CLASS
+  extern DISPLAY_CLASS display;
+  extern MomentaryButton user_btn;
+#endif
+
+bool radio_init();
+uint32_t radio_get_rng_seed();
+void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
+void radio_set_tx_power(uint8_t dbm);
+mesh::LocalIdentity radio_new_identity();
+
+extern SensorManager sensors;
+

+ 15 - 0
variants/mesh_pocket/variant.cpp

@@ -0,0 +1,15 @@
+#include "variant.h"
+#include "nrf.h"
+#include "wiring_constants.h"
+#include "wiring_digital.h"
+
+const int MISO = PIN_SPI1_MISO;
+const int MOSI = PIN_SPI1_MOSI;
+const int SCK = PIN_SPI1_SCK;
+
+const uint32_t g_ADigitalPinMap[] = {
+    // P0 - pins 0 and 1 are hardwired for xtal and should never be enabled
+    0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+
+    // P1
+    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};

+ 124 - 0
variants/mesh_pocket/variant.h

@@ -0,0 +1,124 @@
+/*
+ * variant.h
+ * MIT License
+ */
+
+#pragma once
+
+#include "WVariant.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Low frequency clock source
+
+#define USE_LFXO    // 32.768 kHz crystal oscillator
+#define VARIANT_MCK (64000000ul)
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Power
+
+#define BATTERY_PIN             (0 + 29)
+#define PIN_BAT_CTRL            (32 + 2)
+#define ADC_MULTIPLIER          (4.90F)
+
+#define ADC_RESOLUTION          (14)
+#define BATTERY_SENSE_RES       (12)
+
+#define AREF_VOLTAGE            (3.0)
+
+////////////////////////////////////////////////////////////////////////////////
+// Number of pins
+
+#define PINS_COUNT              (48)
+#define NUM_DIGITAL_PINS        (48)
+#define NUM_ANALOG_INPUTS       (1)
+#define NUM_ANALOG_OUTPUTS      (0)
+
+////////////////////////////////////////////////////////////////////////////////
+// UART pin definition
+
+#define PIN_SERIAL1_RX          (37)
+#define PIN_SERIAL1_TX          (39)
+
+#define PIN_SERIAL2_RX          (7)
+#define PIN_SERIAL2_TX          (8)
+
+////////////////////////////////////////////////////////////////////////////////
+// I2C pin definition
+#define WIRE_INTERFACES_COUNT 	(1)
+
+#define PIN_WIRE_SDA (32+15)
+#define PIN_WIRE_SCL (32+13)
+
+////////////////////////////////////////////////////////////////////////////////
+// Builtin LEDs
+
+#define LED_BUILTIN             (13)
+#define PIN_LED                 LED_BUILTIN
+#define LED_RED                 LED_BUILTIN
+#define LED_BLUE                (-1)            // No blue led, prevents Bluefruit flashing the green LED during advertising
+#define PIN_STATUS_LED          LED_BUILTIN
+
+#define LED_STATE_ON            LOW
+
+////////////////////////////////////////////////////////////////////////////////
+// Builtin buttons
+
+#define PIN_BUTTON1             (32 + 10)
+#define BUTTON_PIN              PIN_BUTTON1
+
+// #define PIN_BUTTON2             (0 + 18)
+// #define BUTTON_PIN2             PIN_BUTTON2
+
+#define PIN_USER_BTN            BUTTON_PIN
+
+////////////////////////////////////////////////////////////////////////////////
+
+// SPI pin definition
+#define SPI_INTERFACES_COUNT    (2)
+
+// Lora
+#define USE_SX1262
+#define SX126X_CS               (0 + 26)
+#define SX126X_DIO1             (0 + 16)
+#define SX126X_BUSY             (0 + 15)
+#define SX126X_RESET            (0 + 12)
+#define SX126X_DIO2_AS_RF_SWITCH true
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+
+#define PIN_SPI_MISO            (32 + 9)
+#define PIN_SPI_MOSI            (0 + 5)
+#define PIN_SPI_SCK             (0 + 4)
+
+#define LORA_CS       SX126X_CS
+#define P_LORA_DIO_1  SX126X_DIO1
+#define P_LORA_NSS    SX126X_CS
+#define P_LORA_RESET  SX126X_RESET
+#define P_LORA_BUSY   SX126X_BUSY
+#define P_LORA_SCLK   PIN_SPI_SCK
+#define P_LORA_MISO   PIN_SPI_MISO
+#define P_LORA_MOSI   PIN_SPI_MOSI
+
+
+////////////////////////////////////////////////////////////////////////////////
+// EInk
+
+#define PIN_DISPLAY_CS                 (24)
+#define PIN_DISPLAY_BUSY               (32 + 6)
+#define PIN_DISPLAY_DC                 (31)
+#define PIN_DISPLAY_RST                (32 + 4)
+
+#define PIN_SPI1_MISO           (-1)
+#define PIN_SPI1_MOSI           (20)
+#define PIN_SPI1_SCK            (22)
+
+
+// GxEPD2 needs that for a panel that is not even used !
+extern const int MISO;
+extern const int MOSI;
+extern const int SCK;
+
+
+#undef HAS_GPS
+#define HAS_GPS 0
+#define HAS_RTC 0

+ 4 - 4
variants/minewsemi_me25ls01/platformio.ini

@@ -52,8 +52,8 @@ lib_deps = ${nrf52840_me25ls01.lib_deps}
 extends = me25ls01
 build_flags = ${me25ls01.build_flags}
   -I examples/companion_radio/ui-orig
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D BLE_PIN_CODE=123456
 ;  -D BLE_DEBUG_LOGGING=1
   -D MESH_PACKET_LOGGING=1
@@ -149,8 +149,8 @@ lib_deps = ${me25ls01.lib_deps}
 extends = me25ls01
 build_flags = ${me25ls01.build_flags}
   -I examples/companion_radio/ui-orig
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   ;-D BLE_PIN_CODE=123456
 ;  -D BLE_DEBUG_LOGGING=1
   -D MESH_PACKET_LOGGING=1

+ 5 - 4
variants/nano_g2_ultra/platformio.ini

@@ -35,13 +35,14 @@ build_flags =
   ${Nano_G2_Ultra.build_flags}
   -I src/helpers/ui
   -I examples/companion_radio/ui-new
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D BLE_PIN_CODE=123456
 ;  -D BLE_DEBUG_LOGGING=0
   -D OFFLINE_QUEUE_SIZE=256
   -D DISPLAY_CLASS=SH1106Display
   -D PIN_BUZZER=4
+  -D QSPIFLASH=1
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 build_src_filter = ${Nano_G2_Ultra.build_src_filter}
@@ -65,8 +66,8 @@ build_flags =
   ${Nano_G2_Ultra.build_flags}
   -I src/helpers/ui
   -I examples/companion_radio/ui-new
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D OFFLINE_QUEUE_SIZE=256
   -D DISPLAY_CLASS=SH1106Display
   -D PIN_BUZZER=4

+ 4 - 0
variants/promicro/PromicroBoard.h

@@ -60,6 +60,10 @@ public:
   void reboot() override {
     NVIC_SystemReset();
   }
+  
+  void powerOff() override {
+    sd_power_system_off();
+  }
 
   bool startOTAUpdate(const char* id, char reply[]) override;
 };

+ 4 - 4
variants/promicro/platformio.ini

@@ -88,8 +88,8 @@ lib_deps = ${Faketec.lib_deps}
 extends = Faketec
 build_flags = ${Faketec.build_flags}
   -I examples/companion_radio/ui-new
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D DISPLAY_CLASS=SSD1306Display
 ; NOTE: DO NOT ENABLE -->  -D MESH_PACKET_LOGGING=1
 ; NOTE: DO NOT ENABLE -->  -D MESH_DEBUG=1
@@ -106,8 +106,8 @@ lib_deps = ${Faketec.lib_deps}
 extends = Faketec
 build_flags = ${Faketec.build_flags}
   -I examples/companion_radio/ui-new
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D BLE_PIN_CODE=123456
   -D BLE_DEBUG_LOGGING=1
   -D OFFLINE_QUEUE_SIZE=256

+ 4 - 4
variants/rak4631/platformio.ini

@@ -70,8 +70,8 @@ build_flags =
   -D PIN_USER_BTN=9
   -D PIN_USER_BTN_ANA=31
   -D DISPLAY_CLASS=SSD1306Display
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
 ; NOTE: DO NOT ENABLE -->  -D MESH_PACKET_LOGGING=1
 ; NOTE: DO NOT ENABLE -->  -D MESH_DEBUG=1
 build_src_filter = ${rak4631.build_src_filter}
@@ -89,8 +89,8 @@ build_flags =
   -D PIN_USER_BTN=9
   -D PIN_USER_BTN_ANA=31
   -D DISPLAY_CLASS=SSD1306Display
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D BLE_PIN_CODE=123456
   -D BLE_DEBUG_LOGGING=1
   -D OFFLINE_QUEUE_SIZE=256

+ 81 - 0
variants/rak_wismesh_tag/RAKWismeshTagBoard.cpp

@@ -0,0 +1,81 @@
+#include <Arduino.h>
+#include "RAKWismeshTagBoard.h"
+
+#include <bluefruit.h>
+#include <Wire.h>
+
+static BLEDfu bledfu;
+
+static void connect_callback(uint16_t conn_handle) {
+  (void)conn_handle;
+  MESH_DEBUG_PRINTLN("BLE client connected");
+}
+
+static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
+  (void)conn_handle;
+  (void)reason;
+
+  MESH_DEBUG_PRINTLN("BLE client disconnected");
+}
+
+void RAKWismeshTagBoard::begin() {
+  // for future use, sub-classes SHOULD call this from their begin()
+  startup_reason = BD_STARTUP_NORMAL;
+  pinMode(PIN_VBAT_READ, INPUT);
+  pinMode(PIN_USER_BTN, INPUT_PULLUP);
+
+  Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
+  Wire.begin();
+
+  pinMode(SX126X_POWER_EN, OUTPUT);
+  digitalWrite(SX126X_POWER_EN, HIGH);
+  delay(10);   // give sx1262 some time to power up
+}
+
+bool RAKWismeshTagBoard::startOTAUpdate(const char* id, char reply[]) {
+  // Config the peripheral connection with maximum bandwidth
+  // more SRAM required by SoftDevice
+  // Note: All config***() function must be called before begin()
+  Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
+  Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
+
+  Bluefruit.begin(1, 0);
+  // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
+  Bluefruit.setTxPower(4);
+  // Set the BLE device name
+  Bluefruit.setName("WISMESHTAG_OTA");
+
+  Bluefruit.Periph.setConnectCallback(connect_callback);
+  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
+
+  // To be consistent OTA DFU should be added first if it exists
+  bledfu.begin();
+
+  // Set up and start advertising
+  // Advertising packet
+  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
+  Bluefruit.Advertising.addTxPower();
+  Bluefruit.Advertising.addName();
+
+  /* Start Advertising
+    - Enable auto advertising if disconnected
+    - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
+    - Timeout for fast mode is 30 seconds
+    - Start(timeout) with timeout = 0 will advertise forever (until connected)
+
+    For recommended advertising interval
+    https://developer.apple.com/library/content/qa/qa1931/_index.html
+  */
+  Bluefruit.Advertising.restartOnDisconnect(true);
+  Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
+  Bluefruit.Advertising.setFastTimeout(30);   // number of seconds in fast mode
+  Bluefruit.Advertising.start(0);             // 0 = Don't stop advertising after n seconds
+
+  uint8_t mac_addr[6];
+  memset(mac_addr, 0, sizeof(mac_addr));
+  Bluefruit.getAddr(mac_addr);
+  sprintf(reply, "OK - mac: %02X:%02X:%02X:%02X:%02X:%02X",
+      mac_addr[5], mac_addr[4], mac_addr[3], mac_addr[2], mac_addr[1], mac_addr[0]);
+
+  return true;
+}

+ 80 - 0
variants/rak_wismesh_tag/RAKWismeshTagBoard.h

@@ -0,0 +1,80 @@
+#pragma once
+
+#include <MeshCore.h>
+#include <Arduino.h>
+
+// built-ins
+#define  PIN_VBAT_READ    5
+#define  ADC_MULTIPLIER   (3 * 1.73 * 1.187 * 1000)
+
+class RAKWismeshTagBoard : public mesh::MainBoard {
+protected:
+  uint8_t startup_reason;
+
+public:
+  void begin();
+  uint8_t getStartupReason() const override { return startup_reason; }
+
+#if defined(P_LORA_TX_LED) && defined(LED_STATE_ON)
+  void onBeforeTransmit() override {
+    digitalWrite(P_LORA_TX_LED, LED_STATE_ON);   // turn TX LED on
+  }
+  void onAfterTransmit() override {
+    digitalWrite(P_LORA_TX_LED, !LED_STATE_ON);   // turn TX LED off
+  }
+#endif
+
+  #define BATTERY_SAMPLES 8
+
+  uint16_t getBattMilliVolts() override {
+    analogReadResolution(12);
+
+    uint32_t raw = 0;
+    for (int i = 0; i < BATTERY_SAMPLES; i++) {
+      raw += analogRead(PIN_VBAT_READ);
+    }
+    raw = raw / BATTERY_SAMPLES;
+
+    return (ADC_MULTIPLIER * raw) / 4096;
+  }
+
+  const char* getManufacturerName() const override {
+    return "RAK WisMesh Tag";
+  }
+
+  void reboot() override {
+    NVIC_SystemReset();
+  }
+
+  bool startOTAUpdate(const char* id, char reply[]) override;
+
+  void powerOff() override {
+    #ifdef BUZZER_EN
+        digitalWrite(BUZZER_EN, LOW);
+    #endif
+
+    #ifdef PIN_GPS_EN
+        digitalWrite(PIN_GPS_EN, LOW);
+    #endif
+
+    // set led on and wait for button release before poweroff
+    #ifdef LED_PIN
+    digitalWrite(LED_PIN, HIGH);
+    #endif
+    #ifdef BUTTON_PIN
+    while(digitalRead(BUTTON_PIN));
+    #endif
+    #ifdef LED_GREEN
+    digitalWrite(LED_GREEN, LOW);
+    #endif
+    #ifdef LED_BLUE
+    digitalWrite(LED_BLUE, LOW);
+    #endif
+
+    #ifdef BUTTON_PIN
+    nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH);
+    #endif
+
+    sd_power_system_off();
+  }
+};

+ 116 - 0
variants/rak_wismesh_tag/platformio.ini

@@ -0,0 +1,116 @@
+[rak_wismesh_tag]
+extends = nrf52_base
+platform = https://github.com/maxgerhardt/platform-nordicnrf52.git#rak
+board = wiscore_rak4631
+board_check = true
+build_flags = ${nrf52_base.build_flags}
+  ${sensor_base.build_flags}
+  -I variants/rak_wismesh_tag
+  -I src/helpers/ui
+  -D RAK_WISMESH_TAG
+  -D RAK_BOARD
+  -D P_LORA_TX_LED=LED_GREEN
+  -D P_LORA_DIO_1=SX126X_DIO1
+  -D P_LORA_NSS=PIN_SPI_NSS
+  -D P_LORA_RESET=SX126X_RESET
+  -D P_LORA_BUSY=SX126X_BUSY
+  -D P_LORA_SCLK=PIN_SPI_SCK
+  -D P_LORA_MISO=PIN_SPI_MISO
+  -D P_LORA_MOSI=PIN_SPI_MOSI
+  -D RADIO_CLASS=CustomSX1262
+  -D WRAPPER_CLASS=CustomSX1262Wrapper
+  -D DISPLAY_CLASS=NullDisplayDriver
+  -D LORA_TX_POWER=22
+  -D SX126X_CURRENT_LIMIT=140
+  -D SX126X_RX_BOOSTED_GAIN=1
+  -D PIN_BUZZER=21
+  -D PIN_BOARD_SDA=PIN_WIRE_SDA
+  -D PIN_BOARD_SCL=PIN_WIRE_SCL
+build_src_filter = ${nrf52_base.build_src_filter}
+  +<../variants/rak_wismesh_tag>
+  +<helpers/ui/buzzer.cpp>
+  +<helpers/ui/MomentaryButton.cpp>
+  +<helpers/ui/NullDisplayDriver.cpp>
+  +<helpers/sensors>
+lib_deps =
+  ${nrf52_base.lib_deps}
+  ${sensor_base.lib_deps}
+
+[env:RAK_WisMesh_Tag_Repeater]
+extends = rak_wismesh_tag
+build_flags =
+  ${rak_wismesh_tag.build_flags}
+  -D ADVERT_NAME='"RAK WM Repeater"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+  -D MAX_NEIGHBOURS=8
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+build_src_filter = ${rak_wismesh_tag.build_src_filter}
+  +<../examples/simple_repeater>
+
+[env:RAK_WisMesh_Tag_room_server]
+extends = rak_wismesh_tag
+build_flags =
+  ${rak_wismesh_tag.build_flags}
+  -D ADVERT_NAME='"RAK WM Room"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+  -D ROOM_PASSWORD='"hello"'
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+build_src_filter = ${rak_wismesh_tag.build_src_filter}
+  +<../examples/simple_room_server>
+
+[env:RAK_WisMesh_Tag_companion_radio_usb]
+extends = rak_wismesh_tag
+build_flags =
+  ${rak_wismesh_tag.build_flags}
+  -I examples/companion_radio/ui-orig
+  -D MAX_CONTACTS=100
+  -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 = ${rak_wismesh_tag.build_src_filter}
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-orig/*.cpp>
+lib_deps =
+  ${rak_wismesh_tag.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+  end2endzone/NonBlockingRTTTL@^1.3.0
+
+[env:RAK_WisMesh_Tag_companion_radio_ble]
+extends = rak_wismesh_tag
+build_flags =
+  ${rak_wismesh_tag.build_flags}
+  -I examples/companion_radio/ui-orig
+  -D MAX_CONTACTS=100
+  -D MAX_GROUP_CHANNELS=8
+  -D BLE_PIN_CODE=123456
+  -D BLE_DEBUG_LOGGING=1
+  -D OFFLINE_QUEUE_SIZE=256
+;  -D MESH_PACKET_LOGGING=1
+  -D MESH_DEBUG=1
+build_src_filter = ${rak_wismesh_tag.build_src_filter}
+  +<helpers/nrf52/SerialBLEInterface.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-orig/*.cpp>
+lib_deps =
+  ${rak4631.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+  end2endzone/NonBlockingRTTTL@^1.3.0
+
+[env:RAK_WisMesh_Tag_sensor]
+extends = rak4631
+build_flags =
+  ${rak4631.build_flags}
+  -D ADVERT_NAME='"RAK WM Sensor"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+;  -D MESH_PACKET_LOGGING=1
+  -D MESH_DEBUG=1
+build_src_filter = ${rak4631.build_src_filter}
+  +<../examples/simple_sensor>

+ 54 - 0
variants/rak_wismesh_tag/target.cpp

@@ -0,0 +1,54 @@
+#include <Arduino.h>
+#include "target.h"
+#include <helpers/ArduinoHelpers.h>
+
+RAKWismeshTagBoard board;
+
+#ifndef PIN_USER_BTN
+  #define PIN_USER_BTN (-1)
+#endif
+
+#ifdef DISPLAY_CLASS
+  DISPLAY_CLASS display;
+  MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true);
+#endif
+
+RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
+
+WRAPPER_CLASS radio_driver(radio, board);
+
+VolatileRTCClock fallback_clock;
+AutoDiscoverRTCClock rtc_clock(fallback_clock);
+
+#if ENV_INCLUDE_GPS
+  #include <helpers/sensors/MicroNMEALocationProvider.h>
+  MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1);
+  EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea);
+#else
+  EnvironmentSensorManager sensors;
+#endif
+
+bool radio_init() {
+  rtc_clock.begin(Wire);
+  return radio.std_init(&SPI);
+}
+
+uint32_t radio_get_rng_seed() {
+  return radio.random(0x7FFFFFFF);
+}
+
+void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
+  radio.setFrequency(freq);
+  radio.setSpreadingFactor(sf);
+  radio.setBandwidth(bw);
+  radio.setCodingRate(cr);
+}
+
+void radio_set_tx_power(uint8_t dbm) {
+  radio.setOutputPower(dbm);
+}
+
+mesh::LocalIdentity radio_new_identity() {
+  RadioNoiseListener rng(radio);
+  return mesh::LocalIdentity(&rng);  // create new random identity
+}

+ 27 - 0
variants/rak_wismesh_tag/target.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#define RADIOLIB_STATIC_ONLY 1
+#include <RadioLib.h>
+#include <helpers/radiolib/RadioLibWrappers.h>
+#include <RAKWismeshTagBoard.h>
+#include <helpers/radiolib/CustomSX1262Wrapper.h>
+#include <helpers/AutoDiscoverRTCClock.h>
+#include <helpers/sensors/EnvironmentSensorManager.h>
+
+#ifdef DISPLAY_CLASS
+  #include <helpers/ui/NullDisplayDriver.h>
+  extern DISPLAY_CLASS display;
+  #include <helpers/ui/MomentaryButton.h>
+  extern MomentaryButton user_btn;
+#endif
+
+extern RAKWismeshTagBoard board;
+extern WRAPPER_CLASS radio_driver;
+extern AutoDiscoverRTCClock rtc_clock;
+extern EnvironmentSensorManager sensors;
+
+bool radio_init();
+uint32_t radio_get_rng_seed();
+void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
+void radio_set_tx_power(uint8_t dbm);
+mesh::LocalIdentity radio_new_identity();

+ 21 - 0
variants/rak_wismesh_tag/variant.cpp

@@ -0,0 +1,21 @@
+#include "variant.h"
+#include "nrf.h"
+#include "wiring_constants.h"
+#include "wiring_digital.h"
+
+const uint32_t g_ADigitalPinMap[] = {
+  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+  32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47
+};
+
+void initVariant() {
+  // LEDs
+  pinMode(LED_BLUE, OUTPUT);
+  pinMode(LED_GREEN, OUTPUT);
+  digitalWrite(LED_BLUE, LOW);
+  digitalWrite(LED_GREEN, LOW);
+
+  // GPS
+  pinMode(PIN_GPS_EN, OUTPUT);
+  digitalWrite(PIN_GPS_EN, HIGH);
+}

+ 121 - 0
variants/rak_wismesh_tag/variant.h

@@ -0,0 +1,121 @@
+/*
+ * variant.h
+ * Copyright (C) 2023 Seeed K.K.
+ * MIT License
+ */
+
+#pragma once
+
+#include "WVariant.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Low frequency clock source
+
+#define USE_LFXO    // 32.768 kHz crystal oscillator
+#define VARIANT_MCK (64000000ul)
+
+#define WIRE_INTERFACES_COUNT   (1)
+#define PIN_TXCO                (21)
+////////////////////////////////////////////////////////////////////////////////
+// Power
+
+#define PIN_PWR_EN              (12)
+
+#define BATTERY_PIN             (5)
+#define ADC_MULTIPLIER          (1.73F)
+
+#define ADC_RESOLUTION          (14)
+#define BATTERY_SENSE_RES       (12)
+
+#define AREF_VOLTAGE            (3.0)
+
+////////////////////////////////////////////////////////////////////////////////
+// Number of pins
+
+#define PINS_COUNT              (48)
+#define NUM_DIGITAL_PINS        (48)
+#define NUM_ANALOG_INPUTS       (6)
+#define NUM_ANALOG_OUTPUTS      (0)
+
+////////////////////////////////////////////////////////////////////////////////
+// UART pin definition
+
+#define PIN_SERIAL1_RX          (15)
+#define PIN_SERIAL1_TX          (16)
+
+////////////////////////////////////////////////////////////////////////////////
+// I2C pin definition
+#define WIRE_INTERFACES_COUNT   (1)
+#define PIN_WIRE_SDA            (25)
+#define PIN_WIRE_SCL            (24)
+
+////////////////////////////////////////////////////////////////////////////////
+// SPI pin definition
+
+#define SPI_INTERFACES_COUNT    (2)
+
+#define PIN_SPI_MISO            (45)
+#define PIN_SPI_MOSI            (44)
+#define PIN_SPI_SCK             (43)
+#define PIN_SPI_NSS             (42)
+
+////////////////////////////////////////////////////////////////////////////////
+// Builtin LEDs
+
+#define LED_RED                 (-1)
+#define LED_BLUE                (36)
+#define LED_GREEN               (35)
+
+//#define PIN_STATUS_LED          LED_BLUE
+#define LED_BUILTIN             LED_GREEN
+#define LED_PIN                 LED_GREEN
+#define LED_STATE_ON            HIGH
+
+////////////////////////////////////////////////////////////////////////////////
+// Builtin buttons
+
+#define PIN_BUTTON1             (9)
+#define BUTTON_PIN              PIN_BUTTON1
+#define PIN_USER_BTN            BUTTON_PIN
+
+#define PIN_BUTTON2             (12)
+#define BUTTON_PIN2             PIN_BUTTON2
+
+////////////////////////////////////////////////////////////////////////////////
+// Lora
+
+#define USE_SX1262
+#define LORA_CS                 (42)
+#define SX126X_DIO1             (47)
+#define SX126X_BUSY             (46)
+#define SX126X_RESET            (38)
+#define SX126X_POWER_EN         (37)
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+
+////////////////////////////////////////////////////////////////////////////////
+// SPI1
+
+#define PIN_SPI1_MISO           (29)
+#define PIN_SPI1_MOSI           (30)
+#define PIN_SPI1_SCK            (3)
+
+// GxEPD2 needs that for a panel that is not even used !
+extern const int MISO;
+extern const int MOSI;
+extern const int SCK;
+
+////////////////////////////////////////////////////////////////////////////////
+// GPS
+
+#define PIN_GPS_RX              (PIN_SERIAL1_TX)
+#define PIN_GPS_TX              (PIN_SERIAL1_RX)
+#define PIN_GPS_PPS             (17)
+#define PIN_GPS_EN              (34)
+
+///////////////////////////////////////////////////////////////////////////////
+// OTHER PINS
+#define PIN_AREF                (2)
+#define PIN_NFC1                (9)
+#define PIN_NFC2                (10)
+#define PIN_BUZZER              (21)

+ 0 - 0
src/helpers/rp2040/PicoWBoard.cpp → variants/rpi_picow/PicoWBoard.cpp


+ 0 - 13
src/helpers/rp2040/PicoWBoard.h → variants/rpi_picow/PicoWBoard.h

@@ -3,19 +3,6 @@
 #include <MeshCore.h>
 #include <Arduino.h>
 
-// LoRa radio module pins for PicoW
-#define  P_LORA_DIO_1   20
-#define  P_LORA_NSS     3
-#define  P_LORA_RESET   15
-#define  P_LORA_BUSY    2
-#define  P_LORA_SCLK    10
-#define  P_LORA_MISO    12
-#define  P_LORA_MOSI    11
-//#define  SX126X_POWER_EN  ??? // Not Sure
-
-#define SX126X_DIO2_AS_RF_SWITCH  true
-#define SX126X_DIO3_TCXO_VOLTAGE   1.8
-
 // built-ins
 #define  PIN_VBAT_READ    26
 #define  ADC_MULTIPLIER   (3.1 * 3.3 * 1000) // MT Uses 3.1

+ 37 - 28
variants/picow/platformio.ini → variants/rpi_picow/platformio.ini

@@ -1,26 +1,35 @@
-[picow]
+[rpi_picow]
 extends = rp2040_base
 platform = https://github.com/maxgerhardt/platform-raspberrypi.git
 board = rpipicow
 board_build.core = earlephilhower
 board_build.filesystem_size = 0.5m
 build_flags = ${rp2040_base.build_flags}
-  -I variants/picow
+  -I variants/rpi_picow
 ;  -D PICOW
 ;  -D HW_SPI1_DEVICE
+  -D P_LORA_DIO_1=20
+  -D P_LORA_NSS=3
+  -D P_LORA_RESET=15
+  -D P_LORA_BUSY=2
+  -D P_LORA_SCLK=10
+  -D P_LORA_MISO=12
+  -D P_LORA_MOSI=11
+  -D SX126X_DIO2_AS_RF_SWITCH=true
+  -D SX126X_DIO3_TCXO_VOLTAGE=1.8
   -D SX126X_CURRENT_LIMIT=130
+  -D SX126X_RX_BOOSTED_GAIN=1
+  -D LORA_TX_POWER=22
   -D RADIO_CLASS=CustomSX1262
   -D WRAPPER_CLASS=CustomSX1262Wrapper
-  -D LORA_TX_POWER=22
-  -D SX126X_RX_BOOSTED_GAIN=1
 build_src_filter = ${rp2040_base.build_src_filter}
-  +<helpers/rp2040/PicoWBoard.cpp>
-  +<../variants/picow>
+  +<PicoWBoard.cpp>
+  +<../variants/rpi_picow>
 lib_deps = ${rp2040_base.lib_deps}
 
 [env:PicoW_Repeater]
-extends = picow
-build_flags = ${picow.build_flags}
+extends = rpi_picow
+build_flags = ${rpi_picow.build_flags}
   -D ADVERT_NAME='"PicoW Repeater"'
   -D ADVERT_LAT=0.0
   -D ADVERT_LON=0.0
@@ -28,12 +37,12 @@ build_flags = ${picow.build_flags}
   -D MAX_NEIGHBOURS=8
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
-build_src_filter = ${picow.build_src_filter}
+build_src_filter = ${rpi_picow.build_src_filter}
   +<../examples/simple_repeater>
 
 [env:PicoW_room_server]
-extends = picow
-build_flags = ${picow.build_flags}
+extends = rpi_picow
+build_flags = ${rpi_picow.build_flags}
   -D ADVERT_NAME='"Test Room"'
   -D ADVERT_LAT=0.0
   -D ADVERT_LON=0.0
@@ -41,38 +50,38 @@ build_flags = ${picow.build_flags}
   -D ROOM_PASSWORD='"hello"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
-build_src_filter = ${picow.build_src_filter}
+build_src_filter = ${rpi_picow.build_src_filter}
   +<../examples/simple_room_server>
 
 [env:PicoW_companion_radio_usb]
-extends = picow
-build_flags = ${picow.build_flags}
+extends = rpi_picow
+build_flags = ${rpi_picow.build_flags}
   -D MAX_CONTACTS=100
   -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 = ${picow.build_src_filter}
+build_src_filter = ${rpi_picow.build_src_filter}
   +<../examples/companion_radio/*.cpp>
-lib_deps = ${picow.lib_deps}
+lib_deps = ${rpi_picow.lib_deps}
   densaugeo/base64 @ ~1.4.0
 
 ; [env:PicoW_companion_radio_ble]
-; extends = picow
-; build_flags = ${picow.build_flags}
+; extends = rpi_picow
+; build_flags = ${rpi_picow.build_flags}
 ;   -D MAX_CONTACTS=100
 ;   -D MAX_GROUP_CHANNELS=8
 ;   -D BLE_PIN_CODE=123456
 ;   -D BLE_DEBUG_LOGGING=1
 ; ;  -D MESH_PACKET_LOGGING=1
 ; ;  -D MESH_DEBUG=1
-; build_src_filter = ${picow.build_src_filter}
+; build_src_filter = ${rpi_picow.build_src_filter}
 ;   +<../examples/companion_radio/*.cpp>
-; lib_deps = ${picow.lib_deps}
+; lib_deps = ${rpi_picow.lib_deps}
 ;   densaugeo/base64 @ ~1.4.0
 
 ; [env:PicoW_companion_radio_wifi]
-; extends = picow
-; build_flags = ${picow.build_flags}
+; extends = rpi_picow
+; build_flags = ${rpi_picow.build_flags}
 ;   -D MAX_CONTACTS=100
 ;   -D MAX_GROUP_CHANNELS=8
 ;   -D WIFI_DEBUG_LOGGING=1
@@ -80,19 +89,19 @@ lib_deps = ${picow.lib_deps}
 ;   -D WIFI_PWD='"mypwd"'
 ; ;  -D MESH_PACKET_LOGGING=1
 ; ;  -D MESH_DEBUG=1
-; build_src_filter = ${picow.build_src_filter}
+; build_src_filter = ${rpi_picow.build_src_filter}
 ;   +<../examples/companion_radio/*.cpp>
-; lib_deps = ${picow.lib_deps}
+; lib_deps = ${rpi_picow.lib_deps}
 ;   densaugeo/base64 @ ~1.4.0
 
 [env:PicoW_terminal_chat]
-extends = picow
-build_flags = ${picow.build_flags}
+extends = rpi_picow
+build_flags = ${rpi_picow.build_flags}
   -D MAX_CONTACTS=100
   -D MAX_GROUP_CHANNELS=1
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
-build_src_filter = ${picow.build_src_filter}
+build_src_filter = ${rpi_picow.build_src_filter}
   +<../examples/simple_secure_chat/main.cpp>
-lib_deps = ${picow.lib_deps}
+lib_deps = ${rpi_picow.lib_deps}
   densaugeo/base64 @ ~1.4.0

+ 0 - 0
variants/picow/target.cpp → variants/rpi_picow/target.cpp


+ 1 - 1
variants/picow/target.h → variants/rpi_picow/target.h

@@ -3,7 +3,7 @@
 #define RADIOLIB_STATIC_ONLY 1
 #include <RadioLib.h>
 #include <helpers/radiolib/RadioLibWrappers.h>
-#include <helpers/rp2040/PicoWBoard.h>
+#include <PicoWBoard.h>
 #include <helpers/radiolib/CustomSX1262Wrapper.h>
 #include <helpers/AutoDiscoverRTCClock.h>
 #include <helpers/SensorManager.h>

+ 4 - 4
variants/sensecap_solar/platformio.ini

@@ -76,8 +76,8 @@ build_src_filter = ${SenseCap_Solar.build_src_filter}
 extends = SenseCap_Solar
 build_flags =
   ${SenseCap_Solar.build_flags}
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D BLE_PIN_CODE=123456
   -D OFFLINE_QUEUE_SIZE=256
 ;  -D BLE_DEBUG_LOGGING=1
@@ -94,8 +94,8 @@ lib_deps =
 extends = SenseCap_Solar
 build_flags =
   ${SenseCap_Solar.build_flags}
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 build_src_filter = ${SenseCap_Solar.build_src_filter}

+ 3 - 0
variants/t1000-e/T1000eBoard.cpp

@@ -11,6 +11,9 @@ void T1000eBoard::begin() {
 
   sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
 
+  // Enable DC/DC converter for improved power efficiency
+  NRF_POWER->DCDCEN = 1;
+
 #ifdef BUTTON_PIN
   pinMode(BATTERY_PIN, INPUT);
   pinMode(BUTTON_PIN, INPUT);

+ 11 - 19
variants/t1000-e/T1000eBoard.h

@@ -3,22 +3,6 @@
 #include <MeshCore.h>
 #include <Arduino.h>
 
-// LoRa and SPI pins
-#define  P_LORA_DIO_1   (32 + 1)  // P1.1
-#define  P_LORA_NSS     (0 + 12)  // P0.12
-#define  P_LORA_RESET   (32 + 10) // P1.10
-#define  P_LORA_BUSY    (0 + 7)   // P0.7
-#define  P_LORA_SCLK    (0 + 11)  // P0.11
-#define  P_LORA_MISO    (32 + 8)  // P1.8
-#define  P_LORA_MOSI    (32 + 9)  // P0.9
- 
-#define LR11X0_DIO_AS_RF_SWITCH  true
-#define LR11X0_DIO3_TCXO_VOLTAGE   1.6
-
-// built-ins
-//#define  PIN_VBAT_READ    5
-//#define  ADC_MULTIPLIER   (3 * 1.73 * 1000)
-
 class T1000eBoard : public mesh::MainBoard {
 protected:
   uint8_t startup_reason;
@@ -72,18 +56,26 @@ public:
         digitalWrite(GPS_RESET, LOW);
         digitalWrite(GPS_SLEEP_INT, LOW);
         digitalWrite(GPS_RTC_INT, LOW);
+        digitalWrite(GPS_EN, LOW);
         pinMode(GPS_RESETB, OUTPUT);
         digitalWrite(GPS_RESETB, LOW);
     #endif
-    
+
     #ifdef BUZZER_EN
         digitalWrite(BUZZER_EN, LOW);
     #endif
-    
+
     #ifdef PIN_3V3_EN
         digitalWrite(PIN_3V3_EN, LOW);
     #endif
 
+    #ifdef PIN_3V3_ACC_EN
+        digitalWrite(PIN_3V3_ACC_EN, LOW);
+    #endif
+    #ifdef SENSOR_EN
+        digitalWrite(SENSOR_EN, LOW);
+    #endif
+
     // set led on and wait for button release before poweroff
     #ifdef LED_PIN
     digitalWrite(LED_PIN, HIGH);
@@ -96,7 +88,7 @@ public:
     #endif
 
     #ifdef BUTTON_PIN
-    nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH);
+    nrf_gpio_cfg_sense_input(BUTTON_PIN, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH);
     #endif
 
     sd_power_system_off();

+ 19 - 26
variants/t1000-e/platformio.ini

@@ -1,25 +1,13 @@
-; ----------------- NRF52 T1000e---------------------
-[nrf52840_t1000e]
+[t1000-e]
 extends = nrf52_base
-platform_packages = framework-arduinoadafruitnrf52
+board = tracker-t1000-e
+board_build.ldscript = boards/nrf52840_s140_v7.ld
 build_flags = ${nrf52_base.build_flags}
   -I src/helpers/nrf52
   -I lib/nrf52/s140_nrf52_7.3.0_API/include
   -I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52
-lib_ignore =
-  BluetoothOTA
-  lvgl
-  lib5b4
-lib_deps =
-  ${nrf52_base.lib_deps}
-  rweather/Crypto @ ^0.4.0
-
-[t1000-e]
-extends = nrf52840_t1000e
-board = tracker-t1000-e
-board_build.ldscript = boards/nrf52840_s140_v7.ld
-build_flags = ${nrf52840_t1000e.build_flags}
   -I variants/t1000-e
+  -I src/helpers/ui
   -D T1000_E
   -D PIN_USER_BTN=6
   -D USER_BTN_PRESSED=HIGH
@@ -27,7 +15,18 @@ build_flags = ${nrf52840_t1000e.build_flags}
   -D RADIO_CLASS=CustomLR1110
   -D WRAPPER_CLASS=CustomLR1110Wrapper
   -D LORA_TX_POWER=22
-build_src_filter = ${nrf52840_t1000e.build_src_filter}
+  -D RF_SWITCH_TABLE
+  -D RX_BOOSTED_GAIN=true
+  -D P_LORA_BUSY=7 ; P0.7
+  -D P_LORA_SCLK=11 ; P0.11
+  -D P_LORA_NSS=12 ; P0.12
+  -D P_LORA_DIO_1=33 ; P1.1
+  -D P_LORA_MISO=40 ; P1.8
+  -D P_LORA_MOSI=41 ; P0.9
+  -D P_LORA_RESET=42 ; P1.10
+  -D LR11X0_DIO_AS_RF_SWITCH=true
+  -D LR11X0_DIO3_TCXO_VOLTAGE=1.6
+build_src_filter = ${nrf52_base.build_src_filter}
   +<helpers/*.cpp>
   +<helpers/nrf52/T1000eBoard.cpp>
   +<../variants/t1000-e>
@@ -45,8 +44,6 @@ build_flags = ${t1000-e.build_flags}
   -D MAX_NEIGHBOURS=8
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
-  -D RX_BOOSTED_GAIN=true
-  -D RF_SWITCH_TABLE
 build_src_filter = ${t1000-e.build_src_filter}
   +<../examples/simple_repeater>
 lib_deps = ${t1000-e.lib_deps}
@@ -63,7 +60,6 @@ build_flags = ${t1000-e.build_flags}
   -D ROOM_PASSWORD='"hello"'
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
-  -D RX_BOOSTED_GAIN=true
   -D RF_SWITCH_TABLE
 build_src_filter = ${t1000-e.build_src_filter}
   +<../examples/simple_room_server>
@@ -79,8 +75,6 @@ build_flags = ${t1000-e.build_flags}
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
   -D OFFLINE_QUEUE_SIZE=256
-  -D RX_BOOSTED_GAIN=true
-  -D RF_SWITCH_TABLE
   -D DISPLAY_CLASS=NullDisplayDriver
   -D PIN_BUZZER=25
   -D PIN_BUZZER_EN=37      ; P1/5 - required for T1000-E
@@ -97,15 +91,14 @@ lib_deps = ${t1000-e.lib_deps}
 extends = t1000-e
 build_flags = ${t1000-e.build_flags}
   -I examples/companion_radio/ui-orig
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D BLE_PIN_CODE=123456
+  -D BLE_TX_POWER=0
 ;  -D BLE_DEBUG_LOGGING=1
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
   -D OFFLINE_QUEUE_SIZE=256
-  -D RX_BOOSTED_GAIN=true
-  -D RF_SWITCH_TABLE
   -D DISPLAY_CLASS=NullDisplayDriver
   -D PIN_BUZZER=25
   -D PIN_BUZZER_EN=37      ; P1/5 - required for T1000-E

+ 87 - 107
variants/t1000-e/t1000e_sensors.cpp

@@ -1,119 +1,99 @@
-#include <Arduino.h>
 #include "t1000e_sensors.h"
 
-#define HEATER_NTC_BX       4250    // thermistor coefficient B
-#define HEATER_NTC_RP       8250    // ohm, series resistance to thermistor
-#define HEATER_NTC_KA       273.15  // 25 Celsius at Kelvin
-#define NTC_REF_VCC         3000    // mV, output voltage of LDO
-#define LIGHT_REF_VCC       2400    // 
- 
-static unsigned int ntc_res2[136]={    
-    113347,107565,102116,96978,92132,87559,83242,79166,75316,71677,
-    68237,64991,61919,59011,56258,53650,51178,48835,46613,44506,
-    42506,40600,38791,37073,35442,33892,32420,31020,29689,28423,
-    27219,26076,24988,23951,22963,22021,21123,20267,19450,18670,                               
-    17926,17214,16534,15886,15266,14674,14108,13566,13049,12554,
-    12081,11628,11195,10780,10382,10000,9634,9284,8947,8624,
-    8315,8018,7734,7461,7199,6948,6707,6475,6253,6039,
-    5834,5636,5445,5262,5086,4917,4754,4597,4446,4301,
-    4161,4026,3896,3771,3651,3535,3423,3315,3211,3111,
-    3014,2922,2834,2748,2666,2586,2509,2435,2364,2294,
-    2228,2163,2100,2040,1981,1925,1870,1817,1766,1716,
-    1669,1622,1578,1535,1493,1452,1413,1375,1338,1303,
-    1268,1234,1202,1170,1139,1110,1081,1053,1026,999,
-    974,949,925,902,880,858,
+#include <Arduino.h>
+
+#define HEATER_NTC_BX 4250   // thermistor coefficient B
+#define HEATER_NTC_RP 8250   // ohm, series resistance to thermistor
+#define HEATER_NTC_KA 273.15 // 25 Celsius at Kelvin
+#define NTC_REF_VCC   3000   // mV, output voltage of LDO
+#define LIGHT_REF_VCC 2400   //
+
+static unsigned int ntc_res2[136] = {
+  113347, 107565, 102116, 96978, 92132, 87559, 83242, 79166, 75316, 71677, 68237, 64991, 61919, 59011,
+  56258,  53650,  51178,  48835, 46613, 44506, 42506, 40600, 38791, 37073, 35442, 33892, 32420, 31020,
+  29689,  28423,  27219,  26076, 24988, 23951, 22963, 22021, 21123, 20267, 19450, 18670, 17926, 17214,
+  16534,  15886,  15266,  14674, 14108, 13566, 13049, 12554, 12081, 11628, 11195, 10780, 10382, 10000,
+  9634,   9284,   8947,   8624,  8315,  8018,  7734,  7461,  7199,  6948,  6707,  6475,  6253,  6039,
+  5834,   5636,   5445,   5262,  5086,  4917,  4754,  4597,  4446,  4301,  4161,  4026,  3896,  3771,
+  3651,   3535,   3423,   3315,  3211,  3111,  3014,  2922,  2834,  2748,  2666,  2586,  2509,  2435,
+  2364,   2294,   2228,   2163,  2100,  2040,  1981,  1925,  1870,  1817,  1766,  1716,  1669,  1622,
+  1578,   1535,   1493,   1452,  1413,  1375,  1338,  1303,  1268,  1234,  1202,  1170,  1139,  1110,
+  1081,   1053,   1026,   999,   974,   949,   925,   902,   880,   858,
 };
- 
-static char ntc_temp2[136]=
-{
-    -30,-29,-28,-27,-26,-25,-24,-23,-22,-21,
-    -20,-19,-18,-17,-16,-15,-14,-13,-12,-11,
-    -10,-9,-8,-7,-6,-5,-4,-3,-2,-1,
-    0,1,2,3,4,5,6,7,8,9,
-    10,11,12,13,14,15,16,17,18,19,
-    20,21,22,23,24,25,26,27,28,29,
-    30,31,32,33,34,35,36,37,38,39,
-    40,41,42,43,44,45,46,47,48,49,
-    50,51,52,53,54,55,56,57,58,59,
-    60,61,62,63,64,65,66,67,68,69,
-    70,71,72,73,74,75,76,77,78,79,
-    80,81,82,83,84,85,86,87,88,89,
-    90,91,92,93,94,95,96,97,98,99,
-    100,101,102,103,104,105,
+
+static char ntc_temp2[136] = {
+  -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11,
+  -10, -9,  -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1,  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
+  10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
+  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
+  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
+  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,
+  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104, 105,
 };
 
-static float get_heater_temperature( unsigned int vcc_volt, unsigned int ntc_volt )
-{
-    int i = 0;
-    float Vout = 0, Rt = 0, temp = 0;
-    Vout = ntc_volt;
-    
-    Rt = ( HEATER_NTC_RP * vcc_volt ) / Vout - HEATER_NTC_RP;   
-        
-    for( i = 0; i < 136; i++ )
-    {
-        if( Rt >= ntc_res2[i] )
-        {
-           break; 
-        }
+static float get_heater_temperature(unsigned int vcc_volt, unsigned int ntc_volt) {
+  int i = 0;
+  float Vout = 0, Rt = 0, temp = 0;
+  Vout = ntc_volt;
+
+  Rt = (HEATER_NTC_RP * vcc_volt) / Vout - HEATER_NTC_RP;
+
+  for (i = 0; i < 136; i++) {
+    if (Rt >= ntc_res2[i]) {
+      break;
     }
- 
-    temp = ntc_temp2[i - 1] + 1 * ( ntc_res2[i - 1] - Rt ) / ( float )( ntc_res2[i - 1] - ntc_res2[i] );
- 
-    temp = ( temp * 100 + 5 ) / 100;
-    return temp;
+  }
+
+  temp = ntc_temp2[i - 1] + 1 * (ntc_res2[i - 1] - Rt) / (float)(ntc_res2[i - 1] - ntc_res2[i]);
+
+  temp = (temp * 100 + 5) / 100;
+  return temp;
 }
 
-static int get_light_lv( unsigned int light_volt )
-{  
-    float Vout = 0, Vin = 0, Rt = 0, temp = 0;
-    unsigned int light_level = 0;     
-    
-    if( light_volt <= 80 )
-    {
-        light_level = 0;
-        return light_level;
-    }
-    else if( light_volt >= 2480 )
-    {
-        light_level = 100;
-        return light_level;    
-    }
-    Vout = light_volt;
-    light_level = 100 * ( Vout - 80 ) / LIGHT_REF_VCC;
-   
+static int get_light_lv(unsigned int light_volt) {
+  float Vout = 0, Vin = 0, Rt = 0, temp = 0;
+  unsigned int light_level = 0;
+
+  if (light_volt <= 80) {
+    light_level = 0;
+    return light_level;
+  } else if (light_volt >= 2480) {
+    light_level = 100;
     return light_level;
-} 
-
-float t1000e_get_temperature( void )
-{
-    unsigned int ntc_v, vcc_v;
-
-    digitalWrite(PIN_3V3_EN, HIGH);
-    digitalWrite(SENSOR_EN, HIGH);
-    analogReference(AR_INTERNAL_3_0);
-    analogReadResolution(12);
-    delay(10);
-    vcc_v = (1000.0*(analogRead(BATTERY_PIN) * ADC_MULTIPLIER * AREF_VOLTAGE)) / 4096;
-    ntc_v = (1000.0 * AREF_VOLTAGE * analogRead(TEMP_SENSOR)) / 4096;
-    digitalWrite(PIN_3V3_EN, LOW);
-    digitalWrite(SENSOR_EN, LOW);
-
-    return get_heater_temperature (vcc_v, ntc_v);
+  }
+  Vout = light_volt;
+  light_level = 100 * (Vout - 80) / LIGHT_REF_VCC;
+
+  return light_level;
+}
+
+float t1000e_get_temperature(void) {
+  unsigned int ntc_v, vcc_v;
+
+  digitalWrite(PIN_3V3_EN, HIGH);
+  digitalWrite(SENSOR_EN, HIGH);
+  analogReference(AR_INTERNAL_3_0);
+  analogReadResolution(12);
+  delay(10);
+  vcc_v = (1000.0 * (analogRead(BATTERY_PIN) * ADC_MULTIPLIER * AREF_VOLTAGE)) / 4096;
+  ntc_v = (1000.0 * AREF_VOLTAGE * analogRead(TEMP_SENSOR)) / 4096;
+  digitalWrite(PIN_3V3_EN, LOW);
+  digitalWrite(SENSOR_EN, LOW);
+
+  return get_heater_temperature(vcc_v, ntc_v);
 }
 
-uint32_t t1000e_get_light( void )
-{
-    int lux = 0;
-    unsigned int lux_v = 0;
- 
-    digitalWrite(SENSOR_EN, HIGH);
-    analogReference(AR_INTERNAL_3_0);
-    analogReadResolution(12);
-    delay(10);
-    lux_v = 1000 * analogRead(LUX_SENSOR) * AREF_VOLTAGE / 4096;
-    lux = get_light_lv( lux_v );
-    digitalWrite(SENSOR_EN, LOW);
- 
-    return lux;
+uint32_t t1000e_get_light(void) {
+  int lux = 0;
+  unsigned int lux_v = 0;
+
+  digitalWrite(SENSOR_EN, HIGH);
+  analogReference(AR_INTERNAL_3_0);
+  analogReadResolution(12);
+  delay(10);
+  lux_v = 1000 * analogRead(LUX_SENSOR) * AREF_VOLTAGE / 4096;
+  lux = get_light_lv(lux_v);
+  digitalWrite(SENSOR_EN, LOW);
+
+  return lux;
 }

+ 2 - 1
variants/t1000-e/t1000e_sensors.h

@@ -1,7 +1,8 @@
 #pragma once
+#include <stdint.h>
 
 // Light and temperature sensors are on ADC ports
-// functions adapted from Seeed examples to get values 
+// functions adapted from Seeed examples to get values
 // see : https://github.com/Seeed-Studio/Seeed-Tracker-T1000-E-for-LoRaWAN-dev-board
 
 extern uint32_t t1000e_get_light();

+ 0 - 9
variants/t1000-e/target.cpp

@@ -146,15 +146,6 @@ void T1000SensorManager::stop_gps() {
 bool T1000SensorManager::begin() {
   // init GPS
   Serial1.begin(115200);
-
-  // make sure gps pin are off
-  digitalWrite(GPS_VRTC_EN, LOW);
-  digitalWrite(GPS_RESET, LOW);
-  digitalWrite(GPS_SLEEP_INT, LOW);
-  digitalWrite(GPS_RTC_INT, LOW);
-  pinMode(GPS_RESETB, OUTPUT);
-  digitalWrite(GPS_RESETB, LOW);
-
   return true;
 }
 

+ 2 - 1
variants/t1000-e/variant.cpp

@@ -69,7 +69,7 @@ void initVariant()
   pinMode(BATTERY_PIN, INPUT);
   pinMode(EXT_CHRG_DETECT, INPUT);
   pinMode(EXT_PWR_DETECT, INPUT);
-  pinMode(GPS_RESETB, INPUT);
+  pinMode(GPS_RESETB, OUTPUT);
   pinMode(PIN_BUTTON1, INPUT);
 
   pinMode(PIN_3V3_EN, OUTPUT);
@@ -92,5 +92,6 @@ void initVariant()
   digitalWrite(GPS_VRTC_EN, LOW);
   digitalWrite(GPS_SLEEP_INT, HIGH);
   digitalWrite(GPS_RTC_INT, LOW);
+  digitalWrite(GPS_RESETB, LOW);
   digitalWrite(LED_PIN, LOW);
 }

+ 0 - 85
variants/techo/platformio.ini

@@ -1,85 +0,0 @@
-[nrf52840_techo]
-extends = nrf52_base
-platform_packages = framework-arduinoadafruitnrf52
-build_flags = ${nrf52_base.build_flags}
-  -I src/helpers/nrf52
-  -I lib/nrf52/s140_nrf52_6.1.1_API/include
-  -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
-lib_deps =
-  ${nrf52_base.lib_deps}
-  rweather/Crypto @ ^0.4.0
-
-[LilyGo_Techo]
-extends = nrf52840_techo
-board = t-echo
-board_build.ldscript = boards/nrf52840_s140_v6.ld
-build_flags = ${nrf52840_techo.build_flags}
-  -I variants/techo
-  -DLILYGO_TECHO
-  -D RADIO_CLASS=CustomSX1262
-  -D WRAPPER_CLASS=CustomSX1262Wrapper
-  -D LORA_TX_POWER=22
-  -D SX126X_CURRENT_LIMIT=140
-  -D SX126X_RX_BOOSTED_GAIN=1
-  -D P_LORA_TX_LED=LED_GREEN
-build_src_filter = ${nrf52840_techo.build_src_filter}
-  +<helpers/*.cpp>
-  +<helpers/nrf52/TechoBoard.cpp>
-  +<../variants/techo>
-lib_deps =
-  ${nrf52840_techo.lib_deps}
-  stevemarple/MicroNMEA @ ^2.0.6
-debug_tool = jlink
-upload_protocol = nrfutil
-
-[env:LilyGo_T-Echo_repeater]
-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_LAT=0.0
-  -D ADVERT_LON=0.0
-  -D ADMIN_PASSWORD='"password"'
-  -D MAX_NEIGHBOURS=8
-;  -D MESH_PACKET_LOGGING=1
-;  -D MESH_DEBUG=1
-
-[env:LilyGo_T-Echo_room_server]
-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_LAT=0.0
-  -D ADVERT_LON=0.0
-  -D ADMIN_PASSWORD='"password"'
-;  -D MESH_PACKET_LOGGING=1
-;  -D MESH_DEBUG=1
-
-[env:LilyGo_T-Echo_companion_radio_ble]
-extends = LilyGo_Techo
-build_flags =
-  ${LilyGo_Techo.build_flags}
-  -I src/helpers/ui
-  -I examples/companion_radio/ui-new
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
-  -D BLE_PIN_CODE=123456
-  -D BLE_DEBUG_LOGGING=1
-  -D DISPLAY_CLASS=GxEPDDisplay
-  -D OFFLINE_QUEUE_SIZE=256
-  -D UI_RECENT_LIST_SIZE=9
-;  -D MESH_PACKET_LOGGING=1
-;  -D MESH_DEBUG=1
-build_src_filter = ${LilyGo_Techo.build_src_filter}
-  +<helpers/nrf52/TechoBoard.cpp>
-  +<helpers/nrf52/SerialBLEInterface.cpp>
-  +<helpers/ui/GxEPDDisplay.cpp>
-  +<helpers/ui/MomentaryButton.cpp>
-  +<../examples/companion_radio/*.cpp>
-  +<../examples/companion_radio/ui-new/*.cpp>
-lib_deps =
-  ${LilyGo_Techo.lib_deps}
-  densaugeo/base64 @ ~1.4.0
-  zinggjm/GxEPD2 @ 1.6.2

+ 0 - 123
variants/techo/target.cpp

@@ -1,123 +0,0 @@
-#include <Arduino.h>
-#include "target.h"
-#include <helpers/ArduinoHelpers.h>
-#include <helpers/sensors/MicroNMEALocationProvider.h>
-
-TechoBoard board;
-
-RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
-
-WRAPPER_CLASS radio_driver(radio, board);
-
-VolatileRTCClock fallback_clock;
-AutoDiscoverRTCClock rtc_clock(fallback_clock);
-MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1);
-TechoSensorManager sensors = TechoSensorManager(nmea);
-
-#ifdef DISPLAY_CLASS
-  DISPLAY_CLASS display;
-  MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
-#endif
-
-bool radio_init() {
-  rtc_clock.begin(Wire);
-  
-  return radio.std_init(&SPI);
-}
-
-uint32_t radio_get_rng_seed() {
-  return radio.random(0x7FFFFFFF);
-}
-
-void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
-  radio.setFrequency(freq);
-  radio.setSpreadingFactor(sf);
-  radio.setBandwidth(bw);
-  radio.setCodingRate(cr);
-}
-
-void radio_set_tx_power(uint8_t dbm) {
-  radio.setOutputPower(dbm);
-}
-
-mesh::LocalIdentity radio_new_identity() {
-  RadioNoiseListener rng(radio);
-  return mesh::LocalIdentity(&rng);  // create new random identity
-}
-
-void TechoSensorManager::start_gps() {
-  if (!gps_active) {
-    gps_active = true;
-    _location->begin();
-  }
-}
-
-void TechoSensorManager::stop_gps() {
-  if (gps_active) {
-    gps_active = false;
-    _location->stop();
-  }
-}
-
-bool TechoSensorManager::begin() {
-  Serial1.begin(9600);
-
-  // GPS enabled pin
-  pinMode(GPS_EN, OUTPUT);
-  
-  return true;
-}
-
-bool TechoSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) {
-  if (requester_permissions & TELEM_PERM_LOCATION) {   // does requester have permission?
-    telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude);
-  }
-  return true;
-}
-
-void TechoSensorManager::loop() {
-  static long next_gps_update = 0;
-  
-  if (!gps_active) {
-    return;  // GPS is not active, skip further processing
-  }
-
-  _location->loop();
-
-  if (millis() > next_gps_update) {
-    if (_location->isValid()) {
-      node_lat = ((double)_location->getLatitude())/1000000.;
-      node_lon = ((double)_location->getLongitude())/1000000.;
-      node_altitude = ((double)_location->getAltitude()) / 1000.0;
-      MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon);
-    }
-    next_gps_update = millis() + 1000;
-  }
-}
-
-int TechoSensorManager::getNumSettings() const {
-  return 1;  // always show GPS setting
-}
-
-const char* TechoSensorManager::getSettingName(int i) const {
-  return (i == 0) ? "gps" : NULL;
-}
-
-const char* TechoSensorManager::getSettingValue(int i) const {
-  if (i == 0) {
-    return gps_active ? "1" : "0";
-  }
-  return NULL;
-}
-
-bool TechoSensorManager::setSettingValue(const char* name, const char* value) {
-  if (strcmp(name, "gps") == 0) {
-    if (strcmp(value, "0") == 0) {
-      stop_gps();
-    } else {
-      start_gps();
-    }
-    return true;
-  }
-  return false;  // not supported
-}

+ 8 - 8
src/helpers/nrf52/ThinkNodeM1Board.cpp → variants/thinknode_m1/ThinkNodeM1Board.cpp

@@ -1,10 +1,10 @@
-#include <Arduino.h>
 #include "ThinkNodeM1Board.h"
+#include <Arduino.h>
 
 #ifdef THINKNODE_M1
 
-#include <bluefruit.h>
 #include <Wire.h>
+#include <bluefruit.h>
 
 static BLEDfu bledfu;
 
@@ -26,14 +26,14 @@ void ThinkNodeM1Board::begin() {
 
   Wire.begin();
 
-  #ifdef P_LORA_TX_LED
-    pinMode(P_LORA_TX_LED, OUTPUT);
-    digitalWrite(P_LORA_TX_LED, LOW);
-  #endif
+#ifdef P_LORA_TX_LED
+  pinMode(P_LORA_TX_LED, OUTPUT);
+  digitalWrite(P_LORA_TX_LED, LOW);
+#endif
 
   pinMode(SX126X_POWER_EN, OUTPUT);
   digitalWrite(SX126X_POWER_EN, HIGH);
-  delay(10);   // give sx1262 some time to power up
+  delay(10); // give sx1262 some time to power up
 }
 
 uint16_t ThinkNodeM1Board::getBattMilliVolts() {
@@ -50,7 +50,7 @@ uint16_t ThinkNodeM1Board::getBattMilliVolts() {
   return (uint16_t)((float)adcvalue * REAL_VBAT_MV_PER_LSB);
 }
 
-bool ThinkNodeM1Board::startOTAUpdate(const char* id, char reply[]) {
+bool ThinkNodeM1Board::startOTAUpdate(const char *id, char reply[]) {
   // Config the peripheral connection with maximum bandwidth
   // more SRAM required by SoftDevice
   // Note: All config***() function must be called before begin()

+ 0 - 13
src/helpers/nrf52/ThinkNodeM1Board.h → variants/thinknode_m1/ThinkNodeM1Board.h

@@ -3,19 +3,6 @@
 #include <MeshCore.h>
 #include <Arduino.h>
 
-// LoRa radio module pins for Elecrow ThinkNode M1
-#define  P_LORA_DIO_1     20
-#define  P_LORA_NSS       24
-#define  P_LORA_RESET     25
-#define  P_LORA_BUSY      17
-#define  P_LORA_SCLK      19
-#define  P_LORA_MISO      23
-#define  P_LORA_MOSI      22
-#define  SX126X_POWER_EN  37
-
-#define SX126X_DIO2_AS_RF_SWITCH  true
-#define SX126X_DIO3_TCXO_VOLTAGE   1.8
-
 // built-ins
 #define VBAT_MV_PER_LSB   (0.73242188F)   // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096
 

+ 49 - 19
variants/thinknode_m1/platformio.ini

@@ -1,33 +1,35 @@
-[nrf52840_thinknode_m1]
+[ThinkNode_M1]
 extends = nrf52_base
-platform_packages = framework-arduinoadafruitnrf52
+board = thinknode_m1
+board_build.ldscript = boards/nrf52840_s140_v6.ld
 build_flags = ${nrf52_base.build_flags}
   -I src/helpers/nrf52
   -I lib/nrf52/s140_nrf52_6.1.1_API/include
   -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
-lib_deps =
-  ${nrf52_base.lib_deps}
-  rweather/Crypto @ ^0.4.0
-
-[ThinkNode_M1]
-extends = nrf52840_thinknode_m1
-board = thinknode_m1
-board_build.ldscript = boards/nrf52840_s140_v6.ld
-build_flags = ${nrf52840_thinknode_m1.build_flags}
   -I variants/thinknode_m1
   -D THINKNODE_M1=1
   -D RADIO_CLASS=CustomSX1262
   -D WRAPPER_CLASS=CustomSX1262Wrapper
-  -D LORA_TX_POWER=22
-  -D P_LORA_TX_LED=13
+  -D P_LORA_DIO_1=20
+  -D P_LORA_NSS=24
+  -D P_LORA_RESET=25
+  -D P_LORA_BUSY=17
+  -D P_LORA_SCLK=19
+  -D P_LORA_MISO=23
+  -D P_LORA_MOSI=22
+  -D SX126X_POWER_EN=37
+  -D SX126X_DIO2_AS_RF_SWITCH=true
+  -D SX126X_DIO3_TCXO_VOLTAGE=1.8
   -D SX126X_CURRENT_LIMIT=140
   -D SX126X_RX_BOOSTED_GAIN=1
-build_src_filter = ${nrf52840_thinknode_m1.build_src_filter}
+  -D LORA_TX_POWER=22
+  -D P_LORA_TX_LED=13
+build_src_filter = ${nrf52_base.build_src_filter}
   +<helpers/*.cpp>
-  +<helpers/nrf52/ThinkNodeM1Board.cpp>
+  +<ThinkNodeM1Board.cpp>
   +<../variants/thinknode_m1>
 lib_deps =
-  ${nrf52840_thinknode_m1.lib_deps}
+  ${nrf52_base.lib_deps}
   stevemarple/MicroNMEA @ ^2.0.6
 debug_tool = jlink
 upload_protocol = nrfutil
@@ -69,8 +71,8 @@ build_flags =
   ${ThinkNode_M1.build_flags}
   -I src/helpers/ui
   -I examples/companion_radio/ui-new
-  -D MAX_CONTACTS=100
-  -D MAX_GROUP_CHANNELS=8
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
   -D BLE_PIN_CODE=123456
   -D BLE_DEBUG_LOGGING=1
   -D DISPLAY_ROTATION=4
@@ -78,10 +80,10 @@ build_flags =
   -D OFFLINE_QUEUE_SIZE=256
   -D PIN_BUZZER=6
   -D AUTO_SHUTDOWN_MILLIVOLTS=3300
+  -D QSPIFLASH=1
 ;  -D MESH_PACKET_LOGGING=1
 ;  -D MESH_DEBUG=1
 build_src_filter = ${ThinkNode_M1.build_src_filter}
-  +<helpers/nrf52/ThinkNodeM1.cpp>
   +<helpers/nrf52/SerialBLEInterface.cpp>
   +<helpers/ui/GxEPDDisplay.cpp>
   +<helpers/ui/buzzer.cpp>
@@ -92,4 +94,32 @@ lib_deps =
   ${ThinkNode_M1.lib_deps}
   densaugeo/base64 @ ~1.4.0
   zinggjm/GxEPD2 @ 1.6.2
+  bakercp/CRC32 @ ^2.0.0
   end2endzone/NonBlockingRTTTL@^1.3.0
+
+[env:ThinkNode_M1_companion_radio_usb]
+extends = ThinkNode_M1
+build_flags =
+  ${ThinkNode_M1.build_flags}
+  -I src/helpers/ui
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=350
+  -D MAX_GROUP_CHANNELS=40
+  -D DISPLAY_ROTATION=4
+  -D QSPIFLASH=1
+  -D DISPLAY_CLASS=GxEPDDisplay
+  -D OFFLINE_QUEUE_SIZE=256
+  -D PIN_BUZZER=6
+  -D AUTO_SHUTDOWN_MILLIVOLTS=3300
+build_src_filter = ${ThinkNode_M1.build_src_filter}
+  +<helpers/ui/GxEPDDisplay.cpp>
+  +<helpers/ui/buzzer.cpp>
+  +<helpers/ui/MomentaryButton.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${ThinkNode_M1.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+  zinggjm/GxEPD2 @ 1.6.2
+  bakercp/CRC32 @ ^2.0.0
+  end2endzone/NonBlockingRTTTL@^1.3.0

+ 1 - 1
variants/thinknode_m1/target.h

@@ -3,7 +3,7 @@
 #define RADIOLIB_STATIC_ONLY 1
 #include <RadioLib.h>
 #include <helpers/radiolib/RadioLibWrappers.h>
-#include <helpers/nrf52/ThinkNodeM1Board.h>
+#include <ThinkNodeM1Board.h>
 #include <helpers/radiolib/CustomSX1262Wrapper.h>
 #include <helpers/AutoDiscoverRTCClock.h>
 #include <helpers/SensorManager.h>

+ 9 - 0
variants/thinknode_m1/variant.h

@@ -110,6 +110,15 @@ extern const int MISO;
 extern const int MOSI;
 extern const int SCK;
 
+////////////////////////////////////////////////////////////////////////////////
+// QSPI
+#define PIN_QSPI_SCK (46)
+#define PIN_QSPI_CS  (47)
+#define PIN_QSPI_IO0 (44)  // MOSI if using two bit interface
+#define PIN_QSPI_IO1 (45)  // MISO if using two bit interface
+#define PIN_QSPI_IO2 (7)   // WP if using two bit interface (i.e. not used)
+#define PIN_QSPI_IO3 (5)   // HOLD if using two bit interface (i.e. not used)
+
 ////////////////////////////////////////////////////////////////////////////////
 // Display
 

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor