From 3e4d7c76c42f95933695065737e674c9234ec464 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Mon, 18 May 2020 21:21:50 +0200 Subject: [PATCH 01/10] Allow non-aligned PSTR() (#7275) * Allow non-aligned PSTR() * Add PSTR4() macro to first 4-bytes aligned PSTR --- cores/esp8266/umm_malloc/umm_local.h | 3 ++- .../xtensa-lx106-elf/include/sys/pgmspace.h | 22 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/cores/esp8266/umm_malloc/umm_local.h b/cores/esp8266/umm_malloc/umm_local.h index c2f05a57d..0a1ae134e 100644 --- a/cores/esp8266/umm_malloc/umm_local.h +++ b/cores/esp8266/umm_malloc/umm_local.h @@ -48,7 +48,8 @@ void ICACHE_FLASH_ATTR print_stats(int force); int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2))); -#define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR(fmt), ##__VA_ARGS__) +#define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR4(fmt), ##__VA_ARGS__) +// use PSTR4() instead of PSTR() to ensure 4-bytes alignment in Flash, whatever the default alignment of PSTR_ALIGN #endif diff --git a/tools/sdk/libc/xtensa-lx106-elf/include/sys/pgmspace.h b/tools/sdk/libc/xtensa-lx106-elf/include/sys/pgmspace.h index ce0dc78b3..6d798ce7c 100644 --- a/tools/sdk/libc/xtensa-lx106-elf/include/sys/pgmspace.h +++ b/tools/sdk/libc/xtensa-lx106-elf/include/sys/pgmspace.h @@ -31,12 +31,26 @@ extern "C" { #define PGM_VOID_P const void * #endif -// PSTR() macro modified to start on a 32-bit boundary. This adds on average -// 1.5 bytes/string, but in return memcpy_P and strcpy_P will work 4~8x faster -#ifndef PSTR +#ifndef PSTR_ALIGN + // PSTR() macro starts by default on a 32-bit boundary. This adds on average + // 1.5 bytes/string, but in return memcpy_P and strcpy_P will work 4~8x faster + // Allow users to override the alignment with PSTR_ALIGN + #define PSTR_ALIGN 4 +#endif +#ifndef PSTRN + // Multi-alignment variant of PSTR, n controls the alignment and should typically be 1 or 4 // Adapted from AVR-specific code at https://forum.arduino.cc/index.php?topic=194603.0 // Uses C attribute section instead of ASM block to allow for C language string concatenation ("x" "y" === "xy") - #define PSTR(s) (__extension__({static const char __c[] __attribute__((__aligned__(4))) __attribute__((section( "\".irom0.pstr." __FILE__ "." __STRINGIZE(__LINE__) "." __STRINGIZE(__COUNTER__) "\", \"aSM\", @progbits, 1 #"))) = (s); &__c[0];})) + #define PSTRN(s,n) (__extension__({static const char __c[] __attribute__((__aligned__(n))) __attribute__((section( "\".irom0.pstr." __FILE__ "." __STRINGIZE(__LINE__) "." __STRINGIZE(__COUNTER__) "\", \"aSM\", @progbits, 1 #"))) = (s); &__c[0];})) +#endif +#ifndef PSTR + // PSTR() uses the default alignment defined by PSTR_ALIGN + #define PSTR(s) PSTRN(s,PSTR_ALIGN) +#endif +#ifndef PSTR4 + // PSTR4() enforces 4-bytes alignment whatever the value of PSTR_ALIGN + // as required by functions like ets_strlen() or ets_memcpy() + #define PSTR4(s) PSTRN(s,4) #endif // Flash memory must be read using 32 bit aligned addresses else a processor From 7c008e31bb60caf69be18e04c5b9efbc6a3e1c6d Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Tue, 19 May 2020 05:16:11 +0200 Subject: [PATCH 02/10] Flash size reduction for mime-type (#7312) * Flash size reduction for mime-type * moving from fixed size strings to standard PROGMEM strings * adding `#define MIMETYPE_MINIMAL` to reduce the footprint to mime-types that are strictly necessary * Added MIMETYPE_MINIMAL conditionals --- .../ESP8266WebServer/src/detail/mimetable.cpp | 119 ++++++++++++------ .../ESP8266WebServer/src/detail/mimetable.h | 10 +- 2 files changed, 90 insertions(+), 39 deletions(-) diff --git a/libraries/ESP8266WebServer/src/detail/mimetable.cpp b/libraries/ESP8266WebServer/src/detail/mimetable.cpp index c4a20cc0e..8a36d0f88 100644 --- a/libraries/ESP8266WebServer/src/detail/mimetable.cpp +++ b/libraries/ESP8266WebServer/src/detail/mimetable.cpp @@ -5,48 +5,97 @@ namespace mime { -// Table of extension->MIME strings stored in PROGMEM, needs to be global due to GCC section typing rules -const Entry mimeTable[maxType] PROGMEM = +static const char kHtmlSuffix[] PROGMEM = ".html"; +static const char kHtmSuffix[] PROGMEM = ".htm"; +static const char kTxtSuffix[] PROGMEM = ".txt"; +#ifndef MIMETYPE_MINIMAL +static const char kCssSuffix[] PROGMEM = ".css"; +static const char kJsSuffix[] PROGMEM = ".js"; +static const char kJsonSuffix[] PROGMEM = ".json"; +static const char kPngSuffix[] PROGMEM = ".png"; +static const char kGifSuffix[] PROGMEM = ".gif"; +static const char kJpgSuffix[] PROGMEM = ".jpg"; +static const char kJpegSuffix[] PROGMEM = ".jpeg"; +static const char kIcoSuffix[] PROGMEM = ".ico"; +static const char kSvgSuffix[] PROGMEM = ".svg"; +static const char kTtfSuffix[] PROGMEM = ".ttf"; +static const char kOtfSuffix[] PROGMEM = ".otf"; +static const char kWoffSuffix[] PROGMEM = ".woff"; +static const char kWoff2Suffix[] PROGMEM = ".woff2"; +static const char kEotSuffix[] PROGMEM = ".eot"; +static const char kSfntSuffix[] PROGMEM = ".sfnt"; +static const char kXmlSuffix[] PROGMEM = ".xml"; +static const char kPdfSuffix[] PROGMEM = ".pdf"; +static const char kZipSuffix[] PROGMEM = ".zip"; +static const char kAppcacheSuffix[] PROGMEM = ".appcache"; +#endif // MIMETYPE_MINIMAL +static const char kGzSuffix[] PROGMEM = ".gz"; +static const char kDefaultSuffix[] PROGMEM = ""; + +static const char kHtml[] PROGMEM = "text/html"; +static const char kTxt[] PROGMEM = "text/plain"; +#ifndef MIMETYPE_MINIMAL +static const char kCss[] PROGMEM = "text/css"; +static const char kJs[] PROGMEM = "application/javascript"; +static const char kJson[] PROGMEM = "application/json"; +static const char kPng[] PROGMEM = "image/png"; +static const char kGif[] PROGMEM = "image/gif"; +static const char kJpg[] PROGMEM = "image/jpeg"; +static const char kJpeg[] PROGMEM = "image/jpeg"; +static const char kIco[] PROGMEM = "image/x-icon"; +static const char kSvg[] PROGMEM = "image/svg+xml"; +static const char kTtf[] PROGMEM = "application/x-font-ttf"; +static const char kOtf[] PROGMEM = "application/x-font-opentype"; +static const char kWoff[] PROGMEM = "application/font-woff"; +static const char kWoff2[] PROGMEM = "application/font-woff2"; +static const char kEot[] PROGMEM = "application/vnd.ms-fontobject"; +static const char kSfnt[] PROGMEM = "application/font-sfnt"; +static const char kXml[] PROGMEM = "text/xml"; +static const char kPdf[] PROGMEM = "application/pdf"; +static const char kZip[] PROGMEM = "application/zip"; +static const char kAppcache[] PROGMEM = "text/cache-manifest"; +#endif // MIMETYPE_MINIMAL +static const char kGz[] PROGMEM = "application/x-gzip"; +static const char kDefault[] PROGMEM = "application/octet-stream"; + +const Entry mimeTable[maxType] PROGMEM = { - { ".html", "text/html" }, - { ".htm", "text/html" }, - { ".css", "text/css" }, - { ".txt", "text/plain" }, - { ".js", "application/javascript" }, - { ".json", "application/json" }, - { ".png", "image/png" }, - { ".gif", "image/gif" }, - { ".jpg", "image/jpeg" }, - { ".jpeg", "image/jpeg" }, - { ".ico", "image/x-icon" }, - { ".svg", "image/svg+xml" }, - { ".ttf", "application/x-font-ttf" }, - { ".otf", "application/x-font-opentype" }, - { ".woff", "application/font-woff" }, - { ".woff2", "application/font-woff2" }, - { ".eot", "application/vnd.ms-fontobject" }, - { ".sfnt", "application/font-sfnt" }, - { ".xml", "text/xml" }, - { ".pdf", "application/pdf" }, - { ".zip", "application/zip" }, - { ".gz", "application/x-gzip" }, - { ".appcache", "text/cache-manifest" }, - { "", "application/octet-stream" } + { kHtmlSuffix, kHtml }, + { kHtmSuffix, kHtml }, + { kTxtSuffix, kTxtSuffix }, +#ifndef MIMETYPE_MINIMAL + { kCssSuffix, kCss }, + { kJsSuffix, kJs }, + { kJsonSuffix, kJson }, + { kPngSuffix, kPng }, + { kGifSuffix, kGif }, + { kJpgSuffix, kJpg }, + { kJpegSuffix, kJpeg }, + { kIcoSuffix, kIco }, + { kSvgSuffix, kSvg }, + { kTtfSuffix, kTtf }, + { kOtfSuffix, kOtf }, + { kWoffSuffix, kWoff }, + { kWoff2Suffix, kWoff2 }, + { kEotSuffix, kEot }, + { kSfntSuffix, kSfnt }, + { kXmlSuffix, kXml }, + { kPdfSuffix, kPdf }, + { kZipSuffix, kZip }, + { kAppcacheSuffix, kAppcache }, +#endif // MIMETYPE_MINIMAL + { kGzSuffix, kGz }, + { kDefaultSuffix, kDefault } }; String getContentType(const String& path) { - char buff[sizeof(mimeTable[0].mimeType)]; - // Check all entries but last one for match, return if found - for (size_t i=0; i < sizeof(mimeTable)/sizeof(mimeTable[0])-1; i++) { - strcpy_P(buff, mimeTable[i].endsWith); - if (path.endsWith(buff)) { - strcpy_P(buff, mimeTable[i].mimeType); - return String(buff); + for (size_t i = 0; i < maxType; i++) { + if (path.endsWith(FPSTR(mimeTable[i].endsWith))) { + return String(FPSTR(mimeTable[i].mimeType)); } } // Fall-through and just return default type - strcpy_P(buff, mimeTable[sizeof(mimeTable)/sizeof(mimeTable[0])-1].mimeType); - return String(buff); + return String(FPSTR(kDefault)); } } diff --git a/libraries/ESP8266WebServer/src/detail/mimetable.h b/libraries/ESP8266WebServer/src/detail/mimetable.h index 6e6a4e963..db66ebb4a 100644 --- a/libraries/ESP8266WebServer/src/detail/mimetable.h +++ b/libraries/ESP8266WebServer/src/detail/mimetable.h @@ -10,8 +10,9 @@ enum type { html, htm, - css, txt, +#ifndef MIMETYPE_MINIMAL // allow to compile with only the strict minimum of mime-types + css, js, json, png, @@ -29,16 +30,17 @@ enum type xml, pdf, zip, - gz, appcache, +#endif // MIMETYPE_MINIMAL + gz, none, maxType }; struct Entry { - const char endsWith[16]; - const char mimeType[32]; + const char * endsWith; + const char * mimeType; }; From 576271227780b553ec70e6809b08297cf953e6d7 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Mon, 18 May 2020 20:48:18 -0700 Subject: [PATCH 03/10] Bring SPIFFS up to latest version (#7276) Manually merged the PATCH files from upstream up until Jan 26, 2020. Looks to only fix a single bug related to reading past the end of a file in certain cases. Other new features (secure erase) are included to make it easier to merge any future releases, but are not enabled in the core. Merges included: 20fc6df0ab2bee391961b5f823a327887abc96b4 da1cf494796d68bb5c02ba70cf78a904db08a170 13935985cdde6d6b4ec77f2685264242ee55e7ac ec68ba8208d7550860e4e78299d58a529b88bf85 f59d958e0b2f169b549e5cdc293a70bc6873cd45 f7d3e9f2b207958dfc2c01cf3fc42f98a4b9b239 554b59c147d96223ccce18374989f191f7222b45 07e013056dd976d12ae36db5c54a275e1497f6c8 d0d44c4908efff7a443ed9b1174d9173fb784a72 3cb24dbfd32a396dca46a7ca56a8e75bdcb97078 8172b40813ff36aa0df4c88a6e9c76e5300d7e32 --- cores/esp8266/spiffs/README.md | 3 +++ cores/esp8266/spiffs/spiffs_check.cpp | 23 ++++++++++++++++++----- cores/esp8266/spiffs/spiffs_config.h | 21 +++++++++++++++++++++ cores/esp8266/spiffs/spiffs_nucleus.cpp | 25 ++++++++++++++++++++----- cores/esp8266/spiffs/spiffs_nucleus.h | 8 ++++---- 5 files changed, 66 insertions(+), 14 deletions(-) diff --git a/cores/esp8266/spiffs/README.md b/cores/esp8266/spiffs/README.md index 64ffcc36b..e75ec5c30 100644 --- a/cores/esp8266/spiffs/README.md +++ b/cores/esp8266/spiffs/README.md @@ -49,6 +49,9 @@ What spiffs does not: - Presently, it does not detect or handle bad blocks. - One configuration, one binary. There's no generic spiffs binary that handles all types of configurations. +## NOTICE + +0.4.0 is under construction. This is a full rewrite and will change the underlying structure. Hence, it will not be compatible with earlier versions of the filesystem. The API is the same, with minor modifications. Some config flags will be removed (as they are mandatory in 0.4.0) and some features might fall away until 0.4.1. If you have any worries or questions, it can be discussed in issue [#179](https://github.com/pellepl/spiffs/issues/179) ## MORE INFO diff --git a/cores/esp8266/spiffs/spiffs_check.cpp b/cores/esp8266/spiffs/spiffs_check.cpp index 258efb30c..73477ed6a 100644 --- a/cores/esp8266/spiffs/spiffs_check.cpp +++ b/cores/esp8266/spiffs/spiffs_check.cpp @@ -163,11 +163,17 @@ static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) { return SPIFFS_OK; } SPIFFS_CHECK_RES(res); - u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE; + u8_t flags = 0xff; +#if SPIFFS_NO_BLIND_WRITES + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); +#endif + flags &= ~SPIFFS_PH_FLAG_IXDELE; res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&flags); + sizeof(flags), &flags); return res; } @@ -425,10 +431,17 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // just finalize SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n"); 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; + u8_t flags = 0xff; +#if SPIFFS_NO_BLIND_WRITES + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); +#endif + flags &= ~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), - sizeof(u8_t), (u8_t*)&flags); + sizeof(flags), &flags); } } } diff --git a/cores/esp8266/spiffs/spiffs_config.h b/cores/esp8266/spiffs/spiffs_config.h index c3343fe2f..c0d87c47b 100644 --- a/cores/esp8266/spiffs/spiffs_config.h +++ b/cores/esp8266/spiffs/spiffs_config.h @@ -326,6 +326,17 @@ typedef uint8_t u8_t; #define SPIFFS_IX_MAP 1 #endif +// By default SPIFFS in some cases relies on the property of NOR flash that bits +// cannot be set from 0 to 1 by writing and that controllers will ignore such +// bit changes. This results in fewer reads as SPIFFS can in some cases perform +// blind writes, with all bits set to 1 and only those it needs reset set to 0. +// Most of the chips and controllers allow this behavior, so the default is to +// use this technique. If your controller is one of the rare ones that don't, +// turn this option on and SPIFFS will perform a read-modify-write instead. +#ifndef SPIFFS_NO_BLIND_WRITES +#define SPIFFS_NO_BLIND_WRITES 0 +#endif + // Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function // in the api. This function will visualize all filesystem using given printf // function. @@ -354,11 +365,20 @@ typedef uint8_t u8_t; #endif #endif +#ifndef SPIFFS_SECURE_ERASE +#define SPIFFS_SECURE_ERASE 0 +#endif + // Types depending on configuration such as the amount of flash bytes // given to spiffs file system in total (spiffs_file_system_size), // the logical block size (log_block_size), and the logical page size // (log_page_size) +// +// Set SPIFFS_TYPES_OVERRIDE if you wish to have your own +// definitions for these types (for example, if you want them +// to be u32_t) +#ifndef SPIFFS_TYPES_OVERRIDE // Block index type. Make sure the size of this type can hold // the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size typedef u16_t spiffs_block_ix; @@ -373,5 +393,6 @@ typedef u16_t spiffs_obj_id; // hold the largest possible span index on the system - // i.e. (spiffs_file_system_size / log_page_size) - 1 typedef u16_t spiffs_span_ix; +#endif #endif /* SPIFFS_CONFIG_H_ */ diff --git a/cores/esp8266/spiffs/spiffs_nucleus.cpp b/cores/esp8266/spiffs/spiffs_nucleus.cpp index 35238f351..81a45f6eb 100644 --- a/cores/esp8266/spiffs/spiffs_nucleus.cpp +++ b/cores/esp8266/spiffs/spiffs_nucleus.cpp @@ -879,8 +879,6 @@ s32_t spiffs_page_delete( spiffs *fs, spiffs_page_ix pix) { s32_t res; - spiffs_page_header hdr; - hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); // mark deleted entry in source object lookup spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, @@ -893,12 +891,29 @@ s32_t spiffs_page_delete( fs->stats_p_deleted++; fs->stats_p_allocated--; +#if SPIFFS_SECURE_ERASE + // Secure erase + unsigned char data[SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)]; + bzero(data, sizeof(data)); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, + 0, + SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_header), sizeof(data), data); + SPIFFS_CHECK_RES(res); +#endif + // mark deleted in source page + u8_t flags = 0xff; +#if SPIFFS_NO_BLIND_WRITES + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); +#endif + flags &= ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&hdr.flags); + sizeof(flags), &flags); return res; } @@ -2027,7 +2042,7 @@ s32_t spiffs_object_read( // remaining data in page 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); + len_to_read = MIN(len_to_read, fd->size - cur_offset); SPIFFS_DBG("read: offset:" _SPIPRIi " rd:" _SPIPRIi " data spix:" _SPIPRIsp " is data_pix:" _SPIPRIpg " addr:" _SPIPRIad "\n", cur_offset, len_to_read, data_spix, data_pix, (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)))); if (len_to_read <= 0) { diff --git a/cores/esp8266/spiffs/spiffs_nucleus.h b/cores/esp8266/spiffs/spiffs_nucleus.h index cc195c7ec..a8b4af062 100644 --- a/cores/esp8266/spiffs/spiffs_nucleus.h +++ b/cores/esp8266/spiffs/spiffs_nucleus.h @@ -147,8 +147,8 @@ extern "C" { -#if defined(__GNUC__) || defined(__clang__) - /* For GCC and clang */ +#if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__) + /* For GCC, clang and TI compilers */ #define SPIFFS_PACKED __attribute__((packed)) #elif defined(__ICCARM__) || defined(__CC_ARM) /* For IAR ARM and Keil MDK-ARM compilers */ @@ -266,8 +266,8 @@ extern "C" { #define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0) #define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0) #else -#define SPIFFS_FH_OFFS(fs, fh) (fh) -#define SPIFFS_FH_UNOFFS(fs, fh) (fh) +#define SPIFFS_FH_OFFS(fs, fh) ((spiffs_file)(fh)) +#define SPIFFS_FH_UNOFFS(fs, fh) ((spiffs_file)(fh)) #endif From 52b3e5b7b3ccedcede665682f7896b637b64dbf5 Mon Sep 17 00:00:00 2001 From: david gauchard Date: Wed, 20 May 2020 22:18:59 +0200 Subject: [PATCH 04/10] rotate right (#7320) --- cores/esp8266/time.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp8266/time.cpp b/cores/esp8266/time.cpp index d6abbfc88..ae5a18765 100644 --- a/cores/esp8266/time.cpp +++ b/cores/esp8266/time.cpp @@ -175,7 +175,7 @@ void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, c tzr->d = 0; tzr->s = 0; tzr->change = 0; - tzr->offset = _timezone; + tzr->offset = -_timezone; } // sntp servers From 0deb87483e9e18a06e118a9d49389b2a709b2ed9 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Tue, 26 May 2020 14:00:41 -0700 Subject: [PATCH 05/10] Document ESP.getMaxFreeBlockSize() != max malloc size (#7328) Fixes #7322. Because of UMM internals, the largest `malloc()`able block will be smaller than the largest contiguous free RAM block. Note in the docs. --- doc/libraries.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/libraries.rst b/doc/libraries.rst index 8e8bdcb63..87130c702 100644 --- a/doc/libraries.rst +++ b/doc/libraries.rst @@ -87,7 +87,7 @@ Some ESP-specific APIs related to deep sleep, RTC and flash memories are availab ``ESP.getHeapFragmentation()`` returns the fragmentation metric (0% is clean, more than ~50% is not harmless) -``ESP.getMaxFreeBlockSize()`` returns the maximum allocatable ram block regarding heap fragmentation +``ESP.getMaxFreeBlockSize()`` returns the largest contiguous free RAM block in the heap, useful for checking heap fragmentation. **NOTE:** Maximum ``malloc()``able block will be smaller due to memory manager overheads. ``ESP.getChipId()`` returns the ESP8266 chip ID as a 32-bit integer. From 51daecc236fbff0d1ed30a9c63a3af6e8b5c7392 Mon Sep 17 00:00:00 2001 From: Mike Nix Date: Wed, 27 May 2020 08:04:49 +0800 Subject: [PATCH 06/10] Xmc flash 2 (#7317) * Remove unnecessary XMC support from eboot eboot is always run with the flash access speed set to 20MHz, so there is no need for special treatment of XMC chips. * After eboot copies the new firmware into place, verify the copy. If the data written to flash is as expected, the line cmp:0 will be displayed after the usual @cp:0 from eboot. * Disable interrupts during the precached part of _SPICommand() For some reason this was an issue during the reboot after an OTA update. --- bootloaders/eboot/eboot.c | 98 +++++++---------------- bootloaders/eboot/eboot.elf | Bin 34780 -> 35800 bytes cores/esp8266/core_esp8266_spi_utils.cpp | 10 ++- 3 files changed, 38 insertions(+), 70 deletions(-) diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index edbd92aa5..be67b0b6f 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -12,7 +12,6 @@ #include #include "flash.h" #include "eboot_command.h" -#include "spi_vendors.h" #include extern unsigned char _gzip_dict; @@ -115,10 +114,12 @@ int uzlib_flash_read_cb(struct uzlib_uncomp *m) } unsigned char gzip_dict[32768]; +uint8_t buffer2[FLASH_SECTOR_SIZE]; // no room for this on the stack int copy_raw(const uint32_t src_addr, const uint32_t dst_addr, - const uint32_t size) + const uint32_t size, + const bool verify) { // require regions to be aligned if ((src_addr & 0xfff) != 0 || @@ -158,8 +159,10 @@ int copy_raw(const uint32_t src_addr, gzip = true; } while (left > 0) { - if (SPIEraseSector(daddr/buffer_size)) { - return 2; + if (!verify) { + if (SPIEraseSector(daddr/buffer_size)) { + return 2; + } } if (!gzip) { if (SPIRead(saddr, buffer, buffer_size)) { @@ -179,8 +182,17 @@ int copy_raw(const uint32_t src_addr, buffer[i] = 0xff; } } - if (SPIWrite(daddr, buffer, buffer_size)) { - return 4; + if (verify) { + if (SPIRead(daddr, buffer2, buffer_size)) { + return 4; + } + if (memcmp(buffer, buffer2, buffer_size)) { + return 9; + } + } else { + if (SPIWrite(daddr, buffer, buffer_size)) { + return 4; + } } saddr += buffer_size; daddr += buffer_size; @@ -190,29 +202,6 @@ int copy_raw(const uint32_t src_addr, return 0; } -//#define XMC_SUPPORT -#ifdef XMC_SUPPORT -// Define a few SPI0 registers we need access to -#define ESP8266_REG(addr) *((volatile uint32_t *)(0x60000000+(addr))) -#define SPI0CMD ESP8266_REG(0x200) -#define SPI0CLK ESP8266_REG(0x218) -#define SPI0C ESP8266_REG(0x208) -#define SPI0W0 ESP8266_REG(0x240) - -#define SPICMDRDID (1 << 28) - -/* spi_flash_get_id() - Returns the flash chip ID - same as the SDK function. - We need our own version as the SDK isn't available here. - */ -uint32_t __attribute__((noinline)) spi_flash_get_id() { - SPI0W0=0; - SPI0CMD=SPICMDRDID; - while (SPI0CMD) {} - return SPI0W0; -} -#endif // XMC_SUPPORT - int main() { int res = 9; @@ -235,47 +224,20 @@ int main() if (cmd.action == ACTION_COPY_RAW) { ets_putc('c'); ets_putc('p'); ets_putc(':'); -#ifdef XMC_SUPPORT - // save the flash access speed registers - uint32_t spi0clk = SPI0CLK; - uint32_t spi0c = SPI0C; - - uint32_t vendor = spi_flash_get_id() & 0x000000ff; - if (vendor == SPI_FLASH_VENDOR_XMC) { - uint32_t flashinfo=0; - if (SPIRead(0, &flashinfo, 4)) { - // failed to read the configured flash speed. - // Do not change anything, - } else { - // select an appropriate flash speed - // Register values are those used by ROM - switch ((flashinfo >> 24) & 0x0f) { - case 0x0: // 40MHz, slow to 20 - case 0x1: // 26MHz, slow to 20 - SPI0CLK = 0x00003043; - SPI0C = 0x00EAA313; - break; - case 0x2: // 20MHz, no change - break; - case 0xf: // 80MHz, slow to 26 - SPI0CLK = 0x00002002; - SPI0C = 0x00EAA202; - break; - default: - break; - } - } - } -#endif // XMC_SUPPORT ets_wdt_disable(); - res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2]); + res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2], false); ets_wdt_enable(); - -#ifdef XMC_SUPPORT - // restore the saved flash access speed registers - SPI0CLK = spi0clk; - SPI0C = spi0c; -#endif + + ets_putc('0'+res); ets_putc('\n'); + + // Verify the copy + ets_putc('c'); ets_putc('m'); ets_putc('p'); ets_putc(':'); + if (res == 0) { + ets_wdt_disable(); + res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2], true); + ets_wdt_enable(); + } + ets_putc('0'+res); ets_putc('\n'); if (res == 0) { cmd.action = ACTION_LOAD_APP; diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index f9a641c2f37fa922c86ed25b7a6ec080d810b940..60329d847e6307f2211d8c762ff1a6f68c4da0d7 100755 GIT binary patch literal 35800 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 return SPI_RESULT_ERR; @@ -69,8 +69,11 @@ _SPICommand(volatile uint32_t spiIfNum, volatile SpiFlashChip *fchip=flashchip; volatile uint32_t spicmdusr=SPICMDUSR; + uint32_t saved_ps=0; + if (!spiIfNum) { - // Only need to precache when using SPI0 + // Only need to disable interrupts and precache when using SPI0 + saved_ps = xt_rsil(15); PRECACHE_START(); Wait_SPI_Idlep((SpiFlashChip *)fchip); } @@ -116,6 +119,9 @@ _SPICommand(volatile uint32_t spiIfNum, SPIREG(SPI0C) = oldSPI0C; PRECACHE_END(); + if (!spiIfNum) { + xt_wsr_ps(saved_ps); + } return (timeout>0 ? SPI_RESULT_OK : SPI_RESULT_TIMEOUT); } From 0d43338a37378aca0fa048e5b9d72028d8ded7dc Mon Sep 17 00:00:00 2001 From: Develo Date: Fri, 29 May 2020 10:12:46 -0400 Subject: [PATCH 07/10] Add FTP client/server lib link (#7336) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6c4c2f9c2..6b085aa5c 100644 --- a/README.md +++ b/README.md @@ -135,3 +135,5 @@ ESP8266 core files are licensed under LGPL. [SoftwareSerial repo](https://github.com/plerup/espsoftwareserial) [Serial Monitor Arduino IDE plugin](https://github.com/mytrain/arduino-esp8266-serial-plugin) Original discussion [here](https://github.com/esp8266/Arduino/issues/1360), quick download [there](http://mytrain.fr/cms//images/mytrain/private/ESP8266SM.v3.zip). + +[FTP Client/Server Library](https://github.com/dplasa/FTPClientServer) From 27ef03fc057c91f66c32316eec1c5a79838a3979 Mon Sep 17 00:00:00 2001 From: Amadeus Date: Fri, 29 May 2020 19:14:45 +0100 Subject: [PATCH 08/10] Add documentation on recovering from deep sleep with WAKE_RF_DISABLED (#7338) * Clarify how to enable WiFi after DeepSleep * Add FAQ item for recovering from DeepSleep * Reference issue * Update issue URL --- doc/faq/readme.rst | 16 ++++++++++++++-- doc/libraries.rst | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/doc/faq/readme.rst b/doc/faq/readme.rst index fda5fb659..e4ff8cb79 100644 --- a/doc/faq/readme.rst +++ b/doc/faq/readme.rst @@ -136,7 +136,7 @@ For reference: Time-wait PCB state helps TCP not confusing two consecutive connections with the same (s-ip,s-port,d-ip,d-port) when the first is already closed but still -having duplicate packets lost in internet arriving later during the second. +having duplicate packets lost in internet arriving later during the second. Artificially clearing them is a workaround to help saving precious heap. The following lines are compatible with both lwIP versions: @@ -147,7 +147,7 @@ The following lines are compatible with both lwIP versions: struct tcp_pcb; extern struct tcp_pcb* tcp_tw_pcbs; extern "C" void tcp_abort (struct tcp_pcb* pcb); - + void tcpCleanup (void) { while (tcp_tw_pcbs) tcp_abort(tcp_tw_pcbs); @@ -168,3 +168,15 @@ This script is also used to manage uncommon options that are currently not available in the IDE menu. `Read more `__. + +My WiFi won't reconnect after deep sleep using ``WAKE_RF_DISABLED`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you implement deep sleep using ``WAKE_RF_DISABLED``, this forces what +appears to be a bare metal disabling of WiFi functionality, which is not +restored using ``WiFi.forceSleepWake()`` or ``WiFi.mode(WIFI_STA)``. If you need +to implement deep sleep with ``WAKE_RF_DISABLED`` and later connect to WiFi, you +will need to implement an additional (short) deep sleep using +``WAKE_RF_DEFAULT``. + +Ref. `#3072 `__ diff --git a/doc/libraries.rst b/doc/libraries.rst index 87130c702..cf860231e 100644 --- a/doc/libraries.rst +++ b/doc/libraries.rst @@ -41,7 +41,7 @@ SPI SPI library supports the entire Arduino SPI API including transactions, including setting phase (CPHA). Setting the Clock polarity (CPOL) is not supported, yet (SPI\_MODE2 and SPI\_MODE3 not working). -The usual SPI pins are: +The usual SPI pins are: - ``MOSI`` = GPIO13 - ``MISO`` = GPIO12 @@ -73,7 +73,7 @@ ESP-specific APIs Some ESP-specific APIs related to deep sleep, RTC and flash memories are available in the ``ESP`` object. -``ESP.deepSleep(microseconds, mode)`` will put the chip into deep sleep. ``mode`` is one of ``WAKE_RF_DEFAULT``, ``WAKE_RFCAL``, ``WAKE_NO_RFCAL``, ``WAKE_RF_DISABLED``. (GPIO16 needs to be tied to RST to wake from deepSleep.) The chip can sleep for at most ``ESP.deepSleepMax()`` microseconds. +``ESP.deepSleep(microseconds, mode)`` will put the chip into deep sleep. ``mode`` is one of ``WAKE_RF_DEFAULT``, ``WAKE_RFCAL``, ``WAKE_NO_RFCAL``, ``WAKE_RF_DISABLED``. (GPIO16 needs to be tied to RST to wake from deepSleep.) The chip can sleep for at most ``ESP.deepSleepMax()`` microseconds. If you implement deep sleep with ``WAKE_RF_DISABLED`` and require WiFi functionality on wake up, you will need to implement an additional ``WAKE_RF_DEFAULT`` before WiFi functionality is available. ``ESP.deepSleepInstant(microseconds, mode)`` works similarly to ``ESP.deepSleep`` but sleeps instantly without waiting for WiFi to shutdown. From 8ee67ab2b53463466fd9f035eef2c542ad9a6775 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Sat, 30 May 2020 21:47:53 -0700 Subject: [PATCH 09/10] Return FALSE on seek past EOF (#7324) Fixes #7323 While I'm not a fan, the Arduino FileSeek API online shows that a seek() past EOF should return FALSE. https://www.arduino.cc/en/Reference/FileSeek SPIFFS and SDFS obey this, but LittleFS followed the POSIX standard or allowing seeks past EOF. Update LittleFS::seek() to follow the Arduino API and add tests for it. --- libraries/LittleFS/src/LittleFS.h | 5 +++++ tests/host/fs/test_fs.inc | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/libraries/LittleFS/src/LittleFS.h b/libraries/LittleFS/src/LittleFS.h index 4d417905e..f78680f66 100644 --- a/libraries/LittleFS/src/LittleFS.h +++ b/libraries/LittleFS/src/LittleFS.h @@ -377,11 +377,16 @@ public: if (mode == SeekEnd) { offset = -offset; // TODO - this seems like its plain wrong vs. POSIX } + auto lastPos = position(); int rc = lfs_file_seek(_fs->getFS(), _getFD(), offset, (int)mode); // NB. SeekMode === LFS_SEEK_TYPES if (rc < 0) { DEBUGV("lfs_file_seek rc=%d\n", rc); return false; } + if (position() > size()) { + seek(lastPos, SeekSet); // Pretend the seek() never happened + return false; + } return true; } diff --git a/tests/host/fs/test_fs.inc b/tests/host/fs/test_fs.inc index 9a10e688f..0a2cc423f 100644 --- a/tests/host/fs/test_fs.inc +++ b/tests/host/fs/test_fs.inc @@ -174,6 +174,30 @@ TEST_CASE(TESTPRE "open(w+) truncates file", TESTPAT) REQUIRE( t == ""); } +TEST_CASE(TESTPRE "peek() returns -1 on EOF", TESTPAT) +{ + FS_MOCK_DECLARE(64, 8, 512, ""); + REQUIRE(FSTYPE.begin()); + createFile("/file1", "some text"); + auto f = FSTYPE.open("/file1", "r+"); + REQUIRE(f.seek(8)); + REQUIRE(f.peek() == 't'); + REQUIRE(f.read() == 't'); + REQUIRE(f.peek() == -1); + REQUIRE(f.read() == -1); + f.close(); +} + +TEST_CASE(TESTPRE "seek() pase EOF returns error (#7323)", TESTPAT) +{ + FS_MOCK_DECLARE(64, 8, 512, ""); + REQUIRE(FSTYPE.begin()); + createFile("/file1", "some text"); + auto f = FSTYPE.open("/file1", "r+"); + REQUIRE_FALSE(f.seek(10)); + f.close(); +} + #ifdef FS_HAS_DIRS #if FSTYPE != SDFS From c3796a4de50795840f5f226886e4e540aed20fd8 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 3 Jun 2020 02:59:16 +0200 Subject: [PATCH 10/10] Graph example (#7299) * New Graph Example * Now using isFlashInterfacePin() no define default GPIO mask. * Added info about zooming. * Adressed requested changes (boolean > bool, using esp8266::polledTimeout::periodicMs, reducing complexity) --- .../ESP8266WebServer/examples/Graph/Graph.ino | 332 +++++++++++ .../examples/Graph/data/index.htm | 527 ++++++++++++++++++ .../ESP8266WebServer/examples/Graph/readme.md | 48 ++ 3 files changed, 907 insertions(+) create mode 100644 libraries/ESP8266WebServer/examples/Graph/Graph.ino create mode 100644 libraries/ESP8266WebServer/examples/Graph/data/index.htm create mode 100644 libraries/ESP8266WebServer/examples/Graph/readme.md diff --git a/libraries/ESP8266WebServer/examples/Graph/Graph.ino b/libraries/ESP8266WebServer/examples/Graph/Graph.ino new file mode 100644 index 000000000..4f7345567 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/Graph/Graph.ino @@ -0,0 +1,332 @@ +/* + Graph - A web-based Graph display of ESP8266 data + + This file is part of the ESP8266WebServer library for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + See readme.md for more information. +*/ + +//////////////////////////////// + +// Select the FileSystem by uncommenting one of the lines below + +//#define USE_SPIFFS +#define USE_LITTLEFS +//#define USE_SDFS + +//////////////////////////////// + +#include +#include +#include +#include +#include + +#if defined USE_SPIFFS +#include +FS* fileSystem = &SPIFFS; +SPIFFSConfig fileSystemConfig = SPIFFSConfig(); +#elif defined USE_LITTLEFS +#include +FS* fileSystem = &LittleFS; +LittleFSConfig fileSystemConfig = LittleFSConfig(); +#elif defined USE_SDFS +#include +FS* fileSystem = &SDFS; +SDFSConfig fileSystemConfig = SDFSConfig(); +// fileSystemConfig.setCSPin(chipSelectPin); +#else +#error Please select a filesystem first by uncommenting one of the "#define USE_xxx" lines at the beginning of the sketch. +#endif + + +#define DBG_OUTPUT_PORT Serial + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif + +// Indicate which digital I/Os should be displayed on the chart. +// From GPIO16 to GPIO0, a '1' means the corresponding GPIO will be shown +// e.g. 0b11111000000111111 +unsigned int gpioMask; + +const char* ssid = STASSID; +const char* password = STAPSK; +const char* host = "graph"; + +ESP8266WebServer server(80); + +static const char TEXT_PLAIN[] PROGMEM = "text/plain"; +static const char FS_INIT_ERROR[] PROGMEM = "FS INIT ERROR"; +static const char FILE_NOT_FOUND[] PROGMEM = "FileNotFound"; + +//////////////////////////////// +// Utils to return HTTP codes + +void replyOK() { + server.send(200, FPSTR(TEXT_PLAIN), ""); +} + +void replyOKWithMsg(String msg) { + server.send(200, FPSTR(TEXT_PLAIN), msg); +} + +void replyNotFound(String msg) { + server.send(404, FPSTR(TEXT_PLAIN), msg); +} + +void replyBadRequest(String msg) { + DBG_OUTPUT_PORT.println(msg); + server.send(400, FPSTR(TEXT_PLAIN), msg + "\r\n"); +} + +void replyServerError(String msg) { + DBG_OUTPUT_PORT.println(msg); + server.send(500, FPSTR(TEXT_PLAIN), msg + "\r\n"); +} + +//////////////////////////////// +// Request handlers + +/* + Read the given file from the filesystem and stream it back to the client +*/ +bool handleFileRead(String path) { + DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); + + if (path.endsWith("/")) { + path += "index.htm"; + } + + String contentType = mime::getContentType(path); + + if (!fileSystem->exists(path)) { + // File not found, try gzip version + path = path + ".gz"; + } + if (fileSystem->exists(path)) { + File file = fileSystem->open(path, "r"); + if (server.streamFile(file, contentType) != file.size()) { + DBG_OUTPUT_PORT.println("Sent less data than expected!"); + } + file.close(); + return true; + } + + return false; +} + + +/* + The "Not Found" handler catches all URI not explicitely declared in code + First try to find and return the requested file from the filesystem, + and if it fails, return a 404 page with debug information +*/ +void handleNotFound() { + String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks + + if (handleFileRead(uri)) { + return; + } + + // Dump debug data + String message; + message.reserve(100); + message = F("Error: File not found\n\nURI: "); + message += uri; + message += F("\nMethod: "); + message += (server.method() == HTTP_GET) ? "GET" : "POST"; + message += F("\nArguments: "); + message += server.args(); + message += '\n'; + for (uint8_t i = 0; i < server.args(); i++) { + message += F(" NAME:"); + message += server.argName(i); + message += F("\n VALUE:"); + message += server.arg(i); + message += '\n'; + } + message += "path="; + message += server.arg("path"); + message += '\n'; + DBG_OUTPUT_PORT.print(message); + + return replyNotFound(message); +} + +void setup(void) { + //////////////////////////////// + // SERIAL INIT + DBG_OUTPUT_PORT.begin(115200); + DBG_OUTPUT_PORT.setDebugOutput(true); + DBG_OUTPUT_PORT.print('\n'); + + //////////////////////////////// + // FILESYSTEM INIT + + fileSystemConfig.setAutoFormat(false); + fileSystem->setConfig(fileSystemConfig); + bool fsOK = fileSystem->begin(); + DBG_OUTPUT_PORT.println(fsOK ? F("Filesystem initialized.") : F("Filesystem init failed!")); + + //////////////////////////////// + // PIN INIT + pinMode(4, INPUT); + pinMode(12, OUTPUT); + pinMode(13, OUTPUT); + pinMode(15, OUTPUT); + + //////////////////////////////// + // WI-FI INIT + DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + DBG_OUTPUT_PORT.print("."); + } + DBG_OUTPUT_PORT.println(""); + DBG_OUTPUT_PORT.print(F("Connected! IP address: ")); + DBG_OUTPUT_PORT.println(WiFi.localIP()); + + //////////////////////////////// + // MDNS INIT + if (MDNS.begin(host)) { + MDNS.addService("http", "tcp", 80); + DBG_OUTPUT_PORT.print(F("Open http://")); + DBG_OUTPUT_PORT.print(host); + DBG_OUTPUT_PORT.println(F(".local to open the graph page")); + } + + //////////////////////////////// + // WEB SERVER INIT + + //get heap status, analog input value and all GPIO statuses in one json call + server.on("/espData", HTTP_GET, []() { + String json; + json.reserve(88); + json = "{\"time\":"; + json += millis(); + json += ", \"heap\":"; + json += ESP.getFreeHeap(); + json += ", \"analog\":"; + json += analogRead(A0); + json += ", \"gpioMask\":"; + json += gpioMask; + json += ", \"gpioData\":"; + json += (uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16)); + json += "}"; + server.send(200, "text/json", json); + }); + + // Default handler for all URIs not defined above + // Use it to read files from filesystem + server.onNotFound(handleNotFound); + + + // Start server + server.begin(); + DBG_OUTPUT_PORT.println("HTTP server started"); + + DBG_OUTPUT_PORT.println("Please pull GPIO4 low (e.g. press button) to switch output mode:"); + DBG_OUTPUT_PORT.println(" 0 (OFF): outputs are off and hidden from chart"); + DBG_OUTPUT_PORT.println(" 1 (AUTO): outputs are rotated automatically every second"); + DBG_OUTPUT_PORT.println(" 2 (MANUAL): outputs can be toggled from the web page"); + +} + +// Return default GPIO mask, that is all I/Os except SD card ones +unsigned int defaultMask() { + unsigned int mask = 0b11111111111111111; + for (auto pin = 0; pin <= 16; pin++) { + if (isFlashInterfacePin(pin)) { + mask &= ~(1 << pin); + } + } + return mask; +} + +int rgbMode = 1; // 0=off - 1=auto - 2=manual +int rgbValue = 0; +esp8266::polledTimeout::periodicMs timeToChange(1000); +bool modeChangeRequested = false; + +void loop(void) { + server.handleClient(); + MDNS.update(); + + if (digitalRead(4) == 0) { + // button pressed + modeChangeRequested = true; + } + + // see if one second has passed since last change, otherwise stop here + if (!timeToChange) { + return; + } + + // see if a mode change was requested + if (modeChangeRequested) { + // increment mode (reset after 2) + rgbMode++; + if (rgbMode > 2) { + rgbMode = 0; + } + + modeChangeRequested = false; + } + + // act according to mode + switch (rgbMode) { + case 0: // off + gpioMask = defaultMask(); + gpioMask &= ~(1 << 12); // Hide GPIO 12 + gpioMask &= ~(1 << 13); // Hide GPIO 13 + gpioMask &= ~(1 << 15); // Hide GPIO 15 + + // reset outputs + digitalWrite(12, 0); + digitalWrite(13, 0); + digitalWrite(15, 0); + break; + + case 1: // auto + gpioMask = defaultMask(); + + // increment value (reset after 7) + rgbValue++; + if (rgbValue > 7) { + rgbValue = 0; + } + + // output new values + digitalWrite(12, rgbValue & 0b001); + digitalWrite(13, rgbValue & 0b010); + digitalWrite(15, rgbValue & 0b100); + break; + + case 2: // manual + gpioMask = defaultMask(); + + // keep outputs unchanged + break; + } +} + diff --git a/libraries/ESP8266WebServer/examples/Graph/data/index.htm b/libraries/ESP8266WebServer/examples/Graph/data/index.htm new file mode 100644 index 000000000..7eb5c1b18 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/Graph/data/index.htm @@ -0,0 +1,527 @@ + + + + + + + + + + +
+ Max number of samples:  + + . Sampling period:  + +  ms  + + + +
+
+
+
+
+
+ + diff --git a/libraries/ESP8266WebServer/examples/Graph/readme.md b/libraries/ESP8266WebServer/examples/Graph/readme.md new file mode 100644 index 000000000..8bfe09255 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/Graph/readme.md @@ -0,0 +1,48 @@ +# Graph readme + +## What is this sketch about ? +This example consists of a web page that displays misc ESP8266 information, namely values of GPIOs, ADC measurement and free heap +using http requests and a html/javascript frontend. +A similar functionality used to be hidden in previous versions of the FSBrowser example. + +## How to use it ? +1. Uncomment one of the `#define USE_xxx` directives in the sketch to select the ESP filesystem to store the index.htm file on +2. Provide the credentials of your WiFi network (search for `STASSID`) +3. Compile and upload the sketch to your ESP8266 device +4. For normal use, copy the contents of the `data` folder to the filesystem. To do so: +- for SDFS, copy that contents (not the data folder itself, just its contents) to the root of a FAT/FAT32-formated SD card connected to the SPI port of the ESP8266 +- for SPIFFS or LittleFS, please follow the instructions at https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#uploading-files-to-file-system +5. Once the data and sketch have been uploaded, access the page by pointing your browser to http://graph.local + +## What does it demonstrate ? +1. Use of the ESP8266WebServer library to return files from the ESP filesystem +2. Use of the ESP8266WebServer library to return a dynamic JSON document +3. Querying the state of ESP I/Os as well as free heap +4. Ajax calls to a JSON API of the ESP from a webpage +5. Rendering of "real-time" data in a webpage + +## Usage +- the page should start showing samples right away +- the sampling period (interval between requests to the ESP) can be selected. If the system cannot keep up with the rhythm, the interval will get longer (and the period input field will turn red to indicate it). Note that the X-axis is the time since ESP bootup, in milliseconds. +- the maximum number of samples can be selected. Warning: this uses up browser memory and power, so a large number might increase the sampling period. +- sampling can be paused or restarted, and graph can be cleared during pause +- the list of GPIOs to be displayed can be customized from Arduino code by changing the gpioMask value included in the json document +- in that list, some GPIOs can be temporarily hidden by clicking on their labels on top +- analog and heap graphs can be zoomed in using the mouse wheel. A click resets the zoom level + +## Options +This sample is "fully compatible" with the FSBrowser sample. In other words, if you copy the `espData` handler over from this sketch to the FSBrowser example sketch, and upload the index.htm page to its filesystem, you will be able to use both the FSBrowser and the graph page at the same time. + +## Dependency +The html page requires the [Chart.js](https://www.chartjs.org/) (v2.9.3 at the time of writing) library, which is loaded from a CDN, as well as its [zoom plugin](https://github.com/chartjs/chartjs-plugin-zoom/blob/master/README.md) (v0.7.7) and [hammer.js](http://hammerjs.github.io/) (v2.0.8) for gesture capture. +Consequently, internet access from your web browser is required for this app to work as-is. +If your browser has no web access (e.g. if you are connected to the ESP8266 as an access-point), you can download those three files locally and upload them along with the index.htm page, and uncomment the block at the top of the index.htm page + +## Notes +- The code in the loop is just to demonstrate that the app is working by toggling a few I/Os. +However, values have been particularly chosen to be meaningful for the [Witty Cloud](https://gregwareblog.wordpress.com/2016/01/10/esp-witty/) board, rotating colors of the RGB led. +When placed close to a reflecting area, the light sensor (LDR) of the board also shows an analog image of the RGB led power. +The button rotates mode between "RGB rotate", "RGB stop", "RGB off" (and corresponding GPIOs disappearing from the graph), . +- If you use SDFS, if your card's CS pin is not connected to the default pin (4), uncomment the `fileSystemConfig.setCSPin(chipSelectPin);` line and specify the GPIO the CS pin is connected to +- `index.htm` is the default index returned if your URL does not end with a filename (works on subfolders as well) +