~gabe/flashlight-firmware/anduril2

« back to all changes in this revision

Viewing changes to ToyKeeper/starry-offtime/starry-offtime.c

  • Committer: Selene Scriven
  • Date: 2016-11-29 22:51:09 UTC
  • mto: This revision was merged to the branch mainline in revision 176.
  • Revision ID: ubuntu@toykeeper.net-20161129225109-oab58sah3mb7mk1j
Copied biscotti.c to gchart/babka/babka.c

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * NANJG 105C Diagram
 
3
 *           ---
 
4
 *         -|   |- VCC
 
5
 *  Star 4 -|   |- Voltage ADC
 
6
 *  Star 3 -|   |- PWM
 
7
 *     GND -|   |- Star 2
 
8
 *           ---
 
9
 *
 
10
 * FUSES
 
11
 *      I use these fuse settings
 
12
 *      Low:  0x75  (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM)
 
13
 *      High: 0xff
 
14
 *
 
15
 *      For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency
 
16
 *
 
17
 * STARS
 
18
 *      Star 2 = second PWM output channel
 
19
 *      Star 3 = mode memory if soldered, no memory by default
 
20
 *      Star 4 = Capacitor for off-time
 
21
 *
 
22
 * VOLTAGE
 
23
 *      Resistor values for voltage divider (reference BLF-VLD README for more info)
 
24
 *      Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
 
25
 *
 
26
 *           VCC
 
27
 *            |
 
28
 *           Vd (~.25 v drop from protection diode)
 
29
 *            |
 
30
 *          1912 (R1 19,100 ohms)
 
31
 *            |
 
32
 *            |---- PB2 from MCU
 
33
 *            |
 
34
 *          4701 (R2 4,700 ohms)
 
35
 *            |
 
36
 *           GND
 
37
 *
 
38
 *      ADC = ((V_bat - V_diode) * R2   * 255) / ((R1    + R2  ) * V_ref)
 
