FabGL
ESP32 Display Controller and Graphics Library
fabutils.cpp
1 /*
2  Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3  Copyright (c) 2019-2021 Fabrizio Di Vittorio.
4  All rights reserved.
5 
6 
7 * Please contact fdivitto2013@gmail.com if you need a commercial license.
8 
9 
10 * This library and related software is available under GPL v3.
11 
12  FabGL is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  FabGL is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <math.h>
32 
33 #include "ff.h"
34 #include "diskio.h"
35 #include "vfs_api.h"
36 #include "esp_vfs_fat.h"
37 #include "esp_task_wdt.h"
38 #include "driver/sdspi_host.h"
39 #include "sdmmc_cmd.h"
40 #include "esp_spiffs.h"
41 #include "soc/efuse_reg.h"
42 #include "soc/rtc.h"
43 #include "esp_ipc.h"
44 #include "soc/adc_channel.h"
45 
46 #include "fabutils.h"
51 
52 
53 #pragma GCC optimize ("O2")
54 
55 
56 
57 namespace fabgl {
58 
59 
60 
62 // TimeOut
63 
64 
65 TimeOut::TimeOut()
66  : m_start(esp_timer_get_time())
67 {
68 }
69 
70 
71 bool TimeOut::expired(int valueMS)
72 {
73  return valueMS > -1 && ((esp_timer_get_time() - m_start) / 1000) > valueMS;
74 }
75 
76 
77 
79 // isqrt
80 
81 // Integer square root by Halleck's method, with Legalize's speedup
82 int isqrt (int x)
83 {
84  if (x < 1)
85  return 0;
86  int squaredbit = 0x40000000;
87  int remainder = x;
88  int root = 0;
89  while (squaredbit > 0) {
90  if (remainder >= (squaredbit | root)) {
91  remainder -= (squaredbit | root);
92  root >>= 1;
93  root |= squaredbit;
94  } else
95  root >>= 1;
96  squaredbit >>= 2;
97  }
98  return root;
99 }
100 
101 
102 
104 // calcParity
105 
106 bool calcParity(uint8_t v)
107 {
108  v ^= v >> 4;
109  v &= 0xf;
110  return (0x6996 >> v) & 1;
111 }
112 
113 
115 // realloc32
116 // free32
117 
118 // size must be a multiple of uint32_t (32 bit)
119 void * realloc32(void * ptr, size_t size)
120 {
121  uint32_t * newBuffer = (uint32_t*) heap_caps_malloc(size, MALLOC_CAP_32BIT);
122  if (ptr) {
123  moveItems(newBuffer, (uint32_t*)ptr, size / sizeof(uint32_t));
124  heap_caps_free(ptr);
125  }
126  return newBuffer;
127 }
128 
129 
130 void free32(void * ptr)
131 {
132  heap_caps_free(ptr);
133 }
134 
135 
136 
138 // msToTicks
139 
140 uint32_t msToTicks(int ms)
141 {
142  return ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(ms);
143 }
144 
145 
147 // getChipPackage
148 
149 ChipPackage getChipPackage()
150 {
151  // read CHIP_VER_PKG (block0, byte 3, 105th bit % 32 = 9, 3 bits)
152  uint32_t ver_pkg = (REG_READ(EFUSE_BLK0_RDATA3_REG) >> 9) & 7;
153  switch (ver_pkg) {
154  case 0:
155  return ChipPackage::ESP32D0WDQ6; // WROOOM-32
156  case 1:
157  return ChipPackage::ESP32D0WDQ5; // WROVER-B
158  case 2:
160  case 5:
161  return ChipPackage::ESP32PICOD4; // TTGO-VGA32
162  default:
163  return ChipPackage::Unknown;
164  }
165 }
166 
167 
169 adc1_channel_t ADC1_GPIO2Channel(gpio_num_t gpio)
170 {
171  switch (gpio) {
172  case ADC1_CHANNEL_0_GPIO_NUM:
173  return ADC1_CHANNEL_0;
174  case ADC1_CHANNEL_1_GPIO_NUM:
175  return ADC1_CHANNEL_1;
176  case ADC1_CHANNEL_2_GPIO_NUM:
177  return ADC1_CHANNEL_2;
178  case ADC1_CHANNEL_3_GPIO_NUM:
179  return ADC1_CHANNEL_3;
180  case ADC1_CHANNEL_4_GPIO_NUM:
181  return ADC1_CHANNEL_4;
182  case ADC1_CHANNEL_5_GPIO_NUM:
183  return ADC1_CHANNEL_5;
184  case ADC1_CHANNEL_6_GPIO_NUM:
185  return ADC1_CHANNEL_6;
186  case ADC1_CHANNEL_7_GPIO_NUM:
187  return ADC1_CHANNEL_7;
188  default:
189  return ADC1_CHANNEL_0;
190  }
191 }
192 
193 
195 // configureGPIO
196 void configureGPIO(gpio_num_t gpio, gpio_mode_t mode)
197 {
198  PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
199  gpio_set_direction(gpio, mode);
200 }
201 
202 
204 // getApbFrequency
205 uint32_t getApbFrequency()
206 {
207  rtc_cpu_freq_config_t conf;
208  rtc_clk_cpu_freq_get_config(&conf);
209  return conf.freq_mhz >= 80 ? 80000000 : (conf.source_freq_mhz * 80000000 / conf.div);
210 }
211 
212 
214 // getCPUFrequencyMHz
215 uint32_t getCPUFrequencyMHz()
216 {
217  rtc_cpu_freq_config_t conf;
218  rtc_clk_cpu_freq_get_config(&conf);
219  return conf.freq_mhz;
220 }
221 
222 
224 // esp_intr_alloc_pinnedToCore
225 
226 struct esp_intr_alloc_args {
227  int source;
228  int flags;
229  intr_handler_t handler;
230  void * arg;
231  intr_handle_t * ret_handle;
232  TaskHandle_t waitingTask;
233 };
234 
235 
236 void esp_intr_alloc_pinnedToCore_call(void * arg)
237 {
238  auto args = (esp_intr_alloc_args*) arg;
239  esp_intr_alloc(args->source, args->flags, args->handler, args->arg, args->ret_handle);
240 }
241 
242 
243 void esp_intr_alloc_pinnedToCore(int source, int flags, intr_handler_t handler, void * arg, intr_handle_t * ret_handle, int core)
244 {
245  esp_intr_alloc_args args = { source, flags, handler, arg, ret_handle, xTaskGetCurrentTaskHandle() };
246  esp_ipc_call_blocking(core, esp_intr_alloc_pinnedToCore_call, &args);
247 }
248 
249 
250 
252 // converts '\' or '/' to newSep
253 
254 void replacePathSep(char * path, char newSep)
255 {
256  for (; *path; ++path)
257  if (*path == '\\' || *path == '/')
258  *path = newSep;
259 }
260 
261 
262 
264 // Sutherland-Cohen line clipping algorithm
265 
266 static int clipLine_code(int x, int y, Rect const & clipRect)
267 {
268  int code = 0;
269  if (x < clipRect.X1)
270  code = 1;
271  else if (x > clipRect.X2)
272  code = 2;
273  if (y < clipRect.Y1)
274  code |= 4;
275  else if (y > clipRect.Y2)
276  code |= 8;
277  return code;
278 }
279 
280 // false = line is out of clipping rect
281 // true = line intersects or is inside the clipping rect (x1, y1, x2, y2 are changed if checkOnly=false)
282 bool clipLine(int & x1, int & y1, int & x2, int & y2, Rect const & clipRect, bool checkOnly)
283 {
284  int newX1 = x1;
285  int newY1 = y1;
286  int newX2 = x2;
287  int newY2 = y2;
288  int topLeftCode = clipLine_code(newX1, newY1, clipRect);
289  int bottomRightCode = clipLine_code(newX2, newY2, clipRect);
290  while (true) {
291  if ((topLeftCode == 0) && (bottomRightCode == 0)) {
292  if (!checkOnly) {
293  x1 = newX1;
294  y1 = newY1;
295  x2 = newX2;
296  y2 = newY2;
297  }
298  return true;
299  } else if (topLeftCode & bottomRightCode) {
300  break;
301  } else {
302  int x = 0, y = 0;
303  int ncode = topLeftCode != 0 ? topLeftCode : bottomRightCode;
304  if (ncode & 8) {
305  x = newX1 + (newX2 - newX1) * (clipRect.Y2 - newY1) / (newY2 - newY1);
306  y = clipRect.Y2;
307  } else if (ncode & 4) {
308  x = newX1 + (newX2 - newX1) * (clipRect.Y1 - newY1) / (newY2 - newY1);
309  y = clipRect.Y1;
310  } else if (ncode & 2) {
311  y = newY1 + (newY2 - newY1) * (clipRect.X2 - newX1) / (newX2 - newX1);
312  x = clipRect.X2;
313  } else if (ncode & 1) {
314  y = newY1 + (newY2 - newY1) * (clipRect.X1 - newX1) / (newX2 - newX1);
315  x = clipRect.X1;
316  }
317  if (ncode == topLeftCode) {
318  newX1 = x;
319  newY1 = y;
320  topLeftCode = clipLine_code(newX1, newY1, clipRect);
321  } else {
322  newX2 = x;
323  newY2 = y;
324  bottomRightCode = clipLine_code(newX2, newY2, clipRect);
325  }
326  }
327  }
328  return false;
329 }
330 
331 
333 // removeRectangle
334 // remove "rectToRemove" from "mainRect", pushing remaining rectangles to "rects" stack
335 
336 void removeRectangle(Stack<Rect> & rects, Rect const & mainRect, Rect const & rectToRemove)
337 {
338  if (!mainRect.intersects(rectToRemove) || rectToRemove.contains(mainRect))
339  return;
340 
341  // top rectangle
342  if (mainRect.Y1 < rectToRemove.Y1)
343  rects.push(Rect(mainRect.X1, mainRect.Y1, mainRect.X2, rectToRemove.Y1 - 1));
344 
345  // bottom rectangle
346  if (mainRect.Y2 > rectToRemove.Y2)
347  rects.push(Rect(mainRect.X1, rectToRemove.Y2 + 1, mainRect.X2, mainRect.Y2));
348 
349  // left rectangle
350  if (mainRect.X1 < rectToRemove.X1)
351  rects.push(Rect(mainRect.X1, tmax(rectToRemove.Y1, mainRect.Y1), rectToRemove.X1 - 1, tmin(rectToRemove.Y2, mainRect.Y2)));
352 
353  // right rectangle
354  if (mainRect.X2 > rectToRemove.X2)
355  rects.push(Rect(rectToRemove.X2 + 1, tmax(rectToRemove.Y1, mainRect.Y1), mainRect.X2, tmin(rectToRemove.Y2, mainRect.Y2)));
356 }
357 
358 
360 // Rect
361 
362 Rect IRAM_ATTR Rect::merge(Rect const & rect) const
363 {
364  return Rect(imin(rect.X1, X1), imin(rect.Y1, Y1), imax(rect.X2, X2), imax(rect.Y2, Y2));
365 }
366 
367 
368 Rect IRAM_ATTR Rect::intersection(Rect const & rect) const
369 {
370  return Rect(tmax(X1, rect.X1), tmax(Y1, rect.Y1), tmin(X2, rect.X2), tmin(Y2, rect.Y2));
371 }
372 
373 
375 // rgb222_to_hsv
376 // R, G, B in the 0..3 range
377 void rgb222_to_hsv(int R, int G, int B, double * h, double * s, double * v)
378 {
379  double r = R / 3.0;
380  double g = G / 3.0;
381  double b = B / 3.0;
382  double cmax = tmax<double>(tmax<double>(r, g), b);
383  double cmin = tmin<double>(tmin<double>(r, g), b);
384  double diff = cmax - cmin;
385  if (cmax == cmin)
386  *h = 0;
387  else if (cmax == r)
388  *h = fmod((60.0 * ((g - b) / diff) + 360.0), 360.0);
389  else if (cmax == g)
390  *h = fmod((60.0 * ((b - r) / diff) + 120.0), 360.0);
391  else if (cmax == b)
392  *h = fmod((60.0 * ((r - g) / diff) + 240.0), 360.0);
393  *s = cmax == 0 ? 0 : (diff / cmax) * 100.0;
394  *v = cmax * 100.0;
395 }
396 
397 
398 
400 // StringList
401 
402 
403 StringList::StringList()
404  : m_items(nullptr),
405  m_selMap(nullptr),
406  m_ownStrings(false),
407  m_count(0),
408  m_allocated(0)
409 {
410 }
411 
412 
413 StringList::~StringList()
414 {
415  clear();
416 }
417 
418 
419 void StringList::clear()
420 {
421  if (m_ownStrings) {
422  for (int i = 0; i < m_count; ++i)
423  free((void*) m_items[i]);
424  }
425  free32(m_items);
426  free32(m_selMap);
427  m_items = nullptr;
428  m_selMap = nullptr;
429  m_count = m_allocated = 0;
430 }
431 
432 
433 void StringList::copyFrom(StringList const & src)
434 {
435  clear();
436  m_count = src.m_count;
437  checkAllocatedSpace(m_count);
438  for (int i = 0; i < m_count; ++i) {
439  m_items[i] = nullptr;
440  set(i, src.m_items[i]);
441  }
442  deselectAll();
443 }
444 
445 
446 void StringList::copySelectionMapFrom(StringList const & src)
447 {
448  int maskLen = (31 + m_allocated) / 32;
449  for (int i = 0; i < maskLen; ++i)
450  m_selMap[i] = src.m_selMap[i];
451 }
452 
453 
454 void StringList::checkAllocatedSpace(int requiredItems)
455 {
456  if (m_allocated < requiredItems) {
457  if (m_allocated == 0) {
458  // first time allocates exact space
459  m_allocated = requiredItems;
460  } else {
461  // next times allocate double
462  while (m_allocated < requiredItems)
463  m_allocated *= 2;
464  }
465  m_items = (char const**) realloc32(m_items, m_allocated * sizeof(char const *));
466  m_selMap = (uint32_t*) realloc32(m_selMap, (31 + m_allocated) / 32 * sizeof(uint32_t));
467  }
468 }
469 
470 
471 void StringList::insert(int index, char const * str)
472 {
473  ++m_count;
474  checkAllocatedSpace(m_count);
475  moveItems(m_items + index + 1, m_items + index, m_count - index - 1);
476  m_items[index] = nullptr;
477  set(index, str);
478  deselectAll();
479 }
480 
481 
482 int StringList::append(char const * str)
483 {
484  insert(m_count, str);
485  return m_count - 1;
486 }
487 
488 
489 int StringList::appendFmt(const char *format, ...)
490 {
491  takeStrings();
492  va_list ap;
493  va_start(ap, format);
494  int size = vsnprintf(nullptr, 0, format, ap) + 1;
495  if (size > 0) {
496  va_end(ap);
497  va_start(ap, format);
498  char buf[size + 1];
499  vsnprintf(buf, size, format, ap);
500  insert(m_count, buf);
501  }
502  va_end(ap);
503  return m_count - 1;
504 }
505 
506 
507 void StringList::append(char const * strlist[], int count)
508 {
509  for (int i = 0; i < count; ++i)
510  insert(m_count, strlist[i]);
511 }
512 
513 
514 // separator cannot be "0"
515 void StringList::appendSepList(char const * strlist, char separator)
516 {
517  if (strlist) {
518  takeStrings();
519  char const * start = strlist;
520  while (*start) {
521  auto end = strchr(start, separator);
522  if (!end)
523  end = strchr(start, 0);
524  int len = end - start;
525  char str[len + 1];
526  memcpy(str, start, len);
527  str[len] = 0;
528  insert(m_count, str);
529  start += len + (*end == 0 ? 0 : 1);
530  }
531  }
532 }
533 
534 
535 void StringList::set(int index, char const * str)
536 {
537  if (m_ownStrings) {
538  free((void*)m_items[index]);
539  m_items[index] = (char const*) malloc(strlen(str) + 1);
540  strcpy((char*)m_items[index], str);
541  } else {
542  m_items[index] = str;
543  }
544 }
545 
546 
547 void StringList::remove(int index)
548 {
549  if (m_ownStrings)
550  free((void*)m_items[index]);
551  moveItems(m_items + index, m_items + index + 1, m_count - index - 1);
552  --m_count;
553  deselectAll();
554 }
555 
556 
557 void StringList::takeStrings()
558 {
559  if (!m_ownStrings) {
560  m_ownStrings = true;
561  // take existing strings
562  for (int i = 0; i < m_count; ++i) {
563  char const * str = m_items[i];
564  m_items[i] = nullptr;
565  set(i, str);
566  }
567  }
568 }
569 
570 
571 void StringList::deselectAll()
572 {
573  for (int i = 0; i < (31 + m_count) / 32; ++i)
574  m_selMap[i] = 0;
575 }
576 
577 
578 bool StringList::selected(int index)
579 {
580  return m_selMap[index / 32] & (1 << (index % 32));
581 }
582 
583 
584 // -1 = no items selected
585 int StringList::getFirstSelected()
586 {
587  for (int i = 0; i < m_count; ++i)
588  if (selected(i))
589  return i;
590  return -1;
591 }
592 
593 
594 void StringList::select(int index, bool value)
595 {
596  if (value)
597  m_selMap[index / 32] |= 1 << (index % 32);
598  else
599  m_selMap[index / 32] &= ~(1 << (index % 32));
600 }
601 
602 
603 
604 // StringList
606 
607 
608 
610 // FileBrowser
611 
612 
613 char const * FileBrowser::s_SPIFFSMountPath;
614 bool FileBrowser::s_SPIFFSMounted = false;
615 size_t FileBrowser::s_SPIFFSMaxFiles;
616 
617 char const * FileBrowser::s_SDCardMountPath;
618 bool FileBrowser::s_SDCardMounted = false;
619 size_t FileBrowser::s_SDCardMaxFiles;
620 int FileBrowser::s_SDCardAllocationUnitSize;
621 int8_t FileBrowser::s_SDCardMISO;
622 int8_t FileBrowser::s_SDCardMOSI;
623 int8_t FileBrowser::s_SDCardCLK;
624 int8_t FileBrowser::s_SDCardCS;
625 sdmmc_card_t * FileBrowser::s_SDCard = nullptr;
626 
627 
628 
629 FileBrowser::FileBrowser()
630  : m_dir(nullptr),
631  m_count(0),
632  m_items(nullptr),
633  m_sorted(true),
634  m_includeHiddenFiles(false),
635  m_namesStorage(nullptr)
636 {
637 }
638 
639 
640 FileBrowser::FileBrowser(char const * path)
641  : FileBrowser()
642 {
643  setDirectory(path);
644 }
645 
646 
647 FileBrowser::~FileBrowser()
648 {
649  clear();
650 
651  free(m_dir);
652  m_dir = nullptr;
653 }
654 
655 
656 void FileBrowser::clear()
657 {
658  free(m_items);
659  m_items = nullptr;
660 
661  free(m_namesStorage);
662  m_namesStorage = nullptr;
663 
664  m_count = 0;
665 }
666 
667 
668 // set absolute directory (full path must be specified)
669 bool FileBrowser::setDirectory(const char * path)
670 {
671  if (m_dir == nullptr || strcmp(path, m_dir) != 0) {
672  free(m_dir);
673  m_dir = strdup(path);
674  }
675  return reload();
676 }
677 
678 
679 // set relative directory:
680 // ".." : go to the parent directory
681 // "dirname": go inside the specified sub directory
682 void FileBrowser::changeDirectory(const char * subdir)
683 {
684  if (!m_dir || strlen(subdir) == 0)
685  return;
686  if (strcmp(subdir, "..") == 0) {
687  // go to parent directory
688  auto lastSlash = strrchr(m_dir, '/');
689  if (lastSlash) {
690  if (lastSlash != m_dir)
691  lastSlash[0] = 0;
692  else
693  lastSlash[1] = 0;
694  reload();
695  }
696  } else {
697  // go to sub directory
698  auto oldLen = strcmp(m_dir, "/") == 0 ? 0 : strlen(m_dir);
699  char * newDir = (char*) malloc(oldLen + 1 + strlen(subdir) + 1); // m_dir + '/' + subdir + 0
700  strcpy(newDir, m_dir);
701  newDir[oldLen] = '/';
702  strcpy(newDir + oldLen + 1, subdir);
703  free(m_dir);
704  m_dir = newDir;
705  reload();
706  }
707 }
708 
709 
710 int FileBrowser::countDirEntries(int * namesLength)
711 {
712  int c = 0;
713  if (strcmp(m_dir, "/") == 0) {
714 
715  // root dir
716  if (s_SPIFFSMounted)
717  ++c;
718  if (s_SDCardMounted)
719  ++c;
720 
721  } else {
722 
723  *namesLength = 0;
724  if (m_dir) {
725  auto dirp = opendir(m_dir);
726  while (dirp) {
727  auto dp = readdir(dirp);
728  if (dp == NULL)
729  break;
730  if (strcmp(".", dp->d_name) && strcmp("..", dp->d_name) && dp->d_type != DT_UNKNOWN) {
731  *namesLength += strlen(dp->d_name) + 1;
732  ++c;
733  }
734  }
735  if (dirp)
736  closedir(dirp);
737  }
738 
739  }
740 
741  return c;
742 }
743 
744 
745 bool FileBrowser::exists(char const * name, bool caseSensitive)
746 {
747  if (caseSensitive) {
748  for (int i = 0; i < m_count; ++i)
749  if (strcmp(name, m_items[i].name) == 0)
750  return true;
751  } else {
752  for (int i = 0; i < m_count; ++i)
753  if (strcasecmp(name, m_items[i].name) == 0)
754  return true;
755  }
756  return false;
757 }
758 
759 
760 bool FileBrowser::filePathExists(char const * filepath)
761 {
762  auto f = openFile(filepath, "rb");
763  if (f)
764  fclose(f);
765  return f != nullptr;
766 }
767 
768 
769 size_t FileBrowser::fileSize(char const * name)
770 {
771  size_t size = 0;
772  char fullpath[strlen(m_dir) + 1 + strlen(name) + 1];
773  sprintf(fullpath, "%s/%s", m_dir, name);
774  auto fr = fopen(fullpath, "rb");
775  if (fr) {
776  fseek(fr, 0, SEEK_END);
777  size = ftell(fr);
778  fclose(fr);
779  }
780  return size;
781 }
782 
783 
784 bool FileBrowser::fileCreationDate(char const * name, int * year, int * month, int * day, int * hour, int * minutes, int * seconds)
785 {
786  char fullpath[strlen(m_dir) + 1 + strlen(name) + 1];
787  sprintf(fullpath, "%s/%s", m_dir, name);
788  struct stat s;
789  if (stat(fullpath, &s))
790  return false;
791  auto tm = *localtime((time_t*)&s.st_ctime); // I know, this is not create date, but status change. Anyway I cannot find "st_birthtimespec"
792  *year = 1900 + tm.tm_year;
793  *month = 1 + tm.tm_mon;
794  *day = tm.tm_mday;
795  *hour = tm.tm_hour;
796  *minutes = tm.tm_min;
797  *seconds = imin(tm.tm_sec, 59); // [0, 61] (until C99), [0, 60] (since C99)
798  return true;
799 }
800 
801 
802 bool FileBrowser::fileUpdateDate(char const * name, int * year, int * month, int * day, int * hour, int * minutes, int * seconds)
803 {
804  char fullpath[strlen(m_dir) + 1 + strlen(name) + 1];
805  sprintf(fullpath, "%s/%s", m_dir, name);
806  struct stat s;
807  if (stat(fullpath, &s))
808  return false;
809  auto tm = *localtime((time_t*)&s.st_mtime);
810  *year = 1900 + tm.tm_year;
811  *month = 1 + tm.tm_mon;
812  *day = tm.tm_mday;
813  *hour = tm.tm_hour;
814  *minutes = tm.tm_min;
815  *seconds = imin(tm.tm_sec, 59); // [0, 61] (until C99), [0, 60] (since C99)
816  return true;
817 }
818 
819 
820 bool FileBrowser::fileAccessDate(char const * name, int * year, int * month, int * day, int * hour, int * minutes, int * seconds)
821 {
822  char fullpath[strlen(m_dir) + 1 + strlen(name) + 1];
823  sprintf(fullpath, "%s/%s", m_dir, name);
824  struct stat s;
825  if (stat(fullpath, &s))
826  return false;
827  auto tm = *localtime((time_t*)&s.st_atime);
828  *year = 1900 + tm.tm_year;
829  *month = 1 + tm.tm_mon;
830  *day = tm.tm_mday;
831  *hour = tm.tm_hour;
832  *minutes = tm.tm_min;
833  *seconds = imin(tm.tm_sec, 59); // [0, 61] (until C99), [0, 60] (since C99)
834  return true;
835 }
836 
837 
838 
839 int DirComp(const void * i1, const void * i2)
840 {
841  DirItem * d1 = (DirItem*)i1;
842  DirItem * d2 = (DirItem*)i2;
843  if (d1->isDir != d2->isDir) // directories first
844  return d1->isDir ? -1 : +1;
845  else
846  return strcmp(d1->name, d2->name);
847 }
848 
849 
851 {
852  bool retval = true;
853 
854  clear();
855  int namesAlloc;
856  int c = countDirEntries(&namesAlloc);
857  m_items = (DirItem*) malloc(sizeof(DirItem) * (c + 1));
858  m_namesStorage = (char*) malloc(namesAlloc);
859  char * sname = m_namesStorage;
860 
861  if (strcmp(m_dir, "/") == 0) {
862 
863  // root dir
864  if (s_SPIFFSMounted) {
865  m_items[m_count].name = s_SPIFFSMountPath + 1; // +1 to bypass "/"
866  m_items[m_count].isDir = true;
867  ++m_count;
868  }
869  if (s_SDCardMounted) {
870  m_items[m_count].name = s_SDCardMountPath + 1; // +1 to bypass "/"
871  m_items[m_count].isDir = true;
872  ++m_count;
873  }
874 
875  } else {
876 
877  // first item is always ".."
878  m_items[0].name = "..";
879  m_items[0].isDir = true;
880  ++m_count;
881 
882  int hiddenFilesCount = 0;
883  auto dirp = opendir(m_dir);
884  while (dirp) {
885  auto dp = readdir(dirp);
886  if (dp == NULL)
887  break;
888  if (strcmp(".", dp->d_name) && strcmp("..", dp->d_name) && dp->d_type != DT_UNKNOWN) {
889  DirItem * di = m_items + m_count;
890  // check if this is a simulated directory (like in SPIFFS)
891  auto slashPos = strchr(dp->d_name, '/');
892  if (slashPos) {
893  // yes, this is a simulated dir. Trunc and avoid to insert it twice
894  auto len = slashPos - dp->d_name;
895  strncpy(sname, dp->d_name, len);
896  sname[len] = 0;
897  if (!exists(sname)) {
898  di->name = sname;
899  di->isDir = true;
900  sname += len + 1;
901  ++m_count;
902  }
903  } else {
904  bool isHidden = dp->d_name[0] == '.';
905  if (!isHidden || m_includeHiddenFiles) {
906  strcpy(sname, dp->d_name);
907  di->name = sname;
908  di->isDir = (dp->d_type == DT_DIR);
909  sname += strlen(sname) + 1;
910  ++m_count;
911  }
912  if (isHidden)
913  ++hiddenFilesCount;
914  }
915  }
916  }
917  if (dirp)
918  closedir(dirp);
919  else
920  retval = false;
921 
922  // for SPIFFS "opendir" returns always true, even for not existing dirs. An hidden file means there is the SPIFFS directory placeholder
923  if (m_count == 1 && hiddenFilesCount == 0 && getDriveType(m_dir) == DriveType::SPIFFS)
924  retval = false; // no hidden files, so the directory doesn't exist
925 
926  }
927 
928  if (m_sorted)
929  qsort(m_items, m_count, sizeof(DirItem), DirComp);
930 
931  return retval;
932 }
933 
934 
935 // note: for SPIFFS this creates an empty ".dirname" file. The SPIFFS path is detected when path starts with "/spiffs" or s_SPIFFSMountPath
936 // "dirname" is not a path, just a directory name created inside "m_dir"
937 void FileBrowser::makeDirectory(char const * dirname)
938 {
939  int dirnameLen = strlen(dirname);
940  if (dirnameLen > 0) {
942  // simulated directory, puts an hidden placeholder
943  char fullpath[strlen(m_dir) + 3 + 2 * dirnameLen + 1];
944  auto name = dirname;
945  while (*name) {
946  auto next = name + 1;
947  while (*next && *next != '\\' && *next != '/')
948  ++next;
949  strcpy(fullpath, m_dir);
950  if (dirname != name) {
951  strcat(fullpath, "/");
952  strncat(fullpath, dirname, name - dirname - 1);
953  }
954  strcat(fullpath, "/");
955  strncat(fullpath, name, next - name);
956  strcat(fullpath, "/.");
957  strncat(fullpath, name, next - name);
958  replacePathSep(fullpath, '/');
959  FILE * f = fopen(fullpath, "wb");
960  fclose(f);
961  if (*next == 0)
962  break;
963  name = next + 1;
964  }
965 
966  } else {
967  char fullpath[strlen(m_dir) + 1 + dirnameLen + 1];
968  sprintf(fullpath, "%s/%s", m_dir, dirname);
969  replacePathSep(fullpath, '/');
970  mkdir(fullpath, ACCESSPERMS);
971  }
972  }
973 }
974 
975 
976 // removes a file or a directory (and all files inside it)
977 // The SPIFFS path is detected when path starts with "/spiffs" or s_SPIFFSMountPath
978 // "name" is not a path, just a file or directory name inside "m_dir"
979 void FileBrowser::remove(char const * name)
980 {
981  char fullpath[strlen(m_dir) + 1 + strlen(name) + 1];
982  sprintf(fullpath, "%s/%s", m_dir, name);
983  int r = unlink(fullpath);
984 
985  if (r != 0) {
986  // failed, try to remove directory
987  r = rmdir(fullpath);
988  }
989 
990  if (r != 0) {
991  // failed, try simulated directory in SPIFFS
993  // simulated directory
994  // maybe this is a directory, remove ".dir" file
995  char hidpath[strlen(m_dir) + 3 + 2 * strlen(name) + 1];
996  sprintf(hidpath, "%s/%s/.%s", m_dir, name, name);
997  unlink(hidpath);
998  // maybe the directory contains files, remove all
999  auto dirp = opendir(fullpath);
1000  while (dirp) {
1001  auto dp = readdir(dirp);
1002  if (dp == NULL)
1003  break;
1004  if (strcmp(".", dp->d_name) && strcmp("..", dp->d_name) && dp->d_type != DT_UNKNOWN) {
1005  char sfullpath[strlen(fullpath) + 1 + strlen(dp->d_name) + 1];
1006  sprintf(sfullpath, "%s/%s", fullpath, dp->d_name);
1007  unlink(sfullpath);
1008  }
1009  }
1010  closedir(dirp);
1011  }
1012  }
1013 }
1014 
1015 
1016 // works only for files
1017 void FileBrowser::rename(char const * oldName, char const * newName)
1018 {
1019  char oldfullpath[strlen(m_dir) + 1 + strlen(oldName) + 1];
1020  sprintf(oldfullpath, "%s/%s", m_dir, oldName);
1021 
1022  char newfullpath[strlen(m_dir) + 1 + strlen(newName) + 1];
1023  sprintf(newfullpath, "%s/%s", m_dir, newName);
1024 
1025  ::rename(oldfullpath, newfullpath);
1026 }
1027 
1028 
1029 // return a full path
1031 {
1032  constexpr int FLEN = 6;
1033  auto ret = (char*) malloc(strlen(m_dir) + 1 + FLEN + 4 + 1);
1034  while (true) {
1035  char name[FLEN + 1] = { 0 };
1036  for (int i = 0; i < FLEN; ++i)
1037  name[i] = 65 + (rand() % 26);
1038  sprintf(ret, "%s/%s.TMP", m_dir, name);
1039  if (!exists(name, false))
1040  return ret;
1041  }
1042 }
1043 
1044 
1045 bool FileBrowser::truncate(char const * name, size_t size)
1046 {
1047  constexpr size_t BUFLEN = 512;
1048 
1049  char fullpath[strlen(m_dir) + 1 + strlen(name) + 1];
1050  sprintf(fullpath, "%s/%s", m_dir, name);
1051 
1052  // in future maybe...
1053  //::truncate(name, size);
1054 
1055  bool retval = false;
1056 
1057  // for now...
1058  char * tempFilename = createTempFilename();
1059  if (::rename(fullpath, tempFilename) == 0) {
1060  void * buf = malloc(BUFLEN);
1061  if (buf) {
1062  auto fr = fopen(tempFilename, "rb");
1063  if (fr) {
1064  auto fw = fopen(fullpath, "wb");
1065  if (fw) {
1066 
1067  while (size > 0) {
1068  auto l = fread(buf, 1, tmin(size, BUFLEN), fr);
1069  if (l == 0)
1070  break;
1071  fwrite(buf, 1, l, fw);
1072  size -= l;
1073  }
1074 
1075  // just in case truncate is used to expand the file
1076  for (; size > 0; --size)
1077  fputc(0, fw);
1078 
1079  retval = true;
1080  fclose(fw);
1081  }
1082  fclose(fr);
1083  }
1084  }
1085  free(buf);
1086  unlink(tempFilename);
1087  }
1088  free(tempFilename);
1089  return retval;
1090 }
1091 
1092 
1093 // concatenates current directory and specified name and store result into fullpath
1094 // Specifying outPath=nullptr returns required length
1095 int FileBrowser::getFullPath(char const * name, char * outPath, int maxlen)
1096 {
1097  return (outPath ? snprintf(outPath, maxlen, "%s/%s", m_dir, name) : snprintf(nullptr, 0, "%s/%s", m_dir, name)) + 1;
1098 }
1099 
1100 
1101 FILE * FileBrowser::openFile(char const * filename, char const * mode)
1102 {
1103  char fullpath[strlen(m_dir) + 1 + strlen(filename) + 1];
1104  strcpy(fullpath, m_dir);
1105  strcat(fullpath, "/");
1106  strcat(fullpath, filename);
1107 
1108  replacePathSep(fullpath, '/');
1109 
1110  return fopen(fullpath, mode);
1111 }
1112 
1113 
1115 {
1116  return getDriveType(m_dir);
1117 }
1118 
1119 
1121 {
1122  if (strncmp(path, "/spiffs", 7) == 0 || (s_SPIFFSMounted && strncmp(path, s_SPIFFSMountPath, strlen(s_SPIFFSMountPath)) == 0)) {
1123  return DriveType::SPIFFS;
1124  } else if (s_SDCardMounted && strncmp(path, s_SDCardMountPath, strlen(s_SDCardMountPath)) == 0) {
1125  return DriveType::SDCard;
1126  } else {
1127  return DriveType::None;
1128  }
1129 }
1130 
1131 
1132 bool FileBrowser::format(DriveType driveType, int drive)
1133 {
1134  esp_task_wdt_init(45, false);
1135 
1136  if (driveType == DriveType::SDCard && s_SDCardMounted) {
1137 
1138  // unmount filesystem
1139  char drv[3] = {(char)('0' + drive), ':', 0};
1140  f_mount(0, drv, 0);
1141 
1142  void * buffer = malloc(FF_MAX_SS);
1143  if (!buffer)
1144  return false;
1145 
1146  // create partition
1147  DWORD plist[] = { 100, 0, 0, 0 };
1148  if (f_fdisk(drive, plist, buffer) != FR_OK) {
1149  free(buffer);
1150  return false;
1151  }
1152 
1153  // make filesystem
1154  if (f_mkfs(drv, FM_ANY, 16 * 1024, buffer, FF_MAX_SS) != FR_OK) {
1155  free(buffer);
1156  return false;
1157  }
1158 
1159  free(buffer);
1160 
1161  remountSDCard();
1162 
1163  return true;
1164 
1165  } else if (driveType == DriveType::SPIFFS && s_SPIFFSMounted) {
1166 
1167  // driveType == DriveType::SPIFFS
1168  bool r = (esp_spiffs_format(nullptr) == ESP_OK);
1169 
1170  remountSPIFFS();
1171 
1172  return r;
1173 
1174  } else
1175  return false;
1176 }
1177 
1178 
1179 bool FileBrowser::mountSDCard(bool formatOnFail, char const * mountPath, size_t maxFiles, int allocationUnitSize, int MISO, int MOSI, int CLK, int CS)
1180 {
1181  switch (getChipPackage()) {
1183  MISO = 2;
1184  MOSI = 12;
1185  break;
1187  MISO = 35;
1188  MOSI = 12;
1189  break;
1190  default:
1191  break;
1192  }
1193 
1194  s_SDCardMountPath = mountPath;
1195  s_SDCardMaxFiles = maxFiles;
1196  s_SDCardAllocationUnitSize = allocationUnitSize;
1197  s_SDCardMISO = MISO;
1198  s_SDCardMOSI = MOSI;
1199  s_SDCardCLK = CLK;
1200  s_SDCardCS = CS;
1201  s_SDCardMounted = false;
1202 
1203  sdmmc_host_t host = SDSPI_HOST_DEFAULT();
1204  host.slot = HSPI_HOST;
1205 
1206  #if FABGL_ESP_IDF_VERSION <= FABGL_ESP_IDF_VERSION_VAL(3, 3, 5)
1207 
1208  sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
1209  slot_config.gpio_miso = int2gpio(MISO);
1210  slot_config.gpio_mosi = int2gpio(MOSI);
1211  slot_config.gpio_sck = int2gpio(CLK);
1212  slot_config.gpio_cs = int2gpio(CS);
1213 
1214  esp_vfs_fat_sdmmc_mount_config_t mount_config;
1215  mount_config.format_if_mount_failed = formatOnFail;
1216  mount_config.max_files = maxFiles;
1217  mount_config.allocation_unit_size = allocationUnitSize;
1218 
1219  s_SDCardMounted = (esp_vfs_fat_sdmmc_mount(mountPath, &host, &slot_config, &mount_config, &s_SDCard) == ESP_OK);
1220 
1221  #else
1222 
1223  // slow down SD card. Using ESP32 core 2.0.0 may crash SD subsystem, having VGA output and WiFi enabled
1224  // @TODO: check again
1225  host.max_freq_khz = 19000;
1226 
1227  spi_bus_config_t bus_cfg = {
1228  .mosi_io_num = int2gpio(MOSI),
1229  .miso_io_num = int2gpio(MISO),
1230  .sclk_io_num = int2gpio(CLK),
1231  .quadwp_io_num = -1,
1232  .quadhd_io_num = -1,
1233  .max_transfer_sz = 4000,
1234  };
1235  auto r = spi_bus_initialize((spi_host_device_t)host.slot, &bus_cfg, 2);
1236 
1237  if (r == ESP_OK || r == ESP_ERR_INVALID_STATE) { // ESP_ERR_INVALID_STATE, maybe spi_bus_initialize already called
1238  sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
1239  slot_config.gpio_cs = int2gpio(CS);
1240  slot_config.host_id = (spi_host_device_t) host.slot;
1241 
1242  esp_vfs_fat_sdmmc_mount_config_t mount_config;
1243  mount_config.format_if_mount_failed = formatOnFail;
1244  mount_config.max_files = maxFiles;
1245  mount_config.allocation_unit_size = allocationUnitSize;
1246 
1247  r = esp_vfs_fat_sdspi_mount(mountPath, &host, &slot_config, &mount_config, &s_SDCard);
1248 
1249  s_SDCardMounted = (r == ESP_OK);
1250  }
1251 
1252  #endif
1253 
1254  return s_SDCardMounted;
1255 }
1256 
1257 
1259 {
1260  if (s_SDCardMounted) {
1261  #if FABGL_ESP_IDF_VERSION <= FABGL_ESP_IDF_VERSION_VAL(3, 3, 5)
1262  esp_vfs_fat_sdmmc_unmount();
1263  #else
1264  esp_vfs_fat_sdcard_unmount(s_SDCardMountPath, s_SDCard);
1265  #endif
1266  s_SDCardMounted = false;
1267  }
1268 }
1269 
1270 
1272 {
1273  unmountSDCard();
1274  return mountSDCard(false, s_SDCardMountPath, s_SDCardMaxFiles, s_SDCardAllocationUnitSize, s_SDCardMISO, s_SDCardMOSI, s_SDCardCLK, s_SDCardCS);
1275 }
1276 
1277 
1278 bool FileBrowser::mountSPIFFS(bool formatOnFail, char const * mountPath, size_t maxFiles)
1279 {
1280  s_SPIFFSMountPath = mountPath;
1281  s_SPIFFSMaxFiles = maxFiles;
1282  esp_vfs_spiffs_conf_t conf = {
1283  .base_path = mountPath,
1284  .partition_label = nullptr,
1285  .max_files = maxFiles,
1286  .format_if_mount_failed = true
1287  };
1288  s_SPIFFSMounted = (esp_vfs_spiffs_register(&conf) == ESP_OK);
1289  return s_SPIFFSMounted;
1290 }
1291 
1292 
1294 {
1295  if (s_SPIFFSMounted) {
1296  esp_vfs_spiffs_unregister(nullptr);
1297  s_SPIFFSMounted = false;
1298  }
1299 }
1300 
1301 
1303 {
1304  unmountSPIFFS();
1305  return mountSPIFFS(false, s_SPIFFSMountPath, s_SPIFFSMaxFiles);
1306 }
1307 
1308 
1309 bool FileBrowser::getFSInfo(DriveType driveType, int drive, int64_t * total, int64_t * used)
1310 {
1311  *total = *used = 0;
1312 
1313  if (driveType == DriveType::SDCard) {
1314 
1315  FATFS * fs;
1316  DWORD free_clusters;
1317  char drv[3] = {(char)('0' + drive), ':', 0};
1318  if (f_getfree(drv, &free_clusters, &fs) != FR_OK)
1319  return false;
1320  int64_t total_sectors = (fs->n_fatent - 2) * fs->csize;
1321  int64_t free_sectors = free_clusters * fs->csize;
1322  *total = total_sectors * fs->ssize;
1323  *used = *total - free_sectors * fs->ssize;
1324 
1325  return true;
1326 
1327  } else if (driveType == DriveType::SPIFFS) {
1328 
1329  size_t stotal = 0, sused = 0;
1330  if (esp_spiffs_info(NULL, &stotal, &sused) != ESP_OK)
1331  return false;
1332  *total = stotal;
1333  *used = sused;
1334  return true;
1335 
1336  } else
1337  return false;
1338 }
1339 
1340 
1341 // FileBrowser
1343 
1344 
1345 
1346 
1348 // LightMemoryPool
1349 
1350 
1351 void LightMemoryPool::mark(int pos, int16_t size, bool allocated)
1352 {
1353  m_mem[pos] = size & 0xff;
1354  m_mem[pos + 1] = ((size >> 8) & 0x7f) | (allocated ? 0x80 : 0);
1355 }
1356 
1357 
1358 int16_t LightMemoryPool::getSize(int pos)
1359 {
1360  return m_mem[pos] | ((m_mem[pos + 1] & 0x7f) << 8);
1361 }
1362 
1363 
1364 bool LightMemoryPool::isFree(int pos)
1365 {
1366  return (m_mem[pos + 1] & 0x80) == 0;
1367 }
1368 
1369 
1370 LightMemoryPool::LightMemoryPool(int poolSize)
1371 {
1372  m_poolSize = poolSize + 2;
1373  m_mem = (uint8_t*) heap_caps_malloc(m_poolSize, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
1374  mark(0, m_poolSize - 2, false);
1375 }
1376 
1377 
1378 LightMemoryPool::~LightMemoryPool()
1379 {
1380  heap_caps_free(m_mem);
1381 }
1382 
1383 
1384 void * LightMemoryPool::alloc(int size)
1385 {
1386  for (int pos = 0; pos < m_poolSize; ) {
1387  int16_t blockSize = getSize(pos);
1388  if (isFree(pos)) {
1389  if (blockSize == size) {
1390  // found a block having the same size
1391  mark(pos, size, true);
1392  return m_mem + pos + 2;
1393  } else if (blockSize > size) {
1394  // found a block having larger size
1395  int remainingSize = blockSize - size - 2;
1396  if (remainingSize > 0)
1397  mark(pos + 2 + size, remainingSize, false); // create new free block at the end of this block
1398  else
1399  size = blockSize; // to avoid to waste last block
1400  mark(pos, size, true); // reduce size of this block and mark as allocated
1401  return m_mem + pos + 2;
1402  } else {
1403  // this block hasn't enough space
1404  // can merge with next block?
1405  int nextBlockPos = pos + 2 + blockSize;
1406  if (nextBlockPos < m_poolSize && isFree(nextBlockPos)) {
1407  // join blocks and stay at this pos
1408  mark(pos, blockSize + getSize(nextBlockPos) + 2, false);
1409  } else {
1410  // move to the next block
1411  pos += blockSize + 2;
1412  }
1413  }
1414  } else {
1415  // move to the next block
1416  pos += blockSize + 2;
1417  }
1418  }
1419  return nullptr;
1420 }
1421 
1422 
1423 bool LightMemoryPool::memCheck()
1424 {
1425  int pos = 0;
1426  while (pos < m_poolSize) {
1427  int16_t blockSize = getSize(pos);
1428  pos += blockSize + 2;
1429  }
1430  return pos == m_poolSize;
1431 }
1432 
1433 
1434 int LightMemoryPool::totFree()
1435 {
1436  int r = 0;
1437  for (int pos = 0; pos < m_poolSize; ) {
1438  int16_t blockSize = getSize(pos);
1439  if (isFree(pos))
1440  r += blockSize;
1441  pos += blockSize + 2;
1442  }
1443  return r;
1444 }
1445 
1446 
1447 int LightMemoryPool::totAllocated()
1448 {
1449  int r = 0;
1450  for (int pos = 0; pos < m_poolSize; ) {
1451  int16_t blockSize = getSize(pos);
1452  if (!isFree(pos))
1453  r += blockSize;
1454  pos += blockSize + 2;
1455  }
1456  return r;
1457 }
1458 
1459 
1460 int LightMemoryPool::largestFree()
1461 {
1462  int r = 0;
1463  for (int pos = 0; pos < m_poolSize; ) {
1464  int16_t blockSize = getSize(pos);
1465  if (isFree(pos) && blockSize > r)
1466  r = blockSize;
1467  pos += blockSize + 2;
1468  }
1469  return r;
1470 }
1471 
1472 
1473 // LightMemoryPool
1475 
1476 
1477 
1479 // CoreUsage
1480 
1481 
1482 int CoreUsage::s_busiestCore = FABGLIB_VIDEO_CPUINTENSIVE_TASKS_CORE;
1483 
1484 
1485 // CoreUsage
1487 
1488 
1489 
1490 
1491 }
1492 
int16_t Y1
Definition: fabutils.h:228
char const * name
Definition: fabutils.h:513
This file contains fabgl::PS2Controller definition.
This file contains fabgl::VGA16Controller definition.
bool truncate(char const *name, size_t size)
Truncates a file to the specified size.
Definition: fabutils.cpp:1045
void rename(char const *oldName, char const *newName)
Renames a file.
Definition: fabutils.cpp:1017
bool filePathExists(char const *filepath)
Determines if a file exists.
Definition: fabutils.cpp:760
void remove(char const *name)
Removes a file or directory.
Definition: fabutils.cpp:979
static bool mountSDCard(bool formatOnFail, char const *mountPath, size_t maxFiles=4, int allocationUnitSize=16 *1024, int MISO=16, int MOSI=17, int CLK=14, int CS=13)
Mounts filesystem on SD Card.
Definition: fabutils.cpp:1179
bool setDirectory(const char *path)
Sets absolute directory path.
Definition: fabutils.cpp:669
uint8_t B
This file contains fabgl::VGAController definition.
int16_t X1
Definition: fabutils.h:227
size_t fileSize(char const *name)
Determines file size.
Definition: fabutils.cpp:769
bool fileUpdateDate(char const *name, int *year, int *month, int *day, int *hour, int *minutes, int *seconds)
Gets file update date and time.
Definition: fabutils.cpp:802
#define FABGLIB_VIDEO_CPUINTENSIVE_TASKS_CORE
Definition: fabglconf.h:138
bool fileCreationDate(char const *name, int *year, int *month, int *day, int *hour, int *minutes, int *seconds)
Gets file creation date and time.
Definition: fabutils.cpp:784
This file contains fabgl::VGA2Controller definition.
static void unmountSDCard()
Unmounts filesystem on SD Card.
Definition: fabutils.cpp:1258
uint8_t G
This file contains some utility classes and functions.
Definition: canvas.cpp:36
FileBrowser item specificator.
Definition: fabutils.h:511
char * createTempFilename()
Creates a random temporary filename, with absolute path.
Definition: fabutils.cpp:1030
int getFullPath(char const *name, char *outPath=nullptr, int maxlen=0)
Composes a full file path given a relative name.
Definition: fabutils.cpp:1095
int16_t Y2
Definition: fabutils.h:230
DriveType getCurrentDriveType()
Returns the drive type of current directory.
Definition: fabutils.cpp:1114
static bool format(DriveType driveType, int drive)
Formats SPIFFS or SD Card.
Definition: fabutils.cpp:1132
DriveType
This enum defines drive types (SPIFFS or SD Card)
Definition: fabutils.h:520
uint8_t R
static bool remountSDCard()
Remounts SDCard filesystem, using the same parameters.
Definition: fabutils.cpp:1271
ChipPackage
This enum defines ESP32 module types (packages)
Definition: fabutils.h:935
void makeDirectory(char const *dirname)
Creates a directory.
Definition: fabutils.cpp:937
bool fileAccessDate(char const *name, int *year, int *month, int *day, int *hour, int *minutes, int *seconds)
Gets file access date and time.
Definition: fabutils.cpp:820
static DriveType getDriveType(char const *path)
Returns the drive type of specified path.
Definition: fabutils.cpp:1120
int16_t X2
Definition: fabutils.h:229
void changeDirectory(const char *subdir)
Sets relative directory path.
Definition: fabutils.cpp:682
FILE * openFile(char const *filename, char const *mode)
Opens a file from current directory.
Definition: fabutils.cpp:1101
static bool mountSPIFFS(bool formatOnFail, char const *mountPath, size_t maxFiles=4)
Mounts filesystem on SPIFFS (Flash)
Definition: fabutils.cpp:1278
bool exists(char const *name, bool caseSensitive=true)
Determines if a file or directory exists.
Definition: fabutils.cpp:745
bool reload()
Reloads directory content.
Definition: fabutils.cpp:850
static bool getFSInfo(DriveType driveType, int drive, int64_t *total, int64_t *used)
Gets total and free space on a filesystem.
Definition: fabutils.cpp:1309
static bool remountSPIFFS()
Remounts SPIFFS filesystem, using the same parameters.
Definition: fabutils.cpp:1302
static void unmountSPIFFS()
Unmounts filesystem on SPIFFS (Flash)
Definition: fabutils.cpp:1293