diff --git a/libraries/FS/library.properties b/libraries/FS/library.properties new file mode 100644 index 00000000..fb35385b --- /dev/null +++ b/libraries/FS/library.properties @@ -0,0 +1,9 @@ +name=FS +version=1.0 +author=Hristo Gochkov, Ivan Grokhtkov +maintainer=Hristo Gochkov +sentence=ESP32 File System +paragraph= +category=Data Storage +url= +architectures=esp32 diff --git a/libraries/FS/src/FS.cpp b/libraries/FS/src/FS.cpp new file mode 100644 index 00000000..3bddf07e --- /dev/null +++ b/libraries/FS/src/FS.cpp @@ -0,0 +1,260 @@ +/* + FS.cpp - file system wrapper + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "FS.h" +#include "FSImpl.h" + +using namespace fs; + +size_t File::write(uint8_t c) +{ + if (!_p) { + return 0; + } + + return _p->write(&c, 1); +} + +size_t File::write(const uint8_t *buf, size_t size) +{ + if (!_p) { + return 0; + } + + return _p->write(buf, size); +} + +int File::available() +{ + if (!_p) { + return false; + } + + return _p->size() - _p->position(); +} + +int File::read() +{ + if (!_p) { + return -1; + } + + uint8_t result; + if (_p->read(&result, 1) != 1) { + return -1; + } + + return result; +} + +size_t File::read(uint8_t* buf, size_t size) +{ + if (!_p) { + return -1; + } + + return _p->read(buf, size); +} + +int File::peek() +{ + if (!_p) { + return -1; + } + + size_t curPos = _p->position(); + int result = read(); + seek(curPos, SeekSet); + return result; +} + +void File::flush() +{ + if (!_p) { + return; + } + + _p->flush(); +} + +bool File::seek(uint32_t pos, SeekMode mode) +{ + if (!_p) { + return false; + } + + return _p->seek(pos, mode); +} + +size_t File::position() const +{ + if (!_p) { + return 0; + } + + return _p->position(); +} + +size_t File::size() const +{ + if (!_p) { + return 0; + } + + return _p->size(); +} + +void File::close() +{ + if (_p) { + _p->close(); + _p = nullptr; + } +} + +File::operator bool() const +{ + return !!_p; +} + +const char* File::name() const +{ + if (!_p) { + return nullptr; + } + + return _p->name(); +} + +//to implement +boolean File::isDirectory(void) +{ + if (!_p) { + return false; + } + return _p->isDirectory(); +} + +File File::openNextFile(const char* mode) +{ + if (!_p) { + return File(); + } + return _p->openNextFile(mode); +} + +void File::rewindDirectory(void) +{ + if (!_p) { + return; + } + _p->rewindDirectory(); +} + +File FS::open(const String& path, const char* mode) +{ + return open(path.c_str(), mode); +} + +File FS::open(const char* path, const char* mode) +{ + if (!_impl) { + return File(); + } + + return File(_impl->open(path, mode)); +} + +bool FS::exists(const char* path) +{ + if (!_impl) { + return false; + } + return _impl->exists(path); +} + +bool FS::exists(const String& path) +{ + return exists(path.c_str()); +} + +bool FS::remove(const char* path) +{ + if (!_impl) { + return false; + } + return _impl->remove(path); +} + +bool FS::remove(const String& path) +{ + return remove(path.c_str()); +} + +bool FS::rename(const char* pathFrom, const char* pathTo) +{ + if (!_impl) { + return false; + } + return _impl->rename(pathFrom, pathTo); +} + +bool FS::rename(const String& pathFrom, const String& pathTo) +{ + return rename(pathFrom.c_str(), pathTo.c_str()); +} + + +bool FS::mkdir(const char *path) +{ + if (!_impl) { + return false; + } + return _impl->mkdir(path); +} + +bool FS::mkdir(const String &path) +{ + return mkdir(path.c_str()); +} + +bool FS::rmdir(const char *path) +{ + if (!_impl) { + return false; + } + return _impl->rmdir(path); +} + +bool FS::rmdir(const String &path) +{ + return rmdir(path.c_str()); +} + + +void FSImpl::mountpoint(const char * mp) +{ + _mountpoint = mp; +} + +const char * FSImpl::mountpoint() +{ + return _mountpoint; +} diff --git a/libraries/FS/src/FS.h b/libraries/FS/src/FS.h new file mode 100644 index 00000000..d9e06bd2 --- /dev/null +++ b/libraries/FS/src/FS.h @@ -0,0 +1,122 @@ +/* + FS.h - file system wrapper + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FS_H +#define FS_H + +#include +#include + +namespace fs +{ + +#define FILE_READ "r" +#define FILE_WRITE "w" +#define FILE_APPEND "a" + +class File; + +class FileImpl; +typedef std::shared_ptr FileImplPtr; +class FSImpl; +typedef std::shared_ptr FSImplPtr; + +enum SeekMode { + SeekSet = 0, + SeekCur = 1, + SeekEnd = 2 +}; + +class File : public Stream +{ +public: + File(FileImplPtr p = FileImplPtr()) : _p(p) {} + + size_t write(uint8_t) override; + size_t write(const uint8_t *buf, size_t size) override; + int available() override; + int read() override; + int peek() override; + void flush() override; + size_t read(uint8_t* buf, size_t size); + size_t readBytes(char *buffer, size_t length) + { + return read((uint8_t*)buffer, length); + } + + bool seek(uint32_t pos, SeekMode mode); + bool seek(uint32_t pos) + { + return seek(pos, SeekSet); + } + size_t position() const; + size_t size() const; + void close(); + operator bool() const; + const char* name() const; + + boolean isDirectory(void); + File openNextFile(const char* mode = FILE_READ); + void rewindDirectory(void); + +protected: + FileImplPtr _p; +}; + +class FS +{ +public: + FS(FSImplPtr impl) : _impl(impl) { } + + File open(const char* path, const char* mode = FILE_READ); + File open(const String& path, const char* mode = FILE_READ); + + bool exists(const char* path); + bool exists(const String& path); + + bool remove(const char* path); + bool remove(const String& path); + + bool rename(const char* pathFrom, const char* pathTo); + bool rename(const String& pathFrom, const String& pathTo); + + bool mkdir(const char *path); + bool mkdir(const String &path); + + bool rmdir(const char *path); + bool rmdir(const String &path); + + +protected: + FSImplPtr _impl; +}; + +} // namespace fs + +#ifndef FS_NO_GLOBALS +using fs::FS; +using fs::File; +using fs::SeekMode; +using fs::SeekSet; +using fs::SeekCur; +using fs::SeekEnd; +#endif //FS_NO_GLOBALS + +#endif //FS_H diff --git a/libraries/FS/src/FSImpl.h b/libraries/FS/src/FSImpl.h new file mode 100644 index 00000000..df1f875d --- /dev/null +++ b/libraries/FS/src/FSImpl.h @@ -0,0 +1,66 @@ +/* + FSImpl.h - base file system interface + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef FSIMPL_H +#define FSIMPL_H + +#include +#include + +namespace fs +{ + +class FileImpl +{ +public: + virtual ~FileImpl() { } + virtual size_t write(const uint8_t *buf, size_t size) = 0; + virtual size_t read(uint8_t* buf, size_t size) = 0; + virtual void flush() = 0; + virtual bool seek(uint32_t pos, SeekMode mode) = 0; + virtual size_t position() const = 0; + virtual size_t size() const = 0; + virtual void close() = 0; + virtual const char* name() const = 0; + virtual boolean isDirectory(void) = 0; + virtual FileImplPtr openNextFile(const char* mode) = 0; + virtual void rewindDirectory(void) = 0; + virtual operator bool() = 0; +}; + +class FSImpl +{ +protected: + const char * _mountpoint; +public: + FSImpl() : _mountpoint(NULL) { } + virtual ~FSImpl() { } + virtual FileImplPtr open(const char* path, const char* mode) = 0; + virtual bool exists(const char* path) = 0; + virtual bool rename(const char* pathFrom, const char* pathTo) = 0; + virtual bool remove(const char* path) = 0; + virtual bool mkdir(const char *path) = 0; + virtual bool rmdir(const char *path) = 0; + void mountpoint(const char *); + const char * mountpoint(); +}; + +} // namespace fs + +#endif //FSIMPL_H diff --git a/libraries/FS/src/vfs_api.cpp b/libraries/FS/src/vfs_api.cpp new file mode 100644 index 00000000..1654c98c --- /dev/null +++ b/libraries/FS/src/vfs_api.cpp @@ -0,0 +1,417 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "vfs_api.h" + +using namespace fs; + +FileImplPtr VFSImpl::open(const char* path, const char* mode) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return FileImplPtr(); + } + + if(!path || path[0] != '/') { + log_e("%s does not start with /", path); + return FileImplPtr(); + } + + char * temp = (char *)malloc(strlen(path)+strlen(_mountpoint)+2); + if(!temp) { + log_e("malloc failed"); + return FileImplPtr(); + } + + sprintf(temp,"%s%s", _mountpoint, path); + + struct stat st; + //file lound + if(!stat(temp, &st)) { + free(temp); + if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) { + return std::make_shared(this, path, mode); + } + log_e("%s has wrong mode 0x%08X", path, st.st_mode); + return FileImplPtr(); + } + + //file not found but mode permits creation + if(mode && mode[0] != 'r') { + free(temp); + return std::make_shared(this, path, mode); + } + + //try to open this as directory (might be mount point) + DIR * d = opendir(temp); + if(d) { + closedir(d); + free(temp); + return std::make_shared(this, path, mode); + } + + log_e("%s does not exist", temp); + free(temp); + return FileImplPtr(); +} + +bool VFSImpl::exists(const char* path) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return false; + } + + VFSFileImpl f(this, path, "r"); + if(f) { + f.close(); + return true; + } + return false; +} + +static inline int real_rename(const char* f, const char* t) +{ + return rename(f,t); +} + +bool VFSImpl::rename(const char* pathFrom, const char* pathTo) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return false; + } + + if(!pathFrom || pathFrom[0] != '/' || !pathTo || pathTo[0] != '/') { + log_e("bad arguments"); + return false; + } + if(!exists(pathFrom)) { + log_e("%s does not exists", pathFrom); + return false; + } + char * temp1 = (char *)malloc(strlen(pathFrom)+strlen(_mountpoint)+1); + if(!temp1) { + log_e("malloc failed"); + return false; + } + char * temp2 = (char *)malloc(strlen(pathTo)+strlen(_mountpoint)+1); + if(!temp2) { + free(temp1); + log_e("malloc failed"); + return false; + } + sprintf(temp1,"%s%s", _mountpoint, pathFrom); + sprintf(temp2,"%s%s", _mountpoint, pathTo); + auto rc = real_rename(temp1, temp2); + free(temp1); + free(temp2); + return rc == 0; +} + +bool VFSImpl::remove(const char* path) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return false; + } + + if(!path || path[0] != '/') { + log_e("bad arguments"); + return false; + } + + VFSFileImpl f(this, path, "r"); + if(!f || f.isDirectory()) { + if(f) { + f.close(); + } + log_e("%s does not exists or is directory", path); + return false; + } + f.close(); + + char * temp = (char *)malloc(strlen(path)+strlen(_mountpoint)+1); + if(!temp) { + log_e("malloc failed"); + return false; + } + sprintf(temp,"%s%s", _mountpoint, path); + auto rc = unlink(temp); + free(temp); + return rc == 0; +} + +static inline int real_mkdir(const char* f) +{ + return mkdir(f, ACCESSPERMS); +} + +bool VFSImpl::mkdir(const char *path) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return false; + } + + VFSFileImpl f(this, path, "r"); + if(f && f.isDirectory()) { + f.close(); + //log_w("%s already exists", path); + return true; + } else if(f) { + f.close(); + log_e("%s is a file", path); + return false; + } + + char * temp = (char *)malloc(strlen(path)+strlen(_mountpoint)+1); + if(!temp) { + log_e("malloc failed"); + return false; + } + sprintf(temp,"%s%s", _mountpoint, path); + auto rc = real_mkdir(temp); + free(temp); + return rc == 0; +} + +bool VFSImpl::rmdir(const char *path) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return false; + } + + VFSFileImpl f(this, path, "r"); + if(!f || !f.isDirectory()) { + if(f) { + f.close(); + } + log_e("%s does not exists or is a file", path); + return false; + } + f.close(); + + char * temp = (char *)malloc(strlen(path)+strlen(_mountpoint)+1); + if(!temp) { + log_e("malloc failed"); + return false; + } + sprintf(temp,"%s%s", _mountpoint, path); + auto rc = unlink(temp); + free(temp); + return rc == 0; +} + + + + +VFSFileImpl::VFSFileImpl(VFSImpl* fs, const char* path, const char* mode) + : _fs(fs) + , _f(NULL) + , _d(NULL) + , _path(NULL) + , _isDirectory(false) + , _written(false) +{ + char * temp = (char *)malloc(strlen(path)+strlen(_fs->_mountpoint)+1); + if(!temp) { + return; + } + sprintf(temp,"%s%s", _fs->_mountpoint, path); + + _path = strdup(path); + if(!_path) { + log_e("strdup(%s) failed", path); + free(temp); + return; + } + + if(!stat(temp, &_stat)) { + //file found + if (S_ISREG(_stat.st_mode)) { + _isDirectory = false; + _f = fopen(temp, mode); + if(!_f) { + log_e("fopen(%s) failed", temp); + } + } else if(S_ISDIR(_stat.st_mode)) { + _isDirectory = true; + _d = opendir(temp); + if(!_d) { + log_e("opendir(%s) failed", temp); + } + } else { + log_e("Unknown type 0x%08X for file %s", ((_stat.st_mode)&_IFMT), temp); + } + } else { + //file not found + if(!mode || mode[0] == 'r') { + //try to open as directory + _d = opendir(temp); + if(_d) { + _isDirectory = true; + } else { + _isDirectory = false; + //log_w("stat(%s) failed", temp); + } + } else { + //lets create this new file + _isDirectory = false; + _f = fopen(temp, mode); + if(!_f) { + log_e("fopen(%s) failed", temp); + } + } + } + free(temp); +} + +VFSFileImpl::~VFSFileImpl() +{ + close(); +} + +void VFSFileImpl::close() +{ + if(_path) { + free(_path); + _path = NULL; + } + if(_isDirectory && _d) { + closedir(_d); + _d = NULL; + _isDirectory = false; + } else if(_f) { + fclose(_f); + _f = NULL; + } +} + +VFSFileImpl::operator bool() +{ + return (_isDirectory && _d != NULL) || _f != NULL; +} + +void VFSFileImpl::_getStat() const +{ + if(!_path) { + return; + } + char * temp = (char *)malloc(strlen(_path)+strlen(_fs->_mountpoint)+1); + if(!temp) { + return; + } + sprintf(temp,"%s%s", _fs->_mountpoint, _path); + if(!stat(temp, &_stat)) { + _written = false; + } + free(temp); +} + +size_t VFSFileImpl::write(const uint8_t *buf, size_t size) +{ + if(_isDirectory || !_f || !buf || !size) { + return 0; + } + _written = true; + return fwrite(buf, 1, size, _f); +} + +size_t VFSFileImpl::read(uint8_t* buf, size_t size) +{ + if(_isDirectory || !_f || !buf || !size) { + return 0; + } + + return fread(buf, 1, size, _f); +} + +void VFSFileImpl::flush() +{ + if(_isDirectory || !_f) { + return; + } + fflush(_f); +} + +bool VFSFileImpl::seek(uint32_t pos, SeekMode mode) +{ + if(_isDirectory || !_f) { + return false; + } + auto rc = fseek(_f, pos, mode); + return rc == 0; +} + +size_t VFSFileImpl::position() const +{ + if(_isDirectory || !_f) { + return 0; + } + return ftell(_f); +} + +size_t VFSFileImpl::size() const +{ + if(_isDirectory || !_f) { + return 0; + } + if (_written) { + _getStat(); + } + return _stat.st_size; +} + +const char* VFSFileImpl::name() const +{ + return (const char*) _path; +} + +//to implement +boolean VFSFileImpl::isDirectory(void) +{ + return _isDirectory; +} + +FileImplPtr VFSFileImpl::openNextFile(const char* mode) +{ + if(!_isDirectory || !_d) { + return FileImplPtr(); + } + struct dirent *file = readdir(_d); + if(file == NULL) { + return FileImplPtr(); + } + if(file->d_type != DT_REG && file->d_type != DT_DIR) { + return openNextFile(mode); + } + String fname = String(file->d_name); + String name = String(_path); + if(!fname.startsWith("/") && !name.endsWith("/")) { + name += "/"; + } + name += fname; + + return std::make_shared(_fs, name.c_str(), mode); +} + +void VFSFileImpl::rewindDirectory(void) +{ + if(!_isDirectory || !_d) { + return; + } + rewinddir(_d); +} diff --git a/libraries/FS/src/vfs_api.h b/libraries/FS/src/vfs_api.h new file mode 100644 index 00000000..e407212b --- /dev/null +++ b/libraries/FS/src/vfs_api.h @@ -0,0 +1,76 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef vfs_api_h +#define vfs_api_h + +#include "FS.h" +#include "FSImpl.h" + +extern "C" { +#include +#include +#include +} + +using namespace fs; + +class VFSFileImpl; + +class VFSImpl : public FSImpl +{ + +protected: + friend class VFSFileImpl; + +public: + FileImplPtr open(const char* path, const char* mode) override; + bool exists(const char* path) override; + bool rename(const char* pathFrom, const char* pathTo) override; + bool remove(const char* path) override; + bool mkdir(const char *path) override; + bool rmdir(const char *path) override; +}; + +class VFSFileImpl : public FileImpl +{ +protected: + VFSImpl* _fs; + FILE * _f; + DIR * _d; + char * _path; + bool _isDirectory; + mutable struct stat _stat; + mutable bool _written; + + void _getStat() const; + +public: + VFSFileImpl(VFSImpl* fs, const char* path, const char* mode); + ~VFSFileImpl() override; + size_t write(const uint8_t *buf, size_t size) override; + size_t read(uint8_t* buf, size_t size) override; + void flush() override; + bool seek(uint32_t pos, SeekMode mode) override; + size_t position() const override; + size_t size() const override; + void close() override; + const char* name() const override; + boolean isDirectory(void) override; + FileImplPtr openNextFile(const char* mode) override; + void rewindDirectory(void) override; + operator bool(); +}; + +#endif diff --git a/libraries/SD/examples/SD_Test/SD_Test.ino b/libraries/SD/examples/SD_Test/SD_Test.ino new file mode 100644 index 00000000..b232ddd3 --- /dev/null +++ b/libraries/SD/examples/SD_Test/SD_Test.ino @@ -0,0 +1,202 @@ +#include "FS.h" +#include "SD.h" +#include "SPI.h" + +void setup(){ + Serial.begin(115200); + if(!SD.begin()){ + Serial.println("Card Mount Failed"); + return; + } + uint8_t cardType = SD.cardType(); + + if(cardType == CARD_NONE){ + Serial.println("No SD card attached"); + return; + } + + Serial.print("SD Card Type: "); + if(cardType == CARD_MMC){ + Serial.println("MMC"); + } else if(cardType == CARD_SD){ + Serial.println("SDSC"); + } else if(cardType == CARD_SDHC){ + Serial.println("SDHC"); + } else { + Serial.println("UNKNOWN"); + } + + uint64_t cardSize = SD.cardSize() / (1024 * 1024); + Serial.printf("SD Card Size: %lluMB\n", cardSize); + + listDir(SD, "/", 0); + createDir(SD, "/mydir"); + listDir(SD, "/", 0); + removeDir(SD, "/mydir"); + listDir(SD, "/", 2); + writeFile(SD, "/hello.txt", "Hello "); + appendFile(SD, "/hello.txt", "World!\n"); + readFile(SD, "/hello.txt"); + deleteFile(SD, "/foo.txt"); + renameFile(SD, "/hello.txt", "/foo.txt"); + readFile(SD, "/foo.txt"); + testFileIO(SD, "/test.txt"); +} + +void loop(){ + +} + +void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ + Serial.printf("Listing directory: %s\n", dirname); + + File root = fs.open(dirname); + if(!root){ + Serial.println("Failed to open directory"); + return; + } + if(!root.isDirectory()){ + Serial.println("Not a directory"); + return; + } + + File file = root.openNextFile(); + while(file){ + if(file.isDirectory()){ + Serial.print(" DIR : "); + Serial.println(file.name()); + if(levels){ + listDir(fs, file.name(), levels -1); + } + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + Serial.println(file.size()); + } + file = root.openNextFile(); + } +} + +void createDir(fs::FS &fs, const char * path){ + Serial.printf("Creating Dir: %s\n", path); + if(fs.mkdir(path)){ + Serial.println("Dir created"); + } else { + Serial.println("mkdir failed"); + } +} + +void removeDir(fs::FS &fs, const char * path){ + Serial.printf("Removing Dir: %s\n", path); + if(fs.rmdir(path)){ + Serial.println("Dir removed"); + } else { + Serial.println("rmdir failed"); + } +} + +void readFile(fs::FS &fs, const char * path){ + Serial.printf("Reading file: %s\n", path); + + File file = fs.open(path); + if(!file){ + Serial.println("Failed to open file for reading"); + return; + } + + Serial.print("Read from file: "); + while(file.available()){ + Serial.write(file.read()); + } +} + +void writeFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Writing file: %s\n", path); + + File file = fs.open(path, FILE_WRITE); + if(!file){ + Serial.println("Failed to open file for writing"); + return; + } + if(file.print(message)){ + Serial.println("File written"); + } else { + Serial.println("Write failed"); + } +} + +void appendFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Appending to file: %s\n", path); + + File file = fs.open(path, FILE_APPEND); + if(!file){ + Serial.println("Failed to open file for appending"); + return; + } + if(file.print(message)){ + Serial.println("Message appended"); + } else { + Serial.println("Append failed"); + } +} + +void renameFile(fs::FS &fs, const char * path1, const char * path2){ + Serial.printf("Renaming file %s to %s\n", path1, path2); + if (fs.rename(path1, path2)) { + Serial.println("File renamed"); + } else { + Serial.println("Rename failed"); + } +} + +void deleteFile(fs::FS &fs, const char * path){ + Serial.printf("Deleting file: %s\n", path); + if(fs.remove(path)){ + Serial.println("File deleted"); + } else { + Serial.println("Delete failed"); + } +} + +void testFileIO(fs::FS &fs, const char * path){ + File file = fs.open(path); + static uint8_t buf[512]; + size_t len = 0; + uint32_t start = millis(); + uint32_t end = start; + if(file){ + len = file.size(); + size_t flen = len; + start = millis(); + while(len){ + size_t toRead = len; + if(toRead > 512){ + toRead = 512; + } + file.read(buf, toRead); + len -= toRead; + } + end = millis() - start; + Serial.printf("%u bytes read for %u ms\n", flen, end); + file.close(); + } else { + Serial.println("Failed to open file for reading"); + } + + + file = fs.open(path, FILE_WRITE); + if(!file){ + Serial.println("Failed to open file for writing"); + return; + } + + size_t i; + start = millis(); + for(i=0; i<2048; i++){ + file.write(buf, 512); + } + end = millis() - start; + Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); + file.close(); +} diff --git a/libraries/SD/library.properties b/libraries/SD/library.properties new file mode 100644 index 00000000..63183b73 --- /dev/null +++ b/libraries/SD/library.properties @@ -0,0 +1,9 @@ +name=SD(esp32) +version=1.0.5 +author=Arduino, SparkFun +maintainer=Arduino +sentence=Enables reading and writing on SD cards. For all Arduino boards. +paragraph=Once an SD memory card is connected to the SPI interfare of the Arduino board you are enabled to create files and read/write on them. You can also move through directories on the SD card. +category=Data Storage +url=http://www.arduino.cc/en/Reference/SD +architectures=esp32 diff --git a/libraries/SD/src/SD.cpp b/libraries/SD/src/SD.cpp new file mode 100644 index 00000000..65e68c0f --- /dev/null +++ b/libraries/SD/src/SD.cpp @@ -0,0 +1,76 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "vfs_api.h" +#include "sd_diskio.h" +#include "FS.h" +#include "SD.h" + +using namespace fs; + +SDFS::SDFS(FSImplPtr impl): FS(impl), _pdrv(0xFF) {} + +bool SDFS::begin(uint8_t ssPin, SPIClass &spi, uint32_t frequency, const char * mountpoint) +{ + if(_pdrv != 0xFF) { + return true; + } + + spi.begin(); + + _pdrv = sdcard_init(ssPin, &spi, frequency); + if(_pdrv == 0xFF) { + return false; + } + + if(!sdcard_mount(_pdrv, mountpoint)){ + sdcard_uninit(_pdrv); + _pdrv = 0xFF; + return false; + } + + _impl->mountpoint(mountpoint); + return true; +} + +void SDFS::end() +{ + if(_pdrv != 0xFF) { + _impl->mountpoint(NULL); + sdcard_unmount(_pdrv); + + sdcard_uninit(_pdrv); + _pdrv = 0xFF; + } +} + +sdcard_type_t SDFS::cardType() +{ + if(_pdrv == 0xFF) { + return CARD_NONE; + } + return sdcard_type(_pdrv); +} + +uint64_t SDFS::cardSize() +{ + if(_pdrv == 0xFF) { + return 0; + } + size_t sectors = sdcard_num_sectors(_pdrv); + size_t sectorSize = sdcard_sector_size(_pdrv); + return (uint64_t)sectors * sectorSize; +} + +SDFS SD = SDFS(FSImplPtr(new VFSImpl())); diff --git a/libraries/SD/src/SD.h b/libraries/SD/src/SD.h new file mode 100644 index 00000000..cf49bb8f --- /dev/null +++ b/libraries/SD/src/SD.h @@ -0,0 +1,46 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SD_H_ +#define _SD_H_ + +#include "FS.h" +#include "SPI.h" +#include "sd_defines.h" + +namespace fs +{ + +class SDFS : public FS +{ +protected: + uint8_t _pdrv; + +public: + SDFS(FSImplPtr impl); + bool begin(uint8_t ssPin=SS, SPIClass &spi=SPI, uint32_t frequency=4000000, const char * mountpoint="/sd"); + void end(); + sdcard_type_t cardType(); + uint64_t cardSize(); +}; + +} + +extern fs::SDFS SD; + +using namespace fs; +typedef fs::File SDFile; +typedef fs::SDFS SDFileSystemClass; +#define SDFileSystem SD + +#endif /* _SD_H_ */ diff --git a/libraries/SD/src/sd_defines.h b/libraries/SD/src/sd_defines.h new file mode 100644 index 00000000..6e42855a --- /dev/null +++ b/libraries/SD/src/sd_defines.h @@ -0,0 +1,25 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SD_DEFINES_H_ +#define _SD_DEFINES_H_ + +typedef enum { + CARD_NONE, + CARD_MMC, + CARD_SD, + CARD_SDHC, + CARD_UNKNOWN +} sdcard_type_t; + +#endif /* _SD_DISKIO_H_ */ diff --git a/libraries/SD/src/sd_diskio.cpp b/libraries/SD/src/sd_diskio.cpp new file mode 100644 index 00000000..32c9cd65 --- /dev/null +++ b/libraries/SD/src/sd_diskio.cpp @@ -0,0 +1,740 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "sd_diskio.h" +extern "C" { + #include "diskio.h" + #include "ffconf.h" + #include "ff.h" + #include "esp_vfs.h" + #include "esp_vfs_fat.h" + char CRC7(const char* data, int length); + unsigned short CRC16(const char* data, int length); +} + +typedef enum { + GO_IDLE_STATE = 0, + SEND_OP_COND = 1, + SEND_CID = 2, + SEND_RELATIVE_ADDR = 3, + SEND_SWITCH_FUNC = 6, + SEND_IF_COND = 8, + SEND_CSD = 9, + STOP_TRANSMISSION = 12, + SEND_STATUS = 13, + SET_BLOCKLEN = 16, + READ_BLOCK_SINGLE = 17, + READ_BLOCK_MULTIPLE = 18, + SEND_NUM_WR_BLOCKS = 22, + SET_WR_BLK_ERASE_COUNT = 23, + WRITE_BLOCK_SINGLE = 24, + WRITE_BLOCK_MULTIPLE = 25, + APP_OP_COND = 41, + APP_CLR_CARD_DETECT = 42, + APP_CMD = 55, + READ_OCR = 58, + CRC_ON_OFF = 59 +} ardu_sdcard_command_t; + +typedef struct { + uint8_t ssPin; + SPIClass * spi; + int frequency; + char * base_path; + sdcard_type_t type; + unsigned long sectors; + bool supports_crc; + int status; +} ardu_sdcard_t; + +static ardu_sdcard_t* s_cards[_VOLUMES] = { NULL }; + +/* + * SD SPI + * */ + +bool sdWait(uint8_t pdrv, int timeout) +{ + char resp; + uint32_t start = millis(); + + do { + resp = s_cards[pdrv]->spi->transfer(0xFF); + } while (resp == 0x00 && (millis() - start) < (unsigned int)timeout); + + return (resp > 0x00); +} + +void sdStop(uint8_t pdrv) +{ + s_cards[pdrv]->spi->write(0xFD); +} + +void sdDeselectCard(uint8_t pdrv) +{ + ardu_sdcard_t * card = s_cards[pdrv]; + digitalWrite(card->ssPin, HIGH); + card->spi->write(0xFF); +} + +bool sdSelectCard(uint8_t pdrv) +{ + ardu_sdcard_t * card = s_cards[pdrv]; + digitalWrite(card->ssPin, LOW); + card->spi->write(0xFF); + + if (sdWait(pdrv, 500)) { + return true; + } else { + log_e("timeout"); + sdDeselectCard(pdrv); + return false; + } +} + +char sdCommand(uint8_t pdrv, char cmd, unsigned int arg, unsigned int* resp) +{ + char token; + ardu_sdcard_t * card = s_cards[pdrv]; + + for (int f = 0; f < 3; f++) { + if (cmd == SEND_NUM_WR_BLOCKS || cmd == SET_WR_BLK_ERASE_COUNT || cmd == APP_OP_COND || cmd == APP_CLR_CARD_DETECT) { + token = sdCommand(pdrv, APP_CMD, 0, NULL); + sdDeselectCard(pdrv); + if (token > 1) { + return token; + } + if(!sdSelectCard(pdrv)) { + return 0xFF; + } + } + + char cmdPacket[7]; + cmdPacket[0] = cmd | 0x40; + cmdPacket[1] = arg >> 24; + cmdPacket[2] = arg >> 16; + cmdPacket[3] = arg >> 8; + cmdPacket[4] = arg; + if(card->supports_crc || cmd == GO_IDLE_STATE || cmd == SEND_IF_COND) { + cmdPacket[5] = (CRC7(cmdPacket, 5) << 1) | 0x01; + } else { + cmdPacket[5] = 0x01; + } + cmdPacket[6] = 0xFF; + + card->spi->writeBytes((uint8_t*)cmdPacket, (cmd == STOP_TRANSMISSION)?7:6); + + for (int i = 0; i < 9; i++) { + token = card->spi->transfer(0xFF); + if (!(token & 0x80)) { + break; + } + } + + if (token == 0xFF) { + log_e("no token received"); + break; + } else if (token & 0x08) { + log_w("crc error"); + continue; + } else if (token > 1) { + log_w("token error [%u] 0x%x", cmd, token); + break; + } + + if (cmd == SEND_STATUS && resp) { + *resp = card->spi->transfer(0xFF); + } else if ((cmd == SEND_IF_COND || cmd == READ_OCR) && resp) { + *resp = card->spi->transfer32(0xFFFFFFFF); + } + + break; + } + + return token; +} + +bool sdReadBytes(uint8_t pdrv, char* buffer, int length) +{ + char token; + unsigned short crc; + ardu_sdcard_t * card = s_cards[pdrv]; + + uint32_t start = millis(); + do { + token = card->spi->transfer(0xFF); + } while (token == 0xFF && (millis() - start) < 500); + + if (token != 0xFE) { + return false; + } + + card->spi->transferBytes(NULL, (uint8_t*)buffer, length); + crc = card->spi->transfer16(0xFFFF); + return (!card->supports_crc || crc == CRC16(buffer, length)); +} + +char sdWriteBytes(uint8_t pdrv, const char* buffer, char token) +{ + ardu_sdcard_t * card = s_cards[pdrv]; + unsigned short crc = (card->supports_crc)?CRC16(buffer, 512):0xFFFF; + if (!sdWait(pdrv, 500)) { + return false; + } + + card->spi->write(token); + card->spi->writeBytes((uint8_t*)buffer, 512); + card->spi->write16(crc); + return (card->spi->transfer(0xFF) & 0x1F); +} + +/* + * SPI SDCARD Communication + * */ + +char sdTransaction(uint8_t pdrv, char cmd, unsigned int arg, unsigned int* resp) +{ + if(!sdSelectCard(pdrv)) { + return 0xFF; + } + char token = sdCommand(pdrv, cmd, arg, resp); + sdDeselectCard(pdrv); + return token; +} + +bool sdReadSector(uint8_t pdrv, char* buffer, unsigned long long sector) +{ + for (int f = 0; f < 3; f++) { + if(!sdSelectCard(pdrv)) { + break; + } + if (!sdCommand(pdrv, READ_BLOCK_SINGLE, (s_cards[pdrv]->type == CARD_SDHC) ? sector : sector << 9, NULL)) { + bool success = sdReadBytes(pdrv, buffer, 512); + sdDeselectCard(pdrv); + if (success) { + return true; + } + } else { + break; + } + } + sdDeselectCard(pdrv); + return false; +} + +bool sdReadSectors(uint8_t pdrv, char* buffer, unsigned long long sector, int count) +{ + for (int f = 0; f < 3;) { + if(!sdSelectCard(pdrv)) { + break; + } + + if (!sdCommand(pdrv, READ_BLOCK_MULTIPLE, (s_cards[pdrv]->type == CARD_SDHC) ? sector : sector << 9, NULL)) { + do { + if (!sdReadBytes(pdrv, buffer, 512)) { + f++; + break; + } + + sector++; + buffer += 512; + f = 0; + } while (--count); + + if (sdCommand(pdrv, STOP_TRANSMISSION, 0, NULL)) { + log_e("command failed"); + break; + } + + sdDeselectCard(pdrv); + if (count == 0) { + return true; + } + } else { + break; + } + } + sdDeselectCard(pdrv); + return false; +} + +bool sdWriteSector(uint8_t pdrv, const char* buffer, unsigned long long sector) +{ + for (int f = 0; f < 3; f++) { + if(!sdSelectCard(pdrv)) { + break; + } + if (!sdCommand(pdrv, WRITE_BLOCK_SINGLE, (s_cards[pdrv]->type == CARD_SDHC) ? sector : sector << 9, NULL)) { + char token = sdWriteBytes(pdrv, buffer, 0xFE); + sdDeselectCard(pdrv); + + if (token == 0x0A) { + continue; + } else if (token == 0x0C) { + return false; + } + + unsigned int resp; + if (sdTransaction(pdrv, SEND_STATUS, 0, &resp) || resp) { + return false; + } + return true; + } else { + break; + } + } + sdDeselectCard(pdrv); + return false; +} + +bool sdWriteSectors(uint8_t pdrv, const char* buffer, unsigned long long sector, int count) +{ + char token; + const char* currentBuffer = buffer; + unsigned long long currentSector = sector; + int currentCount = count; + ardu_sdcard_t * card = s_cards[pdrv]; + + for (int f = 0; f < 3;) { + if (card->type != CARD_MMC) { + if (sdTransaction(pdrv, SET_WR_BLK_ERASE_COUNT, currentCount, NULL)) { + break; + } + } + + if(!sdSelectCard(pdrv)) { + break; + } + + if (!sdCommand(pdrv, WRITE_BLOCK_MULTIPLE, (card->type == CARD_SDHC) ? currentSector : currentSector << 9, NULL)) { + do { + token = sdWriteBytes(pdrv, currentBuffer, 0xFC); + if (token != 0x05) { + f++; + break; + } + currentBuffer += 512; + f = 0; + } while (--currentCount); + + if (!sdWait(pdrv, 500)) { + break; + } + + if (currentCount == 0) { + sdStop(pdrv); + sdDeselectCard(pdrv); + + unsigned int resp; + if (sdTransaction(pdrv, SEND_STATUS, 0, &resp) || resp) { + return false; + } + return true; + } else { + if (sdCommand(pdrv, STOP_TRANSMISSION, 0, NULL)) { + break; + } + + sdDeselectCard(pdrv); + + if (token == 0x0A) { + unsigned int writtenBlocks = 0; + if (card->type != CARD_MMC && sdSelectCard(pdrv)) { + if (!sdCommand(pdrv, SEND_NUM_WR_BLOCKS, 0, NULL)) { + char acmdData[4]; + if (sdReadBytes(pdrv, acmdData, 4)) { + writtenBlocks = acmdData[0] << 24; + writtenBlocks |= acmdData[1] << 16; + writtenBlocks |= acmdData[2] << 8; + writtenBlocks |= acmdData[3]; + } + } + sdDeselectCard(pdrv); + } + currentBuffer = buffer + (writtenBlocks << 9); + currentSector = sector + writtenBlocks; + currentCount = count - writtenBlocks; + continue; + } else { + return false; + } + } + } else { + break; + } + } + sdDeselectCard(pdrv); + return false; +} + +unsigned long sdGetSectorsCount(uint8_t pdrv) +{ + for (int f = 0; f < 3; f++) { + if(!sdSelectCard(pdrv)) { + break; + } + + if (!sdCommand(pdrv, SEND_CSD, 0, NULL)) { + char csd[16]; + bool success = sdReadBytes(pdrv, csd, 16); + sdDeselectCard(pdrv); + if (success) { + if ((csd[0] >> 6) == 0x01) { + unsigned long size = ( + ((unsigned long)(csd[7] & 0x3F) << 16) + | ((unsigned long)csd[8] << 8) + | csd[9] + ) + 1; + return size << 10; + } + unsigned long size = ( + ((unsigned long)(csd[6] & 0x03) << 10) + | ((unsigned long)csd[7] << 2) + | ((csd[8] & 0xC0) >> 6) + ) + 1; + size <<= (( + ((csd[9] & 0x03) << 1) + | ((csd[10] & 0x80) >> 7) + ) + 2); + size <<= (csd[5] & 0x0F); + return size >> 9; + } + } else { + break; + } + } + + sdDeselectCard(pdrv); + return 0; +} + + + + + + + + +/* + * FATFS API + * */ + +DSTATUS ff_sd_initialize(uint8_t pdrv) +{ + char token; + unsigned int resp; + unsigned int start; + ardu_sdcard_t * card = s_cards[pdrv]; + + if (!(card->status & STA_NOINIT)) { + return card->status; + } + + card->spi->beginTransaction(SPISettings(400000, MSBFIRST, SPI_MODE0)); + + if (sdTransaction(pdrv, GO_IDLE_STATE, 0, NULL) != 1) { + goto unknown_card; + } + + token = sdTransaction(pdrv, CRC_ON_OFF, 1, NULL); + if (token == 0x5) { + //old card maybe + card->supports_crc = false; + } else if (token != 1) { + goto unknown_card; + } + + if (sdTransaction(pdrv, SEND_IF_COND, 0x1AA, &resp) == 1) { + if ((resp & 0xFFF) != 0x1AA) { + goto unknown_card; + } + + if (sdTransaction(pdrv, READ_OCR, 0, &resp) != 1 || !(resp & (1 << 20))) { + goto unknown_card; + } + + start = millis(); + do { + token = sdTransaction(pdrv, APP_OP_COND, 0x40100000, NULL); + } while (token == 1 && (millis() - start) < 1000); + + if (token) { + goto unknown_card; + } + + if (!sdTransaction(pdrv, READ_OCR, 0, &resp)) { + if (resp & (1 << 30)) { + card->type = CARD_SDHC; + } else { + card->type = CARD_SD; + } + } else { + goto unknown_card; + } + } else { + if (sdTransaction(pdrv, READ_OCR, 0, &resp) != 1 || !(resp & (1 << 20))) { + goto unknown_card; + } + + start = millis(); + do { + token = sdTransaction(pdrv, APP_OP_COND, 0x100000, NULL); + } while (token == 0x01 && (millis() - start) < 1000); + + if (!token) { + card->type = CARD_SD; + } else { + start = millis(); + do { + token = sdTransaction(pdrv, SEND_OP_COND, 0x100000, NULL); + } while (token != 0x00 && (millis() - start) < 1000); + + if (token == 0x00) { + card->type = CARD_MMC; + } else { + goto unknown_card; + } + } + } + + if (card->type != CARD_MMC) { + if (sdTransaction(pdrv, APP_CLR_CARD_DETECT, 0, NULL)) { + goto unknown_card; + } + } + + if (card->type != CARD_SDHC) { + if (sdTransaction(pdrv, SET_BLOCKLEN, 512, NULL) != 0x00) { + goto unknown_card; + } + } + + card->sectors = sdGetSectorsCount(pdrv); + + if (card->frequency > 25000000) { + card->frequency = 25000000; + } + + card->spi->endTransaction(); + + card->status &= ~STA_NOINIT; + return card->status; + +unknown_card: + card->spi->endTransaction(); + card->type = CARD_UNKNOWN; + return card->status; +} + +DSTATUS ff_sd_status(uint8_t pdrv) +{ + return s_cards[pdrv]->status; +} + +DRESULT ff_sd_read(uint8_t pdrv, uint8_t* buffer, DWORD sector, UINT count) +{ + ardu_sdcard_t * card = s_cards[pdrv]; + if (card->status & STA_NOINIT) { + return RES_NOTRDY; + } + DRESULT res = RES_OK; + + card->spi->beginTransaction(SPISettings(card->frequency, MSBFIRST, SPI_MODE0)); + + if (count > 1) { + res = sdReadSectors(pdrv, (char*)buffer, sector, count) ? RES_OK : RES_ERROR; + } else { + res = sdReadSector(pdrv, (char*)buffer, sector) ? RES_OK : RES_ERROR; + } + + card->spi->endTransaction(); + return res; +} + +DRESULT ff_sd_write(uint8_t pdrv, const uint8_t* buffer, DWORD sector, UINT count) +{ + ardu_sdcard_t * card = s_cards[pdrv]; + if (card->status & STA_NOINIT) { + return RES_NOTRDY; + } + + if (card->status & STA_PROTECT) { + return RES_WRPRT; + } + DRESULT res = RES_OK; + + card->spi->beginTransaction(SPISettings(card->frequency, MSBFIRST, SPI_MODE0)); + + if (count > 1) { + res = sdWriteSectors(pdrv, (const char*)buffer, sector, count) ? RES_OK : RES_ERROR; + } + res = sdWriteSector(pdrv, (const char*)buffer, sector) ? RES_OK : RES_ERROR; + + card->spi->endTransaction(); + return res; +} + +DRESULT ff_sd_ioctl(uint8_t pdrv, uint8_t cmd, void* buff) +{ + switch(cmd) { + case CTRL_SYNC: + if (sdSelectCard(pdrv)) { + sdDeselectCard(pdrv); + return RES_OK; + } + return RES_ERROR; + case GET_SECTOR_COUNT: + *((unsigned long*) buff) = s_cards[pdrv]->sectors; + return RES_OK; + case GET_SECTOR_SIZE: + *((unsigned long*) buff) = 512; + return RES_OK; + case GET_BLOCK_SIZE: + *((unsigned long*)buff) = 1; + return RES_OK; + } + return RES_PARERR; +} + + +/* + * Public methods + * */ + +uint8_t sdcard_uninit(uint8_t pdrv) +{ + ardu_sdcard_t * card = s_cards[pdrv]; + if (pdrv >= _VOLUMES || card == NULL) { + return 1; + } + ff_diskio_register(pdrv, NULL); + s_cards[pdrv] = NULL; + esp_err_t err = ESP_OK; + if (card->base_path) { + err = esp_vfs_fat_unregister_path(card->base_path); + } + free(card); + return err; +} + +uint8_t sdcard_init(uint8_t cs, SPIClass * spi, int hz) +{ + + uint8_t pdrv = 0xFF; + if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == 0xFF) { + return pdrv; + } + + ardu_sdcard_t * card = (ardu_sdcard_t *)malloc(sizeof(ardu_sdcard_t)); + if (!card) { + return 0xFF; + } + + card->base_path = NULL; + card->frequency = hz; + card->spi = spi; + card->ssPin = cs; + + card->supports_crc = true; + card->type = CARD_NONE; + card->status = STA_NOINIT; + + pinMode(card->ssPin, OUTPUT); + digitalWrite(card->ssPin, HIGH); + + s_cards[pdrv] = card; + + static const ff_diskio_impl_t sd_impl = { + .init = &ff_sd_initialize, + .status = &ff_sd_status, + .read = &ff_sd_read, + .write = &ff_sd_write, + .ioctl = &ff_sd_ioctl + }; + ff_diskio_register(pdrv, &sd_impl); + + return pdrv; +} + +uint8_t sdcard_unmount(uint8_t pdrv) +{ + ardu_sdcard_t * card = s_cards[pdrv]; + if (pdrv >= _VOLUMES || card == NULL) { + return 1; + } + card->status |= STA_NOINIT; + card->type = CARD_NONE; + + char drv[3] = {(char)('0' + pdrv), ':', 0}; + f_mount(NULL, drv, 0); + return 0; +} + +bool sdcard_mount(uint8_t pdrv, const char* path) +{ + ardu_sdcard_t * card = s_cards[pdrv]; + if(pdrv >= _VOLUMES || card == NULL){ + return false; + } + + if(card->base_path){ + free(card->base_path); + } + card->base_path = strdup(path); + + FATFS* fs; + char drv[3] = {(char)('0' + pdrv), ':', 0}; + esp_err_t err = esp_vfs_fat_register(path, drv, 5, &fs); + if (err == ESP_ERR_INVALID_STATE) { + log_e("esp_vfs_fat_register failed 0x(%x): SD is registered.", err); + return false; + } else if (err != ESP_OK) { + log_e("esp_vfs_fat_register failed 0x(%x)", err); + return false; + } + + FRESULT res = f_mount(fs, drv, 1); + if (res != FR_OK) { + log_e("f_mount failed 0x(%x)", res); + esp_vfs_fat_unregister_path(path); + return false; + } + card->sectors = sdGetSectorsCount(pdrv); + return true; +} + +uint32_t sdcard_num_sectors(uint8_t pdrv) +{ + ardu_sdcard_t * card = s_cards[pdrv]; + if(pdrv >= _VOLUMES || card == NULL){ + return 0; + } + return card->sectors; +} + +uint32_t sdcard_sector_size(uint8_t pdrv) +{ + if(pdrv >= _VOLUMES || s_cards[pdrv] == NULL){ + return 0; + } + return 512; +} + +sdcard_type_t sdcard_type(uint8_t pdrv) +{ + ardu_sdcard_t * card = s_cards[pdrv]; + if(pdrv >= _VOLUMES || card == NULL){ + return CARD_NONE; + } + return card->type; +} diff --git a/libraries/SD/src/sd_diskio.h b/libraries/SD/src/sd_diskio.h new file mode 100644 index 00000000..17754257 --- /dev/null +++ b/libraries/SD/src/sd_diskio.h @@ -0,0 +1,31 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SD_DISKIO_H_ +#define _SD_DISKIO_H_ + +#include "Arduino.h" +#include "SPI.h" +#include "sd_defines.h" + +uint8_t sdcard_init(uint8_t cs, SPIClass * spi, int hz); +uint8_t sdcard_uninit(uint8_t pdrv); + +bool sdcard_mount(uint8_t pdrv, const char* path); +uint8_t sdcard_unmount(uint8_t pdrv); + +sdcard_type_t sdcard_type(uint8_t pdrv); +uint32_t sdcard_num_sectors(uint8_t pdrv); +uint32_t sdcard_sector_size(uint8_t pdrv); + +#endif /* _SD_DISKIO_H_ */ diff --git a/libraries/SD/src/sd_diskio_crc.c b/libraries/SD/src/sd_diskio_crc.c new file mode 100644 index 00000000..0372df82 --- /dev/null +++ b/libraries/SD/src/sd_diskio_crc.c @@ -0,0 +1,103 @@ +/* SD/MMC File System Library + * Copyright (c) 2014 Neil Thiessen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const char m_CRC7Table[] = { + 0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F, + 0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77, + 0x19, 0x10, 0x0B, 0x02, 0x3D, 0x34, 0x2F, 0x26, + 0x51, 0x58, 0x43, 0x4A, 0x75, 0x7C, 0x67, 0x6E, + 0x32, 0x3B, 0x20, 0x29, 0x16, 0x1F, 0x04, 0x0D, + 0x7A, 0x73, 0x68, 0x61, 0x5E, 0x57, 0x4C, 0x45, + 0x2B, 0x22, 0x39, 0x30, 0x0F, 0x06, 0x1D, 0x14, + 0x63, 0x6A, 0x71, 0x78, 0x47, 0x4E, 0x55, 0x5C, + 0x64, 0x6D, 0x76, 0x7F, 0x40, 0x49, 0x52, 0x5B, + 0x2C, 0x25, 0x3E, 0x37, 0x08, 0x01, 0x1A, 0x13, + 0x7D, 0x74, 0x6F, 0x66, 0x59, 0x50, 0x4B, 0x42, + 0x35, 0x3C, 0x27, 0x2E, 0x11, 0x18, 0x03, 0x0A, + 0x56, 0x5F, 0x44, 0x4D, 0x72, 0x7B, 0x60, 0x69, + 0x1E, 0x17, 0x0C, 0x05, 0x3A, 0x33, 0x28, 0x21, + 0x4F, 0x46, 0x5D, 0x54, 0x6B, 0x62, 0x79, 0x70, + 0x07, 0x0E, 0x15, 0x1C, 0x23, 0x2A, 0x31, 0x38, + 0x41, 0x48, 0x53, 0x5A, 0x65, 0x6C, 0x77, 0x7E, + 0x09, 0x00, 0x1B, 0x12, 0x2D, 0x24, 0x3F, 0x36, + 0x58, 0x51, 0x4A, 0x43, 0x7C, 0x75, 0x6E, 0x67, + 0x10, 0x19, 0x02, 0x0B, 0x34, 0x3D, 0x26, 0x2F, + 0x73, 0x7A, 0x61, 0x68, 0x57, 0x5E, 0x45, 0x4C, + 0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D, 0x04, + 0x6A, 0x63, 0x78, 0x71, 0x4E, 0x47, 0x5C, 0x55, + 0x22, 0x2B, 0x30, 0x39, 0x06, 0x0F, 0x14, 0x1D, + 0x25, 0x2C, 0x37, 0x3E, 0x01, 0x08, 0x13, 0x1A, + 0x6D, 0x64, 0x7F, 0x76, 0x49, 0x40, 0x5B, 0x52, + 0x3C, 0x35, 0x2E, 0x27, 0x18, 0x11, 0x0A, 0x03, + 0x74, 0x7D, 0x66, 0x6F, 0x50, 0x59, 0x42, 0x4B, + 0x17, 0x1E, 0x05, 0x0C, 0x33, 0x3A, 0x21, 0x28, + 0x5F, 0x56, 0x4D, 0x44, 0x7B, 0x72, 0x69, 0x60, + 0x0E, 0x07, 0x1C, 0x15, 0x2A, 0x23, 0x38, 0x31, + 0x46, 0x4F, 0x54, 0x5D, 0x62, 0x6B, 0x70, 0x79 +}; + +char CRC7(const char* data, int length) +{ + char crc = 0; + for (int i = 0; i < length; i++) { + crc = m_CRC7Table[(crc << 1) ^ data[i]]; + } + return crc; +} + +const unsigned short m_CRC16Table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +unsigned short CRC16(const char* data, int length) +{ + unsigned short crc = 0; + for (int i = 0; i < length; i++) { + crc = (crc << 8) ^ m_CRC16Table[((crc >> 8) ^ data[i]) & 0x00FF]; + } + return crc; +} diff --git a/libraries/SD_MMC/examples/SDMMC_Test/SDMMC_Test.ino b/libraries/SD_MMC/examples/SDMMC_Test/SDMMC_Test.ino new file mode 100644 index 00000000..5d1388ee --- /dev/null +++ b/libraries/SD_MMC/examples/SDMMC_Test/SDMMC_Test.ino @@ -0,0 +1,201 @@ +#include "FS.h" +#include "SD_MMC.h" + +void setup(){ + Serial.begin(115200); + if(!SD_MMC.begin()){ + Serial.println("Card Mount Failed"); + return; + } + uint8_t cardType = SD_MMC.cardType(); + + if(cardType == CARD_NONE){ + Serial.println("No SD_MMC card attached"); + return; + } + + Serial.print("SD_MMC Card Type: "); + if(cardType == CARD_MMC){ + Serial.println("MMC"); + } else if(cardType == CARD_SD){ + Serial.println("SDSC"); + } else if(cardType == CARD_SDHC){ + Serial.println("SDHC"); + } else { + Serial.println("UNKNOWN"); + } + + uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024); + Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize); + + listDir(SD_MMC, "/", 0); + createDir(SD_MMC, "/mydir"); + listDir(SD_MMC, "/", 0); + removeDir(SD_MMC, "/mydir"); + listDir(SD_MMC, "/", 2); + writeFile(SD_MMC, "/hello.txt", "Hello "); + appendFile(SD_MMC, "/hello.txt", "World!\n"); + readFile(SD_MMC, "/hello.txt"); + deleteFile(SD_MMC, "/foo.txt"); + renameFile(SD_MMC, "/hello.txt", "/foo.txt"); + readFile(SD_MMC, "/foo.txt"); + testFileIO(SD_MMC, "/test.txt"); +} + +void loop(){ + +} + +void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ + Serial.printf("Listing directory: %s\n", dirname); + + File root = fs.open(dirname); + if(!root){ + Serial.println("Failed to open directory"); + return; + } + if(!root.isDirectory()){ + Serial.println("Not a directory"); + return; + } + + File file = root.openNextFile(); + while(file){ + if(file.isDirectory()){ + Serial.print(" DIR : "); + Serial.println(file.name()); + if(levels){ + listDir(fs, file.name(), levels -1); + } + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + Serial.println(file.size()); + } + file = root.openNextFile(); + } +} + +void createDir(fs::FS &fs, const char * path){ + Serial.printf("Creating Dir: %s\n", path); + if(fs.mkdir(path)){ + Serial.println("Dir created"); + } else { + Serial.println("mkdir failed"); + } +} + +void removeDir(fs::FS &fs, const char * path){ + Serial.printf("Removing Dir: %s\n", path); + if(fs.rmdir(path)){ + Serial.println("Dir removed"); + } else { + Serial.println("rmdir failed"); + } +} + +void readFile(fs::FS &fs, const char * path){ + Serial.printf("Reading file: %s\n", path); + + File file = fs.open(path); + if(!file){ + Serial.println("Failed to open file for reading"); + return; + } + + Serial.print("Read from file: "); + while(file.available()){ + Serial.write(file.read()); + } +} + +void writeFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Writing file: %s\n", path); + + File file = fs.open(path, FILE_WRITE); + if(!file){ + Serial.println("Failed to open file for writing"); + return; + } + if(file.print(message)){ + Serial.println("File written"); + } else { + Serial.println("Write failed"); + } +} + +void appendFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Appending to file: %s\n", path); + + File file = fs.open(path, FILE_APPEND); + if(!file){ + Serial.println("Failed to open file for appending"); + return; + } + if(file.print(message)){ + Serial.println("Message appended"); + } else { + Serial.println("Append failed"); + } +} + +void renameFile(fs::FS &fs, const char * path1, const char * path2){ + Serial.printf("Renaming file %s to %s\n", path1, path2); + if (fs.rename(path1, path2)) { + Serial.println("File renamed"); + } else { + Serial.println("Rename failed"); + } +} + +void deleteFile(fs::FS &fs, const char * path){ + Serial.printf("Deleting file: %s\n", path); + if(fs.remove(path)){ + Serial.println("File deleted"); + } else { + Serial.println("Delete failed"); + } +} + +void testFileIO(fs::FS &fs, const char * path){ + File file = fs.open(path); + static uint8_t buf[512]; + size_t len = 0; + uint32_t start = millis(); + uint32_t end = start; + if(file){ + len = file.size(); + size_t flen = len; + start = millis(); + while(len){ + size_t toRead = len; + if(toRead > 512){ + toRead = 512; + } + file.read(buf, toRead); + len -= toRead; + } + end = millis() - start; + Serial.printf("%u bytes read for %u ms\n", flen, end); + file.close(); + } else { + Serial.println("Failed to open file for reading"); + } + + + file = fs.open(path, FILE_WRITE); + if(!file){ + Serial.println("Failed to open file for writing"); + return; + } + + size_t i; + start = millis(); + for(i=0; i<2048; i++){ + file.write(buf, 512); + } + end = millis() - start; + Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); + file.close(); +} diff --git a/libraries/SD_MMC/library.properties b/libraries/SD_MMC/library.properties new file mode 100644 index 00000000..9e47a6ee --- /dev/null +++ b/libraries/SD_MMC/library.properties @@ -0,0 +1,9 @@ +name=SD_MMC +version=1.0 +author=Hristo Gochkov, Ivan Grokhtkov +maintainer=Hristo Gochkov +sentence=ESP32 SDMMC File System +paragraph= +category=Data Storage +url= +architectures=esp32 diff --git a/libraries/SD_MMC/src/SD_MMC.cpp b/libraries/SD_MMC/src/SD_MMC.cpp new file mode 100644 index 00000000..cc3f1791 --- /dev/null +++ b/libraries/SD_MMC/src/SD_MMC.cpp @@ -0,0 +1,97 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "vfs_api.h" + +extern "C" { +#include +#include +#include +#include "esp_vfs_fat.h" +#include "driver/sdmmc_host.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" +} +#include "SD_MMC.h" + +using namespace fs; +/* + +*/ + +SDMMCFS::SDMMCFS(FSImplPtr impl) + : FS(impl), _card(NULL) +{} + +bool SDMMCFS::begin(const char * mountpoint) +{ + if(_card) { + return true; + } + //mount + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + //host.flags = SDMMC_HOST_FLAG_1BIT; //use 1-line SD mode + + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5 + }; + + esp_err_t ret = esp_vfs_fat_sdmmc_mount(mountpoint, &host, &slot_config, &mount_config, &_card); + if (ret != ESP_OK) { + if (ret == ESP_FAIL) { + log_e("Failed to mount filesystem. If you want the card to be formatted, set format_if_mount_failed = true."); + } else if (ret == ESP_ERR_INVALID_STATE) { + _impl->mountpoint(mountpoint); + log_w("SD Already mounted"); + return true; + } else { + log_e("Failed to initialize the card (%d). Make sure SD card lines have pull-up resistors in place.", ret); + } + _card = NULL; + return false; + } + _impl->mountpoint(mountpoint); + return true; +} + +void SDMMCFS::end() +{ + if(_card) { + esp_vfs_fat_sdmmc_unmount(); + _impl->mountpoint(NULL); + _card = NULL; + } +} + +sdcard_type_t SDMMCFS::cardType() +{ + if(!_card) { + return CARD_NONE; + } + return (_card->ocr & SD_OCR_SDHC_CAP)?CARD_SDHC:CARD_SD; +} + +uint64_t SDMMCFS::cardSize() +{ + if(!_card) { + return 0; + } + return (uint64_t)_card->csd.capacity * _card->csd.sector_size; +} + + +SDMMCFS SD_MMC = SDMMCFS(FSImplPtr(new VFSImpl())); diff --git a/libraries/SD_MMC/src/SD_MMC.h b/libraries/SD_MMC/src/SD_MMC.h new file mode 100644 index 00000000..51d9e8e6 --- /dev/null +++ b/libraries/SD_MMC/src/SD_MMC.h @@ -0,0 +1,41 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SDMMC_H_ +#define _SDMMC_H_ + +#include "FS.h" +#include "driver/sdmmc_types.h" +#include "sd_defines.h" + +namespace fs +{ + +class SDMMCFS : public FS +{ +protected: + sdmmc_card_t* _card; + +public: + SDMMCFS(FSImplPtr impl); + bool begin(const char * mountpoint="/sdcard"); + void end(); + sdcard_type_t cardType(); + uint64_t cardSize(); +}; + +} + +extern fs::SDMMCFS SD_MMC; + +#endif /* _SDMMC_H_ */ diff --git a/libraries/SD_MMC/src/sd_defines.h b/libraries/SD_MMC/src/sd_defines.h new file mode 100644 index 00000000..6e42855a --- /dev/null +++ b/libraries/SD_MMC/src/sd_defines.h @@ -0,0 +1,25 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SD_DEFINES_H_ +#define _SD_DEFINES_H_ + +typedef enum { + CARD_NONE, + CARD_MMC, + CARD_SD, + CARD_SDHC, + CARD_UNKNOWN +} sdcard_type_t; + +#endif /* _SD_DISKIO_H_ */