Skip to content

Commit

Permalink
finished light effects (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
1-rafael-1 authored Aug 8, 2024
1 parent 65b0eb8 commit b858786
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 53 deletions.
37 changes: 24 additions & 13 deletions src/task/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,19 +178,30 @@ pub async fn display_handler(
drop(state_manager_guard);

// get the system datetime
let dt = match rtc_ref.borrow().now() {
Ok(dt) => dt,
Err(e) => {
info!("RTC not running: {:?}", Debug2Format(&e));
// return an empty DateTime
DateTime {
year: 0,
month: 0,
day: 0,
day_of_week: DayOfWeek::Monday,
hour: 0,
minute: 0,
second: 0,
let dt = {
let rtc = match rtc_ref.try_borrow() {
Ok(rtc) => rtc,
Err(_) => {
error!("RTC borrow failed");
Timer::after(Duration::from_secs(1)).await;
continue;
}
};

match rtc.now() {
Ok(dt) => dt,
Err(e) => {
info!("RTC not running: {:?}", Debug2Format(&e));
// Return an empty DateTime
DateTime {
year: 0,
month: 0,
day: 0,
day_of_week: DayOfWeek::Monday,
hour: 0,
minute: 0,
second: 0,
}
}
}
};
Expand Down
150 changes: 110 additions & 40 deletions src/task/light_effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::task::task_messages::{
use defmt::*;
use embassy_executor::Spawner;
use embassy_rp::spi::{Config, Phase, Polarity, Spi};
use embassy_time::Instant;
use embassy_time::{Duration, Timer};
use smart_leds::{brightness, RGB8};
use ws2812_async::Ws2812;
Expand Down Expand Up @@ -49,6 +50,20 @@ impl NeopixelManager {
b: color.2,
}
}

/// Function to convert a color wheel value to RGB
pub fn wheel(&self, mut wheel_pos: u8) -> RGB8 {
wheel_pos = 255 - wheel_pos;
if wheel_pos < 85 {
return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();
}
if wheel_pos < 170 {
wheel_pos -= 85;
return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();
}
wheel_pos -= 170;
(wheel_pos * 3, 255 - wheel_pos * 3, 0).into()
}
}

#[embassy_executor::task]
Expand All @@ -72,9 +87,10 @@ pub async fn light_effects_handler(_spawner: Spawner, r: NeopixelResources) {
let mut data = [RGB8::default(); NUM_LEDS];
np.write(brightness(data.iter().cloned(), 0)).await.ok();

loop {
'_outer: loop {
// wait for the signal to update the neopixel
let command = LIGHTFX_SIGNAL.wait().await;
info!("LightFX signal received: {:?}", command);
let (hour, minute, second) = match command {
Commands::LightFXUpdate((hour, minute, second)) => (hour, minute, second),
_ => (0, 0, 0),
Expand Down Expand Up @@ -143,6 +159,7 @@ pub async fn light_effects_handler(_spawner: Spawner, r: NeopixelResources) {
data[hour_index as usize] = neopixel_mgr.rgb_to_grb((255, 255, 255));
};

// write the data to the neopixel
let _ = np
.write(brightness(
data.iter().cloned(),
Expand All @@ -160,65 +177,118 @@ pub async fn light_effects_handler(_spawner: Spawner, r: NeopixelResources) {
match state_manager.alarm_state {
AlarmState::Sunrise => {
info!("Sunrise effect");
// simumlate a sunrise: start with all leds off, then slowly add leds while all leds that are already used slowly change color from red to warm white

// ToDo: loop through the sunrise effect, just a simple blinking effect for now
let mut cntr = 0;
loop {
if cntr > 10 {
break;
// all off
let mut data = [RGB8::default(); NUM_LEDS];
let _ = np.write(brightness(data.iter().cloned(), 0)).await;

// initialize the variables
let start_color = RGB8::new(139, 0, 0); //dark red
let end_color = RGB8::new(255, 250, 244); // warm white
let end_brightness = 100.0;
let effect_duration: u64 = 60; // seconds
let start_time = Instant::now();

// loop for duration seconds
'sunrise: while Instant::now() - start_time
< Duration::from_secs(effect_duration)
{
// check if the effect should be stopped
if LIGHTFX_STOP_SIGNAL.signaled() {
info!("Sunrise effect aborting");
LIGHTFX_STOP_SIGNAL.reset();
break 'sunrise;
}

// Set all LEDs to blue
let color = neopixel_mgr.rgb_to_grb((0, 0, 255));
let data = [color; NUM_LEDS];
let _ = np
.write(brightness(
data.iter().cloned(),
neopixel_mgr.clock_brightness(),
))
.await;
Timer::after(Duration::from_secs(1)).await;
// calculate the elapsed time and the remaining time
let elapsed_time = Instant::now() - start_time;
let remaining_time =
Duration::from_secs(effect_duration) - elapsed_time;
let fraction_elapsed =
elapsed_time.as_secs() as f32 / effect_duration as f32;

// calculate the current brightness based on the elapsed time
let current_brightness = end_brightness as u8
- (remaining_time.as_secs() as f32 / effect_duration as f32
* end_brightness) as u8;

// calculate the current color based on the elapsed time
let mut current_color = RGB8::new(
start_color.r
+ ((end_color.r as i16 - start_color.r as i16) as f32
/ effect_duration as f32
* elapsed_time.as_secs() as f32)
as u8,
start_color.g
+ ((end_color.g as i16 - start_color.g as i16) as f32
/ effect_duration as f32
* elapsed_time.as_secs() as f32)
as u8,
start_color.b
+ ((end_color.b as i16 - start_color.b as i16) as f32
/ effect_duration as f32
* elapsed_time.as_secs() as f32)
as u8,
);
current_color = neopixel_mgr.rgb_to_grb((
current_color.r,
current_color.g,
current_color.b,
));

// all off
let data = [RGB8::default(); NUM_LEDS];
np.write(brightness(data.iter().cloned(), 0)).await.ok();
Timer::after(Duration::from_secs(1)).await;
// calculate the number of leds to light up based on the elapsed time fraction, min 1, max NUM_LEDS
let current_leds = (((fraction_elapsed * NUM_LEDS as f32) as usize)
+ 1)
.clamp(1, NUM_LEDS);

cntr += 1;
// set the leds
for i in 0..current_leds {
data[i] = current_color;
}

// write the date to the neopixel
let _ = np
.write(brightness(data.iter().cloned(), current_brightness))
.await;
}

EVENT_CHANNEL
.sender()
.send(Events::SunriseEffectFinished)
.await;

// and wait a bit, so that the last of the effect is visible
Timer::after(Duration::from_millis(300)).await;
}
AlarmState::Noise => {
info!("Noise effect");
// ToDo: loop through the noise effect, until the alarm is stopped
loop {

// a beautiful rainbow effect, taken from https://github.com/kalkyl/ws2812-async
let mut data = [RGB8::default(); NUM_LEDS];

'noise: loop {
if LIGHTFX_STOP_SIGNAL.signaled() {
info!("Noise effect aborting");
LIGHTFX_STOP_SIGNAL.reset();
break;
}
break 'noise;
};

// Set all LEDs to red
let color = neopixel_mgr.rgb_to_grb((255, 0, 0));
let data = [color; NUM_LEDS];
let _ = np
.write(brightness(
for j in 0..(256 * 5) {
for i in 0..NUM_LEDS {
data[i] = neopixel_mgr.wheel(
(((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255)
as u8,
);
}
np.write(brightness(
data.iter().cloned(),
neopixel_mgr.clock_brightness(),
neopixel_mgr.alarm_brightness,
))
.await;
Timer::after(Duration::from_secs(1)).await;

// all off
let data = [RGB8::default(); NUM_LEDS];
np.write(brightness(data.iter().cloned(), 0)).await.ok();
Timer::after(Duration::from_secs(1)).await;

Timer::after(Duration::from_secs(1)).await;
.await
.ok();
Timer::after(Duration::from_millis(5)).await;
}
}
}
AlarmState::None => {
Expand Down

0 comments on commit b858786

Please sign in to comment.