Skip to content

Commit

Permalink
Merge pull request #2 from Makuna/rotaryencoderexample
Browse files Browse the repository at this point in the history
New Rotary Encoder Example
  • Loading branch information
Makuna committed Feb 9, 2016
2 parents a69d3d8 + d0c316b commit 128f21b
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 3 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

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.

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.
Expand Down Expand Up @@ -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.
Expand Down
84 changes: 84 additions & 0 deletions examples/RotaryEncoder/RotaryEncoder.ino
Original file line number Diff line number Diff line change
@@ -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 <Arduino.h>

// 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 <Task.h>

// 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());
}
183 changes: 183 additions & 0 deletions examples/RotaryEncoder/RotaryEncoderTask.h
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
};
13 changes: 13 additions & 0 deletions library.json
Original file line number Diff line number Diff line change
@@ -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": "*",
}

4 changes: 2 additions & 2 deletions library.properties
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name=Task by Makuna
version=1.0
version=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.
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=*

0 comments on commit 128f21b

Please sign in to comment.