]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/ambient.c
Adjustments for Yenya
[bike-lights.git] / firmware / ambient.c
1 #include <avr/io.h>
2 #include <avr/eeprom.h>
3
4 #include "lights.h"
5
6 #define AMBIENT_FAST_SHIFT 2    // running avg of ADC vals
7 #define AMBIENT_SLOW_SHIFT 6    // 1 << AMBIENT_SLOW_SHIFT times slower
8
9 static uint16_t ambient_fast, ambient_slow;
10 volatile unsigned char ambient_zone, ambient_shadow;
11 static unsigned char ambient_min, ambient_max, ambient_drop;
12
13 /* logging */
14 #define AMBIENT_LOG_SIZE 256
15 static unsigned char ambient_log_offset_stored EEMEM;
16 static unsigned char ambient_log_offset;
17 static unsigned char ambient_log[AMBIENT_LOG_SIZE] EEMEM;
18
19 /* My photodiode reads 0x00C5 .. 0x033B */
20 typedef struct {
21         uint16_t lo, hi; // TODO: consider changing this to bytes
22 } ambient_zone_t;
23
24 /*
25  * Note: these have to be sorted, starting with 0, ending with 0xFFFF
26  * and having small overlaps in order to provide a bit of hysteresis.
27  */
28 static ambient_zone_t ambient_zones[N_AMBIENT_ZONES] = {
29 #ifdef PAVLINA
30         { 0x0000, 0x02d0 }, // dark
31         { 0x02b0, 0x0318 }, // evening
32         { 0x0308, 0x032c }, // dawn
33         { 0x0324, 0xffff }, // day
34 #else
35         { 0x0000, 0x0250 }, // dark
36         { 0x0230, 0x02e0 }, // evening
37         { 0x02c0, 0x0302 }, // dawn
38         { 0x02fa, 0xffff }, // day
39 #endif
40 };
41
42 #define SHADOW_DROP_LIMIT       0x20 // in ADC units (0..0x3ff)
43
44 void init_ambient()
45 {
46         ambient_slow = 0;
47         ambient_fast = 0;
48         ambient_max = 0;
49         ambient_min = 0xFF;
50         ambient_zone = 1;
51         ambient_drop = 0;
52         ambient_shadow = 0;
53
54         ambient_log_offset = eeprom_read_byte(&ambient_log_offset_stored);
55
56         if (ambient_log_offset == AMBIENT_LOG_SIZE)
57                 ambient_log_offset = 0; // start over
58 }
59
60 void susp_ambient()
61 {
62         unsigned char stored_offset;
63
64         ambient_log_min_max();
65
66         stored_offset = eeprom_read_byte(&ambient_log_offset_stored);
67         if (stored_offset != ambient_log_offset)
68                 eeprom_write_byte(&ambient_log_offset_stored,
69                         ambient_log_offset);
70 }
71
72 void ambient_log_min_max()
73 {
74         if (ambient_log_offset >= AMBIENT_LOG_SIZE - 1)
75                 ambient_log_offset = 0; // start over
76
77         ambient_max -= ambient_min;
78         // ambient_max >>= 2;
79         if (ambient_max > 15)
80                 ambient_max = 15;
81
82         ambient_drop >>= 2;
83         if (ambient_drop > 15)
84                 ambient_drop = 15;
85
86         eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_min);
87         eeprom_write_byte(&ambient_log[ambient_log_offset++],
88                 (ambient_max << 4) | ambient_drop);
89
90         ambient_min = 0xFF;
91         ambient_max = 0;
92         ambient_drop = 0;
93 }
94
95 static inline void ambient_zone_changed()
96 {
97         pwmled_select_brightness();
98         pattern_reload();
99 }
100
101 static unsigned char val_to_zone(uint16_t ambient_val)
102 {
103         unsigned char new_zone = ambient_zone;
104
105         while (ambient_zones[new_zone].lo > ambient_val)
106                 new_zone--;
107
108         while (ambient_zones[new_zone].hi < ambient_val)
109                 new_zone++;
110
111         return new_zone;
112 }
113
114 void ambient_adc(uint16_t adcval)
115 {
116         unsigned char new_zone, user_zone, new_shadow = 0;
117         unsigned char byte_fast, byte_slow, drop;
118         uint16_t fast_10bit, slow_10bit;
119
120         // running avg - shorter timespan
121         ambient_fast += adcval - (ambient_fast >> AMBIENT_FAST_SHIFT);
122
123         // running avg - longer timespan
124         fast_10bit = ambient_fast >> (AMBIENT_FAST_SHIFT + AMBIENT_ADC_SHIFT);
125         ambient_slow += fast_10bit - (ambient_slow >> AMBIENT_SLOW_SHIFT);
126
127         // ambient zones are governed by shorter timespan by default
128         new_zone = val_to_zone(fast_10bit);
129
130         slow_10bit = ambient_slow >> AMBIENT_SLOW_SHIFT;
131
132         if (new_zone > 1 && (
133                 new_zone == ambient_zone-1 || new_zone > ambient_zone)) {
134                 // but change to the neighbouring zone is governed by _slow,
135                 // except to the darkest zone, where we want fast reaction.
136                 new_zone = val_to_zone(slow_10bit);
137         }
138
139         // user_param ambient zone override
140         if ((user_zone = get_user_param(0)) > 0)
141                 new_zone = user_zone - 1;
142
143         // are we entering the shadow?
144         if (!user_zone && new_zone < ambient_zone
145                 && ambient_zone >= 2 && slow_10bit > fast_10bit
146                 && slow_10bit - fast_10bit >= SHADOW_DROP_LIMIT) {
147                 // we are entering the shadow
148                 new_shadow = 0x30;
149         }
150
151         if (ambient_shadow) {
152                 if (new_shadow) {
153                         new_zone = ambient_zone; // don't change while entering shadow
154                         ambient_shadow = new_shadow; // update the timeout
155                 } else {
156                         ambient_shadow--;
157                         if (!ambient_shadow) { // leaving the shadow
158                                 ambient_zone_changed();
159                         }
160                 }
161         } else if (new_shadow) {
162                 ambient_shadow = new_shadow; // set up the timeout
163                 new_zone = ambient_zone; // don't change while entering shadow
164                 ambient_zone_changed();  // notify others the first time
165         }
166
167         // TODO: maybe use these values instead of 10-bit?
168         byte_fast = fast_10bit >> 2;
169         byte_slow = slow_10bit >> 2;
170
171         if (byte_slow > byte_fast) {
172                 drop = byte_slow - byte_fast;
173                 if (drop > ambient_drop)
174                         ambient_drop = drop;
175         }
176
177         if (ambient_min > byte_slow)
178                 ambient_min = byte_slow;
179
180         if (ambient_max < byte_slow)
181                 ambient_max = byte_slow;
182
183         if (new_zone != ambient_zone) {
184                 ambient_zone = new_zone;
185 #if 0
186                 log_byte(0xab);
187                 log_byte(ambient_zone);
188                 log_word(adcval);
189                 log_flush();
190 #endif
191                 ambient_zone_changed();
192         }
193 }
194