/* Arduino SdFat Library * Copyright (C) 2012 by William Greiman * * This file is part of the Arduino SdFat Library * * This Library is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the Arduino SdFat Library. If not, see * . */ #include #ifndef PSTR #define PSTR(x) x #define PGM_P const char* #endif //------------------------------------------------------------------------------ #if USE_SERIAL_FOR_STD_OUT || !defined(UDR0) Print* SdFat::stdOut_ = &Serial; #else // USE_SERIAL_FOR_STD_OUT #include Print* SdFat::stdOut_ = &MiniSerial; #endif // USE_SERIAL_FOR_STD_OUT //------------------------------------------------------------------------------ static void pstrPrint(PGM_P str) { for (uint8_t c; (c = pgm_read_byte(str)); str++) SdFat::stdOut()->write(c); } //------------------------------------------------------------------------------ static void pstrPrintln(PGM_P str) { pstrPrint(str); SdFat::stdOut()->println(); } //------------------------------------------------------------------------------ /** * Initialize an SdFat object. * * Initializes the SD card, SD volume, and root directory. * * \param[in] chipSelectPin SD chip select pin. See Sd2Card::init(). * \param[in] sckRateID value for SPI SCK rate. See Sd2Card::init(). * * \return The value one, true, is returned for success and * the value zero, false, is returned for failure. */ bool SdFat::begin(uint8_t chipSelectPin, uint8_t sckRateID) { return card_.init(sckRateID, chipSelectPin) && vol_.init(&card_) && chdir(1); } //------------------------------------------------------------------------------ /** Change a volume's working directory to root * * Changes the volume's working directory to the SD's root directory. * Optionally set the current working directory to the volume's * working directory. * * \param[in] set_cwd Set the current working directory to this volume's * working directory if true. * * \return The value one, true, is returned for success and * the value zero, false, is returned for failure. */ bool SdFat::chdir(bool set_cwd) { if (set_cwd) SdBaseFile::cwd_ = &vwd_; if (vwd_.isOpen()) vwd_.close(); return vwd_.openRoot(&vol_); } //------------------------------------------------------------------------------ /** Change a volume's working directory * * Changes the volume working directory to the \a path subdirectory. * Optionally set the current working directory to the volume's * working directory. * * Example: If the volume's working directory is "/DIR", chdir("SUB") * will change the volume's working directory from "/DIR" to "/DIR/SUB". * * If path is "/", the volume's working directory will be changed to the * root directory * * \param[in] path The name of the subdirectory. * * \param[in] set_cwd Set the current working directory to this volume's * working directory if true. * * \return The value one, true, is returned for success and * the value zero, false, is returned for failure. */ bool SdFat::chdir(const char *path, bool set_cwd) { SdBaseFile dir; if (path[0] == '/' && path[1] == '\0') return chdir(set_cwd); if (!dir.open(&vwd_, path, O_READ)) goto fail; if (!dir.isDir()) goto fail; vwd_ = dir; if (set_cwd) SdBaseFile::cwd_ = &vwd_; return true; fail: return false; } //------------------------------------------------------------------------------ /** Set the current working directory to a volume's working directory. * * This is useful with multiple SD cards. * * The current working directory is changed to this volume's working directory. * * This is like the Windows/DOS \: command. */ void SdFat::chvol() { SdBaseFile::cwd_ = &vwd_; } //------------------------------------------------------------------------------ /** %Print any SD error code and halt. */ void SdFat::errorHalt() { errorPrint(); while (1); } //------------------------------------------------------------------------------ /** %Print msg, any SD error code, and halt. * * \param[in] msg Message to print. */ void SdFat::errorHalt(char const* msg) { errorPrint(msg); while (1); } //------------------------------------------------------------------------------ /** %Print msg, any SD error code, and halt. * * \param[in] msg Message in program space (flash memory) to print. */ void SdFat::errorHalt_P(PGM_P msg) { errorPrint_P(msg); while (1); } //------------------------------------------------------------------------------ /** %Print any SD error code. */ void SdFat::errorPrint() { if (!card_.errorCode()) return; pstrPrint(PSTR("SD errorCode: 0X")); stdOut_->print(card_.errorCode(), HEX); pstrPrint(PSTR(",0X")); stdOut_->println(card_.errorData(), HEX); } //------------------------------------------------------------------------------ /** %Print msg, any SD error code. * * \param[in] msg Message to print. */ void SdFat::errorPrint(char const* msg) { pstrPrint(PSTR("error: ")); stdOut_->println(msg); errorPrint(); } //------------------------------------------------------------------------------ /** %Print msg, any SD error code. * * \param[in] msg Message in program space (flash memory) to print. */ void SdFat::errorPrint_P(PGM_P msg) { pstrPrint(PSTR("error: ")); pstrPrintln(msg); errorPrint(); } //------------------------------------------------------------------------------ /** * Test for the existence of a file. * * \param[in] name Name of the file to be tested for. * * \return true if the file exists else false. */ bool SdFat::exists(const char* name) { return vwd_.exists(name); } //------------------------------------------------------------------------------ /** %Print error details and halt after SdFat::init() fails. */ void SdFat::initErrorHalt() { initErrorPrint(); while (1); } //------------------------------------------------------------------------------ /**Print message, error details, and halt after SdFat::init() fails. * * \param[in] msg Message to print. */ void SdFat::initErrorHalt(char const *msg) { stdOut_->println(msg); initErrorHalt(); } //------------------------------------------------------------------------------ /**Print message, error details, and halt after SdFat::init() fails. * * \param[in] msg Message in program space (flash memory) to print. */ void SdFat::initErrorHalt_P(PGM_P msg) { pstrPrintln(msg); initErrorHalt(); } //------------------------------------------------------------------------------ /** Print error details after SdFat::init() fails. */ void SdFat::initErrorPrint() { if (card_.errorCode()) { pstrPrintln(PSTR("Can't access SD card. Do not reformat.")); if (card_.errorCode() == SD_CARD_ERROR_CMD0) { pstrPrintln(PSTR("No card, wrong chip select pin, or SPI problem?")); } errorPrint(); } else if (vol_.fatType() == 0) { pstrPrintln(PSTR("Invalid format, reformat SD.")); } else if (!vwd_.isOpen()) { pstrPrintln(PSTR("Can't open root directory.")); } else { pstrPrintln(PSTR("No error found.")); } } //------------------------------------------------------------------------------ /**Print message and error details and halt after SdFat::init() fails. * * \param[in] msg Message to print. */ void SdFat::initErrorPrint(char const *msg) { stdOut_->println(msg); initErrorPrint(); } //------------------------------------------------------------------------------ /**Print message and error details after SdFat::init() fails. * * \param[in] msg Message in program space (flash memory) to print. */ void SdFat::initErrorPrint_P(PGM_P msg) { pstrPrintln(msg); initErrorHalt(); } //------------------------------------------------------------------------------ /** List the directory contents of the volume working directory to stdOut. * * \param[in] flags The inclusive OR of * * LS_DATE - %Print file modification date * * LS_SIZE - %Print file size. * * LS_R - Recursive list of subdirectories. */ void SdFat::ls(uint8_t flags) { vwd_.ls(stdOut_, flags); } //------------------------------------------------------------------------------ /** List the directory contents of the volume working directory. * * \param[in] pr Print stream for list. * * \param[in] flags The inclusive OR of * * LS_DATE - %Print file modification date * * LS_SIZE - %Print file size. * * LS_R - Recursive list of subdirectories. */ void SdFat::ls(Print* pr, uint8_t flags) { vwd_.ls(pr, flags); } //------------------------------------------------------------------------------ /** Make a subdirectory in the volume working directory. * * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. * * \param[in] pFlag Create missing parent directories if true. * * \return The value one, true, is returned for success and * the value zero, false, is returned for failure. */ bool SdFat::mkdir(const char* path, bool pFlag) { SdBaseFile sub; return sub.mkdir(&vwd_, path, pFlag); } //------------------------------------------------------------------------------ /** Remove a file from the volume working directory. * * \param[in] path A path with a valid 8.3 DOS name for the file. * * \return The value one, true, is returned for success and * the value zero, false, is returned for failure. */ bool SdFat::remove(const char* path) { return SdBaseFile::remove(&vwd_, path); } //------------------------------------------------------------------------------ /** Rename a file or subdirectory. * * \param[in] oldPath Path name to the file or subdirectory to be renamed. * * \param[in] newPath New path name of the file or subdirectory. * * The \a newPath object must not exist before the rename call. * * The file to be renamed must not be open. The directory entry may be * moved and file system corruption could occur if the file is accessed by * a file object that was opened before the rename() call. * * \return The value one, true, is returned for success and * the value zero, false, is returned for failure. */ bool SdFat::rename(const char *oldPath, const char *newPath) { SdBaseFile file; if (!file.open(oldPath, O_READ)) return false; return file.rename(&vwd_, newPath); } //------------------------------------------------------------------------------ /** Remove a subdirectory from the volume's working directory. * * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. * * The subdirectory file will be removed only if it is empty. * * \return The value one, true, is returned for success and * the value zero, false, is returned for failure. */ bool SdFat::rmdir(const char* path) { SdBaseFile sub; if (!sub.open(path, O_READ)) return false; return sub.rmdir(); } //------------------------------------------------------------------------------ /** Truncate a file to a specified length. The current file position * will be maintained if it is less than or equal to \a length otherwise * it will be set to end of file. * * \param[in] path A path with a valid 8.3 DOS name for the file. * \param[in] length The desired length for the file. * * \return The value one, true, is returned for success and * the value zero, false, is returned for failure. * Reasons for failure include file is read only, file is a directory, * \a length is greater than the current file size or an I/O error occurs. */ bool SdFat::truncate(const char* path, uint32_t length) { SdBaseFile file; if (!file.open(path, O_WRITE)) return false; return file.truncate(length); }