From 7d105cef67909a6b470552223f76b097a92f5e15 Mon Sep 17 00:00:00 2001 From: Makuna Date: Tue, 22 Mar 2016 16:32:37 -0700 Subject: [PATCH] feature work dynamic time intervals esp8266 deep sleep support fixed some arm compile issues due to incorrect platform use --- README.md | 159 ++++---------------------------------------- library.json | 2 +- library.properties | 2 +- src/Task.h | 13 ++++ src/TaskManager.cpp | 96 +++++++++++++++++--------- src/TaskManager.h | 27 +++++--- 6 files changed, 110 insertions(+), 189 deletions(-) diff --git a/README.md b/README.md index 5a23be6..1469318 100644 --- a/README.md +++ b/README.md @@ -1,161 +1,26 @@ # Task +[![Donate](http://img.shields.io/paypal/donate.png?color=yellow)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6AA97KE54UJR4) + Arduino Nonpreemptive multitasking library -For the latest Arduino IDE, use the library manager to find and install "Task by Makuna" library. +NOTE: Avoid the use of Delay in all high level code as the tasks timing should be used to replace it. -For older versions, you can just clone this into your Arduino\Library folder. +For bugs, make sure there isn't an active issue and then create one. -For a good starting point at understanding the direction of this library, please read https://learn.adafruit.com/multi-tasking-the-arduino-part-1?view=all. -This library was not based on that work though, but they share common goals. +For quick questions jump on Gitter and ask away. +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Makuna/Task?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -NOTE: Avoid the use of Delay in all high level code as the tasks timing should be used to replace it. +## Documentation +[See Wiki](https://github.com/Makuna/Task/wiki) -## Installing This Library +## Installing This Library (you just want to use it) Open the Library Manager and search for "Task by Makuna" and install -## Installing This Library From GitHub +## Installing This Library From GitHub (you want to contribute) Create a directory in your Arduino\Library folder named "Task" Clone (Git) this project into that folder. It should now show up in the import list. -## TaskTime -### Conversion macros -The resolution of the task interval is based on millis() or micros(). The default is millis(). To allow the code to easily switch, callers should use the macros to wrap constants. - -``` -uint32_t taskTime = MsToTaskTime(400); // 400 ms -uint32_t timeMilliseconds = TaskTimeToMs(taskTime); -uint32_t timeMicroseconds = TaskTimeToUs(taskTime); -taskTime = UsToTaskTime(400000) // 4000000us, 400ms also -``` -This is important for passing in time interval when the task is created, and when handling the deltaTime parameter in the Update method. - -``` -FunctionTask taskTurnLedOn(OnUpdateTaskLedOn, MsToTaskTime(400)); 400ms - -void OnUpdateTaskLedOn(uint32_t deltaTime) -{ - uint32_t deltaTimeMs = TaskTimeToMs(deltaTime); -} -``` -### Getting the current Update Time -If you call millis() or micros(), you will get the time as it is right then. If you want relative time from update to update, you can get this from the TaskManager. This value will change only on the update interval, and should only be used within an update call. -``` - uint32_t updateTaskTime = taskManager.CurrentTaskTime(); -``` - -### Using higher resolution time -To switch the library to use a higher resolution interval (microseconds), you need to edit the Task.h file in the library/Task folder and uncomment the following line. - -``` -//#define TASK_MICRO_RESOLUTION -``` -If you have used the macros mentioned above, your project should compile and run, but now at a higher resoltion. -By doing this, the longest time a task interval can be is just over 70 minutes. -TaskTime values will now be microseconds, but the time interval may still be larger as some Arduinos will give values that increment by 2, 4, or even 8. - -## Samples -### BlinkUsingTaskFunctions -This demonstrates the use of the FunctionTask feature of Task library. It will use two FunctionTasks to to blink a LED repeatedly, by alternating which task is active and flpping the state of the LED pin. -In this example, tasks are declared at the top and are associated with a function that will be run when the task gets called to update. - -``` -FunctionTask taskTurnLedOn(OnUpdateTaskLedOn, MsToTaskTime(400)); // turn on the led in 400ms -FunctionTask taskTurnLedOff(OnUpdateTaskLedOff, MsToTaskTime(600)); // turn off the led in 600ms - -... - -void OnUpdateTaskLedOn(uint32_t deltaTime) -{ - digitalWrite(ledPin, HIGH); // turn the LED on (HIGH is the voltage level) - taskManager.StopTask(&taskTurnLedOn); // stop trying to turn the LED On - taskManager.StartTask(&taskTurnLedOff); // start the task to turn the LED off -} - - -void OnUpdateTaskLedOff(uint32_t deltaTime) -{ - digitalWrite(ledPin, LOW); // turn the LED off by making the voltage LOW - taskManager.StopTask(&taskTurnLedOff); // stop trying to turn the LED Off - taskManager.StartTask(&taskTurnLedOn); // start the task to turn the LED On -} -``` -Then one of the tasks is started in the setup function. - -``` -void setup() -{ - pinMode(ledPin, OUTPUT); - - taskManager.StartTask(&taskTurnLedOn); // start with turning it on -} -``` -The tasks will continously toggle their running state handing off to the other to turn off and on the the led. - -### BlinkUsingTaskMacros -This demonstrates the use of the Task macros feature of Task library. It will use a custom tasks defined using the helper macros to to blink a LED repeatedly. -This is intended for intermediate level coders where more control is needed. This exposes to the developer the functions that are called when the task is started and stopped along with updated. -The custom task is defined in the taskBlinkLed.h tab. - -### BlinkUsingCustomTask -This demonstrates the use of the custom Task object feature of Task library. It will use a custom task to blink a LED repeatedly. -This is intended for advanced level coders where the most control is required and knowledge of C++ is understood. - -### ButtonTask -This demonstrates the use of the custom Task object feature of Task library. -It will instance two custom ButtonTasks to monitor two different pins and call back when they change state; with debouce and auto repeat support. - -This requires two momentary buttons attached to any io pins and ground. One button will turn on the on board led when it is pressed down, the other button turn off the on board led when it is released. Both will send usefull information to the serial monitor. -The custom task is defined in the ButtonTask.h tab. - -### ButtonInterrupt -This demonstrates the use of the Sleep feature of the Task library. -It will instance two custom ButtonTasks to monitor two different pins and call back when they change state; with debouce and auto repeat support. -An external interrupt is tied to the buttons to wake the Arduino up on any button press. -There are two tasks implementing a LED Blink showing a "heartbeat" that can only run when awake. -There is a task that will put the Arduino into a deep sleep 15 seconds after any button press. - -This requires two momentary buttons attached any io pins and ground. -This requires a diode per button be attached between the button pin and a single external interrupt pin. The cathode (black band) end of the diode is attached to the button pin; the anode to the interrupt pin. - -### MessageTask -This demonstrates the use of the message passing feature of Task library. -It will instance two custom ButtonTasks to monitor two different pins and they will send messages when they change state. It will also have tasks that provide a heartbeat message. - -This requires two momentary buttons attached between pins 4 & 5 and ground, you can change the pins to any digital pin - -### RotaryEncoder -This demonstrates creating a task to manage the details of rotary encoder. The task will track a value and increment and decrement it when the encoder is rotated. It will also provide a callback so the application can respond. Further, it provides a debounced button callback for the encoder press feature. - -This requires a common rotary encoder be connected to available pins. - -## Sleep Modes (advanced feature) -If you want to have your project to deep sleep and use less power, you can call the EnterSleep method and pass in a sleep modes as defined in Arduino header sleep.h. The default is SLEEP_MODE_PWR_DOWN if you dont provide one. -The processor will be placed into the given sleep mode until it is woke up. Different sleep modes have different actions that can cause it to wake. But external interrupts will wake up the Arduino for all sleep modes. - -``` - Serial.println("sleep"); - Serial.flush(); // flush is important, we are about to sleep the complete Arduino - - taskManager.EnterSleep(); // this will not return until something wakes the Arduino - - Serial.println("AWAKE"); - Serial.flush(); -``` - -## WatchDog Timer is enabled -This library will turn on the watchdog timer and by default set it too 500ms. If any single function takes longer than 500ms, the Arduino will reset and start over. -This is usefull where a bug in the code may cause an intermittant hang. But it requires that the developer make sure to use the Tasks to keep a single function to a small atomic piece of work. -The length of the WatchDog Timer can be changed by passing in one of the flags defined in wdt.h for the supported timer lengths to the loop method. - -``` -void loop() -{ - taskManager.Loop(WDTO_1S); // use one second watch dog timer -} -``` - -## Multiple Tasks are no problem -While the samples show simple examples, the limit to the number of tasks is based on memory and the time spent in the active update calls. -You can have one task blinking an LED, while another task samples a analog in and sets PWM output. Just keep the work inside each update call to the minimum needed for that time period and set the time cycle on the task to an appropriet value. +## Documentation +[See Wiki](https://github.com/Makuna/Task/wiki) \ No newline at end of file diff --git a/library.json b/library.json index 88ab12e..0a8a5b6 100644 --- a/library.json +++ b/library.json @@ -8,6 +8,6 @@ "url": "https://github.com/Makuna/Task.git" }, "frameworks": "arduino", - "platforms": "*", + "platforms": "*" } diff --git a/library.properties b/library.properties index c9ee0f2..85c21b8 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Task by Makuna -version=1.1 +version=1.1.1 author=Michael C. Miller (makuna@live.com) maintainer=Michael C. Miller (makuna@live.com) sentence=A library that makes creating complex mulitple task projects easy. diff --git a/src/Task.h b/src/Task.h index b38d480..c2cb4a8 100644 --- a/src/Task.h +++ b/src/Task.h @@ -61,6 +61,19 @@ class Task { } + void setTimeInterval(uint32_t timeInterval) + { + _timeInterval = timeInterval; + if (_taskState == TaskState_Running) + { + _remainingTime = timeInterval; + } + } + + uint32_t getTimeInterval() + { + return _timeInterval; + } protected: virtual bool OnStart() { return true; }; diff --git a/src/TaskManager.cpp b/src/TaskManager.cpp index 01997b3..1360813 100644 --- a/src/TaskManager.cpp +++ b/src/TaskManager.cpp @@ -13,11 +13,18 @@ See GNU Lesser General Public License at . --------------------------------------------------------------------*/ #include + #include "Task.h" #include "TaskManager.h" -#if defined(ESP8266) -#else +#if defined(ARDUINO_ARCH_ESP8266) +extern "C" +{ +#include +} +#elif defined(__arm__) + +#elif defined(ARDUINO_ARCH_AVR) #include #endif @@ -27,10 +34,7 @@ TaskManager::TaskManager() : _pFirstTask( NULL ), _pLastTask( NULL ) { -#if defined(ESP8266) - //Esp.wdtFeed(); - //Esp.wdtDisable(); -#else +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_AVR) && !defined(__arm__) wdt_reset(); wdt_disable(); #endif @@ -71,12 +75,7 @@ void TaskManager::StopTask(Task* pTask) pTask->Stop(); } -#if defined(ESP8266) -//void TaskManager::Loop(uint16_t watchdogTimeOutMs) -void TaskManager::Loop() -#else void TaskManager::Loop(uint8_t watchdogTimeOutFlag) -#endif { uint32_t currentTick = GetTaskTime(); uint32_t deltaTime = currentTick - _lastTick; @@ -87,17 +86,7 @@ void TaskManager::Loop(uint8_t watchdogTimeOutFlag) uint32_t nextWakeTime = ProcessTasks(deltaTime); RemoveStoppedTasks(); - -#if defined(ESP8266) -// Esp8266 - -#if defined(USE_WDT) - //Esp.wdtFeed(); - //Esp.wdtEnable(watchdogTimeOutMs); -#endif - -#else -// Arduino Normal + // if the next task has more time available than the next // millisecond interupt, then sleep if (nextWakeTime > TaskTimePerMs) @@ -106,6 +95,20 @@ void TaskManager::Loop(uint8_t watchdogTimeOutFlag) // due to Millis() using timer interupt at 1 ms, // the cpu will be woke up by that every millisecond +#if defined(ARDUINO_ARCH_ESP8266) + // the esp8266 really doesn't have an idle mode +#if defined(USE_WDT) + // use watchdog timer for failsafe mode, + // total task update time should be less than watchdogTimeOutFlag + wdt_disable(); + wdt_enable(watchdogTimeOutFlag); +#endif + +#elif defined(__arm__) + // Arm support for sleep/idle not implemented yet + +#elif defined(ARDUINO_ARCH_AVR) + #if defined(USE_WDT) // use watchdog timer for failsafe mode, // total task update time should be less than watchdogTimeOutFlag @@ -126,24 +129,57 @@ void TaskManager::Loop(uint8_t watchdogTimeOutFlag) sei(); sleep_cpu(); // will sleep in this call sleep_disable(); +#endif // Arduino Normal } #if defined(USE_WDT) else { +#if !defined(__arm__) // no arm support for watchdog wdt_reset(); // keep the dog happy +#endif } #endif -#endif // Esp + } } -#if defined(ESP8266) -#else +#if defined(ARDUINO_ARCH_ESP8266) +#define RTC_MEM_SLEEP_ADDR 65 // 64 is being overwritten right now -void TaskManager::EnterSleep(uint8_t sleepMode) +void TaskManager::EnterSleep(uint32_t microSeconds, + void* state, + uint16_t sizeofState, + WakeMode mode) { + if (state != NULL && sizeofState > 0) + { + system_rtc_mem_write(RTC_MEM_SLEEP_ADDR, state, sizeofState); + } + ESP.deepSleep(microSeconds, mode); +} + +bool TaskManager::RestartedFromSleep(void* state, uint16_t sizeofState) +{ + rst_info* resetInfo = ESP.getResetInfoPtr(); + bool wasSleeping = (resetInfo && REASON_DEEP_SLEEP_AWAKE == resetInfo->reason); + if (wasSleeping) + { + if (state != NULL && sizeofState > 0) + { + system_rtc_mem_read(RTC_MEM_SLEEP_ADDR, state, sizeofState); + } + } + return wasSleeping; +} +#elif defined(__arm__) +// Arm support for sleep not implemented yet + +#elif defined(ARDUINO_ARCH_AVR) + +void TaskManager::EnterSleep(uint8_t sleepMode) +{ #if defined(USE_WDT) // disable watchdog so it doesn't wake us up wdt_reset(); @@ -170,9 +206,8 @@ void TaskManager::EnterSleep(uint8_t sleepMode) wdt_reset(); wdt_enable(WDTO_500MS); #endif - } -#endif // Esp +#endif uint32_t TaskManager::ProcessTasks(uint32_t deltaTime) { @@ -192,17 +227,16 @@ uint32_t TaskManager::ProcessTasks(uint32_t deltaTime) uint32_t taskDeltaTime = pIterate->_timeInterval - pIterate->_remainingTime; taskDeltaTime += deltaTime; - pIterate->OnUpdate(taskDeltaTime); - // add the initial time so we don't loose any remainders pIterate->_remainingTime += pIterate->_timeInterval; - // if we are still less than delta time, things are running slow // so push to the next update frame if (pIterate->_remainingTime <= deltaTime) { pIterate->_remainingTime = deltaTime + TaskTimeAccuracy; } + + pIterate->OnUpdate(taskDeltaTime); } uint32_t newRemainingTime = pIterate->_remainingTime - deltaTime; diff --git a/src/TaskManager.h b/src/TaskManager.h index 90d7af4..d9294be 100644 --- a/src/TaskManager.h +++ b/src/TaskManager.h @@ -14,7 +14,11 @@ See GNU Lesser General Public License at . #pragma once -#if !defined(ESP8266) +// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set +#if defined(__arm__) +#define WDTO_500MS 500 + +#elif defined(ARDUINO_ARCH_AVR) #include #include #endif @@ -25,12 +29,8 @@ class TaskManager TaskManager(); void Setup(); -#if defined(ESP8266) - // void Loop(uint16_t watchdogTimeOutMs = 500); - void Loop(); -#else + // for esp8266, its always 3.2 seconds void Loop(uint8_t watchdogTimeOutFlag = WDTO_500MS); -#endif void StartTask(Task* pTask); void StopTask(Task* pTask); void ResetTask(Task* pTask) @@ -38,9 +38,18 @@ class TaskManager StopTask(pTask); StartTask(pTask); } -#if defined(ESP8266) - -#else + +#if defined(ARDUINO_ARCH_ESP8266) + // must have GPIO16 tied to RST + void EnterSleep(uint32_t microSeconds, + void* state = NULL, + uint16_t sizeofState = 0, + WakeMode mode = WAKE_RF_DEFAULT); + bool RestartedFromSleep(void* state = NULL, + uint16_t sizeofState = 0 ); +#elif defined(__arm__) + // Arm support for sleep not implemented yet +#elif defined(ARDUINO_ARCH_AVR) void EnterSleep(uint8_t sleepMode = SLEEP_MODE_PWR_DOWN); #endif