~toykeeper/flashlight-firmware/fsm

« back to all changes in this revision

Viewing changes to _the_/TheStar/TheStar_OffTime_v042.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 Off-time NANJG105C Firmware by _the_ (originally based on STAR 1.1 & STAR Off-time 1.3 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 = Memory if NOT connected
 
39
 *              Star 3 = H-L if connected, L-H if not
 
40
 *              Star 4 = Cap for off-time memory
 
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 in main mode cycle) - 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
// Memory is on by default, it can be turned off by soldering star 2 if any of the above star 2 functionality is not enabled
 
89
 
 
90
// High-to-low mode order, selectable by star 3, consumes 72 bytes
 
91
#define ENABLE_HIGH_TO_LOW
 
92
//#define HIGH_TO_LOW_ON_BY_DEFAULT
 
93
 
 
94
// Normal strobe by default, define this for alternating - consumes 20 bytes
 
95
//#define NORMAL_STROBE
 
96
//#define ALTERNATING_STROBE // +18 bytes
 
97
#define RANDOM_STROBE // +40 bytes
 
98
 
 
99
// SOS mode - consumes 70 bytes
 
100
#define ENABLE_SOS
 
101
 
 
102
// Set of different beacons - consumes 76 to 84 bytes (4- / 3-modes)
 
103
#define ENABLE_BEACONS
 
104
 
 
105
// Voltage monitoring - consumes 180 bytes
 
106
#define ENABLE_VOLTAGE_MONITORING
 
107
 
 
108
// Some standard PWM values
 
109
#define PWM_OFF                                 0                               // Used for signaling
 
110
#define PWM_MIN                                 7                               // Less than this won't light up reliably :(
 
111
#define PWM_MAX                                 255                             // Maximum brightness
 
112
 
 
113
// PWM values for modes
 
114
#define MODE_LOWLOW                                     PWM_MIN         // 2mA@2.8A 3mA@4.2A
 
115
//#define MODE_LOW                                      11                      // 28mA@2.8A 38mA@4.2A
 
116
#define MODE_LOW                                        16                      // 67mA@2.8A 100mA@4.2A
 
117
#define MODE_MED                                        70                      // 0.66A@2.8A 1.0A@4.2A
 
118
#define MODE_HIGH                                       120                     // 1.2A@2.8A 1.8A@4.2A
 
119
#define MODE_TURBO                                      252
 
120
#define MODE_TURBO_STAY_ON                      PWM_MAX
 
121
 
 
122
#define MODE_INDEX_TURBO_RAMPDOWN       0xf0
 
123
 
 
124
// Low light during the pauses in some beacons
 
125
#define PWM_BEACON_BACKGROUND           MODE_MED        // ~0.66A (@2.8A)
 
126
#define PWM_SLOW_BEACON_BACKGROUND      MODE_LOW        // ~0.3A (@2.8A)
 
127
 
 
128
// Turbo timeout - How many WTD ticks before before ramping down (0.5s each)
 
129
//#define TURBO_TIMEOUT                 60                      // 30s for hot rods
 
130
//#define TURBO_TIMEOUT                 180                     // 90s for normal usage
 
131
#define TURBO_TIMEOUT                   120                     // 60s for normal usage (@4.2A)
 
132
 
 
133
// Turbo stepdown PWM value (select one suitable for your light)
 
134
//#define PWM_TURBO_STEPDOWN            140                     // ~1.4A (@2.8A)
 
135
#define PWM_TURBO_STEPDOWN              200                     // ~2.1A (@2.8A)
 
136
//#define PWM_TURBO_STEPDOWN            140                     // ~2.1A (@4.2A)
 
137
//#define PWM_TURBO_STEPDOWN            100                     // ~1.4A (@4.2A)
 
138
 
 
139
// Special mode PWM values - Must be under PWM_MIN
 
140
#define MODE_STROBE                                                     1
 
141
#define MODE_BEACON                                                     2
 
142
#define MODE_ALPINE_DISTRESS_BEACON                     3
 
