Skip to content

Commit 128f21b

Browse files
committed
Merge pull request #2 from Makuna/rotaryencoderexample
New Rotary Encoder Example
2 parents a69d3d8 + d0c316b commit 128f21b

File tree

5 files changed

+293
-3
lines changed

5 files changed

+293
-3
lines changed

README.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@
22

33
Arduino Nonpreemptive multitasking library
44

5-
Clone this into your Arduino\Library folder.
5+
For the latest Arduino IDE, use the library manager to find and install "Task by Makuna" library.
6+
7+
For older versions, you can just clone this into your Arduino\Library folder.
68

79
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.
810
This library was not based on that work though, but they share common goals.
911

1012
NOTE: Avoid the use of Delay in all high level code as the tasks timing should be used to replace it.
1113

1214
## Installing This Library
15+
Open the Library Manager and search for "Task by Makuna" and install
16+
17+
## Installing This Library From GitHub
1318
Create a directory in your Arduino\Library folder named "Task"
1419
Clone (Git) this project into that folder.
1520
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
120125

121126
This requires two momentary buttons attached between pins 4 & 5 and ground, you can change the pins to any digital pin
122127

128+
### RotaryEncoder
129+
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.
130+
131+
This requires a common rotary encoder be connected to available pins.
132+
123133
## Sleep Modes (advanced feature)
124134
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.
125135
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.
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// RotaryEncoder
2+
// This demonstrates the use of the custom Task object feature of Task library
3+
// It will instance one custom RotaryEncoderTask to monitor three pins that a
4+
// rotary encoder is connected to and make call backs when they change state;
5+
// the press button will have debouce and auto repeat support;
6+
// the rotary will tell direction and keep track of "clicks" rotated
7+
// This requires a standard rotary encoder be attached to the defined pins,
8+
//
9+
// Usefull information will be sent to the serial monitor
10+
//
11+
// make sure that you connect your rotary encoder correctly, include gnd and Vcc (+)
12+
13+
#include <Arduino.h>
14+
15+
// nothing special about these pins, just make sure they don't collide with
16+
// other active features
17+
#define ClockPin 4 // labeled either as CLK or as A
18+
#define DataPin 5 // labeled either as DT or as B
19+
#define ButtonPin 2 // labeled as SW
20+
21+
// include libraries
22+
#include <Task.h>
23+
24+
// include sub files
25+
#include "RotaryEncoderTask.h" // this implements the rotary encoder task
26+
27+
TaskManager taskManager;
28+
29+
// foreward delcare functions passed to task constructors now required
30+
void HandleButtonChanged(EncoderButtonState state);
31+
void HandleRotationChanged(int8_t rotationDelta);
32+
33+
34+
RotaryEncoderTask RotaryTask(HandleRotationChanged,
35+
HandleButtonChanged,
36+
ClockPin,
37+
DataPin,
38+
ButtonPin);
39+
40+
void setup()
41+
{
42+
Serial.begin(57600);
43+
while (!Serial); // wait for serial monitor to connect
44+
45+
46+
taskManager.StartTask(&RotaryTask);
47+
48+
Serial.println("Running...");
49+
}
50+
51+
void loop()
52+
{
53+
taskManager.Loop();
54+
}
55+
56+
void HandleButtonChanged(EncoderButtonState state)
57+
{
58+
if (state == EncoderButtonState_Pressed)
59+
{
60+
Serial.println("Pressed - ");
61+
}
62+
else if (state == EncoderButtonState_Released)
63+
{
64+
Serial.println("Released - ");
65+
}
66+
else
67+
{
68+
Serial.println("Auto-repeat - ");
69+
}
70+
}
71+
72+
void HandleRotationChanged(int8_t rotationDelta)
73+
{
74+
if (rotationDelta > 0)
75+
{
76+
Serial.print("clockwise = ");
77+
}
78+
else
79+
{
80+
Serial.print("counter-clockwise = ");
81+
}
82+
83+
Serial.println(RotaryTask.RotationValue());
84+
}
+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// This assumes the encoder is in a circuit that pulls the pins high in the normal off state
2+
// and when then encoder is rotated the pin is pulled low
3+
//
4+
// you can just copy this whole file into your project and use it
5+
6+
enum EncoderButtonState
7+
{
8+
EncoderButtonState_Released = 0b00000000,
9+
EncoderButtonState_Pressed = 0b00000001,
10+
EncoderButtonState_AutoRepeat = 0b00000011,
11+
EncoderButtonState_Tracking = 0b10000001
12+
};
13+
14+
class RotaryEncoderTask : public Task
15+
{
16+
public:
17+
typedef void(*buttonAction)(EncoderButtonState state);
18+
typedef void(*rotationAction)(int8_t rotationDelta);
19+
20+
RotaryEncoderTask(rotationAction rotationFunction,
21+
buttonAction buttonFunction,
22+
uint8_t pinClock, // also refered to as A
23+
uint8_t pinData, // also refered to as B
24+
uint8_t pinButton,
25+
int32_t rotationValue = 0) :
26+
Task(MsToTaskTime(5)), // check every five millisecond, 1-10 ms should be ok
27+
_buttonPin(pinButton),
28+
_clockPin(pinClock),
29+
_dataPin(pinData),
30+
_rotationCallback(rotationFunction),
31+
_buttonCallback(buttonFunction),
32+
_buttonState(EncoderButtonState_Released),
33+
_clockState(HIGH),
34+
_rotationValue(rotationValue)
35+
{
36+
};
37+
38+
int32_t RotationValue()
39+
{
40+
return _rotationValue;
41+
}
42+
43+
private:
44+
static const uint16_t _debouceMs = 50; // (30-100) are good values
45+
static const uint16_t _repeatDelayMs = 600; // (400 - 1200) are reasonable values
46+
static const uint16_t _repeatRateMs = 50; // (40-1000) are reasonable
47+
48+
const uint8_t _buttonPin;
49+
const uint8_t _clockPin;
50+
const uint8_t _dataPin;
51+
52+
const rotationAction _rotationCallback;
53+
const buttonAction _buttonCallback;
54+
55+
int32_t _rotationValue;
56+
uint16_t _buttonTimer;
57+
58+
EncoderButtonState _buttonState;
59+
uint8_t _clockState;
60+
61+
62+
virtual bool OnStart()
63+
{
64+
pinMode(_buttonPin, INPUT_PULLUP);
65+
pinMode(_clockPin, INPUT_PULLUP);
66+
pinMode(_dataPin, INPUT_PULLUP);
67+
_buttonState = EncoderButtonState_Released;
68+
_clockState = HIGH;
69+
return true;
70+
}
71+
72+
virtual void OnUpdate(uint32_t deltaTime)
73+
{
74+
uint16_t deltaTimeMs = TaskTimeToMs(deltaTime);
75+
76+
CheckButton(deltaTimeMs);
77+
78+
// ignore the rotary if button is pressed
79+
if (_buttonState == EncoderButtonState_Released)
80+
{
81+
CheckRotation(deltaTimeMs);
82+
}
83+
}
84+
85+
void CheckRotation(uint16_t deltaTimeMs)
86+
{
87+
uint8_t clockState = digitalRead(_clockPin);
88+
uint8_t dataState = digitalRead(_dataPin);
89+
90+
// check for any change in the clock pin state
91+
if (clockState != _clockState)
92+
{
93+
if (clockState == LOW)
94+
{
95+
// just read clock trigger
96+
if (dataState == LOW)
97+
{
98+
// clockwise
99+
_rotationValue++;
100+
_rotationCallback(1);
101+
}
102+
else
103+
{
104+
// counter-clockwise
105+
_rotationValue--;
106+
_rotationCallback(-1);
107+
}
108+
}
109+
110+
_clockState = clockState;
111+
}
112+
}
113+
114+
void CheckButton(uint16_t deltaTimeMs)
115+
{
116+
EncoderButtonState pinState = (digitalRead(_buttonPin) == LOW) ? EncoderButtonState_Pressed : EncoderButtonState_Released;
117+
118+
if (pinState != (_buttonState & EncoderButtonState_Pressed))
119+
{
120+
if (pinState == EncoderButtonState_Pressed)
121+
{
122+
// just read button down and start timer
123+
_buttonTimer = _debouceMs;
124+
_buttonState = EncoderButtonState_Tracking;
125+
}
126+
else
127+
{
128+
if ((_buttonState & EncoderButtonState_Tracking) == EncoderButtonState_Pressed) // not tracking
129+
{
130+
// triggered released
131+
_buttonCallback(EncoderButtonState_Released);
132+
}
133+
_buttonState = EncoderButtonState_Released;
134+
}
135+
}
136+
else
137+
{
138+
switch (_buttonState)
139+
{
140+
case EncoderButtonState_Tracking:
141+
if (deltaTimeMs >= _buttonTimer)
142+
{
143+
// press debounced
144+
_buttonState = EncoderButtonState_Pressed;
145+
_buttonTimer = _repeatDelayMs;
146+
_buttonCallback(EncoderButtonState_Pressed);
147+
}
148+
else
149+
{
150+
_buttonTimer -= deltaTimeMs;
151+
}
152+
break;
153+
154+
case EncoderButtonState_Pressed:
155+
if (deltaTimeMs >= _buttonTimer)
156+
{
157+
// auto repeat started
158+
_buttonState = EncoderButtonState_AutoRepeat;
159+
_buttonTimer = _repeatRateMs;
160+
_buttonCallback(EncoderButtonState_AutoRepeat);
161+
}
162+
else
163+
{
164+
_buttonTimer -= deltaTimeMs;
165+
}
166+
break;
167+
168+
case EncoderButtonState_AutoRepeat:
169+
if (deltaTimeMs >= _buttonTimer)
170+
{
171+
// auto repeat triggered again
172+
_buttonTimer += _repeatRateMs;
173+
_buttonCallback(EncoderButtonState_AutoRepeat);
174+
}
175+
else
176+
{
177+
_buttonTimer -= deltaTimeMs;
178+
}
179+
break;
180+
}
181+
}
182+
}
183+
};

library.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "Task by Makuna",
3+
"keywords": "multitasking, task, timer",
4+
"description": "A library that makes creating complex mulitple task projects easy.",
5+
"repository":
6+
{
7+
"type": "git",
8+
"url": "https://github.com/Makuna/Task.git"
9+
},
10+
"frameworks": "arduino",
11+
"platforms": "*",
12+
}
13+

library.properties

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name=Task by Makuna
2-
version=1.0
2+
version=1.1
33
author=Michael C. Miller ([email protected])
44
maintainer=Michael C. Miller ([email protected])
55
sentence=A library that makes creating complex mulitple task projects easy.
6-
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.
6+
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.
77
category=Timing
88
url=https://github.com/Makuna/Task
99
architectures=*

0 commit comments

Comments
 (0)