/*
Upon activation of the button, the LED display will exhibit a distinct pattern of colors.
A solitary press of the button will result in the display showing a continuous red color.
Two presses of the button will initiate a transition of the LED display to a continuous yellow color.
In a similar manner, three presses of the button will cause the LED display to transition to a continuous green color.
Holding the button for a duration of one second will prompt the LED display to blink continuously with a yellow color.
Auther: MicroBeaut
Date: 07-APR-2024
*/
#include<Adafruit_NeoPixel.h>
#define BUTTON_PIN 7
#define NEOPIXEL_PIN 3
#define NUMBER_OF_LEDS 3
Adafruit_NeoPixel neoPixels(NUMBER_OF_LEDS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
#define DEBOUNCE_PERIOD 20 // x milliseconds
#define SHORT_PERIOD 500 // x milliseconds
#define LONG_PERIOD 1000 // x milliseconds
#define BLINK_PERIOD 500 // x milliseconds
// Avoid defining a constant using the "CRGB::color"
#define CRGB(r,g,b) ((r * 65536UL) + (g * 256UL) + b)
const uint32_t cRED = CRGB(250, 0, 0);
const uint32_t cYELLOW = CRGB(255, 200, 0);
const uint32_t cGREEN = CRGB(60, 200, 0);
const uint32_t cBLACK = CRGB(0, 0, 0);
enum lightState : int8_t {
RED,
YELLOW,
GREEN
};
enum buttonState : int8_t {
NONE = 0,
ONCE = 1,
TWICE = 2,
THRICE = 3,
SHORT = 4,
LONG = 5
};
#pragma region DEBOUNCE
typedef struct {
private:
bool prevInput;
bool pressedState;
unsigned long startTime;
public:
bool state;
bool pressed;
void mode(uint8_t inputMode);
bool debounce(bool value);
} debounce_t;
void debounce_t::mode(uint8_t inputMode) {
// When the input Mode is set to INPUT,
// the Pressed state will be "true"
pressedState = inputMode == INPUT;
// When the input Mode is set to INPUT,
// the released state will be "false"
state = !pressedState;
prevInput = state;
}
bool debounce_t::debounce(bool input) {
unsigned long currentTime = millis();
if (input == state) {
pressed = false;
} else {
if (input != prevInput) {
startTime = currentTime;
}
unsigned long elapsedTime = currentTime - startTime;
if (elapsedTime >= DEBOUNCE_PERIOD) {
state = input;
pressed = state == pressedState;;
}
}
prevInput = input;
return pressed;
}
#pragma endregion DEBOUNCE
#pragma region BUTTON_ACTION
typedef struct {
private:
bool prevInput;
bool pressedState;
bool computing;
uint8_t count;
unsigned long startTime;
public:
int8_t action;
void mode(uint8_t inputMode);
int8_t getAction(bool input);
} buttonAction_t;
void buttonAction_t::mode(uint8_t inputMode) {
// When the input Mode is set to INPUT,
// the Pressed state will be "true"
pressedState = inputMode == INPUT;
// When the input Mode is set to INPUT,
// the released state will be "false"
prevInput = !pressedState;
action = NONE;
}
int8_t buttonAction_t::getAction(bool input) {
unsigned long currentTime = millis();
action = NONE;
bool pressed = input == pressedState & prevInput != pressedState;
if (pressed) {
startTime = currentTime;
computing = true;
count++;
}
if (computing) {
unsigned long elapsedTime = currentTime - startTime;
if (input == pressedState) {
if (elapsedTime >= LONG_PERIOD) {
action = LONG;
computing = false;
count = 0;
}
} else {
if (elapsedTime >= SHORT_PERIOD) {
switch (count) {
case NONE...TWICE:
action = count;
break;
default:
action = THRICE;
break;
}
computing = false;
count = 0;
}
}
}
prevInput = input;
return action;
}
#pragma endregion BUTTON_ACTION
#pragma region LIGHT
typedef struct {
bool state;
bool blink;
} light_t;
#pragma endregion IGHT
const uint32_t lightColor[3] = {cRED, cYELLOW, cGREEN};
light_t lights[3];
debounce_t pbDebounce;
buttonAction_t pbAction;
int8_t lastIndex;
bool blinkState;
void setup() {
Serial.begin(115200);
neoPixels.begin();
// Defined the pin mode.
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Defined the debounce input mode, action input mode, and the on-changed state callback function.
pbDebounce.mode(INPUT_PULLUP);
pbAction.mode (INPUT_PULLUP);
blinkState = true;
UpdateLightState(YELLOW, true);
}
void loop() {
// Retrieve the value from the button.
bool pbState = digitalRead(BUTTON_PIN);
// Determine the button state via debouncing the pushbutton state for each type.
pbDebounce.debounce(pbState);
// Determine the action of the pushbutton for each type
// and call the OnActionChanged when the state changes.
pbAction.getAction(pbDebounce.state);
OnActionChanged(pbAction.action);
BlinkLight();
}
void OnActionChanged(int8_t acation) {
switch (acation) {
case ONCE:
UpdateLightState(RED, false);
break;
case TWICE:
UpdateLightState(YELLOW, false);
break;
case THRICE:
UpdateLightState(GREEN, false);
break;
case LONG:
UpdateLightState(YELLOW, true);
break;
}
}
void UpdateLightState(uint8_t index, bool blink) {
lastIndex = index;
Selection(lastIndex);
lights[lastIndex].blink = blink;
UpdateNeoPixels();
}
void Selection(uint8_t n) {
for (uint8_t index = 0; index < NUMBER_OF_LEDS; index++) {
lights[index].state = index == n;
if (index != n) {
lights[index].blink = false;
}
}
}
void BlinkLight() {
static unsigned long blinkStartTime;
unsigned long elapsedTime = millis() - blinkStartTime;
if (elapsedTime >= BLINK_PERIOD) {
blinkStartTime = millis() ;
blinkState = !blinkState;
digitalWrite(LED_BUILTIN, blinkState);
if (lights[YELLOW].blink) {
lights[YELLOW].state = blinkState;
UpdateNeoPixels();
}
}
}
void UpdateNeoPixels() {
for (uint8_t index = 0; index < NUMBER_OF_LEDS; index++) {
if (lights[index].state) neoPixels.setPixelColor(index, lightColor[index]);
else neoPixels.setPixelColor(index, cBLACK);
}
neoPixels.show();
}