mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-04-20 22:13:47 +00:00
Merge branch 'dev' into double-acks
This commit is contained in:
commit
c26418016b
15 changed files with 325 additions and 126 deletions
|
|
@ -46,6 +46,9 @@
|
|||
#define CMD_SET_CUSTOM_VAR 41
|
||||
#define CMD_GET_ADVERT_PATH 42
|
||||
#define CMD_GET_TUNING_PARAMS 43
|
||||
// NOTE: CMD range 44..49 parked, potentially for WiFi operations
|
||||
#define CMD_SEND_BINARY_REQ 50
|
||||
#define CMD_FACTORY_RESET 51
|
||||
|
||||
#define RESP_CODE_OK 0
|
||||
#define RESP_CODE_ERR 1
|
||||
|
|
@ -93,6 +96,7 @@
|
|||
#define PUSH_CODE_TRACE_DATA 0x89
|
||||
#define PUSH_CODE_NEW_ADVERT 0x8A
|
||||
#define PUSH_CODE_TELEMETRY_RESPONSE 0x8B
|
||||
#define PUSH_CODE_BINARY_RESPONSE 0x8C
|
||||
|
||||
#define ERR_CODE_UNSUPPORTED_CMD 1
|
||||
#define ERR_CODE_NOT_FOUND 2
|
||||
|
|
@ -468,6 +472,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
|
|||
i += 6; // pub_key_prefix
|
||||
memcpy(&out_frame[i], &tag, 4);
|
||||
i += 4; // NEW: include server timestamp
|
||||
out_frame[i++] = data[7]; // NEW (v7): ACL permissions
|
||||
} else {
|
||||
out_frame[i++] = PUSH_CODE_LOGIN_FAIL;
|
||||
out_frame[i++] = 0; // reserved
|
||||
|
|
@ -490,7 +495,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
|
|||
memcpy(&out_frame[i], &data[4], len - 4);
|
||||
i += (len - 4);
|
||||
_serial->writeFrame(out_frame, i);
|
||||
} else if (len > 4 && tag == pending_telemetry) { // check for telemetry response
|
||||
} else if (len > 4 && tag == pending_telemetry) { // check for matching response tag
|
||||
pending_telemetry = 0;
|
||||
|
||||
int i = 0;
|
||||
|
|
@ -501,6 +506,17 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
|
|||
memcpy(&out_frame[i], &data[4], len - 4);
|
||||
i += (len - 4);
|
||||
_serial->writeFrame(out_frame, i);
|
||||
} else if (len > 4 && tag == pending_req) { // check for matching response tag
|
||||
pending_req = 0;
|
||||
|
||||
int i = 0;
|
||||
out_frame[i++] = PUSH_CODE_BINARY_RESPONSE;
|
||||
out_frame[i++] = 0; // reserved
|
||||
memcpy(&out_frame[i], &tag, 4); // app needs to match this to RESP_CODE_SENT.tag
|
||||
i += 4;
|
||||
memcpy(&out_frame[i], &data[4], len - 4);
|
||||
i += (len - 4);
|
||||
_serial->writeFrame(out_frame, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -566,7 +582,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
|||
_cli_rescue = false;
|
||||
offline_queue_len = 0;
|
||||
app_target_ver = 0;
|
||||
pending_login = pending_status = pending_telemetry = 0;
|
||||
pending_login = pending_status = pending_telemetry = pending_req = 0;
|
||||
next_ack_idx = 0;
|
||||
sign_data = NULL;
|
||||
dirty_contacts_expiry = 0;
|
||||
|
|
@ -1103,7 +1119,7 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||
if (result == MSG_SEND_FAILED) {
|
||||
writeErrFrame(ERR_CODE_TABLE_FULL);
|
||||
} else {
|
||||
pending_telemetry = pending_status = 0;
|
||||
pending_req = pending_telemetry = pending_status = 0;
|
||||
memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse()
|
||||
out_frame[0] = RESP_CODE_SENT;
|
||||
out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
|
||||
|
|
@ -1123,7 +1139,7 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||
if (result == MSG_SEND_FAILED) {
|
||||
writeErrFrame(ERR_CODE_TABLE_FULL);
|
||||
} else {
|
||||
pending_telemetry = pending_login = 0;
|
||||
pending_req = pending_telemetry = pending_login = 0;
|
||||
// FUTURE: pending_status = tag; // match this in onContactResponse()
|
||||
memcpy(&pending_status, recipient->id.pub_key, 4); // legacy matching scheme
|
||||
out_frame[0] = RESP_CODE_SENT;
|
||||
|
|
@ -1135,7 +1151,7 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||
} else {
|
||||
writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
|
||||
}
|
||||
} else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) {
|
||||
} else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) { // can deprecate, in favour of CMD_SEND_BINARY_REQ
|
||||
uint8_t *pub_key = &cmd_frame[4];
|
||||
ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
|
||||
if (recipient) {
|
||||
|
|
@ -1144,7 +1160,7 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||
if (result == MSG_SEND_FAILED) {
|
||||
writeErrFrame(ERR_CODE_TABLE_FULL);
|
||||
} else {
|
||||
pending_status = pending_login = 0;
|
||||
pending_status = pending_login = pending_req = 0;
|
||||
pending_telemetry = tag; // match this in onContactResponse()
|
||||
out_frame[0] = RESP_CODE_SENT;
|
||||
out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
|
||||
|
|
@ -1170,6 +1186,27 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||
memcpy(&out_frame[i], telemetry.getBuffer(), tlen);
|
||||
i += tlen;
|
||||
_serial->writeFrame(out_frame, i);
|
||||
} else if (cmd_frame[0] == CMD_SEND_BINARY_REQ && len >= 2 + PUB_KEY_SIZE) {
|
||||
uint8_t *pub_key = &cmd_frame[1];
|
||||
ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
|
||||
if (recipient) {
|
||||
uint8_t *req_data = &cmd_frame[1 + PUB_KEY_SIZE];
|
||||
uint32_t tag, est_timeout;
|
||||
int result = sendRequest(*recipient, req_data, len - (1 + PUB_KEY_SIZE), tag, est_timeout);
|
||||
if (result == MSG_SEND_FAILED) {
|
||||
writeErrFrame(ERR_CODE_TABLE_FULL);
|
||||
} else {
|
||||
pending_status = pending_login = pending_telemetry = 0;
|
||||
pending_req = tag; // match this in onContactResponse()
|
||||
out_frame[0] = RESP_CODE_SENT;
|
||||
out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
|
||||
memcpy(&out_frame[2], &tag, 4);
|
||||
memcpy(&out_frame[6], &est_timeout, 4);
|
||||
_serial->writeFrame(out_frame, 10);
|
||||
}
|
||||
} else {
|
||||
writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
|
||||
}
|
||||
} else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1 + PUB_KEY_SIZE) {
|
||||
uint8_t *pub_key = &cmd_frame[1];
|
||||
if (hasConnectionTo(pub_key)) {
|
||||
|
|
@ -1325,6 +1362,15 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||
} else {
|
||||
writeErrFrame(ERR_CODE_NOT_FOUND);
|
||||
}
|
||||
} else if (cmd_frame[0] == CMD_FACTORY_RESET && memcmp(&cmd_frame[1], "reset", 5) == 0) {
|
||||
bool success = _store->formatFileSystem();
|
||||
if (success) {
|
||||
writeOKFrame();
|
||||
delay(1000);
|
||||
board.reboot(); // doesn't return
|
||||
} else {
|
||||
writeErrFrame(ERR_CODE_FILE_IO_ERROR);
|
||||
}
|
||||
} else {
|
||||
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
|
||||
MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]);
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@
|
|||
#endif
|
||||
|
||||
/*------------ Frame Protocol --------------*/
|
||||
#define FIRMWARE_VER_CODE 6
|
||||
#define FIRMWARE_VER_CODE 7
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "2 Jul 2025"
|
||||
#define FIRMWARE_BUILD_DATE "15 Jul 2025"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.7.2"
|
||||
#define FIRMWARE_VERSION "v1.7.3"
|
||||
#endif
|
||||
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
|
|
@ -160,7 +160,8 @@ private:
|
|||
NodePrefs _prefs;
|
||||
uint32_t pending_login;
|
||||
uint32_t pending_status;
|
||||
uint32_t pending_telemetry;
|
||||
uint32_t pending_telemetry; // pending _TELEMETRY_REQ
|
||||
uint32_t pending_req; // pending _BINARY_REQ
|
||||
BaseSerialInterface *_serial;
|
||||
|
||||
ContactsIterator _iter;
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@
|
|||
/* ------------------------------ Config -------------------------------- */
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "2 Jul 2025"
|
||||
#define FIRMWARE_BUILD_DATE "15 Jul 2025"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.7.2"
|
||||
#define FIRMWARE_VERSION "v1.7.3"
|
||||
#endif
|
||||
|
||||
#ifndef LORA_FREQ
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@
|
|||
/* ------------------------------ Config -------------------------------- */
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "2 Jul 2025"
|
||||
#define FIRMWARE_BUILD_DATE "15 Jul 2025"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.7.2"
|
||||
#define FIRMWARE_VERSION "v1.7.3"
|
||||
#endif
|
||||
|
||||
#ifndef LORA_FREQ
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#define REQ_TYPE_KEEP_ALIVE 0x02
|
||||
#define REQ_TYPE_GET_TELEMETRY_DATA 0x03
|
||||
#define REQ_TYPE_GET_AVG_MIN_MAX 0x04
|
||||
#define REQ_TYPE_GET_ACCESS_LIST 0x05
|
||||
|
||||
#define RESP_SERVER_LOGIN_OK 0 // response to ANON_REQ
|
||||
|
||||
|
|
@ -58,6 +59,8 @@
|
|||
|
||||
#define LAZY_CONTACTS_WRITE_DELAY 5000
|
||||
|
||||
#define ALERT_ACK_EXPIRY_MILLIS 8000 // wait 8 secs for ACKs to alert messages
|
||||
|
||||
static File openAppend(FILESYSTEM* _fs, const char* fname) {
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
return _fs->open(fname, FILE_O_WRITE);
|
||||
|
|
@ -92,11 +95,11 @@ void SensorMesh::loadContacts() {
|
|||
while (!full) {
|
||||
ContactInfo c;
|
||||
uint8_t pub_key[32];
|
||||
uint8_t unused[5];
|
||||
uint8_t unused[6];
|
||||
|
||||
bool success = (file.read(pub_key, 32) == 32);
|
||||
success = success && (file.read((uint8_t *) &c.permissions, 2) == 2);
|
||||
success = success && (file.read(unused, 5) == 5);
|
||||
success = success && (file.read((uint8_t *) &c.permissions, 1) == 1);
|
||||
success = success && (file.read(unused, 6) == 6);
|
||||
success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1);
|
||||
success = success && (file.read(c.out_path, 64) == 64);
|
||||
success = success && (file.read(c.shared_secret, PUB_KEY_SIZE) == PUB_KEY_SIZE);
|
||||
|
|
@ -128,8 +131,8 @@ void SensorMesh::saveContacts() {
|
|||
if (c->permissions == 0) continue; // skip deleted entries
|
||||
|
||||
bool success = (file.write(c->id.pub_key, 32) == 32);
|
||||
success = success && (file.write((uint8_t *) &c->permissions, 2) == 2);
|
||||
success = success && (file.write(unused, 5) == 5);
|
||||
success = success && (file.write((uint8_t *) &c->permissions, 1) == 1);
|
||||
success = success && (file.write(unused, 6) == 6);
|
||||
success = success && (file.write((uint8_t *)&c->out_path_len, 1) == 1);
|
||||
success = success && (file.write(c->out_path, 64) == 64);
|
||||
success = success && (file.write(c->shared_secret, PUB_KEY_SIZE) == PUB_KEY_SIZE);
|
||||
|
|
@ -163,6 +166,7 @@ static uint8_t getDataSize(uint8_t type) {
|
|||
case LPP_TEMPERATURE:
|
||||
case LPP_CONCENTRATION:
|
||||
case LPP_BAROMETRIC_PRESSURE:
|
||||
case LPP_RELATIVE_HUMIDITY:
|
||||
case LPP_ALTITUDE:
|
||||
case LPP_VOLTAGE:
|
||||
case LPP_CURRENT:
|
||||
|
|
@ -185,6 +189,7 @@ static uint32_t getMultiplier(uint8_t type) {
|
|||
return 100;
|
||||
case LPP_TEMPERATURE:
|
||||
case LPP_BAROMETRIC_PRESSURE:
|
||||
case LPP_RELATIVE_HUMIDITY:
|
||||
return 10;
|
||||
}
|
||||
return 1;
|
||||
|
|
@ -235,7 +240,7 @@ static uint8_t putFloat(uint8_t * dest, float value, uint8_t size, uint32_t mult
|
|||
return size;
|
||||
}
|
||||
|
||||
uint8_t SensorMesh::handleRequest(uint16_t perms, uint32_t sender_timestamp, uint8_t req_type, uint8_t* payload, size_t payload_len) {
|
||||
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) {
|
||||
|
|
@ -243,17 +248,18 @@ uint8_t SensorMesh::handleRequest(uint16_t perms, uint32_t sender_timestamp, uin
|
|||
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);
|
||||
// query other sensors -- target specific
|
||||
sensors.querySensors(0xFF, telemetry); // allow all telemetry permissions for admin or guest
|
||||
// TODO: let requester know permissions they have: telemetry.addPresence(TELEM_CHANNEL_SELF, perms);
|
||||
|
||||
uint8_t tlen = telemetry.getSize();
|
||||
memcpy(&reply_data[4], telemetry.getBuffer(), tlen);
|
||||
return 4 + tlen; // reply_len
|
||||
}
|
||||
if (req_type == REQ_TYPE_GET_AVG_MIN_MAX && (perms & PERM_GET_MIN_MAX_AVG) != 0) {
|
||||
if (req_type == REQ_TYPE_GET_AVG_MIN_MAX && (perms & PERM_GET_OTHER_STATS) != 0) {
|
||||
uint32_t start_secs_ago, end_secs_ago;
|
||||
memcpy(&start_secs_ago, &payload[0], 4);
|
||||
memcpy(&end_secs_ago, &payload[4], 4);
|
||||
uint8_t res1 = payload[8]; // reserved for future (extra query params)
|
||||
uint8_t res2 = payload[8];
|
||||
uint8_t res2 = payload[9];
|
||||
|
||||
MinMaxAvg data[8];
|
||||
int n;
|
||||
|
|
@ -282,6 +288,19 @@ uint8_t SensorMesh::handleRequest(uint16_t perms, uint32_t sender_timestamp, uin
|
|||
}
|
||||
return ofs;
|
||||
}
|
||||
if (req_type == REQ_TYPE_GET_ACCESS_LIST && (perms & PERM_ACL_ROLE_MASK) == PERM_ACL_LEVEL3) {
|
||||
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];
|
||||
memcpy(&reply_data[ofs], c->id.pub_key, 6); ofs += 6; // just 6-byte pub_key prefix
|
||||
reply_data[ofs++] = c->permissions;
|
||||
}
|
||||
return ofs;
|
||||
}
|
||||
}
|
||||
return 0; // unknown command
|
||||
}
|
||||
|
||||
|
|
@ -319,11 +338,11 @@ ContactInfo* SensorMesh::putContact(const mesh::Identity& id) {
|
|||
return c;
|
||||
}
|
||||
|
||||
void SensorMesh::applyContactPermissions(const uint8_t* pubkey, uint16_t perms) {
|
||||
void SensorMesh::applyContactPermissions(const uint8_t* pubkey, uint8_t perms) {
|
||||
mesh::Identity id(pubkey);
|
||||
auto c = putContact(id);
|
||||
|
||||
if (perms == 0) { // no permissions, remove from contacts
|
||||
if ((perms & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) { // guest role is not persisted in contacts
|
||||
memset(c, 0, sizeof(*c));
|
||||
} else {
|
||||
c->permissions = perms; // update their permissions
|
||||
|
|
@ -332,46 +351,54 @@ void SensorMesh::applyContactPermissions(const uint8_t* pubkey, uint16_t perms)
|
|||
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); // trigger saveContacts()
|
||||
}
|
||||
|
||||
void SensorMesh::sendAlert(AlertPriority pri, const char* text) {
|
||||
int text_len = strlen(text);
|
||||
uint16_t pri_mask = (pri == HIGH_PRI_ALERT) ? PERM_RECV_ALERTS_HI : PERM_RECV_ALERTS_LO;
|
||||
void SensorMesh::sendAlert(ContactInfo* c, Trigger* t) {
|
||||
int text_len = strlen(t->text);
|
||||
|
||||
// send text message to all contacts with RECV_ALERT permission
|
||||
for (int i = 0; i < num_contacts; i++) {
|
||||
auto c = &contacts[i];
|
||||
if ((c->permissions & pri_mask) == 0) continue; // contact does NOT want alert
|
||||
uint8_t data[MAX_PACKET_PAYLOAD];
|
||||
memcpy(data, &t->timestamp, 4);
|
||||
data[4] = (TXT_TYPE_PLAIN << 2) | t->attempt; // attempt and flags
|
||||
memcpy(&data[5], t->text, text_len);
|
||||
|
||||
uint8_t data[MAX_PACKET_PAYLOAD];
|
||||
uint32_t now = getRTCClock()->getCurrentTimeUnique(); // need different timestamp per packet
|
||||
memcpy(data, &now, 4);
|
||||
data[4] = (TXT_TYPE_PLAIN << 2); // attempt and flags
|
||||
memcpy(&data[5], text, text_len);
|
||||
// calc expected ACK reply
|
||||
// uint32_t expected_ack;
|
||||
// mesh::Utils::sha256((uint8_t *)&expected_ack, 4, data, 5 + text_len, self_id.pub_key, PUB_KEY_SIZE);
|
||||
// calc expected ACK reply
|
||||
mesh::Utils::sha256((uint8_t *)&t->expected_acks[t->attempt], 4, data, 5 + text_len, self_id.pub_key, PUB_KEY_SIZE);
|
||||
t->attempt++;
|
||||
|
||||
auto pkt = createDatagram(PAYLOAD_TYPE_TXT_MSG, c->id, c->shared_secret, data, 5 + text_len);
|
||||
if (pkt) {
|
||||
if (c->out_path_len >= 0) { // we have an out_path, so send DIRECT
|
||||
sendDirect(pkt, c->out_path, c->out_path_len);
|
||||
} else {
|
||||
sendFlood(pkt);
|
||||
}
|
||||
auto pkt = createDatagram(PAYLOAD_TYPE_TXT_MSG, c->id, c->shared_secret, data, 5 + text_len);
|
||||
if (pkt) {
|
||||
if (c->out_path_len >= 0) { // we have an out_path, so send DIRECT
|
||||
sendDirect(pkt, c->out_path, c->out_path_len);
|
||||
} else {
|
||||
sendFlood(pkt);
|
||||
}
|
||||
}
|
||||
t->send_expiry = futureMillis(ALERT_ACK_EXPIRY_MILLIS);
|
||||
}
|
||||
|
||||
void SensorMesh::alertIf(bool condition, Trigger& t, AlertPriority pri, const char* text) {
|
||||
if (condition) {
|
||||
if (!t.triggered) {
|
||||
t.triggered = true;
|
||||
t.time = getRTCClock()->getCurrentTime();
|
||||
sendAlert(pri, text);
|
||||
if (!t.isTriggered() && num_alert_tasks < MAX_CONCURRENT_ALERTS) {
|
||||
StrHelper::strncpy(t.text, text, sizeof(t.text));
|
||||
t.pri = pri;
|
||||
t.send_expiry = 0; // signal that initial send is needed
|
||||
t.attempt = 4;
|
||||
t.curr_contact_idx = -1; // start iterating thru contacts[]
|
||||
|
||||
alert_tasks[num_alert_tasks++] = &t; // add to queue
|
||||
}
|
||||
} else {
|
||||
if (t.triggered) {
|
||||
t.triggered = false;
|
||||
// TODO: apply debounce logic
|
||||
if (t.isTriggered()) {
|
||||
t.text[0] = 0;
|
||||
// remove 't' from alert queue
|
||||
int i = 0;
|
||||
while (i < num_alert_tasks && alert_tasks[i] != &t) i++;
|
||||
|
||||
if (i < num_alert_tasks) { // found, now delete from array
|
||||
num_alert_tasks--;
|
||||
while (i < num_alert_tasks) {
|
||||
alert_tasks[i] = alert_tasks[i + 1];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -423,7 +450,7 @@ uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t*
|
|||
MESH_DEBUG_PRINTLN("Login success!");
|
||||
client->last_timestamp = sender_timestamp;
|
||||
client->last_activity = getRTCClock()->getCurrentTime();
|
||||
client->permissions = PERM_IS_ADMIN | PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO; // initially opt-in to receive alerts (can opt out)
|
||||
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);
|
||||
|
|
@ -433,7 +460,7 @@ uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t*
|
|||
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[7] = 0; // FUTURE: reserved
|
||||
reply_data[7] = client->permissions;
|
||||
getRNG()->random(&reply_data[8], 4); // random blob to help packet-hash uniqueness
|
||||
|
||||
return 12; // reply length
|
||||
|
|
@ -448,8 +475,13 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r
|
|||
command += 3;
|
||||
}
|
||||
|
||||
// first, see if this is a custom-handled CLI command (ie. in main.cpp)
|
||||
if (handleCustomCommand(sender_timestamp, command, reply)) {
|
||||
return; // command has been handled
|
||||
}
|
||||
|
||||
// handle sensor-specific CLI commands
|
||||
if (memcmp(command, "setperm ", 8) == 0) { // format: setperm {pubkey-hex} {permissions-int16}
|
||||
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) {
|
||||
|
|
@ -459,20 +491,21 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r
|
|||
|
||||
uint8_t pubkey[PUB_KEY_SIZE];
|
||||
if (mesh::Utils::fromHex(pubkey, PUB_KEY_SIZE, hex)) {
|
||||
uint16_t perms = atoi(sp);
|
||||
uint8_t perms = atoi(sp);
|
||||
applyContactPermissions(pubkey, perms);
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Err - bad pubkey");
|
||||
}
|
||||
}
|
||||
} else if (sender_timestamp == 0 && strcmp(command, "getperm") == 0) {
|
||||
Serial.println("Permissions:");
|
||||
} else if (sender_timestamp == 0 && strcmp(command, "get acl") == 0) {
|
||||
Serial.println("ACL:");
|
||||
for (int i = 0; i < num_contacts; i++) {
|
||||
auto c = &contacts[i];
|
||||
|
||||
Serial.printf("%02X ", c->permissions);
|
||||
mesh::Utils::printHex(Serial, c->id.pub_key, PUB_KEY_SIZE);
|
||||
Serial.printf(" %04X\n", c->permissions);
|
||||
Serial.printf("\n");
|
||||
}
|
||||
reply[0] = 0;
|
||||
} else {
|
||||
|
|
@ -629,6 +662,20 @@ bool SensorMesh::onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint
|
|||
return false;
|
||||
}
|
||||
|
||||
void SensorMesh::onAckRecv(mesh::Packet* packet, uint32_t ack_crc) {
|
||||
if (num_alert_tasks > 0) {
|
||||
auto t = alert_tasks[0]; // check current alert task
|
||||
for (int i = 0; i < t->attempt; i++) {
|
||||
if (ack_crc == t->expected_acks[i]) { // matching ACK!
|
||||
t->attempt = 4; // signal to move to next contact
|
||||
t->send_expiry = 0;
|
||||
packet->markDoNotRetransmit(); // ACK was for this node, so don't retransmit
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables)
|
||||
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
|
||||
_cli(board, rtc, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4)
|
||||
|
|
@ -637,6 +684,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
|
|||
next_local_advert = next_flood_advert = 0;
|
||||
dirty_contacts_expiry = 0;
|
||||
last_read_time = 0;
|
||||
num_alert_tasks = 0;
|
||||
|
||||
// defaults
|
||||
memset(&_prefs, 0, sizeof(_prefs));
|
||||
|
|
@ -736,7 +784,14 @@ float SensorMesh::getTelemValue(uint8_t channel, uint8_t type) {
|
|||
}
|
||||
|
||||
bool SensorMesh::getGPS(uint8_t channel, float& lat, float& lon, float& alt) {
|
||||
return false; // TODO
|
||||
if (channel == TELEM_CHANNEL_SELF) {
|
||||
lat = sensors.node_lat;
|
||||
lon = sensors.node_lon;
|
||||
alt = sensors.node_altitude;
|
||||
return true;
|
||||
}
|
||||
// REVISIT: custom GPS channels??
|
||||
return false;
|
||||
}
|
||||
|
||||
void SensorMesh::loop() {
|
||||
|
|
@ -767,6 +822,42 @@ void SensorMesh::loop() {
|
|||
last_read_time = curr;
|
||||
}
|
||||
|
||||
// check the alert send queue
|
||||
if (num_alert_tasks > 0) {
|
||||
auto t = alert_tasks[0]; // process head of queue
|
||||
|
||||
if (millisHasNowPassed(t->send_expiry)) { // next send needed?
|
||||
if (t->attempt >= 4) { // max attempts reached, try next contact
|
||||
t->curr_contact_idx++;
|
||||
if (t->curr_contact_idx >= num_contacts) { // no more contacts to try?
|
||||
num_alert_tasks--; // remove t from queue
|
||||
for (int i = 0; i < num_alert_tasks; i++) {
|
||||
alert_tasks[i] = alert_tasks[i + 1];
|
||||
}
|
||||
} else {
|
||||
auto c = &contacts[t->curr_contact_idx];
|
||||
uint16_t pri_mask = (t->pri == HIGH_PRI_ALERT) ? PERM_RECV_ALERTS_HI : PERM_RECV_ALERTS_LO;
|
||||
|
||||
if (c->permissions & pri_mask) { // contact wants alert
|
||||
// reset attempts
|
||||
t->attempt = (t->pri == LOW_PRI_ALERT) ? 3 : 0; // Low pri alerts, start at attempt #3 (ie. only make ONE attempt)
|
||||
t->timestamp = getRTCClock()->getCurrentTimeUnique(); // need unique timestamp per contact
|
||||
|
||||
sendAlert(c, t); // NOTE: modifies attempt, expected_acks[] and send_expiry
|
||||
} else {
|
||||
// next contact tested in next ::loop()
|
||||
}
|
||||
}
|
||||
} else if (t->curr_contact_idx < num_contacts) {
|
||||
auto c = &contacts[t->curr_contact_idx]; // send next attempt
|
||||
sendAlert(c, t); // NOTE: modifies attempt, expected_acks[] and send_expiry
|
||||
} else {
|
||||
// contact list has likely been modified while waiting for alert ACK, cancel this task
|
||||
t->attempt = 4; // next ::loop() will remove t from queue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// is there are pending dirty contacts write needed?
|
||||
if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) {
|
||||
saveContacts();
|
||||
|
|
|
|||
|
|
@ -23,22 +23,29 @@
|
|||
#include <RTClib.h>
|
||||
#include <target.h>
|
||||
|
||||
#define PERM_IS_ADMIN 0x8000
|
||||
#define PERM_GET_TELEMETRY 0x0001
|
||||
#define PERM_GET_MIN_MAX_AVG 0x0002
|
||||
#define PERM_RECV_ALERTS_LO 0x0100 // low priority alerts
|
||||
#define PERM_RECV_ALERTS_HI 0x0200 // high priority alerts
|
||||
#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_GET_TELEMETRY (1 << 2)
|
||||
#define PERM_GET_OTHER_STATS (1 << 3)
|
||||
#define PERM_RESERVED1 (1 << 4)
|
||||
#define PERM_RESERVED2 (1 << 5)
|
||||
#define PERM_RECV_ALERTS_LO (1 << 6) // low priority alerts
|
||||
#define PERM_RECV_ALERTS_HI (1 << 7) // high priority alerts
|
||||
|
||||
struct ContactInfo {
|
||||
mesh::Identity id;
|
||||
uint16_t permissions;
|
||||
uint8_t permissions;
|
||||
int8_t out_path_len;
|
||||
uint8_t out_path[MAX_PATH_SIZE];
|
||||
uint8_t shared_secret[PUB_KEY_SIZE];
|
||||
uint32_t last_timestamp; // by THEIR clock (transient)
|
||||
uint32_t last_activity; // by OUR clock (transient)
|
||||
|
||||
bool isAdmin() const { return (permissions & PERM_IS_ADMIN) != 0; }
|
||||
bool isAdmin() const { return (permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_LEVEL3; }
|
||||
};
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
|
|
@ -51,11 +58,10 @@ struct ContactInfo {
|
|||
|
||||
#define FIRMWARE_ROLE "sensor"
|
||||
|
||||
#ifndef MAX_CONTACTS
|
||||
#define MAX_CONTACTS 32
|
||||
#endif
|
||||
#define MAX_CONTACTS 20
|
||||
|
||||
#define MAX_SEARCH_RESULTS 8
|
||||
#define MAX_SEARCH_RESULTS 8
|
||||
#define MAX_CONCURRENT_ALERTS 4
|
||||
|
||||
class SensorMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
public:
|
||||
|
|
@ -99,17 +105,25 @@ protected:
|
|||
bool getGPS(uint8_t channel, float& lat, float& lon, float& alt);
|
||||
|
||||
// alerts
|
||||
struct Trigger {
|
||||
bool triggered;
|
||||
uint32_t time;
|
||||
|
||||
Trigger() { triggered = false; time = 0; }
|
||||
};
|
||||
enum AlertPriority { LOW_PRI_ALERT, HIGH_PRI_ALERT };
|
||||
|
||||
struct Trigger {
|
||||
uint32_t timestamp;
|
||||
AlertPriority pri;
|
||||
uint32_t expected_acks[4];
|
||||
int8_t curr_contact_idx;
|
||||
uint8_t attempt;
|
||||
unsigned long send_expiry;
|
||||
char text[MAX_PACKET_PAYLOAD];
|
||||
|
||||
Trigger() { text[0] = 0; }
|
||||
bool isTriggered() const { return text[0] != 0; }
|
||||
};
|
||||
void alertIf(bool condition, Trigger& t, AlertPriority pri, const char* text);
|
||||
|
||||
virtual void onSensorDataRead() = 0; // for app to implement
|
||||
virtual int querySeriesData(uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg dest[], int max_num) = 0; // for app to implement
|
||||
virtual bool handleCustomCommand(uint32_t sender_timestamp, char* command, char* reply) { return false; }
|
||||
|
||||
// Mesh overrides
|
||||
float getAirtimeBudgetFactor() const override;
|
||||
|
|
@ -124,6 +138,7 @@ protected:
|
|||
void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override;
|
||||
void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override;
|
||||
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;
|
||||
void onAckRecv(mesh::Packet* packet, uint32_t ack_crc) override;
|
||||
|
||||
private:
|
||||
FILESYSTEM* _fs;
|
||||
|
|
@ -137,15 +152,17 @@ private:
|
|||
CayenneLPP telemetry;
|
||||
uint32_t last_read_time;
|
||||
int matching_peer_indexes[MAX_SEARCH_RESULTS];
|
||||
int num_alert_tasks;
|
||||
Trigger* alert_tasks[MAX_CONCURRENT_ALERTS];
|
||||
|
||||
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(uint16_t perms, uint32_t sender_timestamp, uint8_t req_type, uint8_t* payload, size_t payload_len);
|
||||
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, uint16_t perms);
|
||||
void applyContactPermissions(const uint8_t* pubkey, uint8_t perms);
|
||||
|
||||
void sendAlert(AlertPriority pri, const char* text);
|
||||
void sendAlert(ContactInfo* c, Trigger* t);
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ void TimeSeriesData::recordData(mesh::RTCClock* clock, float value) {
|
|||
}
|
||||
}
|
||||
|
||||
void TimeSeriesData::calcDataMinMaxAvg(mesh::RTCClock* clock, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) const {
|
||||
void TimeSeriesData::calcMinMaxAvg(mesh::RTCClock* clock, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) const {
|
||||
int i = next, n = num_slots;
|
||||
uint32_t ago = clock->getCurrentTime() - last_timestamp;
|
||||
int num_values = 0;
|
||||
|
|
@ -40,6 +40,6 @@ void TimeSeriesData::calcDataMinMaxAvg(mesh::RTCClock* clock, uint32_t start_sec
|
|||
if (num_values > 0) {
|
||||
dest->_avg = total / num_values;
|
||||
} else {
|
||||
dest->_avg = NAN;
|
||||
dest->_max = dest->_min = dest->_avg = NAN;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,6 @@ public:
|
|||
}
|
||||
|
||||
void recordData(mesh::RTCClock* clock, float value);
|
||||
void calcDataMinMaxAvg(mesh::RTCClock* clock, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) const;
|
||||
void calcMinMaxAvg(mesh::RTCClock* clock, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) const;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,20 +15,29 @@ public:
|
|||
|
||||
protected:
|
||||
/* ========================== custom logic here ========================== */
|
||||
Trigger low_batt;
|
||||
Trigger low_batt, critical_batt;
|
||||
TimeSeriesData battery_data;
|
||||
|
||||
void onSensorDataRead() override {
|
||||
float batt_voltage = getVoltage(TELEM_CHANNEL_SELF);
|
||||
|
||||
battery_data.recordData(getRTCClock(), batt_voltage); // record battery
|
||||
alertIf(batt_voltage < 3.4f, low_batt, HIGH_PRI_ALERT, "Battery low!");
|
||||
alertIf(batt_voltage < 3.4f, critical_batt, HIGH_PRI_ALERT, "Battery is critical!");
|
||||
alertIf(batt_voltage < 3.6f, low_batt, LOW_PRI_ALERT, "Battery is low");
|
||||
}
|
||||
|
||||
int querySeriesData(uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg dest[], int max_num) override {
|
||||
battery_data.calcDataMinMaxAvg(getRTCClock(), start_secs_ago, end_secs_ago, &dest[0], TELEM_CHANNEL_SELF, LPP_VOLTAGE);
|
||||
battery_data.calcMinMaxAvg(getRTCClock(), start_secs_ago, end_secs_ago, &dest[0], TELEM_CHANNEL_SELF, LPP_VOLTAGE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool handleCustomCommand(uint32_t sender_timestamp, char* command, char* reply) override {
|
||||
if (strcmp(command, "magic") == 0) { // example 'custom' command handling
|
||||
strcpy(reply, "**Magic now done**");
|
||||
return true; // handled
|
||||
}
|
||||
return false; // not handled
|
||||
}
|
||||
/* ======================================================================= */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -77,14 +77,6 @@ build_flags = ${arduino_base.build_flags}
|
|||
-D NRF52_PLATFORM
|
||||
-D LFS_NO_ASSERT=1
|
||||
|
||||
[nrf52840_base]
|
||||
extends = nrf52_base
|
||||
build_flags = ${nrf52_base.build_flags}
|
||||
lib_deps =
|
||||
${nrf52_base.lib_deps}
|
||||
rweather/Crypto @ ^0.4.0
|
||||
https://github.com/adafruit/Adafruit_nRF52_Arduino
|
||||
|
||||
; ----------------- RP2040 ---------------------
|
||||
|
||||
[rp2040_base]
|
||||
|
|
|
|||
|
|
@ -402,22 +402,52 @@ bool BaseChatMesh::importContact(const uint8_t src_buf[], uint8_t len) {
|
|||
}
|
||||
|
||||
int BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout) {
|
||||
int tlen;
|
||||
uint8_t temp[24];
|
||||
uint32_t now = getRTCClock()->getCurrentTimeUnique();
|
||||
memcpy(temp, &now, 4); // mostly an extra blob to help make packet_hash unique
|
||||
if (recipient.type == ADV_TYPE_ROOM) {
|
||||
memcpy(&temp[4], &recipient.sync_since, 4);
|
||||
int len = strlen(password); if (len > 15) len = 15; // max 15 chars currently
|
||||
memcpy(&temp[8], password, len);
|
||||
tlen = 8 + len;
|
||||
} else {
|
||||
int len = strlen(password); if (len > 15) len = 15; // max 15 chars currently
|
||||
memcpy(&temp[4], password, len);
|
||||
tlen = 4 + len;
|
||||
}
|
||||
mesh::Packet* pkt;
|
||||
{
|
||||
int tlen;
|
||||
uint8_t temp[24];
|
||||
uint32_t now = getRTCClock()->getCurrentTimeUnique();
|
||||
memcpy(temp, &now, 4); // mostly an extra blob to help make packet_hash unique
|
||||
if (recipient.type == ADV_TYPE_ROOM) {
|
||||
memcpy(&temp[4], &recipient.sync_since, 4);
|
||||
int len = strlen(password); if (len > 15) len = 15; // max 15 chars currently
|
||||
memcpy(&temp[8], password, len);
|
||||
tlen = 8 + len;
|
||||
} else {
|
||||
int len = strlen(password); if (len > 15) len = 15; // max 15 chars currently
|
||||
memcpy(&temp[4], password, len);
|
||||
tlen = 4 + len;
|
||||
}
|
||||
|
||||
auto pkt = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, recipient.id, recipient.shared_secret, temp, tlen);
|
||||
pkt = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, recipient.id, recipient.shared_secret, temp, tlen);
|
||||
}
|
||||
if (pkt) {
|
||||
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
|
||||
if (recipient.out_path_len < 0) {
|
||||
sendFlood(pkt);
|
||||
est_timeout = calcFloodTimeoutMillisFor(t);
|
||||
return MSG_SEND_SENT_FLOOD;
|
||||
} else {
|
||||
sendDirect(pkt, recipient.out_path, recipient.out_path_len);
|
||||
est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len);
|
||||
return MSG_SEND_SENT_DIRECT;
|
||||
}
|
||||
}
|
||||
return MSG_SEND_FAILED;
|
||||
}
|
||||
|
||||
int BaseChatMesh::sendRequest(const ContactInfo& recipient, const uint8_t* req_data, uint8_t data_len, uint32_t& tag, uint32_t& est_timeout) {
|
||||
if (data_len > MAX_PACKET_PAYLOAD - 16) return MSG_SEND_FAILED;
|
||||
|
||||
mesh::Packet* pkt;
|
||||
{
|
||||
uint8_t temp[MAX_PACKET_PAYLOAD];
|
||||
tag = getRTCClock()->getCurrentTimeUnique();
|
||||
memcpy(temp, &tag, 4); // mostly an extra blob to help make packet_hash unique
|
||||
memcpy(&temp[4], req_data, data_len);
|
||||
|
||||
pkt = createDatagram(PAYLOAD_TYPE_REQ, recipient.id, recipient.shared_secret, temp, 4 + data_len);
|
||||
}
|
||||
if (pkt) {
|
||||
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
|
||||
if (recipient.out_path_len < 0) {
|
||||
|
|
@ -434,14 +464,17 @@ int BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password,
|
|||
}
|
||||
|
||||
int BaseChatMesh::sendRequest(const ContactInfo& recipient, uint8_t req_type, uint32_t& tag, uint32_t& est_timeout) {
|
||||
uint8_t temp[13];
|
||||
tag = getRTCClock()->getCurrentTimeUnique();
|
||||
memcpy(temp, &tag, 4); // mostly an extra blob to help make packet_hash unique
|
||||
temp[4] = req_type;
|
||||
memset(&temp[5], 0, 4); // reserved (possibly for 'since' param)
|
||||
getRNG()->random(&temp[9], 4); // random blob to help make packet-hash unique
|
||||
mesh::Packet* pkt;
|
||||
{
|
||||
uint8_t temp[13];
|
||||
tag = getRTCClock()->getCurrentTimeUnique();
|
||||
memcpy(temp, &tag, 4); // mostly an extra blob to help make packet_hash unique
|
||||
temp[4] = req_type;
|
||||
memset(&temp[5], 0, 4); // reserved (possibly for 'since' param)
|
||||
getRNG()->random(&temp[9], 4); // random blob to help make packet-hash unique
|
||||
|
||||
auto pkt = createDatagram(PAYLOAD_TYPE_REQ, recipient.id, recipient.shared_secret, temp, sizeof(temp));
|
||||
pkt = createDatagram(PAYLOAD_TYPE_REQ, recipient.id, recipient.shared_secret, temp, sizeof(temp));
|
||||
}
|
||||
if (pkt) {
|
||||
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
|
||||
if (recipient.out_path_len < 0) {
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ public:
|
|||
bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len);
|
||||
int sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout);
|
||||
int sendRequest(const ContactInfo& recipient, uint8_t req_type, uint32_t& tag, uint32_t& est_timeout);
|
||||
int sendRequest(const ContactInfo& recipient, const uint8_t* req_data, uint8_t data_len, uint32_t& tag, uint32_t& est_timeout);
|
||||
bool shareContactZeroHop(const ContactInfo& contact);
|
||||
uint8_t exportContact(const ContactInfo& contact, uint8_t dest_buf[]);
|
||||
bool importContact(const uint8_t src_buf[], uint8_t len);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
#include "target.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
HeltecV3Board board;
|
||||
|
||||
static SPIClass spi;
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
|
||||
#if defined(P_LORA_SCLK)
|
||||
static SPIClass spi;
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
|
||||
#else
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
|
||||
#endif
|
||||
|
||||
WRAPPER_CLASS radio_driver(radio, board);
|
||||
|
||||
|
|
@ -21,7 +24,11 @@ DISPLAY_CLASS display;
|
|||
bool radio_init() {
|
||||
fallback_clock.begin();
|
||||
rtc_clock.begin(Wire);
|
||||
#if defined(P_LORA_SCLK)
|
||||
return radio.std_init(&spi);
|
||||
#else
|
||||
return radio.std_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t radio_get_rng_seed() {
|
||||
|
|
@ -42,4 +49,4 @@ void radio_set_tx_power(uint8_t dbm) {
|
|||
mesh::LocalIdentity radio_new_identity() {
|
||||
RadioNoiseListener rng(radio);
|
||||
return mesh::LocalIdentity(&rng); // create new random identity
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
[rak4631]
|
||||
extends = nrf52840_base
|
||||
extends = nrf52_base
|
||||
platform = https://github.com/maxgerhardt/platform-nordicnrf52.git#rak
|
||||
board = wiscore_rak4631
|
||||
board_check = true
|
||||
build_flags = ${nrf52840_base.build_flags}
|
||||
build_flags = ${nrf52_base.build_flags}
|
||||
-I variants/rak4631
|
||||
-D RAK_4631
|
||||
-D PIN_BOARD_SCL=14
|
||||
|
|
@ -14,10 +14,10 @@ build_flags = ${nrf52840_base.build_flags}
|
|||
-D LORA_TX_POWER=22
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
build_src_filter = ${nrf52840_base.build_src_filter}
|
||||
build_src_filter = ${nrf52_base.build_src_filter}
|
||||
+<../variants/rak4631>
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
${nrf52_base.lib_deps}
|
||||
adafruit/Adafruit SSD1306 @ ^2.5.13
|
||||
stevemarple/MicroNMEA @ ^2.0.6
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ build_flags =
|
|||
build_src_filter = ${rak4631.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
lib_deps =
|
||||
${rak4631.lib_deps}
|
||||
sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27
|
||||
https://github.com/boschsensortec/Bosch-BSEC2-Library
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ void initVariant() {
|
|||
pinMode(LED_BLUE, OUTPUT);
|
||||
digitalWrite(LED_BLUE, LOW);
|
||||
|
||||
/* disable gps until we actually support it.
|
||||
pinMode(GPS_EN, OUTPUT);
|
||||
digitalWrite(GPS_EN, HIGH);
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue