Skip to content

Commit

Permalink
Merge pull request #5 from Makuna/FeatureUpdate
Browse files Browse the repository at this point in the history
feature work
  • Loading branch information
Makuna committed Mar 22, 2016
2 parents 128f21b + 7d105ce commit de9bbf1
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 189 deletions.
159 changes: 12 additions & 147 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"url": "https://github.com/Makuna/Task.git"
},
"frameworks": "arduino",
"platforms": "*",
"platforms": "*"
}

2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=Task by Makuna
version=1.1
version=1.1.1
author=Michael C. Miller ([email protected])
maintainer=Michael C. Miller ([email protected])
sentence=A library that makes creating complex mulitple task projects easy.
Expand Down
13 changes: 13 additions & 0 deletions src/Task.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; };
Expand Down
96 changes: 65 additions & 31 deletions src/TaskManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@ See GNU Lesser General Public License at <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------*/

#include <Arduino.h>

#include "Task.h"
#include "TaskManager.h"

#if defined(ESP8266)
#else
#if defined(ARDUINO_ARCH_ESP8266)
extern "C"
{
#include <user_interface.h>
}
#elif defined(__arm__)

#elif defined(ARDUINO_ARCH_AVR)
#include <avr/power.h>
#endif

Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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();
Expand All @@ -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)
{
Expand All @@ -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;
Expand Down
Loading

0 comments on commit de9bbf1

Please sign in to comment.