From e2263e2b3872ad0472d79dbdc33f814c859bee68 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 1 Aug 2019 15:45:05 +0200 Subject: [PATCH 01/22] Refactor for consistency - use PolledTimeout throughout or don't use at all - don't use at all. --- cores/esp8266/HardwareSerial.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cores/esp8266/HardwareSerial.cpp b/cores/esp8266/HardwareSerial.cpp index 6ec94500db..aa58a85d7f 100644 --- a/cores/esp8266/HardwareSerial.cpp +++ b/cores/esp8266/HardwareSerial.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include "Arduino.h" #include "HardwareSerial.h" #include "Esp.h" @@ -139,9 +138,9 @@ size_t HardwareSerial::readBytes(char* buffer, size_t size) while (got < size) { - esp8266::polledTimeout::oneShotFastMs timeOut(_timeout); + time_t startMillis = millis(); size_t avail; - while ((avail = available()) == 0 && !timeOut); + while ((avail = available()) == 0 && ((time_t) millis() - startMillis) < _timeout); if (avail == 0) break; got += read(buffer + got, std::min(size - got, avail)); From 70c904ef7b9958d8423af23bdd41859c5c612207 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 1 Aug 2019 16:28:30 +0200 Subject: [PATCH 02/22] Fix compiler warning --- cores/esp8266/HardwareSerial.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cores/esp8266/HardwareSerial.cpp b/cores/esp8266/HardwareSerial.cpp index aa58a85d7f..5d4b8ff974 100644 --- a/cores/esp8266/HardwareSerial.cpp +++ b/cores/esp8266/HardwareSerial.cpp @@ -138,9 +138,9 @@ size_t HardwareSerial::readBytes(char* buffer, size_t size) while (got < size) { - time_t startMillis = millis(); + unsigned long startMillis = millis(); size_t avail; - while ((avail = available()) == 0 && ((time_t) millis() - startMillis) < _timeout); + while ((avail = available()) == 0 && (millis() - startMillis) < _timeout); if (avail == 0) break; got += read(buffer + got, std::min(size - got, avail)); From b998e45d5ea7ba4f4303148e8b53ce77760b3543 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 1 Aug 2019 15:47:00 +0200 Subject: [PATCH 03/22] Refactor PolledTimeout into library - portability (even NONOS to FreeRTOS migration) --- libraries/PolledTimeout/library.properties | 9 +++++++++ .../PolledTimeout/src}/PolledTimeout.h | 0 2 files changed, 9 insertions(+) create mode 100644 libraries/PolledTimeout/library.properties rename {cores/esp8266 => libraries/PolledTimeout/src}/PolledTimeout.h (100%) diff --git a/libraries/PolledTimeout/library.properties b/libraries/PolledTimeout/library.properties new file mode 100644 index 0000000000..02e13294cc --- /dev/null +++ b/libraries/PolledTimeout/library.properties @@ -0,0 +1,9 @@ +name=PolledTimeout +version=1.0 +author= +maintainer= +sentence= +paragraph= +category=Timing +url= +architectures=esp8266,esp32 diff --git a/cores/esp8266/PolledTimeout.h b/libraries/PolledTimeout/src/PolledTimeout.h similarity index 100% rename from cores/esp8266/PolledTimeout.h rename to libraries/PolledTimeout/src/PolledTimeout.h From 9af6ac050a531f3ae400fb1fd57374756d03e695 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 25 Jun 2019 11:07:41 +0200 Subject: [PATCH 04/22] Branch had become mangled with lots of unrelated changes - reduced to core changeset. --- cores/esp8266/core_esp8266_main.cpp | 10 +++------- libraries/Schedule/library.properties | 10 ++++++++++ .../Schedule/src}/Schedule.cpp | 17 ++++++++++++++--- .../Schedule/src}/Schedule.h | 0 tests/host/Makefile | 2 +- 5 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 libraries/Schedule/library.properties rename {cores/esp8266 => libraries/Schedule/src}/Schedule.cpp (93%) rename {cores/esp8266 => libraries/Schedule/src}/Schedule.h (100%) diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 588019f0dd..b422b7edb9 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -23,7 +23,6 @@ //This may be used to change user task stack size: //#define CONT_STACKSIZE 4096 #include -#include "Schedule.h" extern "C" { #include "ets_sys.h" #include "os_type.h" @@ -83,13 +82,12 @@ void preloop_update_frequency() { #endif } - -static inline void esp_yield_within_cont() __attribute__((always_inline)); -static void esp_yield_within_cont() { +extern "C" void __esp_yield_within_cont() { cont_yield(g_pcont); - run_scheduled_recurrent_functions(); } +extern "C" void esp_yield_within_cont() __attribute__ ((weak, alias("__esp_yield_within_cont"))); + extern "C" void esp_yield() { if (cont_can_yield(g_pcont)) { esp_yield_within_cont(); @@ -123,8 +121,6 @@ extern "C" void optimistic_yield(uint32_t interval_us) { extern "C" void __loop_end (void) { - run_scheduled_functions(); - run_scheduled_recurrent_functions(); } extern "C" void loop_end (void) __attribute__ ((weak, alias("__loop_end"))); diff --git a/libraries/Schedule/library.properties b/libraries/Schedule/library.properties new file mode 100644 index 0000000000..0d31af9fb5 --- /dev/null +++ b/libraries/Schedule/library.properties @@ -0,0 +1,10 @@ +name=Schedule +version=1.0 +author=Earle F. Philhower, III +maintainer=Earle F. Philhower, III +sentence= +paragraph= +category=Other +url=https://github.com/esp8266/Arduino +architectures=esp8266 +dot_a_linkage=true diff --git a/cores/esp8266/Schedule.cpp b/libraries/Schedule/src/Schedule.cpp similarity index 93% rename from cores/esp8266/Schedule.cpp rename to libraries/Schedule/src/Schedule.cpp index ed39736e55..b3db5ecc45 100644 --- a/cores/esp8266/Schedule.cpp +++ b/libraries/Schedule/src/Schedule.cpp @@ -2,9 +2,20 @@ #include #include "Schedule.h" -#include "PolledTimeout.h" -#include "interrupts.h" -#include "coredecls.h" +#include +#include +#include + +extern "C" void loop_end() +{ + run_scheduled_functions(); + run_scheduled_recurrent_functions();} + +extern "C" void esp_yield_within_cont() +{ + cont_yield(g_pcont); + run_scheduled_recurrent_functions(); +} typedef std::function mSchedFuncT; struct scheduled_fn_t diff --git a/cores/esp8266/Schedule.h b/libraries/Schedule/src/Schedule.h similarity index 100% rename from cores/esp8266/Schedule.h rename to libraries/Schedule/src/Schedule.h diff --git a/tests/host/Makefile b/tests/host/Makefile index 3768c11b5e..1893cf0847 100644 --- a/tests/host/Makefile +++ b/tests/host/Makefile @@ -67,7 +67,7 @@ CORE_CPP_FILES := $(addprefix $(CORE_PATH)/,\ spiffs/spiffs_nucleus.cpp \ libb64/cencode.cpp \ libb64/cdecode.cpp \ - Schedule.cpp \ + ../../libraries/Schedule/src/Schedule.cpp \ HardwareSerial.cpp \ ) \ $(addprefix $(LIBRARIES_PATH)/ESP8266SdFat/src/, \ From 900aeb0024edde2ddc25f872619ab5a579919b56 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 25 Jun 2019 11:30:56 +0200 Subject: [PATCH 05/22] Core can't reference to libraries, need to move FunctionalInterrupt to libraries, too. --- .../examples/Functional/Functional.ino | 84 +++++++++++++++++++ libraries/FunctionalInterrupt/keywords.txt | 13 +++ .../FunctionalInterrupt/library.properties | 10 +++ .../src}/FunctionalInterrupt.cpp | 0 .../src}/FunctionalInterrupt.h | 0 5 files changed, 107 insertions(+) create mode 100644 libraries/FunctionalInterrupt/examples/Functional/Functional.ino create mode 100644 libraries/FunctionalInterrupt/keywords.txt create mode 100644 libraries/FunctionalInterrupt/library.properties rename {cores/esp8266 => libraries/FunctionalInterrupt/src}/FunctionalInterrupt.cpp (100%) rename {cores/esp8266 => libraries/FunctionalInterrupt/src}/FunctionalInterrupt.h (100%) diff --git a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino new file mode 100644 index 0000000000..5abac705de --- /dev/null +++ b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino @@ -0,0 +1,84 @@ +#include +#include + +#if defined(ESP32) +#define BUTTON1 16 +#define BUTTON2 17 +#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) +#define BUTTON1 D4 +#define BUTTON2 D3 +#else +#define BUTTON1 2 +#define BUTTON2 0 +#endif + +class Button { + public: + Button(uint8_t reqPin) : PIN(reqPin) { + pinMode(PIN, INPUT_PULLUP); + // Arduino C API: + //attachInterruptArg(PIN, [](void* self) { + // static_cast(self)->isr(); + //}, this, FALLING); // works on ESP32; fails on ESP8266: "ISR not in IRAM" + //attachInterruptArg(PIN, reinterpret_cast(&isr_static), this, FALLING); // works on ESP32; works on ESP8266 + // FunctionalInterrupts API: + attachScheduledInterrupt(PIN, [this](InterruptInfo ii) { + Serial.print("Pin "); + Serial.println(ii.pin); + isr(); + }, FALLING); // works on ESP32; works on ESP8266 + }; + ~Button() { + detachInterrupt(PIN); + } + +#if defined(ESP8266) + void ICACHE_RAM_ATTR isr() +#elif defined(ESP32) + void IRAM_ATTR isr() +#endif + { + numberKeyPresses += 1; + pressed = true; + } + +#if defined(ESP8266) + static void ICACHE_RAM_ATTR isr_static(Button* const self) +#elif defined(ESP32) + static void IRAM_ATTR isr_static(Button* const self) +#endif + { + self->isr(); + } + + void checkPressed() { + if (pressed) { + Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); + pressed = false; + } + } + + private: + const uint8_t PIN; + volatile uint32_t numberKeyPresses = 0; + volatile bool pressed = false; +}; + +Button* button1; +Button* button2; + + +void setup() { + Serial.begin(115200); + Serial.println("FunctionalInterrupt test/example"); + + button1 = new Button(BUTTON1); + button2 = new Button(BUTTON2); + + Serial.println("setup() complete"); +} + +void loop() { + button1->checkPressed(); + button2->checkPressed(); +} diff --git a/libraries/FunctionalInterrupt/keywords.txt b/libraries/FunctionalInterrupt/keywords.txt new file mode 100644 index 0000000000..b8c0fc83f1 --- /dev/null +++ b/libraries/FunctionalInterrupt/keywords.txt @@ -0,0 +1,13 @@ +####################################### +# Datatypes (KEYWORD1) +####################################### + +InterruptInfo KEYWORD1 +ArgStructure KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +attachInterrupt KEYWORD2 +attachScheduledInterrupt KEYWORD2 diff --git a/libraries/FunctionalInterrupt/library.properties b/libraries/FunctionalInterrupt/library.properties new file mode 100644 index 0000000000..9738f1e971 --- /dev/null +++ b/libraries/FunctionalInterrupt/library.properties @@ -0,0 +1,10 @@ +name=FunctionalInterrupt +version=1.0 +author=hreintke +maintainer=hreintke +sentence=C++ functional and scheduled interrupt handling +paragraph= +category=Other +url=https://github.com/esp8266/Arduino +architectures=esp8266 +dot_a_linkage=true diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp similarity index 100% rename from cores/esp8266/FunctionalInterrupt.cpp rename to libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp diff --git a/cores/esp8266/FunctionalInterrupt.h b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h similarity index 100% rename from cores/esp8266/FunctionalInterrupt.h rename to libraries/FunctionalInterrupt/src/FunctionalInterrupt.h From 20556d10d350a05c5552181d6ddd19c115527549 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 25 Jun 2019 22:29:21 +0200 Subject: [PATCH 06/22] Fix crazy dependencies. --- cores/esp8266/core_esp8266_wiring_digital.cpp | 19 +++++++++++++++---- .../src/FunctionalInterrupt.cpp | 11 ----------- .../src/FunctionalInterrupt.h | 3 +-- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index 5ed0a40ab7..be831f348a 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -115,16 +115,21 @@ typedef struct { bool functional; } interrupt_handler_t; -//duplicate from functionalInterrupt.h keep in sync +//duplicates from functionalInterrupt.h keep in sync typedef struct InterruptInfo { uint8_t pin; uint8_t value; uint32_t micro; } InterruptInfo; -typedef struct { +typedef struct FunctionInfo { + std::function reqFunction; + std::function reqScheduledFunction; +} FunctionInfo; + +typedef struct ArgStructure { InterruptInfo* interruptInfo; - void* functionInfo; + FunctionInfo* functionInfo; } ArgStructure; static interrupt_handler_t interrupt_handlers[16] = { {0, 0, 0, 0}, }; @@ -172,7 +177,13 @@ void ICACHE_RAM_ATTR interrupt_handler(void*) ETS_GPIO_INTR_ENABLE(); } -extern void cleanupFunctional(void* arg); +static void cleanupFunctional(void* arg) +{ + ArgStructure* localArg = (ArgStructure*)arg; + delete (FunctionInfo*)localArg->functionInfo; + delete (InterruptInfo*)localArg->interruptInfo; + delete localArg; +} static void set_interrupt_handlers(uint8_t pin, voidFuncPtr userFunc, void* arg, uint8_t mode, bool functional) { diff --git a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp index 665d8043b3..40282547a9 100644 --- a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp +++ b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp @@ -23,17 +23,6 @@ void ICACHE_RAM_ATTR interruptFunctional(void* arg) } } -extern "C" -{ - void cleanupFunctional(void* arg) - { - ArgStructure* localArg = (ArgStructure*)arg; - delete (FunctionInfo*)localArg->functionInfo; - delete (InterruptInfo*)localArg->interruptInfo; - delete localArg; - } -} - void attachInterrupt(uint8_t pin, std::function intRoutine, int mode) { // use the local interrupt routine which takes the ArgStructure as argument diff --git a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h index 968793e499..e1f1ea2f3e 100644 --- a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h +++ b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h @@ -31,5 +31,4 @@ struct ArgStructure { void attachInterrupt(uint8_t pin, std::function intRoutine, int mode); void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode); - -#endif //INTERRUPTS_H +#endif //FUNCTIONALINTERRUPT_H From ec5337e050c726526e6e1edf140b386f8fd57301 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 26 Jun 2019 08:43:50 +0200 Subject: [PATCH 07/22] Updated examples --- .../examples/Functional/Functional.ino | 41 ++++------ .../ScheduledFunctional.ino | 75 +++++++++++++++++++ 2 files changed, 89 insertions(+), 27 deletions(-) create mode 100644 libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino diff --git a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino index 5abac705de..3d45019ed7 100644 --- a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino +++ b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino @@ -1,5 +1,8 @@ #include -#include + +#ifndef IRAM_ATTR +#define IRAM_ATTR ICACHE_RAM_ATTR +#endif #if defined(ESP32) #define BUTTON1 16 @@ -16,46 +19,27 @@ class Button { public: Button(uint8_t reqPin) : PIN(reqPin) { pinMode(PIN, INPUT_PULLUP); - // Arduino C API: - //attachInterruptArg(PIN, [](void* self) { - // static_cast(self)->isr(); - //}, this, FALLING); // works on ESP32; fails on ESP8266: "ISR not in IRAM" - //attachInterruptArg(PIN, reinterpret_cast(&isr_static), this, FALLING); // works on ESP32; works on ESP8266 - // FunctionalInterrupts API: - attachScheduledInterrupt(PIN, [this](InterruptInfo ii) { - Serial.print("Pin "); - Serial.println(ii.pin); - isr(); - }, FALLING); // works on ESP32; works on ESP8266 + attachInterrupt(PIN, std::bind(&Button::buttonIsr, this), FALLING); }; ~Button() { detachInterrupt(PIN); } -#if defined(ESP8266) - void ICACHE_RAM_ATTR isr() -#elif defined(ESP32) - void IRAM_ATTR isr() -#endif - { + void IRAM_ATTR buttonIsr() { numberKeyPresses += 1; pressed = true; } -#if defined(ESP8266) - static void ICACHE_RAM_ATTR isr_static(Button* const self) -#elif defined(ESP32) - static void IRAM_ATTR isr_static(Button* const self) -#endif - { - self->isr(); + static void IRAM_ATTR buttonIsr_static(Button* const self) { + self->buttonIsr(); } - void checkPressed() { + uint32_t checkPressed() { if (pressed) { Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); pressed = false; } + return numberKeyPresses; } private: @@ -80,5 +64,8 @@ void setup() { void loop() { button1->checkPressed(); - button2->checkPressed(); + if (nullptr != button2 && 10 < button2->checkPressed()) { + delete button2; + button2 = nullptr; + } } diff --git a/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino b/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino new file mode 100644 index 0000000000..dc0e0fc318 --- /dev/null +++ b/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino @@ -0,0 +1,75 @@ +#include + +#ifndef IRAM_ATTR +#define IRAM_ATTR ICACHE_RAM_ATTR +#endif + +#if defined(ESP32) +#define BUTTON1 16 +#define BUTTON2 17 +#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) +#define BUTTON1 D4 +#define BUTTON2 D3 +#else +#define BUTTON1 2 +#define BUTTON2 0 +#endif + +class Button { + public: + Button(uint8_t reqPin) : PIN(reqPin) { + pinMode(PIN, INPUT_PULLUP); + attachScheduledInterrupt(PIN, [this](const InterruptInfo & ii) { + Serial.print("Pin "); + Serial.println(ii.pin); + buttonIsr(); + }, FALLING); // works on ESP8266 + }; + ~Button() { + detachInterrupt(PIN); + } + + void IRAM_ATTR buttonIsr() { + numberKeyPresses += 1; + pressed = true; + } + + static void IRAM_ATTR buttonIsr_static(Button* const self) { + self->buttonIsr(); + } + + uint32_t checkPressed() { + if (pressed) { + Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); + pressed = false; + } + return numberKeyPresses; + } + + private: + const uint8_t PIN; + volatile uint32_t numberKeyPresses = 0; + volatile bool pressed = false; +}; + +Button* button1; +Button* button2; + + +void setup() { + Serial.begin(115200); + Serial.println("FunctionalInterrupt test/example"); + + button1 = new Button(BUTTON1); + button2 = new Button(BUTTON2); + + Serial.println("setup() complete"); +} + +void loop() { + button1->checkPressed(); + if (nullptr != button2 && 10 < button2->checkPressed()) { + delete button2; + button2 = nullptr; + } +} From e904025e585e4a01f6ee452c9142d444119ccd7b Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sat, 29 Jun 2019 00:10:27 +0200 Subject: [PATCH 08/22] Remove redundant C-style casts --- cores/esp8266/core_esp8266_wiring_digital.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index be831f348a..21568879e0 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -180,8 +180,8 @@ void ICACHE_RAM_ATTR interrupt_handler(void*) static void cleanupFunctional(void* arg) { ArgStructure* localArg = (ArgStructure*)arg; - delete (FunctionInfo*)localArg->functionInfo; - delete (InterruptInfo*)localArg->interruptInfo; + delete localArg->interruptInfo; + delete localArg->functionInfo; delete localArg; } From c14d86c11903687b180eac433c98c96bfe5250fe Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 4 Jul 2019 08:15:38 +0200 Subject: [PATCH 09/22] Updated credits --- libraries/Schedule/library.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/Schedule/library.properties b/libraries/Schedule/library.properties index 0d31af9fb5..433e275659 100644 --- a/libraries/Schedule/library.properties +++ b/libraries/Schedule/library.properties @@ -1,7 +1,7 @@ name=Schedule version=1.0 -author=Earle F. Philhower, III -maintainer=Earle F. Philhower, III +author=David Gauchard +maintainer=David Gauchard sentence= paragraph= category=Other From 52130cedaf090dfb051c27a3d766cb76b0afbb39 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 5 Jul 2019 14:18:36 +0200 Subject: [PATCH 10/22] Corrected library author and maintainer --- libraries/Schedule/library.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/Schedule/library.properties b/libraries/Schedule/library.properties index 433e275659..533268880f 100644 --- a/libraries/Schedule/library.properties +++ b/libraries/Schedule/library.properties @@ -1,7 +1,7 @@ name=Schedule version=1.0 -author=David Gauchard -maintainer=David Gauchard +author=Ivan Grokhotkov +maintainer=Ivan Grokhotkov sentence= paragraph= category=Other From 7ce3faac79e57e68a338e4cc39698882961209ef Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 19 Jul 2019 11:08:40 +0200 Subject: [PATCH 11/22] Update Ticker library --- libraries/Ticker/examples/Blinker/Blinker.ino | 41 +++++++++++++++++++ .../TickerFunctional/TickerFunctional.ino | 4 +- libraries/Ticker/keywords.txt | 20 +++------ libraries/Ticker/{ => src}/Ticker.cpp | 0 libraries/Ticker/{ => src}/Ticker.h | 0 5 files changed, 49 insertions(+), 16 deletions(-) create mode 100644 libraries/Ticker/examples/Blinker/Blinker.ino rename libraries/Ticker/{ => src}/Ticker.cpp (100%) rename libraries/Ticker/{ => src}/Ticker.h (100%) diff --git a/libraries/Ticker/examples/Blinker/Blinker.ino b/libraries/Ticker/examples/Blinker/Blinker.ino new file mode 100644 index 0000000000..ce5a18a316 --- /dev/null +++ b/libraries/Ticker/examples/Blinker/Blinker.ino @@ -0,0 +1,41 @@ +#include +#include + +// attach a LED to pPIO 21 +#define LED_PIN 21 + +Ticker blinker; +Ticker toggler; +Ticker changer; +float blinkerPace = 0.1; //seconds +const float togglePeriod = 5; //seconds + +void change() { + blinkerPace = 0.5; +} + +void blink() { + digitalWrite(LED_PIN, !digitalRead(LED_PIN)); +} + +void toggle() { + static bool isBlinking = false; + if (isBlinking) { + blinker.detach(); + isBlinking = false; + } else { + blinker.attach(blinkerPace, blink); + isBlinking = true; + } + digitalWrite(LED_PIN, LOW); //make sure LED on on after toggling (pin LOW = led ON) +} + +void setup() { + pinMode(LED_PIN, OUTPUT); + toggler.attach(togglePeriod, toggle); + changer.once(30, change); +} + +void loop() { + +} diff --git a/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino b/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino index 33c9435982..387e6d6bcb 100644 --- a/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino +++ b/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino @@ -1,5 +1,5 @@ -#include "Arduino.h" -#include "Ticker.h" +#include +#include #define LED1 2 #define LED2 4 diff --git a/libraries/Ticker/keywords.txt b/libraries/Ticker/keywords.txt index 1ecd8d0eda..b1020c4e59 100644 --- a/libraries/Ticker/keywords.txt +++ b/libraries/Ticker/keywords.txt @@ -1,29 +1,21 @@ -####################################### -# Syntax Coloring Map For Wire -####################################### - ####################################### # Datatypes (KEYWORD1) ####################################### +Ticker KEYWORD1 + ####################################### # Methods and Functions (KEYWORD2) ####################################### +attach_scheduled KEYWORD2 attach KEYWORD2 +attach_ms_scheduled KEYWORD2 attach_ms KEYWORD2 +once_scheduled KEYWORD2 once KEYWORD2 +once_ms_scheduled KEYWORD2 once_ms KEYWORD2 detach KEYWORD2 active KEYWORD2 -####################################### -# Instances (KEYWORD2) -####################################### - -Ticker KEYWORD2 - -####################################### -# Constants (LITERAL1) -####################################### - diff --git a/libraries/Ticker/Ticker.cpp b/libraries/Ticker/src/Ticker.cpp similarity index 100% rename from libraries/Ticker/Ticker.cpp rename to libraries/Ticker/src/Ticker.cpp diff --git a/libraries/Ticker/Ticker.h b/libraries/Ticker/src/Ticker.h similarity index 100% rename from libraries/Ticker/Ticker.h rename to libraries/Ticker/src/Ticker.h From ebddd3dda8cb0603b66ae054f209afdc0c502e62 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 24 Jul 2019 10:13:02 +0200 Subject: [PATCH 12/22] More efficient loop extension --- cores/esp8266/core_esp8266_main.cpp | 11 ++++------- libraries/Schedule/src/Schedule.cpp | 3 ++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index b422b7edb9..e08e296c2f 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -39,7 +39,6 @@ extern "C" { #define OPTIMISTIC_YIELD_TIME_US 16000 extern "C" void call_user_start(); -extern void loop(); extern void setup(); extern void (*__init_array_start)(void); extern void (*__init_array_end)(void); @@ -119,12 +118,11 @@ extern "C" void optimistic_yield(uint32_t interval_us) { } } -extern "C" void __loop_end (void) -{ +extern "C" void esp_loop(void) __attribute__((weak)); +extern "C" void esp_loop(void) { + loop(); } -extern "C" void loop_end (void) __attribute__ ((weak, alias("__loop_end"))); - static void loop_wrapper() { static bool setup_done = false; preloop_update_frequency(); @@ -132,8 +130,7 @@ static void loop_wrapper() { setup(); setup_done = true; } - loop(); - loop_end(); + esp_loop(); esp_schedule(); } diff --git a/libraries/Schedule/src/Schedule.cpp b/libraries/Schedule/src/Schedule.cpp index b3db5ecc45..61faaeab62 100644 --- a/libraries/Schedule/src/Schedule.cpp +++ b/libraries/Schedule/src/Schedule.cpp @@ -6,8 +6,9 @@ #include #include -extern "C" void loop_end() +extern "C" void esp_loop() { + loop(); run_scheduled_functions(); run_scheduled_recurrent_functions();} From 676ff3270dcee00ec0b4ed8733180d864e2a9dd5 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 24 Jul 2019 10:35:10 +0200 Subject: [PATCH 13/22] yield() plugin code more straightforward, same efficiency --- cores/esp8266/core_esp8266_main.cpp | 11 +++-------- libraries/Schedule/src/Schedule.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index e08e296c2f..42fefc7cbb 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -81,15 +81,10 @@ void preloop_update_frequency() { #endif } -extern "C" void __esp_yield_within_cont() { - cont_yield(g_pcont); -} - -extern "C" void esp_yield_within_cont() __attribute__ ((weak, alias("__esp_yield_within_cont"))); - +extern "C" void esp_yield() __attribute__((weak)); extern "C" void esp_yield() { if (cont_can_yield(g_pcont)) { - esp_yield_within_cont(); + cont_yield(g_pcont); } } @@ -101,7 +96,7 @@ extern "C" void esp_schedule() { extern "C" void __yield() { if (cont_can_yield(g_pcont)) { esp_schedule(); - esp_yield_within_cont(); + esp_yield(); } else { panic(); diff --git a/libraries/Schedule/src/Schedule.cpp b/libraries/Schedule/src/Schedule.cpp index 61faaeab62..98540570b8 100644 --- a/libraries/Schedule/src/Schedule.cpp +++ b/libraries/Schedule/src/Schedule.cpp @@ -12,9 +12,11 @@ extern "C" void esp_loop() run_scheduled_functions(); run_scheduled_recurrent_functions();} -extern "C" void esp_yield_within_cont() +extern "C" void esp_yield() { - cont_yield(g_pcont); + if (cont_can_yield(g_pcont)) { + cont_yield(g_pcont); + } run_scheduled_recurrent_functions(); } From 9f60051bc69ed47f33d2b6f7a88b045e359ef70c Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 26 Jul 2019 10:29:21 +0200 Subject: [PATCH 14/22] Two-level hook system for esp_yield to accommodate host test environment --- cores/esp8266/core_esp8266_main.cpp | 6 ++++-- libraries/Schedule/src/Schedule.cpp | 22 +++++++++++++--------- tests/host/common/Arduino.cpp | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 42fefc7cbb..48050209d3 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -81,13 +81,15 @@ void preloop_update_frequency() { #endif } -extern "C" void esp_yield() __attribute__((weak)); -extern "C" void esp_yield() { +extern "C" void __esp_yield() __attribute__((weak)); +extern "C" void __esp_yield() { if (cont_can_yield(g_pcont)) { cont_yield(g_pcont); } } +extern "C" void esp_yield(void) __attribute__ ((weak, alias("__esp_yield"))); + extern "C" void esp_schedule() { // always on CONT stack here ets_post(LOOP_TASK_PRIORITY, 0, 0); diff --git a/libraries/Schedule/src/Schedule.cpp b/libraries/Schedule/src/Schedule.cpp index 98540570b8..6be96d4816 100644 --- a/libraries/Schedule/src/Schedule.cpp +++ b/libraries/Schedule/src/Schedule.cpp @@ -6,18 +6,22 @@ #include #include -extern "C" void esp_loop() +extern "C" { - loop(); - run_scheduled_functions(); - run_scheduled_recurrent_functions();} + void esp_loop() + { + loop(); + run_scheduled_functions(); + run_scheduled_recurrent_functions(); + } -extern "C" void esp_yield() -{ - if (cont_can_yield(g_pcont)) { - cont_yield(g_pcont); + void __esp_yield(); + + extern "C" void esp_yield() + { + __esp_yield(); + run_scheduled_recurrent_functions(); } - run_scheduled_recurrent_functions(); } typedef std::function mSchedFuncT; diff --git a/tests/host/common/Arduino.cpp b/tests/host/common/Arduino.cpp index 5192028e73..93aa923264 100644 --- a/tests/host/common/Arduino.cpp +++ b/tests/host/common/Arduino.cpp @@ -42,7 +42,7 @@ extern "C" void optimistic_yield (uint32_t interval_us) usleep(interval_us); } -extern "C" void esp_yield() +extern "C" void __esp_yield() { } From e571f20ce8a56842527f798feb96ab874f22df08 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 26 Jul 2019 10:32:16 +0200 Subject: [PATCH 15/22] Fix igrr's email address --- libraries/Schedule/library.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/Schedule/library.properties b/libraries/Schedule/library.properties index 533268880f..b9153597be 100644 --- a/libraries/Schedule/library.properties +++ b/libraries/Schedule/library.properties @@ -1,7 +1,7 @@ name=Schedule version=1.0 -author=Ivan Grokhotkov -maintainer=Ivan Grokhotkov +author=Ivan Grokhotkov (ivan@esp8266.com) +maintainer=Ivan Grokhotkov (ivan@esp8266.com) sentence= paragraph= category=Other From c8240584740097b86bc6c5277b3935093a1fde83 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 26 Jul 2019 11:42:28 +0200 Subject: [PATCH 16/22] Rename FunctionalInterrupt to ScheduledInterrupts. This was a review result in another PR. --- .../examples/Functional/Functional.ino | 71 ------------------ .../ScheduledFunctional.ino | 75 ------------------- .../examples/Functional/Functional.ino | 69 +++++++++++++++++ .../ScheduledFunctional.ino | 74 ++++++++++++++++++ .../keywords.txt | 0 .../library.properties | 4 +- .../src/ScheduledInterrupts.cpp} | 4 +- .../src/ScheduledInterrupts.h} | 6 +- 8 files changed, 150 insertions(+), 153 deletions(-) delete mode 100644 libraries/FunctionalInterrupt/examples/Functional/Functional.ino delete mode 100644 libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino create mode 100644 libraries/ScheduledInterrupts/examples/Functional/Functional.ino create mode 100644 libraries/ScheduledInterrupts/examples/ScheduledFunctional/ScheduledFunctional.ino rename libraries/{FunctionalInterrupt => ScheduledInterrupts}/keywords.txt (100%) rename libraries/{FunctionalInterrupt => ScheduledInterrupts}/library.properties (70%) rename libraries/{FunctionalInterrupt/src/FunctionalInterrupt.cpp => ScheduledInterrupts/src/ScheduledInterrupts.cpp} (96%) rename libraries/{FunctionalInterrupt/src/FunctionalInterrupt.h => ScheduledInterrupts/src/ScheduledInterrupts.h} (88%) diff --git a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino deleted file mode 100644 index 3d45019ed7..0000000000 --- a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino +++ /dev/null @@ -1,71 +0,0 @@ -#include - -#ifndef IRAM_ATTR -#define IRAM_ATTR ICACHE_RAM_ATTR -#endif - -#if defined(ESP32) -#define BUTTON1 16 -#define BUTTON2 17 -#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) -#define BUTTON1 D4 -#define BUTTON2 D3 -#else -#define BUTTON1 2 -#define BUTTON2 0 -#endif - -class Button { - public: - Button(uint8_t reqPin) : PIN(reqPin) { - pinMode(PIN, INPUT_PULLUP); - attachInterrupt(PIN, std::bind(&Button::buttonIsr, this), FALLING); - }; - ~Button() { - detachInterrupt(PIN); - } - - void IRAM_ATTR buttonIsr() { - numberKeyPresses += 1; - pressed = true; - } - - static void IRAM_ATTR buttonIsr_static(Button* const self) { - self->buttonIsr(); - } - - uint32_t checkPressed() { - if (pressed) { - Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); - pressed = false; - } - return numberKeyPresses; - } - - private: - const uint8_t PIN; - volatile uint32_t numberKeyPresses = 0; - volatile bool pressed = false; -}; - -Button* button1; -Button* button2; - - -void setup() { - Serial.begin(115200); - Serial.println("FunctionalInterrupt test/example"); - - button1 = new Button(BUTTON1); - button2 = new Button(BUTTON2); - - Serial.println("setup() complete"); -} - -void loop() { - button1->checkPressed(); - if (nullptr != button2 && 10 < button2->checkPressed()) { - delete button2; - button2 = nullptr; - } -} diff --git a/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino b/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino deleted file mode 100644 index dc0e0fc318..0000000000 --- a/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino +++ /dev/null @@ -1,75 +0,0 @@ -#include - -#ifndef IRAM_ATTR -#define IRAM_ATTR ICACHE_RAM_ATTR -#endif - -#if defined(ESP32) -#define BUTTON1 16 -#define BUTTON2 17 -#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) -#define BUTTON1 D4 -#define BUTTON2 D3 -#else -#define BUTTON1 2 -#define BUTTON2 0 -#endif - -class Button { - public: - Button(uint8_t reqPin) : PIN(reqPin) { - pinMode(PIN, INPUT_PULLUP); - attachScheduledInterrupt(PIN, [this](const InterruptInfo & ii) { - Serial.print("Pin "); - Serial.println(ii.pin); - buttonIsr(); - }, FALLING); // works on ESP8266 - }; - ~Button() { - detachInterrupt(PIN); - } - - void IRAM_ATTR buttonIsr() { - numberKeyPresses += 1; - pressed = true; - } - - static void IRAM_ATTR buttonIsr_static(Button* const self) { - self->buttonIsr(); - } - - uint32_t checkPressed() { - if (pressed) { - Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); - pressed = false; - } - return numberKeyPresses; - } - - private: - const uint8_t PIN; - volatile uint32_t numberKeyPresses = 0; - volatile bool pressed = false; -}; - -Button* button1; -Button* button2; - - -void setup() { - Serial.begin(115200); - Serial.println("FunctionalInterrupt test/example"); - - button1 = new Button(BUTTON1); - button2 = new Button(BUTTON2); - - Serial.println("setup() complete"); -} - -void loop() { - button1->checkPressed(); - if (nullptr != button2 && 10 < button2->checkPressed()) { - delete button2; - button2 = nullptr; - } -} diff --git a/libraries/ScheduledInterrupts/examples/Functional/Functional.ino b/libraries/ScheduledInterrupts/examples/Functional/Functional.ino new file mode 100644 index 0000000000..0461ef2879 --- /dev/null +++ b/libraries/ScheduledInterrupts/examples/Functional/Functional.ino @@ -0,0 +1,69 @@ +#include + +#ifndef IRAM_ATTR +#define IRAM_ATTR ICACHE_RAM_ATTR +#endif + +#if defined(ESP32) +#define BUTTON1 16 +#define BUTTON2 17 +#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) +#define BUTTON1 D4 +#define BUTTON2 D3 +#else +#define BUTTON1 2 +#define BUTTON2 0 +#endif + +class Button { + public: + Button(const uint8_t reqPin) : _PIN(reqPin) { + pinMode(_PIN, INPUT_PULLUP); + attachInterrupt(_PIN, std::bind(&Button::buttonIsr, this), FALLING); + }; + ~Button() { + detachInterrupt(_PIN); + } + + void IRAM_ATTR buttonIsr() { + _numberKeyPresses += 1; + _pressed = true; + } + + uint32_t testResetPressed() { + if (_pressed) { + Serial.printf("Button on pin %u has been pressed %u times\n", _PIN, _numberKeyPresses); + _pressed = false; + } + return _numberKeyPresses; + } + + private: + const uint8_t _PIN; + volatile uint32_t _numberKeyPresses = 0; + volatile bool _pressed = false; +}; + +// Pointers and "new" in setup() are used in this example to simply test +// and demonstrate how an ISR object can be constructed and destructed at runtime, +// including the detach of the ISR from the GPIO. +Button* button1 = nullptr; +Button* button2 = nullptr; + +void setup() { + Serial.begin(115200); + Serial.println("ScheduledInterrupts test/example"); + + button1 = new Button(BUTTON1); + button2 = new Button(BUTTON2); + + Serial.println("setup() complete"); +} + +void loop() { + button1->testResetPressed(); + if (nullptr != button2 && 10 < button2->testResetPressed()) { + delete button2; + button2 = nullptr; + } +} diff --git a/libraries/ScheduledInterrupts/examples/ScheduledFunctional/ScheduledFunctional.ino b/libraries/ScheduledInterrupts/examples/ScheduledFunctional/ScheduledFunctional.ino new file mode 100644 index 0000000000..6d21a01c1e --- /dev/null +++ b/libraries/ScheduledInterrupts/examples/ScheduledFunctional/ScheduledFunctional.ino @@ -0,0 +1,74 @@ +#include + +#ifndef IRAM_ATTR +#define IRAM_ATTR ICACHE_RAM_ATTR +#endif + +#if defined(ESP32) +#define BUTTON1 16 +#define BUTTON2 17 +#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) +#define BUTTON1 D4 +#define BUTTON2 D3 +#else +#define BUTTON1 2 +#define BUTTON2 0 +#endif + +class Button { + public: + Button(const uint8_t reqPin) : _PIN(reqPin) { + pinMode(_PIN, INPUT_PULLUP); + attachScheduledInterrupt(_PIN, [this](const InterruptInfo & ii) { + Serial.print("Pin "); + Serial.println(ii.pin); + buttonIsr(); + }, FALLING); // works on ESP8266 + }; + ~Button() { + detachInterrupt(_PIN); + } + + void IRAM_ATTR buttonIsr() { + _numberKeyPresses += 1; + _pressed = true; + } + + uint32_t testResetPressed() { + if (_pressed) { + Serial.printf("Button on pin %u has been pressed %u times\n", _PIN, _numberKeyPresses); + _pressed = false; + } + return _numberKeyPresses; + } + + private: + const uint8_t _PIN; + volatile uint32_t _numberKeyPresses = 0; + volatile bool _pressed = false; +}; + +// Pointers and "new" in setup() are used in this example to simply test +// and demonstrate how an ISR object can be constructed and destructed at runtime, +// including the detach of the ISR from the GPIO. +Button* button1; +Button* button2; + + +void setup() { + Serial.begin(115200); + Serial.println("ScheduledInterrupts test/example"); + + button1 = new Button(BUTTON1); + button2 = new Button(BUTTON2); + + Serial.println("setup() complete"); +} + +void loop() { + button1->testResetPressed(); + if (nullptr != button2 && 10 < button2->testResetPressed()) { + delete button2; + button2 = nullptr; + } +} diff --git a/libraries/FunctionalInterrupt/keywords.txt b/libraries/ScheduledInterrupts/keywords.txt similarity index 100% rename from libraries/FunctionalInterrupt/keywords.txt rename to libraries/ScheduledInterrupts/keywords.txt diff --git a/libraries/FunctionalInterrupt/library.properties b/libraries/ScheduledInterrupts/library.properties similarity index 70% rename from libraries/FunctionalInterrupt/library.properties rename to libraries/ScheduledInterrupts/library.properties index 9738f1e971..e47c0faa4a 100644 --- a/libraries/FunctionalInterrupt/library.properties +++ b/libraries/ScheduledInterrupts/library.properties @@ -1,8 +1,8 @@ -name=FunctionalInterrupt +name=ScheduledInterrupts version=1.0 author=hreintke maintainer=hreintke -sentence=C++ functional and scheduled interrupt handling +sentence=C++ functional, scheduled interrupt handling paragraph= category=Other url=https://github.com/esp8266/Arduino diff --git a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp b/libraries/ScheduledInterrupts/src/ScheduledInterrupts.cpp similarity index 96% rename from libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp rename to libraries/ScheduledInterrupts/src/ScheduledInterrupts.cpp index 40282547a9..d583b48be0 100644 --- a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp +++ b/libraries/ScheduledInterrupts/src/ScheduledInterrupts.cpp @@ -1,6 +1,6 @@ -#include +#include "ScheduledInterrupts.h" #include -#include "Arduino.h" +#include // Duplicate typedefs from core_esp8266_wiring_digital_c typedef void (*voidFuncPtr)(void); diff --git a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h b/libraries/ScheduledInterrupts/src/ScheduledInterrupts.h similarity index 88% rename from libraries/FunctionalInterrupt/src/FunctionalInterrupt.h rename to libraries/ScheduledInterrupts/src/ScheduledInterrupts.h index e1f1ea2f3e..c192fd212e 100644 --- a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h +++ b/libraries/ScheduledInterrupts/src/ScheduledInterrupts.h @@ -1,5 +1,5 @@ -#ifndef FUNCTIONALINTERRUPT_H -#define FUNCTIONALINTERRUPT_H +#ifndef SCHEDULEDINTERRUPTS_H +#define SCHEDULEDINTERRUPTS_H #include #include @@ -31,4 +31,4 @@ struct ArgStructure { void attachInterrupt(uint8_t pin, std::function intRoutine, int mode); void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode); -#endif //FUNCTIONALINTERRUPT_H +#endif // SCHEDULEDINTERRUPTS_H From 01ab60cff2c93825465e3c8efd9554aeba7b80f3 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 26 Jul 2019 11:16:03 +0200 Subject: [PATCH 17/22] Add FastScheduler library, based on pluggable scheduler work for Schedule.h|cpp --- libraries/ESP8266mDNS/src/LEAmDNS.cpp | 2 +- libraries/FastScheduler/library.properties | 10 + libraries/FastScheduler/src/FastScheduler.cpp | 196 +++++++++++ libraries/FastScheduler/src/FastScheduler.h | 73 ++++ .../src/circular_queue/circular_queue.h | 331 ++++++++++++++++++ .../src/circular_queue/circular_queue_mp.h | 192 ++++++++++ libraries/ScheduledInterrupts/keywords.txt | 1 - .../src/ScheduledInterrupts.cpp | 2 +- libraries/SoftwareSerial | 2 +- .../examples/TickerBasic/TickerBasic.ino | 4 + .../TickerParameter/TickerParameter.ino | 4 + libraries/Ticker/src/Ticker.h | 2 +- tests/device/test_schedule/test_schedule.ino | 2 +- tests/host/Makefile | 2 +- 14 files changed, 816 insertions(+), 7 deletions(-) create mode 100644 libraries/FastScheduler/library.properties create mode 100644 libraries/FastScheduler/src/FastScheduler.cpp create mode 100644 libraries/FastScheduler/src/FastScheduler.h create mode 100644 libraries/FastScheduler/src/circular_queue/circular_queue.h create mode 100644 libraries/FastScheduler/src/circular_queue/circular_queue_mp.h diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.cpp b/libraries/ESP8266mDNS/src/LEAmDNS.cpp index 07e385a530..cb2c77cca8 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS.cpp @@ -22,7 +22,7 @@ * */ -#include +#include #include "LEAmDNS_Priv.h" diff --git a/libraries/FastScheduler/library.properties b/libraries/FastScheduler/library.properties new file mode 100644 index 0000000000..6b07ae5564 --- /dev/null +++ b/libraries/FastScheduler/library.properties @@ -0,0 +1,10 @@ +name=FastScheduler +version=1.0 +author=Dirk O. Kaar +maintainer=Dirk O. Kaar +sentence= +paragraph= +category=Other +url=https://github.com/esp8266/Arduino +architectures=esp8266 +dot_a_linkage=true diff --git a/libraries/FastScheduler/src/FastScheduler.cpp b/libraries/FastScheduler/src/FastScheduler.cpp new file mode 100644 index 0000000000..e235f3a39f --- /dev/null +++ b/libraries/FastScheduler/src/FastScheduler.cpp @@ -0,0 +1,196 @@ +#include "FastScheduler.h" +#include +#include +#ifdef ESP8266 +#include +#include +#else +#include +#endif +#include "circular_queue/circular_queue_mp.h" + +extern "C" +{ + void esp_loop() + { + loop(); + run_scheduled_functions(SCHEDULE_FUNCTION_FROM_LOOP); + } + + void __esp_yield(); + + extern "C" void esp_yield() + { + __esp_yield(); + run_scheduled_functions(SCHEDULE_FUNCTION_WITHOUT_YIELDELAYCALLS); + } +} + +typedef std::function mFuncT; + +struct scheduled_fn_t +{ + mFuncT mFunc = nullptr; + esp8266::polledTimeout::periodicFastUs callNow; + schedule_e policy = SCHEDULE_FUNCTION_FROM_LOOP; + scheduled_fn_t() : callNow(esp8266::polledTimeout::periodicFastUs::alwaysExpired) { } +}; + +// anonymous namespace provides compilation-unit internal linkage +namespace { + static circular_queue_mp schedule_queue(SCHEDULED_FN_MAX_COUNT); + static esp8266::polledTimeout::periodicFastMs yieldSchedulerNow(100); // yield every 100ms + static schedule_e activePolicy; + + class FastSchedulerTicker; + // A local heap for Ticker instances to prevent global heap exhaustion + class TickerHeap : public circular_queue_mp { + public: + TickerHeap(const size_t capacity) : circular_queue_mp(capacity) + { + heap = new char[capacity * sizeof(Ticker)]; + for (size_t i = 0; i < capacity; ++i) push(reinterpret_cast(heap + i * sizeof(Ticker))); + } + ~TickerHeap() + { + delete heap; + } + protected: + char* heap; + }; + static TickerHeap tickerHeap(SCHEDULED_FN_MAX_COUNT); + + class FastSchedulerTicker : public Ticker + { + public: + static void operator delete(void* ptr) { + tickerHeap.push(static_cast(ptr)); + } + }; + static_assert(sizeof(FastSchedulerTicker) == sizeof(Ticker), "sizeof(FastSchedulerTicker) != sizeof(Ticker)"); + + void ticker_scheduled(FastSchedulerTicker* ticker, const std::function& fn, uint32_t repeat_us, schedule_e policy) + { +#ifdef ESP8266 + auto repeat_ms = (repeat_us + 500) / 1000; + ticker->once_ms(repeat_ms, [ticker, fn, repeat_us, policy]() +#else + ticker->once_us(repeat_us, [ticker, fn, repeat_us, policy]() +#endif + { + if (!schedule_function([ticker, fn, repeat_us, policy]() + { + if (fn()) ticker_scheduled(ticker, fn, repeat_us, policy); + else delete ticker; + return false; + }, policy)) + { + ticker_scheduled(ticker, fn, repeat_us, policy); + } + }); + } + +#ifdef ESP8266 + constexpr uint32_t TICKER_MIN_US = 5000; +#else + constexpr uint32_t TICKER_MIN_US = 5000; +#endif +}; + +bool IRAM_ATTR schedule_recurrent_function_us(std::function&& fn, uint32_t repeat_us, schedule_e policy) +{ + if (repeat_us >= TICKER_MIN_US) + { + // failure to aquire a Ticker must be returned to caller now. Specifically, allocating + // Tickers from inside the scheduled function doesn't work, any exhaustion of the scheduler + // can dead-lock it forever. + auto tickerPlace = tickerHeap.pop(); + if (!tickerPlace) return false; + auto ticker = new(tickerPlace) FastSchedulerTicker; + if (!schedule_function([ticker, fn, repeat_us, policy]() + { + ticker_scheduled(ticker, std::move(fn), repeat_us, policy); + return false; + }, SCHEDULE_FUNCTION_WITHOUT_YIELDELAYCALLS)) + { + delete ticker; + return false; + } + return true; + } + else + { + scheduled_fn_t item; + item.mFunc = std::move(fn); + if (repeat_us) item.callNow.reset(repeat_us); + item.policy = policy; + return schedule_queue.push(std::move(item)); + } +} + +bool IRAM_ATTR schedule_recurrent_function_us(const std::function& fn, uint32_t repeat_us, schedule_e policy) +{ + return schedule_recurrent_function_us(std::function(fn), repeat_us, policy); +} + +bool IRAM_ATTR schedule_function(std::function&& fn, schedule_e policy) +{ + return schedule_recurrent_function_us([fn]() { fn(); return false; }, 0, policy); +} + +bool IRAM_ATTR schedule_function(const std::function& fn, schedule_e policy) +{ + return schedule_function(std::function(fn), policy); +} + +bool run_function(scheduled_fn_t& func) +{ + if (yieldSchedulerNow) { +#if defined(ESP8266) + esp_schedule(); + cont_yield(g_pcont); +#elif defined(ESP32) + vPortYield(); +#else + yield(); +#endif + } + return + (func.policy != SCHEDULE_FUNCTION_WITHOUT_YIELDELAYCALLS && activePolicy != SCHEDULE_FUNCTION_FROM_LOOP) + || !func.callNow + || func.mFunc(); +} + +void run_scheduled_functions(schedule_e policy) +{ + // Note to the reader: + // There is no exposed API to remove a scheduled function: + // Scheduled functions are removed only from this function, and + // its purpose is that it is never called from an interrupt + // (always on cont stack). + + static std::atomic fence(false); +#ifdef ESP8266 + { + esp8266::InterruptLock lockAllInterruptsInThisScope; + if (fence.load()) { + // prevent any recursive calls from yield() + return; + } + fence.store(true); + } +#else + if (fence.exchange(true)) return; +#endif + + yieldSchedulerNow.reset(100); + activePolicy = policy; + + // run scheduled function: + // - when its schedule policy allows it anytime + // - or if we are called at loop() time + // and + // - its time policy allows it + schedule_queue.for_each_rev_requeue(run_function); + fence.store(false); +} diff --git a/libraries/FastScheduler/src/FastScheduler.h b/libraries/FastScheduler/src/FastScheduler.h new file mode 100644 index 0000000000..4ef3a97b2e --- /dev/null +++ b/libraries/FastScheduler/src/FastScheduler.h @@ -0,0 +1,73 @@ +#ifndef ESP_FASTSCHEDULER_H +#define ESP_FASTSCHEDULER_H + +// This API is stabilizing +// Function signatures may change, internal queue will remain FIFO. +// +// * Add the given lambda to a fifo list of lambdas, which is run when +// - `loop` function returns, +// - or `yield` is called, +// - or `run_scheduled_functions` is called. +// +// * Use lambdas to pass arguments to a function, or call a class/static +// member function. +// +// * Please ensure variables or instances used from inside lambda will exist +// when lambda is later called +// +// * There is no mechanism for cancelling scheduled functions. +// +// * `yield` can be called from inside lambdas +// +// * Returns false if the number of scheduled functions exceeds +// SCHEDULED_FN_MAX_COUNT. + +#ifdef __cplusplus +#include +extern "C" { +#endif + +#define SCHEDULED_FN_MAX_COUNT 32 + + typedef enum schedule_e_t + { + SCHEDULE_FUNCTION_FROM_LOOP, + SCHEDULE_FUNCTION_WITHOUT_YIELDELAYCALLS + } schedule_e; + +#ifdef __cplusplus +} + +// * Run the lambda only once next time +bool schedule_function(std::function&& fn, + schedule_e policy = SCHEDULE_FUNCTION_FROM_LOOP); +bool schedule_function(const std::function& fn, + schedule_e policy = SCHEDULE_FUNCTION_FROM_LOOP); + +// * Run the lambda periodically about every microseconds until +// it returns false. +// * Note that it may be more than microseconds between calls if +// `yield` is not called frequently, and therefore should not be used for +// timing critical operations. +bool schedule_recurrent_function_us(std::function&& fn, + uint32_t repeat_us, + schedule_e policy = SCHEDULE_FUNCTION_FROM_LOOP); +bool schedule_recurrent_function_us(const std::function& fn, + uint32_t repeat_us, + schedule_e policy = SCHEDULE_FUNCTION_FROM_LOOP); + +extern "C" { +#endif /* __cplusplus */ + + // Run all scheduled functions. + // Use this function if your are not using `loop`, or `loop` does not return + // on a regular basis. + +#ifndef __cplusplus + void run_scheduled_functions(schedule_e policy); +#else + void run_scheduled_functions(schedule_e policy = SCHEDULE_FUNCTION_FROM_LOOP); +} +#endif + +#endif //ESP_FASTSCHEDULER_H diff --git a/libraries/FastScheduler/src/circular_queue/circular_queue.h b/libraries/FastScheduler/src/circular_queue/circular_queue.h new file mode 100644 index 0000000000..53279c19f8 --- /dev/null +++ b/libraries/FastScheduler/src/circular_queue/circular_queue.h @@ -0,0 +1,331 @@ +/* +circular_queue.h - Implementation of a lock-free circular queue for EspSoftwareSerial. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __circular_queue_h +#define __circular_queue_h + +#ifdef ARDUINO +#include +#endif +#include +#include +#include +#include + +#if !defined(ESP32) && !defined(ESP8266) +#define ICACHE_RAM_ATTR +#define IRAM_ATTR +#endif + +/*! + @brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO). + This implementation is lock-free between producer and consumer for the available(), peek(), + pop(), and push() type functions. +*/ +template< typename T > +class circular_queue +{ +public: + /*! + @brief Constructs a valid, but zero-capacity dummy queue. + */ + circular_queue() : m_bufSize(1) + { + m_inPos.store(0); + m_outPos.store(0); + } + /*! + @brief Constructs a queue of the given maximum capacity. + */ + circular_queue(const size_t capacity) : m_bufSize(capacity + 1), m_buffer(new T[m_bufSize]) + { + m_inPos.store(0); + m_outPos.store(0); + } + circular_queue(circular_queue&& cq) : + m_bufSize(cq.m_bufSize), m_buffer(cq.m_buffer), m_inPos(cq.m_inPos.load()), m_outPos(cq.m_outPos.load()) + {} + ~circular_queue() + { + m_buffer.reset(); + } + circular_queue(const circular_queue&) = delete; + circular_queue& operator=(circular_queue&& cq) + { + m_bufSize = cq.m_bufSize; + m_buffer = cq.m_buffer; + m_inPos.store(cq.m_inPos.load()); + m_outPos.store(cq.m_outPos.load()); + } + circular_queue& operator=(const circular_queue&) = delete; + + /*! + @brief Get the numer of elements the queue can hold at most. + */ + size_t capacity() const + { + return m_bufSize - 1; + } + + /*! + @brief Resize the queue. The available elements in the queue are preserved. + This is not lock-free and concurrent producer or consumer access + will lead to corruption. + @return True if the new capacity could accommodate the present elements in + the queue, otherwise nothing is done and false is returned. + */ + bool capacity(const size_t cap); + + /*! + @brief Discard all data in the queue. + */ + void flush() + { + m_outPos.store(m_inPos.load()); + } + + /*! + @brief Get a snapshot number of elements that can be retrieved by pop. + */ + size_t available() const + { + int avail = static_cast(m_inPos.load() - m_outPos.load()); + if (avail < 0) avail += m_bufSize; + return avail; + } + + /*! + @brief Get the remaining free elementes for pushing. + */ + size_t available_for_push() const + { + int avail = static_cast(m_outPos.load() - m_inPos.load()) - 1; + if (avail < 0) avail += m_bufSize; + return avail; + } + + /*! + @brief Peek at the next element pop returns without removing it from the queue. + @return An rvalue copy of the next element that can be popped, or a default + value of type T if the queue is empty. + */ + T peek() const + { + const auto outPos = m_outPos.load(std::memory_order_acquire); + const auto inPos = m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + if (inPos == outPos) return defaultValue; + else return m_buffer[outPos]; + } + + /*! + @brief Move the rvalue parameter into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(T&& val); + + /*! + @brief Push a copy of the parameter into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(const T& val) + { + return push(T(val)); + } + + /*! + @brief Push copies of multiple elements from a buffer into the queue, + in order, beginning at buffer's head. + @return The number of elements actually copied into the queue, counted + from the buffer head. + */ + size_t push_n(const T* buffer, size_t size); + + /*! + @brief Pop the next available element from the queue. + @return An rvalue copy of the popped element, or a default + value of type T if the queue is empty. + */ + T pop(); + + /*! + @brief Pop multiple elements in ordered sequence from the queue to a buffer. + @return The number of elements actually popped from the queue to + buffer. + */ + size_t pop_n(T* buffer, size_t size); + + /*! + @brief Iterate over and remove each available element from queue, + calling back fun with an rvalue reference of every single element. + */ + void for_each(const std::function& fun); + + /*! + @brief In reverse order, iterate over, pop and optionally requeue each available element from the queue, + calling back fun with a reference of every single element. + Requeuing is dependent on the return boolean of the callback function. If it + returns true, the requeue occurs. + */ + bool for_each_rev_requeue(const std::function& fun); + +protected: + const T defaultValue = {}; + unsigned m_bufSize; + std::unique_ptr m_buffer; + std::atomic m_inPos; + std::atomic m_outPos; +}; + +template< typename T > +bool circular_queue::capacity(const size_t cap) +{ + if (cap + 1 == m_bufSize) return true; + else if (available() > cap) return false; + std::unique_ptr buffer(new T[cap + 1]); + const auto available = pop_n(buffer, cap); + m_buffer.reset(buffer); + m_bufSize = cap + 1; + std::atomic_thread_fence(std::memory_order_release); + m_inPos.store(available, std::memory_order_relaxed); + m_outPos.store(0, std::memory_order_release); + return true; +} + +template< typename T > +bool IRAM_ATTR circular_queue::push(T&& val) +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const unsigned next = (inPos + 1) % m_bufSize; + if (next == m_outPos.load(std::memory_order_relaxed)) { + return false; + } + + std::atomic_thread_fence(std::memory_order_acquire); + + m_buffer[inPos] = std::move(val); + + std::atomic_thread_fence(std::memory_order_release); + + m_inPos.store(next, std::memory_order_release); + return true; +} + +template< typename T > +size_t circular_queue::push_n(const T* buffer, size_t size) +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const auto outPos = m_outPos.load(std::memory_order_relaxed); + + size_t blockSize = (outPos > inPos) ? outPos - 1 - inPos : (outPos == 0) ? m_bufSize - 1 - inPos : m_bufSize - inPos; + blockSize = std::min(size, blockSize); + if (!blockSize) return 0; + int next = (inPos + blockSize) % m_bufSize; + + std::atomic_thread_fence(std::memory_order_acquire); + + auto dest = m_buffer.get() + inPos; + std::copy_n(std::make_move_iterator(buffer), blockSize, dest); + size = std::min(size - blockSize, outPos > 1 ? static_cast(outPos - next - 1) : 0); + next += size; + dest = m_buffer.get(); + std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest); + + std::atomic_thread_fence(std::memory_order_release); + + m_inPos.store(next, std::memory_order_release); + return blockSize + size; +} + +template< typename T > +T circular_queue::pop() +{ + const auto outPos = m_outPos.load(std::memory_order_acquire); + if (m_inPos.load(std::memory_order_relaxed) == outPos) return defaultValue; + + std::atomic_thread_fence(std::memory_order_acquire); + + auto val = std::move(m_buffer[outPos]); + + std::atomic_thread_fence(std::memory_order_release); + + m_outPos.store((outPos + 1) % m_bufSize, std::memory_order_release); + return val; +} + +template< typename T > +size_t circular_queue::pop_n(T* buffer, size_t size) { + size_t avail = size = std::min(size, available()); + if (!avail) return 0; + const auto outPos = m_outPos.load(std::memory_order_acquire); + size_t n = std::min(avail, static_cast(m_bufSize - outPos)); + + std::atomic_thread_fence(std::memory_order_acquire); + + buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); + avail -= n; + std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); + + std::atomic_thread_fence(std::memory_order_release); + + m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release); + return size; +} + +template< typename T > +void circular_queue::for_each(const std::function& fun) +{ + auto outPos = m_outPos.load(std::memory_order_acquire); + const auto inPos = m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + while (outPos != inPos) + { + fun(std::move(m_buffer[outPos])); + std::atomic_thread_fence(std::memory_order_release); + outPos = (outPos + 1) % m_bufSize; + m_outPos.store(outPos, std::memory_order_release); + } +} + +template< typename T > +bool circular_queue::for_each_rev_requeue(const std::function& fun) +{ + auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); + auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + if (outPos == inPos0) return false; + auto pos = inPos0; + auto outPos1 = inPos0; + const auto posDecr = circular_queue::m_bufSize - 1; + do { + pos = (pos + posDecr) % circular_queue::m_bufSize; + T&& val = std::move(circular_queue::m_buffer[pos]); + if (fun(val)) + { + outPos1 = (outPos1 + posDecr) % circular_queue::m_bufSize; + if (outPos1 != pos) circular_queue::m_buffer[outPos1] = std::move(val); + } + } while (pos != outPos); + circular_queue::m_outPos.store(outPos1, std::memory_order_release); + return true; +} + +#endif // __circular_queue_h diff --git a/libraries/FastScheduler/src/circular_queue/circular_queue_mp.h b/libraries/FastScheduler/src/circular_queue/circular_queue_mp.h new file mode 100644 index 0000000000..b0d12fb2ec --- /dev/null +++ b/libraries/FastScheduler/src/circular_queue/circular_queue_mp.h @@ -0,0 +1,192 @@ +/* +circular_queue_mp.h - Implementation of a lock-free circular queue for EspSoftwareSerial. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __circular_queue_mp_h +#define __circular_queue_mp_h + +#include "circular_queue.h" + +#ifdef ESP8266 +#include "interrupts.h" +#else +#include +#endif + +/*! + @brief Instance class for a multi-producer, single-consumer circular queue / ring buffer (FIFO). + This implementation is lock-free between producers and consumer for the available(), peek(), + pop(), and push() type functions, but is guarded to safely allow only a single producer + at any instant. +*/ +template< typename T > +class circular_queue_mp : protected circular_queue +{ +public: + circular_queue_mp() = default; + circular_queue_mp(const size_t capacity) : circular_queue(capacity) + {} + circular_queue_mp(circular_queue&& cq) : circular_queue(std::move(cq)) + {} + using circular_queue::operator=; + using circular_queue::capacity; + using circular_queue::flush; + using circular_queue::available; + using circular_queue::available_for_push; + using circular_queue::peek; + using circular_queue::pop; + using circular_queue::pop_n; + using circular_queue::for_each; + using circular_queue::for_each_rev_requeue; + + /*! + @brief Resize the queue. The available elements in the queue are preserved. + This is not lock-free, but safe, concurrent producer or consumer access + is guarded. + @return True if the new capacity could accommodate the present elements in + the queue, otherwise nothing is done and false is returned. + */ + bool capacity(const size_t cap) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::capacity(cap); + } + + bool IRAM_ATTR push(T&& val) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::push(std::move(val)); + } + + /*! + @brief Move the rvalue parameter into the queue, guarded + for multiple concurrent producers. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(const T& val) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::push(val); + } + + /*! + @brief Push copies of multiple elements from a buffer into the queue, + in order, beginning at buffer's head. This is guarded for + multiple producers, push_n() is atomic. + @return The number of elements actually copied into the queue, counted + from the buffer head. + */ + size_t push_n(const T* buffer, size_t size) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::push_n(buffer, size); + } + + /*! + @brief Pops the next available element from the queue, requeues + it immediately. + @return A reference to the just requeued element, or the default + value of type T if the queue is empty. + */ + T& pop_requeue(); + + /*! + @brief Iterate over, pop and optionally requeue each available element from the queue, + calling back fun with a reference of every single element. + Requeuing is dependent on the return boolean of the callback function. If it + returns true, the requeue occurs. + */ + bool for_each_requeue(const std::function& fun); + +#ifndef ESP8266 +protected: + std::mutex m_pushMtx; +#endif +}; + +template< typename T > +T& circular_queue_mp::pop_requeue() +{ +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + const auto outPos = circular_queue::m_outPos.load(std::memory_order_acquire); + const auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + if (inPos == outPos) return circular_queue::defaultValue; + T& val = circular_queue::m_buffer[inPos] = std::move(circular_queue::m_buffer[outPos]); + const auto bufSize = circular_queue::m_bufSize; + std::atomic_thread_fence(std::memory_order_release); + circular_queue::m_outPos.store((outPos + 1) % bufSize, std::memory_order_relaxed); + circular_queue::m_inPos.store((inPos + 1) % bufSize, std::memory_order_release); + return val; +} + +template< typename T > +bool circular_queue_mp::for_each_requeue(const std::function& fun) +{ + auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); + auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + if (outPos == inPos0) return false; + do { + T&& val = std::move(circular_queue::m_buffer[outPos]); + if (fun(val)) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + std::atomic_thread_fence(std::memory_order_release); + auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + circular_queue::m_buffer[inPos] = std::move(val); + std::atomic_thread_fence(std::memory_order_release); + circular_queue::m_inPos.store((inPos + 1) % circular_queue::m_bufSize, std::memory_order_release); + } + else + { + std::atomic_thread_fence(std::memory_order_release); + } + outPos = (outPos + 1) % circular_queue::m_bufSize; + circular_queue::m_outPos.store(outPos, std::memory_order_release); + } while (outPos != inPos0); + return true; +} + +#endif // __circular_queue_mp_h diff --git a/libraries/ScheduledInterrupts/keywords.txt b/libraries/ScheduledInterrupts/keywords.txt index b8c0fc83f1..5e6571998c 100644 --- a/libraries/ScheduledInterrupts/keywords.txt +++ b/libraries/ScheduledInterrupts/keywords.txt @@ -9,5 +9,4 @@ ArgStructure KEYWORD1 # Methods and Functions (KEYWORD2) ####################################### -attachInterrupt KEYWORD2 attachScheduledInterrupt KEYWORD2 diff --git a/libraries/ScheduledInterrupts/src/ScheduledInterrupts.cpp b/libraries/ScheduledInterrupts/src/ScheduledInterrupts.cpp index d583b48be0..6c3c0b6c98 100644 --- a/libraries/ScheduledInterrupts/src/ScheduledInterrupts.cpp +++ b/libraries/ScheduledInterrupts/src/ScheduledInterrupts.cpp @@ -1,5 +1,5 @@ #include "ScheduledInterrupts.h" -#include +#include #include // Duplicate typedefs from core_esp8266_wiring_digital_c diff --git a/libraries/SoftwareSerial b/libraries/SoftwareSerial index 4abc14f429..2b0119030c 160000 --- a/libraries/SoftwareSerial +++ b/libraries/SoftwareSerial @@ -1 +1 @@ -Subproject commit 4abc14f4295f3d2dd296f535c48740339edc6d4d +Subproject commit 2b0119030c444571a0f704f1d9470254aef0c9ee diff --git a/libraries/Ticker/examples/TickerBasic/TickerBasic.ino b/libraries/Ticker/examples/TickerBasic/TickerBasic.ino index 2fabd98315..a387b554b7 100644 --- a/libraries/Ticker/examples/TickerBasic/TickerBasic.ino +++ b/libraries/Ticker/examples/TickerBasic/TickerBasic.ino @@ -14,6 +14,10 @@ #include +#ifndef LED_BUILTIN +#define LED_BUILTIN 13 +#endif + Ticker flipper; int count = 0; diff --git a/libraries/Ticker/examples/TickerParameter/TickerParameter.ino b/libraries/Ticker/examples/TickerParameter/TickerParameter.ino index 80734a1a6f..9267c95772 100644 --- a/libraries/Ticker/examples/TickerParameter/TickerParameter.ino +++ b/libraries/Ticker/examples/TickerParameter/TickerParameter.ino @@ -13,6 +13,10 @@ #include +#ifndef LED_BUILTIN +#define LED_BUILTIN 13 +#endif + Ticker tickerSetHigh; Ticker tickerSetAnalog; Ticker tickerSetLow; diff --git a/libraries/Ticker/src/Ticker.h b/libraries/Ticker/src/Ticker.h index 074e5ba32c..6800ebab9b 100644 --- a/libraries/Ticker/src/Ticker.h +++ b/libraries/Ticker/src/Ticker.h @@ -23,7 +23,7 @@ #define TICKER_H #include -#include +#include #include class Ticker diff --git a/tests/device/test_schedule/test_schedule.ino b/tests/device/test_schedule/test_schedule.ino index 26c7d114db..61b91e68c1 100644 --- a/tests/device/test_schedule/test_schedule.ino +++ b/tests/device/test_schedule/test_schedule.ino @@ -1,5 +1,5 @@ #include -#include +#include BS_ENV_DECLARE(); diff --git a/tests/host/Makefile b/tests/host/Makefile index 1893cf0847..ecb27dc1af 100644 --- a/tests/host/Makefile +++ b/tests/host/Makefile @@ -67,7 +67,7 @@ CORE_CPP_FILES := $(addprefix $(CORE_PATH)/,\ spiffs/spiffs_nucleus.cpp \ libb64/cencode.cpp \ libb64/cdecode.cpp \ - ../../libraries/Schedule/src/Schedule.cpp \ + ../../libraries/FastScheduler/src/FastScheduler.cpp \ HardwareSerial.cpp \ ) \ $(addprefix $(LIBRARIES_PATH)/ESP8266SdFat/src/, \ From 59df72772da44979144fae50268878e2f70d43ac Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 26 Jul 2019 12:08:08 +0200 Subject: [PATCH 18/22] Pull in Ticker.cpp into host build, FastScheduler needs it. --- tests/host/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/host/Makefile b/tests/host/Makefile index ecb27dc1af..0b9148a2e2 100644 --- a/tests/host/Makefile +++ b/tests/host/Makefile @@ -67,6 +67,7 @@ CORE_CPP_FILES := $(addprefix $(CORE_PATH)/,\ spiffs/spiffs_nucleus.cpp \ libb64/cencode.cpp \ libb64/cdecode.cpp \ + ../../libraries/Ticker/src/Ticker.cpp \ ../../libraries/FastScheduler/src/FastScheduler.cpp \ HardwareSerial.cpp \ ) \ From f4fbf2fea2ad757b9380c2568dd0cd5f83a9c526 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sat, 27 Jul 2019 12:56:43 +0200 Subject: [PATCH 19/22] circular_queue Windows portability fix - included for version consistency --- .../FastScheduler/src/circular_queue/circular_queue.h | 10 ++++++---- libraries/SoftwareSerial | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/FastScheduler/src/circular_queue/circular_queue.h b/libraries/FastScheduler/src/circular_queue/circular_queue.h index 53279c19f8..7564ee8c55 100644 --- a/libraries/FastScheduler/src/circular_queue/circular_queue.h +++ b/libraries/FastScheduler/src/circular_queue/circular_queue.h @@ -33,6 +33,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define IRAM_ATTR #endif +using std::min; + /*! @brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO). This implementation is lock-free between producer and consumer for the available(), peek(), @@ -236,7 +238,7 @@ size_t circular_queue::push_n(const T* buffer, size_t size) const auto outPos = m_outPos.load(std::memory_order_relaxed); size_t blockSize = (outPos > inPos) ? outPos - 1 - inPos : (outPos == 0) ? m_bufSize - 1 - inPos : m_bufSize - inPos; - blockSize = std::min(size, blockSize); + blockSize = min(size, blockSize); if (!blockSize) return 0; int next = (inPos + blockSize) % m_bufSize; @@ -244,7 +246,7 @@ size_t circular_queue::push_n(const T* buffer, size_t size) auto dest = m_buffer.get() + inPos; std::copy_n(std::make_move_iterator(buffer), blockSize, dest); - size = std::min(size - blockSize, outPos > 1 ? static_cast(outPos - next - 1) : 0); + size = min(size - blockSize, outPos > 1 ? static_cast(outPos - next - 1) : 0); next += size; dest = m_buffer.get(); std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest); @@ -273,10 +275,10 @@ T circular_queue::pop() template< typename T > size_t circular_queue::pop_n(T* buffer, size_t size) { - size_t avail = size = std::min(size, available()); + size_t avail = size = min(size, available()); if (!avail) return 0; const auto outPos = m_outPos.load(std::memory_order_acquire); - size_t n = std::min(avail, static_cast(m_bufSize - outPos)); + size_t n = min(avail, static_cast(m_bufSize - outPos)); std::atomic_thread_fence(std::memory_order_acquire); diff --git a/libraries/SoftwareSerial b/libraries/SoftwareSerial index 2b0119030c..343702048b 160000 --- a/libraries/SoftwareSerial +++ b/libraries/SoftwareSerial @@ -1 +1 @@ -Subproject commit 2b0119030c444571a0f704f1d9470254aef0c9ee +Subproject commit 343702048b1dca715b30ad6e189805c055d01d69 From 725b6c965298d5a98b2d91222802741baa441339 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 30 Jul 2019 21:26:11 +0200 Subject: [PATCH 20/22] Enhanced circular_queue from EspSoftwareSerial. --- .../src/circular_queue/circular_queue.h | 43 ++++++++++++++++--- .../src/circular_queue/circular_queue_mp.h | 10 ++++- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/libraries/FastScheduler/src/circular_queue/circular_queue.h b/libraries/FastScheduler/src/circular_queue/circular_queue.h index 7564ee8c55..5fb25fa56a 100644 --- a/libraries/FastScheduler/src/circular_queue/circular_queue.h +++ b/libraries/FastScheduler/src/circular_queue/circular_queue.h @@ -123,19 +123,35 @@ class circular_queue } /*! - @brief Peek at the next element pop returns without removing it from the queue. - @return An rvalue copy of the next element that can be popped, or a default - value of type T if the queue is empty. + @brief Peek at the next element pop will return without removing it from the queue. + @return An rvalue copy of the next element that can be popped. If the queue is empty, + return an rvalue copy of the element that is pending the next push. */ T peek() const { - const auto outPos = m_outPos.load(std::memory_order_acquire); + const auto outPos = m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + return m_buffer[outPos]; + } + + /*! + @brief Peek at the next pending input value. + @return A reference to the next element that can be pushed. + */ + T& IRAM_ATTR pushpeek() + { const auto inPos = m_inPos.load(std::memory_order_relaxed); std::atomic_thread_fence(std::memory_order_acquire); - if (inPos == outPos) return defaultValue; - else return m_buffer[outPos]; + return m_buffer[inPos]; } + /*! + @brief Release the next pending input value, accessible by pushpeek(), into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(); + /*! @brief Move the rvalue parameter into the queue. @return true if the queue accepted the value, false if the queue @@ -212,6 +228,21 @@ bool circular_queue::capacity(const size_t cap) return true; } +template< typename T > +bool IRAM_ATTR circular_queue::push() +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const unsigned next = (inPos + 1) % m_bufSize; + if (next == m_outPos.load(std::memory_order_relaxed)) { + return false; + } + + std::atomic_thread_fence(std::memory_order_acquire); + + m_inPos.store(next, std::memory_order_release); + return true; +} + template< typename T > bool IRAM_ATTR circular_queue::push(T&& val) { diff --git a/libraries/FastScheduler/src/circular_queue/circular_queue_mp.h b/libraries/FastScheduler/src/circular_queue/circular_queue_mp.h index b0d12fb2ec..1f8d301899 100644 --- a/libraries/FastScheduler/src/circular_queue/circular_queue_mp.h +++ b/libraries/FastScheduler/src/circular_queue/circular_queue_mp.h @@ -71,6 +71,14 @@ class circular_queue_mp : protected circular_queue return circular_queue::capacity(cap); } + bool IRAM_ATTR push() = delete; + + /*! + @brief Move the rvalue parameter into the queue, guarded + for multiple concurrent producers. + @return true if the queue accepted the value, false if the queue + was full. + */ bool IRAM_ATTR push(T&& val) { #ifdef ESP8266 @@ -82,7 +90,7 @@ class circular_queue_mp : protected circular_queue } /*! - @brief Move the rvalue parameter into the queue, guarded + @brief Push a copy of the parameter into the queue, guarded for multiple concurrent producers. @return true if the queue accepted the value, false if the queue was full. From db643569706a8cd93d5df380c0f028b99f233335 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 31 Jul 2019 08:19:35 +0200 Subject: [PATCH 21/22] Futher enhancements to circular_queue from EspSoftwareSerial. --- .../FastScheduler/src/circular_queue/circular_queue.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/FastScheduler/src/circular_queue/circular_queue.h b/libraries/FastScheduler/src/circular_queue/circular_queue.h index 5fb25fa56a..130877a264 100644 --- a/libraries/FastScheduler/src/circular_queue/circular_queue.h +++ b/libraries/FastScheduler/src/circular_queue/circular_queue.h @@ -186,6 +186,7 @@ class circular_queue /*! @brief Pop multiple elements in ordered sequence from the queue to a buffer. + If buffer is nullptr, simply discards up to size elements from the queue. @return The number of elements actually popped from the queue to buffer. */ @@ -313,9 +314,11 @@ size_t circular_queue::pop_n(T* buffer, size_t size) { std::atomic_thread_fence(std::memory_order_acquire); - buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); - avail -= n; - std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); + if (buffer) { + buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); + avail -= n; + std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); + } std::atomic_thread_fence(std::memory_order_release); From 3aceb033713e815bda1ae1d14800b51c7c345df0 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 1 Aug 2019 15:47:29 +0200 Subject: [PATCH 22/22] ESP32 portability fix --- libraries/FastScheduler/src/FastScheduler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/FastScheduler/src/FastScheduler.cpp b/libraries/FastScheduler/src/FastScheduler.cpp index e235f3a39f..aa1e598139 100644 --- a/libraries/FastScheduler/src/FastScheduler.cpp +++ b/libraries/FastScheduler/src/FastScheduler.cpp @@ -17,6 +17,7 @@ extern "C" run_scheduled_functions(SCHEDULE_FUNCTION_FROM_LOOP); } +#ifdef ESP8266 void __esp_yield(); extern "C" void esp_yield() @@ -24,6 +25,7 @@ extern "C" __esp_yield(); run_scheduled_functions(SCHEDULE_FUNCTION_WITHOUT_YIELDELAYCALLS); } +#endif } typedef std::function mFuncT;