diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a01030 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pioenvs +.piolibdeps +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7c486f1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,67 @@ +# Continuous Integration (CI) is the practice, in software +# engineering, of merging all developer working copies with a shared mainline +# several times a day < https://docs.platformio.org/page/ci/index.html > +# +# Documentation: +# +# * Travis CI Embedded Builds with PlatformIO +# < https://docs.travis-ci.com/user/integration/platformio/ > +# +# * PlatformIO integration with Travis CI +# < https://docs.platformio.org/page/ci/travis.html > +# +# * User Guide for `platformio ci` command +# < https://docs.platformio.org/page/userguide/cmd_ci.html > +# +# +# Please choose one of the following templates (proposed below) and uncomment +# it (remove "# " before each line) or use own configuration according to the +# Travis CI documentation (see above). +# + + +# +# Template #1: General project. Test it using existing `platformio.ini`. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio run + + +# +# Template #2: The project is intended to be used as a library with examples. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# env: +# - PLATFORMIO_CI_SRC=path/to/test/file.c +# - PLATFORMIO_CI_SRC=examples/file.ino +# - PLATFORMIO_CI_SRC=path/to/test/directory +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N diff --git a/examples/torture_test/src/main.cpp b/examples/torture_test/src/main.cpp index 3a23dc6..6330f90 100644 --- a/examples/torture_test/src/main.cpp +++ b/examples/torture_test/src/main.cpp @@ -16,6 +16,8 @@ extern "C" { int timer_ticks[NUM_TIMERS]; int tick_counter = 0; +int out_pin_state = 0; + void setup_timers(Reactduino &app) { for (int i=0; ifree(app); + // led_off = nullptr; + //} + + int current = reaction_counter; - led_off = app.onDelay(1000, [] () { digitalWrite(LED_PIN, LOW); }); + app.onDelay(1000, [current] () { + if (reaction_counter==current) { + digitalWrite(LED_PIN, LOW); + } + }); }); } @@ -78,7 +87,7 @@ void setup_tick(Reactduino &app) { Reactduino app([] () { Serial.begin(115200); pinMode(LED_PIN, OUTPUT); - + setup_timers(app); setup_io_pins(app); setup_serial(app); diff --git a/lib/readme.txt b/lib/readme.txt new file mode 100644 index 0000000..cfa16df --- /dev/null +++ b/lib/readme.txt @@ -0,0 +1,41 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link them to executable files. + +The source code of each library should be placed in separate directories, like +"lib/private_lib/[here are source files]". + +For example, see the structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- readme.txt --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +Then in `src/main.c` you should use: + +#include +#include + +// rest H/C/CPP code + +PlatformIO will find your libraries automatically, configure preprocessor's +include paths and build them. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..6bb127a --- /dev/null +++ b/platformio.ini @@ -0,0 +1,14 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:nodemcuv2] +platform = espressif8266 +board = nodemcuv2 +framework = arduino diff --git a/src/Reactduino.cpp b/src/Reactduino.cpp index 91379a4..583f3e9 100644 --- a/src/Reactduino.cpp +++ b/src/Reactduino.cpp @@ -8,16 +8,19 @@ // Reaction classes define the behaviour of each particular // Reaction -void Reaction::free(Reactduino& app, const reaction_idx r) { - this->disable(); - app._table[r] = nullptr; +bool TimedReaction::operator<(const TimedReaction& other) { + return (this->last_trigger_time + this->interval) < + (other.last_trigger_time + other.interval); +} - // Move the top of the stack pointer down if we free from the top - if (app._top == r + 1) { - app._top--; - } +void TimedReaction::alloc() { + Reactduino::app->timed_queue.push(this); +} - return; +void TimedReaction::free() { + 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) @@ -25,49 +28,62 @@ DelayReaction::DelayReaction(uint32_t interval, const react_callback callback) this->last_trigger_time = millis(); } -void DelayReaction::tick(Reactduino& app, const reaction_idx r_pos) { - uint32_t elapsed; - uint32_t now = millis(); - elapsed = now - this->last_trigger_time; - if (elapsed >= this->interval) { - this->last_trigger_time = now; - app.free(r_pos); - this->callback(); - delete this; - } +void DelayReaction::tick() { + this->last_trigger_time = millis(); + this->callback(); + delete this; } -void RepeatReaction::tick(Reactduino& app, const reaction_idx r_pos) { - uint32_t elapsed; - uint32_t now = millis(); - elapsed = now - this->last_trigger_time; - if (elapsed >= this->interval) { - this->last_trigger_time = now; - this->callback(); - } + +void RepeatReaction::tick() { + this->last_trigger_time = millis(); + this->callback(); + Reactduino::app->timed_queue.push(this); +} + + +void UntimedReaction::alloc() { + Reactduino::app->untimed_list.push_front(this); } -void StreamReaction::tick(Reactduino& app, const reaction_idx r_pos) { +void UntimedReaction::free() { + Reactduino::app->untimed_list.remove(this); + delete this; +} + + +void StreamReaction::tick() { if (stream.available()) { this->callback(); } } -void TickReaction::tick(Reactduino& app, const reaction_idx r_pos) { + +void TickReaction::tick() { this->callback(); } -void ISRReaction::tick(Reactduino& app, const reaction_idx r_pos) { - if (react_isr_check(this->pin_number)) { - this->callback(); - } + +void ISRReaction::alloc() { + auto handler = [this] () { + Reactduino::app->isr_pending_list.push_front(this); + //Serial.printf("Got interrupt for pint %d\n", this->pin_number); + }; + attachInterrupt(digitalPinToInterrupt(pin_number), handler, mode); + Reactduino::app->isr_reaction_list.push_front(this); } -void ISRReaction::disable() { - detachInterrupt(this->pin_number); - react_isr_free(this->isr); +void ISRReaction::free() { + Reactduino::app->isr_reaction_list.remove(this); + detachInterrupt(digitalPinToInterrupt(this->pin_number)); + delete this; } +void ISRReaction::tick() { + this->callback(); +} + + // Need to define the static variable outside of the class Reactduino* Reactduino::app = NULL; @@ -92,114 +108,78 @@ void Reactduino::setup(void) _setup(); } -void Reactduino::tick(void) -{ - reaction_idx r; - - for (r = 0; r < _top; r++) { - Reaction* r_entry = _table[r]; - - if (r_entry==nullptr) { +void Reactduino::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; } - - r_entry->tick(*app, r); + trigger_t = top->getTriggerTime(); + if (now>=trigger_t) { + timed_queue.pop(); + top->tick(); + } else { + break; + } } } -reaction_idx Reactduino::onDelay(const uint32_t t, const react_callback cb) { - return alloc(new DelayReaction(t, cb)); -} - -reaction_idx Reactduino::onRepeat(const uint32_t t, const react_callback cb) { - return alloc(new RepeatReaction(t, cb)); -} - -reaction_idx Reactduino::onAvailable(Stream& stream, const react_callback cb) -{ - return alloc(new StreamReaction(stream, cb)); -} - -reaction_idx Reactduino::onInterrupt(const uint8_t number, const react_callback cb, int mode) -{ - reaction_idx r; - int8_t isr; - - // Obtain and use ISR to handle the interrupt, see: ReactduinoISR.c/.h - - isr = react_isr_alloc(); - - if (isr == INVALID_ISR) { - return INVALID_REACTION; +void Reactduino::tickUntimed() { + for (UntimedReaction* re : this->untimed_list) { + re->tick(); } +} - r = alloc(new ISRReaction(number, isr, cb)); - - if (r == INVALID_REACTION) { - react_isr_free(isr); - return INVALID_REACTION; +void Reactduino::tickISR() { + ISRReaction* isrre; + while (!this->isr_pending_list.empty()) { + isrre = this->isr_pending_list.front(); + this->isr_pending_list.pop_front(); + isrre->tick(); } - - attachInterrupt(number, react_isr_get(isr), mode); - - return r; } -reaction_idx Reactduino::onPinRising(const uint8_t pin, const react_callback cb) -{ - return onInterrupt(digitalPinToInterrupt(pin), cb, RISING); +void Reactduino::tick() { + tickTimed(); + tickUntimed(); + tickISR(); } -reaction_idx Reactduino::onPinFalling(const uint8_t pin, const react_callback cb) -{ - return onInterrupt(digitalPinToInterrupt(pin), cb, FALLING); +DelayReaction* Reactduino::onDelay(const uint32_t t, const react_callback cb) { + DelayReaction* dre = new DelayReaction(t, cb); + dre->alloc(); + return dre; } -reaction_idx Reactduino::onPinChange(const uint8_t pin, const react_callback cb) -{ - return onInterrupt(digitalPinToInterrupt(pin), cb, CHANGE); +RepeatReaction* Reactduino::onRepeat(const uint32_t t, const react_callback cb) { + RepeatReaction* rre = new RepeatReaction(t, cb); + rre->alloc(); + return rre; } -reaction_idx Reactduino::onTick(const react_callback cb) -{ - return alloc(new TickReaction(cb)); +StreamReaction* Reactduino::onAvailable(Stream& stream, const react_callback cb) { + StreamReaction *sre = new StreamReaction(stream, cb); + sre->alloc(); + return sre; } -Reaction* Reactduino::free(const reaction_idx r) -{ - if (r == INVALID_REACTION) { - return nullptr; - } - - Reaction* re = _table[r]; - - if (re==nullptr) { - return nullptr; - } - - re->free(*app, r); - return re; +ISRReaction* Reactduino::onInterrupt(const uint8_t pin_number, int mode, const react_callback cb) { + ISRReaction* isrre = new ISRReaction(pin_number, mode, cb); + isrre->alloc(); + return isrre; } -reaction_idx Reactduino::alloc(Reaction *re) -{ - reaction_idx r; - - for (r = 0; r < REACTDUINO_MAX_REACTIONS; r++) { - // If we're at the top of the stak or the allocated flag isn't set - if (r >= _top || _table[r] == nullptr) { - _table[r] = re; - // Reaction is enabled - //_table[r]->flags = REACTION_FLAG_ENABLED; - - // Move the stack pointer up if we add to the top - if (r >= _top) { - _top = r + 1; - } - - return r; - } - } - - return INVALID_REACTION; +TickReaction* Reactduino::onTick(const react_callback cb) { + TickReaction* tre = new TickReaction(cb); + tre->alloc(); + return tre; } diff --git a/src/Reactduino.h b/src/Reactduino.h index ee9df4e..207ff3b 100644 --- a/src/Reactduino.h +++ b/src/Reactduino.h @@ -2,35 +2,12 @@ #define REACTDUINO_H_ #include -#include #include - -#define INVALID_REACTION -1 - -#ifndef REACTDUINO_MAX_REACTIONS -#define REACTDUINO_MAX_REACTIONS 50 -#endif +#include +#include typedef std::function react_callback; -typedef int32_t reaction_idx; - -#define REACTION_FLAG_ENABLED 0x40 -#define REACTION_TYPE_MASK 0x3F - -#define REACTION_TYPE_DELAY 0 -#define REACTION_TYPE_REPEAT 1 -#define REACTION_TYPE_STREAM 2 -#define REACTION_TYPE_INTERRUPT 3 -#define REACTION_TYPE_TICK 4 - -#define REACTION_TYPE(x) ((x) & REACTION_TYPE_MASK) - -#define INPUT_STATE_HIGH HIGH -#define INPUT_STATE_LOW LOW -#define INPUT_STATE_ANY 0xFF -#define INPUT_STATE_UNSET 0xFE - // forward declarations @@ -43,41 +20,63 @@ class Reaction { protected: const react_callback callback; public: - Reaction(const react_callback callback) + Reaction(react_callback callback) : callback(callback) {} - virtual void alloc(Reactduino& app); - void free(Reactduino& app, reaction_idx r); - virtual void disable() {} - uint8_t flags; - virtual void tick(Reactduino& app, const reaction_idx r_pos) {} + // FIXME: why do these have to be defined? + virtual void alloc() = 0; + virtual void free() = 0; + virtual void tick() = 0; }; class TimedReaction : public Reaction { + friend class Reactduino; // FIXME: remove after debugging protected: const uint32_t interval; uint32_t last_trigger_time; + bool enabled; public: - TimedReaction(uint32_t interval, react_callback callback) - : interval(interval), Reaction(callback) {} + TimedReaction(const uint32_t interval, const react_callback callback) + : interval(interval), Reaction(callback) { + last_trigger_time = millis(); + enabled = true; + } + bool operator<(const TimedReaction& other); + void alloc(); + void free(); + uint32_t getTriggerTime() { return last_trigger_time + interval; } + bool isEnabled() { return enabled; } + virtual void tick() = 0; }; +struct TriggerTimeCompare +{ + bool operator()(TimedReaction* a, TimedReaction* b) + { + return *b < *a; + } +}; + + class DelayReaction : public TimedReaction { public: DelayReaction(const uint32_t interval, const react_callback callback); - void tick(Reactduino& app, const reaction_idx r_pos); + void tick(); }; class RepeatReaction: public TimedReaction { public: RepeatReaction(const uint32_t interval, const react_callback callback) : TimedReaction(interval, callback) {} - void tick(Reactduino& app, const reaction_idx r_pos); + void tick(); }; class UntimedReaction : public Reaction { public: UntimedReaction(const react_callback callback) : Reaction(callback) {} + virtual void alloc(); + virtual void free(); + virtual void tick() = 0; }; class StreamReaction : public UntimedReaction { @@ -86,25 +85,26 @@ class StreamReaction : public UntimedReaction { public: StreamReaction(Stream& stream, const react_callback callback) : stream(stream), UntimedReaction(callback) {} - void tick(Reactduino& app, const reaction_idx r_pos); + void tick(); }; class TickReaction : public UntimedReaction { public: TickReaction(const react_callback callback) : UntimedReaction(callback) {} - void tick(Reactduino& app, const reaction_idx r_pos); + void tick(); }; -class ISRReaction : public UntimedReaction { +class ISRReaction : public Reaction { private: const uint32_t pin_number; + const int mode; public: - ISRReaction(uint32_t pin_number, int8_t isr, react_callback callback) - : pin_number(pin_number), isr(isr), UntimedReaction(callback) {} - int8_t isr; - void disable(); - void tick(Reactduino& app, const reaction_idx r_pos); + ISRReaction(uint32_t pin_number, int mode, const react_callback callback) + : pin_number(pin_number), mode(mode), Reaction(callback) {} + void alloc(); + void free(); + void tick(); }; @@ -114,8 +114,12 @@ class ISRReaction : public UntimedReaction { class Reactduino { friend class Reaction; + friend class TimedReaction; + friend class RepeatReaction; + friend class UntimedReaction; + friend class ISRReaction; public: - Reactduino(react_callback cb); + Reactduino(const react_callback cb); void setup(void); void tick(void); @@ -123,24 +127,22 @@ class Reactduino static Reactduino* app; // Public API - reaction_idx onDelay(const uint32_t t, const react_callback cb); - reaction_idx onRepeat(const uint32_t t, const react_callback cb); - reaction_idx onAvailable(Stream& stream, const react_callback cb); - reaction_idx onInterrupt(const uint8_t number, const react_callback cb, int mode); - reaction_idx onPinRising(const uint8_t pin, const react_callback cb); - reaction_idx onPinFalling(const uint8_t pin, const react_callback cb); - reaction_idx onPinChange(const uint8_t pin, const react_callback cb); - reaction_idx onTick(const react_callback cb); - - Reaction* free(const reaction_idx r); + DelayReaction* onDelay(const uint32_t t, const react_callback cb); + RepeatReaction* onRepeat(const uint32_t t, const react_callback cb); + StreamReaction* onAvailable(Stream& stream, const react_callback cb); + ISRReaction* onInterrupt(const uint8_t pin_number, int mode, const react_callback cb); + TickReaction* onTick(const react_callback cb); private: const react_callback _setup; - Reaction* _table[REACTDUINO_MAX_REACTIONS]; - reaction_idx _top = 0; - + std::priority_queue, TriggerTimeCompare> timed_queue; + std::forward_list untimed_list; + std::forward_list isr_reaction_list; + std::forward_list isr_pending_list; + void tickTimed(); + void tickUntimed(); + void tickISR(); void alloc(Reaction* re); - }; #endif