143
#define MODE_SOS                                                        4
 
144
#define MODE_BEACON_WITH_BACKGROUND                     5
 
145
#define MODE_SLOW_BEACON_WITH_BACKGROUND        6
 
146
#define MODE_MOTION_STOPPING_STROBE         254
 
147
//#define MODE_MOTION_STOPPING_STROBE_SLOW      253
 
148
 
 
149
// Basic modes
 
150
#ifdef FOUR_MODES
 
151
#define N_BASIC_MODES 4
 
152
#define BASIC_MODES MODE_LOWLOW, MODE_LOW, MODE_MED, MODE_TURBO
 
153
#else
 
154
#define N_BASIC_MODES 3
 
155
#define BASIC_MODES MODE_LOW, MODE_MED, MODE_TURBO
 
156
#endif
 
157
 
 
158
// Hidden modes
 
159
#ifdef FOUR_MODES
 
160
#  define N_HIDDEN_MODES 7
 
161
#  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
 
162
#else
 
163
#  define N_HIDDEN_MODES 9
 
164
#  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
 
165
#endif
 
166
 
 
167
// When to enter hidden modes?
 
168
#ifdef FOUR_MODES
 
169
#  define HIDDEN_MODE_THRESHOLD 0x70
 
170
#else
 
171
#  define HIDDEN_MODE_THRESHOLD 0x50
 
172
#endif
 
173
 
 
174
// Ramping down from turbo consumes additional 18 bytes (currently used only if MODE_TURBO is on)
 
175
#define RAMP_DOWN
 
176
 
 
177
#define WDT_TIMEOUT                     6       // Number of WTD ticks light needs to be turned on before resetting short click counter (.5 sec each)
 
178
 
 
179
#define ADC_LOW                         125     // When do we warn the user and start ramping down
 
180
#define ADC_CRIT                        120 // When do we shut the light off
 
181
 
 
182
#define CAP_THRESHOLD           130  // Value between 1 and 255 corresponding with cap voltage (0 - 1.1v) where we consider it a short press to move to the next mode
 
183
                                                                 // Not sure the lowest you can go before getting bad readings, but with a value of 70 and a 1uF cap, it seemed to switch sometimes
 
184
                                                                 // even when waiting 10 seconds between presses.
 
185
 
 
186
/*
 
187
 * =========================================================================
 
188
 */
 
189
 
 
190
//#include <avr/pgmspace.h>
 
191
#include <avr/io.h>
 
192
#include <util/delay_basic.h>
 
193
// Having own _delay_ms() saves some bytes
 
194
static void _delay_ms(uint16_t n)
 
195
{       
 
196
        while(n-- > 0)
 
197
        {
 
198
                _delay_loop_2(992);
 
199
        }
 
200
}
 
201
 
 
202
 
 
203
#include <avr/interrupt.h>
 
204
#include <avr/wdt.h>    
 
205
#include <avr/eeprom.h>
 
206
#include <avr/sleep.h>
 
207
//#include <avr/power.h>
 
208
 
 
209
#define STAR2_PIN   PB0
 
210
#define STAR3_PIN   PB4
 
211
 
 
212
#define CAP_PIN     PB3
 
213
#define CAP_CHANNEL 0x03        // MUX 03 corresponds with PB3 (Star 4)
 
214
#define CAP_DIDR    ADC3D       // Digital input disable bit corresponding with PB3
 
215
 
 
216
#define PWM_PIN     PB1
 
217
#define VOLTAGE_PIN PB2
 
218
 
 
219
#define ADC_CHANNEL 0x01        // MUX 01 corresponds with PB2
 
220
#define ADC_DIDR        ADC1D   // Digital input disable bit corresponding with PB2
 
221
#define ADC_PRSCL   0x06        // clk/64
 
222
 
 
223
#define PWM_LVL         OCR0B   // OCR0B is the output compare register for PB1
 
224
 
 
225
/*
 
226
 * global variables
 
227
 */
 
228
 
 
229
// Mode storage
 
230
uint8_t eepos = 0;
 
