First version
This commit is contained in:
commit
e338a5fd0f
66
include/config.h
Normal file
66
include/config.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
// MARK: Conversions
|
||||||
|
|
||||||
|
#define MS_PER_MIN 60000
|
||||||
|
|
||||||
|
// MARK: Hardware
|
||||||
|
|
||||||
|
#define NUM_LEDS 12
|
||||||
|
#define LED_SPIN_MAX_COUNT (NUM_LEDS * 2)
|
||||||
|
#define LED_BATCH_COUNT 2
|
||||||
|
|
||||||
|
// MARK: Demo
|
||||||
|
|
||||||
|
// Enable the demo mode
|
||||||
|
//#define ENABLE_DEMO
|
||||||
|
|
||||||
|
// MARK: Logging
|
||||||
|
|
||||||
|
// Enable logging to serial connection
|
||||||
|
// #define SERIAL_LOGGING
|
||||||
|
|
||||||
|
|
||||||
|
// Hardware pins
|
||||||
|
|
||||||
|
#define LED_STRIP_PIN 5
|
||||||
|
#define BUTTON_PIN 2
|
||||||
|
|
||||||
|
// Storage
|
||||||
|
|
||||||
|
#define DEMO_MODE_BYTE 0
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
|
||||||
|
#define MAX_GREEN 90
|
||||||
|
#define MAX_RED 5
|
||||||
|
|
||||||
|
// Brightness
|
||||||
|
|
||||||
|
#define MAX_BRIGHTNESS 250
|
||||||
|
#define PULSING_BRIGHTNESS 50
|
||||||
|
|
||||||
|
// MARK: Durations
|
||||||
|
|
||||||
|
#define STAY_GREEN_DURATION_DEMO 20000
|
||||||
|
#define FADE_TO_RED_DURATION_DEMO 30000
|
||||||
|
#define PULSING_UNTIL_OFF_DURATION_DEMO 60000
|
||||||
|
|
||||||
|
#define STAY_GREEN_DURATION 30*MS_PER_MIN
|
||||||
|
#define FADE_TO_RED_DURATION 30*MS_PER_MIN
|
||||||
|
#define PULSING_UNTIL_OFF_DURATION 20*MS_PER_MIN
|
||||||
|
|
||||||
|
#define SHORT_FADE_DURATION 1500
|
||||||
|
|
||||||
|
// Pulsing config
|
||||||
|
|
||||||
|
#define PULSING_START_INTERVAL 3500
|
||||||
|
#define PULSING_END_INTERVAL 500
|
||||||
|
#define PULSE_VARIANCE (PULSING_START_INTERVAL-PULSING_END_INTERVAL)
|
||||||
|
|
||||||
|
// Spin config
|
||||||
|
|
||||||
|
#define SPIN_SATURATION 255
|
||||||
|
#define SPIN_DEMO_ENABLED_HUE 0
|
||||||
|
#define SPIN_DEMO_DISABLED_HUE 90
|
||||||
|
|
||||||
|
#define MAXIMUM_DELAY 250
|
86
include/definitions.h
Normal file
86
include/definitions.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#pragma once
|
||||||
|
// MARK: State definition
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
enum class ProgramState {
|
||||||
|
fadeToGreen = 1,
|
||||||
|
stayGreen = 2,
|
||||||
|
fadeToRed = 3,
|
||||||
|
fadePulseLow = 4,
|
||||||
|
fadePulseHigh = 5,
|
||||||
|
fadeToSleeping = 6,
|
||||||
|
sleeping = 7,
|
||||||
|
toggleDemoMode = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FadeComponent {
|
||||||
|
|
||||||
|
// Timestamps for next fade step
|
||||||
|
uint32_t nextFade;
|
||||||
|
|
||||||
|
// Intervals in ms for each fade step
|
||||||
|
uint32_t interval;
|
||||||
|
|
||||||
|
// Fade direction for each component
|
||||||
|
uint8_t direction;
|
||||||
|
|
||||||
|
// Indicator to alternate between LED batches
|
||||||
|
uint8_t skip;
|
||||||
|
|
||||||
|
// Current color
|
||||||
|
uint8_t color;
|
||||||
|
|
||||||
|
// Target color
|
||||||
|
uint8_t endColor;
|
||||||
|
|
||||||
|
uint8_t needsStep(uint32_t time) {
|
||||||
|
if (isDone()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return time >= nextFade;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t isDone() {
|
||||||
|
return color == endColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t nextColor() {
|
||||||
|
return color + direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t advanceSkipAndColorIfNeeded() {
|
||||||
|
skip = (skip + 1) % LED_BATCH_COUNT;
|
||||||
|
nextFade += interval;
|
||||||
|
if (skip) {
|
||||||
|
// Only update color when all batches are done
|
||||||
|
// i.e. when skip is zero
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
if (isDone()) {
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
color += direction;
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNewColor(uint8_t newColor, uint32_t time, uint32_t duration) {
|
||||||
|
endColor = newColor;
|
||||||
|
nextFade = time;
|
||||||
|
skip = 0;
|
||||||
|
if (isDone()) {
|
||||||
|
interval = 255;
|
||||||
|
direction = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t steps;
|
||||||
|
if (endColor > color) {
|
||||||
|
steps = endColor - color;
|
||||||
|
direction = 1;
|
||||||
|
} else {
|
||||||
|
steps = color - endColor;
|
||||||
|
direction = -1;
|
||||||
|
}
|
||||||
|
interval = (duration / steps) / LED_BATCH_COUNT;
|
||||||
|
}
|
||||||
|
};
|
15
include/log.h
Normal file
15
include/log.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifdef SERIAL_LOGGING
|
||||||
|
#define INIT_LOGGING Serial.begin(115200)
|
||||||
|
|
||||||
|
#define LOG_DEBUG(X) \
|
||||||
|
Serial.print(X);
|
||||||
|
|
||||||
|
#define LOG_DEBUG_LN(X) \
|
||||||
|
Serial.println(X);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define INIT_LOGGING
|
||||||
|
#define LOG_DEBUG(X);
|
||||||
|
#define LOG_DEBUG_LN(X);
|
||||||
|
#endif
|
19
platformio.ini
Normal file
19
platformio.ini
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:nanoatmega328]
|
||||||
|
platform = atmelavr
|
||||||
|
board = nanoatmega328
|
||||||
|
framework = arduino
|
||||||
|
lib_deps = fastled/FastLED@^3.5.0
|
||||||
|
platform_packages =
|
||||||
|
toolchain-atmelavr@>=1.70300.0
|
||||||
|
monitor_speed = 115200
|
||||||
|
upload_port = /dev/tty.usbserial-1470
|
360
src/main.cpp
Normal file
360
src/main.cpp
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
//#include <EEPROM.h>
|
||||||
|
#include "FastLED.h"
|
||||||
|
|
||||||
|
#include "definitions.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
// MARK: State memory
|
||||||
|
|
||||||
|
uint8_t spinLed;
|
||||||
|
uint8_t isInDemoMode;
|
||||||
|
uint32_t fadeToRedDuration;
|
||||||
|
uint32_t stayGreenDuration;
|
||||||
|
uint32_t pulsingUntilOffDuration;
|
||||||
|
|
||||||
|
uint8_t numberOfPresses;
|
||||||
|
uint8_t buttonIsPressed;
|
||||||
|
|
||||||
|
ProgramState state = ProgramState::fadeToGreen; // Current phase
|
||||||
|
|
||||||
|
CRGB leds[NUM_LEDS];
|
||||||
|
|
||||||
|
uint32_t pulsingEnd = 0; // The time after which the pulsing should end
|
||||||
|
uint8_t pulsingLow = 0; // Alternate between pulse phases
|
||||||
|
|
||||||
|
uint32_t stayGreenEnd = 0; // Timestamp to start fading to red
|
||||||
|
|
||||||
|
// Fading state
|
||||||
|
|
||||||
|
// Fade info for color components
|
||||||
|
FadeComponent rgb[3];
|
||||||
|
|
||||||
|
void startFadeToOff();
|
||||||
|
void startSwitchDemoMode();
|
||||||
|
|
||||||
|
// MARK: Color fading
|
||||||
|
|
||||||
|
void startFade(CRGB target, uint32_t duration) {
|
||||||
|
uint32_t time = millis();
|
||||||
|
for (uint8_t i = 0; i < 3; i++) {
|
||||||
|
uint8_t end = target[i];
|
||||||
|
rgb[i].setNewColor(end, time, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showFadeEndColor() {
|
||||||
|
CRGB c = CRGB(rgb[0].endColor, rgb[1].endColor, rgb[2].endColor);
|
||||||
|
//rgb[0].endColor = c[0];
|
||||||
|
//rgb[1].endColor = c[1];
|
||||||
|
//rgb[2].endColor = c[2];
|
||||||
|
fill_solid(leds, NUM_LEDS, c);
|
||||||
|
LOG_DEBUG_LN("Fade end");
|
||||||
|
FastLED.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t continueFade() {
|
||||||
|
uint32_t t = millis();
|
||||||
|
uint8_t next = MAXIMUM_DELAY;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 3; i++) {
|
||||||
|
// Skip if color component is already at the right position,
|
||||||
|
// or next fade time is not reached
|
||||||
|
if (!rgb[i].needsStep(t)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance the current led batch in the correct direction
|
||||||
|
for (uint8_t k = rgb[i].skip; k < NUM_LEDS; k += LED_BATCH_COUNT) {
|
||||||
|
leds[k][i] = rgb[i].nextColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until next change
|
||||||
|
uint8_t componentDelay = rgb[i].advanceSkipAndColorIfNeeded();
|
||||||
|
next = min(componentDelay, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal end of fade
|
||||||
|
if (rgb[0].isDone() && rgb[1].isDone() && rgb[2].isDone()) {
|
||||||
|
showFadeEndColor();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FastLED.show();
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: User input
|
||||||
|
|
||||||
|
uint8_t puckIsLifted() {
|
||||||
|
return digitalRead(BUTTON_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Demo mode
|
||||||
|
|
||||||
|
void updateFadeDurations() {
|
||||||
|
if (isInDemoMode) {
|
||||||
|
fadeToRedDuration = FADE_TO_RED_DURATION_DEMO;
|
||||||
|
stayGreenDuration = STAY_GREEN_DURATION_DEMO;
|
||||||
|
pulsingUntilOffDuration = PULSING_UNTIL_OFF_DURATION_DEMO;
|
||||||
|
} else {
|
||||||
|
fadeToRedDuration = FADE_TO_RED_DURATION;
|
||||||
|
stayGreenDuration = STAY_GREEN_DURATION;
|
||||||
|
pulsingUntilOffDuration = PULSING_UNTIL_OFF_DURATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFadeDurations() {
|
||||||
|
#ifdef ENABLE_DEMO
|
||||||
|
isInDemoMode = 1;
|
||||||
|
//EEPROM.read(DEMO_MODE_BYTE);
|
||||||
|
#else
|
||||||
|
isInDemoMode = 0;
|
||||||
|
#endif
|
||||||
|
updateFadeDurations();
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: State transitions
|
||||||
|
|
||||||
|
void startFadeToGreen() {
|
||||||
|
state = ProgramState::fadeToGreen;
|
||||||
|
startFade(CHSV(MAX_GREEN, 255, MAX_BRIGHTNESS), SHORT_FADE_DURATION);
|
||||||
|
LOG_DEBUG_LN("-> green");
|
||||||
|
}
|
||||||
|
|
||||||
|
void startStayingGreen() {
|
||||||
|
state = ProgramState::stayGreen;
|
||||||
|
|
||||||
|
stayGreenEnd = millis() + stayGreenDuration;
|
||||||
|
LOG_DEBUG_LN("-> stay");
|
||||||
|
}
|
||||||
|
|
||||||
|
void startFadeToRed() {
|
||||||
|
state = ProgramState::fadeToRed;
|
||||||
|
startFade(CHSV(MAX_RED, 255, MAX_BRIGHTNESS), fadeToRedDuration);
|
||||||
|
LOG_DEBUG_LN("-> red");
|
||||||
|
}
|
||||||
|
|
||||||
|
void startPulseToLow() {
|
||||||
|
state = ProgramState::fadePulseLow;
|
||||||
|
|
||||||
|
uint32_t t = millis();
|
||||||
|
if (t > pulsingEnd) {
|
||||||
|
startFadeToOff();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t d = (pulsingEnd - t) / (pulsingUntilOffDuration/PULSE_VARIANCE) + PULSING_END_INTERVAL;
|
||||||
|
|
||||||
|
CHSV c = CHSV(MAX_RED, 255, PULSING_BRIGHTNESS);
|
||||||
|
startFade(c, d);
|
||||||
|
|
||||||
|
LOG_DEBUG_LN("-> low");
|
||||||
|
}
|
||||||
|
|
||||||
|
void startPulseToHigh() {
|
||||||
|
state = ProgramState::fadePulseHigh;
|
||||||
|
|
||||||
|
uint32_t t = millis();
|
||||||
|
if (t > pulsingEnd) {
|
||||||
|
startFadeToOff();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t d = (pulsingEnd - t) / (pulsingUntilOffDuration/PULSE_VARIANCE) + PULSING_END_INTERVAL;
|
||||||
|
|
||||||
|
CHSV c = CHSV(MAX_RED, 255, MAX_BRIGHTNESS);
|
||||||
|
startFade(c, d);
|
||||||
|
|
||||||
|
LOG_DEBUG_LN("-> high");
|
||||||
|
}
|
||||||
|
|
||||||
|
void startFadeToOff() {
|
||||||
|
state = ProgramState::fadeToSleeping;
|
||||||
|
startFade(CRGB(0,0,0), SHORT_FADE_DURATION);
|
||||||
|
LOG_DEBUG_LN("-> sleep")
|
||||||
|
}
|
||||||
|
|
||||||
|
void startSleeping() {
|
||||||
|
state = ProgramState::sleeping;
|
||||||
|
LOG_DEBUG_LN("-> off");
|
||||||
|
}
|
||||||
|
|
||||||
|
void startSwitchDemoMode() {
|
||||||
|
state = ProgramState::toggleDemoMode;
|
||||||
|
|
||||||
|
fill_solid(leds, NUM_LEDS, CRGB(0,0,0));
|
||||||
|
|
||||||
|
// if (isInDemoMode) {
|
||||||
|
// EEPROM.write(DEMO_MODE_BYTE, 0);
|
||||||
|
// isInDemoMode = 0;
|
||||||
|
// } else {
|
||||||
|
// EEPROM.write(DEMO_MODE_BYTE, 1);
|
||||||
|
// isInDemoMode = 1;
|
||||||
|
// }
|
||||||
|
updateFadeDurations();
|
||||||
|
LOG_DEBUG_LN("-> demo");
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: State actions
|
||||||
|
|
||||||
|
uint8_t isFadingToGreenAfterPuckIsLifted() {
|
||||||
|
if (puckIsLifted()) {
|
||||||
|
startFadeToGreen();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fadeToGreen() {
|
||||||
|
uint8_t wait = continueFade();
|
||||||
|
if (wait) {
|
||||||
|
delay(wait);
|
||||||
|
} else {
|
||||||
|
// Fade to green completed, stay green
|
||||||
|
startStayingGreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stayGreen() {
|
||||||
|
if (puckIsLifted()) {
|
||||||
|
startFadeToOff();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (millis() > stayGreenEnd) {
|
||||||
|
// Staying green completed, slowly fade to red
|
||||||
|
startFadeToRed();
|
||||||
|
} else {
|
||||||
|
delay(MAXIMUM_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fadeToRed() {
|
||||||
|
if (isFadingToGreenAfterPuckIsLifted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t wait = continueFade();
|
||||||
|
if (wait) {
|
||||||
|
delay(wait);
|
||||||
|
} else {
|
||||||
|
// Fade to red completed
|
||||||
|
pulsingEnd = millis() + pulsingUntilOffDuration;
|
||||||
|
startPulseToLow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fadePulseLow() {
|
||||||
|
if (isFadingToGreenAfterPuckIsLifted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t wait = continueFade();
|
||||||
|
if (wait) {
|
||||||
|
delay(wait);
|
||||||
|
} else {
|
||||||
|
// Pulse completed, start next
|
||||||
|
startPulseToHigh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fadePulseHigh() {
|
||||||
|
if (isFadingToGreenAfterPuckIsLifted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t wait = continueFade();
|
||||||
|
if (wait) {
|
||||||
|
delay(wait);
|
||||||
|
} else {
|
||||||
|
// Pulse completed, start next
|
||||||
|
startPulseToLow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fadeToOff() {
|
||||||
|
uint8_t wait = continueFade();
|
||||||
|
if (wait) {
|
||||||
|
delay(wait);
|
||||||
|
} else {
|
||||||
|
startSleeping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sleeping() {
|
||||||
|
if (puckIsLifted() == 0) {
|
||||||
|
startFadeToGreen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delay(MAXIMUM_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggleDemoMode() {
|
||||||
|
uint8_t hue = isInDemoMode ? SPIN_DEMO_ENABLED_HUE : SPIN_DEMO_DISABLED_HUE;
|
||||||
|
leds[(spinLed + 0) % NUM_LEDS] = CHSV(hue, SPIN_SATURATION, 50);
|
||||||
|
leds[(spinLed + 1) % NUM_LEDS] = CHSV(hue, SPIN_SATURATION, 125);
|
||||||
|
leds[(spinLed + 2) % NUM_LEDS] = CHSV(hue, SPIN_SATURATION, 250);
|
||||||
|
leds[(spinLed + 3) % NUM_LEDS] = CHSV(hue, SPIN_SATURATION, 250);
|
||||||
|
leds[(spinLed + 4) % NUM_LEDS] = CHSV(hue, SPIN_SATURATION, 125);
|
||||||
|
leds[(spinLed + 5) % NUM_LEDS] = CHSV(hue, SPIN_SATURATION, 50);
|
||||||
|
for (uint8_t i = 6; i < NUM_LEDS; i += 1) {
|
||||||
|
leds[(spinLed + i) % NUM_LEDS] = CHSV(hue, SPIN_SATURATION, 0);
|
||||||
|
}
|
||||||
|
FastLED.show();
|
||||||
|
delay(50);
|
||||||
|
spinLed += 1;
|
||||||
|
if (spinLed == LED_SPIN_MAX_COUNT) {
|
||||||
|
spinLed = 0;
|
||||||
|
startFadeToGreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Run loop
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
FastLED.addLeds<WS2812B, LED_STRIP_PIN, GRB>(leds, NUM_LEDS);
|
||||||
|
|
||||||
|
// User buttons
|
||||||
|
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
||||||
|
|
||||||
|
INIT_LOGGING;
|
||||||
|
|
||||||
|
LOG_DEBUG_LN("Started");
|
||||||
|
|
||||||
|
loadFadeDurations();
|
||||||
|
|
||||||
|
startSwitchDemoMode();
|
||||||
|
//startFadeToGreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
switch (state) {
|
||||||
|
case ProgramState::fadeToGreen:
|
||||||
|
fadeToGreen();
|
||||||
|
break;
|
||||||
|
case ProgramState::stayGreen:
|
||||||
|
stayGreen();
|
||||||
|
break;
|
||||||
|
case ProgramState::fadeToRed:
|
||||||
|
fadeToRed();
|
||||||
|
break;
|
||||||
|
case ProgramState::fadePulseLow:
|
||||||
|
fadePulseLow();
|
||||||
|
break;
|
||||||
|
case ProgramState::fadePulseHigh:
|
||||||
|
fadePulseHigh();
|
||||||
|
break;
|
||||||
|
case ProgramState::fadeToSleeping:
|
||||||
|
fadeToOff();
|
||||||
|
break;
|
||||||
|
case ProgramState::sleeping:
|
||||||
|
sleeping();
|
||||||
|
break;
|
||||||
|
case ProgramState::toggleDemoMode:
|
||||||
|
toggleDemoMode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user