~gabe/flashlight-firmware/anduril2

« back to all changes in this revision

Viewing changes to _the_/TheStar/TheStar_v093.c

  • Committer: Selene Scriven
  • Date: 2015-03-17 08:56:50 UTC
  • mto: This revision was merged to the branch mainline in revision 124.
  • Revision ID: ubuntu@toykeeper.net-20150317085650-s89wr9h28n2co7z1
Added TheStar firmwares from _the_

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *
 
3
 * TheStar NANJG105C Firmware by _the_ (originally based on STAR 1.1 by JonnyC)
 
4
 *
 
5
 * Main differences:
 
6
 * - Heavily optimized for size -> free up enough size for all the options
 
7
 * - Star logic changed to match BLF preferences
 
8
 * - Hidden modes (Strobe, 1s beacon, 10s alpine distress beacon, turbo that stays on..)
 
9
 * - Combination of normal (for basic modes) and short cycle memory (for hidden modes)
 
10
 * - High-to-low support rewritten (for size and supporting hidden modes)
 
11
 * - Turbo step down to predefined PWM level (allows leaving the high away for simpler UI)
 
12
 * - Ramp down smoothly during turbo step down to make it less annoying / noticeable
 
13
 * - Voltage monitoring rewritten to consume less power and support blinky modes
 
14
 *
 
15
 * NANJG 105C Diagram
 
16
 *           ---
 
17
 *         -|   |- VCC
 
18
 *  Star 4 -|   |- Voltage ADC
 
19
 *  Star 3 -|   |- PWM
 
20
 *     GND -|   |- Star 2
 
21
 *           ---
 
22
 
 
23
 * CPU speed is 4.8Mhz without the 8x divider when low fuse is 0x75
 
24
 *
 
25
 * define F_CPU 4800000  CPU: 4.8MHz  PWM: 9.4kHz       ####### use low fuse: 0x75  #######
 
26
 *                             /8     PWM: 1.176kHz     ####### use low fuse: 0x65  #######
 
27
 * define F_CPU 9600000  CPU: 9.6MHz  PWM: 19kHz        ####### use low fuse: 0x7a  #######
 
28
 *                             /8     PWM: 2.4kHz       ####### use low fuse: 0x6a  #######
 
29
 * 
 
30
 * Above PWM speeds are for phase-correct PWM.  This program uses Fast-PWM, which when the CPU is 4.8MHz will be 18.75 kHz
 
31
 *
 
32
 * FUSES
 
33
 *              I use these fuse settings
 
34
 *              Low:  0x75
 
35
 *              High: 0xff
 
36
 *
 
37
 * STARS
 
38
 *              Star 2 = Tactical mode (Single mode if Star 4 connected)
 
39
 *              Star 3 = H-L if connected, L-H if not
 
40
 *              Star 4 = Memory if NOT connected
 
41
 *
 
42
 * VOLTAGE
 
43
 *              Resistor values for voltage divider (reference BLF-VLD README for more info)
 
44
 *              Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
 
45
 *
 
46
 *           VCC
 
47
 *            |
 
48
 *           Vd (~.25 v drop from protection diode)
 
49
 *            |
 
50
 *          1912 (R1 19,100 ohms)
 
51
 *            |
 
52
 *            |---- PB2 from MCU
 
53
 *            |
 
54
 *          4701 (R2 4,700 ohms)
 
55
 *            |
 
56
 *           GND
 
57
 *
 
58
 *              ADC = ((V_bat - V_diode) * R2   * 255) / ((R1    + R2  ) * V_ref)
 