231
uint8_t eep[32];
 
232
 
 
233
#ifdef ENABLE_TURNING_OFF_TURBO_TIMER
 
234
uint8_t turbo_timer_enabled =  0;
 
235
#endif
 
236
 
 
237
// Modes (not const, as this gets adjusted when the light starts up based on stars)
 
238
static uint8_t modes[] = { BASIC_MODES, HIDDEN_MODES };
 
239
 
 
240
uint8_t mode_idx = 0;
 
241
uint8_t selected_pwm = 0;
 
242
volatile uint8_t adjusted_pwm = 0; // Volatile, because can be altered in WDT
 
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(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
                store_mode_idx(mode_idx); // Reset short click counter
 
359
        } 
 
360
#ifdef MODE_TURBO
 
361
#ifdef ENABLE_TURNING_OFF_TURBO_TIMER
 
362
        else if(turbo_timer_enabled && ticks == TURBO_TIMEOUT && selected_pwm == MODE_TURBO) // Turbo mode is *not* always at end
 
363
#else
 
364
        else if(ticks == TURBO_TIMEOUT && selected_pwm == MODE_TURBO)
 
365
#endif
 
366
        {
 
367
                store_mode_idx(mode_idx | MODE_INDEX_TURBO_RAMPDOWN); // Store the knowledge of ramp down, so that next mode can be turbo again - do it *before* ramping down
 
368
 
 
369
#ifdef RAMP_DOWN
 
370
                while(adjusted_pwm > PWM_TURBO_STEPDOWN) // might be already less if low voltage stepdown was performed before this!
 
371
                {
 
372
                        PWM_LVL = --adjusted_pwm;
 
373
                        _delay_ms(25);
 
374
                }
 
375
#else
 
376
                if(adjusted_pwm > PWM_TURBO_STEPDOWN) // might be already less if low voltage stepdown was performed before this!
 
377
                        adjusted_pwm = PWM_TURBO_STEPDOWN;
 
378
#endif // RAMP_DOWN
 
379
        }
 
380
#endif // MODE_TURBO
 
381
 
 
382
#ifdef ENABLE_VOLTAGE_MONITORING
 
383
        // Block for voltage monitoring
 
384
        {
 
385
                static uint8_t adjusted_to_min = 0;
 
386
 
 
387
                if(adjusted_to_min) // Already in lowest mode, check only against critically low voltage
 
388
                {
 
389
                        if(low_voltage(ADC_CRIT))
 
390
                                adjusted_pwm = PWM_OFF; // Signal main loop to turn off the light
 
391
                }
 
392
                else if(low_voltage(ADC_LOW)) 
 
393
                {
 
394
                        // We need to go to a lower PWM - blink to notify the user
 
395
                        // One blink is enough to save program space
 
396
                        PWM_LVL = 0;
 
397
                        _delay_ms(250);
 
398
                        //PWM_LVL = adjusted_pwm; // Turned back on in the main loop, unnecessary to waste program space here
 
399
 
 
400
                        adjusted_pwm /= 2;
 
401
 
 
402
                        if(adjusted_pwm < PWM_MIN) // Can't go any lower -> Change to lowest possible PWM level and stay there until critical voltage
 
403
                        {
 
404
                                adjusted_pwm = PWM_MIN;
 
405
                                adjusted_to_min = 1;                            
 
406
                        }                       
 
407
                }
 
408
        }       
 
409
#endif
 
410
}
 
411
 
 
412
#ifdef ENABLE_HIGH_TO_LOW
 
413
void revert_modes(void) // Revert modes in place
 
414
{
 
415
        uint8_t spare, *plow = modes, *phigh = modes+N_BASIC_MODES-1;
 
416
        
 
417
        while(plow < phigh)
 
418
        {
 
419
                spare = *plow;
 
420
                *plow = *phigh;
 
421
                *phigh = spare;
 
422
                plow++, phigh--;
 
423
        }
 
424
}
 
425
#endif
 
426
 
 
427
int main(void)
 
