diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..69a66b9 --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: Google +IndentWidth: 2 diff --git a/platformio.ini b/platformio.ini index 6bb127a..dfc264a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,7 +8,48 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html -[env:nodemcuv2] -platform = espressif8266 -board = nodemcuv2 +; [env:nodemcuv2] +; platform = espressif8266 +; board = nodemcuv2 +; framework = arduino + +[platformio] +default_envs = + d1_mini + +[env] +; Global data for all [env:***] framework = arduino +lib_ldf_mode = deep +monitor_speed = 115200 + +[espressif8266_base] +;this section has config items common to all ESP8266 boards +platform = espressif8266 +build_flags = + -Wl,-Teagle.flash.4m1m.ld + -Wall + -Wno-reorder + +[env:d1_mini] +extends = espressif8266_base +board = d1_mini +build_flags = + ${espressif8266_base.build_flags} + -D LED_BUILTIN=2 +board_build.f_cpu = 160000000L +upload_resetmethod = nodemcu +upload_speed = 460800 + +[espressif32_base] +;this section has config items common to all ESP32 boards +platform = espressif32 +build_unflags = -Werror=reorder +board_build.partitions = min_spiffs.csv +monitor_filters = esp32_exception_decoder + +[env:esp32dev] +extends = espressif32_base +board = esp32dev +build_flags = + -D LED_BUILTIN=2 diff --git a/src/ReactESP.cpp b/src/ReactESP.cpp index a1fba08..b19fbee 100644 --- a/src/ReactESP.cpp +++ b/src/ReactESP.cpp @@ -1,155 +1,176 @@ +#include "ReactESP.h" + #include -#include #include +#include -#include "ReactESP.h" +/** + * @brief Return the current time since the device restart in microseconds + * + * Returns the time since the device restart. Even though the time + * is in microseconds, a 64-bit integer is all but guaranteed not to + * rewrap, ever. + */ +#ifdef ESP32 +uint64_t ICACHE_RAM_ATTR micros64() { return esp_timer_get_time(); } +#endif // Reaction classes define the behaviour of each particular // Reaction bool TimedReaction::operator<(const TimedReaction& other) { - return (this->last_trigger_time + this->interval) < - (other.last_trigger_time + other.interval); + return (this->last_trigger_time + this->interval) < + (other.last_trigger_time + other.interval); } -void TimedReaction::add() { - ReactESP::app->timed_queue.push(this); -} +void TimedReaction::add() { ReactESP::app->timed_queue.push(this); } void TimedReaction::remove() { - this->enabled = false; - // the object will be deleted when it's popped out of the - // timer queue + this->enabled = false; + // the object will be deleted when it's popped out of the + // timer queue } -DelayReaction::DelayReaction(uint32_t interval, const react_callback callback) - : TimedReaction(interval, callback) { - this->last_trigger_time = millis(); +DelayReaction::DelayReaction(uint32_t interval, const react_callback callback) + : TimedReaction(interval, callback) { + this->last_trigger_time = micros64(); } -void DelayReaction::tick() { - this->last_trigger_time = millis(); - this->callback(); - delete this; +DelayReaction::DelayReaction(uint64_t interval, const react_callback callback) + : TimedReaction(interval, callback) { + this->last_trigger_time = micros64(); } +void DelayReaction::tick() { + this->last_trigger_time = micros64(); + this->callback(); + delete this; +} void RepeatReaction::tick() { - this->last_trigger_time = millis(); - this->callback(); - ReactESP::app->timed_queue.push(this); + auto now = micros64(); + this->last_trigger_time = this->last_trigger_time + this->interval; + if (this->last_trigger_time + this->interval < now) { + // we're lagging more than one full interval; reset the time + this->last_trigger_time = now; + } + this->callback(); + ReactESP::app->timed_queue.push(this); } - -void UntimedReaction::add() { - ReactESP::app->untimed_list.push_front(this); -} +void UntimedReaction::add() { ReactESP::app->untimed_list.push_front(this); } void UntimedReaction::remove() { - ReactESP::app->untimed_list.remove(this); - delete this; + ReactESP::app->untimed_list.remove(this); + delete this; } - void StreamReaction::tick() { - if (stream.available()) { - this->callback(); - } -} - - -void TickReaction::tick() { + if (stream.available()) { this->callback(); + } } +void TickReaction::tick() { this->callback(); } void ISRReaction::add() { - attachInterrupt(digitalPinToInterrupt(pin_number), callback, mode); - ReactESP::app->isr_reaction_list.push_front(this); + attachInterrupt(digitalPinToInterrupt(pin_number), callback, mode); + ReactESP::app->isr_reaction_list.push_front(this); } void ISRReaction::remove() { - ReactESP::app->isr_reaction_list.remove(this); - detachInterrupt(digitalPinToInterrupt(this->pin_number)); - delete this; + ReactESP::app->isr_reaction_list.remove(this); + detachInterrupt(digitalPinToInterrupt(this->pin_number)); + delete this; } // Need to define the static variable outside of the class ReactESP* ReactESP::app = NULL; -void setup(void) -{ - ReactESP::app->setup(); -} +void setup(void) { ReactESP::app->setup(); } -void loop(void) -{ - ReactESP::app->tick(); - yield(); +void loop(void) { + ReactESP::app->tick(); + yield(); } void ReactESP::tickTimed() { - uint32_t now = millis(); - uint32_t trigger_t; - TimedReaction* top; - - while (true) { - if (timed_queue.empty()) { - break; - } - top = timed_queue.top(); - if (!top->isEnabled()) { - timed_queue.pop(); - delete top; - continue; - } - trigger_t = top->getTriggerTime(); - if (now>=trigger_t) { - timed_queue.pop(); - top->tick(); - } else { - break; - } + uint64_t now = micros64(); + uint64_t trigger_t; + TimedReaction* top; + + while (true) { + if (timed_queue.empty()) { + break; + } + top = timed_queue.top(); + if (!top->isEnabled()) { + timed_queue.pop(); + delete top; + continue; } + trigger_t = top->getTriggerTimeMicros(); + if (now >= trigger_t) { + timed_queue.pop(); + top->tick(); + } else { + break; + } + } } void ReactESP::tickUntimed() { - for (UntimedReaction* re : this->untimed_list) { - re->tick(); - } + for (UntimedReaction* re : this->untimed_list) { + re->tick(); + } } void ReactESP::tick() { - tickUntimed(); - tickTimed(); + tickUntimed(); + tickTimed(); } DelayReaction* ReactESP::onDelay(const uint32_t t, const react_callback cb) { - DelayReaction* dre = new DelayReaction(t, cb); - dre->add(); - return dre; + DelayReaction* dre = new DelayReaction(t, cb); + dre->add(); + return dre; +} + +DelayReaction* ReactESP::onDelayMicros(const uint64_t t, + const react_callback cb) { + DelayReaction* dre = new DelayReaction(t, cb); + dre->add(); + return dre; } RepeatReaction* ReactESP::onRepeat(const uint32_t t, const react_callback cb) { - RepeatReaction* rre = new RepeatReaction(t, cb); - rre->add(); - return rre; + RepeatReaction* rre = new RepeatReaction(t, cb); + rre->add(); + return rre; +} + +RepeatReaction* ReactESP::onRepeatMicros(const uint64_t t, + const react_callback cb) { + RepeatReaction* rre = new RepeatReaction(t, cb); + rre->add(); + return rre; } StreamReaction* ReactESP::onAvailable(Stream& stream, const react_callback cb) { - StreamReaction *sre = new StreamReaction(stream, cb); - sre->add(); - return sre; + StreamReaction* sre = new StreamReaction(stream, cb); + sre->add(); + return sre; } -ISRReaction* ReactESP::onInterrupt(const uint8_t pin_number, int mode, const react_callback cb) { - ISRReaction* isrre = new ISRReaction(pin_number, mode, cb); - isrre->add(); - return isrre; +ISRReaction* ReactESP::onInterrupt(const uint8_t pin_number, int mode, + const react_callback cb) { + ISRReaction* isrre = new ISRReaction(pin_number, mode, cb); + isrre->add(); + return isrre; } TickReaction* ReactESP::onTick(const react_callback cb) { - TickReaction* tre = new TickReaction(cb); - tre->add(); - return tre; + TickReaction* tre = new TickReaction(cb); + tre->add(); + return tre; } diff --git a/src/ReactESP.h b/src/ReactESP.h index b6296ee..af6b045 100644 --- a/src/ReactESP.h +++ b/src/ReactESP.h @@ -13,6 +13,11 @@ typedef std::function react_callback; class ReactESP; +// ESP32 doesn't have the micros64 function defined +#ifdef ESP32 +uint64_t ICACHE_RAM_ATTR micros64(); +#endif + /** * @brief Reactions are code to be called when a given condition is fulfilled */ @@ -38,27 +43,40 @@ class Reaction { */ class TimedReaction : public Reaction { protected: - const uint32_t interval; - uint32_t last_trigger_time; + const uint64_t interval; + uint64_t last_trigger_time; bool enabled; public: /** * @brief Construct a new Timed Reaction object * - * @param interval Interval or delay for the reaction + * @param interval Interval or delay for the reaction, in milliseconds * @param callback Function to be called when the reaction is triggered */ TimedReaction(const uint32_t interval, const react_callback callback) + : Reaction(callback), interval((uint64_t)1000 * (uint64_t)interval) { + last_trigger_time = micros64(); + enabled = true; + } + /** + * @brief Construct a new Timed Reaction object + * + * @param interval Interval, in microseconds + * @param callback Function to be called when the reaction is triggered + */ + TimedReaction(const uint64_t interval, const react_callback callback) : Reaction(callback), interval(interval) { - last_trigger_time = millis(); + last_trigger_time = micros64(); enabled = true; } + virtual ~TimedReaction() {} bool operator<(const TimedReaction& other); void add(); void remove(); - uint32_t getTriggerTime() { return last_trigger_time + interval; } + uint32_t getTriggerTime() { return (last_trigger_time + interval) / 1000; } + uint64_t getTriggerTimeMicros() { return (last_trigger_time + interval); } bool isEnabled() { return enabled; } virtual void tick() = 0; }; @@ -79,6 +97,13 @@ class DelayReaction : public TimedReaction { * @param callback Function to be called after the delay */ DelayReaction(const uint32_t delay, const react_callback callback); + /** + * @brief Construct a new Delay Reaction object + * + * @param delay Delay, in microseconds + * @param callback Function to be called after the delay + */ + DelayReaction(const uint64_t delay, const react_callback callback); virtual ~DelayReaction() {} void tick(); }; @@ -96,6 +121,14 @@ class RepeatReaction : public TimedReaction { */ RepeatReaction(const uint32_t interval, const react_callback callback) : TimedReaction(interval, callback) {} + /** + * @brief Construct a new Repeat Reaction object + * + * @param interval Repetition interval, in microseconds + * @param callback Function to be called at every repetition + */ + RepeatReaction(const uint64_t interval, const react_callback callback) + : TimedReaction(interval, callback) {} void tick(); }; @@ -206,6 +239,14 @@ class ReactESP { * @return DelayReaction* */ DelayReaction* onDelay(const uint32_t t, const react_callback cb); + /** + * @brief Create a new DelayReaction + * + * @param t Delay, in microseconds + * @param cb Callback function + * @return DelayReaction* + */ + DelayReaction* onDelayMicros(const uint64_t t, const react_callback cb); /** * @brief Create a new RepeatReaction * @@ -214,6 +255,14 @@ class ReactESP { * @return RepeatReaction* */ RepeatReaction* onRepeat(const uint32_t t, const react_callback cb); + /** + * @brief Create a new RepeatReaction + * + * @param t Interval, in microseconds + * @param cb Callback function + * @return RepeatReaction* + */ + RepeatReaction* onRepeatMicros(const uint64_t t, const react_callback cb); /** * @brief Create a new StreamReaction *