Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions include/hardware/NeoPixelController.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,30 @@

class NeoPixelController {
public:
enum class LedState { IDLE, WORK, BREAK, BLINKING };

NeoPixelController();
void begin();
void update();
void blinkLight(int times, int delay_ms);
void setLedState(LedState newState);

private:
LedState _currentLedState; // Current state of the LED
LedState _previousLedState; // State before blinking

// For non-blocking blink
int _blink_sweeps_todo;
int _blink_sweeps_done;
int _blink_color_idx;
bool _blink_is_on_phase;
unsigned long _blink_phase_next_change_time;
int _blink_phase_duration_ms;
uint8_t _blink_original_brightness;

static const CRGB BLINK_COLORS[];
static const int NUM_BLINK_COLORS;

static constexpr uint8_t NUM_PIXELS = 1;
static constexpr uint16_t UPDATE_INTERVAL_MS = 16; // ~60fps
static constexpr float BREATH_SPEED = 0.0167f * .75f ;
Expand Down
2 changes: 1 addition & 1 deletion include/lv_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@
#define LV_WIDGETS_HAS_DEFAULT_VALUE 1 // Default

#define LV_USE_ANIMIMG 1 // Migrated from old config (was 1)
#define LV_USE_ARC 0 // Migrated from old config (was 0)
#define LV_USE_ARC 1 // Migrated from old config (was 0)
#define LV_USE_BAR 0 // Migrated from old config (was 0)
#define LV_USE_BUTTON 0 // Migrated from old config (was 0, mapped from BTN)
#define LV_USE_BUTTONMATRIX 1 // Enable for animation support
Expand Down
157 changes: 126 additions & 31 deletions src/hardware/NeoPixelController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,28 @@
#include <Arduino.h> // For pinMode, digitalWrite, delay, PI, sin, constrain, millis
// FastLED.h is included via NeoPixelController.h

// Define colors for different states
const CRGB NeoPixelController::BLINK_COLORS[] = {CRGB::Blue, CRGB::DeepPink, CRGB::Orange};
const int NeoPixelController::NUM_BLINK_COLORS = sizeof(NeoPixelController::BLINK_COLORS) / sizeof(CRGB);

const CRGB COLOR_IDLE = CRGB::Black; // Or a dim white e.g., CRGB(10,10,10)
const CRGB COLOR_WORK = CRGB(128, 64, 0); // Dim Orange (adjust R,G,B as needed)
const CRGB COLOR_BREAK = CRGB(0, 0, 128); // Dim Blue (adjust R,G,B as needed)
const uint8_t NEOPIXEL_BRIGHTNESS = 50; // Overall brightness (0-255)

NeoPixelController::NeoPixelController()
// No explicit member initializer for leds array or FastLED controller here
: lastUpdate(0),
breathPhase(0.0f) {
: _currentLedState(LedState::IDLE),
_previousLedState(LedState::IDLE),
lastUpdate(0),
breathPhase(0.0f),
// Initialize blink state members
_blink_sweeps_todo(0),
_blink_sweeps_done(0),
_blink_color_idx(0),
_blink_is_on_phase(false),
_blink_phase_next_change_time(0),
_blink_phase_duration_ms(0),
_blink_original_brightness(NEOPIXEL_BRIGHTNESS) {
}

void NeoPixelController::begin() {
Expand All @@ -23,40 +41,117 @@ void NeoPixelController::begin() {
// Initialize FastLED
// Using NEOPIXEL_DATA_PIN from NeoPixelController.h
FastLED.addLeds<WS2812B, NEOPIXEL_DATA_PIN, GRB>(leds, NUM_PIXELS);
FastLED.setBrightness(255); // Set max brightness initially
leds[0] = CRGB::Black; // Clear the pixel
_blink_original_brightness = NEOPIXEL_BRIGHTNESS; // Store initial brightness for restoration
FastLED.setBrightness(_blink_original_brightness);
leds[0] = COLOR_IDLE; // Start with IDLE color
FastLED.show();
_currentLedState = LedState::IDLE; // Explicitly set initial state
}

void NeoPixelController::setLedState(LedState newState) {
// Prevent changing state if currently blinking, unless specifically setting to BLINKING (which blinkLight does)
if (_currentLedState == LedState::BLINKING && newState != LedState::BLINKING) {
// If an external call tries to change state away from BLINKING,
// we might want to cancel the blink and apply the new state.
// For now, blinkLight itself handles transition out of BLINKING.
// This setLedState is for general state changes outside of blink completion.
_currentLedState = newState; // Allow overriding blink if necessary for other logic
FastLED.setBrightness(_blink_original_brightness); // Restore brightness if blink is cut short
} else if (_currentLedState != LedState::BLINKING) {
_currentLedState = newState;
}
}

void NeoPixelController::update() {
unsigned long currentTime = millis();
if (currentTime - lastUpdate < UPDATE_INTERVAL_MS) {
// Minimal interval check, mostly for non-blinking states.
if (_currentLedState != LedState::BLINKING && (currentTime - lastUpdate < UPDATE_INTERVAL_MS)) {
return;
}

lastUpdate = currentTime;

breathPhase += BREATH_SPEED;
if (breathPhase >= BREATH_CYCLE) {
breathPhase -= BREATH_CYCLE;
// lastUpdate = currentTime; // Only update for steady states, blink handles its own timing.

if (_currentLedState == LedState::BLINKING) {
if (currentTime >= _blink_phase_next_change_time) {
_blink_phase_next_change_time = currentTime + _blink_phase_duration_ms;

if (_blink_is_on_phase) { // Was ON, turn OFF
leds[0] = CRGB::Black;
_blink_is_on_phase = false;
} else { // Was OFF, turn ON for next color/sweep or finish
_blink_color_idx++;
if (_blink_color_idx >= NUM_BLINK_COLORS) {
_blink_color_idx = 0;
_blink_sweeps_done++;
}

if (_blink_sweeps_done >= _blink_sweeps_todo) {
// Blinking finished
FastLED.setBrightness(_blink_original_brightness);
_currentLedState = _previousLedState; // Restore previous state
// The switch below will set the color for the new state.
} else {
// Still blinking, set next color for ON phase
leds[0] = BLINK_COLORS[_blink_color_idx];
_blink_is_on_phase = true;
}
}
}
}

// Set color based on current state (could have been changed by blink logic)
// previousLedState is updated here if not blinking to correctly save state before next blink
if (_currentLedState != LedState::BLINKING) {
lastUpdate = currentTime; // Update timestamp for steady state logic
_previousLedState = _currentLedState;
switch (_currentLedState) {
case LedState::IDLE:
leds[0] = COLOR_IDLE;
break;
case LedState::WORK:
leds[0] = COLOR_WORK;
// Optional: Add slow pulsing logic here for WORK state
// breathPhase += BREATH_SPEED;
// if (breathPhase >= BREATH_CYCLE) { breathPhase -= BREATH_CYCLE; }
// float brightness_factor = (sin(breathPhase) + 1.0f) * 0.5f; // 0 to 1
// leds[0] = COLOR_WORK; // Base color
// leds[0].nscale8_video( (uint8_t)(128 + brightness_factor * 127) ); // Scale brightness
break;
case LedState::BREAK:
leds[0] = COLOR_BREAK;
// Optional: Add slow pulsing logic here for BREAK state
break;
default: // Should not include BLINKING here as it's handled above
break;
}
}

float brightness = (sin(breathPhase) + 1.0f) * 0.5f;

float redVar = sin(breathPhase * 1.1f) * COLOR_VARIANCE;
float greenVar = sin(breathPhase * 0.9f) * COLOR_VARIANCE;
float blueVar = sin(breathPhase * 1.2f) * COLOR_VARIANCE;

float red = (brightness + redVar);
float green = (brightness + greenVar);
float blue = (brightness + blueVar);

const uint8_t MIN_VALUE = static_cast<uint8_t>(255.0f * 0.05f);

uint8_t finalRed = constrain(static_cast<long>(red * 255), MIN_VALUE, 255);
uint8_t finalGreen = constrain(static_cast<long>(green * 255), MIN_VALUE, 255);
uint8_t finalBlue = constrain(static_cast<long>(blue * 255), MIN_VALUE, 255);

leds[0] = CRGB(finalRed, finalGreen, finalBlue); // Set pixel color using FastLED
FastLED.show(); // Update the strip
FastLED.show();
}

void NeoPixelController::blinkLight(int sweeps, int phase_duration_ms) {
// If already blinking, and this is a new request, we could choose to ignore, restart, or queue.
// For simplicity, let's restart if a new blink is requested.
// if (_currentLedState == LedState::BLINKING) { return; } // Option: ignore if already blinking

if (_currentLedState != LedState::BLINKING) { // Only save previous state if not already blinking
_previousLedState = _currentLedState;
}
_currentLedState = LedState::BLINKING;

_blink_sweeps_todo = sweeps;
_blink_sweeps_done = 0;
_blink_color_idx = 0; // Start with the first color
_blink_is_on_phase = true; // Start with the light ON
_blink_phase_duration_ms = phase_duration_ms;
_blink_phase_next_change_time = millis() + _blink_phase_duration_ms;

// Store current brightness if not already stored by a previous blinkLight call that was interrupted
// Or, more simply, _blink_original_brightness is set at begin() and only restored by blink finishing.
// If a blink interrupts another, the _blink_original_brightness is already the true original.
if (_previousLedState != LedState::BLINKING) { // Only grab brightness if we weren't already blinking
_blink_original_brightness = FastLED.getBrightness();
}
FastLED.setBrightness(255); // Max brightness for blink

leds[0] = BLINK_COLORS[_blink_color_idx]; // Set initial color for the first ON phase
// FastLED.show() will be handled by update()
}
10 changes: 10 additions & 0 deletions src/ui/CardController.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "ui/CardController.h"
#include "ui/PomodoroCard.h"

CardController::CardController(
lv_obj_t* screen,
Expand Down Expand Up @@ -71,6 +72,15 @@ void CardController::initialize(DisplayInterface* display) {
// Create animation card
createAnimationCard();

// Create Pomodoro card
PomodoroCard* pomodoroCard = new PomodoroCard(screen);

// Add Pomodoro card to navigation stack
cardStack->addCard(pomodoroCard->getCard());

// Register the Pomodoro card as an input handler
cardStack->registerInputHandler(pomodoroCard->getCard(), pomodoroCard);

// Get count of insights to determine card count
std::vector<String> insightIds = configManager.getAllInsightIds();

Expand Down
Loading