39
 *      125 = ((3.0   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
 
40
 *      121 = ((2.9   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
 
41
 *
 
42
 *      Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
 
43
 *      130 and 120
 
44
 *
 
45
 *      To find out what value to use, plug in the target voltage (V) to this equation
 
46
 *          value = (V * 4700 * 255) / (23800 * 1.1)
 
47
 *
 
48
 */
 
49
#define F_CPU 4800000UL
 
50
 
 
51
/*
 
52
 * =========================================================================
 
53
 * Settings to modify per driver
 
54
 */
 
55
 
 
56
//#define FAST 0x23           // fast PWM channel 1 only
 
57
//#define PHASE 0x21          // phase-correct PWM channel 1 only
 
58
#define FAST 0xA3             // fast PWM both channels
 
59
#define PHASE 0xA1            // phase-correct PWM both channels
 
60
 
 
61
#define VOLTAGE_MON         // Comment out to disable
 
62
#define OWN_DELAY           // Should we use the built-in delay or our own?
 
63
// Adjust the timing per-driver, since the hardware has high variance
 
64
// Higher values will run slower, lower values run faster.
 
65
#define DELAY_TWEAK         950
 
66
 
 
67
//#define TICKS_250MS       // If enabled, ticks are every 250 ms. If disabled, ticks are every 500 ms
 
68
                            // Affects turbo timeout/rampdown timing
 
69
 
 
70
#define OFFTIM3             // Use short/med/long off-time presses
 
71
                            // instead of just short/long
 
72
 
 
73
// PWM levels for the big circuit (FET or Nx7135)
 
74
#define MODESNx             0,0,0,70,255
 
75
// PWM levels for the small circuit (1x7135)
 
76
// (if the big circuit is a FET, use 0 for high modes here instead of 255)
 
77
#define MODES1x             3,15,128,255,255
 
78
#define MODES_PWM           PHASE,FAST,FAST,FAST,PHASE
 
79
// Hidden modes are *before* the lowest (moon) mode, and should be specified
 
80
// in reverse order.  So, to go backward from moon to turbo to strobe to
 
81
// battcheck, use BATTCHECK,STROBE,255 .
 
82
#define HIDDENMODES         BATTCHECK,STROBE,255
 
83
#define HIDDENMODES_PWM     PHASE,PHASE,PHASE
 
84
 
 
85
#define MODE_TURBO_LOW      140 // Level turbo ramps down to if turbo enabled
 
86
#define TURBO                   // comment out to disable turbo step-down
 
87
#define TURBO_TIMEOUT       240 // How many WTD ticks before before dropping down.  If ticks set for 500 ms, then 240 x .5 = 120 seconds.  Max value of 255 unless you change "ticks"
 
88
                                // variable to uint8_t
 
89
//#define TURBO_RAMP_DOWN           // By default we will start to gradually ramp down, once TURBO_TIMEOUT ticks are reached, 1 PWM_LVL each tick until reaching MODE_TURBO_LOW PWM_LVL
 
90
                                // If commented out, we will step down to MODE_TURBO_LOW once TURBO_TIMEOUT ticks are reached
 
91
 
 
92
// These values were measured using "Moonlight Special" driver from RMM.
 
93
// Your mileage may vary.
 
94
#define ADC_42          195 // the ADC value we expect for 4.20 volts
 
95
#define ADC_100         195 // the ADC value for 100% full (4.2V resting)
 
96
#define ADC_75          184 // the ADC value for 75% full (4.0V resting)
 
97
#define ADC_50          174 // the ADC value for 50% full (3.8V resting)
 
98
#define ADC_25          159 // the ADC value for 25% full (3.5V resting)
 
99
#define ADC_0           133 // the ADC value for 0% full (3.0V resting)
 
100
#define ADC_LOW         123 // When do we start ramping down (2.8V)
 
101
#define ADC_CRIT        118 // When do we shut the light off (2.7V)
 
102
// These were JonnyC's original values
 
103
//#define ADC_LOW             130     // When do we start ramping
 
104
//#define ADC_CRIT            120     // When do we shut the light off
 
105
 
 
106
#ifdef OFFTIM3
 
107
#define CAP_SHORT           190  // 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
 
108
#define CAP_MED             100  // 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
 
109
#else
 
110
#define CAP_SHORT           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
 
111
                                 // 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
 
112
                                 // even when waiting 10 seconds between presses.
 
113
#endif
 
114
 
 
115
// Comment these out to disable the mode and save space
 
116
#define STROBE    254       // Convenience code for strobe mode
 
117
#define BATTCHECK 253       // Convenience code for battery check mode
 
118
 
 
119
/*
 
120
 * =========================================================================
 
121
 */
 
122
 
 
123
#ifdef OWN_DELAY
 
124
#include <util/delay_basic.h>
 
125
// Having own _delay_ms() saves some bytes AND adds possibility to use variables as input
 
126
static void _delay_ms(uint16_t n)
 
127
{
 
128
    // TODO: make this take tenths of a ms instead of ms,
 
129
    // for more precise timing?
 
130
    while(n-- > 0) _delay_loop_2(DELAY_TWEAK);
 
131
}
 
132
#else
 
133
#include <util/delay.h>
 
134
#endif
 
135
 
 
136
#include <avr/pgmspace.h>
 
137
#include <avr/io.h>
 
138
#include <avr/interrupt.h>
 
139
#include <avr/wdt.h>
 
140
#include <avr/eeprom.h>
 
141
#include <avr/sleep.h>
 
142
//#include <avr/power.h>
 
143
 
 
144
#define STAR2_PIN   PB0
 
145
#define STAR3_PIN   PB4
 
146
#define CAP_PIN     PB3
 
147
#define CAP_CHANNEL 0x03    // MUX 03 corresponds with PB3 (Star 4)
 
148
#define CAP_DIDR    ADC3D   // Digital input disable bit corresponding with PB3
 
149
#define PWM_PIN     PB1
 
150
#define VOLTAGE_PIN PB2
 
151
#define ADC_CHANNEL 0x01    // MUX 01 corresponds with PB2
 
152
#define ADC_DIDR    ADC1D   // Digital input disable bit corresponding with PB2
 
153
#define ADC_PRSCL   0x06    // clk/64
 
154
 
 
155
#define PWM_LVL     OCR0B   // OCR0B is the output compare register for PB1
 
156
#define ALT_PWM_LVL OCR0A   // OCR0A is the output compare register for PB0
 
157
 
 
158
/*
 
159
 * global variables
 
160
 */
 
161
 
 
162
// Mode storage
 
163
uint8_t eepos = 0;
 
164
uint8_t eep[32];
 
165
uint8_t memory = 0;
 
166
 
 
167
// Modes (gets set when the light starts up based on stars)
 
168
PROGMEM const uint8_t modesNx[] = { MODESNx , HIDDENMODES };
 
169
PROGMEM const uint8_t modes1x[] = { MODES1x , HIDDENMODES };
 
170
PROGMEM const uint8_t modes_pwm[] = { MODES_PWM , HIDDENMODES_PWM };
 
171
volatile uint8_t mode_idx = 0;
 
172
// NOTE: Only '1' is known to work; -1 will probably break and is untested.
 
173
// In other words, short press goes to the next (higher) mode,
 
174
// medium press goes to the previous (lower) mode.
 
175
#define mode_dir 1
 
176
uint8_t mode_cnt = sizeof(modesNx);
 
177
 
 
178
uint8_t lowbatt_cnt = 0;
 
179
 
 
180
PROGMEM const uint8_t voltage_blinks[] = {
 
181
    ADC_0,    // 1 blink  for 0%-25%
 
182
    ADC_25,   // 2 blinks for 25%-50%
 
183
    ADC_50,   // 3 blinks for 50%-75%
 
184
    ADC_75,   // 4 blinks for 75%-100%
 
185
    ADC_100,  // 5 blinks for >100%
 
186
};
 
187
 
 
188
void store_mode_idx(uint8_t lvl) {  //central method for writing (with wear leveling)
 
189
    uint8_t oldpos=eepos;
 
190
    eepos=(eepos+1)&31;  //wear leveling, use next cell
 
191
    // Write the current mode
 
192
    EEARL=eepos;  EEDR=lvl; EECR=32+4; EECR=32+4+2;  //WRITE  //32:write only (no erase)  4:enable  2:go
 
193
    while(EECR & 2); //wait for completion
 
194
    // Erase the last mode
 
195
    EEARL=oldpos;           EECR=16+4; EECR=16+4+2;  //ERASE  //16:erase only (no write)  4:enable  2:go
 
196
}
 
197
inline void read_mode_idx() {
 
198
    eeprom_read_block(&eep, 0, 32);
 
199
    while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
 
200
    if (eepos < 32) mode_idx = eep[eepos];//&0x10; What the?
 
201
    else eepos=0;
 
202
}
 
203
 
 
204
inline void next_mode() {
 
205
    mode_idx += mode_dir;
 
206
    if (mode_idx >= mode_cnt) {
 
207
        // Wrap around
 
208
        mode_idx = 0;
 
209
    }
 
210
}
 
211
 
 
212
#ifdef OFFTIM3
 
213
inline void prev_mode() {
 
214
    if (mode_idx > 0) {
 
215
        // Regular mode: is between 1 and TOTAL_MODES
 
216
        mode_idx -= mode_dir;
 
217
    } else {
 
218
        // Otherwise, wrap around
 
219
        mode_idx = mode_cnt - 1;
 
220
    }
 
221
    /* For future use:
 
222
    // FIXME: use a different mechanism for hidden modes
 
223
    if ((0x40 > mode_idx) && (mode_idx > 0)) {
 
224
        // Regular mode: is between 1 and TOTAL_MODES
 
225
        mode_idx -= mode_dir;
 
226
    // FIXME: use a different mechanism for hidden modes
 
227
    } else if ((mode_idx&0x3f) < sizeof(neg_modes)) {
 
228
        // "Negative" mode (uses 0x40 bit to indicate "negative")
 
229
        mode_idx = (mode_idx|0x40) + mode_dir;
 
230
    } else {
 
231
        // Otherwise, always reset to first mode
 
232
        // (mode was too negative or otherwise out of range)
 
233
        mode_idx = 0;
 
234
    }
 
235
    */
 
236
}
 
237
#endif
 
238
 
 
239
inline void check_stars() {
 
240
    // Configure options based on stars
 
241
    // 0 being low for soldered, 1 for pulled-up for not soldered
 
242
#if 0  // not implemented, STAR2_PIN is used for second PWM channel
 
243
    // Moon
 
244
    // enable moon mode?
 
245
    if ((PINB & (1 << STAR2_PIN)) == 0) {
 
246
        modes[mode_cnt++] = MODE_MOON;
 
247
    }
 
248
#endif
 
249
#if 0  // Mode order not as important as mem/no-mem
 
250
    // Mode order
 
251
    if ((PINB & (1 << STAR3_PIN)) == 0) {
 
252
        // High to Low
 
253
        mode_dir = -1;
 
254
    } else {
 
255
        mode_dir = 1;
 
256
    }
 
257
#endif
 
258
    // Memory
 
259
    if ((PINB & (1 << STAR3_PIN)) == 0) {
 
260
        memory = 1;  // solder to enable memory
 
261
    } else {
 
262
        memory = 0;  // unsolder to disable memory
 
263
    }
 
264
}
 
265
 
 
266
inline void WDT_on() {
 
267
    // Setup watchdog timer to only interrupt, not reset
 
268
    cli();                          // Disable interrupts
 
269
    wdt_reset();                    // Reset the WDT
 
270
    WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
271
    #ifdef TICKS_250MS
 
272
    WDTCR = (1<<WDTIE) | (1<<WDP2); // Enable interrupt every 250ms
 
273
    #else
 
274
    WDTCR = (1<<WDTIE) | (1<<WDP2) | (1<<WDP0); // Enable interrupt every 500ms
 
275
    #endif
 
276
    sei();                          // Enable interrupts
 
277
}
 
278
 
 
279
inline void WDT_off()
 
280
{
 
281
    cli();                          // Disable interrupts
 
282
    wdt_reset();                    // Reset the WDT
 
283
    MCUSR &= ~(1<<WDRF);            // Clear Watchdog reset flag
 
284
    WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
285
    WDTCR = 0x00;                   // Disable WDT
 
286
    sei();                          // Enable interrupts
 
287
}
 
288
 
 
289
inline void ADC_on() {
 
290
    DIDR0 |= (1 << ADC_DIDR);                           // disable digital input on ADC pin to reduce power consumption
 
291
    ADMUX  = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
 
292
    ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
 
293
}
 
294
 
 
295
inline void ADC_off() {
 
296
    ADCSRA &= ~(1<<7); //ADC off
 
297
}
 
298
 
 
299
void set_output(uint8_t pwm1, uint8_t pwm2) {
 
300
    // Need PHASE to properly turn off the light
 
301
    if ((pwm1==0) && (pwm2==0)) {
 
302
        TCCR0A = PHASE;
 
303
    }
 
304
    PWM_LVL = pwm1;
 
305
    ALT_PWM_LVL = pwm2;
 
306
}
 
307
 
 
308
void set_mode(uint8_t mode) {
 
309
    TCCR0A = pgm_read_byte(modes_pwm + mode);
 
310
    // Only set output for solid modes
 
311
    uint8_t out = pgm_read_byte(modesNx + mode);
 
312
    if ((out < 250) || (out == 255)) {
 
313
        set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode));
 
314
    }
 
315
}
 
316
 
 
317
#ifdef VOLTAGE_MON
 
318
uint8_t get_voltage() {
 
319
    // Start conversion
 
320
    ADCSRA |= (1 << ADSC);
 
321
    // Wait for completion
 
322
    while (ADCSRA & (1 << ADSC));
 
323
    // See if voltage is lower than what we were looking for
 
324
    return ADCH;
 
325
}
 
326
 
 
327
uint8_t low_voltage(uint8_t voltage_val) {
 
328
    uint8_t voltage = get_voltage();
 
329
    // See if voltage is lower than what we were looking for
 
330
    if (voltage < voltage_val) {
 
331
        // See if it's been low for a while
 
332
        if (++lowbatt_cnt > 8) {
 
333
            lowbatt_cnt = 0;
 
334
            return 1;
 
335
        }
 
336
        _delay_ms(100);  // don't take a reading *too* often
 
337
    } else {
 
338
        lowbatt_cnt = 0;
 
339
    }
 
340
    return 0;
 
341
}
 
342
#endif
 
343
 
 
344
ISR(WDT_vect) {
 
345
    static uint8_t ticks = 0;
 
346
    if (ticks < 255) ticks++;
 
347
    // If you want more than 255 for longer turbo timeouts
 
348
    //static uint16_t ticks = 0;
 
349
    //if (ticks < 60000) ticks++;
 
350
 
 
351
#ifdef TURBO
 
352
    //if (ticks == TURBO_TIMEOUT && modes[mode_idx] == MODE_TURBO) { // Doesn't make any sense why this doesn't work
 
353
    if (ticks >= TURBO_TIMEOUT && mode_idx == (mode_cnt - 1) && PWM_LVL > MODE_TURBO_LOW) {
 
354
        #ifdef TURBO_RAMP_DOWN
 
355
        set_output(PWM_LVL - 1, PWM_LVL - 1);
 
356
        #else
 
357
        // Turbo mode is always at end
 
358
        set_output(MODE_TURBO_LOW, MODE_TURBO_LOW);
 
359
        /*
 
360
        if (MODE_TURBO_LOW <= modes[mode_idx-1]) {
 
361
            // Dropped at or below the previous mode, so set it to the stored mode
 
362
            // Kept this as it was the same functionality as before.  For the TURBO_RAMP_DOWN feature
 
363
            // it doesn't do this logic because I don't know what makes the most sense
 
364
            store_mode_idx(--mode_idx);
 
365
        }
 
366
        */
 
367
        #endif
 
368
    }
 
369
#endif
 
370
 
 
371
}
 
372
 
 
373
int main(void)
 
374
{
 
375
    // All ports default to input, but turn pull-up resistors on for the stars (not the ADC input!  Made that mistake already)
 
376
    // only one star, because one is used for PWM channel 2
 
377
    // and the other is used for the off-time capacitor
 
378
    PORTB = (1 << STAR3_PIN);
 
379
 
 
380
    // Determine what mode we should fire up
 
381
    // Read the last mode that was saved
 
382
    read_mode_idx();
 
383
 
 
384
    check_stars(); // Moving down here as it might take a bit for the pull-up to turn on?
 
385
 
 
386
    // Start up ADC for capacitor pin
 
387
    DIDR0 |= (1 << CAP_DIDR);                           // disable digital input on ADC pin to reduce power consumption
 
388
    ADMUX  = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3
 
389
    ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
 
390
 
 
391
    // Wait for completion
 
392
    while (ADCSRA & (1 << ADSC));
 
393
    // Start again as datasheet says first result is unreliable
 
394
    ADCSRA |= (1 << ADSC);
 
395
    // Wait for completion
 
396
    while (ADCSRA & (1 << ADSC));
 
397
    if (ADCH > CAP_SHORT) {
 
398
        // Indicates they did a short press, go to the next mode
 
399
        next_mode(); // Will handle wrap arounds
 
400
        store_mode_idx(mode_idx);
 
401
#ifdef OFFTIM3
 
402
    } else if (ADCH > CAP_MED) {
 
403
        // User did a medium press, go back one mode
 
404
        prev_mode(); // Will handle "negative" modes and wrap-arounds
 
405
        store_mode_idx(mode_idx);
 
406
#endif
 
407
    } else {
 
408
        // Didn't have a short press, keep the same mode
 
409
        // ... or reset to the first mode
 
410
        if (! memory) {
 
411
            // Reset to the first mode
 
412
            mode_idx = 0;
 
413
            store_mode_idx(mode_idx);
 
414
        }
 
415
    }
 
416
    // Turn off ADC
 
417
    ADC_off();
 
418
 
 
419
    // Charge up the capacitor by setting CAP_PIN to output
 
420
    DDRB  |= (1 << CAP_PIN);    // Output
 
421
    PORTB |= (1 << CAP_PIN);    // High
 
422
 
 
423
    // Set PWM pin to output
 
424
    DDRB |= (1 << PWM_PIN);     // enable main channel
 
425
    DDRB |= (1 << STAR2_PIN);   // enable second channel
 
426
 
 
427
    // Set timer to do PWM for correct output pin and set prescaler timing
 
428
    //TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
 
429
    //TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
430
 
 
431
    // Turn features on or off as needed
 
432
    #ifdef VOLTAGE_MON
 
433
    ADC_on();
 
434
    #else
 
435
    ADC_off();
 
436
    #endif
 
437
    ACSR   |=  (1<<7); //AC off
 
438
 
 
439
    // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
 
440
    // Will allow us to go idle between WDT interrupts
 
441
    //set_sleep_mode(SLEEP_MODE_IDLE);  // not used due to blinky modes
 
442
 
 
443
    WDT_on();
 
444
 
 
445
    // Now just fire up the mode
 
446
    // Set timer to do PWM for correct output pin and set prescaler timing
 
447
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
448
 
 
449
    set_mode(mode_idx);
 
450
 
 
451
    uint8_t output;
 
452
#ifdef VOLTAGE_MON
 
453
    uint8_t i = 0;
 
454
    uint8_t hold_pwm;
 
455
#ifdef BATTCHECK
 
456
    uint8_t voltage;
 
457
#endif
 
458
#endif
 
459
    while(1) {
 
460
        output = pgm_read_byte(modesNx + mode_idx);
 
461
        if (0) {}  // placeholder in case STROBE isn't defined
 
462
#ifdef STROBE
 
463
        else if (output == STROBE) {
 
464
            set_output(255,255);
 
465
            _delay_ms(50);
 
466
            set_output(0,0);
 
467
            _delay_ms(50);
 
468
        }
 
469
#endif
 
470
#ifdef BATTCHECK
 
471
        else if (output == BATTCHECK) {
 
472
            uint8_t blinks = 0;
 
473
            // turn off and wait one second before showing the value
 
474
            // (also, ensure voltage is measured while not under load)
 
475
            set_output(0,0);
 
476
            _delay_ms(1000);
 
477
            voltage = get_voltage();
 
478
            voltage = get_voltage(); // the first one is unreliable
 
479
            // division takes too much flash space
 
480
            //voltage = (voltage-ADC_LOW) / (((ADC_42 - 15) - ADC_LOW) >> 2);
 
481
            // a table uses less space than 5 logic clauses
 
482
            for (i=0; i<sizeof(voltage_blinks); i++) {
 
483
                if (voltage > pgm_read_byte(voltage_blinks + i)) {
 
484
                    blinks ++;
 
485
                }
 
486
            }
 
487
 
 
488
            // blink up to five times to show voltage
 
489
            // (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
 
490
            for(i=0; i<blinks; i++) {
 
491
                set_output(0,40);
 
492
                _delay_ms(100);
 
493
                set_output(0,0);
 
494
                _delay_ms(400);
 
495
            }
 
496
 
 
497
            _delay_ms(1000);  // wait at least 1 second between readouts
 
498
        }
 
499
#endif
 
500
    #ifdef VOLTAGE_MON
 
501
        if (low_voltage(ADC_LOW)) {
 
502
            // We need to go to a lower level
 
503
            if (mode_idx == 0 && ALT_PWM_LVL <= modes1x[mode_idx]) {
 
504
                // Can't go any lower than the lowest mode
 
505
                // Wait until we hit the critical level before flashing 10 times and turning off
 
506
                while (!low_voltage(ADC_CRIT));
 
507
                i = 0;
 
508
                while (i++<10) {
 
509
                    set_output(0,0);
 
510
                    _delay_ms(250);
 
511
                    set_mode(0);
 
512
                    _delay_ms(500);
 
513
                }
 
514
                // Turn off the light
 
515
                set_output(0,0);
 
516
                // Disable WDT so it doesn't wake us up
 
517
                WDT_off();
 
518
                // Power down as many components as possible
 
519
                set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
520
                sleep_mode();
 
521
            } else {
 
522
                // Flash 3 times before lowering
 
523
                hold_pwm = ALT_PWM_LVL;
 
524
                i = 0;
 
525
                while (i++<3) {
 
526
                    set_output(0,0);
 
527
                    _delay_ms(250);
 
528
                    set_output(hold_pwm,hold_pwm);
 
529
                    _delay_ms(500);
 
530
                }
 
531
                // Lower the mode by half, but don't go below lowest level
 
532
                if ((ALT_PWM_LVL >> 1) < modes1x[0]) {
 
533
                    set_mode(0);
 
534
                    mode_idx = 0;
 
535
                } else {
 
536
                    set_output(0,ALT_PWM_LVL >> 1);
 
537
                }
 
538
                // See if we should change the current mode level if we've gone under the current mode.
 
539
                if (ALT_PWM_LVL < modes1x[mode_idx]) {
 
540
                    // Lower our recorded mode
 
541
                    mode_idx--;
 
542
                }
 
543
            }
 
544
            // Wait 3 seconds before lowering the level again
 
545
            _delay_ms(3000);
 
546
        }
 
547
    #endif
 
548
        //sleep_mode();  // incompatible with blinky modes
 
549
    }
 
550
 
 
551
    return 0; // Standard Return Code
 
552
}