diff --git a/.travis.yml b/.travis.yml index e16a1898e..d050978bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,8 @@ script: - which arduino - source hardware/esp8266com/esp8266/tests/common.sh - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - install_libraries - build_sketches arduino $PWD/hardware/esp8266com/esp8266 notifications: diff --git a/boards.txt b/boards.txt index 433158a93..b8b2416de 100644 --- a/boards.txt +++ b/boards.txt @@ -181,7 +181,7 @@ nodemcu.name=NodeMCU 0.9 (ESP-12 Module) nodemcu.upload.tool=esptool nodemcu.upload.speed=115200 -nodemcu.upload.resetmethod=ck +nodemcu.upload.resetmethod=nodemcu nodemcu.upload.maximum_size=1044464 nodemcu.upload.maximum_data_size=81920 nodemcu.upload.wait_for_upload_port=true diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index 6b379d5b1..8f440f27b 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -137,17 +137,29 @@ void timer0_detachInterrupt(void); void ets_intr_lock(); void ets_intr_unlock(); -// level (0-15), -// level 15 will disable ALL interrupts, -// level 0 will disable most software interrupts +#ifndef __STRINGIFY +#define __STRINGIFY(a) #a +#endif + +// these low level routines provide a replacement for SREG interrupt save that AVR uses +// but are esp8266 specific. A normal use pattern is like // -#define xt_disable_interrupts(state, level) __asm__ __volatile__("rsil %0," __STRINGIFY(level) : "=a" (state)) -#define xt_enable_interrupts(state) __asm__ __volatile__("wsr %0,ps; isync" :: "a" (state) : "memory") +//{ +// uint32_t savedPS = xt_rsil(1); // this routine will allow level 2 and above +// // do work here +// xt_wsr_ps(savedPS); // restore the state +//} +// +// level (0-15), interrupts of the given level and above will be active +// level 15 will disable ALL interrupts, +// level 0 will enable ALL interrupts, +// +#define xt_rsil(level) (__extension__({uint32_t state; __asm__ __volatile__("rsil %0," __STRINGIFY(level) : "=a" (state)); state;})) +#define xt_wsr_ps(state) __asm__ __volatile__("wsr %0,ps; isync" :: "a" (state) : "memory") -extern uint32_t interruptsState; +#define interrupts() xt_rsil(0) +#define noInterrupts() xt_rsil(15) -#define interrupts() xt_enable_interrupts(interruptsState) -#define noInterrupts() __asm__ __volatile__("rsil %0,15" : "=a" (interruptsState)) #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) @@ -179,12 +191,12 @@ void initVariant(void); int atexit(void (*func)()) __attribute__((weak)); -void pinMode(uint8_t, uint8_t); -void digitalWrite(uint8_t, uint8_t); -int digitalRead(uint8_t); -int analogRead(uint8_t); +void pinMode(uint8_t pin, uint8_t mode); +void digitalWrite(uint8_t pin, uint8_t val); +int digitalRead(uint8_t pin); +int analogRead(uint8_t pin); void analogReference(uint8_t mode); -void analogWrite(uint8_t, int); +void analogWrite(uint8_t pin, int val); void analogWriteFreq(uint32_t freq); void analogWriteRange(uint32_t range); @@ -198,8 +210,8 @@ unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout); void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); -void attachInterrupt(uint8_t, void (*)(void), int mode); -void detachInterrupt(uint8_t); +void attachInterrupt(uint8_t pin, void (*)(void), int mode); +void detachInterrupt(uint8_t pin); void setup(void); void loop(void); diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index cd3b16f6a..ec7f469da 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -1,8 +1,8 @@ -/* +/* Esp.cpp - ESP8266-specific APIs 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 @@ -22,6 +22,7 @@ #include "flash_utils.h" #include "eboot_command.h" #include +#include "interrupts.h" extern "C" { #include "user_interface.h" @@ -32,7 +33,7 @@ extern struct rst_info resetInfo; //#define DEBUG_SERIAL Serial - + /** * User-defined Literals * usage: @@ -92,23 +93,25 @@ void EspClass::wdtEnable(WDTO_t timeout_ms) void EspClass::wdtDisable(void) { - /// Please dont stop software watchdog too long (less than 6 seconds), + /// Please don't stop software watchdog too long (less than 6 seconds), /// otherwise it will trigger hardware watchdog reset. system_soft_wdt_stop(); } void EspClass::wdtFeed(void) { - -} - -void EspClass::deepSleep(uint32_t time_us, WakeMode mode) -{ - system_deep_sleep_set_option(static_cast(mode)); - system_deep_sleep(time_us); + system_soft_wdt_feed(); } extern "C" void esp_yield(); + +void EspClass::deepSleep(uint32_t time_us, WakeMode mode) +{ + system_deep_sleep_set_option(static_cast(mode)); + system_deep_sleep(time_us); + esp_yield(); +} + extern "C" void __real_system_restart_local(); void EspClass::reset(void) { @@ -119,13 +122,11 @@ void EspClass::restart(void) { system_restart(); esp_yield(); - // todo: provide an alternative code path if this was called - // from system context, not from continuation - // (implement esp_is_cont_ctx()?) } uint16_t EspClass::getVcc(void) { + InterruptLock lock; return system_get_vdd33(); } @@ -333,7 +334,7 @@ uint32_t EspClass::getSketchSize() { DEBUG_SERIAL.printf("num_segments=%u\r\n", image_header.num_segments); #endif for (uint32_t section_index = 0; - section_index < image_header.num_segments; + section_index < image_header.num_segments; ++section_index) { section_header_t section_header = {0}; @@ -395,8 +396,7 @@ bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool #ifdef DEBUG_SERIAL DEBUG_SERIAL.println("Update SUCCESS"); -#endif +#endif if(restartOnSuccess) ESP.restart(); return true; } - diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 17349f688..c7c44aaf0 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -1,8 +1,8 @@ -/* +/* Esp.h - ESP8266-specific APIs 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 @@ -20,6 +20,9 @@ #ifndef ESP_H #define ESP_H + +#include + /** * AVR macros for WDT managment */ diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp index 18eda0c86..125ac527c 100644 --- a/cores/esp8266/FS.cpp +++ b/cores/esp8266/FS.cpp @@ -21,6 +21,8 @@ #include "FS.h" #include "FSImpl.h" +using namespace fs; + static bool sflags(const char* mode, OpenMode& om, AccessMode& am); size_t File::write(uint8_t c) { @@ -41,7 +43,7 @@ int File::available() { if (!_p) return false; - return _p->position() < _p->size(); + return _p->size() - _p->position(); } int File::read() { @@ -112,6 +114,13 @@ File::operator bool() const { return !!_p; } +const char* File::name() const { + if (!_p) + return nullptr; + + return _p->name(); +} + File Dir::openFile(const char* mode) { if (!_impl) { return File(); @@ -135,6 +144,14 @@ String Dir::fileName() { return _impl->fileName(); } +size_t Dir::fileSize() { + if (!_impl) { + return 0; + } + + return _impl->fileSize(); +} + bool Dir::next() { if (!_impl) { return false; diff --git a/cores/esp8266/FS.h b/cores/esp8266/FS.h index ce6d220ba..d8d8dedd7 100644 --- a/cores/esp8266/FS.h +++ b/cores/esp8266/FS.h @@ -24,6 +24,8 @@ #include #include +namespace fs { + class File; class Dir; @@ -64,6 +66,7 @@ public: size_t size() const; void close(); operator bool() const; + const char* name() const; protected: FileImplPtr _p; @@ -75,6 +78,7 @@ public: File openFile(const char* mode); String fileName(); + size_t fileSize(); bool next(); protected: @@ -102,9 +106,18 @@ public: protected: FSImplPtr _impl; - }; +} // namespace fs + +using fs::FS; +using fs::File; +using fs::Dir; +using fs::SeekMode; +using fs::SeekSet; +using fs::SeekCur; +using fs::SeekEnd; + extern FS SPIFFS; #endif //FS_H diff --git a/cores/esp8266/FSImpl.h b/cores/esp8266/FSImpl.h index 8977e64c0..20d886e76 100644 --- a/cores/esp8266/FSImpl.h +++ b/cores/esp8266/FSImpl.h @@ -23,6 +23,8 @@ #include #include +namespace fs { + class FileImpl { public: virtual ~FileImpl() { } @@ -33,6 +35,7 @@ public: virtual size_t position() const = 0; virtual size_t size() const = 0; virtual void close() = 0; + virtual const char* name() const = 0; }; enum OpenMode { @@ -53,6 +56,7 @@ public: virtual ~DirImpl() { } virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0; virtual const char* fileName() = 0; + virtual size_t fileSize() = 0; virtual bool next() = 0; }; @@ -66,5 +70,6 @@ public: }; +} // namespace fs #endif //FSIMPL_H diff --git a/cores/esp8266/Tone.cpp b/cores/esp8266/Tone.cpp index f85ed404a..c704c50aa 100644 --- a/cores/esp8266/Tone.cpp +++ b/cores/esp8266/Tone.cpp @@ -35,10 +35,12 @@ #include "Arduino.h" #include "pins_arduino.h" +/* static int8_t toneBegin(uint8_t _pin) { //TODO implement tone return 0; } +*/ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) { //TODO implement tone diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index be1a04dd6..eac84f466 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -108,12 +108,12 @@ class UpdaterClass { void _reset(); bool _writeBuffer(); + uint8_t _error; uint8_t *_buffer; size_t _bufferLen; size_t _size; uint32_t _startAddress; uint32_t _currentAddress; - uint8_t _error; }; extern UpdaterClass Update; diff --git a/cores/esp8266/cbuf.h b/cores/esp8266/cbuf.h index ceb6a7b6d..728547fad 100644 --- a/cores/esp8266/cbuf.h +++ b/cores/esp8266/cbuf.h @@ -70,7 +70,7 @@ class cbuf { size_t bytes_available = getSize(); size_t size_to_read = (size < bytes_available) ? size : bytes_available; size_t size_read = size_to_read; - if(_end < _begin && size_to_read > _bufend - _begin) { + if(_end < _begin && size_to_read > (size_t)(_bufend - _begin)) { size_t top_size = _bufend - _begin; memcpy(dst, _begin, top_size); _begin = _buf; @@ -95,7 +95,7 @@ class cbuf { size_t bytes_available = room(); size_t size_to_write = (size < bytes_available) ? size : bytes_available; size_t size_written = size_to_write; - if(_end > _begin && size_to_write > _bufend - _end) { + if(_end > _begin && size_to_write > (size_t)(_bufend - _end)) { size_t top_size = _bufend - _end; memcpy(_end, src, top_size); _end = _buf; diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 67c3f3802..a953c8782 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -74,7 +74,9 @@ extern "C" void abort() { } extern "C" void esp_yield() { - cont_yield(&g_cont); + if (cont_can_yield(&g_cont)) { + cont_yield(&g_cont); + } } extern "C" void esp_schedule() { diff --git a/cores/esp8266/core_esp8266_noniso.c b/cores/esp8266/core_esp8266_noniso.c index 40fe5e6dd..d5b9d82f2 100644 --- a/cores/esp8266/core_esp8266_noniso.c +++ b/cores/esp8266/core_esp8266_noniso.c @@ -148,7 +148,8 @@ char* ultoa(unsigned long value, char* result, int base) { } char * dtostrf(double number, signed char width, unsigned char prec, char *s) { - + bool negative = false; + if (isnan(number)) { strcpy(s, "nan"); return s; @@ -158,50 +159,65 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { return s; } - if (number > 4294967040.0 || number < -4294967040.0) { - strcpy(s, "ovf"); - return s; - } - char* out = s; - int signInt_Part = 1; - + + int fillme = width; // how many cells to fill for the integer part + if (prec > 0) { + fillme -= (prec+1); + } + // Handle negative numbers if (number < 0.0) { - signInt_Part = -1; + negative = true; + fillme--; number = -number; } - // calc left over digits - if (prec > 0) - { - width -= (prec + 1); - } - // Round correctly so that print(1.999, 2) prints as "2.00" - double rounding = 0.5; + // I optimized out most of the divisions + double rounding = 2.0; for (uint8_t i = 0; i < prec; ++i) - rounding /= 10.0; + rounding *= 10.0; + rounding = 1.0 / rounding; number += rounding; - - // Extract the integer part of the number and print it - unsigned long int_part = (unsigned long)number; - double remainder = number - (double)int_part; - out += sprintf(out, "%*ld", width, int_part * signInt_Part); - - // Print the decimal point, but only if there are digits beyond - if (prec > 0) { - *out = '.'; - ++out; - - - for (unsigned char decShift = prec; decShift > 0; decShift--) { - remainder *= 10.0; - } - sprintf(out, "%0*d", prec, (int)remainder); + + // Figure out how big our number really is + double tenpow = 1.0; + int digitcount = 1; + while (number >= 10.0 * tenpow) { + tenpow *= 10.0; + digitcount++; + } + + number /= tenpow; + fillme -= digitcount; + + // Pad unused cells with spaces + while (fillme-- > 0) { + *out++ = ' '; + } + + // Handle negative sign + if (negative) *out++ = '-'; + + // Print the digits, and if necessary, the decimal point + digitcount += prec; + int8_t digit = 0; + while (digitcount-- > 0) { + digit = (int8_t)number; + if (digit > 9) digit = 9; // insurance + *out++ = (char)('0' | digit); + if ((digitcount == prec) && (prec > 0)) { + *out++ = '.'; + } + number -= digit; + number *= 10.0; } + // make sure the string is terminated + *out = 0; return s; } + diff --git a/cores/esp8266/core_esp8266_phy.c b/cores/esp8266/core_esp8266_phy.c index a4a189384..ea0beefb1 100644 --- a/cores/esp8266/core_esp8266_phy.c +++ b/cores/esp8266/core_esp8266_phy.c @@ -261,7 +261,10 @@ void user_rf_pre_init() { // *((volatile uint32_t*) 0x60000710) = 0; volatile uint32_t* rtc_reg = (volatile uint32_t*) 0x60001000; - rtc_reg[30] = 0; + if((rtc_reg[24] >> 16) > 4) { + rtc_reg[24] &= 0xFFFF; + rtc_reg[30] = 0; + } system_set_os_print(0); __run_user_rf_pre_init(); diff --git a/cores/esp8266/core_esp8266_postmortem.c b/cores/esp8266/core_esp8266_postmortem.c index f5ede6c76..c34e5918d 100644 --- a/cores/esp8266/core_esp8266_postmortem.c +++ b/cores/esp8266/core_esp8266_postmortem.c @@ -35,7 +35,7 @@ static void uart_write_char_d(char c); static void uart0_write_char_d(char c); static void uart1_write_char_d(char c); static void print_stack(uint32_t start, uint32_t end); -static void print_pcs(uint32_t start, uint32_t end); +//static void print_pcs(uint32_t start, uint32_t end); void __wrap_system_restart_local() { register uint32_t sp asm("a1"); @@ -108,6 +108,7 @@ static void print_stack(uint32_t start, uint32_t end) { ets_printf("<<>>pc>>>\n"); @@ -122,6 +123,7 @@ static void print_pcs(uint32_t start, uint32_t end) { } ets_printf("<<fn && (handler->mode == CHANGE || (handler->mode & 1) == digitalRead(i))) { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + uint32_t savedPS = xt_rsil(15); // stop other interrupts handler->fn(); + xt_wsr_ps(savedPS); } } ETS_GPIO_INTR_ENABLE(); @@ -152,9 +156,6 @@ extern void __detachInterrupt(uint8_t pin) { } } -// stored state for the noInterrupts/interrupts methods -uint32_t interruptsState = 0; - void initPins() { //Disable UART interrupts system_set_os_print(0); diff --git a/cores/esp8266/interrupts.h b/cores/esp8266/interrupts.h index d86f89b1f..813997447 100644 --- a/cores/esp8266/interrupts.h +++ b/cores/esp8266/interrupts.h @@ -8,23 +8,51 @@ extern "C" { #include "ets_sys.h" } +// these auto classes wrap up xt_rsil so your code can be simplier, but can only be +// used in an ino or cpp files. -#define xt_disable_interrupts(state, level) __asm__ __volatile__("rsil %0," __STRINGIFY(level) : "=a" (state)) -#define xt_enable_interrupts(state) __asm__ __volatile__("wsr %0,ps; isync" :: "a" (state) : "memory") +// InterruptLock is used when you want to completely disable locks +//{ +// { +// InterruptLock lock; +// // do work within interrupt lock here +// } +// do work outside of interrupt lock here outside its scope +//} +// class InterruptLock { public: InterruptLock() { - xt_disable_interrupts(_state, 15); + _state = xt_rsil(15); } ~InterruptLock() { - xt_enable_interrupts(_state); + xt_wsr_ps(_state); } protected: uint32_t _state; }; +// AutoInterruptLock is when you need to set a specific level, A normal use pattern is like +// +//{ +// { +// AutoInterruptLock(1); // this routine will allow level 2 and above +// // do work within interrupt lock here +// } +// do work outside of interrupt lock here outside its scope +//} +// +#define AutoInterruptLock(intrLevel) \ +class _AutoDisableIntr { \ +public: \ + _AutoDisableIntr() { _savedPS = xt_rsil(intrLevel); } \ + ~_AutoDisableIntr() { xt_wsr_ps(_savedPS); } \ +private: \ + uint32_t _savedPS; \ + }; \ +_AutoDisableIntr _autoDisableIntr #endif //INTERRUPTS_H diff --git a/cores/esp8266/libc_replacements.c b/cores/esp8266/libc_replacements.c index 84ba411e0..68d71b41f 100644 --- a/cores/esp8266/libc_replacements.c +++ b/cores/esp8266/libc_replacements.c @@ -215,57 +215,6 @@ char* ICACHE_FLASH_ATTR strdup(const char *str) { return cstr; } -long int ICACHE_FLASH_ATTR strtol(const char* str, char** endptr, int base) { - long int result = 0; - int sign = 1; - - while(isspace(*str)) { - str++; - } - - if(*str == 0x00) { - // only space in str? - *endptr = (char*) str; - return result; - } - - switch(base) { - case 10: - - if(*str == '-') { - sign = -1; - str++; - } else if(*str == '+') { - str++; - } - - for(uint8_t i = 0; *str; i++, str++) { - int x = *str - '0'; - if(x < 0 || x > 9) { - break; - } - result = result * 10 + x; - } - break; - case 2: - for(uint8_t i = 0; *str; i++, str++) { - int x = *str - '0'; - if(x < 0 || x > 1) { - break; - } - result = result * 2 + x; - } - break; - case 16: - default: - os_printf("fnk: strtol() only supports base 10 and 2 ATM!\n"); - break; - - } - *endptr = (char*) str; - return sign * result; -} - // based on Source: // https://github.com/anakod/Sming/blob/master/Sming/system/stringconversion.cpp#L93 double ICACHE_FLASH_ATTR strtod(const char* str, char** endptr) { @@ -280,7 +229,7 @@ double ICACHE_FLASH_ATTR strtod(const char* str, char** endptr) { if(*str == 0x00) { // only space in str? - *endptr = (char*) str; + if (endptr) *endptr = (char*) str; return result; } @@ -310,7 +259,7 @@ double ICACHE_FLASH_ATTR strtod(const char* str, char** endptr) { str++; } - *endptr = (char*) str; + if (endptr) *endptr = (char*) str; return result * factor; } @@ -479,16 +428,189 @@ size_t ICACHE_FLASH_ATTR strlcpy(char* dst, const char* src, size_t size) { break; } while (--n != 0); } - + if (n == 0) { if (size != 0) *dst = 0; while (*s++); } - + return(s - src - 1); } /* * end of newlib/string/strlcpy.c */ + + +/** + * strtol() and strtoul() implementations borrowed from newlib: + * http://www.sourceware.org/newlib/ + * newlib/libc/stdlib/strtol.c + * newlib/libc/stdlib/strtoul.c + * + * Adapted for ESP8266 by Kiril Zyapkov + * + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +long ICACHE_FLASH_ATTR strtol(const char *nptr, char **endptr, int base) { + const unsigned char *s = (const unsigned char *)nptr; + unsigned long acc; + int c; + unsigned long cutoff; + int neg = 0, any, cutlim; + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; + cutlim = cutoff % (unsigned long)base; + cutoff /= (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = neg ? LONG_MIN : LONG_MAX; + errno = ERANGE; + } else if (neg) + acc = -acc; + if (endptr != 0) + *endptr = (char *) (any ? (char *)s - 1 : nptr); + return (acc); +} + +unsigned long ICACHE_FLASH_ATTR strtoul(const char *nptr, char **endptr, int base) +{ + const unsigned char *s = (const unsigned char *)nptr; + unsigned long acc; + int c; + unsigned long cutoff; + int neg = 0, any, cutlim; + + /* + * See strtol for comments as to the logic used. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; + cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULONG_MAX; + errno = ERANGE; + } else if (neg) + acc = -acc; + if (endptr != 0) + *endptr = (char *) (any ? (char *)s - 1 : nptr); + return (acc); +} diff --git a/cores/esp8266/spiffs/README b/cores/esp8266/spiffs/README index 6efd656cb..673049d57 100644 --- a/cores/esp8266/spiffs/README +++ b/cores/esp8266/spiffs/README @@ -1,5 +1,5 @@ SPIFFS (SPI Flash File System) -V0.3.0 +V0.3.2 Copyright (c) 2013-2015 Peter Andersson (pelleplutt1976gmail.com) @@ -58,6 +58,30 @@ For testing and contributions, see the docs/IMPLEMENTING file. * HISTORY +0.3.2 + Limit cache size if too much cache is given (thanks pgeiem) + New feature - Controlled erase. #23 + SPIFFS_rename leaks file descriptors #28 (thanks benpicco) + moved dbg print defines in test framework to params_test.h + lseek should return the resulting offset (thanks hefloryd) + fixed type on dbg ifdefs + silence warning about signed/unsigned comparison when spiffs_obj_id is 32 bit (thanks benpicco) + Possible error in test_spiffs.c #21 (thanks yihcdaso-yeskela) + Cache might writethrough too often #16 + even moar testrunner updates + Test framework update and some added tests + Some thoughts for next gen + Test sigsevs when having too many sectors #13 (thanks alonewolfx2) + GC might be suboptimal #11 + Fix eternal readdir when objheader at last block, last entry + + New API functions: + SPIFFS_gc_quick - call a nonintrusive gc + SPIFFS_gc - call a full-scale intrusive gc + +0.3.1 + Removed two return warnings, was too triggerhappy on release + 0.3.0 Added existing namecheck when creating files Lots of static analysis bugs #6 diff --git a/cores/esp8266/spiffs/spiffs.h b/cores/esp8266/spiffs/spiffs.h index 376560436..7b59de9ed 100644 --- a/cores/esp8266/spiffs/spiffs.h +++ b/cores/esp8266/spiffs/spiffs.h @@ -9,7 +9,7 @@ #ifndef SPIFFS_H_ #define SPIFFS_H_ -#ifdef __cplusplus +#if defined(__cplusplus) extern "C" { #endif @@ -47,6 +47,9 @@ extern "C" { #define SPIFFS_ERR_ERASE_FAIL -10027 #define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028 +#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029 + +#define SPIFFS_ERR_FILE_EXISTS -10030 #define SPIFFS_ERR_INTERNAL -10050 @@ -62,12 +65,25 @@ typedef u16_t spiffs_mode; // object type typedef u8_t spiffs_obj_type; +#if SPIFFS_HAL_CALLBACK_EXTRA +struct spiffs_t; + +/* spi read call function type */ +typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst); +/* spi write call function type */ +typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src); +/* spi erase call function type */ +typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size); + +#else // SPIFFS_HAL_CALLBACK_EXTRA + /* spi read call function type */ typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); /* spi write call function type */ typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); /* spi erase call function type */ typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); +#endif // SPIFFS_HAL_CALLBACK_EXTRA /* file system check callback report operation */ typedef enum { @@ -88,21 +104,26 @@ typedef enum { } spiffs_check_report; /* file system check callback function */ +#if SPIFFS_HAL_CALLBACK_EXTRA +typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2); +#else // SPIFFS_HAL_CALLBACK_EXTRA typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, u32_t arg1, u32_t arg2); +#endif // SPIFFS_HAL_CALLBACK_EXTRA #ifndef SPIFFS_DBG #define SPIFFS_DBG(...) \ print(__VA_ARGS__) #endif #ifndef SPIFFS_GC_DBG -#define SPIFFS_GC_DBG(...) c_printf(__VA_ARGS__) +#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) #endif #ifndef SPIFFS_CACHE_DBG -#define SPIFFS_CACHE_DBG(...) c_printf(__VA_ARGS__) +#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) #endif #ifndef SPIFFS_CHECK_DBG -#define SPIFFS_CHECK_DBG(...) c_printf(__VA_ARGS__) +#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__) #endif /* Any write to the filehandle is appended to end of the file */ @@ -119,6 +140,8 @@ typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_repor #define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY) /* Any writes to the filehandle will never be cached */ #define SPIFFS_DIRECT (1<<5) +/* If SPIFFS_CREAT and SPIFFS_EXCL are set, SPIFFS_open() shall fail if the file exists */ +#define SPIFFS_EXCL (1<<6) #define SPIFFS_SEEK_SET (0) #define SPIFFS_SEEK_CUR (1) @@ -166,7 +189,7 @@ typedef struct { #endif } spiffs_config; -typedef struct { +typedef struct spiffs_t { // file system configuration spiffs_config cfg; // number of logical blocks @@ -224,6 +247,8 @@ typedef struct { // mounted flag u8_t mounted; + // user data + void *user_data; // config magic u32_t config_magic; } spiffs; @@ -387,7 +412,7 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); * @param fs the file system struct * @param fh the filehandle of the file to close */ -void SPIFFS_close(spiffs *fs, spiffs_file fh); +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh); /** * Renames a file @@ -440,7 +465,6 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); */ s32_t SPIFFS_check(spiffs *fs); - /** * Returns number of total bytes available and number of used bytes. * This is an estimation, and depends on if there a many files with little @@ -465,27 +489,60 @@ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used); * SPIFFS_format. * If SPIFFS_mount fails, SPIFFS_format can be called directly without calling * SPIFFS_unmount first. + * + * @param fs the file system struct */ s32_t SPIFFS_format(spiffs *fs); /** * Returns nonzero if spiffs is mounted, or zero if unmounted. + * @param fs the file system struct */ u8_t SPIFFS_mounted(spiffs *fs); /** - * Check if EOF reached. - * @param fs the file system struct - * @param fh the filehandle of the file to check + * Tries to find a block where most or all pages are deleted, and erase that + * block if found. Does not care for wear levelling. Will not move pages + * around. + * If parameter max_free_pages are set to 0, only blocks with only deleted + * pages will be selected. + * + * NB: the garbage collector is automatically called when spiffs needs free + * pages. The reason for this function is to give possibility to do background + * tidying when user knows the system is idle. + * + * Use with care. + * + * Setting max_free_pages to anything larger than zero will eventually wear + * flash more as a block containing free pages can be erased. + * + * Will set err_no to SPIFFS_OK if a block was found and erased, + * SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found, + * or other error. + * + * @param fs the file system struct + * @param max_free_pages maximum number allowed free pages in block */ -s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages); /** - * Get the current position of the data pointer. + * Will try to make room for given amount of bytes in the filesystem by moving + * pages and erasing blocks. + * If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If + * there already is this amount (or more) of free space, SPIFFS_gc will + * silently return. It is recommended to call SPIFFS_info before invoking + * this method in order to determine what amount of bytes to give. + * + * NB: the garbage collector is automatically called when spiffs needs free + * pages. The reason for this function is to give possibility to do background + * tidying when user knows the system is idle. + * + * Use with care. + * * @param fs the file system struct - * @param fh the filehandle of the open file + * @param size amount of bytes that should be freed */ -s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); +s32_t SPIFFS_gc(spiffs *fs, u32_t size); #if SPIFFS_TEST_VISUALISATION /** @@ -511,8 +568,9 @@ u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages); #endif #endif - -#ifdef __cplusplus +#if SPIFFS_CACHE +#endif +#if defined(__cplusplus) } #endif diff --git a/cores/esp8266/spiffs/spiffs_cache.c b/cores/esp8266/spiffs/spiffs_cache.c index 5bfe51e86..ea9bc82c5 100644 --- a/cores/esp8266/spiffs/spiffs_cache.c +++ b/cores/esp8266/spiffs/spiffs_cache.c @@ -20,7 +20,7 @@ static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && cp->pix == pix ) { - SPIFFS_CACHE_DBG("CACHE_GET: have cache page %d for %04x\n", i, pix); + SPIFFS_CACHE_DBG("CACHE_GET: have cache page %i for %04x\n", i, pix); cp->last_access = cache->last_access; return cp; } @@ -39,16 +39,16 @@ static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) { (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { u8_t *mem = spiffs_get_cache_page(fs, cache, ix); - res = fs->cfg.hal_write_f(SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); } cp->flags = 0; cache->cpage_use_map &= ~(1 << ix); if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) { - SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %d objid %04x\n", ix, cp->obj_id); + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i objid %04x\n", ix, cp->obj_id); } else { - SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %d pix %04x\n", ix, cp->pix); + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i pix %04x\n", ix, cp->pix); } } @@ -98,7 +98,7 @@ static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) { spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); cache->cpage_use_map |= (1<last_access = cache->last_access; - SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page %d\n", i); + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page %i\n", i); return cp; } } @@ -137,10 +137,7 @@ s32_t spiffs_phys_rd( } else { if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) { // for second layer lookup functions, we do not cache in order to prevent shredding - return fs->cfg.hal_read_f( - addr , - len, - dst); + return SPIFFS_HAL_READ(fs, addr, len, dst); } #if SPIFFS_CACHE_STATS fs->cache_misses++; @@ -151,8 +148,7 @@ s32_t spiffs_phys_rd( cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr); } - - s32_t res2 = fs->cfg.hal_read_f( + s32_t res2 = SPIFFS_HAL_READ(fs, addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), SPIFFS_CFG_LOG_PAGE_SZ(fs), spiffs_get_cache_page(fs, cache, cp->ix)); @@ -161,7 +157,7 @@ s32_t spiffs_phys_rd( } } u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); - c_memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); return res; } @@ -186,24 +182,24 @@ s32_t spiffs_phys_wr( (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) { // page is being deleted, wipe from cache - unless it is a lookup page spiffs_cache_page_free(fs, cp->ix, 0); - return fs->cfg.hal_write_f(addr, len, src); + return SPIFFS_HAL_WRITE(fs, addr, len, src); } u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); - c_memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); + memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); cache->last_access++; cp->last_access = cache->last_access; - if (cp->flags && SPIFFS_CACHE_FLAG_WRTHRU) { + if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) { // page is being updated, no write-cache, just pass thru - return fs->cfg.hal_write_f(addr, len, src); + return SPIFFS_HAL_WRITE(fs, addr, len, src); } else { return SPIFFS_OK; } } else { // no cache page, no write cache - just write thru - return fs->cfg.hal_write_f(addr, len, src); + return SPIFFS_HAL_WRITE(fs, addr, len, src); } } @@ -282,17 +278,17 @@ void spiffs_cache_init(spiffs *fs) { } spiffs_cache cache; - c_memset(&cache, 0, sizeof(spiffs_cache)); + memset(&cache, 0, sizeof(spiffs_cache)); cache.cpage_count = cache_entries; cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache)); cache.cpage_use_map = 0xffffffff; cache.cpage_use_mask = cache_mask; - c_memcpy(fs->cache, &cache, sizeof(spiffs_cache)); + memcpy(fs->cache, &cache, sizeof(spiffs_cache)); spiffs_cache *c = spiffs_get_cache(fs); - c_memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs)); + memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs)); c->cpage_use_map &= ~(c->cpage_use_mask); for (i = 0; i < cache.cpage_count; i++) { diff --git a/cores/esp8266/spiffs/spiffs_check.c b/cores/esp8266/spiffs/spiffs_check.c index 50bbb5c89..180ce345a 100644 --- a/cores/esp8266/spiffs/spiffs_check.c +++ b/cores/esp8266/spiffs/spiffs_check.c @@ -22,6 +22,19 @@ #include "spiffs.h" #include "spiffs_nucleus.h" + +#if SPIFFS_HAL_CALLBACK_EXTRA +#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \ + } while (0) +#else +#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \ + } while (0) +#endif + //--------------------------------------- // Look up consistency @@ -190,13 +203,13 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("LU: FIXUP: index bad %d, cannot mend!\n", res); + SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res); res = spiffs_page_delete(fs, new_pix); SPIFFS_CHECK_RES(res); res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); } else { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); } SPIFFS_CHECK_RES(res); } @@ -216,7 +229,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix); SPIFFS_CHECK_RES(res); *reload_lu = 1; - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); } } else { SPIFFS_CHECK_RES(res); @@ -249,12 +262,12 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("LU: FIXUP: index bad %d, cannot mend!\n", res); + SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res); res = spiffs_page_delete(fs, new_pix); SPIFFS_CHECK_RES(res); res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); *reload_lu = 1; - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); } SPIFFS_CHECK_RES(res); } @@ -306,7 +319,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG; res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x to pix %04x\n", cur_pix, new_ph.obj_id, new_pix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); SPIFFS_CHECK_RES(res); *reload_lu = 1; } else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) || @@ -315,7 +328,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // rewrite as obj_id_lu new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x\n", cur_pix, new_ph.obj_id); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); SPIFFS_CHECK_RES(res); *reload_lu = 1; @@ -353,7 +366,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // if only data page exists, make this page index if (data_pix && objix_pix_d == 0) { SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n"); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); spiffs_page_header new_ph; spiffs_page_ix new_pix; new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); @@ -369,7 +382,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // if only index exists, make data page if (data_pix == 0 && objix_pix_d) { SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n"); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); spiffs_page_header new_ph; spiffs_page_ix new_pix; new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); @@ -406,7 +419,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // page referenced by object index but not final // just finalize SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n"); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL; res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), @@ -418,7 +431,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s if (delete_page) { SPIFFS_CHECK_DBG("LU: FIXUP: deleting page %04x\n", cur_pix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); res = spiffs_page_delete(fs, cur_pix); SPIFFS_CHECK_RES(res); } @@ -434,7 +447,7 @@ static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_bloc spiffs_page_header p_hdr; spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, (cur_block * 256)/fs->block_count, 0); // load header @@ -460,7 +473,7 @@ s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { (void)check_all_objects; s32_t res = SPIFFS_OK; - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0); @@ -469,10 +482,10 @@ s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { } if (res != SPIFFS_OK) { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); } - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); return res; } @@ -501,19 +514,22 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) { // set this flag to abort all checks and rescan the page range u8_t restart = 0; - c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); spiffs_block_ix cur_block = 0; // build consistency bitmap for id range traversing all blocks while (!restart && cur_block < fs->block_count) { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, (pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + ((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), 0); - // traverse each page except for lookup pages spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) { + //if ((cur_pix & 0xff) == 0) + // SPIFFS_CHECK_DBG("PA: processing pix %08x, block %08x of pix %08x, block %08x\n", + // cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count); + // read header spiffs_page_header p_hdr; res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, @@ -597,12 +613,12 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { data_spix_offset + i, data_pix, cur_pix); if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("PA: FIXUP: index bad %d, cannot mend - delete object\n", res); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); + SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend - delete object\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); // delete file res = spiffs_page_delete(fs, cur_pix); } else { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); } SPIFFS_CHECK_RES(res); restart = 1; @@ -636,7 +652,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { if (data_pix == 0) { // not found, this index is badly borked SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id %04x\n", p_hdr.obj_id); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); SPIFFS_CHECK_RES(res); break; @@ -647,11 +663,11 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix); if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("PA: FIXUP: index bad %d, cannot mend!\n", res); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); } else { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); } SPIFFS_CHECK_RES(res); restart = 1; @@ -669,7 +685,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { // the object which is referring to this page SPIFFS_CHECK_DBG("PA: FIXUP: removing object %04x and page %04x\n", p_hdr.obj_id, cur_pix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); SPIFFS_CHECK_RES(res); // extra precaution, delete this page also @@ -763,20 +779,20 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("PA: FIXUP: index bad %d, cannot mend!\n", res); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); res = spiffs_page_delete(fs, cur_pix); SPIFFS_CHECK_RES(res); res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); } else { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); } SPIFFS_CHECK_RES(res); restart = 1; continue; } else if (delete_page) { SPIFFS_CHECK_DBG("PA: FIXUP: deleting page %04x\n", cur_pix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); res = spiffs_page_delete(fs, cur_pix); } SPIFFS_CHECK_RES(res); @@ -818,6 +834,8 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { } } } + + SPIFFS_CHECK_DBG("PA: processed %04x, restart %i\n", pix_offset, restart); // next page range if (!restart) { pix_offset += pages_per_scan; @@ -828,12 +846,12 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { // Checks consistency amongst all pages and fixes irregularities s32_t spiffs_page_consistency_check(spiffs *fs) { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); s32_t res = spiffs_page_consistency_check_i(fs); if (res != SPIFFS_OK) { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); } - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); return res; } @@ -862,7 +880,7 @@ static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id o u32_t *log_ix = (u32_t *)user_p; spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, (cur_block * 256)/fs->block_count, 0); if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { @@ -879,7 +897,7 @@ static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id o (SPIFFS_PH_FLAG_DELET)) { SPIFFS_CHECK_DBG("IX: pix %04x, obj id:%04x spix:%04x header not fully deleted - deleting\n", cur_pix, obj_id, p_hdr.span_ix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); res = spiffs_page_delete(fs, cur_pix); SPIFFS_CHECK_RES(res); return res_c; @@ -935,7 +953,7 @@ static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id o if (delete) { SPIFFS_CHECK_DBG("IX: FIXUP: pix %04x, obj id:%04x spix:%04x is orphan index - deleting\n", cur_pix, obj_id, p_hdr.span_ix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); res = spiffs_page_delete(fs, cur_pix); SPIFFS_CHECK_RES(res); } @@ -956,18 +974,18 @@ s32_t spiffs_object_index_consistency_check(spiffs *fs) { // indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit. // In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate // a reachable/unreachable object id. - c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); u32_t obj_id_log_ix = 0; - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix, 0, 0); if (res == SPIFFS_VIS_END) { res = SPIFFS_OK; } if (res != SPIFFS_OK) { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); } - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); return res; } diff --git a/cores/esp8266/spiffs/spiffs_gc.c b/cores/esp8266/spiffs/spiffs_gc.c index fff659e3a..b60d7d3f2 100644 --- a/cores/esp8266/spiffs/spiffs_gc.c +++ b/cores/esp8266/spiffs/spiffs_gc.c @@ -9,7 +9,7 @@ static s32_t spiffs_gc_erase_block( spiffs_block_ix bix) { s32_t res; - SPIFFS_GC_DBG("gc: erase block %d\n", bix); + SPIFFS_GC_DBG("gc: erase block %i\n", bix); res = spiffs_erase_block(fs, bix); SPIFFS_CHECK_RES(res); @@ -28,7 +28,7 @@ static s32_t spiffs_gc_erase_block( // the block is erased. Compared to the non-quick gc, the quick one ensures // that no updates are needed on existing objects on pages that are erased. s32_t spiffs_gc_quick( - spiffs *fs) { + spiffs *fs, u16_t max_free_pages) { s32_t res = SPIFFS_OK; u32_t blocks = fs->block_count; spiffs_block_ix cur_block = 0; @@ -47,6 +47,7 @@ s32_t spiffs_gc_quick( // check each block while (res == SPIFFS_OK && blocks--) { u16_t deleted_pages_in_block = 0; + u16_t free_pages_in_block = 0; int obj_lookup_page = 0; // check each object lookup page @@ -63,9 +64,12 @@ s32_t spiffs_gc_quick( deleted_pages_in_block++; } else if (obj_id == SPIFFS_OBJ_ID_FREE) { // kill scan, go for next block - obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); - res = 1; // kill object lu loop - break; + free_pages_in_block++; + if (free_pages_in_block > max_free_pages) { + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + res = 1; // kill object lu loop + break; + } } else { // kill scan, go for next block obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); @@ -78,7 +82,9 @@ s32_t spiffs_gc_quick( } // per object lookup page if (res == 1) res = SPIFFS_OK; - if (res == SPIFFS_OK && deleted_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + if (res == SPIFFS_OK && + deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) && + free_pages_in_block <= max_free_pages) { // found a fully deleted block fs->stats_p_deleted -= deleted_pages_in_block; res = spiffs_gc_erase_block(fs, cur_block); @@ -90,10 +96,13 @@ s32_t spiffs_gc_quick( cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); } // per block + if (res == SPIFFS_OK) { + res = SPIFFS_ERR_NO_DELETED_BLOCKS; + } return res; } -// Checks if garbaga collecting is necessary. If so a candidate block is found, +// Checks if garbage collecting is necessary. If so a candidate block is found, // cleansed and erased s32_t spiffs_gc_check( spiffs *fs, @@ -111,16 +120,16 @@ s32_t spiffs_gc_check( u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); // if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { -// SPIFFS_GC_DBG("gc: full freeblk:%d needed:%d free:%d dele:%d\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); +// SPIFFS_GC_DBG("gc: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); // return SPIFFS_ERR_FULL; // } if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) { - SPIFFS_GC_DBG("gc_check: full freeblk:%d needed:%d free:%d dele:%d\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); + SPIFFS_GC_DBG("gc_check: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); return SPIFFS_ERR_FULL; } do { - SPIFFS_GC_DBG("\ngc_check #%d: run gc free_blocks:%d pfree:%d pallo:%d pdele:%d [%d] len:%d of %d\n", + SPIFFS_GC_DBG("\ngc_check #%i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n", tries, fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted), len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs)); @@ -141,13 +150,13 @@ s32_t spiffs_gc_check( #endif cand = cands[0]; fs->cleaning = 1; - //c_printf("gcing: cleaning block %d\n", cand); + //printf("gcing: cleaning block %i\n", cand); res = spiffs_gc_clean(fs, cand); fs->cleaning = 0; if (res < 0) { - SPIFFS_GC_DBG("gc_check: cleaning block %d, result %d\n", cand, res); + SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res); } else { - SPIFFS_GC_DBG("gc_check: cleaning block %d, result %d\n", cand, res); + SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res); } SPIFFS_CHECK_RES(res); @@ -177,7 +186,7 @@ s32_t spiffs_gc_check( res = SPIFFS_ERR_FULL; } - SPIFFS_GC_DBG("gc_check: finished, %d dirty, blocks %d free, %d pages free, %d tries, res %d\n", + SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n", fs->stats_p_allocated + fs->stats_p_deleted, fs->free_blocks, free_pages, tries, res); @@ -215,7 +224,7 @@ s32_t spiffs_gc_erase_page_stats( } // per entry obj_lookup_page++; } // per object lookup page - SPIFFS_GC_DBG("gc_check: wipe pallo:%d pdele:%d\n", allo, dele); + SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele); fs->stats_p_allocated -= allo; fs->stats_p_deleted -= dele; return res; @@ -237,13 +246,18 @@ s32_t spiffs_gc_find_candidate( // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t))); *candidate_count = 0; - c_memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); // divide up work area into block indices and scores - // todo alignment? spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); + // align cand_scores on s32_t boundary +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-to-int-cast" + cand_scores = (s32_t*)(((u32_t)cand_scores + sizeof(s32_t) - 1) & ~(sizeof(s32_t) - 1)); +#pragma GCC diagnostic pop + *block_candidates = cand_blocks; int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); @@ -301,7 +315,7 @@ s32_t spiffs_gc_find_candidate( used_pages_in_block * SPIFFS_GC_HEUR_W_USED + erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); int cand_ix = 0; - SPIFFS_GC_DBG("gc_check: bix:%d del:%d use:%d score:%d\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); + SPIFFS_GC_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); while (cand_ix < max_candidates) { if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { cand_blocks[cand_ix] = cur_block; @@ -370,20 +384,20 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; - SPIFFS_GC_DBG("gc_clean: cleaning block %d\n", bix); + SPIFFS_GC_DBG("gc_clean: cleaning block %i\n", bix); - c_memset(&gc, 0, sizeof(spiffs_gc)); + memset(&gc, 0, sizeof(spiffs_gc)); gc.state = FIND_OBJ_DATA; if (fs->free_cursor_block_ix == bix) { // move free cursor to next block, cannot use free pages from the block we want to clean fs->free_cursor_block_ix = (bix+1)%fs->block_count; fs->free_cursor_obj_lu_entry = 0; - SPIFFS_GC_DBG("gc_clean: move free cursor to block %d\n", fs->free_cursor_block_ix); + SPIFFS_GC_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix); } while (res == SPIFFS_OK && gc.state != FINISHED) { - SPIFFS_GC_DBG("gc_clean: state = %d entry:%d\n", gc.state, cur_entry); + SPIFFS_GC_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry); gc.obj_id_found = 0; // scan through lookup pages @@ -406,7 +420,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { case FIND_OBJ_DATA: if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) { - SPIFFS_GC_DBG("gc_clean: FIND_DATA state:%d - found obj id %04x\n", gc.state, obj_id); + SPIFFS_GC_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id); gc.obj_id_found = 1; gc.cur_obj_id = obj_id; scan = 0; @@ -550,9 +564,10 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { cur_entry = 0; break; } - SPIFFS_GC_DBG("gc_clean: state-> %d\n", gc.state); + SPIFFS_GC_DBG("gc_clean: state-> %i\n", gc.state); } // while state != FINISHED return res; } + diff --git a/cores/esp8266/spiffs/spiffs_hydrogen.c b/cores/esp8266/spiffs/spiffs_hydrogen.c index 6601b61ad..72b75d6ef 100644 --- a/cores/esp8266/spiffs/spiffs_hydrogen.c +++ b/cores/esp8266/spiffs/spiffs_hydrogen.c @@ -8,7 +8,9 @@ #include "spiffs.h" #include "spiffs_nucleus.h" +#if SPIFFS_CACHE == 1 static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); +#endif #if SPIFFS_BUFFER_HELP u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) { @@ -56,12 +58,12 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, void *cache, u32_t cache_size, spiffs_check_callback check_cb_f) { SPIFFS_LOCK(fs); - c_memset(fs, 0, sizeof(spiffs)); - c_memcpy(&fs->cfg, config, sizeof(spiffs_config)); + memset(fs, 0, sizeof(spiffs)); + memcpy(&fs->cfg, config, sizeof(spiffs_config)); fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); fs->work = &work[0]; fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; - c_memset(fd_space, 0, fd_space_size); + memset(fd_space, 0, fd_space_size); // align fd_space pointer to pointer size byte boundary, below is safe u8_t ptr_size = sizeof(void*); #pragma GCC diagnostic push @@ -89,9 +91,10 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, if (cache_size & (ptr_size-1)) { cache_size -= (cache_size & (ptr_size-1)); } + #if SPIFFS_CACHE fs->cache = cache; - fs->cache_size = cache_size; + fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs)*32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs)*32 : cache_size; spiffs_cache_init(fs); #endif @@ -107,14 +110,14 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, res = spiffs_obj_lu_scan(fs); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_DBG("page index byte len: %d\n", SPIFFS_CFG_LOG_PAGE_SZ(fs)); - SPIFFS_DBG("object lookup pages: %d\n", SPIFFS_OBJ_LOOKUP_PAGES(fs)); - SPIFFS_DBG("page pages per block: %d\n", SPIFFS_PAGES_PER_BLOCK(fs)); - SPIFFS_DBG("page header length: %d\n", sizeof(spiffs_page_header)); - SPIFFS_DBG("object header index entries: %d\n", SPIFFS_OBJ_HDR_IX_LEN(fs)); - SPIFFS_DBG("object index entries: %d\n", SPIFFS_OBJ_IX_LEN(fs)); - SPIFFS_DBG("available file descriptors: %d\n", fs->fd_count); - SPIFFS_DBG("free blocks: %d\n", fs->free_blocks); + SPIFFS_DBG("page index byte len: %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: %i\n", SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: %i\n", sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: %i\n", SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: %i\n", fs->fd_count); + SPIFFS_DBG("free blocks: %i\n", fs->free_blocks); fs->check_cb_f = check_cb_f; @@ -188,6 +191,14 @@ spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } + if (res == SPIFFS_OK && + (flags & (SPIFFS_CREAT | SPIFFS_EXCL)) == (SPIFFS_CREAT | SPIFFS_EXCL)) { + // creat and excl and file exists - fail + res = SPIFFS_ERR_FILE_EXISTS; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + if ((flags & SPIFFS_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { spiffs_obj_id obj_id; // no need to enter conflicting name here, already looked for it above @@ -377,7 +388,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page { // boundary violation, write back cache first and allocate new - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %d for fd %d:%04x, boundary viol, offs:%d size:%d\n", + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, boundary viol, offs:%i size:%i\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); res = spiffs_hydro_write(fs, fd, spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), @@ -395,19 +406,19 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { if (fd->cache_page) { fd->cache_page->offset = offset; fd->cache_page->size = 0; - SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page %d for fd %d:%04x\n", + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page %i for fd %i:%04x\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id); } } if (fd->cache_page) { u32_t offset_in_cpage = offset - fd->cache_page->offset; - SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page %d for fd %d:%04x, offs %d:%d len %d\n", + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page %i for fd %i:%04x, offs %i:%i len %i\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id, offset, offset_in_cpage, len); spiffs_cache *cache = spiffs_get_cache(fs); u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); - c_memcpy(&cpage_data[offset_in_cpage], buf, len); + memcpy(&cpage_data[offset_in_cpage], buf, len); fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len); fd->fdoffset += len; SPIFFS_UNLOCK(fs); @@ -423,7 +434,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { // big write, no need to cache it - but first check if there is a cached write already if (fd->cache_page) { // write back cache first - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %d for fd %d:%04x, big write, offs:%d size:%d\n", + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, big write, offs:%i size:%i\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); res = spiffs_hydro_write(fs, fd, spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), @@ -488,7 +499,7 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { SPIFFS_UNLOCK(fs); - return 0; + return offs; } s32_t SPIFFS_remove(spiffs *fs, char *path) { @@ -554,6 +565,7 @@ s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { } static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { + (void)fh; spiffs_page_object_ix_header objix_hdr; spiffs_obj_id obj_id; s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, @@ -616,7 +628,10 @@ s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { // Checks if there are any cached writes for the object id associated with // given filehandle. If so, these writes are flushed. +#if SPIFFS_CACHE == 1 static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { + (void)fs; + (void)fh; s32_t res = SPIFFS_OK; #if SPIFFS_CACHE_WR @@ -630,7 +645,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); } if (fd->cache_page) { - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %d for fd %d:%04x, flush, offs:%d size:%d\n", + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, flush, offs:%i size:%i\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); res = spiffs_hydro_write(fs, fd, spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), @@ -645,8 +660,10 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { return res; } +#endif s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { + (void)fh; SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); s32_t res = SPIFFS_OK; @@ -660,24 +677,23 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { return res; } -void SPIFFS_close(spiffs *fs, spiffs_file fh) { - if (!SPIFFS_CHECK_CFG((fs))) { - (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; - return; - } +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); - if (!SPIFFS_CHECK_MOUNT(fs)) { - fs->err_code = SPIFFS_ERR_NOT_MOUNTED; - return; - } + s32_t res = SPIFFS_OK; SPIFFS_LOCK(fs); #if SPIFFS_CACHE - spiffs_fflush_cache(fs, fh); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #endif - spiffs_fd_return(fs, fh); + res = spiffs_fd_return(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_UNLOCK(fs); + + return res; } s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) { @@ -711,9 +727,8 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) { res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (u8_t*)new, 0, &pix_dummy); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_UNLOCK(fs); @@ -780,7 +795,7 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; return 0; } - SPIFFS_LOCK(fs); + SPIFFS_LOCK(d->fs); spiffs_block_ix bix; int entry; @@ -804,7 +819,7 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { } else { d->fs->err_code = res; } - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(d->fs); return ret; } @@ -856,46 +871,34 @@ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { return res; } -s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { + s32_t res; SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); - spiffs_fd *fd; - s32_t res; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES(fs, res); - -#if SPIFFS_CACHE_WR - spiffs_fflush_cache(fs, fh); -#endif - - res = (fd->fdoffset == fd->size); + res = spiffs_gc_quick(fs, max_free_pages); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_UNLOCK(fs); - return res; + return 0; } -s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { + +s32_t SPIFFS_gc(spiffs *fs, u32_t size) { + s32_t res; SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); - spiffs_fd *fd; - s32_t res; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES(fs, res); - -#if SPIFFS_CACHE_WR - spiffs_fflush_cache(fs, fh); -#endif - - res = fd->fdoffset; + res = spiffs_gc_check(fs, size); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_UNLOCK(fs); - return res; + return 0; } + #if SPIFFS_TEST_VISUALISATION s32_t SPIFFS_vis(spiffs *fs) { s32_t res = SPIFFS_OK; @@ -949,7 +952,7 @@ s32_t SPIFFS_vis(spiffs *fs) { SPIFFS_CHECK_RES(res); if (erase_count != (spiffs_obj_id)-1) { - spiffs_printf("\tera_cnt: %d\n", erase_count); + spiffs_printf("\tera_cnt: %i\n", erase_count); } else { spiffs_printf("\tera_cnt: N/A\n"); } @@ -957,15 +960,15 @@ s32_t SPIFFS_vis(spiffs *fs) { bix++; } // per block - spiffs_printf("era_cnt_max: %d\n", fs->max_erase_count); - spiffs_printf("last_errno: %d\n", fs->err_code); - spiffs_printf("blocks: %d\n", fs->block_count); - spiffs_printf("free_blocks: %d\n", fs->free_blocks); - spiffs_printf("page_alloc: %d\n", fs->stats_p_allocated); - spiffs_printf("page_delet: %d\n", fs->stats_p_deleted); + spiffs_printf("era_cnt_max: %i\n", fs->max_erase_count); + spiffs_printf("last_errno: %i\n", fs->err_code); + spiffs_printf("blocks: %i\n", fs->block_count); + spiffs_printf("free_blocks: %i\n", fs->free_blocks); + spiffs_printf("page_alloc: %i\n", fs->stats_p_allocated); + spiffs_printf("page_delet: %i\n", fs->stats_p_deleted); u32_t total, used; SPIFFS_info(fs, &total, &used); - spiffs_printf("used: %d of %d\n", used, total); + spiffs_printf("used: %i of %i\n", used, total); SPIFFS_UNLOCK(fs); return res; diff --git a/cores/esp8266/spiffs/spiffs_nucleus.c b/cores/esp8266/spiffs/spiffs_nucleus.c index fa75fd8e9..18712c6e1 100644 --- a/cores/esp8266/spiffs/spiffs_nucleus.c +++ b/cores/esp8266/spiffs/spiffs_nucleus.c @@ -64,7 +64,7 @@ s32_t spiffs_phys_rd( u32_t addr, u32_t len, u8_t *dst) { - return fs->cfg.hal_read_f(addr, len, dst); + return SPIFFS_HAL_READ(fs, addr, len, dst); } s32_t spiffs_phys_wr( @@ -72,7 +72,7 @@ s32_t spiffs_phys_wr( u32_t addr, u32_t len, u8_t *src) { - return fs->cfg.hal_write_f(addr, len, src); + return SPIFFS_HAL_WRITE(fs, addr, len, src); } #endif @@ -83,6 +83,7 @@ s32_t spiffs_phys_cpy( u32_t dst, u32_t src, u32_t len) { + (void)fh; s32_t res; u8_t b[SPIFFS_COPY_BUFFER_STACK]; while (len > 0) { @@ -227,7 +228,8 @@ s32_t spiffs_erase_block( // here we ignore res, just try erasing the block while (size > 0) { SPIFFS_DBG("erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); - (void)fs->cfg.hal_erase_f(addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); } @@ -347,7 +349,7 @@ s32_t spiffs_obj_lu_scan( #if SPIFFS_USE_MAGIC if (unerased_bix != (spiffs_block_ix)-1) { // found one unerased block, remedy - SPIFFS_DBG("mount: erase block %d\n", bix); + SPIFFS_DBG("mount: erase block %i\n", bix); res = spiffs_erase_block(fs, unerased_bix); SPIFFS_CHECK_RES(res); } @@ -389,7 +391,10 @@ s32_t spiffs_obj_lu_find_free( int *lu_entry) { s32_t res; if (!fs->cleaning && fs->free_blocks < 2) { - res = spiffs_gc_quick(fs); + res = spiffs_gc_quick(fs, 0); + if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) { + res = SPIFFS_OK; + } SPIFFS_CHECK_RES(res); if (fs->free_blocks < 2) { return SPIFFS_ERR_FULL; @@ -701,7 +706,7 @@ s32_t spiffs_object_create( // find free entry res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); SPIFFS_CHECK_RES(res); - SPIFFS_DBG("create: found free page @ %04x bix:%d entry:%d\n", SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); + SPIFFS_DBG("create: found free page @ %04x bix:%i entry:%i\n", SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); // occupy page in object lookup res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, @@ -806,7 +811,7 @@ void spiffs_cb_object_event( if (cur_fd->file_nbr == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; if (spix == 0) { if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) { - SPIFFS_DBG(" callback: setting fd %d:%04x objix_hdr_pix to %04x, size:%d\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size); + SPIFFS_DBG(" callback: setting fd %i:%04x objix_hdr_pix to %04x, size:%i\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size); cur_fd->objix_hdr_pix = new_pix; if (new_size != 0) { cur_fd->size = new_size; @@ -818,7 +823,7 @@ void spiffs_cb_object_event( } if (cur_fd->cursor_objix_spix == spix) { if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) { - SPIFFS_DBG(" callback: setting fd %d:%04x span:%04x objix_pix to %04x\n", cur_fd->file_nbr, cur_fd->obj_id, spix, new_pix); + SPIFFS_DBG(" callback: setting fd %i:%04x span:%04x objix_pix to %04x\n", cur_fd->file_nbr, cur_fd->obj_id, spix, new_pix); cur_fd->cursor_objix_pix = new_pix; } else { cur_fd->cursor_objix_pix = 0; @@ -878,7 +883,7 @@ s32_t spiffs_object_open_by_page( SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); - SPIFFS_DBG("open: fd %d is obj id %04x\n", fd->file_nbr, fd->obj_id); + SPIFFS_DBG("open: fd %i is obj id %04x\n", fd->file_nbr, fd->obj_id); return res; } @@ -890,7 +895,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { s32_t res = SPIFFS_OK; u32_t written = 0; - SPIFFS_DBG("append: %d bytes @ offs %d of size %d\n", len, offset, fd->size); + SPIFFS_DBG("append: %i bytes @ offs %i of size %i\n", len, offset, fd->size); if (offset > fd->size) { SPIFFS_DBG("append: offset reversed to size\n"); @@ -899,7 +904,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta if (res != SPIFFS_OK) { - SPIFFS_DBG("append: gc check fail %d\n", res); + SPIFFS_DBG("append: gc check fail %i\n", res); } SPIFFS_CHECK_RES(res); @@ -927,7 +932,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // within this clause we return directly if something fails, object index mess-up if (written > 0) { // store previous object index page, unless first pass - SPIFFS_DBG("append: %04x store objix %04x:%04x, written %d\n", fd->obj_id, + SPIFFS_DBG("append: %04x store objix %04x:%04x, written %i\n", fd->obj_id, cur_objix_pix, prev_objix_spix, written); if (prev_objix_spix == 0) { // this is an update to object index header page @@ -944,7 +949,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page); SPIFFS_CHECK_RES(res); - SPIFFS_DBG("append: %04x store new objix_hdr, %04x:%04x, written %d\n", fd->obj_id, + SPIFFS_DBG("append: %04x store new objix_hdr, %04x:%04x, written %i\n", fd->obj_id, new_objix_hdr_page, 0, written); } } else { @@ -960,7 +965,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); SPIFFS_CHECK_RES(res); - SPIFFS_DBG("append: %04x store new size I %d in objix_hdr, %04x:%04x, written %d\n", fd->obj_id, + SPIFFS_DBG("append: %04x store new size I %i in objix_hdr, %04x:%04x, written %i\n", fd->obj_id, offset+written, new_objix_hdr_page, 0, written); } fd->size = offset+written; @@ -987,9 +992,9 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { SPIFFS_CHECK_RES(res); spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); // quick "load" of new object index page - c_memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - c_memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header)); - SPIFFS_DBG("append: %04x create objix page, %04x:%04x, written %d\n", fd->obj_id + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header)); + SPIFFS_DBG("append: %04x create objix page, %04x:%04x, written %i\n", fd->obj_id , cur_objix_pix, cur_objix_spix, written); } else { // on first pass, we load existing object index page @@ -1001,7 +1006,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); SPIFFS_CHECK_RES(res); } - SPIFFS_DBG("append: %04x found object index at page %04x [fd size %d]\n", fd->obj_id, pix, fd->size); + SPIFFS_DBG("append: %04x found object index at page %04x [fd size %i]\n", fd->obj_id, pix, fd->size); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res); @@ -1025,7 +1030,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, &p_hdr, &data[written], to_write, page_offs, 1, &data_page); - SPIFFS_DBG("append: %04x store new data page, %04x:%04x offset:%d, len %d, written %d\n", fd->obj_id, + SPIFFS_DBG("append: %04x store new data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id, data_page, data_spix, page_offs, to_write, written); } else { // append to existing page, fill out free data in existing page @@ -1042,7 +1047,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); - SPIFFS_DBG("append: %04x store to existing data page, %04x:%04x offset:%d, len %d, written %d\n", fd->obj_id + SPIFFS_DBG("append: %04x store to existing data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id , data_page, data_spix, page_offs, to_write, written); } @@ -1078,7 +1083,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { if (cur_objix_spix != 0) { // wrote beyond object index header page // write last modified object index page, unless object header index page - SPIFFS_DBG("append: %04x store objix page, %04x:%04x, written %d\n", fd->obj_id, + SPIFFS_DBG("append: %04x store objix page, %04x:%04x, written %i\n", fd->obj_id, cur_objix_pix, cur_objix_spix, written); res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); @@ -1092,7 +1097,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // update size in object header index page res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_DBG("append: %04x store new size II %d in objix_hdr, %04x:%04x, written %d, res %d\n", fd->obj_id + SPIFFS_DBG("append: %04x store new size II %i in objix_hdr, %04x:%04x, written %i, res %i\n", fd->obj_id , offset+written, new_objix_hdr_page, 0, written, res2); SPIFFS_CHECK_RES(res2); } else { @@ -1100,7 +1105,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { if (offset == 0) { // wrote to empty object - simply update size and write whole page objix_hdr->size = offset+written; - SPIFFS_DBG("append: %04x store fresh objix_hdr page, %04x:%04x, written %d\n", fd->obj_id + SPIFFS_DBG("append: %04x store fresh objix_hdr page, %04x:%04x, written %i\n", fd->obj_id , cur_objix_pix, cur_objix_spix, written); res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); @@ -1115,7 +1120,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // modifying object index header page, update size and make new copy res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page); - SPIFFS_DBG("append: %04x store modified objix_hdr page, %04x:%04x, written %d\n", fd->obj_id + SPIFFS_DBG("append: %04x store modified objix_hdr page, %04x:%04x, written %i\n", fd->obj_id , new_objix_hdr_page, 0, written); SPIFFS_CHECK_RES(res2); } @@ -1163,7 +1168,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // store previous object index header page res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix); - SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %d\n", new_objix_hdr_pix, 0, written); + SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written); SPIFFS_CHECK_RES(res); } else { // store new version of previous object index page @@ -1173,7 +1178,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { SPIFFS_CHECK_RES(res); res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); - SPIFFS_DBG("modify: store previous modified objix page, %04x:%04x, written %d\n", new_objix_pix, objix->p_hdr.span_ix, written); + SPIFFS_DBG("modify: store previous modified objix page, %04x:%04x, written %i\n", new_objix_pix, objix->p_hdr.span_ix, written); SPIFFS_CHECK_RES(res); spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); } @@ -1228,7 +1233,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // a full page, allocate and write a new page of data res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); - SPIFFS_DBG("modify: store new data page, %04x:%04x offset:%d, len %d, written %d\n", data_pix, data_spix, page_offs, to_write, written); + SPIFFS_DBG("modify: store new data page, %04x:%04x offset:%i, len %i, written %i\n", data_pix, data_spix, page_offs, to_write, written); } else { // write to existing page, allocate new and copy unmodified data @@ -1269,7 +1274,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { (u8_t *)&p_hdr.flags); if (res != SPIFFS_OK) break; - SPIFFS_DBG("modify: store to existing data page, src:%04x, dst:%04x:%04x offset:%d, len %d, written %d\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); + SPIFFS_DBG("modify: store to existing data page, src:%04x, dst:%04x:%04x offset:%i, len %i, written %i\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); } // delete original data page @@ -1308,7 +1313,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { SPIFFS_CHECK_RES(res2); res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); - SPIFFS_DBG("modify: store modified objix page, %04x:%04x, written %d\n", new_objix_pix, cur_objix_spix, written); + SPIFFS_DBG("modify: store modified objix page, %04x:%04x, written %i\n", new_objix_pix, cur_objix_spix, written); fd->cursor_objix_pix = new_objix_pix; fd->cursor_objix_spix = cur_objix_spix; SPIFFS_CHECK_RES(res2); @@ -1318,7 +1323,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // wrote within object index header page res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix); - SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %d\n", new_objix_hdr_pix, 0, written); + SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written); SPIFFS_CHECK_RES(res2); } @@ -1438,7 +1443,7 @@ s32_t spiffs_object_truncate( spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); if (prev_objix_spix > 0) { // update object index header page - SPIFFS_DBG("truncate: update objix hdr page %04x:%04x to size %d\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); + SPIFFS_DBG("truncate: update objix hdr page %04x:%04x to size %i\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix); SPIFFS_CHECK_RES(res); @@ -1481,14 +1486,14 @@ s32_t spiffs_object_truncate( // delete full data page res = spiffs_page_data_check(fs, fd, data_pix, data_spix); if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) { - SPIFFS_DBG("truncate: err validating data pix %d\n", res); + SPIFFS_DBG("truncate: err validating data pix %i\n", res); break; } if (res == SPIFFS_OK) { res = spiffs_page_delete(fs, data_pix); if (res != SPIFFS_OK) { - SPIFFS_DBG("truncate: err deleting data pix %d\n", res); + SPIFFS_DBG("truncate: err deleting data pix %i\n", res); break; } } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) { @@ -1503,13 +1508,13 @@ s32_t spiffs_object_truncate( } fd->size = cur_size; fd->offset = cur_size; - SPIFFS_DBG("truncate: delete data page %04x for data spix:%04x, cur_size:%d\n", data_pix, data_spix, cur_size); + SPIFFS_DBG("truncate: delete data page %04x for data spix:%04x, cur_size:%i\n", data_pix, data_spix, cur_size); } else { // delete last page, partially spiffs_page_header p_hdr; spiffs_page_ix new_data_pix; u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); - SPIFFS_DBG("truncate: delete %d bytes from data page %04x for data spix:%04x, cur_size:%d\n", bytes_to_remove, data_pix, data_spix, cur_size); + SPIFFS_DBG("truncate: delete %i bytes from data page %04x for data spix:%04x, cur_size:%i\n", bytes_to_remove, data_pix, data_spix, cur_size); res = spiffs_page_data_check(fs, fd, data_pix, data_spix); if (res != SPIFFS_OK) break; @@ -1572,7 +1577,7 @@ s32_t spiffs_object_truncate( } else { // make uninitialized object SPIFFS_DBG("truncate: reset objix_hdr page %04x\n", objix_pix); - c_memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, + memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, objix_pix, fs->work, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); @@ -1664,7 +1669,7 @@ s32_t spiffs_object_read( len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); // remaining data in file len_to_read = MIN(len_to_read, fd->size); - SPIFFS_DBG("read: offset:%d rd:%d data spix:%04x is data_pix:%04x addr:%08x\n", cur_offset, len_to_read, data_spix, data_pix, + SPIFFS_DBG("read: offset:%i rd:%i data spix:%04x is data_pix:%04x addr:%08x\n", cur_offset, len_to_read, data_spix, data_pix, SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); if (len_to_read <= 0) { res = SPIFFS_ERR_END_OF_OBJECT; @@ -1750,7 +1755,7 @@ static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id if (id >= state->min_obj_id && id <= state->max_obj_id) { u8_t *map = (u8_t *)fs->work; int ix = (id - state->min_obj_id) / state->compaction; - //SPIFFS_DBG("free_obj_id: add ix %d for id %04x min:%04x max%04x comp:%d\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); + //SPIFFS_DBG("free_obj_id: add ix %i for id %04x min:%04x max%04x comp:%i\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); map[ix]++; } } @@ -1780,7 +1785,7 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co u32_t i, j; SPIFFS_DBG("free_obj_id: BITM min:%04x max:%04x\n", state.min_obj_id, state.max_obj_id); - c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, state.min_obj_id, conflicting_name, 0, 0); if (res == SPIFFS_VIS_END) res = SPIFFS_OK; @@ -1823,7 +1828,7 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co return SPIFFS_ERR_FULL; } - SPIFFS_DBG("free_obj_id: COMP select index:%d min_count:%d min:%04x max:%04x compact:%d\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); + SPIFFS_DBG("free_obj_id: COMP select index:%i min_count:%i min:%04x max:%04x compact:%i\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); if (min_count == 0) { // no id in this range, skip compacting and use directly @@ -1843,9 +1848,9 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co // in a work memory of log_page_size bytes, we may fit in log_page_size ids // todo what if compaction is > 255 - then we cannot fit it in a byte state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); - SPIFFS_DBG("free_obj_id: COMP min:%04x max:%04x compact:%d\n", state.min_obj_id, state.max_obj_id, state.compaction); + SPIFFS_DBG("free_obj_id: COMP min:%04x max:%04x compact:%i\n", state.min_obj_id, state.max_obj_id, state.compaction); - c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, 0, &state, 0, 0); if (res == SPIFFS_VIS_END) res = SPIFFS_OK; SPIFFS_CHECK_RES(res); diff --git a/cores/esp8266/spiffs/spiffs_nucleus.h b/cores/esp8266/spiffs/spiffs_nucleus.h index 5d905fe90..f19b24af8 100644 --- a/cores/esp8266/spiffs/spiffs_nucleus.h +++ b/cores/esp8266/spiffs/spiffs_nucleus.h @@ -124,7 +124,7 @@ #define SPIFFS_EV_IX_NEW 1 #define SPIFFS_EV_IX_DEL 2 -#define SPIFFS_OBJ_ID_IX_FLAG (1<<(8*sizeof(spiffs_obj_id)-1)) +#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1))) #define SPIFFS_UNDEFINED_LEN (u32_t)(-1) @@ -311,6 +311,26 @@ // stop searching at end of all look up pages #define SPIFFS_VIS_NO_WRAP (1<<2) +#if SPIFFS_HAL_CALLBACK_EXTRA + +#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ + (_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src)) +#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ + (_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst)) +#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ + (_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len)) + +#else // SPIFFS_HAL_CALLBACK_EXTRA + +#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ + (_fs)->cfg.hal_write_f((_paddr), (_len), (_src)) +#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ + (_fs)->cfg.hal_read_f((_paddr), (_len), (_dst)) +#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ + (_fs)->cfg.hal_erase_f((_paddr), (_len)) + +#endif // SPIFFS_HAL_CALLBACK_EXTRA + #if SPIFFS_CACHE #define SPIFFS_CACHE_FLAG_DIRTY (1<<0) @@ -423,7 +443,7 @@ typedef struct __attribute(( packed )) // common page header spiffs_page_header p_hdr; // alignment - u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; + u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; // size of object u32_t size; // type of object @@ -435,7 +455,7 @@ typedef struct __attribute(( packed )) // object index page header typedef struct __attribute(( packed )) { spiffs_page_header p_hdr; - u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; + u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; } spiffs_page_object_ix; // callback func for object lookup visitor @@ -665,7 +685,7 @@ s32_t spiffs_gc_clean( spiffs_block_ix bix); s32_t spiffs_gc_quick( - spiffs *fs); + spiffs *fs, u16_t max_free_pages); // --------------- diff --git a/cores/esp8266/spiffs_api.cpp b/cores/esp8266/spiffs_api.cpp index d63a1bcd4..c482c8438 100644 --- a/cores/esp8266/spiffs_api.cpp +++ b/cores/esp8266/spiffs_api.cpp @@ -32,6 +32,8 @@ extern "C" { #include "spi_flash.h" } +using namespace fs; + extern int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src); extern int32_t spiffs_hal_erase(uint32_t addr, uint32_t size); extern int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst); @@ -237,7 +239,7 @@ public: size_t position() const override { CHECKFD(); - auto result = SPIFFS_tell(_fs->getFs(), _fd); + auto result = SPIFFS_lseek(_fs->getFs(), _fd, 0, SPIFFS_SEEK_CUR); if (result < 0) { DEBUGV("SPIFFS_tell rc=%d\r\n", result); return 0; @@ -259,6 +261,12 @@ public: DEBUGV("SPIFFS_close: fd=%d\r\n", _fd); } + const char* name() const override { + CHECKFD(); + + return (const char*) _stat.name; + } + protected: SPIFFSImpl* _fs; spiffs_file _fd; @@ -283,10 +291,11 @@ public: return FileImplPtr(); } int mode = getSpiffsMode(openMode, accessMode); - spiffs_file fd = SPIFFS_open_by_dirent(_fs->getFs(), &_dirent, mode, 0); + auto fs = _fs->getFs(); + spiffs_file fd = SPIFFS_open_by_dirent(fs, &_dirent, mode, 0); if (fd < 0) { DEBUGV("SPIFFSDirImpl::openFile: fd=%d path=`%s` openMode=%d accessMode=%d err=%d\r\n", - fd, _dirent.name, openMode, accessMode, _fs.err_code); + fd, _dirent.name, openMode, accessMode, fs->err_code); return FileImplPtr(); } return std::make_shared(_fs, fd); @@ -299,6 +308,13 @@ public: return (const char*) _dirent.name; } + size_t fileSize() override { + if (!_valid) + return 0; + + return _dirent.size; + } + bool next() override { spiffs_dirent* result = SPIFFS_readdir(&_dir, &_dirent); _valid = (result != nullptr); diff --git a/doc/boards.md b/doc/boards.md index 99bb7cb8c..d8eb03f78 100644 --- a/doc/boards.md +++ b/doc/boards.md @@ -5,6 +5,7 @@ title: Supported Hardware - [Adafruit HUZZAH ESP8266 (ESP-12)](#adafruit-huzzah-esp8266-esp-12) - [NodeMCU 0.9](#nodemcu-0-9) - [NodeMCU 1.0](#nodemcu-1-0) +- [Olimex MOD-WIFI-ESP8266-DEV](#olimex-mod-wifi-esp8266-dev) - [Olimex MOD-WIFI-ESP8266](#olimex-mod-wifi-esp8266) - [SparkFun ESP8266 Thing](#sparkfun-esp8266-thing) - [SweetPea ESP-210](#sweetpea-esp-210) @@ -38,11 +39,33 @@ If you want to use NodeMCU pin 5, use D5 for pin number, and it will be translat ### NodeMCU 1.0 -*TODO: add notes* +This module is sold under many names for around $6.50 on AliExpress and it's one of the cheapest, fully integrated ESP8266 solutions. + +It's an open hardware design with an ESP-12E core and 4 MB of SPI flash. + +Acording to the manufacturer, "with a micro USB cable, you can connect NodeMCU devkit to your laptop and flash it without any trouble". This is more or less true: the board comes with a CP2102 onboard USB to serial adapter which just works, well, the majority of the time. Sometimes flashing fails and you have to reset the board by holding down FLASH + RST, then releasing FLASH, then releasing RST. This forces the CP2102 device to power cycle and to be re-numbered by Linux. + +The board also features a NCP1117 voltage regulator, a blue LED on GPIO16 and a 220k/100k Ohm voltage divider on the ADC input pin. + +Full pinout and PDF schematics can be found [here](https://github.com/nodemcu/nodemcu-devkit-v1.0) + +### Olimex MOD-WIFI-ESP8266-DEV + +This board comes with 2 MB of SPI flash and optional accessories (e.g. evaluation board ESP8266-EVB or BAT-BOX for batteries). + +The basic module has three solder jumpers that allow you to switch the operating mode between SDIO, UART and FLASH. + +The board is shipped for FLASH operation mode, with jumpers TD0JP=0, IO0JP=1, IO2JP=1. + +Since jumper IO0JP is tied to GPIO0, which is PIN 21, you'll have to ground it before programming with a USB to serial adapter and reset the board by power cycling it. + +UART pins for programming and serial I/O are GPIO1 (TXD, pin 3) and GPIO3 (RXD, pin 4). + +Get the board schematics [here](https://github.com/OLIMEX/ESP8266/blob/master/HARDWARE/MOD-WIFI-ESP8266-DEV/MOD-WIFI-ESP8266-DEV_schematic.pdf) ### Olimex MOD-WIFI-ESP8266 -*TODO: add notes* +This is a stripped down version of the above. Behaves identically in terms of jumpers but has less pins readily available for I/O. Still 2 MB of SPI flash. ### SparkFun ESP8266 Thing ### diff --git a/doc/changes.md b/doc/changes.md index f9731095e..bed3d0da6 100644 --- a/doc/changes.md +++ b/doc/changes.md @@ -6,18 +6,30 @@ title: Change log ### Core -- Add first batch of file system APIs +- Add file system APIs and documentation +- Add ConfigFile example - Allow user to run code in user_rf_pre_init +- Add strtoul and strtol, fix strtod +- Update documentation for NodeMCU and Olimex boards +- Disable interrupts inside ESP.getVcc (#567) +- Erase RTC RAM only if RF mode looks invalid (#619) ### Libraries - ESP8266WebServer: support for sending of PROGMEM strings +- ESP8266WebServer: support for serving files from file system - ESP8266WiFi: fix mode selection (#529) +- ESP8266mDNS: allow to work on SoftAP interface +- EEPROM: round requested size to 4 bytes (#659) +- Add ESP8266AVRISP library ### Tools -- Update SDK to 1.2.0_15_07_13_p4 +- Update SDK to 1.3.0_15_08_10_p1 +- Update esptool to 0.4.6 +- Bump toolchain version to force libm update on Windows +- ESP8266FS tool update --- ## 1.6.5-947-g39819f0 diff --git a/doc/esp12.png b/doc/esp12.png new file mode 100644 index 000000000..8ec371016 Binary files /dev/null and b/doc/esp12.png differ diff --git a/doc/reference.md b/doc/reference.md index fd556756a..fd57287dc 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -11,7 +11,7 @@ Pin 16 can be `INPUT`, `OUTPUT` or `INPUT_PULLDOWN_16`. At startup, pins are con Pins may also serve other functions, like Serial, I2C, SPI. These functions are normally activated by the corresponding library. The diagram below shows pin mapping for the popular ESP-12 module. -![Pin Functions](https://cdn.rawgit.com/esp8266/Arduino/doc-update/hardware/esp8266com/esp8266/doc/esp12.svg) +![Pin Functions](esp12.png) Digital pins 6—11 are not shown on this diagram because they are used to connect flash memory chip on most modules. Trying to use these pins as IOs will likely cause the program to crash. @@ -103,17 +103,184 @@ const char HTTP[] PROGMEM = "http:"; } ``` +## File system + +Even though file system is stored on the same flash chip as the program, programming new sketch will not modify file system contents. This allows to use file system to store sketch data, configuration files, or content for Web server. + +The following diagram illustrates flash layout used in Arduino environment: + + |--------------|-------|---------------|--|--|--|--|--| + ^ ^ ^ ^ ^ + Sketch OTA update File system EEPROM WiFi config (SDK) + +File system size depends on the flash chip size. Depending on the board which is selected in IDE, you have the following options for flash size: + +Board | Flash chip size, bytes | File system size, bytes +------|-----------------|----------------- +Generic module | 512k | 64k +Generic module | 1M | 64k, 128k, 256k, 512k +Generic module | 2M | 1M +Generic module | 4M | 3M +Adafruit HUZZAH | 4M | 3M +NodeMCU 0.9 | 4M | 3M +NodeMCU 1.0 | 4M | 3M +Olimex MOD-WIFI-ESP8266(-DEV)| 2M | 1M +SparkFun Thing | 512k | 64k +SweetPea ESP-210 | 4M | 3M + +**Note:** to use any of file system functions in the sketch, add the following include to the sketch: + +```c++ +#include "FS.h" +``` + +### File system object (SPIFFS) + +#### begin + +```c++ +SPIFFS.begin() +``` + +This method mounts SPIFFS file system. It must be called before any other +FS APIs are used. Returns *true* if file system was mounted successfully, false +otherwise. + +#### open + +```c++ +SPIFFS.open(path, mode) +``` + +Opens a file. `path` should be an absolute path starting with a slash +(e.g. `/dir/filename.txt`). `mode` is a string specifying access mode. It can be +one of "r", "w", "a", "r+", "w+", "a+". Meaning of these modes is the same as +for `fopen` C function. + +Returns *File* object. To check whether the file was opened successfully, use +the boolean operator. + +```c++ +File f = SPIFFS.open("/f.txt", "w"); +if (!f) { + Serial.println("file open failed"); +} +``` + +#### openDir + +```c++ +SPIFFS.openDir(path) +``` + +Opens a directory given its absolute path. Returns a *Dir* object. To check if +directory was opened successfully, use the boolean operator, similar to opening +a file. + +#### remove + +```c++ +SPIFFS.remove(path) +``` + +Deletes the file given its absolute path. Returns *true* if file was deleted successfully. + +#### rename + +```c++ +SPIFFS.rename(pathFrom, pathTo) +``` + +Renames file from `pathFrom` to `pathTo`. Paths must be absolute. Returns *true* +if file was renamed successfully. + +### Directory object (Dir) + +The purpose of *Dir* object is to iterate over files inside a directory. +It provides three methods: `next()`, `fileName()`, and `openFile(mode)`. + +The following example shows how it should be used: + +```c++ +Dir dir = SPIFFS.openDir("/data"); +while (dir.next()) { + Serial.print(dir.fileName()); + File f = dir.openFile("r"); + Serial.println(f.size()); +} +``` + +`dir.next()` returns true while there are files in the directory to iterate over. +It must be called before calling `fileName` and `openFile` functions. + +`openFile` method takes *mode* argument which has the same meaning as for `SPIFFS.open` function. + +### File object + +`SPIFFS.open` and `dir.openFile` functions return a *File* object. This object +supports all the functions of *Stream*, so you can use `readBytes`, `findUntil`, +`parseInt`, `println`, and all other *Stream* methods. + +There are also some functions which are specific to *File* object. + +#### seek + +```c++ +file.seek(offset, mode) +``` + +This function behaves like `fseek` C function. Depending on the value of `mode`, +it moves current position in a file as follows: + +- if `mode` is `SeekSet`, position is set to `offset` bytes from the beginning. +- if `mode` is `SeekCur`, current position is moved by `offset` bytes. +- if `mode` is `SeekEnd`, position is set to `offset` bytes from the end of the +file. + +Returns *true* if position was set successfully. + +#### position + +```c++ +file.position() +``` + +Returns the current position inside the file, in bytes. + +#### size + +```c++ +file.size() +``` + +Returns file size, in bytes. + + +#### name + +```c++ +String name = file.name(); +``` + +Returns file name, as `const char*`. Convert it to *String* for storage. + +#### close + +```c++ +file.close() +``` + +Close the file. No other operations should be performed on *File* object after `close` function was called. ## WiFi(ESP8266WiFi library) This is mostly similar to WiFi shield library. Differences include: -- `WiFi.mode(m)`: set mode to `WIFI_AP`, `WIFI_STA`, or `WIFI_AP_STA`. +- `WiFi.mode(m)`: set mode to `WIFI_AP`, `WIFI_STA`, `WIFI_AP_STA` or `WIFI_OFF`. - call `WiFi.softAP(ssid)` to set up an open network - call `WiFi.softAP(ssid, password)` to set up a WPA2-PSK network (password should be at least 8 characters) - `WiFi.macAddress(mac)` is for STA, `WiFi.softAPmacAddress(mac)` is for AP. - `WiFi.localIP()` is for STA, `WiFi.softAPIP()` is for AP. -- `WiFi.RSSI()` doesn't work - `WiFi.printDiag(Serial)` will print out some diagnostic info - `WiFiUDP` class supports sending and receiving multicast packets on STA interface. When sending a multicast packet, replace `udp.beginPacket(addr, port)` with @@ -122,7 +289,6 @@ When listening to multicast packets, replace `udp.begin(port)` with `udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port)`. You can use `udp.destinationIP()` to tell whether the packet received was sent to the multicast or unicast address. -Also note that multicast doesn't work on softAP interface. `WiFiServer`, `WiFiClient`, and `WiFiUDP` behave mostly the same way as with WiFi shield library. Four samples are provided for this library. @@ -205,7 +371,6 @@ instead of the one that comes with this package. ## mDNS and DNS-SD responder (ESP8266mDNS library) Allows the sketch to respond to multicast DNS queries for domain names like "foo.local", and DNS-SD (service dicovery) queries. -Currently the library only works on STA interface, AP interface is not supported. See attached example for details. ## SSDP responder (ESP8266SSDP) diff --git a/libraries/EEPROM/EEPROM.cpp b/libraries/EEPROM/EEPROM.cpp index dfd968a4a..41c484306 100644 --- a/libraries/EEPROM/EEPROM.cpp +++ b/libraries/EEPROM/EEPROM.cpp @@ -1,9 +1,9 @@ -/* +/* EEPROM.cpp - esp8266 EEPROM emulation Copyright (c) 2014 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 @@ -34,7 +34,7 @@ EEPROMClass::EEPROMClass(uint32_t sector) : _sector(sector) , _data(0) , _size(0) -, _dirty(false) +, _dirty(false) { } @@ -44,6 +44,8 @@ void EEPROMClass::begin(size_t size) { if (size > SPI_FLASH_SEC_SIZE) size = SPI_FLASH_SEC_SIZE; + size = (size + 3) & (~3); + if (_data) { delete[] _data; } diff --git a/libraries/ESP8266AVRISP/README.md b/libraries/ESP8266AVRISP/README.md new file mode 100644 index 000000000..be691aaa8 --- /dev/null +++ b/libraries/ESP8266AVRISP/README.md @@ -0,0 +1,57 @@ +# AVR In-System Programming over WiFi for ESP8266 + +This library allows an ESP8266 module with the HSPI port available to become +an AVR In-System Programmer. + +## Hardware + +The ESP8266 module connects to the AVR target chip via the standard 6-pin +AVR "Recommended In-System Programming Interface Connector Layout" as seen +in [AVR910](http://www.atmel.com/images/doc0943.pdf) among other places. + +If the AVR target is powered by a different Vcc than what powers your ESP8266 +chip, you **must provide voltage level shifting** or some other form of buffers. +Exposing the pins of ESP8266 to anything larger than 3.6V will damage it. + +Connections are as follows: + +ESP8266 | AVR / SPI +--------|------------ +GPIO12 | MISO +GPIO13 | MOSI +GPIO14 | SCK +any* | RESET + +For RESET use a GPIO other than 0, 2 and 15 (bootselect pins), and apply an +external pullup/down so that the target is normally running. + +## Usage + +See the included example. In short: + +```arduino + +// Create the programmer object +ESP8266AVRISP avrprog(PORT, RESET_PIN) +// ... with custom SPI frequency +ESP8266AVRISP avrprog(PORT, RESET_PIN, 4e6) + +// Check current connection state, but don't perform any actions +AVRISPState_t state = avrprog.update(); + +// Serve the pending connection, execute STK500 commands +AVRISPState_t state = avrprog.serve(); +``` + +### License and Authors + +This library started off from the source of ArduinoISP "sketch" included with +the Arduino IDE: + + ArduinoISP version 04m3 + Copyright (c) 2008-2011 Randall Bohn + If you require a license, see + http://www.opensource.org/licenses/bsd-license.php + + Support for TCP on ESP8266 + Copyright (c) Kiril Zyapkov . diff --git a/libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino b/libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino new file mode 100644 index 000000000..792d702c9 --- /dev/null +++ b/libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +const char* host = "esp8266-avrisp"; +const char* ssid = "**********"; +const char* pass = "**********"; +const uint16_t port = 328; +const uint8_t reset_pin = 5; + +ESP8266AVRISP avrprog(port, reset_pin); + +void setup() { + Serial.begin(115200); + Serial.println(""); + Serial.println("Arduino AVR-ISP over TCP"); + avrprog.setReset(false); // let the AVR run + + WiFi.begin(ssid, pass); + while (WiFi.waitForConnectResult() != WL_CONNECTED); + + MDNS.begin(host); + MDNS.addService("avrisp", "tcp", port); + + IPAddress local_ip = WiFi.localIP(); + Serial.print("IP address: "); + Serial.println(local_ip); + Serial.println("Use your avrdude:"); + Serial.print("avrdude -c arduino -p -P net:"); + Serial.print(local_ip); + Serial.print(":"); + Serial.print(port); + Serial.println(" -t # or -U ..."); + + // listen for avrdudes + avrprog.begin(); +} + +void loop() { + static AVRISPState_t last_state = AVRISP_STATE_IDLE; + AVRISPState_t new_state = avrprog.update(); + if (last_state != new_state) { + switch (new_state) { + case AVRISP_STATE_IDLE: { + Serial.printf("[AVRISP] now idle\r\n"); + // Use the SPI bus for other purposes + break; + } + case AVRISP_STATE_PENDING: { + Serial.printf("[AVRISP] connection pending\r\n"); + // Clean up your other purposes and prepare for programming mode + break; + } + case AVRISP_STATE_ACTIVE: { + Serial.printf("[AVRISP] programming mode\r\n"); + // Stand by for completion + break; + } + } + last_state = new_state; + } + // Serve the client + if (last_state != AVRISP_STATE_IDLE) { + avrprog.serve(); + } +} diff --git a/libraries/ESP8266AVRISP/library.properties b/libraries/ESP8266AVRISP/library.properties new file mode 100644 index 000000000..70fa3cf8e --- /dev/null +++ b/libraries/ESP8266AVRISP/library.properties @@ -0,0 +1,9 @@ +name=ESP8266AVRISP +version=1.0 +author=Kiril Zyapkov +maintainer=Kiril Zyapkov +sentence=AVR In-System Programming over WiFi for ESP8266 +paragraph=This library allows programming 8-bit AVR ICSP targets via TCP over WiFi with ESP8266. +category=Communication +url= +architectures=esp8266 diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp new file mode 100644 index 000000000..1cdeb26e8 --- /dev/null +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp @@ -0,0 +1,541 @@ +/* +AVR In-System Programming over WiFi for ESP8266 +Copyright (c) Kiril Zyapkov + +Original version: + ArduinoISP version 04m3 + Copyright (c) 2008-2011 Randall Bohn + If you require a license, see + http://www.opensource.org/licenses/bsd-license.php +*/ + + +#include +#include +#include +#include + +#include "ESP8266AVRISP.h" +#include "command.h" + +extern "C" { + #include "user_interface.h" + #include "mem.h" +} + +#define malloc os_malloc +#define free os_free + +#ifdef AVRISP_ACTIVE_HIGH_RESET +#define AVRISP_RESET_ON HIGH +#define AVRISP_RESET_OFF LOW +#else +#define AVRISP_RESET_ON LOW +#define AVRISP_RESET_OFF HIGH +#endif + + +// #define AVRISP_DEBUG(fmt, ...) os_printf("[AVRP] " fmt "\r\n", ##__VA_ARGS__ ) +#define AVRISP_DEBUG(...) + +#define AVRISP_HWVER 2 +#define AVRISP_SWMAJ 1 +#define AVRISP_SWMIN 18 +#define AVRISP_PTIME 10 + +#define EECHUNK (32) + +#define beget16(addr) (*addr * 256 + *(addr+1)) + +ESP8266AVRISP::ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq, bool reset_state): + _reset_pin(reset_pin), _reset_state(reset_state), _spi_freq(spi_freq), + _server(WiFiServer(port)), _state(AVRISP_STATE_IDLE) +{ + pinMode(_reset_pin, OUTPUT); + setReset(_reset_state); +} + +void ESP8266AVRISP::begin() { + _server.begin(); +} + +void ESP8266AVRISP::setSpiFrequency(uint32_t freq) { + _spi_freq = freq; + if (_state == AVRISP_STATE_ACTIVE) { + SPI.setFrequency(freq); + } +} + +void ESP8266AVRISP::setReset(bool rst) { + _reset_state = rst; + if (_reset_state) { + digitalWrite(_reset_pin, AVRISP_RESET_ON); + } else { + digitalWrite(_reset_pin, AVRISP_RESET_OFF); + } +} + +AVRISPState_t ESP8266AVRISP::update() { + switch (_state) { + case AVRISP_STATE_IDLE: { + if (_server.hasClient()) { + _client = _server.available(); + _client.setNoDelay(true); + ip_addr_t lip; + lip.addr = _client.remoteIP(); + AVRISP_DEBUG("client connect %d.%d.%d.%d:%d", IP2STR(&lip), _client.remotePort()); + _client.setTimeout(100); // for getch() + _state = AVRISP_STATE_PENDING; + _reject_incoming(); + } + break; + } + case AVRISP_STATE_PENDING: + case AVRISP_STATE_ACTIVE: { + // handle disconnect + if (!_client.connected()) { + _client.stop(); + AVRISP_DEBUG("client disconnect"); + if (pmode) { + SPI.end(); + pmode = 0; + } + setReset(_reset_state); + _state = AVRISP_STATE_IDLE; + } else { + _reject_incoming(); + } + break; + } + } + return _state; +} + +AVRISPState_t ESP8266AVRISP::serve() { + switch (update()) { + case AVRISP_STATE_IDLE: + // should not be called when idle, error? + break; + case AVRISP_STATE_PENDING: { + _state = AVRISP_STATE_ACTIVE; + // fallthrough + } + case AVRISP_STATE_ACTIVE: { + while (_client.available()) { + avrisp(); + } + return update(); + } + } + return _state; +} + +inline void ESP8266AVRISP::_reject_incoming(void) { + while (_server.hasClient()) _server.available().stop(); +} + +uint8_t ESP8266AVRISP::getch() { + while (!_client.available()) yield(); + uint8_t b = (uint8_t)_client.read(); + // AVRISP_DEBUG("< %02x", b); + return b; +} + +void ESP8266AVRISP::fill(int n) { + // AVRISP_DEBUG("fill(%u)", n); + for (int x = 0; x < n; x++) { + buff[x] = getch(); + } +} + +uint8_t ESP8266AVRISP::spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { + uint8_t n; + SPI.transfer(a); + n = SPI.transfer(b); + n = SPI.transfer(c); + return SPI.transfer(d); +} + +void ESP8266AVRISP::empty_reply() { + if (Sync_CRC_EOP == getch()) { + _client.print((char)Resp_STK_INSYNC); + _client.print((char)Resp_STK_OK); + } else { + error++; + _client.print((char)Resp_STK_NOSYNC); + } +} + +void ESP8266AVRISP::breply(uint8_t b) { + if (Sync_CRC_EOP == getch()) { + uint8_t resp[3]; + resp[0] = Resp_STK_INSYNC; + resp[1] = b; + resp[2] = Resp_STK_OK; + _client.write((const uint8_t *)resp, (size_t)3); + } else { + error++; + _client.print((char)Resp_STK_NOSYNC); + } + +} + +void ESP8266AVRISP::get_parameter(uint8_t c) { + switch (c) { + case 0x80: + breply(AVRISP_HWVER); + break; + case 0x81: + breply(AVRISP_SWMAJ); + break; + case 0x82: + breply(AVRISP_SWMIN); + break; + case 0x93: + breply('S'); // serial programmer + break; + default: + breply(0); + } +} + +void ESP8266AVRISP::set_parameters() { + // call this after reading paramter packet into buff[] + param.devicecode = buff[0]; + param.revision = buff[1]; + param.progtype = buff[2]; + param.parmode = buff[3]; + param.polling = buff[4]; + param.selftimed = buff[5]; + param.lockbytes = buff[6]; + param.fusebytes = buff[7]; + param.flashpoll = buff[8]; + // ignore buff[9] (= buff[8]) + // following are 16 bits (big endian) + param.eeprompoll = beget16(&buff[10]); + param.pagesize = beget16(&buff[12]); + param.eepromsize = beget16(&buff[14]); + + // 32 bits flashsize (big endian) + param.flashsize = buff[16] * 0x01000000 + + buff[17] * 0x00010000 + + buff[18] * 0x00000100 + + buff[19]; +} + +void ESP8266AVRISP::start_pmode() { + SPI.begin(); + SPI.setFrequency(_spi_freq); + SPI.setHwCs(false); + + // try to sync the bus + SPI.transfer(0x00); + digitalWrite(_reset_pin, AVRISP_RESET_OFF); + delayMicroseconds(50); + digitalWrite(_reset_pin, AVRISP_RESET_ON); + delay(30); + + spi_transaction(0xAC, 0x53, 0x00, 0x00); + pmode = 1; +} + +void ESP8266AVRISP::end_pmode() { + SPI.end(); + setReset(_reset_state); + pmode = 0; +} + +void ESP8266AVRISP::universal() { + int w; + uint8_t ch; + + fill(4); + ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]); + breply(ch); +} + +void ESP8266AVRISP::flash(uint8_t hilo, int addr, uint8_t data) { + spi_transaction(0x40 + 8 * hilo, + addr >> 8 & 0xFF, + addr & 0xFF, + data); +} + +void ESP8266AVRISP::commit(int addr) { + spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0); + delay(AVRISP_PTIME); +} + +//#define _addr_page(x) (here & 0xFFFFE0) +int ESP8266AVRISP::addr_page(int addr) { + if (param.pagesize == 32) return addr & 0xFFFFFFF0; + if (param.pagesize == 64) return addr & 0xFFFFFFE0; + if (param.pagesize == 128) return addr & 0xFFFFFFC0; + if (param.pagesize == 256) return addr & 0xFFFFFF80; + AVRISP_DEBUG("unknown page size: %d", param.pagesize); + return addr; +} + + +void ESP8266AVRISP::write_flash(int length) { + uint32_t started = millis(); + + fill(length); + + if (Sync_CRC_EOP == getch()) { + _client.print((char) Resp_STK_INSYNC); + _client.print((char) write_flash_pages(length)); + } else { + error++; + _client.print((char) Resp_STK_NOSYNC); + } +} + +uint8_t ESP8266AVRISP::write_flash_pages(int length) { + int x = 0; + int page = addr_page(here); + while (x < length) { + yield(); + if (page != addr_page(here)) { + commit(page); + page = addr_page(here); + } + flash(LOW, here, buff[x++]); + flash(HIGH, here, buff[x++]); + here++; + } + commit(page); + return Resp_STK_OK; +} + +uint8_t ESP8266AVRISP::write_eeprom(int length) { + // here is a word address, get the byte address + int start = here * 2; + int remaining = length; + if (length > param.eepromsize) { + error++; + return Resp_STK_FAILED; + } + while (remaining > EECHUNK) { + write_eeprom_chunk(start, EECHUNK); + start += EECHUNK; + remaining -= EECHUNK; + } + write_eeprom_chunk(start, remaining); + return Resp_STK_OK; +} +// write (length) bytes, (start) is a byte address +uint8_t ESP8266AVRISP::write_eeprom_chunk(int start, int length) { + // this writes byte-by-byte, + // page writing may be faster (4 bytes at a time) + fill(length); + // prog_lamp(LOW); + for (int x = 0; x < length; x++) { + int addr = start + x; + spi_transaction(0xC0, (addr >> 8) & 0xFF, addr & 0xFF, buff[x]); + delay(45); + } + // prog_lamp(HIGH); + return Resp_STK_OK; +} + +void ESP8266AVRISP::program_page() { + char result = (char) Resp_STK_FAILED; + int length = 256 * getch(); + length += getch(); + char memtype = getch(); + char buf[100]; + // flash memory @here, (length) bytes + if (memtype == 'F') { + write_flash(length); + return; + } + + if (memtype == 'E') { + result = (char)write_eeprom(length); + if (Sync_CRC_EOP == getch()) { + _client.print((char) Resp_STK_INSYNC); + _client.print(result); + } else { + error++; + _client.print((char) Resp_STK_NOSYNC); + } + return; + } + _client.print((char)Resp_STK_FAILED); + return; + +} + +uint8_t ESP8266AVRISP::flash_read(uint8_t hilo, int addr) { + return spi_transaction(0x20 + hilo * 8, + (addr >> 8) & 0xFF, + addr & 0xFF, + 0); +} + +void ESP8266AVRISP::flash_read_page(int length) { + uint8_t *data = (uint8_t *) malloc(length + 1); + for (int x = 0; x < length; x += 2) { + *(data + x) = flash_read(LOW, here); + *(data + x + 1) = flash_read(HIGH, here); + here++; + } + *(data + length) = Resp_STK_OK; + _client.write((const uint8_t *)data, (size_t)(length + 1)); + free(data); + return; +} + +void ESP8266AVRISP::eeprom_read_page(int length) { + // here again we have a word address + uint8_t *data = (uint8_t *) malloc(length + 1); + int start = here * 2; + for (int x = 0; x < length; x++) { + int addr = start + x; + uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF); + *(data + x) = ee; + } + *(data + length) = Resp_STK_OK; + _client.write((const uint8_t *)data, (size_t)(length + 1)); + free(data); + return; +} + +void ESP8266AVRISP::read_page() { + char result = (char)Resp_STK_FAILED; + int length = 256 * getch(); + length += getch(); + char memtype = getch(); + if (Sync_CRC_EOP != getch()) { + error++; + _client.print((char) Resp_STK_NOSYNC); + return; + } + _client.print((char) Resp_STK_INSYNC); + if (memtype == 'F') flash_read_page(length); + if (memtype == 'E') eeprom_read_page(length); + return; +} + +void ESP8266AVRISP::read_signature() { + if (Sync_CRC_EOP != getch()) { + error++; + _client.print((char) Resp_STK_NOSYNC); + return; + } + _client.print((char) Resp_STK_INSYNC); + + uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); + _client.print((char) high); + uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00); + _client.print((char) middle); + uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00); + _client.print((char) low); + _client.print((char) Resp_STK_OK); +} + +// It seems ArduinoISP is based on the original STK500 (not v2) +// but implements only a subset of the commands. +int ESP8266AVRISP::avrisp() { + uint8_t data, low, high; + uint8_t ch = getch(); + // AVRISP_DEBUG("CMD 0x%02x", ch); + switch (ch) { + case Cmnd_STK_GET_SYNC: + error = 0; + empty_reply(); + break; + + case Cmnd_STK_GET_SIGN_ON: + if (getch() == Sync_CRC_EOP) { + _client.print((char) Resp_STK_INSYNC); + _client.print(F("AVR ISP")); // AVR061 says "AVR STK"? + _client.print((char) Resp_STK_OK); + } + break; + + case Cmnd_STK_GET_PARAMETER: + get_parameter(getch()); + break; + + case Cmnd_STK_SET_DEVICE: + fill(20); + set_parameters(); + empty_reply(); + break; + + case Cmnd_STK_SET_DEVICE_EXT: // ignored + fill(5); + empty_reply(); + break; + + case Cmnd_STK_ENTER_PROGMODE: + start_pmode(); + empty_reply(); + break; + + case Cmnd_STK_LOAD_ADDRESS: + here = getch(); + here += 256 * getch(); + // AVRISP_DEBUG("here=0x%04x", here); + empty_reply(); + break; + + // XXX: not implemented! + case Cmnd_STK_PROG_FLASH: + low = getch(); + high = getch(); + empty_reply(); + break; + + // XXX: not implemented! + case Cmnd_STK_PROG_DATA: + data = getch(); + empty_reply(); + break; + + case Cmnd_STK_PROG_PAGE: + program_page(); + break; + + case Cmnd_STK_READ_PAGE: + read_page(); + break; + + case Cmnd_STK_UNIVERSAL: + universal(); + break; + + case Cmnd_STK_LEAVE_PROGMODE: + error = 0; + end_pmode(); + empty_reply(); + delay(5); + // if (_client && _client.connected()) + _client.stop(); + // AVRISP_DEBUG("left progmode"); + + break; + + case Cmnd_STK_READ_SIGN: + read_signature(); + break; + // expecting a command, not Sync_CRC_EOP + // this is how we can get back in sync + case Sync_CRC_EOP: // 0x20, space + error++; + _client.print((char) Resp_STK_NOSYNC); + break; + + // anything else we will return STK_UNKNOWN + default: + AVRISP_DEBUG("??!?"); + error++; + if (Sync_CRC_EOP == getch()) { + _client.print((char)Resp_STK_UNKNOWN); + } else { + _client.print((char)Resp_STK_NOSYNC); + } + } +} diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h new file mode 100644 index 000000000..d2bc6f1bc --- /dev/null +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h @@ -0,0 +1,125 @@ +/* +AVR In-System Programming over WiFi for ESP8266 +Copyright (c) Kiril Zyapkov + +Original version: + ArduinoISP version 04m3 + Copyright (c) 2008-2011 Randall Bohn + If you require a license, see + http://www.opensource.org/licenses/bsd-license.php +*/ + +#ifndef _ESP8266AVRISP_H +#define _ESP8266AVRISP_H + +#include + +// uncomment if you use an n-mos to level-shift the reset line +// #define AVRISP_ACTIVE_HIGH_RESET + +// SPI clock frequency in Hz +#define AVRISP_SPI_FREQ 300e3 + +// programmer states +typedef enum { + AVRISP_STATE_IDLE = 0, // no active TCP session + AVRISP_STATE_PENDING, // TCP connected, pending SPI activation + AVRISP_STATE_ACTIVE // programmer is active and owns the SPI bus +} AVRISPState_t; + +// stk500 parameters +typedef struct { + uint8_t devicecode; + uint8_t revision; + uint8_t progtype; + uint8_t parmode; + uint8_t polling; + uint8_t selftimed; + uint8_t lockbytes; + uint8_t fusebytes; + int flashpoll; + int eeprompoll; + int pagesize; + int eepromsize; + int flashsize; +} AVRISP_parameter_t; + + +class ESP8266AVRISP { +public: + ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq=AVRISP_SPI_FREQ, bool reset_state=false); + + void begin(); + + // set the SPI clock frequency + void setSpiFrequency(uint32_t); + + // control the state of the RESET pin of the target + // see AVRISP_ACTIVE_HIGH_RESET + void setReset(bool); + + // check for pending clients if IDLE, check for disconnect otherwise + // returns the updated state + AVRISPState_t update(); + + // transition to ACTIVE if PENDING + // serve STK500 commands from buffer if ACTIVE + // returns the updated state + AVRISPState_t serve(); + +protected: + + inline void _reject_incoming(void); // reject any incoming tcp connections + + int avrisp(void); // handle incoming STK500 commands + + uint8_t getch(void); // retrieve a character from the remote end + uint8_t spi_transaction(uint8_t, uint8_t, uint8_t, uint8_t); + void empty_reply(void); + void breply(uint8_t); + + void get_parameter(uint8_t); + void set_parameters(void); + int addr_page(int); + void flash(uint8_t, int, uint8_t); + void write_flash(int); + uint8_t write_flash_pages(int length); + uint8_t write_eeprom(int length); + uint8_t write_eeprom_chunk(int start, int length); + void commit(int addr); + void program_page(); + uint8_t flash_read(uint8_t hilo, int addr); + void flash_read_page(int length); + void eeprom_read_page(int length); + void read_page(); + void read_signature(); + + void universal(void); + + void fill(int); // fill the buffer with n bytes + void start_pmode(void); // enter program mode + void end_pmode(void); // exit program mode + + + + uint32_t _spi_freq; + WiFiServer _server; + WiFiClient _client; + AVRISPState_t _state; + uint8_t _reset_pin; + bool _reset_state; + + // programmer settings, set by remote end + AVRISP_parameter_t param; + // page buffer + uint8_t buff[256]; + + int error = 0; + bool pmode = 0; + + // address for reading and writing, set by 'U' command + int here; +}; + + +#endif // _ESP8266AVRISP_H diff --git a/libraries/ESP8266AVRISP/src/command.h b/libraries/ESP8266AVRISP/src/command.h new file mode 100644 index 000000000..2adc22bd3 --- /dev/null +++ b/libraries/ESP8266AVRISP/src/command.h @@ -0,0 +1,108 @@ +//**** ATMEL AVR - A P P L I C A T I O N N O T E ************************ +//* +//* Title: AVR061 - STK500 Communication Protocol +//* Filename: command.h +//* Version: 1.0 +//* Last updated: 09.09.2002 +//* +//* Support E-mail: avr@atmel.com +//* +//************************************************************************** + +// *****************[ STK Message constants ]*************************** + +#define STK_SIGN_ON_MESSAGE "AVR STK" // Sign on string for Cmnd_STK_GET_SIGN_ON + +// *****************[ STK Response constants ]*************************** + +#define Resp_STK_OK 0x10 // ' ' +#define Resp_STK_FAILED 0x11 // ' ' +#define Resp_STK_UNKNOWN 0x12 // ' ' +#define Resp_STK_NODEVICE 0x13 // ' ' +#define Resp_STK_INSYNC 0x14 // ' ' +#define Resp_STK_NOSYNC 0x15 // ' ' + +#define Resp_ADC_CHANNEL_ERROR 0x16 // ' ' +#define Resp_ADC_MEASURE_OK 0x17 // ' ' +#define Resp_PWM_CHANNEL_ERROR 0x18 // ' ' +#define Resp_PWM_ADJUST_OK 0x19 // ' ' + +// *****************[ STK Special constants ]*************************** + +#define Sync_CRC_EOP 0x20 // 'SPACE' + +// *****************[ STK Command constants ]*************************** + +#define Cmnd_STK_GET_SYNC 0x30 // ' ' +#define Cmnd_STK_GET_SIGN_ON 0x31 // ' ' +#define Cmnd_STK_RESET 0x32 // ' ' +#define Cmnd_STK_SINGLE_CLOCK 0x33 // ' ' +#define Cmnd_STK_STORE_PARAMETERS 0x34 // ' ' + +#define Cmnd_STK_SET_PARAMETER 0x40 // ' ' +#define Cmnd_STK_GET_PARAMETER 0x41 // ' ' +#define Cmnd_STK_SET_DEVICE 0x42 // ' ' +#define Cmnd_STK_GET_DEVICE 0x43 // ' ' +#define Cmnd_STK_GET_STATUS 0x44 // ' ' +#define Cmnd_STK_SET_DEVICE_EXT 0x45 // ' ' + +#define Cmnd_STK_ENTER_PROGMODE 0x50 // ' ' +#define Cmnd_STK_LEAVE_PROGMODE 0x51 // ' ' +#define Cmnd_STK_CHIP_ERASE 0x52 // ' ' +#define Cmnd_STK_CHECK_AUTOINC 0x53 // ' ' +#define Cmnd_STK_CHECK_DEVICE 0x54 // ' ' +#define Cmnd_STK_LOAD_ADDRESS 0x55 // ' ' +#define Cmnd_STK_UNIVERSAL 0x56 // ' ' + +#define Cmnd_STK_PROG_FLASH 0x60 // ' ' +#define Cmnd_STK_PROG_DATA 0x61 // ' ' +#define Cmnd_STK_PROG_FUSE 0x62 // ' ' +#define Cmnd_STK_PROG_LOCK 0x63 // ' ' +#define Cmnd_STK_PROG_PAGE 0x64 // ' ' +#define Cmnd_STK_PROG_FUSE_EXT 0x65 // ' ' + +#define Cmnd_STK_READ_FLASH 0x70 // ' ' +#define Cmnd_STK_READ_DATA 0x71 // ' ' +#define Cmnd_STK_READ_FUSE 0x72 // ' ' +#define Cmnd_STK_READ_LOCK 0x73 // ' ' +#define Cmnd_STK_READ_PAGE 0x74 // ' ' +#define Cmnd_STK_READ_SIGN 0x75 // ' ' +#define Cmnd_STK_READ_OSCCAL 0x76 // ' ' +#define Cmnd_STK_READ_FUSE_EXT 0x77 // ' ' +#define Cmnd_STK_READ_OSCCAL_EXT 0x78 // ' ' + +// *****************[ STK Parameter constants ]*************************** + +#define Parm_STK_HW_VER 0x80 // ' ' - R +#define Parm_STK_SW_MAJOR 0x81 // ' ' - R +#define Parm_STK_SW_MINOR 0x82 // ' ' - R +#define Parm_STK_LEDS 0x83 // ' ' - R/W +#define Parm_STK_VTARGET 0x84 // ' ' - R/W +#define Parm_STK_VADJUST 0x85 // ' ' - R/W +#define Parm_STK_OSC_PSCALE 0x86 // ' ' - R/W +#define Parm_STK_OSC_CMATCH 0x87 // ' ' - R/W +#define Parm_STK_RESET_DURATION 0x88 // ' ' - R/W +#define Parm_STK_SCK_DURATION 0x89 // ' ' - R/W + +#define Parm_STK_BUFSIZEL 0x90 // ' ' - R/W, Range {0..255} +#define Parm_STK_BUFSIZEH 0x91 // ' ' - R/W, Range {0..255} +#define Parm_STK_DEVICE 0x92 // ' ' - R/W, Range {0..255} +#define Parm_STK_PROGMODE 0x93 // ' ' - 'P' or 'S' +#define Parm_STK_PARAMODE 0x94 // ' ' - TRUE or FALSE +#define Parm_STK_POLLING 0x95 // ' ' - TRUE or FALSE +#define Parm_STK_SELFTIMED 0x96 // ' ' - TRUE or FALSE + + +// *****************[ STK status bit definitions ]*************************** + +#define Stat_STK_INSYNC 0x01 // INSYNC status bit, '1' - INSYNC +#define Stat_STK_PROGMODE 0x02 // Programming mode, '1' - PROGMODE +#define Stat_STK_STANDALONE 0x04 // Standalone mode, '1' - SM mode +#define Stat_STK_RESET 0x08 // RESET button, '1' - Pushed +#define Stat_STK_PROGRAM 0x10 // Program button, ' 1' - Pushed +#define Stat_STK_LEDG 0x20 // Green LED status, '1' - Lit +#define Stat_STK_LEDR 0x40 // Red LED status, '1' - Lit +#define Stat_STK_LEDBLINK 0x80 // LED blink ON/OFF, '1' - Blink + + +// *****************************[ End Of COMMAND.H ]************************** diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index cf0121388..2f5b68a14 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -1,9 +1,9 @@ -/* +/* ESP8266WebServer.cpp - Dead simple web-server. Supports only one simultaneous client, knows how to handle GET and POST. Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - + 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 @@ -25,25 +25,11 @@ #include "WiFiServer.h" #include "WiFiClient.h" #include "ESP8266WebServer.h" - +#include "FS.h" +#include "detail/RequestHandler.h" // #define DEBUG #define DEBUG_OUTPUT Serial -struct ESP8266WebServer::RequestHandler { - RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) - : fn(fn) - , uri(uri) - , method(method) - , next(NULL) - { - } - - ESP8266WebServer::THandlerFunction fn; - String uri; - HTTPMethod method; - RequestHandler* next; - -}; ESP8266WebServer::ESP8266WebServer(int port) : _server(port) @@ -78,15 +64,22 @@ void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction ha void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) { - RequestHandler* handler = new RequestHandler(fn, uri, method); - if (!_lastHandler) { - _firstHandler = handler; - _lastHandler = handler; - } - else { - _lastHandler->next = handler; - _lastHandler = handler; - } + _addRequestHandler(new FunctionRequestHandler(fn, uri, method)); +} + +void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) { + if (!_lastHandler) { + _firstHandler = handler; + _lastHandler = handler; + } + else { + _lastHandler->next = handler; + _lastHandler = handler; + } +} + +void ESP8266WebServer::serveStatic(const char* uri, FS& fs, const char* path) { + _addRequestHandler(new StaticRequestHandler(fs, uri)); } void ESP8266WebServer::handleClient() @@ -185,17 +178,18 @@ void ESP8266WebServer::send(int code, const String& content_type, const String& } void ESP8266WebServer::sendContent(const String& content) { + const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE; size_t size_to_send = content.length(); - size_t size_sent = 0; - while(size_to_send) { - const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE; + const char* send_start = content.c_str(); + + while (size_to_send) { size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size; - size_t sent = _currentClient.write(content.c_str() + size_sent, will_send); - size_to_send -= sent; - size_sent += sent; + size_t sent = _currentClient.write(send_start, will_send); if (sent == 0) { break; } + size_to_send -= sent; + send_start += sent; } } @@ -269,16 +263,9 @@ void ESP8266WebServer::onNotFound(THandlerFunction fn) { void ESP8266WebServer::_handleRequest() { RequestHandler* handler; - for (handler = _firstHandler; handler; handler = handler->next) - { - if (handler->method != HTTP_ANY && handler->method != _currentMethod) - continue; - - if (handler->uri != _currentUri) - continue; - - handler->fn(); - break; + for (handler = _firstHandler; handler; handler = handler->next) { + if (handler->handle(*this, _currentMethod, _currentUri)) + break; } if (!handler){ diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index bf538c3e0..ae38eb8cc 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -1,9 +1,9 @@ -/* +/* ESP8266WebServer.h - Dead simple web-server. Supports only one simultaneous client, knows how to handle GET and POST. Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - + 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 @@ -37,6 +37,12 @@ enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END }; #define CONTENT_LENGTH_UNKNOWN ((size_t) -1) #define CONTENT_LENGTH_NOT_SET ((size_t) -2) +class RequestHandler; + +namespace fs { +class FS; +} + typedef struct { HTTPUploadStatus status; String filename; @@ -59,6 +65,7 @@ public: typedef std::function THandlerFunction; void on(const char* uri, THandlerFunction handler); void on(const char* uri, HTTPMethod method, THandlerFunction fn); + void serveStatic(const char* uri, fs::FS& fs, const char* path); void onNotFound(THandlerFunction fn); //called when handler is not assigned void onFileUpload(THandlerFunction fn); //handle file uploads @@ -66,13 +73,13 @@ public: HTTPMethod method() { return _currentMethod; } WiFiClient client() { return _currentClient; } HTTPUpload& upload() { return _currentUpload; } - + String arg(const char* name); // get request argument value by name String arg(int i); // get request argument value by number String argName(int i); // get request argument name by number int args(); // get arguments count bool hasArg(const char* name); // check if argument exists - + // send response to the client // code - HTTP response code, can be 200 or 404 // content_type - HTTP content type, like "text/plain" or "image/png" @@ -89,7 +96,7 @@ public: template size_t streamFile(T &file, const String& contentType){ setContentLength(file.size()); - if (String(file.name()).endsWith(".gz") && + if (String(file.name()).endsWith(".gz") && contentType != "application/x-gzip" && contentType != "application/octet-stream"){ sendHeader("Content-Encoding", "gzip"); @@ -97,8 +104,9 @@ template size_t streamFile(T &file, const String& contentType){ send(200, contentType, ""); return _currentClient.write(file, HTTP_DOWNLOAD_UNIT_SIZE); } - + protected: + void _addRequestHandler(RequestHandler* handler); void _handleRequest(); bool _parseRequest(WiFiClient& client); void _parseArguments(String data); @@ -108,7 +116,6 @@ protected: uint8_t _uploadReadByte(WiFiClient& client); void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); - struct RequestHandler; struct RequestArgument { String key; String value; diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandler.h b/libraries/ESP8266WebServer/src/detail/RequestHandler.h new file mode 100644 index 000000000..702e24e0b --- /dev/null +++ b/libraries/ESP8266WebServer/src/detail/RequestHandler.h @@ -0,0 +1,96 @@ +#ifndef REQUESTHANDLER_H +#define REQUESTHANDLER_H + +class RequestHandler { +public: + RequestHandler(const char* uri, HTTPMethod method) + : uri(uri) + , method(method) + , next(NULL) + { + } + + virtual bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) = 0; + + RequestHandler* next; + +protected: + String uri; + HTTPMethod method; +}; + + +class FunctionRequestHandler : public RequestHandler { + typedef RequestHandler base; + +public: + FunctionRequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) + : fn(fn) + , base(uri, method) + { + } + + bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { + if (method != HTTP_ANY && method != requestMethod) + return false; + + if (requestUri != uri) + return false; + + fn(); + return true; + } + +protected: + ESP8266WebServer::THandlerFunction fn; +}; + +class StaticRequestHandler : public RequestHandler { + typedef RequestHandler base; + +public: + StaticRequestHandler(FS& fs, const char* uri) + : fs(fs) + , base(uri, HTTP_GET) + { + } + + bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { + if (requestMethod != method) + return false; + DEBUGV("StaticRequestHandler::handle: %s\r\n", requestUri.c_str()); + if (!requestUri.startsWith(uri)) + return false; + + auto prefixLength = uri.length() - 1; + String path = requestUri.substring(prefixLength); + DEBUGV("StaticRequestHandler::handle: %d %s\r\n", prefixLength, path.c_str()); + File f = fs.open(path, "r"); + if (!f) + return false; + + server.streamFile(f, getContentType(path)); + return true; + } + + static String getContentType(const String& path) { + if (path.endsWith(".html")) return "text/html"; + else if (path.endsWith(".htm")) return "text/html"; + else if (path.endsWith(".css")) return "text/css"; + else if (path.endsWith(".txt")) return "text/plain"; + else if (path.endsWith(".js")) return "application/javascript"; + else if (path.endsWith(".png")) return "image/png"; + else if (path.endsWith(".gif")) return "image/gif"; + else if (path.endsWith(".jpg")) return "image/jpeg"; + else if (path.endsWith(".ico")) return "image/x-icon"; + else if (path.endsWith(".xml")) return "text/xml"; + else if (path.endsWith(".pdf")) return "application/pdf"; + else if (path.endsWith(".zip")) return "application/zip"; + return "text/plain"; + } + +protected: + FS fs; +}; + +#endif //REQUESTHANDLER_H diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp index 5400f02d9..161b16f42 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp @@ -666,7 +666,7 @@ bool ESP8266WiFiClass::hostname(String aHostname) { //-------------------------------------------------------------- -void wifi_wps_status_cb(WPS_CB_STATUS_t status) +void wifi_wps_status_cb(wps_cb_status status) { DEBUGV("wps cb status: %d\r\n", status); switch (status) { @@ -682,6 +682,9 @@ void wifi_wps_status_cb(WPS_CB_STATUS_t status) case WPS_CB_ST_TIMEOUT: DEBUGV("wps TIMEOUT\n"); break; + case WPS_CB_ST_WEP: + DEBUGV("wps WEP\n"); + break; } // todo user function to get status @@ -715,7 +718,7 @@ bool ESP8266WiFiClass::beginWPSConfig(void) { return false; } - if(!wifi_set_wps_cb(&wifi_wps_status_cb)) { + if(!wifi_set_wps_cb((wps_st_cb_t) &wifi_wps_status_cb)) { DEBUGV("wps cb faild\n"); return false; } diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.h b/libraries/ESP8266WiFi/src/ESP8266WiFi.h index 369892ecf..b8329c4fc 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.h @@ -44,7 +44,7 @@ public: ESP8266WiFiClass(); void mode(WiFiMode); - + /** * Start Wifi connection * if passphrase is set the most secure supported mode will be automatically selected @@ -95,7 +95,7 @@ public: * param dns: Defined DNS */ void config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns); - + /* Configure access point * * param local_ip: access point IP @@ -336,19 +336,19 @@ public: /* * Start SmartConfig * - */ + */ void beginSmartConfig(); - + /* * Query SmartConfig status, to decide when stop config * - */ + */ bool smartConfigDone(); /* * Stop SmartConfig * - */ + */ void stopSmartConfig(); friend class WiFiClient; @@ -366,7 +366,7 @@ protected: bool _useApMode; bool _useClientMode; bool _useStaticIp; - + static bool _scanAsync; static bool _scanStarted; static bool _scanComplete; diff --git a/libraries/ESP8266mDNS/ESP8266mDNS.cpp b/libraries/ESP8266mDNS/ESP8266mDNS.cpp index 6e50313a4..0767d14e0 100644 --- a/libraries/ESP8266mDNS/ESP8266mDNS.cpp +++ b/libraries/ESP8266mDNS/ESP8266mDNS.cpp @@ -36,7 +36,7 @@ License (MIT license): #include "ESP8266mDNS.h" #include - + #include "debug.h" extern "C" { @@ -88,28 +88,28 @@ static const int MDNS_PORT = 5353; MDNSResponder::MDNSResponder() : _conn(0) { _services = 0; } MDNSResponder::~MDNSResponder() {} -bool MDNSResponder::begin(const char* domain){ +bool MDNSResponder::begin(const char* domain){ // Open the MDNS socket if it isn't already open. - + size_t n = strlen(domain); if (n > 255) { // Can only handle domains that are 255 chars in length. return false; } - + // Copy in domain characters as lowercase for (int i = 0; i < n; ++i) _hostName[i] = tolower(domain[i]); _hostName[n] = '\0'; - + os_strcpy(_boardName, ARDUINO_BOARD); - + // Open the MDNS socket if it isn't already open. if (!_conn) { uint32_t ourIp = _getOurIp(); if(ourIp == 0){ return false; } - + ip_addr_t ifaddr; ifaddr.addr = ourIp; ip_addr_t multicast_addr; @@ -135,7 +135,7 @@ bool MDNSResponder::begin(const char* domain){ void MDNSResponder::update() { if (!_conn->next()) { - return; + return; } _parsePacket(); } @@ -163,15 +163,21 @@ uint16_t MDNSResponder::_getServicePort(char *name, char *proto){ } uint32_t MDNSResponder::_getOurIp(){ - if(wifi_get_opmode() & STATION_MODE){ + int mode = wifi_get_opmode(); + if(mode & STATION_MODE){ struct ip_info staIpInfo; wifi_get_ip_info(STATION_IF, &staIpInfo); return staIpInfo.ip.addr; - } + } else if (mode & SOFTAP_MODE) { + struct ip_info staIpInfo; + wifi_get_ip_info(SOFTAP_IF, &staIpInfo); + return staIpInfo.ip.addr; + } else { #ifdef MDNS_DEBUG_ERR os_printf("ERR_NO_LOCAL_IP\n"); #endif - return 0; + return 0; + } } void MDNSResponder::_parsePacket(){ @@ -180,39 +186,39 @@ void MDNSResponder::_parsePacket(){ bool serviceParsed = false; bool protoParsed = false; bool localParsed = false; - + char hostName[255]; uint8_t hostNameLen; - + char serviceName[32]; uint8_t serviceNameLen; uint16_t servicePort; - + char protoName[32]; uint8_t protoNameLen; - + uint16_t packetHeader[6]; - + for(i=0; i<6; i++) packetHeader[i] = _conn_read16(); if((packetHeader[1] & 0x8000) != 0){ //not parsing responses yet _conn->flush(); return; } - + // PARSE REQUEST NAME - + hostNameLen = _conn_read8(); _conn_readS(hostName, hostNameLen); hostName[hostNameLen] = '\0'; - + if(hostName[0] == '_'){ serviceParsed = true; memcpy(serviceName, hostName+1, hostNameLen); serviceNameLen = hostNameLen-1; hostNameLen = 0; } - + if(hostNameLen > 0 && strcmp(_hostName, hostName) != 0){ #ifdef MDNS_DEBUG_ERR os_printf("ERR_NO_HOST: %s\n", hostName); @@ -220,12 +226,12 @@ void MDNSResponder::_parsePacket(){ _conn->flush(); return; } - + if(!serviceParsed){ serviceNameLen = _conn_read8(); _conn_readS(serviceName, serviceNameLen); serviceName[serviceNameLen] = '\0'; - + if(serviceName[0] == '_'){ memcpy(serviceName, serviceName+1, serviceNameLen); serviceNameLen--; @@ -253,7 +259,7 @@ void MDNSResponder::_parsePacket(){ return; } } - + if(!protoParsed){ protoNameLen = _conn_read8(); _conn_readS(protoName, protoNameLen); @@ -270,7 +276,7 @@ void MDNSResponder::_parsePacket(){ return; } } - + if(!localParsed){ char localName[32]; uint8_t localNameLen = _conn_read8(); @@ -287,7 +293,7 @@ void MDNSResponder::_parsePacket(){ return; } } - + if(serviceNameLen > 0 && protoNameLen > 0){ servicePort = _getServicePort(serviceName, protoName); if(servicePort == 0){ @@ -304,16 +310,16 @@ void MDNSResponder::_parsePacket(){ _conn->flush(); return; } - + // RESPOND - + #ifdef MDNS_DEBUG_RX os_printf("RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]); #endif uint16_t currentType; uint16_t currentClass; - + int numQuestions = packetHeader[2]; if(numQuestions > 4) numQuestions = 4; uint16_t questions[4]; @@ -326,21 +332,21 @@ void MDNSResponder::_parsePacket(){ } currentClass = _conn_read16(); if(currentClass & MDNS_CLASS_IN) questions[question++] = currentType; - + if(numQuestions > 0){ if(_conn_read16() != 0xC00C){//new question but for another host/service _conn->flush(); numQuestions = 0; } } - + #ifdef MDNS_DEBUG_RX os_printf("REQ: "); if(hostNameLen > 0) os_printf("%s.", hostName); if(serviceNameLen > 0) os_printf("_%s.", serviceName); if(protoNameLen > 0) os_printf("_%s.", protoName); os_printf("local. "); - + if(currentType == MDNS_TYPE_AAAA) os_printf(" AAAA "); else if(currentType == MDNS_TYPE_A) os_printf(" A "); else if(currentType == MDNS_TYPE_PTR) os_printf(" PTR "); @@ -351,7 +357,7 @@ void MDNSResponder::_parsePacket(){ if(currentClass == MDNS_CLASS_IN) os_printf(" IN "); else if(currentClass == MDNS_CLASS_IN_FLUSH_CACHE) os_printf(" IN[F] "); else os_printf(" 0x%04X ", currentClass); - + os_printf("\n"); #endif } @@ -362,7 +368,7 @@ void MDNSResponder::_parsePacket(){ else if(questions[i] == MDNS_TYPE_TXT) responseMask |= 0x4; else if(questions[i] == MDNS_TYPE_PTR) responseMask |= 0xF; } - + return _reply(responseMask, (serviceName), (protoName), servicePort); } @@ -371,14 +377,14 @@ void MDNSResponder::_parsePacket(){ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint16_t port){ int i; if(replyMask == 0) return; - + #ifdef MDNS_DEBUG_TX os_printf("TX: mask:%01X, service:%s, proto:%s, port:%u\n", replyMask, service, proto, port); #endif - + char nameLen = os_strlen(_hostName); size_t serviceLen = os_strlen(service); - + uint8_t answerCount = 0; for(i=0;i<4;i++){ if(replyMask & (1 << i)) answerCount++; @@ -394,12 +400,12 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 0x00, 0x00, //Additional records }; _conn->append(reinterpret_cast(head), 12); - + if((replyMask & 0x8) == 0){ _conn->append(reinterpret_cast(&nameLen), 1); _conn->append(reinterpret_cast(_hostName), nameLen); } - + if(replyMask & 0xE){ uint8_t servHead[2] = {(uint8_t)(serviceLen+1), '_'}; uint8_t protoHead[2] = {0x4, '_'}; @@ -408,14 +414,14 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 _conn->append(reinterpret_cast(protoHead), 2); _conn->append(reinterpret_cast(proto), 3); } - + uint8_t local[7] = { 0x05, //strlen(_local) 0x6C, 0x6F, 0x63, 0x61, 0x6C, //local 0x00, //End of domain }; _conn->append(reinterpret_cast(local), 7); - + // PTR Response if(replyMask & 0x8){ uint8_t ptr[10] = { @@ -430,16 +436,16 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 uint8_t ptrTail[2] = {0xC0, 0x0C}; _conn->append(reinterpret_cast(ptrTail), 2); } - + // TXT Response if(replyMask & 0x4){ if(replyMask & 0x8){//send the name uint8_t txtHead[2] = {0xC0, (uint8_t)(36 + serviceLen)}; _conn->append(reinterpret_cast(txtHead), 2); } - + uint8_t boardNameLen = os_strlen(_boardName); - + uint8_t txt[24] = { 0x00, 0x10, //Type TXT 0x80, 0x01, //Class IN, with cache flush @@ -450,9 +456,9 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 }; _conn->append(reinterpret_cast(txt), 17); _conn->append(reinterpret_cast(_boardName), boardNameLen); - + } - + // SRV Response if(replyMask & 0x2){ if(replyMask & 0xC){//send the name @@ -461,7 +467,7 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 srvHead[1] = 36 + serviceLen; _conn->append(reinterpret_cast(srvHead), 2); } - + uint8_t srv[16] = { 0x00, 0x21, //Type SRV 0x80, 0x01, //Class IN, with cache flush @@ -479,7 +485,7 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 srvTail[1] = 19 + serviceLen; _conn->append(reinterpret_cast(srvTail), 2); } - + // A Response if(replyMask & 0x1){ uint32_t ip = _getOurIp(); @@ -488,7 +494,7 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1 _conn->append(reinterpret_cast(_hostName), nameLen); _conn->append(reinterpret_cast(local), 7); } - + uint8_t aaa[14] = { 0x00, 0x01, //TYPE A 0x80, 0x01, //Class IN, with cache flush diff --git a/libraries/ESP8266mDNS/ESP8266mDNS.h b/libraries/ESP8266mDNS/ESP8266mDNS.h index 4a8fa6ad1..a2fd17d24 100644 --- a/libraries/ESP8266mDNS/ESP8266mDNS.h +++ b/libraries/ESP8266mDNS/ESP8266mDNS.h @@ -13,10 +13,9 @@ Requirements: Usage: - Include the ESP8266 Multicast DNS library in the sketch. -- Create an instance of the MDNSResponder class. - Call the begin method in the sketch's setup and provide a domain name (without - the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the - Adafruit CC3000 class instance. Optionally provide a time to live (in seconds) + the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the + Adafruit CC3000 class instance. Optionally provide a time to live (in seconds) for the DNS record--the default is 1 hour. - Call the update method in each iteration of the sketch's loop function. @@ -70,7 +69,7 @@ public: return begin(hostName); } void update(); - + void addService(char *service, char *proto, uint16_t port); void addService(const char *service, const char *proto, uint16_t port){ addService((char *)service, (char *)proto, port); @@ -78,7 +77,7 @@ public: void addService(String service, String proto, uint16_t port){ addService(service.c_str(), proto.c_str(), port); } - + private: struct MDNSService * _services; UdpContext* _conn; diff --git a/libraries/Servo/src/esp8266/Servo.cpp b/libraries/Servo/src/esp8266/Servo.cpp index e9fce77f1..af8bf549e 100644 --- a/libraries/Servo/src/esp8266/Servo.cpp +++ b/libraries/Servo/src/esp8266/Servo.cpp @@ -59,8 +59,6 @@ static uint8_t s_servoCount = 0; // the total number of attached s_se //------------------------------------------------------------------------------ template void Servo_Handler(T* timer) { - noInterrupts(); - uint8_t servoIndex; // clear interrupt @@ -101,8 +99,6 @@ template void Servo_Handler(T* timer) timer->setEndOfCycle(); } - - interrupts(); } static void initISR(ServoTimerSequence timerId) diff --git a/libraries/esp8266/examples/ConfigFile/ConfigFile.ino b/libraries/esp8266/examples/ConfigFile/ConfigFile.ino new file mode 100644 index 000000000..3503a10b5 --- /dev/null +++ b/libraries/esp8266/examples/ConfigFile/ConfigFile.ino @@ -0,0 +1,97 @@ +// Example: storing JSON configuration file in flash file system +// +// Uses ArduinoJson library by Benoit Blanchon. +// https://github.com/bblanchon/ArduinoJson +// +// Created Aug 10, 2015 by Ivan Grokhotkov. +// +// This example code is in the public domain. + +#include +#include "FS.h" + +bool loadConfig() { + File configFile = SPIFFS.open("/config.json", "r"); + if (!configFile) { + Serial.println("Failed to open config file"); + return false; + } + + size_t size = configFile.size(); + if (size > 1024) { + Serial.println("Config file size is too large"); + return false; + } + + // Allocate a buffer to store contents of the file. + std::unique_ptr buf(new char[size]); + + // We don't use String here because ArduinoJson library requires the input + // buffer to be mutable. If you don't use ArduinoJson, you may as well + // use configFile.readString instead. + configFile.readBytes(buf.get(), size); + + StaticJsonBuffer<200> jsonBuffer; + JsonObject& json = jsonBuffer.parseObject(buf.get()); + + if (!json.success()) { + Serial.println("Failed to parse config file"); + return false; + } + + const char* serverName = json["serverName"]; + const char* accessToken = json["accessToken"]; + + // Real world application would store these values in some variables for + // later use. + + Serial.print("Loaded serverName: "); + Serial.println(serverName); + Serial.print("Loaded accessToken: "); + Serial.println(accessToken); + return true; +} + +bool saveConfig() { + StaticJsonBuffer<200> jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + json["serverName"] = "api.example.com"; + json["accessToken"] = "128du9as8du12eoue8da98h123ueh9h98"; + + File configFile = SPIFFS.open("/config.json", "w"); + if (!configFile) { + Serial.println("Failed to open config file for writing"); + return false; + } + + json.printTo(configFile); + return true; +} + +void setup() { + Serial.begin(115200); + Serial.println(""); + delay(1000); + Serial.println("Mounting FS..."); + + if (!SPIFFS.begin()) { + Serial.println("Failed to mount file system"); + return; + } + + + if (!saveConfig()) { + Serial.println("Failed to save config"); + } else { + Serial.println("Config saved"); + } + + if (!loadConfig()) { + Serial.println("Failed to load config"); + } else { + Serial.println("Config loaded"); + } +} + +void loop() { +} diff --git a/tests/common.sh b/tests/common.sh index fab1a90f3..b4e935168 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -20,3 +20,14 @@ function build_sketches() fi done } + +function install_libraries() +{ + mkdir -p $HOME/Arduino/libraries + pushd $HOME/Arduino/libraries + + # install ArduinoJson library + wget https://github.com/bblanchon/ArduinoJson/releases/download/v4.6.1/ArduinoJson-v4.6.1.zip && unzip ArduinoJson-v4.6.1.zip + + popd +} diff --git a/tools/sdk/changelog.txt b/tools/sdk/changelog.txt index 05d32ebb5..7f7db13f7 100644 --- a/tools/sdk/changelog.txt +++ b/tools/sdk/changelog.txt @@ -1,7 +1,65 @@ -esp_iot_sdk_v1.2.0_15_07_13_p4 Release Note +esp_iot_sdk_v1.3.0_15_08_10_p1 Release Note +---------------------------------------- + +Here is a patch based on esp_iot_sdk_v1.3.0 (non-OS SDK) resolved issue that if there are 2 connections, one is normal TCP connection, the other is SSL connection, it may cause memory leak. + +Download and unzip the attachment, replace the lib in esp_iot_sdk/lib folder. + +Please do not set the same priority for two different tasks when using system_os_task. + +Sorry for the inconvenience. + + +esp_iot_sdk_v1.3.0_15_08_08 Release Note +---------------------------------------- + +Resolved Issues(Bugs below are eligible for Bug Bounty Program): +1.Device can't connect to router after it gets ssid and password when using ESPTOUCH with router's ssid hidden. [冯智] +2.Format string of os_random can't be supported by atoi. [杨朝位] +3.Optimized os_printf seems to have an issue on 4 bytes aligned and other valuable suggestions. [Andrey Filimonov] +4.SmartConfig can’t get IP address after connected to router. [孙大明] + +Optimization: +1.Memory optimization to save 12KBytes. +2.Optimize RF calibration to short the booting time,more details in documentation "2A-ESP8266__IOT_SDK_User_Manual" chapter "Appendix". +3.Optimize Wi-Fi function to solve issue that ESP8266 may fail to connect to a special router. +4.Optimize software timer to solve the a connecting problem.Please do not call "os_delay_us" or "while" or "for" to occupy CPU more than 10 ms in timer callback. +5.Optimize system_get_rst_info to obtain more accurate information about the start-up. +6.Optimize function of Wi-Fi scanning to be more reliable. +7.Optimize function of changing Wi-Fi mode to be more reliable. +8.Optimize WPS to improve connectivity.And WPS does not support WEP, it will return status "WPS_CB_ST_WEP". +9.Optimize Wi-Fi function to solve softAP multiple stations DHCP issue. +10.Optimize TCP in LAST_ACK status. +11.Optimize TLS to support SHA256, SHA384, SHA512. +12.Memory optimization during TLS hand-shaking. +13.Optimize OTA funtion to download big chunk of data. +14.Add CRC32 in OTA function.Folder "tools" in esp_iot_sdk has to be updated, otherwise OTA will fail. +15.Optimize mDNS to support both softAP and station interfaces. +16.Optimize ESP-NOW, more details in "Add APIs" +17.Update SmartConfig to version 2.4.7 +18.Remove "-O2" from makefile. +19.Optimize header files to improve compatibility, will not affect compilation. + +Add APIs: +1.system_soft_wdt_feed : feed software watchdog +2.wifi_softap_get_dhcps_lease:get IP range of ESP8266 softAP DHCP server +3.ESP-NOW APIs +esp_now_set_kok: set the secure key to encrypt ESP-NOW communication key +esp_now_register_send_cb: register ESP-NOW send callback +esp_now_unregister_send_cb: unregister ESP-NOW send callback + +AT_v0.40 Release Note: +Note: For AT firmware to support FOTA, flash size need to be 1024KB or more than that. + +1.Add parameter in command "AT+CWSAP" to set the maximum number of connections allowed. + + + + +esp_iot_sdk_v1.2.0_15_07_13_p4 Release Note ------------------------------------------- -Here is a patch of memory optimization based on SDK_v1.2.0 +Here is a patch of memory optimization based on SDK_v1.2.0 1. It saved about 8KBytes memory. 2. It revised problem that change mode may cause memory leak. 3. Update SmartConfig to version 2.4.3 @@ -13,7 +71,7 @@ Thanks for your interest in Espressif Systems and ESP8266 ! -esp_iot_sdk_v1.2.0_15_07_09_p3 Release Note +esp_iot_sdk_v1.2.0_15_07_09_p3 Release Note ------------------------------------------- Here is a patch based on SDK_v1.2.0 solved problem that if AP’s SSID is hidden,ESPTOUCH may get wrong BSSID of AP and cause connection fail. @@ -24,7 +82,7 @@ Sorry for the inconvenience. -esp_iot_sdk_v1.2.0_15_07_09_p2 Release Note +esp_iot_sdk_v1.2.0_15_07_09_p2 Release Note ------------------------------------------- Updated libssl again. To support SHA-256 and SHA-512. @@ -34,7 +92,7 @@ Thanks for your interest in Espressif Systems and ESP8266 ! -esp_iot_sdk_v1.2.0_15_07_08_p1 Release Note +esp_iot_sdk_v1.2.0_15_07_08_p1 Release Note ------------------------------------------- Here is a patch based on SDK_v1.2.0 solved problem that abnormal SSL disconnection may cause reset. @@ -45,7 +103,7 @@ Sorry for the inconvenience. -esp_iot_sdk_v1.2.0_15_07_03 Release Note +esp_iot_sdk_v1.2.0_15_07_03 Release Note ------------------------------------------- Resolved Issues(Bugs below are eligible for Bug Bounty Program): 1.TLS server disconnect to ESP8266 may cause crash. [孙新虎] @@ -77,10 +135,10 @@ esp_now_add_peer: add an ESP-NOW peer esp_now_del_peer: delete an ESP-NOW peer esp_now_set_self_role: set ESP-NOW role of device itself esp_now_get_self_role: get ESP-NOW role of device itself -esp_now_set_peer_role: set ESP-NOW role about another device -esp_now_get_peer_role: get ESP-NOW role about another device -esp_now_set_peer_key: set ESP-NOW key of a device -esp_now_get_peer_key: get ESP-NOW key of a device +esp_now_set_peer_role: set ESP-NOW role about another device +esp_now_get_peer_role: get ESP-NOW role about another device +esp_now_set_peer_key: set ESP-NOW key of a device +esp_now_get_peer_key: get ESP-NOW key of a device 2. WPS APIs wifi_wps_enable : enable WPS function @@ -92,9 +150,9 @@ wifi_set_wps_cb: set WPS callback system_soft_wdt_stop: stop software watchdog system_soft_wdt_restart: restart software watchdog -4.sntp_get_timezone: get SNTP timezone +4.sntp_get_timezone: get SNTP timezone -AT_v0.30 Release Note: +AT_v0.30 Release Note: Note: For AT firmware to support FOTA, flash size need to be 1024KB or more than that. 1.Command "AT+CWSTARTSMART" need not parameter any more, SmartConfig type can be got automatically. @@ -104,15 +162,15 @@ Note: For AT firmware to support FOTA, flash size need to be 1024KB or more than -esp_iot_sdk_1.1.2_15_06_25_p2 Release Note +esp_iot_sdk_1.1.2_15_06_25_p2 Release Note ------------------------------------------- -Here is a patch based on SDK_v1.1.2 solved problem of abnormal current during modem-sleep. +Here is a patch based on SDK_v1.1.2 solved problem of abnormal current during modem-sleep. Please replace the lib in \esp_iot_sdk_v1.1.2\lib Sorry for the inconvenience. -esp_iot_sdk_v1.1.2_15_06_24_p1.1 Release Note +esp_iot_sdk_v1.1.2_15_06_24_p1.1 Release Note ------------------------------------------- Here is a patch for AT firmware based on SDK_v1.1.2 resolved issue that AT user parameter area was located in wrong address,it may cause WiFi configuration goes wrong. @@ -133,7 +191,7 @@ can not FOTA upgrade,please don‘t call “AT+CIPUPDATE” So sorry for the inconvenience. -esp_iot_sdk_v1.1.2_15_06_16_p1 Release Note +esp_iot_sdk_v1.1.2_15_06_16_p1 Release Note ------------------------------------------- Here is a patch based on SDK_v1.1.2 resolved issue that "wifi_station_scan" cause loss of wireless connectivity. @@ -144,7 +202,7 @@ Sorry for the inconvenience. -esp_iot_sdk_v1.1.2_15_06_12 Release Note +esp_iot_sdk_v1.1.2_15_06_12 Release Note ------------------------------------------- Optimization: @@ -165,7 +223,7 @@ Add Documentation: -esp_iot_sdk_v1.1.1_15_06_05 Release Note +esp_iot_sdk_v1.1.1_15_06_05 Release Note ------------------------------------------- Resolved Issues(Bugs below are eligible for Bug Bounty Program): @@ -175,7 +233,7 @@ Resolved Issues(Bugs below are eligible for Bug Bounty Program): Optimization: 1.Update JSON parser to handle with illegal parameter and illegal calling progress. -2.Add parameter of user_esp_platform_check_ip in user_websever.c which in IOT_Demo. +2.Add parameter of user_esp_platform_check_ip in user_websever.c which in IOT_Demo. 3.Update UART driver to solve the problem that if send data through UART while ESP8266 startup may cause UART invalid. 4.Update smartconfig to version 2.2, corresponding phone APP v0.3.2. And update the description and example of smartconfig_start in document "2C_ESP8266__Programming Guide" 5.Update code in iram to solve the problem that space for text is not enough. @@ -185,12 +243,12 @@ Optimization: 9.Remove useless driver code in IOT_Demo 10.Update IOT_Demo to use the latest PWM driver in light demo. 11.Provide liblwip_536.a of which MSS size is 536 -12.Revised issue that boot may fail when 80Mhz SPI clock selected -13.Update esp_init_data_default.bin about RF option in \esp_iot_sdk\bin +12.Revised issue that boot may fail when 80Mhz SPI clock selected +13.Update esp_init_data_default.bin about RF option in \esp_iot_sdk\bin Added APIs: -1.PWM APIs: -Updated: pwm_init,add parameter to set PWM channel and GPIO pin +1.PWM APIs: +Updated: pwm_init,add parameter to set PWM channel and GPIO pin Added: (1)get_pwm_version:get version information of PWM driver (2)pwm_set_period:set PWM period @@ -200,23 +258,23 @@ Deleted: (2)pwm_get_freq:get PWM frequency 2.Read/write flash with protection (1)system_param_save_with_protect:write data into flash with backup protection -(2)system_param_load:read data which saved into flash with backup protection +(2)system_param_load:read data which saved into flash with backup protection 3.system_get_rst_info:get information about current startup,it is a normal startup or watch dog reset 4.at_response:set AT response 5.at_register_response_func:register a callback for user-define AT response. -6.Update document "2C_ESP8266__Programming Guide" to add description of interrupt definition in ets_sys.h +6.Update document "2C_ESP8266__Programming Guide" to add description of interrupt definition in ets_sys.h -AT_v0.25 Release Note: +AT_v0.25 Release Note: Note: For AT firmware to support FOTA, flash size need to be 1024KB or more than that. Optimization: 1.Add parameter about UDP local port in command "AT+SAVETRANSLINK" -Added AT command: +Added AT command: 1.AT+CIPDINFO:set configuration whether show remote IP and remote port with “+IPD” or not -esp_iot_sdk_v1.1.0_15_05_27_p1 Release Note +esp_iot_sdk_v1.1.0_15_05_27_p1 Release Note ------------------------------------------- Here is a patch based on SDK_v1.1.0 resolved issues below: @@ -226,7 +284,7 @@ Here is a patch based on SDK_v1.1.0 resolved issues below: Sorry for the inconvenience. -esp_iot_sdk_v1.1.0_15_05_22 Release Note +esp_iot_sdk_v1.1.0_15_05_22 Release Note ---------------------------------------- Resolved Issues(Bugs below are eligible for Bug Bounty Program): 1.Predictable TLS random values leads to insecure connections [projectgus] @@ -257,26 +315,26 @@ Added APIs: 3.system_uart_de_swap : disable UART0 swap 4.system_get_flash_size_map: get flash size and flash map 5.system_phy_set_max_tpw : set maximum RF TX power -6.system_phy_set_tpw_via_vdd33 :set RF TX power according to VDD33 +6.system_phy_set_tpw_via_vdd33 :set RF TX power according to VDD33 7.system_phy_set_rfoption : set RF option 8.wifi_station_get_rssi:get rssi of AP which ESP8266 station connected to   9.wifi_softap_get_station_num :get number count of stations connected to ESP8266 soft-AP -AT_v0.24 Release Note: +AT_v0.24 Release Note: Note: For AT firmware to support FOTA, flash size need to be 1024KB or more than that. Optimization: 1.Disable data echo of command "AT+CIPSEND" 2.Optimized "AT+CWJAP?" to get channel and rssi 3.ESP8266 station IP can only be got and inquiried after ESP8266 station connected to AP -Added AT command: +Added AT command: 1.AT+RFPOWER :set maximum RF TX power -2.AT+RFVDD : set RF TX power according to VDD33 +2.AT+RFVDD : set RF TX power according to VDD33 自从乐鑫信息科技于 2015-03-20 启动 Bug 赏金计划以来,我们收到了一些重要问题反馈及建议,感谢世界各地的开发者对 ESP8266的关注,推动我们的软件更进一步,技术支持团队也得到迅速地成长。我们将在如下发布日志中点名感谢您的帮助。 -esp_iot_sdk_v1.1.0_15_05_22 Release Note +esp_iot_sdk_v1.1.0_15_05_22 Release Note 修正问题(符合乐鑫 Bug 赏金计划): 1.随机值的生成可以被预测,完善随机数的产生机制 [projectgus] 2.softAP+station 模式下,可能连接 ESP8266 soft-AP 失败 [智捷通] @@ -307,11 +365,11 @@ esp_iot_sdk_v1.1.0_15_05_22 Release Note 4.system_get_flash_size_map: 查询 flash size 和 flash map 5.system_phy_set_max_tpw : 设置 RF TX power 最大值 6.system_phy_set_tpw_via_vdd33 :根据 VDD33 设置 RF TX power -7.system_phy_set_rfoption : 设置 RF +7.system_phy_set_rfoption : 设置 RF 8.wifi_station_get_rssi:查询 ESP8266 station 连接的 AP 的信号强度   9.wifi_softap_get_station_num :查询连接到 ESP8266 soft-AP 的 station 数目 -AT_v0.24 Release Note: +AT_v0.24 Release Note: 注意:运行 AT 固件,支持云端升级,请使用 1024KB 或以上容量的 flash 优化: @@ -319,7 +377,7 @@ AT_v0.24 Release Note: 2.优化"AT+CWJAP?" 可获得信道和信号强度 3.必须在 ESP8266 station 连接到 AP 后,才能查询到 ESP8266 station IP -新增 AT command: +新增 AT command: 1.AT+RFPOWER :设置 RF TX power 最大值 2.AT+RFVDD : 根据 VDD33 设置 RF TX power @@ -327,68 +385,68 @@ Thanks for your interest in ESP8266 ! esp_iot_sdk_v1.0.1_15_05_04_p1 ------------------------------------------- -Here is a patch for station+softAP issue that users may have, based on SDK_v1.0.1, +Here is a patch for station+softAP issue that users may have, based on SDK_v1.0.1, solved problem that connect to ESP8266 softAP may fail in station+softAP mode. Sorry for the inconvenience. -esp_iot_sdk_v1.0.1_15_04_24 Release Note +esp_iot_sdk_v1.0.1_15_04_24 Release Note ------------------------------------------- Resolved Issues(Bugs below are eligible for Bug Bounty Program): -1. SSL connection may fail if SSL packet size larger than 2kBytes [PeteW ] -2. UDP remote IP to be 0.0.0.0 may cause reset [Jerry S] -3. Optimize wifi_get_ip_info to fix loss of wireless connectivity problem -4. Air-Kiss restart [Orgmar] +1. SSL connection may fail if SSL packet size larger than 2kBytes [PeteW ] +2. UDP remote IP to be 0.0.0.0 may cause reset [Jerry S] +3. Optimize wifi_get_ip_info to fix loss of wireless connectivity problem +4. Air-Kiss restart [Orgmar] Optimization: -1. Optimized IOT_Espressif_EspTouch.APK (apply for access from Espressif) for improved compatibility. [???] -2. TCP server can not open again immediately with the same port [624908539] -3. Update UART driver for parity bit value may be incorrect [1062583993] -4. Add define of “ICACHE_RODATA_ATTR” for Symbol 'ICACHE_RODATA_ATTR' could not be resolved. [???] -5. Add API wifi_softap_dhcps_set_offer_option to enable/disable ESP8266 softAP DHCP server default gateway. [xyz769] -6. AT register_uart_rx_intr may enter callback twice. [???] -7.optimize document that WPA password length range : 8 ~ 64 bytes [785057041] -8. ESP8266 softAP DHCP server record 8 DHCP client's IP at most [ygjeon] -9. To set static IP (wifi_set_ip_info) has to disable DHCP first(wifi_softap_dhcps_stop or wifi_station_dhcpc_stop) +1. Optimized IOT_Espressif_EspTouch.APK (apply for access from Espressif) for improved compatibility. [???] +2. TCP server can not open again immediately with the same port [624908539] +3. Update UART driver for parity bit value may be incorrect [1062583993] +4. Add define of “ICACHE_RODATA_ATTR” for Symbol 'ICACHE_RODATA_ATTR' could not be resolved. [???] +5. Add API wifi_softap_dhcps_set_offer_option to enable/disable ESP8266 softAP DHCP server default gateway. [xyz769] +6. AT register_uart_rx_intr may enter callback twice. [???] +7.optimize document that WPA password length range : 8 ~ 64 bytes [785057041] +8. ESP8266 softAP DHCP server record 8 DHCP client's IP at most [ygjeon] +9. To set static IP (wifi_set_ip_info) has to disable DHCP first(wifi_softap_dhcps_stop or wifi_station_dhcpc_stop) 10.Add example of wifi_softap_set_dhcps_lease -11. smartconfig_start can only be called in ESP8266 station mode +11. smartconfig_start can only be called in ESP8266 station mode -Added APIs: -1. Wi-Fi related APIs: -wifi_station_set_reconnect_policy: enable/disable reconnect when ESP8266 disconnect from router,default to be enable reconnect. -wifi_set_event_handler_cb: set event handler of ESP8266 softAP or station status change. -wifi_softap_dhcps_set_offer_option: enable/disable get router information from ESP8266 softAP, default to be enable. -2. SNTP APIs: -sntp_get_current_timestamp: get current timestamp from Jan 01, 1970, 00:00 (GMT) -sntp_get_real_time: char,get real time (GTM + 8 time zone) -sntp_init: initialize SNTP -sntp_stop: stop SNTP -sntp_setserver: set SNTP server by IP -sntp_getserver: get SNTP server IP -sntp_setservername: set SNTP server by domain name -sntp_getservername: get domain name of SNTP server set by sntp_setservername -3. MDNS APIs: -espconn_mdns_init: initialize mDNS -espconn_mdns_close: close mDNS -espconn_mdns_server_register: register mDNS server -espconn_mdns_server_unregister: unregister mDNS server -espconn_mdns_get_servername: get mDNS server name -espconn_mdns_set_servername: set mDNS server name -espconn_mdns_set_hostname: get mDNS host name -espconn_mdns_get_hostname: set mDNS host name -espconn_mdns_disable: disable mDNS -espconn_mdns_enable: endisable mDNS +Added APIs: +1. Wi-Fi related APIs: +wifi_station_set_reconnect_policy: enable/disable reconnect when ESP8266 disconnect from router,default to be enable reconnect. +wifi_set_event_handler_cb: set event handler of ESP8266 softAP or station status change. +wifi_softap_dhcps_set_offer_option: enable/disable get router information from ESP8266 softAP, default to be enable. +2. SNTP APIs: +sntp_get_current_timestamp: get current timestamp from Jan 01, 1970, 00:00 (GMT) +sntp_get_real_time: char,get real time (GTM + 8 time zone) +sntp_init: initialize SNTP +sntp_stop: stop SNTP +sntp_setserver: set SNTP server by IP +sntp_getserver: get SNTP server IP +sntp_setservername: set SNTP server by domain name +sntp_getservername: get domain name of SNTP server set by sntp_setservername +3. MDNS APIs: +espconn_mdns_init: initialize mDNS +espconn_mdns_close: close mDNS +espconn_mdns_server_register: register mDNS server +espconn_mdns_server_unregister: unregister mDNS server +espconn_mdns_get_servername: get mDNS server name +espconn_mdns_set_servername: set mDNS server name +espconn_mdns_set_hostname: get mDNS host name +espconn_mdns_get_hostname: set mDNS host name +espconn_mdns_disable: disable mDNS +espconn_mdns_enable: endisable mDNS -AT_v0.23 Release Note: -Optimized: -1.AT+CWJAP add parameter "bssid", for several APs may have the same SSID +AT_v0.23 Release Note: +Optimized: +1.AT+CWJAP add parameter "bssid", for several APs may have the same SSID -New AT commands: -1. AT+CIPSENDBUF: write data into TCP-send-buffer; non-blocking. Background task automatically handles transmission. Has much higher throughput. -2. AT+CIPBUFRESET: resets segment count in TCP-send-buffer -3. AT+CIPBUFSTATUS: checks status of TCP-send-buffer -4. AT+CIPCHECKSEGID: checks if a specific segment in TCP-send-buffer has sent successfully +New AT commands: +1. AT+CIPSENDBUF: write data into TCP-send-buffer; non-blocking. Background task automatically handles transmission. Has much higher throughput. +2. AT+CIPBUFRESET: resets segment count in TCP-send-buffer +3. AT+CIPBUFSTATUS: checks status of TCP-send-buffer +4. AT+CIPCHECKSEGID: checks if a specific segment in TCP-send-buffer has sent successfully @@ -396,7 +454,7 @@ esp_iot_sdk_v1.0.1_b2_15_04_10 release note ------------------------------------------- Fix bugs: -1.Call espconn_sent to send UDP packet in user_init cause reset.[BBP#2 reporter (????)] +1.Call espconn_sent to send UDP packet in user_init cause reset.[BBP#2 reporter (????)] 2.UART & FlowControl issue: send data to FIFO without CTS flag will cause WDT [BBP#11 reporter (pvxx)] 3.SSL issue,add an API (espconn_secure_set_size) to set SSL buffer size [BBP#29 reporter (PeteW)] 4.UDP broadcast issue in WEP @@ -485,21 +543,21 @@ Add AT commands: XXX_DEF: default, set configuration and save it to Flash 2. Add SmartConfig in AT: AT+CWSTARTSMART/AT+CWSTOPSMART: start / stop SmartConfig - Notice: please refer to the document, call "AT+CWSTOPSMART" to stop SmartConfig first since "AT+CWSTARTSMART", then call other AT commands. Don't call any other AT commands during SmartConfig. + Notice: please refer to the document, call "AT+CWSTOPSMART" to stop SmartConfig first since "AT+CWSTARTSMART", then call other AT commands. Don't call any other AT commands during SmartConfig. 2. AT+SAVETRANSLINK: save transparent transmission link to Flash; Note:AT+CIPMODE=1 set to enter transparent transmission mode, won't save to Flash. Add AT APIs -1. at_customLinkMax: set the max link that allowed, most can be 10; if you want to set it, please set it before at_init; if you didn't set it, the max link allowed is 5 by default. -2. at_enter_special_state/ at_leave_special_state:Enter/leave AT processing state. In processing state, AT core will return "busy" for any further AT commands. +1. at_customLinkMax: set the max link that allowed, most can be 10; if you want to set it, please set it before at_init; if you didn't set it, the max link allowed is 5 by default. +2. at_enter_special_state/ at_leave_special_state:Enter/leave AT processing state. In processing state, AT core will return "busy" for any further AT commands. 3. at_set_custom_info:set custom version information of AT which can be got by AT+GMR; 4. at_get_version:get version information of AT lib . Optimize 1. Add UDP remote ip and remote port is allowed to be parameters of "AT+CIPSEND" 2. Move "AT+CIUPDATE" from lib to AT "demo\esp_iot_sdk\examples\at", AT demo shows how to upgrade AT firmware from a local server. Notice that AT upgrade the bin files name have to be "user1.bin" and "user2.bin". -3. Optimize "AT+CIPSTA", add gateway and netmask as parameters +3. Optimize "AT+CIPSTA", add gateway and netmask as parameters 4. Optimize transparent transmission. esp_iot_sdk_v0.9.5_15_01_22 Release Note @@ -532,7 +590,7 @@ Optimize: 6. update to boot v1.2 to support new format user.bin; 7. update ARP 8. update SSL -9. revised system_deep_sleep,system_deep_sleep(0),set no wake up timer,connect a GPIO to pin RST, the chip will wake up by a falling-edge on pin RST +9. revised system_deep_sleep,system_deep_sleep(0),set no wake up timer,connect a GPIO to pin RST, the chip will wake up by a falling-edge on pin RST esp_iot_sdk_v0.9.4_14_12_19 Release Note ---------------------------------------- diff --git a/tools/sdk/include/espconn.h b/tools/sdk/include/espconn.h index 82212103b..fc4be05c5 100644 --- a/tools/sdk/include/espconn.h +++ b/tools/sdk/include/espconn.h @@ -24,7 +24,7 @@ typedef void (* espconn_reconnect_callback)(void *arg, sint8 err); #define ESPCONN_ISCONN -15 /* Already connected. */ #define ESPCONN_HANDSHAKE -28 /* ssl handshake failed */ -#define ESPCONN_PROTO_MSG -61 /* ssl application invalid */ +#define ESPCONN_SSL_INVALID_DATA -61 /* ssl application invalid */ /** Protocol family and type of the espconn */ enum espconn_type { @@ -267,6 +267,17 @@ sint8 espconn_regist_sentcb(struct espconn *espconn, espconn_sent_callback sent_ sint8 espconn_regist_write_finish(struct espconn *espconn, espconn_connect_callback write_finish_fn); +/****************************************************************************** + * FunctionName : espconn_send + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ + +sint8 espconn_send(struct espconn *espconn, uint8 *psent, uint16 length); + /****************************************************************************** * FunctionName : espconn_sent * Description : sent data for client or server @@ -426,6 +437,17 @@ sint8 espconn_secure_connect(struct espconn *espconn); sint8 espconn_secure_disconnect(struct espconn *espconn); +/****************************************************************************** + * FunctionName : espconn_secure_send + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ + +sint8 espconn_secure_send(struct espconn *espconn, uint8 *psent, uint16 length); + /****************************************************************************** * FunctionName : espconn_encry_sent * Description : sent data for client or server diff --git a/tools/sdk/include/espnow.h b/tools/sdk/include/espnow.h index 8eeb65c12..3bb6f8339 100644 --- a/tools/sdk/include/espnow.h +++ b/tools/sdk/include/espnow.h @@ -13,12 +13,16 @@ enum esp_now_role { ESP_NOW_ROLE_MAX, }; -typedef void (*esp_now_cb_t)(u8 *mac_addr, u8 *data, u8 len); +typedef void (*esp_now_recv_cb_t)(u8 *mac_addr, u8 *data, u8 len); +typedef void (*esp_now_send_cb_t)(u8 *mac_addr, u8 status); int esp_now_init(void); int esp_now_deinit(void); -int esp_now_register_recv_cb(esp_now_cb_t cb); +int esp_now_register_send_cb(esp_now_send_cb_t cb); +int esp_now_unregister_send_cb(void); + +int esp_now_register_recv_cb(esp_now_recv_cb_t cb); int esp_now_unregister_recv_cb(void); int esp_now_send(u8 *da, u8 *data, int len); @@ -44,4 +48,6 @@ int esp_now_is_peer_exist(u8 *mac_addr); int esp_now_get_cnt_info(u8 *all_cnt, u8 *encrypt_cnt); +int esp_now_set_kok(u8 *key, u8 len); + #endif diff --git a/tools/sdk/include/ets_sys.h b/tools/sdk/include/ets_sys.h index 607d601f1..526a24d40 100644 --- a/tools/sdk/include/ets_sys.h +++ b/tools/sdk/include/ets_sys.h @@ -65,8 +65,8 @@ inline bool ETS_INTR_WITHINISR() { uint32_t ps; __asm__ __volatile__("rsr %0,ps":"=a" (ps)); - // PS.EXCM bit check - return ((ps & (1 << 4)) != 0); + // PS.INTLEVEL check + return ((ps & 0x0f) != 0); } inline uint32_t ETS_INTR_ENABLED(void) diff --git a/tools/sdk/include/gpio.h b/tools/sdk/include/gpio.h index bebd2001b..abcc4bed3 100644 --- a/tools/sdk/include/gpio.h +++ b/tools/sdk/include/gpio.h @@ -23,7 +23,7 @@ typedef enum { } GPIO_INT_TYPE; #define GPIO_OUTPUT_SET(gpio_no, bit_value) \ - gpio_output_set(bit_value<>gpio_no)&BIT0) diff --git a/tools/sdk/include/json/json.h b/tools/sdk/include/json/json.h old mode 100755 new mode 100644 diff --git a/tools/sdk/include/json/jsonparse.h b/tools/sdk/include/json/jsonparse.h old mode 100755 new mode 100644 diff --git a/tools/sdk/include/json/jsontree.h b/tools/sdk/include/json/jsontree.h old mode 100755 new mode 100644 diff --git a/tools/sdk/include/upgrade.h b/tools/sdk/include/upgrade.h old mode 100755 new mode 100644 index 3b6bb70ff..cddf8397c --- a/tools/sdk/include/upgrade.h +++ b/tools/sdk/include/upgrade.h @@ -2,6 +2,7 @@ #define __UPGRADE_H__ #define SPI_FLASH_SEC_SIZE 4096 +#define LIMIT_ERASE_SIZE 0x10000 #define USER_BIN1 0x00 #define USER_BIN2 0x01 @@ -37,14 +38,12 @@ struct upgrade_server_info { #define UPGRADE_FLAG_START 0x01 #define UPGRADE_FLAG_FINISH 0x02 -//bool system_upgrade_start(struct upgrade_server_info *server); -bool system_upgrade_start_ssl(struct upgrade_server_info *server); void system_upgrade_init(); void system_upgrade_deinit(); bool system_upgrade(uint8 *data, uint16 len); #ifdef UPGRADE_SSL_ENABLE -bool system_upgrade_start_ssl(struct upgrade_server_info *server); +bool system_upgrade_start_ssl(struct upgrade_server_info *server); // not supported now #else bool system_upgrade_start(struct upgrade_server_info *server); #endif diff --git a/tools/sdk/include/user_interface.h b/tools/sdk/include/user_interface.h index c4ab5ce75..672ee7ad2 100644 --- a/tools/sdk/include/user_interface.h +++ b/tools/sdk/include/user_interface.h @@ -28,10 +28,11 @@ enum rst_reason { REASON_EXCEPTION_RST = 2, REASON_SOFT_WDT_RST = 3, REASON_SOFT_RESTART = 4, - REASON_DEEP_SLEEP_AWAKE = 5 + REASON_DEEP_SLEEP_AWAKE = 5, + REASON_EXT_SYS_RST = 6 }; -struct rst_info { +struct rst_info{ uint32 reason; uint32 exccause; uint32 epc1; @@ -137,6 +138,7 @@ bool system_param_load(uint16 start_sec, uint16 offset, void *param, uint16 len) void system_soft_wdt_stop(void); void system_soft_wdt_restart(void); +void system_soft_wdt_feed(void); #define NULL_MODE 0x00 #define STATION_MODE 0x01 @@ -283,6 +285,7 @@ void wifi_softap_free_station_info(void); bool wifi_softap_dhcps_start(void); bool wifi_softap_dhcps_stop(void); bool wifi_softap_set_dhcps_lease(struct dhcps_lease *please); +bool wifi_softap_get_dhcps_lease(struct dhcps_lease *please); enum dhcp_status wifi_softap_dhcps_status(void); bool wifi_softap_set_dhcps_offer_option(uint8 level, void* optarg); @@ -430,17 +433,18 @@ typedef enum wps_type { WPS_TYPE_MAX } WPS_TYPE_t; -typedef enum wps_cb_status { +enum wps_cb_status { WPS_CB_ST_SUCCESS = 0, WPS_CB_ST_FAILED, - WPS_CB_ST_TIMEOUT -} WPS_CB_STATUS_t; + WPS_CB_ST_TIMEOUT, + WPS_CB_ST_WEP, +}; bool wifi_wps_enable(WPS_TYPE_t wps_type); bool wifi_wps_disable(void); bool wifi_wps_start(void); -typedef void (*wps_st_cb_t)(WPS_CB_STATUS_t status); +typedef void (*wps_st_cb_t)(int status); bool wifi_set_wps_cb(wps_st_cb_t cb); #endif diff --git a/tools/sdk/lib/libat.a b/tools/sdk/lib/libat.a index f37884c3e..eafb08de0 100644 Binary files a/tools/sdk/lib/libat.a and b/tools/sdk/lib/libat.a differ diff --git a/tools/sdk/lib/libcrypto.a b/tools/sdk/lib/libcrypto.a index 5e2b7e31e..d24bf9f70 100644 Binary files a/tools/sdk/lib/libcrypto.a and b/tools/sdk/lib/libcrypto.a differ diff --git a/tools/sdk/lib/libespnow.a b/tools/sdk/lib/libespnow.a index ffa483b9e..f6952d361 100644 Binary files a/tools/sdk/lib/libespnow.a and b/tools/sdk/lib/libespnow.a differ diff --git a/tools/sdk/lib/libjson.a b/tools/sdk/lib/libjson.a index 65d785088..99d4e33b9 100644 Binary files a/tools/sdk/lib/libjson.a and b/tools/sdk/lib/libjson.a differ diff --git a/tools/sdk/lib/liblwip.a b/tools/sdk/lib/liblwip.a index 9c91c1ec5..180e460a7 100644 Binary files a/tools/sdk/lib/liblwip.a and b/tools/sdk/lib/liblwip.a differ diff --git a/tools/sdk/lib/liblwip_536.a b/tools/sdk/lib/liblwip_536.a index 7d8b56536..46f08d599 100644 Binary files a/tools/sdk/lib/liblwip_536.a and b/tools/sdk/lib/liblwip_536.a differ diff --git a/tools/sdk/lib/libmain.a b/tools/sdk/lib/libmain.a index 1c8a4229d..e5b1c202a 100644 Binary files a/tools/sdk/lib/libmain.a and b/tools/sdk/lib/libmain.a differ diff --git a/tools/sdk/lib/libnet80211.a b/tools/sdk/lib/libnet80211.a index d7af5f465..2051f9471 100644 Binary files a/tools/sdk/lib/libnet80211.a and b/tools/sdk/lib/libnet80211.a differ diff --git a/tools/sdk/lib/libphy.a b/tools/sdk/lib/libphy.a index 5467d6d91..0f2fdbdb1 100644 Binary files a/tools/sdk/lib/libphy.a and b/tools/sdk/lib/libphy.a differ diff --git a/tools/sdk/lib/libpp.a b/tools/sdk/lib/libpp.a index d8b550c7f..5d5b3b776 100644 Binary files a/tools/sdk/lib/libpp.a and b/tools/sdk/lib/libpp.a differ diff --git a/tools/sdk/lib/libsmartconfig.a b/tools/sdk/lib/libsmartconfig.a index f3b0d6554..7fc7b1cff 100644 Binary files a/tools/sdk/lib/libsmartconfig.a and b/tools/sdk/lib/libsmartconfig.a differ diff --git a/tools/sdk/lib/libssc.a b/tools/sdk/lib/libssc.a deleted file mode 100755 index ab02abe18..000000000 Binary files a/tools/sdk/lib/libssc.a and /dev/null differ diff --git a/tools/sdk/lib/libssl.a b/tools/sdk/lib/libssl.a index 0634025f1..5977a723b 100644 Binary files a/tools/sdk/lib/libssl.a and b/tools/sdk/lib/libssl.a differ diff --git a/tools/sdk/lib/libupgrade.a b/tools/sdk/lib/libupgrade.a index 31ad244e0..f8ca29228 100644 Binary files a/tools/sdk/lib/libupgrade.a and b/tools/sdk/lib/libupgrade.a differ diff --git a/tools/sdk/lib/libwpa.a b/tools/sdk/lib/libwpa.a index 04541c1e3..d22039e98 100644 Binary files a/tools/sdk/lib/libwpa.a and b/tools/sdk/lib/libwpa.a differ diff --git a/tools/sdk/lib/libwps.a b/tools/sdk/lib/libwps.a index 37e21ab97..ea1deebb6 100644 Binary files a/tools/sdk/lib/libwps.a and b/tools/sdk/lib/libwps.a differ diff --git a/tools/sdk/version b/tools/sdk/version index 4139f843a..e8397674e 100644 --- a/tools/sdk/version +++ b/tools/sdk/version @@ -1 +1 @@ -1.2.0_15_07_13_p4 \ No newline at end of file +1.3.0_15_08_10_p1 \ No newline at end of file