1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-21 10:26:06 +03:00

Merge branch 'master' into wifi_mesh_update_2.2

This commit is contained in:
aerlon 2019-10-29 21:55:08 +01:00 committed by GitHub
commit b0ef9195b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
192 changed files with 18054 additions and 14744 deletions

View File

@ -1,19 +1,11 @@
language: bash language: bash
os: linux os: linux
dist: trusty dist: bionic
git: git:
depth: 1 depth: 1
submodules: false submodules: false
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-7
- gcc-7
before_install: before_install:
- git submodule update --init # no recursive update - git submodule update --init # no recursive update
@ -87,14 +79,14 @@ jobs:
- name: "Host tests" - name: "Host tests"
stage: build stage: build
script: $TRAVIS_BUILD_DIR/tests/ci/host_test.sh script: $TRAVIS_BUILD_DIR/tests/ci/host_test.sh
install: sudo apt-get install valgrind lcov install:
env: CC=gcc-7 CXX=g++-7 - sudo apt-get install valgrind lcov
- name: "Docs" - name: "Docs"
stage: build stage: build
script: $TRAVIS_BUILD_DIR/tests/ci/build_docs.sh script: $TRAVIS_BUILD_DIR/tests/ci/build_docs.sh
install: install:
- sudo apt-get install python3-pip - sudo apt-get install python3-pip python3-setuptools
- pip3 install --user -r doc/requirements.txt; - pip3 install --user -r doc/requirements.txt;
- name: "Style check" - name: "Style check"
@ -105,7 +97,6 @@ jobs:
- name: "Mock trivial test" - name: "Mock trivial test"
stage: build stage: build
script: $TRAVIS_BUILD_DIR/tests/buildm.sh script: $TRAVIS_BUILD_DIR/tests/buildm.sh
env: CC=gcc-7 CXX=g++-7
- name: "Mac OSX can build sketches" - name: "Mac OSX can build sketches"
os: osx os: osx

View File

@ -16,7 +16,7 @@ ESP8266 Arduino core comes with libraries to communicate over WiFi using TCP and
# Contents # Contents
- Installing options: - Installing options:
- [Using Boards Manager](#installing-with-boards-manager) - [Using Boards Manager](#installing-with-boards-manager)
- [Using git version](#using-git-version-basic-instructions) - [Using git version](#using-git-version)
- [Using PlatformIO](#using-platformio) - [Using PlatformIO](#using-platformio)
- [Building with make](#building-with-make) - [Building with make](#building-with-make)
- [Documentation](#documentation) - [Documentation](#documentation)
@ -38,35 +38,13 @@ Boards manager link: `https://arduino.esp8266.com/stable/package_esp8266com_inde
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.5.2/](https://arduino-esp8266.readthedocs.io/en/2.5.2/)
### Using git version (basic instructions) ### Using git version
[![Linux build status](https://travis-ci.org/esp8266/Arduino.svg)](https://travis-ci.org/esp8266/Arduino) [![Linux build status](https://travis-ci.org/esp8266/Arduino.svg)](https://travis-ci.org/esp8266/Arduino)
Also known as latest git or master branch.
- Install the current upstream Arduino IDE at the 1.8 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/main/software). - Install the current upstream Arduino IDE at the 1.8 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/main/software).
- Go to Arduino directory - Follow the [instructions in the documentation](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version).
- For Mac OS X, it is `Arduino.app` showing as the Arduino icon.
This location may be your `~/Downloads`, `~/Desktop` or even `/Applications`.
```bash
cd <application-directory>/Arduino.app/Contents/Java
```
- For Linux, it is ~/Arduino by default.
```bash
cd ~/Arduino
```
- Clone this repository into hardware/esp8266com/esp8266 directory (or clone it elsewhere and create a symlink)
```bash
cd hardware
mkdir esp8266com
cd esp8266com
git clone https://github.com/esp8266/Arduino.git esp8266
cd esp8266
git submodule update --init
```
- Download binary tools (you need Python 2.7)
```bash
cd esp8266/tools
python get.py
```
- Restart Arduino
### Using PlatformIO ### Using PlatformIO

View File

@ -76,6 +76,10 @@ generic.menu.FlashFreq.40=40MHz
generic.menu.FlashFreq.40.build.flash_freq=40 generic.menu.FlashFreq.40.build.flash_freq=40
generic.menu.FlashFreq.80=80MHz generic.menu.FlashFreq.80=80MHz
generic.menu.FlashFreq.80.build.flash_freq=80 generic.menu.FlashFreq.80.build.flash_freq=80
generic.menu.FlashFreq.20=20MHz
generic.menu.FlashFreq.20.build.flash_freq=20
generic.menu.FlashFreq.26=26MHz
generic.menu.FlashFreq.26.build.flash_freq=26
generic.menu.FlashMode.dout=DOUT (compatible) generic.menu.FlashMode.dout=DOUT (compatible)
generic.menu.FlashMode.dout.build.flash_mode=dout generic.menu.FlashMode.dout.build.flash_mode=dout
generic.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT generic.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT
@ -370,11 +374,13 @@ generic.menu.led.15=15
generic.menu.led.15.build.led=-DLED_BUILTIN=15 generic.menu.led.15.build.led=-DLED_BUILTIN=15
generic.menu.led.16=16 generic.menu.led.16=16
generic.menu.led.16.build.led=-DLED_BUILTIN=16 generic.menu.led.16.build.led=-DLED_BUILTIN=16
generic.menu.sdk.nonosdk222_100=nonos-sdk 2.2.1+100 (testing) generic.menu.sdk.nonosdk222_100=nonos-sdk 2.2.1+100 (190703 approved)
generic.menu.sdk.nonosdk222_100.build.sdk=NONOSDK22y generic.menu.sdk.nonosdk222_100.build.sdk=NONOSDK22x_190703
generic.menu.sdk.nonosdk222_111=nonos-sdk 2.2.1+111 (191024 testing)
generic.menu.sdk.nonosdk222_111.build.sdk=NONOSDK22x_191024
generic.menu.sdk.nonosdk221=nonos-sdk 2.2.1 (legacy) generic.menu.sdk.nonosdk221=nonos-sdk 2.2.1 (legacy)
generic.menu.sdk.nonosdk221.build.sdk=NONOSDK221 generic.menu.sdk.nonosdk221.build.sdk=NONOSDK221
generic.menu.sdk.nonosdk3v0=nonos-sdk pre-3 (known issues) generic.menu.sdk.nonosdk3v0=nonos-sdk pre-3 (180626 known issues)
generic.menu.sdk.nonosdk3v0.build.sdk=NONOSDK3V0 generic.menu.sdk.nonosdk3v0.build.sdk=NONOSDK3V0
generic.menu.ip.lm2f=v2 Lower Memory generic.menu.ip.lm2f=v2 Lower Memory
generic.menu.ip.lm2f.build.lwip_include=lwip2/include generic.menu.ip.lm2f.build.lwip_include=lwip2/include
@ -4758,6 +4764,10 @@ wifinfo.menu.FlashFreq.40=40MHz
wifinfo.menu.FlashFreq.40.build.flash_freq=40 wifinfo.menu.FlashFreq.40.build.flash_freq=40
wifinfo.menu.FlashFreq.80=80MHz wifinfo.menu.FlashFreq.80=80MHz
wifinfo.menu.FlashFreq.80.build.flash_freq=80 wifinfo.menu.FlashFreq.80.build.flash_freq=80
wifinfo.menu.FlashFreq.20=20MHz
wifinfo.menu.FlashFreq.20.build.flash_freq=20
wifinfo.menu.FlashFreq.26=26MHz
wifinfo.menu.FlashFreq.26.build.flash_freq=26
wifinfo.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) wifinfo.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB)
wifinfo.menu.eesz.1M64.build.flash_size=1M wifinfo.menu.eesz.1M64.build.flash_size=1M
wifinfo.menu.eesz.1M64.build.flash_size_bytes=0x100000 wifinfo.menu.eesz.1M64.build.flash_size_bytes=0x100000
@ -5778,6 +5788,10 @@ wifi_slot.menu.FlashFreq.40=40MHz
wifi_slot.menu.FlashFreq.40.build.flash_freq=40 wifi_slot.menu.FlashFreq.40.build.flash_freq=40
wifi_slot.menu.FlashFreq.80=80MHz wifi_slot.menu.FlashFreq.80=80MHz
wifi_slot.menu.FlashFreq.80.build.flash_freq=80 wifi_slot.menu.FlashFreq.80.build.flash_freq=80
wifi_slot.menu.FlashFreq.20=20MHz
wifi_slot.menu.FlashFreq.20.build.flash_freq=20
wifi_slot.menu.FlashFreq.26=26MHz
wifi_slot.menu.FlashFreq.26.build.flash_freq=26
wifi_slot.menu.FlashMode.dout=DOUT (compatible) wifi_slot.menu.FlashMode.dout=DOUT (compatible)
wifi_slot.menu.FlashMode.dout.build.flash_mode=dout wifi_slot.menu.FlashMode.dout.build.flash_mode=dout
wifi_slot.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT wifi_slot.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT

View File

@ -275,17 +275,16 @@ long secureRandom(long);
long secureRandom(long, long); long secureRandom(long, long);
long map(long, long, long, long, long); long map(long, long, long, long, long);
extern "C" void configTime(long timezone, int daylightOffset_sec, void configTime(int timezone, int daylightOffset_sec, const char* server1,
const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); const char* server2 = nullptr, const char* server3 = nullptr);
#endif void configTime(const char* tz, const char* server1,
const char* server2 = nullptr, const char* server3 = nullptr);
#endif // __cplusplus
#include "pins_arduino.h" #include "pins_arduino.h"
#ifndef PUYA_SUPPORT
#define PUYA_SUPPORT 1
#endif
#endif #endif
#ifdef DEBUG_ESP_OOM #ifdef DEBUG_ESP_OOM

View File

@ -18,7 +18,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "Arduino.h" #include "Esp.h"
#include "flash_utils.h" #include "flash_utils.h"
#include "eboot_command.h" #include "eboot_command.h"
#include <memory> #include <memory>
@ -36,6 +36,14 @@ extern struct rst_info resetInfo;
//#define DEBUG_SERIAL Serial //#define DEBUG_SERIAL Serial
#ifndef PUYA_SUPPORT
#define PUYA_SUPPORT 1
#endif
#ifndef PUYA_BUFFER_SIZE
// Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
// Always use a multiple of flash page size (256 bytes)
#define PUYA_BUFFER_SIZE 256
#endif
/** /**
* User-defined Literals * User-defined Literals
@ -494,7 +502,7 @@ uint32_t EspClass::getSketchSize() {
image_header_t image_header; image_header_t image_header;
uint32_t pos = APP_START_OFFSET; uint32_t pos = APP_START_OFFSET;
if (spi_flash_read(pos, (uint32_t*) &image_header, sizeof(image_header))) { if (spi_flash_read(pos, (uint32_t*) &image_header, sizeof(image_header)) != SPI_FLASH_RESULT_OK) {
return 0; return 0;
} }
pos += sizeof(image_header); pos += sizeof(image_header);
@ -506,7 +514,7 @@ uint32_t EspClass::getSketchSize() {
++section_index) ++section_index)
{ {
section_header_t section_header = {0, 0}; section_header_t section_header = {0, 0};
if (spi_flash_read(pos, (uint32_t*) &section_header, sizeof(section_header))) { if (spi_flash_read(pos, (uint32_t*) &section_header, sizeof(section_header)) != SPI_FLASH_RESULT_OK) {
return 0; return 0;
} }
pos += sizeof(section_header); pos += sizeof(section_header);
@ -577,26 +585,27 @@ bool EspClass::flashEraseSector(uint32_t sector) {
} }
#if PUYA_SUPPORT #if PUYA_SUPPORT
static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) { static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
if (data == nullptr) { if (data == nullptr) {
return 1; // SPI_FLASH_RESULT_ERR return SPI_FLASH_RESULT_ERR;
} }
// PUYA flash chips need to read existing data, update in memory and write modified data again. // PUYA flash chips need to read existing data, update in memory and write modified data again.
static uint32_t *flash_write_puya_buf = nullptr; static uint32_t *flash_write_puya_buf = nullptr;
int rc = 0;
uint32_t* ptr = data;
if (flash_write_puya_buf == nullptr) { if (flash_write_puya_buf == nullptr) {
flash_write_puya_buf = (uint32_t*) malloc(PUYA_BUFFER_SIZE); flash_write_puya_buf = (uint32_t*) malloc(PUYA_BUFFER_SIZE);
// No need to ever free this, since the flash chip will never change at runtime. // No need to ever free this, since the flash chip will never change at runtime.
if (flash_write_puya_buf == nullptr) { if (flash_write_puya_buf == nullptr) {
// Memory could not be allocated. // Memory could not be allocated.
return 1; // SPI_FLASH_RESULT_ERR return SPI_FLASH_RESULT_ERR;
} }
} }
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
uint32_t* ptr = data;
size_t bytesLeft = size; size_t bytesLeft = size;
uint32_t pos = offset; uint32_t pos = offset;
while (bytesLeft > 0 && rc == 0) { while (bytesLeft > 0 && rc == SPI_FLASH_RESULT_OK) {
size_t bytesNow = bytesLeft; size_t bytesNow = bytesLeft;
if (bytesNow > PUYA_BUFFER_SIZE) { if (bytesNow > PUYA_BUFFER_SIZE) {
bytesNow = PUYA_BUFFER_SIZE; bytesNow = PUYA_BUFFER_SIZE;
@ -605,7 +614,7 @@ static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
bytesLeft = 0; bytesLeft = 0;
} }
rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow); rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow);
if (rc != 0) { if (rc != SPI_FLASH_RESULT_OK) {
return rc; return rc;
} }
for (size_t i = 0; i < bytesNow / 4; ++i) { for (size_t i = 0; i < bytesNow / 4; ++i) {
@ -620,7 +629,7 @@ static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
#endif #endif
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) { bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
int rc = 0; SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
#if PUYA_SUPPORT #if PUYA_SUPPORT
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) { if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
rc = spi_flash_write_puya(offset, data, size); rc = spi_flash_write_puya(offset, data, size);
@ -630,12 +639,12 @@ bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
{ {
rc = spi_flash_write(offset, data, size); rc = spi_flash_write(offset, data, size);
} }
return rc == 0; return rc == SPI_FLASH_RESULT_OK;
} }
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) { bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
int rc = spi_flash_read(offset, (uint32_t*) data, size); auto rc = spi_flash_read(offset, (uint32_t*) data, size);
return rc == 0; return rc == SPI_FLASH_RESULT_OK;
} }
String EspClass::getSketchMD5() String EspClass::getSketchMD5()

View File

@ -23,15 +23,6 @@
#include <Arduino.h> #include <Arduino.h>
#ifndef PUYA_SUPPORT
#define PUYA_SUPPORT 0
#endif
#ifndef PUYA_BUFFER_SIZE
// Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
// Always use a multiple of flash page size (256 bytes)
#define PUYA_BUFFER_SIZE 256
#endif
// Vendor IDs taken from Flashrom project // Vendor IDs taken from Flashrom project
// https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h?h=1.0.x // https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h?h=1.0.x
typedef enum { typedef enum {

View File

@ -108,14 +108,16 @@ int HardwareSerial::available(void)
void HardwareSerial::flush() void HardwareSerial::flush()
{ {
uint8_t bit_length = 0;
if(!_uart || !uart_tx_enabled(_uart)) { if(!_uart || !uart_tx_enabled(_uart)) {
return; return;
} }
bit_length = uart_get_bit_length(_uart_nr); // data width, parity and stop
uart_wait_tx_empty(_uart); uart_wait_tx_empty(_uart);
//Workaround for a bug in serial not actually being finished yet //Workaround for a bug in serial not actually being finished yet
//Wait for 8 data bits, 1 parity and 2 stop bits, just in case //Wait for 8 data bits, 1 parity and 2 stop bits, just in case
delayMicroseconds(11000000 / uart_get_baudrate(_uart) + 1); delayMicroseconds(bit_length * 1000000 / uart_get_baudrate(_uart) + 1);
} }
void HardwareSerial::startDetectBaudrate() void HardwareSerial::startDetectBaudrate()

View File

@ -24,16 +24,18 @@ struct recurrent_fn_t
recurrent_fn_t* mNext = nullptr; recurrent_fn_t* mNext = nullptr;
mRecFuncT mFunc; mRecFuncT mFunc;
esp8266::polledTimeout::periodicFastUs callNow; esp8266::polledTimeout::periodicFastUs callNow;
recurrent_fn_t (esp8266::polledTimeout::periodicFastUs interval): callNow(interval) { } std::function<bool(void)> alarm = nullptr;
recurrent_fn_t(esp8266::polledTimeout::periodicFastUs interval) : callNow(interval) { }
}; };
static recurrent_fn_t* rFirst = nullptr; // fifo not needed static recurrent_fn_t* rFirst = nullptr;
static recurrent_fn_t* rLast = nullptr;
// Returns a pointer to an unused sched_fn_t, // Returns a pointer to an unused sched_fn_t,
// or if none are available allocates a new one, // or if none are available allocates a new one,
// or nullptr if limit is reached // or nullptr if limit is reached
IRAM_ATTR // called from ISR IRAM_ATTR // called from ISR
static scheduled_fn_t* get_fn_unsafe () static scheduled_fn_t* get_fn_unsafe()
{ {
scheduled_fn_t* result = nullptr; scheduled_fn_t* result = nullptr;
// try to get an item from unused items list // try to get an item from unused items list
@ -52,7 +54,7 @@ static scheduled_fn_t* get_fn_unsafe ()
return result; return result;
} }
static void recycle_fn_unsafe (scheduled_fn_t* fn) static void recycle_fn_unsafe(scheduled_fn_t* fn)
{ {
fn->mFunc = nullptr; // special overload in c++ std lib fn->mFunc = nullptr; // special overload in c++ std lib
fn->mNext = sUnused; fn->mNext = sUnused;
@ -60,8 +62,11 @@ static void recycle_fn_unsafe (scheduled_fn_t* fn)
} }
IRAM_ATTR // (not only) called from ISR IRAM_ATTR // (not only) called from ISR
bool schedule_function (const std::function<void(void)>& fn) bool schedule_function(const std::function<void(void)>& fn)
{ {
if (!fn)
return false;
esp8266::InterruptLock lockAllInterruptsInThisScope; esp8266::InterruptLock lockAllInterruptsInThisScope;
scheduled_fn_t* item = get_fn_unsafe(); scheduled_fn_t* item = get_fn_unsafe();
@ -80,27 +85,37 @@ bool schedule_function (const std::function<void(void)>& fn)
return true; return true;
} }
bool schedule_recurrent_function_us (const std::function<bool(void)>& fn, uint32_t repeat_us) bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,
uint32_t repeat_us, const std::function<bool(void)>& alarm)
{ {
assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s) assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s)
esp8266::InterruptLock lockAllInterruptsInThisScope; if (!fn)
return false;
recurrent_fn_t* item = new (std::nothrow) recurrent_fn_t(repeat_us); recurrent_fn_t* item = new (std::nothrow) recurrent_fn_t(repeat_us);
if (!item) if (!item)
return false; return false;
item->mFunc = fn; item->mFunc = fn;
item->alarm = alarm;
if (rFirst) esp8266::InterruptLock lockAllInterruptsInThisScope;
item->mNext = rFirst;
if (rLast)
{
rLast->mNext = item;
}
else
{
rFirst = item; rFirst = item;
}
rLast = item;
return true; return true;
} }
void run_scheduled_functions () void run_scheduled_functions()
{ {
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
@ -128,15 +143,18 @@ void run_scheduled_functions ()
} }
} }
void run_scheduled_recurrent_functions () void run_scheduled_recurrent_functions()
{ {
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
// Note to the reader: // Note to the reader:
// There is no exposed API to remove a scheduled function: // There is no exposed API to remove a scheduled function:
// Scheduled functions are removed only from this function, and // Scheduled functions are removed only from this function, and
// its purpose is that it is never called from an interrupt // its purpose is that it is never called from an interrupt
// (always on cont stack). // (always on cont stack).
if (!rFirst) auto current = rFirst;
if (!current)
return; return;
static bool fence = false; static bool fence = false;
@ -153,26 +171,35 @@ void run_scheduled_recurrent_functions ()
} }
recurrent_fn_t* prev = nullptr; recurrent_fn_t* prev = nullptr;
recurrent_fn_t* current = rFirst; // prevent scheduling of new functions during this run
auto stop = rLast;
while (current) bool done;
do
{ {
if (current->callNow && !current->mFunc()) done = current == stop;
const bool wakeup = current->alarm && current->alarm();
bool callNow = current->callNow;
if ((wakeup || callNow) && !current->mFunc())
{ {
// remove function from stack // remove function from stack
esp8266::InterruptLock lockAllInterruptsInThisScope; esp8266::InterruptLock lockAllInterruptsInThisScope;
auto to_ditch = current; auto to_ditch = current;
// removing rLast
if (rLast == current)
rLast = prev;
current = current->mNext;
if (prev) if (prev)
{ {
current = current->mNext;
prev->mNext = current; prev->mNext = current;
} }
else else
{ {
rFirst = rFirst->mNext; rFirst = current;
current = rFirst;
} }
delete(to_ditch); delete(to_ditch);
@ -182,7 +209,15 @@ void run_scheduled_recurrent_functions ()
prev = current; prev = current;
current = current->mNext; current = current->mNext;
} }
if (yieldNow)
{
// because scheduled functions might last too long for watchdog etc,
// this is yield() in cont stack:
esp_schedule();
cont_yield(g_pcont);
} }
} while (current && !done);
fence = false; fence = false;
} }

View File

@ -10,7 +10,7 @@
// in user stack (called CONT stack) without the common restrictions from // in user stack (called CONT stack) without the common restrictions from
// system context. Details are below. // system context. Details are below.
// The purpose of recurrent scheduled function is to independantly execute // The purpose of recurrent scheduled function is to independently execute
// user code in CONT stack on a regular basis. // user code in CONT stack on a regular basis.
// It has been introduced with ethernet service in mind, it can also be used // It has been introduced with ethernet service in mind, it can also be used
// for all libraries in the need of a regular `libdaemon_handlestuff()`. // for all libraries in the need of a regular `libdaemon_handlestuff()`.
@ -58,14 +58,14 @@ void run_scheduled_functions();
// functions. However a user function returning false will cancel itself. // functions. However a user function returning false will cancel itself.
// * Long running operations or yield() or delay() are not allowed in the // * Long running operations or yield() or delay() are not allowed in the
// recurrent function. // recurrent function.
// * A recurrent function currently must not schedule another recurrent // * If alarm is used, anytime during scheduling when it returns true,
// functions. // any remaining delay from repeat_us is disregarded, and fn is executed.
bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,
bool schedule_recurrent_function_us (const std::function<bool(void)>& fn, uint32_t repeat_us); uint32_t repeat_us, const std::function<bool(void)>& alarm = nullptr);
// Test recurrence and run recurrent scheduled functions. // Test recurrence and run recurrent scheduled functions.
// (internally called at every `yield()` and `loop()`) // (internally called at every `yield()` and `loop()`)
void run_scheduled_recurrent_functions (); void run_scheduled_recurrent_functions();
#endif // ESP_SCHEDULE_H #endif // ESP_SCHEDULE_H

475
cores/esp8266/TZ.h Normal file
View File

@ -0,0 +1,475 @@
// 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
//
// This database is autogenerated from IANA timezone database
// https://www.iana.org/time-zones
// and can be updated on demand in this repository
// or by yourself using the above script
#ifndef TZDB_H
#define TZDB_H
#define TZ_Africa_Abidjan PSTR("GMT0")
#define TZ_Africa_Accra PSTR("GMT0")
#define TZ_Africa_Addis_Ababa PSTR("EAT-3")
#define TZ_Africa_Algiers PSTR("CET-1")
#define TZ_Africa_Asmara PSTR("EAT-3")
#define TZ_Africa_Bamako PSTR("GMT0")
#define TZ_Africa_Bangui PSTR("WAT-1")
#define TZ_Africa_Banjul PSTR("GMT0")
#define TZ_Africa_Bissau PSTR("GMT0")
#define TZ_Africa_Blantyre PSTR("CAT-2")
#define TZ_Africa_Brazzaville PSTR("WAT-1")
#define TZ_Africa_Bujumbura PSTR("CAT-2")
#define TZ_Africa_Cairo PSTR("EET-2")
#define TZ_Africa_Casablanca PSTR("<+01>-1")
#define TZ_Africa_Ceuta PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Africa_Conakry PSTR("GMT0")
#define TZ_Africa_Dakar PSTR("GMT0")
#define TZ_Africa_Dar_es_Salaam PSTR("EAT-3")
#define TZ_Africa_Djibouti PSTR("EAT-3")
#define TZ_Africa_Douala PSTR("WAT-1")
#define TZ_Africa_El_Aaiun PSTR("<+01>-1")
#define TZ_Africa_Freetown PSTR("GMT0")
#define TZ_Africa_Gaborone PSTR("CAT-2")
#define TZ_Africa_Harare PSTR("CAT-2")
#define TZ_Africa_Johannesburg PSTR("SAST-2")
#define TZ_Africa_Juba PSTR("EAT-3")
#define TZ_Africa_Kampala PSTR("EAT-3")
#define TZ_Africa_Khartoum PSTR("CAT-2")
#define TZ_Africa_Kigali PSTR("CAT-2")
#define TZ_Africa_Kinshasa PSTR("WAT-1")
#define TZ_Africa_Lagos PSTR("WAT-1")
#define TZ_Africa_Libreville PSTR("WAT-1")
#define TZ_Africa_Lome PSTR("GMT0")
#define TZ_Africa_Luanda PSTR("WAT-1")
#define TZ_Africa_Lubumbashi PSTR("CAT-2")
#define TZ_Africa_Lusaka PSTR("CAT-2")
#define TZ_Africa_Malabo PSTR("WAT-1")
#define TZ_Africa_Maputo PSTR("CAT-2")
#define TZ_Africa_Maseru PSTR("SAST-2")
#define TZ_Africa_Mbabane PSTR("SAST-2")
#define TZ_Africa_Mogadishu PSTR("EAT-3")
#define TZ_Africa_Monrovia PSTR("GMT0")
#define TZ_Africa_Nairobi PSTR("EAT-3")
#define TZ_Africa_Ndjamena PSTR("WAT-1")
#define TZ_Africa_Niamey PSTR("WAT-1")
#define TZ_Africa_Nouakchott PSTR("GMT0")
#define TZ_Africa_Ouagadougou PSTR("GMT0")
#define TZ_Africa_PortomNovo PSTR("WAT-1")
#define TZ_Africa_Sao_Tome PSTR("GMT0")
#define TZ_Africa_Tripoli PSTR("EET-2")
#define TZ_Africa_Tunis PSTR("CET-1")
#define TZ_Africa_Windhoek PSTR("CAT-2")
#define TZ_America_Adak PSTR("HST10HDT,M3.2.0,M11.1.0")
#define TZ_America_Anchorage PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_Anguilla PSTR("AST4")
#define TZ_America_Antigua PSTR("AST4")
#define TZ_America_Araguaina PSTR("<-03>3")
#define TZ_America_Argentina_Buenos_Aires PSTR("<-03>3")
#define TZ_America_Argentina_Catamarca PSTR("<-03>3")
#define TZ_America_Argentina_Cordoba PSTR("<-03>3")
#define TZ_America_Argentina_Jujuy PSTR("<-03>3")
#define TZ_America_Argentina_La_Rioja PSTR("<-03>3")
#define TZ_America_Argentina_Mendoza PSTR("<-03>3")
#define TZ_America_Argentina_Rio_Gallegos PSTR("<-03>3")
#define TZ_America_Argentina_Salta PSTR("<-03>3")
#define TZ_America_Argentina_San_Juan PSTR("<-03>3")
#define TZ_America_Argentina_San_Luis PSTR("<-03>3")
#define TZ_America_Argentina_Tucuman PSTR("<-03>3")
#define TZ_America_Argentina_Ushuaia PSTR("<-03>3")
#define TZ_America_Aruba PSTR("AST4")
#define TZ_America_Asuncion PSTR("<-04>4<-03>,M10.1.0/0,M3.4.0/0")
#define TZ_America_Atikokan PSTR("EST5")
#define TZ_America_Bahia PSTR("<-03>3")
#define TZ_America_Bahia_Banderas PSTR("CST6CDT,M4.1.0,M10.5.0")
#define TZ_America_Barbados PSTR("AST4")
#define TZ_America_Belem PSTR("<-03>3")
#define TZ_America_Belize PSTR("CST6")
#define TZ_America_BlancmSablon PSTR("AST4")
#define TZ_America_Boa_Vista PSTR("<-04>4")
#define TZ_America_Bogota PSTR("<-05>5")
#define TZ_America_Boise PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Cambridge_Bay PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Campo_Grande PSTR("<-04>4")
#define TZ_America_Cancun PSTR("EST5")
#define TZ_America_Caracas PSTR("<-04>4")
#define TZ_America_Cayenne PSTR("<-03>3")
#define TZ_America_Cayman PSTR("EST5")
#define TZ_America_Chicago PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Chihuahua PSTR("MST7MDT,M4.1.0,M10.5.0")
#define TZ_America_Costa_Rica PSTR("CST6")
#define TZ_America_Creston PSTR("MST7")
#define TZ_America_Cuiaba PSTR("<-04>4")
#define TZ_America_Curacao PSTR("AST4")
#define TZ_America_Danmarkshavn PSTR("GMT0")
#define TZ_America_Dawson PSTR("PST8PDT,M3.2.0,M11.1.0")
#define TZ_America_Dawson_Creek PSTR("MST7")
#define TZ_America_Denver PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Detroit PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Dominica PSTR("AST4")
#define TZ_America_Edmonton PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Eirunepe PSTR("<-05>5")
#define TZ_America_El_Salvador PSTR("CST6")
#define TZ_America_Fortaleza PSTR("<-03>3")
#define TZ_America_Fort_Nelson PSTR("MST7")
#define TZ_America_Glace_Bay PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_America_Godthab PSTR("<-03>3<-02>,M3.5.0/-2,M10.5.0/-1")
#define TZ_America_Goose_Bay PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_America_Grand_Turk PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Grenada PSTR("AST4")
#define TZ_America_Guadeloupe PSTR("AST4")
#define TZ_America_Guatemala PSTR("CST6")
#define TZ_America_Guayaquil PSTR("<-05>5")
#define TZ_America_Guyana PSTR("<-04>4")
#define TZ_America_Halifax PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_America_Havana PSTR("CST5CDT,M3.2.0/0,M11.1.0/1")
#define TZ_America_Hermosillo PSTR("MST7")
#define TZ_America_Indiana_Indianapolis PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Knox PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Marengo PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Petersburg PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Tell_City PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Vevay PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Vincennes PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Winamac PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Inuvik PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Iqaluit PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Jamaica PSTR("EST5")
#define TZ_America_Juneau PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_Kentucky_Louisville PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Kentucky_Monticello PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Kralendijk PSTR("AST4")
#define TZ_America_La_Paz PSTR("<-04>4")
#define TZ_America_Lima PSTR("<-05>5")
#define TZ_America_Los_Angeles PSTR("PST8PDT,M3.2.0,M11.1.0")
#define TZ_America_Lower_Princes PSTR("AST4")
#define TZ_America_Maceio PSTR("<-03>3")
#define TZ_America_Managua PSTR("CST6")
#define TZ_America_Manaus PSTR("<-04>4")
#define TZ_America_Marigot PSTR("AST4")
#define TZ_America_Martinique PSTR("AST4")
#define TZ_America_Matamoros PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Mazatlan PSTR("MST7MDT,M4.1.0,M10.5.0")
#define TZ_America_Menominee PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Merida PSTR("CST6CDT,M4.1.0,M10.5.0")
#define TZ_America_Metlakatla PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_Mexico_City PSTR("CST6CDT,M4.1.0,M10.5.0")
#define TZ_America_Miquelon PSTR("<-03>3<-02>,M3.2.0,M11.1.0")
#define TZ_America_Moncton PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_America_Monterrey PSTR("CST6CDT,M4.1.0,M10.5.0")
#define TZ_America_Montevideo PSTR("<-03>3")
#define TZ_America_Montreal PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Montserrat PSTR("AST4")
#define TZ_America_Nassau PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_New_York PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Nipigon PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Nome PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_Noronha PSTR("<-02>2")
#define TZ_America_North_Dakota_Beulah PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_North_Dakota_Center PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_North_Dakota_New_Salem PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Ojinaga PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Panama PSTR("EST5")
#define TZ_America_Pangnirtung PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Paramaribo PSTR("<-03>3")
#define TZ_America_Phoenix PSTR("MST7")
#define TZ_America_PortmaumPrince PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Port_of_Spain PSTR("AST4")
#define TZ_America_Porto_Velho PSTR("<-04>4")
#define TZ_America_Puerto_Rico PSTR("AST4")
#define TZ_America_Punta_Arenas PSTR("<-03>3")
#define TZ_America_Rainy_River PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Rankin_Inlet PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Recife PSTR("<-03>3")
#define TZ_America_Regina PSTR("CST6")
#define TZ_America_Resolute PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Rio_Branco PSTR("<-05>5")
#define TZ_America_Santarem PSTR("<-03>3")
#define TZ_America_Santiago PSTR("<-04>4<-03>,M9.1.6/24,M4.1.6/24")
#define TZ_America_Santo_Domingo PSTR("AST4")
#define TZ_America_Sao_Paulo PSTR("<-03>3")
#define TZ_America_Scoresbysund PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
#define TZ_America_Sitka PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_St_Barthelemy PSTR("AST4")
#define TZ_America_St_Johns PSTR("NST3:30NDT,M3.2.0,M11.1.0")
#define TZ_America_St_Kitts PSTR("AST4")
#define TZ_America_St_Lucia PSTR("AST4")
#define TZ_America_St_Thomas PSTR("AST4")
#define TZ_America_St_Vincent PSTR("AST4")
#define TZ_America_Swift_Current PSTR("CST6")
#define TZ_America_Tegucigalpa PSTR("CST6")
#define TZ_America_Thule PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_America_Thunder_Bay PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Tijuana PSTR("PST8PDT,M3.2.0,M11.1.0")
#define TZ_America_Toronto PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Tortola PSTR("AST4")
#define TZ_America_Vancouver PSTR("PST8PDT,M3.2.0,M11.1.0")
#define TZ_America_Whitehorse PSTR("PST8PDT,M3.2.0,M11.1.0")
#define TZ_America_Winnipeg PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Yakutat PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_Yellowknife PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_Antarctica_Casey PSTR("<+08>-8")
#define TZ_Antarctica_Davis PSTR("<+07>-7")
#define TZ_Antarctica_DumontDUrville PSTR("<+10>-10")
#define TZ_Antarctica_Macquarie PSTR("<+11>-11")
#define TZ_Antarctica_Mawson PSTR("<+05>-5")
#define TZ_Antarctica_McMurdo PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3")
#define TZ_Antarctica_Palmer PSTR("<-03>3")
#define TZ_Antarctica_Rothera PSTR("<-03>3")
#define TZ_Antarctica_Syowa PSTR("<+03>-3")
#define TZ_Antarctica_Troll PSTR("<+00>0<+02>-2,M3.5.0/1,M10.5.0/3")
#define TZ_Antarctica_Vostok PSTR("<+06>-6")
#define TZ_Arctic_Longyearbyen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Asia_Aden PSTR("<+03>-3")
#define TZ_Asia_Almaty PSTR("<+06>-6")
#define TZ_Asia_Amman PSTR("EET-2EEST,M3.5.4/24,M10.5.5/1")
#define TZ_Asia_Anadyr PSTR("<+12>-12")
#define TZ_Asia_Aqtau PSTR("<+05>-5")
#define TZ_Asia_Aqtobe PSTR("<+05>-5")
#define TZ_Asia_Ashgabat PSTR("<+05>-5")
#define TZ_Asia_Atyrau PSTR("<+05>-5")
#define TZ_Asia_Baghdad PSTR("<+03>-3")
#define TZ_Asia_Bahrain PSTR("<+03>-3")
#define TZ_Asia_Baku PSTR("<+04>-4")
#define TZ_Asia_Bangkok PSTR("<+07>-7")
#define TZ_Asia_Barnaul PSTR("<+07>-7")
#define TZ_Asia_Beirut PSTR("EET-2EEST,M3.5.0/0,M10.5.0/0")
#define TZ_Asia_Bishkek PSTR("<+06>-6")
#define TZ_Asia_Brunei PSTR("<+08>-8")
#define TZ_Asia_Chita PSTR("<+09>-9")
#define TZ_Asia_Choibalsan PSTR("<+08>-8")
#define TZ_Asia_Colombo PSTR("<+0530>-5:30")
#define TZ_Asia_Damascus PSTR("EET-2EEST,M3.5.5/0,M10.5.5/0")
#define TZ_Asia_Dhaka PSTR("<+06>-6")
#define TZ_Asia_Dili PSTR("<+09>-9")
#define TZ_Asia_Dubai PSTR("<+04>-4")
#define TZ_Asia_Dushanbe PSTR("<+05>-5")
#define TZ_Asia_Famagusta PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Asia_Gaza PSTR("EET-2EEST,M3.5.5/0,M10.5.6/1")
#define TZ_Asia_Hebron PSTR("EET-2EEST,M3.5.5/0,M10.5.6/1")
#define TZ_Asia_Ho_Chi_Minh PSTR("<+07>-7")
#define TZ_Asia_Hong_Kong PSTR("HKT-8")
#define TZ_Asia_Hovd PSTR("<+07>-7")
#define TZ_Asia_Irkutsk PSTR("<+08>-8")
#define TZ_Asia_Jakarta PSTR("WIB-7")
#define TZ_Asia_Jayapura PSTR("WIT-9")
#define TZ_Asia_Jerusalem PSTR("IST-2IDT,M3.4.4/26,M10.5.0")
#define TZ_Asia_Kabul PSTR("<+0430>-4:30")
#define TZ_Asia_Kamchatka PSTR("<+12>-12")
#define TZ_Asia_Karachi PSTR("PKT-5")
#define TZ_Asia_Kathmandu PSTR("<+0545>-5:45")
#define TZ_Asia_Khandyga PSTR("<+09>-9")
#define TZ_Asia_Kolkata PSTR("IST-5:30")
#define TZ_Asia_Krasnoyarsk PSTR("<+07>-7")
#define TZ_Asia_Kuala_Lumpur PSTR("<+08>-8")
#define TZ_Asia_Kuching PSTR("<+08>-8")
#define TZ_Asia_Kuwait PSTR("<+03>-3")
#define TZ_Asia_Macau PSTR("CST-8")
#define TZ_Asia_Magadan PSTR("<+11>-11")
#define TZ_Asia_Makassar PSTR("WITA-8")
#define TZ_Asia_Manila PSTR("PST-8")
#define TZ_Asia_Muscat PSTR("<+04>-4")
#define TZ_Asia_Nicosia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Asia_Novokuznetsk PSTR("<+07>-7")
#define TZ_Asia_Novosibirsk PSTR("<+07>-7")
#define TZ_Asia_Omsk PSTR("<+06>-6")
#define TZ_Asia_Oral PSTR("<+05>-5")
#define TZ_Asia_Phnom_Penh PSTR("<+07>-7")
#define TZ_Asia_Pontianak PSTR("WIB-7")
#define TZ_Asia_Pyongyang PSTR("KST-9")
#define TZ_Asia_Qatar PSTR("<+03>-3")
#define TZ_Asia_Qyzylorda PSTR("<+05>-5")
#define TZ_Asia_Riyadh PSTR("<+03>-3")
#define TZ_Asia_Sakhalin PSTR("<+11>-11")
#define TZ_Asia_Samarkand PSTR("<+05>-5")
#define TZ_Asia_Seoul PSTR("KST-9")
#define TZ_Asia_Shanghai PSTR("CST-8")
#define TZ_Asia_Singapore PSTR("<+08>-8")
#define TZ_Asia_Srednekolymsk PSTR("<+11>-11")
#define TZ_Asia_Taipei PSTR("CST-8")
#define TZ_Asia_Tashkent PSTR("<+05>-5")
#define TZ_Asia_Tbilisi PSTR("<+04>-4")
#define TZ_Asia_Tehran PSTR("<+0330>-3:30<+0430>,J79/24,J263/24")
#define TZ_Asia_Thimphu PSTR("<+06>-6")
#define TZ_Asia_Tokyo PSTR("JST-9")
#define TZ_Asia_Tomsk PSTR("<+07>-7")
#define TZ_Asia_Ulaanbaatar PSTR("<+08>-8")
#define TZ_Asia_Urumqi PSTR("<+06>-6")
#define TZ_Asia_UstmNera PSTR("<+10>-10")
#define TZ_Asia_Vientiane PSTR("<+07>-7")
#define TZ_Asia_Vladivostok PSTR("<+10>-10")
#define TZ_Asia_Yakutsk PSTR("<+09>-9")
#define TZ_Asia_Yangon PSTR("<+0630>-6:30")
#define TZ_Asia_Yekaterinburg PSTR("<+05>-5")
#define TZ_Asia_Yerevan PSTR("<+04>-4")
#define TZ_Atlantic_Azores PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
#define TZ_Atlantic_Bermuda PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_Atlantic_Canary PSTR("WET0WEST,M3.5.0/1,M10.5.0")
#define TZ_Atlantic_Cape_Verde PSTR("<-01>1")
#define TZ_Atlantic_Faroe PSTR("WET0WEST,M3.5.0/1,M10.5.0")
#define TZ_Atlantic_Madeira PSTR("WET0WEST,M3.5.0/1,M10.5.0")
#define TZ_Atlantic_Reykjavik PSTR("GMT0")
#define TZ_Atlantic_South_Georgia PSTR("<-02>2")
#define TZ_Atlantic_Stanley PSTR("<-03>3")
#define TZ_Atlantic_St_Helena PSTR("GMT0")
#define TZ_Australia_Adelaide PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
#define TZ_Australia_Brisbane PSTR("AEST-10")
#define TZ_Australia_Broken_Hill PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
#define TZ_Australia_Currie PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
#define TZ_Australia_Darwin PSTR("ACST-9:30")
#define TZ_Australia_Eucla PSTR("<+0845>-8:45")
#define TZ_Australia_Hobart PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
#define TZ_Australia_Lindeman PSTR("AEST-10")
#define TZ_Australia_Lord_Howe PSTR("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0")
#define TZ_Australia_Melbourne PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
#define TZ_Australia_Perth PSTR("AWST-8")
#define TZ_Australia_Sydney PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
#define TZ_Europe_Amsterdam PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Andorra PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Astrakhan PSTR("<+04>-4")
#define TZ_Europe_Athens PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Belgrade PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Berlin PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Bratislava PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Brussels PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Bucharest PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Budapest PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Busingen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Chisinau PSTR("EET-2EEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Copenhagen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Dublin PSTR("IST-1GMT0,M10.5.0,M3.5.0/1")
#define TZ_Europe_Gibraltar PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Guernsey PSTR("GMT0BST,M3.5.0/1,M10.5.0")
#define TZ_Europe_Helsinki PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Isle_of_Man PSTR("GMT0BST,M3.5.0/1,M10.5.0")
#define TZ_Europe_Istanbul PSTR("<+03>-3")
#define TZ_Europe_Jersey PSTR("GMT0BST,M3.5.0/1,M10.5.0")
#define TZ_Europe_Kaliningrad PSTR("EET-2")
#define TZ_Europe_Kiev PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Kirov PSTR("<+03>-3")
#define TZ_Europe_Lisbon PSTR("WET0WEST,M3.5.0/1,M10.5.0")
#define TZ_Europe_Ljubljana PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_London PSTR("GMT0BST,M3.5.0/1,M10.5.0")
#define TZ_Europe_Luxembourg PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Madrid PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Malta PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Mariehamn PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Minsk PSTR("<+03>-3")
#define TZ_Europe_Monaco PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Moscow PSTR("MSK-3")
#define TZ_Europe_Oslo PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Paris PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Podgorica PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Prague PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Riga PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Rome PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Samara PSTR("<+04>-4")
#define TZ_Europe_San_Marino PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Sarajevo PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Saratov PSTR("<+04>-4")
#define TZ_Europe_Simferopol PSTR("MSK-3")
#define TZ_Europe_Skopje PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Sofia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Stockholm PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Tallinn PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Tirane PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Ulyanovsk PSTR("<+04>-4")
#define TZ_Europe_Uzhgorod PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Vaduz PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Vatican PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Vienna PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Vilnius PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Volgograd PSTR("<+04>-4")
#define TZ_Europe_Warsaw PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Zagreb PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Zaporozhye PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Zurich PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Indian_Antananarivo PSTR("EAT-3")
#define TZ_Indian_Chagos PSTR("<+06>-6")
#define TZ_Indian_Christmas PSTR("<+07>-7")
#define TZ_Indian_Cocos PSTR("<+0630>-6:30")
#define TZ_Indian_Comoro PSTR("EAT-3")
#define TZ_Indian_Kerguelen PSTR("<+05>-5")
#define TZ_Indian_Mahe PSTR("<+04>-4")
#define TZ_Indian_Maldives PSTR("<+05>-5")
#define TZ_Indian_Mauritius PSTR("<+04>-4")
#define TZ_Indian_Mayotte PSTR("EAT-3")
#define TZ_Indian_Reunion PSTR("<+04>-4")
#define TZ_Pacific_Apia PSTR("<+13>-13<+14>,M9.5.0/3,M4.1.0/4")
#define TZ_Pacific_Auckland PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3")
#define TZ_Pacific_Bougainville PSTR("<+11>-11")
#define TZ_Pacific_Chatham PSTR("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45")
#define TZ_Pacific_Chuuk PSTR("<+10>-10")
#define TZ_Pacific_Easter PSTR("<-06>6<-05>,M9.1.6/22,M4.1.6/22")
#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_Funafuti PSTR("<+12>-12")
#define TZ_Pacific_Galapagos PSTR("<-06>6")
#define TZ_Pacific_Gambier PSTR("<-09>9")
#define TZ_Pacific_Guadalcanal PSTR("<+11>-11")
#define TZ_Pacific_Guam PSTR("ChST-10")
#define TZ_Pacific_Honolulu PSTR("HST10")
#define TZ_Pacific_Kiritimati PSTR("<+14>-14")
#define TZ_Pacific_Kosrae PSTR("<+11>-11")
#define TZ_Pacific_Kwajalein PSTR("<+12>-12")
#define TZ_Pacific_Majuro PSTR("<+12>-12")
#define TZ_Pacific_Marquesas PSTR("<-0930>9:30")
#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_Noumea PSTR("<+11>-11")
#define TZ_Pacific_Pago_Pago PSTR("SST11")
#define TZ_Pacific_Palau PSTR("<+09>-9")
#define TZ_Pacific_Pitcairn PSTR("<-08>8")
#define TZ_Pacific_Pohnpei PSTR("<+11>-11")
#define TZ_Pacific_Port_Moresby PSTR("<+10>-10")
#define TZ_Pacific_Rarotonga PSTR("<-10>10")
#define TZ_Pacific_Saipan PSTR("ChST-10")
#define TZ_Pacific_Tahiti PSTR("<-10>10")
#define TZ_Pacific_Tarawa PSTR("<+12>-12")
#define TZ_Pacific_Tongatapu PSTR("<+13>-13")
#define TZ_Pacific_Wake PSTR("<+12>-12")
#define TZ_Pacific_Wallis PSTR("<+12>-12")
#define TZ_Etc_GMT PSTR("GMT0")
#define TZ_Etc_GMTm0 PSTR("GMT0")
#define TZ_Etc_GMTm1 PSTR("<+01>-1")
#define TZ_Etc_GMTm2 PSTR("<+02>-2")
#define TZ_Etc_GMTm3 PSTR("<+03>-3")
#define TZ_Etc_GMTm4 PSTR("<+04>-4")
#define TZ_Etc_GMTm5 PSTR("<+05>-5")
#define TZ_Etc_GMTm6 PSTR("<+06>-6")
#define TZ_Etc_GMTm7 PSTR("<+07>-7")
#define TZ_Etc_GMTm8 PSTR("<+08>-8")
#define TZ_Etc_GMTm9 PSTR("<+09>-9")
#define TZ_Etc_GMTm10 PSTR("<+10>-10")
#define TZ_Etc_GMTm11 PSTR("<+11>-11")
#define TZ_Etc_GMTm12 PSTR("<+12>-12")
#define TZ_Etc_GMTm13 PSTR("<+13>-13")
#define TZ_Etc_GMTm14 PSTR("<+14>-14")
#define TZ_Etc_GMT0 PSTR("GMT0")
#define TZ_Etc_GMTp0 PSTR("GMT0")
#define TZ_Etc_GMTp1 PSTR("<-01>1")
#define TZ_Etc_GMTp2 PSTR("<-02>2")
#define TZ_Etc_GMTp3 PSTR("<-03>3")
#define TZ_Etc_GMTp4 PSTR("<-04>4")
#define TZ_Etc_GMTp5 PSTR("<-05>5")
#define TZ_Etc_GMTp6 PSTR("<-06>6")
#define TZ_Etc_GMTp7 PSTR("<-07>7")
#define TZ_Etc_GMTp8 PSTR("<-08>8")
#define TZ_Etc_GMTp9 PSTR("<-09>9")
#define TZ_Etc_GMTp10 PSTR("<-10>10")
#define TZ_Etc_GMTp11 PSTR("<-11>11")
#define TZ_Etc_GMTp12 PSTR("<-12>12")
#define TZ_Etc_UCT PSTR("UTC0")
#define TZ_Etc_UTC PSTR("UTC0")
#define TZ_Etc_Greenwich PSTR("GMT0")
#define TZ_Etc_Universal PSTR("UTC0")
#define TZ_Etc_Zulu PSTR("UTC0")
#endif // TZDB_H

View File

@ -23,6 +23,7 @@ extern "C" {
} }
extern "C" uint32_t _FS_start; extern "C" uint32_t _FS_start;
extern "C" uint32_t _FS_end;
UpdaterClass::UpdaterClass() UpdaterClass::UpdaterClass()
: _async(false) : _async(false)
@ -105,15 +106,17 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
wifi_set_sleep_type(NONE_SLEEP_T); wifi_set_sleep_type(NONE_SLEEP_T);
//address where we will start writing the update
uintptr_t updateStartAddress = 0; uintptr_t updateStartAddress = 0;
if (command == U_FLASH) {
//size of current sketch rounded to a sector //size of current sketch rounded to a sector
size_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); size_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
//address of the end of the space available for sketch and update
uintptr_t updateEndAddress = (uintptr_t)&_FS_start - 0x40200000;
//size of the update rounded to a sector //size of the update rounded to a sector
size_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); size_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
//address where we will start writing the update
if (command == U_FLASH) {
//address of the end of the space available for sketch and update
uintptr_t updateEndAddress = (uintptr_t)&_FS_start - 0x40200000;
updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0; updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0;
#ifdef DEBUG_UPDATER #ifdef DEBUG_UPDATER
@ -129,7 +132,24 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
} }
} }
else if (command == U_FS) { else if (command == U_FS) {
if((uintptr_t)&_FS_start + roundedSize > (uintptr_t)&_FS_end) {
_setError(UPDATE_ERROR_SPACE);
return false;
}
#ifdef ATOMIC_FS_UPDATE
//address of the end of the space available for update
uintptr_t updateEndAddress = (uintptr_t)&_FS_start - 0x40200000;
updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0;
if(updateStartAddress < currentSketchSize) {
_setError(UPDATE_ERROR_SPACE);
return false;
}
#else
updateStartAddress = (uintptr_t)&_FS_start - 0x40200000; updateStartAddress = (uintptr_t)&_FS_start - 0x40200000;
#endif
} }
else { else {
// unknown command // unknown command
@ -272,8 +292,19 @@ bool UpdaterClass::end(bool evenIfRemaining){
#ifdef DEBUG_UPDATER #ifdef DEBUG_UPDATER
DEBUG_UPDATER.printf_P(PSTR("Staged: address:0x%08X, size:0x%08zX\n"), _startAddress, _size); DEBUG_UPDATER.printf_P(PSTR("Staged: address:0x%08X, size:0x%08zX\n"), _startAddress, _size);
#endif
} }
else if (_command == U_FS) { else if (_command == U_FS) {
#ifdef ATOMIC_FS_UPDATE
eboot_command ebcmd;
ebcmd.action = ACTION_COPY_RAW;
ebcmd.args[0] = _startAddress;
ebcmd.args[1] = (uintptr_t)&_FS_start - 0x40200000;
ebcmd.args[2] = _size;
eboot_command_write(&ebcmd);
#endif
#ifdef DEBUG_UPDATER
DEBUG_UPDATER.printf_P(PSTR("Filesystem: address:0x%08X, size:0x%08zX\n"), _startAddress, _size); DEBUG_UPDATER.printf_P(PSTR("Filesystem: address:0x%08X, size:0x%08zX\n"), _startAddress, _size);
#endif #endif
} }
@ -387,7 +418,7 @@ bool UpdaterClass::_verifyHeader(uint8_t data) {
} }
return true; return true;
} else if(_command == U_FS) { } else if(_command == U_FS) {
// no check of SPIFFS possible with first byte. // no check of FS possible with first byte.
return true; return true;
} }
return false; return false;
@ -421,7 +452,7 @@ bool UpdaterClass::_verifyEnd() {
return true; return true;
} else if(_command == U_FS) { } else if(_command == U_FS) {
// SPIFFS is already over written checks make no sense any more. // FS is already over written checks make no sense any more.
return true; return true;
} }
return false; return false;

View File

@ -129,10 +129,9 @@ String::~String() {
// /*********************************************/ // /*********************************************/
inline void String::init(void) { inline void String::init(void) {
setSSO(false); setSSO(true);
setCapacity(0);
setLen(0); setLen(0);
setBuffer(nullptr); wbuffer()[0] = 0;
} }
void String::invalidate(void) { void String::invalidate(void) {

View File

@ -53,7 +53,7 @@ class String {
// if the initial value is null or invalid, or if memory allocation // if the initial value is null or invalid, or if memory allocation
// fails, the string will be marked as invalid (i.e. "if (s)" will // fails, the string will be marked as invalid (i.e. "if (s)" will
// be false). // be false).
String(const char *cstr = ""); String(const char *cstr = nullptr);
String(const String &str); String(const String &str);
String(const __FlashStringHelper *str); String(const __FlashStringHelper *str);
#ifdef __GXX_EXPERIMENTAL_CXX0X__ #ifdef __GXX_EXPERIMENTAL_CXX0X__

View File

@ -31,11 +31,11 @@ extern "C" {
/** /**
* convert input data to base64 * convert input data to base64
* @param data uint8_t * * @param data const uint8_t *
* @param length size_t * @param length size_t
* @return String * @return String
*/ */
String base64::encode(uint8_t * data, size_t length, bool doNewLines) { String base64::encode(const uint8_t * data, size_t length, bool doNewLines) {
// base64 needs more size then the source data, use cencode.h macros // base64 needs more size then the source data, use cencode.h macros
size_t size = ((doNewLines ? base64_encode_expected_len(length) size_t size = ((doNewLines ? base64_encode_expected_len(length)
: base64_encode_expected_len_nonewlines(length)) + 1); : base64_encode_expected_len_nonewlines(length)) + 1);
@ -62,10 +62,10 @@ String base64::encode(uint8_t * data, size_t length, bool doNewLines) {
/** /**
* convert input data to base64 * convert input data to base64
* @param text String * @param text const String&
* @return String * @return String
*/ */
String base64::encode(String text, bool doNewLines) { String base64::encode(const String& text, bool doNewLines) {
return base64::encode((uint8_t *) text.c_str(), text.length(), doNewLines); return base64::encode((const uint8_t *) text.c_str(), text.length(), doNewLines);
} }

View File

@ -30,8 +30,8 @@ class base64 {
// NOTE: The default behaviour of backend (lib64) // NOTE: The default behaviour of backend (lib64)
// is to add a newline every 72 (encoded) characters output. // is to add a newline every 72 (encoded) characters output.
// This may 'break' longer uris and json variables // This may 'break' longer uris and json variables
static String encode(uint8_t * data, size_t length, bool doNewLines = true); static String encode(const uint8_t * data, size_t length, bool doNewLines = true);
static String encode(String text, bool doNewLines = true); static String encode(const String& text, bool doNewLines = true);
private: private:
}; };

View File

@ -18,7 +18,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
.text .section .irom0.text
.align 4 .align 4
.literal_position .literal_position
.global cont_yield .global cont_yield
@ -84,7 +84,7 @@ cont_wrapper:
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
.text .section .irom0.text
.align 4 .align 4
.literal_position .literal_position
.global cont_run .global cont_run

View File

@ -62,6 +62,10 @@ static int s_user_reset_reason = REASON_DEFAULT_RST;
// From UMM, the last caller of a malloc/realloc/calloc which failed: // From UMM, the last caller of a malloc/realloc/calloc which failed:
extern void *umm_last_fail_alloc_addr; extern void *umm_last_fail_alloc_addr;
extern int umm_last_fail_alloc_size; extern int umm_last_fail_alloc_size;
#if defined(DEBUG_ESP_OOM)
extern const char *umm_last_fail_alloc_file;
extern int umm_last_fail_alloc_line;
#endif
static void raise_exception() __attribute__((noreturn)); static void raise_exception() __attribute__((noreturn));
@ -108,8 +112,7 @@ void __wrap_system_restart_local() {
else else
rst_info.reason = s_user_reset_reason; rst_info.reason = s_user_reset_reason;
// TODO: ets_install_putc1 definition is wrong in ets_sys.h, need cast ets_install_putc1(&uart_write_char_d);
ets_install_putc1((void *)&uart_write_char_d);
if (s_panic_line) { if (s_panic_line) {
ets_printf_P(PSTR("\nPanic %S:%d %S"), s_panic_file, s_panic_line, s_panic_func); ets_printf_P(PSTR("\nPanic %S:%d %S"), s_panic_file, s_panic_line, s_panic_func);
@ -182,7 +185,13 @@ void __wrap_system_restart_local() {
// Use cap-X formatting to ensure the standard EspExceptionDecoder doesn't match the address // Use cap-X formatting to ensure the standard EspExceptionDecoder doesn't match the address
if (umm_last_fail_alloc_addr) { if (umm_last_fail_alloc_addr) {
#if defined(DEBUG_ESP_OOM)
ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)@%S:%d\n"),
(uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size,
umm_last_fail_alloc_file, umm_last_fail_alloc_line);
#else
ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)\n"), (uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size); ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)\n"), (uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size);
#endif
} }
custom_crash_callback( &rst_info, sp_dump + offset, stack_end ); custom_crash_callback( &rst_info, sp_dump + offset, stack_end );

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,6 @@ extern bool timeshift64_is_set;
void esp_yield(); void esp_yield();
void esp_schedule(); void esp_schedule();
void tune_timeshift64 (uint64_t now_us); void tune_timeshift64 (uint64_t now_us);
void settimeofday_cb (void (*cb)(void));
void disable_extra4k_at_link_time (void) __attribute__((noinline)); void disable_extra4k_at_link_time (void) __attribute__((noinline));
uint32_t sqrt32 (uint32_t n); uint32_t sqrt32 (uint32_t n);
@ -25,6 +24,14 @@ uint32_t crc32 (const void* data, size_t length, uint32_t crc = 0xffffffff);
#ifdef __cplusplus #ifdef __cplusplus
} }
#include <functional>
using TrivialCB = std::function<void()>;
void settimeofday_cb (TrivialCB&& cb);
void settimeofday_cb (const TrivialCB& cb);
#endif #endif
#endif // __COREDECLS_H #endif // __COREDECLS_H

View File

@ -11,6 +11,27 @@ extern int rom_i2c_readReg_Mask(int, int, int, int, int);
extern int uart_baudrate_detect(int, int); extern int uart_baudrate_detect(int, int);
/*
ROM function, uart_buff_switch(), is used to switch printing between UART0 and
UART1. It updates a structure that only controls a select group of print
functions. ets_putc() and ets_uart_printf() are examples and are not affected by
calls to ets_install_putc1().
Use:
0 for UART0, also clears RX FIFO
1 for UART1
*/
extern void uart_buff_switch(uint8_t);
/*
ROM function, ets_uart_printf(), prints on the UART selected by
uart_buff_switch(). Supported format options are the same as vprintf(). Also
has cooked newline behavior. No flash format/string support; however, ISR safe.
Also, uses a static function in ROM to print characters which is only
controlled by uart_buff_switch().
*/
extern int ets_uart_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
extern void ets_delay_us(uint32_t us); extern void ets_delay_us(uint32_t us);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -7,38 +7,134 @@
#include "umm_malloc/umm_malloc.h" #include "umm_malloc/umm_malloc.h"
#include <c_types.h> #include <c_types.h>
#include <sys/reent.h> #include <sys/reent.h>
#include <user_interface.h>
extern "C" { extern "C" {
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
#define UMM_MALLOC(s) umm_poison_malloc(s)
#define UMM_CALLOC(n,s) umm_poison_calloc(n,s)
#define UMM_REALLOC_FL(p,s,f,l) umm_poison_realloc_fl(p,s,f,l)
#define UMM_FREE_FL(p,f,l) umm_poison_free_fl(p,f,l)
#undef realloc
#undef free
#elif defined(DEBUG_ESP_OOM)
#define UMM_MALLOC(s) umm_malloc(s)
#define UMM_CALLOC(n,s) umm_calloc(n,s)
#define UMM_REALLOC_FL(p,s,f,l) umm_realloc(p,s)
#define UMM_FREE_FL(p,f,l) umm_free(p)
#undef realloc
#undef free
#else // ! UMM_POISON_CHECK && ! DEBUG_ESP_OOM
#define UMM_MALLOC(s) malloc(s)
#define UMM_CALLOC(n,s) calloc(n,s)
#define UMM_REALLOC_FL(p,s,f,l) realloc(p,s)
#define UMM_FREE_FL(p,f,l) free(p)
#endif
#if defined(UMM_POISON_CHECK)
#define POISON_CHECK__ABORT() \
do { \
if ( ! POISON_CHECK() ) \
abort(); \
} while(0)
#define POISON_CHECK__PANIC_FL(file, line) \
do { \
if ( ! POISON_CHECK() ) \
__panic_func(file, line, ""); \
} while(0)
#else // No full heap poison checking.
#define POISON_CHECK__ABORT() do {} while(0)
#define POISON_CHECK__PANIC_FL(file, line) do { (void)file; (void)line; } while(0)
#endif
// Debugging helper, last allocation which returned NULL // Debugging helper, last allocation which returned NULL
void *umm_last_fail_alloc_addr = NULL; void *umm_last_fail_alloc_addr = NULL;
int umm_last_fail_alloc_size = 0; int umm_last_fail_alloc_size = 0;
#if defined(DEBUG_ESP_OOM)
const char *umm_last_fail_alloc_file = NULL;
int umm_last_fail_alloc_line = 0;
#endif
#ifdef UMM_INTEGRITY_CHECK
#define INTEGRITY_CHECK__ABORT() \
do { \
if ( ! INTEGRITY_CHECK() ) \
abort(); \
} while(0)
#define INTEGRITY_CHECK__PANIC_FL(file, line) \
do { \
if ( ! INTEGRITY_CHECK() ) \
__panic_func(file, line, ""); \
} while(0)
#else // ! UMM_INTEGRITY_CHECK
#define INTEGRITY_CHECK__ABORT() do {} while(0)
#define INTEGRITY_CHECK__PANIC_FL(file, line) do { (void)file; (void)line; } while(0)
#endif // UMM_INTEGRITY_CHECK
#if defined(DEBUG_ESP_OOM)
#define PTR_CHECK__LOG_LAST_FAIL_FL(p, s, f, l) \
if(0 != (s) && 0 == p)\
{\
umm_last_fail_alloc_addr = __builtin_return_address(0);\
umm_last_fail_alloc_size = s;\
umm_last_fail_alloc_file = f;\
umm_last_fail_alloc_line = l;\
}
#define PTR_CHECK__LOG_LAST_FAIL(p, s) \
if(0 != (s) && 0 == p)\
{\
umm_last_fail_alloc_addr = __builtin_return_address(0);\
umm_last_fail_alloc_size = s;\
umm_last_fail_alloc_file = NULL;\
umm_last_fail_alloc_line = 0;\
}
#else
#define PTR_CHECK__LOG_LAST_FAIL_FL(p, s, f, l) \
(void)f;\
(void)l;\
if(0 != (s) && 0 == p)\
{\
umm_last_fail_alloc_addr = __builtin_return_address(0);\
umm_last_fail_alloc_size = s;\
}
#define PTR_CHECK__LOG_LAST_FAIL(p, s) \
if(0 != (s) && 0 == p)\
{\
umm_last_fail_alloc_addr = __builtin_return_address(0);\
umm_last_fail_alloc_size = s;\
}
#endif
void* _malloc_r(struct _reent* unused, size_t size) void* _malloc_r(struct _reent* unused, size_t size)
{ {
(void) unused; (void) unused;
void *ret = malloc(size); void *ret = malloc(size);
if (0 != size && 0 == ret) { PTR_CHECK__LOG_LAST_FAIL(ret, size);
umm_last_fail_alloc_addr = __builtin_return_address(0);
umm_last_fail_alloc_size = size;
}
return ret; return ret;
} }
void _free_r(struct _reent* unused, void* ptr) void _free_r(struct _reent* unused, void* ptr)
{ {
(void) unused; (void) unused;
return free(ptr); free(ptr);
} }
void* _realloc_r(struct _reent* unused, void* ptr, size_t size) void* _realloc_r(struct _reent* unused, void* ptr, size_t size)
{ {
(void) unused; (void) unused;
void *ret = realloc(ptr, size); void *ret = realloc(ptr, size);
if (0 != size && 0 == ret) { PTR_CHECK__LOG_LAST_FAIL(ret, size);
umm_last_fail_alloc_addr = __builtin_return_address(0);
umm_last_fail_alloc_size = size;
}
return ret; return ret;
} }
@ -46,140 +142,169 @@ void* _calloc_r(struct _reent* unused, size_t count, size_t size)
{ {
(void) unused; (void) unused;
void *ret = calloc(count, size); void *ret = calloc(count, size);
if (0 != (count * size) && 0 == ret) { PTR_CHECK__LOG_LAST_FAIL(ret, count * size);
umm_last_fail_alloc_addr = __builtin_return_address(0); return ret;
umm_last_fail_alloc_size = count * size; }
#ifdef DEBUG_ESP_OOM
#undef malloc
#undef calloc
#undef realloc
#define DEBUG_HEAP_PRINTF ets_uart_printf
void ICACHE_RAM_ATTR print_loc(size_t size, const char* file, int line)
{
(void)size;
(void)line;
if (system_get_os_print()) {
DEBUG_HEAP_PRINTF(":oom(%d)@", (int)size);
bool inISR = ETS_INTR_WITHINISR();
if (inISR && (uint32_t)file >= 0x40200000) {
DEBUG_HEAP_PRINTF("File: %p", file);
} else if (!inISR && (uint32_t)file >= 0x40200000) {
char buf[ets_strlen(file)] __attribute__ ((aligned(4)));
ets_strcpy(buf, file);
DEBUG_HEAP_PRINTF(buf);
} else {
DEBUG_HEAP_PRINTF(file);
} }
DEBUG_HEAP_PRINTF(":%d\n", line);
}
}
void ICACHE_RAM_ATTR print_oom_size(size_t size)
{
(void)size;
if (system_get_os_print()) {
DEBUG_HEAP_PRINTF(":oom(%d)@?\n", (int)size);
}
}
#define OOM_CHECK__PRINT_OOM(p, s) if (!p) print_oom_size(s)
#define OOM_CHECK__PRINT_LOC(p, s, f, l) if (!p) print_loc(s, f, l)
#else // ! DEBUG_ESP_OOM
#if 1
//C - to be discussed - is this what you want?
//C Skip OOM logging of last fail for malloc/... and pvPort... .
//C It cost 64 more bytes of IRAM to turn on. And was not previously enabled.
#undef PTR_CHECK__LOG_LAST_FAIL_FL
#define PTR_CHECK__LOG_LAST_FAIL_FL(p, s, f, l)
#undef PTR_CHECK__LOG_LAST_FAIL
#define PTR_CHECK__LOG_LAST_FAIL(p, s)
#endif
#define OOM_CHECK__PRINT_OOM(p, s)
#define OOM_CHECK__PRINT_LOC(p, s, f, l)
#endif
#if defined(DEBUG_ESP_OOM) || defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || defined(UMM_INTEGRITY_CHECK)
/*
The thinking behind the ordering of Integrity Check, Full Poison Check, and
the specific *alloc function.
1. Integrity Check - verifies the heap management information is not corrupt.
This allows any other testing, that walks the heap, to run safely.
2. Place Full Poison Check before or after a specific *alloc function?
a. After, when the *alloc function operates on an existing allocation.
b. Before, when the *alloc function creates a new, not modified, allocation.
In a free() or realloc() call, the focus is on their allocation. It is
checked 1st and reported on 1ST if an error exists. Full Posion Check is
done after.
For malloc(), calloc(), and zalloc() Full Posion Check is done 1st since
these functions do not modify an existing allocation.
*/
void* ICACHE_RAM_ATTR malloc(size_t size)
{
INTEGRITY_CHECK__ABORT();
POISON_CHECK__ABORT();
void* ret = UMM_MALLOC(size);
PTR_CHECK__LOG_LAST_FAIL(ret, size);
OOM_CHECK__PRINT_OOM(ret, size);
return ret;
}
void* ICACHE_RAM_ATTR calloc(size_t count, size_t size)
{
INTEGRITY_CHECK__ABORT();
POISON_CHECK__ABORT();
void* ret = UMM_CALLOC(count, size);
PTR_CHECK__LOG_LAST_FAIL(ret, count * size);
OOM_CHECK__PRINT_OOM(ret, size);
return ret;
}
void* ICACHE_RAM_ATTR realloc(void* ptr, size_t size)
{
INTEGRITY_CHECK__ABORT();
void* ret = UMM_REALLOC_FL(ptr, size, NULL, 0);
POISON_CHECK__ABORT();
PTR_CHECK__LOG_LAST_FAIL(ret, size);
OOM_CHECK__PRINT_OOM(ret, size);
return ret;
}
void ICACHE_RAM_ATTR free(void* p)
{
INTEGRITY_CHECK__ABORT();
UMM_FREE_FL(p, NULL, 0);
POISON_CHECK__ABORT();
}
#endif
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line)
{
INTEGRITY_CHECK__PANIC_FL(file, line);
POISON_CHECK__PANIC_FL(file, line);
void* ret = UMM_MALLOC(size);
PTR_CHECK__LOG_LAST_FAIL_FL(ret, size, file, line);
OOM_CHECK__PRINT_LOC(ret, size, file, line);
return ret;
}
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line)
{
INTEGRITY_CHECK__PANIC_FL(file, line);
POISON_CHECK__PANIC_FL(file, line);
void* ret = UMM_CALLOC(count, size);
PTR_CHECK__LOG_LAST_FAIL_FL(ret, count * size, file, line);
OOM_CHECK__PRINT_LOC(ret, size, file, line);
return ret;
}
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line)
{
INTEGRITY_CHECK__PANIC_FL(file, line);
void* ret = UMM_REALLOC_FL(ptr, size, file, line);
POISON_CHECK__PANIC_FL(file, line);
PTR_CHECK__LOG_LAST_FAIL_FL(ret, size, file, line);
OOM_CHECK__PRINT_LOC(ret, size, file, line);
return ret;
}
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line)
{
INTEGRITY_CHECK__PANIC_FL(file, line);
POISON_CHECK__PANIC_FL(file, line);
void* ret = UMM_CALLOC(1, size);
PTR_CHECK__LOG_LAST_FAIL_FL(ret, size, file, line);
OOM_CHECK__PRINT_LOC(ret, size, file, line);
return ret; return ret;
} }
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line) void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line)
{ {
(void) file; INTEGRITY_CHECK__PANIC_FL(file, line);
(void) line; UMM_FREE_FL(ptr, file, line);
free(ptr); POISON_CHECK__PANIC_FL(file, line);
}
#ifdef DEBUG_ESP_OOM
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line)
{
return malloc_loc(size, file, line);
}
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line)
{
return calloc_loc(count, size, file, line);
}
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line)
{
return realloc_loc(ptr, size, file, line);
}
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line)
{
return calloc_loc(1, size, file, line);
}
#undef malloc
#undef calloc
#undef realloc
static const char oom_fmt[] PROGMEM STORE_ATTR = ":oom(%d)@?\n";
static const char oom_fmt_1[] PROGMEM STORE_ATTR = ":oom(%d)@";
static const char oom_fmt_2[] PROGMEM STORE_ATTR = ":%d\n";
void* malloc (size_t s)
{
void* ret = umm_malloc(s);
if (!ret)
os_printf(oom_fmt, (int)s);
return ret;
}
void* calloc (size_t n, size_t s)
{
void* ret = umm_calloc(n, s);
if (!ret)
os_printf(oom_fmt, (int)s);
return ret;
}
void* realloc (void* p, size_t s)
{
void* ret = umm_realloc(p, s);
if (!ret)
os_printf(oom_fmt, (int)s);
return ret;
}
void print_loc (size_t s, const char* file, int line)
{
os_printf(oom_fmt_1, (int)s);
os_printf(file);
os_printf(oom_fmt_2, line);
}
void* malloc_loc (size_t s, const char* file, int line)
{
void* ret = umm_malloc(s);
if (!ret)
print_loc(s, file, line);
return ret;
}
void* calloc_loc (size_t n, size_t s, const char* file, int line)
{
void* ret = umm_calloc(n, s);
if (!ret)
print_loc(s, file, line);
return ret;
}
void* realloc_loc (void* p, size_t s, const char* file, int line)
{
void* ret = umm_realloc(p, s);
if (!ret)
print_loc(s, file, line);
return ret;
}
#else
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line)
{
(void) file;
(void) line;
return malloc(size);
}
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line)
{
(void) file;
(void) line;
return calloc(count, size);
}
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line)
{
(void) file;
(void) line;
return realloc(ptr, size);
}
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line)
{
(void) file;
(void) line;
return calloc(1, size);
}
#endif // !defined(DEBUG_ESP_OOM)
size_t xPortGetFreeHeapSize(void)
{
return umm_free_heap_size();
} }
size_t ICACHE_RAM_ATTR xPortWantedSizeAlign(size_t size) size_t ICACHE_RAM_ATTR xPortWantedSizeAlign(size_t size)

View File

@ -100,7 +100,8 @@ int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) __attribute__((
int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) { int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) {
(void) r; (void) r;
if (file->_file == STDOUT_FILENO) { if (file->_file == STDOUT_FILENO) {
return ets_putc(c); ets_putc(c);
return c;
} }
return EOF; return EOF;
} }

View File

@ -42,16 +42,22 @@
#include <osapi.h> #include <osapi.h>
#include <os_type.h> #include <os_type.h>
#include "coredecls.h" #include "coredecls.h"
#include "Schedule.h"
extern "C" { static TrivialCB _settimeofday_cb;
static void (*_settimeofday_cb)(void) = NULL; void settimeofday_cb (TrivialCB&& cb)
{
_settimeofday_cb = std::move(cb);
}
void settimeofday_cb (void (*cb)(void)) void settimeofday_cb (const TrivialCB& cb)
{ {
_settimeofday_cb = cb; _settimeofday_cb = cb;
} }
extern "C" {
#if LWIP_VERSION_MAJOR == 1 #if LWIP_VERSION_MAJOR == 1
#include <pgmspace.h> #include <pgmspace.h>
@ -478,7 +484,7 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz)
sntp_set_system_time(tv->tv_sec); sntp_set_system_time(tv->tv_sec);
if (_settimeofday_cb) if (_settimeofday_cb)
_settimeofday_cb(); schedule_function(_settimeofday_cb);
} }
return 0; return 0;
} }

View File

@ -25,6 +25,21 @@
using namespace fs; using namespace fs;
// Deprecated functions, to be deleted in next release
int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) {
return flash_hal_write(addr, size, src);
}
int32_t spiffs_hal_erase(uint32_t addr, uint32_t size) {
return flash_hal_erase(addr, size);
}
int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
return flash_hal_read(addr, size, dst);
}
namespace spiffs_impl { namespace spiffs_impl {
FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode) FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode)

View File

@ -39,6 +39,26 @@ extern "C" {
using namespace fs; using namespace fs;
// The following are deprecated symbols and functions, to be removed at the next major release.
// They are provided only for backwards compatibility and to give libs a chance to update.
extern "C" uint32_t _SPIFFS_start __attribute__((deprecated));
extern "C" uint32_t _SPIFFS_end __attribute__((deprecated));
extern "C" uint32_t _SPIFFS_page __attribute__((deprecated));
extern "C" uint32_t _SPIFFS_block __attribute__((deprecated));
#define SPIFFS_PHYS_ADDR ((uint32_t) (&_SPIFFS_start) - 0x40200000)
#define SPIFFS_PHYS_SIZE ((uint32_t) (&_SPIFFS_end) - (uint32_t) (&_SPIFFS_start))
#define SPIFFS_PHYS_PAGE ((uint32_t) &_SPIFFS_page)
#define SPIFFS_PHYS_BLOCK ((uint32_t) &_SPIFFS_block)
extern int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) __attribute__((deprecated));
extern int32_t spiffs_hal_erase(uint32_t addr, uint32_t size) __attribute__((deprecated));
extern int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) __attribute__((deprecated));
namespace spiffs_impl { namespace spiffs_impl {
int getSpiffsMode(OpenMode openMode, AccessMode accessMode); int getSpiffsMode(OpenMode openMode, AccessMode accessMode);
@ -151,11 +171,6 @@ public:
bool begin() override bool begin() override
{ {
#if defined(ARDUINO) && !defined(CORE_MOCK)
if (&_FS_end <= &_FS_start)
return false;
#endif
if (SPIFFS_mounted(&_fs) != 0) { if (SPIFFS_mounted(&_fs) != 0) {
return true; return true;
} }

View File

@ -16,6 +16,7 @@
* *
*/ */
#include <stdlib.h>
#include <time.h> #include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/reent.h> #include <sys/reent.h>
@ -55,25 +56,12 @@ static void setServer(int id, const char* name_or_ip)
{ {
if (name_or_ip) if (name_or_ip)
{ {
//TODO: check whether server is given by name or IP // per current configuration,
// lwIP can receive an IP address or a fqdn
sntp_setservername(id, (char*) name_or_ip); sntp_setservername(id, (char*) name_or_ip);
} }
} }
void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
{
sntp_stop();
setServer(0, server1);
setServer(1, server2);
setServer(2, server3);
sntp_set_timezone_in_seconds(timezone_sec);
sntp_set_daylight(daylightOffset_sec);
sntp_init();
}
int clock_gettime(clockid_t unused, struct timespec *tp) int clock_gettime(clockid_t unused, struct timespec *tp)
{ {
(void) unused; (void) unused;
@ -108,4 +96,33 @@ int _gettimeofday_r(struct _reent* unused, struct timeval *tp, void *tzp)
return 0; return 0;
} }
}; }; // extern "C"
void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
{
sntp_stop();
setServer(0, server1);
setServer(1, server2);
setServer(2, server3);
sntp_set_timezone_in_seconds(timezone_sec);
sntp_set_daylight(daylightOffset_sec);
sntp_init();
}
void configTime(const char* tz, const char* server1, const char* server2, const char* server3)
{
sntp_stop();
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*/);
tzset();
sntp_init();
}

View File

@ -48,12 +48,13 @@ uint8_t twi_status();
uint8_t twi_transmit(const uint8_t*, uint8_t); uint8_t twi_transmit(const uint8_t*, uint8_t);
void twi_attachSlaveRxEvent( void (*)(uint8_t*, size_t) ); void twi_attachSlaveRxEvent(void (*)(uint8_t*, size_t));
void twi_attachSlaveTxEvent( void (*)(void) ); void twi_attachSlaveTxEvent(void (*)(void));
void twi_reply(uint8_t); void twi_reply(uint8_t);
//void twi_stop(void); //void twi_stop(void);
void twi_releaseBus(void); void twi_releaseBus(void);
void twi_enableSlaveMode(void);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -48,6 +48,10 @@
#include "user_interface.h" #include "user_interface.h"
#include "uart_register.h" #include "uart_register.h"
#define MODE2WIDTH(mode) (((mode%16)>>2)+5)
#define MODE2STOP(mode) (((mode)>>5)+1)
#define MODE2PARITY(mode) (mode%4)
/* /*
Some general architecture for GDB integration with the UART to enable Some general architecture for GDB integration with the UART to enable
serial debugging. serial debugging.
@ -204,6 +208,13 @@ uart_read_char_unsafe(uart_t* uart)
return -1; return -1;
} }
uint8_t
uart_get_bit_length(const int uart_nr)
{
// return bit length from uart mode, +1 for the start bit which is always there.
return MODE2WIDTH(USC0(uart_nr)) + MODE2PARITY(USC0(uart_nr)) + MODE2STOP(USC0(uart_nr)) + 1;
}
size_t size_t
uart_rx_available(uart_t* uart) uart_rx_available(uart_t* uart)
{ {
@ -899,18 +910,26 @@ void
uart_set_debug(int uart_nr) uart_set_debug(int uart_nr)
{ {
s_uart_debug_nr = uart_nr; s_uart_debug_nr = uart_nr;
void (*func)(char) = NULL; fp_putc_t func = NULL;
switch(s_uart_debug_nr) switch(s_uart_debug_nr)
{ {
case UART0: case UART0:
func = &uart0_write_char; func = &uart0_write_char;
// This selects the UART for ROM ets_putc which is used by
// ::printf, ets_printf_P in core_esp_postmortem.cpp and others.
// Has a side effect of clearing RX FIFO for UART0
uart_buff_switch(0);
break; break;
case UART1: case UART1:
func = &uart1_write_char; func = &uart1_write_char;
uart_buff_switch(1);
break; break;
case UART_NO: case UART_NO:
default: default:
func = &uart_ignore_char; func = &uart_ignore_char;
// There is no disable option for ets_putc,
// we switch to UART0 for disable case.
uart_buff_switch(0);
break; break;
} }
if(gdbstub_has_putc1_control()) { if(gdbstub_has_putc1_control()) {
@ -921,7 +940,7 @@ uart_set_debug(int uart_nr)
} else { } else {
system_set_os_print(0); system_set_os_print(0);
} }
ets_install_putc1((void *) func); ets_install_putc1(func);
} }
} }

View File

@ -147,6 +147,7 @@ int uart_get_debug();
void uart_start_detect_baudrate(int uart_nr); void uart_start_detect_baudrate(int uart_nr);
int uart_detect_baudrate(int uart_nr); int uart_detect_baudrate(int uart_nr);
uint8_t uart_get_bit_length(const int uart_nr);
#if defined (__cplusplus) #if defined (__cplusplus)
} // extern "C" } // extern "C"

View File

@ -0,0 +1,251 @@
#if 0
/*
* This .h is nothing but comments about thoughts and observations made while
* updating the Arduino ESP8266 Core, with the new upstream umm_malloc. It is
* added as a .h so that it does not get lost and to avoid cluttering up the
* code with a huge block comment.
PR text description:
upstream version of `umm_malloc` customized for Arduino ESP8266 Core
This updates the heap management library, umm_malloc, to the current upstream
version at https://github.com/rhempel/umm_malloc. Some reorganizing and new code
was needed to use the new version.
This is a list of noteworthy changes:
UMM_POISON - now has a lite option as well as the previous intensive check
option. The code for running the full poison test at the call of the various
alloc functions was removed in the upstream version. In this port, the missing
code was added to heap.cpp and umm_local.cpp.
* UMM_POISON - appears to have been partially changed to UMM_POISON_CHECK,
I treat it as deprecated and used UMM_POISON_CHECK when needed.
However, the Arduino Core's references to UMM_POISON were replaced with
UMM_POISON_CHECK_LITE.
* UMM_POISON_CHECK_LITE - Less intense, it just checks poison on active
neighboring allocations.
* UMM_POISON_CHECK - Full heap intensive check of poison
A cautionary note, on the use of UMM_INTEGRITY_CHECK and UMM_POISON_CHECK, and
umm_info(). All of these run with IRQs disabled, for periods that can go into
100's of us. With umm_info(NULL, true) that may go into seconds, depending on
the serial interface speed and the number of memory allocations present. Use
UMM_INTEGRITY_CHECK, UMM_POISON_CHECK, and umm_info() sparingly. If you want to
see numbers for the disabled time, explore using UMM_CRITICAL_METRICS in
umm_malloc_cfg.h.
===============================================================================
New upstream umm_malloc feature delta's from the old umm_malloc we were using:
umm_posion check for a given *alloc - failure - no longer panics.
option to run full poison check at each *alloc call, not present
option to run full interity check at each *alloc call, not present
upstream code does not call panic from poison_check_block.
Defragmenting effect of realloc is gone. It now minimizes copy. This
may have been an accident during code cleanup.
In one form or another these features have been restored in the
reintegration of the upstream umm_malloc into the Arduino ESP8266 Core.
===============================================================================
A list of changes made for local adaptation of newer upstream umm_malloc.
In umm_malloc.c
Renamed to umm_malloc.cpp
Added `extern "C" { ... };` around code.
Surround DBGLOG_LEVEL with #ifndef... Define value of DBGLOG_LEVEL from
umm_malloc_cfg.h
umm_realloc() - Added UMM_CRITICAL_SUSPEND()/UMM_CRITICAL_RESUME() for when
lightweight locks are available. eg. sti/cli. Single threaded single CPU
case.
umm_realloc() - appears to have been refactored to minimize memmove and
memcpy. The old version would always combine an adjacent block in the
direction of the start of the heap when available and do a memmove. This
had a defragging effect. This appears to have been replaced with an attempt
to minimize copy when possible.
Added heap stats tracking.
In umm_info.c
umm_info() - Added UMM_CRITICAL_DECL(id_info), updated critical sections
with tag.
Carried forward: Added NULL ptr check at beginning (umm_malloc.c).
In umm_poison.c:
Resolved C++ compiler error reported on get_poisoned(), and get_unpoisoned().
They now take in void * arg instead of unsigned char *.
Added #if ... || defined(UMM_POISON_CHECK_LITE) to the conditional.
In umm_integrity.c:
Replaced printf with DBGLOG_FUNCTION. This needs to be a malloc free
function and ISR safe.
Added critical sections.
In umm_malloc_cfg.h:
Added macro UMM_CRITICAL_SUSPEND()/UMM_CRITICAL_RESUME()
Globally change across all files %i to %d: umm_info.c, umm_malloc.c,
Added a #ifdef BUILD_UMM_MALLOC_C fence to prevent Arduino IDE from building
the various .c files that are #included into umm_malloc.cpp. They are
normally enabled by #define <feature name> in umm_malloc_cfg.h. In this
case it builds fine; however, if the define is global, the IDE will try and
build the .c by itself.
Notes,
umm_integrity_check() is called by macro INTEGRITY_CHECK which returns 1
on success. No corruption. Does a time consuming scan of the whole heap.
It will call UMM_HEAP_CORRUPTION_CB if an error is found.
umm_poison_check(), formerly known as check_poison_all_blocks(),
is called by macro POISON_CHECK which returns 1 on success for no
corruption. Does a time consuming scan of all active allocations for
modified poison. The new upstream version does *NOT* call
UMM_HEAP_CORRUPTION_CB if an error is found. The option description says
it does!
umm_poison_realloc() and umm_poison_free() no longer call the macro
UMM_HEAP_CORRUPTION_CB on poison error. Just a printf message is
generated. I have added alternative functions umm_poison_free_fl,
umm_poison_realloc_fl, and get_unpoisoned_check_neighbors in
umm_local.cpp. These expand the poison check on the current allocation to
include its nearest allocated neighbors in the heap.
umm_malloc() has been extended to call check_poison_neighbors for the
allocation it selects, conditionally for UMM_POISON_CHECK_LITE.
For upstream umm_malloc "# define POISON_CHECK() 0" should have been 1
add to list to report.
==============================================================================
Notes from searching for the best print option
Printing from the malloc routines is tricky. Since a print library
might call *alloc. Then recursion may follow as each error call may fail
into another error and so on.
Objective: To be able to print "last gasp" diagnostic messages
when interrupts are disabled and w/o availability of heap resources.
It turns out things are more complicated than that. These are three cases for
printing from the heap and the current solution.:
1. Printing detailed heap info through `umm_info(NULL, 1);`. This function
resides in flash and can only be called from non-ISR context. It can use
PROGMEM strings. Because SPI bus will not be busy when called from foreground.
At this time it is believed that, while running from foreground, a cache-miss
at INTLEVEL 15 can be handled. The key factor being the SPI bus is not
busy at the time of the cache-miss. It is not clear what gets invoked to
process the cache-miss. A software vector call? A hardware assisted transfer?
In any case `umm_info_safe_printf_P()` is also in flash.
The focus here is to print w/o allocating memory and use strings
in flash to preserve DRAM.
2. Printing diagnostic messages possibly from from ISR context.
Use `ets_uart_printf()` in boot ROM.
3. Printing diagnostic messages from `heap.cpp` these printf's need to check
`system_get_os_print()` to confirm debug-output is enabled just as
`os_printf()` did.
Do test calls to `system_get_os_print()` and call `ets_uart_printf()`
in boot ROM when debug print allowed.
Considerations:
* can be called from ISR
* can be called from malloc code, cannot use malloc
* can be called from malloc code that was called from an ISR
* can be called from within a critical section, eg. xt_rsil(15);
* this may be effectively the same as being called from an ISR?
Update: Current thinking is that from foreground we have more leeway
than an ISR.
Knowns:
* ets_printf - For RTOS SDK they replaced this function with one in the SDK.
Most of the problems I can see with ets_printf center around not being
able to maintain a port to thread context. That is you cannot have one
thread using one port while another thread uses the other. In the no OS
case we cannot have one area of code using one port and another area of
code using the other port. Most of the ROM printf functions are not built
to support this kind of usage. Things get especially dangerous when you
try to use the ets_external_printf stuff.
* ets_vprintf - by itself is safe.
* newlibc printf - not safe - lives in flash.
* newlibc snprintf - not safe - lives in flash.
* builtin putc1 print function - Is installed when you use
ets_install_uart_printf. Which calls ets_install_putc1. The selection of UART
is performed by calling uart_buff_switch with 0 for UART0 and 1 for UART1.
This should work for our purpose here, if handled as follows:
* call uart_buff_switch at each printf call to reselect UART
* Update: uart_buff_switch is now updated by uart_set_debug() in uart.cpp
* use a stack buffer to hold a copy the PROGMEM string to print from.
* use ets_vprintf for printing with putc1 function.
* os_printf_plus looks interesting. It is in IRAM. If no heap is available it
will use up to 64 bytes of stack space to copy a PROGMEM fmt for printing.
Issues:
* Printing is turned off by system_set_os_print
* putc1 needs to be in IRAM - this is a uart.cpp issue
* Need to force system_get_free_heap_size to return 0 during critical periods.
* won't work for umm_info if it prints over 64 characters.
* along with umm_info there are other debug messages that exceed 64 characters.
* ets_uart_printf - Appears safe. Just no PROGMEM support. Uses
uart_buff_switch to select UART.
===============================================================================
heap.cpp is the entry point for most of the heap API calls.
It is a merge point for abstracted heap API calls, such as _malloc_r,
pvPortMalloc, and malloc. Thin wrappers are created here for these entry points
and others. The wrappers call through to equivalent umm_malloc entry-point.
These wrappers also provide the access points to do debug options, like OOM,
Integrity Check, and Poison Check.
-DEBUG_ESP_OOM or select `Debug Level: "OOM"` from the IDE.
This option will add extra code to save information on the last OOM event. If
your code is built with the `Debug port: "Serial"` option, debug messages will
print on OOM events. You do not have to do `Debug port: "Serial"` to get OOM
debug messages. From within your code, you can make a call to
`Serial.debugOutput(true);` to enable OOM printing. Of course for this to work
your code must be built with `Debug Level: "OOM"` or equal.
-DUUM_POISON is now the same as -DUMM_POISON_CHECK_LITE
This is new behavior with this updated. UMM_POISON_CHECK_LITE - checks the
allocation presented at realloc() and free(). Expands the poison check on the
current allocation to include its nearest allocated neighbors in the heap.
umm_malloc() will also check the neighbors of the selected allocation before
use.
For more details and options search on UMM_POISON_CHECK in `umm_malloc_cfg.h`
TODO: provide some interesting numbers on the time to perform:
* UMM_POISON_CHECK
* UMM_INTEGRITY_CHECK
* umm_info(NUll, 0) built with and without print capability
* umm_info(NUll, 1) printing a report to Serial device.
===============================================================================
Enhancement ideas:
1. Add tagging to heap allocations. Redefine UMM_POISONED_BLOCK_LEN_TYPE,
expand it to include an element for the calling address of allocating
requester. Expand umm_info(NULL, 1) to print the respective address with each
active allocation. The difficulty here will be the ever-growing complexity of
overlapping build options. I think it would be easiest to build this in with
and expand the UMM_POISON_CHECK_LITE option.
2. A build option to not have printing, from umm_info() compiled in. This can
save on the execution time spent with interrupts disabled.
*/
#endif

View File

@ -10,18 +10,18 @@ might get expensive.
## Acknowledgements ## Acknowledgements
Joerg Wunsch and the avr-libc provided the first malloc() implementation Joerg Wunsch and the avr-libc provided the first `malloc()` implementation
that I examined in detail. that I examined in detail.
http://www.nongnu.org/avr-libc `http://www.nongnu.org/avr-libc`
Doug Lea's paper on malloc() was another excellent reference and provides Doug Lea's paper on malloc() was another excellent reference and provides
a lot of detail on advanced memory management techniques such as binning. a lot of detail on advanced memory management techniques such as binning.
http://g.oswego.edu/dl/html/malloc.html `http://g.oswego.edu/dl/html/malloc.html`
Bill Dittman provided excellent suggestions, including macros to support Bill Dittman provided excellent suggestions, including macros to support
using these functions in critical sections, and for optimizing realloc() using these functions in critical sections, and for optimizing `realloc()`
further by checking to see if the previous block was free and could be further by checking to see if the previous block was free and could be
used for the new block size. This can help to reduce heap fragmentation used for the new block size. This can help to reduce heap fragmentation
significantly. significantly.
@ -30,11 +30,73 @@ Yaniv Ankin suggested that a way to dump the current heap condition
might be useful. I combined this with an idea from plarroy to also might be useful. I combined this with an idea from plarroy to also
allow checking a free pointer to make sure it's valid. allow checking a free pointer to make sure it's valid.
Dimitry Frank contributed many helpful additions to make things more
robust including a user specified config file and a method of testing
the integrity of the data structures.
## Usage
Copy the `umm_malloc_cfg_example.h` file to `umm_malloc_cfg.h` and
make the changes required to support your application.
The following `#define`s must be set to something useful for the
library to work at all
- `UMM_MALLOC_CFG_HEAP_ADDR` must be set to the symbol representing
the starting address of the heap. The heap must be
aligned on the natural boundary size of the processor.
- `UMM_MALLOC_CFG_HEAP_SIZE` must be set to the size of the heap.
The heap size must be a multiple of the natural boundary size of
the processor.
The fit algorithm is defined as either:
- `UMM_BEST_FIT` which scans the entire free list and looks
for either an exact fit or the smallest block that will
satisfy the request. This is the default fit method.
- `UMM_FIRST_FIT` which scans the entire free list and looks
for the first block that satisfies the request.
The following `#define`s are disabled by default and should
remain disabled for production use. They are helpful when
testing allocation errors (which are normally due to bugs in
the application code) or for running the test suite when
making changes to the code.
You can define them in your compiler command line or uncomment
the corresponding entries is `umm_malloc_cfg.h`:
- `UMM_INFO` is used to include code that allows dumping
the entire heap structure (helpful when there's a problem).
- `UMM_INTEGRITY_CHECK` is used to include code that
performs an integrity check on the heap structure. It's
up to you to call the `umm_integrity_check()` function.
- `UMM_POISON_CHECK` is used to include code that
adds some bytes around the memory being allocated that
are filled with known data. If the data is not intact
when the block is checked, then somone has written outside
of the memory block they have been allocated. It is up
to you to call the `umm_poison_check()` function.
## API
The following functions are available for your application:
- `void *umm_malloc( size_t size );`
- `void *umm_calloc( size_t num, size_t size );`
- `void *umm_realloc( void *ptr, size_t size );`
- `void umm_free( void *ptr );`
They have exactly the same semantics as the corresponding standard library
functions.
## Background ## Background
The memory manager assumes the following things: The memory manager assumes the following things:
1. The standard POSIX compliant malloc/realloc/free semantics are used 1. The standard POSIX compliant malloc/calloc/realloc/free semantics are used
1. All memory used by the manager is allocated at link time, it is aligned 1. All memory used by the manager is allocated at link time, it is aligned
on a 32 bit boundary, it is contiguous, and its extent (start and end on a 32 bit boundary, it is contiguous, and its extent (start and end
address) is filled in by the linker. address) is filled in by the linker.
@ -45,17 +107,17 @@ The fastest linked list implementations use doubly linked lists so that
its possible to insert and delete blocks in constant time. This memory its possible to insert and delete blocks in constant time. This memory
manager keeps track of both free and used blocks in a doubly linked list. manager keeps track of both free and used blocks in a doubly linked list.
Most memory managers use some kind of list structure made up of pointers Most memory managers use a list structure made up of pointers
to keep track of used - and sometimes free - blocks of memory. In an to keep track of used - and sometimes free - blocks of memory. In an
embedded system, this can get pretty expensive as each pointer can use embedded system, this can get pretty expensive as each pointer can use
up to 32 bits. up to 32 bits.
In most embedded systems there is no need for managing large blocks In most embedded systems there is no need for managing a large quantity
of memory dynamically, so a full 32 bit pointer based data structure of memory blocks dynamically, so a full 32 bit pointer based data structure
for the free and used block lists is wasteful. A block of memory on for the free and used block lists is wasteful. A block of memory on
the free list would use 16 bytes just for the pointers! the free list would use 16 bytes just for the pointers!
This memory management library sees the malloc heap as an array of blocks, This memory management library sees the heap as an array of blocks,
and uses block numbers to keep track of locations. The block numbers are and uses block numbers to keep track of locations. The block numbers are
15 bits - which allows for up to 32767 blocks of memory. The high order 15 bits - which allows for up to 32767 blocks of memory. The high order
bit marks a block as being either free or in use, which will be explained bit marks a block as being either free or in use, which will be explained
@ -75,7 +137,7 @@ can always add more data bytes to the body of the memory block
at the expense of free block size overhead. at the expense of free block size overhead.
There are a lot of little features and optimizations in this memory There are a lot of little features and optimizations in this memory
management system that makes it especially suited to small embedded, but management system that makes it especially suited to small systems, and
the best way to appreciate them is to review the data structures and the best way to appreciate them is to review the data structures and
algorithms used, so let's get started. algorithms used, so let's get started.
@ -124,10 +186,9 @@ Where:
- n is the index of the next block in the heap - n is the index of the next block in the heap
- p is the index of the previous block in the heap - p is the index of the previous block in the heap
Note that the free list information is gone, because it's now being used to Note that the free list information is gone because it's now
store actual data for the application. It would have been nice to store being used to store actual data for the application. If we had
the next and previous free list indexes as well, but that would be a waste even 500 items in use, that would be 2,000 bytes for
of space. If we had even 500 items in use, that would be 2,000 bytes for
free list information. We simply can't afford to waste that much. free list information. We simply can't afford to waste that much.
The address of the `...` area is what is returned to the application The address of the `...` area is what is returned to the application
@ -143,7 +204,7 @@ described.
`...` between memory blocks indicates zero or more additional blocks are `...` between memory blocks indicates zero or more additional blocks are
allocated for use by the upper block. allocated for use by the upper block.
And while we're talking about "upper" and "lower" blocks, we should make While we're talking about "upper" and "lower" blocks, we should make
a comment about adresses. In the diagrams, a block higher up in the a comment about adresses. In the diagrams, a block higher up in the
picture is at a lower address. And the blocks grow downwards their picture is at a lower address. And the blocks grow downwards their
block index increases as does their physical address. block index increases as does their physical address.
@ -166,24 +227,27 @@ we're at the end of the list!
At this point, the malloc has a special test that checks if the current At this point, the malloc has a special test that checks if the current
block index is 0, which it is. This special case initializes the free block index is 0, which it is. This special case initializes the free
list to point at block index 1. list to point at block index 1 and then points block 1 to the
last block (lf) on the heap.
``` ```
BEFORE AFTER BEFORE AFTER
+----+----+----+----+ +----+----+----+----+ +----+----+----+----+ +----+----+----+----+
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 |
+----+----+----+----+ +----+----+----+----+ +----+----+----+----+ +----+----+----+----+
+----+----+----+----+ +----+----+----+----+
1 | 0 | 0 | 0 | 0 | 1 |*lf | 0 | 0 | 0 |
+----+----+----+----+
...
+----+----+----+----+
lf | 0 | 1 | 0 | 0 |
+----+----+----+----+ +----+----+----+----+
``` ```
The heap is now ready to complete the first malloc operation. The heap is now ready to complete the first malloc operation.
### Operation of malloc when we have reached the end of the free list and there is no block large enough to accommodate the request.
### Operation of malloc when we have reached the end of the free list and
there is no block large enough to accommodate the request.
This happens at the very first malloc operation, or any time the free This happens at the very first malloc operation, or any time the free
list is traversed and no free block large enough for the request is list is traversed and no free block large enough for the request is
@ -306,8 +370,7 @@ else
``` ```
Step 1 of the free operation checks if the next block is free, and if it Step 1 of the free operation checks if the next block is free, and if it
is then insert this block into the free list and assimilate the next block is assimilate the next block with this one.
with this one.
Note that c is the block we are freeing up, cf is the free block that Note that c is the block we are freeing up, cf is the free block that
follows it. follows it.

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Ralph Hempel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1 @@
Downloaded from: https://github.com/rhempel/c-helper-macros/tree/develop

View File

@ -0,0 +1,98 @@
/* ----------------------------------------------------------------------------
* dbglog.h - A set of macros that cleans up code that needs to produce debug
* or log information.
*
* Many embedded systems still put a premium on code space and therefore need
* a way to conditionally compile in debug code. Yes, it can lead to code that
* runs differently depending on whether the debug code is cmpiled in or not
* but you need to be able to evaluate the tradeoff.
*
* See copyright notice in LICENSE.TXT
* ----------------------------------------------------------------------------
* NOTE WELL that this file may be included multiple times - this allows you
* to set the trace level #define DBGLOG_LEVEL x
*
* To update which of the DBGLOG macros are compiled in, you must redefine the
* DBGLOG_LEVEL macro and the inlcude the dbglog.h file again, like this:
*
* #undef DBGLOG_LEVEL
* #define DBGLOG_LEVEL 6
* #include "dbglog/dbglog.txt"
*
* To handle multiple inclusion, we need to first undefine any macros we define
* so that the compiler does not warn us that we are changing a macro.
* ----------------------------------------------------------------------------
* The DBGLOG_LEVEL and DBGLOG_FUNCTION should be defined BEFORE this
* file is included or else the following defaults are used:
*
* #define DBGLOG_LEVEL 0
* #define DBGLOG_FUNCTION printf
* ----------------------------------------------------------------------------
* There are macros to handle the following decreasing levels of detail:
*
* 6 = TRACE
* 5 = DEBUG
* 4 = CRITICAL
* 3 = ERROR
* 2 = WARNING
* 1 = INFO
* 0 = FORCE - The DBGLOG_FUNCTION is always compiled in and is called only when
* the first parameter to the macro is non-0
* ----------------------------------------------------------------------------
*/
#undef DBGLOG_TRACE
#undef DBGLOG_DEBUG
#undef DBGLOG_CRITICAL
#undef DBGLOG_ERROR
#undef DBGLOG_WARNING
#undef DBGLOG_INFO
#undef DBGLOG_FORCE
#ifndef DBGLOG_LEVEL
# define DBGLOG_LEVEL 0
#endif
#ifndef DBGLOG_FUNCTION
# define DBGLOG_FUNCTION printf
#endif
/* ------------------------------------------------------------------------- */
#if DBGLOG_LEVEL >= 6
# define DBGLOG_TRACE(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
#else
# define DBGLOG_TRACE(format, ...)
#endif
#if DBGLOG_LEVEL >= 5
# define DBGLOG_DEBUG(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
#else
# define DBGLOG_DEBUG(format, ...)
#endif
#if DBGLOG_LEVEL >= 4
# define DBGLOG_CRITICAL(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
#else
# define DBGLOG_CRITICAL(format, ...)
#endif
#if DBGLOG_LEVEL >= 3
# define DBGLOG_ERROR(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
#else
# define DBGLOG_ERROR(format, ...)
#endif
#if DBGLOG_LEVEL >= 2
# define DBGLOG_WARNING(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
#else
# define DBGLOG_WARNING(format, ...)
#endif
#if DBGLOG_LEVEL >= 1
# define DBGLOG_INFO(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
#else
# define DBGLOG_INFO(format, ...)
#endif
#define DBGLOG_FORCE(force, format, ...) {if(force) {DBGLOG_FUNCTION(format, ## __VA_ARGS__);}}

View File

@ -0,0 +1,185 @@
#if defined(BUILD_UMM_MALLOC_C)
#ifdef UMM_INFO
/* ----------------------------------------------------------------------------
* One of the coolest things about this little library is that it's VERY
* easy to get debug information about the memory heap by simply iterating
* through all of the memory blocks.
*
* As you go through all the blocks, you can check to see if it's a free
* block by looking at the high order bit of the next block index. You can
* also see how big the block is by subtracting the next block index from
* the current block number.
*
* The umm_info function does all of that and makes the results available
* in the ummHeapInfo structure.
* ----------------------------------------------------------------------------
*/
UMM_HEAP_INFO ummHeapInfo;
void *umm_info( void *ptr, int force ) {
UMM_CRITICAL_DECL(id_info);
unsigned short int blockNo = 0;
if (umm_heap == NULL) {
umm_init();
}
/* Protect the critical section... */
UMM_CRITICAL_ENTRY(id_info);
/*
* Clear out all of the entries in the ummHeapInfo structure before doing
* any calculations..
*/
memset( &ummHeapInfo, 0, sizeof( ummHeapInfo ) );
DBGLOG_FORCE( force, "\n" );
DBGLOG_FORCE( force, "+----------+-------+--------+--------+-------+--------+--------+\n" );
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n",
(unsigned long)(&UMM_BLOCK(blockNo)),
blockNo,
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
UMM_PBLOCK(blockNo),
(UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo,
UMM_NFREE(blockNo),
UMM_PFREE(blockNo) );
/*
* Now loop through the block lists, and keep track of the number and size
* of used and free blocks. The terminating condition is an nb pointer with
* a value of zero...
*/
blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK;
while( UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK ) {
size_t curBlocks = (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo;
++ummHeapInfo.totalEntries;
ummHeapInfo.totalBlocks += curBlocks;
/* Is this a free block? */
if( UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK ) {
++ummHeapInfo.freeEntries;
ummHeapInfo.freeBlocks += curBlocks;
ummHeapInfo.freeSize2 += (unsigned int)curBlocks
* (unsigned int)sizeof(umm_block)
* (unsigned int)curBlocks
* (unsigned int)sizeof(umm_block);
if (ummHeapInfo.maxFreeContiguousBlocks < curBlocks) {
ummHeapInfo.maxFreeContiguousBlocks = curBlocks;
}
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|NF %5d|PF %5d|\n",
(unsigned long)(&UMM_BLOCK(blockNo)),
blockNo,
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
UMM_PBLOCK(blockNo),
(unsigned int)curBlocks,
UMM_NFREE(blockNo),
UMM_PFREE(blockNo) );
/* Does this block address match the ptr we may be trying to free? */
if( ptr == &UMM_BLOCK(blockNo) ) {
/* Release the critical section... */
UMM_CRITICAL_EXIT(id_info);
return( ptr );
}
} else {
++ummHeapInfo.usedEntries;
ummHeapInfo.usedBlocks += curBlocks;
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|\n",
(unsigned long)(&UMM_BLOCK(blockNo)),
blockNo,
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
UMM_PBLOCK(blockNo),
(unsigned int)curBlocks );
}
blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK;
}
/*
* Update the accounting totals with information from the last block, the
* rest must be free!
*/
{
size_t curBlocks = UMM_NUMBLOCKS-blockNo;
ummHeapInfo.freeBlocks += curBlocks;
ummHeapInfo.totalBlocks += curBlocks;
if (ummHeapInfo.maxFreeContiguousBlocks < curBlocks) {
ummHeapInfo.maxFreeContiguousBlocks = curBlocks;
}
}
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n",
(unsigned long)(&UMM_BLOCK(blockNo)),
blockNo,
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
UMM_PBLOCK(blockNo),
UMM_NUMBLOCKS-blockNo,
UMM_NFREE(blockNo),
UMM_PFREE(blockNo) );
DBGLOG_FORCE( force, "+----------+-------+--------+--------+-------+--------+--------+\n" );
DBGLOG_FORCE( force, "Total Entries %5d Used Entries %5d Free Entries %5d\n",
ummHeapInfo.totalEntries,
ummHeapInfo.usedEntries,
ummHeapInfo.freeEntries );
DBGLOG_FORCE( force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n",
ummHeapInfo.totalBlocks,
ummHeapInfo.usedBlocks,
ummHeapInfo.freeBlocks );
DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" );
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
if (ummHeapInfo.freeBlocks == ummStats.free_blocks) {
DBGLOG_FORCE( force, "heap info Free blocks and heap statistics Free blocks match.\n");
} else {
DBGLOG_FORCE( force, "\nheap info Free blocks %5d != heap statistics Free Blocks %5d\n\n",
ummHeapInfo.freeBlocks,
ummStats.free_blocks );
}
DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" );
print_stats(force);
#endif
/* Release the critical section... */
UMM_CRITICAL_EXIT(id_info);
return( NULL );
}
/* ------------------------------------------------------------------------ */
size_t umm_free_heap_size( void ) {
umm_info(NULL, 0);
return (size_t)ummHeapInfo.freeBlocks * sizeof(umm_block);
}
size_t umm_max_block_size( void ) {
umm_info(NULL, 0);
return ummHeapInfo.maxFreeContiguousBlocks * sizeof(umm_block);
}
/* ------------------------------------------------------------------------ */
#endif
#endif // defined(BUILD_UMM_MALLOC_C)

View File

@ -0,0 +1,134 @@
#if defined(BUILD_UMM_MALLOC_C)
/* integrity check (UMM_INTEGRITY_CHECK) {{{ */
#if defined(UMM_INTEGRITY_CHECK)
/*
* Perform integrity check of the whole heap data. Returns 1 in case of
* success, 0 otherwise.
*
* First of all, iterate through all free blocks, and check that all backlinks
* match (i.e. if block X has next free block Y, then the block Y should have
* previous free block set to X).
*
* Additionally, we check that each free block is correctly marked with
* `UMM_FREELIST_MASK` on the `next` pointer: during iteration through free
* list, we mark each free block by the same flag `UMM_FREELIST_MASK`, but
* on `prev` pointer. We'll check and unmark it later.
*
* Then, we iterate through all blocks in the heap, and similarly check that
* all backlinks match (i.e. if block X has next block Y, then the block Y
* should have previous block set to X).
*
* But before checking each backlink, we check that the `next` and `prev`
* pointers are both marked with `UMM_FREELIST_MASK`, or both unmarked.
* This way, we ensure that the free flag is in sync with the free pointers
* chain.
*/
int umm_integrity_check(void) {
UMM_CRITICAL_DECL(id_integrity);
int ok = 1;
unsigned short int prev;
unsigned short int cur;
if (umm_heap == NULL) {
umm_init();
}
/* Iterate through all free blocks */
prev = 0;
UMM_CRITICAL_ENTRY(id_integrity);
while(1) {
cur = UMM_NFREE(prev);
/* Check that next free block number is valid */
if (cur >= UMM_NUMBLOCKS) {
DBGLOG_FUNCTION("heap integrity broken: too large next free num: %d "
"(in block %d, addr 0x%lx)\n", cur, prev,
(unsigned long)&UMM_NBLOCK(prev));
ok = 0;
goto clean;
}
if (cur == 0) {
/* No more free blocks */
break;
}
/* Check if prev free block number matches */
if (UMM_PFREE(cur) != prev) {
DBGLOG_FUNCTION("heap integrity broken: free links don't match: "
"%d -> %d, but %d -> %d\n",
prev, cur, cur, UMM_PFREE(cur));
ok = 0;
goto clean;
}
UMM_PBLOCK(cur) |= UMM_FREELIST_MASK;
prev = cur;
}
/* Iterate through all blocks */
prev = 0;
while(1) {
cur = UMM_NBLOCK(prev) & UMM_BLOCKNO_MASK;
/* Check that next block number is valid */
if (cur >= UMM_NUMBLOCKS) {
DBGLOG_FUNCTION("heap integrity broken: too large next block num: %d "
"(in block %d, addr 0x%lx)\n", cur, prev,
(unsigned long)&UMM_NBLOCK(prev));
ok = 0;
goto clean;
}
if (cur == 0) {
/* No more blocks */
break;
}
/* make sure the free mark is appropriate, and unmark it */
if ((UMM_NBLOCK(cur) & UMM_FREELIST_MASK)
!= (UMM_PBLOCK(cur) & UMM_FREELIST_MASK))
{
DBGLOG_FUNCTION("heap integrity broken: mask wrong at addr 0x%lx: n=0x%x, p=0x%x\n",
(unsigned long)&UMM_NBLOCK(cur),
(UMM_NBLOCK(cur) & UMM_FREELIST_MASK),
(UMM_PBLOCK(cur) & UMM_FREELIST_MASK)
);
ok = 0;
goto clean;
}
/* make sure the block list is sequential */
if (cur <= prev ) {
DBGLOG_FUNCTION("heap integrity broken: next block %d is before prev this one "
"(in block %d, addr 0x%lx)\n", cur, prev,
(unsigned long)&UMM_NBLOCK(prev));
ok = 0;
goto clean;
}
/* unmark */
UMM_PBLOCK(cur) &= UMM_BLOCKNO_MASK;
/* Check if prev block number matches */
if (UMM_PBLOCK(cur) != prev) {
DBGLOG_FUNCTION("heap integrity broken: block links don't match: "
"%d -> %d, but %d -> %d\n",
prev, cur, cur, UMM_PBLOCK(cur));
ok = 0;
goto clean;
}
prev = cur;
}
clean:
UMM_CRITICAL_EXIT(id_integrity);
if (!ok){
UMM_HEAP_CORRUPTION_CB();
}
return ok;
}
#endif
/* }}} */
#endif // defined(BUILD_UMM_MALLOC_C)

View File

@ -0,0 +1,215 @@
/*
* Local Additions/Enhancements
*
*/
#if defined(BUILD_UMM_MALLOC_C)
#if defined(UMM_CRITICAL_METRICS)
/*
* umm_malloc performance measurments for critical sections
*/
UMM_TIME_STATS time_stats = {
{0xFFFFFFFF, 0U, 0U, 0U},
{0xFFFFFFFF, 0U, 0U, 0U},
{0xFFFFFFFF, 0U, 0U, 0U},
#ifdef UMM_INFO
{0xFFFFFFFF, 0U, 0U, 0U},
#endif
#ifdef UMM_POISON_CHECK
{0xFFFFFFFF, 0U, 0U, 0U},
#endif
#ifdef UMM_INTEGRITY_CHECK
{0xFFFFFFFF, 0U, 0U, 0U},
#endif
{0xFFFFFFFF, 0U, 0U, 0U} };
bool ICACHE_FLASH_ATTR get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size)
{
UMM_CRITICAL_DECL(id_no_tag);
if (p && sizeof(time_stats) == size)
{
UMM_CRITICAL_ENTRY(id_no_tag);
memcpy(p, &time_stats, size);
UMM_CRITICAL_EXIT(id_no_tag);
return true;
}
return false;
}
#endif
// Alternate Poison functions
#if defined(UMM_POISON_CHECK_LITE)
// We skip this when doing the full poison check.
static int check_poison_neighbors( unsigned short cur ) {
unsigned short int c;
if ( 0 == cur )
return 1;
c = UMM_PBLOCK(cur) & UMM_BLOCKNO_MASK;
while( c && (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) ) {
/*
There can be up to 1 free block neighbor in either direction.
This loop should self limit to 2 passes, due to heap design.
i.e. Adjacent free space is always consolidated.
*/
if ( !(UMM_NBLOCK(c) & UMM_FREELIST_MASK) ) {
if ( !check_poison_block(&UMM_BLOCK(c)) )
return 0;
break;
}
c = UMM_PBLOCK(c) & UMM_BLOCKNO_MASK;
}
c = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK;
while( (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) ) {
if ( !(UMM_NBLOCK(c) & UMM_FREELIST_MASK) ) {
if ( !check_poison_block(&UMM_BLOCK(c)) )
return 0;
break;
}
c = UMM_NBLOCK(c) & UMM_BLOCKNO_MASK;
}
return 1;
}
#endif
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
/* ------------------------------------------------------------------------ */
static void *get_unpoisoned_check_neighbors( void *v_ptr, const char* file, int line ) {
unsigned char *ptr = (unsigned char *)v_ptr;
if (ptr != NULL) {
ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
#if defined(UMM_POISON_CHECK_LITE)
UMM_CRITICAL_DECL(id_poison);
unsigned short int c;
bool poison = false;
/* Figure out which block we're in. Note the use of truncated division... */
c = (((char *)ptr)-(char *)(&(umm_heap[0])))/sizeof(umm_block);
UMM_CRITICAL_ENTRY(id_poison);
poison = check_poison_block(&UMM_BLOCK(c)) && check_poison_neighbors(c);
UMM_CRITICAL_EXIT(id_poison);
if (!poison) {
if (file) {
__panic_func(file, line, "");
} else {
abort();
}
}
#else
/*
* No need to check poison here. POISON_CHECK() has already done a
* full heap check.
*/
(void)file;
(void)line;
#endif
}
return (void *)ptr;
}
/* ------------------------------------------------------------------------ */
void *umm_poison_realloc_fl(void *ptr, size_t size, const char* file, int line) {
void *ret;
ptr = get_unpoisoned_check_neighbors(ptr, file, line);
size += poison_size(size);
ret = umm_realloc(ptr, size);
ret = get_poisoned(ret, size);
return ret;
}
/* ------------------------------------------------------------------------ */
void umm_poison_free_fl(void *ptr, const char* file, int line) {
ptr = get_unpoisoned_check_neighbors(ptr, file, line);
umm_free(ptr);
}
#endif
/* ------------------------------------------------------------------------ */
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
size_t umm_block_size( void ) {
return sizeof(umm_block);
}
#endif
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
UMM_STATISTICS ummStats;
// Keep complete call path in IRAM
size_t umm_free_heap_size_lw( void ) {
return (size_t)ummStats.free_blocks * sizeof(umm_block);
}
#endif
/*
I assume xPortGetFreeHeapSize needs to be in IRAM. Since
system_get_free_heap_size is in IRAM. Which would mean, umm_free_heap_size()
in flash, was not a safe alternative for returning the same information.
*/
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size_lw")));
#elif defined(UMM_INFO)
#warning "No ISR safe function available to implement xPortGetFreeHeapSize()"
size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size")));
#endif
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
void print_stats(int force) {
DBGLOG_FORCE( force, "umm heap statistics:\n");
DBGLOG_FORCE( force, " Free Space %5u\n", ummStats.free_blocks * sizeof(umm_block));
DBGLOG_FORCE( force, " OOM Count %5u\n", ummStats.oom_count);
#if defined(UMM_STATS_FULL)
DBGLOG_FORCE( force, " Low Watermark %5u\n", ummStats.free_blocks_min * sizeof(umm_block));
DBGLOG_FORCE( force, " Low Watermark ISR %5u\n", ummStats.free_blocks_isr_min * sizeof(umm_block));
DBGLOG_FORCE( force, " MAX Alloc Request %5u\n", ummStats.alloc_max_size);
#endif
DBGLOG_FORCE( force, " Size of umm_block %5u\n", sizeof(umm_block));
DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" );
}
#endif
int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) {
/*
To use ets_strlen() and ets_strcpy() safely with PROGMEM, flash storage,
the PROGMEM address must be word (4 bytes) aligned. The destination
address for ets_memcpy must also be word-aligned.
*/
char ram_buf[ets_strlen(fmt)] __attribute__ ((aligned(4)));
ets_strcpy(ram_buf, fmt);
va_list argPtr;
va_start(argPtr, fmt);
int result = ets_vprintf(ets_uart_putc1, ram_buf, argPtr);
va_end(argPtr);
return result;
}
#endif // BUILD_UMM_MALLOC_C

View File

@ -0,0 +1,54 @@
#ifndef _UMM_LOCAL_H
#define _UMM_LOCAL_H
/*
* A home for local items exclusive to umm_malloc.c and not to be shared in
* umm_malloc_cfg.h. And, not for upstream version.
* Also used to redefine defines made in upstream files we donet want to edit.
*
*/
#undef memcpy
#undef memmove
#undef memset
#define memcpy ets_memcpy
#define memmove ets_memmove
#define memset ets_memset
/*
* This redefines DBGLOG_FORCE defined in dbglog/dbglog.h
* Just for printing from umm_info() which is assumed to always be called from
* non-ISR. Thus SPI bus is available to handle cache-miss and reading a flash
* string while INTLEVEL is non-zero.
*/
#undef DBGLOG_FORCE
#define DBGLOG_FORCE(force, format, ...) {if(force) {UMM_INFO_PRINTF(format, ## __VA_ARGS__);}}
// #define DBGLOG_FORCE(force, format, ...) {if(force) {::printf(PSTR(format), ## __VA_ARGS__);}}
#if defined(DEBUG_ESP_OOM) || defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || defined(UMM_INTEGRITY_CHECK)
#else
#define umm_malloc(s) malloc(s)
#define umm_calloc(n,s) calloc(n,s)
#define umm_realloc(p,s) realloc(p,s)
#define umm_free(p) free(p)
#endif
#if defined(UMM_POISON_CHECK_LITE)
static int check_poison_neighbors( unsigned short cur );
#endif
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
void ICACHE_FLASH_ATTR print_stats(int force);
#endif
int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
#define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR(fmt), ##__VA_ARGS__)
#endif

File diff suppressed because it is too large Load Diff

View File

@ -10,42 +10,19 @@
/* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */
//C This include is not in upstream neither are the #ifdef __cplusplus
#include "umm_malloc_cfg.h" /* user-dependent */ #include "umm_malloc_cfg.h" /* user-dependent */
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
typedef struct UMM_HEAP_INFO_t {
unsigned short int totalEntries;
unsigned short int usedEntries;
unsigned short int freeEntries;
unsigned short int totalBlocks;
unsigned short int usedBlocks;
unsigned short int freeBlocks;
unsigned short int maxFreeContiguousBlocks;
unsigned int freeSize2;
}
UMM_HEAP_INFO;
extern UMM_HEAP_INFO ummHeapInfo;
void umm_init( void ); void umm_init( void );
void *umm_info( void *ptr, int force );
void *umm_malloc( size_t size ); void *umm_malloc( size_t size );
void *umm_calloc( size_t num, size_t size ); void *umm_calloc( size_t num, size_t size );
void *umm_realloc( void *ptr, size_t size ); void *umm_realloc( void *ptr, size_t size );
void umm_free( void *ptr ); void umm_free( void *ptr );
size_t umm_free_heap_size( void );
size_t umm_max_block_size( void );
size_t umm_block_size( void );
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1,11 +1,17 @@
/* /*
* Configuration for umm_malloc * Configuration for umm_malloc - target Arduino ESP8266 core
*
* Changes specific to a target platform go here.
*
*/ */
#ifndef _UMM_MALLOC_CFG_H #ifndef _UMM_MALLOC_CFG_H
#define _UMM_MALLOC_CFG_H #define _UMM_MALLOC_CFG_H
#include <debug.h> #include <debug.h>
#include <pgmspace.h>
#include <esp8266_undocumented.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -15,19 +21,6 @@ extern "C" {
#include <osapi.h> #include <osapi.h>
#include "c_types.h" #include "c_types.h"
#include "umm_performance.h"
#include "umm_stats.h"
#undef DBGLOG_FUNCTION
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_ISR)
int _isr_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
// Note, _isr_safe_printf_P will not handle additional string arguments in
// PROGMEM. Only the 1st parameter, fmt, is supported in PROGMEM.
#define DBGLOG_FUNCTION(fmt, ...) _isr_safe_printf_P(PSTR(fmt), ##__VA_ARGS__)
#else
// Macro to place constant strings into PROGMEM and print them properly
#define DBGLOG_FUNCTION(fmt, ...) printf(PSTR(fmt), ## __VA_ARGS__ )
#endif
/* /*
* There are a number of defines you can set at compile time that affect how * There are a number of defines you can set at compile time that affect how
@ -37,19 +30,7 @@ int _isr_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)
* *
* -D UMM_TEST_MAIN * -D UMM_TEST_MAIN
* *
* Set this if you want to compile in the test suite at the end of this file. * Set this if you want to compile in the test suite
*
* If you leave this define unset, then you might want to set another one:
*
* -D UMM_REDEFINE_MEM_FUNCTIONS
*
* If you leave this define unset, then the function names are left alone as
* umm_malloc() umm_free() and umm_realloc() so that they cannot be confused
* with the C runtime functions malloc() free() and realloc()
*
* If you do set this define, then the function names become malloc()
* free() and realloc() so that they can be used as the C runtime functions
* in an embedded environment.
* *
* -D UMM_BEST_FIT (defualt) * -D UMM_BEST_FIT (defualt)
* *
@ -75,46 +56,300 @@ int _isr_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
*/ */
///////////////////////////////////////////////// #ifdef TEST_BUILD
#ifdef DEBUG_ESP_OOM extern char test_umm_heap[];
#define MEMLEAK_DEBUG
// umm_*alloc are not renamed to *alloc
void *umm_malloc( size_t size );
void *umm_calloc( size_t num, size_t size );
void *umm_realloc( void *ptr, size_t size );
#define umm_free free
#define umm_zalloc(s) umm_calloc(1,s)
void* malloc_loc (size_t s, const char* file, int line);
void* calloc_loc (size_t n, size_t s, const char* file, int line);
void* realloc_loc (void* p, size_t s, const char* file, int line);
// *alloc are macro calling *alloc_loc calling+checking umm_*alloc()
// they are defined at the bottom of this file
/////////////////////////////////////////////////
#else // !defined(ESP_DEBUG_OOM)
// umm_*alloc are renamed to *alloc
#define UMM_REDEFINE_MEM_FUNCTIONS
#endif #endif
#define UMM_BEST_FIT #ifdef TEST_BUILD
/* Start addresses and the size of the heap */
#define UMM_MALLOC_CFG_HEAP_ADDR (test_umm_heap)
#define UMM_MALLOC_CFG_HEAP_SIZE 0x10000
#else
/* Start addresses and the size of the heap */ /* Start addresses and the size of the heap */
extern char _heap_start[]; extern char _heap_start[];
#define UMM_MALLOC_CFG__HEAP_ADDR ((uint32_t)&_heap_start) #define UMM_MALLOC_CFG_HEAP_ADDR ((uint32_t)&_heap_start[0])
#define UMM_MALLOC_CFG__HEAP_SIZE ((size_t)(0x3fffc000 - UMM_MALLOC_CFG__HEAP_ADDR)) #define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(0x3fffc000 - UMM_MALLOC_CFG_HEAP_ADDR))
#endif
/* A couple of macros to make packing structures less compiler dependent */ /* A couple of macros to make packing structures less compiler dependent */
#define UMM_H_ATTPACKPRE #define UMM_H_ATTPACKPRE
#define UMM_H_ATTPACKSUF __attribute__((__packed__)) #define UMM_H_ATTPACKSUF __attribute__((__packed__))
#define UMM_BEST_FIT
#undef UMM_FIRST_FIT
/*
* -D UMM_INFO :
*
* Enables a dup of the heap contents and a function to return the total
* heap size that is unallocated - note this is not the same as the largest
* unallocated block on the heap!
*/
#define UMM_INFO
#ifdef UMM_INFO
typedef struct UMM_HEAP_INFO_t {
unsigned short int totalEntries;
unsigned short int usedEntries;
unsigned short int freeEntries;
unsigned short int totalBlocks;
unsigned short int usedBlocks;
unsigned short int freeBlocks;
unsigned short int maxFreeContiguousBlocks;
unsigned int freeSize2;
}
UMM_HEAP_INFO;
extern UMM_HEAP_INFO ummHeapInfo;
void ICACHE_FLASH_ATTR *umm_info( void *ptr, int force );
size_t ICACHE_FLASH_ATTR umm_free_heap_size( void );
size_t ICACHE_FLASH_ATTR umm_max_block_size( void );
#else
#endif
/*
* -D UMM_STATS :
* -D UMM_STATS_FULL
*
* This option provides a lightweight alternative to using `umm_info` just for
* getting `umm_free_heap_size`. With this option, a "free blocks" value is
* updated on each call to malloc/free/realloc. This option does not offer all
* the information that `umm_info` would have generated.
*
* This option is good for cases where the free heap is checked frequently. An
* example is when an app closely monitors free heap to detect memory leaks. In
* this case a single-core CPUs interrupt processing would have suffered the
* most.
*
* UMM_STATS_FULL provides additional heap statistics. It can be used to gain
* additional insight into heap usage. This option would add an additional 132
* bytes of IRAM.
*
* Status: TODO: Needs to be proposed for upstream.
*/
/*
#define UMM_STATS
#define UMM_STATS_FULL
*/
/*
* For the ESP8266 we want at lest UMM_STATS built, so we have an ISR safe
* function to call for implementing xPortGetFreeHeapSize(), because umm_info()
* is in flash.
*/
#if !defined(UMM_STATS) && !defined(UMM_STATS_FULL)
#define UMM_STATS
#endif
#if defined(UMM_STATS) && defined(UMM_STATS_FULL)
#undef UMM_STATS
#endif
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
typedef struct UMM_STATISTICS_t {
unsigned short int free_blocks;
size_t oom_count;
#ifdef UMM_STATS_FULL
unsigned short int free_blocks_min;
unsigned short int free_blocks_isr_min;
size_t alloc_max_size;
size_t last_alloc_size;
size_t id_malloc_count;
size_t id_malloc_zero_count;
size_t id_realloc_count;
size_t id_realloc_zero_count;
size_t id_free_count;
size_t id_free_null_count;
#endif
}
UMM_STATISTICS;
extern UMM_STATISTICS ummStats;
#define STATS__FREE_BLOCKS_UPDATE(s) ummStats.free_blocks += (s)
#define STATS__OOM_UPDATE() ummStats.oom_count += 1
size_t umm_free_heap_size_lw( void );
static inline size_t ICACHE_FLASH_ATTR umm_get_oom_count( void ) {
return ummStats.oom_count;
}
#else // not UMM_STATS or UMM_STATS_FULL
#define STATS__FREE_BLOCKS_UPDATE(s) (void)(s)
#define STATS__OOM_UPDATE() (void)0
#endif
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
size_t ICACHE_FLASH_ATTR umm_block_size( void );
#endif
#ifdef UMM_STATS_FULL
#define STATS__FREE_BLOCKS_MIN() \
do { \
if (ummStats.free_blocks < ummStats.free_blocks_min) \
ummStats.free_blocks_min = ummStats.free_blocks; \
} while(false)
#define STATS__FREE_BLOCKS_ISR_MIN() \
do { \
if (ummStats.free_blocks < ummStats.free_blocks_isr_min) \
ummStats.free_blocks_isr_min = ummStats.free_blocks; \
} while(false)
#define STATS__ALLOC_REQUEST(tag, s) \
do { \
ummStats.tag##_count += 1; \
ummStats.last_alloc_size = s; \
if (ummStats.alloc_max_size < s) \
ummStats.alloc_max_size = s; \
} while(false)
#define STATS__ZERO_ALLOC_REQUEST(tag, s) \
do { \
ummStats.tag##_zero_count += 1; \
} while(false)
#define STATS__NULL_FREE_REQUEST(tag) \
do { \
ummStats.tag##_null_count += 1; \
} while(false)
#define STATS__FREE_REQUEST(tag) \
do { \
ummStats.tag##_count += 1; \
} while(false)
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_lw_min( void ) {
return (size_t)ummStats.free_blocks_min * umm_block_size();
}
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset( void ) {
ummStats.free_blocks_min = ummStats.free_blocks;
return (size_t)ummStats.free_blocks_min * umm_block_size();
}
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_min( void ) {
return ummStats.free_blocks_min * umm_block_size();
}
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_isr_min( void ) {
return ummStats.free_blocks_isr_min * umm_block_size();
}
static inline size_t ICACHE_FLASH_ATTR umm_get_max_alloc_size( void ) {
return ummStats.alloc_max_size;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_last_alloc_size( void ) {
return ummStats.last_alloc_size;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_malloc_count( void ) {
return ummStats.id_malloc_count;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_malloc_zero_count( void ) {
return ummStats.id_malloc_zero_count;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_realloc_count( void ) {
return ummStats.id_realloc_count;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_realloc_zero_count( void ) {
return ummStats.id_realloc_zero_count;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_free_count( void ) {
return ummStats.id_free_count;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_free_null_count( void ) {
return ummStats.id_free_null_count;
}
#else // Not UMM_STATS_FULL
#define STATS__FREE_BLOCKS_MIN() (void)0
#define STATS__FREE_BLOCKS_ISR_MIN() (void)0
#define STATS__ALLOC_REQUEST(tag, s) (void)(s)
#define STATS__ZERO_ALLOC_REQUEST(tag, s) (void)(s)
#define STATS__NULL_FREE_REQUEST(tag) (void)0
#define STATS__FREE_REQUEST(tag) (void)0
#endif
/*
Per Devyte, the core currently doesn't support masking a specific interrupt
level. That doesn't mean it can't be implemented, only that at this time
locking is implemented as all or nothing.
https://github.com/esp8266/Arduino/issues/6246#issuecomment-508612609
So for now we default to all, 15.
*/
#ifndef DEFAULT_CRITICAL_SECTION_INTLEVEL
#define DEFAULT_CRITICAL_SECTION_INTLEVEL 15
#endif
/*
* -D UMM_CRITICAL_METRICS
*
* Build option to collect timing usage data on critical section usage in
* functions: info, malloc, realloc. Collects MIN, MAX, and number of time IRQs
* were disabled at request time. Note, for realloc MAX disabled time will
* include the time spent in calling malloc and/or free. Examine code for
* specifics on what info is available and how to access.
*
* Status: TODO: Needs to be proposed for upstream. Also should include updates
* to UMM_POISON_CHECK and UMM_INTEGRITY_CHECK to include a critical section.
*/
/*
#define UMM_CRITICAL_METRICS
*/
#if defined(UMM_CRITICAL_METRICS)
// This option adds support for gathering time locked data
typedef struct UMM_TIME_STAT_t {
uint32_t min;
uint32_t max;
uint32_t start;
uint32_t intlevel;
}
UMM_TIME_STAT;
typedef struct UMM_TIME_STATS_t UMM_TIME_STATS;
extern UMM_TIME_STATS time_stats;
bool get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size);
static inline void _critical_entry(UMM_TIME_STAT *p, uint32_t *saved_ps) {
*saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL);
if (0U != (*saved_ps & 0x0FU)) {
p->intlevel += 1U;
}
p->start = esp_get_cycle_count();
}
static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
uint32_t elapse = esp_get_cycle_count() - p->start;
if (elapse < p->min)
p->min = elapse;
if (elapse > p->max)
p->max = elapse;
xt_wsr_ps(*saved_ps);
}
#endif
/* /*
* A couple of macros to make it easier to protect the memory allocator * A couple of macros to make it easier to protect the memory allocator
* in a multitasking system. You should set these macros up to use whatever * in a multitasking system. You should set these macros up to use whatever
@ -125,23 +360,82 @@ extern char _heap_start[];
* called from within umm_malloc() * called from within umm_malloc()
*/ */
#ifdef TEST_BUILD
#if defined(UMM_CRITICAL_PERIOD_ANALYZE) extern int umm_critical_depth;
extern int umm_max_critical_depth;
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag #define UMM_CRITICAL_ENTRY() {\
#define UMM_CRITICAL_ENTRY(tag) _critical_entry(&time_stats.tag, &_saved_ps_##tag) ++umm_critical_depth; \
#define UMM_CRITICAL_EXIT(tag) _critical_exit(&time_stats.tag, &_saved_ps_##tag) if (umm_critical_depth > umm_max_critical_depth) { \
umm_max_critical_depth = umm_critical_depth; \
} \
}
#define UMM_CRITICAL_EXIT() (umm_critical_depth--)
#else #else
#if defined(UMM_CRITICAL_METRICS)
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
#define UMM_CRITICAL_ENTRY(tag)_critical_entry(&time_stats.tag, &_saved_ps_##tag)
#define UMM_CRITICAL_EXIT(tag) _critical_exit(&time_stats.tag, &_saved_ps_##tag)
// This method preserves the intlevel on entry and restores the #else // ! UMM_CRITICAL_METRICS
// original intlevel at exit. // This method preserves the intlevel on entry and restores the
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag // original intlevel at exit.
#define UMM_CRITICAL_ENTRY(tag) _saved_ps_##tag = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL) #define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
#define UMM_CRITICAL_EXIT(tag) xt_wsr_ps(_saved_ps_##tag) #define UMM_CRITICAL_ENTRY(tag) _saved_ps_##tag = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL)
#define UMM_CRITICAL_EXIT(tag) xt_wsr_ps(_saved_ps_##tag)
#endif
#endif #endif
/*
* -D UMM_LIGHTWEIGHT_CPU
*
* The use of this macro is hardware/application specific.
*
* With some CPUs, the only available method for locking are the instructions
* for interrupts disable/enable. These macros are meant for lightweight single
* CPU systems that are sensitive to interrupts being turned off for too long. A
* typically UMM_CRITICAL_ENTRY would save current IRQ state then disable IRQs.
* Then UMM_CRITICAL_EXIT would restore previous IRQ state. This option adds
* additional critical entry/exit points by the method of defining the macros
* UMM_CRITICAL_SUSPEND and UMM_CRITICAL_RESUME to the values of
* UMM_CRITICAL_EXIT and UMM_CRITICAL_ENTRY. These additional exit/entries
* allow time to service interrupts during the reentrant sections of the code.
*
* Performance may be impacked if used with multicore CPUs. The higher frquency
* of locking and unlocking may be an issue with locking methods that have a
* high overhead.
*
* Status: TODO: Needs to be proposed for upstream.
*/
/*
*/
#define UMM_LIGHTWEIGHT_CPU
#ifdef UMM_LIGHTWEIGHT_CPU
#define UMM_CRITICAL_SUSPEND(tag) UMM_CRITICAL_EXIT(tag)
#define UMM_CRITICAL_RESUME(tag) UMM_CRITICAL_ENTRY(tag)
#else
#define UMM_CRITICAL_SUSPEND(tag) do {} while(0)
#define UMM_CRITICAL_RESUME(tag) do {} while(0)
#endif
/*
* -D UMM_REALLOC_MINIMIZE_COPY or
* -D UMM_REALLOC_DEFRAG
*
* Pick one of these two stratagies. UMM_REALLOC_MINIMIZE_COPY grows upward or
* shrinks an allocation, avoiding copy when possible. UMM_REALLOC_DEFRAG gives
* priority with growing the revised allocation toward an adjacent hole in the
* direction of the beginning of the heap when possible.
*
* Status: TODO: These are new options introduced to optionally restore the
* previous defrag propery of realloc. The issue has been raised in the upstream
* repo. No response at this time. Based on response, may propose for upstream.
*/
/*
#define UMM_REALLOC_MINIMIZE_COPY
*/
#define UMM_REALLOC_DEFRAG
/* /*
* -D UMM_INTEGRITY_CHECK : * -D UMM_INTEGRITY_CHECK :
* *
@ -155,12 +449,28 @@ extern char _heap_start[];
* 4 bytes, so there might be some trailing "extra" bytes which are not checked * 4 bytes, so there might be some trailing "extra" bytes which are not checked
* for corruption. * for corruption.
*/ */
/*
#define UMM_INTEGRITY_CHECK
*/
/* /*
* -D UMM_POISON : * Not normally enabled. Full intergity check may exceed 10us.
*/
/*
#define UMM_INTEGRITY_CHECK
*/
#ifdef UMM_INTEGRITY_CHECK
int umm_integrity_check( void );
# define INTEGRITY_CHECK() umm_integrity_check()
extern void umm_corruption(void);
# define UMM_HEAP_CORRUPTION_CB() DBGLOG_FUNCTION( "Heap Corruption!" )
#else
# define INTEGRITY_CHECK() 0
#endif
/////////////////////////////////////////////////
/*
* -D UMM_POISON_CHECK :
* -D UMM_POISON_CHECK_LITE
* *
* Enables heap poisoning: add predefined value (poison) before and after each * Enables heap poisoning: add predefined value (poison) before and after each
* allocation, and check before each heap operation that no poison is * allocation, and check before each heap operation that no poison is
@ -185,17 +495,135 @@ extern char _heap_start[];
* *
* If poison corruption is detected, the message is printed and user-provided * If poison corruption is detected, the message is printed and user-provided
* callback is called: `UMM_HEAP_CORRUPTION_CB()` * callback is called: `UMM_HEAP_CORRUPTION_CB()`
*
* UMM_POISON_CHECK - does a global heap check on all active allocation at
* every alloc API call. May exceed 10us due to critical section with IRQs
* disabled.
*
* UMM_POISON_CHECK_LITE - checks the allocation presented at realloc()
* and free(). Expands the poison check on the current allocation to
* include its nearest allocated neighbors in the heap.
* umm_malloc() will also checks the neighbors of the selected allocation
* before use.
*
* Status: TODO?: UMM_POISON_CHECK_LITE is a new option. We could propose for
* upstream; however, the upstream version has much of the framework for calling
* poison check on each alloc call refactored out. Not sure how this will be
* received.
*/ */
/*
* Compatibility for deprecated UMM_POISON
*/
#if defined(UMM_POISON) && !defined(UMM_POISON_CHECK)
#define UMM_POISON_CHECK_LITE
#endif
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_CORE) #if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_CORE)
#define UMM_POISON #if !defined(UMM_POISON_CHECK) && !defined(UMM_POISON_CHECK_LITE)
/*
#define UMM_POISON_CHECK
*/
#define UMM_POISON_CHECK_LITE
#endif
#endif #endif
#define UMM_POISON_SIZE_BEFORE 4 #define UMM_POISON_SIZE_BEFORE 4
#define UMM_POISON_SIZE_AFTER 4 #define UMM_POISON_SIZE_AFTER 4
#define UMM_POISONED_BLOCK_LEN_TYPE uint32_t #define UMM_POISONED_BLOCK_LEN_TYPE uint32_t
#define UMM_HEAP_CORRUPTION_CB() panic() #if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
void *umm_poison_malloc( size_t size );
void *umm_poison_calloc( size_t num, size_t size );
void *umm_poison_realloc( void *ptr, size_t size );
void umm_poison_free( void *ptr );
int umm_poison_check( void );
// Local Additions to better report location in code of the caller.
void *umm_poison_realloc_fl( void *ptr, size_t size, const char* file, int line );
void umm_poison_free_fl( void *ptr, const char* file, int line );
#if defined(UMM_POISON_CHECK_LITE)
/*
* We can safely do individual poison checks at free and realloc and stay
* under 10us or close.
*/
# define POISON_CHECK() 1
# define POISON_CHECK_NEIGHBORS(c) \
do {\
if(!check_poison_neighbors(c)) \
panic();\
} while(false)
#else
/* Not normally enabled. A full heap poison check may exceed 10us. */
# define POISON_CHECK() umm_poison_check()
# define POISON_CHECK_NEIGHBORS(c) do{}while(false)
#endif
#else
# define POISON_CHECK() 1
# define POISON_CHECK_NEIGHBORS(c) do{}while(false)
#endif
/////////////////////////////////////////////////
#undef DBGLOG_FUNCTION
#undef DBGLOG_FUNCTION_P
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_OOM) || \
defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || \
defined(UMM_INTEGRITY_CHECK)
#define DBGLOG_FUNCTION(fmt, ...) ets_uart_printf(fmt, ##__VA_ARGS__)
#else
#define DBGLOG_FUNCTION(fmt, ...) do { (void)fmt; } while(false)
#endif
/////////////////////////////////////////////////
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || defined(UMM_INTEGRITY_CHECK)
#if !defined(DBGLOG_LEVEL) || DBGLOG_LEVEL < 3
// All debug prints in UMM_POISON_CHECK are level 3
#undef DBGLOG_LEVEL
#define DBGLOG_LEVEL 3
#endif
#endif
#if defined(UMM_CRITICAL_METRICS)
struct UMM_TIME_STATS_t {
UMM_TIME_STAT id_malloc;
UMM_TIME_STAT id_realloc;
UMM_TIME_STAT id_free;
#ifdef UMM_INFO
UMM_TIME_STAT id_info;
#endif
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
UMM_TIME_STAT id_poison;
#endif
#ifdef UMM_INTEGRITY_CHECK
UMM_TIME_STAT id_integrity;
#endif
UMM_TIME_STAT id_no_tag;
};
#endif
/////////////////////////////////////////////////
#ifdef DEBUG_ESP_OOM
#define MEMLEAK_DEBUG
// umm_*alloc are not renamed to *alloc
// Assumes umm_malloc.h has already been included.
#define umm_zalloc(s) umm_calloc(1,s)
void* malloc_loc (size_t s, const char* file, int line);
void* calloc_loc (size_t n, size_t s, const char* file, int line);
void* realloc_loc (void* p, size_t s, const char* file, int line);
// *alloc are macro calling *alloc_loc calling+checking umm_*alloc()
// they are defined at the bottom of this file
/////////////////////////////////////////////////
#elif defined(UMM_POISON_CHECK)
void* realloc_loc (void* p, size_t s, const char* file, int line);
void free_loc (void* p, const char* file, int line);
#else // !defined(ESP_DEBUG_OOM)
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
@ -203,12 +631,57 @@ extern char _heap_start[];
#endif /* _UMM_MALLOC_CFG_H */ #endif /* _UMM_MALLOC_CFG_H */
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DEBUG_ESP_OOM #ifdef DEBUG_ESP_OOM
// this must be outside from "#ifndef _UMM_MALLOC_CFG_H" // this must be outside from "#ifndef _UMM_MALLOC_CFG_H"
// because Arduino.h's <cstdlib> does #undef *alloc // because Arduino.h's <cstdlib> does #undef *alloc
// Arduino.h recall us to redefine them // Arduino.h recall us to redefine them
#include <pgmspace.h> #include <pgmspace.h>
#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; malloc_loc(s, mem_debug_file, __LINE__); }) // Reuse pvPort* calls, since they already support passing location information.
#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; calloc_loc(n, s, mem_debug_file, __LINE__); }) void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line);
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; realloc_loc(p, s, mem_debug_file, __LINE__); }) void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line);
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line);
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line);
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line);
#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortMalloc(s, mem_debug_file, __LINE__); })
#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortCalloc(n, s, mem_debug_file, __LINE__); })
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortRealloc(p, s, mem_debug_file, __LINE__); })
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; vPortFree(p, mem_debug_file, __LINE__); })
#else
#define dbg_heap_free(p) free(p)
#endif
#elif defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
#include <pgmspace.h>
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line);
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortRealloc(p, s, mem_debug_file, __LINE__); })
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line);
//C - to be discussed
/*
Problem, I would like to report the file and line number with the umm poison
event as close as possible to the event. The #define method works for malloc,
calloc, and realloc those names are not as generic as free. A #define free
captures too much. Classes with methods called free are included :(
Inline functions would report the address of the inline function in the .h
not where they are called.
Anybody know a trick to make this work?
Create dbg_heap_free() as an alternative for free() when you need a little
more help in debugging the more challenging problems.
*/
#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; vPortFree(p, mem_debug_file, __LINE__); })
#else
#define dbg_heap_free(p) free(p)
#endif /* DEBUG_ESP_OOM */ #endif /* DEBUG_ESP_OOM */
#ifdef __cplusplus
}
#endif

View File

@ -1,81 +0,0 @@
/*
* umm_malloc performance measurments and ESP specifics
*/
#include <stdio.h>
#include <string.h>
#include <pgmspace.h>
#include <core_esp8266_features.h>
#include "umm_performance.h"
#include "umm_stats.h"
extern "C" {
UMM_STATS ummStats = {0, 0, 0, 0};
#ifdef UMM_CRITICAL_PERIOD_ANALYZE
struct _UMM_TIME_STATS time_stats = {
{0xFFFFFFFF, 0U, 0U, 0U},
{0xFFFFFFFF, 0U, 0U, 0U},
{0xFFFFFFFF, 0U, 0U, 0U},
{0xFFFFFFFF, 0U, 0U, 0U} };
bool ICACHE_FLASH_ATTR get_umm_get_perf_data(struct _UMM_TIME_STATS *p, size_t size) {
if (p && sizeof(time_stats) == size) {
uint32_t save_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL);
memcpy(p, &time_stats, size);
xt_wsr_ps(save_ps);
return true;
}
return false;
}
#endif
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_ISR)
/*
Printing from the malloc routines is tricky. Since a lot of library calls
will want to do malloc.
Objective: To be able to print "last gasp" diagnostic messages
when interrupts are disabled and w/o availability of heap resources.
*/
// ROM _putc1, ignores CRs and sends CR/LF for LF, newline.
// Always returns character sent.
int constexpr (*_rom_putc1)(int) = (int (*)(int))0x40001dcc;
void uart_buff_switch(uint8_t);
int _isr_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
int ICACHE_RAM_ATTR _isr_safe_printf_P(const char *fmt, ...) {
#ifdef DEBUG_ESP_PORT
#define VALUE(x) __STRINGIFY(x)
// Preprocessor and compiler together will optimize away the if.
if (strcmp("Serial1", VALUE(DEBUG_ESP_PORT)) == 0) {
uart_buff_switch(1U);
} else {
uart_buff_switch(0U);
}
#else
uart_buff_switch(0U); // Side effect, clears RX FIFO
#endif
/*
To use ets_strlen() and ets_memcpy() safely with PROGMEM, flash storage,
the PROGMEM address must be word (4 bytes) aligned. The destination
address for ets_memcpy must also be word-aligned. We also round the
buf_len up to the nearest word boundary. So that all transfers will be
whole words.
*/
size_t str_len = ets_strlen(fmt);
size_t buf_len = (str_len + 1 + 3) & ~0x03U;
char ram_buf[buf_len] __attribute__ ((aligned(4)));
ets_memcpy(ram_buf, fmt, buf_len);
va_list argPtr;
va_start(argPtr, fmt);
int result = ets_vprintf(_rom_putc1, ram_buf, argPtr);
va_end(argPtr);
return result;
}
#endif
};

View File

@ -1,85 +0,0 @@
/*
* umm_malloc performance measurments and ESP specifics
*/
#ifndef _UMM_PERFORMANCE_H
#define _UMM_PERFORMANCE_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* -D UMM_CRITICAL_PERIOD_ANALYZE :
*
* Build option to collect timing usage data on critical section usage in
* functions: info, malloc, realloc. Collects MIN, MAX, and number of time
* IRQs were disabled at request time. Note, for realloc MAX disabled time
* will not include the time from calling malloc and/or free.
* Examine code for specifics on what info is available and how to access.
*/
// #define UMM_CRITICAL_PERIOD_ANALYZE
/*
Per Devyte, the core currently doesn't support masking a specific interrupt
level. That doesn't mean it can't be implemented, only that at this time
locking is implemented as all or nothing.
https://github.com/esp8266/Arduino/issues/6246#issuecomment-508612609
So for now we default to all, 15.
*/
#ifndef DEFAULT_CRITICAL_SECTION_INTLEVEL
#define DEFAULT_CRITICAL_SECTION_INTLEVEL 15
#endif
#if defined(UMM_CRITICAL_PERIOD_ANALYZE)
// This option adds support for gathering time locked data
typedef struct _TIME_STAT {
uint32_t min;
uint32_t max;
uint32_t start;
uint32_t intlevel;
} time_stat_t;
struct _UMM_TIME_STATS {
time_stat_t id_malloc;
time_stat_t id_realloc;
time_stat_t id_free;
time_stat_t id_info;
};
extern struct _UMM_TIME_STATS time_stats;
bool get_umm_get_perf_data(struct _UMM_TIME_STATS *p, size_t size);
static inline void _critical_entry(time_stat_t *p, uint32_t *saved_ps) {
*saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL);
if (0U != (*saved_ps & 0x0FU)) {
p->intlevel += 1U;
}
p->start = esp_get_cycle_count();
}
static inline void _critical_exit(time_stat_t *p, uint32_t *saved_ps) {
uint32_t elapse = esp_get_cycle_count() - p->start;
if (elapse < p->min)
p->min = elapse;
if (elapse > p->max)
p->max = elapse;
xt_wsr_ps(*saved_ps);
}
#endif
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_ISR)
int _isr_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
#endif
#ifdef __cplusplus
}
#endif
#endif /* _UMM_PERFORMANCE_H */

View File

@ -0,0 +1,241 @@
#if defined(BUILD_UMM_MALLOC_C)
/* poisoning (UMM_POISON_CHECK) {{{ */
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
#define POISON_BYTE (0xa5)
/*
* Yields a size of the poison for the block of size `s`.
* If `s` is 0, returns 0.
*/
static size_t poison_size(size_t s) {
return(s ? (UMM_POISON_SIZE_BEFORE +
sizeof(UMM_POISONED_BLOCK_LEN_TYPE) +
UMM_POISON_SIZE_AFTER)
: 0);
}
/*
* Print memory contents starting from given `ptr`
*/
static void dump_mem ( const unsigned char *ptr, size_t len ) {
while (len--) {
DBGLOG_ERROR(" 0x%.2x", (unsigned int)(*ptr++));
}
}
/*
* Put poison data at given `ptr` and `poison_size`
*/
static void put_poison( unsigned char *ptr, size_t poison_size ) {
memset(ptr, POISON_BYTE, poison_size);
}
/*
* Check poison data at given `ptr` and `poison_size`. `where` is a pointer to
* a string, either "before" or "after", meaning, before or after the block.
*
* If poison is there, returns 1.
* Otherwise, prints the appropriate message, and returns 0.
*/
static int check_poison( const unsigned char *ptr, size_t poison_size,
const char *where) {
size_t i;
int ok = 1;
for (i = 0; i < poison_size; i++) {
if (ptr[i] != POISON_BYTE) {
ok = 0;
break;
}
}
if (!ok) {
DBGLOG_ERROR( "No poison %s block at: 0x%lx, actual data:", where, (unsigned long)ptr);
dump_mem(ptr, poison_size);
DBGLOG_ERROR( "\n" );
}
return ok;
}
/*
* Check if a block is properly poisoned. Must be called only for non-free
* blocks.
*/
static int check_poison_block( umm_block *pblock ) {
int ok = 1;
if (pblock->header.used.next & UMM_FREELIST_MASK) {
DBGLOG_ERROR( "check_poison_block is called for free block 0x%lx\n", (unsigned long)pblock);
} else {
/* the block is used; let's check poison */
unsigned char *pc = (unsigned char *)pblock->body.data;
unsigned char *pc_cur;
pc_cur = pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE);
if (!check_poison(pc_cur, UMM_POISON_SIZE_BEFORE, "before")) {
ok = 0;
goto clean;
}
pc_cur = pc + *((UMM_POISONED_BLOCK_LEN_TYPE *)pc) - UMM_POISON_SIZE_AFTER;
if (!check_poison(pc_cur, UMM_POISON_SIZE_AFTER, "after")) {
ok = 0;
goto clean;
}
}
clean:
return ok;
}
/*
* Takes a pointer returned by actual allocator function (`umm_malloc` or
* `umm_realloc`), puts appropriate poison, and returns adjusted pointer that
* should be returned to the user.
*
* `size_w_poison` is a size of the whole block, including a poison.
*/
static void *get_poisoned( void *v_ptr, size_t size_w_poison ) {
unsigned char *ptr = (unsigned char *)v_ptr;
if (size_w_poison != 0 && ptr != NULL) {
/* Poison beginning and the end of the allocated chunk */
put_poison(ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE),
UMM_POISON_SIZE_BEFORE);
put_poison(ptr + size_w_poison - UMM_POISON_SIZE_AFTER,
UMM_POISON_SIZE_AFTER);
/* Put exact length of the user's chunk of memory */
*(UMM_POISONED_BLOCK_LEN_TYPE *)ptr = (UMM_POISONED_BLOCK_LEN_TYPE)size_w_poison;
/* Return pointer at the first non-poisoned byte */
ptr += sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE;
}
return (void *)ptr;
}
/*
* Takes "poisoned" pointer (i.e. pointer returned from `get_poisoned()`),
* and checks that the poison of this particular block is still there.
*
* Returns unpoisoned pointer, i.e. actual pointer to the allocated memory.
*/
static void *get_unpoisoned( void *v_ptr ) {
unsigned char *ptr = (unsigned char *)v_ptr;
if (ptr != NULL) {
unsigned short int c;
ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
/* Figure out which block we're in. Note the use of truncated division... */
c = (((char *)ptr)-(char *)(&(umm_heap[0])))/sizeof(umm_block);
check_poison_block(&UMM_BLOCK(c));
}
return (void *)ptr;
}
/* }}} */
/* ------------------------------------------------------------------------ */
void *umm_poison_malloc( size_t size ) {
void *ret;
size += poison_size(size);
ret = umm_malloc( size );
ret = get_poisoned(ret, size);
return ret;
}
/* ------------------------------------------------------------------------ */
void *umm_poison_calloc( size_t num, size_t item_size ) {
void *ret;
size_t size = item_size * num;
size += poison_size(size);
ret = umm_malloc(size);
if (NULL != ret)
memset(ret, 0x00, size);
ret = get_poisoned(ret, size);
return ret;
}
/* ------------------------------------------------------------------------ */
void *umm_poison_realloc( void *ptr, size_t size ) {
void *ret;
ptr = get_unpoisoned(ptr);
size += poison_size(size);
ret = umm_realloc( ptr, size );
ret = get_poisoned(ret, size);
return ret;
}
/* ------------------------------------------------------------------------ */
void umm_poison_free( void *ptr ) {
ptr = get_unpoisoned(ptr);
umm_free( ptr );
}
/*
* Iterates through all blocks in the heap, and checks poison for all used
* blocks.
*/
int umm_poison_check(void) {
UMM_CRITICAL_DECL(id_poison);
int ok = 1;
unsigned short int cur;
if (umm_heap == NULL) {
umm_init();
}
UMM_CRITICAL_ENTRY(id_poison);
/* Now iterate through the blocks list */
cur = UMM_NBLOCK(0) & UMM_BLOCKNO_MASK;
while( UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK ) {
if ( !(UMM_NBLOCK(cur) & UMM_FREELIST_MASK) ) {
/* This is a used block (not free), so, check its poison */
ok = check_poison_block(&UMM_BLOCK(cur));
if (!ok){
break;
}
}
cur = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK;
}
UMM_CRITICAL_EXIT(id_poison);
return ok;
}
/* ------------------------------------------------------------------------ */
#endif
#endif // defined(BUILD_UMM_MALLOC_C)

View File

@ -1,36 +0,0 @@
/*
* umm_malloc heap statistics
*/
#ifndef _UMM_STATS_H
#define _UMM_STATS_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct UMM_STATS_t {
unsigned short int free_blocks;
unsigned short int free_blocks_min;
size_t alloc_max_size;
size_t oom_count;
} UMM_STATS;
extern UMM_STATS ummStats;
size_t ICACHE_FLASH_ATTR umm_free_heap_size_min( void );
size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset( void );
inline size_t umm_get_max_alloc_size( void ) {
return ummStats.alloc_max_size;
}
inline size_t umm_get_oom_count( void ) {
return ummStats.oom_count;
}
#ifdef __cplusplus
}
#endif
#endif /* _UMM_STATS_H */

View File

@ -175,16 +175,18 @@ 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.4.0/ESP8266FS-0.4.0.zip
- In your Arduino sketchbook directory, create ``tools`` directory if - In your Arduino sketchbook directory, create ``tools`` directory if
it doesn't exist yet it doesn't exist yet.
- Unpack the tool into ``tools`` directory (the path will look like - Unpack the tool into ``tools`` directory (the path will look like
``<home_dir>/Arduino/tools/ESP8266FS/tool/esp8266fs.jar``) ``<home_dir>/Arduino/tools/ESP8266FS/tool/esp8266fs.jar``)
If upgrading, overwrite the existing JAR file with the newer version. If upgrading, overwrite the existing JAR file with the newer version.
- Restart Arduino IDE - Restart Arduino IDE.
- Open a sketch (or create a new one and save it) - Open a sketch (or create a new one and save it).
- Go to sketch directory (choose Sketch > Show Sketch Folder) - Go to sketch directory (choose Sketch > Show Sketch Folder).
- Create a directory named ``data`` and any files you want in the file - Create a directory named ``data`` and any files you want in the file
system there system there.
- Make sure you have selected a board, port, and closed Serial Monitor - Make sure you have selected a board, port, and closed Serial Monitor.
- If your board requires you to press a button (or other action) to enter
bootload mode for flashing a sketch, do that now.
- Select Tools > ESP8266 Sketch Data Upload. This should start - Select Tools > ESP8266 Sketch Data Upload. This should start
uploading the files into ESP8266 flash file system. When done, IDE uploading the files into ESP8266 flash file system. When done, IDE
status bar will display ``SPIFFS Image Uploaded`` message. status bar will display ``SPIFFS Image Uploaded`` message.

View File

@ -12,6 +12,7 @@ Prerequisites
- Arduino 1.6.8, get it from `Arduino - Arduino 1.6.8, get it from `Arduino
website <https://www.arduino.cc/en/Main/OldSoftwareReleases#previous>`__. website <https://www.arduino.cc/en/Main/OldSoftwareReleases#previous>`__.
- Internet connection - Internet connection
- Python 3 interpreter (Mac/Linux only, Windows installation supplies its own)
Instructions Instructions
~~~~~~~~~~~~ ~~~~~~~~~~~~
@ -46,10 +47,13 @@ Prerequisites
- Python 3.x (https://python.org) - Python 3.x (https://python.org)
- terminal, console, or command prompt (depending on your OS) - terminal, console, or command prompt (depending on your OS)
- Internet connection - Internet connection
- Uninstalling any core version installed via Board Manager
Instructions - Windows 10 Instructions - Windows 10
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
- First, make sure you don't already have the ESP8266 library installed using the Board Manager (see above) - 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.
- Install git for Windows (if not already; see https://git-scm.com/download/win) - Install git for Windows (if not already; see https://git-scm.com/download/win)
@ -130,9 +134,14 @@ Note that you could, in theory install in ``C:\Program Files (x86)\Arduino\hardw
Instructions - Other OS 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.
- Open the console and go to Arduino directory. This can be either your - Open the console and go to Arduino directory. This can be either your
*sketchbook* directory (usually ``<Documents>/Arduino``), or the *sketchbook* directory (usually ``<Documents>/Arduino``), or the
directory of Arduino application itself, the choice is up to you. directory of Arduino application itself, the choice is up to you.
- Clone this repository into hardware/esp8266com/esp8266 directory. - Clone this repository into hardware/esp8266com/esp8266 directory.
Alternatively, clone it elsewhere and create a symlink, if your OS Alternatively, clone it elsewhere and create a symlink, if your OS
supports them. supports them.
@ -186,6 +195,30 @@ Instructions - Other OS
cd esp8266/tools cd esp8266/tools
python3 get.py python3 get.py
If you get an error message stating that python3 is not found, you will need to install it (most modern UNIX-like OSes provide Python 3 as
part of the default install). To install you will need to use ``sudo yum install python3``, ``sudo apt install python3``, or ``brew install python3``
as appropriate. On the Mac you may get an error message like:
.. code:: bash
python3 get.py
Platform: x86_64-apple-darwin
Downloading python3-macosx-placeholder.tar.gz
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/urllib/request.py", line 1317, in do_open
encode_chunked=req.has_header('Transfer-encoding'))
...
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 1117, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
This is because Homebrew on the Mac does not always install the required SSL certificates by default. Install them manually (adjust the Python 3.7 as needed) with:
.. code:: bash
cd "/Applications/Python 3.7/" && sudo "./Install Certificates.command"
- Restart Arduino - Restart Arduino
- When later updating your local library, goto the esp8266 directory and do a git pull - When later updating your local library, goto the esp8266 directory and do a git pull

View File

@ -29,6 +29,8 @@ EEPROM library uses one sector of flash located just after the SPIFFS.
`Three examples <https://github.com/esp8266/Arduino/tree/master/libraries/EEPROM>`__ included. `Three examples <https://github.com/esp8266/Arduino/tree/master/libraries/EEPROM>`__ included.
Note that the sector needs to be re-flashed every time the changed EEPROM data needs to be saved, thus will wear out the flash memory very quickly even if small amounts of data are written. Consider using one of the EEPROM libraries mentioned down below.
I2C (Wire library) I2C (Wire library)
------------------ ------------------
@ -141,14 +143,6 @@ Servo
This library exposes the ability to control RC (hobby) servo motors. It will support up to 24 servos on any available output pin. By default the first 12 servos will use Timer0 and currently this will not interfere with any other support. Servo counts above 12 will use Timer1 and features that use it will be affected. While many RC servo motors will accept the 3.3V IO data pin from a ESP8266, most will not be able to run off 3.3v and will require another power source that matches their specifications. Make sure to connect the grounds between the ESP8266 and the servo motor power supply. This library exposes the ability to control RC (hobby) servo motors. It will support up to 24 servos on any available output pin. By default the first 12 servos will use Timer0 and currently this will not interfere with any other support. Servo counts above 12 will use Timer1 and features that use it will be affected. While many RC servo motors will accept the 3.3V IO data pin from a ESP8266, most will not be able to run off 3.3v and will require another power source that matches their specifications. Make sure to connect the grounds between the ESP8266 and the servo motor power supply.
Improved EEPROM library for ESP (ESP_EEPROM)
--------------------------------------------
An improved EEPROM library for ESPxxxx. Uses flash memory as per the standard ESP EEPROM library but reduces reflash - so reducing wear and improving commit() performance.
As actions on the flash need to stop the interrupts, an EEPROM reflash could noticably affect anything using PWM, etc.
Other libraries (not included with the IDE) Other libraries (not included with the IDE)
------------------------------------------- -------------------------------------------
@ -189,3 +183,5 @@ Libraries that don't rely on low-level access to AVR registers should work well.
- `MFRC522 <https://github.com/miguelbalboa/rfid>`__ - A library for using the Mifare RC522 RFID-tag reader/writer. - `MFRC522 <https://github.com/miguelbalboa/rfid>`__ - A library for using the Mifare RC522 RFID-tag reader/writer.
- `Ping <https://github.com/dancol90/ESP8266Ping>`__ - lets the ESP8266 ping a remote machine. - `Ping <https://github.com/dancol90/ESP8266Ping>`__ - lets the ESP8266 ping a remote machine.
- `AsyncPing <https://github.com/akaJes/AsyncPing>`__ - fully asynchronous Ping library (have full ping statistic and hardware MAC address). - `AsyncPing <https://github.com/akaJes/AsyncPing>`__ - fully asynchronous Ping library (have full ping statistic and hardware MAC address).
- `ESP_EEPROM <https://github.com/jwrw/ESP_EEPROM>`__ - This library writes a new copy of your data when you save (commit) it and keeps track of where in the sector the most recent copy is kept using a bitmap. The flash sector only needs to be erased when there is no more space for copies in the flash sector.
- `EEPROM Rotate <https://github.com/xoseperez/eeprom_rotate>`__ - Instead of using a single sector to persist the data from the emulated EEPROM, this library uses a number of sectors to do so: a sector pool.

View File

@ -646,6 +646,8 @@ Update process - memory view
- the new sketch is now copied "over" the old one. - the new sketch is now copied "over" the old one.
- the new sketch is started. - 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.
.. figure:: update_memory_copy.png .. figure:: update_memory_copy.png
:alt: Memory layout for OTA updates :alt: Memory layout for OTA updates

View File

@ -1,6 +1,42 @@
Reference Reference
========= =========
Interrupts
----------
Interrupts can be used on the ESP8266, but they must be used with care
and have several limitations:
* Interrupt callback functions must be in IRAM, because the flash may be
in the middle of other operations when they occur. Do this by adding
the ``ICACHE_RAM_ATTR`` attribute on the function definition. If this
attribute is not present, the sketch will crash when it attempts to
``attachInterrupt`` with an error message.
.. code:: cpp
ICACHE_RAM_ATTR void gpio_change_handler(void *data) {...
* Interrupts must not call ``delay()`` or ``yield()``, or call any routines
which internally use ``delay()`` or ``yield()`` either.
* Long-running (>1ms) tasks in interrupts will cause instabilty or crashes.
WiFi and other portions of the core can become unstable if interrupts
are blocked by a long-running interrupt. If you have much to do, you can
set a volatile global flag that your main ``loop()`` can check each pass
or use a scheduled function (which will be called outside of the interrupt
context when it is safe) to do long-running work.
* Memory operations can be dangerous and should be avoided in interrupts.
Calls to ``new`` or ``malloc`` should be minimized because they may require
a long running time if memory is fragmented. Calls to ``realloc`` and
``free`` must NEVER be called. Using any routines or objects which call
``free`` or ``realloc`` themselves is also forbidden for the same reason.
This means that ``String``, ``std::string``, ``std::vector`` and other
classes which use contiguous memory that may be resized must be used with
extreme care (ensuring strings aren't changed, vector elements aren't
added, etc.).
Digital IO Digital IO
---------- ----------

View File

@ -21,6 +21,7 @@
#include "Arduino.h" #include "Arduino.h"
#include "EEPROM.h" #include "EEPROM.h"
#include "debug.h"
extern "C" { extern "C" {
#include "c_types.h" #include "c_types.h"
@ -30,7 +31,7 @@ extern "C" {
#include "spi_flash.h" #include "spi_flash.h"
} }
extern "C" uint32_t _FS_end; extern "C" uint32_t _EEPROM_start;
EEPROMClass::EEPROMClass(uint32_t sector) EEPROMClass::EEPROMClass(uint32_t sector)
: _sector(sector) : _sector(sector)
@ -41,7 +42,7 @@ EEPROMClass::EEPROMClass(uint32_t sector)
} }
EEPROMClass::EEPROMClass(void) EEPROMClass::EEPROMClass(void)
: _sector((((uint32_t)&_FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE)) : _sector((((uint32_t)&_EEPROM_start - 0x40200000) / SPI_FLASH_SEC_SIZE))
, _data(0) , _data(0)
, _size(0) , _size(0)
, _dirty(false) , _dirty(false)
@ -49,10 +50,14 @@ EEPROMClass::EEPROMClass(void)
} }
void EEPROMClass::begin(size_t size) { void EEPROMClass::begin(size_t size) {
if (size <= 0) if (size <= 0) {
DEBUGV("EEPROMClass::begin error, size == 0\n");
return; return;
if (size > SPI_FLASH_SEC_SIZE) }
if (size > SPI_FLASH_SEC_SIZE) {
DEBUGV("EEPROMClass::begin error, %d > %d\n", size, SPI_FLASH_SEC_SIZE);
size = SPI_FLASH_SEC_SIZE; size = SPI_FLASH_SEC_SIZE;
}
size = (size + 3) & (~3); size = (size + 3) & (~3);
@ -66,9 +71,9 @@ void EEPROMClass::begin(size_t size) {
_size = size; _size = size;
noInterrupts(); if (!ESP.flashRead(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(_data), _size)) {
spi_flash_read(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(_data), _size); DEBUGV("EEPROMClass::begin flash read failed\n");
interrupts(); }
_dirty = false; //make sure dirty is cleared in case begin() is called 2nd+ time _dirty = false; //make sure dirty is cleared in case begin() is called 2nd+ time
} }
@ -88,19 +93,27 @@ void EEPROMClass::end() {
uint8_t EEPROMClass::read(int const address) { uint8_t EEPROMClass::read(int const address) {
if (address < 0 || (size_t)address >= _size) if (address < 0 || (size_t)address >= _size) {
DEBUGV("EEPROMClass::read error, address %d > %d or %d < 0\n", address, _size, address);
return 0; return 0;
if(!_data) }
if (!_data) {
DEBUGV("EEPROMClass::read without ::begin\n");
return 0; return 0;
}
return _data[address]; return _data[address];
} }
void EEPROMClass::write(int const address, uint8_t const value) { void EEPROMClass::write(int const address, uint8_t const value) {
if (address < 0 || (size_t)address >= _size) if (address < 0 || (size_t)address >= _size) {
DEBUGV("EEPROMClass::write error, address %d > %d or %d < 0\n", address, _size, address);
return; return;
if(!_data) }
if(!_data) {
DEBUGV("EEPROMClass::read without ::begin\n");
return; return;
}
// Optimise _dirty. Only flagged if data written is different. // Optimise _dirty. Only flagged if data written is different.
uint8_t* pData = &_data[address]; uint8_t* pData = &_data[address];
@ -112,7 +125,6 @@ void EEPROMClass::write(int const address, uint8_t const value) {
} }
bool EEPROMClass::commit() { bool EEPROMClass::commit() {
bool ret = false;
if (!_size) if (!_size)
return false; return false;
if(!_dirty) if(!_dirty)
@ -120,16 +132,15 @@ bool EEPROMClass::commit() {
if(!_data) if(!_data)
return false; return false;
noInterrupts(); if (ESP.flashEraseSector(_sector)) {
if(spi_flash_erase_sector(_sector) == SPI_FLASH_RESULT_OK) { if (ESP.flashWrite(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(_data), _size)) {
if(spi_flash_write(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(_data), _size) == SPI_FLASH_RESULT_OK) {
_dirty = false; _dirty = false;
ret = true; return true;
} }
} }
interrupts();
return ret; DEBUGV("EEPROMClass::commit failed\n");
return false;
} }
uint8_t * EEPROMClass::getDataPtr() { uint8_t * EEPROMClass::getDataPtr() {

View File

@ -14,7 +14,7 @@ byte value;
void setup() { void setup() {
// initialize serial and wait for port to open: // initialize serial and wait for port to open:
Serial.begin(9600); Serial.begin(115200);
while (!Serial) { while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only ; // wait for serial port to connect. Needed for Leonardo only
} }

View File

@ -13,6 +13,7 @@
int addr = 0; int addr = 0;
void setup() { void setup() {
Serial.begin(115200);
EEPROM.begin(512); EEPROM.begin(512);
} }
@ -33,7 +34,11 @@ void loop() {
addr = addr + 1; addr = addr + 1;
if (addr == 512) { if (addr == 512) {
addr = 0; addr = 0;
EEPROM.commit(); if (EEPROM.commit()) {
Serial.println("EEPROM successfully committed");
} else {
Serial.println("ERROR! EEPROM commit failed");
}
} }
delay(100); delay(100);

View File

@ -113,8 +113,8 @@ protected:
* constructor * constructor
*/ */
HTTPClient::HTTPClient() HTTPClient::HTTPClient()
: _client(nullptr), _userAgent(F("ESP8266HTTPClient"))
{ {
_client = nullptr;
#if HTTPCLIENT_1_1_COMPATIBLE #if HTTPCLIENT_1_1_COMPATIBLE
_tcpDeprecated.reset(nullptr); _tcpDeprecated.reset(nullptr);
#endif #endif
@ -536,8 +536,8 @@ void HTTPClient::setTimeout(uint16_t timeout)
bool HTTPClient::setURL(String url) bool HTTPClient::setURL(String url)
{ {
// if the new location is only a path then only update the URI // if the new location is only a path then only update the URI
if (_location.startsWith("/")) { if (url && url[0] == '/') {
_uri = _location; _uri = url;
clear(); clear();
return true; return true;
} }
@ -1294,21 +1294,21 @@ int HTTPClient::handleHeaderResponse()
String headerValue = headerLine.substring(headerLine.indexOf(':') + 1); String headerValue = headerLine.substring(headerLine.indexOf(':') + 1);
headerValue.trim(); headerValue.trim();
if(headerName.equalsIgnoreCase("Content-Length")) { if(headerName.equalsIgnoreCase(F("Content-Length"))) {
_size = headerValue.toInt(); _size = headerValue.toInt();
} }
if(_canReuse && headerName.equalsIgnoreCase("Connection")) { if(_canReuse && headerName.equalsIgnoreCase(F("Connection"))) {
if(headerValue.indexOf("close") >= 0 && headerValue.indexOf("keep-alive") < 0) { if(headerValue.indexOf("close") >= 0 && headerValue.indexOf("keep-alive") < 0) {
_canReuse = false; _canReuse = false;
} }
} }
if(headerName.equalsIgnoreCase("Transfer-Encoding")) { if(headerName.equalsIgnoreCase(F("Transfer-Encoding"))) {
transferEncoding = headerValue; transferEncoding = headerValue;
} }
if(headerName.equalsIgnoreCase("Location")) { if(headerName.equalsIgnoreCase(F("Location"))) {
_location = headerValue; _location = headerValue;
} }
@ -1334,7 +1334,7 @@ int HTTPClient::handleHeaderResponse()
if(transferEncoding.length() > 0) { if(transferEncoding.length() > 0) {
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] Transfer-Encoding: %s\n", transferEncoding.c_str()); DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] Transfer-Encoding: %s\n", transferEncoding.c_str());
if(transferEncoding.equalsIgnoreCase("chunked")) { if(transferEncoding.equalsIgnoreCase(F("chunked"))) {
_transferEncoding = HTTPC_TE_CHUNKED; _transferEncoding = HTTPC_TE_CHUNKED;
} else { } else {
return HTTPC_ERROR_ENCODING; return HTTPC_ERROR_ENCODING;

View File

@ -242,7 +242,7 @@ protected:
String _uri; String _uri;
String _protocol; String _protocol;
String _headers; String _headers;
String _userAgent = "ESP8266HTTPClient"; String _userAgent;
String _base64Authorization; String _base64Authorization;
/// Response handling /// Response handling

View File

@ -1,6 +1,6 @@
name=ESP8266HTTPUpdateServer name=ESP8266HTTPUpdateServer
version=1.0 version=1.0
author=Ivan Grokhotkov, Miguel Ángel Ajo author=Ivan Grokhotkov, Miguel Angel Ajo
maintainer=Ivan Grokhtkov <ivan@esp8266.com> maintainer=Ivan Grokhtkov <ivan@esp8266.com>
sentence=Simple HTTP Update server based on the ESP8266WebServer sentence=Simple HTTP Update server based on the ESP8266WebServer
paragraph=The library accepts HTTP post requests to the /update url, and updates the ESP8266 firmware. paragraph=The library accepts HTTP post requests to the /update url, and updates the ESP8266 firmware.

View File

@ -3,6 +3,9 @@
#include <WiFiServer.h> #include <WiFiServer.h>
#include <ESP8266WebServer.h> #include <ESP8266WebServer.h>
#include <WiFiUdp.h> #include <WiFiUdp.h>
#include <flash_hal.h>
#include <FS.h>
#include <LittleFS.h>
#include "StreamString.h" #include "StreamString.h"
#include "ESP8266HTTPUpdateServer.h" #include "ESP8266HTTPUpdateServer.h"
@ -10,11 +13,25 @@ namespace esp8266httpupdateserver {
using namespace esp8266webserver; using namespace esp8266webserver;
static const char serverIndex[] PROGMEM = static const char serverIndex[] PROGMEM =
R"(<html><body><form method='POST' action='' enctype='multipart/form-data'> R"(<!DOCTYPE html>
<input type='file' name='update'> <html lang='en'>
<input type='submit' value='Update'> <head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'/>
</head>
<body>
<form method='POST' action='' enctype='multipart/form-data'>
Firmware:<br>
<input type='file' accept='.bin' name='firmware'>
<input type='submit' value='Update Firmware'>
</form> </form>
</body></html>)"; <form method='POST' action='' enctype='multipart/form-data'>
FileSystem:<br>
<input type='file' accept='.bin' name='filesystem'>
<input type='submit' value='Update FileSystem'>
</form>
</body>
</html>)";
static const char successResponse[] PROGMEM = static const char successResponse[] PROGMEM =
"<META http-equiv=\"refresh\" content=\"15;URL=/\">Update Success! Rebooting..."; "<META http-equiv=\"refresh\" content=\"15;URL=/\">Update Success! Rebooting...";
@ -75,10 +92,19 @@ void ESP8266HTTPUpdateServerTemplate<ServerType>::setup(ESP8266WebServerTemplate
WiFiUDP::stopAll(); WiFiUDP::stopAll();
if (_serial_output) if (_serial_output)
Serial.printf("Update: %s\n", upload.filename.c_str()); 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();
if (!Update.begin(fsSize, U_FS)){//start with max available size
if (_serial_output) Update.printError(Serial);
}
} else {
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if(!Update.begin(maxSketchSpace)){//start with max available size if (!Update.begin(maxSketchSpace, U_FLASH)){//start with max available size
_setUpdaterError(); _setUpdaterError();
} }
}
} else if(_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()){ } else if(_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()){
if (_serial_output) Serial.printf("."); if (_serial_output) Serial.printf(".");
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){ if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){

View File

@ -238,7 +238,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
DEBUG_OUTPUT.println(headerValue); DEBUG_OUTPUT.println(headerValue);
#endif #endif
if (headerName.equalsIgnoreCase("Host")){ if (headerName.equalsIgnoreCase(F("Host"))){
_hostHeader = headerValue; _hostHeader = headerValue;
} }
} }

View File

@ -85,8 +85,9 @@ void setup() {
Serial.swap(); Serial.swap();
// Hardware serial is now on RX:GPIO13 TX:GPIO15 // Hardware serial is now on RX:GPIO13 TX:GPIO15
// use SoftwareSerial on regular RX(3)/TX(1) for logging // use SoftwareSerial on regular RX(3)/TX(1) for logging
logger = new SoftwareSerial(3, 1); logger = new SoftwareSerial();
logger->begin(BAUD_LOGGER); logger->begin(BAUD_LOGGER, 3, 1);
logger->enableIntTx(false);
logger->println("\n\nUsing SoftwareSerial for logging"); logger->println("\n\nUsing SoftwareSerial for logging");
#else #else
logger->begin(BAUD_LOGGER); logger->begin(BAUD_LOGGER);

View File

@ -49,24 +49,24 @@ extern "C" {
* @param p Print interface * @param p Print interface
*/ */
void ESP8266WiFiClass::printDiag(Print& p) { void ESP8266WiFiClass::printDiag(Print& p) {
const char* modes[] = { "NULL", "STA", "AP", "STA+AP" }; const char* const modes[] = { "NULL", "STA", "AP", "STA+AP" };
p.print("Mode: "); p.print(F("Mode: "));
p.println(modes[wifi_get_opmode()]); p.println(modes[wifi_get_opmode()]);
const char* phymodes[] = { "", "B", "G", "N" }; const char* const phymodes[] = { "", "B", "G", "N" };
p.print("PHY mode: "); p.print(F("PHY mode: "));
p.println(phymodes[(int) wifi_get_phy_mode()]); p.println(phymodes[(int) wifi_get_phy_mode()]);
p.print("Channel: "); p.print(F("Channel: "));
p.println(wifi_get_channel()); p.println(wifi_get_channel());
p.print("AP id: "); p.print(F("AP id: "));
p.println(wifi_station_get_current_ap_id()); p.println(wifi_station_get_current_ap_id());
p.print("Status: "); p.print(F("Status: "));
p.println(wifi_station_get_connect_status()); p.println(wifi_station_get_connect_status());
p.print("Auto connect: "); p.print(F("Auto connect: "));
p.println(wifi_station_get_auto_connect()); p.println(wifi_station_get_auto_connect());
struct station_config conf; struct station_config conf;
@ -75,22 +75,14 @@ void ESP8266WiFiClass::printDiag(Print& p) {
char ssid[33]; //ssid can be up to 32chars, => plus null term char ssid[33]; //ssid can be up to 32chars, => plus null term
memcpy(ssid, conf.ssid, sizeof(conf.ssid)); memcpy(ssid, conf.ssid, sizeof(conf.ssid));
ssid[32] = 0; //nullterm in case of 32 char ssid ssid[32] = 0; //nullterm in case of 32 char ssid
p.printf_P(PSTR("SSID (%d): %s\n"), strlen(ssid), ssid);
p.print("SSID (");
p.print(strlen(ssid));
p.print("): ");
p.println(ssid);
char passphrase[65]; char passphrase[65];
memcpy(passphrase, conf.password, sizeof(conf.password)); memcpy(passphrase, conf.password, sizeof(conf.password));
passphrase[64] = 0; passphrase[64] = 0;
p.printf_P(PSTR("Passphrase (%d): %s\n"), strlen(passphrase), passphrase);
p.print("Passphrase ("); p.print(F("BSSID set: "));
p.print(strlen(passphrase));
p.print("): ");
p.println(passphrase);
p.print("BSSID set: ");
p.println(conf.bssid_set); p.println(conf.bssid_set);
} }

View File

@ -43,7 +43,7 @@ ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void)
{ {
} }
#ifdef HTTPUPDATE_1_2_COMPATIBLE #if HTTPUPDATE_1_2_COMPATIBLE
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion,
const String& httpsFingerprint, bool reboot) const String& httpsFingerprint, bool reboot)
{ {
@ -94,7 +94,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::update(WiFiClient& client, const String& url
return handleUpdate(http, currentVersion, false); return handleUpdate(http, currentVersion, false);
} }
#ifdef HTTPUPDATE_1_2_COMPATIBLE #if HTTPUPDATE_1_2_COMPATIBLE
HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint) HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint)
{ {
HTTPClient http; HTTPClient http;
@ -133,7 +133,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(WiFiClient& client, const Strin
return handleUpdate(http, currentVersion, true); return handleUpdate(http, currentVersion, true);
} }
#ifdef HTTPUPDATE_1_2_COMPATIBLE #if HTTPUPDATE_1_2_COMPATIBLE
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion, HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion,
bool https, const String& httpsFingerprint, bool reboot) bool https, const String& httpsFingerprint, bool reboot)
{ {
@ -263,6 +263,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String&
http.setTimeout(_httpClientTimeout); http.setTimeout(_httpClientTimeout);
http.setFollowRedirects(_followRedirects); http.setFollowRedirects(_followRedirects);
http.setUserAgent(F("ESP8266-http-Update")); http.setUserAgent(F("ESP8266-http-Update"));
http.addHeader(F("x-ESP8266-Chip-ID"), String(ESP.getChipId()));
http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress()); http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress());
http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress()); http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress());
http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace())); http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace()));
@ -388,7 +389,11 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String&
DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n"); DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n");
http.end(); http.end();
#ifdef ATOMIC_FS_UPDATE
if(_rebootOnUpdate) {
#else
if(_rebootOnUpdate && !spiffs) { if(_rebootOnUpdate && !spiffs) {
#endif
ESP.restart(); ESP.restart();
} }

View File

@ -26,14 +26,16 @@
#ifndef ESP8266HTTPUPDATE_H_ #ifndef ESP8266HTTPUPDATE_H_
#define ESP8266HTTPUPDATE_H_ #define ESP8266HTTPUPDATE_H_
#define HTTPUPDATE_1_2_COMPATIBLE
#include <Arduino.h> #include <Arduino.h>
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <WiFiClient.h> #include <WiFiClient.h>
#include <WiFiUdp.h> #include <WiFiUdp.h>
#include <ESP8266HTTPClient.h> #include <ESP8266HTTPClient.h>
#ifndef HTTPUPDATE_1_2_COMPATIBLE
#define HTTPUPDATE_1_2_COMPATIBLE HTTPCLIENT_1_1_COMPATIBLE
#endif
#ifdef DEBUG_ESP_HTTP_UPDATE #ifdef DEBUG_ESP_HTTP_UPDATE
#ifdef DEBUG_ESP_PORT #ifdef DEBUG_ESP_PORT
#define DEBUG_HTTP_UPDATE(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ ) #define DEBUG_HTTP_UPDATE(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ )
@ -85,7 +87,7 @@ public:
_ledOn = ledOn; _ledOn = ledOn;
} }
#ifdef HTTPUPDATE_1_2_COMPATIBLE #if HTTPUPDATE_1_2_COMPATIBLE
// This function is deprecated, use rebootOnUpdate and the next one instead // This function is deprecated, use rebootOnUpdate and the next one instead
t_httpUpdate_return update(const String& url, const String& currentVersion, t_httpUpdate_return update(const String& url, const String& currentVersion,
const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
@ -97,7 +99,7 @@ public:
#endif #endif
t_httpUpdate_return update(WiFiClient& client, const String& url, const String& currentVersion = ""); t_httpUpdate_return update(WiFiClient& client, const String& url, const String& currentVersion = "");
#ifdef HTTPUPDATE_1_2_COMPATIBLE #if HTTPUPDATE_1_2_COMPATIBLE
// This function is deprecated, use one of the overloads below along with rebootOnUpdate // This function is deprecated, use one of the overloads below along with rebootOnUpdate
t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion, t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion,
bool https, const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); bool https, const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
@ -112,7 +114,7 @@ public:
t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/", t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/",
const String& currentVersion = ""); const String& currentVersion = "");
#ifdef HTTPUPDATE_1_2_COMPATIBLE #if HTTPUPDATE_1_2_COMPATIBLE
// This function is deprecated, use rebootOnUpdate and the next one instead // This function is deprecated, use rebootOnUpdate and the next one instead
t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion,
const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); const String& httpsFingerprint, bool reboot) __attribute__((deprecated));

View File

@ -1,10 +1,10 @@
#include <ESP8266mDNS.h> #include <ESP8266mDNS.h>
/* /*
* MDNS responder global instance MDNS responder global instance
*
* Class type that is instantiated depends on the type mapping in ESP8266mDNS.h Class type that is instantiated depends on the type mapping in ESP8266mDNS.h
*/ */
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
MDNSResponder MDNS; MDNSResponder MDNS;
#endif #endif

View File

@ -47,10 +47,10 @@
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
// Maps the implementation to use to the global namespace type // Maps the implementation to use to the global namespace type
//using MDNSResponder = Legacy_MDNSResponder::MDNSResponder; //legacy //using MDNSResponder = Legacy_MDNSResponder::MDNSResponder; //legacy
using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; //new using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; //new
extern MDNSResponder MDNS; extern MDNSResponder MDNS;
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,26 @@
/* /*
ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) ESP8266 Multicast DNS (port of CC3000 Multicast DNS library)
Version 1.1 Version 1.1
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
This is a simple implementation of multicast DNS query support for an Arduino This is a simple implementation of multicast DNS query support for an Arduino
running on ESP8266 chip. Only support for resolving address queries is currently running on ESP8266 chip. Only support for resolving address queries is currently
implemented. implemented.
Requirements: Requirements:
- ESP8266WiFi library - ESP8266WiFi library
Usage: Usage:
- Include the ESP8266 Multicast DNS library in the sketch. - Include the ESP8266 Multicast DNS library in the sketch.
- Call the begin method in the sketch's setup and provide a domain name (without - Call the begin method in the sketch's setup and provide a domain name (without
the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the
Adafruit CC3000 class instance. Optionally provide a time to live (in seconds) Adafruit CC3000 class instance. Optionally provide a time to live (in seconds)
for the DNS record--the default is 1 hour. for the DNS record--the default is 1 hour.
- Call the update method in each iteration of the sketch's loop function. - Call the update method in each iteration of the sketch's loop function.
License (MIT license): License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
@ -54,28 +54,33 @@ License (MIT license):
class UdpContext; class UdpContext;
namespace Legacy_MDNSResponder { namespace Legacy_MDNSResponder
{
struct MDNSService; struct MDNSService;
struct MDNSTxt; struct MDNSTxt;
struct MDNSAnswer; struct MDNSAnswer;
class MDNSResponder { class MDNSResponder
{
public: public:
MDNSResponder(); MDNSResponder();
~MDNSResponder(); ~MDNSResponder();
bool begin(const char* hostName); bool begin(const char* hostName);
bool begin(const String& hostName) { bool begin(const String& hostName)
{
return begin(hostName.c_str()); return begin(hostName.c_str());
} }
//for compatibility //for compatibility
bool begin(const char* hostName, IPAddress ip, uint32_t ttl=120){ bool begin(const char* hostName, IPAddress ip, uint32_t ttl = 120)
{
(void) ip; (void) ip;
(void) ttl; (void) ttl;
return begin(hostName); return begin(hostName);
} }
bool begin(const String& hostName, IPAddress ip, uint32_t ttl=120) { bool begin(const String& hostName, IPAddress ip, uint32_t ttl = 120)
{
return begin(hostName.c_str(), ip, ttl); return begin(hostName.c_str(), ip, ttl);
} }
/* Application should call this whenever AP is configured/disabled */ /* Application should call this whenever AP is configured/disabled */
@ -83,39 +88,47 @@ public:
void update(); void update();
void addService(char *service, char *proto, uint16_t port); void addService(char *service, char *proto, uint16_t port);
void addService(const char *service, const char *proto, uint16_t port){ void addService(const char *service, const char *proto, uint16_t port)
{
addService((char *)service, (char *)proto, port); addService((char *)service, (char *)proto, port);
} }
void addService(const String& service, const String& proto, uint16_t port){ void addService(const String& service, const String& proto, uint16_t port)
{
addService(service.c_str(), proto.c_str(), port); addService(service.c_str(), proto.c_str(), port);
} }
bool addServiceTxt(char *name, char *proto, char * key, char * value); bool addServiceTxt(char *name, char *proto, char * key, char * value);
bool addServiceTxt(const char *name, const char *proto, const char *key,const char * value){ bool addServiceTxt(const char *name, const char *proto, const char *key, const char * value)
{
return addServiceTxt((char *)name, (char *)proto, (char *)key, (char *)value); return addServiceTxt((char *)name, (char *)proto, (char *)key, (char *)value);
} }
bool addServiceTxt(const String& name, const String& proto, const String& key, const String& value){ bool addServiceTxt(const String& name, const String& proto, const String& key, const String& value)
{
return addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str()); return addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str());
} }
int queryService(char *service, char *proto); int queryService(char *service, char *proto);
int queryService(const char *service, const char *proto){ int queryService(const char *service, const char *proto)
{
return queryService((char *)service, (char *)proto); return queryService((char *)service, (char *)proto);
} }
int queryService(const String& service, const String& proto){ int queryService(const String& service, const String& proto)
{
return queryService(service.c_str(), proto.c_str()); return queryService(service.c_str(), proto.c_str());
} }
String hostname(int idx); String hostname(int idx);
IPAddress IP(int idx); IPAddress IP(int idx);
uint16_t port(int idx); uint16_t port(int idx);
void enableArduino(uint16_t port, bool auth=false); void enableArduino(uint16_t port, bool auth = false);
void setInstanceName(String name); void setInstanceName(String name);
void setInstanceName(const char * name){ void setInstanceName(const char * name)
{
setInstanceName(String(name)); setInstanceName(String(name));
} }
void setInstanceName(char * name){ void setInstanceName(char * name)
{
setInstanceName(String(name)); setInstanceName(String(name));
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,103 +1,103 @@
/* /*
* LEAmDNS.h LEAmDNS.h
* (c) 2018, LaborEtArs (c) 2018, LaborEtArs
*
* Version 0.9 beta Version 0.9 beta
*
* Some notes (from LaborEtArs, 2018): Some notes (from LaborEtArs, 2018):
* Essentially, this is an rewrite of the original EPS8266 Multicast DNS code (ESP8266mDNS). Essentially, this is an rewrite of the original EPS8266 Multicast DNS code (ESP8266mDNS).
* The target of this rewrite was to keep the existing interface as stable as possible while The target of this rewrite was to keep the existing interface as stable as possible while
* adding and extending the supported set of mDNS features. adding and extending the supported set of mDNS features.
* A lot of the additions were basicly taken from Erik Ekman's lwIP mdns app code. A lot of the additions were basicly taken from Erik Ekman's lwIP mdns app code.
*
* Supported mDNS features (in some cases somewhat limited): Supported mDNS features (in some cases somewhat limited):
* - Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service - Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service
* - Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented - Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented
* - Probing host and service domains for uniqueness in the local network - Probing host and service domains for uniqueness in the local network
* - Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak) - Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak)
* - Announcing available services after successful probing - Announcing available services after successful probing
* - Using fixed service TXT items or - Using fixed service TXT items or
* - Using dynamic service TXT items for presented services (via callback) - Using dynamic service TXT items for presented services (via callback)
* - Remove services (and un-announcing them to the observers by sending goodbye-messages) - Remove services (and un-announcing them to the observers by sending goodbye-messages)
* - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period) - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period)
* - Dynamic queries for DNS-SD services with cached and updated answers and user notifications - Dynamic queries for DNS-SD services with cached and updated answers and user notifications
*
*
* Usage: Usage:
* In most cases, this implementation should work as a 'drop-in' replacement for the original In most cases, this implementation should work as a 'drop-in' replacement for the original
* ESP8266 Multicast DNS code. Adjustments to the existing code would only be needed, if some ESP8266 Multicast DNS code. Adjustments to the existing code would only be needed, if some
* of the new features should be used. of the new features should be used.
*
* For presenting services: For presenting services:
* In 'setup()': In 'setup()':
* Install a callback for the probing of host (and service) domains via 'MDNS.setProbeResultCallback(probeResultCallback, &userData);' Install a callback for the probing of host (and service) domains via 'MDNS.setProbeResultCallback(probeResultCallback, &userData);'
* Register DNS-SD services with 'MDNSResponder::hMDNSService hService = MDNS.addService("MyESP", "http", "tcp", 5000);' Register DNS-SD services with 'MDNSResponder::hMDNSService hService = MDNS.addService("MyESP", "http", "tcp", 5000);'
* (Install additional callbacks for the probing of these service domains via 'MDNS.setServiceProbeResultCallback(hService, probeResultCallback, &userData);') (Install additional callbacks for the probing of these service domains via 'MDNS.setServiceProbeResultCallback(hService, probeResultCallback, &userData);')
* Add service TXT items with 'MDNS.addServiceTxt(hService, "c#", "1");' or by installing a service TXT callback Add service TXT items with 'MDNS.addServiceTxt(hService, "c#", "1");' or by installing a service TXT callback
* using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service specific using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service specific
* 'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);' 'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);'
* Call MDNS.begin("MyHostname"); Call MDNS.begin("MyHostname");
*
* In 'probeResultCallback(MDNSResponder* p_MDNSResponder, const char* p_pcDomain, MDNSResponder:hMDNSService p_hService, bool p_bProbeResult, void* p_pUserdata)': In 'probeResultCallback(MDNSResponder* p_MDNSResponder, const char* p_pcDomain, MDNSResponder:hMDNSService p_hService, bool p_bProbeResult, void* p_pUserdata)':
* Check the probe result and update the host or service domain name if the probe failed Check the probe result and update the host or service domain name if the probe failed
*
* In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, void* p_pUserdata)': In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, void* p_pUserdata)':
* Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, "c#", "1");' Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, "c#", "1");'
*
* In loop(): In loop():
* Call 'MDNS.update();' Call 'MDNS.update();'
*
*
* For querying services: For querying services:
* Static: Static:
* Call 'uint32_t u32AnswerCount = MDNS.queryService("http", "tcp");' Call 'uint32_t u32AnswerCount = MDNS.queryService("http", "tcp");'
* Iterate answers by: 'for (uint32_t u=0; u<u32AnswerCount; ++u) { const char* pHostname = MDNS.answerHostname(u); }' Iterate answers by: 'for (uint32_t u=0; u<u32AnswerCount; ++u) { const char* pHostname = MDNS.answerHostname(u); }'
* You should call MDNS.removeQuery() sometimes later (when the answers are nott needed anymore) You should call MDNS.removeQuery() sometimes later (when the answers are nott needed anymore)
*
* Dynamic: Dynamic:
* Install a dynamic query by calling 'DNSResponder::hMDNSServiceQuery hServiceQuery = MDNS.installServiceQuery("http", "tcp", serviceQueryCallback, &userData);' Install a dynamic query by calling 'DNSResponder::hMDNSServiceQuery hServiceQuery = MDNS.installServiceQuery("http", "tcp", serviceQueryCallback, &userData);'
* The callback 'serviceQueryCallback(MDNSResponder* p_MDNSResponder, const hMDNSServiceQuery p_hServiceQuery, uint32_t p_u32AnswerIndex, The callback 'serviceQueryCallback(MDNSResponder* p_MDNSResponder, const hMDNSServiceQuery p_hServiceQuery, uint32_t p_u32AnswerIndex,
* enuServiceQueryAnswerType p_ServiceQueryAnswerType, bool p_bSetContent, void* p_pUserdata)' enuServiceQueryAnswerType p_ServiceQueryAnswerType, bool p_bSetContent, void* p_pUserdata)'
* is called for any change in the answer set. is called for any change in the answer set.
* Call 'MDNS.removeServiceQuery(hServiceQuery);' when the answers are not needed anymore Call 'MDNS.removeServiceQuery(hServiceQuery);' when the answers are not needed anymore
*
*
* Reference: Reference:
* Used mDNS messages: Used mDNS messages:
* A (0x01): eg. esp8266.local A OP TTL 123.456.789.012 A (0x01): eg. esp8266.local A OP TTL 123.456.789.012
* AAAA (0x1C): eg. esp8266.local AAAA OP TTL 1234:5678::90 AAAA (0x1C): eg. esp8266.local AAAA OP TTL 1234:5678::90
* PTR (0x0C, srv name): eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local PTR (0x0C, srv name): eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local
* PTR (0x0C, srv type): eg. _services._dns-sd._udp.local PTR OP TTL _http._tcp.local PTR (0x0C, srv type): eg. _services._dns-sd._udp.local PTR OP TTL _http._tcp.local
* PTR (0x0C, IP4): eg. 012.789.456.123.in-addr.arpa PTR OP TTL esp8266.local PTR (0x0C, IP4): eg. 012.789.456.123.in-addr.arpa PTR OP TTL esp8266.local
* PTR (0x0C, IP6): eg. 90.0.0.0.0.0.0.0.0.0.0.0.78.56.34.12.ip6.arpa PTR OP TTL esp8266.local PTR (0x0C, IP6): eg. 90.0.0.0.0.0.0.0.0.0.0.0.78.56.34.12.ip6.arpa PTR OP TTL esp8266.local
* SRV (0x21): eg. MyESP._http._tcp.local SRV OP TTL PRIORITY WEIGHT PORT esp8266.local SRV (0x21): eg. MyESP._http._tcp.local SRV OP TTL PRIORITY WEIGHT PORT esp8266.local
* TXT (0x10): eg. MyESP._http._tcp.local TXT OP TTL c#=1 TXT (0x10): eg. MyESP._http._tcp.local TXT OP TTL c#=1
*
* Some NOT used message types: Some NOT used message types:
* OPT (0x29): eDNS OPT (0x29): eDNS
* NSEC (0x2F): DNSSEC NSEC (0x2F): DNSSEC
*
*
* License (MIT license): License (MIT license):
* Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. THE SOFTWARE.
*
*/ */
#ifndef MDNS_H #ifndef MDNS_H
#define MDNS_H #define MDNS_H
@ -115,12 +115,14 @@
#include "ESP8266WiFi.h" #include "ESP8266WiFi.h"
namespace esp8266 { namespace esp8266
{
/** /**
* LEAmDNS LEAmDNS
*/ */
namespace MDNSImplementation { namespace MDNSImplementation
{
//this should be defined at build time //this should be defined at build time
#ifndef ARDUINO_BOARD #ifndef ARDUINO_BOARD
@ -132,41 +134,42 @@ namespace MDNSImplementation {
#ifdef MDNS_IP4_SUPPORT #ifdef MDNS_IP4_SUPPORT
#define MDNS_IP4_SIZE 4 #define MDNS_IP4_SIZE 4
#endif #endif
#ifdef MDNS_IP6_SUPPORT #ifdef MDNS_IP6_SUPPORT
#define MDNS_IP6_SIZE 16 #define MDNS_IP6_SIZE 16
#endif #endif
/* /*
* Maximum length for all service txts for one service Maximum length for all service txts for one service
*/ */
#define MDNS_SERVICE_TXT_MAXLENGTH 1300 #define MDNS_SERVICE_TXT_MAXLENGTH 1300
/* /*
* Maximum length for a full domain name eg. MyESP._http._tcp.local Maximum length for a full domain name eg. MyESP._http._tcp.local
*/ */
#define MDNS_DOMAIN_MAXLENGTH 256 #define MDNS_DOMAIN_MAXLENGTH 256
/* /*
* Maximum length of on label in a domain name (length info fits into 6 bits) Maximum length of on label in a domain name (length info fits into 6 bits)
*/ */
#define MDNS_DOMAIN_LABEL_MAXLENGTH 63 #define MDNS_DOMAIN_LABEL_MAXLENGTH 63
/* /*
* Maximum length of a service name eg. http Maximum length of a service name eg. http
*/ */
#define MDNS_SERVICE_NAME_LENGTH 15 #define MDNS_SERVICE_NAME_LENGTH 15
/* /*
* Maximum length of a service protocol name eg. tcp Maximum length of a service protocol name eg. tcp
*/ */
#define MDNS_SERVICE_PROTOCOL_LENGTH 3 #define MDNS_SERVICE_PROTOCOL_LENGTH 3
/* /*
* Default timeout for static service queries Default timeout for static service queries
*/ */
#define MDNS_QUERYSERVICES_WAIT_TIME 1000 #define MDNS_QUERYSERVICES_WAIT_TIME 1000
/** /**
* MDNSResponder MDNSResponder
*/ */
class MDNSResponder { class MDNSResponder
{
public: public:
/* INTERFACE */ /* INTERFACE */
MDNSResponder(void); MDNSResponder(void);
@ -177,7 +180,10 @@ public:
// (probing, announcing, responding, ...) // (probing, announcing, responding, ...)
// if interfaceAddress is not specified, default interface is STA, or AP when STA is not set // if interfaceAddress is not specified, default interface is STA, or AP when STA is not set
bool begin(const char* p_pcHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/); bool begin(const char* p_pcHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/);
bool begin(const String& p_strHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/) {return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL);} bool begin(const String& p_strHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/)
{
return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL);
}
// Finish MDNS processing // Finish MDNS processing
bool close(void); bool close(void);
@ -189,7 +195,7 @@ public:
bool setHostname(String p_strHostname); bool setHostname(String p_strHostname);
/** /**
* hMDNSService (opaque handle to access the service) hMDNSService (opaque handle to access the service)
*/ */
typedef const void* hMDNSService; typedef const void* hMDNSService;
@ -218,12 +224,18 @@ public:
//for compatibility //for compatibility
//Warning: this has the side effect of changing the hostname. //Warning: this has the side effect of changing the hostname.
//TODO: implement instancename different from hostname //TODO: implement instancename different from hostname
void setInstanceName(const char* p_pcHostname) {setHostname(p_pcHostname);} void setInstanceName(const char* p_pcHostname)
{
setHostname(p_pcHostname);
}
// for esp32 compatibilty // for esp32 compatibilty
void setInstanceName(const String& s_pcHostname) {setInstanceName(s_pcHostname.c_str());} void setInstanceName(const String& s_pcHostname)
{
setInstanceName(s_pcHostname.c_str());
}
/** /**
* hMDNSTxt (opaque handle to access the TXT items) hMDNSTxt (opaque handle to access the TXT items)
*/ */
typedef void* hMDNSTxt; typedef void* hMDNSTxt;
@ -270,8 +282,8 @@ public:
String p_strValue); String p_strValue);
/** /**
* MDNSDynamicServiceTxtCallbackFn MDNSDynamicServiceTxtCallbackFn
* Callback function for dynamic MDNS TXT items Callback function for dynamic MDNS TXT items
*/ */
typedef std::function<void(const hMDNSService p_hService)> MDNSDynamicServiceTxtCallbackFunc; typedef std::function<void(const hMDNSService p_hService)> MDNSDynamicServiceTxtCallbackFunc;
@ -331,14 +343,15 @@ public:
uint16_t port(const uint32_t p_u32AnswerIndex); uint16_t port(const uint32_t p_u32AnswerIndex);
/** /**
* hMDNSServiceQuery (opaque handle to access dynamic service queries) hMDNSServiceQuery (opaque handle to access dynamic service queries)
*/ */
typedef const void* hMDNSServiceQuery; typedef const void* hMDNSServiceQuery;
/** /**
* enuServiceQueryAnswerType enuServiceQueryAnswerType
*/ */
typedef enum _enuServiceQueryAnswerType { typedef enum _enuServiceQueryAnswerType
{
ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name
ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port
ServiceQueryAnswerType_Txts = (1 << 2), // TXT items ServiceQueryAnswerType_Txts = (1 << 2), // TXT items
@ -350,7 +363,8 @@ public:
#endif #endif
} enuServiceQueryAnswerType; } enuServiceQueryAnswerType;
enum class AnswerType : uint32_t { enum class AnswerType : uint32_t
{
Unknown = 0, Unknown = 0,
ServiceDomain = ServiceQueryAnswerType_ServiceDomain, ServiceDomain = ServiceQueryAnswerType_ServiceDomain,
HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort, HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort,
@ -364,12 +378,12 @@ public:
}; };
/** /**
* MDNSServiceQueryCallbackFn MDNSServiceQueryCallbackFn
* Callback function for received answers for dynamic service queries Callback function for received answers for dynamic service queries
*/ */
struct MDNSServiceInfo; // forward declaration struct MDNSServiceInfo; // forward declaration
typedef std::function<void(const MDNSServiceInfo& mdnsServiceInfo, typedef std::function<void(const MDNSServiceInfo& mdnsServiceInfo,
AnswerType answerType , // flag for the updated answer item AnswerType answerType, // flag for the updated answer item
bool p_bSetContent // true: Answer component set, false: component deleted bool p_bSetContent // true: Answer component set, false: component deleted
)> MDNSServiceQueryCallbackFunc; )> MDNSServiceQueryCallbackFunc;
@ -391,7 +405,7 @@ public:
bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery); bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery);
uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery); uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery);
std::vector<MDNSResponder::MDNSServiceInfo> answerInfo (const MDNSResponder::hMDNSServiceQuery p_hServiceQuery); std::vector<MDNSResponder::MDNSServiceInfo> answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery);
const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery, const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery,
const uint32_t p_u32AnswerIndex); const uint32_t p_u32AnswerIndex);
@ -428,8 +442,8 @@ public:
const uint32_t p_u32AnswerIndex); const uint32_t p_u32AnswerIndex);
/** /**
* MDNSProbeResultCallbackFn MDNSProbeResultCallbackFn
* Callback function for (host and service domain) probe results Callback function for (host and service domain) probe results
*/ */
typedef std::function<void(const char* p_pcDomainName, typedef std::function<void(const char* p_pcDomainName,
bool p_bProbeResult)> MDNSHostProbeFn; bool p_bProbeResult)> MDNSHostProbeFn;
@ -484,11 +498,11 @@ public:
public: public:
/** /**
* MDNSServiceInfo, used in application callbacks MDNSServiceInfo, used in application callbacks
*/ */
struct MDNSServiceInfo struct MDNSServiceInfo
{ {
MDNSServiceInfo(MDNSResponder& p_pM,MDNSResponder::hMDNSServiceQuery p_hS,uint32_t p_u32A) MDNSServiceInfo(MDNSResponder& p_pM, MDNSResponder::hMDNSServiceQuery p_hS, uint32_t p_u32A)
: p_pMDNSResponder(p_pM), : p_pMDNSResponder(p_pM),
p_hServiceQuery(p_hS), p_hServiceQuery(p_hS),
p_u32AnswerIndex(p_u32A) p_u32AnswerIndex(p_u32A)
@ -507,14 +521,16 @@ public:
uint32_t p_u32AnswerIndex; uint32_t p_u32AnswerIndex;
KeyValueMap keyValueMap; KeyValueMap keyValueMap;
public: public:
const char* serviceDomain(){ const char* serviceDomain()
{
return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex); return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex);
}; };
bool hostDomainAvailable() bool hostDomainAvailable()
{ {
return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex)); return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex));
} }
const char* hostDomain(){ const char* hostDomain()
{
return (hostDomainAvailable()) ? return (hostDomainAvailable()) ?
p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) : nullptr; p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) : nullptr;
}; };
@ -522,19 +538,23 @@ public:
{ {
return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex)); return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex));
} }
uint16_t hostPort(){ uint16_t hostPort()
{
return (hostPortAvailable()) ? return (hostPortAvailable()) ?
p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) : 0; p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) : 0;
}; };
bool IP4AddressAvailable() bool IP4AddressAvailable()
{ {
return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery,p_u32AnswerIndex )); return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery, p_u32AnswerIndex));
} }
std::vector<IPAddress> IP4Adresses(){ std::vector<IPAddress> IP4Adresses()
{
std::vector<IPAddress> internalIP; std::vector<IPAddress> internalIP;
if (IP4AddressAvailable()) { if (IP4AddressAvailable())
{
uint16_t cntIP4Adress = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex); uint16_t cntIP4Adress = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex);
for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2) { for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2)
{
internalIP.emplace_back(p_pMDNSResponder.answerIP4Address(p_hServiceQuery, p_u32AnswerIndex, u2)); internalIP.emplace_back(p_pMDNSResponder.answerIP4Address(p_hServiceQuery, p_u32AnswerIndex, u2));
} }
} }
@ -544,7 +564,8 @@ public:
{ {
return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex)); return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex));
} }
const char* strKeyValue (){ const char* strKeyValue()
{
return (txtAvailable()) ? return (txtAvailable()) ?
p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) : nullptr; p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) : nullptr;
}; };
@ -552,8 +573,9 @@ public:
{ {
if (txtAvailable() && keyValueMap.size() == 0) if (txtAvailable() && keyValueMap.size() == 0)
{ {
for (auto kv = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex);kv != nullptr;kv = kv->m_pNext) { for (auto kv = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); kv != nullptr; kv = kv->m_pNext)
keyValueMap.emplace(std::pair<const char*,const char*>(kv->m_pcKey,kv->m_pcValue)); {
keyValueMap.emplace(std::pair<const char*, const char*>(kv->m_pcKey, kv->m_pcValue));
} }
} }
return keyValueMap; return keyValueMap;
@ -562,9 +584,11 @@ public:
{ {
char* result = nullptr; char* result = nullptr;
for (stcMDNSServiceTxt* pTxt=p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); pTxt; pTxt=pTxt->m_pNext) { for (stcMDNSServiceTxt* pTxt = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); pTxt; pTxt = pTxt->m_pNext)
{
if ((key) && if ((key) &&
(0 == strcmp(pTxt->m_pcKey, key))) { (0 == strcmp(pTxt->m_pcKey, key)))
{
result = pTxt->m_pcValue; result = pTxt->m_pcValue;
break; break;
} }
@ -575,9 +599,10 @@ public:
protected: protected:
/** /**
* stcMDNSServiceTxt stcMDNSServiceTxt
*/ */
struct stcMDNSServiceTxt { struct stcMDNSServiceTxt
{
stcMDNSServiceTxt* m_pNext; stcMDNSServiceTxt* m_pNext;
char* m_pcKey; char* m_pcKey;
char* m_pcValue; char* m_pcValue;
@ -614,9 +639,10 @@ protected:
}; };
/** /**
* stcMDNSTxts stcMDNSTxts
*/ */
struct stcMDNSServiceTxts { struct stcMDNSServiceTxts
{
stcMDNSServiceTxt* m_pTxts; stcMDNSServiceTxt* m_pTxts;
stcMDNSServiceTxts(void); stcMDNSServiceTxts(void);
@ -650,9 +676,10 @@ protected:
}; };
/** /**
* enuContentFlags enuContentFlags
*/ */
typedef enum _enuContentFlags { typedef enum _enuContentFlags
{
// Host // Host
ContentFlag_A = 0x01, ContentFlag_A = 0x01,
ContentFlag_PTR_IP4 = 0x02, ContentFlag_PTR_IP4 = 0x02,
@ -666,9 +693,10 @@ protected:
} enuContentFlags; } enuContentFlags;
/** /**
* stcMDNS_MsgHeader stcMDNS_MsgHeader
*/ */
struct stcMDNS_MsgHeader { struct stcMDNS_MsgHeader
{
uint16_t m_u16ID; // Identifier uint16_t m_u16ID; // Identifier
bool m_1bQR : 1; // Query/Response flag bool m_1bQR : 1; // Query/Response flag
unsigned char m_4bOpcode : 4; // Operation code unsigned char m_4bOpcode : 4; // Operation code
@ -698,9 +726,10 @@ protected:
}; };
/** /**
* stcMDNS_RRDomain stcMDNS_RRDomain
*/ */
struct stcMDNS_RRDomain { struct stcMDNS_RRDomain
{
char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name
uint16_t m_u16NameLength; // Length (incl. '\0') uint16_t m_u16NameLength; // Length (incl. '\0')
@ -724,9 +753,10 @@ protected:
}; };
/** /**
* stcMDNS_RRAttributes stcMDNS_RRAttributes
*/ */
struct stcMDNS_RRAttributes { struct stcMDNS_RRAttributes
{
uint16_t m_u16Type; // Type uint16_t m_u16Type; // Type
uint16_t m_u16Class; // Class, nearly always 'IN' uint16_t m_u16Class; // Class, nearly always 'IN'
@ -738,9 +768,10 @@ protected:
}; };
/** /**
* stcMDNS_RRHeader stcMDNS_RRHeader
*/ */
struct stcMDNS_RRHeader { struct stcMDNS_RRHeader
{
stcMDNS_RRDomain m_Domain; stcMDNS_RRDomain m_Domain;
stcMDNS_RRAttributes m_Attributes; stcMDNS_RRAttributes m_Attributes;
@ -753,9 +784,10 @@ protected:
}; };
/** /**
* stcMDNS_RRQuestion stcMDNS_RRQuestion
*/ */
struct stcMDNS_RRQuestion { struct stcMDNS_RRQuestion
{
stcMDNS_RRQuestion* m_pNext; stcMDNS_RRQuestion* m_pNext;
stcMDNS_RRHeader m_Header; stcMDNS_RRHeader m_Header;
bool m_bUnicast; // Unicast reply requested bool m_bUnicast; // Unicast reply requested
@ -764,9 +796,10 @@ protected:
}; };
/** /**
* enuAnswerType enuAnswerType
*/ */
typedef enum _enuAnswerType { typedef enum _enuAnswerType
{
AnswerType_A, AnswerType_A,
AnswerType_PTR, AnswerType_PTR,
AnswerType_TXT, AnswerType_TXT,
@ -776,9 +809,10 @@ protected:
} enuAnswerType; } enuAnswerType;
/** /**
* stcMDNS_RRAnswer stcMDNS_RRAnswer
*/ */
struct stcMDNS_RRAnswer { struct stcMDNS_RRAnswer
{
stcMDNS_RRAnswer* m_pNext; stcMDNS_RRAnswer* m_pNext;
const enuAnswerType m_AnswerType; const enuAnswerType m_AnswerType;
stcMDNS_RRHeader m_Header; stcMDNS_RRHeader m_Header;
@ -799,9 +833,10 @@ protected:
#ifdef MDNS_IP4_SUPPORT #ifdef MDNS_IP4_SUPPORT
/** /**
* stcMDNS_RRAnswerA stcMDNS_RRAnswerA
*/ */
struct stcMDNS_RRAnswerA : public stcMDNS_RRAnswer { struct stcMDNS_RRAnswerA : public stcMDNS_RRAnswer
{
IPAddress m_IPAddress; IPAddress m_IPAddress;
stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header, stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header,
@ -813,9 +848,10 @@ protected:
#endif #endif
/** /**
* stcMDNS_RRAnswerPTR stcMDNS_RRAnswerPTR
*/ */
struct stcMDNS_RRAnswerPTR : public stcMDNS_RRAnswer { struct stcMDNS_RRAnswerPTR : public stcMDNS_RRAnswer
{
stcMDNS_RRDomain m_PTRDomain; stcMDNS_RRDomain m_PTRDomain;
stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header, stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header,
@ -826,9 +862,10 @@ protected:
}; };
/** /**
* stcMDNS_RRAnswerTXT stcMDNS_RRAnswerTXT
*/ */
struct stcMDNS_RRAnswerTXT : public stcMDNS_RRAnswer { struct stcMDNS_RRAnswerTXT : public stcMDNS_RRAnswer
{
stcMDNSServiceTxts m_Txts; stcMDNSServiceTxts m_Txts;
stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header, stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header,
@ -840,9 +877,10 @@ protected:
#ifdef MDNS_IP6_SUPPORT #ifdef MDNS_IP6_SUPPORT
/** /**
* stcMDNS_RRAnswerAAAA stcMDNS_RRAnswerAAAA
*/ */
struct stcMDNS_RRAnswerAAAA : public stcMDNS_RRAnswer { struct stcMDNS_RRAnswerAAAA : public stcMDNS_RRAnswer
{
//TODO: IP6Address m_IPAddress; //TODO: IP6Address m_IPAddress;
stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header, stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header,
@ -854,9 +892,10 @@ protected:
#endif #endif
/** /**
* stcMDNS_RRAnswerSRV stcMDNS_RRAnswerSRV
*/ */
struct stcMDNS_RRAnswerSRV : public stcMDNS_RRAnswer { struct stcMDNS_RRAnswerSRV : public stcMDNS_RRAnswer
{
uint16_t m_u16Priority; uint16_t m_u16Priority;
uint16_t m_u16Weight; uint16_t m_u16Weight;
uint16_t m_u16Port; uint16_t m_u16Port;
@ -870,9 +909,10 @@ protected:
}; };
/** /**
* stcMDNS_RRAnswerGeneric stcMDNS_RRAnswerGeneric
*/ */
struct stcMDNS_RRAnswerGeneric : public stcMDNS_RRAnswer { struct stcMDNS_RRAnswerGeneric : public stcMDNS_RRAnswer
{
uint16_t m_u16RDLength; // Length of variable answer uint16_t m_u16RDLength; // Length of variable answer
uint8_t* m_pu8RDData; // Offset of start of variable answer in packet uint8_t* m_pu8RDData; // Offset of start of variable answer in packet
@ -885,9 +925,10 @@ protected:
/** /**
* enuProbingStatus enuProbingStatus
*/ */
typedef enum _enuProbingStatus { typedef enum _enuProbingStatus
{
ProbingStatus_WaitingForData, ProbingStatus_WaitingForData,
ProbingStatus_ReadyToStart, ProbingStatus_ReadyToStart,
ProbingStatus_InProgress, ProbingStatus_InProgress,
@ -895,9 +936,10 @@ protected:
} enuProbingStatus; } enuProbingStatus;
/** /**
* stcProbeInformation stcProbeInformation
*/ */
struct stcProbeInformation { struct stcProbeInformation
{
enuProbingStatus m_ProbingStatus; enuProbingStatus m_ProbingStatus;
uint8_t m_u8SentCount; // Used for probes and announcements uint8_t m_u8SentCount; // Used for probes and announcements
esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements
@ -914,9 +956,10 @@ protected:
/** /**
* stcMDNSService stcMDNSService
*/ */
struct stcMDNSService { struct stcMDNSService
{
stcMDNSService* m_pNext; stcMDNSService* m_pNext;
char* m_pcName; char* m_pcName;
bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied) bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied)
@ -944,23 +987,26 @@ protected:
}; };
/** /**
* stcMDNSServiceQuery stcMDNSServiceQuery
*/ */
struct stcMDNSServiceQuery { struct stcMDNSServiceQuery
{
/** /**
* stcAnswer stcAnswer
*/ */
struct stcAnswer { struct stcAnswer
{
/** /**
* stcTTL stcTTL
*/ */
struct stcTTL { struct stcTTL
{
/** /**
* timeoutLevel_t timeoutLevel_t
*/ */
typedef uint8_t timeoutLevel_t; typedef uint8_t timeoutLevel_t;
/** /**
* TIMEOUTLEVELs TIMEOUTLEVELs
*/ */
const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0; const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0;
const timeoutLevel_t TIMEOUTLEVEL_BASE = 80; const timeoutLevel_t TIMEOUTLEVEL_BASE = 80;
@ -984,9 +1030,10 @@ protected:
}; };
#ifdef MDNS_IP4_SUPPORT #ifdef MDNS_IP4_SUPPORT
/** /**
* stcIP4Address stcIP4Address
*/ */
struct stcIP4Address { struct stcIP4Address
{
stcIP4Address* m_pNext; stcIP4Address* m_pNext;
IPAddress m_IPAddress; IPAddress m_IPAddress;
stcTTL m_TTL; stcTTL m_TTL;
@ -997,9 +1044,10 @@ protected:
#endif #endif
#ifdef MDNS_IP6_SUPPORT #ifdef MDNS_IP6_SUPPORT
/** /**
* stcIP6Address stcIP6Address
*/ */
struct stcIP6Address { struct stcIP6Address
{
stcIP6Address* m_pNext; stcIP6Address* m_pNext;
IP6Address m_IPAddress; IP6Address m_IPAddress;
stcTTL m_TTL; stcTTL m_TTL;
@ -1093,14 +1141,16 @@ protected:
}; };
/** /**
* stcMDNSSendParameter stcMDNSSendParameter
*/ */
struct stcMDNSSendParameter { struct stcMDNSSendParameter
{
protected: protected:
/** /**
* stcDomainCacheItem stcDomainCacheItem
*/ */
struct stcDomainCacheItem { struct stcDomainCacheItem
{
stcDomainCacheItem* m_pNext; stcDomainCacheItem* m_pNext;
const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer) const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer)
bool m_bAdditionalData; // Opaque flag for special info (service domain included) bool m_bAdditionalData; // Opaque flag for special info (service domain included)
@ -1203,7 +1253,10 @@ protected:
uint16_t p_u16QueryType, uint16_t p_u16QueryType,
stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0); stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0);
const IPAddress _getResponseMulticastInterface() const { return IPAddress(m_netif->ip_addr); } const IPAddress _getResponseMulticastInterface() const
{
return IPAddress(m_netif->ip_addr);
}
uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader, uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader,
bool* p_pbFullNameMatch = 0) const; bool* p_pbFullNameMatch = 0) const;

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,26 @@
/* /*
* LEAmDNS_Helpers.cpp LEAmDNS_Helpers.cpp
*
* License (MIT license): License (MIT license):
* Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. THE SOFTWARE.
*
*/ */
#include "lwip/igmp.h" #include "lwip/igmp.h"
@ -28,16 +28,18 @@
#include "LEAmDNS_Priv.h" #include "LEAmDNS_Priv.h"
namespace { namespace
{
/* /*
* strrstr (static) strrstr (static)
*
* Backwards search for p_pcPattern in p_pcString Backwards search for p_pcPattern in p_pcString
* Based on: https://stackoverflow.com/a/1634398/2778898 Based on: https://stackoverflow.com/a/1634398/2778898
*
*/ */
const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pcPattern) { const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pcPattern)
{
const char* pcResult = 0; const char* pcResult = 0;
@ -46,11 +48,14 @@ const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pc
if ((stStringLength) && if ((stStringLength) &&
(stPatternLength) && (stPatternLength) &&
(stPatternLength <= stStringLength)) { (stPatternLength <= stStringLength))
{
// Pattern is shorter or has the same length tham the string // Pattern is shorter or has the same length tham the string
for (const char* s=(p_pcString + stStringLength - stPatternLength); s>=p_pcString; --s) { for (const char* s = (p_pcString + stStringLength - stPatternLength); s >= p_pcString; --s)
if (0 == strncmp(s, p_pcPattern, stPatternLength)) { {
if (0 == strncmp(s, p_pcPattern, stPatternLength))
{
pcResult = s; pcResult = s;
break; break;
} }
@ -66,52 +71,59 @@ const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pc
namespace esp8266 { namespace esp8266
{
/* /*
* LEAmDNS LEAmDNS
*/ */
namespace MDNSImplementation { namespace MDNSImplementation
{
/** /**
* HELPERS HELPERS
*/ */
/* /*
* MDNSResponder::indexDomain (static) MDNSResponder::indexDomain (static)
*
* Updates the given domain 'p_rpcHostname' by appending a delimiter and an index number. Updates the given domain 'p_rpcHostname' by appending a delimiter and an index number.
*
* If the given domain already hasa numeric index (after the given delimiter), this index If the given domain already hasa numeric index (after the given delimiter), this index
* incremented. If not, the delimiter and index '2' is added. incremented. If not, the delimiter and index '2' is added.
*
* If 'p_rpcHostname' is empty (==0), the given default name 'p_pcDefaultHostname' is used, If 'p_rpcHostname' is empty (==0), the given default name 'p_pcDefaultHostname' is used,
* if no default is given, 'esp8266' is used. if no default is given, 'esp8266' is used.
*
*/ */
/*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain, /*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain,
const char* p_pcDivider /*= "-"*/, const char* p_pcDivider /*= "-"*/,
const char* p_pcDefaultDomain /*= 0*/) { const char* p_pcDefaultDomain /*= 0*/)
{
bool bResult = false; bool bResult = false;
// Ensure a divider exists; use '-' as default // Ensure a divider exists; use '-' as default
const char* pcDivider = (p_pcDivider ?: "-"); const char* pcDivider = (p_pcDivider ? : "-");
if (p_rpcDomain) { if (p_rpcDomain)
{
const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider); const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider);
if (pFoundDivider) { // maybe already extended if (pFoundDivider) // maybe already extended
{
char* pEnd = 0; char* pEnd = 0;
unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10); unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10);
if ((ulIndex) && if ((ulIndex) &&
((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain)) && ((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain)) &&
(!*pEnd)) { // Valid (old) index found (!*pEnd)) // Valid (old) index found
{
char acIndexBuffer[16]; char acIndexBuffer[16];
sprintf(acIndexBuffer, "%lu", (++ulIndex)); sprintf(acIndexBuffer, "%lu", (++ulIndex));
size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider)) + strlen(acIndexBuffer) + 1); size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider)) + strlen(acIndexBuffer) + 1);
char* pNewHostname = new char[stLength]; char* pNewHostname = new char[stLength];
if (pNewHostname) { if (pNewHostname)
{
memcpy(pNewHostname, p_rpcDomain, (pFoundDivider - p_rpcDomain + strlen(pcDivider))); memcpy(pNewHostname, p_rpcDomain, (pFoundDivider - p_rpcDomain + strlen(pcDivider)));
pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0; pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0;
strcat(pNewHostname, acIndexBuffer); strcat(pNewHostname, acIndexBuffer);
@ -121,19 +133,23 @@ namespace MDNSImplementation {
bResult = true; bResult = true;
} }
else { else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
} }
} }
else { else
{
pFoundDivider = 0; // Flag the need to (base) extend the hostname pFoundDivider = 0; // Flag the need to (base) extend the hostname
} }
} }
if (!pFoundDivider) { // not yet extended (or failed to increment extension) -> start indexing if (!pFoundDivider) // not yet extended (or failed to increment extension) -> start indexing
{
size_t stLength = strlen(p_rpcDomain) + (strlen(pcDivider) + 1 + 1); // Name + Divider + '2' + '\0' size_t stLength = strlen(p_rpcDomain) + (strlen(pcDivider) + 1 + 1); // Name + Divider + '2' + '\0'
char* pNewHostname = new char[stLength]; char* pNewHostname = new char[stLength];
if (pNewHostname) { if (pNewHostname)
{
sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider); sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider);
delete[] p_rpcDomain; delete[] p_rpcDomain;
@ -141,22 +157,26 @@ namespace MDNSImplementation {
bResult = true; bResult = true;
} }
else { else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
} }
} }
} }
else { else
{
// No given host domain, use base or default // No given host domain, use base or default
const char* cpcDefaultName = (p_pcDefaultDomain ?: "esp8266"); const char* cpcDefaultName = (p_pcDefaultDomain ? : "esp8266");
size_t stLength = strlen(cpcDefaultName) + 1; // '\0' size_t stLength = strlen(cpcDefaultName) + 1; // '\0'
p_rpcDomain = new char[stLength]; p_rpcDomain = new char[stLength];
if (p_rpcDomain) { if (p_rpcDomain)
{
strncpy(p_rpcDomain, cpcDefaultName, stLength); strncpy(p_rpcDomain, cpcDefaultName, stLength);
bResult = true; bResult = true;
} }
else { else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
} }
} }
@ -166,26 +186,28 @@ namespace MDNSImplementation {
/* /*
* UDP CONTEXT UDP CONTEXT
*/ */
bool MDNSResponder::_callProcess(void) { bool MDNSResponder::_callProcess(void)
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); DEBUG_EX_INFO(DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str()););
return _process(false); return _process(false);
} }
/* /*
* MDNSResponder::_allocUDPContext MDNSResponder::_allocUDPContext
*
* (Re-)Creates the one-and-only UDP context for the MDNS responder. (Re-)Creates the one-and-only UDP context for the MDNS responder.
* The context is added to the 'multicast'-group and listens to the MDNS port (5353). The context is added to the 'multicast'-group and listens to the MDNS port (5353).
* The travel-distance for multicast messages is set to 1 (local, via MDNS_MULTICAST_TTL). The travel-distance for multicast messages is set to 1 (local, via MDNS_MULTICAST_TTL).
* Messages are received via the MDNSResponder '_update' function. CAUTION: This function Messages are received via the MDNSResponder '_update' function. CAUTION: This function
* is called from the WiFi stack side of the ESP stack system. is called from the WiFi stack side of the ESP stack system.
*
*/ */
bool MDNSResponder::_allocUDPContext(void) { bool MDNSResponder::_allocUDPContext(void)
{
DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext");); DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext"););
bool bResult = false; bool bResult = false;
@ -199,11 +221,13 @@ bool MDNSResponder::_allocUDPContext(void) {
//TODO: set multicast address (lwip_joingroup() is IPv4 only at the time of writing) //TODO: set multicast address (lwip_joingroup() is IPv4 only at the time of writing)
multicast_addr.addr = DNS_MQUERY_IPV6_GROUP_INIT; multicast_addr.addr = DNS_MQUERY_IPV6_GROUP_INIT;
#endif #endif
if (ERR_OK == igmp_joingroup(ip_2_ip4(&m_netif->ip_addr), ip_2_ip4(&multicast_addr))) { if (ERR_OK == igmp_joingroup(ip_2_ip4(&m_netif->ip_addr), ip_2_ip4(&multicast_addr)))
{
m_pUDPContext = new UdpContext; m_pUDPContext = new UdpContext;
m_pUDPContext->ref(); m_pUDPContext->ref();
if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT)) { if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT))
{
m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL); m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL);
m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this)); m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this));
@ -214,11 +238,13 @@ bool MDNSResponder::_allocUDPContext(void) {
} }
/* /*
* MDNSResponder::_releaseUDPContext MDNSResponder::_releaseUDPContext
*/ */
bool MDNSResponder::_releaseUDPContext(void) { bool MDNSResponder::_releaseUDPContext(void)
{
if (m_pUDPContext) { if (m_pUDPContext)
{
m_pUDPContext->unref(); m_pUDPContext->unref();
m_pUDPContext = 0; m_pUDPContext = 0;
} }
@ -227,16 +253,18 @@ bool MDNSResponder::_releaseUDPContext(void) {
/* /*
* SERVICE QUERY SERVICE QUERY
*/ */
/* /*
* MDNSResponder::_allocServiceQuery MDNSResponder::_allocServiceQuery
*/ */
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void) { MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void)
{
stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery; stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery;
if (pServiceQuery) { if (pServiceQuery)
{
// Link to query list // Link to query list
pServiceQuery->m_pNext = m_pServiceQueries; pServiceQuery->m_pNext = m_pServiceQueries;
m_pServiceQueries = pServiceQuery; m_pServiceQueries = pServiceQuery;
@ -245,30 +273,37 @@ MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void) {
} }
/* /*
* MDNSResponder::_removeServiceQuery MDNSResponder::_removeServiceQuery
*/ */
bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery) { bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery)
{
bool bResult = false; bool bResult = false;
if (p_pServiceQuery) { if (p_pServiceQuery)
{
stcMDNSServiceQuery* pPred = m_pServiceQueries; stcMDNSServiceQuery* pPred = m_pServiceQueries;
while ((pPred) && while ((pPred) &&
(pPred->m_pNext != p_pServiceQuery)) { (pPred->m_pNext != p_pServiceQuery))
{
pPred = pPred->m_pNext; pPred = pPred->m_pNext;
} }
if (pPred) { if (pPred)
{
pPred->m_pNext = p_pServiceQuery->m_pNext; pPred->m_pNext = p_pServiceQuery->m_pNext;
delete p_pServiceQuery; delete p_pServiceQuery;
bResult = true; bResult = true;
} }
else { // No predecesor else // No predecesor
if (m_pServiceQueries == p_pServiceQuery) { {
if (m_pServiceQueries == p_pServiceQuery)
{
m_pServiceQueries = p_pServiceQuery->m_pNext; m_pServiceQueries = p_pServiceQuery->m_pNext;
delete p_pServiceQuery; delete p_pServiceQuery;
bResult = true; bResult = true;
} }
else { else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseServiceQuery: INVALID service query!");); DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseServiceQuery: INVALID service query!"););
} }
} }
@ -277,25 +312,29 @@ bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pS
} }
/* /*
* MDNSResponder::_removeLegacyServiceQuery MDNSResponder::_removeLegacyServiceQuery
*/ */
bool MDNSResponder::_removeLegacyServiceQuery(void) { bool MDNSResponder::_removeLegacyServiceQuery(void)
{
stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery(); stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery();
return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true); return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true);
} }
/* /*
* MDNSResponder::_findServiceQuery MDNSResponder::_findServiceQuery
*
* 'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existance) 'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existance)
*
*/ */
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery)
{
stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries;
while (pServiceQuery) { while (pServiceQuery)
if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery) { {
if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery)
{
break; break;
} }
pServiceQuery = pServiceQuery->m_pNext; pServiceQuery = pServiceQuery->m_pNext;
@ -304,13 +343,16 @@ MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSRespond
} }
/* /*
* MDNSResponder::_findLegacyServiceQuery MDNSResponder::_findLegacyServiceQuery
*/ */
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void) { MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void)
{
stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries;
while (pServiceQuery) { while (pServiceQuery)
if (pServiceQuery->m_bLegacyQuery) { {
if (pServiceQuery->m_bLegacyQuery)
{
break; break;
} }
pServiceQuery = pServiceQuery->m_pNext; pServiceQuery = pServiceQuery->m_pNext;
@ -319,10 +361,12 @@ MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void)
} }
/* /*
* MDNSResponder::_releaseServiceQueries MDNSResponder::_releaseServiceQueries
*/ */
bool MDNSResponder::_releaseServiceQueries(void) { bool MDNSResponder::_releaseServiceQueries(void)
while (m_pServiceQueries) { {
while (m_pServiceQueries)
{
stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext; stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext;
delete m_pServiceQueries; delete m_pServiceQueries;
m_pServiceQueries = pNext; m_pServiceQueries = pNext;
@ -331,15 +375,18 @@ bool MDNSResponder::_releaseServiceQueries(void) {
} }
/* /*
* MDNSResponder::_findNextServiceQueryByServiceType MDNSResponder::_findNextServiceQueryByServiceType
*/ */
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceTypeDomain, MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceTypeDomain,
const stcMDNSServiceQuery* p_pPrevServiceQuery) { const stcMDNSServiceQuery* p_pPrevServiceQuery)
{
stcMDNSServiceQuery* pMatchingServiceQuery = 0; stcMDNSServiceQuery* pMatchingServiceQuery = 0;
stcMDNSServiceQuery* pServiceQuery = (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries); stcMDNSServiceQuery* pServiceQuery = (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries);
while (pServiceQuery) { while (pServiceQuery)
if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain) { {
if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain)
{
pMatchingServiceQuery = pServiceQuery; pMatchingServiceQuery = pServiceQuery;
break; break;
} }
@ -350,13 +397,14 @@ MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServic
/* /*
* HOSTNAME HOSTNAME
*/ */
/* /*
* MDNSResponder::_setHostname MDNSResponder::_setHostname
*/ */
bool MDNSResponder::_setHostname(const char* p_pcHostname) { bool MDNSResponder::_setHostname(const char* p_pcHostname)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"), p_pcHostname);); //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"), p_pcHostname););
bool bResult = false; bool bResult = false;
@ -365,12 +413,15 @@ bool MDNSResponder::_setHostname(const char* p_pcHostname) {
size_t stLength = 0; size_t stLength = 0;
if ((p_pcHostname) && if ((p_pcHostname) &&
(MDNS_DOMAIN_LABEL_MAXLENGTH >= (stLength = strlen(p_pcHostname)))) { // char max size for a single label (MDNS_DOMAIN_LABEL_MAXLENGTH >= (stLength = strlen(p_pcHostname)))) // char max size for a single label
{
// Copy in hostname characters as lowercase // Copy in hostname characters as lowercase
if ((bResult = (0 != (m_pcHostname = new char[stLength + 1])))) { if ((bResult = (0 != (m_pcHostname = new char[stLength + 1]))))
{
#ifdef MDNS_FORCE_LOWERCASE_HOSTNAME #ifdef MDNS_FORCE_LOWERCASE_HOSTNAME
size_t i = 0; size_t i = 0;
for (; i<stLength; ++i) { for (; i < stLength; ++i)
{
m_pcHostname[i] = (isupper(p_pcHostname[i]) ? tolower(p_pcHostname[i]) : p_pcHostname[i]); m_pcHostname[i] = (isupper(p_pcHostname[i]) ? tolower(p_pcHostname[i]) : p_pcHostname[i]);
} }
m_pcHostname[i] = 0; m_pcHostname[i] = 0;
@ -383,11 +434,13 @@ bool MDNSResponder::_setHostname(const char* p_pcHostname) {
} }
/* /*
* MDNSResponder::_releaseHostname MDNSResponder::_releaseHostname
*/ */
bool MDNSResponder::_releaseHostname(void) { bool MDNSResponder::_releaseHostname(void)
{
if (m_pcHostname) { if (m_pcHostname)
{
delete[] m_pcHostname; delete[] m_pcHostname;
m_pcHostname = 0; m_pcHostname = 0;
} }
@ -396,16 +449,17 @@ bool MDNSResponder::_releaseHostname(void) {
/* /*
* SERVICE SERVICE
*/ */
/* /*
* MDNSResponder::_allocService MDNSResponder::_allocService
*/ */
MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName, MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName,
const char* p_pcService, const char* p_pcService,
const char* p_pcProtocol, const char* p_pcProtocol,
uint16_t p_u16Port) { uint16_t p_u16Port)
{
stcMDNSService* pService = 0; stcMDNSService* pService = 0;
if (((!p_pcName) || if (((!p_pcName) ||
@ -416,9 +470,10 @@ MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName
(MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) && (MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) &&
(p_u16Port) && (p_u16Port) &&
(0 != (pService = new stcMDNSService)) && (0 != (pService = new stcMDNSService)) &&
(pService->setName(p_pcName ?: m_pcHostname)) && (pService->setName(p_pcName ? : m_pcHostname)) &&
(pService->setService(p_pcService)) && (pService->setService(p_pcService)) &&
(pService->setProtocol(p_pcProtocol))) { (pService->setProtocol(p_pcProtocol)))
{
pService->m_bAutoName = (0 == p_pcName); pService->m_bAutoName = (0 == p_pcName);
pService->m_u16Port = p_u16Port; pService->m_u16Port = p_u16Port;
@ -431,30 +486,37 @@ MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName
} }
/* /*
* MDNSResponder::_releaseService MDNSResponder::_releaseService
*/ */
bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService) { bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService)
{
bool bResult = false; bool bResult = false;
if (p_pService) { if (p_pService)
{
stcMDNSService* pPred = m_pServices; stcMDNSService* pPred = m_pServices;
while ((pPred) && while ((pPred) &&
(pPred->m_pNext != p_pService)) { (pPred->m_pNext != p_pService))
{
pPred = pPred->m_pNext; pPred = pPred->m_pNext;
} }
if (pPred) { if (pPred)
{
pPred->m_pNext = p_pService->m_pNext; pPred->m_pNext = p_pService->m_pNext;
delete p_pService; delete p_pService;
bResult = true; bResult = true;
} }
else { // No predecesor else // No predecesor
if (m_pServices == p_pService) { {
if (m_pServices == p_pService)
{
m_pServices = p_pService->m_pNext; m_pServices = p_pService->m_pNext;
delete p_pService; delete p_pService;
bResult = true; bResult = true;
} }
else { else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!");); DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!"););
} }
} }
@ -463,12 +525,14 @@ bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService) {
} }
/* /*
* MDNSResponder::_releaseServices MDNSResponder::_releaseServices
*/ */
bool MDNSResponder::_releaseServices(void) { bool MDNSResponder::_releaseServices(void)
{
stcMDNSService* pService = m_pServices; stcMDNSService* pService = m_pServices;
while (pService) { while (pService)
{
_releaseService(pService); _releaseService(pService);
pService = m_pServices; pService = m_pServices;
} }
@ -476,17 +540,20 @@ bool MDNSResponder::_releaseServices(void) {
} }
/* /*
* MDNSResponder::_findService MDNSResponder::_findService
*/ */
MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName, MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName,
const char* p_pcService, const char* p_pcService,
const char* p_pcProtocol) { const char* p_pcProtocol)
{
stcMDNSService* pService = m_pServices; stcMDNSService* pService = m_pServices;
while (pService) { while (pService)
{
if ((0 == strcmp(pService->m_pcName, p_pcName)) && if ((0 == strcmp(pService->m_pcName, p_pcName)) &&
(0 == strcmp(pService->m_pcService, p_pcService)) && (0 == strcmp(pService->m_pcService, p_pcService)) &&
(0 == strcmp(pService->m_pcProtocol, p_pcProtocol))) { (0 == strcmp(pService->m_pcProtocol, p_pcProtocol)))
{
break; break;
} }
@ -496,13 +563,16 @@ MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName,
} }
/* /*
* MDNSResponder::_findService MDNSResponder::_findService
*/ */
MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService) { MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService)
{
stcMDNSService* pService = m_pServices; stcMDNSService* pService = m_pServices;
while (pService) { while (pService)
if (p_hService == (hMDNSService)pService) { {
if (p_hService == (hMDNSService)pService)
{
break; break;
} }
pService = pService->m_pNext; pService = pService->m_pNext;
@ -512,16 +582,17 @@ MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::
/* /*
* SERVICE TXT SERVICE TXT
*/ */
/* /*
* MDNSResponder::_allocServiceTxt MDNSResponder::_allocServiceTxt
*/ */
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService, MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService,
const char* p_pcKey, const char* p_pcKey,
const char* p_pcValue, const char* p_pcValue,
bool p_bTemp) { bool p_bTemp)
{
stcMDNSServiceTxt* pTxt = 0; stcMDNSServiceTxt* pTxt = 0;
@ -531,20 +602,25 @@ MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder:
1 + // Length byte 1 + // Length byte
(p_pcKey ? strlen(p_pcKey) : 0) + (p_pcKey ? strlen(p_pcKey) : 0) +
1 + // '=' 1 + // '='
(p_pcValue ? strlen(p_pcValue) : 0)))) { (p_pcValue ? strlen(p_pcValue) : 0))))
{
pTxt = new stcMDNSServiceTxt; pTxt = new stcMDNSServiceTxt;
if (pTxt) { if (pTxt)
{
size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0); size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0);
pTxt->m_pcKey = new char[stLength + 1]; pTxt->m_pcKey = new char[stLength + 1];
if (pTxt->m_pcKey) { if (pTxt->m_pcKey)
{
strncpy(pTxt->m_pcKey, p_pcKey, stLength); pTxt->m_pcKey[stLength] = 0; strncpy(pTxt->m_pcKey, p_pcKey, stLength); pTxt->m_pcKey[stLength] = 0;
} }
if (p_pcValue) { if (p_pcValue)
{
stLength = (p_pcValue ? strlen(p_pcValue) : 0); stLength = (p_pcValue ? strlen(p_pcValue) : 0);
pTxt->m_pcValue = new char[stLength + 1]; pTxt->m_pcValue = new char[stLength + 1];
if (pTxt->m_pcValue) { if (pTxt->m_pcValue)
{
strncpy(pTxt->m_pcValue, p_pcValue, stLength); pTxt->m_pcValue[stLength] = 0; strncpy(pTxt->m_pcValue, p_pcValue, stLength); pTxt->m_pcValue[stLength] = 0;
} }
} }
@ -558,10 +634,11 @@ MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder:
} }
/* /*
* MDNSResponder::_releaseServiceTxt MDNSResponder::_releaseServiceTxt
*/ */
bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService, bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService,
MDNSResponder::stcMDNSServiceTxt* p_pTxt) { MDNSResponder::stcMDNSServiceTxt* p_pTxt)
{
return ((p_pService) && return ((p_pService) &&
(p_pTxt) && (p_pTxt) &&
@ -569,18 +646,20 @@ bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService
} }
/* /*
* MDNSResponder::_updateServiceTxt MDNSResponder::_updateServiceTxt
*/ */
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService, MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService,
MDNSResponder::stcMDNSServiceTxt* p_pTxt, MDNSResponder::stcMDNSServiceTxt* p_pTxt,
const char* p_pcValue, const char* p_pcValue,
bool p_bTemp) { bool p_bTemp)
{
if ((p_pService) && if ((p_pService) &&
(p_pTxt) && (p_pTxt) &&
(MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() - (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() -
(p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0) + (p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0) +
(p_pcValue ? strlen(p_pcValue) : 0)))) { (p_pcValue ? strlen(p_pcValue) : 0))))
{
p_pTxt->update(p_pcValue); p_pTxt->update(p_pcValue);
p_pTxt->m_bTemp = p_bTemp; p_pTxt->m_bTemp = p_bTemp;
} }
@ -588,41 +667,47 @@ MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder
} }
/* /*
* MDNSResponder::_findServiceTxt MDNSResponder::_findServiceTxt
*/ */
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService,
const char* p_pcKey) { const char* p_pcKey)
{
return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0); return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0);
} }
/* /*
* MDNSResponder::_findServiceTxt MDNSResponder::_findServiceTxt
*/ */
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService,
const hMDNSTxt p_hTxt) { const hMDNSTxt p_hTxt)
{
return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt) : 0); return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt) : 0);
} }
/* /*
* MDNSResponder::_addServiceTxt MDNSResponder::_addServiceTxt
*/ */
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService, MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService,
const char* p_pcKey, const char* p_pcKey,
const char* p_pcValue, const char* p_pcValue,
bool p_bTemp) { bool p_bTemp)
{
stcMDNSServiceTxt* pResult = 0; stcMDNSServiceTxt* pResult = 0;
if ((p_pService) && if ((p_pService) &&
(p_pcKey) && (p_pcKey) &&
(strlen(p_pcKey))) { (strlen(p_pcKey)))
{
stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey); stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey);
if (pTxt) { if (pTxt)
{
pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp); pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp);
} }
else { else
{
pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp); pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp);
} }
} }
@ -630,7 +715,8 @@ MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::s
} }
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery,
const uint32_t p_u32AnswerIndex) { const uint32_t p_u32AnswerIndex)
{
stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery);
stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0);
// Fill m_pcTxts (if not already done) // Fill m_pcTxts (if not already done)
@ -638,71 +724,83 @@ MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServ
} }
/* /*
* MDNSResponder::_collectServiceTxts MDNSResponder::_collectServiceTxts
*/ */
bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService) { bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService)
{
// Call Dynamic service callbacks // Call Dynamic service callbacks
if (m_fnServiceTxtCallback) { if (m_fnServiceTxtCallback)
{
m_fnServiceTxtCallback((hMDNSService)&p_rService); m_fnServiceTxtCallback((hMDNSService)&p_rService);
} }
if (p_rService.m_fnTxtCallback) { if (p_rService.m_fnTxtCallback)
{
p_rService.m_fnTxtCallback((hMDNSService)&p_rService); p_rService.m_fnTxtCallback((hMDNSService)&p_rService);
} }
return true; return true;
} }
/* /*
* MDNSResponder::_releaseTempServiceTxts MDNSResponder::_releaseTempServiceTxts
*/ */
bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService) { bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService)
{
return (p_rService.m_Txts.removeTempTxts()); return (p_rService.m_Txts.removeTempTxts());
} }
/* /*
* MISC MISC
*/ */
#ifdef DEBUG_ESP_MDNS_RESPONDER #ifdef DEBUG_ESP_MDNS_RESPONDER
/* /*
* MDNSResponder::_printRRDomain MDNSResponder::_printRRDomain
*/ */
bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const { bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const
{
//DEBUG_OUTPUT.printf_P(PSTR("Domain: ")); //DEBUG_OUTPUT.printf_P(PSTR("Domain: "));
const char* pCursor = p_RRDomain.m_acName; const char* pCursor = p_RRDomain.m_acName;
uint8_t u8Length = *pCursor++; uint8_t u8Length = *pCursor++;
if (u8Length) { if (u8Length)
while (u8Length) { {
for (uint8_t u=0; u<u8Length; ++u) { while (u8Length)
{
for (uint8_t u = 0; u < u8Length; ++u)
{
DEBUG_OUTPUT.printf_P(PSTR("%c"), *(pCursor++)); DEBUG_OUTPUT.printf_P(PSTR("%c"), *(pCursor++));
} }
u8Length = *pCursor++; u8Length = *pCursor++;
if (u8Length) { if (u8Length)
{
DEBUG_OUTPUT.printf_P(PSTR(".")); DEBUG_OUTPUT.printf_P(PSTR("."));
} }
} }
} }
else { // empty domain else // empty domain
{
DEBUG_OUTPUT.printf_P(PSTR("-empty-")); DEBUG_OUTPUT.printf_P(PSTR("-empty-"));
} }
//DEBUG_OUTPUT.printf_P(PSTR("\n")); //DEBUG_OUTPUT.printf_P(PSTR("\n"));
return true; return true;
} }
/* /*
* MDNSResponder::_printRRAnswer MDNSResponder::_printRRAnswer
*/ */
bool MDNSResponder::_printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const { bool MDNSResponder::_printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const
{
DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] RRAnswer: ")); DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] RRAnswer: "));
_printRRDomain(p_RRAnswer.m_Header.m_Domain); _printRRDomain(p_RRAnswer.m_Header.m_Domain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, "), p_RRAnswer.m_Header.m_Attributes.m_u16Type, p_RRAnswer.m_Header.m_Attributes.m_u16Class, p_RRAnswer.m_u32TTL); DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, "), p_RRAnswer.m_Header.m_Attributes.m_u16Type, p_RRAnswer.m_Header.m_Attributes.m_u16Class, p_RRAnswer.m_u32TTL);
switch (p_RRAnswer.m_Header.m_Attributes.m_u16Type & (~0x8000)) { // Topmost bit might carry 'cache flush' flag switch (p_RRAnswer.m_Header.m_Attributes.m_u16Type & (~0x8000)) // Topmost bit might carry 'cache flush' flag
{
#ifdef MDNS_IP4_SUPPORT #ifdef MDNS_IP4_SUPPORT
case DNS_RRTYPE_A: case DNS_RRTYPE_A:
DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((const stcMDNS_RRAnswerA*)&p_RRAnswer)->m_IPAddress.toString().c_str()); DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((const stcMDNS_RRAnswerA*)&p_RRAnswer)->m_IPAddress.toString().c_str());
@ -712,10 +810,12 @@ bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rSe
DEBUG_OUTPUT.printf_P(PSTR("PTR ")); DEBUG_OUTPUT.printf_P(PSTR("PTR "));
_printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain); _printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain);
break; break;
case DNS_RRTYPE_TXT: { case DNS_RRTYPE_TXT:
{
size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength(); size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength();
char* pTxts = new char[stTxtLength]; char* pTxts = new char[stTxtLength];
if (pTxts) { if (pTxts)
{
((/*const c_str()!!*/stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts); ((/*const c_str()!!*/stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts);
DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts); DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts);
delete[] pTxts; delete[] pTxts;
@ -738,7 +838,7 @@ bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rSe
DEBUG_OUTPUT.printf_P(PSTR("\n")); DEBUG_OUTPUT.printf_P(PSTR("\n"));
return true; return true;
} }
#endif #endif
} // namespace MDNSImplementation } // namespace MDNSImplementation

View File

@ -1,37 +1,39 @@
/* /*
* LEAmDNS_Priv.h LEAmDNS_Priv.h
*
* License (MIT license): License (MIT license):
* Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. THE SOFTWARE.
*
*/ */
#ifndef MDNS_PRIV_H #ifndef MDNS_PRIV_H
#define MDNS_PRIV_H #define MDNS_PRIV_H
namespace esp8266 { namespace esp8266
{
/* /*
* LEAmDNS LEAmDNS
*/ */
namespace MDNSImplementation { namespace MDNSImplementation
{
// Enable class debug functions // Enable class debug functions
#define ESP_8266_MDNS_INCLUDE #define ESP_8266_MDNS_INCLUDE
@ -42,7 +44,7 @@ namespace MDNSImplementation {
#endif #endif
#ifndef LWIP_OPEN_SRC #ifndef LWIP_OPEN_SRC
#define LWIP_OPEN_SRC #define LWIP_OPEN_SRC
#endif #endif
// //
@ -59,87 +61,87 @@ namespace MDNSImplementation {
#endif #endif
#ifdef DEBUG_ESP_MDNS_RESPONDER #ifdef DEBUG_ESP_MDNS_RESPONDER
#ifdef DEBUG_ESP_MDNS_INFO #ifdef DEBUG_ESP_MDNS_INFO
#define DEBUG_EX_INFO(A) A #define DEBUG_EX_INFO(A) A
#else
#define DEBUG_EX_INFO(A) do { (void)0; } while (0)
#endif
#ifdef DEBUG_ESP_MDNS_ERR
#define DEBUG_EX_ERR(A) A
#else
#define DEBUG_EX_ERR(A) do { (void)0; } while (0)
#endif
#ifdef DEBUG_ESP_MDNS_TX
#define DEBUG_EX_TX(A) A
#else
#define DEBUG_EX_TX(A) do { (void)0; } while (0)
#endif
#ifdef DEBUG_ESP_MDNS_RX
#define DEBUG_EX_RX(A) A
#else
#define DEBUG_EX_RX(A) do { (void)0; } while (0)
#endif
#ifdef DEBUG_ESP_PORT
#define DEBUG_OUTPUT DEBUG_ESP_PORT
#else
#define DEBUG_OUTPUT Serial
#endif
#else #else
#define DEBUG_EX_INFO(A) do { (void)0; } while (0) #define DEBUG_EX_INFO(A) do { (void)0; } while (0)
#define DEBUG_EX_ERR(A) do { (void)0; } while (0) #endif
#define DEBUG_EX_TX(A) do { (void)0; } while (0) #ifdef DEBUG_ESP_MDNS_ERR
#define DEBUG_EX_RX(A) do { (void)0; } while (0) #define DEBUG_EX_ERR(A) A
#else
#define DEBUG_EX_ERR(A) do { (void)0; } while (0)
#endif
#ifdef DEBUG_ESP_MDNS_TX
#define DEBUG_EX_TX(A) A
#else
#define DEBUG_EX_TX(A) do { (void)0; } while (0)
#endif
#ifdef DEBUG_ESP_MDNS_RX
#define DEBUG_EX_RX(A) A
#else
#define DEBUG_EX_RX(A) do { (void)0; } while (0)
#endif
#ifdef DEBUG_ESP_PORT
#define DEBUG_OUTPUT DEBUG_ESP_PORT
#else
#define DEBUG_OUTPUT Serial
#endif
#else
#define DEBUG_EX_INFO(A) do { (void)0; } while (0)
#define DEBUG_EX_ERR(A) do { (void)0; } while (0)
#define DEBUG_EX_TX(A) do { (void)0; } while (0)
#define DEBUG_EX_RX(A) do { (void)0; } while (0)
#endif #endif
/* Replaced by 'lwip/prot/dns.h' definitions /* Replaced by 'lwip/prot/dns.h' definitions
#ifdef MDNS_IP4_SUPPORT #ifdef MDNS_IP4_SUPPORT
#define MDNS_MULTICAST_ADDR_IP4 (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT #define MDNS_MULTICAST_ADDR_IP4 (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT
#endif #endif
#ifdef MDNS_IP6_SUPPORT #ifdef MDNS_IP6_SUPPORT
#define MDNS_MULTICAST_ADDR_IP6 (IPAddress("FF02::FB")) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT #define MDNS_MULTICAST_ADDR_IP6 (IPAddress("FF02::FB")) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT
#endif*/ #endif*/
//#define MDNS_MULTICAST_PORT 5353 //#define MDNS_MULTICAST_PORT 5353
/* /*
* This is NOT the TTL (Time-To-Live) for MDNS records, but the This is NOT the TTL (Time-To-Live) for MDNS records, but the
* subnet level distance MDNS records should travel. subnet level distance MDNS records should travel.
* 1 sets the subnet distance to 'local', which is default for MDNS. 1 sets the subnet distance to 'local', which is default for MDNS.
* (Btw.: 255 would set it to 'as far as possible' -> internet) (Btw.: 255 would set it to 'as far as possible' -> internet)
*
* However, RFC 3171 seems to force 255 instead However, RFC 3171 seems to force 255 instead
*/ */
#define MDNS_MULTICAST_TTL 255/*1*/ #define MDNS_MULTICAST_TTL 255/*1*/
/* /*
* This is the MDNS record TTL This is the MDNS record TTL
* Host level records are set to 2min (120s) Host level records are set to 2min (120s)
* service level records are set to 75min (4500s) service level records are set to 75min (4500s)
*/ */
#define MDNS_HOST_TTL 120 #define MDNS_HOST_TTL 120
#define MDNS_SERVICE_TTL 4500 #define MDNS_SERVICE_TTL 4500
/* /*
* Compressed labels are flaged by the two topmost bits of the length byte being set Compressed labels are flaged by the two topmost bits of the length byte being set
*/ */
#define MDNS_DOMAIN_COMPRESS_MARK 0xC0 #define MDNS_DOMAIN_COMPRESS_MARK 0xC0
/* /*
* Avoid endless recursion because of malformed compressed labels Avoid endless recursion because of malformed compressed labels
*/ */
#define MDNS_DOMAIN_MAX_REDIRCTION 6 #define MDNS_DOMAIN_MAX_REDIRCTION 6
/* /*
* Default service priority and weight in SRV answers Default service priority and weight in SRV answers
*/ */
#define MDNS_SRV_PRIORITY 0 #define MDNS_SRV_PRIORITY 0
#define MDNS_SRV_WEIGHT 0 #define MDNS_SRV_WEIGHT 0
/* /*
* Delay between and number of probes for host and service domains Delay between and number of probes for host and service domains
* Delay between and number of announces for host and service domains Delay between and number of announces for host and service domains
* Delay between and number of service queries; the delay is multiplied by the resent number in '_checkServiceQueryCache' Delay between and number of service queries; the delay is multiplied by the resent number in '_checkServiceQueryCache'
*/ */
#define MDNS_PROBE_DELAY 250 #define MDNS_PROBE_DELAY 250
#define MDNS_PROBE_COUNT 3 #define MDNS_PROBE_COUNT 3
#define MDNS_ANNOUNCE_DELAY 1000 #define MDNS_ANNOUNCE_DELAY 1000
@ -149,24 +151,24 @@ namespace MDNSImplementation {
/* /*
* Force host domain to use only lowercase letters Force host domain to use only lowercase letters
*/ */
//#define MDNS_FORCE_LOWERCASE_HOSTNAME //#define MDNS_FORCE_LOWERCASE_HOSTNAME
/* /*
* Enable/disable the usage of the F() macro in debug trace printf calls. Enable/disable the usage of the F() macro in debug trace printf calls.
* There needs to be an PGM comptible printf function to use this. There needs to be an PGM comptible printf function to use this.
*
* USE_PGM_PRINTF and F USE_PGM_PRINTF and F
*/ */
#define USE_PGM_PRINTF #define USE_PGM_PRINTF
#ifdef USE_PGM_PRINTF #ifdef USE_PGM_PRINTF
#else #else
#ifdef F #ifdef F
#undef F #undef F
#endif #endif
#define F(A) A #define F(A) A
#endif #endif
} // namespace MDNSImplementation } // namespace MDNSImplementation

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,26 @@
/* /*
* LEAmDNS_Priv.h LEAmDNS_Priv.h
*
* License (MIT license): License (MIT license):
* Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. THE SOFTWARE.
*
*/ */
#ifndef MDNS_LWIPDEFS_H #ifndef MDNS_LWIPDEFS_H
#define MDNS_LWIPDEFS_H #define MDNS_LWIPDEFS_H

View File

@ -23,22 +23,18 @@
*/ */
#include <Arduino.h> #include <Arduino.h>
#include <bearssl/bearssl_hash.h>
#include "Hash.h" #include "Hash.h"
extern "C" {
#include "sha1/sha1.h"
}
/** /**
* create a sha1 hash from data * create a sha1 hash from data
* @param data uint8_t * * @param data uint8_t *
* @param size uint32_t * @param size uint32_t
* @param hash uint8_t[20] * @param hash uint8_t[20]
*/ */
void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]) { void sha1(const uint8_t* data, uint32_t size, uint8_t hash[20]) {
br_sha1_context ctx;
SHA1_CTX ctx;
#ifdef DEBUG_SHA1 #ifdef DEBUG_SHA1
os_printf("DATA:"); os_printf("DATA:");
@ -53,9 +49,9 @@ void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]) {
os_printf("\n"); os_printf("\n");
#endif #endif
SHA1Init(&ctx); br_sha1_init(&ctx);
SHA1Update(&ctx, data, size); br_sha1_update(&ctx, data, size);
SHA1Final(hash, &ctx); br_sha1_out(&ctx, hash);
#ifdef DEBUG_SHA1 #ifdef DEBUG_SHA1
os_printf("SHA1:"); os_printf("SHA1:");
@ -66,52 +62,35 @@ void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]) {
#endif #endif
} }
void sha1(char * data, uint32_t size, uint8_t hash[20]) { void sha1(const char* data, uint32_t size, uint8_t hash[20]) {
sha1((uint8_t *) data, size, hash); sha1((const uint8_t *) data, size, hash);
} }
void sha1(const uint8_t * data, uint32_t size, uint8_t hash[20]) { void sha1(const String& data, uint8_t hash[20]) {
sha1((uint8_t *) data, size, hash);
}
void sha1(const char * data, uint32_t size, uint8_t hash[20]) {
sha1((uint8_t *) data, size, hash);
}
void sha1(String data, uint8_t hash[20]) {
sha1(data.c_str(), data.length(), hash); sha1(data.c_str(), data.length(), hash);
} }
String sha1(uint8_t* data, uint32_t size) { String sha1(const uint8_t* data, uint32_t size) {
uint8_t hash[20]; uint8_t hash[20];
String hashStr = ""; String hashStr((const char*)nullptr);
hashStr.reserve(20 * 2 + 1);
sha1(&data[0], size, &hash[0]); sha1(&data[0], size, &hash[0]);
for(uint16_t i = 0; i < 20; i++) { for(uint16_t i = 0; i < 20; i++) {
String hex = String(hash[i], HEX); char hex[3];
if(hex.length() < 2) { snprintf(hex, sizeof(hex), "%02x", hash[i]);
hex = "0" + hex;
}
hashStr += hex; hashStr += hex;
} }
return hashStr; return hashStr;
} }
String sha1(char* data, uint32_t size) {
return sha1((uint8_t*) data, size);
}
String sha1(const uint8_t* data, uint32_t size) {
return sha1((uint8_t*) data, size);
}
String sha1(const char* data, uint32_t size) { String sha1(const char* data, uint32_t size) {
return sha1((uint8_t*) data, size); return sha1((const uint8_t*) data, size);
} }
String sha1(String data) { String sha1(const String& data) {
return sha1(data.c_str(), data.length()); return sha1(data.c_str(), data.length());
} }

View File

@ -27,16 +27,12 @@
//#define DEBUG_SHA1 //#define DEBUG_SHA1
void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]); void sha1(const uint8_t* data, uint32_t size, uint8_t hash[20]);
void sha1(char * data, uint32_t size, uint8_t hash[20]); void sha1(const char* data, uint32_t size, uint8_t hash[20]);
void sha1(const uint8_t * data, uint32_t size, uint8_t hash[20]); void sha1(const String& data, uint8_t hash[20]);
void sha1(const char * data, uint32_t size, uint8_t hash[20]);
void sha1(String data, uint8_t hash[20]);
String sha1(uint8_t* data, uint32_t size);
String sha1(char* data, uint32_t size);
String sha1(const uint8_t* data, uint32_t size); String sha1(const uint8_t* data, uint32_t size);
String sha1(const char* data, uint32_t size); String sha1(const char* data, uint32_t size);
String sha1(String data); String sha1(const String& data);
#endif /* HASH_H_ */ #endif /* HASH_H_ */

View File

@ -1,208 +0,0 @@
/**
* @file sha1.c
* @date 20.05.2015
* @author Steve Reid <steve@edmweb.com>
*
* from: http://www.virtualbox.org/svn/vbox/trunk/src/recompiler/tests/sha1.c
*/
/* from valgrind tests */
/* ================ sha1.c ================ */
/*
SHA-1 in C
By Steve Reid <steve@edmweb.com>
100% Public Domain
Test Vectors (from FIPS PUB 180-1)
"abc"
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
A million repetitions of "a"
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
/* #define SHA1HANDSOFF * Copies data before messing with it. */
#define SHA1HANDSOFF
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <c_types.h>
#include "sha1.h"
//#include <endian.h>
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
#if BYTE_ORDER == LITTLE_ENDIAN
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|(rol(block->l[i],8)&0x00FF00FF))
#elif BYTE_ORDER == BIG_ENDIAN
#define blk0(i) block->l[i]
#else
#error "Endianness not defined!"
#endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
/* Hash a single 512-bit block. This is the core of the algorithm. */
void ICACHE_FLASH_ATTR SHA1Transform(uint32_t state[5], uint8_t buffer[64])
{
uint32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
uint32_t l[16];
} CHAR64LONG16;
#ifdef SHA1HANDSOFF
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
memcpy(block, buffer, 64);
#else
/* The following had better never be used because it causes the
* pointer-to-const buffer to be cast into a pointer to non-const.
* And the result is written through. I threw a "const" in, hoping
* this will cause a diagnostic.
*/
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
#endif
/* Copy context->state[] to working vars */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
/* Wipe variables */
a = b = c = d = e = 0;
#ifdef SHA1HANDSOFF
memset(block, '\0', sizeof(block));
#endif
}
/* SHA1Init - Initialize new context */
void ICACHE_FLASH_ATTR SHA1Init(SHA1_CTX* context)
{
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
/* Run your data through this. */
void ICACHE_FLASH_ATTR SHA1Update(SHA1_CTX* context, uint8_t* data, uint32_t len)
{
uint32_t i;
uint32_t j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
context->count[1]++;
context->count[1] += (len>>29);
j = (j >> 3) & 63;
if ((j + len) > 63) {
memcpy(&context->buffer[j], data, (i = 64-j));
SHA1Transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64) {
SHA1Transform(context->state, &data[i]);
}
j = 0;
}
else i = 0;
memcpy(&context->buffer[j], &data[i], len - i);
}
/* Add padding and return the message digest. */
void ICACHE_FLASH_ATTR SHA1Final(unsigned char digest[20], SHA1_CTX* context)
{
unsigned i;
unsigned char finalcount[8];
unsigned char c;
#if 0 /* untested "improvement" by DHR */
/* Convert context->count to a sequence of bytes
* in finalcount. Second element first, but
* big-endian order within element.
* But we do it all backwards.
*/
unsigned char *fcp = &finalcount[8];
for (i = 0; i < 2; i++)
{
uint32_t t = context->count[i];
int j;
for (j = 0; j < 4; t >>= 8, j++)
*--fcp = (unsigned char) t;
}
#else
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
}
#endif
c = 0200;
SHA1Update(context, &c, 1);
while ((context->count[0] & 504) != 448) {
c = 0000;
SHA1Update(context, &c, 1);
}
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
for (i = 0; i < 20; i++) {
digest[i] = (unsigned char)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
/* Wipe variables */
memset(context, '\0', sizeof(*context));
memset(&finalcount, '\0', sizeof(finalcount));
}
/* ================ end of sha1.c ================ */

View File

@ -1,32 +0,0 @@
/**
* @file sha1.h
* @date 20.05.2015
* @author Steve Reid <steve@edmweb.com>
*
* from: http://www.virtualbox.org/svn/vbox/trunk/src/recompiler/tests/sha1.c
*/
/* ================ sha1.h ================ */
/*
SHA-1 in C
By Steve Reid <steve@edmweb.com>
100% Public Domain
*/
#ifndef SHA1_H_
#define SHA1_H_
typedef struct {
uint32_t state[5];
uint32_t count[2];
unsigned char buffer[64];
} SHA1_CTX;
void SHA1Transform(uint32_t state[5], uint8_t buffer[64]);
void SHA1Init(SHA1_CTX* context);
void SHA1Update(SHA1_CTX* context, uint8_t* data, uint32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
#endif /* SHA1_H_ */
/* ================ end of sha1.h ================ */

View File

@ -24,7 +24,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <stdlib.h> #include <stdlib.h>
#define SPI_HAS_TRANSACTION #define SPI_HAS_TRANSACTION 1
// This defines are not representing the real Divider of the ESP8266 // This defines are not representing the real Divider of the ESP8266
// the Defines match to an AVR Arduino on 16MHz for better compatibility // the Defines match to an AVR Arduino on 16MHz for better compatibility

@ -1 +1 @@
Subproject commit 776d49b2f570b93fe88ad7a082519b4fb56be817 Subproject commit 8f85d649000b5bbdde1862d879dba4f225dd3a51

View File

@ -23,9 +23,9 @@
*/ */
extern "C" { extern "C" {
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <inttypes.h> #include <inttypes.h>
} }
#include "twi.h" #include "twi.h"
@ -58,18 +58,20 @@ static int default_scl_pin = SCL;
// Constructors //////////////////////////////////////////////////////////////// // Constructors ////////////////////////////////////////////////////////////////
TwoWire::TwoWire(){} TwoWire::TwoWire() {}
// Public Methods ////////////////////////////////////////////////////////////// // Public Methods //////////////////////////////////////////////////////////////
void TwoWire::begin(int sda, int scl){ void TwoWire::begin(int sda, int scl)
{
default_sda_pin = sda; default_sda_pin = sda;
default_scl_pin = scl; default_scl_pin = scl;
twi_init(sda, scl); twi_init(sda, scl);
flush(); flush();
} }
void TwoWire::begin(int sda, int scl, uint8_t address){ void TwoWire::begin(int sda, int scl, uint8_t address)
{
default_sda_pin = sda; default_sda_pin = sda;
default_scl_pin = scl; default_scl_pin = scl;
twi_setAddress(address); twi_setAddress(address);
@ -79,76 +81,92 @@ void TwoWire::begin(int sda, int scl, uint8_t address){
flush(); flush();
} }
void TwoWire::pins(int sda, int scl){ void TwoWire::pins(int sda, int scl)
{
default_sda_pin = sda; default_sda_pin = sda;
default_scl_pin = scl; default_scl_pin = scl;
} }
void TwoWire::begin(void){ void TwoWire::begin(void)
{
begin(default_sda_pin, default_scl_pin); begin(default_sda_pin, default_scl_pin);
} }
void TwoWire::begin(uint8_t address){ void TwoWire::begin(uint8_t address)
{
twi_setAddress(address); twi_setAddress(address);
twi_attachSlaveTxEvent(onRequestService); twi_attachSlaveTxEvent(onRequestService);
twi_attachSlaveRxEvent(onReceiveService); twi_attachSlaveRxEvent(onReceiveService);
begin(); begin();
} }
uint8_t TwoWire::status(){ uint8_t TwoWire::status()
{
return twi_status(); return twi_status();
} }
void TwoWire::begin(int address){ void TwoWire::begin(int address)
{
begin((uint8_t)address); begin((uint8_t)address);
} }
void TwoWire::setClock(uint32_t frequency){ void TwoWire::setClock(uint32_t frequency)
{
twi_setClock(frequency); twi_setClock(frequency);
} }
void TwoWire::setClockStretchLimit(uint32_t limit){ void TwoWire::setClockStretchLimit(uint32_t limit)
{
twi_setClockStretchLimit(limit); twi_setClockStretchLimit(limit);
} }
size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop)
if(size > BUFFER_LENGTH){ {
if (size > BUFFER_LENGTH)
{
size = BUFFER_LENGTH; size = BUFFER_LENGTH;
} }
size_t read = (twi_readFrom(address, rxBuffer, size, sendStop) == 0)?size:0; size_t read = (twi_readFrom(address, rxBuffer, size, sendStop) == 0) ? size : 0;
rxBufferIndex = 0; rxBufferIndex = 0;
rxBufferLength = read; rxBufferLength = read;
return read; return read;
} }
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop){ uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)
{
return requestFrom(address, static_cast<size_t>(quantity), static_cast<bool>(sendStop)); return requestFrom(address, static_cast<size_t>(quantity), static_cast<bool>(sendStop));
} }
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity){ uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity)
{
return requestFrom(address, static_cast<size_t>(quantity), true); return requestFrom(address, static_cast<size_t>(quantity), true);
} }
uint8_t TwoWire::requestFrom(int address, int quantity){ uint8_t TwoWire::requestFrom(int address, int quantity)
{
return requestFrom(static_cast<uint8_t>(address), static_cast<size_t>(quantity), true); return requestFrom(static_cast<uint8_t>(address), static_cast<size_t>(quantity), true);
} }
uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop){ uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop)
{
return requestFrom(static_cast<uint8_t>(address), static_cast<size_t>(quantity), static_cast<bool>(sendStop)); return requestFrom(static_cast<uint8_t>(address), static_cast<size_t>(quantity), static_cast<bool>(sendStop));
} }
void TwoWire::beginTransmission(uint8_t address){ void TwoWire::beginTransmission(uint8_t address)
{
transmitting = 1; transmitting = 1;
txAddress = address; txAddress = address;
txBufferIndex = 0; txBufferIndex = 0;
txBufferLength = 0; txBufferLength = 0;
} }
void TwoWire::beginTransmission(int address){ void TwoWire::beginTransmission(int address)
{
beginTransmission((uint8_t)address); beginTransmission((uint8_t)address);
} }
uint8_t TwoWire::endTransmission(uint8_t sendStop){ uint8_t TwoWire::endTransmission(uint8_t sendStop)
{
int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, sendStop); int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, sendStop);
txBufferIndex = 0; txBufferIndex = 0;
txBufferLength = 0; txBufferLength = 0;
@ -156,40 +174,56 @@ uint8_t TwoWire::endTransmission(uint8_t sendStop){
return ret; return ret;
} }
uint8_t TwoWire::endTransmission(void){ uint8_t TwoWire::endTransmission(void)
{
return endTransmission(true); return endTransmission(true);
} }
size_t TwoWire::write(uint8_t data){ size_t TwoWire::write(uint8_t data)
if(transmitting){ {
if(txBufferLength >= BUFFER_LENGTH){ if (transmitting)
{
if (txBufferLength >= BUFFER_LENGTH)
{
setWriteError(); setWriteError();
return 0; return 0;
} }
txBuffer[txBufferIndex] = data; txBuffer[txBufferIndex] = data;
++txBufferIndex; ++txBufferIndex;
txBufferLength = txBufferIndex; txBufferLength = txBufferIndex;
} else { }
else
{
twi_transmit(&data, 1); twi_transmit(&data, 1);
} }
return 1; return 1;
} }
size_t TwoWire::write(const uint8_t *data, size_t quantity){ size_t TwoWire::write(const uint8_t *data, size_t quantity)
if(transmitting){ {
for(size_t i = 0; i < quantity; ++i){ if (transmitting)
if(!write(data[i])) return i; {
for (size_t i = 0; i < quantity; ++i)
{
if (!write(data[i]))
{
return i;
} }
}else{ }
}
else
{
twi_transmit(data, quantity); twi_transmit(data, quantity);
} }
return quantity; return quantity;
} }
int TwoWire::available(void){ int TwoWire::available(void)
{
int result = rxBufferLength - rxBufferIndex; int result = rxBufferLength - rxBufferIndex;
if (!result) { if (!result)
{
// yielding here will not make more data "available", // yielding here will not make more data "available",
// but it will prevent the system from going into WDT reset // but it will prevent the system from going into WDT reset
optimistic_yield(1000); optimistic_yield(1000);
@ -198,24 +232,29 @@ int TwoWire::available(void){
return result; return result;
} }
int TwoWire::read(void){ int TwoWire::read(void)
{
int value = -1; int value = -1;
if(rxBufferIndex < rxBufferLength){ if (rxBufferIndex < rxBufferLength)
{
value = rxBuffer[rxBufferIndex]; value = rxBuffer[rxBufferIndex];
++rxBufferIndex; ++rxBufferIndex;
} }
return value; return value;
} }
int TwoWire::peek(void){ int TwoWire::peek(void)
{
int value = -1; int value = -1;
if(rxBufferIndex < rxBufferLength){ if (rxBufferIndex < rxBufferLength)
{
value = rxBuffer[rxBufferIndex]; value = rxBuffer[rxBufferIndex];
} }
return value; return value;
} }
void TwoWire::flush(void){ void TwoWire::flush(void)
{
rxBufferIndex = 0; rxBufferIndex = 0;
rxBufferLength = 0; rxBufferLength = 0;
txBufferIndex = 0; txBufferIndex = 0;
@ -225,7 +264,8 @@ void TwoWire::flush(void){
void TwoWire::onReceiveService(uint8_t* inBytes, size_t numBytes) void TwoWire::onReceiveService(uint8_t* inBytes, size_t numBytes)
{ {
// don't bother if user hasn't registered a callback // don't bother if user hasn't registered a callback
if (!user_onReceive) { if (!user_onReceive)
{
return; return;
} }
// // don't bother if rx buffer is in use by a master requestFrom() op // // don't bother if rx buffer is in use by a master requestFrom() op
@ -237,7 +277,8 @@ void TwoWire::onReceiveService(uint8_t* inBytes, size_t numBytes)
// copy twi rx buffer into local read buffer // copy twi rx buffer into local read buffer
// this enables new reads to happen in parallel // this enables new reads to happen in parallel
for (uint8_t i = 0; i < numBytes; ++i) { for (uint8_t i = 0; i < numBytes; ++i)
{
rxBuffer[i] = inBytes[i]; rxBuffer[i] = inBytes[i];
} }
@ -252,7 +293,8 @@ void TwoWire::onReceiveService(uint8_t* inBytes, size_t numBytes)
void TwoWire::onRequestService(void) void TwoWire::onRequestService(void)
{ {
// don't bother if user hasn't registered a callback // don't bother if user hasn't registered a callback
if (!user_onRequest) { if (!user_onRequest)
{
return; return;
} }
@ -265,19 +307,24 @@ void TwoWire::onRequestService(void)
user_onRequest(); user_onRequest();
} }
void TwoWire::onReceive( void (*function)(int) ) { void TwoWire::onReceive(void (*function)(int))
{
// arduino api compatibility fixer: // arduino api compatibility fixer:
// really hope size parameter will not exceed 2^31 :) // really hope size parameter will not exceed 2^31 :)
static_assert(sizeof(int) == sizeof(size_t), "something is wrong in Arduino kingdom"); static_assert(sizeof(int) == sizeof(size_t), "something is wrong in Arduino kingdom");
user_onReceive = reinterpret_cast<void(*)(size_t)>(function); user_onReceive = reinterpret_cast<void(*)(size_t)>(function);
} }
void TwoWire::onReceive( void (*function)(size_t) ) { void TwoWire::onReceive(void (*function)(size_t))
{
user_onReceive = function; user_onReceive = function;
twi_enableSlaveMode();
} }
void TwoWire::onRequest( void (*function)(void) ){ void TwoWire::onRequest(void (*function)(void))
{
user_onRequest = function; user_onRequest = function;
twi_enableSlaveMode();
} }
// Preinstantiate Objects ////////////////////////////////////////////////////// // Preinstantiate Objects //////////////////////////////////////////////////////

View File

@ -33,7 +33,7 @@
class TwoWire : public Stream class TwoWire : public Stream
{ {
private: private:
static uint8_t rxBuffer[]; static uint8_t rxBuffer[];
static uint8_t rxBufferIndex; static uint8_t rxBufferIndex;
static uint8_t rxBufferLength; static uint8_t rxBufferLength;
@ -48,7 +48,7 @@ class TwoWire : public Stream
static void (*user_onReceive)(size_t); static void (*user_onReceive)(size_t);
static void onRequestService(void); static void onRequestService(void);
static void onReceiveService(uint8_t*, size_t); static void onReceiveService(uint8_t*, size_t);
public: public:
TwoWire(); TwoWire();
void begin(int sda, int scl); void begin(int sda, int scl);
void begin(int sda, int scl, uint8_t address); void begin(int sda, int scl, uint8_t address);
@ -76,9 +76,9 @@ class TwoWire : public Stream
virtual int read(void); virtual int read(void);
virtual int peek(void); virtual int peek(void);
virtual void flush(void); virtual void flush(void);
void onReceive( void (*)(int) ); // arduino api void onReceive(void (*)(int)); // arduino api
void onReceive( void (*)(size_t) ); // legacy esp8266 backward compatibility void onReceive(void (*)(size_t)); // legacy esp8266 backward compatibility
void onRequest( void (*)(void) ); void onRequest(void (*)(void));
using Print::write; using Print::write;
}; };

View File

@ -1,80 +1,160 @@
/* /*
NTP-TZ-DST NTP-TZ-DST (v2)
NetWork Time Protocol - Time Zone - Daylight Saving Time NetWork Time Protocol - Time Zone - Daylight Saving Time
This example shows how to read and set time, This example shows:
and how to use NTP (set NTP0_OR_LOCAL1 to 0 below) - how to read and set time
or an external RTC (set NTP0_OR_LOCAL1 to 1 below) - how to set timezone per country/city
- how is local time automatically handled per official timezone definitions
TZ and DST below have to be manually set - how to change internal sntp start and update delay
according to your local settings. - how to use callbacks when time is updated
This example code is in the public domain. This example code is in the public domain.
*/ */
#include <ESP8266WiFi.h>
#include <time.h> // time() ctime()
#include <sys/time.h> // struct timeval
#include <coredecls.h> // settimeofday_cb()
////////////////////////////////////////////////////////
#ifndef STASSID #ifndef STASSID
#define STASSID "your-ssid" #define STASSID "your-ssid"
#define STAPSK "your-password" #define STAPSK "your-password"
#endif #endif
#define SSID STASSID // initial time (possibly given by an external RTC)
#define SSIDPWD STAPSK #define RTC_UTC_TEST 1510592825 // 1510592825 = Monday 13 November 2017 17:07:05 UTC
#define TZ 1 // (utc+) TZ in hours
#define DST_MN 60 // use 60mn for summer time in some countries
#define NTP0_OR_LOCAL1 1 // 0:use NTP 1:fake external RTC
#define RTC_TEST 1510592825 // 1510592825 = Monday 13 November 2017 17:07:05 UTC // This database is autogenerated from IANA timezone database
// https://www.iana.org/time-zones
// and can be updated on demand in this repository
#include <TZ.h>
// "TZ_" macros follow DST change across seasons without source code change
// check for your nearest city in TZ.h
// espressif headquarter TZ
//#define MYTZ TZ_Asia_Shanghai
// example for "Not Only Whole Hours" timezones:
// Kolkata/Calcutta is shifted by 30mn
//#define MYTZ TZ_Asia_Kolkata
// example of a timezone with a variable Daylight-Saving-Time:
// demo: watch automatic time adjustment on Summer/Winter change (DST)
#define MYTZ TZ_Europe_London
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
#define TZ_MN ((TZ)*60) #include <ESP8266WiFi.h>
#define TZ_SEC ((TZ)*3600) #include <coredecls.h> // settimeofday_cb()
#define DST_SEC ((DST_MN)*60) #include <Schedule.h>
#include <PolledTimeout.h>
timeval cbtime; // time set in callback #include <time.h> // time() ctime()
bool cbtime_set = false; #include <sys/time.h> // struct timeval
void time_is_set(void) { #include <sntp.h> // sntp_servermode_dhcp()
gettimeofday(&cbtime, NULL);
cbtime_set = true;
Serial.println("------------------ settimeofday() was called ------------------");
}
void setup() {
Serial.begin(115200);
settimeofday_cb(time_is_set);
#if NTP0_OR_LOCAL1
// local
ESP.eraseConfig();
time_t rtc = RTC_TEST;
timeval tv = { rtc, 0 };
timezone tz = { TZ_MN + DST_MN, 0 };
settimeofday(&tv, &tz);
#else // ntp
configTime(TZ_SEC, DST_SEC, "pool.ntp.org");
WiFi.mode(WIFI_STA);
WiFi.begin(SSID, SSIDPWD);
// don't wait, observe time changing when ntp timestamp is received
#endif // ntp
}
// for testing purpose: // for testing purpose:
extern "C" int clock_gettime(clockid_t unused, struct timespec *tp); extern "C" int clock_gettime(clockid_t unused, struct timespec *tp);
////////////////////////////////////////////////////////
static timeval tv;
static timespec tp;
static time_t now;
static uint32_t now_ms, now_us;
static esp8266::polledTimeout::periodicMs showTimeNow(60000);
static int time_machine_days = 0; // 0 = now
static bool time_machine_running = false;
// OPTIONAL: change SNTP startup delay
// a weak function is already defined and returns 0 (RFC violation)
// it can be redefined:
//uint32_t sntp_startup_delay_MS_rfc_not_less_than_60000 ()
//{
// //info_sntp_startup_delay_MS_rfc_not_less_than_60000_has_been_called = true;
// return 60000; // 60s (or lwIP's original default: (random() % 5000))
//}
// OPTIONAL: change SNTP update delay
// a weak function is already defined and returns 1 hour
// it can be redefined:
//uint32_t sntp_update_delay_MS_rfc_not_less_than_15000 ()
//{
// //info_sntp_update_delay_MS_rfc_not_less_than_15000_has_been_called = true;
// return 15000; // 15s
//}
void showTime() {
gettimeofday(&tv, nullptr);
clock_gettime(0, &tp);
now = time(nullptr);
now_ms = millis();
now_us = micros();
Serial.println();
printTm("localtime:", localtime(&now));
Serial.println();
printTm("gmtime: ", gmtime(&now));
Serial.println();
// time from boot
Serial.print("clock: ");
Serial.print((uint32_t)tp.tv_sec);
Serial.print("s / ");
Serial.print((uint32_t)tp.tv_nsec);
Serial.println("ns");
// time from boot
Serial.print("millis: ");
Serial.println(now_ms);
Serial.print("micros: ");
Serial.println(now_us);
// EPOCH+tz+dst
Serial.print("gtod: ");
Serial.print((uint32_t)tv.tv_sec);
Serial.print("s / ");
Serial.print((uint32_t)tv.tv_usec);
Serial.println("us");
// EPOCH+tz+dst
Serial.print("time: ");
Serial.println((uint32_t)now);
// timezone and demo in the future
Serial.printf("timezone: %s\n", MYTZ);
// human readable
Serial.print("ctime: ");
Serial.print(ctime(&now));
#if LWIP_VERSION_MAJOR > 1
// LwIP v2 is able to list more details about the currently configured SNTP servers
for (int i = 0; i < SNTP_MAX_SERVERS; i++) {
IPAddress sntp = *sntp_getserver(i);
const char* name = sntp_getservername(i);
if (sntp.isSet()) {
Serial.printf("sntp%d: ", i);
if (name) {
Serial.printf("%s (%s) ", name, sntp.toString().c_str());
} else {
Serial.printf("%s ", sntp.toString().c_str());
}
Serial.printf("IPv6: %s Reachability: %o\n",
sntp.isV6() ? "Yes" : "No",
sntp_getreachability(i));
}
}
#endif
Serial.println();
}
#define PTM(w) \ #define PTM(w) \
Serial.print(":" #w "="); \ Serial.print(" " #w "="); \
Serial.print(tm->tm_##w); Serial.print(tm->tm_##w);
void printTm(const char* what, const tm* tm) { void printTm(const char* what, const tm* tm) {
@ -84,61 +164,74 @@ void printTm(const char* what, const tm* tm) {
PTM(hour); PTM(min); PTM(sec); PTM(hour); PTM(min); PTM(sec);
} }
timeval tv; void time_is_set_scheduled() {
timespec tp; // everything is allowed in this function
time_t now;
uint32_t now_ms, now_us;
void loop() { if (time_machine_days == 0) {
time_machine_running = !time_machine_running;
gettimeofday(&tv, nullptr);
clock_gettime(0, &tp);
now = time(nullptr);
now_ms = millis();
now_us = micros();
// localtime / gmtime every second change
static time_t lastv = 0;
if (lastv != tv.tv_sec) {
lastv = tv.tv_sec;
Serial.println();
printTm("localtime", localtime(&now));
Serial.println();
printTm("gmtime ", gmtime(&now));
Serial.println();
Serial.println();
} }
// time from boot // time machine demo
Serial.print("clock:"); if (time_machine_running) {
Serial.print((uint32_t)tp.tv_sec); if (time_machine_days == 0)
Serial.print("/"); Serial.printf("---- settimeofday() has been called - possibly from SNTP\n"
Serial.print((uint32_t)tp.tv_nsec); " (starting time machine demo to show libc's automatic DST handling)\n\n");
Serial.print("ns"); now = time(nullptr);
const tm* tm = localtime(&now);
// time from boot Serial.printf("future=%3ddays: DST=%s - ",
Serial.print(" millis:"); time_machine_days,
Serial.print(now_ms); tm->tm_isdst ? "true " : "false");
Serial.print(" micros:");
Serial.print(now_us);
// EPOCH+tz+dst
Serial.print(" gtod:");
Serial.print((uint32_t)tv.tv_sec);
Serial.print("/");
Serial.print((uint32_t)tv.tv_usec);
Serial.print("us");
// EPOCH+tz+dst
Serial.print(" time:");
Serial.print((uint32_t)now);
// human readable
Serial.print(" ctime:(UTC+");
Serial.print((uint32_t)(TZ * 60 + DST_MN));
Serial.print("mn)");
Serial.print(ctime(&now)); Serial.print(ctime(&now));
gettimeofday(&tv, nullptr);
// simple drifting loop constexpr int days = 30;
delay(100); time_machine_days += days;
if (time_machine_days > 360) {
tv.tv_sec -= (time_machine_days - days) * 60 * 60 * 24;
time_machine_days = 0;
} else {
tv.tv_sec += days * 60 * 60 * 24;
}
settimeofday(&tv, nullptr);
} else {
showTime();
}
}
void setup() {
Serial.begin(115200);
Serial.println("\nStarting...\n");
// setup RTC time
// it will be used until NTP server will send us real current time
time_t rtc = RTC_UTC_TEST;
timeval tv = { rtc, 0 };
timezone tz = { 0, 0 };
settimeofday(&tv, &tz);
// install callback - called when settimeofday is called (by SNTP or us)
// once enabled (by DHCP), SNTP is updated every hour
settimeofday_cb(time_is_set_scheduled);
// NTP servers may be overriden by your DHCP server for a more local one
// (see below)
configTime(MYTZ, "pool.ntp.org");
// OPTIONAL: disable obtaining SNTP servers from DHCP
//sntp_servermode_dhcp(0); // 0: disable obtaining SNTP servers from DHCP (enabled by default)
// start network
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(STASSID, STAPSK);
// don't wait for network, observe time changing
// when NTP timestamp is received
Serial.printf("Time is currently set by a constant:\n");
showTime();
}
void loop() {
if (showTimeNow) {
showTime();
}
} }

View File

@ -7,4 +7,4 @@ paragraph=
category=Other category=Other
url= url=
architectures=esp8266 architectures=esp8266
dot_a_linkage=true dot_a_linkage=false

View File

@ -107,18 +107,23 @@ Here is an overview of the release process. See the section below for detailed i
The following points assume work in a direct clone of the repository, and not in a personal fork. The following points assume work in a direct clone of the repository, and not in a personal fork.
2. Update `version` to the release in platform.txt and commit. E.g. `2.5.0`. Make a PR, wait for Travis CI, and merge. 2. Make a PR with the following, wait for Travis CI, and merge.
3. Tag the latest commit on the master branch. In this project, tags have form `X.Y.Z`, e.g. `2.4.0`, or `X.Y.Z-betaN` for release candiate versions. Notice that there's no `v`at the beginning of the tag. Tags must be annotated, not lightweight tags. To create a tag, use git command (assuming that the master branch is checked out): * platform.txt: update `version` to the release E.g. `3.0.0`,
* `cores/esp8266/TZ.h`: import the latest database with the following shell command:\
`$ cd tools; sh TZupdate.sh`.
3. Tag the latest commit on the master branch. In this project, tags have form `X.Y.Z`, e.g. `3.0.0`, or `X.Y.Z-betaN` for release candiate versions. Notice that there's no `v`at the beginning of the tag. Tags must be annotated, not lightweight tags. To create a tag, use git command (assuming that the master branch is checked out):
``` ```
git tag -a -m "Release 2.5.0" 2.5.0 git tag -a -m "Release 3.0.0" 3.0.0
``` ```
then push the tag created in step 3 to esp8266/Arduino Github repository: then push the tag created in step 3 to esp8266/Arduino Github repository:
``` ```
git push origin 2.5.0 git push origin 3.0.0
``` ```
4. In case something goes wrong, release can be canceled at any time: 4. In case something goes wrong, release can be canceled at any time:
@ -141,7 +146,7 @@ The following points assume work in a direct clone of the repository, and not in
11. Create a commit to the master branch, updating: 11. Create a commit to the master branch, updating:
* The version in platform.txt file. This should correspond to the version of the *next* milestone, plus `-dev` suffix. E.g. `2.5.0-dev`. * The version in platform.txt file. This should correspond to the version of the *next* milestone, plus `-dev` suffix. E.g. `3.1.0-dev`.
* In main README.md: * In main README.md:

View File

@ -3,25 +3,37 @@
#set -x #set -x
# Extract next version from platform.txt ver=`git describe --tag`
next=`sed -n -E 's/version=([0-9.]+)/\1/p' ../platform.txt` visiblever=$ver
if [ "$ver" = 0.0.1 ]; then
# Figure out how will the package be called git tag -d 0.0.1
ver=`git describe --exact-match` ver=`git describe --tag HEAD`
if [ $? -ne 0 ]; then plain_ver=$ver
else
# Extract next version from platform.txt
next=`sed -n -E 's/version=([0-9.]+)/\1/p' ../platform.txt`
# Figure out how will the package be called
ver=`git describe --exact-match`
if [ $? -ne 0 ]; then
# not tagged version; generate nightly package # not tagged version; generate nightly package
date_str=`date +"%Y%m%d"` date_str=`date +"%Y%m%d"`
is_nightly=1 is_nightly=1
plain_ver="${next}-nightly" plain_ver="${next}-nightly"
ver="${plain_ver}+${date_str}" ver="${plain_ver}+${date_str}"
else else
plain_ver=$ver plain_ver=$ver
fi
visiblever=$ver
fi fi
set -e set -e
package_name=esp8266-$ver package_name=esp8266-$visiblever
echo "Version: $ver" echo "Version: $visiblever ($ver)"
echo "Package name: $package_name" echo "Package name: $package_name"
# Set REMOTE_URL environment variable to the address where the package will be # Set REMOTE_URL environment variable to the address where the package will be
@ -34,7 +46,7 @@ echo "Remote: $REMOTE_URL"
if [ -z "$PKG_URL" ]; then if [ -z "$PKG_URL" ]; then
if [ -z "$PKG_URL_PREFIX" ]; then if [ -z "$PKG_URL_PREFIX" ]; then
PKG_URL_PREFIX="$REMOTE_URL/versions/$ver" PKG_URL_PREFIX="$REMOTE_URL/versions/$visiblever"
fi fi
PKG_URL="$PKG_URL_PREFIX/$package_name.zip" PKG_URL="$PKG_URL_PREFIX/$package_name.zip"
fi fi
@ -43,9 +55,9 @@ echo "Docs: $DOC_URL"
pushd .. pushd ..
# Create directory for the package # Create directory for the package
outdir=package/versions/$ver/$package_name outdir=package/versions/$visiblever/$package_name
srcdir=$PWD srcdir=$PWD
rm -rf package/versions/$ver rm -rf package/versions/$visiblever
mkdir -p $outdir mkdir -p $outdir
# Some files should be excluded from the package # Some files should be excluded from the package
@ -96,7 +108,7 @@ echo \#define ARDUINO_ESP8266_RELEASE_$ver_define >>$outdir/cores/esp8266/core_v
echo \#define ARDUINO_ESP8266_RELEASE \"$ver_define\" >>$outdir/cores/esp8266/core_version.h echo \#define ARDUINO_ESP8266_RELEASE \"$ver_define\" >>$outdir/cores/esp8266/core_version.h
# Zip the package # Zip the package
pushd package/versions/$ver pushd package/versions/$visiblever
echo "Making $package_name.zip" echo "Making $package_name.zip"
zip -qr $package_name.zip $package_name zip -qr $package_name.zip $package_name
rm -rf $package_name rm -rf $package_name
@ -109,7 +121,7 @@ echo SHA-256: $sha
echo "Making package_esp8266com_index.json" echo "Making package_esp8266com_index.json"
jq_arg=".packages[0].platforms[0].version = \"$ver\" | \ jq_arg=".packages[0].platforms[0].version = \"$visiblever\" | \
.packages[0].platforms[0].url = \"$PKG_URL\" |\ .packages[0].platforms[0].url = \"$PKG_URL\" |\
.packages[0].platforms[0].archiveFileName = \"$package_name.zip\"" .packages[0].platforms[0].archiveFileName = \"$package_name.zip\""

View File

@ -39,7 +39,7 @@ build.stdcpp_level=-std=gnu++11
build.float=-u _printf_float -u _scanf_float build.float=-u _printf_float -u _scanf_float
build.led= build.led=
build.sdk=NONOSDK22y build.sdk=NONOSDK22x_190703
compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/ compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/
compiler.sdk.path={runtime.platform.path}/tools/sdk compiler.sdk.path={runtime.platform.path}/tools/sdk
@ -137,7 +137,8 @@ tools.esptool.cmd={runtime.platform.path}/tools/python3/python3
tools.esptool.network_cmd={runtime.platform.path}/tools/python3/python3 tools.esptool.network_cmd={runtime.platform.path}/tools/python3/python3
tools.esptool.upload.protocol=esp tools.esptool.upload.protocol=esp
tools.esptool.upload.params.verbose=--trace # esptool.py --trace option is a debug option, not a verbose option
tools.esptool.upload.params.verbose=
tools.esptool.upload.params.quiet= tools.esptool.upload.params.quiet=
# First, potentially perform an erase or nothing # First, potentially perform an erase or nothing

View File

@ -9,6 +9,7 @@ ${org}/../restyle.sh
# Revert changes which astyle might have done to the submodules, # Revert changes which astyle might have done to the submodules,
# as we don't want to fail the build because of the 3rd party libraries # as we don't want to fail the build because of the 3rd party libraries
git submodule foreach --recursive git reset --hard git --version || true
git submodule foreach --recursive 'git reset --hard'
git diff --exit-code -- $TRAVIS_BUILD_DIR/libraries git diff --exit-code -- $TRAVIS_BUILD_DIR/libraries

View File

@ -153,19 +153,31 @@ function install_libraries()
function install_ide() function install_ide()
{ {
#local idever='nightly'
#local ideurl='https://www.arduino.cc/download.php?f=/arduino-nightly'
local idever='1.8.10'
local ideurl="https://downloads.arduino.cc/arduino-$idever"
echo "using Arduino IDE distribution ${idever}"
local ide_path=$1 local ide_path=$1
local core_path=$2 local core_path=$2
local debug=$3 local debug=$3
if [ "$WINDOWS" = "1" ]; then if [ "$WINDOWS" = "1" ]; then
# Acquire needed packages from Windows package manager # Acquire needed packages from Windows package manager
choco install --no-progress python3 choco install --no-progress python3 >& pylog.txt
export PATH="/c/Python37:$PATH" # Ensure it's live from now on... # Parse the python instrall dir from the output log. Sorry, can't set it via choco on the free version
cp /c/Python37/python.exe /c/Python37/python3.exe PYDIR=$(cat pylog.txt | grep "^Installed to:" | cut -f2 -d"'" | sed 's/C:\\/\/c\//')
echo "Detected python3 install dir: $PYDIR"
export PATH="$PYDIR:$PATH" # Ensure it's live from now on...
cp "$PYDIR/python.exe" "$PYDIR/python3.exe"
choco install --no-progress unzip choco install --no-progress unzip
choco install --no-progress sed choco install --no-progress sed
#choco install --no-progress golang #choco install --no-progress golang
test -r arduino-nightly-windows.zip || wget -nv -O arduino-nightly-windows.zip https://www.arduino.cc/download.php?f=/arduino-nightly-windows.zip test -r arduino-windows.zip || wget -nv -O arduino-windows.zip "${ideurl}-windows.zip"
unzip -q arduino-nightly-windows.zip unzip -q arduino-windows.zip
mv arduino-${idever} arduino-distrib
elif [ "$MACOSX" = "1" ]; then elif [ "$MACOSX" = "1" ]; then
# MACOS only has next-to-obsolete Python2 installed. Install Python 3 from python.org # MACOS only has next-to-obsolete Python2 installed. Install Python 3 from python.org
wget https://www.python.org/ftp/python/3.7.4/python-3.7.4-macosx10.9.pkg wget https://www.python.org/ftp/python/3.7.4/python-3.7.4-macosx10.9.pkg
@ -173,15 +185,17 @@ function install_ide()
# Install the Python3 certificates, because SSL connections fail w/o them and of course they aren't installed by default. # Install the Python3 certificates, because SSL connections fail w/o them and of course they aren't installed by default.
( cd "/Applications/Python 3.7/" && sudo "./Install Certificates.command" ) ( cd "/Applications/Python 3.7/" && sudo "./Install Certificates.command" )
# Hack to place arduino-builder in the same spot as sane OSes # Hack to place arduino-builder in the same spot as sane OSes
test -r arduino.zip || wget -O arduino.zip https://downloads.arduino.cc/arduino-nightly-macosx.zip test -r arduino-macos.zip || wget -O arduino-macos.zip "${ideurl}-macosx.zip"
unzip -q arduino.zip unzip -q arduino-macos.zip
mv Arduino.app arduino-nightly mv Arduino.app arduino-distrib
mv arduino-nightly/Contents/Java/* arduino-nightly/. mv arduino-distrib/Contents/Java/* arduino-distrib/.
else else
test -r arduino.tar.xz || wget -O arduino.tar.xz https://www.arduino.cc/download.php?f=/arduino-nightly-linux64.tar.xz #test -r arduino.tar.xz || wget -O arduino.tar.xz https://www.arduino.cc/download.php?f=/arduino-nightly-linux64.tar.xz
tar xf arduino.tar.xz test -r arduino-linux.tar.xz || wget -O arduino-linux.tar.xz "${ideurl}-linux64.tar.xz"
tar xf arduino-linux.tar.xz
mv arduino-${idever} arduino-distrib
fi fi
mv arduino-nightly $ide_path mv arduino-distrib $ide_path
cd $ide_path/hardware cd $ide_path/hardware
mkdir esp8266com mkdir esp8266com
cd esp8266com cd esp8266com

View File

@ -11,9 +11,9 @@ UPLOAD_PORT ?= $(shell ls /dev/tty* | grep -m 1 -i USB)
UPLOAD_BAUD ?= 460800 UPLOAD_BAUD ?= 460800
UPLOAD_BOARD ?= nodemcu UPLOAD_BOARD ?= nodemcu
BS_DIR ?= libraries/BSTest BS_DIR ?= libraries/BSTest
DEBUG_LEVEL ?= DebugLevel=None____ DEBUG_LEVEL ?= lvl=None____
#FQBN ?= esp8266com:esp8266:generic:CpuFrequency=80,FlashFreq=40,FlashMode=dio,UploadSpeed=115200,FlashSize=4M1M,LwIPVariant=v2mss536,ResetMethod=none,Debug=Serial,$(DEBUG_LEVEL) #FQBN ?= esp8266com:esp8266:generic:CpuFrequency=80,FlashFreq=40,FlashMode=dio,UploadSpeed=115200,FlashSize=4M1M,LwIPVariant=v2mss536,ResetMethod=none,Debug=Serial,$(DEBUG_LEVEL)
FQBN ?= esp8266com:esp8266:generic:xtal=80,FlashFreq=40,FlashMode=dio,baud=115200,eesz=4M1M,ip=lm2f,ResetMethod=none,dbg=Serial,$(DEBUG_LEVEL) FQBN ?= esp8266com:esp8266:generic:xtal=160,FlashFreq=40,FlashMode=dio,baud=115200,eesz=4M1M,ip=lm2f,ResetMethod=none,dbg=Serial,$(DEBUG_LEVEL)
BUILD_TOOL := $(ARDUINO_IDE_PATH)/arduino-builder BUILD_TOOL := $(ARDUINO_IDE_PATH)/arduino-builder
TEST_CONFIG := test_env.cfg TEST_CONFIG := test_env.cfg
TEST_REPORT_XML := test_report.xml TEST_REPORT_XML := test_report.xml
@ -104,7 +104,7 @@ ifneq ("$(NO_RUN)","1")
endif endif
$(TEST_REPORT_XML): $(HARDWARE_DIR) virtualenv $(TEST_REPORT_XML): $(HARDWARE_DIR) virtualenv
$(SILENT)$(BS_DIR)/virtualenv/bin/xunitmerge $(shell find $(BUILD_DIR) -name 'test_result.xml' | xargs echo) $(TEST_REPORT_XML) $(SILENT)$(BS_DIR)/xunitmerge $(shell find $(BUILD_DIR) -name 'test_result.xml' | xargs echo) $(TEST_REPORT_XML)
$(TEST_REPORT_HTML): $(TEST_REPORT_XML) | virtualenv $(TEST_REPORT_HTML): $(TEST_REPORT_XML) | virtualenv
$(SILENT)$(BS_DIR)/virtualenv/bin/junit2html $< $@ $(SILENT)$(BS_DIR)/virtualenv/bin/junit2html $< $@
@ -125,6 +125,7 @@ virtualenv:
clean: clean:
rm -rf $(BUILD_DIR) rm -rf $(BUILD_DIR)
rm -rf $(HARDWARE_DIR) rm -rf $(HARDWARE_DIR)
rm -rf $(BS_DIR)/virtualenv
rm -f $(TEST_REPORT_HTML) $(TEST_REPORT_XML) rm -f $(TEST_REPORT_HTML) $(TEST_REPORT_XML)
distclean: clean distclean: clean

View File

@ -3,6 +3,5 @@ junit-xml
MarkupSafe MarkupSafe
pexpect pexpect
pyserial pyserial
xunitmerge
junit2html junit2html
poster poster3

View File

@ -236,10 +236,10 @@ ser = None
def spawn_port(port_name, baudrate=115200): def spawn_port(port_name, baudrate=115200):
global ser global ser
ser = serial.serial_for_url(port_name, baudrate=baudrate) ser = serial.serial_for_url(port_name, baudrate=baudrate)
return fdpexpect.fdspawn(ser, 'wb', timeout=0) return fdpexpect.fdspawn(ser, 'wb', timeout=0, encoding='cp437')
def spawn_exec(name): def spawn_exec(name):
return pexpect.spawn(name, timeout=0) return pexpect.spawn(name, timeout=0, encoding='cp437')
def run_tests(spawn, name, mocks, env_vars): def run_tests(spawn, name, mocks, env_vars):
tw = BSTestRunner(spawn, name, mocks, env_vars) tw = BSTestRunner(spawn, name, mocks, env_vars)

View File

@ -0,0 +1,154 @@
# Cloned from https://github.com/miki725/xunitmerge
# to fix a Python3 error.
#
# xunitmerge is MIT licensed by Miroslav Shubernetskiy https://github.com/miki725
#
# The MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from contextlib import contextmanager
from xml.etree import ElementTree as etree
from xml.sax.saxutils import quoteattr
import six
CNAME_TAGS = ('system-out', 'skipped', 'error', 'failure')
CNAME_PATTERN = '<![CDATA[{}]]>'
TAG_PATTERN = '<{tag}{attrs}>{text}</{tag}>'
@contextmanager
def patch_etree_cname(etree):
"""
Patch ElementTree's _serialize_xml function so that it will
write text as CDATA tag for tags tags defined in CNAME_TAGS.
>>> import re
>>> from xml.etree import ElementTree
>>> xml_string = '''
... <testsuite name="nosetests" tests="1" errors="0" failures="0" skip="0">
... <testcase classname="some.class.Foo" name="test_system_out" time="0.001">
... <system-out>Some output here</system-out>
... </testcase>
... <testcase classname="some.class.Foo" name="test_skipped" time="0.001">
... <skipped type="unittest.case.SkipTest" message="Skipped">Skipped</skipped>
... </testcase>
... <testcase classname="some.class.Foo" name="test_error" time="0.001">
... <error type="KeyError" message="Error here">Error here</error>
... </testcase>
... <testcase classname="some.class.Foo" name="test_failure" time="0.001">
... <failure type="AssertionError" message="Failure here">Failure here</failure>
... </testcase>
... </testsuite>
... '''
>>> tree = ElementTree.fromstring(xml_string)
>>> with patch_etree_cname(ElementTree):
... saved = str(ElementTree.tostring(tree))
>>> systemout = re.findall(r'(<system-out>.*?</system-out>)', saved)[0]
>>> print(systemout)
<system-out><![CDATA[Some output here]]></system-out>
>>> skipped = re.findall(r'(<skipped.*?</skipped>)', saved)[0]
>>> print(skipped)
<skipped message="Skipped" type="unittest.case.SkipTest"><![CDATA[Skipped]]></skipped>
>>> error = re.findall(r'(<error.*?</error>)', saved)[0]
>>> print(error)
<error message="Error here" type="KeyError"><![CDATA[Error here]]></error>
>>> failure = re.findall(r'(<failure.*?</failure>)', saved)[0]
>>> print(failure)
<failure message="Failure here" type="AssertionError"><![CDATA[Failure here]]></failure>
"""
original_serialize = etree._serialize_xml
def _serialize_xml(write, elem, *args, **kwargs):
if elem.tag in CNAME_TAGS:
attrs = ' '.join(
['{}={}'.format(k, quoteattr(v))
for k, v in sorted(elem.attrib.items())]
)
attrs = ' ' + attrs if attrs else ''
text = CNAME_PATTERN.format(elem.text)
write(TAG_PATTERN.format(
tag=elem.tag,
attrs=attrs,
text=text
))
else:
original_serialize(write, elem, *args, **kwargs)
etree._serialize_xml = etree._serialize['xml'] = _serialize_xml
yield
etree._serialize_xml = etree._serialize['xml'] = original_serialize
def merge_trees(*trees):
"""
Merge all given XUnit ElementTrees into a single ElementTree.
This combines all of the children test-cases and also merges
all of the metadata of how many tests were executed, etc.
"""
first_tree = trees[0]
first_root = first_tree.getroot()
if len(trees) == 0:
return first_tree
for tree in trees[1:]:
root = tree.getroot()
# append children elements (testcases)
first_root.extend(root.getchildren())
# combine root attributes which stores the number
# of executed tests, skipped tests, etc
for key, value in first_root.attrib.items():
if not value.isdigit():
continue
combined = six.text_type(int(value) + int(root.attrib.get(key, '0')))
first_root.set(key, combined)
return first_tree
def merge_xunit(files, output, callback=None):
"""
Merge the given xunit xml files into a single output xml file.
If callback is not None, it will be called with the merged ElementTree
before the output file is written (useful for applying other fixes to
the merged file). This can either modify the element tree in place (and
return None) or return a completely new ElementTree to be written.
"""
trees = []
for f in files:
trees.append(etree.parse(f))
merged = merge_trees(*trees)
if callback is not None:
result = callback(merged)
if result is not None:
merged = result
with patch_etree_cname(etree):
merged.write(output, encoding='utf-8', xml_declaration=True)

View File

@ -0,0 +1,50 @@
#!/usr/bin/env python3
# Cloned from https://github.com/miki725/xunitmerge
# to fix a Python3 error.
#
# xunitmerge is MIT licensed by Miroslav Shubernetskiy https://github.com/miki725
#
# The MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import argparse
from xmerge import merge_xunit
parser = argparse.ArgumentParser(
description='Utility for merging multiple XUnit xml reports '
'into a single xml report.',
)
parser.add_argument(
'report',
nargs='+',
type=argparse.FileType('r'),
help='Path of XUnit xml report. Multiple can be provided.',
)
parser.add_argument(
'output',
help='Path where merged of XUnit will be saved.',
)
if __name__ == '__main__':
args = parser.parse_args()
merge_xunit(args.report, args.output)

View File

@ -1,3 +1,5 @@
#!/usr/bin/env python3
from mock_decorators import setup, teardown from mock_decorators import setup, teardown
from flask import Flask, request from flask import Flask, request
from threading import Thread from threading import Thread
@ -21,7 +23,7 @@ def setup_tcpsrv(e):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
for port in range(8266, 8285 + 1): for port in range(8266, 8285 + 1):
try: try:
print >>sys.stderr, 'trying port', port print ('trying port %d' %port, file=sys.stderr)
server_address = ("0.0.0.0", port) server_address = ("0.0.0.0", port)
sock.bind(server_address) sock.bind(server_address)
sock.listen(1) sock.listen(1)
@ -31,17 +33,17 @@ def setup_tcpsrv(e):
print >>sys.stderr, 'busy' print >>sys.stderr, 'busy'
if not running: if not running:
return return
print >>sys.stderr, 'starting up on %s port %s' % server_address print ('starting up on %s port %s' % server_address, file=sys.stderr)
print >>sys.stderr, 'waiting for connections' print ( 'waiting for connections', file=sys.stderr)
while running: while running:
print >>sys.stderr, 'loop' print ('loop', file=sys.stderr)
readable, writable, errored = select.select([sock], [], [], 1.0) readable, writable, errored = select.select([sock], [], [], 1.0)
if readable: if readable:
connection, client_address = sock.accept() connection, client_address = sock.accept()
try: try:
print >>sys.stderr, 'client connected:', client_address print('client connected: %s' % str(client_address), file=sys.stderr)
finally: finally:
print >>sys.stderr, 'close' print ('close', file=sys.stderr)
connection.shutdown(socket.SHUT_RDWR) connection.shutdown(socket.SHUT_RDWR)
connection.close() connection.close()
@ -54,7 +56,7 @@ def teardown_tcpsrv(e):
global thread global thread
global running global running
print >>sys.stderr, 'closing' print ('closing', file=sys.stderr)
running = False running = False
thread.join() thread.join()
return 0 return 0

View File

@ -1,7 +1,7 @@
from mock_decorators import setup, teardown from mock_decorators import setup, teardown
from flask import Flask, request, redirect from flask import Flask, request, redirect
from threading import Thread from threading import Thread
import urllib2 import urllib
import os import os
import ssl import ssl
import time import time
@ -20,7 +20,7 @@ def setup_http_get(e):
return 'Server shutting down...' return 'Server shutting down...'
@app.route("/", methods = ['GET', 'POST']) @app.route("/", methods = ['GET', 'POST'])
def root(): def root():
print('Got data: ' + request.data); print('Got data: ' + request.data.decode());
return 'hello!!!' return 'hello!!!'
@app.route("/data") @app.route("/data")
def get_data(): def get_data():
@ -48,7 +48,7 @@ def setup_http_get(e):
@teardown('HTTP GET & POST requests') @teardown('HTTP GET & POST requests')
def teardown_http_get(e): def teardown_http_get(e):
response = urllib2.urlopen('http://localhost:8088/shutdown') response = urllib.request.urlopen('http://localhost:8088/shutdown')
html = response.read() html = response.read()
time.sleep(1) # avoid address in use error on macOS time.sleep(1) # avoid address in use error on macOS
@ -86,6 +86,6 @@ def teardown_http_get(e):
ctx.check_hostname = False ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE ctx.verify_mode = ssl.CERT_NONE
p = os.path.dirname(os.path.abspath(__file__)) p = os.path.dirname(os.path.abspath(__file__))
response = urllib2.urlopen('https://localhost:8088/shutdown', context=ctx) response = urllib.request.urlopen('https://localhost:8088/shutdown', context=ctx)
html = response.read() html = response.read()

View File

@ -1,10 +1,9 @@
from collections import OrderedDict from collections import OrderedDict
from mock_decorators import setup, teardown from mock_decorators import setup, teardown
from threading import Thread from threading import Thread
from poster.encode import MultipartParam from poster3.encode import MultipartParam
from poster.encode import multipart_encode from poster3.encode import multipart_encode
from poster.streaminghttp import register_openers from poster3.streaminghttp import register_openers
import urllib2
import urllib import urllib
def http_test(res, url, get=None, post=None): def http_test(res, url, get=None, post=None):
@ -13,8 +12,8 @@ def http_test(res, url, get=None, post=None):
if get: if get:
url += '?' + urllib.urlencode(get) url += '?' + urllib.urlencode(get)
if post: if post:
post = urllib.urlencode(post) post = urllib.parse.quote(post)
request = urllib2.urlopen(url, post, 2) request = urllib.request.urlopen(url, post, 2)
response = request.read() response = request.read()
except: except:
return 1 return 1
@ -60,8 +59,8 @@ def setup_http_upload(e):
register_openers() register_openers()
p = MultipartParam("file", "0123456789abcdef", "test.txt", "text/plain; charset=utf8") p = MultipartParam("file", "0123456789abcdef", "test.txt", "text/plain; charset=utf8")
datagen, headers = multipart_encode( [("var4", "val with spaces"), p] ) datagen, headers = multipart_encode( [("var4", "val with spaces"), p] )
request = urllib2.Request('http://etd.local/upload', datagen, headers) request = urllib.request('http://etd.local/upload', datagen, headers)
response = urllib2.urlopen(request, None, 2).read() response = urllib.request.urlopen(request, None, 2).read()
except: except:
return 1 return 1
if response != 'test.txt:16\nvar4 = val with spaces': if response != 'test.txt:16\nvar4 = val with spaces':

View File

@ -313,6 +313,15 @@ uart_get_baudrate(uart_t* uart)
return uart->baud_rate; return uart->baud_rate;
} }
uint8_t
uart_get_bit_length(const int uart_nr)
{
uint8_t width = ((uart_nr % 16) >> 2) + 5;
uint8_t parity = (uart_nr >> 5) + 1;
uint8_t stop = uart_nr % 4;
return (width + parity + stop + 1);
}
uart_t* 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)
{ {

View File

@ -1,31 +0,0 @@
#!/bin/sh
set -e
org=$(cd ${0%/*}; pwd)
cd ${org}/..
pwd
test -d cores/esp8266
test -d libraries
# this warning question will be removed after restyle-all.sh is renamed to restyle.sh
echo "This is dangerous if you have modified your local repository"
echo "type iknowwhatido to continue"
read ans
test "$ans" = iknowwhatido || exit 1
for d in cores/esp8266 libraries; do
for e in c cpp h; do
find $d -name "*.$e" -exec \
astyle \
--suffix=none \
--options=${org}/astyle_core.conf {} \;
done
done
for d in libraries; do
find $d -name "*.ino" -exec \
astyle \
--suffix=none \
--options=${org}/astyle_examples.conf {} \;
done

19
tests/restyle-examples-only.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
set -e
org=$(cd ${0%/*}; pwd)
cd ${org}/..
pwd
test -d cores/esp8266
test -d libraries
# in a near future, restyle-all.sh will be renamed to restyle.sh
# and will be checked against CI
for d in libraries; do
find $d -name "*.ino" -exec \
astyle \
--suffix=none \
--options=${org}/astyle_examples.conf {} \;
done

Some files were not shown because too many files have changed in this diff Show More