/*
xdrv_98_filesystem.ino - unified file system for Tasmota
Copyright (C) 2020 Gerhard Mutz and Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#ifdef USE_UFILESYS
/*********************************************************************************************\
This driver adds universal file system support for ESP8266 (sd card or littlfs on > 1 M devices
with special linker file e.g. eagle.flash.4m2m.ld) (makes no sense on 1M devices without sd card)
and ESP32 (sd card or little fs or sfatfile system).
The sd card chip select is the standard SDCARD_CS or when not found SDCARD_CS_PIN and initializes
the FS System Pointer ufsp which can be used by all standard file system calls.
The only specific call is ufs_fsinfo() which gets the total size (0) and free size (1).
A button is created in the setup section to show up the file directory to download and upload files
subdirectories are supported.
console calls :
ufs fs info
ufstype get filesytem type 0=none 1=SD 2=Flashfile
ufssize total size in kB
ufsfree free size in kB
The driver enabled by #define USE_UFILESYS
\*********************************************************************************************/
#define XDRV_98 98
#ifndef SDCARD_CS_PIN
#define SDCARD_CS_PIN 4
#endif
#ifdef ESP8266
#include
#include
#ifdef USE_SDCARD
#include
#include
#endif // USE_SDCARD
#endif // ESP8266
#ifdef ESP32
#include
#ifdef USE_SDCARD
#include
#endif // USE_SDCARD
#include "FFat.h"
#include "FS.h"
#endif // ESP32
#define UFS_FILE_WRITE "w"
#define UFS_FILE_READ "r"
// global file system pointer
FS *ufsp;
char ufs_path[48];
File ufs_upload_file;
// 0 = none, 1 = SD, 2 = ffat, 3 = littlefs
// spiffs should be obsolete
uint8_t ufs_type;
#define UFS_TNONE 0
#define UFS_TSDC 1
#define UFS_TFAT 2
#define UFS_TLFS 3
void UFSInit(void) {
ufs_type = 0;
// check for fs options,
// 1. check for SD card
// 2. check for littlefs or FAT
#ifdef USE_SDCARD
if (TasmotaGlobal.spi_enabled) {
// if (1) {
int8_t cs = SDCARD_CS_PIN;
if (PinUsed(GPIO_SDCARD_CS)) {
cs = Pin(GPIO_SDCARD_CS);
}
if (SD.begin(cs)) {
#ifdef ESP8266
ufsp = (FS*)&SD;
#endif // ESP8266
#ifdef ESP32
ufsp = &SD;
#endif // ESP32
ufs_type = UFS_TSDC;
return;
}
}
#endif // USE_SDCARD
// if no success with sd card try flash fs
#ifdef ESP8266
ufsp = &LittleFS;
if (!LittleFS.begin()) {
return;
}
#endif // ESP8266
#ifdef ESP32
// try lfs first
ufsp = &LITTLEFS;
if (!LITTLEFS.begin(true)) {
// ffat is second
ufsp = &FFat;
if (!FFat.begin(true)) {
return;
}
ufs_type = UFS_TFAT;
return;
}
#endif // ESP32
ufs_type = UFS_TLFS;
return;
}
uint32_t ufs_fsinfo(uint32_t sel) {
uint32_t result = 0;
#ifdef ESP8266
FSInfo64 fsinfo;
#endif // ESP8266
switch (ufs_type) {
case UFS_TSDC:
#ifdef USE_SDCARD
#ifdef ESP8266
ufsp->info64(fsinfo);
if (sel == 0) {
result = fsinfo.totalBytes;
} else {
result = (fsinfo.totalBytes - fsinfo.usedBytes);
}
#endif // ESP8266
#ifdef ESP32
if (sel == 0) {
result = SD.totalBytes();
} else {
result = (SD.totalBytes() - SD.usedBytes());
}
#endif
#endif //USE_SDCARD
break;
case UFS_TLFS:
#ifdef ESP8266
ufsp->info64(fsinfo);
if (sel == 0) {
result = fsinfo.totalBytes;
} else {
result = (fsinfo.totalBytes - fsinfo.usedBytes);
}
#endif // ESP8266
#ifdef ESP32
if (sel == 0) {
result = LITTLEFS.totalBytes();
} else {
result = LITTLEFS.totalBytes() - LITTLEFS.usedBytes();
}
#endif // ESP32
break;
case UFS_TFAT:
#ifdef ESP32
if (sel == 0) {
result = FFat.totalBytes();
} else {
result = FFat.freeBytes();
}
#endif // ESP32
break;
}
return result / 1000;
}
#if USE_LONG_FILE_NAMES>0
#undef REJCMPL
#define REJCMPL 6
#else
#undef REJCMPL
#define REJCMPL 8
#endif
uint8_t ufs_reject(char *name) {
char *lcp = strrchr(name,'/');
if (lcp) {
name = lcp + 1;
}
while (*name=='/') { name++; }
if (*name=='_') { return 1; }
if (*name=='.') { return 1; }
if (!strncasecmp(name, "SPOTLI~1", REJCMPL)) { return 1; }
if (!strncasecmp(name, "TRASHE~1", REJCMPL)) { return 1; }
if (!strncasecmp(name, "FSEVEN~1", REJCMPL)) { return 1; }
if (!strncasecmp(name, "SYSTEM~1", REJCMPL)) { return 1; }
if (!strncasecmp(name, "System Volume", 13)) { return 1; }
return 0;
}
// format number with thousand marker
void UFS_form1000(uint32_t number, char *dp, char sc) {
char str[32];
sprintf(str, "%d", number);
char *sp = str;
uint32_t inum = strlen(sp)/3;
uint32_t fnum = strlen(sp)%3;
if (!fnum) { inum--; }
for (uint32_t count = 0; count <= inum; count++) {
if (fnum) {
memcpy(dp, sp, fnum);
dp += fnum;
sp += fnum;
fnum = 0;
} else {
memcpy(dp, sp, 3);
dp += 3;
sp += 3;
}
if (count != inum) {
*dp++ = sc;
}
}
*dp = 0;
}
const char kUFSCommands[] PROGMEM = "Ufs" "|" // Prefix
"|" "Type" "|" "Size" "|" "Free";
void (* const kUFSCommand[])(void) PROGMEM = {
&UFS_info, &UFS_type, &UFS_size, &UFS_free};
void UFS_info(void) {
Response_P(PSTR("{\"Ufs\":{\"Type\":%d,\"Size\":%d,\"Free\":%d}}"), ufs_type, ufs_fsinfo(0), ufs_fsinfo(1));
}
void UFS_type(void) {
ResponseCmndNumber(ufs_type);
}
void UFS_size(void) {
ResponseCmndNumber(ufs_fsinfo(0));
}
void UFS_free(void) {
ResponseCmndNumber(ufs_fsinfo(1));
}
const char UFS_WEB_DIR[] PROGMEM =
"