diff --git a/examples/ping_client/main.cpp b/examples/ping_client/main.cpp deleted file mode 100644 index 60b68533..00000000 --- a/examples/ping_client/main.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include // needed for PlatformIO -#include -#include - -#define RADIOLIB_STATIC_ONLY 1 -#include -#include -#include -#include -#include - -/* ------------------------------ Config -------------------------------- */ - -#ifndef LORA_FREQ - #define LORA_FREQ 915.0 -#endif -#ifndef LORA_BW - #define LORA_BW 125 -#endif -#ifndef LORA_SF - #define LORA_SF 9 -#endif -#ifndef LORA_CR - #define LORA_CR 5 -#endif - -#ifdef HELTEC_LORA_V3 - #include - static HeltecV3Board board; -#else - #error "need to provide a 'board' object" -#endif - -/* ------------------------------ Code -------------------------------- */ - -class MyMesh : public mesh::Mesh { - uint32_t last_advert_timestamp = 0; - mesh::Identity server_id; - uint8_t server_secret[PUB_KEY_SIZE]; - int server_path_len = -1; - uint8_t server_path[MAX_PATH_SIZE]; - bool got_adv = false; - -protected: - void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) override { - if (memcmp(app_data, "PING", 4) == 0) { - Serial.println("Received advertisement from a PING server"); - - // check for replay attacks - if (timestamp > last_advert_timestamp) { - last_advert_timestamp = timestamp; - - server_id = id; - self_id.calcSharedSecret(server_secret, id); // calc ECDH shared secret - got_adv = true; - } - } - } - - int searchPeersByHash(const uint8_t* hash) override { - if (got_adv && server_id.isHashMatch(hash)) { - return 1; - } - return 0; // not found - } - - void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override { - // lookup pre-calculated shared_secret - memcpy(dest_secret, server_secret, PUB_KEY_SIZE); - } - - void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override { - if (type == PAYLOAD_TYPE_RESPONSE) { - Serial.println("Received PING Reply!"); - - if (packet->isRouteFlood()) { - // let server know path TO here, so they can use sendDirect() for future ping responses - mesh::Packet* path = createPathReturn(server_id, secret, packet->path, packet->path_len, 0, NULL, 0); - if (path) sendFlood(path); - } - } - } - - bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override { - // must be from server_id - Serial.printf("PATH to server, path_len=%d\n", (uint32_t) path_len); - - memcpy(server_path, path, server_path_len = path_len); // store a copy of path, for sendDirect() - - if (extra_type == PAYLOAD_TYPE_RESPONSE && extra_len > 0) { - Serial.println("Received PING Reply!"); - } - return true; // send reciprocal path if necessary - } - -public: - MyMesh(mesh::Radio& radio, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables) - : mesh::Mesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables) - { - } - - void sendPingPacket() { - if (!got_adv) return; // have not received Advert yet - - uint32_t now = getRTCClock()->getCurrentTime(); // important, need timestamp in packet, so that packet_hash will be unique - mesh::Packet* ping = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, server_id, server_secret, (uint8_t *) &now, sizeof(now)); - - if (ping) { - if (server_path_len < 0) { - sendFlood(ping); - } else { - sendDirect(ping, server_path, server_path_len); - } - } - } - -}; - -SPIClass spi; -StdRNG fast_rng; -SimpleMeshTables tables; -SX1262 radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -MyMesh the_mesh(*new RadioLibWrapper(radio, board), fast_rng, *new VolatileRTCClock(), tables); -unsigned long nextPing; - -void halt() { - while (1) ; -} - -void setup() { - Serial.begin(115200); - - board.begin(); -#if defined(P_LORA_SCLK) - spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, 22, 8); -#else - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, 22, 8); -#endif - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - halt(); - } - fast_rng.begin(radio.random(0x7FFFFFFF)); - the_mesh.begin(); - - RadioNoiseListener true_rng(radio); - the_mesh.self_id = mesh::LocalIdentity(&true_rng); // create new random identity - - nextPing = 0; -} - -void loop() { - if (the_mesh.millisHasNowPassed(nextPing)) { - the_mesh.sendPingPacket(); - - nextPing = the_mesh.futureMillis(10000); // attempt ping every 10 seconds - } - the_mesh.loop(); -} diff --git a/examples/ping_server/main.cpp b/examples/ping_server/main.cpp deleted file mode 100644 index d5c92a1a..00000000 --- a/examples/ping_server/main.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include // needed for PlatformIO -#include -#include - -#define RADIOLIB_STATIC_ONLY 1 -#include -#include -#include -#include -#include - -/* ------------------------------ Config -------------------------------- */ - -#ifndef LORA_FREQ - #define LORA_FREQ 915.0 -#endif -#ifndef LORA_BW - #define LORA_BW 125 -#endif -#ifndef LORA_SF - #define LORA_SF 9 -#endif -#ifndef LORA_CR - #define LORA_CR 5 -#endif - -#ifdef HELTEC_LORA_V3 - #include - static HeltecV3Board board; -#else - #error "need to provide a 'board' object" -#endif - -/* ------------------------------ Code -------------------------------- */ - -struct ClientInfo { - mesh::Identity id; - uint32_t last_timestamp; - uint8_t secret[PUB_KEY_SIZE]; - int out_path_len; - uint8_t out_path[MAX_PATH_SIZE]; -}; - -#define MAX_CLIENTS 4 - -class MyMesh : public mesh::Mesh { - int num_clients; - ClientInfo known_clients[MAX_CLIENTS]; - - ClientInfo* putClient(const mesh::Identity& id) { - for (int i = 0; i < num_clients; i++) { - if (id.matches(known_clients[i].id)) return &known_clients[i]; // already known - } - if (num_clients < MAX_CLIENTS) { - auto newClient = &known_clients[num_clients++]; - newClient->id = id; - newClient->out_path_len = -1; // initially out_path is unknown - newClient->last_timestamp = 0; - self_id.calcSharedSecret(newClient->secret, id); // calc ECDH shared secret - return newClient; - } - return NULL; // table is full - } - -protected: - void onAnonDataRecv(mesh::Packet* packet, uint8_t type, const mesh::Identity& sender, uint8_t* data, size_t len) override { - if (type == PAYLOAD_TYPE_ANON_REQ) { // received a PING! - uint32_t timestamp; - memcpy(×tamp, data, 4); - - auto client = putClient(sender); // add to known clients (if not already known) - if (client == NULL || timestamp <= client->last_timestamp) { - return; // FATAL: client table is full -OR- replay attack - } - - client->last_timestamp = timestamp; - - uint32_t now = getRTCClock()->getCurrentTime(); // response packets always prefixed with timestamp - - if (packet->isRouteFlood()) { - // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the Ping response - mesh::Packet* path = createPathReturn(sender, client->secret, packet->path, packet->path_len, - PAYLOAD_TYPE_RESPONSE, (uint8_t *) &now, sizeof(now)); - if (path) sendFlood(path); - } else { - mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->secret, (uint8_t *) &now, sizeof(now)); - if (reply) { - if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len); - } else { - sendFlood(reply); - } - } - } - } - } - - int matching_peer_indexes[MAX_CLIENTS]; - - int searchPeersByHash(const uint8_t* hash) override { - int n = 0; - for (int i = 0; i < num_clients; i++) { - if (known_clients[i].id.isHashMatch(hash)) { - matching_peer_indexes[n++] = i; // store the INDEXES of matching contacts (for subsequent 'peer' methods) - } - } - return n; - } - - // not needed for this example, but for sake of 'completeness' of Mesh impl - void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override { - if (peer_idx >= 0 && peer_idx < MAX_CLIENTS) { - // lookup pre-calculated shared_secret - int i = matching_peer_indexes[peer_idx]; - memcpy(dest_secret, known_clients[i].secret, PUB_KEY_SIZE); - } else { - MESH_DEBUG_PRINTLN("Invalid peer_idx: %d", peer_idx); - } - } - - bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override { - if (sender_idx >= 0 && sender_idx < MAX_CLIENTS) { - Serial.printf("PATH to client, path_len=%d\n", (uint32_t) path_len); - - // TODO: prevent replay attacks - - int i = matching_peer_indexes[sender_idx]; - if (i >= 0 && i < num_clients) { - auto client = &known_clients[i]; // get from our known_clients table (sender SHOULD already be known in this context) - memcpy(client->out_path, path, client->out_path_len = path_len); // store a copy of path, for sendDirect() - } - } else { - MESH_DEBUG_PRINTLN("Invalid sender_idx: %d", sender_idx); - } - - // NOTE: no reciprocal path send!! - return false; - } - -public: - MyMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables) - : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(16), tables) - { - num_clients = 0; - } -}; - -SPIClass spi; -StdRNG fast_rng; -SimpleMeshTables tables; -SX1262 radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -MyMesh the_mesh(*new RadioLibWrapper(radio, board), *new ArduinoMillis(), fast_rng, *new VolatileRTCClock(), tables); - -unsigned long nextAnnounce; - -void halt() { - while (1) ; -} - -void setup() { - Serial.begin(115200); - - board.begin(); -#if defined(P_LORA_SCLK) - spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, 22, 8); -#else - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, 22, 8); -#endif - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - halt(); - } - fast_rng.begin(radio.random(0x7FFFFFFF)); - the_mesh.begin(); - - RadioNoiseListener true_rng(radio); - the_mesh.self_id = mesh::LocalIdentity(&true_rng); // create new random identity - - nextAnnounce = 0; -} - -void loop() { - if (the_mesh.millisHasNowPassed(nextAnnounce)) { - mesh::Packet* pkt = the_mesh.createAdvert(the_mesh.self_id, (const uint8_t *)"PING", 4); - if (pkt) the_mesh.sendFlood(pkt); - - nextAnnounce = the_mesh.futureMillis(30000); // announce every 30 seconds (test only, don't do in production!) - } - the_mesh.loop(); - - // TODO: periodically check for OLD entries in known_clients[], and evict -} diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index d7074228..5dcaaf8d 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -13,13 +13,14 @@ #include #include #include +#include #include #include #include /* ------------------------------ Config -------------------------------- */ -#define FIRMWARE_VER_TEXT "v4 (build: 17 Feb 2025)" +#define FIRMWARE_VER_TEXT "v5 (build: 25 Feb 2025)" #ifndef LORA_FREQ #define LORA_FREQ 915.0 @@ -749,10 +750,11 @@ StdRNG fast_rng; SimpleMeshTables tables; #ifdef ESP32 -ESP32RTCClock rtc_clock; +ESP32RTCClock fallback_clock; #else -VolatileRTCClock rtc_clock; +VolatileRTCClock fallback_clock; #endif +AutoDiscoverRTCClock rtc_clock(fallback_clock); MyMesh the_mesh(board, *new WRAPPER_CLASS(radio, board), *new ArduinoMillis(), fast_rng, rtc_clock, tables); @@ -768,8 +770,9 @@ void setup() { board.begin(); #ifdef ESP32 - rtc_clock.begin(); + fallback_clock.begin(); #endif + rtc_clock.begin(Wire); #ifdef SX126X_DIO3_TCXO_VOLTAGE float tcxo = SX126X_DIO3_TCXO_VOLTAGE; diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 10853fd8..df7dd65a 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -13,13 +13,14 @@ #include #include #include +#include #include #include #include /* ------------------------------ Config -------------------------------- */ -#define FIRMWARE_VER_TEXT "v4 (build: 17 Feb 2025)" +#define FIRMWARE_VER_TEXT "v5 (build: 25 Feb 2025)" #ifndef LORA_FREQ #define LORA_FREQ 915.0 @@ -786,10 +787,11 @@ StdRNG fast_rng; SimpleMeshTables tables; #ifdef ESP32 -ESP32RTCClock rtc_clock; +ESP32RTCClock fallback_clock; #else -VolatileRTCClock rtc_clock; +VolatileRTCClock fallback_clock; #endif +AutoDiscoverRTCClock rtc_clock(fallback_clock); MyMesh the_mesh(board, *new WRAPPER_CLASS(radio, board), *new ArduinoMillis(), fast_rng, rtc_clock, tables); @@ -805,8 +807,9 @@ void setup() { board.begin(); #ifdef ESP32 - rtc_clock.begin(); + fallback_clock.begin(); #endif + rtc_clock.begin(Wire); #ifdef SX126X_DIO3_TCXO_VOLTAGE float tcxo = SX126X_DIO3_TCXO_VOLTAGE; diff --git a/examples/test_admin/main.cpp b/examples/test_admin/main.cpp deleted file mode 100644 index 2b8805de..00000000 --- a/examples/test_admin/main.cpp +++ /dev/null @@ -1,315 +0,0 @@ -#include // needed for PlatformIO -#include -#include - -#define RADIOLIB_STATIC_ONLY 1 -#include -#include -#include -#include -#include - -/* ---------------------------------- CONFIGURATION ------------------------------------- */ - -#ifndef LORA_FREQ - #define LORA_FREQ 915.0 -#endif -#ifndef LORA_BW - #define LORA_BW 125 -#endif -#ifndef LORA_SF - #define LORA_SF 9 -#endif -#ifndef LORA_CR - #define LORA_CR 5 -#endif - -#define ADMIN_PASSWORD "h^(kl@#)" - -#ifdef HELTEC_LORA_V3 - #include - static HeltecV3Board board; -#else - #error "need to provide a 'board' object" -#endif - -/* -------------------------------------------------------------------------------------- */ - -#define MAX_TEXT_LEN (10*CIPHER_BLOCK_SIZE) // must be LESS than (MAX_PACKET_PAYLOAD - FROM_HASH_LEN - CIPHER_MAC_SIZE - 1) - -#define CMD_GET_STATS 0x01 -#define CMD_SET_CLOCK 0x02 -#define CMD_SEND_ANNOUNCE 0x03 -#define CMD_SET_CONFIG 0x04 - -struct RepeaterStats { - uint16_t batt_milli_volts; - uint16_t curr_tx_queue_len; - uint16_t curr_free_queue_len; - int16_t last_rssi; - uint32_t n_packets_recv; - uint32_t n_packets_sent; - uint32_t total_air_time_secs; - uint32_t total_up_time_secs; - uint32_t n_sent_flood, n_sent_direct; - uint32_t n_recv_flood, n_recv_direct; - uint32_t n_full_events; -}; - -class MyMesh : public mesh::Mesh { - uint32_t last_advert_timestamp = 0; - mesh::Identity server_id; - uint8_t server_secret[PUB_KEY_SIZE]; - int server_path_len = -1; - uint8_t server_path[MAX_PATH_SIZE]; - bool got_adv = false; - -protected: - void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) override { - if (memcmp(app_data, "repeater:", 9) == 0) { - Serial.println("Received advertisement from a repeater!"); - - // check for replay attacks - if (timestamp > last_advert_timestamp) { - last_advert_timestamp = timestamp; - - server_id = id; - self_id.calcSharedSecret(server_secret, id); // calc ECDH shared secret - got_adv = true; - - // 'login' to repeater. (mainly lets it know our public key) - uint32_t now = getRTCClock()->getCurrentTime(); // important, need timestamp in packet, so that packet_hash will be unique - uint8_t temp[4 + 8]; - memcpy(temp, &now, 4); - memcpy(&temp[4], ADMIN_PASSWORD, 8); - - mesh::Packet* login = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, server_id, server_secret, temp, sizeof(temp)); - if (login) sendFlood(login); // server_path won't be known yet - } - } - } - - void handleResponse(const uint8_t* reply, size_t reply_len) { - if (reply_len >= 4 + sizeof(RepeaterStats)) { // got an GET_STATS reply from repeater - RepeaterStats stats; - memcpy(&stats, &reply[4], sizeof(stats)); - Serial.println("Repeater Stats:"); - Serial.printf(" battery: %d mV\n", (uint32_t) stats.batt_milli_volts); - Serial.printf(" tx queue: %d\n", (uint32_t) stats.curr_tx_queue_len); - Serial.printf(" free queue: %d\n", (uint32_t) stats.curr_free_queue_len); - Serial.printf(" last RSSI: %d\n", (int) stats.last_rssi); - Serial.printf(" num recv: %d\n", stats.n_packets_recv); - Serial.printf(" num sent: %d\n", stats.n_packets_sent); - Serial.printf(" air time (secs): %d\n", stats.total_air_time_secs); - Serial.printf(" up time (secs): %d\n", stats.total_up_time_secs); - } else if (reply_len > 4) { // got an SET_* reply from repeater - char tmp[MAX_PACKET_PAYLOAD]; - memcpy(tmp, &reply[4], reply_len - 4); - tmp[reply_len - 4] = 0; // make a C string of reply - - Serial.print("Reply: "); Serial.println(tmp); - } - } - - int searchPeersByHash(const uint8_t* hash) override { - if (got_adv && server_id.isHashMatch(hash)) { - return 1; - } - return 0; // not found - } - - void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override { - // lookup pre-calculated shared_secret - memcpy(dest_secret, server_secret, PUB_KEY_SIZE); - } - - void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override { - if (type == PAYLOAD_TYPE_RESPONSE) { - handleResponse(data, len); - - if (packet->isRouteFlood()) { - // let server know path TO here, so they can use sendDirect() for future ping responses - mesh::Packet* path = createPathReturn(server_id, secret, packet->path, packet->path_len, 0, NULL, 0); - if (path) sendFlood(path); - } - } - } - - bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override { - // must be from server_id - Serial.printf("PATH to repeater, path_len=%d\n", (uint32_t) path_len); - - memcpy(server_path, path, server_path_len = path_len); // store a copy of path, for sendDirect() - - if (extra_type == PAYLOAD_TYPE_RESPONSE) { - handleResponse(extra, extra_len); - } - return true; // send reciprocal path if necessary - } - -public: - MyMesh(mesh::Radio& radio, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables) - : mesh::Mesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables) - { - } - - mesh::Packet* createStatsRequest(uint32_t max_age) { - uint8_t payload[9]; - uint32_t now = getRTCClock()->getCurrentTime(); - memcpy(payload, &now, 4); - payload[4] = CMD_GET_STATS; - memcpy(&payload[5], &max_age, 4); - - return createDatagram(PAYLOAD_TYPE_REQ, server_id, server_secret, payload, sizeof(payload)); - } - - mesh::Packet* createSetClockRequest(uint32_t timestamp) { - uint8_t payload[9]; - uint32_t now = getRTCClock()->getCurrentTime(); - memcpy(payload, &now, 4); - payload[4] = CMD_SET_CLOCK; - memcpy(&payload[5], &now, 4); // repeated :-( - - return createDatagram(PAYLOAD_TYPE_REQ, server_id, server_secret, payload, sizeof(payload)); - } - - mesh::Packet* createSetAirtimeFactorRequest(float airtime_factor) { - uint8_t payload[16]; - uint32_t now = getRTCClock()->getCurrentTime(); - memcpy(payload, &now, 4); - payload[4] = CMD_SET_CONFIG; - sprintf((char *) &payload[5], "AF%f", airtime_factor); - - return createDatagram(PAYLOAD_TYPE_REQ, server_id, server_secret, payload, sizeof(payload)); - } - - mesh::Packet* createAnnounceRequest() { - uint8_t payload[5]; - uint32_t now = getRTCClock()->getCurrentTime(); - memcpy(payload, &now, 4); - payload[4] = CMD_SEND_ANNOUNCE; - - return createDatagram(PAYLOAD_TYPE_REQ, server_id, server_secret, payload, sizeof(payload)); - } - - mesh::Packet* parseCommand(char* command) { - if (strcmp(command, "stats") == 0) { - return createStatsRequest(60*60); // max_age = one hour - } else if (memcmp(command, "setclock ", 9) == 0) { - uint32_t timestamp = atol(&command[9]); - return createSetClockRequest(timestamp); - } else if (memcmp(command, "set AF=", 7) == 0) { - float factor = atof(&command[7]); - return createSetAirtimeFactorRequest(factor); - } else if (strcmp(command, "ann") == 0) { - return createAnnounceRequest(); - } - return NULL; // unknown command - } - - void sendCommand(mesh::Packet* pkt) { - if (server_path_len < 0) { - sendFlood(pkt); - } else { - sendDirect(pkt, server_path, server_path_len); - } - } -}; - -StdRNG fast_rng; -SimpleMeshTables tables; -#if defined(P_LORA_SCLK) -SPIClass spi; -CustomSX1262 radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else -CustomSX1262 radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif -MyMesh the_mesh(*new CustomSX1262Wrapper(radio, board), fast_rng, *new VolatileRTCClock(), tables); - -void halt() { - while (1) ; -} - -static char command[MAX_TEXT_LEN+1]; - -#include - -void setup() { - Serial.begin(115200); - delay(5000); - - board.begin(); -#if defined(P_LORA_SCLK) - spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, 22, 8); -#else - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, 22, 8); -#endif - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - halt(); - } - - fast_rng.begin(radio.random(0x7FFFFFFF)); - -/* add this to tests - uint8_t mac_encrypted[CIPHER_MAC_SIZE+CIPHER_BLOCK_SIZE]; - const char *orig_msg = "original"; - int enc_len = mesh::Utils::encryptThenMAC(mesh.admin_secret, mac_encrypted, (const uint8_t *) orig_msg, strlen(orig_msg)); - char decrypted[CIPHER_BLOCK_SIZE*2]; - int len = mesh::Utils::MACThenDecrypt(mesh.admin_secret, (uint8_t *)decrypted, mac_encrypted, enc_len); - if (len > 0) { - decrypted[len] = 0; - Serial.print("decrypted text: "); Serial.println(decrypted); - } else { - Serial.println("MACs DONT match!"); - } -*/ - - Serial.println("Help:"); - Serial.println(" enter 'key' to generate new keypair"); - Serial.println(" enter 'stats' to request repeater stats"); - Serial.println(" enter 'setclock {unix-epoch-seconds}' to set repeater's clock"); - Serial.println(" enter 'set AF={factor}' to set airtime budget factor"); - Serial.println(" enter 'ann' to make repeater re-announce to mesh"); - - the_mesh.begin(); - - command[0] = 0; -} - -void loop() { - int len = strlen(command); - while (Serial.available() && len < sizeof(command)-1) { - char c = Serial.read(); - if (c != '\n') { - command[len++] = c; - command[len] = 0; - } - Serial.print(c); - } - if (len == sizeof(command)-1) { // command buffer full - command[sizeof(command)-1] = '\r'; - } - - if (len > 0 && command[len - 1] == '\r') { // received complete line - command[len - 1] = 0; // replace newline with C string null terminator - - if (strcmp(command, "key") == 0) { - mesh::LocalIdentity new_id(the_mesh.getRNG()); - new_id.printTo(Serial); - } else { - mesh::Packet* pkt = the_mesh.parseCommand(command); - if (pkt) { - the_mesh.sendCommand(pkt); - Serial.println(" (request sent)"); - } else { - Serial.print(" ERROR: unknown command: "); Serial.println(command); - } - } - command[0] = 0; // reset command buffer - } - - the_mesh.loop(); -} diff --git a/platformio.ini b/platformio.ini index 3259ad22..c75ccf2b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,6 +16,8 @@ lib_deps = Wire jgromes/RadioLib @ ^6.3.0 rweather/Crypto @ ^0.4.0 + adafruit/RTClib @ ^2.1.3 + melopero/Melopero RV3028 @ ^1.1.0 build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1 -D LORA_FREQ=867.5 -D LORA_BW=250 @@ -32,7 +34,7 @@ build_src_filter = ${arduino_base.build_src_filter} ; ================ [Heltec_lora32_v2] extends = esp32_base -board = heltec_wireless_stick_lite ; heltec_wifi_lora_32_V2 +board = heltec_wifi_lora_32_V2 ; heltec_wifi_lora_32_V2 build_flags = ${esp32_base.build_flags} -D HELTEC_LORA_V2 @@ -53,9 +55,6 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v2.build_src_filter} +<../examples/simple_repeater/main.cpp> -lib_deps = - ${Heltec_lora32_v2.lib_deps} - adafruit/RTClib @ ^2.1.3 [env:Heltec_v2_terminal_chat] extends = Heltec_lora32_v2 @@ -68,7 +67,6 @@ build_flags = build_src_filter = ${Heltec_lora32_v2.build_src_filter} +<../examples/simple_secure_chat/main.cpp> lib_deps = ${Heltec_lora32_v2.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 [env:Heltec_v2_companion_radio_usb] @@ -82,7 +80,23 @@ build_flags = build_src_filter = ${Heltec_lora32_v2.build_src_filter} + +<../examples/companion_radio/main.cpp> lib_deps = ${Heltec_lora32_v2.lib_deps} - adafruit/RTClib @ ^2.1.3 + densaugeo/base64 @ ~1.4.0 + +[env:Heltec_v2_companion_radio_ble] +extends = Heltec_lora32_v2 +build_flags = + ${Heltec_lora32_v2.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=1 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 +; -D ENABLE_PRIVATE_KEY_IMPORT=1 +; -D ENABLE_PRIVATE_KEY_EXPORT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v2.build_src_filter} + +<../examples/companion_radio/main.cpp> +lib_deps = + ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -102,14 +116,6 @@ build_flags = -D SX126X_CURRENT_LIMIT=130.0f ; for best TX power! build_src_filter = ${esp32_base.build_src_filter} -[env:Heltec_v3_ping_server] -extends = Heltec_lora32_v3 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/ping_server/main.cpp> - -[env:Heltec_v3_ping_client] -extends = Heltec_lora32_v3 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/ping_client/main.cpp> - [env:Heltec_v3_repeater] extends = Heltec_lora32_v3 build_flags = @@ -121,9 +127,6 @@ build_flags = -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_repeater/main.cpp> -lib_deps = - ${Heltec_lora32_v3.lib_deps} - adafruit/RTClib @ ^2.1.3 [env:Heltec_v3_room_server] extends = Heltec_lora32_v3 @@ -137,9 +140,6 @@ build_flags = -D ROOM_PASSWORD="\"hello\"" ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -lib_deps = - ${Heltec_lora32_v3.lib_deps} - adafruit/RTClib @ ^2.1.3 [env:Heltec_v3_terminal_chat] extends = Heltec_lora32_v3 @@ -152,7 +152,6 @@ build_flags = build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_secure_chat/main.cpp> lib_deps = ${Heltec_lora32_v3.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 [env:Heltec_v3_companion_radio_usb] @@ -168,7 +167,6 @@ build_flags = build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/companion_radio/main.cpp> lib_deps = ${Heltec_lora32_v3.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 [env:Heltec_v3_companion_radio_ble] @@ -186,15 +184,8 @@ build_flags = build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/companion_radio/main.cpp> lib_deps = ${Heltec_lora32_v3.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 -[env:Heltec_v3_test_admin] -extends = Heltec_lora32_v3 -build_flags = - ${Heltec_lora32_v3.build_flags} -build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/test_admin/main.cpp> - ; ================ [Xiao_esp32_C3] extends = esp32_base @@ -208,6 +199,8 @@ build_flags = -D P_LORA_NSS=D4 -D P_LORA_RESET=RADIOLIB_NC -D P_LORA_BUSY=D1 + -D PIN_BOARD_SDA=D6 + -D PIN_BOARD_SCL=D7 -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=130.0f ; for best TX power! @@ -226,9 +219,6 @@ build_flags = -D ADMIN_PASSWORD="\"password\"" ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -lib_deps = - ${Xiao_esp32_C3.lib_deps} - adafruit/RTClib @ ^2.1.3 [env:Xiao_C3_Repeater_sx1268] extends = Xiao_esp32_C3 @@ -244,9 +234,6 @@ build_flags = -D ADMIN_PASSWORD="\"password\"" ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -lib_deps = - ${Xiao_esp32_C3.lib_deps} - adafruit/RTClib @ ^2.1.3 ; ============= [Xiao_S3_WIO] @@ -281,9 +268,6 @@ build_flags = -D ADMIN_PASSWORD="\"password\"" ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -lib_deps = - ${Xiao_S3_WIO.lib_deps} - adafruit/RTClib @ ^2.1.3 [env:Xiao_S3_WIO_terminal_chat] extends = Xiao_S3_WIO @@ -296,7 +280,6 @@ build_flags = build_src_filter = ${Xiao_S3_WIO.build_src_filter} +<../examples/simple_secure_chat/main.cpp> lib_deps = ${Xiao_S3_WIO.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 [env:Xiao_S3_WIO_companion_radio_ble] @@ -314,7 +297,6 @@ build_flags = build_src_filter = ${Xiao_S3_WIO.build_src_filter} + +<../examples/companion_radio/main.cpp> lib_deps = ${Xiao_S3_WIO.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 ; ============= @@ -350,9 +332,6 @@ build_flags = -D ADMIN_PASSWORD="\"password\"" ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -lib_deps = - ${LilyGo_T3S3_sx1262.lib_deps} - adafruit/RTClib @ ^2.1.3 [env:LilyGo_T3S3_sx1262_terminal_chat] extends = LilyGo_T3S3_sx1262 @@ -365,7 +344,6 @@ build_flags = build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} +<../examples/simple_secure_chat/main.cpp> lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 [env:LilyGo_T3S3_sx1262_companion_radio_usb] @@ -381,7 +359,6 @@ build_flags = build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} +<../examples/companion_radio/main.cpp> lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 [env:LilyGo_T3S3_sx1262_companion_radio_ble] @@ -399,7 +376,6 @@ build_flags = build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + +<../examples/companion_radio/main.cpp> lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 ; ----------------- NRF52 --------------------- @@ -441,9 +417,6 @@ build_flags = -D ADMIN_PASSWORD="\"password\"" ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -lib_deps = - ${rak4631.lib_deps} - adafruit/RTClib @ ^2.1.3 [env:RAK_4631_room_server] extends = rak4631 @@ -457,9 +430,6 @@ build_flags = -D ROOM_PASSWORD="\"hello\"" ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -lib_deps = - ${rak4631.lib_deps} - adafruit/RTClib @ ^2.1.3 [env:RAK_4631_terminal_chat] extends = rak4631 @@ -473,7 +443,6 @@ build_src_filter = ${rak4631.build_src_filter} +<../examples/simple_secure_chat/ lib_deps = ${rak4631.lib_deps} densaugeo/base64 @ ~1.4.0 - adafruit/RTClib @ ^2.1.3 [env:RAK_4631_companion_radio_usb] extends = rak4631 @@ -488,7 +457,6 @@ build_flags = build_src_filter = ${rak4631.build_src_filter} +<../examples/companion_radio/main.cpp> lib_deps = ${rak4631.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 [env:RAK_4631_companion_radio_ble] @@ -506,5 +474,4 @@ build_flags = build_src_filter = ${rak4631.build_src_filter} + +<../examples/companion_radio/main.cpp> lib_deps = ${rak4631.lib_deps} - adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 diff --git a/src/helpers/AutoDiscoverRTCClock.cpp b/src/helpers/AutoDiscoverRTCClock.cpp new file mode 100644 index 00000000..e087744a --- /dev/null +++ b/src/helpers/AutoDiscoverRTCClock.cpp @@ -0,0 +1,61 @@ +#include "AutoDiscoverRTCClock.h" +#include "RTClib.h" +#include + +static RTC_DS3231 rtc_3231; +static bool ds3231_success = false; + +static Melopero_RV3028 rtc_rv3028; +static bool rv3028_success = false; + +#define DS3231_ADDRESS 0x68 +#define RV3028_ADDRESS 0x52 + +bool AutoDiscoverRTCClock::i2c_probe(TwoWire& wire, uint8_t addr) { + wire.beginTransmission(addr); + uint8_t error = wire.endTransmission(); + return (error == 0); +} + +void AutoDiscoverRTCClock::begin(TwoWire& wire) { + if (i2c_probe(wire, DS3231_ADDRESS)) { + ds3231_success = rtc_3231.begin(&wire); + } + if (i2c_probe(wire, RV3028_ADDRESS)) { + rtc_rv3028.initI2C(wire); + rtc_rv3028.writeToRegister(0x35, 0x00); + rtc_rv3028.writeToRegister(0x37, 0xB4); // Direct Switching Mode (DSM): when VDD < VBACKUP, switchover occurs from VDD to VBACKUP + rtc_rv3028.set24HourMode(); // Set the device to use the 24hour format (default) instead of the 12 hour format + rv3028_success = true; + } +} + +uint32_t AutoDiscoverRTCClock::getCurrentTime() { + if (ds3231_success) { + return rtc_3231.now().unixtime(); + } + if (rv3028_success) { + return DateTime( + rtc_rv3028.getYear(), + rtc_rv3028.getMonth(), + rtc_rv3028.getDate(), + rtc_rv3028.getHour(), + rtc_rv3028.getMinute(), + rtc_rv3028.getSecond() + ).unixtime(); + } + return _fallback->getCurrentTime(); +} + +void AutoDiscoverRTCClock::setCurrentTime(uint32_t time) { + if (ds3231_success) { + rtc_3231.adjust(DateTime(time)); + } else if (rv3028_success) { + auto dt = DateTime(time); + uint8_t weekday = (dt.day() + (uint16_t)((2.6 * dt.month()) - 0.2) - (2 * (dt.year() / 100)) + dt.year() + (uint16_t)(dt.year() / 4) + (uint16_t)(dt.year() / 400)) % 7; + + rtc_rv3028.setTime(dt.year(), dt.month(), weekday, dt.day(), dt.hour(), dt.minute(), dt.second()); + } else { + _fallback->setCurrentTime(time); + } +} diff --git a/src/helpers/AutoDiscoverRTCClock.h b/src/helpers/AutoDiscoverRTCClock.h new file mode 100644 index 00000000..02eedf52 --- /dev/null +++ b/src/helpers/AutoDiscoverRTCClock.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +class AutoDiscoverRTCClock : public mesh::RTCClock { + mesh::RTCClock* _fallback; + + bool i2c_probe(TwoWire& wire, uint8_t addr); +public: + AutoDiscoverRTCClock(mesh::RTCClock& fallback) : _fallback(&fallback) { } + + void begin(TwoWire& wire); + uint32_t getCurrentTime() override; + void setCurrentTime(uint32_t time) override; +}; diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index 9f50c0c7..45fb16bd 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -27,6 +27,12 @@ public: pinMode(P_LORA_TX_LED, OUTPUT); digitalWrite(P_LORA_TX_LED, LOW); #endif + + #if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL); + #else + Wire.begin(); + #endif } uint8_t getStartupReason() const override { return startup_reason; } diff --git a/src/helpers/nrf52/RAK4631Board.cpp b/src/helpers/nrf52/RAK4631Board.cpp index c5c93907..dc92f691 100644 --- a/src/helpers/nrf52/RAK4631Board.cpp +++ b/src/helpers/nrf52/RAK4631Board.cpp @@ -20,6 +20,23 @@ static void disconnect_callback(uint16_t conn_handle, uint8_t reason) MESH_DEBUG_PRINTLN("BLE client disconnected"); } +void RAK4631Board::begin() { + // for future use, sub-classes SHOULD call this from their begin() + startup_reason = BD_STARTUP_NORMAL; + + pinMode(PIN_VBAT_READ, INPUT); + +#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL); +#else + Wire.begin(); +#endif + + pinMode(SX126X_POWER_EN, OUTPUT); + digitalWrite(SX126X_POWER_EN, HIGH); + delay(10); // give sx1262 some time to power up +} + bool RAK4631Board::startOTAUpdate() { // Config the peripheral connection with maximum bandwidth // more SRAM required by SoftDevice diff --git a/src/helpers/nrf52/RAK4631Board.h b/src/helpers/nrf52/RAK4631Board.h index 61188258..1688e0f0 100644 --- a/src/helpers/nrf52/RAK4631Board.h +++ b/src/helpers/nrf52/RAK4631Board.h @@ -25,17 +25,7 @@ protected: uint8_t startup_reason; public: - void begin() { - // for future use, sub-classes SHOULD call this from their begin() - startup_reason = BD_STARTUP_NORMAL; - - pinMode(PIN_VBAT_READ, INPUT); - - pinMode(SX126X_POWER_EN, OUTPUT); - digitalWrite(SX126X_POWER_EN, HIGH); - delay(10); // give sx1262 some time to power up - } - + void begin(); uint8_t getStartupReason() const override { return startup_reason; } #define BATTERY_SAMPLES 8 diff --git a/src/helpers/nrf52/T1000eBoard.cpp b/src/helpers/nrf52/T1000eBoard.cpp index 84ca4caa..3f696f6e 100644 --- a/src/helpers/nrf52/T1000eBoard.cpp +++ b/src/helpers/nrf52/T1000eBoard.cpp @@ -4,7 +4,23 @@ #include #include +void T1000eBoard::begin() { + // for future use, sub-classes SHOULD call this from their begin() + startup_reason = BD_STARTUP_NORMAL; +#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL); +#else + Wire.begin(); +#endif + + // pinMode(PIN_VBAT_READ, INPUT); + + // Doesn't seem to be a pwr en pin ... + // pinMode(LR1110_POWER_EN, OUTPUT); + // digitalWrite(LR1110_POWER_EN, HIGH); + delay(10); // give sx1262 some time to power up +} #if 0 static BLEDfu bledfu; diff --git a/src/helpers/nrf52/T1000eBoard.h b/src/helpers/nrf52/T1000eBoard.h index 4e5d6516..58e3b774 100644 --- a/src/helpers/nrf52/T1000eBoard.h +++ b/src/helpers/nrf52/T1000eBoard.h @@ -25,17 +25,7 @@ protected: uint8_t startup_reason; public: - void begin() { - // for future use, sub-classes SHOULD call this from their begin() - startup_reason = BD_STARTUP_NORMAL; - -// pinMode(PIN_VBAT_READ, INPUT); - -// Doesn't seem to be a pwr en pin ... -// pinMode(LR1110_POWER_EN, OUTPUT); -// digitalWrite(LR1110_POWER_EN, HIGH); - delay(10); // give sx1262 some time to power up - } + void begin(); uint16_t getBattMilliVolts() override { return 0;