mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-13 13:01:55 +03:00
Merge branch 'master' into wifi_mesh_update_2.2
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -19,3 +19,6 @@
|
||||
[submodule "tools/esptool"]
|
||||
path = tools/esptool
|
||||
url = https://github.com/espressif/esptool.git
|
||||
[submodule "tools/sdk/uzlib"]
|
||||
path = tools/sdk/uzlib
|
||||
url = https://github.com/earlephilhower/uzlib.git
|
||||
|
28
.travis.yml
28
.travis.yml
@ -24,11 +24,15 @@ jobs:
|
||||
- name: "Platformio (1)"
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/tests/platformio.sh
|
||||
install:
|
||||
- sudo apt-get install python3-pip python3-setuptools
|
||||
env:
|
||||
- BUILD_PARITY=even
|
||||
- name: "Platformio (2)"
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/tests/platformio.sh
|
||||
install:
|
||||
- sudo apt-get install python3-pip python3-setuptools
|
||||
env:
|
||||
- BUILD_PARITY=odd
|
||||
|
||||
@ -76,6 +80,18 @@ jobs:
|
||||
env:
|
||||
- BUILD_PARITY=odd
|
||||
|
||||
- name: "Mac OSX can build sketches"
|
||||
os: osx
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/tests/build.sh
|
||||
env: MACOSX=1 BUILD_PARITY=custom mod=500 rem=1
|
||||
|
||||
- name: "Windows can build sketches"
|
||||
os: windows
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/tests/build.sh
|
||||
env: WINDOWS=1 BUILD_PARITY=custom mod=500 rem=1
|
||||
|
||||
- name: "Host tests"
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/tests/ci/host_test.sh
|
||||
@ -98,18 +114,6 @@ jobs:
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/tests/buildm.sh
|
||||
|
||||
- name: "Mac OSX can build sketches"
|
||||
os: osx
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/tests/build.sh
|
||||
env: MACOSX=1 BUILD_PARITY=custom mod=500 rem=1
|
||||
|
||||
- name: "Windows can build sketches"
|
||||
os: windows
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/tests/build.sh
|
||||
env: WINDOWS=1 BUILD_PARITY=custom mod=500 rem=1
|
||||
|
||||
- name: "Boards"
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/tests/ci/build_boards.sh
|
||||
|
18
README.md
18
README.md
@ -3,7 +3,7 @@ Arduino core for ESP8266 WiFi chip
|
||||
|
||||
# Quick links
|
||||
|
||||
- [Latest release documentation](https://arduino-esp8266.readthedocs.io/en/2.5.2/)
|
||||
- [Latest release documentation](https://arduino-esp8266.readthedocs.io/en/2.6.3/)
|
||||
- [Current "git version" documentation](https://arduino-esp8266.readthedocs.io/en/latest/)
|
||||
- [Install git version](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version) ([sources](doc/installing.rst#using-git-version))
|
||||
|
||||
@ -36,7 +36,7 @@ Starting with 1.6.4, Arduino allows installation of third-party platform package
|
||||
#### Latest release [](https://github.com/esp8266/Arduino/releases/latest/)
|
||||
Boards manager link: `https://arduino.esp8266.com/stable/package_esp8266com_index.json`
|
||||
|
||||
Documentation: [https://arduino-esp8266.readthedocs.io/en/2.5.2/](https://arduino-esp8266.readthedocs.io/en/2.5.2/)
|
||||
Documentation: [https://arduino-esp8266.readthedocs.io/en/2.6.3/](https://arduino-esp8266.readthedocs.io/en/2.6.3/)
|
||||
|
||||
### Using git version
|
||||
[](https://travis-ci.org/esp8266/Arduino)
|
||||
@ -106,7 +106,7 @@ Arduino IDE is developed and maintained by the Arduino team. The IDE is licensed
|
||||
|
||||
ESP8266 core includes an xtensa gcc toolchain, which is also under GPL.
|
||||
|
||||
Esptool written by Christian Klippel is licensed under GPLv2, currently maintained by Ivan Grokhotkov: https://github.com/igrr/esptool-ck.
|
||||
Esptool.py was initially created by Fredrik Ahlberg (@themadinventor, @kongo), and is currently maintained by Angus Gratton (@projectgus) under GPL 2.0 license.
|
||||
|
||||
Espressif SDK included in this build is under Espressif MIT License.
|
||||
|
||||
@ -123,3 +123,15 @@ ESP8266 core files are licensed under LGPL.
|
||||
[BearSSL](https://bearssl.org) library written by Thomas Pornin, built from https://github.com/earlephilhower/bearssl-esp8266, is used in this project. It is distributed under the [MIT License](https://bearssl.org/#legal-details).
|
||||
|
||||
[LittleFS](https://github.com/ARMmbed/littlefs) library written by ARM Limited and released under the [BSD 3-clause license](https://github.com/ARMmbed/littlefs/blob/master/LICENSE.md).
|
||||
|
||||
[uzlib](https://github.com/pfalcon/uzlib) library written and (c) 2014-2018 Paul Sokolovsky, licensed under the ZLib license (https://www.zlib.net/zlib_license.html). uzlib is based on: tinf library by Joergen Ibsen (Deflate decompression); Deflate Static Huffman tree routines by Simon Tatham; LZ77 compressor by Paul Sokolovsky; with library integrated and maintained by Paul Sokolovsky.
|
||||
|
||||
### Other useful links ###
|
||||
|
||||
[Toolchain repo](https://github.com/earlephilhower/esp-quick-toolchain)
|
||||
|
||||
[Lwip link layer repo](https://github.com/d-a-v/esp82xx-nonos-linklayer)
|
||||
|
||||
[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).
|
||||
|
1119
boards.txt
1119
boards.txt
File diff suppressed because it is too large
Load Diff
@ -6,25 +6,30 @@ TARGET_DIR := ./
|
||||
|
||||
TARGET_OBJ_FILES := \
|
||||
eboot.o \
|
||||
eboot_command.o \
|
||||
|
||||
eboot_command.o
|
||||
|
||||
TARGET_OBJ_PATHS := $(addprefix $(TARGET_DIR)/,$(TARGET_OBJ_FILES))
|
||||
|
||||
UZLIB_PATH := ../../tools/sdk/uzlib/src
|
||||
UZLIB_FLAGS := -DRUNTIME_BITS_TABLES
|
||||
|
||||
CC := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-gcc
|
||||
CXX := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-g++
|
||||
AR := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-ar
|
||||
LD := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-gcc
|
||||
OBJDUMP := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-objdump
|
||||
|
||||
INC += -I../../tools/sdk/include
|
||||
INC += -I../../tools/sdk/include -I../../tools/sdk/uzlib/src
|
||||
|
||||
CFLAGS += -std=gnu99
|
||||
|
||||
CFLAGS += -O0 -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mno-text-section-literals
|
||||
CFLAGS += -Os -g -Wall -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mno-text-section-literals -ffunction-sections -fdata-sections
|
||||
|
||||
CFLAGS += $(INC)
|
||||
|
||||
LDFLAGS += -nostdlib -Wl,--no-check-sections -umain
|
||||
CFLAGS += $(UZLIB_FLAGS)
|
||||
|
||||
LDFLAGS += -nostdlib -Wl,--no-check-sections -Wl,--gc-sections -umain -Wl,-Map,$(@:.elf=.map)
|
||||
|
||||
LD_SCRIPT := -Teboot.ld
|
||||
|
||||
@ -32,23 +37,26 @@ APP_OUT:= eboot.elf
|
||||
APP_AR := eboot.a
|
||||
APP_FW := eboot.bin
|
||||
|
||||
all: $(APP_FW)
|
||||
|
||||
$(APP_AR): $(TARGET_OBJ_PATHS)
|
||||
all: $(APP_OUT)
|
||||
|
||||
tinflate.o: $(UZLIB_PATH)/tinflate.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h
|
||||
$(CC) $(CFLAGS) -c -o tinflate.o $(UZLIB_PATH)/tinflate.c
|
||||
|
||||
tinfgzip.o: $(UZLIB_PATH)/tinfgzip.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h
|
||||
$(CC) $(CFLAGS) -c -o tinfgzip.o $(UZLIB_PATH)/tinfgzip.c
|
||||
|
||||
$(APP_AR): $(TARGET_OBJ_PATHS) tinflate.o tinfgzip.o
|
||||
$(AR) cru $@ $^
|
||||
|
||||
|
||||
$(APP_OUT): $(APP_AR)
|
||||
$(APP_OUT): $(APP_AR) eboot.ld | Makefile
|
||||
$(LD) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group -Wl,--whole-archive $(APP_AR) -Wl,--end-group -o $@
|
||||
|
||||
$(APP_FW): $(APP_OUT)
|
||||
$(ESPTOOL) -vvv -eo $(APP_OUT) -bo $@ -bs .text -bs .data -bs .rodata -bc -ec || true
|
||||
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f $(APP_AR)
|
||||
rm -f $(APP_OUT)
|
||||
rm -f *.map
|
||||
|
||||
|
||||
.PHONY: all clean default
|
||||
|
@ -12,6 +12,9 @@
|
||||
#include <string.h>
|
||||
#include "flash.h"
|
||||
#include "eboot_command.h"
|
||||
#include <uzlib.h>
|
||||
|
||||
extern unsigned char _gzip_dict;
|
||||
|
||||
#define SWRST do { (*((volatile uint32_t*) 0x60000700)) |= 0x80000000; } while(0);
|
||||
|
||||
@ -24,10 +27,14 @@ int print_version(const uint32_t flash_addr)
|
||||
if (SPIRead(flash_addr + APP_START_OFFSET + sizeof(image_header_t) + sizeof(section_header_t), &ver, sizeof(ver))) {
|
||||
return 1;
|
||||
}
|
||||
const char* __attribute__ ((aligned (4))) fmtt = "v%08x\n\0\0";
|
||||
uint32_t fmt[2];
|
||||
fmt[0] = ((uint32_t*) fmtt)[0];
|
||||
fmt[1] = ((uint32_t*) fmtt)[1];
|
||||
char fmt[7];
|
||||
fmt[0] = 'v';
|
||||
fmt[1] = '%';
|
||||
fmt[2] = '0';
|
||||
fmt[3] = '8';
|
||||
fmt[4] = 'x';
|
||||
fmt[5] = '\n';
|
||||
fmt[6] = 0;
|
||||
ets_printf((const char*) fmt, ver);
|
||||
return 0;
|
||||
}
|
||||
@ -80,38 +87,97 @@ int load_app_from_flash_raw(const uint32_t flash_addr)
|
||||
pos += section_header.size;
|
||||
}
|
||||
|
||||
register uint32_t sp asm("a1") = 0x3ffffff0;
|
||||
register uint32_t pc asm("a3") = image_header.entry;
|
||||
__asm__ __volatile__ ("jx a3");
|
||||
asm volatile("" ::: "memory");
|
||||
asm volatile ("mov.n a1, %0\n"
|
||||
"mov.n a3, %1\n"
|
||||
"jx a3\n" : : "r" (0x3ffffff0), "r" (image_header.entry) );
|
||||
|
||||
__builtin_unreachable(); // Save a few bytes by letting GCC know no need to pop regs/return
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t read_flash_byte(const uint32_t addr)
|
||||
{
|
||||
uint8_t __attribute__((aligned(4))) buff[4];
|
||||
SPIRead(addr & ~3, buff, 4);
|
||||
return buff[addr & 3];
|
||||
}
|
||||
unsigned char __attribute__((aligned(4))) uzlib_flash_read_cb_buff[4096];
|
||||
uint32_t uzlib_flash_read_cb_addr;
|
||||
int uzlib_flash_read_cb(struct uzlib_uncomp *m)
|
||||
{
|
||||
m->source = uzlib_flash_read_cb_buff;
|
||||
m->source_limit = uzlib_flash_read_cb_buff + sizeof(uzlib_flash_read_cb_buff);
|
||||
SPIRead(uzlib_flash_read_cb_addr, uzlib_flash_read_cb_buff, sizeof(uzlib_flash_read_cb_buff));
|
||||
uzlib_flash_read_cb_addr += sizeof(uzlib_flash_read_cb_buff);
|
||||
return *(m->source++);
|
||||
}
|
||||
|
||||
unsigned char gzip_dict[32768];
|
||||
|
||||
int copy_raw(const uint32_t src_addr,
|
||||
const uint32_t dst_addr,
|
||||
const uint32_t size)
|
||||
{
|
||||
// require regions to be aligned
|
||||
if (src_addr & 0xfff != 0 ||
|
||||
dst_addr & 0xfff != 0) {
|
||||
if ((src_addr & 0xfff) != 0 ||
|
||||
(dst_addr & 0xfff) != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const uint32_t buffer_size = FLASH_SECTOR_SIZE;
|
||||
uint8_t buffer[buffer_size];
|
||||
uint32_t left = ((size+buffer_size-1) & ~(buffer_size-1));
|
||||
int32_t left = ((size+buffer_size-1) & ~(buffer_size-1));
|
||||
uint32_t saddr = src_addr;
|
||||
uint32_t daddr = dst_addr;
|
||||
struct uzlib_uncomp m_uncomp;
|
||||
bool gzip = false;
|
||||
|
||||
while (left) {
|
||||
// Check if we are uncompressing a GZIP upload or not
|
||||
if ((read_flash_byte(saddr) == 0x1f) && (read_flash_byte(saddr + 1) == 0x8b)) {
|
||||
// GZIP signature matched. Find real size as encoded at the end
|
||||
left = read_flash_byte(saddr + size - 4);
|
||||
left += read_flash_byte(saddr + size - 3)<<8;
|
||||
left += read_flash_byte(saddr + size - 2)<<16;
|
||||
left += read_flash_byte(saddr + size - 1)<<24;
|
||||
|
||||
uzlib_init();
|
||||
|
||||
/* all 3 fields below must be initialized by user */
|
||||
m_uncomp.source = NULL;
|
||||
m_uncomp.source_limit = NULL;
|
||||
uzlib_flash_read_cb_addr = src_addr;
|
||||
m_uncomp.source_read_cb = uzlib_flash_read_cb;
|
||||
uzlib_uncompress_init(&m_uncomp, gzip_dict, sizeof(gzip_dict));
|
||||
|
||||
int res = uzlib_gzip_parse_header(&m_uncomp);
|
||||
if (res != TINF_OK) {
|
||||
return 5; // Error uncompress header read
|
||||
}
|
||||
gzip = true;
|
||||
}
|
||||
while (left > 0) {
|
||||
if (SPIEraseSector(daddr/buffer_size)) {
|
||||
return 2;
|
||||
}
|
||||
if (!gzip) {
|
||||
if (SPIRead(saddr, buffer, buffer_size)) {
|
||||
return 3;
|
||||
}
|
||||
} else {
|
||||
m_uncomp.dest_start = buffer;
|
||||
m_uncomp.dest = buffer;
|
||||
int to_read = (left > buffer_size) ? buffer_size : left;
|
||||
m_uncomp.dest_limit = buffer + to_read;
|
||||
int res = uzlib_uncompress(&m_uncomp);
|
||||
if ((res != TINF_DONE) && (res != TINF_OK)) {
|
||||
return 6;
|
||||
}
|
||||
// Fill any remaining with 0xff
|
||||
for (int i = to_read; i < buffer_size; i++) {
|
||||
buffer[i] = 0xff;
|
||||
}
|
||||
}
|
||||
if (SPIWrite(daddr, buffer, buffer_size)) {
|
||||
return 4;
|
||||
}
|
||||
@ -124,17 +190,17 @@ int copy_raw(const uint32_t src_addr,
|
||||
}
|
||||
|
||||
|
||||
|
||||
void main()
|
||||
int main()
|
||||
{
|
||||
int res = 9;
|
||||
bool clear_cmd = false;
|
||||
struct eboot_command cmd;
|
||||
|
||||
print_version(0);
|
||||
|
||||
if (eboot_command_read(&cmd) == 0) {
|
||||
// valid command was passed via RTC_MEM
|
||||
eboot_command_clear();
|
||||
clear_cmd = true;
|
||||
ets_putc('@');
|
||||
} else {
|
||||
// no valid command found
|
||||
@ -155,6 +221,10 @@ void main()
|
||||
}
|
||||
}
|
||||
|
||||
if (clear_cmd) {
|
||||
eboot_command_clear();
|
||||
}
|
||||
|
||||
if (cmd.action == ACTION_LOAD_APP) {
|
||||
ets_putc('l'); ets_putc('d'); ets_putc('\n');
|
||||
res = load_app_from_flash_raw(cmd.args[0]);
|
||||
@ -167,4 +237,7 @@ void main()
|
||||
}
|
||||
|
||||
while(true){}
|
||||
|
||||
__builtin_unreachable();
|
||||
return 0;
|
||||
}
|
||||
|
Binary file not shown.
@ -68,6 +68,8 @@ SECTIONS
|
||||
|
||||
.data : ALIGN(4)
|
||||
{
|
||||
*(COMMON) /* Global vars */
|
||||
. = ALIGN(4);
|
||||
_heap_start = ABSOLUTE(.);
|
||||
/* _stack_sentry = ALIGN(0x8); */
|
||||
} >dram0_0_seg :dram0_0_bss_phdr
|
||||
@ -150,9 +152,26 @@ SECTIONS
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN (8);
|
||||
_bss_end = ABSOLUTE(.);
|
||||
_free_space = 4096 - 17 - (. - _stext);
|
||||
/*
|
||||
The boot loader checksum must be before the CRC, which is written by elf2bin.py.
|
||||
This leaves 16 bytes after the checksum for the CRC placed at the end of the
|
||||
4096-byte sector. */
|
||||
_cs_here = (ALIGN((. + 1), 16) == ALIGN(16)) ? (ALIGN(16) - 1) : (. + 0x0F);
|
||||
|
||||
/*
|
||||
The filling (padding) and values for _crc_size and _crc_val are handled by
|
||||
elf2bin.py. With this, we give values to the symbols without explicitly
|
||||
assigning space. This avoids the linkers back *fill* operation that causes
|
||||
trouble.
|
||||
|
||||
The CRC info is stored in last 8 bytes. */
|
||||
_crc_size = _stext + 4096 - 8;
|
||||
_crc_val = _stext + 4096 - 4;
|
||||
ASSERT((4096 > (17 + (. - _stext))), "Error: No space for CS and CRC in bootloader sector.");
|
||||
ASSERT((_crc_size > _cs_here), "Error: CRC must be located after CS.");
|
||||
} >iram1_0_seg :iram1_0_phdr
|
||||
|
||||
.lit4 : ALIGN(4)
|
||||
|
@ -37,7 +37,7 @@ int eboot_command_read(struct eboot_command* cmd)
|
||||
}
|
||||
|
||||
uint32_t crc32 = eboot_command_calculate_crc32(cmd);
|
||||
if (cmd->magic & EBOOT_MAGIC_MASK != EBOOT_MAGIC ||
|
||||
if ((cmd->magic & EBOOT_MAGIC_MASK) != EBOOT_MAGIC ||
|
||||
cmd->crc32 != crc32) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -204,6 +204,7 @@ void setup(void);
|
||||
void loop(void);
|
||||
|
||||
void yield(void);
|
||||
|
||||
void optimistic_yield(uint32_t interval_us);
|
||||
|
||||
#define _PORT_GPIO16 1
|
||||
@ -281,6 +282,13 @@ void configTime(int timezone, int daylightOffset_sec, const char* server1,
|
||||
void configTime(const char* tz, const char* server1,
|
||||
const char* server2 = nullptr, const char* server3 = nullptr);
|
||||
|
||||
// esp32 api compatibility
|
||||
inline void configTzTime(const char* tz, const char* server1,
|
||||
const char* server2 = nullptr, const char* server3 = nullptr)
|
||||
{
|
||||
configTime(tz, server1, server2, server3);
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#include "pins_arduino.h"
|
||||
|
84
cores/esp8266/CallBackList.h
Normal file
84
cores/esp8266/CallBackList.h
Normal file
@ -0,0 +1,84 @@
|
||||
#ifndef __CALLBACKLIST_H__
|
||||
#define __CALLBACKLIST_H__
|
||||
|
||||
|
||||
/*
|
||||
CallBackList, An implemention for handling callback execution
|
||||
|
||||
Copyright (c) 2019 Herman Reintke. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
|
||||
namespace experimental
|
||||
{
|
||||
namespace CBListImplentation
|
||||
{
|
||||
|
||||
template<class cbFunctionT>
|
||||
class CallBackList
|
||||
{
|
||||
public:
|
||||
CallBackList (){};
|
||||
|
||||
struct CallBackInfo
|
||||
{
|
||||
CallBackInfo(cbFunctionT f) : cbFunction(f, true){};
|
||||
CallBackInfo(cbFunctionT f, bool ar) : cbFunction(f), _allowRemove(ar) {};
|
||||
cbFunctionT cbFunction;
|
||||
bool _allowRemove = true;
|
||||
bool allowRemove()
|
||||
{
|
||||
return _allowRemove;
|
||||
}
|
||||
};
|
||||
using CallBackHandler = std::shared_ptr<CallBackInfo> ;
|
||||
std::list<CallBackHandler> callBackEventList;
|
||||
|
||||
CallBackHandler add(cbFunctionT af, bool ad = true) {
|
||||
CallBackHandler handler = std::make_shared<CallBackInfo>(CallBackInfo(af,ad));
|
||||
callBackEventList.emplace_back(handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
void remove(CallBackHandler& dh) {
|
||||
callBackEventList.remove(dh);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void execute(Args... params) {
|
||||
for(auto it = std::begin(callBackEventList); it != std::end(callBackEventList); ) {
|
||||
CallBackHandler &handler = *it;
|
||||
if (handler->allowRemove() && handler.unique()) {
|
||||
it = callBackEventList.erase(it);
|
||||
}
|
||||
else {
|
||||
handler->cbFunction(params...);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} //CBListImplementation
|
||||
}//experimental
|
||||
|
||||
#endif // __CALLBACKLIST_H__
|
@ -28,29 +28,41 @@
|
||||
#define STRHELPER(x) #x
|
||||
#define STR(x) STRHELPER(x) // stringifier
|
||||
|
||||
static const char arduino_esp8266_git_ver [] PROGMEM = STR(ARDUINO_ESP8266_GIT_DESC);
|
||||
static const char arduino_esp8266_git_ver [] PROGMEM = "/Core:" STR(ARDUINO_ESP8266_GIT_DESC) "=";
|
||||
#if LWIP_VERSION_MAJOR > 1
|
||||
#if LWIP_IPV6
|
||||
static const char lwip_version [] PROGMEM = "/lwIP:IPv6+" LWIP_HASH_STR;
|
||||
#else
|
||||
static const char lwip_version [] PROGMEM = "/lwIP:" LWIP_HASH_STR;
|
||||
#endif
|
||||
#endif
|
||||
static const char bearssl_version [] PROGMEM = "/BearSSL:" STR(BEARSSL_GIT);
|
||||
|
||||
String EspClass::getFullVersion()
|
||||
{
|
||||
return String(F("SDK:")) + system_get_sdk_version()
|
||||
+ F("/Core:") + FPSTR(arduino_esp8266_git_ver)
|
||||
+ F("=") + String(esp8266::coreVersionNumeric())
|
||||
String EspClass::getFullVersion() {
|
||||
String s(F("SDK:"));
|
||||
s.reserve(127);
|
||||
|
||||
s += system_get_sdk_version();
|
||||
s += FPSTR(arduino_esp8266_git_ver);
|
||||
s += String(esp8266::coreVersionNumeric());
|
||||
#if LWIP_VERSION_MAJOR == 1
|
||||
+ F("/lwIP:") + String(LWIP_VERSION_MAJOR) + "." + String(LWIP_VERSION_MINOR) + "." + String(LWIP_VERSION_REVISION)
|
||||
s += F("/lwIP:");
|
||||
s += LWIP_VERSION_MAJOR;
|
||||
s += '.';
|
||||
s += LWIP_VERSION_MINOR;
|
||||
s += '.';
|
||||
s += LWIP_VERSION_REVISION;
|
||||
#if LWIP_VERSION_IS_DEVELOPMENT
|
||||
+ F("-dev")
|
||||
s += F("-dev");
|
||||
#endif
|
||||
#if LWIP_VERSION_IS_RC
|
||||
+ F("rc") + String(LWIP_VERSION_RC)
|
||||
s += F("rc");
|
||||
s += String(LWIP_VERSION_RC);
|
||||
#endif
|
||||
#else // LWIP_VERSION_MAJOR != 1
|
||||
+ F("/lwIP:")
|
||||
#if LWIP_IPV6
|
||||
+ F("IPv6+")
|
||||
#endif // LWIP_IPV6
|
||||
+ F(LWIP_HASH_STR)
|
||||
s += FPSTR(lwip_version);
|
||||
#endif // LWIP_VERSION_MAJOR != 1
|
||||
+ FPSTR(bearssl_version)
|
||||
;
|
||||
s += FPSTR(bearssl_version);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "MD5Builder.h"
|
||||
#include "umm_malloc/umm_malloc.h"
|
||||
#include "cont.h"
|
||||
#include "coredecls.h"
|
||||
|
||||
extern "C" {
|
||||
#include "user_interface.h"
|
||||
@ -447,35 +448,57 @@ bool EspClass::checkFlashConfig(bool needsEquals) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EspClass::checkFlashCRC() {
|
||||
// The CRC and total length are placed in extra space at the end of the 4K chunk
|
||||
// of flash occupied by the bootloader. If the bootloader grows to >4K-8 bytes,
|
||||
// we'll need to adjust this.
|
||||
uint32_t flashsize = *((uint32_t*)(0x40200000 + 4088)); // Start of PROGMEM plus 4K-8
|
||||
uint32_t flashcrc = *((uint32_t*)(0x40200000 + 4092)); // Start of PROGMEM plus 4K-4
|
||||
uint32_t z[2];
|
||||
z[0] = z[1] = 0;
|
||||
|
||||
// Start the checksum
|
||||
uint32_t crc = crc32((const void*)0x40200000, 4096-8, 0xffffffff);
|
||||
// Pretend the 2 words of crc/len are zero to be idempotent
|
||||
crc = crc32(z, 8, crc);
|
||||
// Finish the CRC calculation over the rest of flash
|
||||
crc = crc32((const void*)0x40201000, flashsize-4096, crc);
|
||||
return crc == flashcrc;
|
||||
}
|
||||
|
||||
|
||||
String EspClass::getResetReason(void) {
|
||||
char buff[32];
|
||||
if (resetInfo.reason == REASON_DEFAULT_RST) { // normal startup by power on
|
||||
strcpy_P(buff, PSTR("Power on"));
|
||||
} else if (resetInfo.reason == REASON_WDT_RST) { // hardware watch dog reset
|
||||
strcpy_P(buff, PSTR("Hardware Watchdog"));
|
||||
} else if (resetInfo.reason == REASON_EXCEPTION_RST) { // exception reset, GPIO status won’t change
|
||||
strcpy_P(buff, PSTR("Exception"));
|
||||
} else if (resetInfo.reason == REASON_SOFT_WDT_RST) { // software watch dog reset, GPIO status won’t change
|
||||
strcpy_P(buff, PSTR("Software Watchdog"));
|
||||
} else if (resetInfo.reason == REASON_SOFT_RESTART) { // software restart ,system_restart , GPIO status won’t change
|
||||
strcpy_P(buff, PSTR("Software/System restart"));
|
||||
} else if (resetInfo.reason == REASON_DEEP_SLEEP_AWAKE) { // wake up from deep-sleep
|
||||
strcpy_P(buff, PSTR("Deep-Sleep Wake"));
|
||||
} else if (resetInfo.reason == REASON_EXT_SYS_RST) { // external system reset
|
||||
strcpy_P(buff, PSTR("External System"));
|
||||
} else {
|
||||
strcpy_P(buff, PSTR("Unknown"));
|
||||
const __FlashStringHelper* buff;
|
||||
|
||||
switch(resetInfo.reason) {
|
||||
// normal startup by power on
|
||||
case REASON_DEFAULT_RST: buff = F("Power On"); break;
|
||||
// hardware watch dog reset
|
||||
case REASON_WDT_RST: buff = F("Hardware Watchdog"); break;
|
||||
// exception reset, GPIO status won’t change
|
||||
case REASON_EXCEPTION_RST: buff = F("Exception"); break;
|
||||
// software watch dog reset, GPIO status won’t change
|
||||
case REASON_SOFT_WDT_RST: buff = F("Software Watchdog"); break;
|
||||
// software restart ,system_restart , GPIO status won’t change
|
||||
case REASON_SOFT_RESTART: buff = F("Software/System restart"); break;
|
||||
// wake up from deep-sleep
|
||||
case REASON_DEEP_SLEEP_AWAKE: buff = F("Deep-Sleep Wake"); break;
|
||||
// // external system reset
|
||||
case REASON_EXT_SYS_RST: buff = F("External System"); break;
|
||||
default: buff = F("Unknown"); break;
|
||||
}
|
||||
return String(buff);
|
||||
}
|
||||
|
||||
String EspClass::getResetInfo(void) {
|
||||
if(resetInfo.reason != 0) {
|
||||
if (resetInfo.reason >= REASON_WDT_RST && resetInfo.reason <= REASON_SOFT_WDT_RST) {
|
||||
char buff[200];
|
||||
sprintf(&buff[0], "Fatal exception:%d flag:%d (%s) epc1:0x%08x epc2:0x%08x epc3:0x%08x excvaddr:0x%08x depc:0x%08x", resetInfo.exccause, resetInfo.reason, (resetInfo.reason == 0 ? "DEFAULT" : resetInfo.reason == 1 ? "WDT" : resetInfo.reason == 2 ? "EXCEPTION" : resetInfo.reason == 3 ? "SOFT_WDT" : resetInfo.reason == 4 ? "SOFT_RESTART" : resetInfo.reason == 5 ? "DEEP_SLEEP_AWAKE" : resetInfo.reason == 6 ? "EXT_SYS_RST" : "???"), resetInfo.epc1, resetInfo.epc2, resetInfo.epc3, resetInfo.excvaddr, resetInfo.depc);
|
||||
sprintf_P(buff, PSTR("Fatal exception:%d flag:%d (%s) epc1:0x%08x epc2:0x%08x epc3:0x%08x excvaddr:0x%08x depc:0x%08x"),
|
||||
resetInfo.exccause, resetInfo.reason, getResetReason().c_str(),
|
||||
resetInfo.epc1, resetInfo.epc2, resetInfo.epc3, resetInfo.excvaddr, resetInfo.depc);
|
||||
return String(buff);
|
||||
}
|
||||
return String("flag: 0");
|
||||
return getResetReason();
|
||||
}
|
||||
|
||||
struct rst_info * EspClass::getResetInfoPtr(void) {
|
||||
|
@ -176,6 +176,8 @@ class EspClass {
|
||||
|
||||
bool checkFlashConfig(bool needsEquals = false);
|
||||
|
||||
bool checkFlashCRC();
|
||||
|
||||
bool flashEraseSector(uint32_t sector);
|
||||
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
|
||||
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
|
||||
|
@ -180,6 +180,26 @@ String File::readString()
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_t File::getLastWrite() {
|
||||
if (!_p)
|
||||
return 0;
|
||||
|
||||
return _p->getLastWrite();
|
||||
}
|
||||
|
||||
time_t File::getCreationTime() {
|
||||
if (!_p)
|
||||
return 0;
|
||||
|
||||
return _p->getCreationTime();
|
||||
}
|
||||
|
||||
void File::setTimeCallback(time_t (*cb)(void)) {
|
||||
if (!_p)
|
||||
return;
|
||||
_p->setTimeCallback(cb);
|
||||
}
|
||||
|
||||
File Dir::openFile(const char* mode) {
|
||||
if (!_impl) {
|
||||
return File();
|
||||
@ -192,7 +212,9 @@ File Dir::openFile(const char* mode) {
|
||||
return File();
|
||||
}
|
||||
|
||||
return File(_impl->openFile(om, am), _baseFS);
|
||||
File f(_impl->openFile(om, am), _baseFS);
|
||||
f.setTimeCallback(timeCallback);
|
||||
return f;
|
||||
}
|
||||
|
||||
String Dir::fileName() {
|
||||
@ -203,6 +225,18 @@ String Dir::fileName() {
|
||||
return _impl->fileName();
|
||||
}
|
||||
|
||||
time_t Dir::fileTime() {
|
||||
if (!_impl)
|
||||
return 0;
|
||||
return _impl->fileTime();
|
||||
}
|
||||
|
||||
time_t Dir::fileCreationTime() {
|
||||
if (!_impl)
|
||||
return 0;
|
||||
return _impl->fileCreationTime();
|
||||
}
|
||||
|
||||
size_t Dir::fileSize() {
|
||||
if (!_impl) {
|
||||
return 0;
|
||||
@ -241,6 +275,14 @@ bool Dir::rewind() {
|
||||
return _impl->rewind();
|
||||
}
|
||||
|
||||
void Dir::setTimeCallback(time_t (*cb)(void)) {
|
||||
if (!_impl)
|
||||
return;
|
||||
_impl->setTimeCallback(cb);
|
||||
timeCallback = cb;
|
||||
}
|
||||
|
||||
|
||||
bool FS::setConfig(const FSConfig &cfg) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
@ -254,6 +296,7 @@ bool FS::begin() {
|
||||
DEBUGV("#error: FS: no implementation");
|
||||
return false;
|
||||
}
|
||||
_impl->setTimeCallback(timeCallback);
|
||||
bool ret = _impl->begin();
|
||||
DEBUGV("%s\n", ret? "": "#error: FS could not start");
|
||||
return ret;
|
||||
@ -315,7 +358,9 @@ File FS::open(const char* path, const char* mode) {
|
||||
DEBUGV("FS::open: invalid mode `%s`\r\n", mode);
|
||||
return File();
|
||||
}
|
||||
return File(_impl->open(path, om, am), this);
|
||||
File f(_impl->open(path, om, am), this);
|
||||
f.setTimeCallback(timeCallback);
|
||||
return f;
|
||||
}
|
||||
|
||||
bool FS::exists(const char* path) {
|
||||
@ -334,7 +379,9 @@ Dir FS::openDir(const char* path) {
|
||||
return Dir();
|
||||
}
|
||||
DirImplPtr p = _impl->openDir(path);
|
||||
return Dir(p, this);
|
||||
Dir d(p, this);
|
||||
d.setTimeCallback(timeCallback);
|
||||
return d;
|
||||
}
|
||||
|
||||
Dir FS::openDir(const String& path) {
|
||||
@ -385,6 +432,11 @@ bool FS::rename(const String& pathFrom, const String& pathTo) {
|
||||
return rename(pathFrom.c_str(), pathTo.c_str());
|
||||
}
|
||||
|
||||
void FS::setTimeCallback(time_t (*cb)(void)) {
|
||||
if (!_impl)
|
||||
return;
|
||||
_impl->setTimeCallback(cb);
|
||||
}
|
||||
|
||||
|
||||
static bool sflags(const char* mode, OpenMode& om, AccessMode& am) {
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <Arduino.h>
|
||||
#include <../include/time.h> // See issue #6714
|
||||
|
||||
class SDClass;
|
||||
|
||||
@ -110,6 +111,10 @@ public:
|
||||
|
||||
String readString() override;
|
||||
|
||||
time_t getLastWrite();
|
||||
time_t getCreationTime();
|
||||
void setTimeCallback(time_t (*cb)(void));
|
||||
|
||||
protected:
|
||||
FileImplPtr _p;
|
||||
|
||||
@ -126,15 +131,20 @@ public:
|
||||
|
||||
String fileName();
|
||||
size_t fileSize();
|
||||
time_t fileTime();
|
||||
time_t fileCreationTime();
|
||||
bool isFile() const;
|
||||
bool isDirectory() const;
|
||||
|
||||
bool next();
|
||||
bool rewind();
|
||||
|
||||
void setTimeCallback(time_t (*cb)(void));
|
||||
|
||||
protected:
|
||||
DirImplPtr _impl;
|
||||
FS *_baseFS;
|
||||
time_t (*timeCallback)(void) = nullptr;
|
||||
};
|
||||
|
||||
// Backwards compatible, <4GB filesystem usage
|
||||
@ -161,12 +171,10 @@ struct FSInfo64 {
|
||||
class FSConfig
|
||||
{
|
||||
public:
|
||||
FSConfig(bool autoFormat = true) {
|
||||
_type = FSConfig::fsid::FSId;
|
||||
_autoFormat = autoFormat;
|
||||
}
|
||||
static constexpr uint32_t FSId = 0x00000000;
|
||||
|
||||
FSConfig(uint32_t type = FSId, bool autoFormat = true) : _type(type), _autoFormat(autoFormat) { }
|
||||
|
||||
enum fsid { FSId = 0x00000000 };
|
||||
FSConfig setAutoFormat(bool val = true) {
|
||||
_autoFormat = val;
|
||||
return *this;
|
||||
@ -179,17 +187,17 @@ public:
|
||||
class SPIFFSConfig : public FSConfig
|
||||
{
|
||||
public:
|
||||
SPIFFSConfig(bool autoFormat = true) {
|
||||
_type = SPIFFSConfig::fsid::FSId;
|
||||
_autoFormat = autoFormat;
|
||||
}
|
||||
enum fsid { FSId = 0x53504946 };
|
||||
static constexpr uint32_t FSId = 0x53504946;
|
||||
SPIFFSConfig(bool autoFormat = true) : FSConfig(FSId, autoFormat) { }
|
||||
|
||||
// Inherit _type and _autoFormat
|
||||
// nothing yet, enableTime TBD when SPIFFS has metadate
|
||||
};
|
||||
|
||||
class FS
|
||||
{
|
||||
public:
|
||||
FS(FSImplPtr impl) : _impl(impl) { }
|
||||
FS(FSImplPtr impl) : _impl(impl) { timeCallback = _defaultTimeCB; }
|
||||
|
||||
bool setConfig(const FSConfig &cfg);
|
||||
|
||||
@ -225,14 +233,25 @@ public:
|
||||
bool gc();
|
||||
bool check();
|
||||
|
||||
void setTimeCallback(time_t (*cb)(void));
|
||||
|
||||
friend class ::SDClass; // More of a frenemy, but SD needs internal implementation to get private FAT bits
|
||||
protected:
|
||||
FSImplPtr _impl;
|
||||
FSImplPtr getImpl() { return _impl; }
|
||||
time_t (*timeCallback)(void);
|
||||
static time_t _defaultTimeCB(void) { return time(NULL); }
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void close_all_fs(void);
|
||||
void littlefs_request_end(void);
|
||||
void spiffs_request_end(void);
|
||||
}
|
||||
|
||||
#ifndef FS_NO_GLOBALS
|
||||
using fs::FS;
|
||||
using fs::File;
|
||||
|
@ -41,6 +41,21 @@ public:
|
||||
virtual const char* fullName() const = 0;
|
||||
virtual bool isFile() const = 0;
|
||||
virtual bool isDirectory() const = 0;
|
||||
|
||||
// Filesystems *may* support a timestamp per-file, so allow the user to override with
|
||||
// their own callback for *this specific* file (as opposed to the FSImpl call of the
|
||||
// same name. The default implementation simply returns time(&null)
|
||||
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
|
||||
|
||||
// Return the last written time for a file. Undefined when called on a writable file
|
||||
// as the FS is allowed to return either the time of the last write() operation or the
|
||||
// time present in the filesystem metadata (often the last time the file was closed)
|
||||
virtual time_t getLastWrite() { return 0; } // Default is to not support timestamps
|
||||
// Same for creation time.
|
||||
virtual time_t getCreationTime() { return 0; } // Default is to not support timestamps
|
||||
|
||||
protected:
|
||||
time_t (*timeCallback)(void) = nullptr;
|
||||
};
|
||||
|
||||
enum OpenMode {
|
||||
@ -62,10 +77,23 @@ public:
|
||||
virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0;
|
||||
virtual const char* fileName() = 0;
|
||||
virtual size_t fileSize() = 0;
|
||||
// Return the last written time for a file. Undefined when called on a writable file
|
||||
// as the FS is allowed to return either the time of the last write() operation or the
|
||||
// time present in the filesystem metadata (often the last time the file was closed)
|
||||
virtual time_t fileTime() { return 0; } // By default, FS doesn't report file times
|
||||
virtual time_t fileCreationTime() { return 0; } // By default, FS doesn't report file times
|
||||
virtual bool isFile() const = 0;
|
||||
virtual bool isDirectory() const = 0;
|
||||
virtual bool next() = 0;
|
||||
virtual bool rewind() = 0;
|
||||
|
||||
// Filesystems *may* support a timestamp per-file, so allow the user to override with
|
||||
// their own callback for *this specific* file (as opposed to the FSImpl call of the
|
||||
// same name. The default implementation simply returns time(&null)
|
||||
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
|
||||
|
||||
protected:
|
||||
time_t (*timeCallback)(void) = nullptr;
|
||||
};
|
||||
|
||||
class FSImpl {
|
||||
@ -86,6 +114,14 @@ public:
|
||||
virtual bool rmdir(const char* path) = 0;
|
||||
virtual bool gc() { return true; } // May not be implemented in all file systems.
|
||||
virtual bool check() { return true; } // May not be implemented in all file systems.
|
||||
|
||||
// Filesystems *may* support a timestamp per-file, so allow the user to override with
|
||||
// their own callback for all files on this FS. The default implementation simply
|
||||
// returns the present time as reported by time(&null)
|
||||
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
|
||||
|
||||
protected:
|
||||
time_t (*timeCallback)(void) = nullptr;
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
33
cores/esp8266/FSnoop.cpp
Normal file
33
cores/esp8266/FSnoop.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* no-op implementations
|
||||
* used/linked when no strong implementation already exists elsewhere
|
||||
*/
|
||||
|
||||
#include <FS.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
void close_all_fs(void)
|
||||
{
|
||||
littlefs_request_end();
|
||||
spiffs_request_end();
|
||||
}
|
||||
|
||||
// default weak definitions
|
||||
// they are overriden in their respective real implementation
|
||||
// hint: https://github.com/esp8266/Arduino/pull/6699#issuecomment-549085382
|
||||
|
||||
void littlefs_request_end(void) __attribute__((weak));
|
||||
void littlefs_request_end(void)
|
||||
{
|
||||
//ets_printf("debug: noop: littlefs_request_end\n");
|
||||
}
|
||||
|
||||
void spiffs_request_end(void) __attribute__((weak));
|
||||
void spiffs_request_end(void)
|
||||
{
|
||||
//ets_printf("debug: noop: spiffs_request_end\n");
|
||||
}
|
||||
|
||||
}
|
@ -36,10 +36,10 @@ HardwareSerial::HardwareSerial(int uart_nr)
|
||||
: _uart_nr(uart_nr), _rx_size(256)
|
||||
{}
|
||||
|
||||
void HardwareSerial::begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin)
|
||||
void HardwareSerial::begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin, bool invert)
|
||||
{
|
||||
end();
|
||||
_uart = uart_init(_uart_nr, baud, (int) config, (int) mode, tx_pin, _rx_size);
|
||||
_uart = uart_init(_uart_nr, baud, (int) config, (int) mode, tx_pin, _rx_size, invert);
|
||||
#if defined(DEBUG_ESP_PORT) && !defined(NDEBUG)
|
||||
if (static_cast<void*>(this) == static_cast<void*>(&DEBUG_ESP_PORT))
|
||||
{
|
||||
@ -133,7 +133,7 @@ unsigned long HardwareSerial::testBaudrate()
|
||||
unsigned long HardwareSerial::detectBaudrate(time_t timeoutMillis)
|
||||
{
|
||||
esp8266::polledTimeout::oneShotFastMs timeOut(timeoutMillis);
|
||||
unsigned long detectedBaudrate;
|
||||
unsigned long detectedBaudrate = 0;
|
||||
while (!timeOut) {
|
||||
if ((detectedBaudrate = testBaudrate())) {
|
||||
break;
|
||||
|
@ -28,7 +28,7 @@
|
||||
#define HardwareSerial_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <time.h>
|
||||
#include <../include/time.h> // See issue #6714
|
||||
#include "Stream.h"
|
||||
#include "uart.h"
|
||||
|
||||
@ -73,18 +73,23 @@ public:
|
||||
|
||||
void begin(unsigned long baud)
|
||||
{
|
||||
begin(baud, SERIAL_8N1, SERIAL_FULL, 1);
|
||||
begin(baud, SERIAL_8N1, SERIAL_FULL, 1, false);
|
||||
}
|
||||
void begin(unsigned long baud, SerialConfig config)
|
||||
{
|
||||
begin(baud, config, SERIAL_FULL, 1);
|
||||
begin(baud, config, SERIAL_FULL, 1, false);
|
||||
}
|
||||
void begin(unsigned long baud, SerialConfig config, SerialMode mode)
|
||||
{
|
||||
begin(baud, config, mode, 1);
|
||||
begin(baud, config, mode, 1, false);
|
||||
}
|
||||
|
||||
void begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin);
|
||||
void begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin)
|
||||
{
|
||||
begin(baud, config, mode, tx_pin, false);
|
||||
}
|
||||
|
||||
void begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin, bool invert);
|
||||
|
||||
void end();
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#define LWIP_IPV6_NUM_ADDRESSES 0
|
||||
#define ip_2_ip4(x) (x)
|
||||
#define ipv4_addr ip_addr
|
||||
#define ipv4_addr_t ip_addr_t
|
||||
#define IP_IS_V4_VAL(x) (1)
|
||||
#define IP_SET_TYPE_VAL(x,y) do { (void)0; } while (0)
|
||||
#define IP_ANY_TYPE (&ip_addr_any)
|
||||
|
@ -104,11 +104,19 @@ size_t Print::printf_P(PGM_P format, ...) {
|
||||
size_t Print::print(const __FlashStringHelper *ifsh) {
|
||||
PGM_P p = reinterpret_cast<PGM_P>(ifsh);
|
||||
|
||||
char buff[128] __attribute__ ((aligned(4)));
|
||||
auto len = strlen_P(p);
|
||||
size_t n = 0;
|
||||
while (1) {
|
||||
uint8_t c = pgm_read_byte(p++);
|
||||
if (c == 0) break;
|
||||
n += write(c);
|
||||
while (n < len) {
|
||||
int to_write = std::min(sizeof(buff), len - n);
|
||||
memcpy_P(buff, p, to_write);
|
||||
auto written = write(buff, to_write);
|
||||
n += written;
|
||||
p += written;
|
||||
if (!written) {
|
||||
// Some error, write() should write at least 1 byte before returning
|
||||
break;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@ -260,47 +268,6 @@ size_t Print::printNumber(unsigned long n, uint8_t base) {
|
||||
}
|
||||
|
||||
size_t Print::printFloat(double number, uint8_t digits) {
|
||||
size_t n = 0;
|
||||
|
||||
if(isnan(number))
|
||||
return print("nan");
|
||||
if(isinf(number))
|
||||
return print("inf");
|
||||
if(number > 4294967040.0)
|
||||
return print("ovf"); // constant determined empirically
|
||||
if(number < -4294967040.0)
|
||||
return print("ovf"); // constant determined empirically
|
||||
|
||||
// Handle negative numbers
|
||||
if(number < 0.0) {
|
||||
n += print('-');
|
||||
number = -number;
|
||||
}
|
||||
|
||||
// Round correctly so that print(1.999, 2) prints as "2.00"
|
||||
double rounding = 0.5;
|
||||
for(uint8_t i = 0; i < digits; ++i)
|
||||
rounding /= 10.0;
|
||||
|
||||
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;
|
||||
n += print(int_part);
|
||||
|
||||
// Print the decimal point, but only if there are digits beyond
|
||||
if(digits > 0) {
|
||||
n += print(".");
|
||||
}
|
||||
|
||||
// Extract digits from the remainder one at a time
|
||||
while(digits-- > 0) {
|
||||
remainder *= 10.0;
|
||||
int toPrint = int(remainder);
|
||||
n += print(toPrint);
|
||||
remainder -= toPrint;
|
||||
}
|
||||
|
||||
return n;
|
||||
char buf[40];
|
||||
return write(dtostrf(number, 0, digits, buf));
|
||||
}
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "WString.h"
|
||||
#include "Printable.h"
|
||||
|
||||
#include "stdlib_noniso.h"
|
||||
|
||||
#define DEC 10
|
||||
#define HEX 16
|
||||
#define OCT 8
|
||||
|
@ -119,23 +119,33 @@ void run_scheduled_functions()
|
||||
{
|
||||
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
|
||||
|
||||
while (sFirst)
|
||||
// prevent scheduling of new functions during this run
|
||||
auto stop = sLast;
|
||||
bool done = false;
|
||||
while (sFirst && !done)
|
||||
{
|
||||
done = sFirst == stop;
|
||||
|
||||
sFirst->mFunc();
|
||||
|
||||
{
|
||||
// remove function from stack
|
||||
esp8266::InterruptLock lockAllInterruptsInThisScope;
|
||||
|
||||
auto to_recycle = sFirst;
|
||||
sFirst = sFirst->mNext;
|
||||
if (!sFirst)
|
||||
|
||||
// removing rLast
|
||||
if (sLast == sFirst)
|
||||
sLast = nullptr;
|
||||
|
||||
sFirst = sFirst->mNext;
|
||||
|
||||
recycle_fn_unsafe(to_recycle);
|
||||
}
|
||||
|
||||
if (yieldNow)
|
||||
{
|
||||
// because scheduled function are allowed to last:
|
||||
// because scheduled functions might last too long for watchdog etc,
|
||||
// this is yield() in cont stack:
|
||||
esp_schedule();
|
||||
cont_yield(g_pcont);
|
||||
|
@ -46,7 +46,7 @@ void run_scheduled_functions();
|
||||
|
||||
// recurrent scheduled function:
|
||||
//
|
||||
// * Internal queue may not be a FIFO.
|
||||
// * Internal queue is a FIFO.
|
||||
// * Run the lambda periodically about every <repeat_us> microseconds until
|
||||
// it returns false.
|
||||
// * Note that it may be more than <repeat_us> microseconds between calls if
|
||||
@ -60,6 +60,7 @@ void run_scheduled_functions();
|
||||
// recurrent function.
|
||||
// * If alarm is used, anytime during scheduling when it returns true,
|
||||
// any remaining delay from repeat_us is disregarded, and fn is executed.
|
||||
|
||||
bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,
|
||||
uint32_t repeat_us, const std::function<bool(void)>& alarm = nullptr);
|
||||
|
||||
|
@ -36,7 +36,8 @@ uint32_t *stack_thunk_top = NULL;
|
||||
uint32_t *stack_thunk_save = NULL; /* Saved A1 while in BearSSL */
|
||||
uint32_t stack_thunk_refcnt = 0;
|
||||
|
||||
#define _stackSize (5748/4)
|
||||
/* Largest stack usage seen in the wild at 6120 */
|
||||
#define _stackSize (6200/4)
|
||||
#define _stackPaint 0xdeadbeef
|
||||
|
||||
/* Add a reference, and allocate the stack if necessary */
|
||||
@ -110,14 +111,14 @@ uint32_t stack_thunk_get_max_usage()
|
||||
/* Print the stack from the first used 16-byte chunk to the top, decodable by the exception decoder */
|
||||
void stack_thunk_dump_stack()
|
||||
{
|
||||
uint32_t *pos = stack_thunk_top;
|
||||
while (pos < stack_thunk_ptr) {
|
||||
uint32_t *pos = stack_thunk_ptr;
|
||||
while (pos < stack_thunk_top) {
|
||||
if ((pos[0] != _stackPaint) || (pos[1] != _stackPaint) || (pos[2] != _stackPaint) || (pos[3] != _stackPaint))
|
||||
break;
|
||||
pos += 4;
|
||||
}
|
||||
ets_printf(">>>stack>>>\n");
|
||||
while (pos < stack_thunk_ptr) {
|
||||
while (pos < stack_thunk_top) {
|
||||
ets_printf("%08x: %08x %08x %08x %08x\n", (int32_t)pos, pos[0], pos[1], pos[2], pos[3]);
|
||||
pos += 4;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
// autogenerated from https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv
|
||||
// by script <esp8266 arduino core>/tools/TZupdate.sh
|
||||
// Mon Sep 9 20:58:30 UTC 2019
|
||||
// Mon Dec 16 13:08:18 UTC 2019
|
||||
//
|
||||
// This database is autogenerated from IANA timezone database
|
||||
// https://www.iana.org/time-zones
|
||||
@ -407,7 +407,7 @@
|
||||
#define TZ_Pacific_Efate PSTR("<+11>-11")
|
||||
#define TZ_Pacific_Enderbury PSTR("<+13>-13")
|
||||
#define TZ_Pacific_Fakaofo PSTR("<+13>-13")
|
||||
#define TZ_Pacific_Fiji PSTR("<+12>-12<+13>,M11.1.0,M1.2.2/123")
|
||||
#define TZ_Pacific_Fiji PSTR("<+12>-12<+13>,M11.2.0,M1.2.3/99")
|
||||
#define TZ_Pacific_Funafuti PSTR("<+12>-12")
|
||||
#define TZ_Pacific_Galapagos PSTR("<-06>6")
|
||||
#define TZ_Pacific_Gambier PSTR("<-09>9")
|
||||
@ -422,7 +422,7 @@
|
||||
#define TZ_Pacific_Midway PSTR("SST11")
|
||||
#define TZ_Pacific_Nauru PSTR("<+12>-12")
|
||||
#define TZ_Pacific_Niue PSTR("<-11>11")
|
||||
#define TZ_Pacific_Norfolk PSTR("<+11>-11")
|
||||
#define TZ_Pacific_Norfolk PSTR("<+11>-11<+12>,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Pacific_Noumea PSTR("<+11>-11")
|
||||
#define TZ_Pacific_Pago_Pago PSTR("SST11")
|
||||
#define TZ_Pacific_Palau PSTR("<+09>-9")
|
||||
|
@ -104,7 +104,9 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
_reset();
|
||||
clearError(); // _error = 0
|
||||
|
||||
#ifndef HOST_MOCK
|
||||
wifi_set_sleep_type(NONE_SLEEP_T);
|
||||
#endif
|
||||
|
||||
//address where we will start writing the update
|
||||
uintptr_t updateStartAddress = 0;
|
||||
@ -329,7 +331,8 @@ bool UpdaterClass::_writeBuffer(){
|
||||
bool modifyFlashMode = false;
|
||||
FlashMode_t flashMode = FM_QIO;
|
||||
FlashMode_t bufferFlashMode = FM_QIO;
|
||||
if (_currentAddress == _startAddress + FLASH_MODE_PAGE) {
|
||||
//TODO - GZIP can't do this
|
||||
if ((_currentAddress == _startAddress + FLASH_MODE_PAGE) && (_buffer[0] != 0x1f) && (_command == U_FLASH)) {
|
||||
flashMode = ESP.getFlashChipMode();
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("Header: 0x%1X %1X %1X %1X\n"), _buffer[0], _buffer[1], _buffer[2], _buffer[3]);
|
||||
@ -377,9 +380,7 @@ size_t UpdaterClass::write(uint8_t *data, size_t len) {
|
||||
if(hasError() || !isRunning())
|
||||
return 0;
|
||||
|
||||
if(len > remaining()){
|
||||
//len = remaining();
|
||||
//fail instead
|
||||
if(progress() + _bufferLen + len > _size) {
|
||||
_setError(UPDATE_ERROR_SPACE);
|
||||
return 0;
|
||||
}
|
||||
@ -411,7 +412,7 @@ size_t UpdaterClass::write(uint8_t *data, size_t len) {
|
||||
bool UpdaterClass::_verifyHeader(uint8_t data) {
|
||||
if(_command == U_FLASH) {
|
||||
// check for valid first magic byte (is always 0xE9)
|
||||
if(data != 0xE9) {
|
||||
if ((data != 0xE9) && (data != 0x1f)) {
|
||||
_currentAddress = (_startAddress + _size);
|
||||
_setError(UPDATE_ERROR_MAGIC_BYTE);
|
||||
return false;
|
||||
@ -435,7 +436,12 @@ bool UpdaterClass::_verifyEnd() {
|
||||
}
|
||||
|
||||
// check for valid first magic byte
|
||||
if(buf[0] != 0xE9) {
|
||||
//
|
||||
// TODO: GZIP compresses the chipsize flags, so can't do check here
|
||||
if ((buf[0] == 0x1f) && (buf[1] == 0x8b)) {
|
||||
// GZIP, just assume OK
|
||||
return true;
|
||||
} else if (buf[0] != 0xE9) {
|
||||
_currentAddress = (_startAddress);
|
||||
_setError(UPDATE_ERROR_MAGIC_BYTE);
|
||||
return false;
|
||||
|
@ -70,11 +70,11 @@ long secureRandom(long howsmall, long howbig) {
|
||||
}
|
||||
|
||||
long map(long x, long in_min, long in_max, long out_min, long out_max) {
|
||||
long divisor = (in_max - in_min);
|
||||
if(divisor == 0){
|
||||
return -1; //AVR returns -1, SAM returns 0
|
||||
}
|
||||
return (x - in_min) * (out_max - out_min) / divisor + out_min;
|
||||
const long dividend = out_max - out_min;
|
||||
const long divisor = in_max - in_min;
|
||||
const long delta = x - in_min;
|
||||
|
||||
return (delta * dividend + (divisor / 2)) / divisor + out_min;
|
||||
}
|
||||
|
||||
unsigned int makeWord(unsigned int w) {
|
||||
|
@ -331,6 +331,7 @@ unsigned char String::concat(const char *cstr, unsigned int length) {
|
||||
return 0;
|
||||
memmove_P(wbuffer() + len(), cstr, length + 1);
|
||||
setLen(newlen);
|
||||
wbuffer()[newlen] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -116,6 +116,7 @@ class String {
|
||||
unsigned char concat(float num);
|
||||
unsigned char concat(double num);
|
||||
unsigned char concat(const __FlashStringHelper * str);
|
||||
unsigned char concat(const char *cstr, unsigned int length);
|
||||
|
||||
// if there's not enough memory for the concatenated value, the string
|
||||
// will be left unchanged (but this isn't signalled in any way)
|
||||
@ -202,8 +203,20 @@ class String {
|
||||
unsigned char equalsIgnoreCase(const String &s) const;
|
||||
unsigned char equalsConstantTime(const String &s) const;
|
||||
unsigned char startsWith(const String &prefix) const;
|
||||
unsigned char startsWith(const char * prefix) const {
|
||||
return this->startsWith(String(prefix));
|
||||
}
|
||||
unsigned char startsWith(const __FlashStringHelper * prefix) const {
|
||||
return this->startsWith(String(prefix));
|
||||
}
|
||||
unsigned char startsWith(const String &prefix, unsigned int offset) const;
|
||||
unsigned char endsWith(const String &suffix) const;
|
||||
unsigned char endsWith(const char * suffix) const {
|
||||
return this->endsWith(String(suffix));
|
||||
}
|
||||
unsigned char endsWith(const __FlashStringHelper * suffix) const {
|
||||
return this->endsWith(String(suffix));
|
||||
}
|
||||
|
||||
// character access
|
||||
char charAt(unsigned int index) const;
|
||||
@ -238,6 +251,21 @@ class String {
|
||||
// modification
|
||||
void replace(char find, char replace);
|
||||
void replace(const String& find, const String& replace);
|
||||
void replace(const char * find, const String& replace) {
|
||||
this->replace(String(find), replace);
|
||||
}
|
||||
void replace(const __FlashStringHelper * find, const String& replace) {
|
||||
this->replace(String(find), replace);
|
||||
}
|
||||
void replace(const char * find, const char * replace) {
|
||||
this->replace(String(find), String(replace));
|
||||
}
|
||||
void replace(const __FlashStringHelper * find, const char * replace) {
|
||||
this->replace(String(find), String(replace));
|
||||
}
|
||||
void replace(const __FlashStringHelper * find, const __FlashStringHelper * replace) {
|
||||
this->replace(String(find), String(replace));
|
||||
}
|
||||
void remove(unsigned int index);
|
||||
void remove(unsigned int index, unsigned int count);
|
||||
void toLowerCase(void);
|
||||
@ -284,7 +312,6 @@ class String {
|
||||
void init(void);
|
||||
void invalidate(void);
|
||||
unsigned char changeBuffer(unsigned int maxStrLen);
|
||||
unsigned char concat(const char *cstr, unsigned int length);
|
||||
|
||||
// copy and move
|
||||
String & copy(const char *cstr, unsigned int length);
|
||||
|
1
cores/esp8266/avr/dtostrf.h
Normal file
1
cores/esp8266/avr/dtostrf.h
Normal file
@ -0,0 +1 @@
|
||||
#include <stdlib_noniso.h>
|
@ -24,7 +24,6 @@
|
||||
|
||||
#include "Arduino.h"
|
||||
extern "C" {
|
||||
#include "libb64/cdecode.h"
|
||||
#include "libb64/cencode.h"
|
||||
}
|
||||
#include "base64.h"
|
||||
@ -35,12 +34,17 @@ extern "C" {
|
||||
* @param length size_t
|
||||
* @return String
|
||||
*/
|
||||
String base64::encode(const uint8_t * data, size_t length, bool doNewLines) {
|
||||
String base64::encode(const uint8_t * data, size_t length, bool doNewLines)
|
||||
{
|
||||
String base64;
|
||||
|
||||
// base64 needs more size then the source data, use cencode.h macros
|
||||
size_t size = ((doNewLines ? base64_encode_expected_len( length )
|
||||
: base64_encode_expected_len_nonewlines( length )) + 1);
|
||||
char * buffer = (char *) malloc(size);
|
||||
if(buffer) {
|
||||
|
||||
if (base64.reserve(size))
|
||||
{
|
||||
|
||||
base64_encodestate _state;
|
||||
if (doNewLines)
|
||||
{
|
||||
@ -50,22 +54,23 @@ String base64::encode(const uint8_t * data, size_t length, bool doNewLines) {
|
||||
{
|
||||
base64_init_encodestate_nonewlines(&_state);
|
||||
}
|
||||
int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state);
|
||||
len = base64_encode_blockend((buffer + len), &_state);
|
||||
|
||||
String base64 = String(buffer);
|
||||
free(buffer);
|
||||
constexpr size_t BUFSIZE = 48;
|
||||
char buf[BUFSIZE + 1 /* newline */ + 1 /* NUL */];
|
||||
for (size_t len = 0; len < length; len += BUFSIZE * 3 / 4)
|
||||
{
|
||||
size_t blocklen = base64_encode_block((const char*) data + len,
|
||||
std::min( BUFSIZE * 3 / 4, length - len ), buf, &_state);
|
||||
buf[blocklen] = '\0';
|
||||
base64 += buf;
|
||||
}
|
||||
if (base64_encode_blockend(buf, &_state))
|
||||
base64 += buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
base64 = F("-FAIL-");
|
||||
}
|
||||
|
||||
return base64;
|
||||
}
|
||||
return String("-FAIL-");
|
||||
}
|
||||
|
||||
/**
|
||||
* convert input data to base64
|
||||
* @param text const String&
|
||||
* @return String
|
||||
*/
|
||||
String base64::encode(const String& text, bool doNewLines) {
|
||||
return base64::encode((const uint8_t *) text.c_str(), text.length(), doNewLines);
|
||||
}
|
||||
|
||||
|
@ -25,13 +25,17 @@
|
||||
#ifndef CORE_BASE64_H_
|
||||
#define CORE_BASE64_H_
|
||||
|
||||
class base64 {
|
||||
class base64
|
||||
{
|
||||
public:
|
||||
// NOTE: The default behaviour of backend (lib64)
|
||||
// is to add a newline every 72 (encoded) characters output.
|
||||
// This may 'break' longer uris and json variables
|
||||
static String encode(const uint8_t * data, size_t length, bool doNewLines = true);
|
||||
static String encode(const String& text, bool doNewLines = true);
|
||||
static String inline encode(const String& text, bool doNewLines = true)
|
||||
{
|
||||
return encode( (const uint8_t *) text.c_str(), text.length(), doNewLines );
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
|
50
cores/esp8266/core_esp8266_features.cpp
Normal file
50
cores/esp8266/core_esp8266_features.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
|
||||
/*
|
||||
core_esp8266_features.cpp
|
||||
|
||||
Copyright (c) 2019 Mike Nix. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* precache()
|
||||
* pre-loads flash data into the flash cache
|
||||
* if f==0, preloads instructions starting at the address we were called from.
|
||||
* otherwise preloads flash at the given address.
|
||||
* All preloads are word aligned.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void precache(void *f, uint32_t bytes) {
|
||||
// Size of a cache page in bytes. We only need to read one word per
|
||||
// page (ie 1 word in 8) for this to work.
|
||||
#define CACHE_PAGE_SIZE 32
|
||||
|
||||
register uint32_t a0 asm("a0");
|
||||
register uint32_t lines = (bytes/CACHE_PAGE_SIZE)+2;
|
||||
volatile uint32_t *p = (uint32_t*)((f ? (uint32_t)f : a0) & ~0x03);
|
||||
uint32_t x;
|
||||
for (uint32_t i=0; i<lines; i++, p+=CACHE_PAGE_SIZE/sizeof(uint32_t)) x=*p;
|
||||
(void)x;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -32,10 +32,11 @@
|
||||
|
||||
#define WIFI_HAS_EVENT_CALLBACK
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <stdlib.h> // malloc()
|
||||
#include <stddef.h> // size_t
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace arduino
|
||||
{
|
||||
@ -93,4 +94,26 @@ inline uint32_t esp_get_cycle_count() {
|
||||
}
|
||||
#endif // not CORE_MOCK
|
||||
|
||||
|
||||
// Tools for preloading code into the flash cache
|
||||
#define PRECACHE_ATTR __attribute__((optimize("no-reorder-blocks"))) \
|
||||
__attribute__((noinline))
|
||||
|
||||
#define PRECACHE_START(tag) \
|
||||
precache(NULL,(uint8_t *)&&_precache_end_##tag - (uint8_t*)&&_precache_start_##tag); \
|
||||
_precache_start_##tag:
|
||||
|
||||
#define PRECACHE_END(tag) \
|
||||
_precache_end_##tag:
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void precache(void *f, uint32_t bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CORE_ESP8266_FEATURES_H
|
||||
|
@ -37,7 +37,6 @@ extern "C" {
|
||||
|
||||
#define LOOP_TASK_PRIORITY 1
|
||||
#define LOOP_QUEUE_SIZE 1
|
||||
#define OPTIMISTIC_YIELD_TIME_US 16000
|
||||
|
||||
extern "C" void call_user_start();
|
||||
extern void loop();
|
||||
@ -58,7 +57,7 @@ cont_t* g_pcont __attribute__((section(".noinit")));
|
||||
static os_event_t s_loop_queue[LOOP_QUEUE_SIZE];
|
||||
|
||||
/* Used to implement optimistic_yield */
|
||||
static uint32_t s_micros_at_task_start;
|
||||
static uint32_t s_cycles_at_yield_start;
|
||||
|
||||
/* For ets_intr_lock_nest / ets_intr_unlock_nest
|
||||
* Max nesting seen by SDK so far is 2.
|
||||
@ -90,26 +89,31 @@ void preloop_update_frequency() {
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" bool can_yield() {
|
||||
return cont_can_yield(g_pcont);
|
||||
}
|
||||
|
||||
static inline void esp_yield_within_cont() __attribute__((always_inline));
|
||||
static void esp_yield_within_cont() {
|
||||
cont_yield(g_pcont);
|
||||
s_cycles_at_yield_start = ESP.getCycleCount();
|
||||
run_scheduled_recurrent_functions();
|
||||
}
|
||||
|
||||
extern "C" void esp_yield() {
|
||||
if (cont_can_yield(g_pcont)) {
|
||||
extern "C" void __esp_yield() {
|
||||
if (can_yield()) {
|
||||
esp_yield_within_cont();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void esp_schedule() {
|
||||
// always on CONT stack here
|
||||
extern "C" void esp_yield() __attribute__ ((weak, alias("__esp_yield")));
|
||||
|
||||
extern "C" IRAM_ATTR void esp_schedule() {
|
||||
ets_post(LOOP_TASK_PRIORITY, 0, 0);
|
||||
}
|
||||
|
||||
extern "C" void __yield() {
|
||||
if (cont_can_yield(g_pcont)) {
|
||||
if (can_yield()) {
|
||||
esp_schedule();
|
||||
esp_yield_within_cont();
|
||||
}
|
||||
@ -121,14 +125,19 @@ extern "C" void __yield() {
|
||||
extern "C" void yield(void) __attribute__ ((weak, alias("__yield")));
|
||||
|
||||
extern "C" void optimistic_yield(uint32_t interval_us) {
|
||||
if (cont_can_yield(g_pcont) &&
|
||||
(system_get_time() - s_micros_at_task_start) > interval_us)
|
||||
const uint32_t intvl_cycles = interval_us *
|
||||
#if defined(F_CPU)
|
||||
clockCyclesPerMicrosecond();
|
||||
#else
|
||||
ESP.getCpuFreqMHz();
|
||||
#endif
|
||||
if ((ESP.getCycleCount() - s_cycles_at_yield_start) > intvl_cycles &&
|
||||
can_yield())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Replace ets_intr_(un)lock with nestable versions
|
||||
extern "C" void IRAM_ATTR ets_intr_lock() {
|
||||
if (ets_intr_lock_stack_ptr < ETS_INTR_LOCK_NEST_MAX)
|
||||
@ -179,7 +188,7 @@ static void loop_wrapper() {
|
||||
|
||||
static void loop_task(os_event_t *events) {
|
||||
(void) events;
|
||||
s_micros_at_task_start = system_get_time();
|
||||
s_cycles_at_yield_start = ESP.getCycleCount();
|
||||
cont_run(g_pcont, &loop_wrapper);
|
||||
if (cont_check(g_pcont) != 0) {
|
||||
panic();
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <limits>
|
||||
#include "stdlib_noniso.h"
|
||||
|
||||
extern "C" {
|
||||
@ -77,11 +78,15 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
|
||||
// Figure out how big our number really is
|
||||
double tenpow = 1.0;
|
||||
int digitcount = 1;
|
||||
while (number >= 10.0 * tenpow) {
|
||||
tenpow *= 10.0;
|
||||
double nextpow;
|
||||
while (number >= (nextpow = (10.0 * tenpow))) {
|
||||
tenpow = nextpow;
|
||||
digitcount++;
|
||||
}
|
||||
|
||||
// minimal compensation for possible lack of precision (#7087 addition)
|
||||
number *= 1 + std::numeric_limits<decltype(number)>::epsilon();
|
||||
|
||||
number /= tenpow;
|
||||
fillme -= digitcount;
|
||||
|
||||
|
@ -89,7 +89,7 @@ static void ets_printf_P(const char *str, ...) {
|
||||
vsnprintf(destStr, sizeof(destStr), str, argPtr);
|
||||
va_end(argPtr);
|
||||
while (*c) {
|
||||
ets_putc(*(c++));
|
||||
ets_uart_putc1(*(c++));
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,10 +147,10 @@ void __wrap_system_restart_local() {
|
||||
// (determined empirically, might break)
|
||||
uint32_t offset = 0;
|
||||
if (rst_info.reason == REASON_SOFT_WDT_RST) {
|
||||
offset = 0x1b0;
|
||||
offset = 0x1a0;
|
||||
}
|
||||
else if (rst_info.reason == REASON_EXCEPTION_RST) {
|
||||
offset = 0x1a0;
|
||||
offset = 0x190;
|
||||
}
|
||||
else if (rst_info.reason == REASON_WDT_RST) {
|
||||
offset = 0x10;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "twi.h"
|
||||
#include "pins_arduino.h"
|
||||
#include "wiring_private.h"
|
||||
#include "PolledTimeout.h"
|
||||
|
||||
|
||||
|
||||
@ -30,13 +31,39 @@ extern "C" {
|
||||
#include "ets_sys.h"
|
||||
};
|
||||
|
||||
// Inline helpers
|
||||
static inline __attribute__((always_inline)) void SDA_LOW(const int twi_sda)
|
||||
{
|
||||
GPES = (1 << twi_sda);
|
||||
}
|
||||
static inline __attribute__((always_inline)) void SDA_HIGH(const int twi_sda)
|
||||
{
|
||||
GPEC = (1 << twi_sda);
|
||||
}
|
||||
static inline __attribute__((always_inline)) bool SDA_READ(const int twi_sda)
|
||||
{
|
||||
return (GPI & (1 << twi_sda)) != 0;
|
||||
}
|
||||
static inline __attribute__((always_inline)) void SCL_LOW(const int twi_scl)
|
||||
{
|
||||
GPES = (1 << twi_scl);
|
||||
}
|
||||
static inline __attribute__((always_inline)) void SCL_HIGH(const int twi_scl)
|
||||
{
|
||||
GPEC = (1 << twi_scl);
|
||||
}
|
||||
static inline __attribute__((always_inline)) bool SCL_READ(const int twi_scl)
|
||||
{
|
||||
return (GPI & (1 << twi_scl)) != 0;
|
||||
}
|
||||
|
||||
|
||||
// Implement as a class to reduce code size by allowing access to many global variables with a single base pointer
|
||||
class Twi
|
||||
{
|
||||
private:
|
||||
unsigned int preferred_si2c_clock = 100000;
|
||||
unsigned char twi_dcount = 18;
|
||||
uint32_t twi_dcount = 18;
|
||||
unsigned char twi_sda = 0;
|
||||
unsigned char twi_scl = 0;
|
||||
unsigned char twi_addr = 0;
|
||||
@ -85,7 +112,7 @@ private:
|
||||
bool _slaveEnabled = false;
|
||||
|
||||
// Internal use functions
|
||||
void ICACHE_RAM_ATTR busywait(unsigned char v);
|
||||
void ICACHE_RAM_ATTR busywait(unsigned int v);
|
||||
bool write_start(void);
|
||||
bool write_stop(void);
|
||||
bool write_bit(bool bit);
|
||||
@ -94,37 +121,15 @@ private:
|
||||
unsigned char read_byte(bool nack);
|
||||
void ICACHE_RAM_ATTR onTwipEvent(uint8_t status);
|
||||
|
||||
// Inline helpers
|
||||
inline void SDA_LOW()
|
||||
{
|
||||
GPES = (1 << twi_sda);
|
||||
}
|
||||
inline void SDA_HIGH()
|
||||
{
|
||||
GPEC = (1 << twi_sda);
|
||||
}
|
||||
inline bool SDA_READ()
|
||||
{
|
||||
return (GPI & (1 << twi_sda)) != 0;
|
||||
}
|
||||
inline void SCL_LOW()
|
||||
{
|
||||
GPES = (1 << twi_scl);
|
||||
}
|
||||
inline void SCL_HIGH()
|
||||
{
|
||||
GPEC = (1 << twi_scl);
|
||||
}
|
||||
inline bool SCL_READ()
|
||||
{
|
||||
return (GPI & (1 << twi_scl)) != 0;
|
||||
}
|
||||
// Handle the case where a slave needs to stretch the clock with a time-limited busy wait
|
||||
inline void WAIT_CLOCK_STRETCH()
|
||||
{
|
||||
for (unsigned int t = 0; !SCL_READ() && (t < twi_clockStretchLimit); t++)
|
||||
esp8266::polledTimeout::oneShotFastUs timeout(twi_clockStretchLimit);
|
||||
esp8266::polledTimeout::periodicFastUs yieldTimeout(5000);
|
||||
while(!timeout && !SCL_READ(twi_scl)) // outer loop is stretch duration up to stretch limit
|
||||
{
|
||||
/* noop */
|
||||
if (yieldTimeout) // inner loop yields every 5ms
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,9 +147,8 @@ public:
|
||||
uint8_t transmit(const uint8_t* data, uint8_t length);
|
||||
void attachSlaveRxEvent(void (*function)(uint8_t*, size_t));
|
||||
void attachSlaveTxEvent(void (*function)(void));
|
||||
inline void ICACHE_RAM_ATTR reply(uint8_t ack);
|
||||
inline void ICACHE_RAM_ATTR stop(void);
|
||||
inline void ICACHE_RAM_ATTR releaseBus(void);
|
||||
void ICACHE_RAM_ATTR reply(uint8_t ack);
|
||||
void ICACHE_RAM_ATTR releaseBus(void);
|
||||
void enableSlave();
|
||||
};
|
||||
|
||||
@ -154,80 +158,33 @@ static Twi twi;
|
||||
#define FCPU80 80000000L
|
||||
#endif
|
||||
|
||||
#if F_CPU == FCPU80
|
||||
#define TWI_CLOCK_STRETCH_MULTIPLIER 3
|
||||
#else
|
||||
#define TWI_CLOCK_STRETCH_MULTIPLIER 6
|
||||
#endif
|
||||
|
||||
|
||||
void Twi::setClock(unsigned int freq)
|
||||
{
|
||||
if (freq < 1000) // minimum freq 1000Hz to minimize slave timeouts and WDT resets
|
||||
freq = 1000;
|
||||
|
||||
preferred_si2c_clock = freq;
|
||||
|
||||
#if F_CPU == FCPU80
|
||||
if (freq <= 50000)
|
||||
{
|
||||
twi_dcount = 38; //about 50KHz
|
||||
}
|
||||
else if (freq <= 100000)
|
||||
{
|
||||
twi_dcount = 19; //about 100KHz
|
||||
}
|
||||
else if (freq <= 200000)
|
||||
{
|
||||
twi_dcount = 8; //about 200KHz
|
||||
}
|
||||
else if (freq <= 300000)
|
||||
{
|
||||
twi_dcount = 3; //about 300KHz
|
||||
}
|
||||
else if (freq <= 400000)
|
||||
{
|
||||
twi_dcount = 1; //about 400KHz
|
||||
}
|
||||
else
|
||||
{
|
||||
twi_dcount = 1; //about 400KHz
|
||||
}
|
||||
|
||||
if (freq > 400000)
|
||||
freq = 400000;
|
||||
twi_dcount = (500000000 / freq); // half-cycle period in ns
|
||||
twi_dcount = (1000*(twi_dcount - 1120)) / 62500; // (half cycle - overhead) / busywait loop time
|
||||
|
||||
#else
|
||||
if (freq <= 50000)
|
||||
{
|
||||
twi_dcount = 64; //about 50KHz
|
||||
}
|
||||
else if (freq <= 100000)
|
||||
{
|
||||
twi_dcount = 32; //about 100KHz
|
||||
}
|
||||
else if (freq <= 200000)
|
||||
{
|
||||
twi_dcount = 14; //about 200KHz
|
||||
}
|
||||
else if (freq <= 300000)
|
||||
{
|
||||
twi_dcount = 8; //about 300KHz
|
||||
}
|
||||
else if (freq <= 400000)
|
||||
{
|
||||
twi_dcount = 5; //about 400KHz
|
||||
}
|
||||
else if (freq <= 500000)
|
||||
{
|
||||
twi_dcount = 3; //about 500KHz
|
||||
}
|
||||
else if (freq <= 600000)
|
||||
{
|
||||
twi_dcount = 2; //about 600KHz
|
||||
}
|
||||
else
|
||||
{
|
||||
twi_dcount = 1; //about 700KHz
|
||||
}
|
||||
|
||||
if (freq > 800000)
|
||||
freq = 800000;
|
||||
twi_dcount = (500000000 / freq); // half-cycle period in ns
|
||||
twi_dcount = (1000*(twi_dcount - 560)) / 31250; // (half cycle - overhead) / busywait loop time
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void Twi::setClockStretchLimit(uint32_t limit)
|
||||
{
|
||||
twi_clockStretchLimit = limit * TWI_CLOCK_STRETCH_MULTIPLIER;
|
||||
twi_clockStretchLimit = limit;
|
||||
}
|
||||
|
||||
|
||||
@ -245,7 +202,7 @@ void Twi::init(unsigned char sda, unsigned char scl)
|
||||
pinMode(twi_sda, INPUT_PULLUP);
|
||||
pinMode(twi_scl, INPUT_PULLUP);
|
||||
twi_setClock(preferred_si2c_clock);
|
||||
twi_setClockStretchLimit(230); // default value is 230 uS
|
||||
twi_setClockStretchLimit(150000L); // default value is 150 mS
|
||||
}
|
||||
|
||||
void Twi::setAddress(uint8_t address)
|
||||
@ -264,60 +221,55 @@ void Twi::enableSlave()
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR Twi::busywait(unsigned char v)
|
||||
void ICACHE_RAM_ATTR Twi::busywait(unsigned int v)
|
||||
{
|
||||
unsigned int i;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
unsigned int reg;
|
||||
for (i = 0; i < v; i++)
|
||||
for (i = 0; i < v; i++) // loop time is 5 machine cycles: 31.25ns @ 160MHz, 62.5ns @ 80MHz
|
||||
{
|
||||
reg = GPI;
|
||||
asm("nop"); // minimum element to keep GCC from optimizing this function out.
|
||||
}
|
||||
(void)reg;
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
bool Twi::write_start(void)
|
||||
{
|
||||
SCL_HIGH();
|
||||
SDA_HIGH();
|
||||
if (!SDA_READ())
|
||||
SCL_HIGH(twi_scl);
|
||||
SDA_HIGH(twi_sda);
|
||||
if (!SDA_READ(twi_sda))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
busywait(twi_dcount);
|
||||
SDA_LOW();
|
||||
SDA_LOW(twi_sda);
|
||||
busywait(twi_dcount);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Twi::write_stop(void)
|
||||
{
|
||||
SCL_LOW();
|
||||
SDA_LOW();
|
||||
SCL_LOW(twi_scl);
|
||||
SDA_LOW(twi_sda);
|
||||
busywait(twi_dcount);
|
||||
SCL_HIGH();
|
||||
SCL_HIGH(twi_scl);
|
||||
WAIT_CLOCK_STRETCH();
|
||||
busywait(twi_dcount);
|
||||
SDA_HIGH();
|
||||
SDA_HIGH(twi_sda);
|
||||
busywait(twi_dcount);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Twi::write_bit(bool bit)
|
||||
{
|
||||
SCL_LOW();
|
||||
SCL_LOW(twi_scl);
|
||||
if (bit)
|
||||
{
|
||||
SDA_HIGH();
|
||||
SDA_HIGH(twi_sda);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDA_LOW();
|
||||
SDA_LOW(twi_sda);
|
||||
}
|
||||
busywait(twi_dcount + 1);
|
||||
SCL_HIGH();
|
||||
SCL_HIGH(twi_scl);
|
||||
WAIT_CLOCK_STRETCH();
|
||||
busywait(twi_dcount);
|
||||
return true;
|
||||
@ -325,12 +277,12 @@ bool Twi::write_bit(bool bit)
|
||||
|
||||
bool Twi::read_bit(void)
|
||||
{
|
||||
SCL_LOW();
|
||||
SDA_HIGH();
|
||||
SCL_LOW(twi_scl);
|
||||
SDA_HIGH(twi_sda);
|
||||
busywait(twi_dcount + 2);
|
||||
SCL_HIGH();
|
||||
SCL_HIGH(twi_scl);
|
||||
WAIT_CLOCK_STRETCH();
|
||||
bool bit = SDA_READ();
|
||||
bool bit = SDA_READ(twi_sda);
|
||||
busywait(twi_dcount);
|
||||
return bit;
|
||||
}
|
||||
@ -395,7 +347,7 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned
|
||||
// busywait(twi_dcount);
|
||||
}
|
||||
i = 0;
|
||||
while (!SDA_READ() && (i++) < 10)
|
||||
while (!SDA_READ(twi_sda) && (i++) < 10)
|
||||
{
|
||||
twi_scl_valley();
|
||||
busywait(twi_dcount);
|
||||
@ -434,7 +386,7 @@ unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned
|
||||
// busywait(twi_dcount);
|
||||
}
|
||||
i = 0;
|
||||
while (!SDA_READ() && (i++) < 10)
|
||||
while (!SDA_READ(twi_sda) && (i++) < 10)
|
||||
{
|
||||
twi_scl_valley();
|
||||
busywait(twi_dcount);
|
||||
@ -442,32 +394,36 @@ unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Twi::twi_scl_valley(void)
|
||||
{
|
||||
SCL_LOW(twi_scl);
|
||||
busywait(twi_dcount);
|
||||
SCL_HIGH(twi_scl);
|
||||
WAIT_CLOCK_STRETCH();
|
||||
}
|
||||
|
||||
uint8_t Twi::status()
|
||||
{
|
||||
if (!SCL_READ())
|
||||
WAIT_CLOCK_STRETCH(); // wait for a slow slave to finish
|
||||
if (!SCL_READ(twi_scl))
|
||||
{
|
||||
return I2C_SCL_HELD_LOW; // SCL held low by another device, no procedure available to recover
|
||||
}
|
||||
|
||||
int clockCount = 20;
|
||||
while (!SDA_READ() && clockCount-- > 0) // if SDA low, read the bits slaves have to sent to a max
|
||||
while (!SDA_READ(twi_sda) && clockCount-- > 0) // if SDA low, read the bits slaves have to sent to a max
|
||||
{
|
||||
read_bit();
|
||||
if (!SCL_READ())
|
||||
if (!SCL_READ(twi_scl))
|
||||
{
|
||||
return I2C_SCL_HELD_LOW_AFTER_READ; // I2C bus error. SCL held low beyond slave clock stretch time
|
||||
}
|
||||
}
|
||||
if (!SDA_READ())
|
||||
if (!SDA_READ(twi_sda))
|
||||
{
|
||||
return I2C_SDA_HELD_LOW; // I2C bus error. SDA line held low by slave/another_master after n bits.
|
||||
}
|
||||
|
||||
if (!write_start())
|
||||
{
|
||||
return I2C_SDA_HELD_LOW_AFTER_INIT; // line busy. SDA again held low by another device. 2nd master?
|
||||
}
|
||||
|
||||
return I2C_OK;
|
||||
}
|
||||
|
||||
@ -507,42 +463,34 @@ void Twi::attachSlaveTxEvent(void (*function)(void))
|
||||
twi_onSlaveTransmit = function;
|
||||
}
|
||||
|
||||
inline void ICACHE_RAM_ATTR Twi::reply(uint8_t ack)
|
||||
// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function breakup into
|
||||
// parts and the ICACHE_RAM_ATTR isn't propagated correctly to all parts, which of course causes crashes.
|
||||
// TODO: test with gcc 9.x and if it still fails, disable optimization with -fdisable-ipa-fnsplit
|
||||
void ICACHE_RAM_ATTR Twi::reply(uint8_t ack)
|
||||
{
|
||||
// transmit master read ready signal, with or without ack
|
||||
if (ack)
|
||||
{
|
||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
|
||||
SCL_HIGH(); // _BV(TWINT)
|
||||
SCL_HIGH(twi.twi_scl); // _BV(TWINT)
|
||||
twi_ack = 1; // _BV(TWEA)
|
||||
}
|
||||
else
|
||||
{
|
||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
|
||||
SCL_HIGH(); // _BV(TWINT)
|
||||
SCL_HIGH(twi.twi_scl); // _BV(TWINT)
|
||||
twi_ack = 0; // ~_BV(TWEA)
|
||||
}
|
||||
}
|
||||
|
||||
inline void ICACHE_RAM_ATTR Twi::stop(void)
|
||||
{
|
||||
// send stop condition
|
||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);
|
||||
SCL_HIGH(); // _BV(TWINT)
|
||||
twi_ack = 1; // _BV(TWEA)
|
||||
busywait(5); // Maybe this should be here
|
||||
SDA_HIGH(); // _BV(TWSTO)
|
||||
// update twi state
|
||||
twi_state = TWI_READY;
|
||||
}
|
||||
|
||||
inline void ICACHE_RAM_ATTR Twi::releaseBus(void)
|
||||
void ICACHE_RAM_ATTR Twi::releaseBus(void)
|
||||
{
|
||||
// release bus
|
||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
|
||||
SCL_HIGH(); // _BV(TWINT)
|
||||
SCL_HIGH(twi.twi_scl); // _BV(TWINT)
|
||||
twi_ack = 1; // _BV(TWEA)
|
||||
SDA_HIGH();
|
||||
SDA_HIGH(twi.twi_sda);
|
||||
|
||||
// update twi state
|
||||
twi_state = TWI_READY;
|
||||
@ -623,11 +571,11 @@ void ICACHE_RAM_ATTR Twi::onTwipEvent(uint8_t status)
|
||||
bitCount--;
|
||||
if (twi_data & 0x80)
|
||||
{
|
||||
SDA_HIGH();
|
||||
SDA_HIGH(twi.twi_sda);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDA_LOW();
|
||||
SDA_LOW(twi.twi_sda);
|
||||
}
|
||||
twi_data <<= 1;
|
||||
|
||||
@ -652,19 +600,10 @@ void ICACHE_RAM_ATTR Twi::onTwipEvent(uint8_t status)
|
||||
break;
|
||||
case TW_BUS_ERROR: // bus error, illegal stop/start
|
||||
twi_error = TW_BUS_ERROR;
|
||||
stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Twi::twi_scl_valley(void)
|
||||
{
|
||||
SCL_LOW();
|
||||
busywait(twi_dcount);
|
||||
SCL_HIGH();
|
||||
WAIT_CLOCK_STRETCH();
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR Twi::onTimer(void *unused)
|
||||
{
|
||||
(void)unused;
|
||||
@ -721,8 +660,9 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void)
|
||||
unsigned int scl;
|
||||
|
||||
// Store bool return in int to reduce final code size.
|
||||
sda = twi.SDA_READ();
|
||||
scl = twi.SCL_READ();
|
||||
|
||||
sda = SDA_READ(twi.twi_sda);
|
||||
scl = SCL_READ(twi.twi_scl);
|
||||
|
||||
twi.twip_status = 0xF8; // reset TWI status
|
||||
|
||||
@ -765,7 +705,7 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
twi.SDA_LOW();
|
||||
SDA_LOW(twi.twi_sda);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -776,7 +716,7 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
twi.SDA_LOW();
|
||||
SDA_LOW(twi.twi_sda);
|
||||
}
|
||||
}
|
||||
twi.twip_state = TWIP_WAIT_ACK;
|
||||
@ -794,13 +734,13 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void)
|
||||
{
|
||||
if ((twi.twi_data & 0xFE) != twi.twi_addr)
|
||||
{
|
||||
twi.SDA_HIGH();
|
||||
SDA_HIGH(twi.twi_sda);
|
||||
twi.twip_state = TWIP_WAIT_STOP;
|
||||
}
|
||||
else
|
||||
{
|
||||
twi.SCL_LOW(); // clock stretching
|
||||
twi.SDA_HIGH();
|
||||
SCL_LOW(twi.twi_scl); // clock stretching
|
||||
SDA_HIGH(twi.twi_sda);
|
||||
twi.twip_mode = TWIPM_ADDRESSED;
|
||||
if (!(twi.twi_data & 0x01))
|
||||
{
|
||||
@ -817,8 +757,8 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
twi.SCL_LOW(); // clock stretching
|
||||
twi.SDA_HIGH();
|
||||
SCL_LOW(twi.twi_scl); // clock stretching
|
||||
SDA_HIGH(twi.twi_sda);
|
||||
if (!twi.twi_ack)
|
||||
{
|
||||
twi.onTwipEvent(TW_SR_DATA_NACK);
|
||||
@ -845,11 +785,11 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void)
|
||||
twi.bitCount--;
|
||||
if (twi.twi_data & 0x80)
|
||||
{
|
||||
twi.SDA_HIGH();
|
||||
SDA_HIGH(twi.twi_sda);
|
||||
}
|
||||
else
|
||||
{
|
||||
twi.SDA_LOW();
|
||||
SDA_LOW(twi.twi_sda);
|
||||
}
|
||||
twi.twi_data <<= 1;
|
||||
|
||||
@ -871,7 +811,7 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
twi.SDA_HIGH();
|
||||
SDA_HIGH(twi.twi_sda);
|
||||
twi.twip_state = TWIP_READ_ACK;
|
||||
}
|
||||
}
|
||||
@ -895,7 +835,7 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
twi.SCL_LOW(); // clock stretching
|
||||
SCL_LOW(twi.twi_scl); // clock stretching
|
||||
if (twi.twi_ack && twi.twi_ack_rec)
|
||||
{
|
||||
twi.onTwipEvent(TW_ST_DATA_ACK);
|
||||
@ -918,8 +858,8 @@ void ICACHE_RAM_ATTR Twi::onSdaChange(void)
|
||||
unsigned int scl;
|
||||
|
||||
// Store bool return in int to reduce final code size.
|
||||
sda = twi.SDA_READ();
|
||||
scl = twi.SCL_READ();
|
||||
sda = SDA_READ(twi.twi_sda);
|
||||
scl = SCL_READ(twi.twi_scl);
|
||||
|
||||
int twip_state_mask = S2M(twi.twip_state);
|
||||
if (scl) /* !DATA */
|
||||
@ -941,7 +881,7 @@ void ICACHE_RAM_ATTR Twi::onSdaChange(void)
|
||||
else IFSTATE(S2M(TWIP_START) | S2M(TWIP_REP_START) | S2M(TWIP_SEND_ACK) | S2M(TWIP_WAIT_ACK) | S2M(TWIP_SLA_R) | S2M(TWIP_REC_ACK) | S2M(TWIP_READ_ACK) | S2M(TWIP_RWAIT_ACK) | S2M(TWIP_WRITE))
|
||||
{
|
||||
// START or STOP
|
||||
twi.SDA_HIGH(); // Should not be necessary
|
||||
SDA_HIGH(twi.twi_sda); // Should not be necessary
|
||||
twi.onTwipEvent(TW_BUS_ERROR);
|
||||
twi.twip_mode = TWIPM_WAIT;
|
||||
twi.twip_state = TWIP_BUS_ERR;
|
||||
@ -951,11 +891,11 @@ void ICACHE_RAM_ATTR Twi::onSdaChange(void)
|
||||
if (sda)
|
||||
{
|
||||
// STOP
|
||||
twi.SCL_LOW(); // clock stretching
|
||||
SCL_LOW(twi.twi_scl); // generates a low SCL pulse after STOP
|
||||
ets_timer_disarm(&twi.timer);
|
||||
twi.twip_state = TWIP_IDLE;
|
||||
twi.twip_mode = TWIPM_IDLE;
|
||||
twi.SCL_HIGH();
|
||||
SCL_HIGH(twi.twi_scl);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -985,7 +925,7 @@ void ICACHE_RAM_ATTR Twi::onSdaChange(void)
|
||||
else
|
||||
{
|
||||
// during first bit in byte transfer - ok
|
||||
twi.SCL_LOW(); // clock stretching
|
||||
SCL_LOW(twi.twi_scl); // clock stretching
|
||||
twi.onTwipEvent(TW_SR_STOP);
|
||||
if (sda)
|
||||
{
|
||||
@ -1020,11 +960,6 @@ extern "C" {
|
||||
return twi.setAddress(a);
|
||||
}
|
||||
|
||||
void twi_stop(void)
|
||||
{
|
||||
twi.stop();
|
||||
}
|
||||
|
||||
void twi_setClock(unsigned int freq)
|
||||
{
|
||||
twi.setClock(freq);
|
||||
|
191
cores/esp8266/core_esp8266_spi_utils.cpp
Normal file
191
cores/esp8266/core_esp8266_spi_utils.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
core_esp8266_spi_utils.cpp
|
||||
|
||||
Copyright (c) 2019 Mike Nix. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
// register names
|
||||
#include "esp8266_peri.h"
|
||||
|
||||
// for flashchip
|
||||
#include "spi_flash.h"
|
||||
|
||||
// for PRECACHE_*
|
||||
#include "core_esp8266_features.h"
|
||||
|
||||
#include "spi_utils.h"
|
||||
|
||||
extern "C" uint32_t Wait_SPI_Idle(SpiFlashChip *fc);
|
||||
|
||||
namespace experimental {
|
||||
|
||||
/*
|
||||
* critical part of SPICommand.
|
||||
* Kept in a separate function to aid with precaching
|
||||
* PRECACHE_* saves having to make the function IRAM_ATTR.
|
||||
*
|
||||
* spiIfNum needs to be volatile to keep the optimiser from
|
||||
* deciding it can be treated as a constant (due to this being a
|
||||
* static function only called with spiIfNum set to 0)
|
||||
*
|
||||
* Note: if porting to ESP32 mosi/miso bits are set in 2 registers, not 1.
|
||||
*/
|
||||
static SpiOpResult PRECACHE_ATTR
|
||||
_SPICommand(volatile uint32_t spiIfNum,
|
||||
uint32_t spic,uint32_t spiu,uint32_t spiu1,uint32_t spiu2,
|
||||
uint32_t *data,uint32_t writeWords,uint32_t readWords)
|
||||
{
|
||||
if (spiIfNum>1)
|
||||
return SPI_RESULT_ERR;
|
||||
|
||||
// force SPI register access via base+offest.
|
||||
// Prevents loading individual address constants from flash.
|
||||
uint32_t *spibase = (uint32_t*)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD));
|
||||
#define SPIREG(reg) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD)))))
|
||||
|
||||
// preload any constants and functions we need into variables
|
||||
// Everything defined here must be volatile or the optimizer can
|
||||
// treat them as constants, resulting in the flash reads we're
|
||||
// trying to avoid
|
||||
uint32_t (* volatile Wait_SPI_Idlep)(SpiFlashChip *) = Wait_SPI_Idle;
|
||||
volatile SpiFlashChip *fchip=flashchip;
|
||||
volatile uint32_t spicmdusr=SPICMDUSR;
|
||||
|
||||
if (!spiIfNum) {
|
||||
// Only need to precache when using SPI0
|
||||
PRECACHE_START();
|
||||
Wait_SPI_Idlep((SpiFlashChip *)fchip);
|
||||
}
|
||||
|
||||
// preserve essential controller state such as incoming/outgoing
|
||||
// data lengths and IO mode.
|
||||
uint32_t oldSPI0U = SPIREG(SPI0U);
|
||||
uint32_t oldSPI0U2= SPIREG(SPI0U2);
|
||||
uint32_t oldSPI0C = SPIREG(SPI0C);
|
||||
|
||||
//SPI0S &= ~(SPISE|SPISBE|SPISSE|SPISCD);
|
||||
SPIREG(SPI0C) = spic;
|
||||
SPIREG(SPI0U) = spiu;
|
||||
SPIREG(SPI0U1)= spiu1;
|
||||
SPIREG(SPI0U2)= spiu2;
|
||||
|
||||
if (writeWords>0) {
|
||||
// copy the outgoing data to the SPI hardware
|
||||
uint32_t *src=data;
|
||||
volatile uint32_t *dst=&SPIREG(SPI0W0);
|
||||
for (uint32_t i=0; i<writeWords; i++)
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
// Start the transfer
|
||||
SPIREG(SPI0CMD) = spicmdusr;
|
||||
|
||||
// wait for the command to complete (typically only 1-3 iterations)
|
||||
uint32_t timeout = 1000;
|
||||
while ((SPIREG(SPI0CMD) & spicmdusr) && timeout--);
|
||||
|
||||
if ((readWords>0) && (timeout>0)) {
|
||||
// copy the response back to the buffer
|
||||
uint32_t *dst=data;
|
||||
volatile uint32_t *src=&SPIREG(SPI0W0);
|
||||
for (uint32_t i=0; i<readWords; i++)
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
// Restore saved registers
|
||||
SPIREG(SPI0U) = oldSPI0U;
|
||||
SPIREG(SPI0U2)= oldSPI0U2;
|
||||
SPIREG(SPI0C) = oldSPI0C;
|
||||
|
||||
PRECACHE_END();
|
||||
return (timeout>0 ? SPI_RESULT_OK : SPI_RESULT_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
/* SPI0Command: send a custom SPI command.
|
||||
* This part calculates register values and passes them to _SPI0Command().
|
||||
* Parameters:
|
||||
* cmd The command byte (first 8 bits) to send to the SPI device
|
||||
* *data The buffer containing the outgoing data for the SPI bus.
|
||||
* The data is expected to be mosi_bits long, and the buffer
|
||||
* is overwritten by the incoming bus data, which will be
|
||||
* miso_bits long.
|
||||
* mosi_bits
|
||||
* Number of bits to be sent after the command byte.
|
||||
* miso_bits
|
||||
* Number of bits to read from the SPI bus after the outgoing
|
||||
* data has been sent.
|
||||
*
|
||||
* Note: This code has only been tested with SPI bus 0, but should work
|
||||
* equally well with other busses. The ESP8266 has bus 0 and 1,
|
||||
* newer chips may have more one day.
|
||||
*/
|
||||
SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits) {
|
||||
if (mosi_bits>(64*8))
|
||||
return SPI_RESULT_ERR;
|
||||
if (miso_bits>(64*8))
|
||||
return SPI_RESULT_ERR;
|
||||
|
||||
// Calculate the number of data words (aka registers) that need to be copied
|
||||
// to/from the SPI controller.
|
||||
uint32_t mosi_words=mosi_bits/32;
|
||||
uint32_t miso_words=miso_bits/32;
|
||||
if (mosi_bits % 32 != 0)
|
||||
mosi_words++;
|
||||
if (miso_bits % 32 != 0)
|
||||
miso_words++;
|
||||
|
||||
// Select user defined command mode in the controller
|
||||
uint32_t spiu=SPIUCOMMAND; //SPI_USR_COMMAND
|
||||
|
||||
// Set the command byte to send
|
||||
uint32_t spiu2 = ((7 & SPIMCOMMAND)<<SPILCOMMAND) | cmd;
|
||||
|
||||
uint32_t spiu1 = 0;
|
||||
if (mosi_bits>0) {
|
||||
// set the number of outgoing data bits to send
|
||||
spiu1 |= ((mosi_bits-1) & SPIMMOSI) << SPILMOSI;
|
||||
spiu |= SPIUMOSI; // SPI_USR_MOSI
|
||||
}
|
||||
if (miso_bits>0) {
|
||||
// set the number of incoming bits to read
|
||||
spiu1 |= ((miso_bits-1) & SPIMMISO) << SPILMISO;
|
||||
spiu |= SPIUMISO; // SPI_USR_MISO
|
||||
}
|
||||
|
||||
uint32_t spic = SPI0C;
|
||||
// Select the most basic IO mode for maximum compatibility
|
||||
// Some flash commands are only available in this mode.
|
||||
spic &= ~(SPICQIO | SPICDIO | SPICQOUT | SPICDOUT | SPICAHB | SPICFASTRD);
|
||||
spic |= (SPICRESANDRES | SPICSHARE | SPICWPR | SPIC2BSE);
|
||||
|
||||
SpiOpResult rc =_SPICommand(0,spic,spiu,spiu1,spiu2,data,mosi_words,miso_words);
|
||||
|
||||
if (rc==SPI_RESULT_OK) {
|
||||
// clear any bits we did not read in the last word.
|
||||
if (miso_bits % 32) {
|
||||
data[miso_bits/32] &= ~(0xFFFFFFFF << (miso_bits % 32));
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
} // namespace experimental
|
@ -30,7 +30,7 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
uint8_t esp8266_gpioToFn[16] = {0x34, 0x18, 0x38, 0x14, 0x3C, 0x40, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x04, 0x08, 0x0C, 0x10};
|
||||
volatile uint32_t* const esp8266_gpioToFn[16] PROGMEM = { &GPF0, &GPF1, &GPF2, &GPF3, &GPF4, &GPF5, &GPF6, &GPF7, &GPF8, &GPF9, &GPF10, &GPF11, &GPF12, &GPF13, &GPF14, &GPF15 };
|
||||
|
||||
extern void __pinMode(uint8_t pin, uint8_t mode) {
|
||||
if(pin < 16){
|
||||
|
@ -13,11 +13,14 @@ extern "C" {
|
||||
#include <cont.h> // g_pcont declaration
|
||||
|
||||
extern bool timeshift64_is_set;
|
||||
extern uint32_t sntp_real_timestamp;
|
||||
|
||||
bool can_yield();
|
||||
void esp_yield();
|
||||
void esp_schedule();
|
||||
void tune_timeshift64 (uint64_t now_us);
|
||||
void disable_extra4k_at_link_time (void) __attribute__((noinline));
|
||||
bool sntp_set_timezone_in_seconds(int32_t timezone);
|
||||
|
||||
uint32_t sqrt32 (uint32_t n);
|
||||
uint32_t crc32 (const void* data, size_t length, uint32_t crc = 0xffffffff);
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "coredecls.h"
|
||||
#include "pgmspace.h"
|
||||
|
||||
// moved from core_esp8266_eboot_command.cpp
|
||||
uint32_t crc32 (const void* data, size_t length, uint32_t crc /*= 0xffffffff*/)
|
||||
@ -27,7 +28,7 @@ uint32_t crc32 (const void* data, size_t length, uint32_t crc /*= 0xffffffff*/)
|
||||
const uint8_t* ldata = (const uint8_t*)data;
|
||||
while (length--)
|
||||
{
|
||||
uint8_t c = *ldata++;
|
||||
uint8_t c = pgm_read_byte(ldata++);
|
||||
for (uint32_t i = 0x80; i > 0; i >>= 1)
|
||||
{
|
||||
bool bit = crc & 0x80000000;
|
||||
|
@ -102,8 +102,8 @@
|
||||
#define GPF14 ESP8266_REG(0x80C)
|
||||
#define GPF15 ESP8266_REG(0x810)
|
||||
|
||||
extern uint8_t esp8266_gpioToFn[16];
|
||||
#define GPF(p) ESP8266_REG(0x800 + esp8266_gpioToFn[(p & 0xF)])
|
||||
extern volatile uint32_t* const esp8266_gpioToFn[16];
|
||||
#define GPF(p) (*esp8266_gpioToFn[(p & 0xF)])
|
||||
|
||||
//GPIO (0-15) PIN Function Bits
|
||||
#define GPFSOE 0 //Sleep OE
|
||||
|
@ -5,7 +5,6 @@ This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include <pgmspace.h>
|
||||
#include "cencode.h"
|
||||
|
||||
extern "C" {
|
||||
@ -23,10 +22,20 @@ void base64_init_encodestate_nonewlines(base64_encodestate* state_in){
|
||||
state_in->stepsnewline = -1;
|
||||
}
|
||||
|
||||
char base64_encode_value(char value_in){
|
||||
static const char encoding[] PROGMEM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
if (value_in > 63) return '=';
|
||||
return pgm_read_byte( &encoding[(int)value_in] );
|
||||
char base64_encode_value(const char n) {
|
||||
char r;
|
||||
|
||||
if (n < 26)
|
||||
r = n + 'A';
|
||||
else if (n < 26 + 26)
|
||||
r = n - 26 + 'a';
|
||||
else if (n < 26 + 26 + 10 )
|
||||
r = n - 26 - 26 + '0';
|
||||
else if (n == 62 )
|
||||
r = '+';
|
||||
else
|
||||
r = '/';
|
||||
return r;
|
||||
}
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in){
|
||||
|
@ -31,7 +31,7 @@
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <../include/time.h> // See issue #6714
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
@ -38,7 +38,9 @@
|
||||
*/
|
||||
|
||||
#include <lwip/init.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <osapi.h>
|
||||
#include <os_type.h>
|
||||
#include "coredecls.h"
|
||||
@ -64,7 +66,7 @@ extern "C" {
|
||||
|
||||
static const char stod14[] PROGMEM = "settimeofday() can't set time!\n";
|
||||
bool sntp_set_timezone(sint8 timezone);
|
||||
bool sntp_set_timezone_in_seconds(sint32 timezone)
|
||||
bool sntp_set_timezone_in_seconds(int32_t timezone)
|
||||
{
|
||||
return sntp_set_timezone((sint8)(timezone/(60*60))); //TODO: move this to the same file as sntp_set_timezone() in lwip1.4, and implement correctly over there.
|
||||
}
|
||||
@ -98,394 +100,31 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
||||
|
||||
#include <lwip/apps/sntp.h>
|
||||
|
||||
static uint32 realtime_stamp = 0;
|
||||
static uint16 dst = 0;
|
||||
static sint32 time_zone = 8 * (60 * 60); // espressif HQ's default timezone
|
||||
uint32_t sntp_real_timestamp = 0;
|
||||
LOCAL os_timer_t sntp_timer;
|
||||
|
||||
/*****************************************/
|
||||
#define SECSPERMIN 60L
|
||||
#define MINSPERHOUR 60L
|
||||
#define HOURSPERDAY 24L
|
||||
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
|
||||
#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY)
|
||||
#define DAYSPERWEEK 7
|
||||
#define MONSPERYEAR 12
|
||||
|
||||
#define YEAR_BASE 1900
|
||||
#define EPOCH_YEAR 1970
|
||||
#define EPOCH_WDAY 4
|
||||
#define EPOCH_YEARS_SINCE_LEAP 2
|
||||
#define EPOCH_YEARS_SINCE_CENTURY 70
|
||||
#define EPOCH_YEARS_SINCE_LEAP_CENTURY 370
|
||||
|
||||
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
|
||||
|
||||
int __tznorth;
|
||||
int __tzyear;
|
||||
char reult[100];
|
||||
static const int mon_lengths[2][12] = {
|
||||
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
|
||||
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
||||
} ;
|
||||
|
||||
static const int year_lengths[2] = {
|
||||
365,
|
||||
366
|
||||
} ;
|
||||
struct tm
|
||||
{
|
||||
int tm_sec;
|
||||
int tm_min;
|
||||
int tm_hour;
|
||||
int tm_mday;
|
||||
int tm_mon;
|
||||
int tm_year;
|
||||
int tm_wday;
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
};
|
||||
|
||||
struct tm res_buf;
|
||||
typedef struct __tzrule_struct
|
||||
{
|
||||
char ch;
|
||||
int m;
|
||||
int n;
|
||||
int d;
|
||||
int s;
|
||||
time_t change;
|
||||
int offset;
|
||||
} __tzrule_type;
|
||||
|
||||
__tzrule_type sntp__tzrule[2];
|
||||
struct tm *
|
||||
sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime)
|
||||
{
|
||||
long days, rem;
|
||||
time_t lcltime;
|
||||
int y;
|
||||
int yleap;
|
||||
const int *ip;
|
||||
|
||||
/* base decision about std/dst time on current time */
|
||||
lcltime = *tim_p;
|
||||
|
||||
days = ((long)lcltime) / SECSPERDAY;
|
||||
rem = ((long)lcltime) % SECSPERDAY;
|
||||
while (rem < 0)
|
||||
{
|
||||
rem += SECSPERDAY;
|
||||
--days;
|
||||
}
|
||||
while (rem >= SECSPERDAY)
|
||||
{
|
||||
rem -= SECSPERDAY;
|
||||
++days;
|
||||
}
|
||||
|
||||
/* compute hour, min, and sec */
|
||||
res->tm_hour = (int) (rem / SECSPERHOUR);
|
||||
rem %= SECSPERHOUR;
|
||||
res->tm_min = (int) (rem / SECSPERMIN);
|
||||
res->tm_sec = (int) (rem % SECSPERMIN);
|
||||
|
||||
/* compute day of week */
|
||||
if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
|
||||
res->tm_wday += DAYSPERWEEK;
|
||||
|
||||
/* compute year & day of year */
|
||||
y = EPOCH_YEAR;
|
||||
if (days >= 0)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
yleap = isleap(y);
|
||||
if (days < year_lengths[yleap])
|
||||
break;
|
||||
y++;
|
||||
days -= year_lengths[yleap];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
--y;
|
||||
yleap = isleap(y);
|
||||
days += year_lengths[yleap];
|
||||
} while (days < 0);
|
||||
}
|
||||
|
||||
res->tm_year = y - YEAR_BASE;
|
||||
res->tm_yday = days;
|
||||
ip = mon_lengths[yleap];
|
||||
for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon)
|
||||
days -= ip[res->tm_mon];
|
||||
res->tm_mday = days + 1;
|
||||
|
||||
if (!is_gmtime)
|
||||
{
|
||||
int offset;
|
||||
int hours, mins, secs;
|
||||
|
||||
// TZ_LOCK;
|
||||
// if (_daylight)
|
||||
// {
|
||||
// if (y == __tzyear || __tzcalc_limits (y))
|
||||
// res->tm_isdst = (__tznorth
|
||||
// ? (*tim_p >= __tzrule[0].change && *tim_p < __tzrule[1].change)
|
||||
// : (*tim_p >= __tzrule[0].change || *tim_p < __tzrule[1].change));
|
||||
// else
|
||||
// res->tm_isdst = -1;
|
||||
// }
|
||||
// else
|
||||
res->tm_isdst = -1;
|
||||
|
||||
offset = (res->tm_isdst == 1 ? sntp__tzrule[1].offset : sntp__tzrule[0].offset);
|
||||
|
||||
hours = offset / SECSPERHOUR;
|
||||
offset = offset % SECSPERHOUR;
|
||||
|
||||
mins = offset / SECSPERMIN;
|
||||
secs = offset % SECSPERMIN;
|
||||
|
||||
res->tm_sec -= secs;
|
||||
res->tm_min -= mins;
|
||||
res->tm_hour -= hours;
|
||||
|
||||
if (res->tm_sec >= SECSPERMIN)
|
||||
{
|
||||
res->tm_min += 1;
|
||||
res->tm_sec -= SECSPERMIN;
|
||||
}
|
||||
else if (res->tm_sec < 0)
|
||||
{
|
||||
res->tm_min -= 1;
|
||||
res->tm_sec += SECSPERMIN;
|
||||
}
|
||||
if (res->tm_min >= MINSPERHOUR)
|
||||
{
|
||||
res->tm_hour += 1;
|
||||
res->tm_min -= MINSPERHOUR;
|
||||
}
|
||||
else if (res->tm_min < 0)
|
||||
{
|
||||
res->tm_hour -= 1;
|
||||
res->tm_min += MINSPERHOUR;
|
||||
}
|
||||
if (res->tm_hour >= HOURSPERDAY)
|
||||
{
|
||||
++res->tm_yday;
|
||||
++res->tm_wday;
|
||||
if (res->tm_wday > 6)
|
||||
res->tm_wday = 0;
|
||||
++res->tm_mday;
|
||||
res->tm_hour -= HOURSPERDAY;
|
||||
if (res->tm_mday > ip[res->tm_mon])
|
||||
{
|
||||
res->tm_mday -= ip[res->tm_mon];
|
||||
res->tm_mon += 1;
|
||||
if (res->tm_mon == 12)
|
||||
{
|
||||
res->tm_mon = 0;
|
||||
res->tm_year += 1;
|
||||
res->tm_yday = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (res->tm_hour < 0)
|
||||
{
|
||||
res->tm_yday -= 1;
|
||||
res->tm_wday -= 1;
|
||||
if (res->tm_wday < 0)
|
||||
res->tm_wday = 6;
|
||||
res->tm_mday -= 1;
|
||||
res->tm_hour += 24;
|
||||
if (res->tm_mday == 0)
|
||||
{
|
||||
res->tm_mon -= 1;
|
||||
if (res->tm_mon < 0)
|
||||
{
|
||||
res->tm_mon = 11;
|
||||
res->tm_year -= 1;
|
||||
res->tm_yday = 365 + isleap(res->tm_year);
|
||||
}
|
||||
res->tm_mday = ip[res->tm_mon];
|
||||
}
|
||||
}
|
||||
// TZ_UNLOCK;
|
||||
}
|
||||
else
|
||||
res->tm_isdst = 0;
|
||||
// os_printf("res %d %d %d %d %d\n",res->tm_year,res->tm_mon,res->tm_mday,res->tm_yday,res->tm_hour);
|
||||
return (res);
|
||||
}
|
||||
struct tm *
|
||||
sntp_localtime_r(const time_t * tim_p ,
|
||||
struct tm *res)
|
||||
{
|
||||
return sntp_mktm_r (tim_p, res, 0);
|
||||
}
|
||||
|
||||
struct tm *
|
||||
sntp_localtime(const time_t * tim_p)
|
||||
{
|
||||
return sntp_localtime_r (tim_p, &res_buf);
|
||||
}
|
||||
|
||||
int sntp__tzcalc_limits(int year)
|
||||
{
|
||||
int days, year_days, years;
|
||||
int i, j;
|
||||
|
||||
if (year < EPOCH_YEAR)
|
||||
return 0;
|
||||
|
||||
__tzyear = year;
|
||||
|
||||
years = (year - EPOCH_YEAR);
|
||||
|
||||
year_days = years * 365 +
|
||||
(years - 1 + EPOCH_YEARS_SINCE_LEAP) / 4 - (years - 1 + EPOCH_YEARS_SINCE_CENTURY) / 100 +
|
||||
(years - 1 + EPOCH_YEARS_SINCE_LEAP_CENTURY) / 400;
|
||||
|
||||
for (i = 0; i < 2; ++i)
|
||||
{
|
||||
if (sntp__tzrule[i].ch == 'J')
|
||||
days = year_days + sntp__tzrule[i].d + (isleap(year) && sntp__tzrule[i].d >= 60);
|
||||
else if (sntp__tzrule[i].ch == 'D')
|
||||
days = year_days + sntp__tzrule[i].d;
|
||||
else
|
||||
{
|
||||
int yleap = isleap(year);
|
||||
int m_day, m_wday, wday_diff;
|
||||
const int *ip = mon_lengths[yleap];
|
||||
|
||||
days = year_days;
|
||||
|
||||
for (j = 1; j < sntp__tzrule[i].m; ++j)
|
||||
days += ip[j-1];
|
||||
|
||||
m_wday = (EPOCH_WDAY + days) % DAYSPERWEEK;
|
||||
|
||||
wday_diff = sntp__tzrule[i].d - m_wday;
|
||||
if (wday_diff < 0)
|
||||
wday_diff += DAYSPERWEEK;
|
||||
m_day = (sntp__tzrule[i].n - 1) * DAYSPERWEEK + wday_diff;
|
||||
|
||||
while (m_day >= ip[j-1])
|
||||
m_day -= DAYSPERWEEK;
|
||||
|
||||
days += m_day;
|
||||
}
|
||||
|
||||
/* store the change-over time in GMT form by adding offset */
|
||||
sntp__tzrule[i].change = days * SECSPERDAY + sntp__tzrule[i].s + sntp__tzrule[i].offset;
|
||||
}
|
||||
|
||||
__tznorth = (sntp__tzrule[0].change < sntp__tzrule[1].change);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char* sntp_asctime_r(struct tm *tim_p ,char *result)
|
||||
{
|
||||
static const char day_name[7][4] = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
static const char mon_name[12][4] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
os_sprintf (result, "%s %s %02d %02d:%02d:%02d %02d\n",
|
||||
day_name[tim_p->tm_wday],
|
||||
mon_name[tim_p->tm_mon],
|
||||
tim_p->tm_mday, tim_p->tm_hour, tim_p->tm_min,
|
||||
tim_p->tm_sec, 1900 + tim_p->tm_year);
|
||||
return result;
|
||||
}
|
||||
|
||||
char* sntp_asctime(struct tm *tim_p)
|
||||
{
|
||||
return sntp_asctime_r (tim_p, reult);
|
||||
}
|
||||
|
||||
uint32 ICACHE_RAM_ATTR sntp_get_current_timestamp(void)
|
||||
{
|
||||
return realtime_stamp;
|
||||
}
|
||||
|
||||
char* sntp_get_real_time(time_t t)
|
||||
{
|
||||
return sntp_asctime(sntp_localtime (&t));
|
||||
}
|
||||
|
||||
/* Returns the set timezone in seconds. If the timezone was set as seconds, the fractional part is floored. */
|
||||
sint32 sntp_get_timezone_in_seconds(void)
|
||||
{
|
||||
return time_zone;
|
||||
}
|
||||
|
||||
/* Returns the set timezone in hours. If the timezone was set as seconds, the fractional part is floored. */
|
||||
sint8 sntp_get_timezone(void)
|
||||
{
|
||||
return (sint8)(time_zone / (60 * 60));
|
||||
}
|
||||
|
||||
/* Sets the timezone in hours. Internally, the timezone is converted to seconds. */
|
||||
bool sntp_set_timezone_in_seconds(sint32 timezone)
|
||||
{
|
||||
if(timezone >= (-11 * (60 * 60)) || timezone <= (13 * (60 * 60))) {
|
||||
time_zone = timezone;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Sets the timezone in hours. Internally, the timezone is converted to seconds. */
|
||||
bool sntp_set_timezone(sint8 timezone)
|
||||
{
|
||||
return sntp_set_timezone_in_seconds((sint32)timezone * 60 * 60);
|
||||
}
|
||||
|
||||
|
||||
void sntp_set_daylight(int daylight)
|
||||
{
|
||||
dst = daylight;
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR sntp_time_inc (void)
|
||||
{
|
||||
realtime_stamp++;
|
||||
}
|
||||
|
||||
static void sntp_set_system_time (uint32_t t)
|
||||
{
|
||||
realtime_stamp = t + time_zone + dst;
|
||||
os_timer_disarm(&sntp_timer);
|
||||
os_timer_setfn(&sntp_timer, (os_timer_func_t *)sntp_time_inc, NULL);
|
||||
os_timer_arm(&sntp_timer, 1000, 1);
|
||||
sntp_real_timestamp++;
|
||||
}
|
||||
|
||||
int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
||||
{
|
||||
if (tz) /*before*/
|
||||
{
|
||||
sntp_set_timezone_in_seconds(tz->tz_minuteswest * 60);
|
||||
// apparently tz->tz_dsttime is a bitfield and should not be further used (cf man)
|
||||
sntp_set_daylight(0);
|
||||
}
|
||||
if (tv) /* after*/
|
||||
{
|
||||
if (tz || !tv)
|
||||
// tz is obsolete (cf. man settimeofday)
|
||||
return EINVAL;
|
||||
|
||||
// reset time subsystem
|
||||
tune_timeshift64(tv->tv_sec * 1000000ULL + tv->tv_usec);
|
||||
|
||||
sntp_set_system_time(tv->tv_sec);
|
||||
sntp_real_timestamp = tv->tv_sec;
|
||||
os_timer_disarm(&sntp_timer);
|
||||
os_timer_setfn(&sntp_timer, (os_timer_func_t *)sntp_time_inc, NULL);
|
||||
os_timer_arm(&sntp_timer, 1000, 1);
|
||||
|
||||
if (_settimeofday_cb)
|
||||
schedule_function(_settimeofday_cb);
|
||||
}
|
||||
schedule_recurrent_function_us([](){ _settimeofday_cb(); return false; }, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,6 @@
|
||||
#ifndef __sntp_lwip2_h__
|
||||
#define __sntp_lwip2_h__
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool sntp_set_timezone_in_seconds(sint32 timezone);
|
||||
|
||||
}
|
||||
#include <coredecls.h>
|
||||
|
||||
#endif
|
||||
|
46
cores/esp8266/spi_utils.h
Normal file
46
cores/esp8266/spi_utils.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
spi_utils.h - SPI utility function
|
||||
Copyright (c) 2015 Ivan Grokhotkov. All right 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
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SPI_UTILS_H
|
||||
#define SPI_UTILS_H
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace experimental {
|
||||
typedef enum {
|
||||
SPI_RESULT_OK,
|
||||
SPI_RESULT_ERR,
|
||||
SPI_RESULT_TIMEOUT
|
||||
} SpiOpResult;
|
||||
|
||||
SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif //SPI_UTILS_H
|
@ -141,6 +141,14 @@ FS SPIFFS = FS(FSImplPtr(new spiffs_impl::SPIFFSImpl(
|
||||
FS_PHYS_PAGE,
|
||||
FS_PHYS_BLOCK,
|
||||
SPIFFS_MAX_OPEN_FILES)));
|
||||
|
||||
extern "C" void spiffs_request_end(void)
|
||||
{
|
||||
// override default weak function
|
||||
//ets_printf("debug: not weak spiffs end\n");
|
||||
SPIFFS.end();
|
||||
}
|
||||
|
||||
#endif // ARDUINO
|
||||
#endif // !CORE_MOCK
|
||||
|
||||
|
@ -80,6 +80,11 @@ public:
|
||||
memset(&_fs, 0, sizeof(_fs));
|
||||
}
|
||||
|
||||
~SPIFFSImpl()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) override;
|
||||
bool exists(const char* path) override;
|
||||
DirImplPtr openDir(const char* path) override;
|
||||
@ -162,7 +167,7 @@ public:
|
||||
|
||||
bool setConfig(const FSConfig &cfg) override
|
||||
{
|
||||
if ((cfg._type != SPIFFSConfig::fsid::FSId) || (SPIFFS_mounted(&_fs) != 0)) {
|
||||
if ((cfg._type != SPIFFSConfig::FSId) || (SPIFFS_mounted(&_fs) != 0)) {
|
||||
return false;
|
||||
}
|
||||
_cfg = *static_cast<const SPIFFSConfig *>(&cfg);
|
||||
|
@ -17,12 +17,14 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <../include/time.h> // See issue #6714
|
||||
#include <sys/time.h>
|
||||
#include <sys/reent.h>
|
||||
#include "sntp.h"
|
||||
#include "coredecls.h"
|
||||
|
||||
#include <Arduino.h> // configTime()
|
||||
|
||||
#include "sntp-lwip2.h"
|
||||
|
||||
extern "C" {
|
||||
@ -71,14 +73,45 @@ int clock_gettime(clockid_t unused, struct timespec *tp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LWIP_VERSION_MAJOR == 1
|
||||
// hack for espressif time management included in patched lwIP-1.4
|
||||
#define sntp_real_timestamp sntp_get_current_timestamp()
|
||||
#endif
|
||||
|
||||
#if LWIP_VERSION_MAJOR != 1
|
||||
|
||||
// backport Espressif api
|
||||
|
||||
bool sntp_set_timezone_in_seconds (int32_t timezone_sec)
|
||||
{
|
||||
configTime(timezone_sec, 0, sntp_getservername(0), sntp_getservername(1), sntp_getservername(2));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sntp_set_timezone(sint8 timezone_in_hours)
|
||||
{
|
||||
return sntp_set_timezone_in_seconds(3600 * ((int)timezone_in_hours));
|
||||
}
|
||||
|
||||
char* sntp_get_real_time(time_t t)
|
||||
{
|
||||
return ctime(&t);
|
||||
}
|
||||
|
||||
uint32 sntp_get_current_timestamp()
|
||||
{
|
||||
return sntp_real_timestamp;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
time_t time(time_t * t)
|
||||
{
|
||||
time_t seconds = sntp_get_current_timestamp();
|
||||
if (t)
|
||||
{
|
||||
*t = seconds;
|
||||
*t = sntp_real_timestamp;
|
||||
}
|
||||
return seconds;
|
||||
return sntp_real_timestamp;
|
||||
}
|
||||
|
||||
int _gettimeofday_r(struct _reent* unused, struct timeval *tp, void *tzp)
|
||||
@ -88,7 +121,7 @@ int _gettimeofday_r(struct _reent* unused, struct timeval *tp, void *tzp)
|
||||
if (tp)
|
||||
{
|
||||
if (!timeshift64_is_set)
|
||||
tune_timeshift64(sntp_get_current_timestamp() * 1000000ULL);
|
||||
tune_timeshift64(sntp_real_timestamp * 1000000ULL);
|
||||
uint64_t currentTime_us = timeshift64 + micros64();
|
||||
tp->tv_sec = currentTime_us / 1000000ULL;
|
||||
tp->tv_usec = currentTime_us % 1000000ULL;
|
||||
@ -100,15 +133,57 @@ int _gettimeofday_r(struct _reent* unused, struct timeval *tp, void *tzp)
|
||||
|
||||
void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
|
||||
{
|
||||
sntp_stop();
|
||||
// There is no way to tell when DST starts or stop with this API
|
||||
// So DST is always integrated in TZ
|
||||
// The other API should be preferred
|
||||
|
||||
/*** portable version using posix API
|
||||
(calls sprintf here, then sscanf internally)
|
||||
|
||||
int tzs = daylightOffset_sec + timezone_sec;
|
||||
int tzh = tzs / 3600;
|
||||
tzs -= tzh * 3600;
|
||||
int tzm = tzs / 60;
|
||||
tzs -= tzm * 60;
|
||||
|
||||
// man tzset:
|
||||
char tzstr [64];
|
||||
snprintf(tzstr, sizeof tzstr, "ESPUSER<%+d:%02d:%02d>", tzh, tzm, tzs);
|
||||
return configTime(tzstr, server1, server2, server3);
|
||||
|
||||
Replaced by light code found from
|
||||
newlib inspection and internal structure hacking
|
||||
(no sprintf, no sscanf, -7584 flash bytes):
|
||||
|
||||
***/
|
||||
|
||||
static char gmt[] = "GMT";
|
||||
|
||||
_timezone = timezone_sec + daylightOffset_sec;
|
||||
_daylight = 0;
|
||||
_tzname[0] = gmt;
|
||||
_tzname[1] = gmt;
|
||||
auto tz = __gettzinfo();
|
||||
tz->__tznorth = 1;
|
||||
tz->__tzyear = 0;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
auto tzr = &tz->__tzrule[i];
|
||||
tzr->ch = 74;
|
||||
tzr->m = 0;
|
||||
tzr->n = 0;
|
||||
tzr->d = 0;
|
||||
tzr->s = 0;
|
||||
tzr->change = 0;
|
||||
tzr->offset = _timezone;
|
||||
}
|
||||
|
||||
// sntp servers
|
||||
setServer(0, server1);
|
||||
setServer(1, server2);
|
||||
setServer(2, server3);
|
||||
|
||||
sntp_set_timezone_in_seconds(timezone_sec);
|
||||
sntp_set_daylight(daylightOffset_sec);
|
||||
sntp_init();
|
||||
/*** end of posix replacement ***/
|
||||
}
|
||||
|
||||
void configTime(const char* tz, const char* server1, const char* server2, const char* server3)
|
||||
@ -118,7 +193,6 @@ void configTime(const char* tz, const char* server1, const char* server2, const
|
||||
setServer(0, server1);
|
||||
setServer(1, server2);
|
||||
setServer(2, server3);
|
||||
|
||||
char tzram[strlen_P(tz) + 1];
|
||||
memcpy_P(tzram, tz, sizeof(tzram));
|
||||
setenv("TZ", tzram, 1/*overwrite*/);
|
||||
|
@ -577,7 +577,7 @@ uart_get_baudrate(uart_t* uart)
|
||||
}
|
||||
|
||||
uart_t*
|
||||
uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size)
|
||||
uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size, bool invert)
|
||||
{
|
||||
uart_t* uart = (uart_t*) malloc(sizeof(uart_t));
|
||||
if(uart == NULL)
|
||||
@ -657,6 +657,10 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx
|
||||
}
|
||||
|
||||
uart_set_baudrate(uart, baudrate);
|
||||
if(uart->uart_nr == UART0 && invert)
|
||||
{
|
||||
config |= BIT(UCDTRI) | BIT(UCRTSI) | BIT(UCTXI) | BIT(UCDSRI) | BIT(UCCTSI) | BIT(UCRXI);
|
||||
}
|
||||
USC0(uart->uart_nr) = config;
|
||||
|
||||
if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) {
|
||||
|
@ -113,7 +113,7 @@ extern "C" {
|
||||
struct uart_;
|
||||
typedef struct uart_ uart_t;
|
||||
|
||||
uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size);
|
||||
uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size, bool invert);
|
||||
void uart_uninit(uart_t* uart);
|
||||
|
||||
void uart_swap(uart_t* uart, int tx_pin);
|
||||
|
@ -162,6 +162,10 @@ UMM_STATISTICS ummStats;
|
||||
|
||||
// Keep complete call path in IRAM
|
||||
size_t umm_free_heap_size_lw( void ) {
|
||||
if (umm_heap == NULL) {
|
||||
umm_init();
|
||||
}
|
||||
|
||||
return (size_t)ummStats.free_blocks * sizeof(umm_block);
|
||||
}
|
||||
#endif
|
||||
@ -211,5 +215,3 @@ int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) {
|
||||
}
|
||||
|
||||
#endif // BUILD_UMM_MALLOC_C
|
||||
|
||||
|
||||
|
@ -145,7 +145,7 @@ constructor:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
String(const char *cstr = ""); // constructor from const char *
|
||||
String(const char *cstr = nullptr); // constructor from const char *
|
||||
String(const String &str); // copy constructor
|
||||
String(const __FlashStringHelper *str); // constructor for flash strings
|
||||
|
||||
@ -237,6 +237,42 @@ the value back.
|
||||
}
|
||||
}
|
||||
|
||||
How do I declare Arrays of strings in PROGMEM and retrieve an element from it.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
It is often convenient when working with large amounts of text, such as a project with an LCD display, to setup an array of strings. Because strings themselves are arrays, this is actually an example of a two-dimensional array.
|
||||
|
||||
These tend to be large structures so putting them into program memory is often desirable. The code below illustrates the idea.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
// Define Strings
|
||||
const char string_0[] PROGMEM = "String 0";
|
||||
const char string_1[] PROGMEM = "String 1";
|
||||
const char string_2[] PROGMEM = "String 2";
|
||||
const char string_3[] PROGMEM = "String 3";
|
||||
const char string_4[] PROGMEM = "String 4";
|
||||
const char string_5[] PROGMEM = "String 5";
|
||||
|
||||
// Initialize Table of Strings
|
||||
const char* const string_table[] PROGMEM = { string_0, string_1, string_2, string_3, string_4, string_5 };
|
||||
|
||||
char buffer[30]; // buffer for reading the string to (needs to be large enough to take the longest string
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println("OK");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
strcpy_P(buffer, (char*)pgm_read_dword(&(string_table[i])));
|
||||
Serial.println(buffer);
|
||||
delay(500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
In summary
|
||||
----------
|
||||
|
||||
|
@ -317,6 +317,11 @@ SparkFun ESP8266 Thing Dev
|
||||
|
||||
Product page: https://www.sparkfun.com/products/13711
|
||||
|
||||
SparkFun Blynk Board
|
||||
--------------------
|
||||
|
||||
Product page: https://www.sparkfun.com/products/13794
|
||||
|
||||
SweetPea ESP-210
|
||||
----------------
|
||||
|
||||
@ -447,3 +452,30 @@ Initially designed for kids in mind, everybody should be able to use it. Yet it'
|
||||
|
||||
More details at https://shop.makestro.com/product/espectrocore/
|
||||
|
||||
Schirmilabs Eduino WiFi
|
||||
-----------------------
|
||||
|
||||
Eduino WiFi is an Arduino-compatible DIY WiFi development board using an ESP-12 module
|
||||
|
||||
Product page: https://schirmilabs.de/?page_id=165
|
||||
|
||||
ITEAD Sonoff
|
||||
------------
|
||||
|
||||
ESP8266 based devices from ITEAD: Sonoff SV, Sonoff TH, Sonoff Basic, and Sonoff S20
|
||||
|
||||
These are not development boards. The development process is inconvenient with these devices. When flashing firmware you will need a Serial Adapter to connect it to your computer.
|
||||
|
||||
| Most of these devices, during normal operation, are connected to *wall power (AKA Mains Electricity)*. **NEVER** try to flash these devices when connected to *wall power*. **ALWAYS** have them disconnected from *wall power* when connecting them to your computer. Your life may depend on it!
|
||||
|
||||
When flashing you will need to hold down the push button connected to the GPIO0 pin, while powering up with a safe 3.3 Volt source. Some USB Serial Adapters may supply enough power to handle flashing; however, it many may not supply enough power to handle the activities when the device reboots.
|
||||
|
||||
More product details at the bottom of https://www.itead.cc/wiki/Product/
|
||||
|
||||
DOIT ESP-Mx DevKit (ESP8285)
|
||||
----------------------------
|
||||
|
||||
DOIT ESP-Mx DevKit - This is a development board by DOIT, with a DOIT ESP-Mx module (`datasheet <https://github.com/SmartArduino/SZDOITWiKi/wiki/ESP8285---ESP-M2>`__) using a ESP8285 Chip. With the DOIT ESP-Mx module, GPIO pins 9 and 10 are not available. The DOIT ESP-Mx DevKit board has a red power LED and a blue LED connected to GPIO16 and is active low to turn on. It uses a CH340C, USB to Serial converter chip.
|
||||
ESP8285 (`datasheet <http://www.espressif.com/sites/default/files/0a-esp8285_datasheet_en_v1.0_20160422.pdf>`__) is a multi-chip package which contains ESP8266 and 1MB flash.
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
BearSSL Secure Server Class
|
||||
---------------------------
|
||||
|
||||
Implements a TLS encrypted server with optional client certificate validation. See `Server Class <server-class.rst>`__ for general information and `BearSSL Secure Client Class <bearssl-secure-client-class.rst>`__ for basic server and BearSSL concepts.
|
||||
Implements a TLS encrypted server with optional client certificate validation. See `Server Class <server-class.rst>`__ for general information and `BearSSL Secure Client Class <bearssl-client-secure-class.rst>`__ for basic server and BearSSL concepts.
|
||||
|
||||
setBufferSizes(int recv, int xmit)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -54,6 +54,8 @@ As of today you can:
|
||||
|
||||
* change the default lwIP version (1.4 or 2)
|
||||
|
||||
* create an abridged boards.txt file
|
||||
|
||||
|
||||
When do I need to mess with it ?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -78,4 +80,61 @@ After a modification in the generator, it is **mandatory** to regenerate all
|
||||
files (option ``--allgen``) and add them in the pull-request.
|
||||
|
||||
|
||||
How to create an abridged boards.txt file
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The list of boards presented by the IDE has gotten quite long. You can reduce
|
||||
the ESP8266 boards shown by the IDE to a favorites list. This can
|
||||
be done by generating a new boards.txt file using the ``--filter <file>``
|
||||
option.
|
||||
|
||||
Start by getting a current list of boards supported by boards.txt.py.
|
||||
This command will write a list of supported board names to favorites.txt.
|
||||
|
||||
::
|
||||
|
||||
./tools/boards.txt.py --boardnames >favorites.txt
|
||||
|
||||
Edit favorites.txt, keeping the name of the boards you want generated in
|
||||
boards.txt.
|
||||
|
||||
to generate a new abridged boards.txt run:
|
||||
|
||||
::
|
||||
|
||||
./tools/boards.txt.py --boardsgen --filter favorites.txt
|
||||
|
||||
|
||||
You can turn the process around by creating a list of boards, you do not want
|
||||
to be generated. To do this we use the ``--xfilter <file>`` option.
|
||||
|
||||
to generate this abridged boards.txt run:
|
||||
|
||||
::
|
||||
|
||||
./tools/boards.txt.py --boardsgen --xfilter favorites.txt
|
||||
|
||||
|
||||
Yet another option, you can split the boards between boards.txt and
|
||||
boards.local.txt.
|
||||
|
||||
The commands below will generate a boards.txt file that omits the boards named
|
||||
in favorites.txt, and generates a boards.local.txt ( via option ``--boardslocalgen`` ) that only contains boards
|
||||
named in favorites.txt.
|
||||
|
||||
::
|
||||
|
||||
./tools/boards.txt.py --boardsgen --xfilter favorites.txt
|
||||
./tools/boards.txt.py --boardslocalgen --filter favorites.txt
|
||||
|
||||
Additional Notes:
|
||||
|
||||
1. The boards.txt file will always contain the generic and esp8285 boards.
|
||||
|
||||
2. If boards.txt file exist and no backup copy named boards.txt.orig exist, the current boards.txt will be renamed to boards.txt.orig. Otherwise, the existing boards.txt is over-writen when you generate a new boards.txt file. Similar behavior for when generating a new boards.local.txt.
|
||||
|
||||
3. The boards in the boards.txt file will be in the order they were listed in your favorites file, specified by option ``--filter <file>``.
|
||||
|
||||
4. It is outside the scope of this document, but you could manually edit any boards.txt file to have fewer boards. One last observation, the Arduino IDE appears to need at least one board in a board.txt file.
|
||||
|
||||
`FAQ list :back: <readme.rst>`__
|
||||
|
@ -92,6 +92,27 @@ and ``SPIFFS.open()`` to ``LittleFS.open()`` with the rest of the
|
||||
code remaining untouched.
|
||||
|
||||
|
||||
SDFS and SD
|
||||
-----------
|
||||
FAT filesystems are supported on the ESP8266 using the old Arduino wrapper
|
||||
"SD.h" which wraps the "SDFS.h" filesystem transparently.
|
||||
|
||||
Any commands discussed below pertaining to SPIFFS or LittleFS are
|
||||
applicable to SD/SDFS.
|
||||
|
||||
For legacy applications, the classic SD filesystem may continue to be used,
|
||||
but for new applications, directly accessing the SDFS filesystem is
|
||||
recommended as it may expose additional functionality that the old Arduino
|
||||
SD filesystem didn't have.
|
||||
|
||||
Note that in earlier releases of the core, using SD and SPIFFS in the same
|
||||
sketch was complicated and required the use of ``NO_FS_GLOBALS``. The
|
||||
current design makes SD, SDFS, SPIFFS, and LittleFS fully source compatible
|
||||
and so please remove any ``NO_FS_GLOBALS`` definitions in your projects
|
||||
when updgrading core versions.
|
||||
|
||||
|
||||
|
||||
SPIFFS file system limitations
|
||||
------------------------------
|
||||
|
||||
@ -169,11 +190,11 @@ directory into ESP8266 flash file system.
|
||||
|
||||
**Warning**: Due to the move from the obsolete esptool-ck.exe to the
|
||||
supported esptool.py upload tool, upgraders from pre 2.5.1 will need to
|
||||
update the ESP8266FS tool referenced below to 0.4.0 or later. Prior versions
|
||||
update the ESP8266FS tool referenced below to 0.5.0 or later. Prior versions
|
||||
will fail with a "esptool not found" error because they don't know how to
|
||||
use esptool.py.
|
||||
|
||||
- Download the tool: https://github.com/esp8266/arduino-esp8266fs-plugin/releases/download/0.4.0/ESP8266FS-0.4.0.zip
|
||||
- Download the tool: https://github.com/esp8266/arduino-esp8266fs-plugin/releases/download/0.5.0/ESP8266FS-0.5.0.zip
|
||||
- In your Arduino sketchbook directory, create ``tools`` directory if
|
||||
it doesn't exist yet.
|
||||
- Unpack the tool into ``tools`` directory (the path will look like
|
||||
@ -193,13 +214,13 @@ use esptool.py.
|
||||
|
||||
*ESP8266LittleFS* is the equivalent tool for LittleFS.
|
||||
|
||||
- Download the tool: https://github.com/earlephilhower/arduino-esp8266littlefs-plugin/releases
|
||||
- Download the 2.6.0 or later version of the tool: https://github.com/earlephilhower/arduino-esp8266littlefs-plugin/releases
|
||||
- Install as above
|
||||
- To upload a LittleFS filesystem use Tools > ESP8266 LittleFS Data Upload
|
||||
|
||||
|
||||
File system object (SPIFFS/LittleFS)
|
||||
------------------------------------
|
||||
File system object (SPIFFS/LittleFS/SD/SDFS)
|
||||
--------------------------------------------
|
||||
|
||||
setConfig
|
||||
~~~~~~~~~
|
||||
@ -369,41 +390,6 @@ rename
|
||||
Renames file from ``pathFrom`` to ``pathTo``. Paths must be absolute.
|
||||
Returns *true* if file was renamed successfully.
|
||||
|
||||
info
|
||||
~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
FSInfo fs_info;
|
||||
SPIFFS.info(fs_info);
|
||||
or LittleFS.info(fs_info);
|
||||
|
||||
Fills `FSInfo structure <#filesystem-information-structure>`__ with
|
||||
information about the file system. Returns ``true`` is successful,
|
||||
``false`` otherwise.
|
||||
|
||||
Filesystem information structure
|
||||
--------------------------------
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
struct FSInfo {
|
||||
size_t totalBytes;
|
||||
size_t usedBytes;
|
||||
size_t blockSize;
|
||||
size_t pageSize;
|
||||
size_t maxOpenFiles;
|
||||
size_t maxPathLength;
|
||||
};
|
||||
|
||||
This is the structure which may be filled using FS::info method. -
|
||||
``totalBytes`` — total size of useful data on the file system -
|
||||
``usedBytes`` — number of bytes used by files - ``blockSize`` — filesystem
|
||||
block size - ``pageSize`` — filesystem logical page size - ``maxOpenFiles``
|
||||
— max number of files which may be open simultaneously -
|
||||
``maxPathLength`` — max file name length (including one byte for zero
|
||||
termination)
|
||||
|
||||
gc
|
||||
~~
|
||||
|
||||
@ -429,6 +415,81 @@ Only implemented in SPIFFS. Performs an in-depth check of the filesystem metada
|
||||
correct what is repairable. Not normally needed, and not guaranteed to actually fix
|
||||
anything should there be corruption.
|
||||
|
||||
info
|
||||
~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
FSInfo fs_info;
|
||||
SPIFFS.info(fs_info);
|
||||
or LittleFS.info(fs_info);
|
||||
|
||||
Fills `FSInfo structure <#filesystem-information-structure>`__ with
|
||||
information about the file system. Returns ``true`` if successful,
|
||||
``false`` otherwise.
|
||||
|
||||
Filesystem information structure
|
||||
--------------------------------
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
struct FSInfo {
|
||||
size_t totalBytes;
|
||||
size_t usedBytes;
|
||||
size_t blockSize;
|
||||
size_t pageSize;
|
||||
size_t maxOpenFiles;
|
||||
size_t maxPathLength;
|
||||
};
|
||||
|
||||
This is the structure which may be filled using FS::info method. -
|
||||
``totalBytes`` — total size of useful data on the file system -
|
||||
``usedBytes`` — number of bytes used by files - ``blockSize`` — filesystem
|
||||
block size - ``pageSize`` — filesystem logical page size - ``maxOpenFiles``
|
||||
— max number of files which may be open simultaneously -
|
||||
``maxPathLength`` — max file name length (including one byte for zero
|
||||
termination)
|
||||
|
||||
info64
|
||||
~~~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
FSInfo64 fsinfo;
|
||||
SD.info(fsinfo);
|
||||
or LittleFS(fsinfo);
|
||||
|
||||
Performs the same operation as ``info`` but allows for reporting greater than
|
||||
4GB for filesystem size/used/etc. Should be used with the SD and SDFS
|
||||
filesystems since most SD cards today are greater than 4GB in size.
|
||||
|
||||
setTimeCallback(time_t (*cb)(void))
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
time_t myTimeCallback() {
|
||||
return 1455451200; // UNIX timestamp
|
||||
}
|
||||
void setup () {
|
||||
LittleFS.setTimeCallback(myTimeCallback);
|
||||
...
|
||||
// Any files will now be made with Pris' incept date
|
||||
}
|
||||
|
||||
|
||||
The SD, SDFS, and LittleFS filesystems support a file timestamp, updated when the file is
|
||||
opened for writing. By default, the ESP8266 will use the internal time returned from
|
||||
``time(NULL)`` (i.e. local time, not UTC, to conform to the existing FAT filesystem), but this
|
||||
can be overridden to GMT or any other standard you'd like by using ``setTimeCallback()``.
|
||||
If your app sets the system time using NTP before file operations, then
|
||||
you should not need to use this function. However, if you need to set a specific time
|
||||
for a file, or the system clock isn't correct and you need to read the time from an external
|
||||
RTC or use a fixed time, this call allows you do to so.
|
||||
|
||||
In general use, with a functioning ``time()`` call, user applications should not need
|
||||
to use this function.
|
||||
|
||||
Directory object (Dir)
|
||||
----------------------
|
||||
|
||||
@ -468,6 +529,17 @@ fileSize
|
||||
Returns the size of the current file pointed to
|
||||
by the internal iterator.
|
||||
|
||||
fileTime
|
||||
~~~~~~~~
|
||||
|
||||
Returns the time_t write time of the current file pointed
|
||||
to by the internal iterator.
|
||||
|
||||
fileCreationTime
|
||||
~~~~~~~~~~~~~~~~
|
||||
Returns the time_t creation time of the current file
|
||||
pointed to by the internal iterator.
|
||||
|
||||
isFile
|
||||
~~~~~~
|
||||
|
||||
@ -491,6 +563,13 @@ rewind
|
||||
|
||||
Resets the internal pointer to the start of the directory.
|
||||
|
||||
setTimeCallback(time_t (*cb)(void))
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sets the time callback for any files accessed from this Dir object via openNextFile.
|
||||
Note that the SD and SDFS filesystems only support a filesystem-wide callback and
|
||||
calls to ``Dir::setTimeCallback`` may produce unexpected behavior.
|
||||
|
||||
File object
|
||||
-----------
|
||||
|
||||
@ -562,6 +641,17 @@ fullName
|
||||
|
||||
Returns the full path file name as a ``const char*``.
|
||||
|
||||
getLastWrite
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Returns the file last write time, and only valid for files opened in read-only
|
||||
mode. If a file is opened for writing, the returned time may be indeterminate.
|
||||
|
||||
getCreationTime
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Returns the file creation time, if available.
|
||||
|
||||
isFile
|
||||
~~~~~~
|
||||
|
||||
@ -616,3 +706,10 @@ rewindDirectory (compatibiity method, not recommended for new code)
|
||||
|
||||
Resets the ``openNextFile`` pointer to the top of the directory. Only
|
||||
valid when ``File.isDirectory() == true``.
|
||||
|
||||
setTimeCallback(time_t (*cb)(void))
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sets the time callback for this specific file. Note that the SD and
|
||||
SDFS filesystems only support a filesystem-wide callback and calls to
|
||||
``Dir::setTimeCallback`` may produce unexpected behavior.
|
||||
|
@ -53,7 +53,8 @@ Instructions - Windows 10
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
- First, make sure you don't already have an ESP8266 core version installed
|
||||
using the Board Manager (see above). If you do, uninstall it from the
|
||||
Board Manager before proceeding.
|
||||
Board Manager before proceeding. It is also advisable to erase the Arduino15
|
||||
contents.
|
||||
|
||||
- Install git for Windows (if not already; see https://git-scm.com/download/win)
|
||||
|
||||
@ -136,7 +137,8 @@ Instructions - Other OS
|
||||
|
||||
- First, make sure you don't already have an ESP8266 core version installed
|
||||
using the Board Manager (see above). If you do, uninstall it from the
|
||||
Board Manager before proceeding.
|
||||
Board Manager before proceeding. It is also advisable to erase the .arduino15 (Linux)
|
||||
or Arduino15 (MacOS) contents.
|
||||
|
||||
- Open the console and go to Arduino directory. This can be either your
|
||||
*sketchbook* directory (usually ``<Documents>/Arduino``), or the
|
||||
|
@ -113,6 +113,8 @@ Some ESP-specific APIs related to deep sleep, RTC and flash memories are availab
|
||||
|
||||
``ESP.getCycleCount()`` returns the cpu instruction cycle count since start as an unsigned 32-bit. This is useful for accurate timing of very short actions like bit banging.
|
||||
|
||||
``ESP.checkFlashCRC()`` calculates the CRC of the program memory (not including any filesystems) and compares it to the one embedded in the image. If this call returns ``false`` then the flash has been corrupted. At that point, you may want to consider trying to send a MQTT message, to start a re-download of the application, blink a LED in an `SOS` pattern, etc. However, since the flash is known corrupted at this point there is no guarantee the app will be able to perform any of these operations, so in safety critical deployments an immediate shutdown to a fail-safe mode may be indicated.
|
||||
|
||||
``ESP.getVcc()`` may be used to measure supply voltage. ESP needs to reconfigure the ADC at startup in order for this feature to be available. Add the following line to the top of your sketch to use ``getVcc``:
|
||||
|
||||
.. code:: cpp
|
||||
|
38
doc/ota_updates/readme.rst
Normal file → Executable file
38
doc/ota_updates/readme.rst
Normal file → Executable file
@ -123,7 +123,7 @@ Compile the sketch normally and, once a `.bin` file is available, sign it using
|
||||
|
||||
.. code:: bash
|
||||
|
||||
<ESP8266ArduioPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary>
|
||||
<ESP8266ArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary>
|
||||
|
||||
Old And New Signature Formats
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -134,7 +134,34 @@ To create a legacy signature, call the signing script with --legacy:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
<ESP8266ArduioPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary> --legacy <path-to-legacy-file>
|
||||
<ESP8266ArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary> --legacy <path-to-legacy-file>
|
||||
|
||||
|
||||
Compression
|
||||
-----------
|
||||
|
||||
The eboot bootloader incorporates a GZIP decompressor, built for very low code requirements. For applications, this optional decompression is completely transparent. For uploading compressed filesystems, the application must be built with `ATOMIC_FS_UPDATE` defined because, otherwise, eboot will not be involved in writing the filesystem.
|
||||
|
||||
No changes to the application are required. The `Updater` class and `eboot` bootloader (which performs actual application overwriting on update) automatically search for the `gzip` header in the uploaded binary, and if found, handle it.
|
||||
|
||||
Compress an application `.bin` file or filesystem package using any `gzip` available, at any desired compression level (`gzip -9` is recommended because it provides the maximum compression and uncompresses as fast as any other compressino level). For example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
gzip -9 sketch.bin # Maximum compression, output sketch.bin.gz
|
||||
<Upload the resultant sketch.bin.gz>
|
||||
|
||||
If signing is desired, sign the gzip compressed file *after* compression.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
gzip -9 sketch.bin
|
||||
<ESP8266ArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin sketch.bin.gz --out sketch.bin.gz.signed
|
||||
|
||||
Updating apps in the field to support compression
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you have applications deployed in the field and wish to update them to support compressed OTA uploads, you will need to first recompile the application, then _upload the uncompressed `.bin` file once_. Attempting to upload a `gzip` compressed binary to a legacy app will result in the Updater rejecting the upload as it does not understand the `gzip` format. After this initial upload, which will include the new bootloader and `Updater` class with compression support, compressed updates can then be used.
|
||||
|
||||
|
||||
Safety
|
||||
@ -483,6 +510,11 @@ HTTP Server
|
||||
|
||||
``ESPhttpUpdate`` class can check for updates and download a binary file from HTTP web server. It is possible to download updates from every IP or domain address on the network or Internet.
|
||||
|
||||
Note that by default this class closes all other connections except the one used by the update, this is because the update method blocks. This means that if there's another application receiving data then TCP packets will build up in the buffer leading to out of memory errors causing the OTA update to fail. There's also a limited number of receive buffers available and all may be used up by other applications.
|
||||
|
||||
There are some cases where you know that you won't be receiving any data but would still like to send progress updates.
|
||||
It's possible to disable the default behaviour (and keep connections open) by calling closeConnectionsOnUpdate(false).
|
||||
|
||||
Requirements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@ -646,7 +678,7 @@ Update process - memory view
|
||||
- the new sketch is now copied "over" the old one.
|
||||
- the new sketch is started.
|
||||
|
||||
By default, filesystem updates are overriding the target flash directly. In order to use the same two step process set the `ATOMIC_FS_UPDATE` flag.
|
||||
By default, OTA filesystem updates overwrite the target flash directly. This can lead to the file system being corrupted if there is a power outage during the update process. In order to use the same two step process that is used for OTA application firmware updates, set the `ATOMIC_FS_UPDATE` flag. Note that you will need to have enough unused space for the new filesystem image to be stored, hence is why this is not the default behaviour.
|
||||
|
||||
.. figure:: update_memory_copy.png
|
||||
:alt: Memory layout for OTA updates
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Requirements file for pip
|
||||
# list of Python packages used in documentation build
|
||||
sphinx==1.7.9
|
||||
sphinx
|
||||
sphinx-rtd-theme
|
||||
breathe
|
||||
nbsphinx
|
||||
|
@ -168,7 +168,7 @@ int ArduinoOTAClass::parseInt(){
|
||||
}
|
||||
|
||||
String ArduinoOTAClass::readStringUntil(char end){
|
||||
String res = "";
|
||||
String res;
|
||||
int value;
|
||||
while(true){
|
||||
value = _udp_ota->read();
|
||||
@ -231,7 +231,7 @@ void ArduinoOTAClass::_onRx(){
|
||||
return;
|
||||
}
|
||||
|
||||
String challenge = _password + ":" + String(_nonce) + ":" + cnonce;
|
||||
String challenge = _password + ':' + String(_nonce) + ':' + cnonce;
|
||||
MD5Builder _challengemd5;
|
||||
_challengemd5.begin();
|
||||
_challengemd5.add(challenge);
|
||||
@ -303,7 +303,7 @@ void ArduinoOTAClass::_runUpdate() {
|
||||
client.setNoDelay(true);
|
||||
|
||||
uint32_t written, total = 0;
|
||||
while (!Update.isFinished() && client.connected()) {
|
||||
while (!Update.isFinished() && (client.connected() || client.available())) {
|
||||
int waited = 1000;
|
||||
while (!client.available() && waited--)
|
||||
delay(1);
|
||||
|
@ -119,7 +119,7 @@ void DNSServer::respondToRequest(uint8_t *buffer, size_t length)
|
||||
query, queryLength);
|
||||
|
||||
// If we have no domain name configured, just return an error
|
||||
if (_domainName == "")
|
||||
if (_domainName.isEmpty())
|
||||
return replyWithError(dnsHeader, _errorReplyCode,
|
||||
query, queryLength);
|
||||
|
||||
|
@ -0,0 +1,84 @@
|
||||
/**
|
||||
PostHTTPClient.ino
|
||||
|
||||
Created on: 21.11.2016
|
||||
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
/* this can be run with an emulated server on host:
|
||||
cd esp8266-core-root-dir
|
||||
cd tests/host
|
||||
make ../../libraries/ESP8266WebServer/examples/PostServer/PostServer
|
||||
bin/PostServer/PostServer
|
||||
then put your PC's IP address in SERVER_IP below, port 9080 (instead of default 80):
|
||||
*/
|
||||
//#define SERVER_IP "10.0.1.7:9080" // PC address with emulation on host
|
||||
#define SERVER_IP "192.168.1.42"
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
|
||||
USE_SERIAL.begin(115200);
|
||||
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
|
||||
WiFi.begin(STASSID, STAPSK);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
USE_SERIAL.print(".");
|
||||
}
|
||||
USE_SERIAL.println("");
|
||||
USE_SERIAL.print("Connected! IP address: ");
|
||||
USE_SERIAL.println(WiFi.localIP());
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFi.status() == WL_CONNECTED)) {
|
||||
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
|
||||
USE_SERIAL.print("[HTTP] begin...\n");
|
||||
// configure traged server and url
|
||||
http.begin(client, "http://" SERVER_IP "/postplain/"); //HTTP
|
||||
http.addHeader("Content-Type", "application/json");
|
||||
|
||||
USE_SERIAL.print("[HTTP] POST...\n");
|
||||
// start connection and send HTTP header and body
|
||||
int httpCode = http.POST("{\"hello\":\"world\"}");
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
USE_SERIAL.printf("[HTTP] POST... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
const String& payload = http.getString();
|
||||
USE_SERIAL.println("received payload:\n<<");
|
||||
USE_SERIAL.println(payload);
|
||||
USE_SERIAL.println(">>");
|
||||
}
|
||||
} else {
|
||||
USE_SERIAL.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
delay(10000);
|
||||
}
|
@ -137,9 +137,9 @@ void HTTPClient::clear()
|
||||
{
|
||||
_returnCode = 0;
|
||||
_size = -1;
|
||||
_headers = "";
|
||||
_headers.clear();
|
||||
_location.clear();
|
||||
_payload.reset();
|
||||
_location = "";
|
||||
}
|
||||
|
||||
|
||||
@ -150,7 +150,7 @@ void HTTPClient::clear()
|
||||
* @param https bool
|
||||
* @return success bool
|
||||
*/
|
||||
bool HTTPClient::begin(WiFiClient &client, String url) {
|
||||
bool HTTPClient::begin(WiFiClient &client, const String& url) {
|
||||
#if HTTPCLIENT_1_1_COMPATIBLE
|
||||
if(_tcpDeprecated) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][begin] mix up of new and deprecated api\n");
|
||||
@ -188,7 +188,7 @@ bool HTTPClient::begin(WiFiClient &client, String url) {
|
||||
* @param https bool
|
||||
* @return success bool
|
||||
*/
|
||||
bool HTTPClient::begin(WiFiClient &client, String host, uint16_t port, String uri, bool https)
|
||||
bool HTTPClient::begin(WiFiClient &client, const String& host, uint16_t port, const String& uri, bool https)
|
||||
{
|
||||
#if HTTPCLIENT_1_1_COMPATIBLE
|
||||
if(_tcpDeprecated) {
|
||||
@ -281,8 +281,10 @@ bool HTTPClient::begin(String url)
|
||||
}
|
||||
#endif // HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
|
||||
bool HTTPClient::beginInternal(const String& __url, const char* expectedProtocol)
|
||||
{
|
||||
String url(__url);
|
||||
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str());
|
||||
clear();
|
||||
|
||||
@ -317,7 +319,7 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
|
||||
// auth info
|
||||
String auth = host.substring(0, index);
|
||||
host.remove(0, index + 1); // remove auth part including @
|
||||
_base64Authorization = base64::encode(auth);
|
||||
_base64Authorization = base64::encode(auth, false /* doNewLines */);
|
||||
}
|
||||
|
||||
// get port
|
||||
@ -500,9 +502,9 @@ void HTTPClient::setAuthorization(const char * user, const char * password)
|
||||
{
|
||||
if(user && password) {
|
||||
String auth = user;
|
||||
auth += ":";
|
||||
auth += ':';
|
||||
auth += password;
|
||||
_base64Authorization = base64::encode(auth);
|
||||
_base64Authorization = base64::encode(auth, false /* doNewLines */);
|
||||
}
|
||||
}
|
||||
|
||||
@ -514,6 +516,7 @@ void HTTPClient::setAuthorization(const char * auth)
|
||||
{
|
||||
if(auth) {
|
||||
_base64Authorization = auth;
|
||||
_base64Authorization.replace(String('\n'), emptyString);
|
||||
}
|
||||
}
|
||||
|
||||
@ -533,7 +536,7 @@ void HTTPClient::setTimeout(uint16_t timeout)
|
||||
* set the URL to a new value. Handy for following redirects.
|
||||
* @param url
|
||||
*/
|
||||
bool HTTPClient::setURL(String url)
|
||||
bool HTTPClient::setURL(const String& url)
|
||||
{
|
||||
// if the new location is only a path then only update the URI
|
||||
if (url && url[0] == '/') {
|
||||
@ -542,13 +545,13 @@ bool HTTPClient::setURL(String url)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!url.startsWith(_protocol + ":")) {
|
||||
if (!url.startsWith(_protocol + ':')) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][setURL] new URL not the same protocol, expected '%s', URL: '%s'\n", _protocol.c_str(), url.c_str());
|
||||
return false;
|
||||
}
|
||||
// disconnect but preserve _client
|
||||
// disconnect but preserve _client (clear _canReuse so disconnect will close the connection)
|
||||
_canReuse = false;
|
||||
disconnect(true);
|
||||
clear();
|
||||
return beginInternal(url, nullptr);
|
||||
}
|
||||
|
||||
@ -587,16 +590,16 @@ int HTTPClient::GET()
|
||||
|
||||
/**
|
||||
* sends a post request to the server
|
||||
* @param payload uint8_t *
|
||||
* @param payload const uint8_t *
|
||||
* @param size size_t
|
||||
* @return http code
|
||||
*/
|
||||
int HTTPClient::POST(uint8_t * payload, size_t size)
|
||||
int HTTPClient::POST(const uint8_t* payload, size_t size)
|
||||
{
|
||||
return sendRequest("POST", payload, size);
|
||||
}
|
||||
|
||||
int HTTPClient::POST(String payload)
|
||||
int HTTPClient::POST(const String& payload)
|
||||
{
|
||||
return POST((uint8_t *) payload.c_str(), payload.length());
|
||||
}
|
||||
@ -607,26 +610,26 @@ int HTTPClient::POST(String payload)
|
||||
* @param size size_t
|
||||
* @return http code
|
||||
*/
|
||||
int HTTPClient::PUT(uint8_t * payload, size_t size) {
|
||||
int HTTPClient::PUT(const uint8_t* payload, size_t size) {
|
||||
return sendRequest("PUT", payload, size);
|
||||
}
|
||||
|
||||
int HTTPClient::PUT(String payload) {
|
||||
return PUT((uint8_t *) payload.c_str(), payload.length());
|
||||
int HTTPClient::PUT(const String& payload) {
|
||||
return PUT((const uint8_t *) payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a patch request to the server
|
||||
* @param payload uint8_t *
|
||||
* @param payload const uint8_t *
|
||||
* @param size size_t
|
||||
* @return http code
|
||||
*/
|
||||
int HTTPClient::PATCH(uint8_t * payload, size_t size) {
|
||||
int HTTPClient::PATCH(const uint8_t * payload, size_t size) {
|
||||
return sendRequest("PATCH", payload, size);
|
||||
}
|
||||
|
||||
int HTTPClient::PATCH(String payload) {
|
||||
return PATCH((uint8_t *) payload.c_str(), payload.length());
|
||||
int HTTPClient::PATCH(const String& payload) {
|
||||
return PATCH((const uint8_t *) payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -635,19 +638,19 @@ int HTTPClient::PATCH(String payload) {
|
||||
* @param payload String data for the message body
|
||||
* @return
|
||||
*/
|
||||
int HTTPClient::sendRequest(const char * type, String payload)
|
||||
int HTTPClient::sendRequest(const char * type, const String& payload)
|
||||
{
|
||||
return sendRequest(type, (uint8_t *) payload.c_str(), payload.length());
|
||||
return sendRequest(type, (const uint8_t *) payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* sendRequest
|
||||
* @param type const char * "GET", "POST", ....
|
||||
* @param payload uint8_t * data for the message body if null not send
|
||||
* @param payload const uint8_t * data for the message body if null not send
|
||||
* @param size size_t size for the message body if 0 not send
|
||||
* @return -1 if no info or > 0 when Content-Length is set by server
|
||||
*/
|
||||
int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size)
|
||||
int HTTPClient::sendRequest(const char * type, const uint8_t * payload, size_t size)
|
||||
{
|
||||
bool redirect = false;
|
||||
int code = 0;
|
||||
@ -655,7 +658,7 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size)
|
||||
// wipe out any existing headers from previous request
|
||||
for(size_t i = 0; i < _headerKeysCount; i++) {
|
||||
if (_currentHeaders[i].value.length() > 0) {
|
||||
_currentHeaders[i].value = "";
|
||||
_currentHeaders[i].value.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -676,8 +679,20 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size)
|
||||
|
||||
// send Payload if needed
|
||||
if (payload && size > 0) {
|
||||
if(_client->write(&payload[0], size) != size) {
|
||||
size_t bytesWritten = 0;
|
||||
const uint8_t *p = payload;
|
||||
size_t originalSize = size;
|
||||
while (bytesWritten < originalSize) {
|
||||
int written;
|
||||
int towrite = std::min((int)size, (int)HTTP_TCP_BUFFER_SIZE);
|
||||
written = _client->write(p + bytesWritten, towrite);
|
||||
if (written < 0) {
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
} else if (written == 0) {
|
||||
return returnError(HTTPC_ERROR_CONNECTION_LOST);
|
||||
}
|
||||
bytesWritten += written;
|
||||
size -= written;
|
||||
}
|
||||
}
|
||||
|
||||
@ -745,7 +760,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
}
|
||||
|
||||
if(size > 0) {
|
||||
addHeader("Content-Length", String(size));
|
||||
addHeader(F("Content-Length"), String(size));
|
||||
}
|
||||
|
||||
// send Header
|
||||
@ -1068,7 +1083,9 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first,
|
||||
!name.equalsIgnoreCase(F("Host")) &&
|
||||
!(name.equalsIgnoreCase(F("Authorization")) && _base64Authorization.length())) {
|
||||
|
||||
String headerLine = name;
|
||||
String headerLine;
|
||||
headerLine.reserve(name.length() + value.length() + 4);
|
||||
headerLine += name;
|
||||
headerLine += ": ";
|
||||
|
||||
if (replace) {
|
||||
@ -1087,7 +1104,6 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first,
|
||||
_headers += headerLine;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void HTTPClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount)
|
||||
@ -1212,42 +1228,50 @@ bool HTTPClient::sendHeader(const char * type)
|
||||
return false;
|
||||
}
|
||||
|
||||
String header = String(type) + " " + (_uri.length() ? _uri : F("/")) + F(" HTTP/1.");
|
||||
String header;
|
||||
// 128: Arbitrarily chosen to have enough buffer space for avoiding internal reallocations
|
||||
header.reserve(_headers.length() + _uri.length() +
|
||||
_base64Authorization.length() + _host.length() + _userAgent.length() + 128);
|
||||
header += type;
|
||||
header += ' ';
|
||||
if (_uri.length()) {
|
||||
header += _uri;
|
||||
} else {
|
||||
header += '/';
|
||||
}
|
||||
header += F(" HTTP/1.");
|
||||
|
||||
if(_useHTTP10) {
|
||||
header += "0";
|
||||
header += '0';
|
||||
} else {
|
||||
header += "1";
|
||||
header += '1';
|
||||
}
|
||||
|
||||
header += String(F("\r\nHost: ")) + _host;
|
||||
header += F("\r\nHost: ");
|
||||
header += _host;
|
||||
if (_port != 80 && _port != 443)
|
||||
{
|
||||
header += ':';
|
||||
header += String(_port);
|
||||
}
|
||||
header += String(F("\r\nUser-Agent: ")) + _userAgent +
|
||||
F("\r\nConnection: ");
|
||||
|
||||
if(_reuse) {
|
||||
header += F("keep-alive");
|
||||
} else {
|
||||
header += F("close");
|
||||
}
|
||||
header += "\r\n";
|
||||
header += F("\r\nUser-Agent: ");
|
||||
header += _userAgent;
|
||||
|
||||
if (!_useHTTP10) {
|
||||
header += F("Accept-Encoding: identity;q=1,chunked;q=0.1,*;q=0\r\n");
|
||||
header += F("\r\nAccept-Encoding: identity;q=1,chunked;q=0.1,*;q=0");
|
||||
}
|
||||
|
||||
if (_base64Authorization.length()) {
|
||||
_base64Authorization.replace("\n", "");
|
||||
header += F("Authorization: Basic ");
|
||||
header += F("\r\nAuthorization: Basic ");
|
||||
header += _base64Authorization;
|
||||
header += "\r\n";
|
||||
}
|
||||
|
||||
header += _headers + "\r\n";
|
||||
header += F("\r\nConnection: ");
|
||||
header += _reuse ? F("keep-alive") : F("close");
|
||||
header += "\r\n";
|
||||
|
||||
header += _headers;
|
||||
header += "\r\n";
|
||||
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client] sending request header\n-----\n%s-----\n", header.c_str());
|
||||
|
||||
@ -1278,20 +1302,23 @@ int HTTPClient::handleHeaderResponse()
|
||||
size_t len = _client->available();
|
||||
if(len > 0) {
|
||||
String headerLine = _client->readStringUntil('\n');
|
||||
headerLine.trim(); // remove \r
|
||||
|
||||
lastDataTime = millis();
|
||||
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] RX: '%s'\n", headerLine.c_str());
|
||||
|
||||
if(headerLine.startsWith("HTTP/1.")) {
|
||||
if (headerLine.startsWith(F("HTTP/1."))) {
|
||||
if (_canReuse) {
|
||||
_canReuse = (headerLine[sizeof "HTTP/1." - 1] != '0');
|
||||
}
|
||||
_returnCode = headerLine.substring(9, headerLine.indexOf(' ', 9)).toInt();
|
||||
} else if(headerLine.indexOf(':')) {
|
||||
String headerName = headerLine.substring(0, headerLine.indexOf(':'));
|
||||
String headerValue = headerLine.substring(headerLine.indexOf(':') + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
int headerSeparator = headerLine.indexOf(':');
|
||||
if (headerSeparator > 0) {
|
||||
String headerName = headerLine.substring(0, headerSeparator);
|
||||
String headerValue = headerLine.substring(headerSeparator + 1);
|
||||
headerValue.trim();
|
||||
|
||||
if(headerName.equalsIgnoreCase(F("Content-Length"))) {
|
||||
@ -1299,7 +1326,8 @@ int HTTPClient::handleHeaderResponse()
|
||||
}
|
||||
|
||||
if(_canReuse && headerName.equalsIgnoreCase(F("Connection"))) {
|
||||
if(headerValue.indexOf("close") >= 0 && headerValue.indexOf("keep-alive") < 0) {
|
||||
if (headerValue.indexOf(F("close")) >= 0 &&
|
||||
headerValue.indexOf(F("keep-alive")) < 0) {
|
||||
_canReuse = false;
|
||||
}
|
||||
}
|
||||
@ -1314,18 +1342,22 @@ int HTTPClient::handleHeaderResponse()
|
||||
|
||||
for (size_t i = 0; i < _headerKeysCount; i++) {
|
||||
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
|
||||
if (_currentHeaders[i].value != "") {
|
||||
if (!_currentHeaders[i].value.isEmpty()) {
|
||||
// Existing value, append this one with a comma
|
||||
_currentHeaders[i].value += "," + headerValue;
|
||||
_currentHeaders[i].value += ',';
|
||||
_currentHeaders[i].value += headerValue;
|
||||
} else {
|
||||
_currentHeaders[i].value = headerValue;
|
||||
}
|
||||
break; // We found a match, stop looking
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(headerLine == "") {
|
||||
headerLine.trim(); // remove \r
|
||||
|
||||
if (headerLine.isEmpty()) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] code: %d\n", _returnCode);
|
||||
|
||||
if(_size > 0) {
|
||||
|
@ -147,8 +147,8 @@ public:
|
||||
* Since both begin() functions take a reference to client as a parameter, you need to
|
||||
* ensure the client object lives the entire time of the HTTPClient
|
||||
*/
|
||||
bool begin(WiFiClient &client, String url);
|
||||
bool begin(WiFiClient &client, String host, uint16_t port, String uri = "/", bool https = false);
|
||||
bool begin(WiFiClient &client, const String& url);
|
||||
bool begin(WiFiClient &client, const String& host, uint16_t port, const String& uri = "/", bool https = false);
|
||||
|
||||
#if HTTPCLIENT_1_1_COMPATIBLE
|
||||
// Plain HTTP connection, unencrypted
|
||||
@ -175,19 +175,19 @@ public:
|
||||
void setTimeout(uint16_t timeout);
|
||||
void setFollowRedirects(bool follow);
|
||||
void setRedirectLimit(uint16_t limit); // max redirects to follow for a single request
|
||||
bool setURL(String url); // handy for handling redirects
|
||||
bool setURL(const String& url); // handy for handling redirects
|
||||
void useHTTP10(bool usehttp10 = true);
|
||||
|
||||
/// request handling
|
||||
int GET();
|
||||
int POST(uint8_t * payload, size_t size);
|
||||
int POST(String payload);
|
||||
int PUT(uint8_t * payload, size_t size);
|
||||
int PUT(String payload);
|
||||
int PATCH(uint8_t * payload, size_t size);
|
||||
int PATCH(String payload);
|
||||
int sendRequest(const char * type, String payload);
|
||||
int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0);
|
||||
int POST(const uint8_t* payload, size_t size);
|
||||
int POST(const String& payload);
|
||||
int PUT(const uint8_t* payload, size_t size);
|
||||
int PUT(const String& payload);
|
||||
int PATCH(const uint8_t* payload, size_t size);
|
||||
int PATCH(const String& payload);
|
||||
int sendRequest(const char* type, const String& payload);
|
||||
int sendRequest(const char* type, const uint8_t* payload = NULL, size_t size = 0);
|
||||
int sendRequest(const char* type, Stream * stream, size_t size = 0);
|
||||
|
||||
void addHeader(const String& name, const String& value, bool first = false, bool replace = true);
|
||||
@ -216,7 +216,7 @@ protected:
|
||||
String value;
|
||||
};
|
||||
|
||||
bool beginInternal(String url, const char* expectedProtocol);
|
||||
bool beginInternal(const String& url, const char* expectedProtocol);
|
||||
void disconnect(bool preserveClient = false);
|
||||
void clear();
|
||||
int returnError(int error);
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include <WiFiUdp.h>
|
||||
#include <flash_hal.h>
|
||||
#include <FS.h>
|
||||
#include <LittleFS.h>
|
||||
#include "StreamString.h"
|
||||
#include "ESP8266HTTPUpdateServer.h"
|
||||
|
||||
@ -22,12 +21,12 @@ static const char serverIndex[] PROGMEM =
|
||||
<body>
|
||||
<form method='POST' action='' enctype='multipart/form-data'>
|
||||
Firmware:<br>
|
||||
<input type='file' accept='.bin' name='firmware'>
|
||||
<input type='file' accept='.bin,.bin.gz' name='firmware'>
|
||||
<input type='submit' value='Update Firmware'>
|
||||
</form>
|
||||
<form method='POST' action='' enctype='multipart/form-data'>
|
||||
FileSystem:<br>
|
||||
<input type='file' accept='.bin' name='filesystem'>
|
||||
<input type='file' accept='.bin,.bin.gz' name='filesystem'>
|
||||
<input type='submit' value='Update FileSystem'>
|
||||
</form>
|
||||
</body>
|
||||
@ -78,7 +77,7 @@ void ESP8266HTTPUpdateServerTemplate<ServerType>::setup(ESP8266WebServerTemplate
|
||||
HTTPUpload& upload = _server->upload();
|
||||
|
||||
if(upload.status == UPLOAD_FILE_START){
|
||||
_updaterError = String();
|
||||
_updaterError.clear();
|
||||
if (_serial_output)
|
||||
Serial.setDebugOutput(true);
|
||||
|
||||
@ -94,8 +93,7 @@ void ESP8266HTTPUpdateServerTemplate<ServerType>::setup(ESP8266WebServerTemplate
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
if (upload.name == "filesystem") {
|
||||
size_t fsSize = ((size_t) &_FS_end - (size_t) &_FS_start);
|
||||
SPIFFS.end();
|
||||
LittleFS.end();
|
||||
close_all_fs();
|
||||
if (!Update.begin(fsSize, U_FS)){//start with max available size
|
||||
if (_serial_output) Update.printError(Serial);
|
||||
}
|
||||
|
@ -69,11 +69,11 @@ static const char _ssdp_notify_template[] PROGMEM =
|
||||
|
||||
static const char _ssdp_packet_template[] PROGMEM =
|
||||
"%s" // _ssdp_response_template / _ssdp_notify_template
|
||||
"CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL
|
||||
"CACHE-CONTROL: max-age=%u\r\n" // _interval
|
||||
"SERVER: Arduino/1.0 UPNP/1.1 %s/%s\r\n" // _modelName, _modelNumber
|
||||
"USN: %s\r\n" // _uuid
|
||||
"%s: %s\r\n" // "NT" or "ST", _deviceType
|
||||
"LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL
|
||||
"LOCATION: http://%s:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL
|
||||
"\r\n";
|
||||
|
||||
static const char _ssdp_schema_template[] PROGMEM =
|
||||
@ -88,7 +88,7 @@ static const char _ssdp_schema_template[] PROGMEM =
|
||||
"<major>1</major>"
|
||||
"<minor>0</minor>"
|
||||
"</specVersion>"
|
||||
"<URLBase>http://%u.%u.%u.%u:%u/</URLBase>" // WiFi.localIP(), _port
|
||||
"<URLBase>http://%s:%u/</URLBase>" // WiFi.localIP(), _port
|
||||
"<device>"
|
||||
"<deviceType>%s</deviceType>"
|
||||
"<friendlyName>%s</friendlyName>"
|
||||
@ -130,6 +130,7 @@ SSDPClass::SSDPClass() :
|
||||
_timer(0),
|
||||
_port(80),
|
||||
_ttl(SSDP_MULTICAST_TTL),
|
||||
_interval(SSDP_INTERVAL),
|
||||
_respondToAddr(0,0,0,0),
|
||||
_respondToPort(0),
|
||||
_pending(false),
|
||||
@ -241,13 +242,13 @@ void SSDPClass::_send(ssdp_method_t method) {
|
||||
int len = snprintf_P(buffer, sizeof(buffer),
|
||||
_ssdp_packet_template,
|
||||
valueBuffer,
|
||||
SSDP_INTERVAL,
|
||||
_interval,
|
||||
_modelName,
|
||||
_modelNumber,
|
||||
_uuid,
|
||||
(method == NONE) ? "ST" : "NT",
|
||||
(_st_is_uuid) ? _uuid : _deviceType,
|
||||
ip[0], ip[1], ip[2], ip[3], _port, _schemaURL
|
||||
ip.toString().c_str(), _port, _schemaURL
|
||||
);
|
||||
|
||||
_server->append(buffer, len);
|
||||
@ -276,12 +277,12 @@ void SSDPClass::_send(ssdp_method_t method) {
|
||||
_server->send(remoteAddr, remotePort);
|
||||
}
|
||||
|
||||
void SSDPClass::schema(WiFiClient client) {
|
||||
void SSDPClass::schema(Print &client) const {
|
||||
IPAddress ip = WiFi.localIP();
|
||||
char buffer[strlen_P(_ssdp_schema_template) + 1];
|
||||
strcpy_P(buffer, _ssdp_schema_template);
|
||||
client.printf(buffer,
|
||||
ip[0], ip[1], ip[2], ip[3], _port,
|
||||
ip.toString().c_str(), _port,
|
||||
_deviceType,
|
||||
_friendlyName,
|
||||
_presentationURL,
|
||||
@ -428,7 +429,7 @@ void SSDPClass::_update() {
|
||||
if (_pending && (millis() - _process_time) > _delay) {
|
||||
_pending = false; _delay = 0;
|
||||
_send(NONE);
|
||||
} else if(_notify_time == 0 || (millis() - _notify_time) > (SSDP_INTERVAL * 1000L)){
|
||||
} else if(_notify_time == 0 || (millis() - _notify_time) > (_interval * 1000L)){
|
||||
_notify_time = millis();
|
||||
_st_is_uuid = false;
|
||||
_send(NOTIFY);
|
||||
@ -493,11 +494,14 @@ void SSDPClass::setManufacturerURL(const char *url) {
|
||||
strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL));
|
||||
}
|
||||
|
||||
|
||||
void SSDPClass::setTTL(const uint8_t ttl) {
|
||||
_ttl = ttl;
|
||||
}
|
||||
|
||||
void SSDPClass::setInterval(uint32_t interval) {
|
||||
_interval = interval;
|
||||
}
|
||||
|
||||
void SSDPClass::_onTimerStatic(SSDPClass* self) {
|
||||
self->_update();
|
||||
}
|
||||
|
@ -62,7 +62,8 @@ class SSDPClass{
|
||||
~SSDPClass();
|
||||
bool begin();
|
||||
void end();
|
||||
void schema(WiFiClient client);
|
||||
void schema(WiFiClient client) const { schema((Print&)std::ref(client)); }
|
||||
void schema(Print &print) const;
|
||||
void setDeviceType(const String& deviceType) { setDeviceType(deviceType.c_str()); }
|
||||
void setDeviceType(const char *deviceType);
|
||||
|
||||
@ -91,6 +92,7 @@ class SSDPClass{
|
||||
void setManufacturerURL(const char *url);
|
||||
void setHTTPPort(uint16_t port);
|
||||
void setTTL(uint8_t ttl);
|
||||
void setInterval(uint32_t interval);
|
||||
|
||||
protected:
|
||||
void _send(ssdp_method_t method);
|
||||
@ -103,6 +105,7 @@ class SSDPClass{
|
||||
SSDPTimer* _timer;
|
||||
uint16_t _port;
|
||||
uint8_t _ttl;
|
||||
uint32_t _interval;
|
||||
|
||||
IPAddress _respondToAddr;
|
||||
uint16_t _respondToPort;
|
||||
|
Submodule libraries/ESP8266SdFat updated: af4ed0c5ec...b240d2231a
@ -96,7 +96,7 @@ Getting information about request arguments
|
||||
int args();
|
||||
bool hasArg();
|
||||
|
||||
``arg`` - get request argument value
|
||||
``arg`` - get request argument value, use ``arg("plain")`` to get POST body
|
||||
|
||||
``argName`` - get request argument name
|
||||
|
||||
|
@ -94,6 +94,25 @@ void handleNotFound() {
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void drawGraph() {
|
||||
String out;
|
||||
out.reserve(2600);
|
||||
char temp[70];
|
||||
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
|
||||
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
|
||||
out += "<g stroke=\"black\">\n";
|
||||
int y = rand() % 130;
|
||||
for (int x = 10; x < 390; x += 10) {
|
||||
int y2 = rand() % 130;
|
||||
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
|
||||
out += temp;
|
||||
y = y2;
|
||||
}
|
||||
out += "</g>\n</svg>\n";
|
||||
|
||||
server.send(200, "image/svg+xml", out);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
pinMode(led, OUTPUT);
|
||||
digitalWrite(led, 0);
|
||||
@ -133,20 +152,3 @@ void loop(void) {
|
||||
MDNS.update();
|
||||
}
|
||||
|
||||
void drawGraph() {
|
||||
String out = "";
|
||||
char temp[100];
|
||||
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
|
||||
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
|
||||
out += "<g stroke=\"black\">\n";
|
||||
int y = rand() % 130;
|
||||
for (int x = 10; x < 390; x += 10) {
|
||||
int y2 = rand() % 130;
|
||||
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
|
||||
out += temp;
|
||||
y = y2;
|
||||
}
|
||||
out += "</g>\n</svg>\n";
|
||||
|
||||
server.send(200, "image/svg+xml", out);
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ void handleFileUpload() {
|
||||
}
|
||||
DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename);
|
||||
fsUploadFile = filesystem->open(filename, "w");
|
||||
filename = String();
|
||||
filename.clear();
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
//DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
|
||||
if (fsUploadFile) {
|
||||
@ -150,7 +150,7 @@ void handleFileDelete() {
|
||||
}
|
||||
filesystem->remove(path);
|
||||
server.send(200, "text/plain", "");
|
||||
path = String();
|
||||
path.clear();
|
||||
}
|
||||
|
||||
void handleFileCreate() {
|
||||
@ -172,7 +172,7 @@ void handleFileCreate() {
|
||||
return server.send(500, "text/plain", "CREATE FAILED");
|
||||
}
|
||||
server.send(200, "text/plain", "");
|
||||
path = String();
|
||||
path.clear();
|
||||
}
|
||||
|
||||
void handleFileList() {
|
||||
@ -184,7 +184,7 @@ void handleFileList() {
|
||||
String path = server.arg("dir");
|
||||
DBG_OUTPUT_PORT.println("handleFileList: " + path);
|
||||
Dir dir = filesystem->openDir(path);
|
||||
path = String();
|
||||
path.clear();
|
||||
|
||||
String output = "[";
|
||||
while (dir.next()) {
|
||||
@ -275,13 +275,13 @@ void setup(void) {
|
||||
|
||||
//get heap status, analog input value and all GPIO statuses in one json call
|
||||
server.on("/all", HTTP_GET, []() {
|
||||
String json = "{";
|
||||
String json('{');
|
||||
json += "\"heap\":" + String(ESP.getFreeHeap());
|
||||
json += ", \"analog\":" + String(analogRead(A0));
|
||||
json += ", \"gpio\":" + String((uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16)));
|
||||
json += "}";
|
||||
server.send(200, "text/json", json);
|
||||
json = String();
|
||||
json.clear();
|
||||
});
|
||||
server.begin();
|
||||
DBG_OUTPUT_PORT.println("HTTP server started");
|
||||
|
@ -67,6 +67,23 @@ void setup(void) {
|
||||
server.send(200, "text/plain", "this works as well");
|
||||
});
|
||||
|
||||
server.on("/gif", []() {
|
||||
static const uint8_t gif[] PROGMEM = {
|
||||
0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x19, 0x8c, 0x8f, 0xa9, 0xcb, 0x9d,
|
||||
0x00, 0x5f, 0x74, 0xb4, 0x56, 0xb0, 0xb0, 0xd2, 0xf2, 0x35, 0x1e, 0x4c,
|
||||
0x0c, 0x24, 0x5a, 0xe6, 0x89, 0xa6, 0x4d, 0x01, 0x00, 0x3b
|
||||
};
|
||||
char gif_colored[sizeof(gif)];
|
||||
memcpy_P(gif_colored, gif, sizeof(gif));
|
||||
// Set the background to a random set of colors
|
||||
gif_colored[16] = millis() % 256;
|
||||
gif_colored[17] = millis() % 256;
|
||||
gif_colored[18] = millis() % 256;
|
||||
server.send(200, "image/gif", gif_colored, sizeof(gif_colored));
|
||||
});
|
||||
|
||||
server.onNotFound(handleNotFound);
|
||||
|
||||
server.begin();
|
||||
|
@ -0,0 +1,61 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
|
||||
#include <uri/UriBraces.h>
|
||||
#include <uri/UriRegex.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *password = STAPSK;
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
|
||||
// Wait for connection
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("esp8266")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
|
||||
server.on(F("/"), []() {
|
||||
server.send(200, "text/plain", "hello from esp8266!");
|
||||
});
|
||||
|
||||
server.on(UriBraces("/users/{}"), []() {
|
||||
String user = server.pathArg(0);
|
||||
server.send(200, "text/plain", "User: '" + user + "'");
|
||||
});
|
||||
|
||||
server.on(UriRegex("^\\/users\\/([0-9]+)\\/devices\\/([0-9]+)$"), []() {
|
||||
String user = server.pathArg(0);
|
||||
String device = server.pathArg(1);
|
||||
server.send(200, "text/plain", "User: '" + user + "' and Device: '" + device + "'");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
Serial.println("HTTP server started");
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
}
|
126
libraries/ESP8266WebServer/examples/PostServer/PostServer.ino
Normal file
126
libraries/ESP8266WebServer/examples/PostServer/PostServer.ino
Normal file
@ -0,0 +1,126 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
|
||||
const int led = LED_BUILTIN;
|
||||
|
||||
const String postForms = "<html>\
|
||||
<head>\
|
||||
<title>ESP8266 Web Server POST handling</title>\
|
||||
<style>\
|
||||
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
|
||||
</style>\
|
||||
</head>\
|
||||
<body>\
|
||||
<h1>POST plain text to /postplain/</h1><br>\
|
||||
<form method=\"post\" enctype=\"text/plain\" action=\"/postplain/\">\
|
||||
<input type=\"text\" name=\'{\"hello\": \"world\", \"trash\": \"\' value=\'\"}\'><br>\
|
||||
<input type=\"submit\" value=\"Submit\">\
|
||||
</form>\
|
||||
<h1>POST form data to /postform/</h1><br>\
|
||||
<form method=\"post\" enctype=\"application/x-www-form-urlencoded\" action=\"/postform/\">\
|
||||
<input type=\"text\" name=\"hello\" value=\"world\"><br>\
|
||||
<input type=\"submit\" value=\"Submit\">\
|
||||
</form>\
|
||||
</body>\
|
||||
</html>";
|
||||
|
||||
void handleRoot() {
|
||||
digitalWrite(led, 1);
|
||||
server.send(200, "text/html", postForms);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void handlePlain() {
|
||||
if (server.method() != HTTP_POST) {
|
||||
digitalWrite(led, 1);
|
||||
server.send(405, "text/plain", "Method Not Allowed");
|
||||
digitalWrite(led, 0);
|
||||
} else {
|
||||
digitalWrite(led, 1);
|
||||
server.send(200, "text/plain", "POST body was:\n" + server.arg("plain"));
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void handleForm() {
|
||||
if (server.method() != HTTP_POST) {
|
||||
digitalWrite(led, 1);
|
||||
server.send(405, "text/plain", "Method Not Allowed");
|
||||
digitalWrite(led, 0);
|
||||
} else {
|
||||
digitalWrite(led, 1);
|
||||
String message = "POST form was:\n";
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
server.send(200, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void handleNotFound() {
|
||||
digitalWrite(led, 1);
|
||||
String message = "File Not Found\n\n";
|
||||
message += "URI: ";
|
||||
message += server.uri();
|
||||
message += "\nMethod: ";
|
||||
message += (server.method() == HTTP_GET) ? "GET" : "POST";
|
||||
message += "\nArguments: ";
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
server.send(404, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
pinMode(led, OUTPUT);
|
||||
digitalWrite(led, 0);
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
|
||||
// Wait for connection
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("esp8266")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
|
||||
server.on("/", handleRoot);
|
||||
|
||||
server.on("/postplain/", handlePlain);
|
||||
|
||||
server.on("/postform/", handleForm);
|
||||
|
||||
server.onNotFound(handleNotFound);
|
||||
|
||||
server.begin();
|
||||
Serial.println("HTTP server started");
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
}
|
@ -209,7 +209,7 @@ void printDirectory() {
|
||||
return returnFail("BAD PATH");
|
||||
}
|
||||
File dir = SD.open((char *)path.c_str());
|
||||
path = String();
|
||||
path.clear();
|
||||
if (!dir.isDirectory()) {
|
||||
dir.close();
|
||||
return returnFail("NOT DIR");
|
||||
@ -241,6 +241,7 @@ void printDirectory() {
|
||||
entry.close();
|
||||
}
|
||||
server.sendContent("]");
|
||||
server.sendContent(""); // Terminate the HTTP chunked transmission with a 0-length chunk
|
||||
dir.close();
|
||||
}
|
||||
|
||||
|
@ -252,28 +252,28 @@ void ESP8266WebServerTemplate<ServerType>::requestAuthentication(HTTPAuthMethod
|
||||
_srealm = String(realm);
|
||||
}
|
||||
if(mode == BASIC_AUTH) {
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\"")));
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String('\"'));
|
||||
} else {
|
||||
_snonce=_getRandomHexString();
|
||||
_sopaque=_getRandomHexString();
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\"")));
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String('\"'));
|
||||
}
|
||||
using namespace mime;
|
||||
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg);
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::on(const String &uri, ESP8266WebServerTemplate<ServerType>::THandlerFunction handler) {
|
||||
void ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, ESP8266WebServerTemplate<ServerType>::THandlerFunction handler) {
|
||||
on(uri, HTTP_ANY, handler);
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::on(const String &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn) {
|
||||
void ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn) {
|
||||
on(uri, method, fn, _fileUploadHandler);
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::on(const String &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn, ESP8266WebServerTemplate<ServerType>::THandlerFunction ufn) {
|
||||
void ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn, ESP8266WebServerTemplate<ServerType>::THandlerFunction ufn) {
|
||||
_addRequestHandler(new FunctionRequestHandler<ServerType>(fn, ufn, uri, method));
|
||||
}
|
||||
|
||||
@ -319,7 +319,7 @@ void ESP8266WebServerTemplate<ServerType>::handleClient() {
|
||||
bool keepCurrentClient = false;
|
||||
bool callYield = false;
|
||||
|
||||
if (_currentClient.connected()) {
|
||||
if (_currentClient.connected() || _currentClient.available()) {
|
||||
switch (_currentStatus) {
|
||||
case HC_NONE:
|
||||
// No-op to avoid C++ compiler warning
|
||||
@ -464,9 +464,11 @@ void ESP8266WebServerTemplate<ServerType>::send_P(int code, PGM_P content_type,
|
||||
char type[64];
|
||||
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
|
||||
_prepareHeader(header, code, (const char* )type, contentLength);
|
||||
sendContent(header);
|
||||
_currentClient.write((const uint8_t *)header.c_str(), header.length());
|
||||
if (contentLength) {
|
||||
sendContent_P(content, contentLength);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::send(int code, char* content_type, const String& content) {
|
||||
@ -524,7 +526,7 @@ String ESP8266WebServerTemplate<ServerType>::credentialHash(const String& userna
|
||||
{
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
md5.add(username + ":" + realm + ":" + password); // md5 of the user:realm:password
|
||||
md5.add(username + ':' + realm + ':' + password); // md5 of the user:realm:password
|
||||
md5.calculate();
|
||||
return md5.toString();
|
||||
}
|
||||
@ -542,9 +544,15 @@ void ESP8266WebServerTemplate<ServerType>::_streamFileCore(const size_t fileSize
|
||||
send(200, contentType, emptyString);
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
const String& ESP8266WebServerTemplate<ServerType>::pathArg(unsigned int i) const {
|
||||
if (_currentHandler != nullptr)
|
||||
return _currentHandler->pathArg(i);
|
||||
return emptyString;
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
const String& ESP8266WebServerTemplate<ServerType>::arg(String name) const {
|
||||
const String& ESP8266WebServerTemplate<ServerType>::arg(const String& name) const {
|
||||
for (int j = 0; j < _postArgsLen; ++j) {
|
||||
if ( _postArgs[j].key == name )
|
||||
return _postArgs[j].value;
|
||||
@ -590,7 +598,7 @@ bool ESP8266WebServerTemplate<ServerType>::hasArg(const String& name) const {
|
||||
|
||||
|
||||
template <typename ServerType>
|
||||
const String& ESP8266WebServerTemplate<ServerType>::header(String name) const {
|
||||
const String& ESP8266WebServerTemplate<ServerType>::header(const String& name) const {
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
if (_currentHeaders[i].key.equalsIgnoreCase(name))
|
||||
return _currentHeaders[i].value;
|
||||
@ -630,7 +638,7 @@ int ESP8266WebServerTemplate<ServerType>::headers() const {
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
bool ESP8266WebServerTemplate<ServerType>::hasHeader(String name) const {
|
||||
bool ESP8266WebServerTemplate<ServerType>::hasHeader(const String& name) const {
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0))
|
||||
return true;
|
||||
@ -693,49 +701,136 @@ void ESP8266WebServerTemplate<ServerType>::_finalizeResponse() {
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
const String ESP8266WebServerTemplate<ServerType>::responseCodeToString(const int code) {
|
||||
switch (code) {
|
||||
case 100: return F("Continue");
|
||||
case 101: return F("Switching Protocols");
|
||||
case 200: return F("OK");
|
||||
case 201: return F("Created");
|
||||
case 202: return F("Accepted");
|
||||
case 203: return F("Non-Authoritative Information");
|
||||
case 204: return F("No Content");
|
||||
case 205: return F("Reset Content");
|
||||
case 206: return F("Partial Content");
|
||||
case 300: return F("Multiple Choices");
|
||||
case 301: return F("Moved Permanently");
|
||||
case 302: return F("Found");
|
||||
case 303: return F("See Other");
|
||||
case 304: return F("Not Modified");
|
||||
case 305: return F("Use Proxy");
|
||||
case 307: return F("Temporary Redirect");
|
||||
case 400: return F("Bad Request");
|
||||
case 401: return F("Unauthorized");
|
||||
case 402: return F("Payment Required");
|
||||
case 403: return F("Forbidden");
|
||||
case 404: return F("Not Found");
|
||||
case 405: return F("Method Not Allowed");
|
||||
case 406: return F("Not Acceptable");
|
||||
case 407: return F("Proxy Authentication Required");
|
||||
case 408: return F("Request Time-out");
|
||||
case 409: return F("Conflict");
|
||||
case 410: return F("Gone");
|
||||
case 411: return F("Length Required");
|
||||
case 412: return F("Precondition Failed");
|
||||
case 413: return F("Request Entity Too Large");
|
||||
case 414: return F("Request-URI Too Large");
|
||||
case 415: return F("Unsupported Media Type");
|
||||
case 416: return F("Requested range not satisfiable");
|
||||
case 417: return F("Expectation Failed");
|
||||
case 418: return F("I'm a teapot");
|
||||
case 500: return F("Internal Server Error");
|
||||
case 501: return F("Not Implemented");
|
||||
case 502: return F("Bad Gateway");
|
||||
case 503: return F("Service Unavailable");
|
||||
case 504: return F("Gateway Time-out");
|
||||
case 505: return F("HTTP Version not supported");
|
||||
default: return F("");
|
||||
String ESP8266WebServerTemplate<ServerType>::responseCodeToString(const int code) {
|
||||
// By first determining the pointer to the flash stored string in the switch
|
||||
// statement and then doing String(FlashStringHelper) return reduces the total code
|
||||
// size of this function by over 50%.
|
||||
const __FlashStringHelper *r;
|
||||
switch (code)
|
||||
{
|
||||
case 100:
|
||||
r = F("Continue");
|
||||
break;
|
||||
case 101:
|
||||
r = F("Switching Protocols");
|
||||
break;
|
||||
case 200:
|
||||
r = F("OK");
|
||||
break;
|
||||
case 201:
|
||||
r = F("Created");
|
||||
break;
|
||||
case 202:
|
||||
r = F("Accepted");
|
||||
break;
|
||||
case 203:
|
||||
r = F("Non-Authoritative Information");
|
||||
break;
|
||||
case 204:
|
||||
r = F("No Content");
|
||||
break;
|
||||
case 205:
|
||||
r = F("Reset Content");
|
||||
break;
|
||||
case 206:
|
||||
r = F("Partial Content");
|
||||
break;
|
||||
case 300:
|
||||
r = F("Multiple Choices");
|
||||
break;
|
||||
case 301:
|
||||
r = F("Moved Permanently");
|
||||
break;
|
||||
case 302:
|
||||
r = F("Found");
|
||||
break;
|
||||
case 303:
|
||||
r = F("See Other");
|
||||
break;
|
||||
case 304:
|
||||
r = F("Not Modified");
|
||||
break;
|
||||
case 305:
|
||||
r = F("Use Proxy");
|
||||
break;
|
||||
case 307:
|
||||
r = F("Temporary Redirect");
|
||||
break;
|
||||
case 400:
|
||||
r = F("Bad Request");
|
||||
break;
|
||||
case 401:
|
||||
r = F("Unauthorized");
|
||||
break;
|
||||
case 402:
|
||||
r = F("Payment Required");
|
||||
break;
|
||||
case 403:
|
||||
r = F("Forbidden");
|
||||
break;
|
||||
case 404:
|
||||
r = F("Not Found");
|
||||
break;
|
||||
case 405:
|
||||
r = F("Method Not Allowed");
|
||||
break;
|
||||
case 406:
|
||||
r = F("Not Acceptable");
|
||||
break;
|
||||
case 407:
|
||||
r = F("Proxy Authentication Required");
|
||||
break;
|
||||
case 408:
|
||||
r = F("Request Timeout");
|
||||
break;
|
||||
case 409:
|
||||
r = F("Conflict");
|
||||
break;
|
||||
case 410:
|
||||
r = F("Gone");
|
||||
break;
|
||||
case 411:
|
||||
r = F("Length Required");
|
||||
break;
|
||||
case 412:
|
||||
r = F("Precondition Failed");
|
||||
break;
|
||||
case 413:
|
||||
r = F("Request Entity Too Large");
|
||||
break;
|
||||
case 414:
|
||||
r = F("URI Too Long");
|
||||
break;
|
||||
case 415:
|
||||
r = F("Unsupported Media Type");
|
||||
break;
|
||||
case 416:
|
||||
r = F("Range not satisfiable");
|
||||
break;
|
||||
case 417:
|
||||
r = F("Expectation Failed");
|
||||
break;
|
||||
case 500:
|
||||
r = F("Internal Server Error");
|
||||
break;
|
||||
case 501:
|
||||
r = F("Not Implemented");
|
||||
break;
|
||||
case 502:
|
||||
r = F("Bad Gateway");
|
||||
break;
|
||||
case 503:
|
||||
r = F("Service Unavailable");
|
||||
break;
|
||||
case 504:
|
||||
r = F("Gateway Timeout");
|
||||
break;
|
||||
case 505:
|
||||
r = F("HTTP Version not supported");
|
||||
break;
|
||||
default:
|
||||
r = F("");
|
||||
break;
|
||||
}
|
||||
return String(r);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <FS.h>
|
||||
#include "detail/mimetable.h"
|
||||
#include "Uri.h"
|
||||
|
||||
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_HEAD, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS };
|
||||
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
|
||||
@ -91,9 +92,9 @@ public:
|
||||
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") );
|
||||
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
void on(const String &uri, THandlerFunction handler);
|
||||
void on(const String &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
|
||||
void on(const Uri &uri, THandlerFunction handler);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
|
||||
void addHandler(RequestHandlerType* handler);
|
||||
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL );
|
||||
void onNotFound(THandlerFunction fn); //called when handler is not assigned
|
||||
@ -107,17 +108,18 @@ public:
|
||||
// Allows setting server options (i.e. SSL keys) by the instantiator
|
||||
ServerType &getServer() { return _server; }
|
||||
|
||||
const String& arg(String name) const; // get request argument value by name
|
||||
const String& pathArg(unsigned int i) const; // get request path argument by number
|
||||
const String& arg(const String& name) const; // get request argument value by name
|
||||
const String& arg(int i) const; // get request argument value by number
|
||||
const String& argName(int i) const; // get request argument name by number
|
||||
int args() const; // get arguments count
|
||||
bool hasArg(const String& name) const; // check if argument exists
|
||||
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect
|
||||
const String& header(String name) const; // get request header value by name
|
||||
const String& header(const String& name) const; // get request header value by name
|
||||
const String& header(int i) const; // get request header value by number
|
||||
const String& headerName(int i) const; // get request header name by number
|
||||
int headers() const; // get header count
|
||||
bool hasHeader(String name) const; // check if header exists
|
||||
bool hasHeader(const String& name) const; // check if header exists
|
||||
const String& hostHeader() const; // get request host header if available or empty String if not
|
||||
|
||||
// send response to the client
|
||||
@ -127,6 +129,15 @@ public:
|
||||
void send(int code, const char* content_type = NULL, const String& content = String(""));
|
||||
void send(int code, char* content_type, const String& content);
|
||||
void send(int code, const String& content_type, const String& content);
|
||||
void send(int code, const char *content_type, const char *content) {
|
||||
send_P(code, content_type, content);
|
||||
}
|
||||
void send(int code, const char *content_type, const char *content, size_t content_length) {
|
||||
send_P(code, content_type, content, content_length);
|
||||
}
|
||||
void send(int code, const char *content_type, const uint8_t *content, size_t content_length) {
|
||||
send_P(code, content_type, (const char *)content, content_length);
|
||||
}
|
||||
void send_P(int code, PGM_P content_type, PGM_P content);
|
||||
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
|
||||
|
||||
@ -142,13 +153,25 @@ public:
|
||||
|
||||
static String urlDecode(const String& text);
|
||||
|
||||
// Handle a GET request by sending a response header and stream file content to response body
|
||||
template<typename T>
|
||||
size_t streamFile(T &file, const String& contentType) {
|
||||
_streamFileCore(file.size(), file.name(), contentType);
|
||||
return _currentClient.write(file);
|
||||
return streamFile(file, contentType, HTTP_GET);
|
||||
}
|
||||
|
||||
static const String responseCodeToString(const int code);
|
||||
// Implement GET and HEAD requests for files.
|
||||
// Stream body on HTTP_GET but not on HTTP_HEAD requests.
|
||||
template<typename T>
|
||||
size_t streamFile(T &file, const String& contentType, HTTPMethod requestMethod) {
|
||||
size_t contentLength = 0;
|
||||
_streamFileCore(file.size(), file.name(), contentType);
|
||||
if (requestMethod == HTTP_GET) {
|
||||
contentLength = _currentClient.write(file);
|
||||
}
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
static String responseCodeToString(const int code);
|
||||
|
||||
protected:
|
||||
void _addRequestHandler(RequestHandlerType* handler);
|
||||
|
@ -89,7 +89,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
||||
String url = req.substring(addr_start + 1, addr_end);
|
||||
String versionEnd = req.substring(addr_end + 8);
|
||||
_currentVersion = atoi(versionEnd.c_str());
|
||||
String searchStr = "";
|
||||
String searchStr;
|
||||
int hasSearch = url.indexOf('?');
|
||||
if (hasSearch != -1){
|
||||
searchStr = url.substring(hasSearch + 1);
|
||||
@ -144,7 +144,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
||||
while(1){
|
||||
req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (req == "") break;//no moar headers
|
||||
if (req.isEmpty()) break;//no moar headers
|
||||
int headerDiv = req.indexOf(':');
|
||||
if (headerDiv == -1){
|
||||
break;
|
||||
@ -222,7 +222,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
||||
while(1){
|
||||
req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (req == "") break;//no moar headers
|
||||
if (req.isEmpty()) break;//no moar headers
|
||||
int headerDiv = req.indexOf(':');
|
||||
if (headerDiv == -1){
|
||||
break;
|
||||
@ -452,7 +452,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.startsWith("--"+boundary)) break;
|
||||
if (argValue.length() > 0) argValue += "\n";
|
||||
if (argValue.length() > 0) argValue += '\n';
|
||||
argValue += line;
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
@ -600,7 +600,7 @@ readfile:
|
||||
template <typename ServerType>
|
||||
String ESP8266WebServerTemplate<ServerType>::urlDecode(const String& text)
|
||||
{
|
||||
String decoded = "";
|
||||
String decoded;
|
||||
char temp[] = "0x00";
|
||||
unsigned int len = text.length();
|
||||
unsigned int i = 0;
|
||||
|
27
libraries/ESP8266WebServer/src/Uri.h
Normal file
27
libraries/ESP8266WebServer/src/Uri.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef URI_H
|
||||
#define URI_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
|
||||
class Uri {
|
||||
|
||||
protected:
|
||||
const String _uri;
|
||||
|
||||
public:
|
||||
Uri(const char *uri) : _uri(uri) {}
|
||||
Uri(const String &uri) : _uri(uri) {}
|
||||
Uri(const __FlashStringHelper *uri) : _uri(String(uri)) {}
|
||||
virtual ~Uri() {}
|
||||
|
||||
virtual Uri* clone() const {
|
||||
return new Uri(_uri);
|
||||
};
|
||||
|
||||
virtual bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) {
|
||||
return _uri == requestUri;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -2,6 +2,8 @@
|
||||
#define REQUESTHANDLER_H
|
||||
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
|
||||
template<typename ServerType>
|
||||
class RequestHandler {
|
||||
@ -18,6 +20,15 @@ public:
|
||||
|
||||
private:
|
||||
RequestHandler<ServerType>* _next = nullptr;
|
||||
|
||||
protected:
|
||||
std::vector<String> pathArgs;
|
||||
|
||||
public:
|
||||
const String& pathArg(unsigned int i) {
|
||||
assert(i < pathArgs.size());
|
||||
return pathArgs[i];
|
||||
}
|
||||
};
|
||||
|
||||
#endif //REQUESTHANDLER_H
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "RequestHandler.h"
|
||||
#include "mimetable.h"
|
||||
#include "WString.h"
|
||||
#include "Uri.h"
|
||||
|
||||
using namespace mime;
|
||||
|
||||
@ -12,22 +13,23 @@ template<typename ServerType>
|
||||
class FunctionRequestHandler : public RequestHandler<ServerType> {
|
||||
using WebServerType = ESP8266WebServerTemplate<ServerType>;
|
||||
public:
|
||||
FunctionRequestHandler(typename WebServerType::THandlerFunction fn, typename WebServerType::THandlerFunction ufn, const String &uri, HTTPMethod method)
|
||||
FunctionRequestHandler(typename WebServerType::THandlerFunction fn, typename WebServerType::THandlerFunction ufn, const Uri &uri, HTTPMethod method)
|
||||
: _fn(fn)
|
||||
, _ufn(ufn)
|
||||
, _uri(uri)
|
||||
, _uri(uri.clone())
|
||||
, _method(method)
|
||||
{
|
||||
}
|
||||
|
||||
~FunctionRequestHandler() {
|
||||
delete _uri;
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
|
||||
if (_method != HTTP_ANY && _method != requestMethod)
|
||||
return false;
|
||||
|
||||
if (requestUri != _uri)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return _uri->canHandle(requestUri, RequestHandler<ServerType>::pathArgs);
|
||||
}
|
||||
|
||||
bool canUpload(String requestUri) override {
|
||||
@ -56,7 +58,7 @@ public:
|
||||
protected:
|
||||
typename WebServerType::THandlerFunction _fn;
|
||||
typename WebServerType::THandlerFunction _ufn;
|
||||
String _uri;
|
||||
Uri *_uri;
|
||||
HTTPMethod _method;
|
||||
};
|
||||
|
||||
@ -70,13 +72,21 @@ public:
|
||||
, _path(path)
|
||||
, _cache_header(cache_header)
|
||||
{
|
||||
_isFile = fs.exists(path);
|
||||
if (fs.exists(path)) {
|
||||
File file = fs.open(path, "r");
|
||||
_isFile = file && file.isFile();
|
||||
file.close();
|
||||
}
|
||||
else {
|
||||
_isFile = false;
|
||||
}
|
||||
|
||||
DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header);
|
||||
_baseUriLength = _uri.length();
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
|
||||
if (requestMethod != HTTP_GET)
|
||||
if ((requestMethod != HTTP_GET) && (requestMethod != HTTP_HEAD))
|
||||
return false;
|
||||
|
||||
if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
|
||||
@ -101,6 +111,12 @@ public:
|
||||
|
||||
// Append whatever follows this URI in request to get the file path.
|
||||
path += requestUri.substring(_baseUriLength);
|
||||
|
||||
// If neither <blah> nor <blah>.gz exist, and <blah> is a file.htm, try it with file.html instead
|
||||
// For the normal case this will give a search order of index.htm, index.htm.gz, index.html, index.html.gz
|
||||
if (!_fs.exists(path) && !_fs.exists(path + ".gz") && path.endsWith(".htm")) {
|
||||
path += "l";
|
||||
}
|
||||
}
|
||||
DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
|
||||
|
||||
@ -118,10 +134,15 @@ public:
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
if (!f.isFile()) {
|
||||
f.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cache_header.length() != 0)
|
||||
server.sendHeader("Cache-Control", _cache_header);
|
||||
|
||||
server.streamFile(f, contentType);
|
||||
server.streamFile(f, contentType, requestMethod);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
54
libraries/ESP8266WebServer/src/uri/UriBraces.h
Normal file
54
libraries/ESP8266WebServer/src/uri/UriBraces.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef URI_BRACES_H
|
||||
#define URI_BRACES_H
|
||||
|
||||
#include "Uri.h"
|
||||
|
||||
class UriBraces : public Uri {
|
||||
|
||||
public:
|
||||
explicit UriBraces(const char *uri) : Uri(uri) {};
|
||||
explicit UriBraces(const String &uri) : Uri(uri) {};
|
||||
|
||||
Uri* clone() const override final {
|
||||
return new UriBraces(_uri);
|
||||
};
|
||||
|
||||
bool canHandle(const String &requestUri, std::vector<String> &pathArgs) override final {
|
||||
if (Uri::canHandle(requestUri, pathArgs))
|
||||
return true;
|
||||
|
||||
pathArgs.clear();
|
||||
|
||||
size_t uriLength = _uri.length();
|
||||
unsigned int requestUriIndex = 0;
|
||||
for (unsigned int i = 0; i < uriLength; i++, requestUriIndex++) {
|
||||
char uriChar = _uri[i];
|
||||
char requestUriChar = requestUri[requestUriIndex];
|
||||
|
||||
if (uriChar == requestUriChar)
|
||||
continue;
|
||||
if (uriChar != '{')
|
||||
return false;
|
||||
|
||||
i += 2; // index of char after '}'
|
||||
if (i >= uriLength) {
|
||||
// there is no char after '}'
|
||||
pathArgs.push_back(requestUri.substring(requestUriIndex));
|
||||
return pathArgs.back().indexOf("/") == -1; // path argument may not contain a '/'
|
||||
}
|
||||
else
|
||||
{
|
||||
char charEnd = _uri[i];
|
||||
int uriIndex = requestUri.indexOf(charEnd, requestUriIndex);
|
||||
if (uriIndex < 0)
|
||||
return false;
|
||||
pathArgs.push_back(requestUri.substring(requestUriIndex, uriIndex));
|
||||
requestUriIndex = (unsigned int) uriIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return requestUriIndex >= requestUri.length();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
22
libraries/ESP8266WebServer/src/uri/UriGlob.h
Normal file
22
libraries/ESP8266WebServer/src/uri/UriGlob.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef URI_GLOB_H
|
||||
#define URI_GLOB_H
|
||||
|
||||
#include "Uri.h"
|
||||
#include <fnmatch.h>
|
||||
|
||||
class UriGlob : public Uri {
|
||||
|
||||
public:
|
||||
explicit UriGlob(const char *uri) : Uri(uri) {};
|
||||
explicit UriGlob(const String &uri) : Uri(uri) {};
|
||||
|
||||
Uri* clone() const override final {
|
||||
return new UriGlob(_uri);
|
||||
};
|
||||
|
||||
bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) override final {
|
||||
return fnmatch(_uri.c_str(), requestUri.c_str(), 0) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
54
libraries/ESP8266WebServer/src/uri/UriRegex.h
Normal file
54
libraries/ESP8266WebServer/src/uri/UriRegex.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef URI_REGEX_H
|
||||
#define URI_REGEX_H
|
||||
|
||||
#include "Uri.h"
|
||||
#include <regex.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef REGEX_MAX_GROUPS
|
||||
#define REGEX_MAX_GROUPS 10
|
||||
#endif
|
||||
|
||||
class UriRegex : public Uri {
|
||||
|
||||
private:
|
||||
regex_t _regexCompiled;
|
||||
|
||||
public:
|
||||
explicit UriRegex(const char *uri) : Uri(uri) {
|
||||
assert(regcomp(&_regexCompiled, uri, REG_EXTENDED) == 0);
|
||||
};
|
||||
explicit UriRegex(const String &uri) : UriRegex(uri.c_str()) {};
|
||||
|
||||
~UriRegex() {
|
||||
regfree(&_regexCompiled);
|
||||
}
|
||||
|
||||
Uri* clone() const override final {
|
||||
return new UriRegex(_uri);
|
||||
};
|
||||
|
||||
bool canHandle(const String &requestUri, std::vector<String> &pathArgs) override final {
|
||||
if (Uri::canHandle(requestUri, pathArgs))
|
||||
return true;
|
||||
|
||||
regmatch_t groupArray[REGEX_MAX_GROUPS];
|
||||
if (regexec(&_regexCompiled, requestUri.c_str(), REGEX_MAX_GROUPS, groupArray, 0) == 0) {
|
||||
// matches
|
||||
pathArgs.clear();
|
||||
|
||||
unsigned int g = 1;
|
||||
for (; g < REGEX_MAX_GROUPS; g++) {
|
||||
if (groupArray[g].rm_so == (long int)-1)
|
||||
break; // No more groups
|
||||
|
||||
pathArgs.push_back(requestUri.substring(groupArray[g].rm_so, groupArray[g].rm_eo));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <StackThunk.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifndef STASSID
|
||||
@ -81,7 +82,8 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_
|
||||
}
|
||||
client->stop();
|
||||
uint32_t freeStackEnd = ESP.getFreeContStack();
|
||||
Serial.printf("\nCONT stack used: %d\n-------\n\n", freeStackStart - freeStackEnd);
|
||||
Serial.printf("\nCONT stack used: %d\n", freeStackStart - freeStackEnd);
|
||||
Serial.printf("BSSL stack used: %d\n-------\n\n", stack_thunk_get_max_usage());
|
||||
}
|
||||
|
||||
void fetchNoConfig() {
|
||||
|
@ -85,8 +85,8 @@ void setup() {
|
||||
Serial.swap();
|
||||
// Hardware serial is now on RX:GPIO13 TX:GPIO15
|
||||
// use SoftwareSerial on regular RX(3)/TX(1) for logging
|
||||
logger = new SoftwareSerial();
|
||||
logger->begin(BAUD_LOGGER, 3, 1);
|
||||
logger = new SoftwareSerial(3, 1);
|
||||
logger->begin(BAUD_LOGGER);
|
||||
logger->enableIntTx(false);
|
||||
logger->println("\n\nUsing SoftwareSerial for logging");
|
||||
#else
|
||||
|
@ -82,6 +82,10 @@ int CertStore::initCertStore(FS &fs, const char *indexFileName, const char *data
|
||||
|
||||
_fs = &fs;
|
||||
|
||||
// In case initCertStore called multiple times, don't leak old filenames
|
||||
free(_indexName);
|
||||
free(_dataName);
|
||||
|
||||
// No strdup_P, so manually do it
|
||||
_indexName = (char *)malloc(strlen_P(indexFileName) + 1);
|
||||
_dataName = (char *)malloc(strlen_P(dataFileName) + 1);
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <list>
|
||||
#include <string.h>
|
||||
#include <coredecls.h>
|
||||
#include <PolledTimeout.h>
|
||||
#include "ESP8266WiFi.h"
|
||||
#include "ESP8266WiFiGeneric.h"
|
||||
|
||||
@ -434,6 +435,25 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m, WiFiState* state) {
|
||||
}
|
||||
ETS_UART_INTR_ENABLE();
|
||||
|
||||
if(!ret)
|
||||
return false; //calling wifi_set_opmode failed
|
||||
|
||||
//Wait for mode change, which is asynchronous.
|
||||
//Only wait if in CONT context. If this were called from SYS, it's up to the user to serialize
|
||||
//tasks to wait correctly.
|
||||
constexpr unsigned int timeoutValue = 1000; //1 second
|
||||
if(can_yield()) {
|
||||
using oneShot = esp8266::polledTimeout::oneShotFastMs;
|
||||
oneShot timeout(timeoutValue);
|
||||
while(wifi_get_opmode() != (uint8) m && !timeout)
|
||||
delay(5);
|
||||
|
||||
//if at this point mode still hasn't been reached, give up
|
||||
if(wifi_get_opmode() != (uint8) m) {
|
||||
return false; //timeout
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -603,6 +623,7 @@ int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResul
|
||||
} else if(err == ERR_INPROGRESS) {
|
||||
_dns_lookup_pending = true;
|
||||
delay(timeout_ms);
|
||||
// will resume on timeout or when wifi_dns_found_callback fires
|
||||
_dns_lookup_pending = false;
|
||||
// will return here when dns_found_callback fires
|
||||
if(aResult.isSet()) {
|
||||
@ -634,7 +655,7 @@ void wifi_dns_found_callback(const char *name, CONST ip_addr_t *ipaddr, void *ca
|
||||
if(ipaddr) {
|
||||
(*reinterpret_cast<IPAddress*>(callback_arg)) = IPAddress(ipaddr);
|
||||
}
|
||||
esp_schedule(); // resume the hostByName function
|
||||
esp_schedule(); // break delay in hostByName
|
||||
}
|
||||
|
||||
uint32_t ESP8266WiFiGenericClass::shutdownCRC (const WiFiState* state)
|
||||
|
@ -38,6 +38,10 @@ bool ESP8266WiFiMulti::addAP(const char* ssid, const char *passphrase) {
|
||||
return APlistAdd(ssid, passphrase);
|
||||
}
|
||||
|
||||
void ESP8266WiFiMulti::cleanAPlist(void) {
|
||||
APlistClean();
|
||||
}
|
||||
|
||||
bool ESP8266WiFiMulti::existsAP(const char* ssid, const char *passphrase) {
|
||||
return APlistExists(ssid, passphrase);
|
||||
}
|
||||
|
@ -57,6 +57,8 @@ class ESP8266WiFiMulti {
|
||||
|
||||
wl_status_t run(void);
|
||||
|
||||
void cleanAPlist(void);
|
||||
|
||||
private:
|
||||
WifiAPlist APlist;
|
||||
bool APlistAdd(const char* ssid, const char *passphrase = NULL);
|
||||
|
@ -71,7 +71,7 @@ bool ESP8266WiFiSTAClass::beginWPSConfig(void) {
|
||||
}
|
||||
|
||||
esp_yield();
|
||||
// will return here when wifi_wps_status_cb fires
|
||||
// will resume when wifi_wps_status_cb fires
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -107,5 +107,5 @@ void wifi_wps_status_cb(wps_cb_status status) {
|
||||
}
|
||||
// TODO user function to get status
|
||||
|
||||
esp_schedule(); // resume the beginWPSConfig function
|
||||
esp_schedule(); // resume beginWPSConfig
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ int8_t ESP8266WiFiScanClass::scanNetworks(bool async, bool show_hidden, uint8 ch
|
||||
return WIFI_SCAN_RUNNING;
|
||||
}
|
||||
|
||||
esp_yield();
|
||||
esp_yield(); // will resume when _scanDone fires
|
||||
return ESP8266WiFiScanClass::_scanCount;
|
||||
} else {
|
||||
return WIFI_SCAN_FAILED;
|
||||
@ -323,7 +323,7 @@ void ESP8266WiFiScanClass::_scanDone(void* result, int status) {
|
||||
ESP8266WiFiScanClass::_scanComplete = true;
|
||||
|
||||
if(!ESP8266WiFiScanClass::_scanAsync) {
|
||||
esp_schedule();
|
||||
esp_schedule(); // resume scanNetworks
|
||||
} else if (ESP8266WiFiScanClass::_onComplete) {
|
||||
ESP8266WiFiScanClass::_onComplete(ESP8266WiFiScanClass::_scanCount);
|
||||
ESP8266WiFiScanClass::_onComplete = nullptr;
|
||||
|
@ -346,7 +346,7 @@ uint8_t WiFiClient::status()
|
||||
|
||||
WiFiClient::operator bool()
|
||||
{
|
||||
return connected();
|
||||
return available() || connected();
|
||||
}
|
||||
|
||||
IPAddress WiFiClient::remoteIP()
|
||||
|
@ -35,12 +35,17 @@ extern "C" {
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/init.h" // LWIP_VERSION_
|
||||
#include <include/ClientContext.h>
|
||||
|
||||
#ifndef MAX_PENDING_CLIENTS_PER_PORT
|
||||
#define MAX_PENDING_CLIENTS_PER_PORT 5
|
||||
#endif
|
||||
|
||||
WiFiServer::WiFiServer(const IPAddress& addr, uint16_t port)
|
||||
: _port(port)
|
||||
, _addr(addr)
|
||||
, _pcb(nullptr)
|
||||
, _listen_pcb(nullptr)
|
||||
, _unclaimed(nullptr)
|
||||
, _discarded(nullptr)
|
||||
{
|
||||
@ -49,7 +54,7 @@ WiFiServer::WiFiServer(const IPAddress& addr, uint16_t port)
|
||||
WiFiServer::WiFiServer(uint16_t port)
|
||||
: _port(port)
|
||||
, _addr(IP_ANY_TYPE)
|
||||
, _pcb(nullptr)
|
||||
, _listen_pcb(nullptr)
|
||||
, _unclaimed(nullptr)
|
||||
, _discarded(nullptr)
|
||||
{
|
||||
@ -60,9 +65,14 @@ void WiFiServer::begin() {
|
||||
}
|
||||
|
||||
void WiFiServer::begin(uint16_t port) {
|
||||
return begin(port, MAX_PENDING_CLIENTS_PER_PORT);
|
||||
}
|
||||
|
||||
void WiFiServer::begin(uint16_t port, uint8_t backlog) {
|
||||
close();
|
||||
if (!backlog)
|
||||
return;
|
||||
_port = port;
|
||||
err_t err;
|
||||
tcp_pcb* pcb = tcp_new();
|
||||
if (!pcb)
|
||||
return;
|
||||
@ -70,19 +80,23 @@ void WiFiServer::begin(uint16_t port) {
|
||||
pcb->so_options |= SOF_REUSEADDR;
|
||||
|
||||
// (IPAddress _addr) operator-converted to (const ip_addr_t*)
|
||||
err = tcp_bind(pcb, _addr, _port);
|
||||
|
||||
if (err != ERR_OK) {
|
||||
if (tcp_bind(pcb, _addr, _port) != ERR_OK) {
|
||||
tcp_close(pcb);
|
||||
return;
|
||||
}
|
||||
|
||||
#if LWIP_VERSION_MAJOR == 1
|
||||
tcp_pcb* listen_pcb = tcp_listen(pcb);
|
||||
#else
|
||||
tcp_pcb* listen_pcb = tcp_listen_with_backlog(pcb, backlog);
|
||||
#endif
|
||||
|
||||
if (!listen_pcb) {
|
||||
tcp_close(pcb);
|
||||
return;
|
||||
}
|
||||
_pcb = listen_pcb;
|
||||
_listen_pcb = listen_pcb;
|
||||
_port = _listen_pcb->local_port;
|
||||
tcp_accept(listen_pcb, &WiFiServer::_s_accept);
|
||||
tcp_arg(listen_pcb, (void*) this);
|
||||
}
|
||||
@ -110,9 +124,15 @@ WiFiClient WiFiServer::available(byte* status) {
|
||||
(void) status;
|
||||
if (_unclaimed) {
|
||||
WiFiClient result(_unclaimed);
|
||||
#if LWIP_VERSION_MAJOR != 1
|
||||
// pcb can be null when peer has already closed the connection
|
||||
if (_unclaimed->getPCB())
|
||||
// give permission to lwIP to accept one more peer
|
||||
tcp_backlog_accepted(_unclaimed->getPCB());
|
||||
#endif
|
||||
_unclaimed = _unclaimed->next();
|
||||
result.setNoDelay(getNoDelay());
|
||||
DEBUGV("WS:av\r\n");
|
||||
DEBUGV("WS:av status=%d WCav=%d\r\n", result.status(), result.available());
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -121,17 +141,21 @@ WiFiClient WiFiServer::available(byte* status) {
|
||||
}
|
||||
|
||||
uint8_t WiFiServer::status() {
|
||||
if (!_pcb)
|
||||
if (!_listen_pcb)
|
||||
return CLOSED;
|
||||
return _pcb->state;
|
||||
return _listen_pcb->state;
|
||||
}
|
||||
|
||||
uint16_t WiFiServer::port() const {
|
||||
return _port;
|
||||
}
|
||||
|
||||
void WiFiServer::close() {
|
||||
if (!_pcb) {
|
||||
if (!_listen_pcb) {
|
||||
return;
|
||||
}
|
||||
tcp_close(_pcb);
|
||||
_pcb = nullptr;
|
||||
tcp_close(_listen_pcb);
|
||||
_listen_pcb = nullptr;
|
||||
}
|
||||
|
||||
void WiFiServer::stop() {
|
||||
@ -164,9 +188,28 @@ T* slist_append_tail(T* head, T* item) {
|
||||
long WiFiServer::_accept(tcp_pcb* apcb, long err) {
|
||||
(void) err;
|
||||
DEBUGV("WS:ac\r\n");
|
||||
|
||||
// always accept new PCB so incoming data can be stored in our buffers even before
|
||||
// user calls ::available()
|
||||
ClientContext* client = new ClientContext(apcb, &WiFiServer::_s_discard, this);
|
||||
|
||||
#if LWIP_VERSION_MAJOR == 1
|
||||
|
||||
tcp_accepted(_listen_pcb);
|
||||
|
||||
#else
|
||||
|
||||
// backlog doc:
|
||||
// http://lwip.100.n7.nabble.com/Problem-re-opening-listening-pbc-tt32484.html#a32494
|
||||
// https://www.nongnu.org/lwip/2_1_x/group__tcp__raw.html#gaeff14f321d1eecd0431611f382fcd338
|
||||
|
||||
// increase lwIP's backlog
|
||||
tcp_backlog_delayed(apcb);
|
||||
|
||||
#endif
|
||||
|
||||
_unclaimed = slist_append_tail(_unclaimed, client);
|
||||
tcp_accepted(_pcb);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user