|
|
@ -48,7 +48,7 @@ |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
#ifndef ADMIN_PASSWORD |
|
|
#ifndef ADMIN_PASSWORD |
|
|
#define ADMIN_PASSWORD "h^(kl@#)" |
|
|
#define ADMIN_PASSWORD "password" |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
#if defined(HELTEC_LORA_V3) |
|
|
#if defined(HELTEC_LORA_V3) |
|
|
@ -103,9 +103,17 @@ struct ClientInfo { |
|
|
// NOTE: need to space the ACK and the reply text apart (in CLI)
|
|
|
// NOTE: need to space the ACK and the reply text apart (in CLI)
|
|
|
#define CLI_REPLY_DELAY_MILLIS 1500 |
|
|
#define CLI_REPLY_DELAY_MILLIS 1500 |
|
|
|
|
|
|
|
|
|
|
|
struct NodePrefs { // persisted to file
|
|
|
|
|
|
float airtime_factor; |
|
|
|
|
|
char node_name[32]; |
|
|
|
|
|
double node_lat, node_lon; |
|
|
|
|
|
char password[16]; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
class MyMesh : public mesh::Mesh { |
|
|
class MyMesh : public mesh::Mesh { |
|
|
RadioLibWrapper* my_radio; |
|
|
RadioLibWrapper* my_radio; |
|
|
float airtime_factor; |
|
|
FILESYSTEM* _fs; |
|
|
|
|
|
NodePrefs _prefs; |
|
|
uint8_t reply_data[MAX_PACKET_PAYLOAD]; |
|
|
uint8_t reply_data[MAX_PACKET_PAYLOAD]; |
|
|
int num_clients; |
|
|
int num_clients; |
|
|
ClientInfo known_clients[MAX_CLIENTS]; |
|
|
ClientInfo known_clients[MAX_CLIENTS]; |
|
|
@ -164,7 +172,7 @@ class MyMesh : public mesh::Mesh { |
|
|
|
|
|
|
|
|
protected: |
|
|
protected: |
|
|
float getAirtimeBudgetFactor() const override { |
|
|
float getAirtimeBudgetFactor() const override { |
|
|
return airtime_factor; |
|
|
return _prefs.airtime_factor; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool allowPacketForward(const mesh::Packet* packet) override { |
|
|
bool allowPacketForward(const mesh::Packet* packet) override { |
|
|
@ -176,7 +184,7 @@ protected: |
|
|
uint32_t timestamp; |
|
|
uint32_t timestamp; |
|
|
memcpy(×tamp, data, 4); |
|
|
memcpy(×tamp, data, 4); |
|
|
|
|
|
|
|
|
if (memcmp(&data[4], ADMIN_PASSWORD, strlen(ADMIN_PASSWORD)) == 0) { // check for valid password
|
|
|
if (memcmp(&data[4], _prefs.password, strlen(_prefs.password)) == 0) { // check for valid password
|
|
|
auto client = putClient(sender); // add to known clients (if not already known)
|
|
|
auto client = putClient(sender); // add to known clients (if not already known)
|
|
|
if (client == NULL || timestamp <= client->last_timestamp) { |
|
|
if (client == NULL || timestamp <= client->last_timestamp) { |
|
|
MESH_DEBUG_PRINTLN("Client table full, or replay attack!"); |
|
|
MESH_DEBUG_PRINTLN("Client table full, or replay attack!"); |
|
|
@ -345,15 +353,49 @@ public: |
|
|
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables) |
|
|
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables) |
|
|
{ |
|
|
{ |
|
|
my_radio = &radio; |
|
|
my_radio = &radio; |
|
|
airtime_factor = 1.0; // one half
|
|
|
|
|
|
num_clients = 0; |
|
|
num_clients = 0; |
|
|
|
|
|
|
|
|
|
|
|
// defaults
|
|
|
|
|
|
_prefs.airtime_factor = 1.0; // one half
|
|
|
|
|
|
strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)-1); |
|
|
|
|
|
_prefs.node_name[sizeof(_prefs.node_name)-1] = 0; // truncate if necessary
|
|
|
|
|
|
_prefs.node_lat = ADVERT_LAT; |
|
|
|
|
|
_prefs.node_lon = ADVERT_LON; |
|
|
|
|
|
strncpy(_prefs.password, ADMIN_PASSWORD, sizeof(_prefs.password)-1); |
|
|
|
|
|
_prefs.password[sizeof(_prefs.password)-1] = 0; // truncate if necessary
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void begin(FILESYSTEM* fs) { |
|
|
|
|
|
mesh::Mesh::begin(); |
|
|
|
|
|
_fs = fs; |
|
|
|
|
|
// load persisted prefs
|
|
|
|
|
|
if (_fs->exists("/node_prefs")) { |
|
|
|
|
|
File file = _fs->open("/node_prefs"); |
|
|
|
|
|
if (file) { |
|
|
|
|
|
file.read((uint8_t *) &_prefs, sizeof(_prefs)); |
|
|
|
|
|
file.close(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void savePrefs() { |
|
|
|
|
|
#if defined(NRF52_PLATFORM) |
|
|
|
|
|
File file = _fs->open("/node_prefs", FILE_O_WRITE); |
|
|
|
|
|
if (file) { file.seek(0); file.truncate(); } |
|
|
|
|
|
#else |
|
|
|
|
|
File file = _fs->open("/node_prefs", "w", true); |
|
|
|
|
|
#endif |
|
|
|
|
|
if (file) { |
|
|
|
|
|
file.write((const uint8_t *)&_prefs, sizeof(_prefs)); |
|
|
|
|
|
file.close(); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void sendSelfAdvertisement() { |
|
|
void sendSelfAdvertisement() { |
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE]; |
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE]; |
|
|
uint8_t app_data_len; |
|
|
uint8_t app_data_len; |
|
|
{ |
|
|
{ |
|
|
AdvertDataBuilder builder(ADV_TYPE_REPEATER, ADVERT_NAME, ADVERT_LAT, ADVERT_LON); |
|
|
AdvertDataBuilder builder(ADV_TYPE_REPEATER, _prefs.node_name, _prefs.node_lat, _prefs.node_lon); |
|
|
app_data_len = builder.encodeTo(app_data); |
|
|
app_data_len = builder.encodeTo(app_data); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -385,9 +427,30 @@ public: |
|
|
uint32_t now = getRTCClock()->getCurrentTime(); |
|
|
uint32_t now = getRTCClock()->getCurrentTime(); |
|
|
DateTime dt = DateTime(now); |
|
|
DateTime dt = DateTime(now); |
|
|
sprintf(reply, "%02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year()); |
|
|
sprintf(reply, "%02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year()); |
|
|
|
|
|
} else if (memcmp(command, "password ", 9) == 0) { |
|
|
|
|
|
// change admin password
|
|
|
|
|
|
strncpy(_prefs.password, &command[9], sizeof(_prefs.password)-1); |
|
|
|
|
|
_prefs.password[sizeof(_prefs.password)-1] = 0; // truncate if necesary
|
|
|
|
|
|
savePrefs(); |
|
|
|
|
|
sprintf(reply, "password now: %s", _prefs.password); // echo back just to let admin know for sure!!
|
|
|
} else if (memcmp(command, "set ", 4) == 0) { |
|
|
} else if (memcmp(command, "set ", 4) == 0) { |
|
|
if (memcmp(&command[4], "AF", 2) == 0 || memcmp(&command[4], "af=", 2) == 0) { |
|
|
const char* config = &command[4]; |
|
|
airtime_factor = atof(&command[7]); |
|
|
if (memcmp(config, "af ", 3) == 0) { |
|
|
|
|
|
_prefs.airtime_factor = atof(&config[3]); |
|
|
|
|
|
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
|
|
|
|
|
|
savePrefs(); |
|
|
|
|
|
strcpy(reply, "OK"); |
|
|
|
|
|
} else if (memcmp(config, "lat ", 4) == 0) { |
|
|
|
|
|
_prefs.node_lat = atof(&config[4]); |
|
|
|
|
|
savePrefs(); |
|
|
|
|
|
strcpy(reply, "OK"); |
|
|
|
|
|
} else if (memcmp(config, "lon ", 4) == 0) { |
|
|
|
|
|
_prefs.node_lon = atof(&config[4]); |
|
|
|
|
|
savePrefs(); |
|
|
strcpy(reply, "OK"); |
|
|
strcpy(reply, "OK"); |
|
|
} else { |
|
|
} else { |
|
|
sprintf(reply, "unknown config: %s", &command[4]); |
|
|
sprintf(reply, "unknown config: %s", &command[4]); |
|
|
@ -464,11 +527,14 @@ void setup() { |
|
|
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH); |
|
|
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH); |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
FILESYSTEM* fs; |
|
|
#if defined(NRF52_PLATFORM) |
|
|
#if defined(NRF52_PLATFORM) |
|
|
InternalFS.begin(); |
|
|
InternalFS.begin(); |
|
|
|
|
|
fs = &InternalFS; |
|
|
IdentityStore store(InternalFS, "/identity"); |
|
|
IdentityStore store(InternalFS, "/identity"); |
|
|
#elif defined(ESP32) |
|
|
#elif defined(ESP32) |
|
|
SPIFFS.begin(true); |
|
|
SPIFFS.begin(true); |
|
|
|
|
|
fs = &SPIFFS; |
|
|
IdentityStore store(SPIFFS, "/identity"); |
|
|
IdentityStore store(SPIFFS, "/identity"); |
|
|
#else |
|
|
#else |
|
|
#error "need to define filesystem" |
|
|
#error "need to define filesystem" |
|
|
@ -483,7 +549,7 @@ void setup() { |
|
|
|
|
|
|
|
|
command[0] = 0; |
|
|
command[0] = 0; |
|
|
|
|
|
|
|
|
the_mesh.begin(); |
|
|
the_mesh.begin(fs); |
|
|
|
|
|
|
|
|
// send out initial Advertisement to the mesh
|
|
|
// send out initial Advertisement to the mesh
|
|
|
the_mesh.sendSelfAdvertisement(); |
|
|
the_mesh.sendSelfAdvertisement(); |
|
|
|