Add FS, SD and SD_MMC (#256)
This commit is contained in:
parent
56ffec855d
commit
e625b3b08e
9
libraries/FS/library.properties
Normal file
9
libraries/FS/library.properties
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
name=FS
|
||||||
|
version=1.0
|
||||||
|
author=Hristo Gochkov, Ivan Grokhtkov
|
||||||
|
maintainer=Hristo Gochkov <hristo@espressif.com>
|
||||||
|
sentence=ESP32 File System
|
||||||
|
paragraph=
|
||||||
|
category=Data Storage
|
||||||
|
url=
|
||||||
|
architectures=esp32
|
260
libraries/FS/src/FS.cpp
Normal file
260
libraries/FS/src/FS.cpp
Normal file
@ -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;
|
||||||
|
}
|
122
libraries/FS/src/FS.h
Normal file
122
libraries/FS/src/FS.h
Normal file
@ -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 <memory>
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
namespace fs
|
||||||
|
{
|
||||||
|
|
||||||
|
#define FILE_READ "r"
|
||||||
|
#define FILE_WRITE "w"
|
||||||
|
#define FILE_APPEND "a"
|
||||||
|
|
||||||
|
class File;
|
||||||
|
|
||||||
|
class FileImpl;
|
||||||
|
typedef std::shared_ptr<FileImpl> FileImplPtr;
|
||||||
|
class FSImpl;
|
||||||
|
typedef std::shared_ptr<FSImpl> 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
|
66
libraries/FS/src/FSImpl.h
Normal file
66
libraries/FS/src/FSImpl.h
Normal file
@ -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 <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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
|
417
libraries/FS/src/vfs_api.cpp
Normal file
417
libraries/FS/src/vfs_api.cpp
Normal file
@ -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<VFSFileImpl>(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<VFSFileImpl>(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<VFSFileImpl>(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<VFSFileImpl>(_fs, name.c_str(), mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VFSFileImpl::rewindDirectory(void)
|
||||||
|
{
|
||||||
|
if(!_isDirectory || !_d) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rewinddir(_d);
|
||||||
|
}
|
76
libraries/FS/src/vfs_api.h
Normal file
76
libraries/FS/src/vfs_api.h
Normal file
@ -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 <sys/unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
202
libraries/SD/examples/SD_Test/SD_Test.ino
Normal file
202
libraries/SD/examples/SD_Test/SD_Test.ino
Normal file
@ -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();
|
||||||
|
}
|
9
libraries/SD/library.properties
Normal file
9
libraries/SD/library.properties
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
name=SD(esp32)
|
||||||
|
version=1.0.5
|
||||||
|
author=Arduino, SparkFun
|
||||||
|
maintainer=Arduino <info@arduino.cc>
|
||||||
|
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
|
76
libraries/SD/src/SD.cpp
Normal file
76
libraries/SD/src/SD.cpp
Normal file
@ -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()));
|
46
libraries/SD/src/SD.h
Normal file
46
libraries/SD/src/SD.h
Normal file
@ -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_ */
|
25
libraries/SD/src/sd_defines.h
Normal file
25
libraries/SD/src/sd_defines.h
Normal file
@ -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_ */
|
740
libraries/SD/src/sd_diskio.cpp
Normal file
740
libraries/SD/src/sd_diskio.cpp
Normal file
@ -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;
|
||||||
|
}
|
31
libraries/SD/src/sd_diskio.h
Normal file
31
libraries/SD/src/sd_diskio.h
Normal file
@ -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_ */
|
103
libraries/SD/src/sd_diskio_crc.c
Normal file
103
libraries/SD/src/sd_diskio_crc.c
Normal file
@ -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;
|
||||||
|
}
|
201
libraries/SD_MMC/examples/SDMMC_Test/SDMMC_Test.ino
Normal file
201
libraries/SD_MMC/examples/SDMMC_Test/SDMMC_Test.ino
Normal file
@ -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();
|
||||||
|
}
|
9
libraries/SD_MMC/library.properties
Normal file
9
libraries/SD_MMC/library.properties
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
name=SD_MMC
|
||||||
|
version=1.0
|
||||||
|
author=Hristo Gochkov, Ivan Grokhtkov
|
||||||
|
maintainer=Hristo Gochkov <hristo@espressif.com>
|
||||||
|
sentence=ESP32 SDMMC File System
|
||||||
|
paragraph=
|
||||||
|
category=Data Storage
|
||||||
|
url=
|
||||||
|
architectures=esp32
|
97
libraries/SD_MMC/src/SD_MMC.cpp
Normal file
97
libraries/SD_MMC/src/SD_MMC.cpp
Normal file
@ -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 <sys/unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#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()));
|
41
libraries/SD_MMC/src/SD_MMC.h
Normal file
41
libraries/SD_MMC/src/SD_MMC.h
Normal file
@ -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_ */
|
25
libraries/SD_MMC/src/sd_defines.h
Normal file
25
libraries/SD_MMC/src/sd_defines.h
Normal file
@ -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_ */
|
Loading…
Reference in New Issue
Block a user