428
{       
 
429
        uint8_t short_click = 0;
 
430
        
 
431
        // All ports default to input, but turn pull-up resistors on for the stars (not the ADC input!  Made that mistake already)
 
432
        PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN);
 
433
        
 
434
        // Start up ADC for capacitor pin
 
435
        DIDR0 |= (1 << CAP_DIDR);                                                       // disable digital input on ADC pin to reduce power consumption
 
436
        ADMUX  = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3
 
437
        ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
 
438
        
 
439
        // Wait for completion
 
440
        while (ADCSRA & (1 << ADSC))
 
441
                ;
 
442
        // Start again as datasheet says first result is unreliable
 
443
        ADCSRA |= (1 << ADSC);
 
444
        // Wait for completion
 
445
        while (ADCSRA & (1 << ADSC))
 
446
                ;
 
447
 
 
448
        if(ADCH > CAP_THRESHOLD) 
 
449
                short_click = 1;
 
450
 
 
451
        // Turn off ADC
 
452
        ADC_off();
 
453
 
 
454
        // Charge up the capacitor by setting CAP_PIN to output
 
455
        DDRB  |= (1 << CAP_PIN);        // Output
 
456
    PORTB |= (1 << CAP_PIN);    // High
 
457
 
 
458
    // Set PWM pin to output
 
459
    DDRB = (1 << PWM_PIN);
 
460
 
 
461
    // Set timer to do PWM for correct output pin and set prescaler timing
 
462
    TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
 
463
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
464
        
 
465
        // Turn features on or off as needed
 
466
#ifdef ENABLE_VOLTAGE_MONITORING
 
467
        ADC_on();
 
468
#else
 
469
        ADC_off();
 
470
#endif
 
471
        ACSR   |=  (1<<7); //AC off
 
472
        
 
473
#ifdef ENABLE_TURNING_OFF_TURBO_TIMER
 
474
        turbo_timer_enabled = ((PINB & (1 << STAR2_PIN)) > 0) ? 1 : 0;
 
475
#endif
 
476
 
 
477
#ifdef ENABLE_SINGLE_MODE
 
478
        if((PINB & (1 << STAR2_PIN)) == 0)
 
479
        {
 
480
                        modes[0] = modes[1] = 
 
481
#ifdef FOUR_MODES
 
482
                        modes[2] = 
 
483
#endif
 
484
                        MODE_TURBO;
 
485
 
 
486
                        // Last basic mode is already MODE_TURBO - no need to waste bytes changing that
 
487
        }               
 
488
#endif
 
489
 
 
490
#ifdef ENABLE_HIGH_TO_LOW
 
491
#ifdef HIGH_TO_LOW_ON_BY_DEFAULT
 
492
        if((PINB & (1 << STAR3_PIN)) > 0)
 
493
#else
 
494
        if((PINB & (1 << STAR3_PIN)) == 0)
 
495
#endif
 
496
                revert_modes();
 
497
#endif
 
498
        
 
499
#ifdef ENABLE_TACTICAL_MODE
 
500
#ifdef TACTICAL_MODE_ON_BY_DEFAULT
 
501
        if((PINB & (1 << STAR2_PIN)) > 0)
 
502
#else
 
503
        if((PINB & (1 << STAR2_PIN)) == 0)
 
504
#endif
 
505
        {
 
506
                modes[1] = MODE_TURBO;
 
507
 
 
508
                if(modes[0] == MODE_TURBO) // Single mode
 
509
                {
 
510
                        modes[2] = 
 
511
#ifdef FOUR_MODES
 
512
                        modes[3] = 
 
513
#endif
 
514
                        MODE_TURBO;
 
515
                }
 
516
                else
 
517
                {
 
518
                        modes[0] = MODE_STROBE;
 
519
                        modes[2] = MODE_LOW;
 
520
#ifdef FOUR_MODES
 
521
                        modes[3] = MODE_LOWLOW;
 
522
#endif
 
523
                }
 
524
        }
 
525
#endif
 
526
 
 
527
 
 
528
        // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
 
