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