]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/ambient.c
mudflap for dual rearlights
[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, 0x02e8 }, // evening
37         { 0x02d0, 0x0302 }, // dawn
38         { 0x02fc, 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         if (new_zone >= N_AMBIENT_ZONES)
106                 new_zone = N_AMBIENT_ZONES-1;
107
108         while (ambient_zones[new_zone].lo > ambient_val)
109                 new_zone--;
110
111         while (ambient_zones[new_zone].hi < ambient_val)
112                 new_zone++;
113
114         return new_zone;
115 }
116
117 void ambient_adc(uint16_t adcval)
118 {
119         unsigned char new_zone, user_zone, new_shadow = 0;
120         unsigned char byte_fast, byte_slow, drop;
121         uint16_t fast_10bit, slow_10bit;
122
123         // running avg - shorter timespan
124         ambient_fast += adcval - (ambient_fast >> AMBIENT_FAST_SHIFT);
125
126         // running avg - longer timespan
127         fast_10bit = ambient_fast >> (AMBIENT_FAST_SHIFT + AMBIENT_ADC_SHIFT);
128         ambient_slow += fast_10bit - (ambient_slow >> AMBIENT_SLOW_SHIFT);
129
130         // ambient zones are governed by shorter timespan by default
131         new_zone = val_to_zone(fast_10bit);
132
133         slow_10bit = ambient_slow >> AMBIENT_SLOW_SHIFT;
134
135         if ((new_zone > ambient_zone)
136                 || (new_zone > 1 && new_zone == ambient_zone - 1)) {
137                 // but change to the neighbouring zone is governed by _slow,
138                 // except to the darkest zone, where we want fast reaction.
139                 new_zone = val_to_zone(slow_10bit);
140         }
141
142         // user_param ambient zone override
143         if ((user_zone = get_user_param(0)) > 0)
144                 new_zone = user_zone - 1;
145
146 #if 0   // ignore shadow-entering code altogether for now
147         // are we entering the shadow?
148         if (!user_zone && new_zone < ambient_zone
149                 && ambient_zone >= 2 && slow_10bit > fast_10bit
150                 && slow_10bit - fast_10bit >= SHADOW_DROP_LIMIT) {
151                 // we are entering the shadow
152                 new_shadow = 0x30;
153         }
154
155         if (ambient_shadow) {
156                 if (new_shadow) {
157                         new_zone = ambient_zone; // don't change while entering shadow
158                         ambient_shadow = new_shadow; // update the timeout
159                 } else {
160                         ambient_shadow--;
161                         if (!ambient_shadow) { // leaving the shadow
162                                 ambient_zone_changed();
163                         }
164                 }
165         } else if (new_shadow) {
166                 ambient_shadow = new_shadow; // set up the timeout
167                 new_zone = ambient_zone; // don't change while entering shadow
168                 ambient_zone_changed();  // notify others the first time
169         }
170 #endif
171
172         // TODO: maybe use these values instead of 10-bit?
173         byte_fast = fast_10bit >> 2;
174         byte_slow = slow_10bit >> 2;
175
176         if (byte_slow > byte_fast) {
177                 drop = byte_slow - byte_fast;
178                 if (drop > ambient_drop)
179                         ambient_drop = drop;
180         }
181
182         if (ambient_min > byte_slow)
183                 ambient_min = byte_slow;
184
185         if (ambient_max < byte_slow)
186                 ambient_max = byte_slow;
187
188         if (new_zone != ambient_zone) {
189                 ambient_zone = new_zone;
190 #if 0
191                 log_byte(0xab);
192                 log_byte(ambient_zone);
193                 log_word(adcval);
194                 log_flush();
195 #endif
196                 ambient_zone_changed();
197         }
198 }
199