+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/interrupt.h>
+
+#include "rgbstring.h"
+
+// #define CHRISTMAS_TREE 1
+
+#define rgb_return(r, g, b) do { send_rgb((r), (g), (b)); return 1; } while(0)
+
+#define VERT_SIZE 47
+
+/* RNG from ADC noise */
+static unsigned char rand_pool[8], rand_pool_off, prev_bit, rand_pool_out;
+
+static void init_rng()
+{
+ ADCSRA |= _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1); // enable, clk/64
+ ADMUX = _BV(REFS1) | _BV(MUX0) | _BV(MUX3); // 1.1V, PB5:PB5, gain 20
+ DIDR0 = _BV(ADC0D);
+ ADCSRA |= _BV(ADIE) | _BV(ADSC);
+ rand_pool_off = 0;
+ prev_bit = 0;
+}
+
+static unsigned char rand() {
+ unsigned char rv = 0;
+
+ rv = rand_pool[rand_pool_out];
+ rand_pool_out++;
+ if (rand_pool_out >= sizeof(rand_pool))
+ rand_pool_out = 0;
+
+ return rv;
+}
+
+ISR(ADC_vect) {
+ ADCSRA |= _BV(ADSC);
+ if ((rand_pool_off & 1) == 0) { // first bit of the pair
+ prev_bit = ADCW & 1;
+ rand_pool_off++;
+ } else {
+ unsigned char bit = ADCW & 1;
+ if (bit == prev_bit) { // whitening fail: try again
+ rand_pool_off--;
+ return;
+ }
+
+ if (bit) {
+ rand_pool[rand_pool_off >> 4]
+ ^= 1 << ((rand_pool_off >> 1) & 7);
+ }
+
+ rand_pool_off++;
+ if (rand_pool_off >= 16*sizeof(rand_pool))
+ rand_pool_off = 0;
+ }
+}
+
+static unsigned int slow_dim[] = {
+ 255, 27, 7, 2,
+};
+
+static void fill_color(unsigned char r, unsigned char g, unsigned char b)
+{
+ unsigned char i;
+
+ for (i = 0; i < STRIP_SIZE; i++)
+ send_rgb(r, g, b);
+
+ end_frame();
+}
+
+unsigned int state;
+
+static void do_buttons()
+{
+ static uint8_t prev_buttons = _BV(PB0) | _BV(PB3) | _BV(PB4);
+ static uint16_t prev_len = 0;
+
+ uint8_t buttons = PINB & (_BV(PB0) | _BV(PB3) | _BV(PB4));
+
+ if (prev_buttons == buttons) {
+ prev_len++;
+ return;
+ }
+
+ // was change
+ if (prev_len < 3 || (buttons != (_BV(PB0) | _BV(PB3) | _BV(PB4)))) {
+ prev_buttons = buttons;
+ prev_len = 0;
+ return;
+ }
+
+ if ((prev_buttons & _BV(PB0)) == 0) {
+ if (state)
+ state--;
+ } else if ((prev_buttons & _BV(PB3)) == 0) {
+ if (state < 5)
+ state++;
+ } else if ((prev_buttons & _BV(PB4)) == 0) {
+ state = 1;
+ }
+
+ prev_buttons = buttons;
+ prev_len = 0;
+}
+
+#define LED_MAX (2*STRIP_SIZE/5)
+#define N_COLORS 8
+#define R_BIAS(x) ((x) << 3)
+#define G_BIAS(x) ((x) >> 1)
+#define B_BIAS(x) ((x) << 1)
+
+static void do_hue()
+{
+ static unsigned char color, led_off;
+ static uint16_t jiffies;
+ unsigned char i, c0, l0;
+
+ if ((jiffies++ & 0x03f) == 0) {
+ if (++led_off >= LED_MAX) {
+ led_off = 0;
+ color++;
+ if (color >= 3*N_COLORS)
+ color = 0;
+ }
+ }
+
+ l0 = led_off;
+ c0 = color;
+
+ for (i = 0; i < STRIP_SIZE; i++) {
+ if (c0 < N_COLORS) {
+ send_rgb(R_BIAS(N_COLORS-c0), G_BIAS(c0), 0);
+ } else if (c0 < 2*N_COLORS) {
+ send_rgb(0, G_BIAS(2*N_COLORS-c0), B_BIAS(c0-N_COLORS));
+ } else {
+ send_rgb(R_BIAS(c0-2*N_COLORS), 0, B_BIAS(3*N_COLORS-c0));
+ }
+
+ if (++l0 >= LED_MAX) {
+ l0 = 0;
+ c0++;
+ if (c0 >= 3*N_COLORS)
+ c0 = 0;
+ }
+ }
+ end_frame();
+}
+
+int main(void)
+{
+
+ init_log();
+ init_rng();
+ init_serial();
+
+ _delay_ms(3000/8); // wait for a bit and then increase the CPU clock
+ CLKPR = _BV(CLKPCE);
+ CLKPR = 0;
+
+ PORTB |= _BV(PB0) | _BV(PB3) | _BV(PB4); // pull-ups for buttons
+
+ state = 0;
+
+ sei();
+
+ while (1) {
+ unsigned char i;
+
+ do_buttons();
+
+ switch (state) {
+ case 0:
+ zero_frame();
+ break;
+ case 1:
+ i = 0;
+ while (i < STRIP_SIZE) {
+ send_rgb(4, 0, 0);
+ send_rgb(4, 1, 0);
+ send_rgb(0, 2, 0);
+ send_rgb(0, 1, 1);
+ send_rgb(0, 0, 2);
+ send_rgb(4, 0, 2);
+ i += 6;
+ }
+ end_frame();
+ break;
+ case 2:
+ do_hue();
+ break;
+ case 3:
+ fill_color(16, 4, 8);
+ break;
+ case 4:
+ fill_color(96, 64, 64);
+ break;
+ case 5:
+ fill_color(255, 255, 255);
+ break;
+ default:
+ { unsigned char light;
+
+ light = slow_dim[sizeof(slow_dim)/sizeof(slow_dim[0]) - state];
+ fill_color(4*light, light, 2*light);
+ }
+ break;
+ }
+ }
+}
+