button menu-interface

This commit is contained in:
Patrick Haßel 2025-03-12 23:47:53 +01:00
parent 63a2b7f23d
commit 1d8cb0d2f0
7 changed files with 309 additions and 122 deletions

38
src/button.cpp Normal file
View File

@ -0,0 +1,38 @@
#include "button.h"
#include <Arduino.h>
#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();
}
}

8
src/button.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef WS2812B_BUTTON_H
#define WS2812B_BUTTON_H
void buttonSetup();
void buttonLoop();
#endif

173
src/countdown.cpp Normal file
View File

@ -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;
}
}

12
src/countdown.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef WS2812B_COUNTDOWN_H
#define WS2812B_COUNTDOWN_H
void countdownSetup();
void countdownLoop();
void countdownLongPress();
void countdownShortPress();
#endif

View File

@ -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;
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();
} else {
const auto deci = (int) millisTotal / 100 % 10;
Serial.printf("%2d.%02d\n", seconds, deci);
pixels.clear();
if (minutes > 0) {
drawNumber(0, minutes, false, color);
drawDots(BLACK, color, color, BLACK);
drawNumber(2, seconds, true, color);
} else {
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(2, '0' + deci, true, color);
}
drawChar(pixels, 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();
}

View File

@ -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

View File

@ -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();
}