|
|
|
@ -91,6 +91,7 @@ static uint32_t _atoi(const char* sp) { |
|
|
|
#define CMD_SET_ADVERT_NAME 8 |
|
|
|
#define CMD_ADD_UPDATE_CONTACT 9 |
|
|
|
#define CMD_SYNC_NEXT_MESSAGE 10 |
|
|
|
#define CMD_SET_RADIO_PARAMS 11 |
|
|
|
|
|
|
|
#define RESP_CODE_OK 0 |
|
|
|
#define RESP_CODE_ERR 1 |
|
|
|
@ -110,8 +111,21 @@ static uint32_t _atoi(const char* sp) { |
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------------------- */ |
|
|
|
|
|
|
|
struct NodePrefs { // persisted to file
|
|
|
|
float airtime_factor; |
|
|
|
char node_name[32]; |
|
|
|
double node_lat, node_lon; |
|
|
|
float freq; |
|
|
|
uint8_t sf; |
|
|
|
uint8_t cr; |
|
|
|
uint8_t reserved1; |
|
|
|
uint8_t reserved2; |
|
|
|
float bw; |
|
|
|
}; |
|
|
|
|
|
|
|
class MyMesh : public BaseChatMesh { |
|
|
|
FILESYSTEM* _fs; |
|
|
|
NodePrefs _prefs; |
|
|
|
uint32_t expected_ack_crc; // TODO: keep table of expected ACKs
|
|
|
|
mesh::GroupChannel* _public; |
|
|
|
BaseSerialInterface* _serial; |
|
|
|
@ -338,31 +352,68 @@ protected: |
|
|
|
} |
|
|
|
|
|
|
|
public: |
|
|
|
char self_name[sizeof(ContactInfo::name)]; |
|
|
|
|
|
|
|
MyMesh(RadioLibWrapper& radio, mesh::RNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables) |
|
|
|
: BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), _serial(NULL) |
|
|
|
{ |
|
|
|
_iter_started = false; |
|
|
|
|
|
|
|
// defaults
|
|
|
|
_prefs.airtime_factor = 1.0; // one half
|
|
|
|
strcpy(_prefs.node_name, "NONAME"); |
|
|
|
_prefs.node_lat = 0; |
|
|
|
_prefs.node_lon = 0; |
|
|
|
_prefs.freq = LORA_FREQ; |
|
|
|
_prefs.sf = LORA_SF; |
|
|
|
_prefs.bw = LORA_BW; |
|
|
|
_prefs.cr = LORA_CR; |
|
|
|
_prefs.reserved1 = 0; |
|
|
|
_prefs.reserved2 = 0; |
|
|
|
} |
|
|
|
|
|
|
|
float getFreqPref() const { return _prefs.freq; } |
|
|
|
uint8_t getSFPref() const { return _prefs.sf; } |
|
|
|
uint8_t getCRPref() const { return _prefs.cr; } |
|
|
|
float getBWPref() const { return _prefs.bw; } |
|
|
|
|
|
|
|
void begin(FILESYSTEM& fs, BaseSerialInterface& serial) { |
|
|
|
_fs = &fs; |
|
|
|
_serial = &serial; |
|
|
|
|
|
|
|
BaseChatMesh::begin(); |
|
|
|
|
|
|
|
strcpy(self_name, "UNSET"); |
|
|
|
IdentityStore store(fs, "/identity"); |
|
|
|
if (!store.load("_main", self_id, self_name, sizeof(self_name))) { |
|
|
|
if (!store.load("_main", self_id)) { |
|
|
|
self_id = mesh::LocalIdentity(getRNG()); // create new random identity
|
|
|
|
store.save("_main", self_id); |
|
|
|
} |
|
|
|
|
|
|
|
// 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(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
loadContacts(); |
|
|
|
_public = addChannel(PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
|
|
|
|
} |
|
|
|
|
|
|
|
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 handleCmdFrame(size_t len) { |
|
|
|
if (cmd_frame[0] == CMD_APP_START && len >= 8) { // sent when app establishes connection, respond with node ID
|
|
|
|
uint8_t app_ver = cmd_frame[1]; |
|
|
|
@ -382,8 +433,16 @@ public: |
|
|
|
memcpy(&out_frame[i], &latlonsats, 4); i += 4; // reserved future, for companion radios with GPS (like T-Beam, T1000)
|
|
|
|
memcpy(&out_frame[i], &latlonsats, 4); i += 4; |
|
|
|
memcpy(&out_frame[i], &latlonsats, 4); i += 4; |
|
|
|
int tlen = strlen(self_name); // revisit: UTF_8 ??
|
|
|
|
memcpy(&out_frame[i], self_name, tlen); i += tlen; |
|
|
|
|
|
|
|
uint32_t freq = _prefs.freq * 1000; |
|
|
|
memcpy(&out_frame[i], &freq, 4); i += 4; |
|
|
|
uint32_t bw = _prefs.bw*1000; |
|
|
|
memcpy(&out_frame[i], &bw, 4); i += 4; |
|
|
|
out_frame[i++] = _prefs.sf; |
|
|
|
out_frame[i++] = _prefs.cr; |
|
|
|
|
|
|
|
int tlen = strlen(_prefs.node_name); // revisit: UTF_8 ??
|
|
|
|
memcpy(&out_frame[i], _prefs.node_name, tlen); i += tlen; |
|
|
|
_serial->writeFrame(out_frame, i); |
|
|
|
} else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) { |
|
|
|
int i = 1; |
|
|
|
@ -457,11 +516,10 @@ public: |
|
|
|
} |
|
|
|
} else if (cmd_frame[0] == CMD_SET_ADVERT_NAME && len >= 2) { |
|
|
|
int nlen = len - 1; |
|
|
|
if (nlen > sizeof(self_name)-1) nlen = sizeof(self_name)-1; // max len
|
|
|
|
memcpy(self_name, &cmd_frame[1], nlen); |
|
|
|
self_name[nlen] = 0; |
|
|
|
IdentityStore store(*_fs, "/identity"); // update IdentityStore
|
|
|
|
store.save("_main", self_id, self_name); |
|
|
|
if (nlen > sizeof(_prefs.node_name)-1) nlen = sizeof(_prefs.node_name)-1; // max len
|
|
|
|
memcpy(_prefs.node_name, &cmd_frame[1], nlen); |
|
|
|
_prefs.node_name[nlen] = 0; // null terminator
|
|
|
|
savePrefs(); |
|
|
|
writeOKFrame(); |
|
|
|
} else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) { |
|
|
|
uint8_t reply[5]; |
|
|
|
@ -480,7 +538,7 @@ public: |
|
|
|
writeErrFrame(); |
|
|
|
} |
|
|
|
} else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) { |
|
|
|
auto pkt = createSelfAdvert(self_name); |
|
|
|
auto pkt = createSelfAdvert(_prefs.node_name); |
|
|
|
if (pkt) { |
|
|
|
if (len >= 2 && cmd_frame[1] == 1) { // optional param (1 = flood, 0 = zero hop)
|
|
|
|
sendFlood(pkt); |
|
|
|
@ -515,6 +573,25 @@ public: |
|
|
|
if ((out_len = getFromOfflineQueue(out_frame)) > 0) { |
|
|
|
_serial->writeFrame(out_frame, out_len); |
|
|
|
} |
|
|
|
} else if (cmd_frame[0] == CMD_SET_RADIO_PARAMS) { |
|
|
|
int i = 1; |
|
|
|
uint32_t freq; |
|
|
|
memcpy(&freq, &cmd_frame[i], 4); i += 4; |
|
|
|
uint32_t bw; |
|
|
|
memcpy(&bw, &cmd_frame[i], 4); i += 4; |
|
|
|
uint8_t sf = out_frame[i++]; |
|
|
|
uint8_t cr = out_frame[i++]; |
|
|
|
|
|
|
|
if (freq >= 300000 && freq <= 2500000 && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7000 && bw <= 500000) { |
|
|
|
_prefs.sf = sf; |
|
|
|
_prefs.cr = cr; |
|
|
|
_prefs.freq = (float)freq / 1000.0; |
|
|
|
_prefs.bw = (float)bw / 1000.0; |
|
|
|
savePrefs(); |
|
|
|
writeOKFrame(); // reboot now required!
|
|
|
|
} else { |
|
|
|
writeErrFrame(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
writeErrFrame(); |
|
|
|
MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); |
|
|
|
@ -631,6 +708,19 @@ void setup() { |
|
|
|
#else |
|
|
|
#error "need to define filesystem" |
|
|
|
#endif |
|
|
|
|
|
|
|
if (LORA_FREQ != the_mesh.getFreqPref()) { |
|
|
|
radio.setFrequency(the_mesh.getFreqPref()); |
|
|
|
} |
|
|
|
if (LORA_SF != the_mesh.getSFPref()) { |
|
|
|
radio.setSpreadingFactor(the_mesh.getSFPref()); |
|
|
|
} |
|
|
|
if (LORA_BW != the_mesh.getBWPref()) { |
|
|
|
radio.setBandwidth(the_mesh.getBWPref()); |
|
|
|
} |
|
|
|
if (LORA_CR != the_mesh.getCRPref()) { |
|
|
|
radio.setCodingRate(the_mesh.getCRPref()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void loop() { |
|
|
|
|