Skip to content

Commit 79eff34

Browse files
author
Scott Powell
committed
Merge branch 'datastore' into dev
2 parents 7dd7b71 + 381bb50 commit 79eff34

File tree

10 files changed

+599
-355
lines changed

10 files changed

+599
-355
lines changed
Lines changed: 391 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,391 @@
1+
#include <Arduino.h>
2+
#include "DataStore.h"
3+
4+
DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _clock(&clock),
5+
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
6+
identity_store(fs, "")
7+
#elif defined(RP2040_PLATFORM)
8+
identity_store(fs, "/identity")
9+
#else
10+
identity_store(fs, "/identity")
11+
#endif
12+
{
13+
}
14+
15+
static File openWrite(FILESYSTEM* _fs, const char* filename) {
16+
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
17+
_fs->remove(filename);
18+
return _fs->open(filename, FILE_O_WRITE);
19+
#elif defined(RP2040_PLATFORM)
20+
return _fs->open(filename, "w");
21+
#else
22+
return _fs->open(filename, "w", true);
23+
#endif
24+
}
25+
26+
void DataStore::begin() {
27+
#if defined(RP2040_PLATFORM)
28+
identity_store.begin();
29+
#endif
30+
31+
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
32+
checkAdvBlobFile();
33+
#else
34+
// init 'blob store' support
35+
_fs->mkdir("/bl");
36+
#endif
37+
}
38+
39+
#if defined(ESP32)
40+
#include <SPIFFS.h>
41+
#elif defined(RP2040_PLATFORM)
42+
#include <LittleFS.h>
43+
#endif
44+
45+
File DataStore::openRead(const char* filename) {
46+
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
47+
return _fs->open(filename, FILE_O_READ);
48+
#elif defined(RP2040_PLATFORM)
49+
return _fs->open(filename, "r");
50+
#else
51+
return _fs->open(filename, "r", false);
52+
#endif
53+
}
54+
55+
bool DataStore::removeFile(const char* filename) {
56+
return _fs->remove(filename);
57+
}
58+
59+
bool DataStore::formatFileSystem() {
60+
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
61+
return _fs->format();
62+
#elif defined(RP2040_PLATFORM)
63+
return LittleFS.format();
64+
#elif defined(ESP32)
65+
return ((fs::SPIFFSFS *)_fs)->format();
66+
#else
67+
#error "need to implement format()"
68+
#endif
69+
}
70+
71+
bool DataStore::loadMainIdentity(mesh::LocalIdentity &identity) {
72+
return identity_store.load("_main", identity);
73+
}
74+
75+
bool DataStore::saveMainIdentity(const mesh::LocalIdentity &identity) {
76+
return identity_store.save("_main", identity);
77+
}
78+
79+
void DataStore::loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon) {
80+
if (_fs->exists("/new_prefs")) {
81+
loadPrefsInt("/new_prefs", prefs, node_lat, node_lon); // new filename
82+
} else if (_fs->exists("/node_prefs")) {
83+
loadPrefsInt("/node_prefs", prefs, node_lat, node_lon);
84+
savePrefs(prefs, node_lat, node_lon); // save to new filename
85+
_fs->remove("/node_prefs"); // remove old
86+
}
87+
}
88+
89+
void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& node_lat, double& node_lon) {
90+
#if defined(RP2040_PLATFORM)
91+
File file = _fs->open(filename, "r");
92+
#else
93+
File file = _fs->open(filename);
94+
#endif
95+
if (file) {
96+
uint8_t pad[8];
97+
98+
file.read((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0
99+
file.read((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4
100+
file.read(pad, 4); // 36
101+
file.read((uint8_t *)&node_lat, sizeof(node_lat)); // 40
102+
file.read((uint8_t *)&node_lon, sizeof(node_lon)); // 48
103+
file.read((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56
104+
file.read((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60
105+
file.read((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61
106+
file.read((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
107+
file.read((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63
108+
file.read((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64
109+
file.read((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
110+
file.read((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69
111+
file.read((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70
112+
file.read((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71
113+
file.read((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
114+
file.read(pad, 4); // 76
115+
file.read((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
116+
117+
file.close();
118+
}
119+
}
120+
121+
void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_lon) {
122+
File file = openWrite(_fs, "/new_prefs");
123+
if (file) {
124+
uint8_t pad[8];
125+
memset(pad, 0, sizeof(pad));
126+
127+
file.write((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0
128+
file.write((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4
129+
file.write(pad, 4); // 36
130+
file.write((uint8_t *)&node_lat, sizeof(node_lat)); // 40
131+
file.write((uint8_t *)&node_lon, sizeof(node_lon)); // 48
132+
file.write((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56
133+
file.write((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60
134+
file.write((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61
135+
file.write((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
136+
file.write((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63
137+
file.write((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64
138+
file.write((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
139+
file.write((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69
140+
file.write((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70
141+
file.write((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71
142+
file.write((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
143+
file.write(pad, 4); // 76
144+
file.write((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
145+
146+
file.close();
147+
}
148+
}
149+
150+
void DataStore::loadContacts(DataStoreHost* host) {
151+
if (_fs->exists("/contacts3")) {
152+
#if defined(RP2040_PLATFORM)
153+
File file = _fs->open("/contacts3", "r");
154+
#else
155+
File file = _fs->open("/contacts3");
156+
#endif
157+
if (file) {
158+
bool full = false;
159+
while (!full) {
160+
ContactInfo c;
161+
uint8_t pub_key[32];
162+
uint8_t unused;
163+
164+
bool success = (file.read(pub_key, 32) == 32);
165+
success = success && (file.read((uint8_t *)&c.name, 32) == 32);
166+
success = success && (file.read(&c.type, 1) == 1);
167+
success = success && (file.read(&c.flags, 1) == 1);
168+
success = success && (file.read(&unused, 1) == 1);
169+
success = success && (file.read((uint8_t *)&c.sync_since, 4) == 4); // was 'reserved'
170+
success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1);
171+
success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4);
172+
success = success && (file.read(c.out_path, 64) == 64);
173+
success = success && (file.read((uint8_t *)&c.lastmod, 4) == 4);
174+
success = success && (file.read((uint8_t *)&c.gps_lat, 4) == 4);
175+
success = success && (file.read((uint8_t *)&c.gps_lon, 4) == 4);
176+
177+
if (!success) break; // EOF
178+
179+
c.id = mesh::Identity(pub_key);
180+
if (!host->onContactLoaded(c)) full = true;
181+
}
182+
file.close();
183+
}
184+
}
185+
}
186+
187+
void DataStore::saveContacts(DataStoreHost* host) {
188+
File file = openWrite(_fs, "/contacts3");
189+
if (file) {
190+
uint32_t idx = 0;
191+
ContactInfo c;
192+
uint8_t unused = 0;
193+
194+
while (host->getContactForSave(idx, c)) {
195+
bool success = (file.write(c.id.pub_key, 32) == 32);
196+
success = success && (file.write((uint8_t *)&c.name, 32) == 32);
197+
success = success && (file.write(&c.type, 1) == 1);
198+
success = success && (file.write(&c.flags, 1) == 1);
199+
success = success && (file.write(&unused, 1) == 1);
200+
success = success && (file.write((uint8_t *)&c.sync_since, 4) == 4);
201+
success = success && (file.write((uint8_t *)&c.out_path_len, 1) == 1);
202+
success = success && (file.write((uint8_t *)&c.last_advert_timestamp, 4) == 4);
203+
success = success && (file.write(c.out_path, 64) == 64);
204+
success = success && (file.write((uint8_t *)&c.lastmod, 4) == 4);
205+
success = success && (file.write((uint8_t *)&c.gps_lat, 4) == 4);
206+
success = success && (file.write((uint8_t *)&c.gps_lon, 4) == 4);
207+
208+
if (!success) break; // write failed
209+
210+
idx++; // advance to next contact
211+
}
212+
file.close();
213+
}
214+
}
215+
216+
void DataStore::loadChannels(DataStoreHost* host) {
217+
if (_fs->exists("/channels2")) {
218+
#if defined(RP2040_PLATFORM)
219+
File file = _fs->open("/channels2", "r");
220+
#else
221+
File file = _fs->open("/channels2");
222+
#endif
223+
if (file) {
224+
bool full = false;
225+
uint8_t channel_idx = 0;
226+
while (!full) {
227+
ChannelDetails ch;
228+
uint8_t unused[4];
229+
230+
bool success = (file.read(unused, 4) == 4);
231+
success = success && (file.read((uint8_t *)ch.name, 32) == 32);
232+
success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32);
233+
234+
if (!success) break; // EOF
235+
236+
if (host->onChannelLoaded(channel_idx, ch)) {
237+
channel_idx++;
238+
} else {
239+
full = true;
240+
}
241+
}
242+
file.close();
243+
}
244+
}
245+
}
246+
247+
void DataStore::saveChannels(DataStoreHost* host) {
248+
File file = openWrite(_fs, "/channels2");
249+
if (file) {
250+
uint8_t channel_idx = 0;
251+
ChannelDetails ch;
252+
uint8_t unused[4];
253+
memset(unused, 0, 4);
254+
255+
while (host->getChannelForSave(channel_idx, ch)) {
256+
bool success = (file.write(unused, 4) == 4);
257+
success = success && (file.write((uint8_t *)ch.name, 32) == 32);
258+
success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32);
259+
260+
if (!success) break; // write failed
261+
channel_idx++;
262+
}
263+
file.close();
264+
}
265+
}
266+
267+
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
268+
269+
#define MAX_ADVERT_PKT_LEN (2 + 32 + PUB_KEY_SIZE + 4 + SIGNATURE_SIZE + MAX_ADVERT_DATA_SIZE)
270+
271+
struct BlobRec {
272+
uint32_t timestamp;
273+
uint8_t key[7];
274+
uint8_t len;
275+
uint8_t data[MAX_ADVERT_PKT_LEN];
276+
};
277+
278+
void DataStore::checkAdvBlobFile() {
279+
if (!_fs->exists("/adv_blobs")) {
280+
File file = openWrite(_fs, "/adv_blobs");
281+
if (file) {
282+
BlobRec zeroes;
283+
memset(&zeroes, 0, sizeof(zeroes));
284+
for (int i = 0; i < 20; i++) { // pre-allocate to fixed size
285+
file.write((uint8_t *) &zeroes, sizeof(zeroes));
286+
}
287+
file.close();
288+
}
289+
}
290+
}
291+
292+
uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) {
293+
File file = _fs->open("/adv_blobs");
294+
uint8_t len = 0; // 0 = not found
295+
296+
if (file) {
297+
BlobRec tmp;
298+
while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) {
299+
if (memcmp(key, tmp.key, sizeof(tmp.key)) == 0) { // only match by 7 byte prefix
300+
len = tmp.len;
301+
memcpy(dest_buf, tmp.data, len);
302+
break;
303+
}
304+
}
305+
file.close();
306+
}
307+
return len;
308+
}
309+
310+
bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) {
311+
if (len < PUB_KEY_SIZE+4+SIGNATURE_SIZE || len > MAX_ADVERT_PKT_LEN) return false;
312+
313+
checkAdvBlobFile();
314+
315+
File file = _fs->open("/adv_blobs", FILE_O_WRITE);
316+
if (file) {
317+
uint32_t pos = 0, found_pos = 0;
318+
uint32_t min_timestamp = 0xFFFFFFFF;
319+
320+
// search for matching key OR evict by oldest timestmap
321+
BlobRec tmp;
322+
file.seek(0);
323+
while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) {
324+
if (memcmp(key, tmp.key, sizeof(tmp.key)) == 0) { // only match by 7 byte prefix
325+
found_pos = pos;
326+
break;
327+
}
328+
if (tmp.timestamp < min_timestamp) {
329+
min_timestamp = tmp.timestamp;
330+
found_pos = pos;
331+
}
332+
333+
pos += sizeof(tmp);
334+
}
335+
336+
memcpy(tmp.key, key, sizeof(tmp.key)); // just record 7 byte prefix of key
337+
memcpy(tmp.data, src_buf, len);
338+
tmp.len = len;
339+
tmp.timestamp = _clock->getCurrentTime();
340+
341+
file.seek(found_pos);
342+
file.write((uint8_t *) &tmp, sizeof(tmp));
343+
344+
file.close();
345+
return true;
346+
}
347+
return false; // error
348+
}
349+
#else
350+
uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) {
351+
char path[64];
352+
char fname[18];
353+
354+
if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix)
355+
mesh::Utils::toHex(fname, key, key_len);
356+
sprintf(path, "/bl/%s", fname);
357+
358+
if (_fs->exists(path)) {
359+
#if defined(RP2040_PLATFORM)
360+
File f = _fs->open(path, "r");
361+
#else
362+
File f = _fs->open(path);
363+
#endif
364+
if (f) {
365+
int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!!
366+
f.close();
367+
return len;
368+
}
369+
}
370+
return 0; // not found
371+
}
372+
373+
bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) {
374+
char path[64];
375+
char fname[18];
376+
377+
if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix)
378+
mesh::Utils::toHex(fname, key, key_len);
379+
sprintf(path, "/bl/%s", fname);
380+
381+
File f = openWrite(_fs, path);
382+
if (f) {
383+
int n = f.write(src_buf, len);
384+
f.close();
385+
if (n == len) return true; // success!
386+
387+
_fs->remove(path); // blob was only partially written!
388+
}
389+
return false; // error
390+
}
391+
#endif

0 commit comments

Comments
 (0)