mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-21 10:26:06 +03:00
Add time to filesystem API (#6544)
* Add time to filesystem API Support the ESP32 File::getLastWrite() call and setting the time on all filesystems automatically (assuming the system clock has been set properly and time(NULL) returns the proper time!). Adds Dir::fileTime() to get the time of a file being listed, similar to Dir::fileName() and Dir::fileSize(). Adds ::setTimeCallback(time_t (*cb)()) to File, Dir, and FS, allowing users to override the default timestamp on a per-file, directory, or filesystem basis. By default, a simple callback returning time(nullptr) is implemented. LittleFS uses the 't' attribute and should be backwards compatible. SD/SDFS work and include wrappers for obsolete SdFat timestamp callbacks using the MSDOS time. This PR does not update SPIFFS, due to compatability concerns and a possible massive rewrite which would make it possible to determine if an old-style ot metadata enabled FS is present at mount time. Includes an updated SD/listfiles and LittleFS_time example. Replaces #6315 * Add links to new mklittlefs w/timestamp support Include the update mklittlefs which generated 't' metadata on imported files. ../tools/sdk/lwip2/include/netif/lowpan6_opts.h * Add explicit note about timestamp being local time * Address review concerns Clean up some awkward object instantiations. Remove the _enableTime flag/setter from SPIFFS. Clean up the FSConfig constructors using C++ style init lists.
This commit is contained in:
parent
b4c28e74d6
commit
72dd589599
@ -180,6 +180,19 @@ String File::readString()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time_t File::getLastWrite() {
|
||||||
|
if (!_p)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return _p->getLastWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void File::setTimeCallback(time_t (*cb)(void)) {
|
||||||
|
if (!_p)
|
||||||
|
return;
|
||||||
|
_p->setTimeCallback(cb);
|
||||||
|
}
|
||||||
|
|
||||||
File Dir::openFile(const char* mode) {
|
File Dir::openFile(const char* mode) {
|
||||||
if (!_impl) {
|
if (!_impl) {
|
||||||
return File();
|
return File();
|
||||||
@ -192,7 +205,9 @@ File Dir::openFile(const char* mode) {
|
|||||||
return File();
|
return File();
|
||||||
}
|
}
|
||||||
|
|
||||||
return File(_impl->openFile(om, am), _baseFS);
|
File f(_impl->openFile(om, am), _baseFS);
|
||||||
|
f.setTimeCallback(timeCallback);
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
String Dir::fileName() {
|
String Dir::fileName() {
|
||||||
@ -203,6 +218,12 @@ String Dir::fileName() {
|
|||||||
return _impl->fileName();
|
return _impl->fileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time_t Dir::fileTime() {
|
||||||
|
if (!_impl)
|
||||||
|
return 0;
|
||||||
|
return _impl->fileTime();
|
||||||
|
}
|
||||||
|
|
||||||
size_t Dir::fileSize() {
|
size_t Dir::fileSize() {
|
||||||
if (!_impl) {
|
if (!_impl) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -241,6 +262,20 @@ bool Dir::rewind() {
|
|||||||
return _impl->rewind();
|
return _impl->rewind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time_t Dir::getLastWrite() {
|
||||||
|
if (!_impl)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return _impl->getLastWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dir::setTimeCallback(time_t (*cb)(void)) {
|
||||||
|
if (!_impl)
|
||||||
|
return;
|
||||||
|
_impl->setTimeCallback(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FS::setConfig(const FSConfig &cfg) {
|
bool FS::setConfig(const FSConfig &cfg) {
|
||||||
if (!_impl) {
|
if (!_impl) {
|
||||||
return false;
|
return false;
|
||||||
@ -315,7 +350,9 @@ File FS::open(const char* path, const char* mode) {
|
|||||||
DEBUGV("FS::open: invalid mode `%s`\r\n", mode);
|
DEBUGV("FS::open: invalid mode `%s`\r\n", mode);
|
||||||
return File();
|
return File();
|
||||||
}
|
}
|
||||||
return File(_impl->open(path, om, am), this);
|
File f(_impl->open(path, om, am), this);
|
||||||
|
f.setTimeCallback(timeCallback);
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FS::exists(const char* path) {
|
bool FS::exists(const char* path) {
|
||||||
@ -334,7 +371,9 @@ Dir FS::openDir(const char* path) {
|
|||||||
return Dir();
|
return Dir();
|
||||||
}
|
}
|
||||||
DirImplPtr p = _impl->openDir(path);
|
DirImplPtr p = _impl->openDir(path);
|
||||||
return Dir(p, this);
|
Dir d(p, this);
|
||||||
|
d.setTimeCallback(timeCallback);
|
||||||
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dir FS::openDir(const String& path) {
|
Dir FS::openDir(const String& path) {
|
||||||
@ -385,6 +424,11 @@ bool FS::rename(const String& pathFrom, const String& pathTo) {
|
|||||||
return rename(pathFrom.c_str(), pathTo.c_str());
|
return rename(pathFrom.c_str(), pathTo.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FS::setTimeCallback(time_t (*cb)(void)) {
|
||||||
|
if (!_impl)
|
||||||
|
return;
|
||||||
|
_impl->setTimeCallback(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool sflags(const char* mode, OpenMode& om, AccessMode& am) {
|
static bool sflags(const char* mode, OpenMode& om, AccessMode& am) {
|
||||||
|
@ -110,12 +110,16 @@ public:
|
|||||||
|
|
||||||
String readString() override;
|
String readString() override;
|
||||||
|
|
||||||
|
time_t getLastWrite();
|
||||||
|
void setTimeCallback(time_t (*cb)(void));
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FileImplPtr _p;
|
FileImplPtr _p;
|
||||||
|
|
||||||
// Arduino SD class emulation
|
// Arduino SD class emulation
|
||||||
std::shared_ptr<Dir> _fakeDir;
|
std::shared_ptr<Dir> _fakeDir;
|
||||||
FS *_baseFS;
|
FS *_baseFS;
|
||||||
|
time_t (*timeCallback)(void) = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Dir {
|
class Dir {
|
||||||
@ -126,15 +130,21 @@ public:
|
|||||||
|
|
||||||
String fileName();
|
String fileName();
|
||||||
size_t fileSize();
|
size_t fileSize();
|
||||||
|
time_t fileTime();
|
||||||
bool isFile() const;
|
bool isFile() const;
|
||||||
bool isDirectory() const;
|
bool isDirectory() const;
|
||||||
|
|
||||||
bool next();
|
bool next();
|
||||||
bool rewind();
|
bool rewind();
|
||||||
|
|
||||||
|
time_t getLastWrite();
|
||||||
|
void setTimeCallback(time_t (*cb)(void));
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DirImplPtr _impl;
|
DirImplPtr _impl;
|
||||||
FS *_baseFS;
|
FS *_baseFS;
|
||||||
|
time_t (*timeCallback)(void) = nullptr;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Backwards compatible, <4GB filesystem usage
|
// Backwards compatible, <4GB filesystem usage
|
||||||
@ -161,12 +171,10 @@ struct FSInfo64 {
|
|||||||
class FSConfig
|
class FSConfig
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FSConfig(bool autoFormat = true) {
|
static constexpr uint32_t FSId = 0x00000000;
|
||||||
_type = FSConfig::fsid::FSId;
|
|
||||||
_autoFormat = autoFormat;
|
FSConfig(uint32_t type = FSId, bool autoFormat = true) : _type(type), _autoFormat(autoFormat) { }
|
||||||
}
|
|
||||||
|
|
||||||
enum fsid { FSId = 0x00000000 };
|
|
||||||
FSConfig setAutoFormat(bool val = true) {
|
FSConfig setAutoFormat(bool val = true) {
|
||||||
_autoFormat = val;
|
_autoFormat = val;
|
||||||
return *this;
|
return *this;
|
||||||
@ -179,17 +187,17 @@ public:
|
|||||||
class SPIFFSConfig : public FSConfig
|
class SPIFFSConfig : public FSConfig
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SPIFFSConfig(bool autoFormat = true) {
|
static constexpr uint32_t FSId = 0x53504946;
|
||||||
_type = SPIFFSConfig::fsid::FSId;
|
SPIFFSConfig(bool autoFormat = true) : FSConfig(FSId, autoFormat) { }
|
||||||
_autoFormat = autoFormat;
|
|
||||||
}
|
// Inherit _type and _autoFormat
|
||||||
enum fsid { FSId = 0x53504946 };
|
// nothing yet, enableTime TBD when SPIFFS has metadate
|
||||||
};
|
};
|
||||||
|
|
||||||
class FS
|
class FS
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FS(FSImplPtr impl) : _impl(impl) { }
|
FS(FSImplPtr impl) : _impl(impl) { timeCallback = _defaultTimeCB; }
|
||||||
|
|
||||||
bool setConfig(const FSConfig &cfg);
|
bool setConfig(const FSConfig &cfg);
|
||||||
|
|
||||||
@ -225,10 +233,14 @@ public:
|
|||||||
bool gc();
|
bool gc();
|
||||||
bool check();
|
bool check();
|
||||||
|
|
||||||
|
void setTimeCallback(time_t (*cb)(void));
|
||||||
|
|
||||||
friend class ::SDClass; // More of a frenemy, but SD needs internal implementation to get private FAT bits
|
friend class ::SDClass; // More of a frenemy, but SD needs internal implementation to get private FAT bits
|
||||||
protected:
|
protected:
|
||||||
FSImplPtr _impl;
|
FSImplPtr _impl;
|
||||||
FSImplPtr getImpl() { return _impl; }
|
FSImplPtr getImpl() { return _impl; }
|
||||||
|
time_t (*timeCallback)(void);
|
||||||
|
static time_t _defaultTimeCB(void) { return time(NULL); }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fs
|
} // namespace fs
|
||||||
|
@ -41,6 +41,19 @@ public:
|
|||||||
virtual const char* fullName() const = 0;
|
virtual const char* fullName() const = 0;
|
||||||
virtual bool isFile() const = 0;
|
virtual bool isFile() const = 0;
|
||||||
virtual bool isDirectory() const = 0;
|
virtual bool isDirectory() const = 0;
|
||||||
|
|
||||||
|
// Filesystems *may* support a timestamp per-file, so allow the user to override with
|
||||||
|
// their own callback for *this specific* file (as opposed to the FSImpl call of the
|
||||||
|
// same name. The default implementation simply returns time(&null)
|
||||||
|
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
|
||||||
|
|
||||||
|
// Return the last written time for a file. Undefined when called on a writable file
|
||||||
|
// as the FS is allowed to return either the time of the last write() operation or the
|
||||||
|
// time present in the filesystem metadata (often the last time the file was closed)
|
||||||
|
virtual time_t getLastWrite() { return 0; } // Default is to not support timestamps
|
||||||
|
|
||||||
|
protected:
|
||||||
|
time_t (*timeCallback)(void) = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum OpenMode {
|
enum OpenMode {
|
||||||
@ -62,10 +75,24 @@ public:
|
|||||||
virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0;
|
virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0;
|
||||||
virtual const char* fileName() = 0;
|
virtual const char* fileName() = 0;
|
||||||
virtual size_t fileSize() = 0;
|
virtual size_t fileSize() = 0;
|
||||||
|
virtual time_t fileTime() { return 0; } // By default, FS doesn't report file times
|
||||||
virtual bool isFile() const = 0;
|
virtual bool isFile() const = 0;
|
||||||
virtual bool isDirectory() const = 0;
|
virtual bool isDirectory() const = 0;
|
||||||
virtual bool next() = 0;
|
virtual bool next() = 0;
|
||||||
virtual bool rewind() = 0;
|
virtual bool rewind() = 0;
|
||||||
|
|
||||||
|
// Filesystems *may* support a timestamp per-file, so allow the user to override with
|
||||||
|
// their own callback for *this specific* file (as opposed to the FSImpl call of the
|
||||||
|
// same name. The default implementation simply returns time(&null)
|
||||||
|
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
|
||||||
|
|
||||||
|
// Return the last written time for a file. Undefined when called on a writable file
|
||||||
|
// as the FS is allowed to return either the time of the last write() operation or the
|
||||||
|
// time present in the filesystem metadata (often the last time the file was closed)
|
||||||
|
virtual time_t getLastWrite() { return 0; } // Default is to not support timestamps
|
||||||
|
|
||||||
|
protected:
|
||||||
|
time_t (*timeCallback)(void) = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FSImpl {
|
class FSImpl {
|
||||||
@ -86,6 +113,14 @@ public:
|
|||||||
virtual bool rmdir(const char* path) = 0;
|
virtual bool rmdir(const char* path) = 0;
|
||||||
virtual bool gc() { return true; } // May not be implemented in all file systems.
|
virtual bool gc() { return true; } // May not be implemented in all file systems.
|
||||||
virtual bool check() { return true; } // May not be implemented in all file systems.
|
virtual bool check() { return true; } // May not be implemented in all file systems.
|
||||||
|
|
||||||
|
// Filesystems *may* support a timestamp per-file, so allow the user to override with
|
||||||
|
// their own callback for all files on this FS. The default implementation simply
|
||||||
|
// returns the present time as reported by time(&null)
|
||||||
|
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
time_t (*timeCallback)(void) = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fs
|
} // namespace fs
|
||||||
|
@ -162,7 +162,7 @@ public:
|
|||||||
|
|
||||||
bool setConfig(const FSConfig &cfg) override
|
bool setConfig(const FSConfig &cfg) override
|
||||||
{
|
{
|
||||||
if ((cfg._type != SPIFFSConfig::fsid::FSId) || (SPIFFS_mounted(&_fs) != 0)) {
|
if ((cfg._type != SPIFFSConfig::FSId) || (SPIFFS_mounted(&_fs) != 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_cfg = *static_cast<const SPIFFSConfig *>(&cfg);
|
_cfg = *static_cast<const SPIFFSConfig *>(&cfg);
|
||||||
|
@ -92,6 +92,27 @@ and ``SPIFFS.open()`` to ``LittleFS.open()`` with the rest of the
|
|||||||
code remaining untouched.
|
code remaining untouched.
|
||||||
|
|
||||||
|
|
||||||
|
SDFS and SD
|
||||||
|
-----------
|
||||||
|
FAT filesystems are supported on the ESP8266 using the old Arduino wrapper
|
||||||
|
"SD.h" which wraps the "SDFS.h" filesystem transparently.
|
||||||
|
|
||||||
|
Any commands discussed below pertaining to SPIFFS or LittleFS are
|
||||||
|
applicable to SD/SDFS.
|
||||||
|
|
||||||
|
For legacy applications, the classic SD filesystem may continue to be used,
|
||||||
|
but for new applications, directly accessing the SDFS filesystem is
|
||||||
|
recommended as it may expose additional functionality that the old Arduino
|
||||||
|
SD filesystem didn't have.
|
||||||
|
|
||||||
|
Note that in earlier releases of the core, using SD and SPIFFS in the same
|
||||||
|
sketch was complicated and required the use of ``NO_FS_GLOBALS``. The
|
||||||
|
current design makes SD, SDFS, SPIFFS, and LittleFS fully source compatible
|
||||||
|
and so please remove any ``NO_FS_GLOBALS`` definitions in your projects
|
||||||
|
when updgrading core versions.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SPIFFS file system limitations
|
SPIFFS file system limitations
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
@ -198,8 +219,8 @@ use esptool.py.
|
|||||||
- To upload a LittleFS filesystem use Tools > ESP8266 LittleFS Data Upload
|
- To upload a LittleFS filesystem use Tools > ESP8266 LittleFS Data Upload
|
||||||
|
|
||||||
|
|
||||||
File system object (SPIFFS/LittleFS)
|
File system object (SPIFFS/LittleFS/SD/SDFS)
|
||||||
------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
setConfig
|
setConfig
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
@ -369,41 +390,6 @@ rename
|
|||||||
Renames file from ``pathFrom`` to ``pathTo``. Paths must be absolute.
|
Renames file from ``pathFrom`` to ``pathTo``. Paths must be absolute.
|
||||||
Returns *true* if file was renamed successfully.
|
Returns *true* if file was renamed successfully.
|
||||||
|
|
||||||
info
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
.. code:: cpp
|
|
||||||
|
|
||||||
FSInfo fs_info;
|
|
||||||
SPIFFS.info(fs_info);
|
|
||||||
or LittleFS.info(fs_info);
|
|
||||||
|
|
||||||
Fills `FSInfo structure <#filesystem-information-structure>`__ with
|
|
||||||
information about the file system. Returns ``true`` is successful,
|
|
||||||
``false`` otherwise.
|
|
||||||
|
|
||||||
Filesystem information structure
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
.. code:: cpp
|
|
||||||
|
|
||||||
struct FSInfo {
|
|
||||||
size_t totalBytes;
|
|
||||||
size_t usedBytes;
|
|
||||||
size_t blockSize;
|
|
||||||
size_t pageSize;
|
|
||||||
size_t maxOpenFiles;
|
|
||||||
size_t maxPathLength;
|
|
||||||
};
|
|
||||||
|
|
||||||
This is the structure which may be filled using FS::info method. -
|
|
||||||
``totalBytes`` — total size of useful data on the file system -
|
|
||||||
``usedBytes`` — number of bytes used by files - ``blockSize`` — filesystem
|
|
||||||
block size - ``pageSize`` — filesystem logical page size - ``maxOpenFiles``
|
|
||||||
— max number of files which may be open simultaneously -
|
|
||||||
``maxPathLength`` — max file name length (including one byte for zero
|
|
||||||
termination)
|
|
||||||
|
|
||||||
gc
|
gc
|
||||||
~~
|
~~
|
||||||
|
|
||||||
@ -429,6 +415,81 @@ Only implemented in SPIFFS. Performs an in-depth check of the filesystem metada
|
|||||||
correct what is repairable. Not normally needed, and not guaranteed to actually fix
|
correct what is repairable. Not normally needed, and not guaranteed to actually fix
|
||||||
anything should there be corruption.
|
anything should there be corruption.
|
||||||
|
|
||||||
|
info
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
FSInfo fs_info;
|
||||||
|
SPIFFS.info(fs_info);
|
||||||
|
or LittleFS.info(fs_info);
|
||||||
|
|
||||||
|
Fills `FSInfo structure <#filesystem-information-structure>`__ with
|
||||||
|
information about the file system. Returns ``true`` if successful,
|
||||||
|
``false`` otherwise.
|
||||||
|
|
||||||
|
Filesystem information structure
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
struct FSInfo {
|
||||||
|
size_t totalBytes;
|
||||||
|
size_t usedBytes;
|
||||||
|
size_t blockSize;
|
||||||
|
size_t pageSize;
|
||||||
|
size_t maxOpenFiles;
|
||||||
|
size_t maxPathLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
This is the structure which may be filled using FS::info method. -
|
||||||
|
``totalBytes`` — total size of useful data on the file system -
|
||||||
|
``usedBytes`` — number of bytes used by files - ``blockSize`` — filesystem
|
||||||
|
block size - ``pageSize`` — filesystem logical page size - ``maxOpenFiles``
|
||||||
|
— max number of files which may be open simultaneously -
|
||||||
|
``maxPathLength`` — max file name length (including one byte for zero
|
||||||
|
termination)
|
||||||
|
|
||||||
|
info64
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
FSInfo64 fsinfo;
|
||||||
|
SD.info(fsinfo);
|
||||||
|
or LittleFS(fsinfo);
|
||||||
|
|
||||||
|
Performs the same operation as ``info`` but allows for reporting greater than
|
||||||
|
4GB for filesystem size/used/etc. Should be used with the SD and SDFS
|
||||||
|
filesystems since most SD cards today are greater than 4GB in size.
|
||||||
|
|
||||||
|
setTimeCallback(time_t (*cb)(void))
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
time_t myTimeCallback() {
|
||||||
|
return 1455451200; // UNIX timestamp
|
||||||
|
}
|
||||||
|
void setup () {
|
||||||
|
LittleFS.setTimeCallback(myTimeCallback);
|
||||||
|
...
|
||||||
|
// Any files will now be made with Pris' incept date
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
The SD, SDFS, and LittleFS filesystems support a file timestamp, updated when the file is
|
||||||
|
opened for writing. By default, the ESP8266 will use the internal time returned from
|
||||||
|
``time(NULL)`` (i.e. local time, not UTC, to conform to the existing FAT filesystem), but this
|
||||||
|
can be overridden to GMT or any other standard you'd like by using ``setTimeCallback()``.
|
||||||
|
If your app sets the system time using NTP before file operations, then
|
||||||
|
you should not need to use this function. However, if you need to set a specific time
|
||||||
|
for a file, or the system clock isn't correct and you need to read the time from an external
|
||||||
|
RTC or use a fixed time, this call allows you do to so.
|
||||||
|
|
||||||
|
In general use, with a functioning ``time()`` call, user applications should not need
|
||||||
|
to use this function.
|
||||||
|
|
||||||
Directory object (Dir)
|
Directory object (Dir)
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -468,6 +529,12 @@ fileSize
|
|||||||
Returns the size of the current file pointed to
|
Returns the size of the current file pointed to
|
||||||
by the internal iterator.
|
by the internal iterator.
|
||||||
|
|
||||||
|
fileTime
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Returns the time_t write time of the current file pointed
|
||||||
|
to by the internal iterator.
|
||||||
|
|
||||||
isFile
|
isFile
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
@ -491,6 +558,13 @@ rewind
|
|||||||
|
|
||||||
Resets the internal pointer to the start of the directory.
|
Resets the internal pointer to the start of the directory.
|
||||||
|
|
||||||
|
setTimeCallback(time_t (*cb)(void))
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Sets the time callback for any files accessed from this Dir object via openNextFile.
|
||||||
|
Note that the SD and SDFS filesystems only support a filesystem-wide callback and
|
||||||
|
calls to ``Dir::setTimeCallback`` may produce unexpected behavior.
|
||||||
|
|
||||||
File object
|
File object
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
@ -562,6 +636,12 @@ fullName
|
|||||||
|
|
||||||
Returns the full path file name as a ``const char*``.
|
Returns the full path file name as a ``const char*``.
|
||||||
|
|
||||||
|
getLastWrite
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Returns the file last write time, and only valid for files opened in read-only
|
||||||
|
mode. If a file is opened for writing, the returned time may be indeterminate.
|
||||||
|
|
||||||
isFile
|
isFile
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
@ -616,3 +696,10 @@ rewindDirectory (compatibiity method, not recommended for new code)
|
|||||||
|
|
||||||
Resets the ``openNextFile`` pointer to the top of the directory. Only
|
Resets the ``openNextFile`` pointer to the top of the directory. Only
|
||||||
valid when ``File.isDirectory() == true``.
|
valid when ``File.isDirectory() == true``.
|
||||||
|
|
||||||
|
setTimeCallback(time_t (*cb)(void))
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Sets the time callback for this specific file. Note that the SD and
|
||||||
|
SDFS filesystems only support a filesystem-wide callback and calls to
|
||||||
|
``Dir::setTimeCallback`` may produce unexpected behavior.
|
||||||
|
@ -0,0 +1,181 @@
|
|||||||
|
/* Example showing timestamp support in LittleFS */
|
||||||
|
/* Released into the public domain. */
|
||||||
|
/* Earle F. Philhower, III <earlephilhower@yahoo.com> */
|
||||||
|
|
||||||
|
#include <FS.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
|
||||||
|
#ifndef STASSID
|
||||||
|
#define STASSID "your-ssid"
|
||||||
|
#define STAPSK "your-password"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char *ssid = STASSID;
|
||||||
|
const char *pass = STAPSK;
|
||||||
|
|
||||||
|
long timezone = 2;
|
||||||
|
byte daysavetime = 1;
|
||||||
|
|
||||||
|
|
||||||
|
bool getLocalTime(struct tm * info, uint32_t ms) {
|
||||||
|
uint32_t count = ms / 10;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
time(&now);
|
||||||
|
localtime_r(&now, info);
|
||||||
|
|
||||||
|
if (info->tm_year > (2016 - 1900)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (count--) {
|
||||||
|
delay(10);
|
||||||
|
time(&now);
|
||||||
|
localtime_r(&now, info);
|
||||||
|
if (info->tm_year > (2016 - 1900)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void listDir(const char * dirname) {
|
||||||
|
Serial.printf("Listing directory: %s\n", dirname);
|
||||||
|
|
||||||
|
Dir root = LittleFS.openDir(dirname);
|
||||||
|
|
||||||
|
while (root.next()) {
|
||||||
|
File file = root.openFile("r");
|
||||||
|
Serial.print(" FILE: ");
|
||||||
|
Serial.print(root.fileName());
|
||||||
|
Serial.print(" SIZE: ");
|
||||||
|
Serial.print(file.size());
|
||||||
|
time_t t = file.getLastWrite();
|
||||||
|
struct tm * tmstruct = localtime(&t);
|
||||||
|
file.close();
|
||||||
|
Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void readFile(const char * path) {
|
||||||
|
Serial.printf("Reading file: %s\n", path);
|
||||||
|
|
||||||
|
File file = LittleFS.open(path, "r");
|
||||||
|
if (!file) {
|
||||||
|
Serial.println("Failed to open file for reading");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Read from file: ");
|
||||||
|
while (file.available()) {
|
||||||
|
Serial.write(file.read());
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeFile(const char * path, const char * message) {
|
||||||
|
Serial.printf("Writing file: %s\n", path);
|
||||||
|
|
||||||
|
File file = LittleFS.open(path, "w");
|
||||||
|
if (!file) {
|
||||||
|
Serial.println("Failed to open file for writing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (file.print(message)) {
|
||||||
|
Serial.println("File written");
|
||||||
|
} else {
|
||||||
|
Serial.println("Write failed");
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendFile(const char * path, const char * message) {
|
||||||
|
Serial.printf("Appending to file: %s\n", path);
|
||||||
|
|
||||||
|
File file = LittleFS.open(path, "a");
|
||||||
|
if (!file) {
|
||||||
|
Serial.println("Failed to open file for appending");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (file.print(message)) {
|
||||||
|
Serial.println("Message appended");
|
||||||
|
} else {
|
||||||
|
Serial.println("Append failed");
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renameFile(const char * path1, const char * path2) {
|
||||||
|
Serial.printf("Renaming file %s to %s\n", path1, path2);
|
||||||
|
if (LittleFS.rename(path1, path2)) {
|
||||||
|
Serial.println("File renamed");
|
||||||
|
} else {
|
||||||
|
Serial.println("Rename failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteFile(const char * path) {
|
||||||
|
Serial.printf("Deleting file: %s\n", path);
|
||||||
|
if (LittleFS.remove(path)) {
|
||||||
|
Serial.println("File deleted");
|
||||||
|
} else {
|
||||||
|
Serial.println("Delete failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
// We start by connecting to a WiFi network
|
||||||
|
Serial.println();
|
||||||
|
Serial.println();
|
||||||
|
Serial.print("Connecting to ");
|
||||||
|
Serial.println(ssid);
|
||||||
|
|
||||||
|
WiFi.begin(ssid, pass);
|
||||||
|
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
Serial.println("WiFi connected");
|
||||||
|
Serial.println("IP address: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
Serial.println("Contacting Time Server");
|
||||||
|
configTime(3600 * timezone, daysavetime * 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
|
||||||
|
struct tm tmstruct ;
|
||||||
|
delay(2000);
|
||||||
|
tmstruct.tm_year = 0;
|
||||||
|
getLocalTime(&tmstruct, 5000);
|
||||||
|
Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
|
||||||
|
Serial.println("");
|
||||||
|
|
||||||
|
Serial.printf("Formatting LittleFS filesystem\n");
|
||||||
|
LittleFS.format();
|
||||||
|
listDir("/");
|
||||||
|
deleteFile("/hello.txt");
|
||||||
|
writeFile("/hello.txt", "Hello ");
|
||||||
|
appendFile("/hello.txt", "World!\n");
|
||||||
|
listDir("/");
|
||||||
|
|
||||||
|
Serial.printf("The timestamp should be valid above\n");
|
||||||
|
|
||||||
|
Serial.printf("Now unmount and remount and perform the same operation.\n");
|
||||||
|
Serial.printf("Timestamp should be valid, data should be good.\n");
|
||||||
|
LittleFS.end();
|
||||||
|
Serial.printf("Now mount it\n");
|
||||||
|
if (!LittleFS.begin()) {
|
||||||
|
Serial.println("LittleFS mount failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
readFile("/hello.txt");
|
||||||
|
listDir("/");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() { }
|
||||||
|
|
@ -47,11 +47,8 @@ class LittleFSDirImpl;
|
|||||||
class LittleFSConfig : public FSConfig
|
class LittleFSConfig : public FSConfig
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LittleFSConfig(bool autoFormat = true) {
|
static constexpr uint32_t FSId = 0x4c495454;
|
||||||
_type = LittleFSConfig::fsid::FSId;
|
LittleFSConfig(bool autoFormat = true) : FSConfig(FSId, autoFormat) { }
|
||||||
_autoFormat = autoFormat;
|
|
||||||
}
|
|
||||||
enum fsid { FSId = 0x4c495454 };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LittleFSImpl : public FSImpl
|
class LittleFSImpl : public FSImpl
|
||||||
@ -176,7 +173,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool setConfig(const FSConfig &cfg) override {
|
bool setConfig(const FSConfig &cfg) override {
|
||||||
if ((cfg._type != LittleFSConfig::fsid::FSId) || _mounted) {
|
if ((cfg._type != LittleFSConfig::FSId) || _mounted) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_cfg = *static_cast<const LittleFSConfig *>(&cfg);
|
_cfg = *static_cast<const LittleFSConfig *>(&cfg);
|
||||||
@ -422,8 +419,26 @@ public:
|
|||||||
lfs_file_close(_fs->getFS(), _getFD());
|
lfs_file_close(_fs->getFS(), _getFD());
|
||||||
_opened = false;
|
_opened = false;
|
||||||
DEBUGV("lfs_file_close: fd=%p\n", _getFD());
|
DEBUGV("lfs_file_close: fd=%p\n", _getFD());
|
||||||
|
if (timeCallback) {
|
||||||
|
// Add metadata with last write time
|
||||||
|
time_t now = timeCallback();
|
||||||
|
int rc = lfs_setattr(_fs->getFS(), _name.get(), 't', (const void *)&now, sizeof(now));
|
||||||
|
if (rc < 0) {
|
||||||
|
DEBUGV("Unable to set time on '%s' to %d\n", _name.get(), now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t getLastWrite() override {
|
||||||
|
time_t ftime = 0;
|
||||||
|
if (_opened && _fd) {
|
||||||
|
int rc = lfs_getattr(_fs->getFS(), _name.get(), 't', (void *)&ftime, sizeof(ftime));
|
||||||
|
if (rc != sizeof(ftime))
|
||||||
|
ftime = 0; // Error, so clear read value
|
||||||
|
}
|
||||||
|
return ftime;
|
||||||
|
}
|
||||||
|
|
||||||
const char* name() const override {
|
const char* name() const override {
|
||||||
if (!_opened) {
|
if (!_opened) {
|
||||||
@ -520,6 +535,27 @@ public:
|
|||||||
return _dirent.size;
|
return _dirent.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time_t fileTime() override {
|
||||||
|
if (!_valid) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int nameLen = 3; // Slashes, terminator
|
||||||
|
nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0;
|
||||||
|
nameLen += strlen(_dirent.name);
|
||||||
|
char *tmpName = (char*)malloc(nameLen);
|
||||||
|
if (!tmpName) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
|
||||||
|
time_t ftime = 0;
|
||||||
|
int rc = lfs_getattr(_fs->getFS(), tmpName, 't', (void *)&ftime, sizeof(ftime));
|
||||||
|
if (rc != sizeof(ftime))
|
||||||
|
ftime = 0; // Error, so clear read value
|
||||||
|
free(tmpName);
|
||||||
|
return ftime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool isFile() const override {
|
bool isFile() const override {
|
||||||
return _valid && (_dirent.type == LFS_TYPE_REG);
|
return _valid && (_dirent.type == LFS_TYPE_REG);
|
||||||
}
|
}
|
||||||
|
@ -28,14 +28,14 @@ File root;
|
|||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// Open serial communications and wait for port to open:
|
// Open serial communications and wait for port to open:
|
||||||
Serial.begin(9600);
|
Serial.begin(115200);
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
; // wait for serial port to connect. Needed for Leonardo only
|
; // wait for serial port to connect. Needed for Leonardo only
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.print("Initializing SD card...");
|
Serial.print("Initializing SD card...");
|
||||||
|
|
||||||
if (!SD.begin(4)) {
|
if (!SD.begin(SS)) {
|
||||||
Serial.println("initialization failed!");
|
Serial.println("initialization failed!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -70,11 +70,13 @@ void printDirectory(File dir, int numTabs) {
|
|||||||
} else {
|
} else {
|
||||||
// files have sizes, directories do not
|
// files have sizes, directories do not
|
||||||
Serial.print("\t\t");
|
Serial.print("\t\t");
|
||||||
Serial.println(entry.size(), DEC);
|
Serial.print(entry.size(), DEC);
|
||||||
|
Serial.print("\t\t");
|
||||||
|
time_t ft = entry.getLastWrite();
|
||||||
|
struct tm *tm = localtime(&ft);
|
||||||
|
// US format. Feel free to convert to your own locale...
|
||||||
|
Serial.printf("%02d-%02d-%02d %02d:%02d:%02d\n", tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100, tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||||
}
|
}
|
||||||
entry.close();
|
entry.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,3 +3,5 @@
|
|||||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD)
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD)
|
||||||
SDClass SD;
|
SDClass SD;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void (*__SD__userDateTimeCB)(uint16_t*, uint16_t*) = nullptr;
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#undef FILE_WRITE
|
#undef FILE_WRITE
|
||||||
#define FILE_WRITE (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT | sdfat::O_APPEND)
|
#define FILE_WRITE (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT | sdfat::O_APPEND)
|
||||||
|
|
||||||
|
|
||||||
class SDClass {
|
class SDClass {
|
||||||
public:
|
public:
|
||||||
boolean begin(uint8_t csPin, SPISettings cfg = SPI_HALF_SPEED) {
|
boolean begin(uint8_t csPin, SPISettings cfg = SPI_HALF_SPEED) {
|
||||||
@ -137,6 +138,17 @@ public:
|
|||||||
return ((uint64_t)clusterSize() * (uint64_t)totalClusters());
|
return ((uint64_t)clusterSize() * (uint64_t)totalClusters());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setTimeCallback(time_t (*cb)(void)) {
|
||||||
|
SDFS.setTimeCallback(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper to allow obsolete datetimecallback use, silently convert to time_t in wrappertimecb
|
||||||
|
void dateTimeCallback(void (*cb)(uint16_t*, uint16_t*)) {
|
||||||
|
extern void (*__SD__userDateTimeCB)(uint16_t*, uint16_t*);
|
||||||
|
__SD__userDateTimeCB = cb;
|
||||||
|
SDFS.setTimeCallback(wrapperTimeCB);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char *getMode(uint8_t mode) {
|
const char *getMode(uint8_t mode) {
|
||||||
bool read = (mode & sdfat::O_READ) ? true : false;
|
bool read = (mode & sdfat::O_READ) ? true : false;
|
||||||
@ -150,8 +162,46 @@ private:
|
|||||||
else { return "r"; }
|
else { return "r"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static time_t wrapperTimeCB(void) {
|
||||||
|
extern void (*__SD__userDateTimeCB)(uint16_t*, uint16_t*);
|
||||||
|
if (__SD__userDateTimeCB) {
|
||||||
|
uint16_t d, t;
|
||||||
|
__SD__userDateTimeCB(&d, &t);
|
||||||
|
return sdfs::SDFSImpl::FatToTimeT(d, t);
|
||||||
|
}
|
||||||
|
return time(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Expose FatStructs.h helpers for MSDOS date/time for use with dateTimeCallback
|
||||||
|
static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) {
|
||||||
|
return (year - 1980) << 9 | month << 5 | day;
|
||||||
|
}
|
||||||
|
static inline uint16_t FAT_YEAR(uint16_t fatDate) {
|
||||||
|
return 1980 + (fatDate >> 9);
|
||||||
|
}
|
||||||
|
static inline uint8_t FAT_MONTH(uint16_t fatDate) {
|
||||||
|
return (fatDate >> 5) & 0XF;
|
||||||
|
}
|
||||||
|
static inline uint8_t FAT_DAY(uint16_t fatDate) {
|
||||||
|
return fatDate & 0X1F;
|
||||||
|
}
|
||||||
|
static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) {
|
||||||
|
return hour << 11 | minute << 5 | second >> 1;
|
||||||
|
}
|
||||||
|
static inline uint8_t FAT_HOUR(uint16_t fatTime) {
|
||||||
|
return fatTime >> 11;
|
||||||
|
}
|
||||||
|
static inline uint8_t FAT_MINUTE(uint16_t fatTime) {
|
||||||
|
return (fatTime >> 5) & 0X3F;
|
||||||
|
}
|
||||||
|
static inline uint8_t FAT_SECOND(uint16_t fatTime) {
|
||||||
|
return 2*(fatTime & 0X1F);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD)
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD)
|
||||||
extern SDClass SD;
|
extern SDClass SD;
|
||||||
#endif
|
#endif
|
||||||
|
@ -45,22 +45,9 @@ class SDFSDirImpl;
|
|||||||
class SDFSConfig : public FSConfig
|
class SDFSConfig : public FSConfig
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SDFSConfig() {
|
static constexpr uint32_t FSId = 0x53444653;
|
||||||
_type = SDFSConfig::fsid::FSId;
|
|
||||||
_autoFormat = false;
|
|
||||||
_csPin = 4;
|
|
||||||
_spiSettings = SD_SCK_MHZ(10);
|
|
||||||
_part = 0;
|
|
||||||
}
|
|
||||||
SDFSConfig(uint8_t csPin, SPISettings spi) {
|
|
||||||
_type = SDFSConfig::fsid::FSId;
|
|
||||||
_autoFormat = false;
|
|
||||||
_csPin = csPin;
|
|
||||||
_spiSettings = spi;
|
|
||||||
_part = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum fsid { FSId = 0x53444653 };
|
SDFSConfig(uint8_t csPin = 4, SPISettings spi = SD_SCK_MHZ(10)) : FSConfig(FSId, false), _csPin(csPin), _part(0), _spiSettings(spi) { }
|
||||||
|
|
||||||
SDFSConfig setAutoFormat(bool val = true) {
|
SDFSConfig setAutoFormat(bool val = true) {
|
||||||
_autoFormat = val;
|
_autoFormat = val;
|
||||||
@ -152,7 +139,7 @@ public:
|
|||||||
|
|
||||||
bool setConfig(const FSConfig &cfg) override
|
bool setConfig(const FSConfig &cfg) override
|
||||||
{
|
{
|
||||||
if ((cfg._type != SDFSConfig::fsid::FSId) || _mounted) {
|
if ((cfg._type != SDFSConfig::FSId) || _mounted) {
|
||||||
DEBUGV("SDFS::setConfig: invalid config or already mounted\n");
|
DEBUGV("SDFS::setConfig: invalid config or already mounted\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -203,6 +190,20 @@ public:
|
|||||||
return (clusterSize() * totalClusters());
|
return (clusterSize() * totalClusters());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function, takes FAT and makes standard time_t
|
||||||
|
static time_t FatToTimeT(uint16_t d, uint16_t t) {
|
||||||
|
struct tm tiempo;
|
||||||
|
memset(&tiempo, 0, sizeof(tiempo));
|
||||||
|
tiempo.tm_sec = (((int)t) << 1) & 0x3e;
|
||||||
|
tiempo.tm_min = (((int)t) >> 5) & 0x3f;
|
||||||
|
tiempo.tm_hour = (((int)t) >> 11) & 0x1f;
|
||||||
|
tiempo.tm_mday = (int)(d & 0x1f);
|
||||||
|
tiempo.tm_mon = ((int)(d >> 5) & 0x0f) - 1;
|
||||||
|
tiempo.tm_year = ((int)(d >> 9) & 0x7f) + 80;
|
||||||
|
tiempo.tm_isdst = -1;
|
||||||
|
return mktime(&tiempo);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class SDFileImpl;
|
friend class SDFileImpl;
|
||||||
friend class SDFSDirImpl;
|
friend class SDFSDirImpl;
|
||||||
@ -212,6 +213,7 @@ protected:
|
|||||||
return &_fs;
|
return &_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static uint8_t _getFlags(OpenMode openMode, AccessMode accessMode) {
|
static uint8_t _getFlags(OpenMode openMode, AccessMode accessMode) {
|
||||||
uint8_t mode = 0;
|
uint8_t mode = 0;
|
||||||
if (openMode & OM_CREATE) {
|
if (openMode & OM_CREATE) {
|
||||||
@ -350,6 +352,17 @@ public:
|
|||||||
return _opened ? _fd->isDirectory() : false;
|
return _opened ? _fd->isDirectory() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time_t getLastWrite() override {
|
||||||
|
time_t ftime = 0;
|
||||||
|
if (_opened && _fd) {
|
||||||
|
sdfat::dir_t tmp;
|
||||||
|
if (_fd.get()->dirEntry(&tmp)) {
|
||||||
|
ftime = SDFSImpl::FatToTimeT(tmp.lastWriteDate, tmp.lastWriteTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ftime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SDFSImpl* _fs;
|
SDFSImpl* _fs;
|
||||||
@ -425,6 +438,12 @@ public:
|
|||||||
_size = file.fileSize();
|
_size = file.fileSize();
|
||||||
_isFile = file.isFile();
|
_isFile = file.isFile();
|
||||||
_isDirectory = file.isDirectory();
|
_isDirectory = file.isDirectory();
|
||||||
|
sdfat::dir_t tmp;
|
||||||
|
if (file.dirEntry(&tmp)) {
|
||||||
|
_time = SDFSImpl::FatToTimeT(tmp.lastWriteDate, tmp.lastWriteTime);
|
||||||
|
} else {
|
||||||
|
_time = 0;
|
||||||
|
}
|
||||||
file.getName(_lfn, sizeof(_lfn));
|
file.getName(_lfn, sizeof(_lfn));
|
||||||
file.close();
|
file.close();
|
||||||
} else {
|
} else {
|
||||||
@ -447,6 +466,7 @@ protected:
|
|||||||
std::shared_ptr<sdfat::File> _dir;
|
std::shared_ptr<sdfat::File> _dir;
|
||||||
bool _valid;
|
bool _valid;
|
||||||
char _lfn[64];
|
char _lfn[64];
|
||||||
|
time_t _time;
|
||||||
std::shared_ptr<char> _dirPath;
|
std::shared_ptr<char> _dirPath;
|
||||||
uint32_t _size;
|
uint32_t _size;
|
||||||
bool _isFile;
|
bool _isFile;
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"packager": "esp8266",
|
"packager": "esp8266",
|
||||||
"version": "2.5.0-4-b40a506",
|
"version": "2.5.0-4-69bd9e6",
|
||||||
"name": "mklittlefs"
|
"name": "mklittlefs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -302,57 +302,50 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"version": "2.5.0-4-b40a506",
|
"version": "2.5.0-4-69bd9e6",
|
||||||
"name": "mklittlefs",
|
"name": "mklittlefs",
|
||||||
"systems": [
|
"systems": [
|
||||||
{
|
{
|
||||||
"host": "aarch64-linux-gnu",
|
"host": "aarch64-linux-gnu",
|
||||||
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/aarch64-linux-gnu.mklittlefs-7f77f2b.1563313032.tar.gz",
|
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/aarch64-linux-gnu-mklittlefs-69bd9e6.tar.gz",
|
||||||
"archiveFileName": "aarch64-linux-gnu.mklittlefs-7f77f2b.1563313032.tar.gz",
|
"archiveFileName": "aarch64-linux-gnu-mklittlefs-69bd9e6.tar.gz",
|
||||||
"checksum": "SHA-256:25c4dcf818d175e19c3cc22bc0388c61fa3d9bdf82a1fad388323cef34caa169",
|
"checksum": "SHA-256:74d938f15a3fb8ac20aeb0f938ace2c6759f622451419c09446aa79866302e18",
|
||||||
"size": "44059"
|
"size": "44342"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"host": "arm-linux-gnueabihf",
|
"host": "arm-linux-gnueabihf",
|
||||||
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/arm-linux-gnueabihf.mklittlefs-7f77f2b.1563313032.tar.gz",
|
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/arm-linux-gnueabihf-mklittlefs-69bd9e6.tar.gz",
|
||||||
"archiveFileName": "arm-linux-gnueabihf.mklittlefs-7f77f2b.1563313032.tar.gz",
|
"archiveFileName": "arm-linux-gnueabihf-mklittlefs-69bd9e6.tar.gz",
|
||||||
"checksum": "SHA-256:75a284f4e8c54d302b1880df46dd48e18857f69c21baa0977b1e6efc404caf18",
|
"checksum": "SHA-256:926cca1c1f8f732a8ac79809ce0a52cabe283ab4137aa3237bca0fcca6bc2236",
|
||||||
"size": "36567"
|
"size": "36871"
|
||||||
},
|
|
||||||
{
|
|
||||||
"host": "i686-pc-linux-gnu",
|
|
||||||
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/i686-linux-gnu.mklittlefs-7f77f2b.1563313032.tar.gz",
|
|
||||||
"archiveFileName": "i686-linux-gnu.mklittlefs-7f77f2b.1563313032.tar.gz",
|
|
||||||
"checksum": "SHA-256:022c96df4d110f957d43f6d23e9c5e8b699a66d8ab041056dd5da7411a8ade42",
|
|
||||||
"size": "47544"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"host": "i686-mingw32",
|
"host": "i686-mingw32",
|
||||||
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/i686-w64-mingw32.mklittlefs-7f77f2b.1563313032.zip",
|
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/i686-w64-mingw32-mklittlefs-69bd9e6.zip",
|
||||||
"archiveFileName": "i686-w64-mingw32.mklittlefs-7f77f2b.1563313032.zip",
|
"archiveFileName": "i686-w64-mingw32-mklittlefs-69bd9e6.zip",
|
||||||
"checksum": "SHA-256:7778209e9df8c8c5f5da82660ff9a95b866defee3c9eb5c22371e0fd84b1addc",
|
"checksum": "SHA-256:da916c66f70e162f4aec22dbcb4542dd8b8187d12c35c915d563e2262cfe6fbd",
|
||||||
"size": "332057"
|
"size": "332325"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"host": "x86_64-apple-darwin",
|
"host": "x86_64-apple-darwin",
|
||||||
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-apple-darwin14.mklittlefs-7f77f2b.1563313032.tar.gz",
|
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-apple-darwin14-mklittlefs-69bd9e6.tar.gz",
|
||||||
"archiveFileName": "x86_64-apple-darwin14.mklittlefs-7f77f2b.1563313032.tar.gz",
|
"archiveFileName": "x86_64-apple-darwin14-mklittlefs-69bd9e6.tar.gz",
|
||||||
"checksum": "SHA-256:c465da766026c6c66d731442b741fb5a7f8b741e9473d181e6c5e588c541f588",
|
"checksum": "SHA-256:35610be5f725121eaa9baea83c686693f340742e61739af6789d00feff4e90ba",
|
||||||
"size": "362014"
|
"size": "362366"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"host": "x86_64-pc-linux-gnu",
|
"host": "x86_64-pc-linux-gnu",
|
||||||
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-linux-gnu.mklittlefs-7f77f2b.1563313032.tar.gz",
|
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-linux-gnu-mklittlefs-69bd9e6.tar.gz",
|
||||||
"archiveFileName": "x86_64-linux-gnu.mklittlefs-7f77f2b.1563313032.tar.gz",
|
"archiveFileName": "x86_64-linux-gnu-mklittlefs-69bd9e6.tar.gz",
|
||||||
"checksum": "SHA-256:6a358716d4c780fa459b4c774723302431b3ad5e1ee3f7edae62be331541615c",
|
"checksum": "SHA-256:e4ce7cc80eceab6a9a2e620f2badfb1ef09ee88f7af529f290c65b4b72f19358",
|
||||||
"size": "46164"
|
"size": "46518"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"host": "x86_64-mingw32",
|
"host": "x86_64-mingw32",
|
||||||
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-w64-mingw32.mklittlefs-7f77f2b.1563313032.zip",
|
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-w64-mingw32-mklittlefs-69bd9e6.zip",
|
||||||
"archiveFileName": "x86_64-w64-mingw32.mklittlefs-7f77f2b.1563313032.zip",
|
"archiveFileName": "x86_64-w64-mingw32-mklittlefs-69bd9e6.zip",
|
||||||
"checksum": "SHA-256:d5d44b5f21681a831318a23b31957bc9368c50f0766964ead409c3d2fe4747d2",
|
"checksum": "SHA-256:c65ee1ee38f65ce67f664bb3118301ee6e93bec38a7a7efaf8e1d8455c6a4a18",
|
||||||
"size": "344578"
|
"size": "344780"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user