-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuffered_sd.cpp
More file actions
368 lines (293 loc) · 9.96 KB
/
buffered_sd.cpp
File metadata and controls
368 lines (293 loc) · 9.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
#include "FS.h"
#include <SD.h>
#include <buffered_sd.h>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <sys/types.h>
BufferedSD::BufferedSD(
SPIClass &spi_bus, uint8_t CS, const char *base_filepath, const char *extension,
size_t buffer_size
)
: _spi(&spi_bus), _CS_pin(CS), _buffer_size(buffer_size), _base_path(base_filepath),
_extension(extension), _file_number(-1) {
// add the () behind to zero-initialize the buffer
_write_buffer = new uint8_t[buffer_size]();
configASSERT(_write_buffer && "Failed to initialize write buffer");
_buffer_idx = 0;
}
BufferedSD::~BufferedSD() {
flush_buffer();
delete[] _write_buffer;
}
bool BufferedSD::begin() {
for (int i = 0; i < NUM_TRIES_TO_OPEN; ++i) {
if (SD.begin(_CS_pin, *_spi, 1200000)) {
break;
} else if (i == NUM_TRIES_TO_OPEN - 1) {
return false;
}
delay(100);
}
create_new_file();
return true;
}
int BufferedSD::write(const char *data) { return write(data, strlen(data)); }
int BufferedSD::write(const char *data, size_t length) {
// Handle data larger than buffer
if (length > _buffer_size) {
flush_buffer();
// Write directly to file for oversized data
return write_immediate(data, length);
}
if (_buffer_idx + length > _buffer_size) {
flush_buffer();
}
memcpy(_write_buffer + _buffer_idx, data, length);
_buffer_idx += length;
return length;
}
int BufferedSD::write_immediate(const char *data) { return write_immediate(data, strlen(data)); }
int BufferedSD::write_immediate(const char *data, size_t length) {
flush_buffer();
File f = SD.open(_filepath, FILE_APPEND);
if (!f)
return -1;
size_t written_length = 0;
written_length += f.write(reinterpret_cast<const uint8_t *>(data), length);
f.close();
return written_length;
}
void BufferedSD::print_contents() {
File f = SD.open(_filepath, FILE_READ);
if (!f) {
Serial.print("The file cannot be opened");
return;
}
while (f.available()) {
Serial.print((char)f.read());
}
f.close();
}
void BufferedSD::find_first_available_file(const char *planned_filepath, const char *extension) {
char temp_filepath[FILEPATH_NAME_MAX_LENGTH];
while (true) {
// FAT32 (file format used by XTSD has a limit of 256 files per directory)
for (int i = 0; i < 256; ++i) {
snprintf(
temp_filepath, FILEPATH_NAME_MAX_LENGTH, "/%s-%d%s", planned_filepath, i, extension
);
if (!SD.exists(temp_filepath)) {
strncpy(_filepath, temp_filepath, FILEPATH_NAME_MAX_LENGTH);
_file_number = i;
// null terminate the resulting string just in case
_filepath[FILEPATH_NAME_MAX_LENGTH - 1] = '\0';
return;
}
}
}
}
sd_card_update BufferedSD::get_file_update() {
File file = SD.open(_filepath, "r");
if (!file) {
return {0, 0};
}
uint32_t file_size = file.size();
// skip past the final \n character
uint32_t offset = file_size - 3;
file.seek(offset, SeekSet);
char curr = file.peek();
// find the second last \n character
// this is only used by the radio boards, whose file sizes are not as important
// we add a hard cap to prevent them from erroring out when the file is empty
uint32_t seek_limit = file_size < 4096 ? file_size : 4096;
for (int i = 0; i < seek_limit; i++) {
if (curr == '\n') {
break;
}
file.seek(offset, SeekSet);
offset--;
curr = file.peek();
}
if (curr != '\n') {
return {0, 0};
}
// biggest value is 4,294,967,295
// go back and read the last timestamp present on the file
char number[11];
int i = 0;
while (file.available()) {
number[i] = file.read();
if (number[i] == ',') {
break;
}
i++;
}
number[i++] = '\0';
char *endptr;
return {file_size, strtoul(number, &endptr, 10)};
}
sd_card_update BufferedSD::get_file_update_bin(
sd_card_header_footer_info hf_info[], uint8_t hf_into_count, uint8_t struct_buffer[]
) {
File file = SD.open(_filepath, "r");
if (!file) {
return {0, 0};
}
uint32_t file_size = file.size();
// other odd behavior
// if the chunk size is anything but 1, I have to little endian it
//
// have not entirely figured out why a chunk size of < 4 does not work though
constexpr uint32_t backtrack_chunk_size = 512;
uint32_t offset = file_size;
uint8_t backtrack_buffer[backtrack_chunk_size]{};
// track the matching struct
int found_idx = -1;
// track the position within the buffer that the file was found at
int64_t header_found_position = -1;
// search for the file header
uint8_t candidate_header[sizeof(uint32_t)]{};
while (true) {
while (found_idx == -1 && offset != 0 && header_found_position == -1) {
uint32_t backtrack_amt = min(offset, backtrack_chunk_size);
offset -= backtrack_amt;
file.seek(offset, SeekSet);
for (uint32_t i = 0; i < backtrack_amt; i++) {
backtrack_buffer[i] = file.read();
}
// try to seek in the buffer for the header
int buffer_idx = 0;
for (int i = 0; i < backtrack_amt; i++) {
// we write to the back of the array, even though we are seeking forward
// in the file, because everything is in little endian
candidate_header[3] = backtrack_buffer[i];
uint32_t potential_header = 0;
memcpy(&potential_header, candidate_header, sizeof(uint32_t));
if (potential_header != 0) {
for (int k = 0; k < hf_into_count; k++) {
if (potential_header == hf_info[k].header) {
// we subtract 3 here, since we are little-endianing the number
// when we've found the correct number, we would be 3 past the
// start of the struct
header_found_position = offset + i - 3;
found_idx = k;
break;
}
}
}
// if not found, slide the window one byte back
// this byte is kept thorughout the parent while loop
if (found_idx != -1 && header_found_position != -1) {
break;
}
candidate_header[0] = candidate_header[1];
candidate_header[1] = candidate_header[2];
candidate_header[2] = candidate_header[3];
}
}
if (found_idx == -1 || header_found_position == -1) {
return {0, 0};
}
file.seek(header_found_position, SeekSet);
for (int i = 0; i < hf_info[found_idx].struct_size; i++) {
struct_buffer[i] = file.read();
}
int64_t timestamp{};
memcpy(×tamp, (struct_buffer + hf_info[found_idx].timestamp_offset), sizeof(int64_t));
// the timestamp is an int64_t but most of our code was previously written assuming it was
// uint32_t this is something that could definitely be improved in the future though
return {file_size, timestamp};
}
}
void BufferedSD::get_file_name(char *buf) { strncpy(buf, _filepath, strlen(_filepath) + 1); }
bool BufferedSD::delete_all_files() { return delete_all_files_with_exception(nullptr); }
bool BufferedSD::delete_all_files_with_exception(const char *exception) {
File root = SD.open("/");
if (!root) {
return false;
}
File file = root.openNextFile();
while (file) {
char name[FILEPATH_NAME_MAX_LENGTH];
snprintf(name, FILEPATH_NAME_MAX_LENGTH, "/%s", file.name());
if (exception && strcmp(name, exception) == 0) {
continue;
}
SD.remove(name);
file = root.openNextFile();
// add timeout so there is no watchdog reset
delay(1);
}
return true;
}
int BufferedSD::count_top_level_files() {
int counter = 0;
File root = SD.open("/");
if (!root) {
return -1;
}
if (!root.isDirectory()) {
return -2;
}
File file = root.openNextFile();
while (file) {
counter++;
file = root.openNextFile();
}
return counter;
}
bool BufferedSD::create_new_file() {
find_first_available_file(_base_path, _extension);
File f = SD.open(_filepath, FILE_WRITE);
if (!f) {
return false;
}
f.close();
return true;
}
unsigned long BufferedSD::get_free_space() {
return (SD.totalBytes() - SD.usedBytes()) / (1024 * 1024);
}
int BufferedSD::update_config(const char tx_buf[], size_t config_length) {
File file_handle = {};
if (!SD.exists(SD_CONFIG_FILEPATH)) {
file_handle = SD.open(SD_CONFIG_FILEPATH, FILE_WRITE);
} else {
file_handle = SD.open(SD_CONFIG_FILEPATH, FILE_APPEND);
}
if (!file_handle) {
return -1;
}
int wrote = file_handle.write(reinterpret_cast<const uint8_t *>(tx_buf), config_length);
file_handle.close();
return wrote;
}
int BufferedSD::read_config(char rx_buf[], size_t buf_len) {
File file_handle = {};
if (!SD.exists(SD_CONFIG_FILEPATH)) {
return -1;
}
file_handle = SD.open(SD_CONFIG_FILEPATH, FILE_READ);
if (!file_handle)
return -1;
int i = 0;
for (; i < buf_len; i++) {
if (file_handle.available()) {
rx_buf[i] = file_handle.read();
} else {
break;
}
}
rx_buf[i] = '\0';
return i;
}
void BufferedSD::clear_config_file() {
if (!SD.exists(SD_CONFIG_FILEPATH)) {
return;
} else {
SD.remove(SD_CONFIG_FILEPATH);
}
delay(10);
SD.open(SD_CONFIG_FILEPATH, FILE_WRITE);
}