-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathRG_Rotary_Encoder.ino
More file actions
327 lines (265 loc) · 10.6 KB
/
Copy pathRG_Rotary_Encoder.ino
File metadata and controls
327 lines (265 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
// "RG_Rotary_Encoder"
// Example sketch based on the Lilypad MP3 Player
// but modified for the the Arduino Uno/RedBoard
// Modified By: Ho Yun Bobby Chan
// By: Mike Grusin, SparkFun Electronics
// http://www.sparkfun.com
// This sketch demonstrates (only) the use of the rotary encoder
// hardware on the Lilypad MP3 Player board from Sparkfun. If you
// want to use the rotary encoder in your own sketches, this code
// will give you a starting point.
// HARDWARE CONNECTIONS
// Rotary encoder pin A to digital pin 3*
// Rotary encoder pin B to analog pin 3
// Rotary encoder pin C to ground
// This sketch implements software debounce, but you can further
// improve performance by placing 0.1uF capacitors between
// A and ground, and B and ground.
// If you wish to use the RG LED and button functions of
// SparkFun part number COM-10596 use the following connections:
// Rotary encoder pin 1 (red anode) to digital pin 10
// Rotary encoder pin 2 (green anode) to analog pin 1
// Rotary encoder pin 3 (button) to digital pin 4
// This pin is commented out since it is a RG rotary switch
// Rotary encoder pin 4 (blue cathode) to digital pin 5
// Note that because this is a common cathode device,
// the pushbutton requires an external 1K-10K pullUP resistor
// to operate.
// SERIAL MONITOR
// Run this sketch with the serial monitor window set to 115200 baud.
// HOW IT WORKS
// The I/O pins used by the rotary encoder hardware are set up to
// automatically call interrupt functions (rotaryIRQ and buttonIRQ)
// each time the rotary encoder changes states.
// The rotaryIRQ function transparently maintains a counter that
// increments or decrements by one for each detent ("click") of
// the rotary encoder knob. This function also sets a flag
// (rotary_change) to true whenever the counter changes. You can
// check this flag in your main loop() code and perform an action
// when the knob is turned.
// The buttonIRQ function does the same thing for the pushbutton
// built into the rotary encoder knob. It will set flags for
// button_pressed and button_released that you can monitor in your
// main loop() code. There is also a variable for button_downtime
// which records how long the button was held down.
// There is also code in the main loop() that keeps track
// of whether the button is currently being held down and for
// how long. This is useful for "hold button down for five seconds
// to power off"-type situations, which cannot be handled by
// interrupts alone because no interrupts will be called until
// the button is actually released.
// Uses the EnableInterrupt library from
// https://github.com/GreyGnome/EnableInterrupt
// License:
// We use the "beerware" license for our firmware. You can do
// ANYTHING you want with this code. If you like it, and we meet
// someday, you can, but are under no obligation to, buy me a
// (root) beer in return.
// Have fun!
// -your friends at SparkFun
// Revision history:
// 1.0 initial release MDG 2013/01/24
// 1.1 Use EnableInterrupt library
// Use INPUT_PULLUP
#include <EnableInterrupt.h>
// Not all of these are used in this
// sketch; the unused pins are commented out:
#define ROT_LEDG A1 // green LED
#define ROT_B A3 // rotary B
#define ROT_A 3 // rotary A
#define ROT_SW 4 // rotary puhbutton
//#define ROT_LEDB 5 // blue LED
#define ROT_LEDR 10 // red LED
// RGB LED colors (for common cathode LED, 1 is on, 0 is off)
//You will only have RG available since it is an RG Encoder
#define OFF B000
#define RED B001
#define GREEN B010
#define YELLOW B011
/*
#define BLUE B100
#define PURPLE B101
#define CYAN B110
#define WHITE B111
*/
// Global variables that can be changed in interrupt routines
volatile int rotary_counter = 0; // current "position" of rotary encoder (increments CW)
volatile boolean rotary_change = false; // will turn true if rotary_counter has changed
volatile boolean button_pressed = false; // will turn true if the button has been pushed
volatile boolean button_released = false; // will turn true if the button has been released (sets button_downtime)
volatile unsigned long button_downtime = 0L; // ms the button was pushed before release
// "Static" variables are initalized once the first time
// that loop runs, but they keep their values through
// successive loops.
static unsigned char x = 0; //LED color
static boolean button_down = false;
static unsigned long int button_down_start, button_down_time;
void setup()
{
// Set up all the I/O pins.
pinMode(ROT_B, INPUT_PULLUP);
pinMode(ROT_A, INPUT_PULLUP);
pinMode(ROT_SW, INPUT_PULLUP);
pinMode(ROT_LEDG, OUTPUT);
pinMode(ROT_LEDR, OUTPUT);
setLED(YELLOW);
Serial.begin(115200); // Use serial for debugging
Serial.println("Begin RG Rotary Encoder Testing");
enableInterrupt(ROT_A, &rotaryIRQ, CHANGE);
enableInterrupt(ROT_SW, &buttonIRQ, CHANGE);
setLED(0);
}
void buttonIRQ()
{
// Process rotary encoder button presses and releases, including
// debouncing (extra "presses" from noisy switch contacts).
// If button is pressed, the button_pressed flag is set to true.
// (Manually set this to false after handling the change.)
// If button is released, the button_released flag is set to true,
// and button_downtime will contain the duration of the button
// press in ms. (Set this to false after handling the change.)
static boolean button_state = false;
static unsigned long start, end;
int btn;
btn = digitalRead(ROT_SW);
if ((btn == LOW) && (button_state == false))
// Button was up, but is currently being pressed down
{
// Discard button presses too close together (debounce)
start = millis();
if (start > (end + 10)) // 10ms debounce timer
{
button_state = true;
button_pressed = true;
}
}
else if ((btn == HIGH) && (button_state == true))
// Button was down, but has just been released
{
// Discard button releases too close together (debounce)
end = millis();
if (end > (start + 10)) // 10ms debounce timer
{
button_state = false;
button_released = true;
button_downtime = end - start;
}
}
}
void rotaryIRQ()
{
// Process input from the rotary encoder.
// The rotary "position" is held in rotary_counter, increasing for CW rotation (changes by one per detent).
// If the position changes, rotary_change will be set true. (You may manually set this to false after handling the change).
// This function will automatically run when rotary encoder input A transitions in either direction (low to high or high to low)
// By saving the state of the A and B pins through two interrupts, we'll determine the direction of rotation
// int rotary_counter will be updated with the new value, and boolean rotary_change will be true if there was a value change
// Based on concepts from Oleg at circuits@home (http://www.circuitsathome.com/mcu/rotary-encoder-interrupt-service-routine-for-avr-micros)
// Unlike Oleg's original code, this code uses only one interrupt and has only two transition states;
// it has less resolution but needs only one interrupt, is very smooth, and handles switchbounce well.
static unsigned char rotary_state = 0; // current and previous encoder states
rotary_state <<= 2; // remember previous state
rotary_state |= (digitalRead(ROT_A) | (digitalRead(ROT_B) << 1)); // mask in current state
rotary_state &= 0x0F; // zero upper nybble
//Serial.println(rotary_state,HEX);
if (rotary_state == 0x09) // from 10 to 01, increment counter. Also try 0x06 if unreliable
{
rotary_counter++;
rotary_change = true;
}
else if (rotary_state == 0x03) // from 00 to 11, decrement counter. Also try 0x0C if unreliable
{
rotary_counter--;
rotary_change = true;
}
}
void loop()
{
static boolean long_press = false;
// The rotary IRQ sets the flag rotary_change to true
// if the knob position has changed. We can use this flag
// to do something in the main loop() each time there's
// a change. We'll clear this flag when we're done, so
// that we'll only do this if() once for each change.
if (rotary_change)
{
Serial.print("rotary: ");
Serial.println(rotary_counter, DEC);
rotary_change = false; // Clear flag
// blink for visual feedback
// Don't make delay too long, otherwise the Arduino will miss ticks
// Flash on delay doesn't need to be as long to be noticeable
// as the flash off delay does.
if ((x & YELLOW) == 0) {
setLED(RED);
delay(5);
}
else {
setLED(OFF);
delay(10);
}
setLED(x);
}
// The button IRQ also sets flags to true, one for
// button_pressed, one for button_released. Like the rotary
// flag, we'll clear these when we're done handling them.
if (button_pressed)
{
Serial.println("button press");
//x++; setLED(x); // Change the color of the knob LED
button_pressed = false; // Clear flag
// We'll set another flag saying the button is now down
// this is so we can keep track of how long the button
// is being held down. (We can't do this in interrupts,
// because the button state is not changing).
button_down = true;
button_down_start = millis();
}
if (button_released)
{
Serial.print("button release, downtime: ");
Serial.println(button_downtime, DEC);
long_press = false;
x++; setLED(x); // Change the color of the knob LED
button_released = false; // Clear flag
// Clear our button-being-held-down flag
button_down = false;
//blink when you release the button press
for (int i = 0; i < 4; i++) {
digitalWrite(ROT_LEDR, HIGH);
digitalWrite(ROT_LEDG, LOW);
//digitalWrite(ROT_LEDB, LOW);
delay(50);
digitalWrite(ROT_LEDR, LOW);
digitalWrite(ROT_LEDG, LOW);
//digitalWrite(ROT_LEDB, LOW);
delay(50);
}
setLED(x); // Change the color back to original color
}
// Now we can keep track of how long the button is being
// held down, and perform actions based on that time.
// This is useful for "hold down for five seconds to power off"
// -type functions.
if (button_down)
{
button_down_time = millis() - button_down_start;
// Serial.println(button_down_time);
if (button_down_time > 1000) {
if (!long_press)
Serial.println("button held down for one second");
long_press = true;
}
//if LED is pressed down, display red
digitalWrite(ROT_LEDR, HIGH);
digitalWrite(ROT_LEDG, LOW);
//digitalWrite(ROT_LEDB, LOW);
}
}
void setLED(unsigned char color)
// Set RGB LED to one of eight colors (see #defines above)
{
digitalWrite(ROT_LEDR, color & B001);
digitalWrite(ROT_LEDG, color & B010);
//digitalWrite(ROT_LEDB, color & B100);
}