Merge branch 'main' into rx_delay

# Conflicts:
#	examples/simple_repeater/main.cpp
#	examples/simple_room_server/main.cpp
This commit is contained in:
Scott Powell
2025-02-08 12:04:17 +11:00
2 changed files with 126 additions and 24 deletions

View File

@@ -19,7 +19,7 @@
/* ------------------------------ Config -------------------------------- */
#define FIRMWARE_VER_TEXT "v3 (build: 4 Feb 2025)"
#define FIRMWARE_VER_TEXT "v3 (build: 8 Feb 2025)"
#ifndef LORA_FREQ
#define LORA_FREQ 915.0
@@ -51,6 +51,8 @@
#define ADMIN_PASSWORD "password"
#endif
#define MIN_LOCAL_ADVERT_INTERVAL 8
#if defined(HELTEC_LORA_V3)
#include <helpers/HeltecV3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
@@ -121,7 +123,8 @@ struct NodePrefs { // persisted to file
float freq;
uint8_t tx_power_dbm;
uint8_t disable_fwd;
uint8_t unused[2];
uint8_t advert_interval; // minutes
uint8_t unused;
float rx_delay_base;
float tx_delay_factor;
};
@@ -130,6 +133,7 @@ class MyMesh : public mesh::Mesh {
RadioLibWrapper* my_radio;
FILESYSTEM* _fs;
mesh::MainBoard* _board;
unsigned long next_local_advert;
NodePrefs _prefs;
uint8_t reply_data[MAX_PACKET_PAYLOAD];
int num_clients;
@@ -187,6 +191,31 @@ class MyMesh : public mesh::Mesh {
return 0; // reply_len
}
void checkAdvertInterval() {
if (_prefs.advert_interval < MIN_LOCAL_ADVERT_INTERVAL) {
_prefs.advert_interval = 0; // turn it off, now that device has been manually configured
}
}
void updateAdvertTimer() {
if (_prefs.advert_interval > 0) { // schedule local advert timer
next_local_advert = futureMillis(_prefs.advert_interval * 60 * 1000);
} else {
next_local_advert = 0; // stop the timer
}
}
mesh::Packet* createSelfAdvert() {
uint8_t app_data[MAX_ADVERT_DATA_SIZE];
uint8_t app_data_len;
{
AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, _prefs.node_lat, _prefs.node_lon);
app_data_len = builder.encodeTo(app_data);
}
return createAdvert(self_id, app_data, app_data_len);
}
protected:
float getAirtimeBudgetFactor() const override {
return _prefs.airtime_factor;
@@ -381,6 +410,7 @@ public:
{
my_radio = &radio;
num_clients = 0;
next_local_advert = 0;
// defaults
memset(&_prefs, 0, sizeof(_prefs));
@@ -395,6 +425,7 @@ public:
_prefs.password[sizeof(_prefs.password)-1] = 0; // truncate if necessary
_prefs.freq = LORA_FREQ;
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.advert_interval = 2; // default to 2 minutes for NEW installs
}
float getFreqPref() const { return _prefs.freq; }
@@ -411,6 +442,8 @@ public:
file.close();
}
}
updateAdvertTimer();
}
void savePrefs() {
@@ -427,14 +460,7 @@ public:
}
void sendSelfAdvertisement(int delay_millis) {
uint8_t app_data[MAX_ADVERT_DATA_SIZE];
uint8_t app_data_len;
{
AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, _prefs.node_lat, _prefs.node_lon);
app_data_len = builder.encodeTo(app_data);
}
mesh::Packet* pkt = createAdvert(self_id, app_data, app_data_len);
mesh::Packet* pkt = createSelfAdvert();
if (pkt) {
sendFlood(pkt, delay_millis);
} else {
@@ -442,6 +468,19 @@ public:
}
}
void loop() {
mesh::Mesh::loop();
if (next_local_advert && millisHasNowPassed(next_local_advert)) {
mesh::Packet* pkt = createSelfAdvert();
if (pkt) {
sendZeroHop(pkt);
}
updateAdvertTimer(); // schedule next local advert
}
}
void handleCommand(uint32_t sender_timestamp, const char* command, char reply[]) {
while (*command == ' ') command++; // skip leading spaces
@@ -481,6 +520,7 @@ public:
// change admin password
strncpy(_prefs.password, &command[9], sizeof(_prefs.password)-1);
_prefs.password[sizeof(_prefs.password)-1] = 0; // truncate if necesary
checkAdvertInterval();
savePrefs();
sprintf(reply, "password now: %s", _prefs.password); // echo back just to let admin know for sure!!
} else if (memcmp(command, "set ", 4) == 0) {
@@ -489,9 +529,22 @@ public:
_prefs.airtime_factor = atof(&config[3]);
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "advert.interval ", 16) == 0) {
int mins = _atoi(&config[16]);
if (mins > 0 && mins < MIN_LOCAL_ADVERT_INTERVAL) {
sprintf(reply, "Error: min is %d mins", MIN_LOCAL_ADVERT_INTERVAL);
} else if (mins > 120) {
strcpy(reply, "Error: max is 120 mins");
} else {
_prefs.advert_interval = (uint8_t)mins;
updateAdvertTimer();
savePrefs();
strcpy(reply, "OK");
}
} else if (memcmp(config, "name ", 5) == 0) {
strncpy(_prefs.node_name, &config[5], sizeof(_prefs.node_name)-1);
_prefs.node_name[sizeof(_prefs.node_name)-1] = 0; // truncate if nec
checkAdvertInterval();
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "repeat ", 7) == 0) {
@@ -500,10 +553,12 @@ public:
strcpy(reply, _prefs.disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON");
} else if (memcmp(config, "lat ", 4) == 0) {
_prefs.node_lat = atof(&config[4]);
checkAdvertInterval();
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "lon ", 4) == 0) {
_prefs.node_lon = atof(&config[4]);
checkAdvertInterval();
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "rxdelay ", 8) == 0) {

View File

@@ -19,7 +19,7 @@
/* ------------------------------ Config -------------------------------- */
#define FIRMWARE_VER_TEXT "v4 (build: 4 Feb 2025)"
#define FIRMWARE_VER_TEXT "v4 (build: 8 Feb 2025)"
#ifndef LORA_FREQ
#define LORA_FREQ 915.0
@@ -59,6 +59,8 @@
#define MAX_UNSYNCED_POSTS 16
#endif
#define MIN_LOCAL_ADVERT_INTERVAL 8
#if defined(HELTEC_LORA_V3)
#include <helpers/HeltecV3Board.h>
@@ -138,7 +140,8 @@ struct NodePrefs { // persisted to file
float freq;
uint8_t tx_power_dbm;
uint8_t disable_fwd;
uint8_t unused[2];
uint8_t advert_interval; // minutes
uint8_t unused;
float rx_delay_base;
float tx_delay_factor;
};
@@ -147,6 +150,7 @@ class MyMesh : public mesh::Mesh {
RadioLibWrapper* my_radio;
FILESYSTEM* _fs;
mesh::MainBoard* _board;
unsigned long next_local_advert;
NodePrefs _prefs;
uint8_t reply_data[MAX_PACKET_PAYLOAD];
int num_clients;
@@ -245,6 +249,31 @@ class MyMesh : public mesh::Mesh {
return false;
}
void checkAdvertInterval() {
if (_prefs.advert_interval < MIN_LOCAL_ADVERT_INTERVAL) {
_prefs.advert_interval = 0; // turn it off, now that device has been manually configured
}
}
void updateAdvertTimer() {
if (_prefs.advert_interval > 0) { // schedule local advert timer
next_local_advert = futureMillis(_prefs.advert_interval * 60 * 1000);
} else {
next_local_advert = 0; // stop the timer
}
}
mesh::Packet* createSelfAdvert() {
uint8_t app_data[MAX_ADVERT_DATA_SIZE];
uint8_t app_data_len;
{
AdvertDataBuilder builder(ADV_TYPE_ROOM, _prefs.node_name, _prefs.node_lat, _prefs.node_lon);
app_data_len = builder.encodeTo(app_data);
}
return createAdvert(self_id, app_data, app_data_len);
}
protected:
float getAirtimeBudgetFactor() const override {
return _prefs.airtime_factor;
@@ -493,6 +522,7 @@ public:
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), _board(&board)
{
my_radio = &radio;
next_local_advert = 0;
// defaults
memset(&_prefs, 0, sizeof(_prefs));
@@ -508,6 +538,7 @@ public:
_prefs.freq = LORA_FREQ;
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.disable_fwd = 1;
_prefs.advert_interval = 2; // default to 2 minutes for NEW installs
num_clients = 0;
next_post_idx = 0;
@@ -530,6 +561,8 @@ public:
file.close();
}
}
updateAdvertTimer();
}
void savePrefs() {
@@ -545,17 +578,10 @@ public:
}
}
void sendSelfAdvertisement() {
uint8_t app_data[MAX_ADVERT_DATA_SIZE];
uint8_t app_data_len;
{
AdvertDataBuilder builder(ADV_TYPE_ROOM, _prefs.node_name, _prefs.node_lat, _prefs.node_lon);
app_data_len = builder.encodeTo(app_data);
}
mesh::Packet* pkt = createAdvert(self_id, app_data, app_data_len);
void sendSelfAdvertisement(int delay_millis) {
mesh::Packet* pkt = createSelfAdvert();
if (pkt) {
sendFlood(pkt, 1200); // add slight delay
sendFlood(pkt, delay_millis);
} else {
MESH_DEBUG_PRINTLN("ERROR: unable to create advertisement packet!");
}
@@ -567,7 +593,7 @@ public:
if (memcmp(command, "reboot", 6) == 0) {
board.reboot(); // doesn't return
} else if (memcmp(command, "advert", 6) == 0) {
sendSelfAdvertisement();
sendSelfAdvertisement(400);
strcpy(reply, "OK - Advert sent");
} else if (memcmp(command, "clock sync", 10) == 0) {
uint32_t curr = getRTCClock()->getCurrentTime();
@@ -608,6 +634,18 @@ public:
_prefs.airtime_factor = atof(&config[3]);
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "advert.interval ", 16) == 0) {
int mins = _atoi(&config[16]);
if (mins > 0 && mins < MIN_LOCAL_ADVERT_INTERVAL) {
sprintf(reply, "Error: min is %d mins", MIN_LOCAL_ADVERT_INTERVAL);
} else if (mins > 120) {
strcpy(reply, "Error: max is 120 mins");
} else {
_prefs.advert_interval = (uint8_t)mins;
updateAdvertTimer();
savePrefs();
strcpy(reply, "OK");
}
} else if (memcmp(config, "name ", 5) == 0) {
strncpy(_prefs.node_name, &config[5], sizeof(_prefs.node_name)-1);
_prefs.node_name[sizeof(_prefs.node_name)-1] = 0; // truncate if nec
@@ -696,6 +734,15 @@ public:
next_push = futureMillis(SYNC_PUSH_INTERVAL);
}
if (next_local_advert && millisHasNowPassed(next_local_advert)) {
mesh::Packet* pkt = createSelfAdvert();
if (pkt) {
sendZeroHop(pkt);
}
updateAdvertTimer(); // schedule next local advert
}
// TODO: periodically check for OLD/inactive entries in known_clients[], and evict
}
};
@@ -799,7 +846,7 @@ void setup() {
}
// send out initial Advertisement to the mesh
the_mesh.sendSelfAdvertisement();
the_mesh.sendSelfAdvertisement(2000);
}
void loop() {