529
        // Will allow us to go idle between WDT interrupts
 
530
        set_sleep_mode(SLEEP_MODE_IDLE);
 
531
        
 
532
        // Mode memory handling block
 
533
        {
 
534
                uint8_t n_short_clicks = 0;
 
535
 
 
536
                // Determine what mode we should fire up
 
537
                // Read the last mode that was saved
 
538
                mode_idx = read_stored_idx();
 
539
 
 
540
                // Handle short press counter
 
541
                n_short_clicks = (mode_idx & 0xf0);
 
542
                mode_idx &= 0x0f;
 
543
 
 
544
                if(short_click) // This click was short
 
545
                {
 
546
                        if(n_short_clicks == MODE_INDEX_TURBO_RAMPDOWN) // TODO: Test if this logic works in practice, or do we need to use always double tap from turbo?
 
547
                                n_short_clicks = 0; // Keep turbo, reset counter
 
548
                        else
 
549
                                next_mode(n_short_clicks); // Will handle wrap arounds
 
550
 
 
551
                        store_mode_idx(mode_idx | ((n_short_clicks < HIDDEN_MODE_THRESHOLD) ? n_short_clicks+0x10 : n_short_clicks));
 
552
                } 
 
553
                else // Didn't have a short press, keep the same mode, stored without short click counter
 
554
                {
 
555
                        if((PINB & (1 << STAR2_PIN)) == 0) // Tactical or Single or No memory, reset to 1st mode
 
556
                                mode_idx = 0;
 
557
 
 
558
                        store_mode_idx(mode_idx);
 
559
                }
 
560
        }               
 
561
        
 
562
        // Start watchdog timer (used for storing the mode after delay, turbo timer, and voltage monitoring)
 
563
        WDT_on();
 
564
        
 
565
        // Now just fire up the mode
 
566
        selected_pwm = modes[mode_idx]; // Note: Actual PWM can be less than selected PWM (e.g. in case of low voltage)
 
567
        
 
568
        if(selected_pwm < PWM_MIN) // Hidden blinky modes
 
569
                adjusted_pwm = PWM_MAX; // All blinky modes initially with full power
 
570
        else
 
571
                adjusted_pwm = selected_pwm;
 
572
 
 
573
        // block for main loop
 
