Просмотр исходного кода

Merge branch 'ripplebiz:dev' into dev

Rastislav Vysoky 1 год назад
Родитель
Сommit
a6c8dc4866

+ 52 - 6
examples/companion_radio/MyMesh.cpp

@@ -46,6 +46,9 @@
 #define CMD_SET_CUSTOM_VAR            41
 #define CMD_GET_ADVERT_PATH           42
 #define CMD_GET_TUNING_PARAMS         43
+// NOTE: CMD range 44..49 parked, potentially for WiFi operations
+#define CMD_SEND_BINARY_REQ           50
+#define CMD_FACTORY_RESET             51
 
 #define RESP_CODE_OK                  0
 #define RESP_CODE_ERR                 1
@@ -93,6 +96,7 @@
 #define PUSH_CODE_TRACE_DATA            0x89
 #define PUSH_CODE_NEW_ADVERT            0x8A
 #define PUSH_CODE_TELEMETRY_RESPONSE    0x8B
+#define PUSH_CODE_BINARY_RESPONSE       0x8C
 
 #define ERR_CODE_UNSUPPORTED_CMD        1
 #define ERR_CODE_NOT_FOUND              2
@@ -468,6 +472,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
       i += 6; // pub_key_prefix
       memcpy(&out_frame[i], &tag, 4);
       i += 4; // NEW: include server timestamp
+      out_frame[i++] = data[7]; // NEW (v7): ACL permissions
     } else {
       out_frame[i++] = PUSH_CODE_LOGIN_FAIL;
       out_frame[i++] = 0; // reserved
@@ -490,7 +495,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
     memcpy(&out_frame[i], &data[4], len - 4);
     i += (len - 4);
     _serial->writeFrame(out_frame, i);
-  } else if (len > 4 && tag == pending_telemetry) { // check for telemetry response
+  } else if (len > 4 && tag == pending_telemetry) {  // check for matching response tag
     pending_telemetry = 0;
 
     int i = 0;
@@ -501,6 +506,17 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
     memcpy(&out_frame[i], &data[4], len - 4);
     i += (len - 4);
     _serial->writeFrame(out_frame, i);
+  } else if (len > 4 && tag == pending_req) {  // check for matching response tag
+    pending_req = 0;
+
+    int i = 0;
+    out_frame[i++] = PUSH_CODE_BINARY_RESPONSE;
+    out_frame[i++] = 0; // reserved
+    memcpy(&out_frame[i], &tag, 4);   // app needs to match this to RESP_CODE_SENT.tag
+    i += 4;
+    memcpy(&out_frame[i], &data[4], len - 4);
+    i += (len - 4);
+    _serial->writeFrame(out_frame, i);
   }
 }
 
@@ -566,7 +582,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
   _cli_rescue = false;
   offline_queue_len = 0;
   app_target_ver = 0;
-  pending_login = pending_status = pending_telemetry = 0;
+  pending_login = pending_status = pending_telemetry = pending_req = 0;
   next_ack_idx = 0;
   sign_data = NULL;
   dirty_contacts_expiry = 0;
