From a641fad56ce0d007269e1e445f2fd5cb91c5eb1e Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Fri, 9 Aug 2019 09:17:05 +0700 Subject: [PATCH] update Code --- .../WaterishOS-minimal2.0.ino | 102 +- .../FlowMeter-master/.github/CONTRIBUTING.md | 7 + .../.github/ISSUE_TEMPLATE.md | 9 + .../.github/ISSUE_TEMPLATE/bug_report.md | 37 + .../.github/ISSUE_TEMPLATE/feature_request.md | 17 + .../.github/PULL_REQUEST_TEMPLATE.md | 11 + libraries/FlowMeter-master/LICENSE | 22 + libraries/FlowMeter-master/README.md | 87 ++ .../examples/Calibration/Calibration.ino | 48 + .../FlowMeter-master/examples/Multi/Multi.ino | 51 + .../examples/Simple/Simple.ino | 40 + .../examples/Simulator/Simulator.ino | 43 + .../FlowMeter-master/examples/platformio.ini | 19 + libraries/FlowMeter-master/src/FlowMeter.cpp | 120 ++ libraries/FlowMeter-master/src/FlowMeter.h | 171 +++ libraries/MQTT_Client/MQTTClient.cpp | 409 +++++++ libraries/MQTT_Client/MQTTClient.h | 98 ++ libraries/MQTT_Client/README.md | 91 ++ .../MQTT_Client/examples/Hello/Hello.ino | 59 + .../examples/LowPowerHello/LowPowerHello.ino | 79 ++ .../PublishLightAndDark.ino | 123 ++ libraries/MQTT_Client/keywords.txt | 7 + libraries/MQTT_Client/library.properties | 9 + libraries/PubSubClient/CHANGES.txt | 76 ++ libraries/PubSubClient/LICENSE.txt | 20 + libraries/PubSubClient/README.md | 48 + .../examples/mqtt_auth/mqtt_auth.ino | 43 + .../examples/mqtt_basic/mqtt_basic.ino | 77 ++ .../examples/mqtt_esp8266/mqtt_esp8266.ino | 132 +++ .../mqtt_large_message/mqtt_large_message.ino | 179 +++ .../mqtt_publish_in_callback.ino | 60 + .../mqtt_reconnect_nonblocking.ino | 67 ++ .../examples/mqtt_stream/mqtt_stream.ino | 57 + libraries/PubSubClient/keywords.txt | 33 + libraries/PubSubClient/library.json | 17 + libraries/PubSubClient/library.properties | 9 + libraries/PubSubClient/src/PubSubClient.cpp | 653 +++++++++++ libraries/PubSubClient/src/PubSubClient.h | 173 +++ libraries/PubSubClient/tests/.gitignore | 4 + libraries/PubSubClient/tests/Makefile | 25 + libraries/PubSubClient/tests/README.md | 93 ++ .../PubSubClient/tests/src/connect_spec.cpp | 302 +++++ .../PubSubClient/tests/src/keepalive_spec.cpp | 185 +++ .../PubSubClient/tests/src/lib/Arduino.h | 26 + .../PubSubClient/tests/src/lib/BDDTest.cpp | 50 + .../PubSubClient/tests/src/lib/BDDTest.h | 23 + .../PubSubClient/tests/src/lib/Buffer.cpp | 34 + libraries/PubSubClient/tests/src/lib/Buffer.h | 23 + libraries/PubSubClient/tests/src/lib/Client.h | 21 + .../PubSubClient/tests/src/lib/IPAddress.cpp | 44 + .../PubSubClient/tests/src/lib/IPAddress.h | 72 ++ libraries/PubSubClient/tests/src/lib/Print.h | 28 + .../PubSubClient/tests/src/lib/ShimClient.cpp | 153 +++ .../PubSubClient/tests/src/lib/ShimClient.h | 51 + .../PubSubClient/tests/src/lib/Stream.cpp | 39 + libraries/PubSubClient/tests/src/lib/Stream.h | 22 + libraries/PubSubClient/tests/src/lib/trace.h | 10 + .../PubSubClient/tests/src/publish_spec.cpp | 190 +++ .../PubSubClient/tests/src/receive_spec.cpp | 279 +++++ .../PubSubClient/tests/src/subscribe_spec.cpp | 177 +++ .../PubSubClient/tests/testcases/__init__.py | 0 .../tests/testcases/mqtt_basic.py | 39 + .../testcases/mqtt_publish_in_callback.py | 59 + .../PubSubClient/tests/testcases/settings.py | 2 + libraries/PubSubClient/tests/testsuite.py | 181 +++ libraries/PubSubClientTools/MqttWildcard.cpp | 53 + libraries/PubSubClientTools/MqttWildcard.h | 17 + .../PubSubClientTools/PubSubClientTools.cpp | 83 ++ .../PubSubClientTools/PubSubClientTools.h | 59 + libraries/PubSubClientTools/README.md | 10 + .../examples/mqtt_esp32/mqtt_esp32.ino | 70 ++ .../examples/mqtt_esp8266/mqtt_esp8266.ino | 70 ++ libraries/PubSubClientTools/keywords.txt | 21 + .../PubSubClientTools/library.properties | 9 + libraries/TaskScheduler-master/LICENSE.txt | 27 + libraries/TaskScheduler-master/README | 211 ++++ libraries/TaskScheduler-master/README.md | 77 ++ .../Scheduler_example01.ino | 84 ++ .../Scheduler_example02.ino | 78 ++ .../Scheduler_example03.ino | 80 ++ .../Scheduler_example04_StatusRequest.ino | 90 ++ .../Scheduler_example05_StatusRequest.ino | 181 +++ .../Scheduler_example06_IDLE.ino | 117 ++ .../Scheduler_example07_WDT.ino | 133 +++ .../Scheduler_example08_LTS.ino | 147 +++ .../Scheduler_example09_TimeCritical.ino | 57 + .../Scheduler_example10_Benchmark.ino | 106 ++ .../Scheduler_example11_Priority.ino | 105 ++ .../Scheduler_example12_Priority.ino | 136 +++ .../Scheduler_example13_Micros.ino | 71 ++ .../Scheduler_example14_Yield.ino | 339 ++++++ .../Scheduler_example15_STDFunction.ino | 53 + .../Scheduler_example16_Multitab.ino | 18 + .../Scheduler_example16_Multitab/file1.cpp | 31 + .../Scheduler_example16_Multitab/file2.cpp | 55 + .../Scheduler_example16_Multitab/header.hpp | 15 + .../Scheduler_example16_Multitab/ts.cpp | 20 + .../Scheduler_example17_Timeout.ino | 90 ++ ...xample18_StatusRequest_LTS_WDT_Timeout.ino | 172 +++ .../Scheduler_example19_Dynamic_Tasks.ino | 540 +++++++++ ...0_StatusRequest_LTS_WDT_Timeout_Object.ino | 185 +++ .../SuperSensor.cpp | 36 + .../SuperSensor.h | 25 + .../Calculator.cpp | 66 ++ .../Calculator.h | 34 + .../Scheduler_example21_OO_Callbacks.ino | 76 ++ .../SuperSensor.cpp | 83 ++ .../SuperSensor.h | 44 + .../Ticker.cpp | 20 + .../Scheduler_example21_OO_Callbacks/Ticker.h | 28 + .../Scheduler_template/Scheduler_template.ino | 78 ++ .../extras/TaskScheduler.doc | Bin 0 -> 253952 bytes .../extras/TaskScheduler.pdf | Bin 0 -> 341993 bytes .../extras/TaskScheduler_html.png | Bin 0 -> 41281 bytes libraries/TaskScheduler-master/keywords.txt | 108 ++ libraries/TaskScheduler-master/library.json | 22 + .../TaskScheduler-master/library.properties | 9 + .../TaskScheduler-master/src/TaskScheduler.h | 1019 +++++++++++++++++ .../src/TaskSchedulerDeclarations.h | 309 +++++ 119 files changed, 10997 insertions(+), 5 deletions(-) create mode 100644 libraries/FlowMeter-master/.github/CONTRIBUTING.md create mode 100644 libraries/FlowMeter-master/.github/ISSUE_TEMPLATE.md create mode 100644 libraries/FlowMeter-master/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 libraries/FlowMeter-master/.github/ISSUE_TEMPLATE/feature_request.md create mode 100644 libraries/FlowMeter-master/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 libraries/FlowMeter-master/LICENSE create mode 100644 libraries/FlowMeter-master/README.md create mode 100644 libraries/FlowMeter-master/examples/Calibration/Calibration.ino create mode 100644 libraries/FlowMeter-master/examples/Multi/Multi.ino create mode 100644 libraries/FlowMeter-master/examples/Simple/Simple.ino create mode 100644 libraries/FlowMeter-master/examples/Simulator/Simulator.ino create mode 100644 libraries/FlowMeter-master/examples/platformio.ini create mode 100644 libraries/FlowMeter-master/src/FlowMeter.cpp create mode 100644 libraries/FlowMeter-master/src/FlowMeter.h create mode 100644 libraries/MQTT_Client/MQTTClient.cpp create mode 100644 libraries/MQTT_Client/MQTTClient.h create mode 100644 libraries/MQTT_Client/README.md create mode 100644 libraries/MQTT_Client/examples/Hello/Hello.ino create mode 100644 libraries/MQTT_Client/examples/LowPowerHello/LowPowerHello.ino create mode 100644 libraries/MQTT_Client/examples/PublishLightAndDark/PublishLightAndDark.ino create mode 100644 libraries/MQTT_Client/keywords.txt create mode 100644 libraries/MQTT_Client/library.properties create mode 100644 libraries/PubSubClient/CHANGES.txt create mode 100644 libraries/PubSubClient/LICENSE.txt create mode 100644 libraries/PubSubClient/README.md create mode 100644 libraries/PubSubClient/examples/mqtt_auth/mqtt_auth.ino create mode 100644 libraries/PubSubClient/examples/mqtt_basic/mqtt_basic.ino create mode 100644 libraries/PubSubClient/examples/mqtt_esp8266/mqtt_esp8266.ino create mode 100644 libraries/PubSubClient/examples/mqtt_large_message/mqtt_large_message.ino create mode 100644 libraries/PubSubClient/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino create mode 100644 libraries/PubSubClient/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino create mode 100644 libraries/PubSubClient/examples/mqtt_stream/mqtt_stream.ino create mode 100644 libraries/PubSubClient/keywords.txt create mode 100644 libraries/PubSubClient/library.json create mode 100644 libraries/PubSubClient/library.properties create mode 100644 libraries/PubSubClient/src/PubSubClient.cpp create mode 100644 libraries/PubSubClient/src/PubSubClient.h create mode 100644 libraries/PubSubClient/tests/.gitignore create mode 100644 libraries/PubSubClient/tests/Makefile create mode 100644 libraries/PubSubClient/tests/README.md create mode 100644 libraries/PubSubClient/tests/src/connect_spec.cpp create mode 100644 libraries/PubSubClient/tests/src/keepalive_spec.cpp create mode 100644 libraries/PubSubClient/tests/src/lib/Arduino.h create mode 100644 libraries/PubSubClient/tests/src/lib/BDDTest.cpp create mode 100644 libraries/PubSubClient/tests/src/lib/BDDTest.h create mode 100644 libraries/PubSubClient/tests/src/lib/Buffer.cpp create mode 100644 libraries/PubSubClient/tests/src/lib/Buffer.h create mode 100644 libraries/PubSubClient/tests/src/lib/Client.h create mode 100644 libraries/PubSubClient/tests/src/lib/IPAddress.cpp create mode 100644 libraries/PubSubClient/tests/src/lib/IPAddress.h create mode 100644 libraries/PubSubClient/tests/src/lib/Print.h create mode 100644 libraries/PubSubClient/tests/src/lib/ShimClient.cpp create mode 100644 libraries/PubSubClient/tests/src/lib/ShimClient.h create mode 100644 libraries/PubSubClient/tests/src/lib/Stream.cpp create mode 100644 libraries/PubSubClient/tests/src/lib/Stream.h create mode 100644 libraries/PubSubClient/tests/src/lib/trace.h create mode 100644 libraries/PubSubClient/tests/src/publish_spec.cpp create mode 100644 libraries/PubSubClient/tests/src/receive_spec.cpp create mode 100644 libraries/PubSubClient/tests/src/subscribe_spec.cpp create mode 100644 libraries/PubSubClient/tests/testcases/__init__.py create mode 100644 libraries/PubSubClient/tests/testcases/mqtt_basic.py create mode 100644 libraries/PubSubClient/tests/testcases/mqtt_publish_in_callback.py create mode 100644 libraries/PubSubClient/tests/testcases/settings.py create mode 100644 libraries/PubSubClient/tests/testsuite.py create mode 100644 libraries/PubSubClientTools/MqttWildcard.cpp create mode 100644 libraries/PubSubClientTools/MqttWildcard.h create mode 100644 libraries/PubSubClientTools/PubSubClientTools.cpp create mode 100644 libraries/PubSubClientTools/PubSubClientTools.h create mode 100644 libraries/PubSubClientTools/README.md create mode 100644 libraries/PubSubClientTools/examples/mqtt_esp32/mqtt_esp32.ino create mode 100644 libraries/PubSubClientTools/examples/mqtt_esp8266/mqtt_esp8266.ino create mode 100644 libraries/PubSubClientTools/keywords.txt create mode 100644 libraries/PubSubClientTools/library.properties create mode 100644 libraries/TaskScheduler-master/LICENSE.txt create mode 100644 libraries/TaskScheduler-master/README create mode 100644 libraries/TaskScheduler-master/README.md create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example01/Scheduler_example01.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example02/Scheduler_example02.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example03/Scheduler_example03.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example04_StatusRequest/Scheduler_example04_StatusRequest.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example05_StatusRequest/Scheduler_example05_StatusRequest.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example06_IDLE/Scheduler_example06_IDLE.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example07_WDT/Scheduler_example07_WDT.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example08_LTS/Scheduler_example08_LTS.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example09_TimeCritical/Scheduler_example09_TimeCritical.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example11_Priority/Scheduler_example11_Priority.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example12_Priority/Scheduler_example12_Priority.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example14_Yield/Scheduler_example14_Yield.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example15_STDFunction/Scheduler_example15_STDFunction.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/Scheduler_example16_Multitab.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/file1.cpp create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/file2.cpp create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/header.hpp create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/ts.cpp create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example17_Timeout/Scheduler_example17_Timeout.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example18_StatusRequest_LTS_WDT_Timeout/Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example19_Dynamic_Tasks/Scheduler_example19_Dynamic_Tasks.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.cpp create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.h create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Calculator.cpp create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Calculator.h create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Scheduler_example21_OO_Callbacks.ino create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/SuperSensor.cpp create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/SuperSensor.h create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Ticker.cpp create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Ticker.h create mode 100644 libraries/TaskScheduler-master/examples/Scheduler_template/Scheduler_template.ino create mode 100644 libraries/TaskScheduler-master/extras/TaskScheduler.doc create mode 100644 libraries/TaskScheduler-master/extras/TaskScheduler.pdf create mode 100644 libraries/TaskScheduler-master/extras/TaskScheduler_html.png create mode 100644 libraries/TaskScheduler-master/keywords.txt create mode 100644 libraries/TaskScheduler-master/library.json create mode 100644 libraries/TaskScheduler-master/library.properties create mode 100644 libraries/TaskScheduler-master/src/TaskScheduler.h create mode 100644 libraries/TaskScheduler-master/src/TaskSchedulerDeclarations.h diff --git a/WaterishOS-minimal2.0/WaterishOS-minimal2.0.ino b/WaterishOS-minimal2.0/WaterishOS-minimal2.0.ino index 95c2b6e..775187e 100644 --- a/WaterishOS-minimal2.0/WaterishOS-minimal2.0.ino +++ b/WaterishOS-minimal2.0/WaterishOS-minimal2.0.ino @@ -1,9 +1,101 @@ -void setup() { - // put your setup code here, to run once: +#include +#include +#include +Adafruit_MCP23017 mcp; +byte ledPin=13; +byte InteruptPinA=1; +byte InteruptPinB=3; +byte arduinoInterrupt=1; +FlowMeter sensor[12]= +volatile boolean awakenByInterrupt = false; +// Two pins at the MCP (Ports A/B where some buttons have been setup.) +// Buttons connect the pin to grond, and pins are pulled up. +byte mcpPinA=7; +byte mcpPinB=15; + +void setup(){ + pinMode(1, FUNCTION_3); + pinMode(3, FUNCTION_3); + + pinMode(arduinoIntPin,INPUT); + mcp.begin(); // use default address 0 + mcp.setupInterrupts(true,false,LOW); + // configuration for a button on port A + mcp.pinMode(mcpPinA, INPUT); + // mcp.pullUp(mcpPinA, HIGH); // turn on a 100K pullup internally + mcp.setupInterruptPin(mcpPinA,RISING); + mcp.pinMode(mcpPinB, INPUT); + // mcp.pullUp(mcpPinB, HIGH); // turn on a 100K pullup internall + mcp.setupInterruptPin(mcpPinB,RISING); + + // We will setup a pin for flashing from the int routine + pinMode(ledPin, OUTPUT); } -void loop() { - // put your main code here, to run repeatedly: - +// The int handler will just signal that the int has happen +// we will do the work from the main loop. +void intCallBack(){ + awakenByInterrupt=true; +} + +void handleInterrupt(){ + + // Get more information from the MCP from the INT + uint8_t pin=mcp.getLastInterruptPin(); + uint8_t val=mcp.getLastInterruptPinValue(); + + // We will flash the led 1 or 2 times depending on the PIN that triggered the Interrupt + // 3 and 4 flases are supposed to be impossible conditions... just for debugging. + uint8_t flashes=4; + if(pin==mcpPinA) flashes=1; + if(pin==mcpPinB) flashes=2; + if(val!=LOW) flashes=3; + + // simulate some output associated to this + for(int i=0;i // https://github.com/sekdiy/FlowMeter + +// enter your own sensor properties here, including calibration points +FlowSensorProperties MySensor = {60.0f, 5.5f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; +FlowMeter Meter = FlowMeter(2, MySensor); + +// timekeeping variables +long period = 1000; // one second (in milliseconds) +long lastTime = 0; + +// define an 'interrupt service routine' (ISR) +void MeterISR() { + // let our flow meter count the pulses + Meter.count(); +} + +void setup() { + // prepare serial communication + Serial.begin(9600); + + // enable a call to the 'interrupt service handler' (ISR) on every rising edge at the interrupt pin + attachInterrupt(INT0, MeterISR, RISING); + + // sometimes initializing the gear generates some pulses that we should ignore + Meter.reset(); +} + +void loop() { + // do some timekeeping + long currentTime = millis(); + long duration = currentTime - lastTime; + + // wait between display updates + if (duration >= period) { + + // process the counted ticks + Meter.tick(duration); + + // output some measurement result + Serial.println("FlowMeter - current flow rate: " + String(Meter.getCurrentFlowrate()) + " l/min, " + + "nominal volume: " + String(Meter.getTotalVolume()) + " l, " + + "compensated error: " + String(Meter.getCurrentError()) + " %, " + + "duration: " + String(Meter.getTotalDuration() / 1000) + " s."); + + // prepare for next cycle + lastTime = currentTime; + } +} diff --git a/libraries/FlowMeter-master/examples/Multi/Multi.ino b/libraries/FlowMeter-master/examples/Multi/Multi.ino new file mode 100644 index 0000000..afead68 --- /dev/null +++ b/libraries/FlowMeter-master/examples/Multi/Multi.ino @@ -0,0 +1,51 @@ +#include // https://github.com/sekdiy/FlowMeter + +// connect a flow meter to an interrupt pin (see notes on your Arduino model for pin numbers) +FlowMeter Meter1 = FlowMeter(2); +FlowMeter Meter2 = FlowMeter(3); + +// set the measurement update period to 1s (1000 ms) +const unsigned long period = 1000; + +// define an 'interrupt service handler' (ISR) for every interrupt pin you use +void Meter1ISR() { + // let our flow meter count the pulses + Meter1.count(); +} + +// define an 'interrupt service handler' (ISR) for every interrupt pin you use +void Meter2ISR() { + // let our flow meter count the pulses + Meter2.count(); +} + +void setup() { + // prepare serial communication + Serial.begin(9600); + + // enable a call to the 'interrupt service handler' (ISR) on every rising edge at the interrupt pin + // do this setup step for every ISR you have defined, depending on how many interrupts you use + attachInterrupt(INT0, Meter1ISR, RISING); + attachInterrupt(INT1, Meter2ISR, RISING); + + // sometimes initializing the gear generates some pulses that we should ignore + Meter1.reset(); + Meter2.reset(); +} + +void loop() { + // wait between output updates + delay(period); + + // process the (possibly) counted ticks + Meter1.tick(period); + Meter2.tick(period); + + // output some measurement result + Serial.println("Meter 1 currently " + String(Meter1.getCurrentFlowrate()) + " l/min, " + String(Meter1.getTotalVolume())+ " l total."); + Serial.println("Meter 2 currently " + String(Meter2.getCurrentFlowrate()) + " l/min, " + String(Meter2.getTotalVolume())+ " l total."); + + // + // any other code can go here + // +} \ No newline at end of file diff --git a/libraries/FlowMeter-master/examples/Simple/Simple.ino b/libraries/FlowMeter-master/examples/Simple/Simple.ino new file mode 100644 index 0000000..1838df3 --- /dev/null +++ b/libraries/FlowMeter-master/examples/Simple/Simple.ino @@ -0,0 +1,40 @@ +#include // https://github.com/sekdiy/FlowMeter + +// connect a flow meter to an interrupt pin (see notes on your Arduino model for pin numbers) +FlowMeter Meter = FlowMeter(2); + +// set the measurement update period to 1s (1000 ms) +const unsigned long period = 1000; + +// define an 'interrupt service handler' (ISR) for every interrupt pin you use +void MeterISR() { + // let our flow meter count the pulses + Meter.count(); +} + +void setup() { + // prepare serial communication + Serial.begin(9600); + + // enable a call to the 'interrupt service handler' (ISR) on every rising edge at the interrupt pin + // do this setup step for every ISR you have defined, depending on how many interrupts you use + attachInterrupt(INT0, MeterISR, RISING); + + // sometimes initializing the gear generates some pulses that we should ignore + Meter.reset(); +} + +void loop() { + // wait between output updates + delay(period); + + // process the (possibly) counted ticks + Meter.tick(period); + + // output some measurement result + Serial.println("Currently " + String(Meter.getCurrentFlowrate()) + " l/min, " + String(Meter.getTotalVolume())+ " l total."); + + // + // any other code can go here + // +} diff --git a/libraries/FlowMeter-master/examples/Simulator/Simulator.ino b/libraries/FlowMeter-master/examples/Simulator/Simulator.ino new file mode 100644 index 0000000..b2efdde --- /dev/null +++ b/libraries/FlowMeter-master/examples/Simulator/Simulator.ino @@ -0,0 +1,43 @@ +#include // https://github.com/sekdiy/FlowMeter + +// let's provide our own sensor properties, including calibration points for error correction +FlowSensorProperties MySensor = {60.0f, 4.5f, {1.2, 1.1, 1.05, 1, 1, 1, 1, 0.95, 0.9, 0.8}}; + +// let's pretend there's a flow sensor connected to pin 3 +FlowMeter Meter = FlowMeter(3, MySensor); + +void setup() { + // prepare serial communication + Serial.begin(9600); + + // sometimes initializing the gear generates some pulses that we should ignore + Meter.reset(); +} + +void loop() { + // randomly change simulation period and pulse rate + long frequency = random(MySensor.capacity * MySensor.kFactor); // Hz + long period = random(3 * frequency, 5000); // ms + + // simulate random flow meter pulses within a random period + for (long i = 0; i < (long) ((float) period * (float) frequency / 1000.0f); i++) { + Meter.count(); + } + + // wait that random period + delay(period); + + // process the counted ticks + Meter.tick(period); + + // output some measurement result + Serial.println("FlowMeter - period: " + String(Meter.getCurrentDuration() / 1000.0f, 3) + "s, " + + "frequency: " + String(Meter.getCurrentFrequency(), 0) + "Hz, " + + "volume: " + Meter.getCurrentVolume() + "l, " + + "flow rate: " + Meter.getCurrentFlowrate() + "l/min, " + + "error: " + Meter.getCurrentError() + "%, " + + "total duration: " + Meter.getTotalDuration() / 1000.0f + "s, " + + "total volume: " + Meter.getTotalVolume() + "l, " + + "average flow rate: " + Meter.getTotalFlowrate() + "l/min, " + + "average error: " + Meter.getTotalError() + "%."); +} diff --git a/libraries/FlowMeter-master/examples/platformio.ini b/libraries/FlowMeter-master/examples/platformio.ini new file mode 100644 index 0000000..2f3612f --- /dev/null +++ b/libraries/FlowMeter-master/examples/platformio.ini @@ -0,0 +1,19 @@ +# +# Project Configuration File +# +# A detailed documentation with the EXAMPLES is located here: +# http://docs.platformio.org/en/latest/projectconf.html +# + +[env:2009] +platform = atmelavr +framework = arduino +board = diecimilaatmega328 + +[env:pro8] +platform = atmelavr +framework = arduino +board = pro8MHzatmega328 + +# Automatic targets - enable auto-uploading +targets = upload diff --git a/libraries/FlowMeter-master/src/FlowMeter.cpp b/libraries/FlowMeter-master/src/FlowMeter.cpp new file mode 100644 index 0000000..8c9e153 --- /dev/null +++ b/libraries/FlowMeter-master/src/FlowMeter.cpp @@ -0,0 +1,120 @@ +/* + * Flow Meter + */ + +#include "Arduino.h" +#include "FlowMeter.h" // https://github.com/sekdiy/FlowMeter +#include + +FlowMeter::FlowMeter(unsigned int pin, FlowSensorProperties prop) : + _pin(pin), //!< store pin number + _properties(prop) //!< store sensor properties +{ + pinMode(pin, INPUT_PULLUP); //!< initialize interrupt pin as input with pullup +} + +double FlowMeter::getCurrentFlowrate() { + return this->_currentFlowrate; //!< in l/min +} + +double FlowMeter::getCurrentVolume() { + return this->_currentVolume; //!< in l +} + +double FlowMeter::getTotalFlowrate() { + return this->_totalVolume / (this->_totalDuration / 1000.0f) * 60.0f; //!< in l/min +} + +double FlowMeter::getTotalVolume() { + return this->_totalVolume; //!< in l +} + +void FlowMeter::tick(unsigned long duration) { + /* sampling and normalisation */ + double seconds = duration / 1000.0f; //!< normalised duration (in s, i.e. per 1000ms) + cli(); //!< going to change interrupt variable(s) + double frequency = this->_currentPulses / seconds; //!< normalised frequency (in 1/s) + this->_currentPulses = 0; //!< reset pulse counter after successfull sampling + sei(); //!< done changing interrupt variable(s) + + /* determine current correction factor (from sensor properties) */ + unsigned int decile = floor(10.0f * frequency / (this->_properties.capacity * this->_properties.kFactor)); //!< decile of current flow relative to sensor capacity + unsigned int ceiling = 9; //!< highest possible decile index + this->_currentCorrection = this->_properties.kFactor / this->_properties.mFactor[min(decile, ceiling)]; //!< combine constant k-factor and m-factor for decile + + /* update current calculations: */ + this->_currentFlowrate = frequency / this->_currentCorrection; //!< get flow rate (in l/min) from normalised frequency and combined correction factor + this->_currentVolume = this->_currentFlowrate / (60.0f/seconds); //!< get volume (in l) from normalised flow rate and normalised time + + /* update statistics: */ + this->_currentDuration = duration; //!< store current tick duration (convenience, in ms) + this->_currentFrequency = frequency; //!< store current pulses per second (convenience, in 1/s) + this->_totalDuration += duration; //!< accumulate total duration (in ms) + this->_totalVolume += this->_currentVolume; //!< accumulate total volume (in l) + this->_totalCorrection += this->_currentCorrection * duration; //!< accumulate corrections over time +} + +void FlowMeter::count() { + this->_currentPulses++; //!< this should be called from an interrupt service routine +} + +void FlowMeter::reset() { + cli(); //!< going to change interrupt variable(s) + this->_currentPulses = 0; //!< reset pulse counter + sei(); //!< done changing interrupt variable(s) + + this->_currentFrequency = 0.0f; + this->_currentDuration = 0.0f; + this->_currentFlowrate = 0.0f; + this->_currentVolume = 0.0f; + this->_currentCorrection = 0.0f; +} + +unsigned int FlowMeter::getPin() { + return this->_pin; +} + +unsigned long FlowMeter::getCurrentDuration() { + return this->_currentDuration; //!< in ms +} + +double FlowMeter::getCurrentFrequency() { + return this->_currentFrequency; //!< in 1/s +} + +double FlowMeter::getCurrentError() { + /// error (in %) = error * 100 + /// error = correction rate - 1 + /// correction rate = k-factor / correction + return (this->_properties.kFactor / this->_currentCorrection - 1) * 100; //!< in % +} + +unsigned long FlowMeter::getTotalDuration() { + return this->_totalDuration; //!< in ms +} + +double FlowMeter::getTotalError() { + /// average error (in %) = average error * 100 + /// average error = average correction rate - 1 + /// average correction rate = k-factor / corrections over time * total time + return (this->_properties.kFactor / this->_totalCorrection * this->_totalDuration - 1) * 100; +} + +FlowMeter* FlowMeter::setTotalDuration(unsigned long totalDuration) { + this->_totalDuration = totalDuration; + return this; +} + +FlowMeter* FlowMeter::setTotalVolume(double totalVolume) { + this->_totalVolume = totalVolume; + return this; +} + +FlowMeter* FlowMeter::setTotalCorrection(double totalCorrection) { + this->_totalCorrection = totalCorrection; + return this; +} + +FlowSensorProperties UncalibratedSensor = {60.0f, 5.0f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; +FlowSensorProperties FS300A = {60.0f, 5.5f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; +FlowSensorProperties FS400A = {60.0f, 4.8f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; diff --git a/libraries/FlowMeter-master/src/FlowMeter.h b/libraries/FlowMeter-master/src/FlowMeter.h new file mode 100644 index 0000000..5c39125 --- /dev/null +++ b/libraries/FlowMeter-master/src/FlowMeter.h @@ -0,0 +1,171 @@ +/** + * Flow Meter + * + * An Arduino flow meter library that provides calibrated liquid flow and volume measurement with flow sensors. + * + * @author sekdiy (https://github.com/sekdiy/FlowMeter) + * @date 14.07.2015 Initial release. + * @version See git comments for changes. + */ + +#ifndef FLOWMETER_H +#define FLOWMETER_H + +#include "Arduino.h" + +/** + * FlowSensorProperties + * + * Structure that holds essential information about a flow sensor. + * Stores general sensor properties and calibration points. + * + * See file G34_Flow_rate_to_frequency.jpg for reference. + */ +typedef struct { + double capacity; //!< capacity, upper limit of flow rate (in l/min) + double kFactor; //!< "k-factor" (in (pulses/s) / (l/min)), e.g.: 1 pulse/s = kFactor * l/min + double mFactor[10]; //!< multiplicative correction factor near unity, "meter factor" (per decile of flow) +} FlowSensorProperties; + +extern FlowSensorProperties UncalibratedSensor; //!< default sensor +extern FlowSensorProperties FS300A; //!< see documentation about FS300A/SEN02141B +extern FlowSensorProperties FS400A; //!< see documentation about FS400A/USN-HS10TA + +/** + * FlowMeter + */ +class FlowMeter { + public: + /** + * Initializes a new flow meter object. + * + * @param pin The pin that the flow sensor is connected to (has to be interrupt capable, default: INT0). + * @param prop The properties of the actual flow sensor being used (default: UncalibratedSensor). + */ + FlowMeter(unsigned int pin = 2, FlowSensorProperties prop = UncalibratedSensor); + + double getCurrentFlowrate(); //!< Returns the current flow rate since last reset (in l/min). + double getCurrentVolume(); //!< Returns the current volume since last reset (in l). + + double getTotalFlowrate(); //!< Returns the (linear) average flow rate in this flow meter instance (in l/min). + double getTotalVolume(); //!< Returns the total volume flown trough this flow meter instance (in l). + + /** + * The tick method updates all internal calculations at the end of a measurement period. + * + * We're calculating flow and volume data over time. + * The actual pulses have to be sampled using the count method (i.e. via an interrupt service routine). + * + * Flow sensor formulae: + * + * Let K: pulses per second per unit of measure (i.e. (1/s)/(l/min)), + * f: pulse frequency (1/s), + * Q: flow rate (l/min), + * p: sensor pulses (no dimension/unit), + * t: time since last measurements (s). + * + * K = f / Q | units: (1/s) / (l/min) = (1/s) / (l/min) + * <=> | Substitute p / t for f in order to allow for different measurement intervals + * K = (p / t) / Q | units: ((1/s)/(l/min)) = (1/s) / (l/min) + * <=> | Solve for Q: + * Q = (p / t) / K | untis: l/min = 1/s / (1/s / (l/min)) + * <=> | Volume in l: + * V = Q / 60 | units: l = (l/min) / (min) + * + * The property K is sometimes stated in pulses per liter or pulses per gallon. + * In these cases the unit of measure has to be converted accordingly (e.g. from gal/s to l/min). + * See file G34_Flow_rate_to_frequency.jpg for reference. + * + * @param duration The tick duration (in ms). + */ + void tick(unsigned long duration = 1000); + void count(); //!< Increments the internal pulse counter. Serves as an interrupt callback routine. + void reset(); //!< Prepares the flow meter for a fresh measurement. Resets all current values, but not the totals. + + /* + * setters enabling continued metering across power cycles + */ + FlowMeter* setTotalDuration(unsigned long totalDuration); //!< Sets the total (overall) duration (i.e. after power up). + FlowMeter* setTotalVolume(double totalVolume); //!< Sets the total (overall) volume (i.e. after power up). + FlowMeter* setTotalCorrection(double totalCorrection); //!< Sets the total (overall) correction factor (i.e. after power up). + + /* + * convenience methods and calibration helpers + */ + unsigned int getPin(); //!< Returns the Arduino pin number that the flow sensor is connected to. + + unsigned long getCurrentDuration(); //!< Returns the duration of the current tick (in ms). + double getCurrentFrequency(); //!< Returns the pulse rate in the current tick (in 1/s). + double getCurrentError(); //!< Returns the error resulting from the current measurement (in %). + + unsigned long getTotalDuration(); //!< Returns the total run time of this flow meter instance (in ms). + double getTotalError(); //!< Returns the (linear) average error of this flow meter instance (in %). + + protected: + unsigned int _pin; //!< connection pin (has to be interrupt capable!) + FlowSensorProperties _properties; //!< sensor properties (including calibration data) + + unsigned long _currentDuration; //!< current tick duration (convenience, in ms) + double _currentFrequency; //!< current pulses per second (convenience, in 1/s) + double _currentFlowrate = 0.0f; //!< current flow rate (in l/tick), e.g.: 1 l / min = 1 pulse / s / (pulses / s / l / min) + double _currentVolume = 0.0f; //!< current volume (in l), e.g.: 1 l = 1 (l / min) / (60 * s) + double _currentCorrection; //!< currently applied correction factor + + unsigned long _totalDuration = 0.0f; //!< total measured duration since begin of measurement (in ms) + double _totalVolume = 0.0f; //!< total volume since begin of measurement (in l) + double _totalCorrection = 0.0f; //!< accumulated correction factors + + volatile unsigned long _currentPulses = 0; //!< pulses within current sample period +}; + +/** + * FlowSensorCalibration + * + * Convenience class for manipulating sensor properties. + */ +class FlowSensorCalibration { + public: + FlowSensorCalibration() {}; + FlowSensorCalibration(FlowSensorProperties properties): _properties(properties) {}; + + FlowSensorCalibration* setProperties(FlowSensorProperties properties) { + this->_properties = properties; + return this; + }; + + FlowSensorCalibration* setCapacity(double capacity) { + this->_properties.capacity = capacity; + return this; + } + + FlowSensorCalibration* setKFactor(double kFactor) { + this->_properties.kFactor = kFactor; + return this; + } + + FlowSensorCalibration* setMeterFactorPerDecile(unsigned int decile, unsigned int mFactor) { + this->_properties.mFactor[decile] = mFactor; + return this; + } + + FlowSensorProperties getProperties() { + return this->_properties; + } + + double getCapacity() { + return this->_properties.capacity; + } + + double getKFactor() { + return this->_properties.kFactor; + } + + unsigned int getMeterFactorPerDecile(unsigned int decile) { + return this->_properties.mFactor[decile]; + } + + protected: + FlowSensorProperties _properties; +}; + +#endif // FLOWMETER_H diff --git a/libraries/MQTT_Client/MQTTClient.cpp b/libraries/MQTT_Client/MQTTClient.cpp new file mode 100644 index 0000000..d928dfa --- /dev/null +++ b/libraries/MQTT_Client/MQTTClient.cpp @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2018 Andreas Motzek andreas-motzek@t-online.de + * + * This file is part of the MQTT Client package. + * + * You can use, redistribute and/or modify this file under the terms of the Modified Artistic License. + * See http://simplysomethings.de/open+source/modified+artistic+license.html for details. + * + * This file 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. + */ + +#include "MQTTClient.h" + +MQTTClient* MQTTClient::current = NULL; + +MQTTClient::MQTTClient(CooperativeMultitasking* _tasks, Client* _client, const char* _host, uint16_t _port, const char* _clientid, const char* _username, const char* _password, uint16_t _keepalive) { + tasks = _tasks; + client = _client; + host = strdup(_host); + port = _port; + clientid = strdup(_clientid); + username = strdupOrNull(_username); + password = strdupOrNull(_password); + keepalive = _keepalive; + isconnected = false; + head = NULL; + tail = NULL; +} + +MQTTClient::~MQTTClient() { + free(host); + free(clientid); + free(username); + free(password); + host = NULL; + clientid = NULL; + username = NULL; + password = NULL; + // + while (head) { + PublishPacket* next = head->next; + free(head->payload); + delete head; + head = next; + } + // + tail = NULL; + // + if (current == this) current = NULL; +} + +bool MQTTClient::connect() { + if (!current) { + if (client->connect(host, port)) { + if (sendConnectPacket()) { + current = this; + auto task1 = tasks->ifThen([] () -> bool { return current ? current->available() >= 4 : true; }, + [] () -> void { if (current) current->receiveConnectAcknowledgementPacket(); }); + auto task2 = tasks->after(10000, [] () -> void { if (current) current->stop(); }); + tasks->onlyOneOf(task1, task2); + // + return true; + } + // + Serial.println("cannot send connect packet"); + // + stop(); + } else { + Serial.print("cannot connect to "); + Serial.print(host); + Serial.print(":"); + Serial.println(port); + } + } else { + Serial.println("another mqtt client is connected"); + } + // + return false; +} + +bool MQTTClient::connected() { + return isconnected; +} + +bool MQTTClient::publishAcknowledged() { + return !head; +} + +bool MQTTClient::publish(bool retain, const char* topicname, const char* payload) { + PublishPacket* packet = new PublishPacket(); // std::nothrow is default + // + if (packet) { + packet->retain = retain; + packet->topicname = topicname; + packet->payload = strdup(payload); + enqueuePublishPacket(packet); + transmitPublishPacketsAfter(0); + // + return true; + } + // + Serial.println("cannot enqueue publish packet"); + // + return false; +} + +void MQTTClient::disconnect() { + if (isconnected) sendDisconnectPacket(); + // + stop(); +} + +void MQTTClient::enqueuePublishPacket(PublishPacket* packet) { + uint16_t packetid = 1; // 2.3.1 non-zero 16-bit packetid + // + if (head) { + PublishPacket* packet = head; + // + while (packet) { + if (packet->packetid > packetid) packetid = packet->packetid; + // + packet = packet->next; + } + // + packetid++; // biggest packetid plus 1 + } + // + packet->packetid = packetid; + packet->trycount = 0; + packet->next = head; + head = packet; + // + if (!tail) tail = head; +} + +void MQTTClient::transmitPublishPacketsAfter(unsigned long duration) { + tasks->after(duration, [] () -> void { if (current) current->transmitPublishPackets(); }); +} + +void MQTTClient::transmitPublishPackets() { + if (isconnected && current == this && head) { + if (available() >= 4) { + receivePublishAcknowledgementPacket(); + transmitPublishPacketsAfter(100); + // + return; + } + // + if (head->trycount >= 20) { + Serial.println("discarding packet"); + // + removePublishPacket(head->packetid); + transmitPublishPacketsAfter(100); + // + return; + } + // + if (sendHeadPublishPacket()) { + head->trycount++; + rotatePublishPackets(); + transmitPublishPacketsAfter(10000); + // + return; + } + // + Serial.println("cannot send publish packet"); + // + stop(); + } +} + +void MQTTClient::removePublishPacket(uint16_t packetid) { + PublishPacket* last = NULL; + PublishPacket* packet = head; + // + while (packet) { + if (packet->packetid == packetid) { + if (last) last->next = packet->next; + // + if (packet == head) head = packet->next; + // + if (packet == tail) tail = last; + // + free(packet->payload); + delete packet; + // + return; + } + // + last = packet; + packet = packet->next; + } +} + +void MQTTClient::rotatePublishPackets() { + if (!head || head == tail) return; // less than 2 packets + // + tail->next = head; + tail = head; + head = head->next; + tail->next = NULL; +} + +bool MQTTClient::sendConnectPacket() { + int packetlength = 2 + 4 + 1 + 1 + 2 + 2 + strlen(clientid); + // + if (username != NULL) packetlength += (2 + strlen(username)); + // + if (password != NULL) packetlength += (2 + strlen(password)); + // + uint8_t connectflags = 2; // clean session + // + if (username != NULL) connectflags |= 128; + // + if (password != NULL) connectflags |= 64; + // + // Type, Flags, Packet Length + writeTypeFlags(1, 0); // connect, 0 + writePacketLength(packetlength); + // + // Header + writeLengthString("MQTT"); // protocol name + writeByte(4); // protocol level + writeByte(connectflags); + writeShort(keepalive); + // + // Payload + writeLengthString(clientid); + // + if (username != NULL) writeLengthString(username); + // + if (password != NULL) writeLengthString(password); + // + flush(); + // + return !getWriteError(); +} + +void MQTTClient::receiveConnectAcknowledgementPacket() { + uint8_t typeflags = readByte(); + uint8_t packetlength = readByte(); + uint8_t sessionpresent = readByte(); + uint8_t returncode = readByte(); + // + if (typeflags == (2 << 4) && packetlength == 2) { + switch (returncode) { + case 0: Serial.println("connection accepted"); isconnected = true; transmitPublishPacketsAfter(0); return; + case 1: Serial.println("unacceptable protocol version"); break; + case 2: Serial.println("identifier rejected"); break; + case 3: Serial.println("server unavailable"); break; + case 4: Serial.println("bad user name or password"); break; + case 5: Serial.println("not authorized"); break; + default: Serial.println(returncode); break; + } + } else { + Serial.println("not a connect acknowledgement"); + } + // + stop(); +} + +bool MQTTClient::sendHeadPublishPacket() { + int packetlength = 2 + strlen(head->topicname) + 2 + strlen(head->payload); + uint8_t flags = 2; // QoS 1 + // + if (head->trycount > 0) flags |= 8; // duplicate + // + if (head->retain) flags |= 1; + // + // Type, Flags, Packet Length + writeTypeFlags(3, flags); // publish, flags + writePacketLength(packetlength); + // + // Header + writeLengthString(head->topicname); + writeShort(head->packetid); + // + // Payload + writeString(head->payload, strlen(head->payload)); + // + flush(); + // + return !getWriteError(); +} + +void MQTTClient::receivePublishAcknowledgementPacket() { + uint8_t typeflags = readByte(); + uint8_t packetlength = readByte(); + uint16_t packetid = readShort(); + // + if (typeflags == (4 << 4) && packetlength == 2) { + removePublishPacket(packetid); + // + Serial.println("publish acknowledged"); + // + return; + } + // + Serial.println("not a publish acknowledgement"); + disconnect(); +} + +void MQTTClient::sendDisconnectPacket() { + // Type, Flags, Packet Length + writeTypeFlags(14, 0); // disconnect, 0 + writePacketLength(0); + // + flush(); +} + +void MQTTClient::writeTypeFlags(uint8_t type, uint8_t flags) { + writeByte(type << 4 | flags); +} + +void MQTTClient::writePacketLength(int value) { + while (true) { + int digit = value & 127; + value >>= 7; + // + if (value > 0) { + writeByte(digit | 128); + } else { + writeByte(digit); + // + break; + } + } +} + +void MQTTClient::writeLengthString(const char* value) { + size_t len = strlen(value); + // + if (len > 65535) return; + // + writeShort(len); + writeString(value, len); +} + +void MQTTClient::writeString(const char* value, size_t len) { + client->write((uint8_t*) value, len); +} + +void MQTTClient::writeShort(uint16_t value) { + writeByte(value >> 8); + writeByte(value & 255); +} + +void MQTTClient::writeByte(uint8_t value) { + client->write(value); +} + +void MQTTClient::flush() { + client->flush(); +} + +int MQTTClient::getWriteError() { + return client->getWriteError(); +} + +int MQTTClient::available() { + return client->available(); +} + +uint8_t MQTTClient::readByte() { + return client->read(); +} + +uint16_t MQTTClient::readShort() { + uint16_t value = client->read(); + value <<= 8; + value += client->read(); + // + return value; +} + +void MQTTClient::stop() { + if (client->connected()) { + client->stop(); + client->clearWriteError(); + } + // + isconnected = false; + current = NULL; +} + +char* MQTTClient::strdupOrNull(const char* string) { + if (string == NULL) return NULL; + // + return strdup(string); +} + +/* + * + */ + +MQTTTopic::MQTTTopic(MQTTClient* _client, const char* _topicname) { + client = _client; + topicname = strdup(_topicname); +} + +MQTTTopic::~MQTTTopic() { + free(topicname); + topicname = NULL; +} + +bool MQTTTopic::publish(const char* payload, bool retain) { + return client->publish(retain, topicname, payload); +} diff --git a/libraries/MQTT_Client/MQTTClient.h b/libraries/MQTT_Client/MQTTClient.h new file mode 100644 index 0000000..262bcff --- /dev/null +++ b/libraries/MQTT_Client/MQTTClient.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 Andreas Motzek andreas-motzek@t-online.de + * + * This file is part of the MQTT Client package. + * + * You can use, redistribute and/or modify this file under the terms of the Modified Artistic License. + * See http://simplysomethings.de/open+source/modified+artistic+license.html for details. + * + * This file 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. + */ + +#ifndef MQTTClient_h +#define MQTTClient_h + +#include "Client.h" +#include "CooperativeMultitasking.h" + +class MQTTClient { + friend class MQTTTopic; + + private: + struct PublishPacket { + bool retain; + const char* topicname; + char* payload; + uint16_t packetid; + uint16_t trycount; + PublishPacket* next; + }; + + static MQTTClient* current; + + CooperativeMultitasking* tasks; + Client* client; + char* host; + uint16_t port; + char* clientid; + char* username; + char* password; + uint16_t keepalive; + bool isconnected; + PublishPacket* head; + PublishPacket* tail; + + void enqueuePublishPacket(PublishPacket* packet); + void transmitPublishPacketsAfter(unsigned long duration); + void transmitPublishPackets(); + void removePublishPacket(uint16_t packetid); + void rotatePublishPackets(); + + bool sendConnectPacket(); + void receiveConnectAcknowledgementPacket(); + bool sendHeadPublishPacket(); + void receivePublishAcknowledgementPacket(); + void sendDisconnectPacket(); + + void writeTypeFlags(uint8_t type, uint8_t flags); + void writePacketLength(int value); + void writeLengthString(const char* value); + void writeString(const char* value, size_t len); + void writeShort(uint16_t value); + void writeByte(uint8_t value); + uint8_t readByte(); + uint16_t readShort(); + + void flush(); + int getWriteError(); + int available(); + void stop(); + + static char* strdupOrNull(const char* string); + + protected: + bool publish(bool retain, const char* topicname, const char* payload); + + public: + MQTTClient(CooperativeMultitasking* tasks, Client* client, const char* host, uint16_t port, const char* clientid, const char* username, const char* password, uint16_t keepalive = 300); + virtual ~MQTTClient(); + bool connect(); + bool connected(); + bool publishAcknowledged(); + void disconnect(); +}; + +class MQTTTopic { + private: + MQTTClient* client; + char* topicname; + bool retain; + + public: + MQTTTopic(MQTTClient* client, const char* topicname); + virtual ~MQTTTopic(); + bool publish(const char* payload, bool retain = true); +}; + +#endif \ No newline at end of file diff --git a/libraries/MQTT_Client/README.md b/libraries/MQTT_Client/README.md new file mode 100644 index 0000000..624de8c --- /dev/null +++ b/libraries/MQTT_Client/README.md @@ -0,0 +1,91 @@ +## MQTT Client + +MQTT Client is a library that can publish strings to a topic of a MQTT broker. +For using it you have to include the library +[Cooperative Multitasking](https://bitbucket.org/amotzek/cooperative-multitasking) too. + +#### Example + +The following example publishes the string `"Hello"` to the topic `"amotzek/hello"` +of the broker broker.hivemq.com repeatedly. The message can be observed in HiveMQs +[Websockets client](http://www.hivemq.com/demos/websocket-client/). + +The example assumes that you connect to the internet via `Wifi101`, so you need the +SSID and the password of your WLAN. You also need an Arduino board that is compatible +with `Wifi101`, e.g. the MKR1000. If your board connects to the internet in a +different way, you have to pass a different client (for example an `EthernetClient`) +to the constructor of `MQTTClient`. + + #include + #include + #include + #include + + char ssid[] = "..."; + char pass[] = "..."; + char host[] = "broker.hivemq.com"; + char clientid[] = "..."; + char username[] = "..."; + char password[] = "..."; + char topicname[] = "amotzek/hello"; + + CooperativeMultitasking tasks; + WiFiClient wificlient; + MQTTClient mqttclient(&tasks, &wificlient, host, 1883, clientid, username, password); + MQTTTopic topic(&mqttclient, topicname); + + void setup() { + Serial.begin(9600); + // + while (!Serial) { + delay(1000); + } + // + WiFi.begin(ssid, pass); + delay(10000); // wait until WiFi connection is established + } + + void loop() { + if (mqttclient.connect()) { + topic.publish("Hello"); + // + while (tasks.available()) { + tasks.run(); // receive connect acknowledgement, send publish, receive publish acknowledgement + } + // + mqttclient.disconnect(); + } + // + switch (WiFi.status()) { + case WL_CONNECT_FAILED: + case WL_CONNECTION_LOST: + case WL_DISCONNECTED: WiFi.begin(ssid, pass); // reconnect WiFi if necessary + } + // + delay(30000); + } + +#### Details + +To create an instance of class `MQTTClient` you need a reference to an instance of class +`CooperativeMultitasking`, a reference to an instance of a network connection (e.g. from +`WiFi101` or `Ethernet`), the host name of the MQTT broker, its port number (typically +1883) and a client id. If the broker requires that, you also have to pass a user name and +a password. + +To create an instance of class `MQTTTopic` you have to pass a reference to an instance of +`MQTTClient` and the topic name. + +The method `connect()` of `MQTTClient` creates a network connection to the broker. + +You can publish a string by calling the method `publish("...")` of `MQTTTopic`. The +method can be called independently of an established connection to the MQTT broker. It +will delay publishing your message until a connection becomes available. + +The message is published with QoS 1. That means that `MQTTClient` waits for a publish +acknowledgement from the MQTT broker. If the acknowledgement does not arrive in time, +`MQTTClient` retries publishing the message up to 15 times until it gives up. + +`connect` and `publish` work asynchronously. They create tasks that are managed by the +instance of `CooperativeMultitasking`. That's why you have to call it's method `run()` +until all tasks are finished. diff --git a/libraries/MQTT_Client/examples/Hello/Hello.ino b/libraries/MQTT_Client/examples/Hello/Hello.ino new file mode 100644 index 0000000..172dc3a --- /dev/null +++ b/libraries/MQTT_Client/examples/Hello/Hello.ino @@ -0,0 +1,59 @@ +/* + * Publishes "Hello" at regular intervals. The message can be subscribed and observed in HiveMQs + * Websockets client http://www.hivemq.com/demos/websocket-client/ + * + * Copyright (C) 2018 Andreas Motzek andreas-motzek@t-online.de + * + * You can use, redistribute and/or modify this file under the terms of the Modified Artistic License. + * See http://simplysomethings.de/open+source/modified+artistic+license.html for details. + * + * This file 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. + */ + +#include +#include +#include +#include + +char ssid[] = "..."; +char pass[] = "..."; +char host[] = "broker.hivemq.com"; +char clientid[] = " "; +char topicname[] = "amotzek/hello"; + +CooperativeMultitasking tasks; +WiFiClient wificlient; +MQTTClient mqttclient(&tasks, &wificlient, host, 1883, clientid, NULL, NULL); +MQTTTopic topic(&mqttclient, topicname); + +void setup() { + Serial.begin(9600); + // + while (!Serial) { + delay(1000); + } + // + WiFi.begin(ssid, pass); + delay(10000); +} + +void loop() { + if (mqttclient.connect()) { + topic.publish("Hello"); + // + while (tasks.available()) { + tasks.run(); + } + // + mqttclient.disconnect(); + } + // + switch (WiFi.status()) { + case WL_CONNECT_FAILED: + case WL_CONNECTION_LOST: + case WL_DISCONNECTED: WiFi.begin(ssid, pass); + } + // + delay(30000); +} diff --git a/libraries/MQTT_Client/examples/LowPowerHello/LowPowerHello.ino b/libraries/MQTT_Client/examples/LowPowerHello/LowPowerHello.ino new file mode 100644 index 0000000..d33951d --- /dev/null +++ b/libraries/MQTT_Client/examples/LowPowerHello/LowPowerHello.ino @@ -0,0 +1,79 @@ +/* + * Publishes "Hello" at regular intervals. The message can be subscribed and observed in HiveMQs + * Websockets client http://www.hivemq.com/demos/websocket-client/ + * + * If the libraries Arduino Low Power and RTC Zero are included then Cooperative Multitasking + * uses LowPower.idle(...) instead of delay(...). + * + * Copyright (C) 2018 Andreas Motzek andreas-motzek@t-online.de + * + * You can use, redistribute and/or modify this file under the terms of the Modified Artistic License. + * See http://simplysomethings.de/open+source/modified+artistic+license.html for details. + * + * This file 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. + */ + +#include +#include +#include +#include +#include + +char ssid[] = "..."; +char pass[] = "..."; +char host[] = "broker.hivemq.com"; +char clientid[] = " "; +char topicname[] = "amotzek/hello"; + +CooperativeMultitasking tasks; +WiFiClient wificlient; +MQTTClient mqttclient(&tasks, &wificlient, host, 1883, clientid, NULL, NULL); +MQTTTopic topic(&mqttclient, topicname); + +void setup() { + Serial.begin(9600); + // + while (!Serial) { + delay(1000); + } + // + WiFi.begin(ssid, pass); + tasks.after(10000, checkWiFi); // after 10 seconds call checkWiFi() + tasks.after(10000, checkBroker); // after 10 seconds call checkBroker() + tasks.after(30000, publishHello); // after 30 seconds call publishHello() +} + +void loop() { + tasks.run(); +} + +void checkWiFi() { + switch (WiFi.status()) { + case WL_CONNECT_FAILED: + case WL_CONNECTION_LOST: + case WL_DISCONNECTED: + Serial.println("wifi not connected"); + WiFi.begin(ssid, pass); + tasks.after(10000, checkWiFi); // after 10 seconds call checkWiFi() + // + return; + } + // + tasks.after(30000, checkWiFi); // after 30 seconds call checkWiFi() +} + +void checkBroker() { + if (!mqttclient.connected()) { + Serial.println("mqtt client not connected"); + mqttclient.connect(); + } + // + tasks.after(30000, checkBroker); // after 30 seconds call checkBroker() +} + +void publishHello() { + if (mqttclient.connected()) topic.publish("Hello"); + // + tasks.after(30000, publishHello); // after 30 seconds call publishHello() +} diff --git a/libraries/MQTT_Client/examples/PublishLightAndDark/PublishLightAndDark.ino b/libraries/MQTT_Client/examples/PublishLightAndDark/PublishLightAndDark.ino new file mode 100644 index 0000000..9abfbd3 --- /dev/null +++ b/libraries/MQTT_Client/examples/PublishLightAndDark/PublishLightAndDark.ino @@ -0,0 +1,123 @@ +/* + * Make the builtin LED blink, but only when it's dark. Publish light or dark condition to a + * MQTT topic. + * + * Connect an analog light sensor with the Arduino: GND to GND, + * VCC to VCC, SIG to A1. + * + * Copyright (C) 2018 Andreas Motzek andreas-motzek@t-online.de + * + * You can use, redistribute and/or modify this file under the terms of the Modified Artistic License. + * See http://simplysomethings.de/open+source/modified+artistic+license.html for details. + * + * This file 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. + */ + +#include +#include +#include +#include + +char ssid[] = "... WLAN SSID ..."; +char pass[] = "... WLAN password ..."; +char host[] = "... MQTT broker host name ..."; +char clientid[] = "... MQTT client id ..."; +char username[] = "... MQTT user name ..."; +char password[] = "... MQTT password ..."; +char topicname[] = "... MQTT topic name ...."; + +CooperativeMultitasking tasks; +Continuation beginWiFiIfNeeded; +Continuation connectMQTTClientIfNeeded; +Continuation light; +Continuation dark; +Guard isDark; +Continuation on; +Continuation off; +Guard isLight; + +WiFiClient wificlient; +MQTTClient mqttclient(&tasks, &wificlient, host, 1883, clientid, username, password); +MQTTTopic topic(&mqttclient, topicname); + +void setup() { + Serial.begin(9600); + // + while (!Serial) { + delay(1000); + } + // + Serial.println("begin wifi"); + // + WiFi.begin(ssid, pass); + tasks.after(10000, beginWiFiIfNeeded); // after 10 seconds call beginWiFiIfNeeded() + // + Serial.println("connect mqtt client"); + // + mqttclient.connect(); + tasks.after(15000, connectMQTTClientIfNeeded); // after 15 seconds call connectMQTTClientIfNeeded() + // + pinMode(LED_BUILTIN, OUTPUT); + tasks.now(light); // call light() now +} + +void loop() { + tasks.run(); +} + +void light() { + topic.publish("\"light\""); + // + tasks.ifForThen(isDark, 5000, dark); // if isDark() for 5 seconds then call dark() +} + +void dark() { + topic.publish("\"dark\""); + // + tasks.now(on); // call on() now +} + +bool isDark() { + return analogRead(A1) < 300; +} + +void on() { + digitalWrite(LED_BUILTIN, HIGH); + tasks.after(1000, off); // after 1 second call off() +} + +void off() { + digitalWrite(LED_BUILTIN, LOW); + auto task1 = tasks.after(1000, on); // after 1 second call on() + auto task2 = tasks.ifForThen(isLight, 0, light); // if isLight() then call light() + tasks.onlyOneOf(task1, task2); // do either task1 or task2 +} + +bool isLight() { + return analogRead(A1) >= 400; +} + +void beginWiFiIfNeeded() { + switch (WiFi.status()) { + case WL_IDLE_STATUS: + case WL_CONNECTED: tasks.after(30000, beginWiFiIfNeeded); return; // after 30 seconds call beginWiFiIfNeeded() again + case WL_NO_SHIELD: Serial.println("no wifi shield"); return; // do not check again + case WL_CONNECT_FAILED: Serial.println("wifi connect failed"); break; + case WL_CONNECTION_LOST: Serial.println("wifi connection lost"); break; + case WL_DISCONNECTED: Serial.println("wifi disconnected"); break; + } + // + WiFi.begin(ssid, pass); + tasks.after(10000, beginWiFiIfNeeded); // after 10 seconds call beginWiFiIfNeeded() again +} + +void connectMQTTClientIfNeeded() { + if (!mqttclient.connected()) { + Serial.println("mqtt client not connected"); + // + mqttclient.connect(); + } + // + tasks.after(30000, connectMQTTClientIfNeeded); // after 30 seconds call connectMQTTClientIfNeeded() again +} diff --git a/libraries/MQTT_Client/keywords.txt b/libraries/MQTT_Client/keywords.txt new file mode 100644 index 0000000..423676e --- /dev/null +++ b/libraries/MQTT_Client/keywords.txt @@ -0,0 +1,7 @@ +MQTTClient KEYWORD1 +MQTTTopic KEYWORD1 +connect KEYWORD2 +connected KEYWORD2 +publishAcknowledged KEYWORD2 +disconnect KEYWORD2 +publish KEYWORD2 diff --git a/libraries/MQTT_Client/library.properties b/libraries/MQTT_Client/library.properties new file mode 100644 index 0000000..bb6a78d --- /dev/null +++ b/libraries/MQTT_Client/library.properties @@ -0,0 +1,9 @@ +name=MQTT Client +version=1.0.1 +author=Andreas Motzek +maintainer=Andreas Motzek +sentence=MQTT Client lets you connect to a MQTT broker and publish strings to a topic. +paragraph=First create a MQTTClient. You have to pass a task list from Cooperative Multitasking, a client (e.g. WiFiClient from WiFi101), host, port and credentials of the MQTT broker. Then create a MQTTTopic. Use the MQTTClient and the topic name as arguments when doing that. To connect to the broker call MQTTClient::connect(). After that you can call MQTTTopic:publish("...") to publish a string. Requires Cooperative Multitasking for processing the acknowledgement packets of the broker. So you have call CooperativeMultitasking::run() in your loop() function. See the examples for details. +category=Communication +url=https://bitbucket.org/amotzek/arduino/src/fab21e1e7785fe9473d83107048d4431c8fd25a9/src/main/cpp/MQTTClient/?at=master +architectures=samd diff --git a/libraries/PubSubClient/CHANGES.txt b/libraries/PubSubClient/CHANGES.txt new file mode 100644 index 0000000..ff4da62 --- /dev/null +++ b/libraries/PubSubClient/CHANGES.txt @@ -0,0 +1,76 @@ +2.7 + * Fix remaining-length handling to prevent buffer overrun + * Add large-payload API - beginPublish/write/publish/endPublish + * Add yield call to improve reliability on ESP + * Add Clean Session flag to connect options + * Add ESP32 support for functional callback signature + * Various other fixes + +2.4 + * Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely + whilst waiting for inbound data + * Fixed return code when publishing >256 bytes + +2.3 + * Add publish(topic,payload,retained) function + +2.2 + * Change code layout to match Arduino Library reqs + +2.1 + * Add MAX_TRANSFER_SIZE def to chunk messages if needed + * Reject topic/payloads that exceed MQTT_MAX_PACKET_SIZE + +2.0 + * Add (and default to) MQTT 3.1.1 support + * Fix PROGMEM handling for Intel Galileo/ESP8266 + * Add overloaded constructors for convenience + * Add chainable setters for server/callback/client/stream + * Add state function to return connack return code + +1.9 + * Do not split MQTT packets over multiple calls to _client->write() + * API change: All constructors now require an instance of Client + to be passed in. + * Fixed example to match 1.8 api changes - dpslwk + * Added username/password support - WilHall + * Added publish_P - publishes messages from PROGMEM - jobytaffey + +1.8 + * KeepAlive interval is configurable in PubSubClient.h + * Maximum packet size is configurable in PubSubClient.h + * API change: Return boolean rather than int from various functions + * API change: Length parameter in message callback changed + from int to unsigned int + * Various internal tidy-ups around types +1.7 + * Improved keepalive handling + * Updated to the Arduino-1.0 API +1.6 + * Added the ability to publish a retained message + +1.5 + * Added default constructor + * Fixed compile error when used with arduino-0021 or later + +1.4 + * Fixed connection lost handling + +1.3 + * Fixed packet reading bug in PubSubClient.readPacket + +1.2 + * Fixed compile error when used with arduino-0016 or later + + +1.1 + * Reduced size of library + * Added support for Will messages + * Clarified licensing - see LICENSE.txt + + +1.0 + * Only Quality of Service (QOS) 0 messaging is supported + * The maximum message size, including header, is 128 bytes + * The keepalive interval is set to 30 seconds + * No support for Will messages diff --git a/libraries/PubSubClient/LICENSE.txt b/libraries/PubSubClient/LICENSE.txt new file mode 100644 index 0000000..217df35 --- /dev/null +++ b/libraries/PubSubClient/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-2015 Nicholas O'Leary + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/libraries/PubSubClient/README.md b/libraries/PubSubClient/README.md new file mode 100644 index 0000000..69cbb8f --- /dev/null +++ b/libraries/PubSubClient/README.md @@ -0,0 +1,48 @@ +# Arduino Client for MQTT + +This library provides a client for doing simple publish/subscribe messaging with +a server that supports MQTT. + +## Examples + +The library comes with a number of example sketches. See File > Examples > PubSubClient +within the Arduino application. + +Full API documentation is available here: https://pubsubclient.knolleary.net + +## Limitations + + - It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1. + - The maximum message size, including header, is **128 bytes** by default. This + is configurable via `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h`. + - The keepalive interval is set to 15 seconds by default. This is configurable + via `MQTT_KEEPALIVE` in `PubSubClient.h`. + - The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by + changing value of `MQTT_VERSION` in `PubSubClient.h`. + + +## Compatible Hardware + +The library uses the Arduino Ethernet Client api for interacting with the +underlying network hardware. This means it Just Works with a growing number of +boards and shields, including: + + - Arduino Ethernet + - Arduino Ethernet Shield + - Arduino YUN – use the included `YunClient` in place of `EthernetClient`, and + be sure to do a `Bridge.begin()` first + - Arduino WiFi Shield - if you want to send packets > 90 bytes with this shield, + enable the `MQTT_MAX_TRANSFER_SIZE` define in `PubSubClient.h`. + - Sparkfun WiFly Shield – [library](https://github.com/dpslwk/WiFly) + - TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library) + - Intel Galileo/Edison + - ESP8266 + - ESP32 + +The library cannot currently be used with hardware based on the ENC28J60 chip – +such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an +[alternative library](https://github.com/njh/NanodeMQTT) available. + +## License + +This code is released under the MIT License. diff --git a/libraries/PubSubClient/examples/mqtt_auth/mqtt_auth.ino b/libraries/PubSubClient/examples/mqtt_auth/mqtt_auth.ino new file mode 100644 index 0000000..e9f7b18 --- /dev/null +++ b/libraries/PubSubClient/examples/mqtt_auth/mqtt_auth.ino @@ -0,0 +1,43 @@ +/* + Basic MQTT example with Authentication + + - connects to an MQTT server, providing username + and password + - publishes "hello world" to the topic "outTopic" + - subscribes to the topic "inTopic" +*/ + +#include +#include +#include + +// Update these with values suitable for your network. +byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; +IPAddress ip(172, 16, 0, 100); +IPAddress server(172, 16, 0, 2); + +void callback(char* topic, byte* payload, unsigned int length) { + // handle message arrived +} + +EthernetClient ethClient; +PubSubClient client(server, 1883, callback, ethClient); + +void setup() +{ + Ethernet.begin(mac, ip); + // Note - the default maximum packet size is 128 bytes. If the + // combined length of clientId, username and password exceed this, + // you will need to increase the value of MQTT_MAX_PACKET_SIZE in + // PubSubClient.h + + if (client.connect("arduinoClient", "testuser", "testpass")) { + client.publish("outTopic","hello world"); + client.subscribe("inTopic"); + } +} + +void loop() +{ + client.loop(); +} diff --git a/libraries/PubSubClient/examples/mqtt_basic/mqtt_basic.ino b/libraries/PubSubClient/examples/mqtt_basic/mqtt_basic.ino new file mode 100644 index 0000000..f545ade --- /dev/null +++ b/libraries/PubSubClient/examples/mqtt_basic/mqtt_basic.ino @@ -0,0 +1,77 @@ +/* + Basic MQTT example + + This sketch demonstrates the basic capabilities of the library. + It connects to an MQTT server then: + - publishes "hello world" to the topic "outTopic" + - subscribes to the topic "inTopic", printing out any messages + it receives. NB - it assumes the received payloads are strings not binary + + It will reconnect to the server if the connection is lost using a blocking + reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to + achieve the same result without blocking the main loop. + +*/ + +#include +#include +#include + +// Update these with values suitable for your network. +byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; +IPAddress ip(172, 16, 0, 100); +IPAddress server(172, 16, 0, 2); + +void callback(char* topic, byte* payload, unsigned int length) { + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + for (int i=0;i Preferences -> Additional Boards Manager URLs": + http://arduino.esp8266.com/stable/package_esp8266com_index.json + - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266" + - Select your ESP8266 in "Tools -> Board" + +*/ + +#include +#include + +// Update these with values suitable for your network. + +const char* ssid = "........"; +const char* password = "........"; +const char* mqtt_server = "broker.mqtt-dashboard.com"; + +WiFiClient espClient; +PubSubClient client(espClient); +long lastMsg = 0; +char msg[50]; +int value = 0; + +void setup_wifi() { + + delay(10); + // We start by connecting to a WiFi network + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + randomSeed(micros()); + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); +} + +void callback(char* topic, byte* payload, unsigned int length) { + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + for (int i = 0; i < length; i++) { + Serial.print((char)payload[i]); + } + Serial.println(); + + // Switch on the LED if an 1 was received as first character + if ((char)payload[0] == '1') { + digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level + // but actually the LED is on; this is because + // it is active low on the ESP-01) + } else { + digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH + } + +} + +void reconnect() { + // Loop until we're reconnected + while (!client.connected()) { + Serial.print("Attempting MQTT connection..."); + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); + // Attempt to connect + if (client.connect(clientId.c_str())) { + Serial.println("connected"); + // Once connected, publish an announcement... + client.publish("outTopic", "hello world"); + // ... and resubscribe + client.subscribe("inTopic"); + } else { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void setup() { + pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output + Serial.begin(115200); + setup_wifi(); + client.setServer(mqtt_server, 1883); + client.setCallback(callback); +} + +void loop() { + + if (!client.connected()) { + reconnect(); + } + client.loop(); + + long now = millis(); + if (now - lastMsg > 2000) { + lastMsg = now; + ++value; + snprintf (msg, 50, "hello world #%ld", value); + Serial.print("Publish message: "); + Serial.println(msg); + client.publish("outTopic", msg); + } +} diff --git a/libraries/PubSubClient/examples/mqtt_large_message/mqtt_large_message.ino b/libraries/PubSubClient/examples/mqtt_large_message/mqtt_large_message.ino new file mode 100644 index 0000000..e048c3e --- /dev/null +++ b/libraries/PubSubClient/examples/mqtt_large_message/mqtt_large_message.ino @@ -0,0 +1,179 @@ +/* + Long message ESP8266 MQTT example + + This sketch demonstrates sending arbitrarily large messages in combination + with the ESP8266 board/library. + + It connects to an MQTT server then: + - publishes "hello world" to the topic "outTopic" + - subscribes to the topic "greenBottles/#", printing out any messages + it receives. NB - it assumes the received payloads are strings not binary + - If the sub-topic is a number, it publishes a "greenBottles/lyrics" message + with a payload consisting of the lyrics to "10 green bottles", replacing + 10 with the number given in the sub-topic. + + It will reconnect to the server if the connection is lost using a blocking + reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to + achieve the same result without blocking the main loop. + + To install the ESP8266 board, (using Arduino 1.6.4+): + - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs": + http://arduino.esp8266.com/stable/package_esp8266com_index.json + - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266" + - Select your ESP8266 in "Tools -> Board" + +*/ + +#include +#include + +// Update these with values suitable for your network. + +const char* ssid = "........"; +const char* password = "........"; +const char* mqtt_server = "broker.mqtt-dashboard.com"; + +WiFiClient espClient; +PubSubClient client(espClient); +long lastMsg = 0; +char msg[50]; +int value = 0; + +void setup_wifi() { + + delay(10); + // We start by connecting to a WiFi network + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + randomSeed(micros()); + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); +} + +void callback(char* topic, byte* payload, unsigned int length) { + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + for (int i = 0; i < length; i++) { + Serial.print((char)payload[i]); + } + Serial.println(); + + // Find out how many bottles we should generate lyrics for + String topicStr(topic); + int bottleCount = 0; // assume no bottles unless we correctly parse a value from the topic + if (topicStr.indexOf('/') >= 0) { + // The topic includes a '/', we'll try to read the number of bottles from just after that + topicStr.remove(0, topicStr.indexOf('/')+1); + // Now see if there's a number of bottles after the '/' + bottleCount = topicStr.toInt(); + } + + if (bottleCount > 0) { + // Work out how big our resulting message will be + int msgLen = 0; + for (int i = bottleCount; i > 0; i--) { + String numBottles(i); + msgLen += 2*numBottles.length(); + if (i == 1) { + msgLen += 2*String(" green bottle, standing on the wall\n").length(); + } else { + msgLen += 2*String(" green bottles, standing on the wall\n").length(); + } + msgLen += String("And if one green bottle should accidentally fall\nThere'll be ").length(); + switch (i) { + case 1: + msgLen += String("no green bottles, standing on the wall\n\n").length(); + break; + case 2: + msgLen += String("1 green bottle, standing on the wall\n\n").length(); + break; + default: + numBottles = i-1; + msgLen += numBottles.length(); + msgLen += String(" green bottles, standing on the wall\n\n").length(); + break; + }; + } + + // Now we can start to publish the message + client.beginPublish("greenBottles/lyrics", msgLen, false); + for (int i = bottleCount; i > 0; i--) { + for (int j = 0; j < 2; j++) { + client.print(i); + if (i == 1) { + client.print(" green bottle, standing on the wall\n"); + } else { + client.print(" green bottles, standing on the wall\n"); + } + } + client.print("And if one green bottle should accidentally fall\nThere'll be "); + switch (i) { + case 1: + client.print("no green bottles, standing on the wall\n\n"); + break; + case 2: + client.print("1 green bottle, standing on the wall\n\n"); + break; + default: + client.print(i-1); + client.print(" green bottles, standing on the wall\n\n"); + break; + }; + } + // Now we're done! + client.endPublish(); + } +} + +void reconnect() { + // Loop until we're reconnected + while (!client.connected()) { + Serial.print("Attempting MQTT connection..."); + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); + // Attempt to connect + if (client.connect(clientId.c_str())) { + Serial.println("connected"); + // Once connected, publish an announcement... + client.publish("outTopic", "hello world"); + // ... and resubscribe + client.subscribe("greenBottles/#"); + } else { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void setup() { + pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output + Serial.begin(115200); + setup_wifi(); + client.setServer(mqtt_server, 1883); + client.setCallback(callback); +} + +void loop() { + + if (!client.connected()) { + reconnect(); + } + client.loop(); +} diff --git a/libraries/PubSubClient/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino b/libraries/PubSubClient/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino new file mode 100644 index 0000000..42afb2a --- /dev/null +++ b/libraries/PubSubClient/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino @@ -0,0 +1,60 @@ +/* + Publishing in the callback + + - connects to an MQTT server + - subscribes to the topic "inTopic" + - when a message is received, republishes it to "outTopic" + + This example shows how to publish messages within the + callback function. The callback function header needs to + be declared before the PubSubClient constructor and the + actual callback defined afterwards. + This ensures the client reference in the callback function + is valid. + +*/ + +#include +#include +#include + +// Update these with values suitable for your network. +byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; +IPAddress ip(172, 16, 0, 100); +IPAddress server(172, 16, 0, 2); + +// Callback function header +void callback(char* topic, byte* payload, unsigned int length); + +EthernetClient ethClient; +PubSubClient client(server, 1883, callback, ethClient); + +// Callback function +void callback(char* topic, byte* payload, unsigned int length) { + // In order to republish this payload, a copy must be made + // as the orignal payload buffer will be overwritten whilst + // constructing the PUBLISH packet. + + // Allocate the correct amount of memory for the payload copy + byte* p = (byte*)malloc(length); + // Copy the payload to the new buffer + memcpy(p,payload,length); + client.publish("outTopic", p, length); + // Free the memory + free(p); +} + +void setup() +{ + + Ethernet.begin(mac, ip); + if (client.connect("arduinoClient")) { + client.publish("outTopic","hello world"); + client.subscribe("inTopic"); + } +} + +void loop() +{ + client.loop(); +} diff --git a/libraries/PubSubClient/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino b/libraries/PubSubClient/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino new file mode 100644 index 0000000..080b739 --- /dev/null +++ b/libraries/PubSubClient/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino @@ -0,0 +1,67 @@ +/* + Reconnecting MQTT example - non-blocking + + This sketch demonstrates how to keep the client connected + using a non-blocking reconnect function. If the client loses + its connection, it attempts to reconnect every 5 seconds + without blocking the main loop. + +*/ + +#include +#include +#include + +// Update these with values suitable for your hardware/network. +byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; +IPAddress ip(172, 16, 0, 100); +IPAddress server(172, 16, 0, 2); + +void callback(char* topic, byte* payload, unsigned int length) { + // handle message arrived +} + +EthernetClient ethClient; +PubSubClient client(ethClient); + +long lastReconnectAttempt = 0; + +boolean reconnect() { + if (client.connect("arduinoClient")) { + // Once connected, publish an announcement... + client.publish("outTopic","hello world"); + // ... and resubscribe + client.subscribe("inTopic"); + } + return client.connected(); +} + +void setup() +{ + client.setServer(server, 1883); + client.setCallback(callback); + + Ethernet.begin(mac, ip); + delay(1500); + lastReconnectAttempt = 0; +} + + +void loop() +{ + if (!client.connected()) { + long now = millis(); + if (now - lastReconnectAttempt > 5000) { + lastReconnectAttempt = now; + // Attempt to reconnect + if (reconnect()) { + lastReconnectAttempt = 0; + } + } + } else { + // Client connected + + client.loop(); + } + +} diff --git a/libraries/PubSubClient/examples/mqtt_stream/mqtt_stream.ino b/libraries/PubSubClient/examples/mqtt_stream/mqtt_stream.ino new file mode 100644 index 0000000..67c2287 --- /dev/null +++ b/libraries/PubSubClient/examples/mqtt_stream/mqtt_stream.ino @@ -0,0 +1,57 @@ +/* + Example of using a Stream object to store the message payload + + Uses SRAM library: https://github.com/ennui2342/arduino-sram + but could use any Stream based class such as SD + + - connects to an MQTT server + - publishes "hello world" to the topic "outTopic" + - subscribes to the topic "inTopic" +*/ + +#include +#include +#include +#include + +// Update these with values suitable for your network. +byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; +IPAddress ip(172, 16, 0, 100); +IPAddress server(172, 16, 0, 2); + +SRAM sram(4, SRAM_1024); + +void callback(char* topic, byte* payload, unsigned int length) { + sram.seek(1); + + // do something with the message + for(uint8_t i=0; i +maintainer=Nick O'Leary +sentence=A client library for MQTT messaging. +paragraph=MQTT is a lightweight messaging protocol ideal for small devices. This library allows you to send and receive MQTT messages. It supports the latest MQTT 3.1.1 protocol and can be configured to use the older MQTT 3.1 if needed. It supports all Arduino Ethernet Client compatible hardware, including the Intel Galileo/Edison, ESP8266 and TI CC3000. +category=Communication +url=http://pubsubclient.knolleary.net +architectures=* diff --git a/libraries/PubSubClient/src/PubSubClient.cpp b/libraries/PubSubClient/src/PubSubClient.cpp new file mode 100644 index 0000000..0fa420d --- /dev/null +++ b/libraries/PubSubClient/src/PubSubClient.cpp @@ -0,0 +1,653 @@ +/* + PubSubClient.cpp - A simple client for MQTT. + Nick O'Leary + http://knolleary.net +*/ + +#include "PubSubClient.h" +#include "Arduino.h" + +PubSubClient::PubSubClient() { + this->_state = MQTT_DISCONNECTED; + this->_client = NULL; + this->stream = NULL; + setCallback(NULL); +} + +PubSubClient::PubSubClient(Client& client) { + this->_state = MQTT_DISCONNECTED; + setClient(client); + this->stream = NULL; +} + +PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(addr, port); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(addr,port); + setClient(client); + setStream(stream); +} +PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(addr, port); + setCallback(callback); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(addr,port); + setCallback(callback); + setClient(client); + setStream(stream); +} + +PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(ip, port); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(ip,port); + setClient(client); + setStream(stream); +} +PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(ip, port); + setCallback(callback); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(ip,port); + setCallback(callback); + setClient(client); + setStream(stream); +} + +PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(domain,port); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(domain,port); + setClient(client); + setStream(stream); +} +PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(domain,port); + setCallback(callback); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(domain,port); + setCallback(callback); + setClient(client); + setStream(stream); +} + +boolean PubSubClient::connect(const char *id) { + return connect(id,NULL,NULL,0,0,0,0,1); +} + +boolean PubSubClient::connect(const char *id, const char *user, const char *pass) { + return connect(id,user,pass,0,0,0,0,1); +} + +boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { + return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1); +} + +boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { + return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1); +} + +boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) { + if (!connected()) { + int result = 0; + + if (domain != NULL) { + result = _client->connect(this->domain, this->port); + } else { + result = _client->connect(this->ip, this->port); + } + if (result == 1) { + nextMsgId = 1; + // Leave room in the buffer for header and variable length field + uint16_t length = MQTT_MAX_HEADER_SIZE; + unsigned int j; + +#if MQTT_VERSION == MQTT_VERSION_3_1 + uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION}; +#define MQTT_HEADER_VERSION_LENGTH 9 +#elif MQTT_VERSION == MQTT_VERSION_3_1_1 + uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION}; +#define MQTT_HEADER_VERSION_LENGTH 7 +#endif + for (j = 0;j>1); + } + } + + buffer[length++] = v; + + buffer[length++] = ((MQTT_KEEPALIVE) >> 8); + buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF); + + CHECK_STRING_LENGTH(length,id) + length = writeString(id,buffer,length); + if (willTopic) { + CHECK_STRING_LENGTH(length,willTopic) + length = writeString(willTopic,buffer,length); + CHECK_STRING_LENGTH(length,willMessage) + length = writeString(willMessage,buffer,length); + } + + if(user != NULL) { + CHECK_STRING_LENGTH(length,user) + length = writeString(user,buffer,length); + if(pass != NULL) { + CHECK_STRING_LENGTH(length,pass) + length = writeString(pass,buffer,length); + } + } + + write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE); + + lastInActivity = lastOutActivity = millis(); + + while (!_client->available()) { + unsigned long t = millis(); + if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) { + _state = MQTT_CONNECTION_TIMEOUT; + _client->stop(); + return false; + } + } + uint8_t llen; + uint16_t len = readPacket(&llen); + + if (len == 4) { + if (buffer[3] == 0) { + lastInActivity = millis(); + pingOutstanding = false; + _state = MQTT_CONNECTED; + return true; + } else { + _state = buffer[3]; + } + } + _client->stop(); + } else { + _state = MQTT_CONNECT_FAILED; + } + return false; + } + return true; +} + +// reads a byte into result +boolean PubSubClient::readByte(uint8_t * result) { + uint32_t previousMillis = millis(); + while(!_client->available()) { + yield(); + uint32_t currentMillis = millis(); + if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){ + return false; + } + } + *result = _client->read(); + return true; +} + +// reads a byte into result[*index] and increments index +boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){ + uint16_t current_index = *index; + uint8_t * write_address = &(result[current_index]); + if(readByte(write_address)){ + *index = current_index + 1; + return true; + } + return false; +} + +uint16_t PubSubClient::readPacket(uint8_t* lengthLength) { + uint16_t len = 0; + if(!readByte(buffer, &len)) return 0; + bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH; + uint32_t multiplier = 1; + uint16_t length = 0; + uint8_t digit = 0; + uint16_t skip = 0; + uint8_t start = 0; + + do { + if (len == 5) { + // Invalid remaining length encoding - kill the connection + _state = MQTT_DISCONNECTED; + _client->stop(); + return 0; + } + if(!readByte(&digit)) return 0; + buffer[len++] = digit; + length += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + *lengthLength = len-1; + + if (isPublish) { + // Read in topic length to calculate bytes to skip over for Stream writing + if(!readByte(buffer, &len)) return 0; + if(!readByte(buffer, &len)) return 0; + skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2]; + start = 2; + if (buffer[0]&MQTTQOS1) { + // skip message id + skip += 2; + } + } + + for (uint16_t i = start;istream) { + if (isPublish && len-*lengthLength-2>skip) { + this->stream->write(digit); + } + } + if (len < MQTT_MAX_PACKET_SIZE) { + buffer[len] = digit; + } + len++; + } + + if (!this->stream && len > MQTT_MAX_PACKET_SIZE) { + len = 0; // This will cause the packet to be ignored. + } + + return len; +} + +boolean PubSubClient::loop() { + if (connected()) { + unsigned long t = millis(); + if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) { + if (pingOutstanding) { + this->_state = MQTT_CONNECTION_TIMEOUT; + _client->stop(); + return false; + } else { + buffer[0] = MQTTPINGREQ; + buffer[1] = 0; + _client->write(buffer,2); + lastOutActivity = t; + lastInActivity = t; + pingOutstanding = true; + } + } + if (_client->available()) { + uint8_t llen; + uint16_t len = readPacket(&llen); + uint16_t msgId = 0; + uint8_t *payload; + if (len > 0) { + lastInActivity = t; + uint8_t type = buffer[0]&0xF0; + if (type == MQTTPUBLISH) { + if (callback) { + uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */ + memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */ + buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */ + char *topic = (char*) buffer+llen+2; + // msgId only present for QOS>0 + if ((buffer[0]&0x06) == MQTTQOS1) { + msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1]; + payload = buffer+llen+3+tl+2; + callback(topic,payload,len-llen-3-tl-2); + + buffer[0] = MQTTPUBACK; + buffer[1] = 2; + buffer[2] = (msgId >> 8); + buffer[3] = (msgId & 0xFF); + _client->write(buffer,4); + lastOutActivity = t; + + } else { + payload = buffer+llen+3+tl; + callback(topic,payload,len-llen-3-tl); + } + } + } else if (type == MQTTPINGREQ) { + buffer[0] = MQTTPINGRESP; + buffer[1] = 0; + _client->write(buffer,2); + } else if (type == MQTTPINGRESP) { + pingOutstanding = false; + } + } else if (!connected()) { + // readPacket has closed the connection + return false; + } + } + return true; + } + return false; +} + +boolean PubSubClient::publish(const char* topic, const char* payload) { + return publish(topic,(const uint8_t*)payload,strlen(payload),false); +} + +boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) { + return publish(topic,(const uint8_t*)payload,strlen(payload),retained); +} + +boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) { + return publish(topic, payload, plength, false); +} + +boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { + if (connected()) { + if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) { + // Too long + return false; + } + // Leave room in the buffer for header and variable length field + uint16_t length = MQTT_MAX_HEADER_SIZE; + length = writeString(topic,buffer,length); + uint16_t i; + for (i=0;i 0) { + digit |= 0x80; + } + buffer[pos++] = digit; + llen++; + } while(len>0); + + pos = writeString(topic,buffer,pos); + + rc += _client->write(buffer,pos); + + for (i=0;iwrite((char)pgm_read_byte_near(payload + i)); + } + + lastOutActivity = millis(); + + return rc == tlen + 4 + plength; +} + +boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) { + if (connected()) { + // Send the header and variable length field + uint16_t length = MQTT_MAX_HEADER_SIZE; + length = writeString(topic,buffer,length); + uint16_t i; + uint8_t header = MQTTPUBLISH; + if (retained) { + header |= 1; + } + size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE); + uint16_t rc = _client->write(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen)); + lastOutActivity = millis(); + return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen))); + } + return false; +} + +int PubSubClient::endPublish() { + return 1; +} + +size_t PubSubClient::write(uint8_t data) { + lastOutActivity = millis(); + return _client->write(data); +} + +size_t PubSubClient::write(const uint8_t *buffer, size_t size) { + lastOutActivity = millis(); + return _client->write(buffer,size); +} + +size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) { + uint8_t lenBuf[4]; + uint8_t llen = 0; + uint8_t digit; + uint8_t pos = 0; + uint16_t len = length; + do { + digit = len % 128; + len = len / 128; + if (len > 0) { + digit |= 0x80; + } + lenBuf[pos++] = digit; + llen++; + } while(len>0); + + buf[4-llen] = header; + for (int i=0;i 0) && result) { + bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining; + rc = _client->write(writeBuf,bytesToWrite); + result = (rc == bytesToWrite); + bytesRemaining -= rc; + writeBuf += rc; + } + return result; +#else + rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen); + lastOutActivity = millis(); + return (rc == hlen+length); +#endif +} + +boolean PubSubClient::subscribe(const char* topic) { + return subscribe(topic, 0); +} + +boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { + if (qos > 1) { + return false; + } + if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { + // Too long + return false; + } + if (connected()) { + // Leave room in the buffer for header and variable length field + uint16_t length = MQTT_MAX_HEADER_SIZE; + nextMsgId++; + if (nextMsgId == 0) { + nextMsgId = 1; + } + buffer[length++] = (nextMsgId >> 8); + buffer[length++] = (nextMsgId & 0xFF); + length = writeString((char*)topic, buffer,length); + buffer[length++] = qos; + return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE); + } + return false; +} + +boolean PubSubClient::unsubscribe(const char* topic) { + if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { + // Too long + return false; + } + if (connected()) { + uint16_t length = MQTT_MAX_HEADER_SIZE; + nextMsgId++; + if (nextMsgId == 0) { + nextMsgId = 1; + } + buffer[length++] = (nextMsgId >> 8); + buffer[length++] = (nextMsgId & 0xFF); + length = writeString(topic, buffer,length); + return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE); + } + return false; +} + +void PubSubClient::disconnect() { + buffer[0] = MQTTDISCONNECT; + buffer[1] = 0; + _client->write(buffer,2); + _state = MQTT_DISCONNECTED; + _client->flush(); + _client->stop(); + lastInActivity = lastOutActivity = millis(); +} + +uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) { + const char* idp = string; + uint16_t i = 0; + pos += 2; + while (*idp) { + buf[pos++] = *idp++; + i++; + } + buf[pos-i-2] = (i >> 8); + buf[pos-i-1] = (i & 0xFF); + return pos; +} + + +boolean PubSubClient::connected() { + boolean rc; + if (_client == NULL ) { + rc = false; + } else { + rc = (int)_client->connected(); + if (!rc) { + if (this->_state == MQTT_CONNECTED) { + this->_state = MQTT_CONNECTION_LOST; + _client->flush(); + _client->stop(); + } + } + } + return rc; +} + +PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) { + IPAddress addr(ip[0],ip[1],ip[2],ip[3]); + return setServer(addr,port); +} + +PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) { + this->ip = ip; + this->port = port; + this->domain = NULL; + return *this; +} + +PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) { + this->domain = domain; + this->port = port; + return *this; +} + +PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) { + this->callback = callback; + return *this; +} + +PubSubClient& PubSubClient::setClient(Client& client){ + this->_client = &client; + return *this; +} + +PubSubClient& PubSubClient::setStream(Stream& stream){ + this->stream = &stream; + return *this; +} + +int PubSubClient::state() { + return this->_state; +} diff --git a/libraries/PubSubClient/src/PubSubClient.h b/libraries/PubSubClient/src/PubSubClient.h new file mode 100644 index 0000000..2fd6f1d --- /dev/null +++ b/libraries/PubSubClient/src/PubSubClient.h @@ -0,0 +1,173 @@ +/* + PubSubClient.h - A simple client for MQTT. + Nick O'Leary + http://knolleary.net +*/ + +#ifndef PubSubClient_h +#define PubSubClient_h + +#include +#include "IPAddress.h" +#include "Client.h" +#include "Stream.h" + +#define MQTT_VERSION_3_1 3 +#define MQTT_VERSION_3_1_1 4 + +// MQTT_VERSION : Pick the version +//#define MQTT_VERSION MQTT_VERSION_3_1 +#ifndef MQTT_VERSION +#define MQTT_VERSION MQTT_VERSION_3_1_1 +#endif + +// MQTT_MAX_PACKET_SIZE : Maximum packet size +#ifndef MQTT_MAX_PACKET_SIZE +#define MQTT_MAX_PACKET_SIZE 128 +#endif + +// MQTT_KEEPALIVE : keepAlive interval in Seconds +#ifndef MQTT_KEEPALIVE +#define MQTT_KEEPALIVE 15 +#endif + +// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds +#ifndef MQTT_SOCKET_TIMEOUT +#define MQTT_SOCKET_TIMEOUT 15 +#endif + +// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client +// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to +// pass the entire MQTT packet in each write call. +//#define MQTT_MAX_TRANSFER_SIZE 80 + +// Possible values for client.state() +#define MQTT_CONNECTION_TIMEOUT -4 +#define MQTT_CONNECTION_LOST -3 +#define MQTT_CONNECT_FAILED -2 +#define MQTT_DISCONNECTED -1 +#define MQTT_CONNECTED 0 +#define MQTT_CONNECT_BAD_PROTOCOL 1 +#define MQTT_CONNECT_BAD_CLIENT_ID 2 +#define MQTT_CONNECT_UNAVAILABLE 3 +#define MQTT_CONNECT_BAD_CREDENTIALS 4 +#define MQTT_CONNECT_UNAUTHORIZED 5 + +#define MQTTCONNECT 1 << 4 // Client request to connect to Server +#define MQTTCONNACK 2 << 4 // Connect Acknowledgment +#define MQTTPUBLISH 3 << 4 // Publish message +#define MQTTPUBACK 4 << 4 // Publish Acknowledgment +#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1) +#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2) +#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3) +#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request +#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment +#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request +#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment +#define MQTTPINGREQ 12 << 4 // PING Request +#define MQTTPINGRESP 13 << 4 // PING Response +#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting +#define MQTTReserved 15 << 4 // Reserved + +#define MQTTQOS0 (0 << 1) +#define MQTTQOS1 (1 << 1) +#define MQTTQOS2 (2 << 1) + +// Maximum size of fixed header and variable length size header +#define MQTT_MAX_HEADER_SIZE 5 + +#if defined(ESP8266) || defined(ESP32) +#include +#define MQTT_CALLBACK_SIGNATURE std::function callback +#else +#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int) +#endif + +#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;} + +class PubSubClient : public Print { +private: + Client* _client; + uint8_t buffer[MQTT_MAX_PACKET_SIZE]; + uint16_t nextMsgId; + unsigned long lastOutActivity; + unsigned long lastInActivity; + bool pingOutstanding; + MQTT_CALLBACK_SIGNATURE; + uint16_t readPacket(uint8_t*); + boolean readByte(uint8_t * result); + boolean readByte(uint8_t * result, uint16_t * index); + boolean write(uint8_t header, uint8_t* buf, uint16_t length); + uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos); + // Build up the header ready to send + // Returns the size of the header + // Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start + // (MQTT_MAX_HEADER_SIZE - ) bytes into the buffer + size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length); + IPAddress ip; + const char* domain; + uint16_t port; + Stream* stream; + int _state; +public: + PubSubClient(); + PubSubClient(Client& client); + PubSubClient(IPAddress, uint16_t, Client& client); + PubSubClient(IPAddress, uint16_t, Client& client, Stream&); + PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); + PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); + PubSubClient(uint8_t *, uint16_t, Client& client); + PubSubClient(uint8_t *, uint16_t, Client& client, Stream&); + PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); + PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); + PubSubClient(const char*, uint16_t, Client& client); + PubSubClient(const char*, uint16_t, Client& client, Stream&); + PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); + PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); + + PubSubClient& setServer(IPAddress ip, uint16_t port); + PubSubClient& setServer(uint8_t * ip, uint16_t port); + PubSubClient& setServer(const char * domain, uint16_t port); + PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE); + PubSubClient& setClient(Client& client); + PubSubClient& setStream(Stream& stream); + + boolean connect(const char* id); + boolean connect(const char* id, const char* user, const char* pass); + boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); + boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); + boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession); + void disconnect(); + boolean publish(const char* topic, const char* payload); + boolean publish(const char* topic, const char* payload, boolean retained); + boolean publish(const char* topic, const uint8_t * payload, unsigned int plength); + boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); + boolean publish_P(const char* topic, const char* payload, boolean retained); + boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); + // Start to publish a message. + // This API: + // beginPublish(...) + // one or more calls to write(...) + // endPublish() + // Allows for arbitrarily large payloads to be sent without them having to be copied into + // a new buffer and held in memory at one time + // Returns 1 if the message was started successfully, 0 if there was an error + boolean beginPublish(const char* topic, unsigned int plength, boolean retained); + // Finish off this publish message (started with beginPublish) + // Returns 1 if the packet was sent successfully, 0 if there was an error + int endPublish(); + // Write a single byte of payload (only to be used with beginPublish/endPublish) + virtual size_t write(uint8_t); + // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish) + // Returns the number of bytes written + virtual size_t write(const uint8_t *buffer, size_t size); + boolean subscribe(const char* topic); + boolean subscribe(const char* topic, uint8_t qos); + boolean unsubscribe(const char* topic); + boolean loop(); + boolean connected(); + int state(); +}; + + +#endif diff --git a/libraries/PubSubClient/tests/.gitignore b/libraries/PubSubClient/tests/.gitignore new file mode 100644 index 0000000..215de78 --- /dev/null +++ b/libraries/PubSubClient/tests/.gitignore @@ -0,0 +1,4 @@ +.build +tmpbin +logs +*.pyc diff --git a/libraries/PubSubClient/tests/Makefile b/libraries/PubSubClient/tests/Makefile new file mode 100644 index 0000000..1f71636 --- /dev/null +++ b/libraries/PubSubClient/tests/Makefile @@ -0,0 +1,25 @@ +SRC_PATH=./src +OUT_PATH=./bin +TEST_SRC=$(wildcard ${SRC_PATH}/*_spec.cpp) +TEST_BIN= $(TEST_SRC:${SRC_PATH}/%.cpp=${OUT_PATH}/%) +VPATH=${SRC_PATH} +SHIM_FILES=${SRC_PATH}/lib/*.cpp +PSC_FILE=../src/PubSubClient.cpp +CC=g++ +CFLAGS=-I${SRC_PATH}/lib -I../src + +all: $(TEST_BIN) + +${OUT_PATH}/%: ${SRC_PATH}/%.cpp ${PSC_FILE} ${SHIM_FILES} + mkdir -p ${OUT_PATH} + ${CC} ${CFLAGS} $^ -o $@ + +clean: + @rm -rf ${OUT_PATH} + +test: + @bin/connect_spec + @bin/publish_spec + @bin/receive_spec + @bin/subscribe_spec + @bin/keepalive_spec diff --git a/libraries/PubSubClient/tests/README.md b/libraries/PubSubClient/tests/README.md new file mode 100644 index 0000000..e5700a6 --- /dev/null +++ b/libraries/PubSubClient/tests/README.md @@ -0,0 +1,93 @@ +# Arduino Client for MQTT Test Suite + +This is a regression test suite for the `PubSubClient` library. + +There are two parts: + + - Tests that can be compiled and run on any machine + - Tests that build the example sketches using the Arduino IDE + + +It is a work-in-progress and is subject to complete refactoring as the whim takes +me. + + +## Local tests + +These are a set of executables that can be run to test specific areas of functionality. +They do not require a real Arduino to be attached, nor the use of the Arduino IDE. + +The tests include a set of mock files to stub out the parts of the Arduino environment the library +depends on. + +### Dependencies + + - g++ + +### Running + +Build the tests using the provided `Makefile`: + + $ make + +This will create a set of executables in `./bin/`. Run each of these executables to test the corresponding functionality. + +*Note:* the `connect_spec` and `keepalive_spec` tests involve testing keepalive timers so naturally take a few minutes to run through. + +## Arduino tests + +*Note:* INO Tool doesn't currently play nicely with Arduino 1.5. This has broken this test suite. + +Without a suitable arduino plugged in, the test suite will only check the +example sketches compile cleanly against the library. + +With an arduino plugged in, each sketch that has a corresponding python +test case is built, uploaded and then the tests run. + +### Dependencies + + - Python 2.7+ + - [INO Tool](http://inotool.org/) - this provides command-line build/upload of Arduino sketches + +### Running + +The test suite _does not_ run an MQTT server - it is assumed to be running already. + + $ python testsuite.py + +A summary of activity is printed to the console. More comprehensive logs are written +to the `logs` directory. + +### What it does + +For each sketch in the library's `examples` directory, e.g. `mqtt_basic.ino`, the suite looks for a matching test case +`testcases/mqtt_basic.py`. + +The test case must follow these conventions: + - sub-class `unittest.TestCase` + - provide the class methods `setUpClass` and `tearDownClass` (TODO: make this optional) + - all test method names begin with `test_` + +The suite will call the `setUpClass` method _before_ uploading the sketch. This +allows any test setup to be performed before the sketch runs - such as connecting +a client and subscribing to topics. + + +### Settings + +The file `testcases/settings.py` is used to config the test environment. + + - `server_ip` - the IP address of the broker the client should connect to (the broker port is assumed to be 1883). + - `arduino_ip` - the IP address the arduino should use (when not testing DHCP). + +Before each sketch is compiled, these values are automatically substituted in. To +do this, the suite looks for lines that _start_ with the following: + + byte server[] = { + byte ip[] = { + +and replaces them with the appropriate values. + + + + diff --git a/libraries/PubSubClient/tests/src/connect_spec.cpp b/libraries/PubSubClient/tests/src/connect_spec.cpp new file mode 100644 index 0000000..e27a1f5 --- /dev/null +++ b/libraries/PubSubClient/tests/src/connect_spec.cpp @@ -0,0 +1,302 @@ +#include "PubSubClient.h" +#include "ShimClient.h" +#include "Buffer.h" +#include "BDDTest.h" +#include "trace.h" + + +byte server[] = { 172, 16, 0, 2 }; + +void callback(char* topic, byte* payload, unsigned int length) { + // handle message arrived +} + + +int test_connect_fails_no_network() { + IT("fails to connect if underlying client doesn't connect"); + ShimClient shimClient; + shimClient.setAllowConnect(false); + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_FALSE(rc); + int state = client.state(); + IS_TRUE(state == MQTT_CONNECT_FAILED); + END_IT +} + +int test_connect_fails_on_no_response() { + IT("fails to connect if no response received after 15 seconds"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_FALSE(rc); + int state = client.state(); + IS_TRUE(state == MQTT_CONNECTION_TIMEOUT); + END_IT +} + +int test_connect_properly_formatted() { + IT("sends a properly formatted connect packet and succeeds"); + ShimClient shimClient; + + shimClient.setAllowConnect(true); + byte expectServer[] = { 172, 16, 0, 2 }; + shimClient.expectConnect(expectServer,1883); + byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + + shimClient.expect(connect,26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int state = client.state(); + IS_TRUE(state == MQTT_DISCONNECTED); + + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + state = client.state(); + IS_TRUE(state == MQTT_CONNECTED); + + END_IT +} + +int test_connect_properly_formatted_hostname() { + IT("accepts a hostname"); + ShimClient shimClient; + + shimClient.setAllowConnect(true); + shimClient.expectConnect((char* const)"localhost",1883); + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client((char* const)"localhost", 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + END_IT +} + + +int test_connect_fails_on_bad_rc() { + IT("fails to connect if a bad return code is received"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + byte connack[] = { 0x20, 0x02, 0x00, 0x01 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_FALSE(rc); + + int state = client.state(); + IS_TRUE(state == 0x01); + + END_IT +} + +int test_connect_non_clean_session() { + IT("sends a properly formatted non-clean session connect packet and succeeds"); + ShimClient shimClient; + + shimClient.setAllowConnect(true); + byte expectServer[] = { 172, 16, 0, 2 }; + shimClient.expectConnect(expectServer,1883); + byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x0,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + + shimClient.expect(connect,26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int state = client.state(); + IS_TRUE(state == MQTT_DISCONNECTED); + + int rc = client.connect((char*)"client_test1",0,0,0,0,0,0,0); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + state = client.state(); + IS_TRUE(state == MQTT_CONNECTED); + + END_IT +} + +int test_connect_accepts_username_password() { + IT("accepts a username and password"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connect[] = { 0x10,0x24,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xc2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x4,0x70,0x61,0x73,0x73}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.expect(connect,0x26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"pass"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_connect_accepts_username_no_password() { + IT("accepts a username but no password"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connect[] = { 0x10,0x1e,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x82,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.expect(connect,0x20); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1",(char*)"user",0); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + END_IT +} +int test_connect_accepts_username_blank_password() { + IT("accepts a username and blank password"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connect[] = { 0x10,0x20,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xc2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x0}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.expect(connect,0x26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"pass"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_connect_ignores_password_no_username() { + IT("ignores a password but no username"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.expect(connect,26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1",0,(char*)"pass"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_connect_with_will() { + IT("accepts a will"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connect[] = {0x10,0x30,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xe,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x9,0x77,0x69,0x6c,0x6c,0x54,0x6f,0x70,0x69,0x63,0x0,0xb,0x77,0x69,0x6c,0x6c,0x4d,0x65,0x73,0x73,0x61,0x67,0x65}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.expect(connect,0x32); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1",(char*)"willTopic",1,0,(char*)"willMessage"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_connect_with_will_username_password() { + IT("accepts a will, username and password"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connect[] = {0x10,0x40,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xce,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x9,0x77,0x69,0x6c,0x6c,0x54,0x6f,0x70,0x69,0x63,0x0,0xb,0x77,0x69,0x6c,0x6c,0x4d,0x65,0x73,0x73,0x61,0x67,0x65,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x8,0x70,0x61,0x73,0x73,0x77,0x6f,0x72,0x64}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.expect(connect,0x42); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"password",(char*)"willTopic",1,0,(char*)"willMessage"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_connect_disconnect_connect() { + IT("connects, disconnects and connects again"); + ShimClient shimClient; + + shimClient.setAllowConnect(true); + byte expectServer[] = { 172, 16, 0, 2 }; + shimClient.expectConnect(expectServer,1883); + byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + + shimClient.expect(connect,26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + + int state = client.state(); + IS_TRUE(state == MQTT_DISCONNECTED); + + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + state = client.state(); + IS_TRUE(state == MQTT_CONNECTED); + + byte disconnect[] = {0xE0,0x00}; + shimClient.expect(disconnect,2); + + client.disconnect(); + + IS_FALSE(client.connected()); + IS_FALSE(shimClient.connected()); + IS_FALSE(shimClient.error()); + + state = client.state(); + IS_TRUE(state == MQTT_DISCONNECTED); + + shimClient.expect(connect,28); + shimClient.respond(connack,4); + rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + state = client.state(); + IS_TRUE(state == MQTT_CONNECTED); + + END_IT +} + +int main() +{ + SUITE("Connect"); + + test_connect_fails_no_network(); + test_connect_fails_on_no_response(); + + test_connect_properly_formatted(); + test_connect_non_clean_session(); + test_connect_accepts_username_password(); + test_connect_fails_on_bad_rc(); + test_connect_properly_formatted_hostname(); + + test_connect_accepts_username_no_password(); + test_connect_ignores_password_no_username(); + test_connect_with_will(); + test_connect_with_will_username_password(); + test_connect_disconnect_connect(); + FINISH +} diff --git a/libraries/PubSubClient/tests/src/keepalive_spec.cpp b/libraries/PubSubClient/tests/src/keepalive_spec.cpp new file mode 100644 index 0000000..ea643cf --- /dev/null +++ b/libraries/PubSubClient/tests/src/keepalive_spec.cpp @@ -0,0 +1,185 @@ +#include "PubSubClient.h" +#include "ShimClient.h" +#include "Buffer.h" +#include "BDDTest.h" +#include "trace.h" +#include + +byte server[] = { 172, 16, 0, 2 }; + +void callback(char* topic, byte* payload, unsigned int length) { + // handle message arrived +} + + +int test_keepalive_pings_idle() { + IT("keeps an idle connection alive (takes 1 minute)"); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte pingreq[] = { 0xC0,0x0 }; + shimClient.expect(pingreq,2); + byte pingresp[] = { 0xD0,0x0 }; + shimClient.respond(pingresp,2); + + for (int i = 0; i < 50; i++) { + sleep(1); + if ( i == 15 || i == 31 || i == 47) { + shimClient.expect(pingreq,2); + shimClient.respond(pingresp,2); + } + rc = client.loop(); + IS_TRUE(rc); + } + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_keepalive_pings_with_outbound_qos0() { + IT("keeps a connection alive that only sends qos0 (takes 1 minute)"); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + + for (int i = 0; i < 50; i++) { + TRACE(i<<":"); + shimClient.expect(publish,16); + rc = client.publish((char*)"topic",(char*)"payload"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + sleep(1); + if ( i == 15 || i == 31 || i == 47) { + byte pingreq[] = { 0xC0,0x0 }; + shimClient.expect(pingreq,2); + byte pingresp[] = { 0xD0,0x0 }; + shimClient.respond(pingresp,2); + } + rc = client.loop(); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + } + + END_IT +} + +int test_keepalive_pings_with_inbound_qos0() { + IT("keeps a connection alive that only receives qos0 (takes 1 minute)"); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + + for (int i = 0; i < 50; i++) { + TRACE(i<<":"); + sleep(1); + if ( i == 15 || i == 31 || i == 47) { + byte pingreq[] = { 0xC0,0x0 }; + shimClient.expect(pingreq,2); + byte pingresp[] = { 0xD0,0x0 }; + shimClient.respond(pingresp,2); + } + shimClient.respond(publish,16); + rc = client.loop(); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + } + + END_IT +} + +int test_keepalive_no_pings_inbound_qos1() { + IT("does not send pings for connections with inbound qos1 (takes 1 minute)"); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + byte puback[] = {0x40,0x2,0x12,0x34}; + + for (int i = 0; i < 50; i++) { + shimClient.respond(publish,18); + shimClient.expect(puback,4); + sleep(1); + rc = client.loop(); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + } + + END_IT +} + +int test_keepalive_disconnects_hung() { + IT("disconnects a hung connection (takes 30 seconds)"); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte pingreq[] = { 0xC0,0x0 }; + shimClient.expect(pingreq,2); + + for (int i = 0; i < 32; i++) { + sleep(1); + rc = client.loop(); + } + IS_FALSE(rc); + + int state = client.state(); + IS_TRUE(state == MQTT_CONNECTION_TIMEOUT); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int main() +{ + SUITE("Keep-alive"); + test_keepalive_pings_idle(); + test_keepalive_pings_with_outbound_qos0(); + test_keepalive_pings_with_inbound_qos0(); + test_keepalive_no_pings_inbound_qos1(); + test_keepalive_disconnects_hung(); + + FINISH +} diff --git a/libraries/PubSubClient/tests/src/lib/Arduino.h b/libraries/PubSubClient/tests/src/lib/Arduino.h new file mode 100644 index 0000000..2a00f24 --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/Arduino.h @@ -0,0 +1,26 @@ +#ifndef Arduino_h +#define Arduino_h + +#include +#include +#include +#include +#include "Print.h" + + +extern "C"{ + typedef uint8_t byte ; + typedef uint8_t boolean ; + + /* sketch */ + extern void setup( void ) ; + extern void loop( void ) ; + uint32_t millis( void ); +} + +#define PROGMEM +#define pgm_read_byte_near(x) *(x) + +#define yield(x) {} + +#endif // Arduino_h diff --git a/libraries/PubSubClient/tests/src/lib/BDDTest.cpp b/libraries/PubSubClient/tests/src/lib/BDDTest.cpp new file mode 100644 index 0000000..a72bf65 --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/BDDTest.cpp @@ -0,0 +1,50 @@ +#include "BDDTest.h" +#include "trace.h" +#include +#include +#include +#include + +int testCount = 0; +int testPasses = 0; +const char* testDescription; + +std::list failureList; + +void bddtest_suite(const char* name) { + LOG(name << "\n"); +} + +int bddtest_test(const char* file, int line, const char* assertion, int result) { + if (!result) { + LOG("✗\n"); + std::ostringstream os; + os << " ! "<::iterator it = failureList.begin(); it != failureList.end(); it++) { + LOG("\n"); + LOG(*it); + LOG("\n"); + } + + LOG(std::dec << testPasses << "/" << testCount << " tests passed\n\n"); + if (testPasses == testCount) { + return 0; + } + return 1; +} diff --git a/libraries/PubSubClient/tests/src/lib/BDDTest.h b/libraries/PubSubClient/tests/src/lib/BDDTest.h new file mode 100644 index 0000000..1197fdd --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/BDDTest.h @@ -0,0 +1,23 @@ +#ifndef bddtest_h +#define bddtest_h + +void bddtest_suite(const char* name); +int bddtest_test(const char*, int, const char*, int); +void bddtest_start(const char*); +void bddtest_end(); +int bddtest_summary(); + +#define SUITE(x) { bddtest_suite(x); } +#define TEST(x) { if (!bddtest_test(__FILE__, __LINE__, #x, (x))) return false; } + +#define IT(x) { bddtest_start(x); } +#define END_IT { bddtest_end();return true;} + +#define FINISH { return bddtest_summary(); } + +#define IS_TRUE(x) TEST(x) +#define IS_FALSE(x) TEST(!(x)) +#define IS_EQUAL(x,y) TEST(x==y) +#define IS_NOT_EQUAL(x,y) TEST(x!=y) + +#endif diff --git a/libraries/PubSubClient/tests/src/lib/Buffer.cpp b/libraries/PubSubClient/tests/src/lib/Buffer.cpp new file mode 100644 index 0000000..f07759a --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/Buffer.cpp @@ -0,0 +1,34 @@ +#include "Buffer.h" +#include "Arduino.h" + +Buffer::Buffer() { + this->pos = 0; + this->length = 0; +} + +Buffer::Buffer(uint8_t* buf, size_t size) { + this->pos = 0; + this->length = 0; + this->add(buf,size); +} +bool Buffer::available() { + return this->pos < this->length; +} + +uint8_t Buffer::next() { + if (this->available()) { + return this->buffer[this->pos++]; + } + return 0; +} + +void Buffer::reset() { + this->pos = 0; +} + +void Buffer::add(uint8_t* buf, size_t size) { + uint16_t i = 0; + for (;ibuffer[this->length++] = buf[i]; + } +} diff --git a/libraries/PubSubClient/tests/src/lib/Buffer.h b/libraries/PubSubClient/tests/src/lib/Buffer.h new file mode 100644 index 0000000..f448cad --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/Buffer.h @@ -0,0 +1,23 @@ +#ifndef buffer_h +#define buffer_h + +#include "Arduino.h" + +class Buffer { +private: + uint8_t buffer[1024]; + uint16_t pos; + uint16_t length; + +public: + Buffer(); + Buffer(uint8_t* buf, size_t size); + + virtual bool available(); + virtual uint8_t next(); + virtual void reset(); + + virtual void add(uint8_t* buf, size_t size); +}; + +#endif diff --git a/libraries/PubSubClient/tests/src/lib/Client.h b/libraries/PubSubClient/tests/src/lib/Client.h new file mode 100644 index 0000000..9e18c07 --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/Client.h @@ -0,0 +1,21 @@ +#ifndef client_h +#define client_h +#include "IPAddress.h" + +class Client { +public: + virtual int connect(IPAddress ip, uint16_t port) =0; + virtual int connect(const char *host, uint16_t port) =0; + virtual size_t write(uint8_t) =0; + virtual size_t write(const uint8_t *buf, size_t size) =0; + virtual int available() = 0; + virtual int read() = 0; + virtual int read(uint8_t *buf, size_t size) = 0; + virtual int peek() = 0; + virtual void flush() = 0; + virtual void stop() = 0; + virtual uint8_t connected() = 0; + virtual operator bool() = 0; +}; + +#endif diff --git a/libraries/PubSubClient/tests/src/lib/IPAddress.cpp b/libraries/PubSubClient/tests/src/lib/IPAddress.cpp new file mode 100644 index 0000000..610ff4c --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/IPAddress.cpp @@ -0,0 +1,44 @@ + +#include +#include + +IPAddress::IPAddress() +{ + memset(_address, 0, sizeof(_address)); +} + +IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) +{ + _address[0] = first_octet; + _address[1] = second_octet; + _address[2] = third_octet; + _address[3] = fourth_octet; +} + +IPAddress::IPAddress(uint32_t address) +{ + memcpy(_address, &address, sizeof(_address)); +} + +IPAddress::IPAddress(const uint8_t *address) +{ + memcpy(_address, address, sizeof(_address)); +} + +IPAddress& IPAddress::operator=(const uint8_t *address) +{ + memcpy(_address, address, sizeof(_address)); + return *this; +} + +IPAddress& IPAddress::operator=(uint32_t address) +{ + memcpy(_address, (const uint8_t *)&address, sizeof(_address)); + return *this; +} + +bool IPAddress::operator==(const uint8_t* addr) +{ + return memcmp(addr, _address, sizeof(_address)) == 0; +} + diff --git a/libraries/PubSubClient/tests/src/lib/IPAddress.h b/libraries/PubSubClient/tests/src/lib/IPAddress.h new file mode 100644 index 0000000..e75a8fe --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/IPAddress.h @@ -0,0 +1,72 @@ +/* + * + * MIT License: + * Copyright (c) 2011 Adrian McEwen + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * adrianm@mcqn.com 1/1/2011 + */ + +#ifndef IPAddress_h +#define IPAddress_h + + +// A class to make it easier to handle and pass around IP addresses + +class IPAddress { +private: + uint8_t _address[4]; // IPv4 address + // Access the raw byte array containing the address. Because this returns a pointer + // to the internal structure rather than a copy of the address this function should only + // be used when you know that the usage of the returned uint8_t* will be transient and not + // stored. + uint8_t* raw_address() { return _address; }; + +public: + // Constructors + IPAddress(); + IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); + IPAddress(uint32_t address); + IPAddress(const uint8_t *address); + + // Overloaded cast operator to allow IPAddress objects to be used where a pointer + // to a four-byte uint8_t array is expected + operator uint32_t() { return *((uint32_t*)_address); }; + bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); }; + bool operator==(const uint8_t* addr); + + // Overloaded index operator to allow getting and setting individual octets of the address + uint8_t operator[](int index) const { return _address[index]; }; + uint8_t& operator[](int index) { return _address[index]; }; + + // Overloaded copy operators to allow initialisation of IPAddress objects from other types + IPAddress& operator=(const uint8_t *address); + IPAddress& operator=(uint32_t address); + + + friend class EthernetClass; + friend class UDP; + friend class Client; + friend class Server; + friend class DhcpClass; + friend class DNSClient; +}; + + +#endif diff --git a/libraries/PubSubClient/tests/src/lib/Print.h b/libraries/PubSubClient/tests/src/lib/Print.h new file mode 100644 index 0000000..02ef77c --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/Print.h @@ -0,0 +1,28 @@ +/* + Print.h - Base class that provides print() and println() + Copyright (c) 2008 David A. Mellis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef Print_h +#define Print_h + +class Print { + public: + virtual size_t write(uint8_t) = 0; +}; + +#endif diff --git a/libraries/PubSubClient/tests/src/lib/ShimClient.cpp b/libraries/PubSubClient/tests/src/lib/ShimClient.cpp new file mode 100644 index 0000000..f70115f --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/ShimClient.cpp @@ -0,0 +1,153 @@ +#include "ShimClient.h" +#include "trace.h" +#include +#include +#include + +extern "C" { + uint32_t millis(void) { + return time(0)*1000; + } +} + +ShimClient::ShimClient() { + this->responseBuffer = new Buffer(); + this->expectBuffer = new Buffer(); + this->_allowConnect = true; + this->_connected = false; + this->_error = false; + this->expectAnything = true; + this->_received = 0; + this->_expectedPort = 0; +} + +int ShimClient::connect(IPAddress ip, uint16_t port) { + if (this->_allowConnect) { + this->_connected = true; + } + if (this->_expectedPort !=0) { + // if (memcmp(ip,this->_expectedIP,4) != 0) { + // TRACE( "ip mismatch\n"); + // this->_error = true; + // } + if (port != this->_expectedPort) { + TRACE( "port mismatch\n"); + this->_error = true; + } + } + return this->_connected; +} +int ShimClient::connect(const char *host, uint16_t port) { + if (this->_allowConnect) { + this->_connected = true; + } + if (this->_expectedPort !=0) { + if (strcmp(host,this->_expectedHost) != 0) { + TRACE( "host mismatch\n"); + this->_error = true; + } + if (port != this->_expectedPort) { + TRACE( "port mismatch\n"); + this->_error = true; + } + + } + return this->_connected; +} +size_t ShimClient::write(uint8_t b) { + this->_received += 1; + TRACE(std::hex << (unsigned int)b); + if (!this->expectAnything) { + if (this->expectBuffer->available()) { + uint8_t expected = this->expectBuffer->next(); + if (expected != b) { + this->_error = true; + TRACE("!=" << (unsigned int)expected); + } + } else { + this->_error = true; + } + } + TRACE("\n"<< std::dec); + return 1; +} +size_t ShimClient::write(const uint8_t *buf, size_t size) { + this->_received += size; + TRACE( "[" << std::dec << (unsigned int)(size) << "] "); + uint16_t i=0; + for (;i0) { + TRACE(":"); + } + TRACE(std::hex << (unsigned int)(buf[i])); + + if (!this->expectAnything) { + if (this->expectBuffer->available()) { + uint8_t expected = this->expectBuffer->next(); + if (expected != buf[i]) { + this->_error = true; + TRACE("!=" << (unsigned int)expected); + } + } else { + this->_error = true; + } + } + } + TRACE("\n"<responseBuffer->available(); +} +int ShimClient::read() { return this->responseBuffer->next(); } +int ShimClient::read(uint8_t *buf, size_t size) { + uint16_t i = 0; + for (;iread(); + } + return size; +} +int ShimClient::peek() { return 0; } +void ShimClient::flush() {} +void ShimClient::stop() { + this->setConnected(false); +} +uint8_t ShimClient::connected() { return this->_connected; } +ShimClient::operator bool() { return true; } + + +ShimClient* ShimClient::respond(uint8_t *buf, size_t size) { + this->responseBuffer->add(buf,size); + return this; +} + +ShimClient* ShimClient::expect(uint8_t *buf, size_t size) { + this->expectAnything = false; + this->expectBuffer->add(buf,size); + return this; +} + +void ShimClient::setConnected(bool b) { + this->_connected = b; +} +void ShimClient::setAllowConnect(bool b) { + this->_allowConnect = b; +} + +bool ShimClient::error() { + return this->_error; +} + +uint16_t ShimClient::received() { + return this->_received; +} + +void ShimClient::expectConnect(IPAddress ip, uint16_t port) { + this->_expectedIP = ip; + this->_expectedPort = port; +} + +void ShimClient::expectConnect(const char *host, uint16_t port) { + this->_expectedHost = host; + this->_expectedPort = port; +} diff --git a/libraries/PubSubClient/tests/src/lib/ShimClient.h b/libraries/PubSubClient/tests/src/lib/ShimClient.h new file mode 100644 index 0000000..2e3f874 --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/ShimClient.h @@ -0,0 +1,51 @@ +#ifndef shimclient_h +#define shimclient_h + +#include "Arduino.h" +#include "Client.h" +#include "IPAddress.h" +#include "Buffer.h" + + +class ShimClient : public Client { +private: + Buffer* responseBuffer; + Buffer* expectBuffer; + bool _allowConnect; + bool _connected; + bool expectAnything; + bool _error; + uint16_t _received; + IPAddress _expectedIP; + uint16_t _expectedPort; + const char* _expectedHost; + +public: + ShimClient(); + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, uint16_t port); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual int available(); + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual int peek(); + virtual void flush(); + virtual void stop(); + virtual uint8_t connected(); + virtual operator bool(); + + virtual ShimClient* respond(uint8_t *buf, size_t size); + virtual ShimClient* expect(uint8_t *buf, size_t size); + + virtual void expectConnect(IPAddress ip, uint16_t port); + virtual void expectConnect(const char *host, uint16_t port); + + virtual uint16_t received(); + virtual bool error(); + + virtual void setAllowConnect(bool b); + virtual void setConnected(bool b); +}; + +#endif diff --git a/libraries/PubSubClient/tests/src/lib/Stream.cpp b/libraries/PubSubClient/tests/src/lib/Stream.cpp new file mode 100644 index 0000000..b0ecbb4 --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/Stream.cpp @@ -0,0 +1,39 @@ +#include "Stream.h" +#include "trace.h" +#include +#include + +Stream::Stream() { + this->expectBuffer = new Buffer(); + this->_error = false; + this->_written = 0; +} + +size_t Stream::write(uint8_t b) { + this->_written++; + TRACE(std::hex << (unsigned int)b); + if (this->expectBuffer->available()) { + uint8_t expected = this->expectBuffer->next(); + if (expected != b) { + this->_error = true; + TRACE("!=" << (unsigned int)expected); + } + } else { + this->_error = true; + } + TRACE("\n"<< std::dec); + return 1; +} + + +bool Stream::error() { + return this->_error; +} + +void Stream::expect(uint8_t *buf, size_t size) { + this->expectBuffer->add(buf,size); +} + +uint16_t Stream::length() { + return this->_written; +} diff --git a/libraries/PubSubClient/tests/src/lib/Stream.h b/libraries/PubSubClient/tests/src/lib/Stream.h new file mode 100644 index 0000000..4e41f86 --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/Stream.h @@ -0,0 +1,22 @@ +#ifndef Stream_h +#define Stream_h + +#include "Arduino.h" +#include "Buffer.h" + +class Stream { +private: + Buffer* expectBuffer; + bool _error; + uint16_t _written; + +public: + Stream(); + virtual size_t write(uint8_t); + + virtual bool error(); + virtual void expect(uint8_t *buf, size_t size); + virtual uint16_t length(); +}; + +#endif diff --git a/libraries/PubSubClient/tests/src/lib/trace.h b/libraries/PubSubClient/tests/src/lib/trace.h new file mode 100644 index 0000000..42eb991 --- /dev/null +++ b/libraries/PubSubClient/tests/src/lib/trace.h @@ -0,0 +1,10 @@ +#ifndef trace_h +#define trace_h +#include + +#include + +#define LOG(x) {std::cout << x << std::flush; } +#define TRACE(x) {if (getenv("TRACE")) { std::cout << x << std::flush; }} + +#endif diff --git a/libraries/PubSubClient/tests/src/publish_spec.cpp b/libraries/PubSubClient/tests/src/publish_spec.cpp new file mode 100644 index 0000000..232df0d --- /dev/null +++ b/libraries/PubSubClient/tests/src/publish_spec.cpp @@ -0,0 +1,190 @@ +#include "PubSubClient.h" +#include "ShimClient.h" +#include "Buffer.h" +#include "BDDTest.h" +#include "trace.h" + + +byte server[] = { 172, 16, 0, 2 }; + +void callback(char* topic, byte* payload, unsigned int length) { + // handle message arrived +} + +int test_publish() { + IT("publishes a null-terminated string"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + shimClient.expect(publish,16); + + rc = client.publish((char*)"topic",(char*)"payload"); + IS_TRUE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + + +int test_publish_bytes() { + IT("publishes a byte array"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte payload[] = { 0x01,0x02,0x03,0x0,0x05 }; + int length = 5; + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5}; + shimClient.expect(publish,14); + + rc = client.publish((char*)"topic",payload,length); + IS_TRUE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + + +int test_publish_retained() { + IT("publishes retained - 1"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte payload[] = { 0x01,0x02,0x03,0x0,0x05 }; + int length = 5; + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5}; + shimClient.expect(publish,14); + + rc = client.publish((char*)"topic",payload,length,true); + IS_TRUE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_publish_retained_2() { + IT("publishes retained - 2"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,'A','B','C','D','E'}; + shimClient.expect(publish,14); + + rc = client.publish((char*)"topic",(char*)"ABCDE",true); + IS_TRUE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_publish_not_connected() { + IT("publish fails when not connected"); + ShimClient shimClient; + + PubSubClient client(server, 1883, callback, shimClient); + + int rc = client.publish((char*)"topic",(char*)"payload"); + IS_FALSE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_publish_too_long() { + IT("publish fails when topic/payload are too long"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + // 0 1 2 3 4 5 6 7 8 9 0 1 2 + rc = client.publish((char*)"topic",(char*)"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); + IS_FALSE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_publish_P() { + IT("publishes using PROGMEM"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte payload[] = { 0x01,0x02,0x03,0x0,0x05 }; + int length = 5; + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5}; + shimClient.expect(publish,14); + + rc = client.publish_P((char*)"topic",payload,length,true); + IS_TRUE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + + + + +int main() +{ + SUITE("Publish"); + test_publish(); + test_publish_bytes(); + test_publish_retained(); + test_publish_retained_2(); + test_publish_not_connected(); + test_publish_too_long(); + test_publish_P(); + + FINISH +} diff --git a/libraries/PubSubClient/tests/src/receive_spec.cpp b/libraries/PubSubClient/tests/src/receive_spec.cpp new file mode 100644 index 0000000..9a18af0 --- /dev/null +++ b/libraries/PubSubClient/tests/src/receive_spec.cpp @@ -0,0 +1,279 @@ +#include "PubSubClient.h" +#include "ShimClient.h" +#include "Buffer.h" +#include "BDDTest.h" +#include "trace.h" + + +byte server[] = { 172, 16, 0, 2 }; + +bool callback_called = false; +char lastTopic[1024]; +char lastPayload[1024]; +unsigned int lastLength; + +void reset_callback() { + callback_called = false; + lastTopic[0] = '\0'; + lastPayload[0] = '\0'; + lastLength = 0; +} + +void callback(char* topic, byte* payload, unsigned int length) { + callback_called = true; + strcpy(lastTopic,topic); + memcpy(lastPayload,payload,length); + lastLength = length; +} + +int test_receive_callback() { + IT("receives a callback message"); + reset_callback(); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + shimClient.respond(publish,16); + + rc = client.loop(); + + IS_TRUE(rc); + + IS_TRUE(callback_called); + IS_TRUE(strcmp(lastTopic,"topic")==0); + IS_TRUE(memcmp(lastPayload,"payload",7)==0); + IS_TRUE(lastLength == 7); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_receive_stream() { + IT("receives a streamed callback message"); + reset_callback(); + + Stream stream; + stream.expect((uint8_t*)"payload",7); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient, stream); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + shimClient.respond(publish,16); + + rc = client.loop(); + + IS_TRUE(rc); + + IS_TRUE(callback_called); + IS_TRUE(strcmp(lastTopic,"topic")==0); + IS_TRUE(lastLength == 7); + + IS_FALSE(stream.error()); + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_receive_max_sized_message() { + IT("receives an max-sized message"); + reset_callback(); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + int length = MQTT_MAX_PACKET_SIZE; + byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + byte bigPublish[length]; + memset(bigPublish,'A',length); + bigPublish[length] = 'B'; + memcpy(bigPublish,publish,16); + shimClient.respond(bigPublish,length); + + rc = client.loop(); + + IS_TRUE(rc); + + IS_TRUE(callback_called); + IS_TRUE(strcmp(lastTopic,"topic")==0); + IS_TRUE(lastLength == length-9); + IS_TRUE(memcmp(lastPayload,bigPublish+9,lastLength)==0); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_receive_oversized_message() { + IT("drops an oversized message"); + reset_callback(); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + int length = MQTT_MAX_PACKET_SIZE+1; + byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + byte bigPublish[length]; + memset(bigPublish,'A',length); + bigPublish[length] = 'B'; + memcpy(bigPublish,publish,16); + shimClient.respond(bigPublish,length); + + rc = client.loop(); + + IS_TRUE(rc); + + IS_FALSE(callback_called); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_drop_invalid_remaining_length_message() { + IT("drops invalid remaining length message"); + reset_callback(); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,0x92,0x92,0x92,0x92,0x01,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + shimClient.respond(publish,20); + + rc = client.loop(); + + IS_FALSE(rc); + + IS_FALSE(callback_called); + + IS_FALSE(shimClient.error()); + + END_IT +} + + +int test_receive_oversized_stream_message() { + IT("drops an oversized message"); + reset_callback(); + + Stream stream; + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient, stream); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + int length = MQTT_MAX_PACKET_SIZE+1; + byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + + byte bigPublish[length]; + memset(bigPublish,'A',length); + bigPublish[length] = 'B'; + memcpy(bigPublish,publish,16); + + shimClient.respond(bigPublish,length); + stream.expect(bigPublish+9,length-9); + + rc = client.loop(); + + IS_TRUE(rc); + + IS_TRUE(callback_called); + IS_TRUE(strcmp(lastTopic,"topic")==0); + IS_TRUE(lastLength == length-9); + + IS_FALSE(stream.error()); + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_receive_qos1() { + IT("receives a qos1 message"); + reset_callback(); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + shimClient.respond(publish,18); + + byte puback[] = {0x40,0x2,0x12,0x34}; + shimClient.expect(puback,4); + + rc = client.loop(); + + IS_TRUE(rc); + + IS_TRUE(callback_called); + IS_TRUE(strcmp(lastTopic,"topic")==0); + IS_TRUE(memcmp(lastPayload,"payload",7)==0); + IS_TRUE(lastLength == 7); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int main() +{ + SUITE("Receive"); + test_receive_callback(); + test_receive_stream(); + test_receive_max_sized_message(); + test_drop_invalid_remaining_length_message(); + test_receive_oversized_message(); + test_receive_oversized_stream_message(); + test_receive_qos1(); + + FINISH +} diff --git a/libraries/PubSubClient/tests/src/subscribe_spec.cpp b/libraries/PubSubClient/tests/src/subscribe_spec.cpp new file mode 100644 index 0000000..a419823 --- /dev/null +++ b/libraries/PubSubClient/tests/src/subscribe_spec.cpp @@ -0,0 +1,177 @@ +#include "PubSubClient.h" +#include "ShimClient.h" +#include "Buffer.h" +#include "BDDTest.h" +#include "trace.h" + + +byte server[] = { 172, 16, 0, 2 }; + +void callback(char* topic, byte* payload, unsigned int length) { + // handle message arrived +} + +int test_subscribe_no_qos() { + IT("subscribe without qos defaults to 0"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x0 }; + shimClient.expect(subscribe,12); + byte suback[] = { 0x90,0x3,0x0,0x2,0x0 }; + shimClient.respond(suback,5); + + rc = client.subscribe((char*)"topic"); + IS_TRUE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_subscribe_qos_1() { + IT("subscribes qos 1"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1 }; + shimClient.expect(subscribe,12); + byte suback[] = { 0x90,0x3,0x0,0x2,0x1 }; + shimClient.respond(suback,5); + + rc = client.subscribe((char*)"topic",1); + IS_TRUE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_subscribe_not_connected() { + IT("subscribe fails when not connected"); + ShimClient shimClient; + + PubSubClient client(server, 1883, callback, shimClient); + + int rc = client.subscribe((char*)"topic"); + IS_FALSE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_subscribe_invalid_qos() { + IT("subscribe fails with invalid qos values"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + rc = client.subscribe((char*)"topic",2); + IS_FALSE(rc); + rc = client.subscribe((char*)"topic",254); + IS_FALSE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_subscribe_too_long() { + IT("subscribe fails with too long topic"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + // max length should be allowed + // 0 1 2 3 4 5 6 7 8 9 0 1 2 + rc = client.subscribe((char*)"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); + IS_TRUE(rc); + + // 0 1 2 3 4 5 6 7 8 9 0 1 2 + rc = client.subscribe((char*)"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); + IS_FALSE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + + +int test_unsubscribe() { + IT("unsubscribes"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte unsubscribe[] = { 0xA2,0x9,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63 }; + shimClient.expect(unsubscribe,12); + byte unsuback[] = { 0xB0,0x2,0x0,0x2 }; + shimClient.respond(unsuback,4); + + rc = client.unsubscribe((char*)"topic"); + IS_TRUE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int test_unsubscribe_not_connected() { + IT("unsubscribe fails when not connected"); + ShimClient shimClient; + + PubSubClient client(server, 1883, callback, shimClient); + + int rc = client.unsubscribe((char*)"topic"); + IS_FALSE(rc); + + IS_FALSE(shimClient.error()); + + END_IT +} + +int main() +{ + SUITE("Subscribe"); + test_subscribe_no_qos(); + test_subscribe_qos_1(); + test_subscribe_not_connected(); + test_subscribe_invalid_qos(); + test_subscribe_too_long(); + test_unsubscribe(); + test_unsubscribe_not_connected(); + FINISH +} diff --git a/libraries/PubSubClient/tests/testcases/__init__.py b/libraries/PubSubClient/tests/testcases/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libraries/PubSubClient/tests/testcases/mqtt_basic.py b/libraries/PubSubClient/tests/testcases/mqtt_basic.py new file mode 100644 index 0000000..f23ef71 --- /dev/null +++ b/libraries/PubSubClient/tests/testcases/mqtt_basic.py @@ -0,0 +1,39 @@ +import unittest +import settings +import time +import mosquitto + + +def on_message(mosq, obj, msg): + obj.message_queue.append(msg) + + +class mqtt_basic(unittest.TestCase): + + message_queue = [] + + @classmethod + def setUpClass(self): + self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self) + self.client.connect(settings.server_ip) + self.client.on_message = on_message + self.client.subscribe("outTopic", 0) + + @classmethod + def tearDownClass(self): + self.client.disconnect() + + def test_one(self): + i = 30 + while len(self.message_queue) == 0 and i > 0: + self.client.loop() + time.sleep(0.5) + i -= 1 + self.assertTrue(i > 0, "message receive timed-out") + self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") + msg = self.message_queue[0] + self.assertEqual(msg.mid, 0, "message id not 0") + self.assertEqual(msg.topic, "outTopic", "message topic incorrect") + self.assertEqual(msg.payload, "hello world") + self.assertEqual(msg.qos, 0, "message qos not 0") + self.assertEqual(msg.retain, False, "message retain flag incorrect") diff --git a/libraries/PubSubClient/tests/testcases/mqtt_publish_in_callback.py b/libraries/PubSubClient/tests/testcases/mqtt_publish_in_callback.py new file mode 100644 index 0000000..45b0a85 --- /dev/null +++ b/libraries/PubSubClient/tests/testcases/mqtt_publish_in_callback.py @@ -0,0 +1,59 @@ +import unittest +import settings +import time +import mosquitto + + +def on_message(mosq, obj, msg): + obj.message_queue.append(msg) + + +class mqtt_publish_in_callback(unittest.TestCase): + + message_queue = [] + + @classmethod + def setUpClass(self): + self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self) + self.client.connect(settings.server_ip) + self.client.on_message = on_message + self.client.subscribe("outTopic", 0) + + @classmethod + def tearDownClass(self): + self.client.disconnect() + + def test_connect(self): + i = 30 + while len(self.message_queue) == 0 and i > 0: + self.client.loop() + time.sleep(0.5) + i -= 1 + self.assertTrue(i > 0, "message receive timed-out") + self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") + msg = self.message_queue.pop(0) + self.assertEqual(msg.mid, 0, "message id not 0") + self.assertEqual(msg.topic, "outTopic", "message topic incorrect") + self.assertEqual(msg.payload, "hello world") + self.assertEqual(msg.qos, 0, "message qos not 0") + self.assertEqual(msg.retain, False, "message retain flag incorrect") + + def test_publish(self): + self.assertEqual(len(self.message_queue), 0, "message queue not empty") + payload = "abcdefghij" + self.client.publish("inTopic", payload) + + i = 30 + while len(self.message_queue) == 0 and i > 0: + self.client.loop() + time.sleep(0.5) + i -= 1 + + self.assertTrue(i > 0, "message receive timed-out") + self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") + msg = self.message_queue.pop(0) + self.assertEqual(msg.mid, 0, "message id not 0") + self.assertEqual(msg.topic, "outTopic", "message topic incorrect") + self.assertEqual(msg.payload, payload) + self.assertEqual(msg.qos, 0, "message qos not 0") + self.assertEqual(msg.retain, False, "message retain flag incorrect") diff --git a/libraries/PubSubClient/tests/testcases/settings.py b/libraries/PubSubClient/tests/testcases/settings.py new file mode 100644 index 0000000..4ad8719 --- /dev/null +++ b/libraries/PubSubClient/tests/testcases/settings.py @@ -0,0 +1,2 @@ +server_ip = "172.16.0.2" +arduino_ip = "172.16.0.100" diff --git a/libraries/PubSubClient/tests/testsuite.py b/libraries/PubSubClient/tests/testsuite.py new file mode 100644 index 0000000..788fc5d --- /dev/null +++ b/libraries/PubSubClient/tests/testsuite.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python +import os +import os.path +import sys +import shutil +from subprocess import call +import importlib +import unittest +import re + +from testcases import settings + + +class Workspace(object): + + def __init__(self): + self.root_dir = os.getcwd() + self.build_dir = os.path.join(self.root_dir, "tmpbin") + self.log_dir = os.path.join(self.root_dir, "logs") + self.tests_dir = os.path.join(self.root_dir, "testcases") + self.examples_dir = os.path.join(self.root_dir, "../PubSubClient/examples") + self.examples = [] + self.tests = [] + if not os.path.isdir("../PubSubClient"): + raise Exception("Cannot find PubSubClient library") + try: + return __import__('ino') + except ImportError: + raise Exception("ino tool not installed") + + def init(self): + if os.path.isdir(self.build_dir): + shutil.rmtree(self.build_dir) + os.mkdir(self.build_dir) + if os.path.isdir(self.log_dir): + shutil.rmtree(self.log_dir) + os.mkdir(self.log_dir) + + os.chdir(self.build_dir) + call(["ino", "init"]) + + shutil.copytree("../../PubSubClient", "lib/PubSubClient") + + filenames = [] + for root, dirs, files in os.walk(self.examples_dir): + filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")] + filenames.sort() + for e in filenames: + self.examples.append(Sketch(self, e)) + + filenames = [] + for root, dirs, files in os.walk(self.tests_dir): + filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")] + filenames.sort() + for e in filenames: + self.tests.append(Sketch(self, e)) + + def clean(self): + shutil.rmtree(self.build_dir) + + +class Sketch(object): + def __init__(self, wksp, fn): + self.w = wksp + self.filename = fn + self.basename = os.path.basename(self.filename) + self.build_log = os.path.join(self.w.log_dir, "%s.log" % (os.path.basename(self.filename),)) + self.build_err_log = os.path.join(self.w.log_dir, "%s.err.log" % (os.path.basename(self.filename),)) + self.build_upload_log = os.path.join(self.w.log_dir, "%s.upload.log" % (os.path.basename(self.filename),)) + + def build(self): + sys.stdout.write(" Build: ") + sys.stdout.flush() + + # Copy sketch over, replacing IP addresses as necessary + fin = open(self.filename, "r") + lines = fin.readlines() + fin.close() + fout = open(os.path.join(self.w.build_dir, "src", "sketch.ino"), "w") + for l in lines: + if re.match(r"^byte server\[\] = {", l): + fout.write("byte server[] = { %s };\n" % (settings.server_ip.replace(".", ", "),)) + elif re.match(r"^byte ip\[\] = {", l): + fout.write("byte ip[] = { %s };\n" % (settings.arduino_ip.replace(".", ", "),)) + else: + fout.write(l) + fout.flush() + fout.close() + + # Run build + fout = open(self.build_log, "w") + ferr = open(self.build_err_log, "w") + rc = call(["ino", "build"], stdout=fout, stderr=ferr) + fout.close() + ferr.close() + if rc == 0: + sys.stdout.write("pass") + sys.stdout.write("\n") + return True + else: + sys.stdout.write("fail") + sys.stdout.write("\n") + with open(self.build_err_log) as f: + for line in f: + print(" " + line) + return False + + def upload(self): + sys.stdout.write(" Upload: ") + sys.stdout.flush() + fout = open(self.build_upload_log, "w") + rc = call(["ino", "upload"], stdout=fout, stderr=fout) + fout.close() + if rc == 0: + sys.stdout.write("pass") + sys.stdout.write("\n") + return True + else: + sys.stdout.write("fail") + sys.stdout.write("\n") + with open(self.build_upload_log) as f: + for line in f: + print(" " + line) + return False + + def test(self): + # import the matching test case, if it exists + try: + basename = os.path.basename(self.filename)[:-4] + i = importlib.import_module("testcases." + basename) + except: + sys.stdout.write(" Test: no tests found") + sys.stdout.write("\n") + return + c = getattr(i, basename) + + testmethods = [m for m in dir(c) if m.startswith("test_")] + testmethods.sort() + tests = [] + for m in testmethods: + tests.append(c(m)) + + result = unittest.TestResult() + c.setUpClass() + if self.upload(): + sys.stdout.write(" Test: ") + sys.stdout.flush() + for t in tests: + t.run(result) + print(str(result.testsRun - len(result.failures) - len(result.errors)) + "/" + str(result.testsRun)) + if not result.wasSuccessful(): + if len(result.failures) > 0: + for f in result.failures: + print("-- " + str(f[0])) + print(f[1]) + if len(result.errors) > 0: + print(" Errors:") + for f in result.errors: + print("-- " + str(f[0])) + print(f[1]) + c.tearDownClass() + + +if __name__ == '__main__': + run_tests = True + + w = Workspace() + w.init() + + for e in w.examples: + print("--------------------------------------") + print("[" + e.basename + "]") + if e.build() and run_tests: + e.test() + for e in w.tests: + print("--------------------------------------") + print("[" + e.basename + "]") + if e.build() and run_tests: + e.test() + + w.clean() diff --git a/libraries/PubSubClientTools/MqttWildcard.cpp b/libraries/PubSubClientTools/MqttWildcard.cpp new file mode 100644 index 0000000..9251a8c --- /dev/null +++ b/libraries/PubSubClientTools/MqttWildcard.cpp @@ -0,0 +1,53 @@ +#include "MqttWildcard.h" + +int MqttWildcard::explode(String *results, String source, char delimiter) { + int count = 0; + int index = 0; + + for (int i = 0; i < source.length(); i++) { + if (source.charAt(i) == delimiter) { + results[count++] = source.substring(index, i); + index = i+1; + } + } + results[count++] = source.substring(index); + + return count; +} +bool MqttWildcard::wildcardMatch(String topic, String wildcard) { + // Catch trivial matches + if (topic == wildcard) return true; + if (wildcard == "#") return true; + + String exploded_topic[TOPIC_BUFFER_SIZE]; + int exploded_topic_count = MqttWildcard::explode(exploded_topic, topic, '/'); + + String exploded_wildcard[TOPIC_BUFFER_SIZE]; + int exploded_wildcard_count = MqttWildcard::explode(exploded_wildcard, wildcard, '/'); + + // Impossible to match since wildcard "+/+/#" is not matched by topic foo/bar + if (exploded_wildcard_count > exploded_topic_count) return false; + + int match_count = 0; + for (int i = 0; i < exploded_wildcard_count; i++) { + if (exploded_wildcard[i] == "+") { + continue; + } + if (exploded_wildcard[i] == "#") { + return true; + } + if (exploded_wildcard[i] != exploded_topic[i]) { + return false; + } + } + + /* + If this point is reached and we did not return yet, + topic- and wildcard-depth must be equal, otherwise it cant't be a valid match: + topic: foo/bar/example + wildcard_1: foo/bar/+ + wildcard_2: foo/+ + Both wildcards would make it to this point, bot only wildcard_1 would be a valid match. + */ + return (exploded_wildcard_count == exploded_topic_count); +} \ No newline at end of file diff --git a/libraries/PubSubClientTools/MqttWildcard.h b/libraries/PubSubClientTools/MqttWildcard.h new file mode 100644 index 0000000..d3347c9 --- /dev/null +++ b/libraries/PubSubClientTools/MqttWildcard.h @@ -0,0 +1,17 @@ +#ifndef MqttWildcard_h +#define MqttWildcard_h + +#include +#include + +#ifndef TOPIC_BUFFER_SIZE +#define TOPIC_BUFFER_SIZE 100 +#endif + +class MqttWildcard { + public: + static int explode(String *results, String source, char delimiter); + static bool wildcardMatch(String topic, String wildcard); +}; + +#endif \ No newline at end of file diff --git a/libraries/PubSubClientTools/PubSubClientTools.cpp b/libraries/PubSubClientTools/PubSubClientTools.cpp new file mode 100644 index 0000000..c02f0cd --- /dev/null +++ b/libraries/PubSubClientTools/PubSubClientTools.cpp @@ -0,0 +1,83 @@ +#include "PubSubClientTools.h" + +// Public +PubSubClientTools::PubSubClientTools(PubSubClient& _pubSub) : pubSub(_pubSub) { + pubSub.setCallback(mqtt_callback); +}; + +bool PubSubClientTools::connected() { + return pubSub.connected(); +} +bool PubSubClientTools::connect(String clientId) { + char client_char[CLIENTID_BUFFER_SIZE]; + clientId.toCharArray(client_char, CLIENTID_BUFFER_SIZE); + return pubSub.connect(client_char); +} +bool PubSubClientTools::connect(String clientId, String willTopic, int willQoS, bool willRetain, String willMessage) { + char client_char[CLIENTID_BUFFER_SIZE]; + char topic_char[TOPIC_BUFFER_SIZE]; + char msg_char[MESSAGE_BUFFER_SIZE]; + + clientId.toCharArray(client_char, CLIENTID_BUFFER_SIZE); + willTopic.toCharArray(topic_char, TOPIC_BUFFER_SIZE); + willMessage.toCharArray(msg_char, MESSAGE_BUFFER_SIZE); + + return pubSub.connect(client_char, topic_char, willQoS, willRetain, msg_char); +} + +bool PubSubClientTools::publish(String topic, String message) { + return this->publish(topic, message, false); +} +bool PubSubClientTools::publish(String topic, String message, bool retained) { + char topic_char[TOPIC_BUFFER_SIZE]; + char msg_char[MESSAGE_BUFFER_SIZE]; + + topic.toCharArray(topic_char, TOPIC_BUFFER_SIZE); + message.toCharArray(msg_char, MESSAGE_BUFFER_SIZE); + + return pubSub.publish(topic_char, msg_char, retained); +} + +bool PubSubClientTools::subscribe(String topic, CALLBACK_SIGNATURE) { + if (callbackCount >= CALLBACK_LIST_SIZE) return false; + + char topic_char[TOPIC_BUFFER_SIZE]; + topic.toCharArray(topic_char, TOPIC_BUFFER_SIZE); + + callbackList[callbackCount].topic = topic; + callbackList[callbackCount].callback = callback; + callbackCount++; + + return pubSub.subscribe(topic_char); +} + +int PubSubClientTools::resubscribe() { + int count = 0; + + pubSub.setCallback(mqtt_callback); + + for (int i = 0; i < callbackCount; i++) { + char topic_char[TOPIC_BUFFER_SIZE]; + callbackList[i].topic.toCharArray(topic_char, TOPIC_BUFFER_SIZE); + + if ( pubSub.subscribe(topic_char) ) { + count++; + } + } + return count; +} + +// Private +void PubSubClientTools::callback(PUBSUBCLIENT_CALLBACK_PARAMETERS) { + String topic = String(topicChar); + String message = ""; + for (int i = 0; i < length; i++) { + message += (char)payload[i]; + } + + for (int i = 0; i < callbackCount; i++) { + if (MqttWildcard::wildcardMatch(topic, callbackList[i].topic)) { + (*callbackList[i].callback)(topic,message); + } + } +} \ No newline at end of file diff --git a/libraries/PubSubClientTools/PubSubClientTools.h b/libraries/PubSubClientTools/PubSubClientTools.h new file mode 100644 index 0000000..71777c9 --- /dev/null +++ b/libraries/PubSubClientTools/PubSubClientTools.h @@ -0,0 +1,59 @@ +#ifndef PubSubClientTools_h +#define PubSubClientTools_h + +#if !defined(ESP8266) && !defined(ESP32) +#warning This library was developed for ESP8266 and ESP32 microcontrollers +#endif + +#include + +#include +#include "PubSubClient.h" +#include "MqttWildcard.h" + +#define CALLBACK_SIGNATURE void (*callback)(String topic, String message) +#define PUBSUBCLIENT_CALLBACK_PARAMETERS char* topicChar, uint8_t* payload, unsigned int length + +#ifndef CLIENTID_BUFFER_SIZE +#define CLIENTID_BUFFER_SIZE 50 +#endif +#ifndef TOPIC_BUFFER_SIZE +#define TOPIC_BUFFER_SIZE 100 +#endif +#ifndef MESSAGE_BUFFER_SIZE +#define MESSAGE_BUFFER_SIZE MQTT_MAX_PACKET_SIZE +#endif +#ifndef CALLBACK_LIST_SIZE +#define CALLBACK_LIST_SIZE 50 +#endif + +struct callbackTopic { + String topic; + void (*callback)(String topic, String message); +}; + +class PubSubClientTools { + private: + PubSubClient& pubSub; + struct callbackTopic callbackList[CALLBACK_LIST_SIZE]; + int callbackCount = 0; + + std::function mqtt_callback = std::bind(&PubSubClientTools::callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); + void callback(PUBSUBCLIENT_CALLBACK_PARAMETERS); + + public: + PubSubClientTools(PubSubClient& pubSub); + + bool connected(); + bool connect(String clientId); + bool connect(String clientId, String willTopic, int willQoS, bool willRetain, String willMessage); + + bool publish(String topic, String message); + bool publish(String topic, String message, bool retained); + + bool subscribe(String topic, CALLBACK_SIGNATURE); + + int resubscribe(); +}; + +#endif diff --git a/libraries/PubSubClientTools/README.md b/libraries/PubSubClientTools/README.md new file mode 100644 index 0000000..9950820 --- /dev/null +++ b/libraries/PubSubClientTools/README.md @@ -0,0 +1,10 @@ +This library provides some functions to make life easier when using PubSubClient for Arduino + +## Examples + +The library comes with a number of example sketches. See File > Examples > PubSubClientTools +within the Arduino application. + +## Credits + +[PubSubClient](https://github.com/knolleary/pubsubclient) diff --git a/libraries/PubSubClientTools/examples/mqtt_esp32/mqtt_esp32.ino b/libraries/PubSubClientTools/examples/mqtt_esp32/mqtt_esp32.ino new file mode 100644 index 0000000..72f6ddc --- /dev/null +++ b/libraries/PubSubClientTools/examples/mqtt_esp32/mqtt_esp32.ino @@ -0,0 +1,70 @@ +#include +#include +#include + +#include // https://github.com/ivanseidel/ArduinoThread +#include + +#define WIFI_SSID "........" +#define WIFI_PASS "........" +#define MQTT_SERVER "broker.mqtt-dashboard.com" + +WiFiClient espClient; +PubSubClient client(MQTT_SERVER, 1883, espClient); +PubSubClientTools mqtt(client); + +ThreadController threadControl = ThreadController(); +Thread thread = Thread(); + +int value = 0; +const String s = ""; + +void setup() { + Serial.begin(115200); + Serial.println(); + + // Connect to WiFi + Serial.print(s+"Connecting to WiFi: "+WIFI_SSID+" "); + WiFi.begin(WIFI_SSID, WIFI_PASS); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println("connected"); + + // Connect to MQTT + Serial.print(s+"Connecting to MQTT: "+MQTT_SERVER+" ... "); + if (client.connect("ESP32Client")) { + Serial.println("connected"); + + mqtt.subscribe("test_in/foo/bar", topic1_subscriber); + mqtt.subscribe("test_in/+/bar", topic2_subscriber); + mqtt.subscribe("test_in/#", topic3_subscriber); + } else { + Serial.println(s+"failed, rc="+client.state()); + } + + // Enable Thread + thread.onRun(publisher); + thread.setInterval(2000); + threadControl.add(&thread); +} + +void loop() { + client.loop(); + threadControl.run(); +} + +void publisher() { + ++value; + mqtt.publish("test_out/hello_world", s+"Hello World! - No. "+value); +} +void topic1_subscriber(String topic, String message) { + Serial.println(s+"Message arrived in function 1 ["+topic+"] "+message); +} +void topic2_subscriber(String topic, String message) { + Serial.println(s+"Message arrived in function 2 ["+topic+"] "+message); +} +void topic3_subscriber(String topic, String message) { + Serial.println(s+"Message arrived in function 3 ["+topic+"] "+message); +} diff --git a/libraries/PubSubClientTools/examples/mqtt_esp8266/mqtt_esp8266.ino b/libraries/PubSubClientTools/examples/mqtt_esp8266/mqtt_esp8266.ino new file mode 100644 index 0000000..8b8da98 --- /dev/null +++ b/libraries/PubSubClientTools/examples/mqtt_esp8266/mqtt_esp8266.ino @@ -0,0 +1,70 @@ +#include +#include +#include + +#include // https://github.com/ivanseidel/ArduinoThread +#include + +#define WIFI_SSID "........" +#define WIFI_PASS "........" +#define MQTT_SERVER "broker.mqtt-dashboard.com" + +WiFiClient espClient; +PubSubClient client(MQTT_SERVER, 1883, espClient); +PubSubClientTools mqtt(client); + +ThreadController threadControl = ThreadController(); +Thread thread = Thread(); + +int value = 0; +const String s = ""; + +void setup() { + Serial.begin(115200); + Serial.println(); + + // Connect to WiFi + Serial.print(s+"Connecting to WiFi: "+WIFI_SSID+" "); + WiFi.begin(WIFI_SSID, WIFI_PASS); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println("connected"); + + // Connect to MQTT + Serial.print(s+"Connecting to MQTT: "+MQTT_SERVER+" ... "); + if (client.connect("ESP8266Client")) { + Serial.println("connected"); + + mqtt.subscribe("test_in/foo/bar", topic1_subscriber); + mqtt.subscribe("test_in/+/bar", topic2_subscriber); + mqtt.subscribe("test_in/#", topic3_subscriber); + } else { + Serial.println(s+"failed, rc="+client.state()); + } + + // Enable Thread + thread.onRun(publisher); + thread.setInterval(2000); + threadControl.add(&thread); +} + +void loop() { + client.loop(); + threadControl.run(); +} + +void publisher() { + ++value; + mqtt.publish("test_out/hello_world", s+"Hello World! - No. "+value); +} +void topic1_subscriber(String topic, String message) { + Serial.println(s+"Message arrived in function 1 ["+topic+"] "+message); +} +void topic2_subscriber(String topic, String message) { + Serial.println(s+"Message arrived in function 2 ["+topic+"] "+message); +} +void topic3_subscriber(String topic, String message) { + Serial.println(s+"Message arrived in function 3 ["+topic+"] "+message); +} diff --git a/libraries/PubSubClientTools/keywords.txt b/libraries/PubSubClientTools/keywords.txt new file mode 100644 index 0000000..6f7008e --- /dev/null +++ b/libraries/PubSubClientTools/keywords.txt @@ -0,0 +1,21 @@ +####################################### +# Syntax Coloring +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +PubSubClientTools KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +publish KEYWORD2 +subscribe KEYWORD2 +resubscribe KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libraries/PubSubClientTools/library.properties b/libraries/PubSubClientTools/library.properties new file mode 100644 index 0000000..f6271cb --- /dev/null +++ b/libraries/PubSubClientTools/library.properties @@ -0,0 +1,9 @@ +name=PubSubClientTools +version=0.6 +author=Simon Christmann +maintainer=Simon Christmann +sentence=Tools for easier usage of PubSubClient +paragraph=Provides useful tools for PubSubClient, however they may consume more power and storage. Therefore it's recommended for powerful microcontrollers like ESP8266. +category=Communication +url=https://github.com/dersimn/ArduinoPubSubClientTools +architectures=* diff --git a/libraries/TaskScheduler-master/LICENSE.txt b/libraries/TaskScheduler-master/LICENSE.txt new file mode 100644 index 0000000..71a431c --- /dev/null +++ b/libraries/TaskScheduler-master/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2015, Anatoli Arkhipenko. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/libraries/TaskScheduler-master/README b/libraries/TaskScheduler-master/README new file mode 100644 index 0000000..e30dccd --- /dev/null +++ b/libraries/TaskScheduler-master/README @@ -0,0 +1,211 @@ +Task Scheduler – cooperative multitasking for Arduino and ESP8266 microcontrollers +Version 3.0.2 2018-11-14 + +If you find TaskScheduler useful for your Arduino project, please drop me an email: arkhipenko@hotmail.com +---------------------------------------------------------------------------------------------------------- + +OVERVIEW: + A lightweight implementation of cooperative multitasking (task scheduling) supporting: + 1. Periodic task execution (with dynamic execution period in milliseconds or microseconds) + 2. Number of iterations (n times) + 3. Execution of tasks in predefined sequence + 4. Dynamic change of task execution parameters (frequency, number of iterations, callback function) + 5. Power saving via entering IDLE sleep mode between tasks are scheduled to run + 6. Task invocation via Status Request object + 7. Task IDs and Control Points for error handling and watchdog timer + 8. Local Task Storage pointer (allowing use of same callback code for multiple tasks) + 9. Layered task prioritization +10. Support for std::functions (ESP8266 only) +11. Overall task timeout +12. Static and dynamic callback method binding +13. Support for STM32F1 ARM Cortex-M3 boards + +Scheduling overhead: between 15 and 18 microseconds per scheduling pass (check the benchmark example). + +Tested on the following microcontrollers: + - Arduino Uno R3 + - Arduino Nano + - Arduino Micro + - ATtiny85 + - ESP8266 (Node MCU v2.0) + - ESP32 + - Teensy (tested on Teensy 3.5) + - STM32F1 (tested on Mini USB STM32F103RCBT6 ARM Cortex-M3 leaflabs Leaf maple mini module F) + +For detailed functionality overview please refer to TaskScheduler documentation in the 'extras' folder. +======================================================================================================= + +Check out what TaskScheduler can do: +==================================== + Endeavor - build a space capable craft, with the ability to maneuver both in and out of atmosphere. + (by Radical Space Technologies + http://radicalspacetechnologies.com/2015/10/01/endeavor-phase-i/ + + "So after some digging I found TaskScheduler an awesome little library developed by Anatoli Arkhipenko, + his github is here and the documentation is here. Its fantastic, so far I’ve been able to replicate + some things I’ve seen in Ardupilot. ..." + link: http://radicalspacetechnologies.com/2015/10/25/endeavors-code-definitely-progress/ + + + 3 Devo - Quality 3D printing filament, now made accessible and affordable + http://3devo.eu/ (http://3devo.eu/license-information/) + + + Houston midi clock project - TaskScheduler with microseconds resolution + (by chaffneue: My first arduino project. It's a multi-master midi controller with a shared clock and + auto count in behaviour. Project files on github: https://github.com/chaffneue/houston + youtube: https://www.youtube.com/watch?v=QRof550TtXo) + + + Hackabot Nano - Compact Plug and Play Arduino compatible robotic kit + (by Funnyvale: http://hackarobot.com/ + also: https://www.kickstarter.com/projects/hackarobot/hackabot-nano-compact-plug-and-play-arduino-robot) + + + Arduino Nano based Hexbug Scarab Robotic Spider + (by arkhipenko: http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider/) + + + Wave your hand to control OWI Robotic Arm... no strings attached + (by arkhipenko: http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/) + + + APIS - Automated Plant Irrigation System + (by arkhipenko: http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/) + + + IoT APIS v2 - Autonomous IoT-enabled Automated Plant Irrigation System + (by arkhipenko: http://www.instructables.com/id/IoT-APIS-V2-Autonomous-IoT-enabled-Automated-Plant/) + + + Interactive Halloween Pumpkin + (by arkhipenko: http://www.instructables.com/id/Interactive-Halloween-Pumpkin/) + + +Changelog: +========= +v3.0.0: MAJOR RELEASE + 2018-03-15 - Optional Dynamic callback method binding (_TASK_OO_CALLBACKS compilation directive). + +v2.6.1: + 2018-02-13 - Bug: Support for task self-destruction in the OnDisable method. + Example 19: dynamic task creation and destruction. + 2018-03-14 - Bug: high level scheduler ignored if lower level chain is empty + Example 20: use of local task storage to work with task-specific class objects + +v2.6.0: + 2018-01-30 - _TASK_TIMEOUT compilation directive: Task overall timeout functionality + 2018-01-30 - Support for ESP32 + (Contributed by Marco Tombesi: https://github.com/baggior) + +v2.5.2: + 2018-01-09 - _TASK_INLINE compilation directive making all methods declared "inline" (issue #42) + + +v2.5.1: + 2018-01-06 - support for IDLE sleep on Teensy boards (tested on Teensy 3.6) + +v2.5.0: + 2017-04-27 - added optional support for std::functions via _TASK_STD_FUNCTION + (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder) + 2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY! + *** USE AT YOUR OWN RISK *** + 2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37) + 2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski) + +v2.4.0: + 2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain + upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder) + +v2.3.0: + 2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is + scheduled to run next time + +v2.2.1: + 2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and + chain back together long running callback methods + 2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled. + Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method. + +v2.2.0: + 2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files + +v2.1.0: + 2016-02-01 - support for microsecond resolution + 2016-02-02 - added Scheduler baseline start time reset method: startNow() + +v2.0.1: + 2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation + +v2.0.0: + 2015-12-22 - _TASK_PRIORITY - support for layered task prioritization + +v1.9.2: + 2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary) + 2015-12-16 - bug fixes: automatic millis rollover support for delay methods + 2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay() + +v1.9.0: + 2015-11-24 - packed three byte-long status variables into one byte-long bit array structure data type - saving 2 bytes per each task instance + +v1.8.5: + 2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval + 2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner + +v1.8.4: + 2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before. Especially since OnEnable method can change the interval + +v1.8.3: + 2015-11-05 - support for task activation on a status request with arbitrary interval and number of iterations (0 and 1 are still default values) + 2015-11-05 - implement waitForDelayed() method to allow task activation on the status request completion delayed for one current interval + 2015-11-09 - added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility + 2015-11-14 - added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.) + 2015-11-14 - significant optimization of the scheduler's execute loop, including millis() rollover fix option + +v1.8.2: + 2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks) + 2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods + 2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable) + 2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call + +v1.8.1: + 2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging + +v1.8.0: + 2015-10-13 - support for status request objects allowing tasks waiting on requests + 2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch + +v1.7.0: + 2015-10-08 - introduced callback run counter - callback functions can branch on the iteration number. + 2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, false if was disabled. + 2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled) + 2015-10-11 - introduced callback functions "on enable" and "on disable". On enable runs every time enable is called, on disable runs only if task was enabled + 2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass regardless how much time is left + +v1.6.0: + 2015-09-22 - revert back to having all tasks disable on last iteration. + 2015-09-22 - deprecated disableOnLastIteration method as a result + 2015-10-01 - made version numbers semver compliant (documentation only) + +v1.5.1: + 2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). + Thanks to Hannes Morgenstern for catching this one + +v1.5.0: + 2015-09-20 - access to currently executing task (for callback functions) + 2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain + 2015-09-20 - option to create a task already enabled + +v1.4.1: + 2015-09-15 - more careful placement of AVR-specific includes for sleep functions (compatibility with DUE) + sleep on idle run is no longer a default and should be explicitly compiled with _TASK_SLEEP_ON_IDLE_RUN defined + +v1.0.0: + 2015-02-24 - Initial release + 2015-02-28 - added delay() and disableOnLastIteration() functions + 2015-03-25 - changed scheduler execute() function for a more precise delay calculation: + 1. Do not delay if any of the tasks ran (making request for immediate execution redundant) + 2. Delay is invoked only if none of the tasks ran + 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute function itself. + 2015-05-11 - added restart() and restartDelayed() functions to restart tasks which are on hold after running all iterations + 2015-05-19 - completely removed delay from the scheduler since there are no power saving there. using 1 ms sleep instead diff --git a/libraries/TaskScheduler-master/README.md b/libraries/TaskScheduler-master/README.md new file mode 100644 index 0000000..33ac61d --- /dev/null +++ b/libraries/TaskScheduler-master/README.md @@ -0,0 +1,77 @@ +# Task Scheduler +### Cooperative multitasking for Arduino microcontrollers +#### Version 3.0.2: 2018-11-14 + +### OVERVIEW: +A lightweight implementation of cooperative multitasking (task scheduling) supporting: +1. Periodic task execution, with dynamic execution period in `milliseconds` (default) or `microseconds` (if explicitly enabled) – frequency of execution +2. Number of iterations (limited or infinite number of iterations) +3. Execution of tasks in predefined sequence +4. Dynamic change of task execution parameters (frequency, number of iterations, callback methods) +5. Power saving via entering **IDLE** sleep mode when tasks are not scheduled to run +6. Support for event-driven task invocation via Status Request object +7. Support for task IDs and Control Points for error handling and watchdog timer +8. Support for Local Task Storage pointer (allowing use of same callback code for multiple tasks) +9. Support for layered task prioritization +10. Support for `std::functions` (`ESP8266` only) +11. Overall task timeout +12. Static and dynamic callback method binding +13. Support for STM32F1 ARM Cortex-M3 boards + +Scheduling overhead: between `15` and `18` microseconds per scheduling pass (Arduino UNO rev 3 @ `16MHz` clock, single scheduler w/o prioritization) + +**TaskScheduler** was tested on the following platforms: +* Arduino Uno R3 +* Arduino Nano +* Arduino Micro +* ATtiny85 +* ESP8266 (Node MCU v2.0) +* ESP32 +* Teensy (tested on Teensy 3.5) +* STM32F1 (tested on Mini USB STM32F103RCBT6 ARM Cortex-M3 leaflabs Leaf maple mini module F) +--- +![TaskScheduler process diagram](https://github.com/arkhipenko/TaskScheduler/raw/master/extras/TaskScheduler_html.png) +--- +### Changelog is located [here.](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog) + + +#### For detailed functionality overview please refer to TaskScheduler documentation in the 'extras' folder or in the [Wiki page](https://github.com/arkhipenko/TaskScheduler/wiki). + +### Check out what TaskScheduler can do: + +* [3 Devo](http://3devo.eu/) - Quality 3D printing filament, now made accessible and affordable +(http://3devo.eu/license-information/) + + +* [Houston midi](https://github.com/chaffneue/houston) clock project - TaskScheduler with microseconds resolution + >by chaffneue: + >>My first arduino project. It's a multi-master midi controller with a shared clock and + auto count in behaviour. + + youtube: https://www.youtube.com/watch?v=QRof550TtXo + + +* [Hackabot Nano](http://hackarobot.com/) by Funnyvale - Compact Plug and Play Arduino compatible robotic kit + https://www.kickstarter.com/projects/hackarobot/hackabot-nano-compact-plug-and-play-arduino-robot + + +* Arduino Nano based Hexbug Scarab Robotic Spider + (by arkhipenko: http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider/) + +* Wave your hand to control OWI Robotic Arm... no strings attached + (by arkhipenko: http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/) + + +* APIS - Automated Plant Irrigation System + (by arkhipenko: http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/) + + +* IoT APIS v2 - Autonomous IoT-enabled Automated Plant Irrigation System + (by arkhipenko: http://www.instructables.com/id/IoT-APIS-V2-Autonomous-IoT-enabled-Automated-Plant/) + +* Interactive Halloween Pumpkin + (by arkhipenko: http://www.instructables.com/id/Interactive-Halloween-Pumpkin/) + +* Interactive Predator Costume with Real-Time Head Tracking Plasma Cannon + (by arkhipenko: https://www.instructables.com/id/Interactive-Predator-Costume-With-Head-Tracking-Pl/) + diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example01/Scheduler_example01.ino b/libraries/TaskScheduler-master/examples/Scheduler_example01/Scheduler_example01.ino new file mode 100644 index 0000000..2175e57 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example01/Scheduler_example01.ino @@ -0,0 +1,84 @@ +/** + * TaskScheduler Test + * + * Initially only tasks 1 and 2 are enabled + * Task1 runs every 2 seconds 10 times and then stops + * Task2 runs every 3 seconds indefinitely + * Task1 enables Task3 at its first run + * Task3 run every 5 seconds + * Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds + * At the end Task2 is the only task running every 1/2 seconds + */ + + +#include + +// Callback methods prototypes +void t1Callback(); +void t2Callback(); +void t3Callback(); + +//Tasks +Task t4(); +Task t1(2000, 10, &t1Callback); +Task t2(3000, TASK_FOREVER, &t2Callback); +Task t3(5000, TASK_FOREVER, &t3Callback); + +Scheduler runner; + + +void t1Callback() { + Serial.print("t1: "); + Serial.println(millis()); + + if (t1.isFirstIteration()) { + runner.addTask(t3); + t3.enable(); + Serial.println("t1: enabled t3 and added to the chain"); + } + + if (t1.isLastIteration()) { + t3.disable(); + runner.deleteTask(t3); + t2.setInterval(500); + Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); + } +} + +void t2Callback() { + Serial.print("t2: "); + Serial.println(millis()); + +} + +void t3Callback() { + Serial.print("t3: "); + Serial.println(millis()); + +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler TEST"); + + runner.init(); + Serial.println("Initialized scheduler"); + + runner.addTask(t1); + Serial.println("added t1"); + + runner.addTask(t2); + Serial.println("added t2"); + + delay(5000); + + t1.enable(); + Serial.println("Enabled t1"); + t2.enable(); + Serial.println("Enabled t2"); +} + + +void loop () { + runner.execute(); +} \ No newline at end of file diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example02/Scheduler_example02.ino b/libraries/TaskScheduler-master/examples/Scheduler_example02/Scheduler_example02.ino new file mode 100644 index 0000000..662ef2e --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example02/Scheduler_example02.ino @@ -0,0 +1,78 @@ +#define _TASK_SLEEP_ON_IDLE_RUN +#include + +Scheduler runner; +// Callback methods prototypes +void t1Callback(); +void t2Callback(); +void t3Callback(); + +// Tasks +Task t4(); +Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation +Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation +Task t3(5000, TASK_FOREVER, &t3Callback); + +// Test +// Initially only tasks 1 and 2 are enabled +// Task1 runs every 2 seconds 10 times and then stops +// Task2 runs every 3 seconds indefinitely +// Task1 enables Task3 at its first run +// Task3 run every 5 seconds +// loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected) +// Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds +// Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now +// At the end Task2 is the only task running every 1/2 seconds +// +// NOTE that t1 and t2 are affected by the delay() function in the setup() method and are scheduled immediately twice to "catch up" with millis(). + + + + + +void t1Callback() { + Serial.print("t1: "); + Serial.println(millis()); + + if (t1.isFirstIteration()) { + runner.addTask(t3); + t3.enable(); + Serial.println("t1: enabled t3 and added to the chain"); + } + + if (t1.isLastIteration()) { + t3.disable(); + runner.deleteTask(t3); + t2.setInterval(500); + Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); + } +} + +void t2Callback() { + Serial.print("t2: "); + Serial.println(millis()); + +} + +void t3Callback() { + Serial.print("t3: "); + Serial.println(millis()); + +} + +void setup () { + Serial.begin(115200); + delay(5000); + Serial.println("Scheduler TEST"); + + runner.startNow(); // set point-in-time for scheduling start +} + + +void loop () { + + runner.execute(); + +// Serial.println("Loop ticks at: "); +// Serial.println(millis()); +} \ No newline at end of file diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example03/Scheduler_example03.ino b/libraries/TaskScheduler-master/examples/Scheduler_example03/Scheduler_example03.ino new file mode 100644 index 0000000..789ee97 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example03/Scheduler_example03.ino @@ -0,0 +1,80 @@ +/** + * TaskScheduler Test of OnEnable and OnDisable methods and illustration of using wrapper tasks for timout purposes + * + * A wrapper task runs every 10 seconds and initiates the test case + * Another task is run once for 5 seconds, and serves as a LED blinking timeout - 5 seconds + * Finally, a dedicated task which controls LED is running periodically until stopped, and makes the LED blink with 0.5 to 1 second interval. + * + */ + +#define _TASK_SLEEP_ON_IDLE_RUN +#include + +#ifndef LED_BUILTIN +#define LED_BUILTIN 13 // define appropriate pin for your board +#endif + +Scheduler ts; + +// Callback methods prototypes +void WrapperCallback(); +bool BlinkOnEnable(); +void BlinkOnDisable(); +void LEDOn(); +void LEDOff(); + +// Tasks +Task tWrapper(10000L, TASK_FOREVER, &WrapperCallback, &ts, true); +Task tBlink(5000, TASK_ONCE, NULL, &ts, false, &BlinkOnEnable, &BlinkOnDisable); +Task tLED(0, TASK_FOREVER, NULL, &ts, false, NULL, &LEDOff); + +void WrapperCallback() { + tBlink.restartDelayed(); // LED blinking is initiated + //every 30 seconds for 5 seconds +} + + +// Upon being enabled, tBlink will define the parameters +// and enable LED blinking task, which actually controls +// the hardware (LED in this example) +bool BlinkOnEnable() { + tLED.setInterval( 200 + random(801) ); + tLED.setCallback( &LEDOn); + tLED.enable(); + + return true; // Task should be enabled +} + +// tBlink does not really need a callback function +// since it just waits for 5 seconds for the first +// and only iteration to occur. Once the iteration +// takes place, tBlink is disabled by the Scheduler, +// thus executing its OnDisable method below. + +void BlinkOnDisable() { + tLED.disable(); +} + +void LEDOn () { + digitalWrite(LED_BUILTIN , HIGH); + tLED.setCallback( &LEDOff); +} + +void LEDOff () { + digitalWrite(LED_BUILTIN , LOW); + tLED.setCallback( &LEDOn); +} + +// Note that LEDOff method serves as OnDisable method +// to make sure the LED is turned off when the tBlink +// task finishes (or disabled ahead of time) + +void setup() { +// put your setup code here, to run once: + pinMode(LED_BUILTIN , OUTPUT); +} + +void loop() { +// put your main code here, to run repeatedly: + ts.execute(); +} \ No newline at end of file diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example04_StatusRequest/Scheduler_example04_StatusRequest.ino b/libraries/TaskScheduler-master/examples/Scheduler_example04_StatusRequest/Scheduler_example04_StatusRequest.ino new file mode 100644 index 0000000..5c05606 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example04_StatusRequest/Scheduler_example04_StatusRequest.ino @@ -0,0 +1,90 @@ +/** This test demonstrates interaction between three simple tasks via StatusRequest object. + * Task T1 runs every 5 seconds and signals completion of a status request st. + * Tasks T2 and T3 are waiting on the same request (st) + * Task T3 does not renew its interest in status request st, so it is only invoked once (first iteration) + * Task T2 is invoked every time st completes, because it renews its interest in status of status request object st every iteration of T1 + */ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STATUS_REQUEST +#include + +StatusRequest st; + +Scheduler ts; + +// Callback methods prototypes +void Callback1(); +void Disable1(); +void Callback2(); +void Callback3(); +void PrepareStatus(); + +// Tasks +Task t1(5000, TASK_ONCE, &Callback1, &ts, true, NULL, &Disable1); +Task t2(&Callback2, &ts); +Task t3(&Callback3, &ts); + +/** T1 callback + * T1 just signals completion of st every 5 seconds + */ +void Callback1() { + Serial.println("T1: Signaling completion of ST"); + st.signalComplete(); +} + +/** T1 On Disable callback + * This callback renews the status request and restarts T1 delayed to run again in 5 seconds + */ +void Disable1() { + PrepareStatus(); + t1.restartDelayed(); +} + +/** T2 callback + * Invoked when status request st completes + */ +void Callback2() { + Serial.println("T2: Invoked due to completion of ST"); +} + + +/** T3 callback + * Invoked when status request st completes. + * This is only run once since T3 does not renew its interest in the status request st after first iteration + */ + void Callback3() { + Serial.println("T3: Invoked due to completion of ST"); + +} + +/** Prepare Status request st for another iteration + * + */ +void PrepareStatus() { + st.setWaiting(); // set the statusrequest object for waiting + t2.waitFor(&st); // request tasks 1 & 2 to wait on the object st +} + + +/** Main Arduino code + * Not much to do here. Just init Serial and set the initial status request + */ +void setup() { + + Serial.begin(115200); + delay(1000); + Serial.println("TaskScheduler: Status Request Test 1. Simple Test."); + + ts.startNow(); + PrepareStatus(); + t3.waitFor(&st); + + t1.delay(); +} + +void loop() { + + ts.execute(); + +} \ No newline at end of file diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example05_StatusRequest/Scheduler_example05_StatusRequest.ino b/libraries/TaskScheduler-master/examples/Scheduler_example05_StatusRequest/Scheduler_example05_StatusRequest.ino new file mode 100644 index 0000000..574d3c6 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example05_StatusRequest/Scheduler_example05_StatusRequest.ino @@ -0,0 +1,181 @@ +/** This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay + * (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values. + * The overall timeout of 1 second is setup as well. + * An error message needs to be printed if a timeout occurred instead of a value. + */ + + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STATUS_REQUEST +#include + +#ifdef ARDUINO_ARCH_STM32F1 +#define A0 3 +#endif + +StatusRequest measure; + +Scheduler ts; + +// Callback methods prototypes +void CycleCallback(); +void MeasureCallback(); +bool MeasureEnable(); +void MeasureDisable(); +void CalcCallback(); +void S1Callback(); bool S1Enable(); +void S2Callback(); bool S2Enable(); +void S3Callback(); bool S3Enable(); + +// Tasks +Task tCycle(10000, TASK_FOREVER, &CycleCallback, &ts, true); +Task tMeasure(1000, TASK_ONCE, &MeasureCallback, &ts, false, &MeasureEnable, &MeasureDisable); +Task tCalculate(&CalcCallback, &ts); +Task tSensor1(0, TASK_ONCE, &S1Callback, &ts, false, &S1Enable); +Task tSensor2(0, TASK_ONCE, &S2Callback, &ts, false, &S2Enable); +Task tSensor3(0, TASK_ONCE, &S3Callback, &ts, false, &S3Enable); + + +long distance, d1, d2, d3; + +void CycleCallback() { + Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); + + tMeasure.restartDelayed(); +} + + + +bool MeasureEnable() { + Serial.println("MeasureEnable: Activating sensors"); + + distance = 0; + measure.setWaiting(3); // Set the StatusRequest to wait for 3 signals. + tCalculate.waitFor(&measure); + + tSensor1.restartDelayed(); + tSensor2.restartDelayed(); + tSensor3.restartDelayed(); + + return true; +} + +void MeasureCallback() { + Serial.println("MeasureCallback: Invoked by calculate task or one second later"); + + if (measure.pending()) { + tCalculate.disable(); + measure.signalComplete(-1); // signal error + Serial.println("MeasureCallback: Timeout!"); + } + else { + Serial.print("MeasureCallback: Min distance=");Serial.println(distance); + } +} + +void MeasureDisable() { + Serial.println("MeasureDisable: Cleaning up"); + + tSensor1.disable(); + tSensor2.disable(); + tSensor3.disable(); +} + + +void CalcCallback() { + Serial.println("CalcCallback: calculating"); + distance = -1; + if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully + distance = d1 < d2 ? d1 : d2; + distance = d3 < distance ? d3 : distance; + tMeasure.forceNextIteration(); + } +} + + +/** Simulation code for sensor 1 + * ---------------------------- + */ +bool S1Enable() { + Serial.print("S1Enable: Triggering sensor1. Delay="); + + tSensor1.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout + d1 = 0; + + Serial.println( tSensor1.getInterval() ); + return true; +} + +void S1Callback() { + Serial.print("S1Callback: Emulating measurement. d1="); + d1 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement + measure.signal(); + + Serial.println(d1); +} + + +/** Simulation code for sensor 2 + * ---------------------------- + */ +bool S2Enable() { + Serial.print("S2Enable: Triggering sensor2. Delay="); + + tSensor2.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout + d2 = 0; + + Serial.println( tSensor2.getInterval() ); + return true; +} + +void S2Callback() { + Serial.print("S2Callback: Emulating measurement. d2="); + d2 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement + measure.signal(); + + Serial.println(d2); +} + + +/** Simulation code for sensor 3 + * ---------------------------- + */ +bool S3Enable() { + Serial.print("S3Enable: Triggering sensor3. Delay="); + + tSensor3.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout + d3 = 0; + + Serial.println( tSensor3.getInterval() ); + return true; +} + +void S3Callback() { + Serial.print("S3Callback: Emulating measurement. d3="); + d3 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement + measure.signal(); + + Serial.println(d3); +} + + +/** Main Arduino code + * Not much is left here - everything is taken care of by the framework + */ +void setup() { + + Serial.begin(115200); + Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); + +#ifdef ARDUINO_ARCH_STM32F1 + pinMode(A0, INPUT_ANALOG); +#endif + + randomSeed(analogRead(A0)+millis()); +} + +void loop() { + + ts.execute(); + +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example06_IDLE/Scheduler_example06_IDLE.ino b/libraries/TaskScheduler-master/examples/Scheduler_example06_IDLE/Scheduler_example06_IDLE.ino new file mode 100644 index 0000000..d2a214c --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example06_IDLE/Scheduler_example06_IDLE.ino @@ -0,0 +1,117 @@ + +/** + * This is a test to prove that processor really goes into IDLE sleep. + * For this setup: + * + * + +Task c(10, -1, &Count, &ts); +Task t(10000, 1, NULL, &ts, true, &tOn, &tOff); + +The result are: + +1): With #define _TASK_SLEEP_ON_IDLE_RUN enabled +On Arduino Uno: + Start + c1=10771 - v2.5.0 (v1.9.0: same) + c2=1001 + +On Teensy 3.5 (120MHz ARM): + Start + c1=21065 + c2=1001 + +On esp8266 (80 MHz) + Start + c1=10492 + c2=1001 + +On STM32F103RCBT6 (Maple Mini @72 MHz) + Start + c1=21004 + c2=1001 + +and + +2): With #define _TASK_SLEEP_ON_IDLE_RUN disabled (commented out) +Arduino Uno: + Start + c1=722426 - v3.0.2 + c1=635735 - v2.5.0 + c1=551947 - v1.9.0 + c2=1001 + +On Teensy 3.5 (120MHz ARM): + Start + c1=2690322 + c2=1001 + +On esp8266 (80 MHz) + Start + c1=351085 (689833 at 160Mhz) + c2=1001 + +On STM32F103RCBT6 (Maple Mini @72 MHz) + Start + c1=4665019 + c2=1001 + +C1 in scenario 2) is much higher than in scenario 1) because processor is put to sleep for 1), but not for 2) + + */ + + +/** + * Compile and run once with _TASK_SLEEP_ON_IDLE_RUN enabled, then with _TASK_SLEEP_ON_IDLE_RUN disabled. + * Compare the results. + */ + +//#define _TASK_SLEEP_ON_IDLE_RUN +#include + +Scheduler ts; + +// Callback methods prototypes +void Count(); +bool tOn(); void tOff(); + +// Tasks +Task c(10, TASK_FOREVER, &Count, &ts); +Task t(10000, TASK_ONCE, NULL, &ts, true, &tOn, &tOff); + + +volatile unsigned long c1, c2; +bool tOn() { + c1 = 0; + c2 = 0; + c.enable(); + + return true; +} + +void tOff() { + c.disable(); + Serial.print("c1=");Serial.println(c1); + Serial.print("c2=");Serial.println(c2); +} + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + delay(1000); + Serial.println("Start"); + + ts.startNow(); + t.delay(); +} + +void Count() { + c2++; +} + + +void loop() { + // put your main code here, to run repeatedly: + ts.execute(); + c1++; +} \ No newline at end of file diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example07_WDT/Scheduler_example07_WDT.ino b/libraries/TaskScheduler-master/examples/Scheduler_example07_WDT/Scheduler_example07_WDT.ino new file mode 100644 index 0000000..f02376e --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example07_WDT/Scheduler_example07_WDT.ino @@ -0,0 +1,133 @@ +/** + * TaskScheduler Test sketch - use of task IDs and watchdog timer to identify hung tasks + * THIS SKETCH RUNS ON AVR BOARDS ONLY + * Test case: + * Watchdog timer is set to 2 seconds (interrupt + reset) + * A hearbeat task (resetting the watchdog timer) is scheduled with 500 ms interval + * A number of tasks are running every 1 second and "rolling the dice" 0..19. If 5, task is made to enter infinite loop + * Device should reset in 2 seconds after a task enters infinite loop + * A task id and a control point number are saved to EEPROM prior to device reset, and are displayed after reboot. + * In real life, device might chose to NOT activate certain tasks which failed previously (failed sensors for instance) + */ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_WDT_IDS +#include + +#include +#include + +Scheduler ts; + +// Callback methods prototypes +void TaskCB(); +void HB(); bool HBOn(); void HBOff(); + +// Three tasks emulating accidental infinite loop +Task tTask1(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); +Task tTask2(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); +Task tTask3(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); + +// Heartbeat task - resetting the watchdog timer periodically +// Initiates WDT on enable, and deactivates it on disable +Task tHB(500, TASK_FOREVER, &HB, &ts, false, &HBOn, &HBOff); + +/** + * Emulating task callback function + * Prints task id and randomly "hangs" in two places. + * Control points are stored on the task prior to section which might hang, + * making this information available to the WDT interrupt handler + */ +void TaskCB() { + Task& T = ts.currentTask(); + + Serial.print("Task #:"); + Serial.print(T.getId()); + Serial.print(" current iteration = "); + Serial.println(T.getRunCounter()); + +// Hang if random number between 0 and 19 is 5 (5% probability) + T.setControlPoint(10); + if (random(20) == 5) for(;;); + +// Hang if random number between 0 and 99 is more that 95 (5% probability) + T.setControlPoint(95); + if (random(100) > 94) for(;;); +} + +/** + * This On Enable method sets up the WDT + * for interrupt and reset after 2 seconds + */ +bool HBOn() { + + //disable interrupts + cli(); + //reset watchdog + wdt_reset(); + //set up WDT interrupt + WDTCSR = (1<id); + Serial.print("Sum: "); Serial.println(vars[i]->sum); + Serial.print("Product: "); Serial.println(vars[i]->product); + Serial.println(); + } +} + + +/** + * This method is executed when each calculator task is enabled + * The purpose is to initiate all local variables + */ +bool CalcOn() { + Task& T = ts.currentTask(); + task_var& var = *((task_var*) T.getLtsPointer()); + +// Initialize local variables + var.id = T.getId(); + var.sum = 0; + var.product = var.id; + + return true; +} + + +/** + * This method performs simple calculations on task's local variables + */ +void Calculate() { + Task& T = ts.currentTask(); +// Another way to get to LTS pointer: + task_var& var = *((task_var*) ts.currentLts()); + + + Serial.print("Calculating for task: "); + Serial.print(T.getId()); + Serial.print("; Task id per LTS is: "); + Serial.println( var.id ); + + var.sum += T.getId(); + var.product = var.product * 10; + +} + + +/** + * Standard Arduino setup and loop methods + */ +void setup() { + Serial.begin(115200); + + randomSeed(analogRead(0)+analogRead(5)); + + pinMode(13, OUTPUT); + digitalWrite(13, LOW); + + Serial.println("Local Task Storage pointer test"); + + tWrapper.enableDelayed(); +} + +void loop() { + ts.execute(); +} \ No newline at end of file diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example09_TimeCritical/Scheduler_example09_TimeCritical.ino b/libraries/TaskScheduler-master/examples/Scheduler_example09_TimeCritical/Scheduler_example09_TimeCritical.ino new file mode 100644 index 0000000..b330d6f --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example09_TimeCritical/Scheduler_example09_TimeCritical.ino @@ -0,0 +1,57 @@ +/** + * TaskScheduler Test + * Illustration of use of Time Critical Information + * + * Task1 runs every 1 second indefinitely + * On each run it reports how delayed the invokation of the callback method was, + * and what was the scheduling overun. + * Each run task 1 is dealyed randomly for up to 2 seconds, thus simulating scheduling overrun + */ + + #define _TASK_TIMECRITICAL + #define _TASK_SLEEP_ON_IDLE_RUN +#include + +// Callback methods prototypes +void t1Callback(); + + +//Tasks +Task t1(1000, -1, &t1Callback); + +Scheduler runner; + + +void t1Callback() { + Serial.print(millis()); + Serial.print(": overrun = "); + Serial.print(t1.getOverrun()); + Serial.print(", start delayed by "); + Serial.println(t1.getStartDelay()); + + int i = random(2000); + Serial.print("Delaying for "); Serial.println(i); + delay(i); +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler TimeCritical TEST"); + + runner.init(); + Serial.println("Initialized scheduler"); + + runner.addTask(t1); + Serial.println("added t1. Waiting for 5 seconds."); + + delay(5000); + + t1.enable(); + + Serial.println("Enabled t1"); +} + + +void loop () { + runner.execute(); +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino b/libraries/TaskScheduler-master/examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino new file mode 100644 index 0000000..0015d8d --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino @@ -0,0 +1,106 @@ + +/** + * This is a test to benchmark TaskScheduler execution. + * + * This test executes 1,000,000 cycles of a task with empty callback method + * Compiled with different options, you can assess the impact of each on the size of the Task object + * and the execution overhead of the main execution pass route. + * + * Sample execution times (in milliseconds per 1M iterations) are provided below. + * The test board is Arduino UNO 16MHz processor. + * + +TaskScheduler 2.1.0: +No modifiers +Duration=19869 + +with SLEEP +Duration=20058 + +with status request: +Duration=20058 + +with time critical: +Duration=27289 + + +TaskScheduler 1.9.0: +No modifiers +Duration=15656 + +with SLEEP +Duration=16285 + +with status request: +Duration=16600 + +with rollover fix: +Duration=18109 + + +TaskScheduler 1.8.5: +Duration=15719 + +with SLEEP +Duration=16348 + +with status request: +Duration=18360 + +with rollover fix: +Duration=18423 + + */ + + +//#define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +//#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +//#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +//#define _TASK_LTS_POINTER // Compile with support for local task storage pointer +//#define _TASK_SLEEP_ON_IDLE_RUN +//#define _TASK_MICRO_RES +#include + +Scheduler ts; + +// Callback methods prototypes +bool tOn(); void tOff(); +void callback(); + +// Tasks +Task t(TASK_IMMEDIATE, 1000000, &callback, &ts, false, &tOn, &tOff); + +unsigned long c1, c2; + +bool tOn() { + c1 = millis(); + c2 = 0; + + return true; +} + +void tOff() { + c2 = millis(); + Serial.println("done."); + Serial.print("Tstart =");Serial.println(c1); + Serial.print("Tfinish=");Serial.println(c2); + Serial.print("Duration=");Serial.println(c2-c1); +} + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + Serial.print("Start..."); + + t.enable(); +} + +void callback() { + +} + + +void loop() { + // put your main code here, to run repeatedly: + ts.execute(); +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example11_Priority/Scheduler_example11_Priority.ino b/libraries/TaskScheduler-master/examples/Scheduler_example11_Priority/Scheduler_example11_Priority.ino new file mode 100644 index 0000000..ef260bc --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example11_Priority/Scheduler_example11_Priority.ino @@ -0,0 +1,105 @@ +/** + * This is a test of TaskScheduler layered priority funtionality + * + * Current test employs two priority layers: + * Base scheduler runs tasks t1, t2 and t3 + * High priority scheduler runs tasks t4 and t5 + * + * Sequence of task scheduling (not execution!) is: + * 4, 5, 1, 4, 5, 2, 4, 5, 3 = one base scheduler pass + * + * Scheduling overhead (at 20 micros per one pass) is: (B + B * H) * T = (3 + 3 * 2) * 18 = 162 micros + * where + * B - number of tasks in the base scheduler's chain + * H - number of tasks in the high priority scheduler's chain + * T - scheduling overhead for 1 pass (~15-18 microseconds) + * + * Actual task execution order: + +Scheduler Priority Test +Task: 40: 0 Start delay = 0 +Task: 50: 10 Start delay = 10 +Task: 1: 21 Start delay = 21 +Task: 2: 31 Start delay = 31 +Task: 3: 41 Start delay = 41 + +Task: 40: 500 Start delay = 0 +Task: 40: 1000 Start delay = 0 +Task: 50: 1010 Start delay = 10 +Task: 1: 1021 Start delay = 20 +Task: 40: 1500 Start delay = 0 +Task: 40: 2000 Start delay = 0 +Task: 50: 2011 Start delay = 11 +Task: 1: 2022 Start delay = 21 +Task: 2: 2032 Start delay = 32 +Task: 40: 2500 Start delay = 0 +Task: 40: 3000 Start delay = 0 +Task: 50: 3010 Start delay = 10 +Task: 1: 3021 Start delay = 20 +Task: 3: 3032 Start delay = 32 + +Task: 40: 3500 Start delay = 0 +Task: 40: 4000 Start delay = 0 +Task: 50: 4011 Start delay = 11 +Task: 1: 4022 Start delay = 21 +Task: 2: 4032 Start delay = 32 +Task: 40: 4500 Start delay = 0 +Task: 40: 5000 Start delay = 0 +Task: 50: 5010 Start delay = 10 +Task: 1: 5021 Start delay = 20 +Task: 40: 5500 Start delay = 0 +Task: 40: 6000 Start delay = 0 +Task: 50: 6010 Start delay = 10 +Task: 1: 6022 Start delay = 21 +Task: 2: 6032 Start delay = 32 +Task: 3: 6043 Start delay = 42 + + */ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_PRIORITY +#define _TASK_WDT_IDS +#define _TASK_TIMECRITICAL +#include + +Scheduler r, hpr; + +// Callback methods prototypes +void tCallback(); + +// Tasks +Task t1(1000, TASK_FOREVER, &tCallback, &r); //adding task to the chain on creation +Task t2(2000, TASK_FOREVER, &tCallback, &r); +Task t3(3000, TASK_FOREVER, &tCallback, &r); + +Task t4(500, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation +Task t5(1000, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation + +void tCallback() { + Scheduler &s = Scheduler::currentScheduler(); + Task &t = s.currentTask(); + + Serial.print("Task: "); Serial.print(t.getId());Serial.print(":\t"); + Serial.print(millis()); Serial.print("\tStart delay = "); Serial.println(t.getStartDelay()); + delay(10); + + if (t.getId() == 3) Serial.println(); +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler Priority Test"); + + t4.setId(40); + t5.setId(50); + + r.setHighPriorityScheduler(&hpr); + r.enableAll(true); // this will recursively enable the higher priority tasks as well +} + + +void loop () { + + r.execute(); + +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example12_Priority/Scheduler_example12_Priority.ino b/libraries/TaskScheduler-master/examples/Scheduler_example12_Priority/Scheduler_example12_Priority.ino new file mode 100644 index 0000000..799c4fa --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example12_Priority/Scheduler_example12_Priority.ino @@ -0,0 +1,136 @@ +/** + * This is a test of TaskScheduler layered priority funtionality + * + * Current test employs three priority layers: + * Base scheduler runs tasks t1, t2 and t3 + * High priority scheduler runs tasks t4 and t5 + * Highest priority scheduler runs tasks t6 and t7 + * + * Sequence of task scheduling (not execution!) is: + * 6, 7, 4, 6, 7, 5, 1, 6, 7, 4, 6, 7, 5, 2, 6, 7, 4, 6, 7, 5, 3 = one base scheduler pass + * + * Scheduling overhead (at 20 micros per one pass) is: (B + B * H + B * H * C) * T = (3 + 3 * 2 + 3 * 2 * 2) * 18 = 378 micros + * where + * B - number of tasks in the base scheduler's chain + * H - number of tasks in the high priority scheduler's chain + * C - number of tasks in the critical priority scheduler's chain + * T - scheduling overhead for 1 pass (~15-18 microseconds) + * + * Actual task execution order: + +Scheduler Priority Test +Task: 600: 0 Start delay = 0 +Task: 700: 10 Start delay = 10 +Task: 40: 21 Start delay = 21 +Task: 50: 31 Start delay = 31 +Task: 1: 43 Start delay = 41 +Task: 2: 53 Start delay = 53 +Task: 3: 63 Start delay = 63 + +Task: 600: 500 Start delay = 0 +Task: 40: 510 Start delay = 10 +Task: 600: 1000 Start delay = 0 +Task: 700: 1010 Start delay = 10 +Task: 40: 1021 Start delay = 21 +Task: 50: 1032 Start delay = 32 +Task: 1: 1043 Start delay = 43 +Task: 600: 1500 Start delay = 0 +Task: 40: 1510 Start delay = 10 +Task: 600: 2000 Start delay = 0 +Task: 700: 2011 Start delay = 11 +Task: 40: 2022 Start delay = 22 +Task: 50: 2032 Start delay = 32 +Task: 1: 2043 Start delay = 43 +Task: 2: 2054 Start delay = 54 +Task: 600: 2500 Start delay = 0 +Task: 40: 2510 Start delay = 10 +Task: 600: 3000 Start delay = 0 +Task: 700: 3010 Start delay = 10 +Task: 40: 3021 Start delay = 21 +Task: 50: 3032 Start delay = 32 +Task: 1: 3043 Start delay = 43 +Task: 3: 3053 Start delay = 53 + +Task: 600: 3500 Start delay = 0 +Task: 40: 3510 Start delay = 10 +Task: 600: 4000 Start delay = 0 +Task: 700: 4011 Start delay = 11 +Task: 40: 4022 Start delay = 22 +Task: 50: 4032 Start delay = 32 +Task: 1: 4043 Start delay = 43 +Task: 2: 4054 Start delay = 54 +Task: 600: 4500 Start delay = 0 +Task: 40: 4510 Start delay = 10 +Task: 600: 5000 Start delay = 0 +Task: 700: 5010 Start delay = 10 +Task: 40: 5021 Start delay = 21 +Task: 50: 5031 Start delay = 31 +Task: 1: 5043 Start delay = 43 +Task: 600: 5500 Start delay = 0 +Task: 40: 5511 Start delay = 11 +Task: 600: 6000 Start delay = 0 +Task: 700: 6010 Start delay = 10 +Task: 40: 6022 Start delay = 22 +Task: 50: 6032 Start delay = 32 +Task: 1: 6043 Start delay = 43 +Task: 2: 6053 Start delay = 53 +Task: 3: 6065 Start delay = 65 + + */ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_PRIORITY +#define _TASK_WDT_IDS +#define _TASK_TIMECRITICAL +#include + +Scheduler r; +Scheduler hpr; +Scheduler cpr; + +// Callback methods prototypes +void tCallback(); + +// Tasks +Task t1(1000, TASK_FOREVER, &tCallback, &r); //adding task to the chain on creation +Task t2(2000, TASK_FOREVER, &tCallback, &r); +Task t3(3000, TASK_FOREVER, &tCallback, &r); + +Task t4(500, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation +Task t5(1000, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation + +Task t6(500, TASK_FOREVER, &tCallback, &cpr); //adding task to the chain on creation +Task t7(1000, TASK_FOREVER, &tCallback, &cpr); //adding task to the chain on creation + +void tCallback() { + Scheduler &s = Scheduler::currentScheduler(); + Task &t = s.currentTask(); + + Serial.print("Task: "); Serial.print(t.getId());Serial.print(":\t"); + Serial.print(millis()); Serial.print("\tStart delay = "); Serial.println(t.getStartDelay()); + delay(10); + + if (t.getId() == 3) Serial.println(); +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler Priority Test"); + + t4.setId(40); + t5.setId(50); + + t6.setId(600); + t7.setId(700); + + r.setHighPriorityScheduler(&hpr); + hpr.setHighPriorityScheduler(&cpr); + r.enableAll(true); // this will recursively enable the higher priority tasks as well +} + + +void loop () { + + r.execute(); + +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino b/libraries/TaskScheduler-master/examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino new file mode 100644 index 0000000..ea5dd86 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino @@ -0,0 +1,71 @@ +/** + * TaskScheduler Test of microsecond scheduling resolution + * + * Task 1 runs starting with 211 microseconds intervals, doubling the interval on every iteration + * until it wraps when interval reaches about 72 minutes mark + * + * Task 2 provides heartbeat at a steady 5 seconds intervals + * + */ + +#define _TASK_MICRO_RES +#include + +#define T1_INIT (211L) + +Scheduler runner; +// Callback methods prototypes +void t1Callback(); +void t1OnDisable(); +void t2Callback(); + + +unsigned long t1_interval = T1_INIT; + +// Tasks +Task t1(t1_interval, 1, &t1Callback, &runner, true, NULL, &t1OnDisable); //adding task to the chain on creation +Task t2(5 * TASK_SECOND, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation + + + +void t1Callback() { + unsigned long t = micros(); + Serial.print("t1: "); + Serial.println(t); +} + +void t1OnDisable() { + t1_interval += t1_interval; + if (t1_interval < T1_INIT) t1_interval = T1_INIT; + t1.setInterval(t1_interval); + t1.restartDelayed(); +} + +void t2Callback() { + unsigned long t = micros(); + Serial.print("t2: "); + Serial.print(t); + Serial.println(" heartbeat"); +} + + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler TEST Microsecond Resolution"); + + Serial.println("5 seconds delay"); + delay(5000); + + runner.startNow(); // This creates a new scheduling starting point for all ACTIVE tasks. + // PLEASE NOTE - THIS METHOD DOES NOT ACTIVATE TASKS, JUST RESETS THE START TIME + t1.delay(); // Tasks which need to start delayed, need to be delayed again after startNow(); + +// Alternatively, tasks should be just enabled at the bottom of setup() method +// runner.enableAll(); +// t1.delay(); +} + + +void loop () { + runner.execute(); +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example14_Yield/Scheduler_example14_Yield.ino b/libraries/TaskScheduler-master/examples/Scheduler_example14_Yield/Scheduler_example14_Yield.ino new file mode 100644 index 0000000..50aa109 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example14_Yield/Scheduler_example14_Yield.ino @@ -0,0 +1,339 @@ +/** + This test illustrates the use if yield methods and internal StatusRequest objects + THIS TEST HAS BEEN TESTED ON NODEMCU V.2 (ESP8266) + + The WiFi initialization and NTP update is executed in parallel to blinking the onboard LED + and an external LED connected to D2 (GPIO04) + Try running with and without correct WiFi parameters to observe the difference in behaviour +*/ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STATUS_REQUEST +#include + +#include +#include + +Scheduler ts; + +// Callback methods prototypes +void connectInit(); +void ledCallback(); +bool ledOnEnable(); +void ledOnDisable(); +void ledOn(); +void ledOff(); +void ntpUpdateInit(); + +// Tasks + +Task tConnect (TASK_SECOND, TASK_FOREVER, &connectInit, &ts, true); +Task tLED (TASK_IMMEDIATE, TASK_FOREVER, &ledCallback, &ts, false, &ledOnEnable, &ledOnDisable); + +// Tasks running on events +Task tNtpUpdate (&ntpUpdateInit, &ts); + +// Replace with WiFi parameters of your Access Point/Router: +const char *ssid = "wifi_network"; +const char *pwd = "wifi_password"; + +long ledDelayOn, ledDelayOff; + +#define LEDPIN D0 // Onboard LED pin - linked to WiFi +#define LEDPIN2 D2 // External LED +#define CONNECT_TIMEOUT 30 // Seconds +#define CONNECT_OK 0 // Status of successful connection to WiFi +#define CONNECT_FAILED (-99) // Status of failed connection to WiFi + +// NTP Related Definitions +#define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes of the message + +IPAddress timeServerIP; // time.nist.gov NTP server address +const char* ntpServerName = "time.nist.gov"; +byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets +unsigned long epoch; + +WiFiUDP udp; // A UDP instance to let us send and receive packets over UDP + +#define LOCAL_NTP_PORT 2390 // Local UDP port for NTP update + + + +void setup() { + Serial.begin(74880); + Serial.println(F("TaskScheduler test #14 - Yield and internal StatusRequests")); + Serial.println(F("==========================================================")); + Serial.println(); + + pinMode (LEDPIN, OUTPUT); + pinMode (LEDPIN2, OUTPUT); + + tNtpUpdate.waitFor( tConnect.getInternalStatusRequest() ); // NTP Task will start only after connection is made +} + +void loop() { + ts.execute(); // Only Scheduler should be executed in the loop +} + +/** + Initiate connection to the WiFi network +*/ +void connectInit() { + Serial.print(millis()); + Serial.println(F(": connectInit.")); + Serial.println(F("WiFi parameters: ")); + Serial.print(F("SSID: ")); Serial.println(ssid); + Serial.print(F("PWD : ")); Serial.println(pwd); + + WiFi.mode(WIFI_STA); + WiFi.hostname("esp8266"); + WiFi.begin(ssid, pwd); + yield(); + + ledDelayOn = TASK_SECOND / 2; + ledDelayOff = TASK_SECOND / 4; + tLED.enable(); + + tConnect.yield(&connectCheck); // This will pass control back to Scheduler and then continue with connection checking +} + +/** + Periodically check if connected to WiFi + Re-request connection every 5 seconds + Stop trying after a timeout +*/ +void connectCheck() { + Serial.print(millis()); + Serial.println(F(": connectCheck.")); + + if (WiFi.status() == WL_CONNECTED) { // Connection established + Serial.print(millis()); + Serial.print(F(": Connected to AP. Local ip: ")); + Serial.println(WiFi.localIP()); + tConnect.disable(); + } + else { + + if (tConnect.getRunCounter() % 5 == 0) { // re-request connection every 5 seconds + + Serial.print(millis()); + Serial.println(F(": Re-requesting connection to AP...")); + + WiFi.disconnect(true); + yield(); // This is an esp8266 standard yield to allow linux wifi stack run + WiFi.hostname("esp8266"); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, pwd); + yield(); // This is an esp8266 standard yield to allow linux wifi stack run + } + + if (tConnect.getRunCounter() == CONNECT_TIMEOUT) { // Connection Timeout + tConnect.getInternalStatusRequest()->signal(CONNECT_FAILED); // Signal unsuccessful completion + tConnect.disable(); + + Serial.print(millis()); + Serial.println(F(": connectOnDisable.")); + Serial.print(millis()); + Serial.println(F(": Unable to connect to WiFi.")); + + ledDelayOn = TASK_SECOND / 16; // Blink LEDs quickly due to error + ledDelayOff = TASK_SECOND / 16; + tLED.enable(); + } + } +} + +/** + Initiate NTP update if connection was established +*/ +void ntpUpdateInit() { + Serial.print(millis()); + Serial.println(F(": ntpUpdateInit.")); + + if ( tConnect.getInternalStatusRequest()->getStatus() != CONNECT_OK ) { // Check status of the Connect Task + Serial.print(millis()); + Serial.println(F(": cannot update NTP - not connected.")); + return; + } + + udp.begin(LOCAL_NTP_PORT); + if ( WiFi.hostByName(ntpServerName, timeServerIP) ) { //get a random server from the pool + + Serial.print(millis()); + Serial.print(F(": timeServerIP = ")); + Serial.println(timeServerIP); + + sendNTPpacket(timeServerIP); // send an NTP packet to a time server + } + else { + Serial.print(millis()); + Serial.println(F(": NTP server address lookup failed.")); + tLED.disable(); + udp.stop(); + tNtpUpdate.disable(); + return; + } + + ledDelayOn = TASK_SECOND / 8; + ledDelayOff = TASK_SECOND / 8; + tLED.enable(); + + tNtpUpdate.set( TASK_SECOND, CONNECT_TIMEOUT, &ntpCheck ); + tNtpUpdate.enableDelayed(); +} + +/** + * Check if NTP packet was received + * Re-request every 5 seconds + * Stop trying after a timeout + */ +void ntpCheck() { + Serial.print(millis()); + Serial.println(F(": ntpCheck.")); + + if ( tNtpUpdate.getRunCounter() % 5 == 0) { + + Serial.print(millis()); + Serial.println(F(": Re-requesting NTP update...")); + + udp.stop(); + yield(); + udp.begin(LOCAL_NTP_PORT); + sendNTPpacket(timeServerIP); + return; + } + + if ( doNtpUpdateCheck()) { + Serial.print(millis()); + Serial.println(F(": NTP Update successful")); + + Serial.print(millis()); + Serial.print(F(": Unix time = ")); + Serial.println(epoch); + + tLED.disable(); + tNtpUpdate.disable(); + udp.stop(); + } + else { + if ( tNtpUpdate.isLastIteration() ) { + Serial.print(millis()); + Serial.println(F(": NTP Update failed")); + tLED.disable(); + udp.stop(); + } + } +} + +/** + * Send NTP packet to NTP server + */ +unsigned long sendNTPpacket(IPAddress & address) +{ + Serial.print(millis()); + Serial.println(F(": sendNTPpacket.")); + + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + + // all NTP fields have been given values, now + // you can send a packet requesting a timestamp: + udp.beginPacket(address, 123); //NTP requests are to port 123 + udp.write(packetBuffer, NTP_PACKET_SIZE); + udp.endPacket(); + yield(); +} + +/** + * Check if a packet was recieved. + * Process NTP information if yes + */ +bool doNtpUpdateCheck() { + + Serial.print(millis()); + Serial.println(F(": doNtpUpdateCheck.")); + + yield(); + int cb = udp.parsePacket(); + if (cb) { + // We've received a packet, read the data from it + udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer + + //the timestamp starts at byte 40 of the received packet and is four bytes, + // or two words, long. First, esxtract the two words: + + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + // now convert NTP time into everyday time: + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + // subtract seventy years: + epoch = secsSince1900 - seventyYears; + return (epoch != 0); + } + return false; +} + +/** + * Flip the LED state based on the current state + */ +bool ledState; +void ledCallback() { + if ( ledState ) ledOff(); + else ledOn(); +} + +/** + * Make sure the LED starts lit + */ +bool ledOnEnable() { + ledOn(); + return true; +} + +/** + * Make sure LED ends dimmed + */ +void ledOnDisable() { + ledOff(); +} + +/** + * Turn LEDs on. + * Set appropriate delay. + * PLEASE NOTE: NodeMCU onbaord LED is active-low + */ +void ledOn() { + ledState = true; + digitalWrite(LEDPIN, LOW); + digitalWrite(LEDPIN2, HIGH); + tLED.delay( ledDelayOn ); +} + +/** + * Turn LEDs off. + * Set appropriate delay. + * PLEASE NOTE: NodeMCU onbaord LED is active-low + */ +void ledOff() { + ledState = false; + digitalWrite(LEDPIN, HIGH); + digitalWrite(LEDPIN2, LOW); + tLED.delay( ledDelayOff ); +} + diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example15_STDFunction/Scheduler_example15_STDFunction.ino b/libraries/TaskScheduler-master/examples/Scheduler_example15_STDFunction/Scheduler_example15_STDFunction.ino new file mode 100644 index 0000000..cd4f2ac --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example15_STDFunction/Scheduler_example15_STDFunction.ino @@ -0,0 +1,53 @@ +/** + * TaskScheduler Test sketch - Showing how to use std::function + * to get acces to variables from within the task callback function + * + * Support for std::function is only available for ESP8266 architecture + */ +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STD_FUNCTION // Compile with support for std::function +#include + +Scheduler ts; +int counter = 0; + +class Calculator { +public: + int cumSum = 0; // cumulative sum + Calculator(int b) { + // Pass the this pointer, so that we get access to this->cumSum + // Also pass a copy of b + calculateTask.set(TASK_SECOND, TASK_FOREVER, [this, b]() { + counter++; + Serial.printf("%u. %u: cumSum = %u + %u\t", counter, millis(), cumSum, b); + cumSum += b; + Serial.printf("Resulting cumulative sum: %u\n", cumSum); + }); + ts.addTask(calculateTask); + calculateTask.enable(); + } + + Task calculateTask; +}; + +Calculator calc1(2); +Calculator calc2(4); +Calculator calc3(8); + +// Disable tasks after 10 seconds +Task tWrapper(10*TASK_SECOND, TASK_ONCE, []() { + ts.disableAll(); +}, &ts); + +/** + * Standard Arduino setup and loop methods + */ +void setup() { + Serial.begin(74880); + Serial.println("std::function test"); + tWrapper.enableDelayed(); +} + +void loop() { + ts.execute(); +} \ No newline at end of file diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/Scheduler_example16_Multitab.ino b/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/Scheduler_example16_Multitab.ino new file mode 100644 index 0000000..a92becd --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/Scheduler_example16_Multitab.ino @@ -0,0 +1,18 @@ +//This file is intentionally left blank. +// +//Arduino IDE plays some dirty tricks on the main sketch .ino file: +//it rearranges #includes, blindly creates forward definitions, +//includes every file in the project that does not have .c or .cpp +//file extension. +// +//Usually it all turns well if you have only one source file and you are either +//inexperienced or really expert C++ Arduino programmer. +//For the folks with the middle ground skills level, when you want +//to split your code into several .cpp files, it is best to leave +//this main sketch empty. +// +//It doesn't matter where you define the void loop() and void setup(). +//Just make sure there is exactly one definition of each. +// +//And if you want to use standard Arduino functions +//like digitalWrite or the Serial object - just add #include. diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/file1.cpp b/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/file1.cpp new file mode 100644 index 0000000..03e49cb --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/file1.cpp @@ -0,0 +1,31 @@ +#include +#include "header.hpp" + + +//Declare the functions we want to use before we are ready to define them +void t1Callback(); + + +// Tasks +Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation +Task t3(5000, TASK_FOREVER, &t3Callback); + + +void t1Callback() { + Serial.print("t1: "); + Serial.println(millis()); + + if (t1.isFirstIteration()) { + runner.addTask(t3); + t3.enable(); + Serial.println("t1: enabled t3 and added to the chain"); + } + + if (t1.isLastIteration()) { + t3.disable(); + runner.deleteTask(t3); + t2.setInterval(500); + Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); + } +} + diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/file2.cpp b/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/file2.cpp new file mode 100644 index 0000000..3a687a5 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/file2.cpp @@ -0,0 +1,55 @@ +// Test the same as example#2: +// Initially only tasks 1 and 2 are enabled +// Task1 runs every 2 seconds 10 times and then stops +// Task2 runs every 3 seconds indefinitely +// Task1 enables Task3 at its first run +// Task3 run every 5 seconds +// loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected) +// Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds +// Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now +// At the end Task2 is the only task running every 1/2 seconds + + +//Header that declares all shared objects between .cpp files +#include "header.hpp" + +#include //for Serial and delay + +Scheduler runner; //Let the scheduler live here, in the main file, ok? + + +//Pretend, that the t2 task is a special task, +//that needs to live in file2 object file. +void t2Callback() { + Serial.print("t2: "); + Serial.println(millis()); +} +Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true); + +//Lets define t3Callback here. We are going to use it in file1 +//for Task 1. +void t3Callback() { + Serial.print("t3: "); + Serial.println(millis()); +} + + + + + + +void setup () { + Serial.begin(115200); + delay(5000); + Serial.println("Scheduler TEST (multi-tab)"); + + runner.startNow(); // set point-in-time for scheduling start +} + + +void loop () { + runner.execute(); + +// Serial.println("Loop ticks at: "); +// Serial.println(millis()); +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/header.hpp b/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/header.hpp new file mode 100644 index 0000000..a3cf633 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/header.hpp @@ -0,0 +1,15 @@ +//This is the place to declare every single function +//and global variable that is going to be reused between cpp files. + + +//We are going to use the TaskScheduler, but only the declarations part. +//Remember to put customization macros before the #include: +#define _TASK_SLEEP_ON_IDLE_RUN +#include + +//Let the runner object be a global, single instance shared between object files. +extern Scheduler runner; +extern Task t2; //the t2 is defined in file2, but we need to access it from file1. + +//This function needs to be shared (between file2 and file1). +void t3Callback(); diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/ts.cpp b/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/ts.cpp new file mode 100644 index 0000000..c7cac8f --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example16_Multitab/ts.cpp @@ -0,0 +1,20 @@ +//This is the only .cpp file that gets the #include. +//Without it, the linker would not find necessary TaskScheduler's compiled code. +// +//Remember to put customization macros here as well. +// +//And don't import any common headers (here: header.hpp) +// +//Really. This file needs to be short. All stuff is in TaskScheduler.h. + +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer +// #define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) +// #define _TASK_DEBUG // Make all methods and variables public for debug purposes + +#include diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example17_Timeout/Scheduler_example17_Timeout.ino b/libraries/TaskScheduler-master/examples/Scheduler_example17_Timeout/Scheduler_example17_Timeout.ino new file mode 100644 index 0000000..60392ba --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example17_Timeout/Scheduler_example17_Timeout.ino @@ -0,0 +1,90 @@ +/* + This eaxmple illustrates the use of overall Task timeout functionality: + + Task 1 - runs every 1 seconds and times out in 10 seconds + Task 2 - runs every 5 seconds and resets the timeout every run, so runs continuosly even though the timeout is set to 10 seconds +*/ + + +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +//#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer +// #define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) +// #define _TASK_DEBUG // Make all methods and variables public for debug purposes +// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +#define _TASK_TIMEOUT + +#include + +Scheduler ts; + +void task1Callback(); +void task1OnDisable(); +void task2Callback(); +void task2OnDisable(); + +Task t1(1 * TASK_SECOND, TASK_FOREVER, &task1Callback, &ts, false, NULL, &task1OnDisable); +Task t2(5 * TASK_SECOND, TASK_FOREVER, &task2Callback, &ts, false, NULL, &task2OnDisable); + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + + Serial.println("TaskScheduler Timeout example"); + Serial.println("============================="); + + t1.setTimeout(10 * TASK_SECOND); + t2.setTimeout(10 * TASK_SECOND); + + ts.enableAll(); +} + +void loop() { + // put your main code here, to run repeatedly: + ts.execute(); +} + + +void task1Callback() { + Serial.print("Task 1:\t"); + Serial.print(millis()); + Serial.print(": t/out="); + Serial.print(t1.getTimeout()); + Serial.print("\tms until t/out="); + Serial.println( t1.untilTimeout()); + +} +void task1OnDisable() { + if (t1.timedOut()) { + Serial.println("Task 1 has timed out. Restarting"); + t1.setInterval(1 * TASK_SECOND); + t1.setIterations(15); + t1.setTimeout(TASK_NOTIMEOUT); + t1.enable(); + } + else { + Serial.println("Task 1 has been disabled"); + } +} + +void task2Callback() { + Serial.print("Task 2:\t"); + Serial.print(millis()); + Serial.print(": t/out="); + Serial.print(t2.getTimeout()); + Serial.print("\tms until t/out="); + Serial.println( t2.untilTimeout()); + t2.resetTimeout(); +} +void task2OnDisable() { + if (t2.timedOut()) { + Serial.println("Task 2 has timed out"); + } + else { + Serial.println("Task 2 has been disabled"); + } +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example18_StatusRequest_LTS_WDT_Timeout/Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino b/libraries/TaskScheduler-master/examples/Scheduler_example18_StatusRequest_LTS_WDT_Timeout/Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino new file mode 100644 index 0000000..c17fed6 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example18_StatusRequest_LTS_WDT_Timeout/Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino @@ -0,0 +1,172 @@ +/** + This is example 5 rewritten with Timeout, LTS and WDT functioanlity: + - 1 second timeout is set for the main calculation task + - LTS is used to address individual array elements for each sensor sinlce the callback code is shared + - WDT is used to set the Task ID and use that as an index for array of distances (alternative to LTS) + + Original description: + ==================== + This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay + (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values. + The overall timeout of 1 second is setup as well. + An error message needs to be printed if a timeout occurred instead of a value. + + Example5: + Sketch uses 6066 bytes (18%) of program storage space. Maximum is 32256 bytes. + Global variables use 1039 bytes (50%) of dynamic memory, leaving 1009 bytes for local variables. Maximum is 2048 bytes. + Example 18: + Sketch uses 5142 bytes (15%) of program storage space. Maximum is 32256 bytes. + Global variables use 878 bytes (42%) of dynamic memory, leaving 1170 bytes for local variables. Maximum is 2048 bytes. +*/ + +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +#define _TASK_LTS_POINTER // Compile with support for local task storage pointer +#define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) +#define _TASK_DEBUG // Make all methods and variables public for debug purposes +#define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +#define _TASK_TIMEOUT // Support for overall task timeout + +#include + +StatusRequest measure; + +Scheduler ts, hts; + +// Callback methods prototypes +void CycleCallback(); +void CalcCallback(); +bool CalcEnable(); +void CalcDisable(); +void SCallback(); bool SEnable(); + +// Tasks +Task tSensor1(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 1 +Task tSensor2(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 2 +Task tSensor3(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 3 + +Task tCycle(10000, TASK_FOREVER, &CycleCallback, &hts); +Task tCalculate(TASK_IMMEDIATE , TASK_ONCE, &CalcCallback, &hts, false, &CalcEnable, &CalcDisable); + +#define NO_OF_SENSORS 3 +long distance, d[NO_OF_SENSORS + 1], d_lts[NO_OF_SENSORS]; // d[] will be populated via task ID used as array indexes, d_lts will be addressed via LTS pointers + +void CycleCallback() { + Serial.println(); + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); + + distance = 0; + measure.setWaiting(NO_OF_SENSORS); // Set the StatusRequest to wait for 3 signals. + tCalculate.waitFor(&measure); +} + +bool CalcEnable() { + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CalcEnable: OnEnable"); + Serial.println("Activating sensors and setting timeout"); + + tSensor1.restartDelayed(); + tSensor2.restartDelayed(); + tSensor3.restartDelayed(); + + return true; +} + +void CalcDisable() { + if (tCalculate.timedOut()) { + measure.signalComplete(-1); // signal error + Serial.print(millis()); Serial.print(":\t"); + Serial.println("MeasureCallback: ***** Timeout *****"); + // tSensor1.disable(); + // tSensor2.disable(); + // tSensor3.disable(); + } +} + + +void CalcCallback() { + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CalcCallback: calculating"); + distance = -1; + if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully + distance = d[1] < d[2] ? d[1] : d[2]; + distance = d[3] < distance ? d[3] : distance; + Serial.print("CalcCallback: Min distance="); Serial.println(distance); + Serial.println(); + } +} + + +/** Simulation code for all sensors + ------------------------------- +*/ +bool SEnable() { + Task &t = ts.currentTask(); + int i = t.getId(); + + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SEnable: TaskID="); + Serial.println(i); + Serial.print("Triggering sensor. Delay="); + + t.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout + // One way to update the 3 distances with one codebase - use task id as an index + d[i] = 0; + + // Another way to update the 3 distances with one codebase - use LTS pointers + int *pd = (int*) t.getLtsPointer(); + *pd = 0; + + Serial.println( t.getInterval() ); + return true; +} + +void SCallback() { + Task &t = ts.currentTask(); + int i = t.getId(); + + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SCallback: TaskID="); + Serial.println(i); + Serial.print("Emulating measurement. d="); + + d[i] = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement + int *pd = (int*) t.getLtsPointer(); + *pd = d[i]; + + measure.signal(); + + Serial.print(d[i]); + Serial.print("\t"); + Serial.println(*pd); +} + +/** Main Arduino code + Not much is left here - everything is taken care of by the framework +*/ +void setup() { + + Serial.begin(115200); + Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); + randomSeed(analogRead(A0) + millis()); + + tSensor1.setLtsPointer(&d_lts[0]); + tSensor2.setLtsPointer(&d_lts[1]); + tSensor3.setLtsPointer(&d_lts[2]); + + ts.setHighPriorityScheduler(&hts); + + tCalculate.setTimeout(1 * TASK_SECOND); + tCycle.enable(); +} + +void loop() { + + ts.execute(); + +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example19_Dynamic_Tasks/Scheduler_example19_Dynamic_Tasks.ino b/libraries/TaskScheduler-master/examples/Scheduler_example19_Dynamic_Tasks/Scheduler_example19_Dynamic_Tasks.ino new file mode 100644 index 0000000..222a8d2 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example19_Dynamic_Tasks/Scheduler_example19_Dynamic_Tasks.ino @@ -0,0 +1,540 @@ +/** + TaskScheduler Test sketch - test of Task destructor + Test case: + Main task runs every 100 milliseconds 100 times and in 50% cases generates a task object + which runs 1 to 10 times with 100 ms to 5 s interval, and then destroyed. + + This sketch uses a FreeMemory library: https://github.com/McNeight/MemoryFree +*/ + +#define _TASK_WDT_IDS // To enable task unique IDs +#define _TASK_SLEEP_ON_IDLE_RUN // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run +#define _TASK_LTS_POINTER // Compile with support for Local Task Storage pointer +#include + +#include + +Scheduler ts; + +// Callback methods prototypes +void MainLoop(); + +// Statis task +Task tMain(100*TASK_MILLISECOND, 100, MainLoop, &ts, true); + + +void Iteration(); +bool OnEnable(); +void OnDisable(); + +int noOfTasks = 0; + +void MainLoop() { + Serial.print(millis()); Serial.print("\t"); + Serial.print("MainLoop run: "); + int i = tMain.getRunCounter(); + Serial.print(i); Serial.print(F(".\t")); + + if ( random(0, 101) > 50 ) { // generate a new task only in 50% of cases + // Generating another task + long p = random(100, 5001); // from 100 ms to 5 seconds + long j = random(1, 11); // from 1 to 10 iterations) + Task *t = new Task(p, j, Iteration, &ts, false, OnEnable, OnDisable); + + Serial.print(F("Generated a new task:\t")); Serial.print(t->getId()); Serial.print(F("\tInt, Iter = \t")); + Serial.print(p); Serial.print(", "); Serial.print(j); Serial.print(F("\tFree mem=")); + Serial.print(freeMemory()); Serial.print(F("\tNo of tasks=")); Serial.println(++noOfTasks); + t->enable(); + } + else { + Serial.println(F("Skipped generating a task")); + } +} + + +void Iteration() { + Task &t = ts.currentTask(); + + Serial.print(millis()); Serial.print("\t"); + Serial.print("Task N"); Serial.print(t.getId()); Serial.print(F("\tcurrent iteration: ")); + int i = t.getRunCounter(); + Serial.println(i); +} + +bool OnEnable() { + // to-do: think of something to put in here. + return true; +} + +void OnDisable() { + Task *t = &ts.currentTask(); + unsigned int tid = t->getId(); + + delete t; + Serial.print(millis()); Serial.print("\t"); + Serial.print("Task N"); Serial.print(tid); Serial.print(F("\tfinished and destroyed.\tFree mem=")); + Serial.print(freeMemory());Serial.print(F("\tNo of tasks=")); Serial.println(--noOfTasks); +} + +/** + Standard Arduino setup and loop methods +*/ +void setup() { + Serial.begin(115200); + + randomSeed(analogRead(0) + analogRead(5)); + noOfTasks = 0; + + Serial.println(F("Dynamic Task Creation/Destruction Example")); + Serial.println(); + Serial.print(F("Free mem=")); + Serial.print(freeMemory()); Serial.print(F("\tNo of tasks=")); Serial.println(noOfTasks); + Serial.println(); +} + +void loop() { + ts.execute(); +} + + +/* Output on Arduino Uno: + * + * Compile: + +Sketch uses 5312 bytes (16%) of program storage space. Maximum is 32256 bytes. +Global variables use 282 bytes (13%) of dynamic memory, leaving 1766 bytes for local variables. Maximum is 2048 bytes. + + * Execution: + +Dynamic Task Creation/Destruction Example + +Free mem=1758 No of tasks=0 + +1 MainLoop run: 1. Generated a new task: 2 Int, Iter = 421, 3 Free mem=1701 No of tasks=1 +8 Task N2 current iteration: 1 +100 MainLoop run: 2. Generated a new task: 3 Int, Iter = 4099, 9 Free mem=1656 No of tasks=2 +102 Task N3 current iteration: 1 +200 MainLoop run: 3. Skipped generating a task +300 MainLoop run: 4. Generated a new task: 4 Int, Iter = 1795, 1 Free mem=1611 No of tasks=3 +302 Task N4 current iteration: 1 +305 Task N4 finished and destroyed. Free mem=1613 No of tasks=2 +400 MainLoop run: 5. Skipped generating a task +429 Task N2 current iteration: 2 +500 MainLoop run: 6. Skipped generating a task +600 MainLoop run: 7. Skipped generating a task +700 MainLoop run: 8. Generated a new task: 5 Int, Iter = 4623, 7 Free mem=1611 No of tasks=3 +702 Task N5 current iteration: 1 +800 MainLoop run: 9. Generated a new task: 6 Int, Iter = 4987, 4 Free mem=1566 No of tasks=4 +802 Task N6 current iteration: 1 +850 Task N2 current iteration: 3 +850 Task N2 finished and destroyed. Free mem=1568 No of tasks=3 +900 MainLoop run: 10. Generated a new task: 7 Int, Iter = 600, 4 Free mem=1566 No of tasks=4 +902 Task N7 current iteration: 1 +1000 MainLoop run: 11. Skipped generating a task +1100 MainLoop run: 12. Generated a new task: 8 Int, Iter = 2530, 1 Free mem=1521 No of tasks=5 +1102 Task N8 current iteration: 1 +1105 Task N8 finished and destroyed. Free mem=1523 No of tasks=4 +1200 MainLoop run: 13. Skipped generating a task +1300 MainLoop run: 14. Generated a new task: 9 Int, Iter = 2215, 7 Free mem=1521 No of tasks=5 +1302 Task N9 current iteration: 1 +1400 MainLoop run: 15. Skipped generating a task +1500 MainLoop run: 16. Skipped generating a task +1502 Task N7 current iteration: 2 +1600 MainLoop run: 17. Skipped generating a task +1700 MainLoop run: 18. Skipped generating a task +1800 MainLoop run: 19. Skipped generating a task +1900 MainLoop run: 20. Skipped generating a task +2000 MainLoop run: 21. Generated a new task: 10 Int, Iter = 189, 10 Free mem=1476 No of tasks=6 +2002 Task N10 current iteration: 1 +2100 MainLoop run: 22. Generated a new task: 11 Int, Iter = 2898, 9 Free mem=1431 No of tasks=7 +2102 Task N7 current iteration: 3 +2105 Task N11 current iteration: 1 +2191 Task N10 current iteration: 2 +2200 MainLoop run: 23. Generated a new task: 12 Int, Iter = 1691, 6 Free mem=1386 No of tasks=8 +2202 Task N12 current iteration: 1 +2300 MainLoop run: 24. Generated a new task: 13 Int, Iter = 1448, 7 Free mem=1341 No of tasks=9 +2304 Task N13 current iteration: 1 +2380 Task N10 current iteration: 3 +2400 MainLoop run: 25. Generated a new task: 14 Int, Iter = 3919, 7 Free mem=1296 No of tasks=10 +2403 Task N14 current iteration: 1 +2500 MainLoop run: 26. Generated a new task: 15 Int, Iter = 3745, 5 Free mem=1251 No of tasks=11 +2503 Task N15 current iteration: 1 +2569 Task N10 current iteration: 4 +2600 MainLoop run: 27. Skipped generating a task +2700 MainLoop run: 28. Skipped generating a task +2702 Task N7 current iteration: 4 +2702 Task N7 finished and destroyed. Free mem=1253 No of tasks=10 +2758 Task N10 current iteration: 5 +2800 MainLoop run: 29. Generated a new task: 16 Int, Iter = 2144, 1 Free mem=1251 No of tasks=11 +2803 Task N16 current iteration: 1 +2806 Task N16 finished and destroyed. Free mem=1253 No of tasks=10 +2900 MainLoop run: 30. Generated a new task: 17 Int, Iter = 4618, 10 Free mem=1251 No of tasks=11 +2904 Task N17 current iteration: 1 +2947 Task N10 current iteration: 6 +3000 MainLoop run: 31. Skipped generating a task +3100 MainLoop run: 32. Generated a new task: 18 Int, Iter = 2885, 6 Free mem=1206 No of tasks=12 +3103 Task N18 current iteration: 1 +3136 Task N10 current iteration: 7 +3200 MainLoop run: 33. Skipped generating a task +3300 MainLoop run: 34. Skipped generating a task +3325 Task N10 current iteration: 8 +3400 MainLoop run: 35. Skipped generating a task +3500 MainLoop run: 36. Generated a new task: 19 Int, Iter = 2250, 4 Free mem=1161 No of tasks=13 +3503 Task N19 current iteration: 1 +3514 Task N10 current iteration: 9 +3518 Task N9 current iteration: 2 +3600 MainLoop run: 37. Skipped generating a task +3700 MainLoop run: 38. Generated a new task: 20 Int, Iter = 1689, 7 Free mem=1116 No of tasks=14 +3703 Task N10 current iteration: 10 +3706 Task N20 current iteration: 1 +3709 Task N10 finished and destroyed. Free mem=1118 No of tasks=13 +3750 Task N13 current iteration: 2 +3800 MainLoop run: 39. Generated a new task: 21 Int, Iter = 2607, 5 Free mem=1116 No of tasks=14 +3803 Task N21 current iteration: 1 +3893 Task N12 current iteration: 2 +3900 MainLoop run: 40. Generated a new task: 22 Int, Iter = 1390, 6 Free mem=1071 No of tasks=15 +3903 Task N22 current iteration: 1 +4000 MainLoop run: 41. Generated a new task: 23 Int, Iter = 3340, 8 Free mem=1026 No of tasks=16 +4003 Task N23 current iteration: 1 +4100 MainLoop run: 42. Skipped generating a task +4200 MainLoop run: 43. Skipped generating a task +4201 Task N3 current iteration: 2 +4300 MainLoop run: 44. Skipped generating a task +4400 MainLoop run: 45. Generated a new task: 24 Int, Iter = 4083, 2 Free mem=981 No of tasks=17 +4403 Task N24 current iteration: 1 +4500 MainLoop run: 46. Generated a new task: 25 Int, Iter = 4510, 1 Free mem=936 No of tasks=18 +4503 Task N25 current iteration: 1 +4506 Task N25 finished and destroyed. Free mem=938 No of tasks=17 +4600 MainLoop run: 47. Generated a new task: 26 Int, Iter = 4782, 10 Free mem=936 No of tasks=18 +4603 Task N26 current iteration: 1 +4700 MainLoop run: 48. Generated a new task: 27 Int, Iter = 641, 6 Free mem=891 No of tasks=19 +4702 Task N27 current iteration: 1 +4800 MainLoop run: 49. Generated a new task: 28 Int, Iter = 695, 5 Free mem=846 No of tasks=20 +4802 Task N28 current iteration: 1 +4900 MainLoop run: 50. Generated a new task: 29 Int, Iter = 3520, 3 Free mem=801 No of tasks=21 +4903 Task N29 current iteration: 1 +5000 MainLoop run: 51. Generated a new task: 30 Int, Iter = 3091, 9 Free mem=756 No of tasks=22 +5002 Task N11 current iteration: 2 +5006 Task N30 current iteration: 1 +5100 MainLoop run: 52. Skipped generating a task +5198 Task N13 current iteration: 3 +5200 MainLoop run: 53. Skipped generating a task +5293 Task N22 current iteration: 2 +5300 MainLoop run: 54. Generated a new task: 31 Int, Iter = 4359, 9 Free mem=711 No of tasks=23 +5303 Task N31 current iteration: 1 +5325 Task N5 current iteration: 2 +5343 Task N27 current iteration: 2 +5392 Task N20 current iteration: 2 +5400 MainLoop run: 55. Generated a new task: 32 Int, Iter = 837, 4 Free mem=666 No of tasks=24 +5403 Task N32 current iteration: 1 +5497 Task N28 current iteration: 2 +5501 MainLoop run: 56. Generated a new task: 33 Int, Iter = 274, 8 Free mem=621 No of tasks=25 +5505 Task N33 current iteration: 1 +5584 Task N12 current iteration: 3 +5600 MainLoop run: 57. Generated a new task: 34 Int, Iter = 923, 7 Free mem=576 No of tasks=26 +5603 Task N34 current iteration: 1 +5700 MainLoop run: 58. Generated a new task: 35 Int, Iter = 1007, 8 Free mem=531 No of tasks=27 +5703 Task N35 current iteration: 1 +5732 Task N9 current iteration: 3 +5753 Task N19 current iteration: 2 +5778 Task N33 current iteration: 2 +5789 Task N6 current iteration: 2 +5800 MainLoop run: 59. Skipped generating a task +5900 MainLoop run: 60. Generated a new task: 36 Int, Iter = 608, 4 Free mem=486 No of tasks=28 +5903 Task N36 current iteration: 1 +5984 Task N27 current iteration: 3 +5988 Task N18 current iteration: 2 +6000 MainLoop run: 61. Generated a new task: 37 Int, Iter = 4043, 3 Free mem=441 No of tasks=29 +6003 Task N37 current iteration: 1 +6052 Task N33 current iteration: 3 +6100 MainLoop run: 62. Skipped generating a task +6192 Task N28 current iteration: 3 +6200 MainLoop run: 63. Skipped generating a task +6239 Task N32 current iteration: 2 +6248 Task N15 current iteration: 2 +6300 MainLoop run: 64. Skipped generating a task +6322 Task N14 current iteration: 2 +6326 Task N33 current iteration: 4 +6400 MainLoop run: 65. Skipped generating a task +6410 Task N21 current iteration: 2 +6500 MainLoop run: 66. Skipped generating a task +6510 Task N36 current iteration: 2 +6525 Task N34 current iteration: 2 +6600 MainLoop run: 67. Skipped generating a task +6600 Task N33 current iteration: 5 +6625 Task N27 current iteration: 4 +6646 Task N13 current iteration: 4 +6683 Task N22 current iteration: 3 +6700 MainLoop run: 68. Generated a new task: 38 Int, Iter = 1907, 9 Free mem=396 No of tasks=30 +6703 Task N38 current iteration: 1 +6709 Task N35 current iteration: 2 +6800 MainLoop run: 69. Skipped generating a task +6874 Task N33 current iteration: 6 +6887 Task N28 current iteration: 4 +6900 MainLoop run: 70. Generated a new task: 39 Int, Iter = 2697, 1 Free mem=351 No of tasks=31 +6903 Task N39 current iteration: 1 +6906 Task N39 finished and destroyed. Free mem=353 No of tasks=30 +7000 MainLoop run: 71. Generated a new task: 40 Int, Iter = 2849, 4 Free mem=351 No of tasks=31 +7003 Task N40 current iteration: 1 +7076 Task N32 current iteration: 3 +7081 Task N20 current iteration: 3 +7100 MainLoop run: 72. Skipped generating a task +7118 Task N36 current iteration: 3 +7148 Task N33 current iteration: 7 +7200 MainLoop run: 73. Skipped generating a task +7266 Task N27 current iteration: 5 +7275 Task N12 current iteration: 4 +7300 MainLoop run: 74. Generated a new task: 41 Int, Iter = 4466, 4 Free mem=306 No of tasks=32 +7303 Task N41 current iteration: 1 +7343 Task N23 current iteration: 2 +7400 MainLoop run: 75. Skipped generating a task +7422 Task N33 current iteration: 8 +7422 Task N33 finished and destroyed. Free mem=308 No of tasks=31 +7448 Task N34 current iteration: 3 +7500 MainLoop run: 76. Generated a new task: 42 Int, Iter = 2133, 3 Free mem=306 No of tasks=32 +7503 Task N42 current iteration: 1 +7522 Task N17 current iteration: 2 +7582 Task N28 current iteration: 5 +7582 Task N28 finished and destroyed. Free mem=308 No of tasks=31 +7600 MainLoop run: 77. Skipped generating a task +7700 MainLoop run: 78. Skipped generating a task +7716 Task N35 current iteration: 3 +7726 Task N36 current iteration: 4 +7726 Task N36 finished and destroyed. Free mem=353 No of tasks=30 +7800 MainLoop run: 79. Generated a new task: 43 Int, Iter = 4113, 6 Free mem=351 No of tasks=31 +7803 Task N43 current iteration: 1 +7898 Task N11 current iteration: 3 +7900 MainLoop run: 80. Skipped generating a task +7907 Task N27 current iteration: 6 +7907 Task N27 finished and destroyed. Free mem=353 No of tasks=30 +7913 Task N32 current iteration: 4 +7913 Task N32 finished and destroyed. Free mem=398 No of tasks=29 +7947 Task N9 current iteration: 4 +8000 MainLoop run: 81. Skipped generating a task +8003 Task N19 current iteration: 3 +8073 Task N22 current iteration: 4 +8093 Task N30 current iteration: 2 +8094 Task N13 current iteration: 5 +8100 MainLoop run: 82. Skipped generating a task +8200 MainLoop run: 83. Generated a new task: 44 Int, Iter = 3389, 1 Free mem=396 No of tasks=30 +8203 Task N44 current iteration: 1 +8206 Task N44 finished and destroyed. Free mem=398 No of tasks=29 +8300 MainLoop run: 84. Generated a new task: 45 Int, Iter = 3548, 10 Free mem=396 No of tasks=30 +8303 Task N3 current iteration: 3 +8306 Task N45 current iteration: 1 +8371 Task N34 current iteration: 4 +8400 MainLoop run: 85. Skipped generating a task +8422 Task N29 current iteration: 2 +8485 Task N24 current iteration: 2 +8485 Task N24 finished and destroyed. Free mem=398 No of tasks=29 +8500 MainLoop run: 86. Generated a new task: 46 Int, Iter = 4962, 2 Free mem=396 No of tasks=30 +8503 Task N46 current iteration: 1 +8600 MainLoop run: 87. Skipped generating a task +8609 Task N38 current iteration: 2 +8700 MainLoop run: 88. Skipped generating a task +8723 Task N35 current iteration: 4 +8770 Task N20 current iteration: 4 +8800 MainLoop run: 89. Skipped generating a task +8873 Task N18 current iteration: 3 +8900 MainLoop run: 90. Skipped generating a task +8966 Task N12 current iteration: 5 +9000 MainLoop run: 91. Skipped generating a task +9017 Task N21 current iteration: 3 +9100 MainLoop run: 92. Skipped generating a task +9200 MainLoop run: 93. Skipped generating a task +9294 Task N34 current iteration: 5 +9300 MainLoop run: 94. Skipped generating a task +9385 Task N26 current iteration: 2 +9400 MainLoop run: 95. Generated a new task: 47 Int, Iter = 3556, 9 Free mem=351 No of tasks=31 +9403 Task N47 current iteration: 1 +9463 Task N22 current iteration: 5 +9500 MainLoop run: 96. Generated a new task: 48 Int, Iter = 1226, 3 Free mem=306 No of tasks=32 +9503 Task N48 current iteration: 1 +9542 Task N13 current iteration: 6 +9600 MainLoop run: 97. Generated a new task: 49 Int, Iter = 2850, 9 Free mem=261 No of tasks=33 +9603 Task N49 current iteration: 1 +9635 Task N42 current iteration: 2 +9661 Task N31 current iteration: 2 +9700 MainLoop run: 98. Skipped generating a task +9730 Task N35 current iteration: 5 +9800 MainLoop run: 99. Generated a new task: 50 Int, Iter = 2782, 10 Free mem=216 No of tasks=34 +9803 Task N50 current iteration: 1 +9851 Task N40 current iteration: 2 +9900 MainLoop run: 100. Skipped generating a task +9948 Task N5 current iteration: 3 +9993 Task N15 current iteration: 3 +10045 Task N37 current iteration: 2 +10162 Task N9 current iteration: 5 +10217 Task N34 current iteration: 6 +10241 Task N14 current iteration: 3 +10253 Task N19 current iteration: 4 +10253 Task N19 finished and destroyed. Free mem=218 No of tasks=33 +10459 Task N20 current iteration: 5 +10516 Task N38 current iteration: 3 +10657 Task N12 current iteration: 6 +10657 Task N12 finished and destroyed. Free mem=263 No of tasks=32 +10683 Task N23 current iteration: 3 +10728 Task N48 current iteration: 2 +10737 Task N35 current iteration: 6 +10776 Task N6 current iteration: 3 +10796 Task N11 current iteration: 4 +10853 Task N22 current iteration: 6 +10853 Task N22 finished and destroyed. Free mem=308 No of tasks=31 +10990 Task N13 current iteration: 7 +10990 Task N13 finished and destroyed. Free mem=353 No of tasks=30 +11140 Task N34 current iteration: 7 +11140 Task N34 finished and destroyed. Free mem=398 No of tasks=29 +11184 Task N30 current iteration: 3 +11624 Task N21 current iteration: 4 +11744 Task N35 current iteration: 7 +11758 Task N18 current iteration: 4 +11768 Task N41 current iteration: 2 +11768 Task N42 current iteration: 3 +11769 Task N42 finished and destroyed. Free mem=443 No of tasks=28 +11851 Task N45 current iteration: 2 +11915 Task N43 current iteration: 2 +11942 Task N29 current iteration: 3 +11942 Task N29 finished and destroyed. Free mem=488 No of tasks=27 +11954 Task N48 current iteration: 3 +11954 Task N48 finished and destroyed. Free mem=533 No of tasks=26 +12140 Task N17 current iteration: 3 +12148 Task N20 current iteration: 6 +12377 Task N9 current iteration: 6 +12399 Task N3 current iteration: 4 +12423 Task N38 current iteration: 4 +12452 Task N49 current iteration: 2 +12585 Task N50 current iteration: 2 +12700 Task N40 current iteration: 3 +12751 Task N35 current iteration: 8 +12751 Task N35 finished and destroyed. Free mem=578 No of tasks=25 +12958 Task N47 current iteration: 2 +13464 Task N46 current iteration: 2 +13464 Task N46 finished and destroyed. Free mem=623 No of tasks=24 +13694 Task N11 current iteration: 5 +13739 Task N15 current iteration: 4 +13837 Task N20 current iteration: 7 +13837 Task N20 finished and destroyed. Free mem=668 No of tasks=23 +14020 Task N31 current iteration: 3 +14023 Task N23 current iteration: 4 +14088 Task N37 current iteration: 3 +14088 Task N37 finished and destroyed. Free mem=713 No of tasks=22 +14160 Task N14 current iteration: 4 +14167 Task N26 current iteration: 3 +14231 Task N21 current iteration: 5 +14231 Task N21 finished and destroyed. Free mem=758 No of tasks=21 +14275 Task N30 current iteration: 4 +14330 Task N38 current iteration: 5 +14571 Task N5 current iteration: 4 +14592 Task N9 current iteration: 7 +14592 Task N9 finished and destroyed. Free mem=803 No of tasks=20 +14643 Task N18 current iteration: 5 +15302 Task N49 current iteration: 3 +15367 Task N50 current iteration: 3 +15399 Task N45 current iteration: 3 +15549 Task N40 current iteration: 4 +15549 Task N40 finished and destroyed. Free mem=848 No of tasks=19 +15763 Task N6 current iteration: 4 +15763 Task N6 finished and destroyed. Free mem=893 No of tasks=18 +16028 Task N43 current iteration: 3 +16234 Task N41 current iteration: 3 +16237 Task N38 current iteration: 6 +16498 Task N3 current iteration: 5 +16514 Task N47 current iteration: 3 +16592 Task N11 current iteration: 6 +16758 Task N17 current iteration: 4 +17363 Task N23 current iteration: 5 +17366 Task N30 current iteration: 5 +17483 Task N15 current iteration: 5 +17483 Task N15 finished and destroyed. Free mem=938 No of tasks=17 +17528 Task N18 current iteration: 6 +17528 Task N18 finished and destroyed. Free mem=983 No of tasks=16 +18079 Task N14 current iteration: 5 +18144 Task N38 current iteration: 7 +18149 Task N50 current iteration: 4 +18152 Task N49 current iteration: 4 +18379 Task N31 current iteration: 4 +18947 Task N45 current iteration: 4 +18949 Task N26 current iteration: 4 +19194 Task N5 current iteration: 5 +19490 Task N11 current iteration: 7 +20051 Task N38 current iteration: 8 +20070 Task N47 current iteration: 4 +20141 Task N43 current iteration: 4 +20457 Task N30 current iteration: 6 +20597 Task N3 current iteration: 6 +20700 Task N41 current iteration: 4 +20700 Task N41 finished and destroyed. Free mem=1028 No of tasks=15 +20703 Task N23 current iteration: 6 +20931 Task N50 current iteration: 5 +21002 Task N49 current iteration: 5 +21376 Task N17 current iteration: 5 +21958 Task N38 current iteration: 9 +21958 Task N38 finished and destroyed. Free mem=1073 No of tasks=14 +21998 Task N14 current iteration: 6 +22388 Task N11 current iteration: 8 +22495 Task N45 current iteration: 5 +22738 Task N31 current iteration: 5 +23548 Task N30 current iteration: 7 +23626 Task N47 current iteration: 5 +23713 Task N50 current iteration: 6 +23731 Task N26 current iteration: 5 +23817 Task N5 current iteration: 6 +23852 Task N49 current iteration: 6 +24043 Task N23 current iteration: 7 +24254 Task N43 current iteration: 5 +24696 Task N3 current iteration: 7 +25286 Task N11 current iteration: 9 +25286 Task N11 finished and destroyed. Free mem=1118 No of tasks=13 +25917 Task N14 current iteration: 7 +25917 Task N14 finished and destroyed. Free mem=1163 No of tasks=12 +25994 Task N17 current iteration: 6 +26043 Task N45 current iteration: 6 +26496 Task N50 current iteration: 7 +26639 Task N30 current iteration: 8 +26702 Task N49 current iteration: 7 +27097 Task N31 current iteration: 6 +27182 Task N47 current iteration: 6 +27383 Task N23 current iteration: 8 +27383 Task N23 finished and destroyed. Free mem=1208 No of tasks=11 +28367 Task N43 current iteration: 6 +28367 Task N43 finished and destroyed. Free mem=1253 No of tasks=10 +28440 Task N5 current iteration: 7 +28440 Task N5 finished and destroyed. Free mem=1298 No of tasks=9 +28513 Task N26 current iteration: 6 +28795 Task N3 current iteration: 8 +29277 Task N50 current iteration: 8 +29552 Task N49 current iteration: 8 +29591 Task N45 current iteration: 7 +29730 Task N30 current iteration: 9 +29730 Task N30 finished and destroyed. Free mem=1343 No of tasks=8 +30612 Task N17 current iteration: 7 +30738 Task N47 current iteration: 7 +31456 Task N31 current iteration: 7 +32059 Task N50 current iteration: 9 +32402 Task N49 current iteration: 9 +32402 Task N49 finished and destroyed. Free mem=1388 No of tasks=7 +32894 Task N3 current iteration: 9 +32894 Task N3 finished and destroyed. Free mem=1433 No of tasks=6 +33139 Task N45 current iteration: 8 +33295 Task N26 current iteration: 7 +34294 Task N47 current iteration: 8 +34841 Task N50 current iteration: 10 +34841 Task N50 finished and destroyed. Free mem=1478 No of tasks=5 +35230 Task N17 current iteration: 8 +35815 Task N31 current iteration: 8 +36687 Task N45 current iteration: 9 +37850 Task N47 current iteration: 9 +37850 Task N47 finished and destroyed. Free mem=1523 No of tasks=4 +38077 Task N26 current iteration: 8 +39848 Task N17 current iteration: 9 +40174 Task N31 current iteration: 9 +40174 Task N31 finished and destroyed. Free mem=1568 No of tasks=3 +40235 Task N45 current iteration: 10 +40235 Task N45 finished and destroyed. Free mem=1613 No of tasks=2 +42859 Task N26 current iteration: 9 +44466 Task N17 current iteration: 10 +44466 Task N17 finished and destroyed. Free mem=1658 No of tasks=1 +47641 Task N26 current iteration: 10 +47641 Task N26 finished and destroyed. Free mem=1703 No of tasks=0 + + */ diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino b/libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino new file mode 100644 index 0000000..fdfa1c9 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino @@ -0,0 +1,185 @@ +/** + This is example 5 rewritten with Timeout, LTS, WDT functioanlity + multitab and extra classes + - 1 second timeout is set for the main calculation task + - LTS is used to address task-specific sensor class object + - WDT is used to set the Task ID and use that for identifying the tasks (debug) + + Original description: + ==================== + This test emulates querying 1 to 10 sensors once every 10 seconds, each could respond with a different delay + (ultrasonic sensors for instance) and printing a max value of them when all have reported their values. + The overall timeout of 1 second is setup as well. + An error message needs to be printed if a timeout occurred instead of a distance value. + + Task and SuperSensor objects are dynamically created and destroyed as needed every 10 seconds +*/ + +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +#define _TASK_LTS_POINTER // Compile with support for local task storage pointer +#define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) +#define _TASK_DEBUG // Make all methods and variables public for debug purposes +#define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +#define _TASK_TIMEOUT // Support for overall task timeout + +#include +#include "SuperSensor.h" + +StatusRequest measure; + +Scheduler ts, hts; + +// Callback methods prototypes +void CycleCallback(); +void CalcCallback(); +bool CalcEnable(); +void CalcDisable(); +void SCallback(); +bool SEnable(); +void SDisable(); + +// Tasks + +Task tCycle(10000, TASK_FOREVER, &CycleCallback, &hts); +Task tCalculate(TASK_IMMEDIATE , TASK_ONCE, &CalcCallback, &hts, false, &CalcEnable, &CalcDisable); + +int numberSensors; +long distance; +int pins[] = { 1, 9, 3, 7, 5, 6, 4, 8, 2, 10 }; + +void CycleCallback() { + Serial.println();Serial.println();Serial.println(); + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); + Serial.print("Number of sensors="); + + numberSensors = random(1, 11); // 1 to 10 sensors, randomly + distance = 0; + Serial.println(numberSensors); + + measure.setWaiting(numberSensors); // Set the StatusRequest to wait for 3 signals. + tCalculate.waitFor(&measure); + tCalculate.setTimeout(1000 * TASK_MILLISECOND); +} + +bool CalcEnable() { + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CalcEnable: OnEnable"); + Serial.println("Activating sensors"); + + + for (int i = 0; i < numberSensors; i++) { + Task *t = new Task(TASK_MILLISECOND, TASK_FOREVER, &SCallback, &ts, false, &SEnable, &SDisable); + SuperSensor *s = new SuperSensor( pins[i] ); + t->setLtsPointer( (void*) s); + t->setId(i + 1); + + s->begin(); + + t->restartDelayed(); + } + + return true; +} + +void CalcDisable() { + if (tCalculate.timedOut()) { + measure.signalComplete(-1); // signal error + Serial.print(millis()); Serial.print(":\t"); + Serial.println("MeasureCallback: ***** Timeout *****"); + } + ts.disableAll(false); // only disable tasks in the ts scheduler +} + + +void CalcCallback() { + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CalcCallback: calculating"); + if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully + Serial.print("CalcCallback: Max distance="); Serial.println(distance); + Serial.println(); + } +} + + +/** Simulation code for all sensors + ------------------------------- +*/ +bool SEnable() { + Task &t = ts.currentTask(); + int i = t.getId(); + + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SEnable: TaskID="); + Serial.println(i); + Serial.print("Triggering sensor. Delay="); + + + // Another way to update the distances with one codebase - use LTS pointers + SuperSensor *s = (SuperSensor*) t.getLtsPointer(); + + long dly = s->trigger(); + + + Serial.println( dly ); + return true; +} + +void SCallback() { + Task &t = ts.currentTask(); + + SuperSensor *s = (SuperSensor*) t.getLtsPointer(); + if ( s->measurementReady() ) { + int i = t.getId(); + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SCallback: TaskID="); + Serial.println(i); + Serial.print("Emulating measurement. d="); + + long d = s->value(); + if ( d > distance ) distance = d; + + Serial.println(d); + + measure.signal(); + t.disable(); + } +} + +void SDisable() { + Task &t = ts.currentTask(); + int i = t.getId(); + + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SDisable: TaskID="); + Serial.println(i); + + SuperSensor *s = (SuperSensor*) ts.currentLts(); + s->stop(); + + delete s; + delete &t; +} + +/** Main Arduino code + Not much is left here - everything is taken care of by the framework +*/ +void setup() { + + Serial.begin(115200); + Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); + randomSeed(analogRead(A0) + millis()); + + ts.setHighPriorityScheduler(&hts); + + tCalculate.setTimeout(1 * TASK_SECOND); + tCycle.enable(); +} + +void loop() { + ts.execute(); +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.cpp b/libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.cpp new file mode 100644 index 0000000..231c2ea --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.cpp @@ -0,0 +1,36 @@ +#include "SuperSensor.h" + +SuperSensor::SuperSensor(int aPin) { + iPin = aPin; +} + +SuperSensor::~SuperSensor() { + iValue = -1; +} + +void SuperSensor::begin() { + iDelay = random(300, 1500); + iValue = -1; +} + +void SuperSensor::stop() { + //nothing to do +} + +long SuperSensor::trigger() { + iStart = millis(); + return iDelay; +} + +bool SuperSensor::measurementReady() { + if ( millis() - iStart > iDelay ) { + iValue = random(501); + return true; + } + return false; +} + +long SuperSensor::value() { + return iValue; +} + diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.h b/libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.h new file mode 100644 index 0000000..eb491bf --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.h @@ -0,0 +1,25 @@ +#ifndef _SUPER_SENSOR_H +#define _SUPER_SENSOR_H + + +#include "Arduino.h" +#include + +class SuperSensor { + public: + SuperSensor(int aPin); + ~SuperSensor(); + void begin(); + void stop(); + long trigger(); + bool measurementReady(); + long value(); + + private: + long iDelay; + long iValue; + int iPin; + unsigned long iStart; +}; + +#endif // _SUPER_SENSOR_H diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Calculator.cpp b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Calculator.cpp new file mode 100644 index 0000000..cb2a384 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Calculator.cpp @@ -0,0 +1,66 @@ +#include "Calculator.h" +#include "SuperSensor.h" + +#if defined (ARDUINO_ARCH_AVR) +#include +#endif + +#if defined(__arm__) +extern "C" char* sbrk(int incr); +static int freeMemory() { + char top = 't'; + return &top - reinterpret_cast(sbrk(0)); +} + +#endif +Calculator::Calculator( Scheduler* aS, Scheduler* aSensors) : Task(aS) { + iS = aSensors; + setTimeout(1000 * TASK_MILLISECOND); +} + +bool Calculator::Callback() { + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CalcCallback: calculating"); + if ( getStatusRequest()->getStatus() >= 0) { // only calculate if statusrequest ended successfully + Serial.print("CalcCallback: Max distance="); Serial.println(distance); + } + return false; +} + +extern int pins[]; + +bool Calculator::OnEnable() { + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CalcEnable: OnEnable"); + Serial.println("Activating sensors"); + + StatusRequest* sr = getStatusRequest(); + iNS = sr->getCount(); + + distance = 0; + for (int i = 0; i < iNS; i++) { + SuperSensor *s = new SuperSensor( iS, pins[i], this, sr); + s->setId(i + 1); + s->begin(); + s->restartDelayed(); + } + + return true; +} + +void Calculator::OnDisable() { + if ( timedOut() ) { + getStatusRequest()->signalComplete(-1); // signal error + Serial.print(millis()); Serial.print(":\t"); + Serial.println("MeasureCallback: ***** Timeout *****"); + } + iS->disableAll(false); // only disable tasks in the ts scheduler +#if defined (ARDUINO_ARCH_AVR) || defined(__arm__) + Serial.print("Free mem = "); Serial.println(freeMemory()); Serial.println(); +#endif +} + +void Calculator::reportDistance(long aD) { + if (distance < aD) distance = aD; +} + diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Calculator.h b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Calculator.h new file mode 100644 index 0000000..64cf200 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Calculator.h @@ -0,0 +1,34 @@ +#ifndef _CALCULATOR_H +#define _CALCULATOR_H + +#include "Arduino.h" + +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +#define _TASK_PRIORITY // Support for layered scheduling priority +#define _TASK_TIMEOUT // Support for overall task timeout +#define _TASK_OO_CALLBACKS // Support for dynamic callback method binding + +#include + +class Calculator : public Task { + public: + Calculator( Scheduler* aS, Scheduler* aSensors); + + void reportDistance(long aD); + + bool Callback(); + bool OnEnable(); + void OnDisable(); + + private: + Scheduler* iS; + + long distance; + int iNS; + +}; + +#endif // _CALCULATOR_H + diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Scheduler_example21_OO_Callbacks.ino b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Scheduler_example21_OO_Callbacks.ino new file mode 100644 index 0000000..3867f8b --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Scheduler_example21_OO_Callbacks.ino @@ -0,0 +1,76 @@ +/** + This is example 5 rewritten with dynamic binding of callback methods + - 1 second timeout is set for the main calculation task + - LTS is used to address task-specific sensor class object + - WDT is used to set the Task ID and use that for identifying the tasks (debug) + + Original description: + ==================== + This test emulates querying 1 to 10 sensors once every 10 seconds, each could respond with a different delay + (ultrasonic sensors for instance) and printing a max value of them when all have reported their values. + The overall timeout of 1 second is setup as well. + An error message needs to be printed if a timeout occurred instead of a distance value. + + Task and SuperSensor objects are dynamically created and destroyed as needed every 10 seconds + + This sketch uses a FreeMemory library: https://github.com/McNeight/MemoryFree + FreeMemory for ARM32 boards is based on: http://www.stm32duino.com/viewtopic.php?f=18&t=2065 +*/ + + +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +#define _TASK_PRIORITY // Support for layered scheduling priority +#define _TASK_TIMEOUT // Support for overall task timeout +#define _TASK_OO_CALLBACKS + +#include + +#include "SuperSensor.h" +#include "Calculator.h" +#include "Ticker.h" + +StatusRequest measure; + +Scheduler ts, hts; + +// Tasks + +Calculator* tCalculate; +Ticker* tCycle; + +int pins[] = { 1, 9, 3, 7, 5, 6, 4, 8, 2, 10 }; + +#ifdef ARDUINO_ARCH_STM32F1 +#define A0 3 +#endif + +/** Main Arduino code + Not much is left here - everything is taken care of by the framework +*/ +void setup() { + + Serial.begin(115200); + delay(1000); + while (!Serial) {} + Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); + +#ifdef ARDUINO_ARCH_STM32F1 + pinMode(A0, INPUT_ANALOG); +#endif + + randomSeed(analogRead(A0) + millis()); + + ts.setHighPriorityScheduler(&hts); + + tCalculate = new Calculator (&hts, &ts); + tCycle = new Ticker (&hts, (Task*) tCalculate, &measure); + + tCalculate->setTimeout(1 * TASK_SECOND); + tCycle->enable(); +} + +void loop() { + ts.execute(); +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/SuperSensor.cpp b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/SuperSensor.cpp new file mode 100644 index 0000000..fb0a477 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/SuperSensor.cpp @@ -0,0 +1,83 @@ +#include "SuperSensor.h" + +SuperSensor::SuperSensor(Scheduler* aScheduler, int aPin, Calculator* aC, StatusRequest* aS) : Task(TASK_MILLISECOND, TASK_FOREVER, aScheduler, false) { + iPin = aPin; + iC = aC; + iS = aS; +} + +SuperSensor::~SuperSensor() { + iValue = -1; +} + +void SuperSensor::begin() { + iDelay = random(300, 1500); + iValue = -1; +} + +void SuperSensor::stop() { + //nothing to do +} + +long SuperSensor::trigger() { + iStart = millis(); + return iDelay; +} + +bool SuperSensor::measurementReady() { + if ( millis() - iStart > iDelay ) { + iValue = random(501); + return true; + } + return false; +} + +long SuperSensor::value() { + return iValue; +} + +bool SuperSensor::OnEnable() { + int i = getId(); + + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SEnable: TaskID="); + Serial.println(i); + Serial.print("Triggering sensor. Delay="); + + long dly = trigger(); + + Serial.println( dly ); + return true; +} + +bool SuperSensor::Callback() { + + if ( measurementReady() ) { + int i = getId(); + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SCallback: TaskID="); + Serial.println(i); + Serial.print("Emulating measurement. d="); + + long d = value(); + iC->reportDistance(d); + + Serial.println(d); + + iS->signal(); + disable(); + delete this; + return true; + } + return false; +} + +void SuperSensor::OnDisable() { + int i = getId(); + + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SDisable: TaskID="); + Serial.println(i); + + stop(); +} diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/SuperSensor.h b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/SuperSensor.h new file mode 100644 index 0000000..e8bef41 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/SuperSensor.h @@ -0,0 +1,44 @@ +#ifndef _SUPER_SENSOR_H +#define _SUPER_SENSOR_H + + +#include "Arduino.h" + +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +#define _TASK_PRIORITY // Support for layered scheduling priority +#define _TASK_TIMEOUT // Support for overall task timeout +#define _TASK_OO_CALLBACKS + +#include +#include "Calculator.h" + +//class Calculator; + +class SuperSensor : public Task { + + public: + SuperSensor(Scheduler* aScheduler, int aPin, Calculator* aC, StatusRequest* aS); + ~SuperSensor(); + + void begin(); + void stop(); + long trigger(); + bool measurementReady(); + long value(); + + bool Callback(); + bool OnEnable(); + void OnDisable(); + + private: + long iDelay; + long iValue; + int iPin; + unsigned long iStart; + Calculator* iC; + StatusRequest* iS; +}; + +#endif // _SUPER_SENSOR_H diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Ticker.cpp b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Ticker.cpp new file mode 100644 index 0000000..73d4d6d --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Ticker.cpp @@ -0,0 +1,20 @@ +#include "Ticker.h" + +Ticker::Ticker(Scheduler* aS, Task* aCalc, StatusRequest* aM) : Task(10000, TASK_FOREVER, aS, false) { + iCalc = aCalc; + iMeasure = aM; +} + +bool Ticker::Callback() { + Serial.println(); Serial.println(); Serial.println(); + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); + + int numberSensors = random(1, 11); // 1 to 10 sensors, randomly + Serial.print("Number of sensors="); + Serial.println(numberSensors); + + iMeasure->setWaiting(numberSensors); // Set the StatusRequest to wait for 1 to 10 signals. + iCalc->waitFor(iMeasure); +} + diff --git a/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Ticker.h b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Ticker.h new file mode 100644 index 0000000..3ea0d5c --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_example21_OO_Callbacks/Ticker.h @@ -0,0 +1,28 @@ +#ifndef _TICKER_H +#define _TICKER_H + +#include "Arduino.h" + +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +#define _TASK_PRIORITY // Support for layered scheduling priority +#define _TASK_TIMEOUT // Support for overall task timeout +#define _TASK_OO_CALLBACKS + +#include + +class Ticker : public Task { + public: + Ticker(Scheduler* aS, Task* aCalc, StatusRequest* aM); + ~Ticker() {}; + + bool Callback(); + + private: + Task *iCalc; + StatusRequest* iMeasure; +}; + + +#endif diff --git a/libraries/TaskScheduler-master/examples/Scheduler_template/Scheduler_template.ino b/libraries/TaskScheduler-master/examples/Scheduler_template/Scheduler_template.ino new file mode 100644 index 0000000..0a8a073 --- /dev/null +++ b/libraries/TaskScheduler-master/examples/Scheduler_template/Scheduler_template.ino @@ -0,0 +1,78 @@ +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer +// #define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) +// #define _TASK_DEBUG // Make all methods and variables public for debug purposes +// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +// #define _TASK_TIMEOUT // Support for overall task timeout +// #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding +#include + +// Debug and Test options +#define _DEBUG_ +//#define _TEST_ + +#ifdef _DEBUG_ +#define _PP(a) Serial.print(a); +#define _PL(a) Serial.println(a); +#else +#define _PP(a) +#define _PL(a) +#endif + + +// Scheduler +Scheduler ts; + +void task1Callback(); +void task2Callback(); + +/* + Scheduling defines: + TASK_MILLISECOND + TASK_SECOND + TASK_MINUTE + TASK_HOUR + TASK_IMMEDIATE + TASK_FOREVER + TASK_ONCE + TASK_NOTIMEOUT +*/ + +Task t1 (100 * TASK_MILLISECOND, TASK_FOREVER, &task1Callback, &ts, true); +Task t2 (TASK_IMMEDIATE, 100, &task2Callback, &ts, true); + + +void setup() { + // put your setup code here, to run once: +#if defined(_DEBUG_) || defined(_TEST_) + Serial.begin(115200); + delay(2000); + _PL("Scheduler Template: setup()"); +#endif +} + +void loop() { + ts.execute(); +} + + +void task1Callback() { +_PP(millis()); +_PL(": task1Callback()"); + +} + +void task2Callback() { +_PP(millis()); +_PL(": task2Callb()"); + +} + + + + diff --git a/libraries/TaskScheduler-master/extras/TaskScheduler.doc b/libraries/TaskScheduler-master/extras/TaskScheduler.doc new file mode 100644 index 0000000000000000000000000000000000000000..c60b98026f08a13c837c0d3bab1406a5f46826c4 GIT binary patch literal 253952 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

wl_ynMXK05Vk&< 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 =DgVs4Xzq~yZaz!g&IiiE)hFsRW4z(y2E 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

k!3Kn=#5qsZyP1KFs?-WE! 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 literal 0 HcmV?d00001 diff --git a/libraries/TaskScheduler-master/extras/TaskScheduler_html.png b/libraries/TaskScheduler-master/extras/TaskScheduler_html.png new file mode 100644 index 0000000000000000000000000000000000000000..febb8c73e0cbaacd08167df66b76d2ad753f1e77 GIT binary patch literal 41281 zcmeFZcTkgE_b!Z&4FyG{i+~gZN)wbKRZ2jVt~6;%5m1UiXi{Q9goGX`f)J$(2-2mi z5PA`jE<^$89U(vhIs1QhneW}~9o!$Z9feA2jih70`I<*u);{Lh8A{y6Z*fjbvX+^MJz*F*ny#Rwi| z2OqK_;Kqn6E;kWgH{5Kgv>dG6T-?E5CvvIPs+xDh4nH2}u*BAANcr=HxNwUsHH?c`d>sev!i(EDOq;04~YopI} zh}=G)W>0&1T1lVpojuN&t&Mv7*wWio>h|}l$Mkqk-hX?E?+ON0dd~AIWBNxfQkM~y zbM1qU4&i(zcEMen>|LE*v-fVmv&pvUg>F1yddIIO@hBD5J!YpB@Po?fs5tn;@Bo

q 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 +maintainer=Anatoli Arkhipenko +sentence=A light-weight cooperative multitasking library for arduino and esp8266 microcontrollers. +paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds or microseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, event-driven task invocation via Status Request object, task IDs and Control Points for error handling and watchdog timer, Local Task Storage pointer (allowing use of same callback code for multiple tasks), layered task prioritization, std::functions (esp8266, esp32 only), overall task timeout, static and dynamic callback method binding. +category=Timing +url=https://github.com/arkhipenko/TaskScheduler.git +architectures=* diff --git a/libraries/TaskScheduler-master/src/TaskScheduler.h b/libraries/TaskScheduler-master/src/TaskScheduler.h new file mode 100644 index 0000000..9afeac9 --- /dev/null +++ b/libraries/TaskScheduler-master/src/TaskScheduler.h @@ -0,0 +1,1019 @@ +// Cooperative multitasking library for Arduino +// Copyright (c) 2015-2017 Anatoli Arkhipenko +// +// Changelog: +// v1.0.0: +// 2015-02-24 - Initial release +// 2015-02-28 - added delay() and disableOnLastIteration() methods +// 2015-03-25 - changed scheduler execute() method for a more precise delay calculation: +// 1. Do not delay if any of the tasks ran (making request for immediate execution redundant) +// 2. Delay is invoked only if none of the tasks ran +// 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute method itself. +// 2015-05-11 - added restart() and restartDelayed() methods to restart tasks which are on hold after running all iterations +// 2015-05-19 - completely removed delay from the scheduler since there are no power saving there. using 1 ms sleep instead +// +// v1.4.1: +// 2015-09-15 - more careful placement of AVR-specific includes for sleep method (compatibility with DUE) +// sleep on idle run is no longer a default and should be explicitly compiled with +// _TASK_SLEEP_ON_IDLE_RUN defined +// +// v1.5.0: +// 2015-09-20 - access to currently executing task (for callback methods) +// 2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain +// 2015-09-20 - option to create a task already enabled +// +// v1.5.1: +// 2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). +// Thanks to Hannes Morgenstern for catching this one +// +// v1.6.0: +// 2015-09-22 - revert back to having all tasks disable on last iteration. +// 2015-09-22 - deprecated disableOnLastIteration method as a result +// 2015-09-22 - created a separate branch 'disable-on-last-iteration' for this +// 2015-10-01 - made version numbers semver compliant (documentation only) +// +// v1.7.0: +// 2015-10-08 - introduced callback run counter - callback methods can branch on the iteration number. +// 2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, +// false if was disabled. +// 2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled) +// 2015-10-11 - introduced callback methods "on enable" and "on disable". On enable runs every time enable is called, +// on disable runs only if task was enabled +// 2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass +// regardless how much time is left +// +// v1.8.0: +// 2015-10-13 - support for status request objects allowing tasks waiting on requests +// 2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch +// +// v1.8.1: +// 2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging +// +// v1.8.2: +// 2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks) +// 2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods +// 2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable) +// 2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call +// +// v1.8.3: +// 2015-11-05 - support for task activation on a status request with arbitrary interval and number of iterations +// (0 and 1 are still default values) +// 2015-11-05 - implement waitForDelayed() method to allow task activation on the status request completion +// delayed for one current interval +// 2015-11-09 - added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility +// 2015-11-14 - added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.) +// 2015-11-14 - significant optimization of the scheduler's execute loop, including millis() rollover fix option +// +// v1.8.4: +// 2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before. +// Especially since OnEnable method can change the interval +// 2015-11-16 - further optimizations of the task scheduler execute loop +// +// v1.8.5: +// 2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval +// 2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner +// +// v1.9.0: +// 2015-11-24 - packed three byte-long status variables into bit array structure data type - saving 2 bytes per each task instance +// +// v1.9.2: +// 2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary) +// 2015-12-16 - bug fixes: automatic millis rollover support for delay methods +// 2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay() +// +// v2.0.0: +// 2015-12-22 - _TASK_PRIORITY - support for layered task prioritization +// +// v2.0.1: +// 2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation +// +// v2.0.2: +// 2016-01-05 - bug fix: time constants wrapped inside compile option +// 2016-01-05 - support for ESP8266 wifi power saving mode for _TASK_SLEEP_ON_IDLE_RUN compile option +// +// v2.1.0: +// 2016-02-01 - support for microsecond resolution +// 2016-02-02 - added Scheduler baseline start time reset method: startNow() +// +// v2.2.0: +// 2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files +// +// v2.2.1: +// 2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and chain +// back together long running callback methods +// 2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled. +// Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method. +// +// v2.3.0: +// 2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is +// scheduled to run next time +// +// v2.4.0: +// 2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain +// upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder) +// +// v2.5.0: +// 2017-04-27 - ESP8266 ONLY: added optional support for std::functions via _TASK_STD_FUNCTION compilation option +// (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder) +// 2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY! +// Use at your own risk! +// 2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37) +// 2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski) +// +// v2.5.1: +// 2018-01-06 - support for IDLE sleep on Teensy boards (tested on Teensy 3.5) +// +// v2.5.2: +// 2018-01-09 - _TASK_INLINE compilation directive making all methods declared "inline" (issue #42) +// +// v2.6.0: +// 2018-01-30 - _TASK_TIMEOUT compilation directive: Task overall timeout functionality +// 2018-01-30 - ESP32 support (experimental) +// (Contributed by Marco Tombesi: https://github.com/baggior) +// +// v2.6.1: +// 2018-02-13 - Bug: support for task self-destruction in the OnDisable method +// Example 19: dynamic tasks creation and destruction +// 2018-03-14 - Bug: high level scheduler ignored if lower level chain is empty +// Example 20: use of local task storage to work with task-specific class objects +// +// v3.0.0: +// 2018-03-15 - Major Release: Support for dynamic callback methods binding via compilation parameter _TASK_OO_CALLBACKS +// +// v3.0.1: +// 2018-11-09 - bug: task deleted from the execution chain cannot be added back (github issue #67) +// +// v3.0.2: +// 2018-11-11 - bug: default constructor is ambiguous when Status Request objects are enabled (github issue #65 & #68) + +#include +#include "TaskSchedulerDeclarations.h" + +#ifndef _TASKSCHEDULER_H_ +#define _TASKSCHEDULER_H_ + +// ---------------------------------------- +// The following "defines" control library functionality at compile time, +// and should be used in the main sketch depending on the functionality required +// +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer +// #define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) +// #define _TASK_DEBUG // Make all methods and variables public for debug purposes +// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +// #define _TASK_TIMEOUT // Support for overall task timeout +// #define _TASK_OO_CALLBACKS // Support for callbacks via inheritance + + #ifdef _TASK_MICRO_RES + + #undef _TASK_SLEEP_ON_IDLE_RUN // SLEEP_ON_IDLE has only millisecond resolution + #define _TASK_TIME_FUNCTION() micros() + + #else + #define _TASK_TIME_FUNCTION() millis() + + #endif // _TASK_MICRO_RES + + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + +#ifdef ARDUINO_ARCH_AVR +#include +#include +#endif // ARDUINO_ARCH_AVR + +#ifdef ARDUINO_ARCH_ESP8266 +#define _TASK_ESP8266_DLY_THRESHOLD 200L +extern "C" { +#include "user_interface.h" +} +#endif //ARDUINO_ARCH_ESP8266 + +#ifdef ARDUINO_ARCH_ESP32 +#define _TASK_ESP8266_DLY_THRESHOLD 200L +#warning _TASK_SLEEP_ON_IDLE_RUN for ESP32 cannot use light sleep mode but a standard delay for 1 ms +#endif // ARDUINO_ARCH_ESP32 + +#ifdef ARDUINO_ARCH_STM32F1 +#include +#include +#endif // ARDUINO_ARCH_STM32F1 + +#endif // _TASK_SLEEP_ON_IDLE_RUN + + +#if !defined (ARDUINO_ARCH_ESP8266) && !defined (ARDUINO_ARCH_ESP32) +#ifdef _TASK_STD_FUNCTION + #error Support for std::function only for ESP8266 or ESP32 architecture +#undef _TASK_STD_FUNCTION +#endif // _TASK_STD_FUNCTION +#endif // ARDUINO_ARCH_ESP8266 + +#ifdef _TASK_WDT_IDS + static unsigned int __task_id_counter = 0; // global task ID counter for assiging task IDs automatically. +#endif // _TASK_WDT_IDS + +#ifdef _TASK_PRIORITY + Scheduler* iCurrentScheduler; +#endif // _TASK_PRIORITY + + +// ------------------ TaskScheduler implementation -------------------- + + +/** Constructor, uses default values for the parameters + * so could be called with no parameters. + */ +#ifdef _TASK_OO_CALLBACKS +Task::Task( unsigned long aInterval, long aIterations, Scheduler* aScheduler, bool aEnable ) { + reset(); + set(aInterval, aIterations); +#else +Task::Task( unsigned long aInterval, long aIterations, TaskCallback aCallback, Scheduler* aScheduler, bool aEnable, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) { + reset(); + set(aInterval, aIterations, aCallback, aOnEnable, aOnDisable); +#endif + + if (aScheduler) aScheduler->addTask(*this); + +#ifdef _TASK_WDT_IDS + iTaskID = ++__task_id_counter; +#endif // _TASK_WDT_IDS + + if (aEnable) enable(); +} + +/** Destructor. + * Makes sure the task disabled and deleted out of the chain + * prior to being deleted. + */ +Task::~Task() { + disable(); + if (iScheduler) + iScheduler->deleteTask(*this); +} + + +#ifdef _TASK_STATUS_REQUEST + +/** Constructor with reduced parameter list for tasks created for + * StatusRequest only triggering (always immediate and only 1 iteration) + */ + + +#ifdef _TASK_OO_CALLBACKS +Task::Task( Scheduler* aScheduler ) { + reset(); + set(TASK_IMMEDIATE, TASK_ONCE); +#else +Task::Task( TaskCallback aCallback, Scheduler* aScheduler, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) { + reset(); + set(TASK_IMMEDIATE, TASK_ONCE, aCallback, aOnEnable, aOnDisable); +#endif // _TASK_OO_CALLBACKS + + if (aScheduler) aScheduler->addTask(*this); + +#ifdef _TASK_WDT_IDS + iTaskID = ++__task_id_counter; +#endif // _TASK_WDT_IDS +} + + +StatusRequest::StatusRequest() +{ + iCount = 0; + iStatus = 0; +} + +void StatusRequest::setWaiting(unsigned int aCount) { iCount = aCount; iStatus = 0; } +bool StatusRequest::pending() { return (iCount != 0); } +bool StatusRequest::completed() { return (iCount == 0); } +int StatusRequest::getStatus() { return iStatus; } +int StatusRequest::getCount() { return iCount; } +StatusRequest* Task::getStatusRequest() { return iStatusRequest; } +StatusRequest* Task::getInternalStatusRequest() { return &iMyStatusRequest; } + +/** Signals completion of the StatusRequest by one of the participating events + * @param: aStatus - if provided, sets the return code of the StatusRequest: negative = error, 0 (default) = OK, positive = OK with a specific status code + * Negative status will complete Status Request fully (since an error occured). + * @return: true, if StatusRequest is complete, false otherwise (still waiting for other events) + */ +bool StatusRequest::signal(int aStatus) { + if ( iCount) { // do not update the status request if it was already completed + if (iCount > 0) --iCount; + if ( (iStatus = aStatus) < 0 ) iCount = 0; // if an error is reported, the status is requested to be completed immediately + } + return (iCount == 0); +} + +void StatusRequest::signalComplete(int aStatus) { + if (iCount) { // do not update the status request if it was already completed + iCount = 0; + iStatus = aStatus; + } +} + +/** Sets a Task to wait until a particular event completes + * @param: aStatusRequest - a pointer for the StatusRequest to wait for. + * If aStatusRequest is NULL, request for waiting is ignored, and the waiting task is not enabled. + */ +void Task::waitFor(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) { + iStatusRequest = aStatusRequest; + if ( iStatusRequest != NULL ) { // assign internal StatusRequest var and check if it is not NULL + setIterations(aIterations); + setInterval(aInterval); + iStatus.waiting = _TASK_SR_NODELAY; // no delay + enable(); + } +} + +void Task::waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) { + iStatusRequest = aStatusRequest; + if ( iStatusRequest != NULL ) { // assign internal StatusRequest var and check if it is not NULL + setIterations(aIterations); + if ( aInterval ) setInterval(aInterval); // For the dealyed version only set the interval if it was not a zero + iStatus.waiting = _TASK_SR_DELAY; // with delay equal to the current interval + enable(); + } +} +#endif // _TASK_STATUS_REQUEST + +bool Task::isEnabled() { return iStatus.enabled; } + +unsigned long Task::getInterval() { return iInterval; } + +long Task::getIterations() { return iIterations; } + +unsigned long Task::getRunCounter() { return iRunCounter; } + +#ifdef _TASK_OO_CALLBACKS + +// bool Task::Callback() { return true; } +bool Task::OnEnable() { return true; } +void Task::OnDisable() { } + +#else + +void Task::setCallback(TaskCallback aCallback) { iCallback = aCallback; } +void Task::setOnEnable(TaskOnEnable aCallback) { iOnEnable = aCallback; } +void Task::setOnDisable(TaskOnDisable aCallback) { iOnDisable = aCallback; } + +#endif // _TASK_OO_CALLBACKS + + +/** Resets (initializes) the task/ + * Task is not enabled and is taken out + * out of the execution chain as a result + */ +void Task::reset() { + iStatus.enabled = false; + iStatus.inonenable = false; + iPreviousMillis = 0; + iInterval = iDelay = 0; + iPrev = NULL; + iNext = NULL; + iScheduler = NULL; + iRunCounter = 0; + +#ifdef _TASK_TIMECRITICAL + iOverrun = 0; + iStartDelay = 0; +#endif // _TASK_TIMECRITICAL + +#ifdef _TASK_WDT_IDS + iControlPoint = 0; +#endif // _TASK_WDT_IDS + +#ifdef _TASK_LTS_POINTER + iLTS = NULL; +#endif // _TASK_LTS_POINTER + +#ifdef _TASK_STATUS_REQUEST + iStatusRequest = NULL; + iStatus.waiting = 0; + iMyStatusRequest.signalComplete(); +#endif // _TASK_STATUS_REQUEST + +#ifdef _TASK_TIMEOUT + iTimeout = 0; + iStarttime = 0; + iStatus.timeout = false; +#endif // _TASK_TIMEOUT +} + +/** Explicitly set Task execution parameters + * @param aInterval - execution interval in ms + * @param aIterations - number of iterations, use -1 for no limit + * @param aCallback - pointer to the callback method which executes the task actions + * @param aOnEnable - pointer to the callback method which is called on enable() + * @param aOnDisable - pointer to the callback method which is called on disable() + */ + +#ifdef _TASK_OO_CALLBACKS +void Task::set(unsigned long aInterval, long aIterations) { +#else +void Task::set(unsigned long aInterval, long aIterations, TaskCallback aCallback, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable) { + iCallback = aCallback; + iOnEnable = aOnEnable; + iOnDisable = aOnDisable; +#endif // _TASK_OO_CALLBACKS + + setInterval(aInterval); + iSetIterations = iIterations = aIterations; +} + +/** Sets number of iterations for the task + * if task is enabled, schedule for immediate execution + * @param aIterations - number of iterations, use -1 for no limit + */ +void Task::setIterations(long aIterations) { + iSetIterations = iIterations = aIterations; +} + +#ifndef _TASK_OO_CALLBACKS + +/** Prepare task for next step iteration following yielding of control to the scheduler + * @param aCallback - pointer to the callback method for the next step + */ +void Task::yield (TaskCallback aCallback) { + iCallback = aCallback; + forceNextIteration(); + + // The next 2 lines adjust runcounter and number of iterations + // as if it is the same run of the callback, just split between + // a series of callback methods + iRunCounter--; + if ( iIterations >= 0 ) iIterations++; +} + +/** Prepare task for next step iteration following yielding of control to the scheduler + * @param aCallback - pointer to the callback method for the next step + */ +void Task::yieldOnce (TaskCallback aCallback) { + yield(aCallback); + iIterations = 1; +} +#endif // _TASK_OO_CALLBACKS + + +/** Enables the task + * schedules it for execution as soon as possible, + * and resets the RunCounter back to zero + */ +void Task::enable() { + if (iScheduler) { // activation without active scheduler does not make sense + iRunCounter = 0; + +#ifdef _TASK_OO_CALLBACKS + if ( !iStatus.inonenable ) { + Task *current = iScheduler->iCurrent; + iScheduler->iCurrent = this; + iStatus.inonenable = true; // Protection against potential infinite loop + iStatus.enabled = OnEnable(); + iStatus.inonenable = false; // Protection against potential infinite loop + iScheduler->iCurrent = current; + } +#else + if ( iOnEnable && !iStatus.inonenable ) { + Task *current = iScheduler->iCurrent; + iScheduler->iCurrent = this; + iStatus.inonenable = true; // Protection against potential infinite loop + iStatus.enabled = iOnEnable(); + iStatus.inonenable = false; // Protection against potential infinite loop + iScheduler->iCurrent = current; + } + else { + iStatus.enabled = true; + } +#endif // _TASK_OO_CALLBACKS + + iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval); + +#ifdef _TASK_TIMEOUT + resetTimeout(); +#endif // _TASK_TIMEOUT + +#ifdef _TASK_STATUS_REQUEST + if ( iStatus.enabled ) { + iMyStatusRequest.setWaiting(); + } +#endif // _TASK_STATUS_REQUEST + } +} + +/** Enables the task only if it was not enabled already + * Returns previous state (true if was already enabled, false if was not) + */ +bool Task::enableIfNot() { + bool previousEnabled = iStatus.enabled; + if ( !previousEnabled ) enable(); + return (previousEnabled); +} + +/** Enables the task + * and schedules it for execution after a delay = aInterval + */ +void Task::enableDelayed(unsigned long aDelay) { + enable(); + delay(aDelay); +} + +#ifdef _TASK_TIMEOUT +void Task::setTimeout(unsigned long aTimeout, bool aReset) { + iTimeout = aTimeout; + if (aReset) resetTimeout(); +} + +void Task::resetTimeout() { + iStarttime = _TASK_TIME_FUNCTION(); + iStatus.timeout = false; +} + +unsigned long Task::getTimeout() { + return iTimeout; +} + +long Task::untilTimeout() { + if ( iTimeout ) { + return ( (long) (iStarttime + iTimeout) - (long) _TASK_TIME_FUNCTION() ); + } + return -1; +} + +bool Task::timedOut() { + return iStatus.timeout; +} + +#endif // _TASK_TIMEOUT + + + +/** Delays Task for execution after a delay = aInterval (if task is enabled). + * leaves task enabled or disabled + * if aDelay is zero, delays for the original scheduling interval from now + */ +void Task::delay(unsigned long aDelay) { +// if (!aDelay) aDelay = iInterval; + iDelay = aDelay ? aDelay : iInterval; + iPreviousMillis = _TASK_TIME_FUNCTION(); // - iInterval + aDelay; +} + +/** Schedules next iteration of Task for execution immediately (if enabled) + * leaves task enabled or disabled + * Task's original schedule is shifted, and all subsequent iterations will continue from this point in time + */ +void Task::forceNextIteration() { + iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval); +} + +/** Sets the execution interval. + * Task execution is delayed for aInterval + * Use enable() to schedule execution ASAP + * @param aInterval - new execution interval + */ +void Task::setInterval (unsigned long aInterval) { + iInterval = aInterval; + delay(); // iDelay will be updated by the delay() function +} + +/** Disables task + * Task will no longer be executed by the scheduler + * Returns status of the task before disable was called (i.e., if the task was already disabled) + */ + +bool Task::disable() { + bool previousEnabled = iStatus.enabled; + iStatus.enabled = false; + iStatus.inonenable = false; + +#ifdef _TASK_OO_CALLBACKS + if (previousEnabled) { +#else + if (previousEnabled && iOnDisable) { +#endif // _TASK_OO_CALLBACKS + + Task *current = iScheduler->iCurrent; + iScheduler->iCurrent = this; +#ifdef _TASK_OO_CALLBACKS + OnDisable(); +#else + iOnDisable(); +#endif // _TASK_OO_CALLBACKS + + iScheduler->iCurrent = current; + } +#ifdef _TASK_STATUS_REQUEST + iMyStatusRequest.signalComplete(); +#endif + return (previousEnabled); +} + +/** Restarts task + * Task will run number of iterations again + */ + +void Task::restart() { + enable(); + iIterations = iSetIterations; +} + +/** Restarts task delayed + * Task will run number of iterations again + */ +void Task::restartDelayed(unsigned long aDelay) { + enableDelayed(aDelay); + iIterations = iSetIterations; +} + +bool Task::isFirstIteration() { return (iRunCounter <= 1); } + +bool Task::isLastIteration() { return (iIterations == 0); } + +#ifdef _TASK_TIMECRITICAL + +long Task::getOverrun() { return iOverrun; } +long Task::getStartDelay() { return iStartDelay; } + +#endif // _TASK_TIMECRITICAL + + +#ifdef _TASK_WDT_IDS + +void Task::setId(unsigned int aID) { iTaskID = aID; } +unsigned int Task::getId() { return iTaskID; } +void Task::setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; } +unsigned int Task::getControlPoint() { return iControlPoint; } + +#endif // _TASK_WDT_IDS + +#ifdef _TASK_LTS_POINTER + +void Task::setLtsPointer(void *aPtr) { iLTS = aPtr; } +void* Task::getLtsPointer() { return iLTS; } + +#endif // _TASK_LTS_POINTER + +// ------------------ Scheduler implementation -------------------- + +/** Default constructor. + * Creates a scheduler with an empty execution chain. + */ +Scheduler::Scheduler() { + init(); +} + +/* +Scheduler::~Scheduler() { +#ifdef _TASK_SLEEP_ON_IDLE_RUN +#endif // _TASK_SLEEP_ON_IDLE_RUN +} +*/ + +/** Initializes all internal varaibles + */ +void Scheduler::init() { + iFirst = NULL; + iLast = NULL; + iCurrent = NULL; + +#ifdef _TASK_PRIORITY + iHighPriority = NULL; +#endif // _TASK_PRIORITY + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + allowSleep(true); +#endif // _TASK_SLEEP_ON_IDLE_RUN +} + +/** Appends task aTask to the tail of the execution chain. + * @param &aTask - reference to the Task to be appended. + * @note Task can only be part of the chain once. + */ + void Scheduler::addTask(Task& aTask) { + +// Avoid adding task twice to the same scheduler + if (aTask.iScheduler == this) + return; + + aTask.iScheduler = this; +// First task situation: + if (iFirst == NULL) { + iFirst = &aTask; + aTask.iPrev = NULL; + } + else { +// This task gets linked back to the previous last one + aTask.iPrev = iLast; + iLast->iNext = &aTask; + } +// "Previous" last task gets linked to this one - as this one becomes the last one + aTask.iNext = NULL; + iLast = &aTask; +} + +/** Deletes specific Task from the execution chain + * @param &aTask - reference to the task to be deleted from the chain + */ +void Scheduler::deleteTask(Task& aTask) { + aTask.iScheduler = NULL; + if (aTask.iPrev == NULL) { + if (aTask.iNext == NULL) { + iFirst = NULL; + iLast = NULL; + return; + } + else { + aTask.iNext->iPrev = NULL; + iFirst = aTask.iNext; + aTask.iNext = NULL; + return; + } + } + + if (aTask.iNext == NULL) { + aTask.iPrev->iNext = NULL; + iLast = aTask.iPrev; + aTask.iPrev = NULL; + return; + } + + aTask.iPrev->iNext = aTask.iNext; + aTask.iNext->iPrev = aTask.iPrev; + aTask.iPrev = NULL; + aTask.iNext = NULL; +} + +/** Disables all tasks in the execution chain + * Convenient for error situations, when the only + * task remaining active is an error processing task + * @param aRecursive - if true, tasks of the higher priority chains are disabled as well recursively + */ +void Scheduler::disableAll(bool aRecursive) { + Task *current = iFirst; + while (current) { + current->disable(); + current = current->iNext; + } + +#ifdef _TASK_PRIORITY + if (aRecursive && iHighPriority) iHighPriority->disableAll(true); +#endif // _TASK_PRIORITY +} + + +/** Enables all the tasks in the execution chain + * @param aRecursive - if true, tasks of the higher priority chains are enabled as well recursively + */ + void Scheduler::enableAll(bool aRecursive) { + Task *current = iFirst; + while (current) { + current->enable(); + current = current->iNext; + } + +#ifdef _TASK_PRIORITY + if (aRecursive && iHighPriority) iHighPriority->enableAll(true); +#endif // _TASK_PRIORITY + +} + +/** Sets scheduler for the higher priority tasks (support for layered task priority) + * @param aScheduler - pointer to a scheduler for the higher priority tasks + */ +#ifdef _TASK_PRIORITY +void Scheduler::setHighPriorityScheduler(Scheduler* aScheduler) { + if (aScheduler != this) iHighPriority = aScheduler; // Setting yourself as a higher priority one will create infinite recursive call + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + if (iHighPriority) { + iHighPriority->allowSleep(false); // Higher priority schedulers should not do power management + } +#endif // _TASK_SLEEP_ON_IDLE_RUN + +}; +#endif // _TASK_PRIORITY + + +#ifdef _TASK_SLEEP_ON_IDLE_RUN +void Scheduler::allowSleep(bool aState) { + iAllowSleep = aState; + +#ifdef ARDUINO_ARCH_ESP8266 + wifi_set_sleep_type( iAllowSleep ? LIGHT_SLEEP_T : NONE_SLEEP_T ); +#endif // ARDUINO_ARCH_ESP8266 + +#ifdef ARDUINO_ARCH_ESP32 + // TO-DO; find a suitable replacement for ESP32 if possible. +#endif // ARDUINO_ARCH_ESP32 +} +#endif // _TASK_SLEEP_ON_IDLE_RUN + + +void Scheduler::startNow( bool aRecursive ) { + unsigned long t = _TASK_TIME_FUNCTION(); + + iCurrent = iFirst; + while (iCurrent) { + if ( iCurrent->iStatus.enabled ) iCurrent->iPreviousMillis = t - iCurrent->iDelay; + iCurrent = iCurrent->iNext; + } + +#ifdef _TASK_PRIORITY + if (aRecursive && iHighPriority) iHighPriority->startNow( true ); +#endif // _TASK_PRIORITY +} + +/** Returns number millis or micros until next scheduled iteration of a given task + * + * @param aTask - reference to task which next iteration is in question + */ +long Scheduler::timeUntilNextIteration(Task& aTask) { + +#ifdef _TASK_STATUS_REQUEST + + StatusRequest *s = aTask.getStatusRequest(); + if ( s != NULL && s->pending() ) + return (-1); // cannot be determined +#endif + if ( !aTask.isEnabled() ) + return (-1); // cannot be determined + + long d = (long) aTask.iDelay - ( (long) ((_TASK_TIME_FUNCTION() - aTask.iPreviousMillis)) ); + + if ( d < 0 ) + return (0); // Task will run as soon as possible + return ( d ); +} + +Task& Scheduler::currentTask() { return *iCurrent; } + +#ifdef _TASK_LTS_POINTER +void* Scheduler::currentLts() { return iCurrent->iLTS; } +#endif // _TASK_LTS_POINTER +#ifdef _TASK_TIMECRITICAL +bool Scheduler::isOverrun() { return (iCurrent->iOverrun < 0); } +#endif // _TASK_TIMECRITICAL + + + +/** Makes one pass through the execution chain. + * Tasks are executed in the order they were added to the chain + * There is no concept of priority + * Different pseudo "priority" could be achieved + * by running task more frequently + */ +bool Scheduler::execute() { + bool idleRun = true; + register unsigned long m, i; // millis, interval; + +#ifdef _TASK_SLEEP_ON_IDLE_RUN +#if defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32) + unsigned long t1 = micros(); + unsigned long t2 = 0; +#endif // ARDUINO_ARCH_ESP8266 +#endif // _TASK_SLEEP_ON_IDLE_RUN + + Task *nextTask; // support for deleting the task in the onDisable method + iCurrent = iFirst; + +#ifdef _TASK_PRIORITY + // If lower priority scheduler does not have a single task in the chain + // the higher priority scheduler still has to have a chance to run + if (!iCurrent && iHighPriority) iHighPriority->execute(); + iCurrentScheduler = this; +#endif // _TASK_PRIORITY + + + while (iCurrent) { + +#ifdef _TASK_PRIORITY + // If scheduler for higher priority tasks is set, it's entire chain is executed on every pass of the base scheduler + if (iHighPriority) idleRun = iHighPriority->execute() && idleRun; + iCurrentScheduler = this; +#endif // _TASK_PRIORITY + nextTask = iCurrent->iNext; + do { + if ( iCurrent->iStatus.enabled ) { + +#ifdef _TASK_WDT_IDS + // For each task the control points are initialized to avoid confusion because of carry-over: + iCurrent->iControlPoint = 0; +#endif // _TASK_WDT_IDS + + // Disable task on last iteration: + if (iCurrent->iIterations == 0) { + iCurrent->disable(); + break; + } + m = _TASK_TIME_FUNCTION(); + i = iCurrent->iInterval; + +#ifdef _TASK_TIMEOUT + // Disable task on a timeout + if ( iCurrent->iTimeout && (m - iCurrent->iStarttime > iCurrent->iTimeout) ) { + iCurrent->iStatus.timeout = true; + iCurrent->disable(); + break; + } +#endif // _TASK_TIMEOUT + +#ifdef _TASK_STATUS_REQUEST + // If StatusRequest object was provided, and still pending, and task is waiting, this task should not run + // Otherwise, continue with execution as usual. Tasks waiting to StatusRequest need to be rescheduled according to + // how they were placed into waiting state (waitFor or waitForDelayed) + if ( iCurrent->iStatus.waiting ) { + if ( (iCurrent->iStatusRequest)->pending() ) break; + if (iCurrent->iStatus.waiting == _TASK_SR_NODELAY) { + iCurrent->iPreviousMillis = m - (iCurrent->iDelay = i); + } + else { + iCurrent->iPreviousMillis = m; + } + iCurrent->iStatus.waiting = 0; + } +#endif // _TASK_STATUS_REQUEST + + if ( m - iCurrent->iPreviousMillis < iCurrent->iDelay ) break; + + if ( iCurrent->iIterations > 0 ) iCurrent->iIterations--; // do not decrement (-1) being a signal of never-ending task + iCurrent->iRunCounter++; + iCurrent->iPreviousMillis += iCurrent->iDelay; + +#ifdef _TASK_TIMECRITICAL + // Updated_previous+current interval should put us into the future, so iOverrun should be positive or zero. + // If negative - the task is behind (next execution time is already in the past) + unsigned long p = iCurrent->iPreviousMillis; + iCurrent->iOverrun = (long) ( p + i - m ); + iCurrent->iStartDelay = (long) ( m - p ); +#endif // _TASK_TIMECRITICAL + + iCurrent->iDelay = i; + +#ifdef _TASK_OO_CALLBACKS + idleRun = !iCurrent->Callback(); +#else + if ( iCurrent->iCallback ) { + iCurrent->iCallback(); + idleRun = false; + } +#endif // _TASK_OO_CALLBACKS + + } + } while (0); //guaranteed single run - allows use of "break" to exit + iCurrent = nextTask; +#if defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32) + yield(); +#endif // ARDUINO_ARCH_ESP8266 + } + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + if (idleRun && iAllowSleep) { + +#ifdef ARDUINO_ARCH_AVR // Could be used only for AVR-based boards. + set_sleep_mode(SLEEP_MODE_IDLE); + sleep_enable(); + /* Now enter sleep mode. */ + sleep_mode(); + + /* The program will continue from here after the timer timeout ~1 ms */ + sleep_disable(); /* First thing to do is disable sleep. */ +#endif // ARDUINO_ARCH_AVR + +#ifdef CORE_TEENSY + asm("wfi"); +#endif //CORE_TEENSY + +#ifdef ARDUINO_ARCH_ESP8266 +// to do: find suitable sleep function for esp8266 + t2 = micros() - t1; + if (t2 < _TASK_ESP8266_DLY_THRESHOLD) delay(1); // ESP8266 implementation of delay() uses timers and yield +#endif // ARDUINO_ARCH_ESP8266 + +#ifdef ARDUINO_ARCH_ESP32 +//TODO: find a correct light sleep implementation for ESP32 + // esp_sleep_enable_timer_wakeup(1000); //1 ms + // int ret= esp_light_sleep_start(); + t2 = micros() - t1; + if (t2 < _TASK_ESP8266_DLY_THRESHOLD) delay(1); +#endif // ARDUINO_ARCH_ESP32 + +#ifdef ARDUINO_ARCH_STM32F1 + // Now go into stop mode, wake up on interrupt. + // Systick interrupt will run every 1 milliseconds. + asm(" wfi"); +#endif // ARDUINO_ARCH_STM32 + + } +#endif // _TASK_SLEEP_ON_IDLE_RUN + + return (idleRun); +} + + + +#endif /* _TASKSCHEDULER_H_ */ diff --git a/libraries/TaskScheduler-master/src/TaskSchedulerDeclarations.h b/libraries/TaskScheduler-master/src/TaskSchedulerDeclarations.h new file mode 100644 index 0000000..96866e3 --- /dev/null +++ b/libraries/TaskScheduler-master/src/TaskSchedulerDeclarations.h @@ -0,0 +1,309 @@ +// Cooperative multitasking library for Arduino +// Copyright (c) 2015-2017 Anatoli Arkhipenko + +#include +#include + +#ifndef _TASKSCHEDULERDECLARATIONS_H_ +#define _TASKSCHEDULERDECLARATIONS_H_ + +// ---------------------------------------- +// The following "defines" control library functionality at compile time, +// and should be used in the main sketch depending on the functionality required +// +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer +// #define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) +// #define _TASK_DEBUG // Make all methods and variables public for debug purposes +// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +// #define _TASK_TIMEOUT // Support for overall task timeout +// #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding + +#ifdef _TASK_DEBUG + #define _TASK_SCOPE public +#else + #define _TASK_SCOPE private +#endif + +#define TASK_IMMEDIATE 0 +#define TASK_FOREVER (-1) +#define TASK_ONCE 1 + +#ifdef _TASK_TIMEOUT +#define TASK_NOTIMEOUT 0 +#endif + +#ifdef _TASK_PRIORITY + class Scheduler; + extern Scheduler* iCurrentScheduler; +#endif // _TASK_PRIORITY + +#if !defined(ARDUINO) + extern unsigned long micros(void); + extern unsigned long millis(void); +#endif + + +#ifdef _TASK_INLINE +#define INLINE inline +#else +#define INLINE +#endif + + +#ifndef _TASK_MICRO_RES + +#define TASK_MILLISECOND 1UL +#define TASK_SECOND 1000UL +#define TASK_MINUTE 60000UL +#define TASK_HOUR 3600000UL + +#else + +#define TASK_MILLISECOND 1000UL +#define TASK_SECOND 1000000UL +#define TASK_MINUTE 60000000UL +#define TASK_HOUR 3600000000UL + +#endif // _TASK_MICRO_RES + + +#ifdef _TASK_STATUS_REQUEST + +#define _TASK_SR_NODELAY 1 +#define _TASK_SR_DELAY 2 + +class StatusRequest { + public: + INLINE StatusRequest(); + INLINE void setWaiting(unsigned int aCount = 1); + INLINE bool signal(int aStatus = 0); + INLINE void signalComplete(int aStatus = 0); + INLINE bool pending(); + INLINE bool completed(); + INLINE int getStatus(); + INLINE int getCount(); + + _TASK_SCOPE: + unsigned int iCount; // number of statuses to wait for. waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient + int iStatus; // status of the last completed request. negative = error; zero = OK; positive = OK with a specific status +}; +#endif // _TASK_STATUS_REQUEST + +#ifdef _TASK_STD_FUNCTION +#include +typedef std::function TaskCallback; +typedef std::function TaskOnDisable; +typedef std::function TaskOnEnable; +#else +typedef void (*TaskCallback)(); +typedef void (*TaskOnDisable)(); +typedef bool (*TaskOnEnable)(); +#endif + +typedef struct { + bool enabled : 1; // indicates that task is enabled or not. + bool inonenable : 1; // indicates that task execution is inside OnEnable method (preventing infinite loops) + +#ifdef _TASK_STATUS_REQUEST + uint8_t waiting : 2; // indication if task is waiting on the status request +#endif + +#ifdef _TASK_TIMEOUT + bool timeout : 1; // indication if task is waiting on the status request +#endif + +} __task_status; + +class Scheduler; + + +class Task { + friend class Scheduler; + public: + +#ifdef _TASK_OO_CALLBACKS + INLINE Task(unsigned long aInterval=0, long aIterations=0, Scheduler* aScheduler=NULL, bool aEnable=false); +#else + INLINE Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL); +#endif // _TASK_OO_CALLBACKS + + +#ifdef _TASK_STATUS_REQUEST +#ifdef _TASK_OO_CALLBACKS +// INLINE Task(Scheduler* aScheduler=NULL); + INLINE Task(Scheduler* aScheduler); +#else +// INLINE Task(TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL); + INLINE Task(TaskCallback aCallback, Scheduler* aScheduler, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL); +#endif // _TASK_OO_CALLBACKS +#endif // _TASK_STATUS_REQUEST + + INLINE ~Task(); + +#ifdef _TASK_TIMEOUT + INLINE void setTimeout(unsigned long aTimeout, bool aReset=false); + INLINE void resetTimeout(); + INLINE unsigned long getTimeout(); + INLINE long untilTimeout(); + INLINE bool timedOut(); +#endif + + INLINE void enable(); + INLINE bool enableIfNot(); + INLINE void enableDelayed(unsigned long aDelay=0); + INLINE void restart(); + INLINE void restartDelayed(unsigned long aDelay=0); + + INLINE void delay(unsigned long aDelay=0); + INLINE void forceNextIteration(); + INLINE bool disable(); + INLINE bool isEnabled(); + +#ifdef _TASK_OO_CALLBACKS + INLINE void set(unsigned long aInterval, long aIterations); +#else + INLINE void set(unsigned long aInterval, long aIterations, TaskCallback aCallback,TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL); +#endif // _TASK_OO_CALLBACKS + INLINE void setInterval(unsigned long aInterval); + INLINE unsigned long getInterval(); + INLINE void setIterations(long aIterations); + INLINE long getIterations(); + INLINE unsigned long getRunCounter() ; + +#ifdef _TASK_OO_CALLBACKS + virtual INLINE bool Callback() =0; // return true if run was "productive - this will disable sleep on the idle run for next pass + virtual INLINE bool OnEnable(); // return true if task should be enabled, false if it should remain disabled + virtual INLINE void OnDisable(); +#else + INLINE void setCallback(TaskCallback aCallback) ; + INLINE void setOnEnable(TaskOnEnable aCallback) ; + INLINE void setOnDisable(TaskOnDisable aCallback) ; + INLINE void yield(TaskCallback aCallback); + INLINE void yieldOnce(TaskCallback aCallback); +#endif // _TASK_OO_CALLBACKS + + INLINE bool isFirstIteration() ; + INLINE bool isLastIteration() ; + +#ifdef _TASK_TIMECRITICAL + INLINE long getOverrun() ; + INLINE long getStartDelay() ; +#endif // _TASK_TIMECRITICAL + +#ifdef _TASK_STATUS_REQUEST + INLINE void waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1); + INLINE void waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1); + INLINE StatusRequest* getStatusRequest() ; + INLINE StatusRequest* getInternalStatusRequest() ; +#endif // _TASK_STATUS_REQUEST + +#ifdef _TASK_WDT_IDS + INLINE void setId(unsigned int aID) ; + INLINE unsigned int getId() ; + INLINE void setControlPoint(unsigned int aPoint) ; + INLINE unsigned int getControlPoint() ; +#endif // _TASK_WDT_IDS + +#ifdef _TASK_LTS_POINTER + INLINE void setLtsPointer(void *aPtr) ; + void* getLtsPointer() ; +#endif // _TASK_LTS_POINTER + + _TASK_SCOPE: + INLINE void reset(); + + volatile __task_status iStatus; + volatile unsigned long iInterval; // execution interval in milliseconds (or microseconds). 0 - immediate + volatile unsigned long iDelay; // actual delay until next execution (usually equal iInterval) + volatile unsigned long iPreviousMillis; // previous invocation time (millis). Next invocation = iPreviousMillis + iInterval. Delayed tasks will "catch up" + +#ifdef _TASK_TIMECRITICAL + volatile long iOverrun; // negative if task is "catching up" to it's schedule (next invocation time is already in the past) + volatile long iStartDelay; // actual execution of the task's callback method was delayed by this number of millis +#endif // _TASK_TIMECRITICAL + + volatile long iIterations; // number of iterations left. 0 - last iteration. -1 - infinite iterations + long iSetIterations; // number of iterations originally requested (for restarts) + unsigned long iRunCounter; // current number of iteration (starting with 1). Resets on enable. + +#ifndef _TASK_OO_CALLBACKS + TaskCallback iCallback; // pointer to the void callback method + TaskOnEnable iOnEnable; // pointer to the bool OnEnable callback method + TaskOnDisable iOnDisable; // pointer to the void OnDisable method +#endif // _TASK_OO_CALLBACKS + + Task *iPrev, *iNext; // pointers to the previous and next tasks in the chain + Scheduler *iScheduler; // pointer to the current scheduler + +#ifdef _TASK_STATUS_REQUEST + StatusRequest *iStatusRequest; // pointer to the status request task is or was waiting on + StatusRequest iMyStatusRequest; // internal Status request to let other tasks know of completion +#endif // _TASK_STATUS_REQUEST + +#ifdef _TASK_WDT_IDS + unsigned int iTaskID; // task ID (for debugging and watchdog identification) + unsigned int iControlPoint; // current control point within the callback method. Reset to 0 by scheduler at the beginning of each pass +#endif // _TASK_WDT_IDS + +#ifdef _TASK_LTS_POINTER + void *iLTS; // pointer to task's local storage. Needs to be recast to appropriate type (usually a struct). +#endif // _TASK_LTS_POINTER + +#ifdef _TASK_TIMEOUT + unsigned long iTimeout; // Task overall timeout + unsigned long iStarttime; // millis at task start time +#endif // _TASK_TIMEOUT +}; + +class Scheduler { + friend class Task; + public: + INLINE Scheduler(); +// ~Scheduler(); + INLINE void init(); + INLINE void addTask(Task& aTask); + INLINE void deleteTask(Task& aTask); + INLINE void disableAll(bool aRecursive = true); + INLINE void enableAll(bool aRecursive = true); + INLINE bool execute(); // Returns true if none of the tasks' callback methods was invoked (true = idle run) + INLINE void startNow(bool aRecursive = true); // reset ALL active tasks to immediate execution NOW. + INLINE Task& currentTask() ; + INLINE long timeUntilNextIteration(Task& aTask); // return number of ms until next iteration of a given Task + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + INLINE void allowSleep(bool aState = true); +#endif // _TASK_SLEEP_ON_IDLE_RUN + +#ifdef _TASK_LTS_POINTER + INLINE void* currentLts(); +#endif // _TASK_LTS_POINTER + +#ifdef _TASK_TIMECRITICAL + INLINE bool isOverrun(); +#endif // _TASK_TIMECRITICAL + +#ifdef _TASK_PRIORITY + INLINE void setHighPriorityScheduler(Scheduler* aScheduler); + INLINE static Scheduler& currentScheduler() { return *(iCurrentScheduler); }; +#endif // _TASK_PRIORITY + + _TASK_SCOPE: + Task *iFirst, *iLast, *iCurrent; // pointers to first, last and current tasks in the chain + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + bool iAllowSleep; // indication if putting avr to IDLE_SLEEP mode is allowed by the program at this time. +#endif // _TASK_SLEEP_ON_IDLE_RUN + +#ifdef _TASK_PRIORITY + Scheduler *iHighPriority; // Pointer to a higher priority scheduler +#endif // _TASK_PRIORITY +}; + + +#endif /* _TASKSCHEDULERDECLARATIONS_H_ */