diff --git a/src/button.cpp b/src/button.cpp new file mode 100644 index 0000000..32e2466 --- /dev/null +++ b/src/button.cpp @@ -0,0 +1,38 @@ +#include "button.h" + +#include +#include "countdown.h" + +bool buttonLastState = false; + +bool buttonLongPressed = false; + +unsigned long buttonLastMillis = 0; + +void buttonSetup() { + pinMode(25, INPUT_PULLUP); + buttonLastState = digitalRead(25) == LOW; + buttonLastMillis = millis(); +} + +void buttonLoop() { + const auto currentMillis = millis(); + const auto durationMillis = currentMillis - buttonLastMillis; + if (durationMillis >= 100) { + const auto currentState = digitalRead(25) == LOW; + if (buttonLastState != currentState) { + buttonLastState = currentState; + buttonLastMillis = currentMillis; + buttonLongPressed = false; + if (!buttonLastState) { + if (durationMillis < 1000) { + countdownShortPress(); + } + } + } + } + if (buttonLastState && currentMillis - buttonLastMillis >= 1000 && !buttonLongPressed) { + buttonLongPressed = true; + countdownLongPress(); + } +} \ No newline at end of file diff --git a/src/button.h b/src/button.h new file mode 100644 index 0000000..61b71bb --- /dev/null +++ b/src/button.h @@ -0,0 +1,8 @@ +#ifndef WS2812B_BUTTON_H +#define WS2812B_BUTTON_H + +void buttonSetup(); + +void buttonLoop(); + +#endif diff --git a/src/countdown.cpp b/src/countdown.cpp new file mode 100644 index 0000000..db422bf --- /dev/null +++ b/src/countdown.cpp @@ -0,0 +1,173 @@ +#include "countdown.h" + +#include "display.h" + +enum State { + CONFIG, READY, RUNNING, PAUSED, END +}; + +State state = CONFIG; + +long configMillis = 6 * 60 * 1000; + +long rest = configMillis; + +unsigned long last = 0; + +void updateTime() { + if (state != RUNNING) { + return; + } + const auto now = max(1UL, millis()); + if (last != 0) { + const auto diff = now - last; + rest -= (long) diff; + if (rest < 0) { + rest = 0; + } + } + last = now; +} + +void drawDashes() { + displayClear(); + drawChar(0, '-', true, RED); + drawChar(1, '-', true, RED); + drawChar(2, '-', true, RED); + drawChar(3, '-', true, RED); + displayShow(); +} + +void drawWhite() { + displayClear(); + drawChar(0, '8', true, WHITE); + drawChar(1, '8', true, WHITE); + drawDots(WHITE, WHITE, WHITE, WHITE); + drawChar(2, '8', true, WHITE); + drawChar(3, '8', true, WHITE); + displayShow(); +} + +void drawBlack() { + displayClear(); + displayShow(); +} + +void drawSequence() { + for (int x = 0; x < 3; ++x) { + for (int i = 0; i < 2; ++i) { + drawWhite(); + delay(100); + drawBlack(); + delay(100); + } + drawDashes(); + delay(500); + } +} + +void setState(State newState) { + if (state == newState) { + return; + } + + state = newState; + const char *name; + switch (state) { + case CONFIG: + rest = configMillis; + name = "CONFIG"; + break; + case READY: + rest = configMillis; + name = "READY"; + break; + case RUNNING: + last = millis(); + name = "RUNNING"; + break; + case PAUSED: + name = "PAUSED"; + break; + case END: + name = "END"; + break; + default: + name = "[???]"; + break; + } + Serial.printf("Mode: %s\n", name); +} + +void countdownUpdate() { + switch (state) { + case CONFIG: + drawMillis(rest, MAGENTA); + break; + case READY: + drawMillis(rest, GREEN); + break; + case RUNNING: { + if (rest > 0) { + const auto color = rest >= 60000 ? WHITE : (rest >= 10000 ? YELLOW : RED); + drawMillis(rest, color); + } else { + drawSequence(); + setState(END); + } + break; + } + case PAUSED: + drawMillis(rest, BLUE); + break; + case END: + drawDashes(); + break; + } +} + +void countdownSetup() { + setState(READY); +} + +void countdownLoop() { + updateTime(); + countdownUpdate(); +} + +void countdownLongPress() { + switch (state) { + case CONFIG: + setState(READY); + break; + case READY: + case END: + setState(CONFIG); + break; + case RUNNING: + case PAUSED: + setState(READY); + break; + } +} + +void countdownShortPress() { + switch (state) { + case CONFIG: { + auto seconds = (configMillis / 30000) * 30 % (15 * 60) + 30; + configMillis = seconds * 1000; + rest = configMillis; + break; + } + case RUNNING: + setState(PAUSED); + break; + case READY: + case PAUSED: + setState(RUNNING); + break; + case END: + setState(READY); + break; + } +} \ No newline at end of file diff --git a/src/countdown.h b/src/countdown.h new file mode 100644 index 0000000..6e3997b --- /dev/null +++ b/src/countdown.h @@ -0,0 +1,12 @@ +#ifndef WS2812B_COUNTDOWN_H +#define WS2812B_COUNTDOWN_H + +void countdownSetup(); + +void countdownLoop(); + +void countdownLongPress(); + +void countdownShortPress(); + +#endif diff --git a/src/display.cpp b/src/display.cpp index af40895..a93fccf 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -1,11 +1,18 @@ #include "display.h" +#define DIGIT_COUNT 4 #define SEGMENTS_PER_DIGIT 7 #define LEDS_PER_SEGMENT 6 #define LEDS_PER_DOT 2 #define DOT_COUNT 4 #define DOTS_AFTER_DIGIT 1 +#define GPIO 26 +#define PIXELS (DIGIT_COUNT * SEGMENTS_PER_DIGIT * LEDS_PER_SEGMENT + DOT_COUNT * LEDS_PER_DOT) +#define BRIGHTNESS 32 + +Adafruit_NeoPixel pixels(PIXELS, GPIO, NEO_GRB + NEO_KHZ800); + uint8_t CHARS[] = { 0b11111100, // 0 0b00110000, // 1 @@ -50,11 +57,26 @@ uint8_t mapChar(char c) { return 0; } -void drawChar(Adafruit_NeoPixel& pixels, int digit, char c, const Color& color) { - drawChar(pixels, digit, c, true, color); +void displaySetup() { + pixels.begin(); + pixels.setBrightness(BRIGHTNESS); + pixels.clear(); + pixels.show(); } -void drawChar(Adafruit_NeoPixel& pixels, int digit, char c, bool showIfZero, const Color& color) { +void displayClear() { + pixels.clear(); +} + +void displayShow() { + pixels.show(); +} + +void drawChar(int digit, char c, const Color& color) { + drawChar(digit, c, true, color); +} + +void drawChar(int digit, char c, bool showIfZero, const Color& color) { if (c == '0' && !showIfZero) { return; } @@ -75,55 +97,57 @@ void drawChar(Adafruit_NeoPixel& pixels, int digit, char c, bool showIfZero, con } } -void drawNumber(Adafruit_NeoPixel& pixels, int digit, int number, bool zero, const Color& color) { +void drawNumber(int digit, int number, bool zero, const Color& color) { const char ten = '0' + (number / 10 % 10); const char one = '0' + (number % 10); - drawChar(pixels, digit + 0, ten, zero, color); - drawChar(pixels, digit + 1, one, true, color); + drawChar(digit + 0, ten, zero, color); + drawChar(digit + 1, one, true, color); } -void drawDot(Adafruit_NeoPixel& pixels, int dot, const Color& c) { +void drawDot(int dot, const Color& c) { int index = 2 * SEGMENTS_PER_DIGIT * LEDS_PER_SEGMENT + dot * LEDS_PER_DOT; pixels.setPixelColor(index + 0, c.r, c.g, c.b); pixels.setPixelColor(index + 1, c.r, c.g, c.b); } -void drawDots(Adafruit_NeoPixel& pixels, const Color& bottom, const Color& middleBottom, const Color& middleTop, const Color& top) { - drawDot(pixels, 0, bottom); - drawDot(pixels, 1, middleBottom); - drawDot(pixels, 2, middleTop); - drawDot(pixels, 3, top); +void drawDots(const Color& bottom, const Color& middleBottom, const Color& middleTop, const Color& top) { + drawDot(0, bottom); + drawDot(1, middleBottom); + drawDot(2, middleTop); + drawDot(3, top); } -void drawMillis(Adafruit_NeoPixel& pixels, const unsigned long millisTotal, const Color& color) { +void drawMillisDeci(unsigned long millisTotal, const Color& color) { const auto secondsTotal = millisTotal / 1000; const auto minutes = (int) secondsTotal / 60; const auto seconds = (int) secondsTotal % 60; + const auto deci = (int) millisTotal / 100 % 10; + pixels.clear(); if (minutes > 0) { - static auto lastSeconds = -1; - if (lastSeconds == seconds) { - return; - } - lastSeconds = seconds; - - Serial.printf("%2d:%02d\n", minutes, seconds); - - pixels.clear(); - drawNumber(pixels, 0, minutes, false, color); - drawDots(pixels, BLACK, color, color, BLACK); - drawNumber(pixels, 2, seconds, true, color); - pixels.show(); + drawNumber(0, minutes, false, color); + drawDots(BLACK, color, color, BLACK); + drawNumber(2, seconds, true, color); } else { - const auto deci = (int) millisTotal / 100 % 10; - Serial.printf("%2d.%02d\n", seconds, deci); - - pixels.clear(); if (seconds > 0) { - drawNumber(pixels, 0, seconds, false, color); - drawDots(pixels, color, BLACK, BLACK, BLACK); + drawNumber(0, seconds, false, color); + drawDots(color, BLACK, BLACK, BLACK); } - drawChar(pixels, 2, '0' + deci, true, color); - pixels.show(); + drawChar(2, '0' + deci, true, color); } + pixels.show(); +} + +void drawMillis(unsigned long millisTotal, const Color& color) { + const auto secondsTotal = (unsigned long) ceil(millisTotal / 1000.0); + const auto minutes = (int) secondsTotal / 60; + const auto seconds = (int) secondsTotal % 60; + + pixels.clear(); + if (minutes > 0) { + drawNumber(0, minutes, false, color); + drawDots(BLACK, color, color, BLACK); + } + drawNumber(2, seconds, minutes > 0, color); + pixels.show(); } diff --git a/src/display.h b/src/display.h index b2d00cf..713233b 100644 --- a/src/display.h +++ b/src/display.h @@ -5,14 +5,22 @@ #include "Color.h" -void drawChar(Adafruit_NeoPixel& pixels, int digit, char c, const Color& color); +void displaySetup(); -void drawChar(Adafruit_NeoPixel& pixels, int digit, char c, bool showIfZero, const Color& color); +void displayClear(); -void drawNumber(Adafruit_NeoPixel& pixels, int digit, int number, bool zero, const Color& color); +void displayShow(); -void drawDots(Adafruit_NeoPixel& pixels, const Color& bottom, const Color& middleBottom, const Color& middleTop, const Color& top); +void drawChar(int digit, char c, const Color& color); -void drawMillis(Adafruit_NeoPixel& pixels, unsigned long millisTotal, const Color& color); +void drawChar(int digit, char c, bool showIfZero, const Color& color); + +void drawNumber(int digit, int number, bool zero, const Color& color); + +void drawDots(const Color& bottom, const Color& middleBottom, const Color& middleTop, const Color& top); + +void drawMillisDeci(unsigned long millisTotal, const Color& color); + +void drawMillis(unsigned long millisTotal, const Color& color); #endif diff --git a/src/main.cpp b/src/main.cpp index f8f791f..64376c9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,93 +1,17 @@ #include "display.h" - -#define GPIO 26 -#define PIXELS (4 * 7 * 6 + 4 * 4) -#define BRIGHTNESS 32 - -Adafruit_NeoPixel pixels(PIXELS, GPIO, NEO_GRB + NEO_KHZ800); +#include "button.h" +#include "countdown.h" void setup() { delay(500); Serial.begin(115200); Serial.println("\n\n\nstartup"); - pixels.begin(); - pixels.setBrightness(BRIGHTNESS); - pixels.clear(); - pixels.show(); -} - -long configCountdownMillis = 6 * 60 * 1000; - -long rest = configCountdownMillis; - -unsigned long last = 0; - -void updateTime() { - const auto now = max(1UL, millis()); - if (last != 0) { - const auto diff = now - last; - rest -= (long) diff; - if (rest < 0) { - rest = 0; - } - } - last = now; -} - -bool running = true; - -void dashes() { - pixels.clear(); - drawChar(pixels, 0, '-', true, RED); - drawChar(pixels, 1, '-', true, RED); - drawChar(pixels, 2, '-', true, RED); - drawChar(pixels, 3, '-', true, RED); - pixels.show(); -} - -void white() { - pixels.clear(); - drawChar(pixels, 0, '8', true, WHITE); - drawChar(pixels, 1, '8', true, WHITE); - drawDots(pixels, WHITE, WHITE, WHITE, WHITE); - drawChar(pixels, 2, '8', true, WHITE); - drawChar(pixels, 3, '8', true, WHITE); - pixels.show(); -} - -void black() { - pixels.clear(); - pixels.show(); -} - -void endSequence() { - for (int x = 0; x < 3; ++x) { - for (int i = 0; i < 2; ++i) { - white(); - delay(100); - black(); - delay(100); - } - dashes(); - delay(500); - } -} - -void showTime() { - if (!running) { - return; - } - if (rest > 0) { - const auto color = rest >= 60000 ? WHITE : (rest >= 10000 ? YELLOW : RED); - drawMillis(pixels, rest, color); - } else { - running = false; - Serial.println("END"); - endSequence(); - } + buttonSetup(); + displaySetup(); + countdownSetup(); } void loop() { - updateTime(); - showTime(); + buttonLoop(); + countdownLoop(); }