]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/ambient.c
ambient.c: logging modification
[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;
11 static unsigned char ambient_min, ambient_max, ambient_drop;
12
13 /* logging */
14 #define AMBIENT_LOG_SIZE 128
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         { 0x0000, 0x0250 }, // dark
30         { 0x0240, 0x02e0 }, // evening
31         { 0x02c0, 0x0300 }, // dawn
32         { 0x02f8, 0xffff }, // day
33 };
34
35 void init_ambient()
36 {
37         ambient_slow = 0;
38         ambient_fast = 0;
39         ambient_max = 0;
40         ambient_min = 0xFF;
41         ambient_zone = 1;
42         ambient_drop = 0;
43
44         ambient_log_offset = eeprom_read_byte(&ambient_log_offset_stored);
45
46         if (ambient_log_offset == AMBIENT_LOG_SIZE)
47                 ambient_log_offset = 0; // start over
48 }
49
50 void susp_ambient()
51 {
52         unsigned char stored_offset;
53
54         ambient_log_min_max();
55
56         stored_offset = eeprom_read_byte(&ambient_log_offset_stored);
57         if (stored_offset != ambient_log_offset)
58                 eeprom_write_byte(&ambient_log_offset_stored,
59                         ambient_log_offset);
60 }
61
62 void ambient_log_min_max()
63 {
64         if (ambient_log_offset >= AMBIENT_LOG_SIZE - 1)
65                 return;
66
67         ambient_max -= ambient_min;
68         // ambient_max >>= 2;
69         if (ambient_max > 15)
70                 ambient_max = 15;
71
72         ambient_drop >>= 2;
73         if (ambient_drop > 15)
74                 ambient_drop = 15;
75
76         eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_min);
77         eeprom_write_byte(&ambient_log[ambient_log_offset++],
78                 (ambient_max << 4) | ambient_drop);
79
80         ambient_min = 0xFF;
81         ambient_max = 0;
82         ambient_drop = 0;
83 }
84
85 static inline void ambient_zone_changed()
86 {
87         pwmled_select_brightness();
88         pattern_reload();
89 }
90
91 static unsigned char val_to_zone(uint16_t ambient_val)
92 {
93         unsigned char new_zone = ambient_zone;
94
95         while (ambient_zones[new_zone].lo > ambient_val)
96                 new_zone--;
97
98         while (ambient_zones[new_zone].hi < ambient_val)
99                 new_zone++;
100
101         return new_zone;
102 }
103
104 void ambient_adc(uint16_t adcval)
105 {
106         unsigned char new_zone, user_zone;
107         unsigned char byte_fast, byte_slow, drop;
108         uint16_t a_10bit;
109
110         // running avg - shorter timespan
111         ambient_fast += adcval - (ambient_fast >> AMBIENT_FAST_SHIFT);
112
113         // running avg - longer timespan
114         a_10bit = ambient_fast >> (AMBIENT_FAST_SHIFT + AMBIENT_ADC_SHIFT);
115         ambient_slow += a_10bit - (ambient_slow >> AMBIENT_SLOW_SHIFT);
116
117         // ambient zones are governed by shorter timespan by default
118         new_zone = val_to_zone(a_10bit);
119
120         if (new_zone > 1 && (
121                 new_zone == ambient_zone-1 || new_zone == ambient_zone+1)) {
122                 // but change to the neighbouring zone is governed by _slow,
123                 // except to the darkest zone, where we want fast reaction.
124                 new_zone = val_to_zone(ambient_slow >> AMBIENT_SLOW_SHIFT);
125         }
126
127         // user_param ambient zone override
128         if ((user_zone = get_user_param(0)) > 0)
129                 new_zone = user_zone - 1;
130
131         // TODO: maybe use these values instead of 10-bit?
132         byte_fast = a_10bit >> 2;
133         byte_slow = ambient_slow >> (AMBIENT_SLOW_SHIFT + 2);
134
135         if (byte_slow > byte_fast) {
136                 drop = byte_slow - byte_fast;
137                 if (drop > ambient_drop)
138                         ambient_drop = drop;
139         }
140
141         if (ambient_min > byte_slow)
142                 ambient_min = byte_slow;
143
144         if (ambient_max < byte_slow)
145                 ambient_max = byte_slow;
146
147         if (new_zone != ambient_zone) {
148                 ambient_zone = new_zone;
149 #if 0
150                 log_byte(0xab);
151                 log_byte(ambient_zone);
152                 log_word(adcval);
153                 log_flush();
154 #endif
155                 ambient_zone_changed();
156         }
157 }
158