#include #include #include "lights.h" #define AMBIENT_FAST_SHIFT 2 // running avg of ADC vals #define AMBIENT_SLOW_SHIFT 6 // 1 << AMBIENT_SLOW_SHIFT times slower static uint16_t ambient_fast, ambient_slow; volatile unsigned char ambient_zone, ambient_shadow; static unsigned char ambient_min, ambient_max, ambient_drop; /* logging */ #define AMBIENT_LOG_SIZE 256 static unsigned char ambient_log_offset_stored EEMEM; static unsigned char ambient_log_offset; static unsigned char ambient_log[AMBIENT_LOG_SIZE] EEMEM; /* My photodiode reads 0x00C5 .. 0x033B */ typedef struct { uint16_t lo, hi; // TODO: consider changing this to bytes } ambient_zone_t; /* * Note: these have to be sorted, starting with 0, ending with 0xFFFF * and having small overlaps in order to provide a bit of hysteresis. */ static ambient_zone_t ambient_zones[N_AMBIENT_ZONES] = { #ifdef PAVLINA { 0x0000, 0x02d0 }, // dark { 0x02b0, 0x0318 }, // evening { 0x0308, 0x032c }, // dawn { 0x0324, 0xffff }, // day #else { 0x0000, 0x0250 }, // dark { 0x0230, 0x02e8 }, // evening { 0x02d0, 0x0302 }, // dawn { 0x02fc, 0xffff }, // day #endif }; #define SHADOW_DROP_LIMIT 0x20 // in ADC units (0..0x3ff) void init_ambient() { ambient_slow = 0; ambient_fast = 0; ambient_max = 0; ambient_min = 0xFF; ambient_zone = 1; ambient_drop = 0; ambient_shadow = 0; ambient_log_offset = eeprom_read_byte(&ambient_log_offset_stored); if (ambient_log_offset == AMBIENT_LOG_SIZE) ambient_log_offset = 0; // start over } void susp_ambient() { unsigned char stored_offset; ambient_log_min_max(); stored_offset = eeprom_read_byte(&ambient_log_offset_stored); if (stored_offset != ambient_log_offset) eeprom_write_byte(&ambient_log_offset_stored, ambient_log_offset); } void ambient_log_min_max() { if (ambient_log_offset >= AMBIENT_LOG_SIZE - 1) ambient_log_offset = 0; // start over ambient_max -= ambient_min; // ambient_max >>= 2; if (ambient_max > 15) ambient_max = 15; ambient_drop >>= 2; if (ambient_drop > 15) ambient_drop = 15; eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_min); eeprom_write_byte(&ambient_log[ambient_log_offset++], (ambient_max << 4) | ambient_drop); ambient_min = 0xFF; ambient_max = 0; ambient_drop = 0; } static inline void ambient_zone_changed() { pwmled_select_brightness(); pattern_reload(); } static unsigned char val_to_zone(uint16_t ambient_val) { unsigned char new_zone = ambient_zone; if (new_zone >= N_AMBIENT_ZONES) new_zone = N_AMBIENT_ZONES-1; while (ambient_zones[new_zone].lo > ambient_val) new_zone--; while (ambient_zones[new_zone].hi < ambient_val) new_zone++; return new_zone; } void ambient_adc(uint16_t adcval) { unsigned char new_zone, user_zone, new_shadow = 0; unsigned char byte_fast, byte_slow, drop; uint16_t fast_10bit, slow_10bit; // running avg - shorter timespan ambient_fast += adcval - (ambient_fast >> AMBIENT_FAST_SHIFT); // running avg - longer timespan fast_10bit = ambient_fast >> (AMBIENT_FAST_SHIFT + AMBIENT_ADC_SHIFT); ambient_slow += fast_10bit - (ambient_slow >> AMBIENT_SLOW_SHIFT); // ambient zones are governed by shorter timespan by default new_zone = val_to_zone(fast_10bit); slow_10bit = ambient_slow >> AMBIENT_SLOW_SHIFT; if ((new_zone > ambient_zone) || (new_zone > 1 && new_zone == ambient_zone - 1)) { // but change to the neighbouring zone is governed by _slow, // except to the darkest zone, where we want fast reaction. new_zone = val_to_zone(slow_10bit); } // user_param ambient zone override if ((user_zone = get_user_param(0)) > 0) new_zone = user_zone - 1; #if 0 // ignore shadow-entering code altogether for now // are we entering the shadow? if (!user_zone && new_zone < ambient_zone && ambient_zone >= 2 && slow_10bit > fast_10bit && slow_10bit - fast_10bit >= SHADOW_DROP_LIMIT) { // we are entering the shadow new_shadow = 0x30; } if (ambient_shadow) { if (new_shadow) { new_zone = ambient_zone; // don't change while entering shadow ambient_shadow = new_shadow; // update the timeout } else { ambient_shadow--; if (!ambient_shadow) { // leaving the shadow ambient_zone_changed(); } } } else if (new_shadow) { ambient_shadow = new_shadow; // set up the timeout new_zone = ambient_zone; // don't change while entering shadow ambient_zone_changed(); // notify others the first time } #endif // TODO: maybe use these values instead of 10-bit? byte_fast = fast_10bit >> 2; byte_slow = slow_10bit >> 2; if (byte_slow > byte_fast) { drop = byte_slow - byte_fast; if (drop > ambient_drop) ambient_drop = drop; } if (ambient_min > byte_slow) ambient_min = byte_slow; if (ambient_max < byte_slow) ambient_max = byte_slow; if (new_zone != ambient_zone) { ambient_zone = new_zone; #if 0 log_byte(0xab); log_byte(ambient_zone); log_word(adcval); log_flush(); #endif ambient_zone_changed(); } }