|
1 /** |
|
2 * @file task_sdcard.c |
|
3 * @brief SD/MMC driver. This driver is for a slot for the user on |
|
4 * the front panel. It will detect an inserted card and then |
|
5 * mount it. But, it cannot detect the removal of a card!! |
|
6 * Also, brew logging is handled here and finished brewlogs |
|
7 * are copied to the SD card if it is mounted. |
|
8 * Recipes to import must go into the /sdcard/recipe folder |
|
9 * and have extension .xml and the contents be a beerxml file. |
|
10 */ |
|
11 |
|
12 |
|
13 #include "vfs_fat_internal.h" |
|
14 #include "driver/sdmmc_host.h" |
|
15 #include "driver/sdspi_host.h" |
|
16 #include "sdmmc_cmd.h" |
|
17 #include "diskio.h" |
|
18 |
|
19 #include "config.h" |
|
20 |
|
21 |
|
22 |
|
23 SDCARD_State * sdcard_state; |
|
24 JSON_log * json_log; |
|
25 EventGroupHandle_t xEventGroupSDcard; ///< SD card events. |
|
26 |
|
27 static const char *TAG = "task_sdcard"; |
|
28 static sdmmc_card_t* s_card = NULL; |
|
29 static uint8_t s_pdrv = 0; |
|
30 static char * s_base_path = NULL; |
|
31 |
|
32 BYTE pdrv = 0xFF; |
|
33 |
|
34 |
|
35 #define SDCARD_HOST_SLOT VSPI_HOST ///< HSPI_HOST is used by the TFT |
|
36 #define SDCARD_PIN_NUM_MISO 2 |
|
37 #define SDCARD_PIN_NUM_MOSI 15 |
|
38 #define SDCARD_PIN_NUM_CLK 14 |
|
39 #define SDCARD_PIN_NUM_CS 13 |
|
40 #define SDCARD_DMA_CHANNEL 2 ///< Channel 1 is used by the TFT |
|
41 |
|
42 |
|
43 const int TASK_SDCARD_LOG_CLEAN = BIT0; ///< Clean spiffs logfile directory |
|
44 const int TASK_SDCARD_LOG_CLOSE = BIT1; ///< Close the logfile. |
|
45 |
|
46 extern time_t now; |
|
47 extern struct tm timeinfo; |
|
48 |
|
49 |
|
50 |
|
51 void log_begin(time_t t) |
|
52 { |
|
53 struct tm timeinfo; |
|
54 |
|
55 /* |
|
56 * If there is an open logfile from an crashed brew, open that one. |
|
57 */ |
|
58 if (! strlen(sdcard_state->logfile) && strlen(runtime.Logfile)) { |
|
59 sprintf(sdcard_state->logfile, "%s", runtime.Logfile); |
|
60 ESP_LOGI(TAG, "Resumed %s", sdcard_state->logfile); |
|
61 } else { |
|
62 localtime_r(&t, &timeinfo); |
|
63 if (timeinfo.tm_year > (2016 - 1900)) { |
|
64 // Valid time, construct the filename. |
|
65 strftime(sdcard_state->logfile, sizeof(sdcard_state->logfile), "br%y%m%d%H%M", &timeinfo); |
|
66 } else { |
|
67 sprintf(sdcard_state->logfile, "brewlog"); |
|
68 } |
|
69 ESP_LOGI(TAG, "Create %s", sdcard_state->logfile); |
|
70 sprintf(runtime.Logfile, "%s", sdcard_state->logfile); |
|
71 } |
|
72 } |
|
73 |
|
74 |
|
75 |
|
76 void log_close(void) |
|
77 { |
|
78 xEventGroupSetBits(xEventGroupSDcard, TASK_SDCARD_LOG_CLOSE); |
|
79 } |
|
80 |
|
81 |
|
82 |
|
83 void log_clean(void) |
|
84 { |
|
85 xEventGroupSetBits(xEventGroupSDcard, TASK_SDCARD_LOG_CLEAN); |
|
86 } |
|
87 |
|
88 |
|
89 |
|
90 void log_json(void) |
|
91 { |
|
92 char filename[32], strftime_buf[64]; |
|
93 FILE *f; |
|
94 bool addcomma = true; |
|
95 |
|
96 if (strlen(sdcard_state->logfile)) { |
|
97 sprintf(filename, "/spiffs/log/%s.json", sdcard_state->logfile); |
|
98 f = fopen(filename, "r"); |
|
99 if (f == NULL) { |
|
100 // Create the file and add the header |
|
101 f = fopen(filename, "a"); |
|
102 if (f) { |
|
103 strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); |
|
104 fprintf(f, "{\n"); |
|
105 fprintf(f, " \"brew\": [{\n"); |
|
106 fprintf(f, " \"Recipe\":\"%s\",\n", recipe.Name); |
|
107 fprintf(f, " \"Date\":\"%s\",\n", strftime_buf); |
|
108 fprintf(f, " \"brewdata\":[\n"); |
|
109 addcomma = false; |
|
110 fclose(f); |
|
111 } else { |
|
112 ESP_LOGE(TAG, "Failed to create %s error %d", filename, errno); |
|
113 return; |
|
114 } |
|
115 } else { |
|
116 fclose(f); // Was open for reading. |
|
117 } |
|
118 |
|
119 f = fopen(filename, "a"); |
|
120 if (f) { |
|
121 if (addcomma) { |
|
122 fprintf(f, ",\n"); |
|
123 } |
|
124 addcomma = true; |
|
125 fprintf(f, " {\"MLT_sp\":\"%.3f\",\"MLT_pv\":\"%.3f\",\"MLT_pwm\":\"%d\",\"MLT_tr\":\"%d\",\"Pump\":\"%d\",", |
|
126 json_log->mlt_sp, json_log->mlt_pv, json_log->mlt_power, json_log->mlt_tempreached, json_log->pump_run); |
|
127 if (json_log->hlt_sp > 0.0) { |
|
128 fprintf(f, "\"HLT_sp\":\"%.3f\",\"HLT_pv\":\"%.3f\",\"HLT_pwm\":\"%d\",", json_log->hlt_sp, json_log->hlt_pv, json_log->hlt_power); |
|
129 } |
|
130 fprintf(f, "\"Label\":\"%s\"}", json_log->time); |
|
131 printf("{\"MLT_sp\":\"%.3f\",\"MLT_pv\":\"%.3f\",\"MLT_pwm\":\"%d\",\"MLT_tr\":\"%d\",\"Pump\":\"%d\",", |
|
132 json_log->mlt_sp, json_log->mlt_pv, json_log->mlt_power, json_log->mlt_tempreached, json_log->pump_run); |
|
133 if (json_log->hlt_sp > 0.0) { |
|
134 printf("\"HLT_sp\":\"%.3f\",\"HLT_pv\":\"%.3f\",\"HLT_pwm\":\"%d\",", json_log->hlt_sp, json_log->hlt_pv, json_log->hlt_power); |
|
135 } |
|
136 printf("\"Label\":\"%s\"}\n", json_log->time); |
|
137 fclose(f); |
|
138 } |
|
139 } |
|
140 } |
|
141 |
|
142 |
|
143 |
|
144 void log_annotation(int annotation_type, char *label) |
|
145 { |
|
146 char filename[32]; |
|
147 char bordercolor[9], color[9], pos[8]; |
|
148 FILE *f; |
|
149 bool addcomma = true; |
|
150 |
|
151 if (strlen(sdcard_state->logfile)) { |
|
152 sprintf(filename, "/spiffs/log/%s.anno", sdcard_state->logfile); |
|
153 |
|
154 switch (annotation_type) { |
|
155 case ANNOTATION_STAGE: snprintf(bordercolor, 8, "#8942f4"); |
|
156 snprintf(color, 8, "#00215b"); |
|
157 snprintf(pos, 7, "bottom"); |
|
158 break; |
|
159 |
|
160 case ANNOTATION_EVENT: snprintf(bordercolor, 8, "#42f445"); |
|
161 snprintf(color, 8, "#00215b"); |
|
162 snprintf(pos, 7, "top"); |
|
163 break; |
|
164 |
|
165 case ANNOTATION_SYSTEM: snprintf(bordercolor, 8, "black"); |
|
166 snprintf(color, 8, "red"); |
|
167 snprintf(pos, 7, "center"); |
|
168 break; |
|
169 } |
|
170 |
|
171 // Check if the file exists to see if we need to insert a comma. |
|
172 f = fopen(filename, "r"); |
|
173 if (f == NULL) { |
|
174 addcomma = false; |
|
175 } else { |
|
176 fclose(f); |
|
177 } |
|
178 |
|
179 f = fopen(filename, "a"); |
|
180 if (f) { |
|
181 if (addcomma) { |
|
182 fprintf(f, ",\n"); |
|
183 } |
|
184 addcomma = true; |
|
185 fprintf(f, "{\"type\":\"line\",\"mode\":\"vertical\",\"scaleID\":\"x-axis-0\",\"value\":\"%s\",\"borderColor\":\"%s\",\"borderWidth\":2,\"label\":{\"backgroundColor\":\"%s\",\"content\":\"%s\",\"position\":\"%s\",\"enabled\":true}}", json_log->time, bordercolor, color, label, pos); |
|
186 fclose(f); |
|
187 printf( "{\"type\":\"line\",\"mode\":\"vertical\",\"scaleID\":\"x-axis-0\",\"value\":\"%s\",\"borderColor\":\"%s\",\"borderWidth\":2,\"label\":{\"backgroundColor\":\"%s\",\"content\":\"%s\",\"position\":\"%s\",\"enabled\":true}}\n", json_log->time, bordercolor, color, label, pos); |
|
188 } |
|
189 } |
|
190 } |
|
191 |
|
192 |
|
193 |
|
194 /* |
|
195 * This is a local modified version of the esp_vfs_fat_sdmmc_mount() function in |
|
196 * the FreeRTOS components library. It is here so we can better handle errors |
|
197 * for our application. |
|
198 */ |
|
199 esp_err_t my_vfs_fat_sdmmc_init(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config) |
|
200 { |
|
201 if (s_card != NULL) { |
|
202 return ESP_ERR_INVALID_STATE; |
|
203 } |
|
204 |
|
205 // connect SDMMC driver to FATFS |
|
206 pdrv = 0xFF; |
|
207 if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == 0xFF) { |
|
208 ESP_LOGI(TAG, "the maximum count of volumes is already mounted"); |
|
209 return ESP_ERR_NO_MEM; |
|
210 } |
|
211 |
|
212 s_base_path = strdup(base_path); |
|
213 if (!s_base_path) { |
|
214 ESP_LOGI(TAG, "could not copy base_path"); |
|
215 return ESP_ERR_NO_MEM; |
|
216 } |
|
217 esp_err_t err = ESP_OK; |
|
218 s_card = malloc(sizeof(sdmmc_card_t)); |
|
219 if (s_card == NULL) { |
|
220 err = ESP_ERR_NO_MEM; |
|
221 goto fail; |
|
222 } |
|
223 |
|
224 err = (*host_config->init)(); |
|
225 if (err != ESP_OK) { |
|
226 ESP_LOGI(TAG, "host init returned rc=0x%x", err); |
|
227 goto fail; |
|
228 } |
|
229 |
|
230 // configure SD slot |
|
231 if (host_config->flags == SDMMC_HOST_FLAG_SPI) { |
|
232 err = sdspi_host_init_slot(host_config->slot, (const sdspi_slot_config_t*) slot_config); |
|
233 } else { |
|
234 err = sdmmc_host_init_slot(host_config->slot, (const sdmmc_slot_config_t*) slot_config); |
|
235 } |
|
236 if (err != ESP_OK) { |
|
237 ESP_LOGI(TAG, "slot_config returned rc=0x%x", err); |
|
238 goto fail; |
|
239 } |
|
240 return ESP_OK; |
|
241 |
|
242 fail: |
|
243 host_config->deinit(); |
|
244 free(s_card); |
|
245 s_card = NULL; |
|
246 return err; |
|
247 } |
|
248 |
|
249 |
|
250 |
|
251 esp_err_t my_esp_vfs_fat_sdmmc_mount(const char* base_path, |
|
252 const sdmmc_host_t* host_config, |
|
253 const void* slot_config, |
|
254 const esp_vfs_fat_mount_config_t* mount_config, |
|
255 sdmmc_card_t** out_card) |
|
256 { |
|
257 FATFS* fs = NULL; |
|
258 esp_err_t err = ESP_OK; |
|
259 |
|
260 if (s_card == NULL) { |
|
261 return ESP_ERR_INVALID_STATE; |
|
262 } |
|
263 |
|
264 // probe and initialize card |
|
265 err = sdmmc_card_init(host_config, s_card); |
|
266 if (err != ESP_OK) { |
|
267 if (err != ESP_ERR_INVALID_RESPONSE) { // No card present, do not log |
|
268 ESP_LOGI(TAG, "sdmmc_card_init failed 0x(%x)", err); |
|
269 } |
|
270 goto fail; |
|
271 } |
|
272 if (out_card != NULL) { |
|
273 *out_card = s_card; |
|
274 } |
|
275 |
|
276 ff_diskio_register_sdmmc(pdrv, s_card); |
|
277 s_pdrv = pdrv; |
|
278 ESP_LOGD(TAG, "using pdrv=%i", pdrv); |
|
279 char drv[3] = {(char)('0' + pdrv), ':', 0}; |
|
280 |
|
281 // connect FATFS to VFS |
|
282 err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs); |
|
283 if (err == ESP_ERR_INVALID_STATE) { |
|
284 // it's okay, already registered with VFS |
|
285 } else if (err != ESP_OK) { |
|
286 ESP_LOGI(TAG, "esp_vfs_fat_register failed 0x(%x)", err); |
|
287 goto fail; |
|
288 } |
|
289 |
|
290 // Try to mount partition |
|
291 FRESULT res = f_mount(fs, drv, 1); |
|
292 if (res != FR_OK) { |
|
293 err = ESP_FAIL; |
|
294 ESP_LOGD(TAG, "f_mount failed (%d)", res); |
|
295 goto fail; |
|
296 } |
|
297 return ESP_OK; |
|
298 |
|
299 fail: |
|
300 if (fs) { |
|
301 f_mount(NULL, drv, 0); |
|
302 } |
|
303 esp_vfs_fat_unregister_path(base_path); |
|
304 ff_diskio_unregister(pdrv); |
|
305 return err; |
|
306 } |
|
307 |
|
308 |
|
309 |
|
310 esp_err_t my_esp_vfs_fat_sdmmc_unmount() |
|
311 { |
|
312 if (s_card == NULL) { |
|
313 return ESP_ERR_INVALID_STATE; |
|
314 } |
|
315 // unmount |
|
316 char drv[3] = {(char)('0' + s_pdrv), ':', 0}; |
|
317 f_mount(0, drv, 0); |
|
318 return ESP_OK; |
|
319 } |
|
320 |
|
321 |
|
322 |
|
323 int FileCopy(char *ff, char *tf) |
|
324 { |
|
325 FILE *f, *t; |
|
326 uint8_t buf[512]; |
|
327 size_t bytes; |
|
328 |
|
329 f = fopen(ff, "r"); |
|
330 if (f == NULL) { |
|
331 ESP_LOGE(TAG, "FileCopy cannot open %s for read, error %d", ff, errno); |
|
332 return 1; |
|
333 } |
|
334 |
|
335 t = fopen(tf, "w+"); |
|
336 if (t == NULL) { |
|
337 ESP_LOGE(TAG, "FileCopy cannot open %s for create/write, error %d", tf, errno); |
|
338 fclose(f); |
|
339 return 1; |
|
340 } |
|
341 |
|
342 while ((bytes = fread(&buf, 1, 512, f))) { |
|
343 fwrite(&buf, 1, bytes, t); |
|
344 vTaskDelay(10 / portTICK_PERIOD_MS); |
|
345 } |
|
346 |
|
347 fclose(f); |
|
348 fclose(t); |
|
349 return 0; |
|
350 } |
|
351 |
|
352 |
|
353 |
|
354 void SyncDirs(char *fromdir, char *todir) |
|
355 { |
|
356 char ff[64], tf[64]; |
|
357 struct stat fs, ts; |
|
358 int rc; |
|
359 |
|
360 ESP_LOGI(TAG, "SyncDirs(%s, %s)", fromdir, todir); |
|
361 |
|
362 DIR *dir = opendir(fromdir); |
|
363 struct dirent* de = readdir(dir); |
|
364 while (de) { |
|
365 if (de->d_type == DT_REG) { |
|
366 sprintf(ff, "%s/%s", fromdir, de->d_name); |
|
367 if (stat(ff, &fs) == ESP_OK) { |
|
368 |
|
369 sprintf(tf, "%s/%s", todir, de->d_name); |
|
370 if (stat(tf, &ts) != ESP_OK) { |
|
371 ts.st_size = 0; |
|
372 } |
|
373 |
|
374 if (fs.st_size && (fs.st_size != ts.st_size)) { |
|
375 rc = FileCopy(ff, tf); |
|
376 ESP_LOGI(TAG, "Copy %s to %s, %ld bytes, rc=%d", ff, todir, fs.st_size, rc); |
|
377 } |
|
378 } |
|
379 } |
|
380 de = readdir(dir); |
|
381 vTaskDelay(50 / portTICK_PERIOD_MS); |
|
382 } |
|
383 closedir(dir); |
|
384 |
|
385 /* |
|
386 * Now see if we need to remove files. |
|
387 */ |
|
388 dir = opendir(todir); |
|
389 de = readdir(dir); |
|
390 while (de) { |
|
391 sprintf(tf, "%s/%s", todir, de->d_name); |
|
392 sprintf(ff, "%s/%s", fromdir, de->d_name); |
|
393 |
|
394 if (stat(ff, &fs) != ESP_OK) { |
|
395 if (unlink(tf) == ESP_OK) { |
|
396 ESP_LOGI(TAG, "Removed %s", tf); |
|
397 } |
|
398 } |
|
399 |
|
400 de = readdir(dir); |
|
401 vTaskDelay(50 / portTICK_PERIOD_MS); |
|
402 } |
|
403 closedir(dir); |
|
404 } |
|
405 |
|
406 |
|
407 |
|
408 void task_sdcard(void *pvParameter) |
|
409 { |
|
410 sdmmc_card_t* card; |
|
411 esp_err_t ret; |
|
412 EventBits_t uxBits; |
|
413 char filename[64]; |
|
414 |
|
415 sdcard_state = malloc(sizeof(SDCARD_State)); |
|
416 sdcard_state->host_ok = false; |
|
417 sdcard_state->card_present = false; |
|
418 sdcard_state->logfile[0] = '\0'; |
|
419 |
|
420 json_log = malloc(sizeof(JSON_log)); |
|
421 |
|
422 ESP_LOGI(TAG, "Starting SD card"); |
|
423 sdmmc_host_t host = SDSPI_HOST_DEFAULT(); |
|
424 host.slot = SDCARD_HOST_SLOT; // HSPI_HOST is in use by the TFT. |
|
425 sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT(); |
|
426 slot_config.gpio_miso = SDCARD_PIN_NUM_MISO; |
|
427 slot_config.gpio_mosi = SDCARD_PIN_NUM_MOSI; |
|
428 slot_config.gpio_sck = SDCARD_PIN_NUM_CLK; |
|
429 slot_config.gpio_cs = SDCARD_PIN_NUM_CS; |
|
430 slot_config.dma_channel = SDCARD_DMA_CHANNEL; |
|
431 // This initializes the slot without card detect (CD) and write protect (WP) signals. |
|
432 // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. |
|
433 |
|
434 /* |
|
435 * No errors from the sdmmc_cmd driver. |
|
436 */ |
|
437 esp_log_level_set("sdmmc_cmd", ESP_LOG_NONE); |
|
438 |
|
439 /* |
|
440 * Options for mounting the filesystem. |
|
441 * If format_if_mount_failed is set to true, SD card will be partitioned and |
|
442 * formatted in case when mounting fails. |
|
443 */ |
|
444 esp_vfs_fat_sdmmc_mount_config_t mount_config = { |
|
445 .format_if_mount_failed = false, |
|
446 .max_files = 5, |
|
447 .allocation_unit_size = 16 * 1024 |
|
448 }; |
|
449 |
|
450 ret = my_vfs_fat_sdmmc_init("/sdcard", &host, &slot_config); |
|
451 if (ret == ESP_OK) { |
|
452 ESP_LOGI(TAG, "SPI card interface ready"); |
|
453 sdcard_state->host_ok = true; |
|
454 } else { |
|
455 ESP_LOGE(TAG, "SPI card interface failed, abort task"); |
|
456 vTaskDelete(NULL); |
|
457 return; |
|
458 } |
|
459 |
|
460 xEventGroupSDcard = xEventGroupCreate(); |
|
461 |
|
462 /* |
|
463 * Task loop, continues check of the inserted cards. |
|
464 */ |
|
465 while (1) { |
|
466 |
|
467 if (sdcard_state->card_present == false) { |
|
468 /* |
|
469 * If the card is not mounted, try it. |
|
470 */ |
|
471 ret = my_esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card); |
|
472 if (ret == ESP_OK) { |
|
473 ESP_LOGI(TAG, "SD card mounted on /sdcard"); |
|
474 sdcard_state->card_present = true; |
|
475 |
|
476 DIR* dir = opendir("/sdcard/w/log"); |
|
477 if (dir == NULL) { |
|
478 ret = mkdir("/sdcard/w/log", 0755); |
|
479 printf("Dir created ret=%d\n", ret); |
|
480 } else { |
|
481 closedir(dir); |
|
482 } |
|
483 |
|
484 |
|
485 SyncDirs("/sdcard/w", "/spiffs/w"); |
|
486 SyncDirs("/sdcard/w/js", "/spiffs/w/js"); |
|
487 SyncDirs("/sdcard/w/css", "/spiffs/w/css"); |
|
488 // SyncDirs("/sdcard/w/js/modl" , "/spiffs/w/js/modl"); // |
|
489 // SyncDirs("/sdcard/w/js/utils", "/spiffs/w/js/utils"); |
|
490 // SyncDirs("/sdcard/w/js/zlib", "/spiffs/w/js/zlib"); |
|
491 // SyncDirs("/sdcard/w/core", "/spiffs/w/core"); |
|
492 // SyncDirs("/sdcard/w/core/input", "/spiffs/w/core/input"); |
|
493 // SyncDirs("/sdcard/w/core/util", "/spiffs/w/core/util"); |
|
494 // SyncDirs("/sdcard/w/app", "/spiffs/w/app"); |
|
495 // SyncDirs("/sdcard/w/app/images", "/spiffs/w/app/images"); |
|
496 // SyncDirs("/sdcard/w/app/locale", "/spiffs/w/app/locale"); |
|
497 // SyncDirs("/sdcard/w/app/sounds", "/spiffs/w/app/sounds"); // |
|
498 SyncDirs("/sdcard/w/app/styles", "/spiffs/w/app/styles"); |
|
499 // SyncDirs("/sdcard/fonts", "/spiffs/fonts"); // |
|
500 |
|
501 } |
|
502 } else { |
|
503 /* |
|
504 * Check if the mounted card is still in the slot. |
|
505 */ |
|
506 DIR* dir = opendir("/sdcard/w/log"); |
|
507 if (dir == NULL) { |
|
508 ESP_LOGI(TAG, "SD card missing, unmount"); |
|
509 my_esp_vfs_fat_sdmmc_unmount(); |
|
510 sdcard_state->card_present = false; |
|
511 } else { |
|
512 closedir(dir); |
|
513 } |
|
514 } |
|
515 |
|
516 uxBits = xEventGroupWaitBits(xEventGroupSDcard, |
|
517 TASK_SDCARD_LOG_CLEAN | TASK_SDCARD_LOG_CLOSE, pdFALSE, pdFALSE, 1000 / portTICK_PERIOD_MS); |
|
518 |
|
519 if (uxBits & TASK_SDCARD_LOG_CLEAN) { |
|
520 DIR *dir = opendir("/spiffs/log"); |
|
521 char lf[32]; |
|
522 |
|
523 if (dir != NULL) { |
|
524 struct dirent *de = readdir(dir); |
|
525 while (de) { |
|
526 sprintf(lf, "/spiffs/log/%s", de->d_name); |
|
527 if (unlink(lf) == ESP_OK) { |
|
528 ESP_LOGI(TAG, "Removed old %s", lf); |
|
529 } |
|
530 de = readdir(dir); |
|
531 vTaskDelay(2 / portTICK_PERIOD_MS); |
|
532 } |
|
533 closedir(dir); |
|
534 } |
|
535 xEventGroupClearBits(xEventGroupSDcard, TASK_SDCARD_LOG_CLEAN); |
|
536 } |
|
537 |
|
538 if (uxBits & TASK_SDCARD_LOG_CLOSE) { |
|
539 // Close the logfile. |
|
540 if (strlen(sdcard_state->logfile) && (sdcard_state->card_present == true)) { |
|
541 ESP_LOGI(TAG, "Closing logfile"); |
|
542 char destname[64]; |
|
543 sprintf(destname, "/sdcard/w/log"); |
|
544 int rc = mkdir(destname, 0755); |
|
545 if (rc && (errno != EEXIST)) { |
|
546 ESP_LOGE(TAG, "Cannot create %s error %d", destname, errno); |
|
547 } else { |
|
548 sprintf(filename, "/spiffs/log/%s.json", sdcard_state->logfile); |
|
549 // First close the JSON data records |
|
550 FILE *f = fopen(filename, "a+"); |
|
551 if (f) { |
|
552 fprintf(f, " ],\n"); // End of brewdata |
|
553 fprintf(f, " \"annotations\":[\n"); |
|
554 // Insert annotation records |
|
555 sprintf(destname, "/spiffs/log/%s.anno", sdcard_state->logfile); |
|
556 FILE *a = fopen(destname, "r"); |
|
557 char buf[256]; |
|
558 if (a) { |
|
559 while(true) { |
|
560 if (fgets(buf, sizeof(buf), a)) { |
|
561 fprintf(f, "%s", buf); |
|
562 } else { |
|
563 break; |
|
564 } |
|
565 } |
|
566 fclose(a); |
|
567 unlink(destname); |
|
568 } |
|
569 fprintf(f, " ]\n"); // End of annotations |
|
570 fprintf(f, " }]\n"); // End of brew |
|
571 fprintf(f, "}\n"); |
|
572 fclose(f); |
|
573 } |
|
574 sprintf(destname, "/sdcard/w/log/%s.json", sdcard_state->logfile); |
|
575 if (FileCopy(filename, destname) == 0) { |
|
576 ESP_LOGI(TAG, "JSON file copied to %s", destname); |
|
577 unlink(filename); |
|
578 } |
|
579 } |
|
580 } |
|
581 sdcard_state->logfile[0] = '\0'; // Clear logfile name |
|
582 runtime.Logfile[0] = '\0'; |
|
583 xEventGroupClearBits(xEventGroupSDcard, TASK_SDCARD_LOG_CLOSE); |
|
584 } |
|
585 } |
|
586 } |
|
587 |
|
588 |