mirror of https://github.com/meshcore-dev/MeshCore
10 changed files with 599 additions and 355 deletions
@ -0,0 +1,391 @@ |
|||
#include <Arduino.h> |
|||
#include "DataStore.h" |
|||
|
|||
DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _clock(&clock), |
|||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
|||
identity_store(fs, "") |
|||
#elif defined(RP2040_PLATFORM) |
|||
identity_store(fs, "/identity") |
|||
#else |
|||
identity_store(fs, "/identity") |
|||
#endif |
|||
{ |
|||
} |
|||
|
|||
static File openWrite(FILESYSTEM* _fs, const char* filename) { |
|||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
|||
_fs->remove(filename); |
|||
return _fs->open(filename, FILE_O_WRITE); |
|||
#elif defined(RP2040_PLATFORM) |
|||
return _fs->open(filename, "w"); |
|||
#else |
|||
return _fs->open(filename, "w", true); |
|||
#endif |
|||
} |
|||
|
|||
void DataStore::begin() { |
|||
#if defined(RP2040_PLATFORM) |
|||
identity_store.begin(); |
|||
#endif |
|||
|
|||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
|||
checkAdvBlobFile(); |
|||
#else |
|||
// init 'blob store' support
|
|||
_fs->mkdir("/bl"); |
|||
#endif |
|||
} |
|||
|
|||
#if defined(ESP32) |
|||
#include <SPIFFS.h> |
|||
#elif defined(RP2040_PLATFORM) |
|||
#include <LittleFS.h> |
|||
#endif |
|||
|
|||
File DataStore::openRead(const char* filename) { |
|||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
|||
return _fs->open(filename, FILE_O_READ); |
|||
#elif defined(RP2040_PLATFORM) |
|||
return _fs->open(filename, "r"); |
|||
#else |
|||
return _fs->open(filename, "r", false); |
|||
#endif |
|||
} |
|||
|
|||
bool DataStore::removeFile(const char* filename) { |
|||
return _fs->remove(filename); |
|||
} |
|||
|
|||
bool DataStore::formatFileSystem() { |
|||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
|||
return _fs->format(); |
|||
#elif defined(RP2040_PLATFORM) |
|||
return LittleFS.format(); |
|||
#elif defined(ESP32) |
|||
return ((fs::SPIFFSFS *)_fs)->format(); |
|||
#else |
|||
#error "need to implement format()" |
|||
#endif |
|||
} |
|||
|
|||
bool DataStore::loadMainIdentity(mesh::LocalIdentity &identity) { |
|||
return identity_store.load("_main", identity); |
|||
} |
|||
|
|||
bool DataStore::saveMainIdentity(const mesh::LocalIdentity &identity) { |
|||
return identity_store.save("_main", identity); |
|||
} |
|||
|
|||
void DataStore::loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon) { |
|||
if (_fs->exists("/new_prefs")) { |
|||
loadPrefsInt("/new_prefs", prefs, node_lat, node_lon); // new filename
|
|||
} else if (_fs->exists("/node_prefs")) { |
|||
loadPrefsInt("/node_prefs", prefs, node_lat, node_lon); |
|||
savePrefs(prefs, node_lat, node_lon); // save to new filename
|
|||
_fs->remove("/node_prefs"); // remove old
|
|||
} |
|||
} |
|||
|
|||
void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& node_lat, double& node_lon) { |
|||
#if defined(RP2040_PLATFORM) |
|||
File file = _fs->open(filename, "r"); |
|||
#else |
|||
File file = _fs->open(filename); |
|||
#endif |
|||
if (file) { |
|||
uint8_t pad[8]; |
|||
|
|||
file.read((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0
|
|||
file.read((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4
|
|||
file.read(pad, 4); // 36
|
|||
file.read((uint8_t *)&node_lat, sizeof(node_lat)); // 40
|
|||
file.read((uint8_t *)&node_lon, sizeof(node_lon)); // 48
|
|||
file.read((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56
|
|||
file.read((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60
|
|||
file.read((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61
|
|||
file.read((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
|
|||
file.read((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63
|
|||
file.read((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64
|
|||
file.read((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
|
|||
file.read((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69
|
|||
file.read((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70
|
|||
file.read((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71
|
|||
file.read((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
|
|||
file.read(pad, 4); // 76
|
|||
file.read((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
|
|||
|
|||
file.close(); |
|||
} |
|||
} |
|||
|
|||
void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_lon) { |
|||
File file = openWrite(_fs, "/new_prefs"); |
|||
if (file) { |
|||
uint8_t pad[8]; |
|||
memset(pad, 0, sizeof(pad)); |
|||
|
|||
file.write((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0
|
|||
file.write((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4
|
|||
file.write(pad, 4); // 36
|
|||
file.write((uint8_t *)&node_lat, sizeof(node_lat)); // 40
|
|||
file.write((uint8_t *)&node_lon, sizeof(node_lon)); // 48
|
|||
file.write((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56
|
|||
file.write((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60
|
|||
file.write((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61
|
|||
file.write((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
|
|||
file.write((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63
|
|||
file.write((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64
|
|||
file.write((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
|
|||
file.write((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69
|
|||
file.write((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70
|
|||
file.write((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71
|
|||
file.write((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
|
|||
file.write(pad, 4); // 76
|
|||
file.write((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
|
|||
|
|||
file.close(); |
|||
} |
|||
} |
|||
|
|||
void DataStore::loadContacts(DataStoreHost* host) { |
|||
if (_fs->exists("/contacts3")) { |
|||
#if defined(RP2040_PLATFORM) |
|||
File file = _fs->open("/contacts3", "r"); |
|||
#else |
|||
File file = _fs->open("/contacts3"); |
|||
#endif |
|||
if (file) { |
|||
bool full = false; |
|||
while (!full) { |
|||
ContactInfo c; |
|||
uint8_t pub_key[32]; |
|||
uint8_t unused; |
|||
|
|||
bool success = (file.read(pub_key, 32) == 32); |
|||
success = success && (file.read((uint8_t *)&c.name, 32) == 32); |
|||
success = success && (file.read(&c.type, 1) == 1); |
|||
success = success && (file.read(&c.flags, 1) == 1); |
|||
success = success && (file.read(&unused, 1) == 1); |
|||
success = success && (file.read((uint8_t *)&c.sync_since, 4) == 4); // was 'reserved'
|
|||
success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1); |
|||
success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4); |
|||
success = success && (file.read(c.out_path, 64) == 64); |
|||
success = success && (file.read((uint8_t *)&c.lastmod, 4) == 4); |
|||
success = success && (file.read((uint8_t *)&c.gps_lat, 4) == 4); |
|||
success = success && (file.read((uint8_t *)&c.gps_lon, 4) == 4); |
|||
|
|||
if (!success) break; // EOF
|
|||
|
|||
c.id = mesh::Identity(pub_key); |
|||
if (!host->onContactLoaded(c)) full = true; |
|||
} |
|||
file.close(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void DataStore::saveContacts(DataStoreHost* host) { |
|||
File file = openWrite(_fs, "/contacts3"); |
|||
if (file) { |
|||
uint32_t idx = 0; |
|||
ContactInfo c; |
|||
uint8_t unused = 0; |
|||
|
|||
while (host->getContactForSave(idx, c)) { |
|||
bool success = (file.write(c.id.pub_key, 32) == 32); |
|||
success = success && (file.write((uint8_t *)&c.name, 32) == 32); |
|||
success = success && (file.write(&c.type, 1) == 1); |
|||
success = success && (file.write(&c.flags, 1) == 1); |
|||
success = success && (file.write(&unused, 1) == 1); |
|||
success = success && (file.write((uint8_t *)&c.sync_since, 4) == 4); |
|||
success = success && (file.write((uint8_t *)&c.out_path_len, 1) == 1); |
|||
success = success && (file.write((uint8_t *)&c.last_advert_timestamp, 4) == 4); |
|||
success = success && (file.write(c.out_path, 64) == 64); |
|||
success = success && (file.write((uint8_t *)&c.lastmod, 4) == 4); |
|||
success = success && (file.write((uint8_t *)&c.gps_lat, 4) == 4); |
|||
success = success && (file.write((uint8_t *)&c.gps_lon, 4) == 4); |
|||
|
|||
if (!success) break; // write failed
|
|||
|
|||
idx++; // advance to next contact
|
|||
} |
|||
file.close(); |
|||
} |
|||
} |
|||
|
|||
void DataStore::loadChannels(DataStoreHost* host) { |
|||
if (_fs->exists("/channels2")) { |
|||
#if defined(RP2040_PLATFORM) |
|||
File file = _fs->open("/channels2", "r"); |
|||
#else |
|||
File file = _fs->open("/channels2"); |
|||
#endif |
|||
if (file) { |
|||
bool full = false; |
|||
uint8_t channel_idx = 0; |
|||
while (!full) { |
|||
ChannelDetails ch; |
|||
uint8_t unused[4]; |
|||
|
|||
bool success = (file.read(unused, 4) == 4); |
|||
success = success && (file.read((uint8_t *)ch.name, 32) == 32); |
|||
success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32); |
|||
|
|||
if (!success) break; // EOF
|
|||
|
|||
if (host->onChannelLoaded(channel_idx, ch)) { |
|||
channel_idx++; |
|||
} else { |
|||
full = true; |
|||
} |
|||
} |
|||
file.close(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void DataStore::saveChannels(DataStoreHost* host) { |
|||
File file = openWrite(_fs, "/channels2"); |
|||
if (file) { |
|||
uint8_t channel_idx = 0; |
|||
ChannelDetails ch; |
|||
uint8_t unused[4]; |
|||
memset(unused, 0, 4); |
|||
|
|||
while (host->getChannelForSave(channel_idx, ch)) { |
|||
bool success = (file.write(unused, 4) == 4); |
|||
success = success && (file.write((uint8_t *)ch.name, 32) == 32); |
|||
success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32); |
|||
|
|||
if (!success) break; // write failed
|
|||
channel_idx++; |
|||
} |
|||
file.close(); |
|||
} |
|||
} |
|||
|
|||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
|||
|
|||
#define MAX_ADVERT_PKT_LEN (2 + 32 + PUB_KEY_SIZE + 4 + SIGNATURE_SIZE + MAX_ADVERT_DATA_SIZE) |
|||
|
|||
struct BlobRec { |
|||
uint32_t timestamp; |
|||
uint8_t key[7]; |
|||
uint8_t len; |
|||
uint8_t data[MAX_ADVERT_PKT_LEN]; |
|||
}; |
|||
|
|||
void DataStore::checkAdvBlobFile() { |
|||
if (!_fs->exists("/adv_blobs")) { |
|||
File file = openWrite(_fs, "/adv_blobs"); |
|||
if (file) { |
|||
BlobRec zeroes; |
|||
memset(&zeroes, 0, sizeof(zeroes)); |
|||
for (int i = 0; i < 20; i++) { // pre-allocate to fixed size
|
|||
file.write((uint8_t *) &zeroes, sizeof(zeroes)); |
|||
} |
|||
file.close(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { |
|||
File file = _fs->open("/adv_blobs"); |
|||
uint8_t len = 0; // 0 = not found
|
|||
|
|||
if (file) { |
|||
BlobRec tmp; |
|||
while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) { |
|||
if (memcmp(key, tmp.key, sizeof(tmp.key)) == 0) { // only match by 7 byte prefix
|
|||
len = tmp.len; |
|||
memcpy(dest_buf, tmp.data, len); |
|||
break; |
|||
} |
|||
} |
|||
file.close(); |
|||
} |
|||
return len; |
|||
} |
|||
|
|||
bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) { |
|||
if (len < PUB_KEY_SIZE+4+SIGNATURE_SIZE || len > MAX_ADVERT_PKT_LEN) return false; |
|||
|
|||
checkAdvBlobFile(); |
|||
|
|||
File file = _fs->open("/adv_blobs", FILE_O_WRITE); |
|||
if (file) { |
|||
uint32_t pos = 0, found_pos = 0; |
|||
uint32_t min_timestamp = 0xFFFFFFFF; |
|||
|
|||
// search for matching key OR evict by oldest timestmap
|
|||
BlobRec tmp; |
|||
file.seek(0); |
|||
while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) { |
|||
if (memcmp(key, tmp.key, sizeof(tmp.key)) == 0) { // only match by 7 byte prefix
|
|||
found_pos = pos; |
|||
break; |
|||
} |
|||
if (tmp.timestamp < min_timestamp) { |
|||
min_timestamp = tmp.timestamp; |
|||
found_pos = pos; |
|||
} |
|||
|
|||
pos += sizeof(tmp); |
|||
} |
|||
|
|||
memcpy(tmp.key, key, sizeof(tmp.key)); // just record 7 byte prefix of key
|
|||
memcpy(tmp.data, src_buf, len); |
|||
tmp.len = len; |
|||
tmp.timestamp = _clock->getCurrentTime(); |
|||
|
|||
file.seek(found_pos); |
|||
file.write((uint8_t *) &tmp, sizeof(tmp)); |
|||
|
|||
file.close(); |
|||
return true; |
|||
} |
|||
return false; // error
|
|||
} |
|||
#else |
|||
uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { |
|||
char path[64]; |
|||
char fname[18]; |
|||
|
|||
if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix)
|
|||
mesh::Utils::toHex(fname, key, key_len); |
|||
sprintf(path, "/bl/%s", fname); |
|||
|
|||
if (_fs->exists(path)) { |
|||
#if defined(RP2040_PLATFORM) |
|||
File f = _fs->open(path, "r"); |
|||
#else |
|||
File f = _fs->open(path); |
|||
#endif |
|||
if (f) { |
|||
int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!!
|
|||
f.close(); |
|||
return len; |
|||
} |
|||
} |
|||
return 0; // not found
|
|||
} |
|||
|
|||
bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) { |
|||
char path[64]; |
|||
char fname[18]; |
|||
|
|||
if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix)
|
|||
mesh::Utils::toHex(fname, key, key_len); |
|||
sprintf(path, "/bl/%s", fname); |
|||
|
|||
File f = openWrite(_fs, path); |
|||
if (f) { |
|||
int n = f.write(src_buf, len); |
|||
f.close(); |
|||
if (n == len) return true; // success!
|
|||
|
|||
_fs->remove(path); // blob was only partially written!
|
|||
} |
|||
return false; // error
|
|||
} |
|||
#endif |
|||
@ -0,0 +1,42 @@ |
|||
#pragma once |
|||
|
|||
#include <helpers/IdentityStore.h> |
|||
#include <helpers/ContactInfo.h> |
|||
#include <helpers/ChannelDetails.h> |
|||
#include "NodePrefs.h" |
|||
|
|||
class DataStoreHost { |
|||
public: |
|||
virtual bool onContactLoaded(const ContactInfo& contact) =0; |
|||
virtual bool getContactForSave(uint32_t idx, ContactInfo& contact) =0; |
|||
virtual bool onChannelLoaded(uint8_t channel_idx, const ChannelDetails& ch) =0; |
|||
virtual bool getChannelForSave(uint8_t channel_idx, ChannelDetails& ch) =0; |
|||
}; |
|||
|
|||
class DataStore { |
|||
FILESYSTEM* _fs; |
|||
mesh::RTCClock* _clock; |
|||
IdentityStore identity_store; |
|||
|
|||
void loadPrefsInt(const char *filename, NodePrefs& prefs, double& node_lat, double& node_lon); |
|||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
|||
void checkAdvBlobFile(); |
|||
#endif |
|||
|
|||
public: |
|||
DataStore(FILESYSTEM& fs, mesh::RTCClock& clock); |
|||
void begin(); |
|||
bool formatFileSystem(); |
|||
bool loadMainIdentity(mesh::LocalIdentity &identity); |
|||
bool saveMainIdentity(const mesh::LocalIdentity &identity); |
|||
void loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon); |
|||
void savePrefs(const NodePrefs& prefs, double node_lat, double node_lon); |
|||
void loadContacts(DataStoreHost* host); |
|||
void saveContacts(DataStoreHost* host); |
|||
void loadChannels(DataStoreHost* host); |
|||
void saveChannels(DataStoreHost* host); |
|||
uint8_t getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); |
|||
bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len); |
|||
File openRead(const char* filename); |
|||
bool removeFile(const char* filename); |
|||
}; |
|||
@ -0,0 +1,9 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> |
|||
#include <Mesh.h> |
|||
|
|||
struct ChannelDetails { |
|||
mesh::GroupChannel channel; |
|||
char name[32]; |
|||
}; |
|||
@ -0,0 +1,18 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> |
|||
#include <Mesh.h> |
|||
|
|||
struct ContactInfo { |
|||
mesh::Identity id; |
|||
char name[32]; |
|||
uint8_t type; // on of ADV_TYPE_*
|
|||
uint8_t flags; |
|||
int8_t out_path_len; |
|||
uint8_t out_path[MAX_PATH_SIZE]; |
|||
uint32_t last_advert_timestamp; // by THEIR clock
|
|||
uint8_t shared_secret[PUB_KEY_SIZE]; |
|||
uint32_t lastmod; // by OUR clock
|
|||
int32_t gps_lat, gps_lon; // 6 dec places
|
|||
uint32_t sync_since; |
|||
}; |
|||
Loading…
Reference in new issue