-
Notifications
You must be signed in to change notification settings - Fork 37
/
Copy pathLadderButtons.ino
153 lines (132 loc) · 5.36 KB
/
LadderButtons.ino
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
/*
* A demo of LadderButtonConfig for buttons connected to a single pin
* using a resistor ladder.
*/
#include <stdint.h>
#include <Arduino.h>
#include <AceButton.h>
using ace_button::AceButton;
using ace_button::ButtonConfig;
using ace_button::LadderButtonConfig;
//-----------------------------------------------------------------------------
// Configure built-in LED
//-----------------------------------------------------------------------------
#if defined(ESP32)
// Different ESP32 boards use different pins, so you have to supply the
// correct value here.
static const int LED_PIN = 2;
#else
static const int LED_PIN = LED_BUILTIN;
#endif
// LED states. On (most?) AVR processors, HIGH turns on the LED. But on other
// microcontrollers (e.g. ESP8266), the LED polarity is reversed.
#if defined(ESP8266)
static const int LED_ON = LOW;
static const int LED_OFF = HIGH;
#else
static const int LED_ON = HIGH;
static const int LED_OFF = LOW;
#endif
//-----------------------------------------------------------------------------
// Configure AceButton
//-----------------------------------------------------------------------------
// The ADC pin used by the resistor ladder.
static const uint8_t BUTTON_PIN = A0;
// Create 4 AceButton objects, with their corresonding virtual pin numbers 0 to
// 3. Note that we could use an array of `AceButton BUTTONS[NUM_BUTTONS]`, and
// use a loop in setup() to initialize these buttons. But I think writing this
// out explicitly is easier to understand for demo purposes.
//
// We use the 4-parameter AceButton() constructor with the `buttonConfig`
// parameter set to `nullptr` to prevent the creation of the default
// SystemButtonConfig which will never be used. This saves about 30 bytes of
// flash and 26 bytes of static RAM on an AVR processor.
static const uint8_t NUM_BUTTONS = 4;
static AceButton b0(nullptr, 0);
static AceButton b1(nullptr, 1);
static AceButton b2(nullptr, 2);
static AceButton b3(nullptr, 3);
// button 4 cannot be used because it represents "no button pressed"
static AceButton* const BUTTONS[NUM_BUTTONS] = {
&b0, &b1, &b2, &b3,
};
// Define the ADC voltage levels for each button. In this example, we want 4
// buttons, so we need 5 levels. Ideally, the voltage levels should correspond
// to 0%, 25%, 50%, 75%, 100%. We can get pretty close by using some common
// resistor values (4.7k, 10k, 47k). Use the examples/LadderButtonCalibrator
// program to double-check these calculated values.
static const uint8_t NUM_LEVELS = NUM_BUTTONS + 1;
static const uint16_t LEVELS[NUM_LEVELS] = {
0 /* 0%, short to ground */,
327 /* 32%, 4.7 kohm */,
512 /* 50%, 10 kohm */,
844 /* 82%, 47 kohm */,
1023 /* 100%, open circuit */,
};
// The LadderButtonConfig constructor binds the AceButton objects in the BUTTONS
// array to the LadderButtonConfig.
static LadderButtonConfig buttonConfig(
BUTTON_PIN, NUM_LEVELS, LEVELS, NUM_BUTTONS, BUTTONS
);
// The event handler for the buttons.
void handleEvent(AceButton* button, uint8_t eventType, uint8_t buttonState) {
// Print out a message for all events.
Serial.print(F("handleEvent(): "));
Serial.print(F("virtualPin: "));
Serial.print(button->getPin());
Serial.print(F("; eventType: "));
Serial.print(AceButton::eventName(eventType));
Serial.print(F("; buttonState: "));
Serial.println(buttonState);
// Control the LED only for the Pressed and Released events.
// Notice that if the MCU is rebooted while the button is pressed down, no
// event is triggered and the LED remains off.
switch (eventType) {
case AceButton::kEventPressed:
digitalWrite(LED_PIN, LED_ON);
break;
case AceButton::kEventReleased:
digitalWrite(LED_PIN, LED_OFF);
break;
}
}
// On most processors, this should be called every 4-5ms or faster, if the
// default debouncing time is ~20ms. On a ESP8266, we must sample *no* faster
// than 4-5 ms to avoid disconnecting the WiFi connection. See
// https://github.com/esp8266/Arduino/issues/1634 and
// https://github.com/esp8266/Arduino/issues/5083. To be safe, let's rate-limit
// this on all processors to about 200 samples/second.
void checkButtons() {
static uint16_t prev = millis();
// DO NOT USE delay(5) to do this.
// The (uint16_t) cast is required on 32-bit processors, harmless on 8-bit.
uint16_t now = millis();
if ((uint16_t) (now - prev) >= 5) {
prev = now;
buttonConfig.checkButtons();
}
}
//-----------------------------------------------------------------------------
void setup() {
delay(1000); // some microcontrollers reboot twice
Serial.begin(115200);
while (! Serial); // Wait until Serial is ready - Leonardo/Micro
Serial.println(F("setup(): begin"));
// Initialize built-in LED as an output, and start with LED off.
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LED_OFF);
// Don't use internal pull-up resistor because it will change the effective
// resistance of the resistor ladder.
pinMode(BUTTON_PIN, INPUT);
// Configure the ButtonConfig with the event handler, and enable all higher
// level events.
buttonConfig.setEventHandler(handleEvent);
buttonConfig.setFeature(ButtonConfig::kFeatureClick);
buttonConfig.setFeature(ButtonConfig::kFeatureDoubleClick);
buttonConfig.setFeature(ButtonConfig::kFeatureLongPress);
buttonConfig.setFeature(ButtonConfig::kFeatureRepeatPress);
Serial.println(F("setup(): ready"));
}
void loop() {
checkButtons();
}