Refactor work timer structure for improved state management and simplify color definitions. Remove unused warning color definitions and clarify break warning time comment.

This commit is contained in:
TS Design Works 2025-05-17 15:25:55 -04:00
parent cc50f2cd92
commit 7f49c0408f
2 changed files with 214 additions and 624 deletions

View File

@ -19,237 +19,159 @@
#include "rgb_effects/rgb_effects.h" #include "rgb_effects/rgb_effects.h"
#include "timer.h" #include "timer.h"
// Bitpacked flags to save memory // Simple work timer structure
typedef struct { typedef struct {
uint8_t active: 1; // Basic state
uint8_t paused: 1; bool active; // Is the timer running?
uint8_t lunch_break: 1; bool paused; // Is the timer paused?
uint8_t mid_break: 1; work_timer_type_t type; // Timer type (30min, 1hr, etc.)
uint8_t lunch_warning_shown: 1;
uint8_t mid_break_warning_shown: 1;
uint8_t end_warning_shown: 1;
uint8_t pulse_active: 1; // Flag to track if any pulse is currently active
} work_timer_flags_t;
// Simplified work timer state structure // Time tracking
typedef struct { uint32_t start_time; // When this timer started
work_timer_flags_t flags; uint32_t end_time; // When this timer should end
work_timer_type_t timer_type; uint32_t pause_time; // When timer was paused (if paused)
uint32_t start_time; // When timer was started uint32_t break_time; // Break duration for this timer
uint32_t end_time; // When timer should end (calculated)
uint32_t pause_time; // When pause started (if paused) // Midpoint break tracking
uint32_t break_start_time; // When current break started uint32_t mid_point; // Time value at midpoint (when break starts)
uint32_t mid_break_time; // When mid-break should occur uint32_t mid_point_end; // Time value when break ends
uint32_t mid_break_duration; // How long the mid-break should last
bool has_lunch_break; // Whether this timer has a lunch break
} work_timer_t; } work_timer_t;
// Global work timer state with simplified initialization // Global timer state
static work_timer_t work_timer = { static work_timer_t timer = {0};
.flags = {0},
.timer_type = TIMER_TYPE_8HR,
.start_time = 0,
.end_time = 0,
.pause_time = 0,
.break_start_time = 0,
.mid_break_time = 0,
.mid_break_duration = LUNCH_BREAK_DURATION,
.has_lunch_break = true
};
// Predefined RGB colors for timer states // Timer colors
static const rgb_color_t WORK_TIMER_START_COLOR = {WORK_TIMER_START_R, WORK_TIMER_START_G, WORK_TIMER_START_B}; static const rgb_color_t TIMER_START_COLOR = {WORK_TIMER_START_R, WORK_TIMER_START_G, WORK_TIMER_START_B};
static const rgb_color_t WORK_TIMER_MID_COLOR = {WORK_TIMER_MID_R, WORK_TIMER_MID_G, WORK_TIMER_MID_B}; static const rgb_color_t TIMER_MID_COLOR = {WORK_TIMER_MID_R, WORK_TIMER_MID_G, WORK_TIMER_MID_B};
static const rgb_color_t WORK_TIMER_END_COLOR = {WORK_TIMER_END_R, WORK_TIMER_END_G, WORK_TIMER_END_B}; static const rgb_color_t TIMER_END_COLOR = {WORK_TIMER_END_R, WORK_TIMER_END_G, WORK_TIMER_END_B};
static const rgb_color_t WORK_TIMER_LUNCH_COLOR = {WORK_TIMER_LUNCH_R, WORK_TIMER_LUNCH_G, WORK_TIMER_LUNCH_B}; static const rgb_color_t TIMER_BREAK_COLOR = {WORK_TIMER_LUNCH_R, WORK_TIMER_LUNCH_G, WORK_TIMER_LUNCH_B};
static const rgb_color_t WORK_TIMER_WARNING_COLOR = {WORK_TIMER_WARNING_R, WORK_TIMER_WARNING_G, WORK_TIMER_WARNING_B};
// Function prototypes for internal functions // Function declarations
static void configure_timer_for_type(work_timer_type_t timer_type); static void save_timer_state(void);
static void display_progress_bar(uint8_t num_leds, float overall_progress, float brightness_factor); static void load_timer_state(void);
static void save_work_timer_state(void); static void display_progress_bar(uint8_t num_leds, float progress, float brightness);
static void load_work_timer_state(void);
static void update_pulse_active_state(void);
/** /**
* Configure timer parameters based on timer type * Configure a timer with the specified type
*/ */
static void configure_timer_for_type(work_timer_type_t timer_type) { static void configure_timer(work_timer_type_t type) {
work_timer.timer_type = timer_type; timer.type = type;
switch (timer_type) { // Set break duration based on timer type
switch (type) {
case TIMER_TYPE_30MIN: case TIMER_TYPE_30MIN:
work_timer.mid_break_time = TIMER_30MIN_DURATION / 2; timer.break_time = MID_BREAK_30MIN_DURATION;
work_timer.mid_break_duration = MID_BREAK_30MIN_DURATION;
work_timer.has_lunch_break = false;
work_timer.end_time = work_timer.start_time + TIMER_30MIN_DURATION;
break; break;
case TIMER_TYPE_1HR: case TIMER_TYPE_1HR:
work_timer.mid_break_time = TIMER_1HR_DURATION / 2; timer.break_time = MID_BREAK_1HR_DURATION;
work_timer.mid_break_duration = MID_BREAK_1HR_DURATION;
work_timer.has_lunch_break = false;
work_timer.end_time = work_timer.start_time + TIMER_1HR_DURATION;
break; break;
case TIMER_TYPE_4HR: case TIMER_TYPE_4HR:
work_timer.mid_break_time = TIMER_4HR_DURATION / 2; timer.break_time = MID_BREAK_4HR_DURATION;
work_timer.mid_break_duration = MID_BREAK_4HR_DURATION;
work_timer.has_lunch_break = false;
work_timer.end_time = work_timer.start_time + TIMER_4HR_DURATION;
break; break;
case TIMER_TYPE_8HR: case TIMER_TYPE_8HR:
work_timer.mid_break_time = TIMER_8HR_DURATION / 2; timer.break_time = LUNCH_BREAK_DURATION;
work_timer.mid_break_duration = LUNCH_BREAK_DURATION;
work_timer.has_lunch_break = true;
work_timer.end_time = work_timer.start_time + TIMER_8HR_DURATION;
break; break;
case TIMER_TYPE_10HR: case TIMER_TYPE_10HR:
work_timer.mid_break_time = TIMER_10HR_DURATION / 2; timer.break_time = LUNCH_BREAK_DURATION;
work_timer.mid_break_duration = LUNCH_BREAK_DURATION;
work_timer.has_lunch_break = true;
work_timer.end_time = work_timer.start_time + TIMER_10HR_DURATION;
break; break;
default: default:
// Default to 8HR if something goes wrong timer.break_time = LUNCH_BREAK_DURATION;
work_timer.mid_break_time = TIMER_8HR_DURATION / 2;
work_timer.mid_break_duration = LUNCH_BREAK_DURATION;
work_timer.has_lunch_break = true;
work_timer.end_time = work_timer.start_time + TIMER_8HR_DURATION;
break; break;
} }
// Calculate timer duration (excluding break)
uint32_t work_duration = 0;
switch (type) {
case TIMER_TYPE_30MIN: work_duration = TIMER_30MIN_DURATION; break;
case TIMER_TYPE_1HR: work_duration = TIMER_1HR_DURATION; break;
case TIMER_TYPE_4HR: work_duration = TIMER_4HR_DURATION; break;
case TIMER_TYPE_8HR: work_duration = TIMER_8HR_DURATION; break;
case TIMER_TYPE_10HR: work_duration = TIMER_10HR_DURATION; break;
default: work_duration = TIMER_8HR_DURATION; break;
}
// Calculate end time and midpoint
timer.end_time = timer.start_time + work_duration;
timer.mid_point = timer.start_time + (work_duration / 2);
timer.mid_point_end = timer.mid_point + timer.break_time;
} }
/** /**
* Save the work timer state to EEPROM * Save timer state to EEPROM
*/ */
static void save_work_timer_state(void) { static void save_timer_state(void) {
// Create a buffer to store all our data uint8_t buffer[24] = {0};
uint8_t buffer[24] = {0}; // Increased from 21 to 24 bytes to have enough space
// Set the active flag // Basic state
buffer[0] = work_timer.flags.active; buffer[0] = timer.active ? 1 : 0;
buffer[1] = timer.paused ? 1 : 0;
buffer[2] = (uint8_t)timer.type;
// Only save time info if timer is active // Time values
if (work_timer.flags.active) { memcpy(&buffer[3], &timer.start_time, sizeof(uint32_t));
// Save start time (4 bytes) memcpy(&buffer[7], &timer.end_time, sizeof(uint32_t));
memcpy(&buffer[1], &work_timer.start_time, sizeof(uint32_t)); memcpy(&buffer[11], &timer.pause_time, sizeof(uint32_t));
memcpy(&buffer[15], &timer.break_time, sizeof(uint32_t));
memcpy(&buffer[19], &timer.mid_point, sizeof(uint32_t));
// Save end time (4 bytes) // Write to EEPROM
memcpy(&buffer[5], &work_timer.end_time, sizeof(uint32_t));
// Save timer type (1 byte)
buffer[9] = (uint8_t)work_timer.timer_type;
// Save pause state and time (5 bytes)
buffer[10] = work_timer.flags.paused;
memcpy(&buffer[11], &work_timer.pause_time, sizeof(uint32_t));
// Save break state (1 byte)
buffer[15] = (work_timer.flags.lunch_break ? 1 : 0) |
(work_timer.flags.mid_break ? 2 : 0);
// Save break start time (4 bytes)
memcpy(&buffer[16], &work_timer.break_start_time, sizeof(uint32_t));
// Save mid-break time (4 bytes) - fixed buffer position
memcpy(&buffer[20], &work_timer.mid_break_time, sizeof(uint32_t));
}
// Write all data at once to EEPROM
eeprom_update_block(buffer, (void *)EEPROM_WORK_TIMER_ACTIVE, sizeof(buffer)); eeprom_update_block(buffer, (void *)EEPROM_WORK_TIMER_ACTIVE, sizeof(buffer));
} }
/** /**
* Load the work timer state from EEPROM * Load timer state from EEPROM
*/ */
static void load_work_timer_state(void) { static void load_timer_state(void) {
// Create a buffer to read all our data uint8_t buffer[24] = {0};
uint8_t buffer[24] = {0}; // Increased from 21 to 24 bytes to match save function
// Read all data at once from EEPROM // Read from EEPROM
eeprom_read_block(buffer, (const void *)EEPROM_WORK_TIMER_ACTIVE, sizeof(buffer)); eeprom_read_block(buffer, (const void *)EEPROM_WORK_TIMER_ACTIVE, sizeof(buffer));
// Get the active flag // Basic state
work_timer.flags.active = buffer[0]; timer.active = buffer[0] ? true : false;
timer.paused = buffer[1] ? true : false;
timer.type = (work_timer_type_t)buffer[2];
// Only process the rest if timer was active // Time values
if (work_timer.flags.active) { memcpy(&timer.start_time, &buffer[3], sizeof(uint32_t));
// Load timer type memcpy(&timer.end_time, &buffer[7], sizeof(uint32_t));
work_timer_type_t saved_type = (work_timer_type_t)buffer[9]; memcpy(&timer.pause_time, &buffer[11], sizeof(uint32_t));
memcpy(&timer.break_time, &buffer[15], sizeof(uint32_t));
memcpy(&timer.mid_point, &buffer[19], sizeof(uint32_t));
// Get start time (4 bytes) // Calculate mid_point_end from loaded values
memcpy(&work_timer.start_time, &buffer[1], sizeof(uint32_t)); if (timer.active) {
timer.mid_point_end = timer.mid_point + timer.break_time;
// Get end time (4 bytes) // Verify timer is still valid
memcpy(&work_timer.end_time, &buffer[5], sizeof(uint32_t)); if (!timer.paused) {
// Get pause state and time (5 bytes)
work_timer.flags.paused = buffer[10];
memcpy(&work_timer.pause_time, &buffer[11], sizeof(uint32_t));
// Get break state (1 byte)
uint8_t break_state = buffer[15];
work_timer.flags.lunch_break = (break_state & 1) > 0;
work_timer.flags.mid_break = (break_state & 2) > 0;
// Get break start time (4 bytes)
memcpy(&work_timer.break_start_time, &buffer[16], sizeof(uint32_t));
// Get mid-break time (4 bytes) - fixed buffer position
memcpy(&work_timer.mid_break_time, &buffer[20], sizeof(uint32_t));
// Apply configuration for this timer type (durations, etc.)
configure_timer_for_type(saved_type);
// If currently in a break, we need to adjust for time passed since poweroff
if (work_timer.flags.lunch_break || work_timer.flags.mid_break) {
uint32_t current_time = timer_read32(); uint32_t current_time = timer_read32();
uint32_t time_off = current_time - work_timer.break_start_time; if (current_time > timer.end_time) {
timer.active = false;
// If we've been powered off longer than the break duration, end the break save_timer_state();
if (time_off > work_timer.mid_break_duration) {
work_timer.flags.lunch_break = 0;
work_timer.flags.mid_break = 0;
} }
} }
// Validate timer values
uint32_t current_time = timer_read32();
// If end time is in the past, timer is done
if (work_timer.end_time <= current_time && !work_timer.flags.paused) {
work_timer.flags.active = 0;
save_work_timer_state();
}
// Check for end warning state (within 5 minutes of end)
if (!work_timer.flags.paused &&
current_time >= (work_timer.end_time - BREAK_WARNING_TIME)) {
work_timer.flags.end_warning_shown = 1;
}
// Update pulse active state
update_pulse_active_state();
} }
} }
/** /**
* Display progress bar with gradient colors * Display progress bar with gradient colors
*/ */
static void display_progress_bar(uint8_t num_leds, float overall_progress, float brightness_factor) { static void display_progress_bar(uint8_t num_leds, float progress, float brightness) {
// Calculate hour segments and LED positions // Progress is a value from 0.0 to 1.0
float hours_per_led = 1.0f / (float)num_leds; float hours_per_led = 1.0f / (float)num_leds;
// Determine how many LEDs should be fully lit // Determine how many LEDs should be lit
uint8_t leds_lit = (uint8_t)(overall_progress / hours_per_led); uint8_t leds_lit = (uint8_t)(progress / hours_per_led);
if (leds_lit > num_leds) leds_lit = num_leds; if (leds_lit > num_leds) leds_lit = num_leds;
// Calculate progress within the current LED // Calculate progress for the partially-lit LED
float current_led_progress = (overall_progress - (leds_lit * hours_per_led)) / hours_per_led; float current_led_progress = (progress - (leds_lit * hours_per_led)) / hours_per_led;
// Set colors for each LED in the progress bar // Set colors for each LED in the progress bar
for (uint8_t i = 0; i < num_leds; i++) { for (uint8_t i = 0; i < num_leds; i++) {
@ -257,46 +179,41 @@ static void display_progress_bar(uint8_t num_leds, float overall_progress, float
if (i < leds_lit) { if (i < leds_lit) {
// Fully lit LED - calculate gradient color based on position // Fully lit LED - calculate gradient color based on position
float led_position = (float)i / (float)(num_leds - 1); float position = (float)i / (float)(num_leds - 1);
// Adjust transition points - more green-to-orange (first 80% of bar), // Use green to orange gradient for most of bar, orange to red at end
// and less orange-to-red (last 20% of bar) if (position < 0.7f) {
if (led_position < 0.3f) { // First 70% is green to orange
// Scale to 0.0 - 1.0 for green-to-orange part (now 80% of the bar) float adj_progress = position / 0.7f;
float adjusted_progress = led_position / 0.3f; color = calculate_gradient_color(TIMER_START_COLOR, TIMER_MID_COLOR, adj_progress);
color = calculate_gradient_color(WORK_TIMER_START_COLOR, WORK_TIMER_MID_COLOR, adjusted_progress); } else {
} // Last 30% is orange to red
else { float adj_progress = (position - 0.7f) / 0.3f;
float adjusted_progress = (led_position - 0.3f) / 0.7f; // Scale to 0.0 - 1.0 for last 20% color = calculate_gradient_color(TIMER_MID_COLOR, TIMER_END_COLOR, adj_progress);
color = calculate_gradient_color(WORK_TIMER_MID_COLOR, WORK_TIMER_END_COLOR, adjusted_progress);
} }
// Apply overall RGB brightness factor // Apply brightness
color.r = (uint8_t)((float)color.r * brightness_factor); color.r = (uint8_t)((float)color.r * brightness);
color.g = (uint8_t)((float)color.g * brightness_factor); color.g = (uint8_t)((float)color.g * brightness);
color.b = (uint8_t)((float)color.b * brightness_factor); color.b = (uint8_t)((float)color.b * brightness);
} }
else if (i == leds_lit && current_led_progress > 0.0f) { else if (i == leds_lit && current_led_progress > 0.0f) {
// Current LED - partially lit based on progress // Partially lit LED
float led_position = (float)i / (float)(num_leds - 1); float position = (float)i / (float)(num_leds - 1);
rgb_color_t full_color; rgb_color_t full_color;
// Adjust transition points - more green-to-orange, less orange-to-red
if (led_position < 0.3f) { if (position < 0.7f) {
// Scale to 0.0 - 1.0 for green-to-orange part float adj_progress = position / 0.7f;
float adjusted_progress = led_position / 0.3f; full_color = calculate_gradient_color(TIMER_START_COLOR, TIMER_MID_COLOR, adj_progress);
full_color = calculate_gradient_color(WORK_TIMER_START_COLOR, WORK_TIMER_MID_COLOR, adjusted_progress); } else {
} float adj_progress = (position - 0.7f) / 0.3f;
// Last 30% is orange-to-red gradient full_color = calculate_gradient_color(TIMER_MID_COLOR, TIMER_END_COLOR, adj_progress);
else {
float adjusted_progress = (led_position - 0.3f) / 0.7f; // Scale to 0.0 - 1.0 for last 20%
full_color = calculate_gradient_color(WORK_TIMER_MID_COLOR, WORK_TIMER_END_COLOR, adjusted_progress);
} }
// Dim the color based on progress within this LED and overall brightness // Dim by progress and brightness
color.r = (uint8_t)((float)full_color.r * current_led_progress * brightness_factor); color.r = (uint8_t)((float)full_color.r * current_led_progress * brightness);
color.g = (uint8_t)((float)full_color.g * current_led_progress * brightness_factor); color.g = (uint8_t)((float)full_color.g * current_led_progress * brightness);
color.b = (uint8_t)((float)full_color.b * current_led_progress * brightness_factor); color.b = (uint8_t)((float)full_color.b * current_led_progress * brightness);
} }
// Set the LED color // Set the LED color
@ -305,326 +222,114 @@ static void display_progress_bar(uint8_t num_leds, float overall_progress, float
} }
/** /**
* Update the pulse active state * Initialize the work timer
* This determines if any pulse effect is currently active
*/ */
static void update_pulse_active_state(void) { void work_timer_init(void) {
if (!work_timer.flags.active || work_timer.flags.paused) { load_timer_state();
work_timer.flags.pulse_active = false;
return;
}
uint32_t current_time = timer_read32();
// Check if any pulse condition is active
bool lunch_warning = false;
bool lunch_end_warning = false;
bool mid_point_warning = false;
bool end_warning = false;
// Mid-break (lunch) timing logic
if (work_timer.has_lunch_break) {
// Time until mid-break (lunch)
uint32_t time_to_mid_break = 0;
if (current_time < work_timer.mid_break_time) {
time_to_mid_break = work_timer.mid_break_time - current_time;
}
// Lunch break warning (before lunch)
lunch_warning = !work_timer.flags.lunch_break &&
(time_to_mid_break > 0 && time_to_mid_break < BREAK_WARNING_TIME);
// Lunch end warning (before end of lunch)
lunch_end_warning = work_timer.flags.lunch_break &&
(timer_elapsed32(work_timer.break_start_time) >=
(work_timer.mid_break_duration - BREAK_WARNING_TIME));
}
else {
// Simple mid-break logic
uint32_t time_to_mid_break = 0;
if (current_time < work_timer.mid_break_time) {
time_to_mid_break = work_timer.mid_break_time - current_time;
}
// Mid-point break warning for shorter timers
mid_point_warning = !work_timer.flags.mid_break &&
(time_to_mid_break > 0 && time_to_mid_break < BREAK_WARNING_TIME);
}
// End timer warning (5 minutes before end)
end_warning = (current_time >= (work_timer.end_time - BREAK_WARNING_TIME) &&
current_time < work_timer.end_time);
// Set pulse active if any of these conditions are true
work_timer.flags.pulse_active = (
work_timer.flags.mid_break || // Mid-point break pulse
work_timer.flags.lunch_break || // Lunch break
work_timer.flags.end_warning_shown || // End warning
lunch_warning || // Pre-lunch warning
lunch_end_warning || // End of lunch warning
mid_point_warning || // Mid-point warning for shorter timers
end_warning // End warning for all timers
);
} }
/** /**
* Toggle the work timer on/off * Toggle the work timer on/off
*/ */
void toggle_work_timer(void) { void toggle_work_timer(void) {
if (work_timer.flags.active) { if (timer.active) {
// If timer is active, stop it and reset all state flags // Turn off timer
work_timer.flags.active = 0; timer.active = false;
work_timer.flags.paused = 0; timer.paused = false;
work_timer.flags.lunch_break = 0;
work_timer.flags.mid_break = 0;
work_timer.flags.lunch_warning_shown = 0;
work_timer.flags.mid_break_warning_shown = 0;
work_timer.flags.end_warning_shown = 0;
work_timer.flags.pulse_active = 0;
// Save clean state to EEPROM save_timer_state();
save_work_timer_state();
// Force immediate RGB refresh // Force RGB refresh
rgb_matrix_mode_noeeprom(rgb_matrix_get_mode()); rgb_matrix_mode_noeeprom(rgb_matrix_get_mode());
} else { } else {
// If timer is inactive, start a new timer // Start a new timer
work_timer.flags.active = 1; timer.active = true;
work_timer.flags.paused = 0; timer.paused = false;
work_timer.flags.lunch_break = 0; timer.start_time = timer_read32();
work_timer.flags.mid_break = 0; timer.pause_time = 0;
work_timer.flags.lunch_warning_shown = 0;
work_timer.flags.mid_break_warning_shown = 0;
work_timer.flags.end_warning_shown = 0;
work_timer.flags.pulse_active = 0;
// Set timer start and calculate end time // Configure for current timer type
work_timer.start_time = timer_read32(); configure_timer(timer.type);
work_timer.break_start_time = 0; save_timer_state();
// Set up mid-break time based on current time
uint32_t duration = 0;
switch (work_timer.timer_type) {
case TIMER_TYPE_30MIN: duration = TIMER_30MIN_DURATION; break;
case TIMER_TYPE_1HR: duration = TIMER_1HR_DURATION; break;
case TIMER_TYPE_4HR: duration = TIMER_4HR_DURATION; break;
case TIMER_TYPE_8HR: duration = TIMER_8HR_DURATION; break;
case TIMER_TYPE_10HR: duration = TIMER_10HR_DURATION; break;
default: duration = TIMER_8HR_DURATION; break;
}
// Set mid-break time (halfway through timer)
work_timer.mid_break_time = work_timer.start_time + (duration / 2);
// Set end time
work_timer.end_time = work_timer.start_time + duration;
save_work_timer_state();
} }
update_pulse_active_state();
}
/**
* Initialize the work timer - load state from EEPROM
*/
void work_timer_init(void) {
load_work_timer_state();
} }
/** /**
* Start a specific timer type * Start a specific timer type
*/ */
void start_timer(work_timer_type_t timer_type) { void start_timer(work_timer_type_t type) {
// Reset timer state // Start fresh
work_timer.flags.active = 1; timer.active = true;
work_timer.flags.paused = 0; timer.paused = false;
work_timer.flags.lunch_break = 0; timer.start_time = timer_read32();
work_timer.flags.mid_break = 0; timer.pause_time = 0;
work_timer.flags.lunch_warning_shown = 0;
work_timer.flags.mid_break_warning_shown = 0;
work_timer.flags.end_warning_shown = 0;
work_timer.flags.pulse_active = 0;
work_timer.start_time = timer_read32(); // Configure based on type
work_timer.break_start_time = 0; configure_timer(type);
save_timer_state();
// Configure timer parameters based on type
work_timer.timer_type = timer_type;
configure_timer_for_type(timer_type);
save_work_timer_state();
// Update pulse state immediately after starting timer
update_pulse_active_state();
} }
/** /**
* Pause or resume the work timer * Pause or resume the timer
*/ */
void toggle_pause_work_timer(void) { void toggle_pause_work_timer(void) {
if (!work_timer.flags.active) return; if (!timer.active) return;
if (!work_timer.flags.paused) { if (timer.paused) {
// Pause the timer // Resume - calculate time paused and adjust timers
work_timer.flags.paused = 1;
work_timer.pause_time = timer_read32();
save_work_timer_state();
} else {
// Resume the timer - extend all time values by pause duration
uint32_t current_time = timer_read32(); uint32_t current_time = timer_read32();
uint32_t pause_duration = current_time - work_timer.pause_time; uint32_t pause_duration = current_time - timer.pause_time;
// Extend end time by pause duration // Extend all time points by pause duration
work_timer.end_time += pause_duration; timer.end_time += pause_duration;
timer.mid_point += pause_duration;
timer.mid_point_end += pause_duration;
// If mid-break hasn't happened yet, extend that too timer.paused = false;
if (current_time < work_timer.mid_break_time) { } else {
work_timer.mid_break_time += pause_duration; // Pause
} timer.pause_time = timer_read32();
timer.paused = true;
// If in a break, adjust break start time too
if (work_timer.flags.lunch_break || work_timer.flags.mid_break) {
work_timer.break_start_time += pause_duration;
}
work_timer.flags.paused = 0;
save_work_timer_state();
} }
update_pulse_active_state(); save_timer_state();
} }
/** /**
* Update the work timer state * Check if the blue pulse effect should be active
*/ */
void update_work_timer(void) { bool is_timer_pulse_active(void) {
if (!work_timer.flags.active || work_timer.flags.paused) return; if (!timer.active || timer.paused) return false;
uint32_t current_time = timer_read32(); uint32_t current_time = timer_read32();
// Process different timer states based on timer type // Only return true when we're in the mid-point break window
if (work_timer.has_lunch_break) { return (current_time >= timer.mid_point && current_time < timer.mid_point_end);
// Handle lunch break state transitions
if (!work_timer.flags.lunch_break) {
// Check if it's time to start lunch
if (!work_timer.flags.lunch_warning_shown &&
current_time >= (work_timer.mid_break_time - BREAK_WARNING_TIME) &&
current_time < work_timer.mid_break_time) {
// Pre-lunch warning
work_timer.flags.lunch_warning_shown = 1;
}
else if (current_time >= work_timer.mid_break_time) {
// Start lunch break
work_timer.flags.lunch_break = 1;
work_timer.break_start_time = current_time;
work_timer.flags.lunch_warning_shown = 0; // Reset for lunch end warning
// Don't need to extend end time yet - will do that when break ends
save_work_timer_state();
}
}
else {
// Currently in lunch break
uint32_t break_elapsed = timer_elapsed32(work_timer.break_start_time);
// Check for lunch end warning
if (!work_timer.flags.lunch_warning_shown &&
break_elapsed >= (work_timer.mid_break_duration - BREAK_WARNING_TIME)) {
// Pre-end warning
work_timer.flags.lunch_warning_shown = 1;
}
// End lunch break after duration (plus grace period)
if (break_elapsed >= (work_timer.mid_break_duration + 5000)) {
// End lunch break
work_timer.flags.lunch_break = 0;
work_timer.flags.lunch_warning_shown = 0;
// Extend end time by break duration
work_timer.end_time += break_elapsed;
save_work_timer_state();
}
}
}
else {
// Handle mid-break state transitions for shorter timers
if (!work_timer.flags.mid_break) {
// Check if it's time to start mid-break
if (!work_timer.flags.mid_break_warning_shown &&
current_time >= (work_timer.mid_break_time - BREAK_WARNING_TIME) &&
current_time < work_timer.mid_break_time) {
// Mid-break warning
work_timer.flags.mid_break_warning_shown = 1;
}
else if (current_time >= work_timer.mid_break_time) {
// Start mid-break
work_timer.flags.mid_break = 1;
work_timer.break_start_time = current_time;
work_timer.flags.mid_break_warning_shown = 0;
save_work_timer_state();
}
}
else {
// Currently in mid-break
uint32_t break_elapsed = timer_elapsed32(work_timer.break_start_time);
// End mid-break after duration (plus grace period)
if (break_elapsed >= (work_timer.mid_break_duration + 5000)) {
// End mid-break
work_timer.flags.mid_break = 0;
work_timer.flags.mid_break_warning_shown = 0;
// Extend end time by break duration
work_timer.end_time += break_elapsed;
save_work_timer_state();
}
}
}
// Check for end of day warning
if (!work_timer.flags.end_warning_shown &&
current_time >= (work_timer.end_time - BREAK_WARNING_TIME)) {
work_timer.flags.end_warning_shown = 1;
}
// Auto-stop after timer ends
if (current_time >= work_timer.end_time) {
work_timer.flags.active = 0;
work_timer.flags.lunch_break = 0;
work_timer.flags.mid_break = 0;
save_work_timer_state();
}
update_pulse_active_state();
} }
/** /**
* Check if any timer pulse is currently active * Update timer state
* Used for easier state checking
*
* @return true if any timer pulse effect is active, false otherwise
*/ */
bool is_timer_pulse_active(void) { void update_work_timer(void) {
// If timer is not active or is paused, no pulse is active if (!timer.active || timer.paused) return;
if (!work_timer.flags.active || work_timer.flags.paused) {
return false;
}
// Return the pulse_active flag directly from the work timer state uint32_t current_time = timer_read32();
return work_timer.flags.pulse_active;
// Check if timer has ended
if (current_time >= timer.end_time) {
timer.active = false;
save_timer_state();
rgb_matrix_mode_noeeprom(rgb_matrix_get_mode());
}
} }
/** /**
* Handle the work timer visualization on LEDs * Handle work timer visualization
*/ */
void handle_work_timer(void) { void handle_work_timer(void) {
static bool was_active = false; static bool was_active = false;
if (!work_timer.flags.active) { if (!timer.active) {
// If the timer was just deactivated, force RGB refresh
if (was_active) { if (was_active) {
rgb_matrix_mode_noeeprom(rgb_matrix_get_mode()); rgb_matrix_mode_noeeprom(rgb_matrix_get_mode());
was_active = false; was_active = false;
@ -634,165 +339,55 @@ void handle_work_timer(void) {
was_active = true; was_active = true;
// Get current RGB matrix brightness // Get current time and brightness
uint32_t current_time = timer_read32();
uint8_t rgb_brightness = rgb_matrix_get_val(); uint8_t rgb_brightness = rgb_matrix_get_val();
float brightness_factor = (float)rgb_brightness / 255.0f; float brightness_factor = (float)rgb_brightness / 255.0f;
// Number of LEDs in the progress bar // Number of LEDs in the progress bar
const uint8_t num_leds = WORK_TIMER_LED_END - WORK_TIMER_LED_START + 1; const uint8_t num_leds = WORK_TIMER_LED_END - WORK_TIMER_LED_START + 1;
// Calculate overall progress (0.0 - 1.0) // Calculate progress based on active timer duration
uint32_t current_time = timer_read32(); float progress;
float overall_progress; if (timer.paused) {
// When paused, use pause time for progress calculation
// Simple progress calculation based on total time range progress = (float)(timer.pause_time - timer.start_time) /
if (work_timer.flags.paused) { (float)(timer.end_time - timer.start_time);
// When paused, use the pause time for calculation
overall_progress = (float)(work_timer.pause_time - work_timer.start_time) /
(float)(work_timer.end_time - work_timer.start_time);
} else { } else {
// Normal operation - current progress through timer // When active, use current time for progress calculation
overall_progress = (float)(current_time - work_timer.start_time) / progress = (float)(current_time - timer.start_time) /
(float)(work_timer.end_time - work_timer.start_time); (float)(timer.end_time - timer.start_time);
} }
if (overall_progress > 1.0f) overall_progress = 1.0f; // Clamp progress to valid range
if (progress > 1.0f) progress = 1.0f;
if (progress < 0.0f) progress = 0.0f;
// Create pulsing effect // Check if we're in the break window (midpoint of the timer)
uint8_t pulse_brightness = abs((timer_read() / 4) % 510 - 255); if (!timer.paused && current_time >= timer.mid_point && current_time < timer.mid_point_end) {
float pulse_ratio = (float)pulse_brightness / 255.0f; // In break time - show blue pulse
rgb_color_t color = TIMER_BREAK_COLOR;
// Check for various timer states // Slow pulse for break time
bool lunch_warning = false; uint8_t break_pulse = timer_read() / 10;
bool lunch_end_warning = false; float break_intensity = (float)(abs(break_pulse % 510 - 255)) / 255.0f;
bool mid_break_warning = false;
bool end_warning = false;
// For timers with lunch breaks // Apply blue pulse to all LEDs in progress bar
if (work_timer.has_lunch_break) { for (uint8_t i = 0; i < num_leds; i++) {
// Pre-lunch warning (red pulse) rgb_matrix_set_color(WORK_TIMER_LED_START + i,
lunch_warning = !work_timer.flags.lunch_break && (uint8_t)((float)color.r * break_intensity * brightness_factor),
(current_time >= (work_timer.mid_break_time - BREAK_WARNING_TIME) && (uint8_t)((float)color.g * break_intensity * brightness_factor),
current_time < work_timer.mid_break_time); (uint8_t)((float)color.b * break_intensity * brightness_factor));
// Lunch-end warning (red pulse)
lunch_end_warning = work_timer.flags.lunch_break &&
work_timer.break_start_time != 0 &&
(timer_elapsed32(work_timer.break_start_time) >=
(work_timer.mid_break_duration - BREAK_WARNING_TIME));
// End warning
end_warning = current_time >= (work_timer.end_time - BREAK_WARNING_TIME) &&
current_time < work_timer.end_time;
// Choose appropriate display based on current state
if (lunch_warning || lunch_end_warning) {
// Pre/Post lunch red warning pulse
rgb_color_t pulse_color = WORK_TIMER_WARNING_COLOR;
// Apply pulsing effect to all progress bar LEDs
for (uint8_t i = 0; i < num_leds; i++) {
rgb_matrix_set_color(WORK_TIMER_LED_START + i,
(uint8_t)((float)pulse_color.r * pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.g * pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.b * pulse_ratio * brightness_factor));
}
}
else if (work_timer.flags.lunch_break && work_timer.break_start_time != 0) {
// During lunch break - blue pulse
rgb_color_t pulse_color = WORK_TIMER_LUNCH_COLOR;
// Use a slower pulse for regular lunch break
uint8_t lunch_pulse = abs((timer_read() / 10) % 510 - 255);
float lunch_pulse_ratio = (float)lunch_pulse / 255.0f;
// Apply lunch break color to all progress bar LEDs
for (uint8_t i = 0; i < num_leds; i++) {
rgb_matrix_set_color(WORK_TIMER_LED_START + i,
(uint8_t)((float)pulse_color.r * lunch_pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.g * lunch_pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.b * lunch_pulse_ratio * brightness_factor));
}
}
else if (work_timer.flags.end_warning_shown || end_warning) {
// End of day warning - red pulse
rgb_color_t pulse_color = WORK_TIMER_WARNING_COLOR;
// Apply pulsing effect to all progress bar LEDs
for (uint8_t i = 0; i < num_leds; i++) {
rgb_matrix_set_color(WORK_TIMER_LED_START + i,
(uint8_t)((float)pulse_color.r * pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.g * pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.b * pulse_ratio * brightness_factor));
}
}
else {
// Normal progress bar display
display_progress_bar(num_leds, overall_progress, brightness_factor);
}
}
// For timers without lunch breaks (30MIN, 1HR, 4HR)
else {
mid_break_warning = !work_timer.flags.mid_break &&
(current_time >= (work_timer.mid_break_time - BREAK_WARNING_TIME) &&
current_time < work_timer.mid_break_time);
end_warning = current_time >= (work_timer.end_time - BREAK_WARNING_TIME) &&
current_time < work_timer.end_time;
// Choose appropriate display based on current state
if (mid_break_warning) {
// Mid-break warning - red pulse
rgb_color_t pulse_color = WORK_TIMER_WARNING_COLOR;
// Apply pulsing effect to all progress bar LEDs
for (uint8_t i = 0; i < num_leds; i++) {
rgb_matrix_set_color(WORK_TIMER_LED_START + i,
(uint8_t)((float)pulse_color.r * pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.g * pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.b * pulse_ratio * brightness_factor));
}
}
// Mid-break active
else if (work_timer.flags.mid_break && work_timer.break_start_time != 0) {
// Mid-break active - blue pulse
rgb_color_t pulse_color = WORK_TIMER_LUNCH_COLOR;
// Use a faster pulse for mid-break reminder
uint8_t mid_pulse = abs((timer_read() / 3) % 510 - 255);
float mid_pulse_ratio = (float)mid_pulse / 255.0f;
// Apply pulsing effect to all progress bar LEDs
for (uint8_t i = 0; i < num_leds; i++) {
rgb_matrix_set_color(WORK_TIMER_LED_START + i,
(uint8_t)((float)pulse_color.r * mid_pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.g * mid_pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.b * mid_pulse_ratio * brightness_factor));
}
}
else if (work_timer.flags.end_warning_shown || end_warning) {
// End of timer warning - red pulse
rgb_color_t pulse_color = WORK_TIMER_WARNING_COLOR;
// Apply pulsing effect to all progress bar LEDs
for (uint8_t i = 0; i < num_leds; i++) {
rgb_matrix_set_color(WORK_TIMER_LED_START + i,
(uint8_t)((float)pulse_color.r * pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.g * pulse_ratio * brightness_factor),
(uint8_t)((float)pulse_color.b * pulse_ratio * brightness_factor));
}
}
else {
// Normal progress bar display
display_progress_bar(num_leds, overall_progress, brightness_factor);
} }
} else {
// Normal progress display
display_progress_bar(num_leds, progress, brightness_factor);
} }
} }
/** /**
* Work timer task - can be called periodically * Periodic timer task
*/ */
void work_timer_task(void) { void work_timer_task(void) {
// Update work timer state
update_work_timer(); update_work_timer();
} }

View File

@ -58,11 +58,6 @@ typedef enum {
#define WORK_TIMER_LUNCH_G 0 #define WORK_TIMER_LUNCH_G 0
#define WORK_TIMER_LUNCH_B 255 #define WORK_TIMER_LUNCH_B 255
// Warning color (red)
#define WORK_TIMER_WARNING_R 255
#define WORK_TIMER_WARNING_G 0
#define WORK_TIMER_WARNING_B 0
// Standard timer durations (in milliseconds) // Standard timer durations (in milliseconds)
// IMPORTANT: These represent actual working time (excluding breaks) // IMPORTANT: These represent actual working time (excluding breaks)
#define TIMER_30MIN_DURATION 1800000 // 30 minutes #define TIMER_30MIN_DURATION 1800000 // 30 minutes
@ -72,7 +67,7 @@ typedef enum {
#define TIMER_10HR_DURATION 36000000 // 10 hours (real world 11 hours with lunch) #define TIMER_10HR_DURATION 36000000 // 10 hours (real world 11 hours with lunch)
// Break durations // Break durations
#define BREAK_WARNING_TIME 60000 // 60 seconds #define BREAK_WARNING_TIME 60000 // 60 seconds (kept for calculations but not used for warnings)
#define MID_BREAK_30MIN_DURATION 30000 // 30 seconds #define MID_BREAK_30MIN_DURATION 30000 // 30 seconds
#define MID_BREAK_1HR_DURATION 45000 // 45 seconds #define MID_BREAK_1HR_DURATION 45000 // 45 seconds
#define MID_BREAK_4HR_DURATION 60000 // 60 seconds #define MID_BREAK_4HR_DURATION 60000 // 60 seconds