@@ -1103,7 +1119,7 @@ void MyMesh::handleCmdFrame(size_t len) {
       if (result == MSG_SEND_FAILED) {
         writeErrFrame(ERR_CODE_TABLE_FULL);
       } else {
-        pending_telemetry = pending_status = 0;
+        pending_req = pending_telemetry = pending_status = 0;
         memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse()
         out_frame[0] = RESP_CODE_SENT;
         out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
@@ -1123,7 +1139,7 @@ void MyMesh::handleCmdFrame(size_t len) {
       if (result == MSG_SEND_FAILED) {
         writeErrFrame(ERR_CODE_TABLE_FULL);
       } else {
-        pending_telemetry = pending_login = 0;
+        pending_req = pending_telemetry = pending_login = 0;
         // FUTURE:  pending_status = tag;  // match this in onContactResponse()
         memcpy(&pending_status, recipient->id.pub_key, 4); // legacy matching scheme
         out_frame[0] = RESP_CODE_SENT;
@@ -1135,7 +1151,7 @@ void MyMesh::handleCmdFrame(size_t len) {
     } else {
       writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
     }
-  } else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) {
+  } else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) {  // can deprecate, in favour of CMD_SEND_BINARY_REQ
     uint8_t *pub_key = &cmd_frame[4];
     ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
     if (recipient) {
@@ -1144,7 +1160,7 @@ void MyMesh::handleCmdFrame(size_t len) {
       if (result == MSG_SEND_FAILED) {
         writeErrFrame(ERR_CODE_TABLE_FULL);
       } else {
-        pending_status = pending_login = 0;
+        pending_status = pending_login = pending_req = 0;
         pending_telemetry = tag; // match this in onContactResponse()
         out_frame[0] = RESP_CODE_SENT;
         out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
@@ -1170,6 +1186,27 @@ void MyMesh::handleCmdFrame(size_t len) {
     memcpy(&out_frame[i], telemetry.getBuffer(), tlen);
     i += tlen;
     _serial->writeFrame(out_frame, i);
+  } else if (cmd_frame[0] == CMD_SEND_BINARY_REQ && len >= 2 + PUB_KEY_SIZE) {
+    uint8_t *pub_key = &cmd_frame[1];
+    ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
+    if (recipient) {
+      uint8_t *req_data = &cmd_frame[1 + PUB_KEY_SIZE];
+      uint32_t tag, est_timeout;
+      int result = sendRequest(*recipient, req_data, len - (1 + PUB_KEY_SIZE), tag, est_timeout);
+      if (result == MSG_SEND_FAILED) {
+        writeErrFrame(ERR_CODE_TABLE_FULL);
+      } else {
+        pending_status = pending_login = pending_telemetry = 0;
+        pending_req = tag; // match this in onContactResponse()
+        out_frame[0] = RESP_CODE_SENT;
+        out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
+        memcpy(&out_frame[2], &tag, 4);
+        memcpy(&out_frame[6], &est_timeout, 4);
+        _serial->writeFrame(out_frame, 10);
+      }
+    } else {
+      writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
+    }
   } else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) {
     uint8_t *pub_key = &cmd_frame[1];
     if (hasConnectionTo(pub_key)) {
@@ -1325,6 +1362,15 @@ void MyMesh::handleCmdFrame(size_t len) {
     } else {
       writeErrFrame(ERR_CODE_NOT_FOUND);
     }
+  } else if (cmd_frame[0] == CMD_FACTORY_RESET && memcmp(&cmd_frame[1], "reset", 5) == 0) {
+    bool success = _store->formatFileSystem();
+    if (success) {
+      writeOKFrame();
+      delay(1000);
+      board.reboot();  // doesn't return
+    } else {
+      writeErrFrame(ERR_CODE_FILE_IO_ERROR);
+    }
   } else {
     writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
     MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]);

+ 5 - 4
examples/companion_radio/MyMesh.h

@@ -7,14 +7,14 @@
 #endif
 
 /*------------ Frame Protocol --------------*/
-#define FIRMWARE_VER_CODE 6
+#define FIRMWARE_VER_CODE 7
 
 #ifndef FIRMWARE_BUILD_DATE
-#define FIRMWARE_BUILD_DATE "2 Jul 2025"
+#define FIRMWARE_BUILD_DATE "15 Jul 2025"
 #endif
 
 #ifndef FIRMWARE_VERSION
-#define FIRMWARE_VERSION "v1.7.2"
+#define FIRMWARE_VERSION "v1.7.3"
 #endif
 
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
@@ -160,7 +160,8 @@ private:
   NodePrefs _prefs;
   uint32_t pending_login;
   uint32_t pending_status;
-  uint32_t pending_telemetry;
+  uint32_t pending_telemetry;   // pending _TELEMETRY_REQ
+  uint32_t pending_req;   // pending _BINARY_REQ
   BaseSerialInterface *_serial;
 
   ContactsIterator _iter;

+ 2 - 2
examples/simple_repeater/main.cpp

@@ -22,11 +22,11 @@
 /* ------------------------------ Config -------------------------------- */
 
 #ifndef FIRMWARE_BUILD_DATE
-  #define FIRMWARE_BUILD_DATE   "2 Jul 2025"
+  #define FIRMWARE_BUILD_DATE   "15 Jul 2025"
 #endif
 
 #ifndef FIRMWARE_VERSION
-  #define FIRMWARE_VERSION   "v1.7.2"
+  #define FIRMWARE_VERSION   "v1.7.3"
 #endif
 
 #ifndef LORA_FREQ

+ 2 - 2
examples/simple_room_server/main.cpp

@@ -22,11 +22,11 @@
 /* ------------------------------ Config -------------------------------- */
 
 #ifndef FIRMWARE_BUILD_DATE
-  #define FIRMWARE_BUILD_DATE   "2 Jul 2025"
+  #define FIRMWARE_BUILD_DATE   "15 Jul 2025"
 #endif
 
 #ifndef FIRMWARE_VERSION
-  #define FIRMWARE_VERSION   "v1.7.2"
+  #define FIRMWARE_VERSION   "v1.7.3"
 #endif
 
 #ifndef LORA_FREQ

+ 22 - 20
examples/simple_sensor/SensorMesh.cpp

@@ -59,7 +59,7 @@
 
 #define LAZY_CONTACTS_WRITE_DELAY       5000
 
-#define ALERT_ACK_EXPIRY_MILLIS         6000   // wait 6 secs for ACKs to alert messages
+#define ALERT_ACK_EXPIRY_MILLIS         8000   // wait 8 secs for ACKs to alert messages
 
 static File openAppend(FILESYSTEM* _fs, const char* fname) {
   #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
@@ -95,11 +95,11 @@ void SensorMesh::loadContacts() {
       while (!full) {
         ContactInfo c;
         uint8_t pub_key[32];
-        uint8_t unused[5];
+        uint8_t unused[6];
 
         bool success = (file.read(pub_key, 32) == 32);
-        success = success && (file.read((uint8_t *) &c.permissions, 2) == 2);
-        success = success && (file.read(unused, 5) == 5);
+        success = success && (file.read((uint8_t *) &c.permissions, 1) == 1);
+        success = success && (file.read(unused, 6) == 6);
         success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1);
         success = success && (file.read(c.out_path, 64) == 64);
         success = success && (file.read(c.shared_secret, PUB_KEY_SIZE) == PUB_KEY_SIZE);
@@ -131,8 +131,8 @@ void SensorMesh::saveContacts() {
       if (c->permissions == 0) continue;    // skip deleted entries
 
       bool success = (file.write(c->id.pub_key, 32) == 32);
-      success = success && (file.write((uint8_t *) &c->permissions, 2) == 2);
-      success = success && (file.write(unused, 5) == 5);
+      success = success && (file.write((uint8_t *) &c->permissions, 1) == 1);
+      success = success && (file.write(unused, 6) == 6);
       success = success && (file.write((uint8_t *)&c->out_path_len, 1) == 1);
       success = success && (file.write(c->out_path, 64) == 64);
       success = success && (file.write(c->shared_secret, PUB_KEY_SIZE) == PUB_KEY_SIZE);
@@ -240,7 +240,7 @@ static uint8_t putFloat(uint8_t * dest, float value, uint8_t size, uint32_t mult
   return size;
 }
 
-uint8_t SensorMesh::handleRequest(uint16_t perms, uint32_t sender_timestamp, uint8_t req_type, uint8_t* payload, size_t payload_len) {
+uint8_t SensorMesh::handleRequest(uint8_t perms, uint32_t sender_timestamp, uint8_t req_type, uint8_t* payload, size_t payload_len) {
   memcpy(reply_data, &sender_timestamp, 4);   // reflect sender_timestamp back in response packet (kind of like a 'tag')
 
   if (req_type == REQ_TYPE_GET_TELEMETRY_DATA && (perms & PERM_GET_TELEMETRY) != 0) {
@@ -248,12 +248,13 @@ uint8_t SensorMesh::handleRequest(uint16_t perms, uint32_t sender_timestamp, uin
     telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);
     // query other sensors -- target specific
     sensors.querySensors(0xFF, telemetry);  // allow all telemetry permissions for admin or guest
+    // TODO: let requester know permissions they have:  telemetry.addPresence(TELEM_CHANNEL_SELF, perms);
 
     uint8_t tlen = telemetry.getSize();
     memcpy(&reply_data[4], telemetry.getBuffer(), tlen);
     return 4 + tlen;  // reply_len
   }
-  if (req_type == REQ_TYPE_GET_AVG_MIN_MAX && (perms & PERM_GET_MIN_MAX_AVG) != 0) {
+  if (req_type == REQ_TYPE_GET_AVG_MIN_MAX && (perms & PERM_GET_OTHER_STATS) != 0) {
     uint32_t start_secs_ago, end_secs_ago;
     memcpy(&start_secs_ago, &payload[0], 4);
     memcpy(&end_secs_ago, &payload[4], 4);
@@ -287,15 +288,15 @@ uint8_t SensorMesh::handleRequest(uint16_t perms, uint32_t sender_timestamp, uin
     }
     return ofs;
   }
-  if (req_type == REQ_TYPE_GET_ACCESS_LIST && (perms & PERM_IS_ADMIN) != 0) {
+  if (req_type == REQ_TYPE_GET_ACCESS_LIST && (perms & PERM_ACL_ROLE_MASK) == PERM_ACL_LEVEL3) {
     uint8_t res1 = payload[0];   // reserved for future  (extra query params)
     uint8_t res2 = payload[1];
     if (res1 == 0 && res2 == 0) {
       uint8_t ofs = 4;
-      for (int i = 0; i < num_contacts && ofs + 8 <= sizeof(reply_data) - 4; i++) {
+      for (int i = 0; i < num_contacts && ofs + 7 <= sizeof(reply_data) - 4; i++) {
         auto c = &contacts[i];
         memcpy(&reply_data[ofs], c->id.pub_key, 6); ofs += 6;  // just 6-byte pub_key prefix
-        memcpy(&reply_data[ofs], &c->permissions, 2); ofs += 2;
+        reply_data[ofs++] = c->permissions;
       }
       return ofs;
     }
@@ -337,11 +338,11 @@ ContactInfo* SensorMesh::putContact(const mesh::Identity& id) {
   return c;
 }
 
-void SensorMesh::applyContactPermissions(const uint8_t* pubkey, uint16_t perms) {
+void SensorMesh::applyContactPermissions(const uint8_t* pubkey, uint8_t perms) {
   mesh::Identity id(pubkey);
   auto c = putContact(id);
 
-  if (perms == 0) {  // no permissions, remove from contacts
+  if ((perms & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) {  // guest role is not persisted in contacts
     memset(c, 0, sizeof(*c));
   } else {
     c->permissions = perms;  // update their permissions
@@ -449,7 +450,7 @@ uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t*
   MESH_DEBUG_PRINTLN("Login success!");
   client->last_timestamp = sender_timestamp;
   client->last_activity = getRTCClock()->getCurrentTime();
-  client->permissions = PERM_IS_ADMIN | PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO;  // initially opt-in to receive alerts (can opt out)
+  client->permissions = PERM_ACL_LEVEL3 | PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO;  // initially opt-in to receive alerts (can opt out)
   memcpy(client->shared_secret, secret, PUB_KEY_SIZE);
 
   dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
@@ -459,7 +460,7 @@ uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t*
   reply_data[4] = RESP_SERVER_LOGIN_OK;
   reply_data[5] = 0;  // NEW: recommended keep-alive interval (secs / 16)
   reply_data[6] = 1;  // 1 = is admin
-  reply_data[7] = 0;  // FUTURE: reserved
+  reply_data[7] = client->permissions;
   getRNG()->random(&reply_data[8], 4);   // random blob to help packet-hash uniqueness
 
   return 12;  // reply length
@@ -480,7 +481,7 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r
   }
 
   // handle sensor-specific CLI commands
-  if (memcmp(command, "setperm ", 8) == 0) {   // format:  setperm {pubkey-hex} {permissions-int16}
+  if (memcmp(command, "setperm ", 8) == 0) {   // format:  setperm {pubkey-hex} {permissions-int8}
     char* hex = &command[8];
     char* sp = strchr(hex, ' ');   // look for separator char
     if (sp == NULL || sp - hex != PUB_KEY_SIZE*2) {
@@ -490,20 +491,21 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r
 
       uint8_t pubkey[PUB_KEY_SIZE];
       if (mesh::Utils::fromHex(pubkey, PUB_KEY_SIZE, hex)) {
-        uint16_t perms = atoi(sp);
+        uint8_t perms = atoi(sp);
         applyContactPermissions(pubkey, perms);
         strcpy(reply, "OK");
       } else {
         strcpy(reply, "Err - bad pubkey");
       }
     }
-  } else if (sender_timestamp == 0 && strcmp(command, "getperm") == 0) {
-    Serial.println("Permissions:");
+  } else if (sender_timestamp == 0 && strcmp(command, "get acl") == 0) {
+    Serial.println("ACL:");
     for (int i = 0; i < num_contacts; i++) {
       auto c = &contacts[i];
 
+      Serial.printf("%02X ", c->permissions);
       mesh::Utils::printHex(Serial, c->id.pub_key, PUB_KEY_SIZE);
-      Serial.printf(" %04X\n", c->permissions);
+      Serial.printf("\n");
     }
     reply[0] = 0;
   } else {

+ 16 - 9
examples/simple_sensor/SensorMesh.h

@@ -23,22 +23,29 @@
 #include <RTClib.h>
 #include <target.h>
 
-#define PERM_IS_ADMIN          0x8000
-#define PERM_GET_TELEMETRY     0x0001
-#define PERM_GET_MIN_MAX_AVG   0x0002
-#define PERM_RECV_ALERTS_LO    0x0100   // low priority alerts
-#define PERM_RECV_ALERTS_HI    0x0200   // high priority alerts
+#define PERM_ACL_ROLE_MASK     3   // lower 2 bits
+#define PERM_ACL_GUEST         0
+#define PERM_ACL_LEVEL1        1
+#define PERM_ACL_LEVEL2        2
+#define PERM_ACL_LEVEL3        3   // admin
+
+#define PERM_GET_TELEMETRY     (1 << 2)
+#define PERM_GET_OTHER_STATS   (1 << 3)
+#define PERM_RESERVED1         (1 << 4)
+#define PERM_RESERVED2         (1 << 5)
+#define PERM_RECV_ALERTS_LO    (1 << 6)   // low priority alerts
+#define PERM_RECV_ALERTS_HI    (1 << 7)   // high priority alerts
 
 struct ContactInfo {
   mesh::Identity id;
-  uint16_t permissions;
+  uint8_t permissions;
   int8_t out_path_len;
   uint8_t out_path[MAX_PATH_SIZE];
   uint8_t shared_secret[PUB_KEY_SIZE];
   uint32_t last_timestamp;   // by THEIR clock  (transient)
   uint32_t last_activity;    // by OUR clock    (transient)
 
-  bool isAdmin() const { return (permissions & PERM_IS_ADMIN) != 0; }
+  bool isAdmin() const { return (permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_LEVEL3; }
 };
 
 #ifndef FIRMWARE_BUILD_DATE
@@ -151,10 +158,10 @@ private:
   void loadContacts();
   void saveContacts();
   uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data);
-  uint8_t handleRequest(uint16_t perms, uint32_t sender_timestamp, uint8_t req_type, uint8_t* payload, size_t payload_len);
+  uint8_t handleRequest(uint8_t perms, uint32_t sender_timestamp, uint8_t req_type, uint8_t* payload, size_t payload_len);
   mesh::Packet* createSelfAdvert();
   ContactInfo* putContact(const mesh::Identity& id);
-  void applyContactPermissions(const uint8_t* pubkey, uint16_t perms);
+  void applyContactPermissions(const uint8_t* pubkey, uint8_t perms);
 
   void sendAlert(ContactInfo* c, Trigger* t);
 

+ 55 - 22
src/helpers/BaseChatMesh.cpp

@@ -403,22 +403,52 @@ bool BaseChatMesh::importContact(const uint8_t src_buf[], uint8_t len) {
 }
 
 int BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout) {
-  int tlen;
-  uint8_t temp[24];
-  uint32_t now = getRTCClock()->getCurrentTimeUnique();
-  memcpy(temp, &now, 4);   // mostly an extra blob to help make packet_hash unique
-  if (recipient.type == ADV_TYPE_ROOM) {
-    memcpy(&temp[4], &recipient.sync_since, 4);
-    int len = strlen(password); if (len > 15) len = 15;  // max 15 chars currently
-    memcpy(&temp[8], password, len);
-    tlen = 8 + len;
-  } else {
-    int len = strlen(password); if (len > 15) len = 15;  // max 15 chars currently
-    memcpy(&temp[4], password, len);
-    tlen = 4 + len;
+  mesh::Packet* pkt;
+  {
+    int tlen;
+    uint8_t temp[24];
+    uint32_t now = getRTCClock()->getCurrentTimeUnique();
+    memcpy(temp, &now, 4);   // mostly an extra blob to help make packet_hash unique
+    if (recipient.type == ADV_TYPE_ROOM) {
+      memcpy(&temp[4], &recipient.sync_since, 4);
+      int len = strlen(password); if (len > 15) len = 15;  // max 15 chars currently
+      memcpy(&temp[8], password, len);
+      tlen = 8 + len;
+    } else {
+      int len = strlen(password); if (len > 15) len = 15;  // max 15 chars currently
+      memcpy(&temp[4], password, len);
+      tlen = 4 + len;
+    }
+
+    pkt = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, recipient.id, recipient.shared_secret, temp, tlen);
+  }
+  if (pkt) {
+    uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
+    if (recipient.out_path_len < 0) {
+      sendFlood(pkt);
+      est_timeout = calcFloodTimeoutMillisFor(t);
+      return MSG_SEND_SENT_FLOOD;
+    } else {
+      sendDirect(pkt, recipient.out_path, recipient.out_path_len);
+      est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len);
+      return MSG_SEND_SENT_DIRECT;
+    }
   }
+  return MSG_SEND_FAILED;
+}
+
+int  BaseChatMesh::sendRequest(const ContactInfo& recipient, const uint8_t* req_data, uint8_t data_len, uint32_t& tag, uint32_t& est_timeout) {
+  if (data_len > MAX_PACKET_PAYLOAD - 16) return MSG_SEND_FAILED;
 
-  auto pkt = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, recipient.id, recipient.shared_secret, temp, tlen);
+  mesh::Packet* pkt;
+  {
+    uint8_t temp[MAX_PACKET_PAYLOAD];
+    tag = getRTCClock()->getCurrentTimeUnique();
+    memcpy(temp, &tag, 4);   // mostly an extra blob to help make packet_hash unique
+    memcpy(&temp[4], req_data, data_len);
+
+    pkt = createDatagram(PAYLOAD_TYPE_REQ, recipient.id, recipient.shared_secret, temp, 4 + data_len);
+  }
   if (pkt) {
     uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
     if (recipient.out_path_len < 0) {
@@ -435,14 +465,17 @@ int BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password,
 }
 
 int  BaseChatMesh::sendRequest(const ContactInfo& recipient, uint8_t req_type, uint32_t& tag, uint32_t& est_timeout) {
-  uint8_t temp[13];
-  tag = getRTCClock()->getCurrentTimeUnique();
-  memcpy(temp, &tag, 4);   // mostly an extra blob to help make packet_hash unique
-  temp[4] = req_type;
-  memset(&temp[5], 0, 4);  // reserved (possibly for 'since' param)
-  getRNG()->random(&temp[9], 4);   // random blob to help make packet-hash unique
-
-  auto pkt = createDatagram(PAYLOAD_TYPE_REQ, recipient.id, recipient.shared_secret, temp, sizeof(temp));
+  mesh::Packet* pkt;
+  {
+    uint8_t temp[13];
+    tag = getRTCClock()->getCurrentTimeUnique();
+    memcpy(temp, &tag, 4);   // mostly an extra blob to help make packet_hash unique
+    temp[4] = req_type;
+    memset(&temp[5], 0, 4);  // reserved (possibly for 'since' param)
+    getRNG()->random(&temp[9], 4);   // random blob to help make packet-hash unique
+
+    pkt = createDatagram(PAYLOAD_TYPE_REQ, recipient.id, recipient.shared_secret, temp, sizeof(temp));
+  }
   if (pkt) {
     uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
     if (recipient.out_path_len < 0) {

+ 1 - 0
src/helpers/BaseChatMesh.h

@@ -134,6 +134,7 @@ public:
   bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len);
   int  sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout);
   int  sendRequest(const ContactInfo& recipient, uint8_t req_type, uint32_t& tag, uint32_t& est_timeout);
+  int  sendRequest(const ContactInfo& recipient, const uint8_t* req_data, uint8_t data_len, uint32_t& tag, uint32_t& est_timeout);
   bool shareContactZeroHop(const ContactInfo& contact);
   uint8_t exportContact(const ContactInfo& contact, uint8_t dest_buf[]);
   bool importContact(const uint8_t src_buf[], uint8_t len);

+ 11 - 4
variants/heltec_wireless_paper/target.cpp

@@ -1,11 +1,14 @@
 #include "target.h"
-
 #include <Arduino.h>
 
 HeltecV3Board board;
 
-static SPIClass spi;
-RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
+#if defined(P_LORA_SCLK)
+  static SPIClass spi;
+  RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
+#else
+  RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
+#endif
 
 WRAPPER_CLASS radio_driver(radio, board);
 
@@ -21,7 +24,11 @@ DISPLAY_CLASS display;
 bool radio_init() {
   fallback_clock.begin();
   rtc_clock.begin(Wire);
+#if defined(P_LORA_SCLK)
   return radio.std_init(&spi);
+#else
+  return radio.std_init();
+#endif
 }
 
 uint32_t radio_get_rng_seed() {
@@ -42,4 +49,4 @@ void radio_set_tx_power(uint8_t dbm) {
 mesh::LocalIdentity radio_new_identity() {
   RadioNoiseListener rng(radio);
   return mesh::LocalIdentity(&rng); // create new random identity
-}
+}

+ 2 - 0
variants/sensecap_solar/variant.cpp

@@ -64,6 +64,8 @@ void initVariant() {
     pinMode(LED_BLUE, OUTPUT);
     digitalWrite(LED_BLUE, LOW);
 
+    /* disable gps until we actually support it.
     pinMode(GPS_EN, OUTPUT);
     digitalWrite(GPS_EN, HIGH);
+    */
 }