From d0c316b455605f98a9bd2b1584af229c10addfc4 Mon Sep 17 00:00:00 2001 From: Makuna Date: Tue, 9 Feb 2016 14:02:45 -0800 Subject: [PATCH] New Rotary Encoder Example Updated library manager properties --- README.md | 12 +- examples/RotaryEncoder/RotaryEncoder.ino | 84 ++++++++++ examples/RotaryEncoder/RotaryEncoderTask.h | 183 +++++++++++++++++++++ library.json | 13 ++ library.properties | 4 +- 5 files changed, 293 insertions(+), 3 deletions(-) create mode 100644 examples/RotaryEncoder/RotaryEncoder.ino create mode 100644 examples/RotaryEncoder/RotaryEncoderTask.h create mode 100644 library.json diff --git a/README.md b/README.md index fe84f4c..5a23be6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ Arduino Nonpreemptive multitasking library -Clone this into your Arduino\Library folder. +For the latest Arduino IDE, use the library manager to find and install "Task by Makuna" library. + +For older versions, you can just clone this into your Arduino\Library folder. 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. @@ -10,6 +12,9 @@ This library was not based on that work though, but they share common goals. NOTE: Avoid the use of Delay in all high level code as the tasks timing should be used to replace it. ## Installing This Library +Open the Library Manager and search for "Task by Makuna" and install + +## Installing This Library From GitHub 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. @@ -120,6 +125,11 @@ It will instance two custom ButtonTasks to monitor two different pins and they w 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. diff --git a/examples/RotaryEncoder/RotaryEncoder.ino b/examples/RotaryEncoder/RotaryEncoder.ino new file mode 100644 index 0000000..d8e7f99 --- /dev/null +++ b/examples/RotaryEncoder/RotaryEncoder.ino @@ -0,0 +1,84 @@ +// RotaryEncoder +// This demonstrates the use of the custom Task object feature of Task library +// It will instance one custom RotaryEncoderTask to monitor three pins that a +// rotary encoder is connected to and make call backs when they change state; +// the press button will have debouce and auto repeat support; +// the rotary will tell direction and keep track of "clicks" rotated +// This requires a standard rotary encoder be attached to the defined pins, +// +// Usefull information will be sent to the serial monitor +// +// make sure that you connect your rotary encoder correctly, include gnd and Vcc (+) + +#include + +// nothing special about these pins, just make sure they don't collide with +// other active features +#define ClockPin 4 // labeled either as CLK or as A +#define DataPin 5 // labeled either as DT or as B +#define ButtonPin 2 // labeled as SW + +// include libraries +#include + +// include sub files +#include "RotaryEncoderTask.h" // this implements the rotary encoder task + +TaskManager taskManager; + +// foreward delcare functions passed to task constructors now required +void HandleButtonChanged(EncoderButtonState state); +void HandleRotationChanged(int8_t rotationDelta); + + +RotaryEncoderTask RotaryTask(HandleRotationChanged, + HandleButtonChanged, + ClockPin, + DataPin, + ButtonPin); + +void setup() +{ + Serial.begin(57600); + while (!Serial); // wait for serial monitor to connect + + + taskManager.StartTask(&RotaryTask); + + Serial.println("Running..."); +} + +void loop() +{ + taskManager.Loop(); +} + +void HandleButtonChanged(EncoderButtonState state) +{ + if (state == EncoderButtonState_Pressed) + { + Serial.println("Pressed - "); + } + else if (state == EncoderButtonState_Released) + { + Serial.println("Released - "); + } + else + { + Serial.println("Auto-repeat - "); + } +} + +void HandleRotationChanged(int8_t rotationDelta) +{ + if (rotationDelta > 0) + { + Serial.print("clockwise = "); + } + else + { + Serial.print("counter-clockwise = "); + } + + Serial.println(RotaryTask.RotationValue()); +} diff --git a/examples/RotaryEncoder/RotaryEncoderTask.h b/examples/RotaryEncoder/RotaryEncoderTask.h new file mode 100644 index 0000000..9eb3657 --- /dev/null +++ b/examples/RotaryEncoder/RotaryEncoderTask.h @@ -0,0 +1,183 @@ +// This assumes the encoder is in a circuit that pulls the pins high in the normal off state +// and when then encoder is rotated the pin is pulled low +// +// you can just copy this whole file into your project and use it + +enum EncoderButtonState +{ + EncoderButtonState_Released = 0b00000000, + EncoderButtonState_Pressed = 0b00000001, + EncoderButtonState_AutoRepeat = 0b00000011, + EncoderButtonState_Tracking = 0b10000001 +}; + +class RotaryEncoderTask : public Task +{ +public: + typedef void(*buttonAction)(EncoderButtonState state); + typedef void(*rotationAction)(int8_t rotationDelta); + + RotaryEncoderTask(rotationAction rotationFunction, + buttonAction buttonFunction, + uint8_t pinClock, // also refered to as A + uint8_t pinData, // also refered to as B + uint8_t pinButton, + int32_t rotationValue = 0) : + Task(MsToTaskTime(5)), // check every five millisecond, 1-10 ms should be ok + _buttonPin(pinButton), + _clockPin(pinClock), + _dataPin(pinData), + _rotationCallback(rotationFunction), + _buttonCallback(buttonFunction), + _buttonState(EncoderButtonState_Released), + _clockState(HIGH), + _rotationValue(rotationValue) + { + }; + + int32_t RotationValue() + { + return _rotationValue; + } + +private: + static const uint16_t _debouceMs = 50; // (30-100) are good values + static const uint16_t _repeatDelayMs = 600; // (400 - 1200) are reasonable values + static const uint16_t _repeatRateMs = 50; // (40-1000) are reasonable + + const uint8_t _buttonPin; + const uint8_t _clockPin; + const uint8_t _dataPin; + + const rotationAction _rotationCallback; + const buttonAction _buttonCallback; + + int32_t _rotationValue; + uint16_t _buttonTimer; + + EncoderButtonState _buttonState; + uint8_t _clockState; + + + virtual bool OnStart() + { + pinMode(_buttonPin, INPUT_PULLUP); + pinMode(_clockPin, INPUT_PULLUP); + pinMode(_dataPin, INPUT_PULLUP); + _buttonState = EncoderButtonState_Released; + _clockState = HIGH; + return true; + } + + virtual void OnUpdate(uint32_t deltaTime) + { + uint16_t deltaTimeMs = TaskTimeToMs(deltaTime); + + CheckButton(deltaTimeMs); + + // ignore the rotary if button is pressed + if (_buttonState == EncoderButtonState_Released) + { + CheckRotation(deltaTimeMs); + } + } + + void CheckRotation(uint16_t deltaTimeMs) + { + uint8_t clockState = digitalRead(_clockPin); + uint8_t dataState = digitalRead(_dataPin); + + // check for any change in the clock pin state + if (clockState != _clockState) + { + if (clockState == LOW) + { + // just read clock trigger + if (dataState == LOW) + { + // clockwise + _rotationValue++; + _rotationCallback(1); + } + else + { + // counter-clockwise + _rotationValue--; + _rotationCallback(-1); + } + } + + _clockState = clockState; + } + } + + void CheckButton(uint16_t deltaTimeMs) + { + EncoderButtonState pinState = (digitalRead(_buttonPin) == LOW) ? EncoderButtonState_Pressed : EncoderButtonState_Released; + + if (pinState != (_buttonState & EncoderButtonState_Pressed)) + { + if (pinState == EncoderButtonState_Pressed) + { + // just read button down and start timer + _buttonTimer = _debouceMs; + _buttonState = EncoderButtonState_Tracking; + } + else + { + if ((_buttonState & EncoderButtonState_Tracking) == EncoderButtonState_Pressed) // not tracking + { + // triggered released + _buttonCallback(EncoderButtonState_Released); + } + _buttonState = EncoderButtonState_Released; + } + } + else + { + switch (_buttonState) + { + case EncoderButtonState_Tracking: + if (deltaTimeMs >= _buttonTimer) + { + // press debounced + _buttonState = EncoderButtonState_Pressed; + _buttonTimer = _repeatDelayMs; + _buttonCallback(EncoderButtonState_Pressed); + } + else + { + _buttonTimer -= deltaTimeMs; + } + break; + + case EncoderButtonState_Pressed: + if (deltaTimeMs >= _buttonTimer) + { + // auto repeat started + _buttonState = EncoderButtonState_AutoRepeat; + _buttonTimer = _repeatRateMs; + _buttonCallback(EncoderButtonState_AutoRepeat); + } + else + { + _buttonTimer -= deltaTimeMs; + } + break; + + case EncoderButtonState_AutoRepeat: + if (deltaTimeMs >= _buttonTimer) + { + // auto repeat triggered again + _buttonTimer += _repeatRateMs; + _buttonCallback(EncoderButtonState_AutoRepeat); + } + else + { + _buttonTimer -= deltaTimeMs; + } + break; + } + } + } +}; \ No newline at end of file diff --git a/library.json b/library.json new file mode 100644 index 0000000..88ab12e --- /dev/null +++ b/library.json @@ -0,0 +1,13 @@ +{ + "name": "Task by Makuna", + "keywords": "multitasking, task, timer", + "description": "A library that makes creating complex mulitple task projects easy.", + "repository": + { + "type": "git", + "url": "https://github.com/Makuna/Task.git" + }, + "frameworks": "arduino", + "platforms": "*", +} + diff --git a/library.properties b/library.properties index baeb4a5..c9ee0f2 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=Task by Makuna -version=1.0 +version=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. -paragraph=This implements a Nonpreemptive multitasking library which is effecient in speed and memory, which is good for small Arduino hardware. While multitasking is an advanced topic, our friends at AdaFruit have a great article on it here (https://learn.adafruit.com/multi-tasking-the-arduino-part-1?view=all), Samples include blinking an LED without using delay(), monitoring and reacting to a button press, and cross task messaging. Tested on esp8266. +paragraph=This implements a Nonpreemptive multitasking library which is effecient in speed and memory, which is good for small Arduino hardware. While multitasking is an advanced topic, our friends at AdaFruit have a great article on it here (https://learn.adafruit.com/multi-tasking-the-arduino-part-1?view=all), Samples include blinking an LED without using delay(), monitoring and reacting to a button press, cross task messaging, and rotary encoder. Tested on AVR and esp8266. category=Timing url=https://github.com/Makuna/Task architectures=* \ No newline at end of file