ENTS-tock
Environmental NeTworked Sensor firmware on Tock OS
Loading...
Searching...
No Matches
microsd.cpp
Go to the documentation of this file.
1#include "modules/microsd.hpp"
2
3#include <ArduinoLog.h>
4#include <SD.h>
5#include <nanopb/pb_decode.h>
6
7static const uint8_t chipSelect_pin = 7;
8static UserConfiguration uc = UserConfiguration_init_zero;
9static char dataFileFilename[256];
10
11static void printCardInfo(void);
12static void printFileInfo(File f);
13static void printFileContents(File f);
14static bool microsd_detect_card(void);
15
17 // set module type
18 type = Esp32Command_microsd_command_tag;
19}
20
22
23void ModuleMicroSD::OnReceive(const Esp32Command& cmd) {
24 Log.traceln("ModuleMicroSD::OnReceive");
25
26 // check if microSD command
27 if (cmd.which_command != Esp32Command_microsd_command_tag) {
28 return;
29 }
30
31 Log.traceln("MicroSDCommand type: %d", cmd.command.microsd_command.type);
32
33 // switch for command types
34 switch (cmd.command.microsd_command.type) {
35 case MicroSDCommand_Type_SAVE:
36 Log.traceln("Calling SAVE");
37 Save(cmd);
38 break;
39 case MicroSDCommand_Type_USERCONFIG:
40 Log.traceln("Calling USERCONFIG");
41 UserConfig(cmd);
42 break;
43 case MicroSDCommand_Type_LOG:
44 Log.traceln("Calling LOG");
45 WriteLog(cmd);
46 break;
47 default:
48 Log.warningln("MicroSD command type not found!");
49 break;
50 }
51}
52
54 Log.traceln("ModuleMicroSD::OnRequest");
55 memcpy(buffer, request_buffer, request_buffer_len);
56 return request_buffer_len;
57}
58
59void ModuleMicroSD::Save(const Esp32Command& cmd) {
60 static uint32_t last_ts = 0;
61 // init return microSD command
62 MicroSDCommand microsd_cmd = MicroSDCommand_init_zero;
63 microsd_cmd.type = MicroSDCommand_Type_SAVE;
64
65 Log.traceln("ModuleMicroSD::Save");
66
67 Log.verbose("microsd_command.filename: %s\r\n",
68 cmd.command.microsd_command.filename);
69 Log.verbose("microsd_command.type: %u\r\n", cmd.command.microsd_command.type);
70 if (cmd.command.microsd_command.which_data == MicroSDCommand_meas_tag) {
71 Log.verbose("microsd_command.which_data: %u (%s)\r\n",
72 cmd.command.microsd_command.which_data,
73 "MicroSDCommand_meas_tag");
74 } else if (cmd.command.microsd_command.which_data == MicroSDCommand_uc_tag) {
75 Log.verbose("microsd_command.which_data: %u (%s)\r\n",
76 cmd.command.microsd_command.which_data,
77 "MicroSDCommand_uc_tag");
78 } else {
79 Log.verbose("microsd_command.which_data: %u (%s)\r\n",
80 cmd.command.microsd_command.which_data, "UNKNOWN");
81 }
82
83 File dataFile;
84
85 // The ESP32-C3 IO7 pins:
86 // IO4: CLK / SCK / SCLK
87 // IO5: SDO / DO / MISO
88 // IO6: SDI / DI / MOSI
89 // IO7: CS / SS
90 // IO10: CD "card detect" (for insertion detection).
91 // Note: Pin is floating when no card is inserted, grounded when a card is
92 // inserted.
93 if (!microsd_detect_card()) {
94 Log.error("Aborting save to micro SD card.\r\n");
95 microsd_cmd.rc = MicroSDCommand_ReturnCode_ERROR_MICROSD_NOT_INSERTED;
96 } else {
97 // Note: SD.begin(chipSelect) assumes the default SCLK, MISO, MOSI pins.
98 // For non-default pin assignments, call SPI.begin(SCLK, MISO, MOSI, CS)
99 // prior to SD.begin(CS).
100 SD.end();
101 if (!SD.begin(chipSelect_pin)) {
102 Log.error(
103 "Failed to begin, make sure that a FAT32 formatted SD card is "
104 "inserted. Aborting save to micro SD card.\r\n");
105 microsd_cmd.rc =
106 MicroSDCommand_ReturnCode_ERROR_FILE_SYSTEM_NOT_MOUNTABLE;
107 } else {
108 { // Meas log print
109 if (cmd.command.microsd_command.data.meas.has_meta) {
110 Log.verbose("meas.meta.cell_id: %u\r\n",
111 cmd.command.microsd_command.data.meas.meta.cell_id);
112 Log.verbose("meas.meta.logger_id: %u\r\n",
113 cmd.command.microsd_command.data.meas.meta.logger_id);
114 Log.verbose("meas.meta.ts: %u\r\n",
115 cmd.command.microsd_command.data.meas.meta.ts);
116 }
117 Log.verbose("meas.which_measurement: %u\r\n",
118 cmd.command.microsd_command.data.meas.which_measurement);
119 switch (cmd.command.microsd_command.data.meas.which_measurement) {
120 case Measurement_power_tag:
121 Log.verbose(
122 ",%lf,%lf",
123 cmd.command.microsd_command.data.meas.measurement.power.voltage,
124 cmd.command.microsd_command.data.meas.measurement.power
125 .current);
126 break;
127 case Measurement_teros12_tag:
128 Log.verbose(
129 ",%lf,%u,%lf",
130 cmd.command.microsd_command.data.meas.measurement.teros12
131 .vwc_adj,
132 cmd.command.microsd_command.data.meas.measurement.teros12.ec,
133 cmd.command.microsd_command.data.meas.measurement.teros12.temp);
134 break;
135 case Measurement_phytos31_tag:
136 Log.verbose(",%lf,%lf",
137 cmd.command.microsd_command.data.meas.measurement
138 .phytos31.voltage,
139 cmd.command.microsd_command.data.meas.measurement
140 .phytos31.leaf_wetness);
141 break;
142 case Measurement_bme280_tag:
143 Log.verbose(",%u,%d,%u",
144 cmd.command.microsd_command.data.meas.measurement.bme280
145 .pressure,
146 cmd.command.microsd_command.data.meas.measurement.bme280
147 .temperature,
148 cmd.command.microsd_command.data.meas.measurement.bme280
149 .humidity);
150 break;
151 case Measurement_teros21_tag:
152 Log.verbose(
153 ",%lf,%lf",
154 cmd.command.microsd_command.data.meas.measurement.teros21
155 .matric_pot,
156 cmd.command.microsd_command.data.meas.measurement.teros21.temp);
157 break;
158 case Measurement_sen0308_tag:
159 Log.verbose(",%lf,%lf",
160 cmd.command.microsd_command.data.meas.measurement
161 .sen0308.voltage,
162 cmd.command.microsd_command.data.meas.measurement
163 .sen0308.humidity);
164 break;
165 case Measurement_sen0257_tag:
166 Log.verbose(",%lf,%lf",
167 cmd.command.microsd_command.data.meas.measurement
168 .sen0257.voltage,
169 cmd.command.microsd_command.data.meas.measurement
170 .sen0257.pressure);
171 break;
172 case Measurement_yfs210c_tag:
173 Log.verbose(
174 ",%lf",
175 cmd.command.microsd_command.data.meas.measurement.yfs210c.flow);
176 break;
177 case Measurement_pcap02_tag:
178 Log.verbose(",%lf", cmd.command.microsd_command.data.meas
179 .measurement.pcap02.capacitance);
180 break;
181 case Measurement_meta_tag:
182 Log.verbose(
183 "meta tag: %d\r\n",
184 cmd.command.microsd_command.data.meas.which_measurement);
185 break;
186 default:
187 Log.verbose(
188 "Unrecognized measurement type: %d\r\n",
189 cmd.command.microsd_command.data.meas.which_measurement);
190 microsd_cmd.rc = MicroSDCommand_ReturnCode_ERROR_GENERAL;
191 break;
192 }
193 }
194 dataFile = SD.open(dataFileFilename, FILE_APPEND);
195 if (!dataFile) {
196 Log.error("Failed to open '%s' with '%s'\r\n", dataFileFilename,
197 FILE_APPEND);
198 microsd_cmd.rc = MicroSDCommand_ReturnCode_ERROR_FILE_NOT_OPENED;
199 strncpy(microsd_cmd.filename, dataFileFilename,
200 sizeof(microsd_cmd.filename));
201 } else {
202 Log.trace("Successfully opened '%s' with '%s'\r\n", dataFileFilename,
203 FILE_APPEND);
204
205 // If the new (current) measurement is on a new timestamp, create a
206 // new line
207 if (cmd.command.microsd_command.data.meas.meta.ts != last_ts) {
208 dataFile.printf("\r\n%u",
209 cmd.command.microsd_command.data.meas.meta.ts);
210 }
211
212 // Note: Assume that the measurements are sent in ascending order of
213 // timestamp and _tag (such that the transmission order matches the CSV
214 // row 1 header order).
215 switch (cmd.command.microsd_command.data.meas.which_measurement) {
216 case Measurement_power_tag:
217 dataFile.printf(
218 ",%lf,%lf",
219 cmd.command.microsd_command.data.meas.measurement.power.voltage,
220 cmd.command.microsd_command.data.meas.measurement.power
221 .current);
222 break;
223 case Measurement_teros12_tag:
224 dataFile.printf(
225 ",%lf,%u,%lf",
226 cmd.command.microsd_command.data.meas.measurement.teros12
227 .vwc_adj,
228 cmd.command.microsd_command.data.meas.measurement.teros12.ec,
229 cmd.command.microsd_command.data.meas.measurement.teros12.temp);
230 break;
231 case Measurement_phytos31_tag:
232 dataFile.printf(",%lf,%lf",
233 cmd.command.microsd_command.data.meas.measurement
234 .phytos31.voltage,
235 cmd.command.microsd_command.data.meas.measurement
236 .phytos31.leaf_wetness);
237 break;
238 case Measurement_bme280_tag:
239 dataFile.printf(",%u,%d,%u",
240 cmd.command.microsd_command.data.meas.measurement
241 .bme280.pressure,
242 cmd.command.microsd_command.data.meas.measurement
243 .bme280.temperature,
244 cmd.command.microsd_command.data.meas.measurement
245 .bme280.humidity);
246 break;
247 case Measurement_teros21_tag:
248 dataFile.printf(
249 ",%lf,%lf",
250 cmd.command.microsd_command.data.meas.measurement.teros21
251 .matric_pot,
252 cmd.command.microsd_command.data.meas.measurement.teros21.temp);
253 break;
254 case Measurement_sen0308_tag:
255 dataFile.printf(",%lf,%lf",
256 cmd.command.microsd_command.data.meas.measurement
257 .sen0308.voltage,
258 cmd.command.microsd_command.data.meas.measurement
259 .sen0308.humidity);
260 break;
261 case Measurement_sen0257_tag:
262 dataFile.printf(",%lf,%lf",
263 cmd.command.microsd_command.data.meas.measurement
264 .sen0257.voltage,
265 cmd.command.microsd_command.data.meas.measurement
266 .sen0257.pressure);
267 break;
268 case Measurement_yfs210c_tag:
269 dataFile.printf(
270 ",%lf",
271 cmd.command.microsd_command.data.meas.measurement.yfs210c.flow);
272 break;
273 case Measurement_pcap02_tag:
274 dataFile.printf(",%lf", cmd.command.microsd_command.data.meas
275 .measurement.pcap02.capacitance);
276 break;
277 default:
278 Log.error("Unrecognized measurement type: %d\r\n",
279 cmd.command.microsd_command.data.meas.which_measurement);
280 microsd_cmd.rc = MicroSDCommand_ReturnCode_ERROR_GENERAL;
281 break;
282 }
283
284 dataFile.close();
285 Log.trace("Wrote to and closed '%s'\r\n", dataFileFilename);
286
287 last_ts = cmd.command.microsd_command.data.meas.meta.ts;
288 }
289 }
290 }
291 // encode command in buffer
292 this->request_buffer_len = EncodeMicroSDCommand(&microsd_cmd, request_buffer,
293 sizeof(request_buffer));
294}
295
296void ModuleMicroSD::UserConfig(const Esp32Command& cmd) {
297 // init return microSD command
298 MicroSDCommand microsd_cmd = MicroSDCommand_init_zero;
299 microsd_cmd.type = MicroSDCommand_Type_USERCONFIG;
300
301 Log.traceln("ModuleMicroSD::UserConfig");
302
303 memcpy(&uc, &cmd.command.microsd_command.data.uc, sizeof(UserConfiguration));
304
305 // Check for SD card
306 if (!microsd_detect_card()) {
307 Log.error("Aborting save to micro SD card.\r\n");
308 microsd_cmd.rc = MicroSDCommand_ReturnCode_ERROR_MICROSD_NOT_INSERTED;
309 } else {
310 // Note: SD.begin(chipSelect) assumes the default SCLK, MISO, MOSI pins.
311 // For non-default pin assignments, call SPI.begin(SCLK, MISO, MOSI, CS)
312 // prior to SD.begin(CS).
313 SD.end();
314 if (!SD.begin(chipSelect_pin)) {
315 Log.error(
316 "Failed to begin, make sure that a FAT32 formatted SD card is "
317 "inserted. Aborting save to micro SD card.\r\n");
318 microsd_cmd.rc =
319 MicroSDCommand_ReturnCode_ERROR_FILE_SYSTEM_NOT_MOUNTABLE;
320 } else {
321 // (Over)Write a file for the userConfig
322 char userConfigFileFilename[sizeof(dataFileFilename)] = {0};
323 strncpy(userConfigFileFilename, cmd.command.microsd_command.filename,
324 sizeof(userConfigFileFilename));
325 strncat(userConfigFileFilename, ".userconfig", sizeof(".userconfig"));
326 File userConfigFile = SD.open(userConfigFileFilename, FILE_WRITE);
327 if (!userConfigFile) {
328 Log.error("Failed to open '%s' with '%s'\r\n", userConfigFileFilename,
329 FILE_WRITE);
330 microsd_cmd.rc = MicroSDCommand_ReturnCode_ERROR_FILE_NOT_OPENED;
331 strncpy(microsd_cmd.filename, userConfigFileFilename,
332 sizeof(microsd_cmd.filename));
333 } else {
334 // Write the userconfig to the file
335 userConfigFile.printf("logger_id=%d\r\n", uc.logger_id);
336 userConfigFile.printf("cell_id=%d\r\n", uc.cell_id);
337 char upload_method_s[][18] = {"Uploadmethod_LoRa", "Uploadmethod_WiFi",
338 "UNKNOWN"};
339 userConfigFile.printf("Upload_method=%d (%s)\r\n", uc.Upload_method,
340 upload_method_s[uc.Upload_method]);
341
342 userConfigFile.printf("Upload_interval=%d\r\n", uc.Upload_interval);
343 userConfigFile.printf("enabled_sensors_count=%d\r\n",
344 uc.enabled_sensors_count);
345
346 char enabled_sensors_s[][9] = {
347 "Voltage", "Current", "Teros12", "Teros21", "BME280",
348 "Phytos31", "SEN0308", "SEN0257", "YFS210C", "PCAP02"};
349 for (int i = 0; i < uc.enabled_sensors_count; i++) {
350 userConfigFile.printf("enabled_sensors[%d]=%d (%s)\r\n", i,
351 uc.enabled_sensors[i], enabled_sensors_s[i]);
352 }
353
354 userConfigFile.printf("Voltage_Slope=%lf\r\n", uc.Voltage_Slope);
355 userConfigFile.printf("Voltage_Offset=%lf\r\n", uc.Voltage_Offset);
356 userConfigFile.printf("Current_Slope=%lf\r\n", uc.Current_Slope);
357 userConfigFile.printf("Current_Offset=%lf\r\n", uc.Current_Offset);
358 userConfigFile.printf("WiFi_SSID=%s\r\n", uc.WiFi_SSID);
359 userConfigFile.printf("WiFi_Password=%s\r\n", uc.WiFi_Password);
360 userConfigFile.printf("API_Endpoint_URL=%s\r\n", uc.API_Endpoint_URL);
361 userConfigFile.printf("API_Endpoint_Port=%d\r\n", uc.API_Endpoint_Port);
362
363 userConfigFile.close();
364
365 // Create a new file for the CSV
366 strncpy(dataFileFilename, cmd.command.microsd_command.filename,
367 sizeof(dataFileFilename));
368
369 File dataFile = SD.open(dataFileFilename, FILE_WRITE);
370 if (!dataFile) {
371 Log.error("Failed to open '%s' with '%s'\r\n", dataFileFilename,
372 FILE_WRITE);
373 microsd_cmd.rc = MicroSDCommand_ReturnCode_ERROR_FILE_NOT_OPENED;
374 strncpy(microsd_cmd.filename, dataFileFilename,
375 sizeof(microsd_cmd.filename));
376 } else {
377 // Add the headers
378 dataFile.printf("timestamp");
379 for (int i = 0; i < uc.enabled_sensors_count; i++) {
380 switch (uc.enabled_sensors[i]) {
381 case EnabledSensor_Voltage:
382 case EnabledSensor_Current:
383 dataFile.printf(",voltage,current");
384 break;
385 case EnabledSensor_Teros12:
386 dataFile.printf(",vwc_Teros12,ec_Teros12,temp_Teros12");
387 break;
388 case EnabledSensor_Teros21:
389 dataFile.printf(",matricpotential_Teros21,temp_Teros21");
390 break;
391 case EnabledSensor_BME280:
392 dataFile.printf(
393 ",pressure_BME280,temperature_BME280,humidity_BME280");
394 break;
395 case EnabledSensor_Phytos31:
396 dataFile.printf(",voltage_Phytos31,leaf_wetness_Phytos31");
397 break;
398 case EnabledSensor_SEN0308:
399 dataFile.printf(",voltage_SEN0308,humidity_SEN0308");
400 break;
401 case EnabledSensor_SEN0257:
402 dataFile.printf(",voltage_SEN0257,pressure_SEN0257");
403 break;
404 case EnabledSensor_YFS210C:
405 dataFile.printf(",flow_YFS210C");
406 break;
407 case EnabledSensor_PCAP02:
408 dataFile.printf(",capacitance_PCAP02");
409 break;
410 default:
411 dataFile.printf(",ERROR Unknown sensor type");
412 microsd_cmd.rc = MicroSDCommand_ReturnCode_ERROR_GENERAL;
413 break;
414 }
415 }
416 dataFile.close();
417 }
418 }
419 }
420 }
421
422 // encode command in buffer
423 this->request_buffer_len = EncodeMicroSDCommand(&microsd_cmd, request_buffer,
424 sizeof(request_buffer));
425}
426
427void ModuleMicroSD::WriteLog(const Esp32Command& cmd) {
428 // init return microSD command
429 MicroSDCommand microsd_cmd = MicroSDCommand_init_zero;
430 microsd_cmd.type = MicroSDCommand_Type_LOG;
431
432 Log.traceln("ModuleMicroSD::WriteLog");
433
434 // Base filename for log
435 const char* base = cmd.command.microsd_command.filename;
436
437 if (!microsd_detect_card()) {
438 Log.error("Aborting log to micro SD card.\r\n");
439 microsd_cmd.rc = MicroSDCommand_ReturnCode_ERROR_MICROSD_NOT_INSERTED;
440 } else {
441 SD.end();
442 if (!SD.begin(chipSelect_pin)) {
443 Log.error(
444 "Failed to begin, make sure that a FAT32 formatted SD card is "
445 "inserted. Aborting log to micro SD card.\r\n");
446 microsd_cmd.rc =
447 MicroSDCommand_ReturnCode_ERROR_FILE_SYSTEM_NOT_MOUNTABLE;
448 } else {
449 Log.verbose("microsd_command.log: %s\r\n",
450 cmd.command.microsd_command.data.log);
451
452 // boot index
453 static char logFileFilename[32] = "";
454
455 // Find the next boot index for filename
456 if (strlen(logFileFilename) == 0) {
457 int start_idx = 0;
458
459 // find non-existant bootnumber
460 while (1) {
461 snprintf(logFileFilename, sizeof(logFileFilename), "/%s.%d.txt", base,
462 start_idx);
463
464 // check if sd card exsits
465 if (!SD.exists(logFileFilename)) {
466 break;
467 } else {
468 Log.verbose("File name \"%s\" already exsits! Trying next index.");
469 }
470
471 // try next index
472 start_idx++;
473 }
474 }
475
476 // Open file, preventing overwrites
477 File logFile = SD.open(logFileFilename, FILE_APPEND);
478
479 if (!logFile) {
480 Log.error("Failed to open '%s' with '%s'\r\n", logFileFilename,
481 FILE_APPEND);
482
483 microsd_cmd.rc = MicroSDCommand_ReturnCode_ERROR_FILE_NOT_OPENED;
484 strncpy(microsd_cmd.filename, logFileFilename,
485 sizeof(microsd_cmd.filename));
486 } else {
487 // save and close file
488 logFile.println(cmd.command.microsd_command.data.log);
489 logFile.close();
490
491 Log.trace("Wrote to and closed '%s'\r\n", logFileFilename);
492 }
493 }
494 }
495
496 // encode command in buffer
497 this->request_buffer_len = EncodeMicroSDCommand(&microsd_cmd, request_buffer,
498 sizeof(request_buffer));
499}
500
501static void printCardInfo(void) {
502 Log.verbose("\r\n-----Card info START-----\r\n");
503 Log.verbose("sectors, sector size, total size: %zd * %zd = %llu\r\n",
504 SD.numSectors(), SD.sectorSize(), SD.cardSize());
505 String sdcard_type_t_strings[] = {"CARD_NONE", "CARD_MMC", "CARD_SD",
506 "CARD_SDHC", "CARD_UNKNOWN"};
507 Log.verbose("card type: (%d) %s\r\n", SD.cardType(),
508 sdcard_type_t_strings[SD.cardType()]);
509 Log.verbose("bytes used / total: %llu / %llu\r\n", SD.usedBytes(),
510 SD.totalBytes());
511 Log.verbose("-----Card info END-----\r\n\r\n");
512}
513
514static void printFileInfo(File f) {
515 Log.verbose("\r\n-----File info START-----\r\n");
516 Log.verbose("available: %d\r\n", f.available());
517 Log.verbose("timeout: %lu\r\n", f.getTimeout());
518 Log.verbose("name: %s\r\n", f.name());
519 Log.verbose("path: %s\r\n", f.path());
520 Log.verbose("position: %zd\r\n", f.position());
521 Log.verbose("size: %zd\r\n", f.size());
522 Log.verbose("-----File info END-----\r\n\r\n");
523}
524
525static void printFileContents(File f) {
526 Log.verbose("\r\n-----%s START-----\r\n", f.path());
527 char c = '\n';
528 uint32_t line = 0;
529 do {
530 if (c == '\n') {
531 Log.verbose("[%d]:\t", line++);
532 }
533 c = f.read();
534 Log.verbose("%c", c);
535 } while (f.available());
536 Log.verbose("\r\n-----%s END-----\r\n", f.path());
537}
538
539static bool microsd_detect_card(void) {
540 const uint8_t cardDetect_pin = 10;
541
542 pinMode(cardDetect_pin, INPUT_PULLUP);
543 int pinState = digitalRead(cardDetect_pin);
544 pinMode(cardDetect_pin, INPUT); // After checking the SD card, revert to
545 // the lowest power pin mode.
546
547 if (pinState == LOW) {
548 Log.trace("Card detected.\r\n");
549 return true;
550 } else {
551 Log.error("Card NOT detected.\r\n");
552 return false;
553 }
554}
~ModuleMicroSD(void)
Definition microsd.cpp:21
size_t OnRequest(uint8_t *buffer)
Definition microsd.cpp:53
ModuleMicroSD(void)
Definition microsd.cpp:16
void OnReceive(const Esp32Command &cmd)
Definition microsd.cpp:23
uint8_t buffer[32]
uint8_t i
File dataFile
size_t EncodeMicroSDCommand(const MicroSDCommand *microsd_cmd, uint8_t *buffer, size_t size)
Encodes a MicroSDCommand.
Definition transcoder.c:278
Module for accessing the microSD card.