59
 *              125 = ((3.0   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
 
60
 *              121 = ((2.9   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
 
61
 *
 
62
 *              To find out what value to use, plug in the target voltage (V) to this equation
 
63
 *                      value = (V * 4700 * 255) / (23800 * 1.1)
 
64
 *      
 
65
 */
 
66
#define F_CPU 4800000UL
 
67
 
 
68
 
 
69
/*
 
70
 * =========================================================================
 
71
 * Settings to modify per driver - consumed bytes are only for reference, they may change radically because of some other settings (and global optimization)
 
72
 */
 
73
 
 
74
// Three basic modes by default, define this for four basic modes (lowlow included) - consumes about 24 bytes more
 
75
//#define FOUR_MODES
 
76
 
 
77
// Things to do with star 2 - select one of the following
 
78
// Turbo timer is enabled by default, it can be turned off by soldering star 2 if this is enabled - consumes 22 bytes
 
79
// #define ENABLE_TURNING_OFF_TURBO_TIMER
 
80
// 
 
81
// Light can be changed to tactical mode (strobe, turbo, low) + hidden goodies with star 2 - consumes 70 bytes
 
82
#define ENABLE_TACTICAL_MODE
 
83
//#define TACTICAL_MODE_ON_BY_DEFAULT
 
84
//
 
85
// Light can be changed to single mode + hidden goodies with star 2 - consumes 18 bytes
 
86
//#define ENABLE_SINGLE_MODE
 
87
 
 
88
// High-to-low mode order, selectable by star 3, consumes 72 bytes
 
89
#define ENABLE_HIGH_TO_LOW
 
90
//#define HIGH_TO_LOW_ON_BY_DEFAULT
 
91
 
 
92
// Memory is on by default, it can be turned off by soldering star 4 if this is enabled - consumes 22 bytes
 
93
#define ENABLE_TURNING_OFF_MEMORY
 
94
#define MEMORY_ON_BY_DEFAULT
 
95
 
 
96
// Normal strobe by default, define this for alternating - consumes 20 bytes
 
97
//#define NORMAL_STROBE
 
98
//#define ALTERNATING_STROBE // +18 bytes
 
99
#define RANDOM_STROBE // +40 bytes
 
100
 
 
101
// SOS mode - consumes 70 bytes
 
102
#define ENABLE_SOS
 
103
 
 
104
// Set of different beacons - consumes 76 to 84 bytes (4- / 3-modes)
 
105
#define ENABLE_BEACONS
 
106
 
 
107
// Alpine distress beacon (6 x 10s beacon + 60s delay) - consumes 38 bytes
 
108
#define ENABLE_ALPINE_DISTRESS_BEACON
 
109
 
 
110
// Voltage monitoring - consumes 180 bytes
 
111
#define ENABLE_VOLTAGE_MONITORING
 
112
 
 
113
// Some standard PWM values
 
114
#define PWM_OFF                                 0                               // Used for signaling
 
115
#define PWM_MIN                                 7                               // Less than this won't light up reliably :(
 
116
#define PWM_MAX                                 255                             // Maximum brightness
 
117
 
 
118
// PWM values for modes
 
119
#define MODE_LOWLOW                                     7                       // 2mA@2.8A 3mA@4.2A
 
120
//#define MODE_LOW                                      11                      // 28mA@2.8A 38mA@4.2A
 
121
#define MODE_LOW                                        16                      // 67mA@2.8A 100mA@4.2A
 
122
#define MODE_MED                                        70                      // 0.66A@2.8A 1.0A@4.2A
 
123
#define MODE_HIGH                                       120                     // 1.2A@2.8A 1.8A@4.2A
 
124
#define MODE_TURBO                                      253
 
125
#define MODE_TURBO_STAY_ON                      PWM_MAX
 
126
 
 
127
// Low light during the pauses in some beacons
 
128
#define PWM_BEACON_BACKGROUND           MODE_MED        // ~0.66A (@2.8A)
 
129
#define PWM_SLOW_BEACON_BACKGROUND      MODE_LOW        // ~0.3A (@2.8A)
 
130
 
 
131
// Turbo timeout - How many WTD ticks before before ramping down (0.5s each)
 
132
//#define TURBO_TIMEOUT                 60                      // 30s for hot rods
 
133
#define TURBO_TIMEOUT                   180                     // 90s for normal usage
 
134
//#define TURBO_TIMEOUT                 120                     // 60s for normal usage (@4.2A)
 
135
 
 
136
// Turbo stepdown PWM value (select one suitable for your light)
 
137
//#define PWM_TURBO_STEPDOWN            140                     // ~1.4A (@2.8A)
 
138
//#define PWM_TURBO_STEPDOWN            200                     // ~2.1A (@2.8A)
 
139
#define PWM_TURBO_STEPDOWN              140                     // ~2.1A (@4.2A)
 
140
//#define PWM_TURBO_STEPDOWN            100                     // ~1.4A (@4.2A)
 
141
 
 
142
// Special mode PWM values - Must be under PWM_MIN
 
143
#define MODE_STROBE                                                     1
 
144
#define MODE_BEACON                                                     2
 
145
#define MODE_ALPINE_DISTRESS_BEACON                     3
 
146
#define MODE_SOS                                                        4
 
147
#define MODE_BEACON_WITH_BACKGROUND                     5
 
148
#define MODE_SLOW_BEACON_WITH_BACKGROUND        6
 
149
#define MODE_MOTION_STOPPING_STROBE         254
 
150
 
 
151
// Basic modes
 
152
#ifdef FOUR_MODES
 
153
#define N_BASIC_MODES 4
 
154
#define BASIC_MODES MODE_LOWLOW, MODE_LOW, MODE_MED, MODE_TURBO
 
155
#else
 
156
#define N_BASIC_MODES 3
 
157
#define BASIC_MODES MODE_LOW, MODE_MED, MODE_TURBO
 
158
#endif
 
159
 
 
160
// Hidden modes
 
161
#ifdef FOUR_MODES
 
162
#  define N_HIDDEN_MODES 7
 
163
#  define HIDDEN_MODES MODE_STROBE, MODE_BEACON_WITH_BACKGROUND, MODE_SLOW_BEACON_WITH_BACKGROUND, MODE_BEACON, MODE_ALPINE_DISTRESS_BEACON, MODE_SOS, MODE_TURBO_STAY_ON
 
164
#else
 
165
#  define N_HIDDEN_MODES 9
 
166
#  define HIDDEN_MODES PWM_MIN, MODE_STROBE, MODE_MOTION_STOPPING_STROBE, MODE_BEACON_WITH_BACKGROUND, MODE_SLOW_BEACON_WITH_BACKGROUND, MODE_BEACON, MODE_ALPINE_DISTRESS_BEACON, MODE_SOS, MODE_TURBO_STAY_ON
 
167
#endif
 
168
 
 
169
// When to enter hidden modes?
 
170
#ifdef FOUR_MODES
 
171
#  define HIDDEN_MODE_THRESHOLD 0x80
 
172
#else
 
173
#  define HIDDEN_MODE_THRESHOLD 0x60
 
174
#endif
 
175
 
 
176
// Ramping down from turbo consumes additional 18 bytes (currently used only if MODE_TURBO is on)
 
177
#define RAMP_DOWN
 
178
 
 
179
#define WDT_TIMEOUT                     2       // Number of WTD ticks before mode is saved (.5 sec each)
 
180
 
 
181
#define ADC_LOW                         125     // When do we warn the user and start ramping down
 
182
#define ADC_CRIT                        120 // When do we shut the light off
 
183
 
 
184
 
 
185
/*
 
186
 * =========================================================================
 
187
 */
 
188
 
 
189
//#include <avr/pgmspace.h>
 
190
#include <avr/io.h>
 
191
#include <util/delay_basic.h>
 
192
// Having own _delay_ms() saves some bytes
 
193
static void _delay_ms(uint16_t n)
 
194
{       
 
195
        while(n-- > 0)
 
196
        {
 
197
                _delay_loop_2(992);
 
198
        }
 
199
}
 
200
 
 
201
 
 
202
#include <avr/interrupt.h>
 
203
#include <avr/wdt.h>    
 
204
#include <avr/eeprom.h>
 
205
#include <avr/sleep.h>
 
206
//#include <avr/power.h>
 
207
 
 
208
#define STAR2_PIN   PB0
 
209
#define STAR3_PIN   PB4
 
210
#define STAR4_PIN   PB3
 
211
#define PWM_PIN     PB1
 
212
#define VOLTAGE_PIN PB2
 
213
#define ADC_CHANNEL 0x01        // MUX 01 corresponds with PB2
 
214
#define ADC_DIDR        ADC1D   // Digital input disable bit corresponding with PB2
 
215
#define ADC_PRSCL   0x06        // clk/64
 
216
 
 
217
#define PWM_LVL         OCR0B   // OCR0B is the output compare register for PB1
 
218
 
 
219
/*
 
220
 * global variables
 
221
 */
 
222
 
 
223
// Mode storage
 
224
uint8_t eepos = 0;
 
225
uint8_t eep[32];
 
226
 
 
227
#ifdef ENABLE_TURNING_OFF_MEMORY
 
228
uint8_t memory_enabled = 0;
 
229
#endif
 
230
 
 
231
#ifdef ENABLE_TURNING_OFF_TURBO_TIMER
 
232
uint8_t turbo_timer_enabled =  0;
 
233
#endif
 
234
 
 
235
// Modes (not const, as this gets adjusted when the light starts up based on stars)
 
236
static uint8_t modes[] = { BASIC_MODES, HIDDEN_MODES };
 
237
 
 
238
uint8_t mode_idx = 0;
 
239
uint8_t selected_pwm = 0;
 
240
volatile uint8_t adjusted_pwm = 0; // Volatile, because can be altered in WDT
 
241
 
 
242
uint8_t wdt_timeout = WDT_TIMEOUT;
 
243
 
 
244
void store_mode_idx(uint8_t lvl)
 
245
{  
 
246
        // Central method for writing (with wear leveling)
 
247
        uint8_t oldpos=eepos;
 
248
        eepos=(eepos+1)&31;  //wear leveling, use next cell
 
249
 
 
250
        // Write the current mode
 
251
        EEARL=eepos;  EEDR=lvl; EECR=32+4; EECR=32+4+2;  //WRITE  //32:write only (no erase)  4:enable  2:go
 
252
 
 
253
        while(EECR & 2) //wait for completion
 
254
                ; // Empty loop, do nothing, just wait..
 
255
 
 
256
        // Erase the last mode
 
257
        EEARL=oldpos;           EECR=16+4; EECR=16+4+2;  //ERASE  //16:erase only (no write)  4:enable  2:go
 
258
}
 
259
 
 
260
inline uint8_t read_stored_idx()
 
261
{
 
262
        uint8_t *peep = eep;
 
263
        eeprom_read_block(eep, 0, 32);
 
264
 
 
265
        while((*peep == 0xff) && (eepos < 32)) 
 
266
                eepos++, peep++;
 
267
 
 
268
        if(eepos < 32) 
 
269
                return *peep;
 
270
        else 
 
271
                eepos=0;
 
272
 
 
273
        return 0;
 
274
}
 
275
 
 
276
inline void next_mode(uint8_t short_clicks) 
 
277
{
 
278
        if(++mode_idx >= N_BASIC_MODES) 
 
279
        {
 
280
                if((wdt_timeout != 1 && short_clicks < HIDDEN_MODE_THRESHOLD) ||        // too little short clicks -> wrap around, else go to hidden
 
281
                   mode_idx >= (N_BASIC_MODES + N_HIDDEN_MODES))                                        // or all hidden modes used -> wrap around
 
282
                        mode_idx = 0; // Wrap around
 
283
        }
 
284
}
 
285
 
 
286
inline void WDT_on() 
 
287
{
 
288
        // Setup watchdog timer to only interrupt, not reset, every 500ms.
 
289
        cli();                                                  // Disable interrupts
 
290
        wdt_reset();                                    // Reset the WDT
 
291
        WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
292
        WDTCR = (1<<WDTIE) | (1<<WDP2) | (1<<WDP0); // Enable interrupt every 500ms
 
293
        sei();                                                  // Enable interrupts
 
294
}
 
295
 
 
296
inline void WDT_off()
 
297
{
 
298
        cli();                                                  // Disable interrupts
 
299
        wdt_reset();                                    // Reset the WDT
 
300
        MCUSR &= ~(1<<WDRF);                    // Clear Watchdog reset flag
 
301
        WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
302
        WDTCR = 0x00;                                   // Disable WDT
 
303
        sei();                                                  // Enable interrupts
 
304
}
 
305
 
 
306
inline void ADC_on() 
 
307
{
 
308
        ADMUX  = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
 
309
    DIDR0 |= (1 << ADC_DIDR);                                                   // disable digital input on ADC pin to reduce power consumption
 
310
        ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
 
311
}
 
312
 
 
313
inline void ADC_off() 
 
314
{
 
315
        ADCSRA &= ~(1<<7); //ADC off
 
316
}
 
317
 
 
318
#ifdef ENABLE_VOLTAGE_MONITORING
 
319
uint8_t low_voltage(uint8_t voltage_val) 
 
320
{
 
321
    static uint8_t lowbatt_cnt = 0;
 
322
 
 
323
        // Start conversion
 
324
        ADCSRA |= (1 << ADSC);
 
325
 
 
326
        // Wait for completion
 
327
        while(ADCSRA & (1 << ADSC))
 
328
                ; // Empty loop, do nothing, just wait..
 
329
 
 
330
        // See if voltage is lower than what we were looking for        
 
331
        if(ADCH < voltage_val) 
 
332
        {
 
333
                // See if it's been low for a while
 
334
                if(++lowbatt_cnt > 8) 
 
335
                {
 
336
                        lowbatt_cnt = 0;
 
337
                        return 1;
 
338
                }
 
339
        } 
 
340
        else 
 
341
        {
 
342
                lowbatt_cnt = 0;
 
343
        }
 
344
        return 0;
 
345
}
 
346
#endif
 
347
 
 
348
 
 
349
ISR(WDT_vect) 
 
350
{
 
351
        static uint8_t ticks = 0;
 
352
 
 
353
        if(ticks < 255) 
 
354
                ticks++;
 
355
        
 
356
        if(ticks == wdt_timeout) 
 
357
        {
 
358
#ifdef ENABLE_TURNING_OFF_MEMORY
 
359
                if(memory_enabled) 
 
360
                {
 
361
#endif
 
362
                        store_mode_idx(mode_idx);
 
363
#ifdef ENABLE_TURNING_OFF_MEMORY
 
364
                } 
 
365
                else 
 
366
                {
 
367
                        // Reset the mode to the start for next time
 
368
                        store_mode_idx(0);
 
369
                }
 
370
#endif
 
371
        } 
 
372
#ifdef MODE_TURBO
 
373
#ifdef ENABLE_TURNING_OFF_TURBO_TIMER
 
374
        else if(turbo_timer_enabled && ticks == TURBO_TIMEOUT && selected_pwm == MODE_TURBO) // Turbo mode is *not* always at end
 
375
#else
 
376
        else if(ticks == TURBO_TIMEOUT && selected_pwm == MODE_TURBO)
 
377
#endif
 
378
        {
 
379
#ifdef RAMP_DOWN
 
380
                while(adjusted_pwm > PWM_TURBO_STEPDOWN) // might be already less if low voltage stepdown was performed before this!
 
381
                {
 
382
                        PWM_LVL = --adjusted_pwm;
 
383
                        _delay_ms(25);
 
384
                }
 
385
#else
 
386
                if(adjusted_pwm > PWM_TURBO_STEPDOWN) // might be already less if low voltage stepdown was performed before this!
 
387
                        adjusted_pwm = PWM_TURBO_STEPDOWN;
 
388
#endif // RAMP_DOWN
 
389
        }
 
390
#endif // MODE_TURBO
 
391
 
 
392
#ifdef ENABLE_VOLTAGE_MONITORING
 
393
        // Block for voltage monitoring
 
394
        {
 
395
                static uint8_t adjusted_to_min = 0;
 
396
 
 
397
                if(adjusted_to_min) // Already in lowest mode, check only against critically low voltage
 
398
                {
 
399
                        if(low_voltage(ADC_CRIT))
 
400
                                adjusted_pwm = PWM_OFF; // Signal main loop to turn off the light
 
401
                }
 
402
                else if(low_voltage(ADC_LOW)) 
 
403
                {
 
404
                        // We need to go to a lower PWM - blink to notify the user
 
405
                        // One blink is enough to save program space
 
406
                        PWM_LVL = 0;
 
407
                        _delay_ms(250);
 
408
                        //PWM_LVL = adjusted_pwm; // Turned back on in the main loop, unnecessary to waste program space here
 
409
 
 
410
                        adjusted_pwm /= 2;
 
411
 
 
412
                        if(adjusted_pwm < PWM_MIN) // Can't go any lower -> Change to lowest possible PWM level and stay there until critical voltage
 
413
                        {
 
414
                                adjusted_pwm = PWM_MIN;
 
415
                                adjusted_to_min = 1;                            
 
416
                        }                       
 
417
                }
 
418
        }       
 
419
#endif
 
420
}
 
421
 
 
422
#ifdef ENABLE_HIGH_TO_LOW
 
423
void revert_modes(void) // Revert modes in place
 
424
{
 
425
        uint8_t spare, *plow = modes, *phigh = modes+N_BASIC_MODES-1;
 
426
        
 
427
        while(plow < phigh)
 
428
        {
 
429
                spare = *plow;
 
430
                *plow = *phigh;
 
431
                *phigh = spare;
 
432
                plow++, phigh--;
 
433
        }
 
434
}
 
435
#endif
 
436
 
 
437
int main(void)
 
438
{       
 
439
        // All ports default to input, but turn pull-up resistors on for the stars (not the ADC input!  Made that mistake already)
 
440
        PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN) | (1 << STAR4_PIN);
 
441
        
 
442
    // Set PWM pin to output
 
443
    DDRB = (1 << PWM_PIN);
 
444
 
 
445
    // Set timer to do PWM for correct output pin and set prescaler timing
 
446
    TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
 
447
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
448
        
 
449
        // Turn features on or off as needed
 
450
        #ifdef ENABLE_VOLTAGE_MONITORING
 
451
        ADC_on();
 
452
        #else
 
453
        ADC_off();
 
454
        #endif
 
455
        ACSR   |=  (1<<7); //AC off
 
456
        
 
457
#ifdef ENABLE_TURNING_OFF_MEMORY
 
458
        // Soldering Star 4 disables memory
 
459
#ifdef MEMORY_ON_BY_DEFAULT
 
460
        memory_enabled = ((PINB & (1 << STAR4_PIN)) > 0) ? 1 : 0;
 
461
#else
 
462
        memory_enabled = ((PINB & (1 << STAR4_PIN)) == 0) ? 1 : 0;
 
463
#endif
 
464
#endif
 
465
 
 
466
#ifdef ENABLE_TURNING_OFF_TURBO_TIMER
 
467
        turbo_timer_enabled = ((PINB & (1 << STAR2_PIN)) > 0) ? 1 : 0;
 
468
#endif
 
469
 
 
470
#ifdef ENABLE_SINGLE_MODE
 
471
        if((PINB & (1 << STAR2_PIN)) == 0)
 
472
        {
 
473
                        modes[0] = modes[1] = 
 
474
#ifdef FOUR_MODES
 
475
                        modes[2] = 
 
476
#endif
 
477
                        MODE_TURBO;
 
478
 
 
479
                        // Last basic mode is already MODE_TURBO - no need to waste bytes changing that
 
480
        }               
 
481
#endif
 
482
 
 
483
#ifdef ENABLE_TACTICAL_MODE
 
484
#ifdef TACTICAL_MODE_ON_BY_DEFAULT
 
485
        if((PINB & (1 << STAR2_PIN)) > 0)
 
486
#else
 
487
        if((PINB & (1 << STAR2_PIN)) == 0)
 
488
#endif
 
489
        {
 
490
#ifdef ENABLE_TURNING_OFF_MEMORY
 
491
                if(memory_enabled) // Single mode
 
492
                {
 
493
                        modes[0] = modes[1] = 
 
494
#ifdef FOUR_MODES
 
495
                        modes[2] = 
 
496
#endif
 
497
                        MODE_TURBO;
 
498
 
 
499
                        // Last basic mode is already MODE_TURBO - no need to waste bytes changing that
 
500
                }
 
501
                else
 
502
#endif
 
503
                {
 
504
                        modes[0] = MODE_STROBE;
 
505
                        modes[1] = MODE_TURBO;
 
506
                        modes[2] = MODE_LOW;
 
507
#ifdef FOUR_MODES
 
508
                        modes[3] = MODE_LOWLOW;
 
509
#endif
 
510
                        wdt_timeout = 1;
 
511
                }
 
512
        }
 
513
#endif
 
514
 
 
515
 
 
516
#ifdef ENABLE_HIGH_TO_LOW
 
517
#ifdef HIGH_TO_LOW_ON_BY_DEFAULT
 
518
        if((PINB & (1 << STAR3_PIN)) > 0)
 
519
#else
 
520
        if((PINB & (1 << STAR3_PIN)) == 0)
 
521
#endif
 
522
                revert_modes();
 
523
#endif
 
524
        
 
525
        // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
 
526
        // Will allow us to go idle between WDT interrupts
 
527
        set_sleep_mode(SLEEP_MODE_IDLE);
 
528
        
 
529
        // Mode memory handling block
 
530
        {
 
531
                uint8_t short_clicks = 0;
 
532
 
 
533
                // Determine what mode we should fire up
 
534
                // Read the last mode that was saved
 
535
                mode_idx = read_stored_idx();
 
536
 
 
537
                // Handle short press indicator
 
538
                short_clicks = (mode_idx & 0xf0);
 
539
                mode_idx &= 0x0f;
 
540
                
 
541
                if(short_clicks) // One or more short clicks
 
542
                {
 
543
                        // Indicates we did a short press last time, go to the next mode
 
544
                        next_mode(short_clicks); // Will handle wrap arounds
 
545
                } 
 
546
                // else: Didn't have a short press, keep the same mode, nothing to do
 
547
        
 
548
                // Store mode with short press indicator
 
549
                store_mode_idx(mode_idx | ((short_clicks < HIDDEN_MODE_THRESHOLD) ? short_clicks+0x10 : short_clicks));
 
550
        }               
 
551
        
 
552
        // Start watchdog timer (used for storing the mode after delay, turbo timer, and voltage monitoring)
 
553
        WDT_on();
 
554
        
 
555
        // Now just fire up the mode
 
556
        selected_pwm = modes[mode_idx]; // Note: Actual PWM can be less than selected PWM (e.g. in case of low voltage)
 
557
        
 
558
        if(selected_pwm < PWM_MIN) // Hidden blinky modes
 
559
                adjusted_pwm = PWM_MAX; // All blinky modes initially with full power
 
560
        else
 
561
                adjusted_pwm = selected_pwm;
 
562
 
 
563
        // block for main loop
 
564
        {
 
565
                uint8_t ii = 0; // Loop counter, used by multiple branches
 
566
#ifdef ENABLE_BEACONS
 
567
                uint8_t beacon_background = PWM_OFF;
 
568
#endif
 
569
        
 
570
                while(1)
 
571
                {
 
572
#ifdef ENABLE_VOLTAGE_MONITORING
 
573
                        if(adjusted_pwm == PWM_OFF) // Voltage monitoring signaled us to turn off the light -> break out of the main loop
 
574
                                break;
 
575
#endif
 
576
 
 
577
                        PWM_LVL = adjusted_pwm; // must be set inside loop, is volatile & might have changed because of voltage monitoring
 
578
 
 
579
                        switch(selected_pwm)
 
580
                        {
 
581
                        case MODE_STROBE: // Disorienting alternating strobe
 
582
#ifdef NORMAL_STROBE
 
583
                                _delay_ms(20);
 
584
                                PWM_LVL = 0;
 
585
                                _delay_ms(40);
 
586
#endif
 
587
#ifdef ALTERNATING_STROBE
 
588
                                _delay_ms(31);
 
589
                                PWM_LVL = 0;
 
590
                                if(ii < 28) // 51ms = ~13Hz, ~60% DC
 
591
                                        _delay_ms(20);
 
592
                                else if(ii < 48) // 77ms = ~19.5Hz, ~40% DC
 
593
                                        _delay_ms(46);
 
594
                                else
 
595
                                        ii = 255;
 
596
#endif
 
597
#ifdef RANDOM_STROBE 
 
598
                                {   // 77ms = 13Hz, 51ms = 19.5Hz / 40-60% DC
 
599
                                        ii = (5 * ii) + 128;
 
600
                                        _delay_ms(31);
 
601
                                        PWM_LVL = 0;
 
602
                                        _delay_ms(ii > 127 ? 46 : 20);
 
603
                                }                                       
 
604
#endif
 
605
                                break;
 
606
                        case MODE_MOTION_STOPPING_STROBE: // 10Hz, 2% DC
 
607
                                _delay_ms(2);
 
608
                                PWM_LVL = 0;
 
609
                                _delay_ms(98);
 
610
                                break;
 
611
#ifdef ENABLE_SOS
 
612
                        case MODE_SOS:
 
613
                                if(ii / 3 == 1)
 
614
                                        _delay_ms(600);  // Dash for 'O' (3xDot)
 
615
                                else
 
616
                                        _delay_ms(200);  // Dot for 'S'
 
617
 
 
618
                                PWM_LVL = 0;
 
619
 
 
620
                                switch(ii)
 
621
                                {
 
622
                                default:
 
623
                                        _delay_ms(200);  // Pause inside a letter (1xDot)
 
624
                                        break;
 
625
                                case 2:
 
626
                                case 5:
 
627
                                        _delay_ms(600); // Pause between letters (3xDot)
 
628
                                        break;
 
629
                                case 8:
 
630
                                        _delay_ms(2500); // Pause between "words" (should be 7xDot, but I like it longer)
 
631
                                        ii = 255;
 
632
                                        break;
 
633
                                }
 
634
                                break;
 
635
#endif
 
636
#ifdef ENABLE_BEACONS
 
637
                        case MODE_BEACON_WITH_BACKGROUND:
 
638
                        case MODE_SLOW_BEACON_WITH_BACKGROUND:
 
639
#ifdef FOUR_MODES
 
640
                                beacon_background = PWM_SLOW_BEACON_BACKGROUND;
 
641
#else
 
642
                                switch(selected_pwm)
 
643
                                {
 
644
                                case MODE_BEACON_WITH_BACKGROUND:
 
645
                                        beacon_background = PWM_BEACON_BACKGROUND;
 
646
                                        break;
 
647
                                case MODE_SLOW_BEACON_WITH_BACKGROUND:
 
648
                                        beacon_background = PWM_SLOW_BEACON_BACKGROUND;
 
649
                                        break;
 
650
                                }
 
651
#endif
 
652
                                // no break - fall through to beacon code
 
653
                        case MODE_BEACON:
 
654
                        case MODE_ALPINE_DISTRESS_BEACON:
 
655
                                _delay_ms(50);
 
656
                                PWM_LVL = beacon_background;
 
657
 
 
658
                                _delay_ms(950);
 
659
                                if(selected_pwm == MODE_ALPINE_DISTRESS_BEACON)
 
660
                                {
 
661
                                        if(ii > 5)
 
662
                                        {
 
663
                                                _delay_ms(59000);
 
664
                                                ii = 255;
 
665
                                        }
 
666
                                        else
 
667
                                                _delay_ms(9000);
 
668
                                }
 
669
                                else if(selected_pwm == MODE_SLOW_BEACON_WITH_BACKGROUND)
 
670
                                        _delay_ms(1500);
 
671
#endif
 
672
                                break;
 
673
                        default:
 
674
                                sleep_mode();
 
675
                                break;
 
676
                        }
 
677
 
 
678
                        ii++; // Loop counter, used by multiple branches
 
679
                }
 
680
        }
 
681
 
 
682
#ifdef ENABLE_VOLTAGE_MONITORING
 
683
        //
 
684
        // Critically low voltage -> Turn off the light
 
685
        // 
 
686
                
 
687
        // Disable WDT so it doesn't wake us up from sleep
 
688
        WDT_off();
 
689
 
 
690
        // Would be nice to blink a couple of times with lowest brightness to notify the user, but not implemented due space restrictions
 
691
 
 
692
        // Turn the light off
 
693
        PWM_LVL = 0;
 
694
 
 
695
        // Disable ADC so it doesn't consume power
 
696
        ADC_off();
 
697
 
 
698
        // Power down as many components as possible
 
699
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
700
 
 
701
        // Deep sleep until powered off - consumes ~110uA during the deep sleep
 
702
        while(1)
 
703
                sleep_mode();
 
704
#endif
 
705
 
 
706
    return 0; // Standard Return Code -> would return to idle loop with interrupts disabled.
 
707
}