574
        {
 
575
                uint8_t ii = 0; // Loop counter, used by multiple branches
 
576
#ifdef ENABLE_BEACONS
 
577
                uint8_t beacon_background = PWM_OFF;
 
578
#endif
 
579
        
 
580
                while(1)
 
581
                {
 
582
#ifdef ENABLE_VOLTAGE_MONITORING
 
583
                        if(adjusted_pwm == PWM_OFF) // Voltage monitoring signaled us to turn off the light -> break out of the main loop
 
584
                                break;
 
585
#endif
 
586
 
 
587
                        PWM_LVL = adjusted_pwm; // must be set inside loop, is volatile & might have changed because of voltage monitoring
 
588
 
 
589
                        switch(selected_pwm)
 
590
                        {
 
591
                        case MODE_STROBE: // Disorienting alternating strobe
 
592
#ifdef NORMAL_STROBE
 
593
                                // 51ms = ~19.5Hz, ~60% DC
 
594
                                _delay_ms(25);
 
595
                                PWM_LVL = 0;
 
596
                                _delay_ms(26);
 
597
#endif
 
598
#ifdef ALTERNATING_STROBE
 
599
                                _delay_ms(31);
 
600
                                PWM_LVL = 0;
 
601
                                if(ii < 19) // 51ms = ~19.5Hz, ~60% DC
 
602
                                        _delay_ms(20);
 
603
                                else if(ii < 32) // 77ms = ~13Hz, ~40% DC
 
604
                                        _delay_ms(46);
 
605
                                else
 
606
                                        ii = 255;
 
607
#endif
 
608
#ifdef RANDOM_STROBE 
 
609
                                {   // 77ms = 13Hz, 51ms = 19.5Hz / 40-60% DC
 
610
                                        ii = (5 * ii) + 128;
 
611
                                        _delay_ms(31);
 
612
                                        PWM_LVL = 0;
 
613
                                        _delay_ms(ii > 127 ? 46 : 20);
 
614
                                }                                       
 
615
#endif
 
616
                                break;
 
617
                        case MODE_MOTION_STOPPING_STROBE: // 8Hz, 1.6% DC
 
618
                                _delay_ms(2);
 
619
                                PWM_LVL = 0;
 
620
                                _delay_ms(123);
 
621
                                break;
 
622
#ifdef ENABLE_SOS
 
623
                        case MODE_SOS:
 
624
                                if(ii / 3 == 1)
 
625
                                        _delay_ms(600);  // Dash for 'O' (3xDot)
 
626
                                else
 
627
                                        _delay_ms(200);  // Dot for 'S'
 
628
 
 
629
                                PWM_LVL = 0;
 
630
 
 
631
                                switch(ii)
 
632
                                {
 
633
                                default:
 
634
                                        _delay_ms(200);  // Pause inside a letter (1xDot)
 
635
                                        break;
 
636
                                case 2:
 
637
                                case 5:
 
638
                                        _delay_ms(600); // Pause between letters (3xDot)
 
639
                                        break;
 
640
                                case 8:
 
641
                                        _delay_ms(2500); // Pause between "words" (should be 7xDot, but I like it longer)
 
642
                                        ii = 255;
 
643
                                        break;
 
644
                                }
 
645
                                break;
 
646
#endif
 
647
#ifdef ENABLE_BEACONS
 
648
                        case MODE_BEACON_WITH_BACKGROUND:
 
649
                                beacon_background = PWM_BEACON_BACKGROUND;
 
650
                                goto beacon_common;
 
651
                        case MODE_SLOW_BEACON_WITH_BACKGROUND:
 
652
                                beacon_background = PWM_SLOW_BEACON_BACKGROUND;
 
653
                                // no break - fall through to beacon code
 
654
                        case MODE_BEACON:
 
655
                        case MODE_ALPINE_DISTRESS_BEACON:
 
656
                        beacon_common:
 
657
                                _delay_ms(50);
 
658
                                PWM_LVL = beacon_background;
 
659
                                _delay_ms(950);
 
660
                                
 
661
                                if(selected_pwm == MODE_ALPINE_DISTRESS_BEACON)
 
662
                                {
 
663
                                        if(ii > 5)
 
664
                                        {
 
665
                                                _delay_ms(59000);
 
666
                                                ii = 255;
 
667
                                        }
 
668
                                        else
 
669
                                                _delay_ms(9000);
 
670
                                }
 
671
                                else if(selected_pwm == MODE_SLOW_BEACON_WITH_BACKGROUND)
 
672
                                        _delay_ms(1500);
 
673
#endif
 
674
                                break;
 
675
                        default:
 
676
                                sleep_mode();
 
677
                                break;
 
678
                        }
 
679
 
 
680
                        ii++; // Loop counter, used by multiple branches
 
681
                }
 
682
        }
 
683
 
 
684
#ifdef ENABLE_VOLTAGE_MONITORING
 
685
        //
 
686
        // Critically low voltage -> Turn off the light
 
687
        // 
 
688
                
 
689
        // Disable WDT so it doesn't wake us up from sleep
 
690
        WDT_off();
 
691
 
 
692
        // Would be nice to blink a couple of times with lowest brightness to notify the user, but not implemented due space restrictions
 
693
 
 
694
        // Turn the light off
 
695
        PWM_LVL = 0;
 
696
 
 
697
        // Disable ADC so it doesn't consume power
 
698
        ADC_off();
 
699
 
 
700
        // Power down as many components as possible
 
701
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
702
 
 
703
        // Deep sleep until powered off - consumes ~110uA during the deep sleep
 
704
        while(1)
 
705
                sleep_mode();
 
706
#endif
 
707
 
 
708
    return 0; // Standard Return Code -> would return to idle loop with interrupts disabled.
 
709
}