diff --git a/docs/packet_structure.md b/docs/packet_structure.md index 4a28526f..aa260855 100644 --- a/docs/packet_structure.md +++ b/docs/packet_structure.md @@ -1,11 +1,12 @@ # Packet Structure -| Field | Size (bytes) | Description | -|----------|----------------------------------|-----------------------------------------------------------| -| header | 1 | Contains routing type, payload type, and payload version. | -| path_len | 1 | Length of the path field in bytes. | -| path | up to 64 (`MAX_PATH_SIZE`) | Stores the routing path if applicable. | -| payload | up to 184 (`MAX_PACKET_PAYLOAD`) | The actual data being transmitted. | +| Field | Size (bytes) | Description | +|-----------------|----------------------------------|-----------------------------------------------------------| +| header | 1 | Contains routing type, payload type, and payload version. | +| transport_codes | 4 (optional) | 2x 16-bit transport codes (if ROUTE_TYPE_TRANSPORT_*) | +| path_len | 1 | Length of the path field in bytes. | +| path | up to 64 (`MAX_PATH_SIZE`) | Stores the routing path if applicable. | +| payload | up to 184 (`MAX_PACKET_PAYLOAD`) | The actual data being transmitted. | Note: see the [payloads doc](./payloads.md) for more information about the content of payload. @@ -42,6 +43,7 @@ bit 0 means the lowest bit (1s place) | `0x07` | `PAYLOAD_TYPE_ANON_REQ` | Anonymous request. | | `0x08` | `PAYLOAD_TYPE_PATH` | Returned path. | | `0x09` | `PAYLOAD_TYPE_TRACE` | trace a path, collecting SNI for each hop. | +| `0x0A` | `PAYLOAD_TYPE_MULTIPART` | packet is part of a sequence of packets. | | `0x0F` | `PAYLOAD_TYPE_RAW_CUSTOM` | Custom packet (raw bytes, custom encryption). | ## Payload Version Values diff --git a/docs/payloads.md b/docs/payloads.md index b094d9a9..4d00f930 100644 --- a/docs/payloads.md +++ b/docs/payloads.md @@ -10,13 +10,16 @@ Inside of each [meshcore packet](./packet_structure.md) is a payload, identified * Anonymous request. * Group text message (unverified). * Group datagram (unverified). +* Multi-part packet * Custom packet (raw bytes, custom encryption). -This document defines the structure of each of these payload types +This document defines the structure of each of these payload types. + +NOTE: all 16 and 32-bit integer fields are Little Endian. ## Important concepts: -* Node/channel hash: the first byte of the node or channel's public key +* Node hash: the first byte of the node's public key # Node advertisement This kind of payload notifies receivers that a node exists, and gives information about the node @@ -33,10 +36,10 @@ Appdata | Field | Size (bytes) | Description | |---------------|-----------------|-------------------------------------------------------| | flags | 1 | specifies which of the fields are present, see below | -| latitude | 4 | decimal latitude multiplied by 1000000, integer | -| longitude | 4 | decimal longitude multiplied by 1000000, integer | -| feature 1 | 2 | reserved for future use | -| feature 2 | 2 | reserved for future use | +| latitude | 4 (optional) | decimal latitude multiplied by 1000000, integer | +| longitude | 4 (optional) | decimal longitude multiplied by 1000000, integer | +| feature 1 | 2 (optional) | reserved for future use | +| feature 2 | 2 (optional) | reserved for future use | | name | rest of appdata | name of the node | Appdata Flags @@ -46,6 +49,7 @@ Appdata Flags | `0x01` | is chat node | advert is for a chat node | | `0x02` | is repeater | advert is for a repeater | | `0x03` | is room server | advert is for a room server | +| `0x04` | is sensor | advert is for a sensor server | | `0x10` | has location | appdata contains lat/long information | | `0x20` | has feature 1 | Reserved for future use. | | `0x40` | has feature 2 | Reserved for future use. | @@ -92,13 +96,15 @@ Returned path messages provide a description of the route a packet took from the Request type -| Value | Name | Description | -|--------|--------------------|---------------------------------------| -| `0x01` | get status | get status of repeater or room server | -| `0x02` | keepalive | TODO | -| `0x03` | get telemetry data | TODO | +| Value | Name | Description | +|--------|----------------------|---------------------------------------| +| `0x01` | get stats | get stats of repeater or room server | +| `0x02` | keepalive | (deprecated) | +| `0x03` | get telemetry data | TODO | +| `0x04` | get min,max,avg data | sensor nodes - get min, max, average for given time span | +| `0x05` | get access list | get node's approved access list | -### Get status +### Get stats Gets information about the node, possibly including the following: @@ -121,10 +127,6 @@ Gets information about the node, possibly including the following: * Number posted (?) * Number of post pushes (?) -### Keepalive - -No-op request. - ### Get telemetry data Request data about sensors on the node, including battery level. @@ -138,11 +140,11 @@ Request data about sensors on the node, including battery level. ## Plain text message -| Field | Size (bytes) | Description | -|--------------|-----------------|--------------------------------------------------------------| -| timestamp | 4 | send time (unix timestamp) | -| flags + TODO | 1 | first six bits are flags (see below), last two bits are TODO | -| message | rest of payload | the message content, see next table | +| Field | Size (bytes) | Description | +|-----------------|-----------------|--------------------------------------------------------------| +| timestamp | 4 | send time (unix timestamp) | +| flags + attempt | 1 | upper six bits are flags (see below), lower two bits are attempt number (0..3) | +| message | rest of payload | the message content, see next table | Flags @@ -150,7 +152,7 @@ Flags |--------|---------------------------|------------------------------------------------------------| | `0x00` | plain text message | the plain text of the message | | `0x01` | CLI command | the command text of the message | -| `0x02` | signed plain text message | two bytes of sender prefix, followed by plain text message | +| `0x02` | signed plain text message | first four bytes is sender pubkey prefix, followed by plain text message | # Anonymous request @@ -166,14 +168,14 @@ Plaintext message | Field | Size (bytes) | Description | |----------------|-----------------|-------------------------------------------------------------------------------| | timestamp | 4 | send time (unix timestamp) | -| sync timestamp | 4 | for room server, otherwise absent: sender's "sync messages SINCE x" timestamp | +| sync timestamp | 4 | NOTE: room server only! - sender's "sync messages SINCE x" timestamp | | password | rest of message | password for repeater/room | # Group text message / datagram | Field | Size (bytes) | Description | |--------------|-----------------|--------------------------------------------| -| channel hash | 1 | the first byte of the channel's public key | +| channel hash | 1 | first byte of SHA256 of channel's shared key | | cipher MAC | 2 | MAC for encrypted data in next field | | ciphertext | rest of payload | encrypted message, see below for details | diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 5c19d7a1..b5d70edc 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -154,7 +154,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56 file.read((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60 file.read((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61 - file.read((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 + file.read(pad, 1); // 62 file.read((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 file.read((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64 file.read((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 @@ -163,7 +163,8 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 file.read((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 file.read((uint8_t *)&_prefs.advert_loc_policy, sizeof(_prefs.advert_loc_policy)); // 76 - file.read(pad, 3); // 77 + file.read((uint8_t *)&_prefs.multi_acks, sizeof(_prefs.multi_acks)); // 77 + file.read(pad, 2); // 78 file.read((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 file.close(); @@ -184,7 +185,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56 file.write((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60 file.write((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61 - file.write((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 + file.write(pad, 1); // 62 file.write((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 file.write((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64 file.write((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 @@ -193,7 +194,8 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 file.write((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 file.write((uint8_t *)&_prefs.advert_loc_policy, sizeof(_prefs.advert_loc_policy)); // 76 - file.write(pad, 3); // 77 + file.write((uint8_t *)&_prefs.multi_acks, sizeof(_prefs.multi_acks)); // 77 + file.write(pad, 2); // 78 file.write((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 file.close(); diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 9331a50c..939c93de 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -210,6 +210,10 @@ int MyMesh::calcRxDelay(float score, uint32_t air_time) const { return (int)((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); } +uint8_t MyMesh::getExtraAckTransmitCount() const { + return _prefs.multi_acks; +} + void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { if (_serial->isConnected() && len + 3 <= MAX_FRAME_SIZE) { int i = 0; @@ -719,7 +723,7 @@ void MyMesh::handleCmdFrame(size_t len) { i += 4; memcpy(&out_frame[i], &lon, 4); i += 4; - out_frame[i++] = 0; // reserved + out_frame[i++] = _prefs.multi_acks; // new v7+ out_frame[i++] = _prefs.advert_loc_policy; out_frame[i++] = (_prefs.telemetry_mode_env << 4) | (_prefs.telemetry_mode_loc << 2) | (_prefs.telemetry_mode_base); // v5+ @@ -1050,6 +1054,9 @@ void MyMesh::handleCmdFrame(size_t len) { if (len >= 4) { _prefs.advert_loc_policy = cmd_frame[3]; + if (len >= 5) { + _prefs.multi_acks = cmd_frame[4]; + } } } savePrefs(); diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 81bf261e..5ad6198f 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -10,11 +10,11 @@ #define FIRMWARE_VER_CODE 7 #ifndef FIRMWARE_BUILD_DATE -#define FIRMWARE_BUILD_DATE "15 Jul 2025" +#define FIRMWARE_BUILD_DATE "24 Jul 2025" #endif #ifndef FIRMWARE_VERSION -#define FIRMWARE_VERSION "v1.7.3" +#define FIRMWARE_VERSION "v1.7.4" #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) @@ -97,6 +97,7 @@ protected: float getAirtimeBudgetFactor() const override; int getInterferenceThreshold() const override; int calcRxDelay(float score, uint32_t air_time) const override; + uint8_t getExtraAckTransmitCount() const override; void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; bool isAutoAddEnabled() const override; diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index bf4739e4..bfde7218 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -14,7 +14,7 @@ struct NodePrefs { // persisted to file float freq; uint8_t sf; uint8_t cr; - uint8_t reserved1; + uint8_t multi_acks; uint8_t manual_add_contacts; float bw; uint8_t tx_power_dbm; diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 1eb5be8e..a7f03a26 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -408,8 +408,12 @@ void UITask::handleButtonQuadruplePress() { if (strcmp(_sensors->getSettingName(i), "gps") == 0) { if (strcmp(_sensors->getSettingValue(i), "1") == 0) { _sensors->setSettingValue("gps", "0"); + soundBuzzer(UIEventType::ack); + sprintf(_alert, "GPS: Disabled"); } else { _sensors->setSettingValue("gps", "1"); + soundBuzzer(UIEventType::ack); + sprintf(_alert, "GPS: Enabled"); } break; } diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 2ec94c4b..a28df7a4 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -22,11 +22,11 @@ /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "15 Jul 2025" + #define FIRMWARE_BUILD_DATE "24 Jul 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.7.3" + #define FIRMWARE_VERSION "v1.7.4" #endif #ifndef LORA_FREQ @@ -120,7 +120,7 @@ struct NeighbourInfo { int8_t snr; // multiplied by 4, user should divide to get float value }; -#define CLI_REPLY_DELAY_MILLIS 1000 +#define CLI_REPLY_DELAY_MILLIS 600 class MyMesh : public mesh::Mesh, public CommonCLICallbacks { FILESYSTEM* _fs; @@ -134,6 +134,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { NeighbourInfo neighbours[MAX_NEIGHBOURS]; #endif CayenneLPP telemetry; + unsigned long set_radio_at, revert_radio_at; + float pending_freq; + float pending_bw; + uint8_t pending_sf; + uint8_t pending_cr; ClientInfo* putClient(const mesh::Identity& id) { uint32_t min_time = 0xFFFFFFFF; @@ -339,6 +344,9 @@ protected: int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } + uint8_t getExtraAckTransmitCount() const override { + return _prefs.multi_acks; + } void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override { if (packet->getPayloadType() == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin client (unknown at this stage) @@ -555,6 +563,7 @@ public: { memset(known_clients, 0, sizeof(known_clients)); next_local_advert = next_flood_advert = 0; + set_radio_at = revert_radio_at = 0; _logging = false; #if MAX_NEIGHBOURS @@ -606,6 +615,16 @@ public: _cli.savePrefs(_fs); } + void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override { + set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params + pending_freq = freq; + pending_bw = bw; + pending_sf = sf; + pending_cr = cr; + + revert_radio_at = futureMillis(2000 + timeout_mins*60*1000); // schedule when to revert radio params + } + bool formatFileSystem() override { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) return InternalFS.format(); @@ -731,6 +750,19 @@ public: updateAdvertTimer(); // schedule next local advert } + + if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params + set_radio_at = 0; // clear timer + radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr); + MESH_DEBUG_PRINTLN("Temp radio params"); + } + + if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig + revert_radio_at = 0; // clear timer + radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + MESH_DEBUG_PRINTLN("Radio params restored"); + } + #ifdef DISPLAY_CLASS ui_task.loop(); #endif diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index b8f74a0c..9a416835 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -22,11 +22,11 @@ /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "15 Jul 2025" + #define FIRMWARE_BUILD_DATE "24 Jul 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.7.3" + #define FIRMWARE_VERSION "v1.7.4" #endif #ifndef LORA_FREQ @@ -165,6 +165,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { int next_post_idx; PostInfo posts[MAX_UNSYNCED_POSTS]; // cyclic queue CayenneLPP telemetry; + unsigned long set_radio_at, revert_radio_at; + float pending_freq; + float pending_bw; + uint8_t pending_sf; + uint8_t pending_cr; ClientInfo* putClient(const mesh::Identity& id) { for (int i = 0; i < num_clients; i++) { @@ -424,6 +429,9 @@ protected: int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } + uint8_t getExtraAckTransmitCount() const override { + return _prefs.multi_acks; + } bool allowPacketForward(const mesh::Packet* packet) override { if (_prefs.disable_fwd) return false; @@ -578,15 +586,22 @@ protected: uint32_t delay_millis; if (send_ack) { - mesh::Packet* ack = createAck(ack_hash); - if (ack) { - if (client->out_path_len < 0) { - sendFlood(ack, TXT_ACK_DELAY); - } else { - sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); + if (client->out_path_len < 0) { + mesh::Packet* ack = createAck(ack_hash); + if (ack) sendFlood(ack, TXT_ACK_DELAY); + delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS; + } else { + uint32_t d = TXT_ACK_DELAY; + if (getExtraAckTransmitCount() > 0) { + mesh::Packet* a1 = createMultiAck(ack_hash, 1); + if (a1) sendDirect(a1, client->out_path, client->out_path_len, d); + d += 300; } + + mesh::Packet* a2 = createAck(ack_hash); + if (a2) sendDirect(a2, client->out_path, client->out_path_len, d); + delay_millis = d + REPLY_DELAY_MILLIS; } - delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS; } else { delay_millis = 0; } @@ -711,6 +726,7 @@ public: { next_local_advert = next_flood_advert = 0; _logging = false; + set_radio_at = revert_radio_at = 0; // defaults memset(&_prefs, 0, sizeof(_prefs)); @@ -768,6 +784,16 @@ public: _cli.savePrefs(_fs); } + void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override { + set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params + pending_freq = freq; + pending_bw = bw; + pending_sf = sf; + pending_cr = cr; + + revert_radio_at = futureMillis(2000 + timeout_mins*60*1000); // schedule when to revert radio params + } + bool formatFileSystem() override { #if defined(NRF52_PLATFORM) return InternalFS.format(); @@ -912,6 +938,18 @@ public: updateAdvertTimer(); // schedule next local advert } + if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params + set_radio_at = 0; // clear timer + radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr); + MESH_DEBUG_PRINTLN("Temp radio params"); + } + + if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig + revert_radio_at = 0; // clear timer + radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + MESH_DEBUG_PRINTLN("Radio params restored"); + } + #ifdef DISPLAY_CLASS ui_task.loop(); #endif diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 5bbe9f4d..0816af72 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -243,7 +243,7 @@ static uint8_t putFloat(uint8_t * dest, float value, uint8_t size, uint32_t mult 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) { + if (req_type == REQ_TYPE_GET_TELEMETRY_DATA) { // allow all telemetry.reset(); telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); // query other sensors -- target specific @@ -254,7 +254,7 @@ uint8_t SensorMesh::handleRequest(uint8_t perms, uint32_t sender_timestamp, uint memcpy(&reply_data[4], telemetry.getBuffer(), tlen); return 4 + tlen; // reply_len } - if (req_type == REQ_TYPE_GET_AVG_MIN_MAX && (perms & PERM_GET_OTHER_STATS) != 0) { + if (req_type == REQ_TYPE_GET_AVG_MIN_MAX && (perms & PERM_ACL_ROLE_MASK) >= PERM_ACL_READ_ONLY) { uint32_t start_secs_ago, end_secs_ago; memcpy(&start_secs_ago, &payload[0], 4); memcpy(&end_secs_ago, &payload[4], 4); @@ -288,13 +288,14 @@ uint8_t SensorMesh::handleRequest(uint8_t perms, uint32_t sender_timestamp, uint } return ofs; } - if (req_type == REQ_TYPE_GET_ACCESS_LIST && (perms & PERM_ACL_ROLE_MASK) == PERM_ACL_LEVEL3) { + if (req_type == REQ_TYPE_GET_ACCESS_LIST && (perms & PERM_ACL_ROLE_MASK) == PERM_ACL_ADMIN) { 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 + 7 <= sizeof(reply_data) - 4; i++) { auto c = &contacts[i]; + if (c->permissions == 0) continue; // skip deleted entries memcpy(&reply_data[ofs], c->id.pub_key, 6); ofs += 6; // just 6-byte pub_key prefix reply_data[ofs++] = c->permissions; } @@ -315,7 +316,14 @@ mesh::Packet* SensorMesh::createSelfAdvert() { return createAdvert(self_id, app_data, app_data_len); } -ContactInfo* SensorMesh::putContact(const mesh::Identity& id) { +ContactInfo* SensorMesh::getContact(const uint8_t* pubkey, int key_len) { + for (int i = 0; i < num_contacts; i++) { + if (memcmp(pubkey, contacts[i].id.pub_key, key_len) == 0) return &contacts[i]; // already known + } + return NULL; // not found +} + +ContactInfo* SensorMesh::putContact(const mesh::Identity& id, uint8_t init_perms) { uint32_t min_time = 0xFFFFFFFF; ContactInfo* oldest = &contacts[MAX_CONTACTS - 1]; for (int i = 0; i < num_contacts; i++) { @@ -333,22 +341,35 @@ ContactInfo* SensorMesh::putContact(const mesh::Identity& id) { c = oldest; // evict least active contact } memset(c, 0, sizeof(*c)); + c->permissions = init_perms; c->id = id; c->out_path_len = -1; // initially out_path is unknown return c; } -void SensorMesh::applyContactPermissions(const uint8_t* pubkey, uint8_t perms) { - mesh::Identity id(pubkey); - auto c = putContact(id); - +bool SensorMesh::applyContactPermissions(const uint8_t* pubkey, int key_len, uint8_t perms) { + ContactInfo* c; if ((perms & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) { // guest role is not persisted in contacts - memset(c, 0, sizeof(*c)); + c = getContact(pubkey, key_len); + if (c == NULL) return false; // partial pubkey not found + + num_contacts--; // delete from contacts[] + int i = c - contacts; + while (i < num_contacts) { + contacts[i] = contacts[i + 1]; + i++; + } } else { + if (key_len < PUB_KEY_SIZE) return false; // need complete pubkey when adding/modifying + + mesh::Identity id(pubkey); + c = putContact(id, 0); + c->permissions = perms; // update their permissions self_id.calcSharedSecret(c->shared_secret, pubkey); } dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); // trigger saveContacts() + return true; } void SensorMesh::sendAlert(ContactInfo* c, Trigger* t) { @@ -434,32 +455,43 @@ int SensorMesh::getAGCResetInterval() const { } uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data) { - if (strcmp((char *) data, _prefs.password) != 0) { // check for valid password - #if MESH_DEBUG - MESH_DEBUG_PRINTLN("Invalid password: %s", &data[4]); - #endif - return 0; + ContactInfo* client; + if (data[0] == 0) { // blank password, just check if sender is in ACL + client = getContact(sender.pub_key, PUB_KEY_SIZE); + if (client == NULL) { + #if MESH_DEBUG + MESH_DEBUG_PRINTLN("Login, sender not in ACL"); + #endif + return 0; + } + } else { + if (strcmp((char *) data, _prefs.password) != 0) { // check for valid admin password + #if MESH_DEBUG + MESH_DEBUG_PRINTLN("Invalid password: %s", &data[4]); + #endif + return 0; + } + + client = putContact(sender, PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO); // add to contacts (if not already known) + if (sender_timestamp <= client->last_timestamp) { + MESH_DEBUG_PRINTLN("Possible login replay attack!"); + return 0; // FATAL: client table is full -OR- replay attack + } + + MESH_DEBUG_PRINTLN("Login success!"); + client->last_timestamp = sender_timestamp; + client->last_activity = getRTCClock()->getCurrentTime(); + client->permissions |= PERM_ACL_ADMIN; + memcpy(client->shared_secret, secret, PUB_KEY_SIZE); + + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); } - auto client = putContact(sender); // add to contacts (if not already known) - if (sender_timestamp <= client->last_timestamp) { - MESH_DEBUG_PRINTLN("Possible login replay attack!"); - return 0; // FATAL: client table is full -OR- replay attack - } - - MESH_DEBUG_PRINTLN("Login success!"); - client->last_timestamp = sender_timestamp; - client->last_activity = getRTCClock()->getCurrentTime(); - 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); - uint32_t now = getRTCClock()->getCurrentTimeUnique(); memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp 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[6] = client->isAdmin() ? 1 : 0; reply_data[7] = client->permissions; getRNG()->random(&reply_data[8], 4); // random blob to help packet-hash uniqueness @@ -484,16 +516,20 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r 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) { - strcpy(reply, "Err - bad pubkey len"); + if (sp == NULL) { + strcpy(reply, "Err - bad params"); } else { *sp++ = 0; // replace space with null terminator uint8_t pubkey[PUB_KEY_SIZE]; - if (mesh::Utils::fromHex(pubkey, PUB_KEY_SIZE, hex)) { + int hex_len = min(sp - hex, PUB_KEY_SIZE*2); + if (mesh::Utils::fromHex(pubkey, hex_len / 2, hex)) { uint8_t perms = atoi(sp); - applyContactPermissions(pubkey, perms); - strcpy(reply, "OK"); + if (applyContactPermissions(pubkey, hex_len / 2, perms)) { + strcpy(reply, "OK"); + } else { + strcpy(reply, "Err - invalid params"); + } } else { strcpy(reply, "Err - bad pubkey"); } @@ -502,6 +538,7 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r Serial.println("ACL:"); for (int i = 0; i < num_contacts; i++) { auto c = &contacts[i]; + if (c->permissions == 0) continue; // skip deleted entries Serial.printf("%02X ", c->permissions); mesh::Utils::printHex(Serial, c->id.pub_key, PUB_KEY_SIZE); @@ -569,7 +606,7 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i memcpy(×tamp, data, 4); if (timestamp > from.last_timestamp) { // prevent replay attacks - uint8_t reply_len = handleRequest(from.isAdmin() ? 0xFFFF : from.permissions, timestamp, data[4], &data[5], len - 5); + uint8_t reply_len = handleRequest(from.isAdmin() ? 0xFF : from.permissions, timestamp, data[4], &data[5], len - 5); if (reply_len == 0) return; // invalid command from.last_timestamp = timestamp; @@ -685,6 +722,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise dirty_contacts_expiry = 0; last_read_time = 0; num_alert_tasks = 0; + set_radio_at = revert_radio_at = 0; // defaults memset(&_prefs, 0, sizeof(_prefs)); @@ -735,6 +773,16 @@ bool SensorMesh::formatFileSystem() { #endif } +void SensorMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { + set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params + pending_freq = freq; + pending_bw = bw; + pending_sf = sf; + pending_cr = cr; + + revert_radio_at = futureMillis(2000 + timeout_mins*60*1000); // schedule when to revert radio params +} + void SensorMesh::sendSelfAdvertisement(int delay_millis) { mesh::Packet* pkt = createSelfAdvert(); if (pkt) { @@ -810,6 +858,18 @@ void SensorMesh::loop() { updateAdvertTimer(); // schedule next local advert } + if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params + set_radio_at = 0; // clear timer + radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr); + MESH_DEBUG_PRINTLN("Temp radio params"); + } + + if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig + revert_radio_at = 0; // clear timer + radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + MESH_DEBUG_PRINTLN("Radio params restored"); + } + uint32_t curr = getRTCClock()->getCurrentTime(); if (curr >= last_read_time + SENSOR_READ_INTERVAL_SECS) { telemetry.reset(); diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index a357506f..8f6e3bc3 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -25,14 +25,14 @@ #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_ACL_READ_ONLY 1 +#define PERM_ACL_READ_WRITE 2 +#define PERM_ACL_ADMIN 3 -#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_RESERVED1 (1 << 2) +#define PERM_RESERVED2 (1 << 3) +#define PERM_RESERVED3 (1 << 4) +#define PERM_RESERVED4 (1 << 5) #define PERM_RECV_ALERTS_LO (1 << 6) // low priority alerts #define PERM_RECV_ALERTS_HI (1 << 7) // high priority alerts @@ -45,15 +45,15 @@ struct ContactInfo { uint32_t last_timestamp; // by THEIR clock (transient) uint32_t last_activity; // by OUR clock (transient) - bool isAdmin() const { return (permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_LEVEL3; } + bool isAdmin() const { return (permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_ADMIN; } }; #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "2 Jul 2025" + #define FIRMWARE_BUILD_DATE "24 Jul 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.7.2" + #define FIRMWARE_VERSION "v1.7.4" #endif #define FIRMWARE_ROLE "sensor" @@ -90,6 +90,7 @@ public: } const uint8_t* getSelfIdPubKey() override { return self_id.pub_key; } void clearStats() override { } + void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override; float getTelemValue(uint8_t channel, uint8_t type); @@ -154,14 +155,20 @@ private: int matching_peer_indexes[MAX_SEARCH_RESULTS]; int num_alert_tasks; Trigger* alert_tasks[MAX_CONCURRENT_ALERTS]; + unsigned long set_radio_at, revert_radio_at; + float pending_freq; + float pending_bw; + uint8_t pending_sf; + uint8_t pending_cr; 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(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, uint8_t perms); + ContactInfo* getContact(const uint8_t* pubkey, int key_len); + ContactInfo* putContact(const mesh::Identity& id, uint8_t init_perms); + bool applyContactPermissions(const uint8_t* pubkey, int key_len, uint8_t perms); void sendAlert(ContactInfo* c, Trigger* t); diff --git a/platformio.ini b/platformio.ini index 00a2d0fb..cd1c21ad 100644 --- a/platformio.ini +++ b/platformio.ini @@ -46,6 +46,7 @@ build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1 build_src_filter = +<*.cpp> + + + ; ----------------- ESP32 --------------------- diff --git a/src/Mesh.cpp b/src/Mesh.cpp index f34f6f77..b055d811 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -22,6 +22,9 @@ uint32_t Mesh::getRetransmitDelay(const mesh::Packet* packet) { uint32_t Mesh::getDirectRetransmitDelay(const Packet* packet) { return 0; // by default, no delay } +uint8_t Mesh::getExtraAckTransmitCount() const { + return 0; +} uint32_t Mesh::getCADFailRetryDelay() const { return _rng->nextInt(1, 4)*120; @@ -67,22 +70,22 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { if (pkt->isRouteDirect() && pkt->path_len >= PATH_HASH_SIZE) { if (self_id.isHashMatch(pkt->path) && allowPacketForward(pkt)) { - if (_tables->hasSeen(pkt)) return ACTION_RELEASE; // don't retransmit! - - // remove our hash from 'path', then re-broadcast - pkt->path_len -= PATH_HASH_SIZE; - #if 0 - memcpy(pkt->path, &pkt->path[PATH_HASH_SIZE], pkt->path_len); - #elif PATH_HASH_SIZE == 1 - for (int k = 0; k < pkt->path_len; k++) { // shuffle bytes by 1 - pkt->path[k] = pkt->path[k + 1]; + if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) { + return forwardMultipartDirect(pkt); + } else if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) { + if (!_tables->hasSeen(pkt)) { // don't retransmit! + removeSelfFromPath(pkt); + routeDirectRecvAcks(pkt, 0); + } + return ACTION_RELEASE; } - #else - #error "need path remove impl" - #endif - uint32_t d = getDirectRetransmitDelay(pkt); - return ACTION_RETRANSMIT_DELAYED(0, d); // Routed traffic is HIGHEST priority + if (!_tables->hasSeen(pkt)) { + removeSelfFromPath(pkt); + + uint32_t d = getDirectRetransmitDelay(pkt); + return ACTION_RETRANSMIT_DELAYED(0, d); // Routed traffic is HIGHEST priority + } } return ACTION_RELEASE; // this node is NOT the next hop (OR this packet has already been forwarded), so discard. } @@ -261,6 +264,32 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } break; } + case PAYLOAD_TYPE_MULTIPART: + if (pkt->payload_len > 2) { + uint8_t remaining = pkt->payload[0] >> 4; // num of packets in this multipart sequence still to be sent + uint8_t type = pkt->payload[0] & 0x0F; + + if (type == PAYLOAD_TYPE_ACK && pkt->payload_len >= 5) { // a multipart ACK + Packet tmp; + tmp.header = pkt->header; + tmp.path_len = pkt->path_len; + memcpy(tmp.path, pkt->path, pkt->path_len); + tmp.payload_len = pkt->payload_len - 1; + memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len); + + if (!_tables->hasSeen(&tmp)) { + uint32_t ack_crc; + memcpy(&ack_crc, tmp.payload, 4); + + onAckRecv(&tmp, ack_crc); + //action = routeRecvPacket(&tmp); // NOTE: currently not needed, as multipart ACKs not sent Flood + } + } else { + // FUTURE: other multipart types?? + } + } + break; + default: MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): unknown payload type, header: %d", getLogDateTime(), (int) pkt->header); // Don't flood route unknown packet types! action = routeRecvPacket(pkt); @@ -269,6 +298,20 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { return action; } +void Mesh::removeSelfFromPath(Packet* pkt) { + // remove our hash from 'path' + pkt->path_len -= PATH_HASH_SIZE; +#if 0 + memcpy(pkt->path, &pkt->path[PATH_HASH_SIZE], pkt->path_len); +#elif PATH_HASH_SIZE == 1 + for (int k = 0; k < pkt->path_len; k++) { // shuffle bytes by 1 + pkt->path[k] = pkt->path[k + 1]; + } +#else + #error "need path remove impl" +#endif +} + DispatcherAction Mesh::routeRecvPacket(Packet* packet) { if (packet->isRouteFlood() && !packet->isMarkedDoNotRetransmit() && packet->path_len + PATH_HASH_SIZE <= MAX_PATH_SIZE && allowPacketForward(packet)) { @@ -282,6 +325,54 @@ DispatcherAction Mesh::routeRecvPacket(Packet* packet) { return ACTION_RELEASE; } +DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) { + uint8_t remaining = pkt->payload[0] >> 4; // num of packets in this multipart sequence still to be sent + uint8_t type = pkt->payload[0] & 0x0F; + + if (type == PAYLOAD_TYPE_ACK && pkt->payload_len >= 5) { // a multipart ACK + Packet tmp; + tmp.header = pkt->header; + tmp.path_len = pkt->path_len; + memcpy(tmp.path, pkt->path, pkt->path_len); + tmp.payload_len = pkt->payload_len - 1; + memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len); + + if (!_tables->hasSeen(&tmp)) { // don't retransmit! + removeSelfFromPath(&tmp); + routeDirectRecvAcks(&tmp, ((uint32_t)remaining + 1) * 300); // expect multipart ACKs 300ms apart (x2) + } + } + return ACTION_RELEASE; +} + +void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { + if (!packet->isMarkedDoNotRetransmit()) { + uint32_t crc; + memcpy(&crc, packet->payload, 4); + + uint8_t extra = getExtraAckTransmitCount(); + while (extra > 0) { + delay_millis += getDirectRetransmitDelay(packet) + 300; + auto a1 = createMultiAck(crc, extra); + if (a1) { + memcpy(a1->path, packet->path, a1->path_len = packet->path_len); + a1->header &= ~PH_ROUTE_MASK; + a1->header |= ROUTE_TYPE_DIRECT; + sendPacket(a1, 0, delay_millis); + } + extra--; + } + + auto a2 = createAck(crc); + if (a2) { + memcpy(a2->path, packet->path, a2->path_len = packet->path_len); + a2->header &= ~PH_ROUTE_MASK; + a2->header |= ROUTE_TYPE_DIRECT; + sendPacket(a2, 0, delay_millis); + } + } +} + Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, size_t app_data_len) { if (app_data_len > MAX_ADVERT_DATA_SIZE) return NULL; @@ -449,6 +540,21 @@ Packet* Mesh::createAck(uint32_t ack_crc) { return packet; } +Packet* Mesh::createMultiAck(uint32_t ack_crc, uint8_t remaining) { + Packet* packet = obtainNewPacket(); + if (packet == NULL) { + MESH_DEBUG_PRINTLN("%s Mesh::createMultiAck(): error, packet pool empty", getLogDateTime()); + return NULL; + } + packet->header = (PAYLOAD_TYPE_MULTIPART << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later + + packet->payload[0] = (remaining << 4) | PAYLOAD_TYPE_ACK; + memcpy(&packet->payload[1], &ack_crc, 4); + packet->payload_len = 5; + + return packet; +} + Packet* Mesh::createRawData(const uint8_t* data, size_t len) { if (len > sizeof(Packet::payload)) return NULL; // invalid arg diff --git a/src/Mesh.h b/src/Mesh.h index b2047ac1..a8fdb2a4 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -28,6 +28,11 @@ class Mesh : public Dispatcher { RNG* _rng; MeshTables* _tables; + void removeSelfFromPath(Packet* packet); + void routeDirectRecvAcks(Packet* packet, uint32_t delay_millis); + //void routeRecvAcks(Packet* packet, uint32_t delay_millis); + DispatcherAction forwardMultipartDirect(Packet* pkt); + protected: DispatcherAction onRecvPacket(Packet* pkt) override; @@ -54,6 +59,11 @@ protected: */ virtual uint32_t getDirectRetransmitDelay(const Packet* packet); + /** + * \returns number of extra (Direct) ACK transmissions wanted. + */ + virtual uint8_t getExtraAckTransmitCount() const; + /** * \brief Perform search of local DB of peers/contacts. * \returns Number of peers with matching hash @@ -165,6 +175,7 @@ public: Packet* createAnonDatagram(uint8_t type, const LocalIdentity& sender, const Identity& dest, const uint8_t* secret, const uint8_t* data, size_t data_len); Packet* createGroupDatagram(uint8_t type, const GroupChannel& channel, const uint8_t* data, size_t data_len); Packet* createAck(uint32_t ack_crc); + Packet* createMultiAck(uint32_t ack_crc, uint8_t remaining); Packet* createPathReturn(const uint8_t* dest_hash, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len); Packet* createPathReturn(const Identity& dest, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len); Packet* createRawData(const uint8_t* data, size_t len); diff --git a/src/Packet.h b/src/Packet.h index 7c7df8e3..e52ab526 100644 --- a/src/Packet.h +++ b/src/Packet.h @@ -26,6 +26,7 @@ namespace mesh { #define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...) #define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra) #define PAYLOAD_TYPE_TRACE 0x09 // trace a path, collecting SNI for each hop +#define PAYLOAD_TYPE_MULTIPART 0x0A // packet is one of a set of packets //... #define PAYLOAD_TYPE_RAW_CUSTOM 0x0F // custom packet as raw bytes, for applications with custom encryption, payloads, etc diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index d6ba17b9..476e6e8f 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -31,6 +31,23 @@ mesh::Packet* BaseChatMesh::createSelfAdvert(const char* name, double lat, doubl return createAdvert(self_id, app_data, app_data_len); } +void BaseChatMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) { + if (dest.out_path_len < 0) { + mesh::Packet* ack = createAck(ack_hash); + if (ack) sendFlood(ack, TXT_ACK_DELAY); + } else { + uint32_t d = TXT_ACK_DELAY; + if (getExtraAckTransmitCount() > 0) { + mesh::Packet* a1 = createMultiAck(ack_hash, 1); + if (a1) sendDirect(a1, dest.out_path, dest.out_path_len, d); + d += 300; + } + + mesh::Packet* a2 = createAck(ack_hash); + if (a2) sendDirect(a2, dest.out_path, dest.out_path_len, d); + } +} + void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) { AdvertDataParser parser(app_data, app_data_len); if (!(parser.isValid() && parser.hasName())) { @@ -152,14 +169,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4); if (path) sendFlood(path, TXT_ACK_DELAY); } else { - mesh::Packet* ack = createAck(ack_hash); - if (ack) { - if (from.out_path_len < 0) { - sendFlood(ack, TXT_ACK_DELAY); - } else { - sendDirect(ack, from.out_path, from.out_path_len, TXT_ACK_DELAY); - } - } + sendAckTo(from, ack_hash); } } else if (flags == TXT_TYPE_CLI_DATA) { onCommandDataRecv(from, packet, timestamp, (const char *) &data[5]); // let UI know @@ -185,14 +195,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4); if (path) sendFlood(path, TXT_ACK_DELAY); } else { - mesh::Packet* ack = createAck(ack_hash); - if (ack) { - if (from.out_path_len < 0) { - sendFlood(ack, TXT_ACK_DELAY); - } else { - sendDirect(ack, from.out_path, from.out_path_len, TXT_ACK_DELAY); - } - } + sendAckTo(from, ack_hash); } } else { MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported message type: %u", (uint32_t) flags); diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index 903b4ece..683af852 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -72,6 +72,7 @@ class BaseChatMesh : public mesh::Mesh { ConnectionInfo connections[MAX_CONNECTIONS]; mesh::Packet* composeMsgPacket(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char *text, uint32_t& expected_ack); + void sendAckTo(const ContactInfo& dest, uint32_t ack_hash); protected: BaseChatMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::PacketManager& mgr, mesh::MeshTables& tables) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index df510c28..d62253f9 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -51,7 +51,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112 file.read((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113 file.read((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 - file.read((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115 + file.read((uint8_t *) &_prefs->multi_acks, sizeof(_prefs->multi_acks)); // 115 file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 file.read((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 file.read(pad, 3); // 121 @@ -69,6 +69,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->sf = constrain(_prefs->sf, 7, 12); _prefs->cr = constrain(_prefs->cr, 5, 8); _prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, 1, 30); + _prefs->multi_acks = constrain(_prefs->multi_acks, 0, 1); file.close(); } @@ -106,7 +107,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112 file.write((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113 file.write((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 - file.write((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115 + file.write((uint8_t *) &_prefs->multi_acks, sizeof(_prefs->multi_acks)); // 115 file.write((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 file.write((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 file.write(pad, 3); // 121 @@ -131,7 +132,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch if (memcmp(command, "reboot", 6) == 0) { _board->reboot(); // doesn't return } else if (memcmp(command, "advert", 6) == 0) { - _callbacks->sendSelfAdvertisement(400); + _callbacks->sendSelfAdvertisement(1500); // longer delay, give CLI response time to be sent first strcpy(reply, "OK - Advert sent"); } else if (memcmp(command, "clock sync", 10) == 0) { uint32_t curr = getRTCClock()->getCurrentTime(); @@ -164,6 +165,21 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } } else if (memcmp(command, "neighbors", 9) == 0) { _callbacks->formatNeighborsReply(reply); + } else if (memcmp(command, "tempradio ", 10) == 0) { + strcpy(tmp, &command[10]); + const char *parts[5]; + int num = mesh::Utils::parseTextParts(tmp, parts, 5); + float freq = num > 0 ? atof(parts[0]) : 0.0f; + float bw = num > 1 ? atof(parts[1]) : 0.0f; + uint8_t sf = num > 2 ? atoi(parts[2]) : 0; + uint8_t cr = num > 3 ? atoi(parts[3]) : 0; + int temp_timeout_mins = num > 4 ? atoi(parts[4]) : 0; + if (freq >= 300.0f && freq <= 2500.0f && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f && temp_timeout_mins > 0) { + _callbacks->applyTempRadioParams(freq, bw, sf, cr, temp_timeout_mins); + sprintf(reply, "OK - temp params for %d mins", temp_timeout_mins); + } else { + strcpy(reply, "Error, invalid params"); + } } else if (memcmp(command, "password ", 9) == 0) { // change admin password StrHelper::strncpy(_prefs->password, &command[9], sizeof(_prefs->password)); @@ -180,6 +196,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold); } else if (memcmp(config, "agc.reset.interval", 18) == 0) { sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4); + } else if (memcmp(config, "multi.acks", 10) == 0) { + sprintf(reply, "> %d", (uint32_t) _prefs->multi_acks); } else if (memcmp(config, "allow.read.only", 15) == 0) { sprintf(reply, "> %s", _prefs->allow_read_only ? "on" : "off"); } else if (memcmp(config, "flood.advert.interval", 21) == 0) { @@ -235,6 +253,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _prefs->agc_reset_interval = atoi(&config[19]) / 4; savePrefs(); strcpy(reply, "OK"); + } else if (memcmp(config, "multi.acks ", 11) == 0) { + _prefs->multi_acks = atoi(&config[11]); + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "allow.read.only ", 16) == 0) { _prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0; savePrefs(); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index b50bf7f3..e2608379 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -21,7 +21,7 @@ struct NodePrefs { // persisted to file uint8_t sf; uint8_t cr; uint8_t allow_read_only; - uint8_t reserved2; + uint8_t multi_acks; float bw; uint8_t flood_max; uint8_t interference_threshold; @@ -45,6 +45,7 @@ public: virtual void formatNeighborsReply(char *reply) = 0; virtual const uint8_t* getSelfIdPubKey() = 0; virtual void clearStats() = 0; + virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0; }; class CommonCLI { diff --git a/src/helpers/CustomLLCC68.h b/src/helpers/radiolib/CustomLLCC68.h similarity index 100% rename from src/helpers/CustomLLCC68.h rename to src/helpers/radiolib/CustomLLCC68.h diff --git a/src/helpers/CustomLLCC68Wrapper.h b/src/helpers/radiolib/CustomLLCC68Wrapper.h similarity index 100% rename from src/helpers/CustomLLCC68Wrapper.h rename to src/helpers/radiolib/CustomLLCC68Wrapper.h diff --git a/src/helpers/CustomLR1110.h b/src/helpers/radiolib/CustomLR1110.h similarity index 100% rename from src/helpers/CustomLR1110.h rename to src/helpers/radiolib/CustomLR1110.h diff --git a/src/helpers/CustomLR1110Wrapper.h b/src/helpers/radiolib/CustomLR1110Wrapper.h similarity index 100% rename from src/helpers/CustomLR1110Wrapper.h rename to src/helpers/radiolib/CustomLR1110Wrapper.h diff --git a/src/helpers/CustomSTM32WLx.h b/src/helpers/radiolib/CustomSTM32WLx.h similarity index 100% rename from src/helpers/CustomSTM32WLx.h rename to src/helpers/radiolib/CustomSTM32WLx.h diff --git a/src/helpers/CustomSTM32WLxWrapper.h b/src/helpers/radiolib/CustomSTM32WLxWrapper.h similarity index 100% rename from src/helpers/CustomSTM32WLxWrapper.h rename to src/helpers/radiolib/CustomSTM32WLxWrapper.h diff --git a/src/helpers/CustomSX1262.h b/src/helpers/radiolib/CustomSX1262.h similarity index 100% rename from src/helpers/CustomSX1262.h rename to src/helpers/radiolib/CustomSX1262.h diff --git a/src/helpers/CustomSX1262Wrapper.h b/src/helpers/radiolib/CustomSX1262Wrapper.h similarity index 100% rename from src/helpers/CustomSX1262Wrapper.h rename to src/helpers/radiolib/CustomSX1262Wrapper.h diff --git a/src/helpers/CustomSX1268.h b/src/helpers/radiolib/CustomSX1268.h similarity index 100% rename from src/helpers/CustomSX1268.h rename to src/helpers/radiolib/CustomSX1268.h diff --git a/src/helpers/CustomSX1268Wrapper.h b/src/helpers/radiolib/CustomSX1268Wrapper.h similarity index 100% rename from src/helpers/CustomSX1268Wrapper.h rename to src/helpers/radiolib/CustomSX1268Wrapper.h diff --git a/src/helpers/CustomSX1276.h b/src/helpers/radiolib/CustomSX1276.h similarity index 100% rename from src/helpers/CustomSX1276.h rename to src/helpers/radiolib/CustomSX1276.h diff --git a/src/helpers/CustomSX1276Wrapper.h b/src/helpers/radiolib/CustomSX1276Wrapper.h similarity index 100% rename from src/helpers/CustomSX1276Wrapper.h rename to src/helpers/radiolib/CustomSX1276Wrapper.h diff --git a/src/helpers/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp similarity index 98% rename from src/helpers/RadioLibWrappers.cpp rename to src/helpers/radiolib/RadioLibWrappers.cpp index 96fb7aab..9014743a 100644 --- a/src/helpers/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -96,8 +96,9 @@ bool RadioLibWrapper::isInRecvMode() const { } int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) { + int len = 0; if (state & STATE_INT_READY) { - int len = _radio->getPacketLength(); + len = _radio->getPacketLength(); if (len > 0) { if (len > sz) { len = sz; } int err = _radio->readData(bytes, len); @@ -110,7 +111,6 @@ int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) { } } state = STATE_IDLE; // need another startReceive() - return len; } if (state != STATE_RX) { @@ -121,7 +121,7 @@ int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) { MESH_DEBUG_PRINTLN("RadioLibWrapper: error: startReceive(%d)", err); } } - return 0; + return len; } uint32_t RadioLibWrapper::getEstAirtimeFor(int len_bytes) { diff --git a/src/helpers/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h similarity index 100% rename from src/helpers/RadioLibWrappers.h rename to src/helpers/radiolib/RadioLibWrappers.h diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index ab5b459e..c95b1828 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -1,5 +1,11 @@ #include "EnvironmentSensorManager.h" +#if ENV_PIN_SDA && ENV_PIN_SCL +#define TELEM_WIRE &Wire1 // Use Wire1 as the I2C bus for Environment Sensors +#else +#define TELEM_WIRE &Wire // Use default I2C bus for Environment Sensors +#endif + #if ENV_INCLUDE_AHTX0 #define TELEM_AHTX_ADDRESS 0x38 // AHT10, AHT20 temperature and humidity sensor I2C address #include @@ -47,13 +53,43 @@ static Adafruit_INA3221 INA3221; static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS); #endif +#if ENV_INCLUDE_MLX90614 +#define TELEM_MLX90614_ADDRESS 0x5A // MLX90614 IR temperature sensor I2C address +#include +static Adafruit_MLX90614 MLX90614; +#endif + +#if ENV_INCLUDE_VL53L0X +#define TELEM_VL53L0X_ADDRESS 0x29 // VL53L0X time-of-flight distance sensor I2C address +#include +static Adafruit_VL53L0X VL53L0X; +#endif + +#if ENV_INCLUDE_GPS && RAK_BOARD +static uint32_t gpsResetPin = 0; +static bool i2cGPSFlag = false; +static bool serialGPSFlag = false; +#define TELEM_RAK12500_ADDRESS 0x42 //RAK12500 Ublox GPS via i2c +#include +static SFE_UBLOX_GNSS ublox_GNSS; +#endif + bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_GPS + #if RAK_BOARD + rakGPSInit(); //probe base board/sockets for GPS + #else initBasicGPS(); #endif + #endif + + #if ENV_PIN_SDA && ENV_PIN_SCL + Wire1.begin(ENV_PIN_SDA, ENV_PIN_SCL, 100000); + MESH_DEBUG_PRINTLN("Second I2C initialized on pins SDA: %d SCL: %d", ENV_PIN_SDA, ENV_PIN_SCL); + #endif #if ENV_INCLUDE_AHTX0 - if (AHTX0.begin(&Wire, 0, TELEM_AHTX_ADDRESS)) { + if (AHTX0.begin(TELEM_WIRE, 0, TELEM_AHTX_ADDRESS)) { MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS); AHTX0_initialized = true; } else { @@ -63,7 +99,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_BME280 - if (BME280.begin(TELEM_BME280_ADDRESS, &Wire)) { + if (BME280.begin(TELEM_BME280_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESS); MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID()); BME280_initialized = true; @@ -105,7 +141,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA3221 - if (INA3221.begin(TELEM_INA3221_ADDRESS, &Wire)) { + if (INA3221.begin(TELEM_INA3221_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS); MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID()); @@ -120,7 +156,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA219 - if (INA219.begin(&Wire)) { + if (INA219.begin(TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS); INA219_initialized = true; } else { @@ -129,6 +165,26 @@ bool EnvironmentSensorManager::begin() { } #endif + #if ENV_INCLUDE_MLX90614 + if (MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found MLX90614 at address: %02X", TELEM_MLX90614_ADDRESS); + MLX90614_initialized = true; + } else { + MLX90614_initialized = false; + MESH_DEBUG_PRINTLN("MLX90614 was not found at I2C address %02X", TELEM_MLX90614_ADDRESS); + } + #endif + + #if ENV_INCLUDE_VL53L0X + if (VL53L0X.begin(TELEM_VL53L0X_ADDRESS, false, TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found VL53L0X at address: %02X", TELEM_VL53L0X_ADDRESS); + VL53L0X_initialized = true; + } else { + VL53L0X_initialized = false; + MESH_DEBUG_PRINTLN("VL53L0X was not found at I2C address %02X", TELEM_VL53L0X_ADDRESS); + } + #endif + return true; } @@ -162,7 +218,7 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen #if ENV_INCLUDE_BMP280 if (BMP280_initialized) { telemetry.addTemperature(TELEM_CHANNEL_SELF, BMP280.readTemperature()); - telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BMP280.readPressure()); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BMP280.readPressure()/100); telemetry.addAltitude(TELEM_CHANNEL_SELF, BME280.readAltitude(TELEM_BME280_SEALEVELPRESSURE_HPA)); } #endif @@ -209,6 +265,25 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } #endif + #if ENV_INCLUDE_MLX90614 + if (MLX90614_initialized) { + telemetry.addTemperature(TELEM_CHANNEL_SELF, MLX90614.readObjectTempC()); + telemetry.addTemperature(TELEM_CHANNEL_SELF + 1, MLX90614.readAmbientTempC()); + } + #endif + + #if ENV_INCLUDE_VL53L0X + if (VL53L0X_initialized) { + VL53L0X_RangingMeasurementData_t measure; + VL53L0X.rangingTest(&measure, false); // pass in 'true' to get debug data + if (measure.RangeStatus != 4) { // phase failures + telemetry.addDistance(TELEM_CHANNEL_SELF, measure.RangeMilliMeter / 1000.0f); // convert mm to m + } else { + telemetry.addDistance(TELEM_CHANNEL_SELF, 0.0f); // no valid measurement + } + } + #endif + } return true; @@ -296,8 +371,85 @@ void EnvironmentSensorManager::initBasicGPS() { gps_active = false; //Set GPS visibility off until setting is changed } +#ifdef RAK_BOARD +void EnvironmentSensorManager::rakGPSInit(){ + + Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX); + + #ifdef GPS_BAUD_RATE + Serial1.begin(GPS_BAUD_RATE); + #else + Serial1.begin(9600); + #endif + + //search for the correct IO standby pin depending on socket used + if(gpsIsAwake(WB_IO2)){ + // MESH_DEBUG_PRINTLN("RAK base board is RAK19007/10"); + // MESH_DEBUG_PRINTLN("GPS is installed on Socket A"); + } + else if(gpsIsAwake(WB_IO4)){ + // MESH_DEBUG_PRINTLN("RAK base board is RAK19003/9"); + // MESH_DEBUG_PRINTLN("GPS is installed on Socket C"); + } + else if(gpsIsAwake(WB_IO5)){ + // MESH_DEBUG_PRINTLN("RAK base board is RAK19001/11"); + // MESH_DEBUG_PRINTLN("GPS is installed on Socket F"); + } + else{ + MESH_DEBUG_PRINTLN("No GPS found"); + gps_active = false; + gps_detected = false; + return; + } + + #ifndef FORCE_GPS_ALIVE // for use with repeaters, until GPS toggle is implimented + //Now that GPS is found and set up, set to sleep for initial state + stop_gps(); + #endif +} + +bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ + + //set initial waking state + pinMode(ioPin,OUTPUT); + digitalWrite(ioPin,LOW); + delay(500); + digitalWrite(ioPin,HIGH); + delay(500); + + //Try to init RAK12500 on I2C + if (ublox_GNSS.begin(Wire) == true){ + MESH_DEBUG_PRINTLN("RAK12500 GPS init correctly with pin %i",ioPin); + ublox_GNSS.setI2COutput(COM_TYPE_NMEA); + ublox_GNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); + gpsResetPin = ioPin; + i2cGPSFlag = true; + gps_active = true; + gps_detected = true; + return true; + } + else if(Serial1){ + MESH_DEBUG_PRINTLN("Serial GPS init correctly and is turned on"); + if(PIN_GPS_EN){ + gpsResetPin = PIN_GPS_EN; + } + serialGPSFlag = true; + gps_active = true; + gps_detected = true; + return true; + } + MESH_DEBUG_PRINTLN("GPS did not init with this IO pin... try the next"); + return false; +} +#endif + void EnvironmentSensorManager::start_gps() { gps_active = true; + #ifdef RAK_BOARD + pinMode(gpsResetPin, OUTPUT); + digitalWrite(gpsResetPin, HIGH); + return; + #endif #ifdef PIN_GPS_EN pinMode(PIN_GPS_EN, OUTPUT); digitalWrite(PIN_GPS_EN, HIGH); @@ -309,6 +461,11 @@ void EnvironmentSensorManager::start_gps() { void EnvironmentSensorManager::stop_gps() { gps_active = false; + #ifdef RAK_BOARD + pinMode(gpsResetPin, OUTPUT); + digitalWrite(gpsResetPin, LOW); + return; + #endif #ifdef PIN_GPS_EN pinMode(PIN_GPS_EN, OUTPUT); digitalWrite(PIN_GPS_EN, LOW); @@ -324,12 +481,29 @@ void EnvironmentSensorManager::loop() { _location->loop(); if (millis() > next_gps_update) { - if (gps_active && _location->isValid()) { + 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 + if(i2cGPSFlag){ + node_lat = ((double)ublox_GNSS.getLatitude())/10000000.; + node_lon = ((double)ublox_GNSS.getLongitude())/10000000.; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + } + else if (serialGPSFlag && _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 + //MESH_DEBUG_PRINTLN("No valid GPS data"); + #endif + } next_gps_update = millis() + 1000; } } -#endif \ No newline at end of file +#endif diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index f7804431..63c56643 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -15,6 +15,8 @@ protected: bool INA219_initialized = false; bool SHTC3_initialized = false; bool LPS22HB_initialized = false; + bool MLX90614_initialized = false; + bool VL53L0X_initialized = false; bool gps_detected = false; bool gps_active = false; @@ -24,6 +26,10 @@ protected: void start_gps(); void stop_gps(); void initBasicGPS(); + #ifdef RAK_BOARD + void rakGPSInit(); + bool gpsIsAwake(uint8_t ioPin); + #endif #endif diff --git a/variants/generic-e22/target.h b/variants/generic-e22/target.h index e5c116e1..442706f3 100644 --- a/variants/generic-e22/target.h +++ b/variants/generic-e22/target.h @@ -2,10 +2,10 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include -#include +#include +#include #include #include diff --git a/variants/heltec_ct62/target.cpp b/variants/heltec_ct62/target.cpp index eae43bd1..a8c15f5f 100644 --- a/variants/heltec_ct62/target.cpp +++ b/variants/heltec_ct62/target.cpp @@ -10,43 +10,10 @@ 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); - -#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 + rtc_clock.begin(Wire); + return radio.std_init(&SPI); } uint32_t radio_get_rng_seed() { diff --git a/variants/heltec_ct62/target.h b/variants/heltec_ct62/target.h index ff114e6e..9639ab2d 100644 --- a/variants/heltec_ct62/target.h +++ b/variants/heltec_ct62/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include "HT-CT62Board.h" -#include +#include #include #include diff --git a/variants/heltec_tracker/target.h b/variants/heltec_tracker/target.h index 3184bec9..c08be80a 100644 --- a/variants/heltec_tracker/target.h +++ b/variants/heltec_tracker/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/heltec_v2/target.h b/variants/heltec_v2/target.h index f758c193..0c330316 100644 --- a/variants/heltec_v2/target.h +++ b/variants/heltec_v2/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index edc683d2..67415ae9 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -147,6 +147,30 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_v3_sensor] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D ADVERT_NAME='"Heltec v3 Sensor"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ENV_PIN_SDA=33 + -D ENV_PIN_SCL=34 + -D ENV_INCLUDE_MLX90614=1 + -D ENV_INCLUDE_VL53L0X=1 + -D DISPLAY_CLASS=SSD1306Display +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/simple_sensor> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + ${esp32_ota.lib_deps} + adafruit/Adafruit MLX90614 Library @ ^2.1.5 + adafruit/Adafruit_VL53L0X @ ^1.2.4 + [env:Heltec_WSL3_repeater] extends = Heltec_lora32_v3 build_flags = @@ -214,4 +238,3 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} - diff --git a/variants/heltec_v3/target.h b/variants/heltec_v3/target.h index 701f9cd5..992a3d2c 100644 --- a/variants/heltec_v3/target.h +++ b/variants/heltec_v3/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/heltec_wireless_paper/target.h b/variants/heltec_wireless_paper/target.h index 7d901c01..cb95905c 100644 --- a/variants/heltec_wireless_paper/target.h +++ b/variants/heltec_wireless_paper/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_t3s3/target.h b/variants/lilygo_t3s3/target.h index 609248ae..b768b2b0 100644 --- a/variants/lilygo_t3s3/target.h +++ b/variants/lilygo_t3s3/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_t3s3_sx1276/target.h b/variants/lilygo_t3s3_sx1276/target.h index c5fc0be0..52ecf867 100644 --- a/variants/lilygo_t3s3_sx1276/target.h +++ b/variants/lilygo_t3s3_sx1276/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_tbeam_SX1262/target.h b/variants/lilygo_tbeam_SX1262/target.h index da4c5d5c..cb9b28df 100644 --- a/variants/lilygo_tbeam_SX1262/target.h +++ b/variants/lilygo_tbeam_SX1262/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_tbeam_SX1276/target.h b/variants/lilygo_tbeam_SX1276/target.h index 41435210..bcd8cb0b 100644 --- a/variants/lilygo_tbeam_SX1276/target.h +++ b/variants/lilygo_tbeam_SX1276/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 //#include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.h b/variants/lilygo_tbeam_supreme_SX1262/target.h index ea1138af..62a92329 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.h +++ b/variants/lilygo_tbeam_supreme_SX1262/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include diff --git a/variants/lilygo_tlora_c6/target.h b/variants/lilygo_tlora_c6/target.h index eef923ab..c26d5958 100644 --- a/variants/lilygo_tlora_c6/target.h +++ b/variants/lilygo_tlora_c6/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include diff --git a/variants/lilygo_tlora_v2_1/target.h b/variants/lilygo_tlora_v2_1/target.h index 8e48c3e7..f05b8055 100644 --- a/variants/lilygo_tlora_v2_1/target.h +++ b/variants/lilygo_tlora_v2_1/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/meshadventurer/target.h b/variants/meshadventurer/target.h index 0e0235ba..6aeaf079 100644 --- a/variants/meshadventurer/target.h +++ b/variants/meshadventurer/target.h @@ -2,10 +2,10 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include -#include +#include +#include #include #include #include diff --git a/variants/minewsemi_me25ls01/target.h b/variants/minewsemi_me25ls01/target.h index db832f91..a5da5823 100644 --- a/variants/minewsemi_me25ls01/target.h +++ b/variants/minewsemi_me25ls01/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/nano_g2_ultra/target.h b/variants/nano_g2_ultra/target.h index e0d891e9..5cde6405 100644 --- a/variants/nano_g2_ultra/target.h +++ b/variants/nano_g2_ultra/target.h @@ -3,8 +3,8 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include "nano-g2.h" -#include -#include +#include +#include #include #include #ifdef DISPLAY_CLASS @@ -12,8 +12,7 @@ #endif #include -class NanoG2UltraSensorManager : public SensorManager -{ +class NanoG2UltraSensorManager : public SensorManager { bool gps_active = false; LocationProvider *_location; diff --git a/variants/picow/target.h b/variants/picow/target.h index a89b5ba8..7b1e7437 100644 --- a/variants/picow/target.h +++ b/variants/picow/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include diff --git a/variants/promicro/target.h b/variants/promicro/target.h index 7646d459..de2719e6 100644 --- a/variants/promicro/target.h +++ b/variants/promicro/target.h @@ -2,10 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include -#include +#include #include #ifdef DISPLAY_CLASS #include @@ -27,4 +26,3 @@ 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(); - diff --git a/variants/rak3x72/platformio.ini b/variants/rak3x72/platformio.ini index 386866ba..2fee1a76 100644 --- a/variants/rak3x72/platformio.ini +++ b/variants/rak3x72/platformio.ini @@ -21,6 +21,14 @@ build_flags = ${rak3x72.build_flags} build_src_filter = ${rak3x72.build_src_filter} +<../examples/simple_repeater/main.cpp> +[env:rak3x72-sensor] +extends = rak3x72 +build_flags = ${rak3x72.build_flags} + -D ADVERT_NAME='"RAK3x72 Sensor"' + -D ADMIN_PASSWORD='"password"' +build_src_filter = ${rak3x72.build_src_filter} + +<../examples/simple_sensor> + [env:rak3x72_companion_radio_usb] extends = rak3x72 build_flags = ${rak3x72.build_flags} diff --git a/variants/rak3x72/target.h b/variants/rak3x72/target.h index 6c86a702..61e4747d 100644 --- a/variants/rak3x72/target.h +++ b/variants/rak3x72/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include @@ -22,7 +22,7 @@ public: uint32_t raw = 0; for (int i=0; i<8;i++) { raw += analogRead(PIN_VBAT_READ); - } + } return ((double)raw) * ADC_MULTIPLIER / 8 / 4096; } }; diff --git a/variants/rak4631/RAK4631Board.h b/variants/rak4631/RAK4631Board.h index 9232e39c..7f3a8fea 100644 --- a/variants/rak4631/RAK4631Board.h +++ b/variants/rak4631/RAK4631Board.h @@ -13,14 +13,11 @@ #define P_LORA_MOSI 44 #define SX126X_POWER_EN 37 -#define P_GPS_SDA 13 //GPS SDA pin (output option) -#define P_GPS_SCL 14 //GPS SCL pin (output option) -#define P_GPS_TX 16 //GPS TX pin -#define P_GPS_RX 15 //GPS RX pin -#define P_GPS_STANDBY_A 34 //GPS Reset/Standby pin (IO2 for socket A) -#define P_GPS_STANDBY_C 4 //GPS Reset/Standby pin (IO4 for socket C) -#define P_GPS_STANDBY_F 9 //GPS Reset/Standby pin (IO5 for socket F) -#define P_GPS_1PPS 17 //GPS PPS pin +//#define PIN_GPS_SDA 13 //GPS SDA pin (output option) +//#define PIN_GPS_SCL 14 //GPS SCL pin (output option) +//#define PIN_GPS_TX 16 //GPS TX pin +//#define PIN_GPS_RX 15 //GPS RX pin +#define PIN_GPS_1PPS 17 //GPS PPS pin #define GPS_BAUD_RATE 9600 #define GPS_ADDRESS 0x42 //i2c address for GPS diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index d4f8028b..29d07a78 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -6,20 +6,41 @@ board_check = true build_flags = ${nrf52_base.build_flags} -I variants/rak4631 -D RAK_4631 + -D RAK_BOARD -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 + -D PIN_GPS_TX=16 + -D PIN_GPS_RX=15 + -D PIN_GPS_EN=-1 -D PIN_OLED_RESET=-1 -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 ENV_INCLUDE_GPS=1 + -D ENV_INCLUDE_AHTX0=1 + -D ENV_INCLUDE_BME280=1 + -D ENV_INCLUDE_BMP280=1 + -D ENV_INCLUDE_SHTC3=1 + -D ENV_INCLUDE_LPS22HB=1 + -D ENV_INCLUDE_INA3221=1 + -D ENV_INCLUDE_INA219=1 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> + + lib_deps = ${nrf52_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 stevemarple/MicroNMEA @ ^2.0.6 + arduino-libraries/Arduino_LPS22HB@^1.0.2 + adafruit/Adafruit INA3221 Library @ ^1.0.1 + adafruit/Adafruit INA219 @ ^1.2.3 + adafruit/Adafruit AHTX0 @ ^2.0.5 + adafruit/Adafruit BME280 Library @ ^2.3.0 + adafruit/Adafruit BMP280 Library @ ^2.6.8 + adafruit/Adafruit SHTC3 Library @ ^1.0.1 + sparkfun/SparkFun u-blox GNSS Arduino Library@^2.2.27 [env:RAK_4631_Repeater] extends = rak4631 @@ -37,30 +58,6 @@ build_src_filter = ${rak4631.build_src_filter} + +<../examples/simple_repeater> -[env:RAK_4631_GPS_Repeater] -extends = rak4631 -build_flags = - ${rak4631.build_flags} - -D DISPLAY_CLASS=SSD1306Display - -D ADVERT_NAME='"RAK4631 GPS Repeater"' - -D ADVERT_LAT=0.0 - -D ADVERT_LON=0.0 - -D ADMIN_PASSWORD='"password"' - -D MAX_NEIGHBOURS=8 - -D FORCE_GPS_ALIVE=1 - -D ENV_INCLUDE_GPS=1 - -D ENV_INCLUDE_BME680=1 -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -build_src_filter = ${rak4631.build_src_filter} - + - +<../examples/simple_repeater> -lib_deps = - ${rak4631.lib_deps} - sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 - https://github.com/boschsensortec/Bosch-BSEC2-Library - https://github.com/boschsensortec/Bosch-BME68x-Library - [env:RAK_4631_room_server] extends = rak4631 build_flags = @@ -117,33 +114,6 @@ lib_deps = ${rak4631.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:RAK_4631_GPS_companion_radio_ble] -extends = rak4631 -build_flags = - ${rak4631.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 BLE_PIN_CODE=123456 - -D BLE_DEBUG_LOGGING=1 - -D OFFLINE_QUEUE_SIZE=256 - -D ENV_INCLUDE_GPS=1 - -D ENV_INCLUDE_BME680=1 -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -build_src_filter = ${rak4631.build_src_filter} - + - + - +<../examples/companion_radio> -lib_deps = - ${rak4631.lib_deps} - sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 - https://github.com/boschsensortec/Bosch-BSEC2-Library - https://github.com/boschsensortec/Bosch-BME68x-Library - densaugeo/base64 @ ~1.4.0 - [env:RAK_4631_terminal_chat] extends = rak4631 build_flags = diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index e9d80421..4e9d3cce 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -1,10 +1,13 @@ #include #include "target.h" #include -#include RAK4631Board board; +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; +#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); @@ -13,80 +16,11 @@ VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS -MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Wire); -RAK4631SensorManager sensors = RAK4631SensorManager(nmea); + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); #else -RAK4631SensorManager sensors; -#endif - -#if ENV_INCLUDE_BME680 -#ifndef TELEM_BME680_ADDRESS -#define TELEM_BME680_ADDRESS 0x76 // BME680 environmental sensor I2C address -#endif -#include -static Bsec2 BME680; -static float rawPressure = 0; -static float rawTemperature = 0; -static float compTemperature = 0; -static float rawHumidity = 0; -static float compHumidity = 0; -static float readIAQ = 0; -static float readStaticIAQ = 0; -static float readCO2 = 0; -#endif - -#ifdef DISPLAY_CLASS - DISPLAY_CLASS display; -#endif - -#ifdef MESH_DEBUG -uint32_t deviceOnline = 0x00; -static void scanDevices(TwoWire *w) -{ - uint8_t err, addr; - int nDevices = 0; - uint32_t start = 0; - - Serial.println("Scanning I2C for Devices"); - for (addr = 1; addr < 127; addr++) { - start = millis(); - w->beginTransmission(addr); delay(2); - err = w->endTransmission(); - if (err == 0) { - nDevices++; - switch (addr) { - case 0x42: - Serial.println("\tFound RAK12500 GPS Sensor"); - deviceOnline |= RAK12500_ONLINE; - break; - case 0x76: - Serial.println("\tFound RAK1906 Environment Sensor"); - deviceOnline |= BME680_ONLINE; - break; - default: - Serial.print("\tI2C device found at address 0x"); - if (addr < 16) { - Serial.print("0"); - } - Serial.print(addr, HEX); - Serial.println(" !"); - break; - } - - } else if (err == 4) { - Serial.print("Unknow error at address 0x"); - if (addr < 16) { - Serial.print("0"); - } - Serial.println(addr, HEX); - } - } - if (nDevices == 0) - Serial.println("No I2C devices found\n"); - - Serial.println("Scan for devices is complete."); - Serial.println("\n"); -} + EnvironmentSensorManager sensors; #endif bool radio_init() { @@ -109,324 +43,6 @@ void radio_set_tx_power(uint8_t dbm) { radio.setOutputPower(dbm); } -#if ENV_INCLUDE_GPS -void RAK4631SensorManager::start_gps() -{ - //function currently not used - gps_active = true; - pinMode(disStandbyPin, OUTPUT); - digitalWrite(disStandbyPin, 1); - MESH_DEBUG_PRINTLN("GPS should be on now"); -} - -void RAK4631SensorManager::stop_gps() -{ - //function currently not used - gps_active = false; - pinMode(disStandbyPin, OUTPUT); - digitalWrite(disStandbyPin, 0); - MESH_DEBUG_PRINTLN("GPS should be off now"); -} - -void RAK4631SensorManager::sleep_gps() { - gps_active = false; - ublox_GNSS.powerSaveMode(); - MESH_DEBUG_PRINTLN("GPS should be sleeping now"); -} - -void RAK4631SensorManager::wake_gps() { - gps_active = true; - ublox_GNSS.powerSaveMode(false); - MESH_DEBUG_PRINTLN("GPS should be waking now"); -} - -bool RAK4631SensorManager::gpsIsAwake(uint32_t ioPin){ - - int pinInitialState = 0; - - //set initial waking state - pinMode(ioPin,OUTPUT); - digitalWrite(ioPin,0); - delay(1000); - digitalWrite(ioPin,1); - delay(1000); - - if (ublox_GNSS.begin(Wire) == true){ - MESH_DEBUG_PRINTLN("GPS init correctly and GPS is turned on"); - ublox_GNSS.setI2COutput(COM_TYPE_NMEA); - ublox_GNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); - disStandbyPin = ioPin; - gps_active = true; - gps_detected = true; - return true; - } - else - MESH_DEBUG_PRINTLN("GPS failed to init on this IO pin... try the next"); - //digitalWrite(ioPin,pinInitialState); //reset the IO pin to initial state - return false; -} -#endif - -#if ENV_INCLUDE_BME680 -static void checkBMEStatus(Bsec2 bsec) { - if (bsec.status < BSEC_OK) - { - MESH_DEBUG_PRINTLN("BSEC error code : %f", float(bsec.status)); - } - else if (bsec.status > BSEC_OK) - { - MESH_DEBUG_PRINTLN("BSEC warning code : %f", float(bsec.status)); - } - - if (bsec.sensor.status < BME68X_OK) - { - MESH_DEBUG_PRINTLN("BME68X error code : %f", bsec.sensor.status); - } - else if (bsec.sensor.status > BME68X_OK) - { - MESH_DEBUG_PRINTLN("BME68X warning code : %f", bsec.sensor.status); - } -} - -static void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec) { - if (!outputs.nOutputs) { - MESH_DEBUG_PRINTLN("No new data to report out"); - return; - } - - MESH_DEBUG_PRINTLN("BSEC outputs:\n\tTime stamp = %f", (int) (outputs.output[0].time_stamp / INT64_C(1000000))); - for (uint8_t i = 0; i < outputs.nOutputs; i++) { - const bsecData output = outputs.output[i]; - switch (output.sensor_id) - { - case BSEC_OUTPUT_IAQ: - readIAQ = output.signal; - MESH_DEBUG_PRINTLN("\tIAQ = %f", output.signal); - MESH_DEBUG_PRINTLN("\tIAQ accuracy = %f", output.accuracy); - break; - case BSEC_OUTPUT_RAW_TEMPERATURE: - rawTemperature = output.signal; - MESH_DEBUG_PRINTLN("\tTemperature = %f", output.signal); - break; - case BSEC_OUTPUT_RAW_PRESSURE: - rawPressure = output.signal; - MESH_DEBUG_PRINTLN("\tPressure = %f", output.signal); - break; - case BSEC_OUTPUT_RAW_HUMIDITY: - rawHumidity = output.signal; - MESH_DEBUG_PRINTLN("\tHumidity = %f", output.signal); - break; - case BSEC_OUTPUT_RAW_GAS: - MESH_DEBUG_PRINTLN("\tGas resistance = %f", output.signal); - break; - case BSEC_OUTPUT_STABILIZATION_STATUS: - MESH_DEBUG_PRINTLN("\tStabilization status = %f", output.signal); - break; - case BSEC_OUTPUT_RUN_IN_STATUS: - MESH_DEBUG_PRINTLN("\tRun in status = %f", output.signal); - break; - case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE: - compTemperature = output.signal; - MESH_DEBUG_PRINTLN("\tCompensated temperature = %f", output.signal); - break; - case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY: - compHumidity = output.signal; - MESH_DEBUG_PRINTLN("\tCompensated humidity = %f", output.signal); - break; - case BSEC_OUTPUT_STATIC_IAQ: - readStaticIAQ = output.signal; - MESH_DEBUG_PRINTLN("\tStatic IAQ = %f", output.signal); - break; - case BSEC_OUTPUT_CO2_EQUIVALENT: - readCO2 = output.signal; - MESH_DEBUG_PRINTLN("\tCO2 Equivalent = %f", output.signal); - break; - case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT: - MESH_DEBUG_PRINTLN("\tbVOC equivalent = %f", output.signal); - break; - case BSEC_OUTPUT_GAS_PERCENTAGE: - MESH_DEBUG_PRINTLN("\tGas percentage = %f", output.signal); - break; - case BSEC_OUTPUT_COMPENSATED_GAS: - MESH_DEBUG_PRINTLN("\tCompensated gas = %f", output.signal); - break; - default: - break; - } - } -} -#endif - -bool RAK4631SensorManager::begin() { - - #ifdef MESH_DEBUG - scanDevices(&Wire); - #endif - - #if ENV_INCLUDE_GPS - //search for the correct IO standby pin depending on socket used - if(gpsIsAwake(P_GPS_STANDBY_A)){ - MESH_DEBUG_PRINTLN("GPS is on socket A"); - } - else if(gpsIsAwake(P_GPS_STANDBY_C)){ - MESH_DEBUG_PRINTLN("GPS is on socket C"); - } - else if(gpsIsAwake(P_GPS_STANDBY_F)){ - MESH_DEBUG_PRINTLN("GPS is on socket F"); - } - else{ - MESH_DEBUG_PRINTLN("Error: No GPS found on sockets A, C or F"); - gps_active = false; - gps_detected = false; - return false; - } - - #ifndef FORCE_GPS_ALIVE - //Now that GPS is found and set up, set to sleep for initial state - stop_gps(); - #endif - #endif - - #if ENV_INCLUDE_BME680 - - bsecSensor sensorList[5] = { - BSEC_OUTPUT_IAQ, - // BSEC_OUTPUT_RAW_TEMPERATURE, - BSEC_OUTPUT_RAW_PRESSURE, - // BSEC_OUTPUT_RAW_HUMIDITY, - // BSEC_OUTPUT_RAW_GAS, - // BSEC_OUTPUT_STABILIZATION_STATUS, - // BSEC_OUTPUT_RUN_IN_STATUS, - BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, - BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, - BSEC_OUTPUT_STATIC_IAQ, - // BSEC_OUTPUT_CO2_EQUIVALENT, - // BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, - // BSEC_OUTPUT_GAS_PERCENTAGE, - // BSEC_OUTPUT_COMPENSATED_GAS - }; - - if(!BME680.begin(TELEM_BME680_ADDRESS, Wire)){ - checkBMEStatus(BME680); - bme680_present = false; - bme680_active = false; - return false; - } - - MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESS); - bme680_present = true; - bme680_active = true; - - if (SAMPLING_RATE == BSEC_SAMPLE_RATE_ULP) - { - BME680.setTemperatureOffset(BSEC_SAMPLE_RATE_ULP); - } - else if (SAMPLING_RATE == BSEC_SAMPLE_RATE_LP) - { - BME680.setTemperatureOffset(TEMP_OFFSET_LP); - } - - if (!BME680.updateSubscription(sensorList, ARRAY_LEN(sensorList), SAMPLING_RATE)) - { - checkBMEStatus(BME680); - } - - BME680.attachCallback(newDataCallback); - - #endif -} - -bool RAK4631SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { - #ifdef ENV_INCLUDE_GPS - if (requester_permissions & TELEM_PERM_LOCATION && gps_active) { // does requester have permission? - telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); - } - #endif - - if (requester_permissions & TELEM_PERM_ENVIRONMENT) { - - #if ENV_INCLUDE_BME680 - if (bme680_active) { - telemetry.addTemperature(TELEM_CHANNEL_SELF, compTemperature); - telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, compHumidity); - telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, rawPressure); - telemetry.addTemperature(TELEM_CHANNEL_SELF+1, readIAQ); - telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF+1, readStaticIAQ); - } - #endif - } - return true; -} - -void RAK4631SensorManager::loop() { - static long next_update = 0; - - #ifdef ENV_INCLUDE_GPS - _nmea->loop(); - #endif - - if (millis() > next_update) { - - #ifdef ENV_INCLUDE_GPS - if(gps_active){ - node_lat = (double)ublox_GNSS.getLatitude()/10000000.; - node_lon = (double)ublox_GNSS.getLongitude()/10000000.; - node_altitude = (double)ublox_GNSS.getAltitude()/1000.; - MESH_DEBUG_PRINT("lat %f lon %f alt %f\r\n", node_lat, node_lon, node_altitude); - } - #endif - - #ifdef ENV_INCLUDE_BME680 - if(bme680_active){ - if (!BME680.run()){ - checkBMEStatus(BME680); - } - } - #endif - next_update = millis() + 1000; - } - -} - -int RAK4631SensorManager::getNumSettings() const { - #if ENV_INCLUDE_GPS - return gps_detected ? 1 : 0; // only show GPS setting if GPS is detected - #else - return 0; - #endif -} - -const char* RAK4631SensorManager::getSettingName(int i) const { - #if ENV_INCLUDE_GPS - return (gps_detected && i == 0) ? "gps" : NULL; - #else - return NULL; - #endif -} - -const char* RAK4631SensorManager::getSettingValue(int i) const { - #if ENV_INCLUDE_GPS - if (gps_detected && i == 0) { - return gps_active ? "1" : "0"; - } - #endif - return NULL; -} - -bool RAK4631SensorManager::setSettingValue(const char* name, const char* value) { - #if ENV_INCLUDE_GPS - if (gps_detected && strcmp(name, "gps") == 0) { - if (strcmp(value, "0") == 0) { - stop_gps(); - } else { - start_gps(); - } - return true; - } - #endif - return false; // not supported -} - mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity diff --git a/variants/rak4631/target.h b/variants/rak4631/target.h index e1545581..c4c88183 100644 --- a/variants/rak4631/target.h +++ b/variants/rak4631/target.h @@ -2,84 +2,21 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include -#include -#if ENV_INCLUDE_GPS - #include - #include -#endif +#include + #ifdef DISPLAY_CLASS #include + extern DISPLAY_CLASS display; #endif -#define _BV(x) (1 << x) - -class RAK4631SensorManager: public SensorManager { - #if ENV_INCLUDE_GPS - bool gps_active = false; - bool gps_detected = false; - LocationProvider * _nmea; - SFE_UBLOX_GNSS ublox_GNSS; - uint32_t disStandbyPin = 0; - - void start_gps(); - void stop_gps(); - void sleep_gps(); - void wake_gps(); - bool gpsIsAwake(uint32_t ioPin); - #endif - - #if ENV_INCLUDE_BME680 - bool bme680_active = false; - bool bme680_present = false; - #define SAMPLING_RATE BSEC_SAMPLE_RATE_ULP - #endif - - public: - #if ENV_INCLUDE_GPS - RAK4631SensorManager(LocationProvider &nmea): _nmea(&nmea) { } - #else - RAK4631SensorManager() { } - #endif - - void loop() override; - bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) 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; - bool begin() override; -}; - extern RAK4631Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern RAK4631SensorManager sensors; - -#ifdef DISPLAY_CLASS - extern DISPLAY_CLASS display; -#endif - -enum { - POWERMANAGE_ONLINE = _BV(0), - DISPLAY_ONLINE = _BV(1), - RADIO_ONLINE = _BV(2), - GPS_ONLINE = _BV(3), - PSRAM_ONLINE = _BV(4), - SDCARD_ONLINE = _BV(5), - AXDL345_ONLINE = _BV(6), - BME280_ONLINE = _BV(7), - BMP280_ONLINE = _BV(8), - BME680_ONLINE = _BV(9), - QMC6310_ONLINE = _BV(10), - QMI8658_ONLINE = _BV(11), - PCF8563_ONLINE = _BV(12), - OSC32768_ONLINE = _BV(13), - RAK12500_ONLINE = _BV(14), -}; +extern EnvironmentSensorManager sensors; bool radio_init(); uint32_t radio_get_rng_seed(); diff --git a/variants/sensecap_solar/platformio.ini b/variants/sensecap_solar/platformio.ini index 281f6688..9626e9cd 100644 --- a/variants/sensecap_solar/platformio.ini +++ b/variants/sensecap_solar/platformio.ini @@ -32,13 +32,11 @@ build_flags = ${nrf52_base.build_flags} build_src_filter = ${nrf52_base.build_src_filter} + + - + - +<../variants/SenseCap_Solar> + +<../variants/sensecap_solar> debug_tool = jlink upload_protocol = nrfutil lib_deps = ${nrf52_base.lib_deps} - rweather/Crypto @ ^0.4.0 adafruit/Adafruit INA3221 Library @ ^1.0.1 adafruit/Adafruit INA219 @ ^1.2.3 adafruit/Adafruit AHTX0 @ ^2.0.5 @@ -72,4 +70,37 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${SenseCap_Solar.build_src_filter} - +<../examples/simple_room_server/main.cpp> \ No newline at end of file + +<../examples/simple_room_server/main.cpp> + +[env:SenseCap_Solar_companion_radio_ble] +extends = SenseCap_Solar +build_flags = + ${SenseCap_Solar.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${SenseCap_Solar.build_src_filter} + + + +<../examples/companion_radio> +lib_deps = + ${SenseCap_Solar.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:SenseCap_Solar_companion_radio_usb] +extends = SenseCap_Solar +build_flags = + ${SenseCap_Solar.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${SenseCap_Solar.build_src_filter} + + + +<../examples/companion_radio> +lib_deps = + ${SenseCap_Solar.lib_deps} + densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/variants/sensecap_solar/target.h b/variants/sensecap_solar/target.h index 63506951..90d60ba5 100644 --- a/variants/sensecap_solar/target.h +++ b/variants/sensecap_solar/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/station_g2/target.h b/variants/station_g2/target.h index 695e700a..6d80f098 100644 --- a/variants/station_g2/target.h +++ b/variants/station_g2/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include diff --git a/variants/t1000-e/target.h b/variants/t1000-e/target.h index 855c4d67..6ac0d3a6 100644 --- a/variants/t1000-e/target.h +++ b/variants/t1000-e/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include "T1000eBoard.h" -#include +#include #include #include #include diff --git a/variants/t114/target.h b/variants/t114/target.h index 0f6ebaa1..8831d9f7 100644 --- a/variants/t114/target.h +++ b/variants/t114/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/techo/target.h b/variants/techo/target.h index 8fb8b6ca..7c05e742 100644 --- a/variants/techo/target.h +++ b/variants/techo/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/tenstar_c3/target.h b/variants/tenstar_c3/target.h index 0aea87dd..fa29e52b 100644 --- a/variants/tenstar_c3/target.h +++ b/variants/tenstar_c3/target.h @@ -2,10 +2,10 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include -#include +#include +#include #include #include diff --git a/variants/thinknode_m1/target.h b/variants/thinknode_m1/target.h index 550ee62a..c938d422 100644 --- a/variants/thinknode_m1/target.h +++ b/variants/thinknode_m1/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/waveshare_rp2040_lora/target.h b/variants/waveshare_rp2040_lora/target.h index 3ebe0570..149b9469 100644 --- a/variants/waveshare_rp2040_lora/target.h +++ b/variants/waveshare_rp2040_lora/target.h @@ -4,8 +4,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/variants/wio-e5-dev/target.h b/variants/wio-e5-dev/target.h index 83fd9cf1..5fdd0aba 100644 --- a/variants/wio-e5-dev/target.h +++ b/variants/wio-e5-dev/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include @@ -16,7 +16,7 @@ public: // Just returns ADC value for now to test adc uint16_t getBattMilliVolts() override { - uint32_t raw = analogRead(PIN_A3); + uint32_t raw = analogRead(PIN_A3); return raw; } }; diff --git a/variants/wio-e5-mini/target.h b/variants/wio-e5-mini/target.h index d0ad24b7..921c38d3 100644 --- a/variants/wio-e5-mini/target.h +++ b/variants/wio-e5-mini/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS @@ -37,14 +37,14 @@ public: uint32_t raw = 0; for (int i=0; i<8;i++) { raw += analogRead(PIN_A3); - } + } return ((double)raw) * 1.73 * 5 * 1000 / 8 / 4096; } }; class WIOE5SensorManager : public SensorManager { BME280I2C bme; - bool has_bme = false; + bool has_bme = false; public: WIOE5SensorManager() {} diff --git a/variants/wio-tracker-l1/target.h b/variants/wio-tracker-l1/target.h index 0aac6c59..ab42b7b5 100644 --- a/variants/wio-tracker-l1/target.h +++ b/variants/wio-tracker-l1/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/wio-tracker-l1/variant.h b/variants/wio-tracker-l1/variant.h index 7f51dcb8..094f8edf 100644 --- a/variants/wio-tracker-l1/variant.h +++ b/variants/wio-tracker-l1/variant.h @@ -30,7 +30,7 @@ #define PIN_BUTTON3 (26) // Joystick Down #define PIN_BUTTON4 (27) // Joystick Left #define PIN_BUTTON5 (28) // Joystick Right -#define PIN_BUTTON6 (28) // Joystick Press +#define PIN_BUTTON6 (29) // Joystick Press #define PIN_USER_BTN PIN_BUTTON1 #define JOYSTICK_UP PIN_BUTTON2 #define JOYSTICK_DOWN PIN_BUTTON3 @@ -70,6 +70,8 @@ #define P_LORA_NSS (4) #define SX126X_RXEN (5) #define SX126X_TXEN RADIOLIB_NC +#define SX126X_DIO2_AS_RF_SWITCH true +#define SX126X_DIO3_TCXO_VOLTAGE (1.8f) // Wire Interfaces #define WIRE_INTERFACES_COUNT (2) diff --git a/variants/xiao_c3/target.h b/variants/xiao_c3/target.h index 0aea87dd..fa29e52b 100644 --- a/variants/xiao_c3/target.h +++ b/variants/xiao_c3/target.h @@ -2,10 +2,10 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include -#include +#include +#include #include #include diff --git a/variants/xiao_c6/target.h b/variants/xiao_c6/target.h index eef923ab..c26d5958 100644 --- a/variants/xiao_c6/target.h +++ b/variants/xiao_c6/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include diff --git a/variants/xiao_nrf52/target.h b/variants/xiao_nrf52/target.h index c8c6a42a..86f546b8 100644 --- a/variants/xiao_nrf52/target.h +++ b/variants/xiao_nrf52/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/xiao_rp2040/target.h b/variants/xiao_rp2040/target.h index 6a3c192b..34861db3 100644 --- a/variants/xiao_rp2040/target.h +++ b/variants/xiao_rp2040/target.h @@ -4,8 +4,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/variants/xiao_s3_wio/target.h b/variants/xiao_s3_wio/target.h index 609248ae..b768b2b0 100644 --- a/variants/xiao_s3_wio/target.h +++ b/variants/xiao_s3_wio/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS