/* Copyright 2022, Savanni D'Gerinel This file is part of Savanni's AVR library. This AVR library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This AVR library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this AVR library. If not, see . */ #include #include #include #include #include #include #include #include #include #define FPS 15 #define FRAME_DELAY_MS 1000 / FPS // #define FRAME_DELAY_MS 1000 #define LIGHT_COUNT 3 #define PULSE_OFF_COUNT FPS * 5 #define PULSE_ON_COUNT 5 typedef enum { normal, spooky, eerie, flash } color_scheme_e; uint8_t bound_uint8(int value, uint8_t min, uint8_t max) { if (value > max) { return max; } else if (value < min) { return min; } else { return value; } } uint8_t random_step(uint8_t value, uint8_t min, uint8_t max, rng_t *rng) { int8_t step = (rand() % 40) - 20; int new_value = value + step; return bound_uint8(new_value, min, max); } typedef struct { rgb_t color; time_line_t red_line; time_line_t green_line; time_line_t blue_line; uint8_t frame; uint8_t duration; } animation_t; animation_t animation_new(void) { return (animation_t) { .color = { .brightness = 1, .r = 0, .g = 0, .b = 0 }, .red_line = time_line_new(0, 0, 0, 0), .green_line = time_line_new(0, 0, 0, 0), .blue_line = time_line_new(0, 0, 0, 0), .frame = 0, .duration = 0, }; } void animation_begin(animation_t *self, uint8_t rdest, uint8_t gdest, uint8_t bdest, uint8_t duration) { self->red_line = time_line_new(0, self->color.r, duration, rdest); self->green_line = time_line_new(0, self->color.g, duration, gdest); self->blue_line = time_line_new(0, self->color.b, duration, bdest); self->frame = 0; self->duration = duration; } void animation_step(animation_t *self) { if (self->frame == self->duration) { self->frame = 0; self->duration = 0; } else { self->frame++; self->color.r = bound_uint8(time_line_next(&self->red_line), 0, 255); self->color.g = bound_uint8(time_line_next(&self->green_line), 0, 255); self->color.b = bound_uint8(time_line_next(&self->blue_line), 0, 255); } } bool animation_running(animation_t *self) { return self->frame != self->duration; } typedef struct { sk9822_t lights; color_scheme_e color_scheme; animation_t animations[LIGHT_COUNT]; } lantern_t; void display_lantern(display_t *display, lantern_t *lantern); lantern_t lantern_new(sk9822_t lights) { sk9822_init(&lights); return (lantern_t){ .lights = lights, .color_scheme = normal, .animations = { animation_new(), animation_new(), animation_new(), } }; } void lantern_start_normal(lantern_t *self, rng_t *rng, int light_idx) { switch (light_idx) { case 0: animation_begin( &self->animations[0], random_step(self->animations[0].color.r, 180, 255, rng), random_step(self->animations[0].color.g, 20, 50, rng), 0, 5); break; case 1: animation_begin( &self->animations[1], random_step(self->animations[1].color.r, 160, 230, rng), random_step(self->animations[1].color.g, 10, 30, rng), 0, 15); break; case 2: animation_begin( &self->animations[2], random_step(self->animations[2].color.r, 140, 170, rng), random_step(self->animations[2].color.g, 0, 10, rng), 0, 30); break; } } /* void lantern_start_spooky(lantern_t *self, rng_t *rng) { animation_begin( &self->animations[0], 0, random_step(self->animations[0].color.g, 80, 120, rng), 0, 15); animation_begin( &self->animations[1], 0, random_step(self->animations[1].color.g, 60, 100, rng), 0, 15); animation_begin( &self->animations[2], 0, random_step(self->animations[2].color.g, 20, 40, rng), 0, 15); } void lantern_start_eerie(lantern_t *self, rng_t *rng) { animation_begin( &self->animations[0], random_step(self->animations[0].color.g, 20, 40, rng), random_step(self->animations[0].color.g, 20, 40, rng), random_step(self->animations[0].color.g, 80, 120, rng), 15); animation_begin( &self->animations[1], random_step(self->animations[0].color.g, 0, 30, rng), random_step(self->animations[0].color.g, 0, 30, rng), random_step(self->animations[1].color.g, 60, 100, rng), 15); animation_begin( &self->animations[2], random_step(self->animations[0].color.g, 0, 10, rng), random_step(self->animations[0].color.g, 0, 10, rng), random_step(self->animations[2].color.g, 20, 40, rng), 15); } */ void lantern_set_mode(lantern_t *self, color_scheme_e scheme) { self->color_scheme = scheme; } void lantern_step(lantern_t *self, display_t *display, rng_t *rng, rgb_t colors[LIGHT_COUNT]) { /* if (!animation_running(&self->animations[0])) { // display_lantern(display, self); switch (self->color_scheme) { case test: lantern_start_test(self); break; case normal: lantern_start_normal(self, rng); break; case spooky: lantern_start_spooky(self, rng); break; case eerie: lantern_start_eerie(self, rng); break; case flash: break; } } */ for (int i = 0; i < LIGHT_COUNT; i++) { if (!animation_running(&self->animations[i])) { switch (self->color_scheme) { case normal: lantern_start_normal(self, rng, i); break; case spooky: break; case eerie: break; case flash: break; } } animation_step(&self->animations[i]); colors[i] = self->animations[i].color; } } void display_lantern(display_t *display, lantern_t *lantern) { char msg1[20]; char msg2[20]; snprintf(msg1, 20, "[%d,%d] %x,%x,%x", lantern->animations[0].frame, lantern->animations[0].duration, lantern->animations[0].color.r, lantern->animations[0].color.g, lantern->animations[0].color.b ); snprintf(msg2, 20, "%x,%x,%x %x,%x,%x", lantern->animations[1].color.r, lantern->animations[1].color.g, lantern->animations[1].color.b, lantern->animations[2].color.r, lantern->animations[2].color.g, lantern->animations[2].color.b ); display_clear(display); display_write_message(display, msg1); display_set_location(display, 1, 0); display_write_message(display, msg2); } typedef struct { bool on; int timeout; } power_pulse_t; int main(void) { dio_t status_light = { .ddr = &DDRC, .port = &PORTC, .pin = &PINC, .addr = 7 }; dio_set_direction(&status_light, LINE_OUT); dio_t power_pulse = { .ddr = &DDRF, .port = &PORTF, .pin = &PINF, .addr = 0 }; dio_set_direction(&power_pulse, LINE_OUT); display_t display = { .reg = { .output = { .ddr = &DDRF, .port = &PORTF, .pin = &PINF, .addr = 7 }, .shift_clock = { .ddr = &DDRF, .port = &PORTF, .pin = &PINF, .addr = 6 }, .latch_clock = { .ddr = &DDRF, .port = &PORTF, .pin = &PINF, .addr = 5 }, } }; display_init(&display); dio_t buttons[4] = { { .ddr = &DDRB, .port = &PORTB, .pin = &PINB, .addr = 5 }, { .ddr = &DDRB, .port = &PORTB, .pin = &PINB, .addr = 6 }, { .ddr = &DDRB, .port = &PORTB, .pin = &PINB, .addr = 7 }, { .ddr = &DDRD, .port = &PORTD, .pin = &PIND, .addr = 6 }, }; for (int i = 0; i < 4; i++) { dio_set_direction(&buttons[i], LINE_IN); } sk9822_t lights = { .data_pin = { .ddr = &DDRD, .port = &PORTD, .pin = &PIND, .addr = 1 }, .clock_pin = { .ddr = &DDRD, .port = &PORTD, .pin = &PIND, .addr = 0 } }; rgb_t colors[LIGHT_COUNT]; lantern_t lantern = lantern_new(lights); lantern_set_mode(&lantern, normal); rng_t rng = rng_new(15); lantern_step(&lantern, &display, &rng, colors); sk9822_send(&lights, colors, LIGHT_COUNT); power_pulse_t timer = { .on = false, .timeout = PULSE_OFF_COUNT }; while(1) { lantern_step(&lantern, &display, &rng, colors); sk9822_send(&lights, colors, LIGHT_COUNT); if (timer.timeout > 0) { timer.timeout--; } else { if (timer.on) { timer.on = false; dio_set(&power_pulse, 0); timer.timeout = PULSE_OFF_COUNT; } else { timer.on = true; dio_set(&power_pulse, 1); timer.timeout = PULSE_ON_COUNT; } } _delay_ms(FRAME_DELAY_MS); } return 0; }