~gabe/flashlight-firmware/anduril2

« back to all changes in this revision

Viewing changes to ToyKeeper/cypreus/cypreus.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
 * This is intended for use on flashlights with a clicky switch and off-time
 
3
 * memory capacitor.  Ideally, a triple XP-L powered by a BLF17DD in a Sinner
 
4
 * Cypreus tri-EDC host.  It's mostly based on JonnyC's STAR firmware and
 
5
 * ToyKeeper's s7.c firmware.
 
6
 *
 
7
 * Original author: JonnyC
 
8
 * Modifications: ToyKeeper / Selene Scriven
 
9
 *
 
10
 * NANJG 105C Diagram
 
11
 *           ---
 
12
 *         -|1  8|- VCC
 
13
 * mem cap -|2  7|- Voltage ADC
 
14
 *  Star 3 -|3  6|- PWM
 
15
 *     GND -|4  5|- Star 2
 
16
 *           ---
 
17
 *
 
18
 * CPU speed is 4.8Mhz without the 8x divider when low fuse is 0x75
 
19
 *
 
20
 * define F_CPU 4800000  CPU: 4.8MHz  PWM: 9.4kHz       ####### use low fuse: 0x75  #######
 
21
 *                             /8     PWM: 1.176kHz     ####### use low fuse: 0x65  #######
 
22
 * define F_CPU 9600000  CPU: 9.6MHz  PWM: 19kHz        ####### use low fuse: 0x7a  #######
 
23
 *                             /8     PWM: 2.4kHz       ####### use low fuse: 0x6a  #######
 
24
 *
 
25
 * Above PWM speeds are for phase-correct PWM.  This program uses Fast-PWM,
 
26
 * which when the CPU is 4.8MHz will be 18.75 kHz
 
27
 *
 
28
 * FUSES
 
29
 *      I use these fuse settings
 
30
 *      Low:  0x75
 
31
 *      High: 0xff
 
32
 *
 
33
 * STARS (not used)
 
34
 *
 
35
 * VOLTAGE
 
36
 *      Resistor values for voltage divider (reference BLF-VLD README for more info)
 
37
 *      Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
 
38
 *
 
39
 *           VCC
 
40
 *            |
 
41
 *           Vd (~.25 v drop from protection diode)
 
42
 *            |
 
43
 *          1912 (R1 19,100 ohms)
 
44
 *            |
 
45
 *            |---- PB2 from MCU
 
46
 *            |
 
47
 *          4701 (R2 4,700 ohms)
 
48
 *            |
 
49
 *           GND
 
50
 *
 
51
 *      ADC = ((V_bat - V_diode) * R2   * 255) / ((R1    + R2  ) * V_ref)
 
52
 *      125 = ((3.0   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
 
53
 *      121 = ((2.9   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
 
54
 *
 
55
 *      Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
 
56
 *      130 and 120
 
57
 *
 
58
 *      To find out what value to use, plug in the target voltage (V) to this equation
 
59
 *          value = (V * 4700 * 255) / (23800 * 1.1)
 
60
 *
 
61
 */
 
62
#define F_CPU 4800000UL
 
63
#define FASTPWM 0x23
 
64
#define PHASEPWM 0x21
 
65
 
 
66
/*
 
67
 * =========================================================================
 
68
 * Settings to modify per driver
 
69
 */
 
70
 
 
71
#define VOLTAGE_MON              // Comment out to disable all battery monitoring
 
72
#define OWN_DELAY                // Should we use the built-in delay or our own?
 
73
#define USE_PFM                  // Use PFM to make moon mode brighter
 
74
// Obsoleted; use moon_ceilings below instead
 
75
//#define MOON_PFM_LVL        30   // lower is brighter, 255 is max (same as no PFM)
 
76
// NOTE: WDT is required for on-time memory and WDT-based turbo step-down
 
77
// NOTE: WDT isn't tested, and probably doesn't work
 
78
//#define ENABLE_WDT               // comment out to turn off WDT and save space
 
79
#define NON_WDT_TURBO            // enable turbo step-down without WDT
 
80
#define TURBO_TIMEOUT       4000 // "ticks" until turbo step-down (~0.005s each)
 
81
                                 // (maximum is 65535, or 127 seconds)
 
82
                                 // Remove me: (maximum is 254, or 127 seconds)
 
83
#define TURBO_MODES         2    // Treat top N modes as "turbo" modes with timed step-down
 
84
                                 // (1 for just highest, 2 for two highest, etc)
 
85
 
 
86
// Set your PWM levels here (low to high, maximum 255)
 
87
// 1,8,39,120,255 for 5-step phase-correct PWM
 
88
#define MODE_MOON              0    // can use PFM to fine-tune brightness
 
89
#define MODE_LOW               1    // lowest normal mode, ~0.4% / ~12 lm
 
90
#define MODE_MED               8    // ~3.1% / ~94 lm
 
91
#define MODE_HIGH              42   // ~16.5% / ~494 lm
 
92
#define MODE_HIGHER            120  // ~47% / ~1411 lm
 
93
#define MODE_MAX               255  // direct drive, ~3000lm
 
94
// If you change these, you'll probably want to change the "modes" array below
 
95
// Each value must be cumulative, so include the value just above it.
 
96
// How many non-blinky modes will we have?
 
97
#define SOLID_MODES            6
 
98
// How many beacon modes will we have (with background light on)?
 
99
#define DUAL_BEACON_MODES      3+SOLID_MODES
 
100
// How many beacon modes will we have (without background light on)?
 
101
#define SINGLE_BEACON_MODES    1+DUAL_BEACON_MODES
 
102
// How many constant-speed strobe modes?
 
103
#define FIXED_STROBE_MODES     3+SINGLE_BEACON_MODES
 
104
// How many variable-speed strobe modes?
 
105
#define VARIABLE_STROBE_MODES  1+FIXED_STROBE_MODES
 
106
// battery check mode index
 
107
#define BATT_CHECK_MODE        1+VARIABLE_STROBE_MODES
 
108
// Note: don't use more than 32 modes,
 
109
// or it will interfere with the mechanism used for mode memory
 
110
#define TOTAL_MODES            BATT_CHECK_MODE
 
111
 
 
112
// NOTE: mode isn't saved this way, this value only applies to on-time memory
 
113
#ifdef ENABLE_WDT
 
114
#define WDT_TIMEOUT         2    // Number of WTD ticks before mode is saved (.5 sec each)
 
115
#endif
 
116
 
 
117
// These values were measured using a Ferrero Rocher F6DD driver and a DMM
 
118
// Your mileage may vary.  May be off by up to 0.1V or so on different hardware.
 
119
#define ADC_42          185 // the ADC value we expect for 4.20 volts
 
120
#define VOLTAGE_FULL    171 // 3.9 V, 4 blinks
 
121
#define VOLTAGE_GREEN   156 // 3.6 V, 3 blinks
 
122
#define VOLTAGE_YELLOW  141 // 3.3 V, 2 blinks
 
123
#define VOLTAGE_RED     126 // 3.0 V, 1 blink
 
124
#define ADC_LOW         125 // When do we start ramping down
 
125
#define ADC_CRIT        115 // When do we shut the light off
 
126
// these two are just for testing low-batt behavior w/ a CR123 cell
 
127
//#define ADC_LOW         139 // When do we start ramping down
 
128
//#define ADC_CRIT        138 // When do we shut the light off
 
129
// These were JonnyC's original values
 
130
//#define ADC_LOW             130     // When do we start ramping
 
131
//#define ADC_CRIT            120     // When do we shut the light off
 
132
 
 
133
// Values between 1 and 255 corresponding with cap voltage (0 - 1.1v) where we
 
134
// consider it a short press to move to the next mode.
 
135
// Not sure the lowest you can go before getting bad readings, but with a value
 
136
// of 70 and a 1uF cap, it seemed to switch sometimes even when waiting 10
 
137
// seconds between presses.
 
138
#define CAP_SHORT       190 // Above this is a "short" press
 
139
#define CAP_MED         100  // Above this is a "medium" press
 
140
                            // ... and anything below that is a "long" press
 
141
#define CAP_PIN         PB3
 
142
#define CAP_CHANNEL     0x03   // MUX 03 corresponds with PB3 (Star 4)
 
143
#define CAP_DIDR        ADC3D  // Digital input disable bit corresponding with PB3
 
144
 
 
145
 
 
146
/*
 
147
 * =========================================================================
 
148
 */
 
149
 
 
150
#ifdef OWN_DELAY
 
151
#include <util/delay_basic.h>
 
152
// Having own _delay_ms() saves some bytes AND adds possibility to use variables as input
 
153
static void _delay_ms(uint16_t n)
 
154
{
 
155
    // TODO: make this take tenths of a ms instead of ms,
 
156
    // for more precise timing?
 
157
    // (would probably be better than the if/else here for a special-case
 
158
    // sub-millisecond delay)
 
159
    if (n==0) { _delay_loop_2(400); }
 
160
    else {
 
161
        while(n-- > 0)
 
162
            _delay_loop_2(890);
 
163
    }
 
164
}
 
165
#else
 
166
#include <util/delay.h>
 
167
#endif
 
168
 
 
169
#include <avr/interrupt.h>
 
170
#ifdef ENABLE_WDT
 
171
#include <avr/wdt.h>
 
172
#endif
 
173
#include <avr/pgmspace.h>
 
174
#include <avr/eeprom.h>
 
175
#include <avr/sleep.h>
 
176
 
 
177
#define STAR2_PIN   PB0
 
178
#define STAR3_PIN   PB4
 
179
#define STAR4_PIN   PB3
 
180
#define PWM_PIN     PB1
 
181
#define VOLTAGE_PIN PB2
 
182
#define ADC_CHANNEL 0x01    // MUX 01 corresponds with PB2
 
183
#define ADC_DIDR    ADC1D   // Digital input disable bit corresponding with PB2
 
184
#define ADC_PRSCL   0x06    // clk/64
 
185
 
 
186
#define PWM_LVL     OCR0B   // OCR0B is the output compare register for PB1
 
187
#ifdef USE_PFM
 
188
#define CEIL_LVL    OCR0A   // OCR0A is the number of "frames" per PWM loop
 
189
#endif
 
190
 
 
191
/*
 
192
 * global variables
 
193
 */
 
194
 
 
195
// Mode storage
 
196
uint8_t eepos = 0;
 
197
uint8_t eep[32];
 
198
// change to 1 if you want on-time mode memory instead of "short-cycle" memory
 
199
// NOTE: Not currently implemented; leave it at 0.
 
200
#define memory 0
 
201
 
 
202
// Modes (hardcoded to save space, const also to save space)
 
203
const uint8_t modes[] = {
 
204
    // regular solid modes
 
205
    MODE_MOON, MODE_LOW, MODE_MED, MODE_HIGH, MODE_HIGHER, MODE_MAX,
 
206
    // dual beacon modes (this level and this level + 2)
 
207
    MODE_MOON, MODE_LOW, MODE_MED,
 
208
    // heartbeat beacon
 
209
    MODE_MAX,
 
210
    // constant-speed strobe modes (12.5 Hz, 24 Hz, 60 Hz)
 
211
    // these values represent delay in ms between 1ms flashes
 
212
    79, 41, 15,
 
213
    // variable-speed strobe mode
 
214
    MODE_MAX,
 
215
    // battery check mode
 
216
    MODE_MED,
 
217
};
 
218
// Semi-hidden modes, only accessible via a "backward" press from first mode.
 
219
// Each value is an index into the modes[] array.
 
220
const uint8_t neg_modes[] = {
 
221
    SOLID_MODES-1,           // Turbo / "impress" mode
 
222
    FIXED_STROBE_MODES-2,    // 24Hz strobe
 
223
    BATT_CHECK_MODE-1,       // Battery check
 
224
};
 
225
PROGMEM const uint8_t moon_ceilings[] = {
 
226
    (VOLTAGE_FULL+ADC_42)/2, 160,         // > 4.05V
 
227
    VOLTAGE_FULL, 60,                     // > 3.9V
 
228
    (VOLTAGE_YELLOW+VOLTAGE_FULL)/2, 30,  // > 3.75V
 
229
    VOLTAGE_YELLOW, 5,                    // > 3.6V
 
230
    0, 2,                                 // < 3.6V
 
231
};
 
232
PROGMEM const uint8_t voltage_blinks[] = {
 
233
    VOLTAGE_RED,    // 1 blink
 
234
    VOLTAGE_YELLOW, // 2 blinks
 
235
    VOLTAGE_GREEN,  // 3 blinks
 
236
    VOLTAGE_FULL,   // 4 blinks
 
237
    ADC_42,         // 5 blinks
 
238
};
 
239
volatile uint8_t mode_idx = 0;
 
240
// 1 or -1. Do we increase or decrease the idx when moving up to a higher mode?
 
241
// Was set by checking stars in the original STAR firmware, but that's removed
 
242
// to save space.
 
243
// NOTE: Only '1' is known to work; -1 will probably break and is untested.
 
244
#define mode_dir 1
 
245
 
 
246
void store_mode_idx() {  //central method for writing (with wear leveling)
 
247
    uint8_t oldpos=eepos;
 
248
    eepos=(eepos+1)&31;  //wear leveling, use next cell
 
249
    // Write the current mode
 
250
    EEARL=eepos;  EEDR=mode_idx; EECR=32+4; EECR=32+4+2;  //WRITE  //32:write only (no erase)  4:enable  2:go
 
251
    while(EECR & 2); //wait for completion
 
252
    // Erase the last mode
 
253
    EEARL=oldpos;           EECR=16+4; EECR=16+4+2;  //ERASE  //16:erase only (no write)  4:enable  2:go
 
254
}
 
255
inline void read_mode_idx() {
 
256
    eeprom_read_block(&eep, 0, 32);
 
257
    while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
 
258
    if (eepos < 32) mode_idx = eep[eepos];
 
259
    else eepos=0;
 
260
}
 
261
 
 
262
inline void next_mode() {
 
263
    mode_idx += mode_dir;
 
264
    if (mode_idx > (TOTAL_MODES - 1)) {
 
265
        // Wrap around
 
266
        mode_idx = 0;
 
267
    }
 
268
}
 
269
 
 
270
inline void prev_mode() {
 
271
    if ((0x40 > mode_idx) && (mode_idx > 0)) {
 
272
        // Regular mode: is between 1 and TOTAL_MODES
 
273
        mode_idx -= mode_dir;
 
274
    } else if ((mode_idx&0x3f) < sizeof(neg_modes)) {
 
275
        // "Negative" mode (uses 0x40 bit to indicate "negative")
 
276
        mode_idx = (mode_idx|0x40) + mode_dir;
 
277
        // FIXME: should maybe change mode group instead?
 
278
    } else {
 
279
        // Otherwise, always reset to first mode
 
280
        // (mode was too negative or otherwise out of range)
 
281
        mode_idx = 0;
 
282
    }
 
283
}
 
284
 
 
285
#ifdef ENABLE_WDT
 
286
inline void WDT_on() {
 
287
    // Setup watchdog timer to only interrupt, not reset, every 500ms.
 
288
    cli();                          // Disable interrupts
 
289
    wdt_reset();                    // Reset the WDT
 
290
    WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
291
    WDTCR = (1<<WDTIE) | (1<<WDP2) | (1<<WDP0); // Enable interrupt every 500ms
 
292
    sei();                          // Enable interrupts
 
293
}
 
294
 
 
295
inline void WDT_off()
 
296
{
 
297
    cli();                          // Disable interrupts
 
298
    wdt_reset();                    // Reset the WDT
 
299
    MCUSR &= ~(1<<WDRF);            // Clear Watchdog reset flag
 
300
    WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
301
    WDTCR = 0x00;                   // Disable WDT
 
302
    sei();                          // Enable interrupts
 
303
}
 
304
#endif
 
305
 
 
306
inline void ADC_on() {
 
307
    DIDR0 |= (1 << ADC_DIDR);                           // disable digital input on ADC pin to reduce power consumption
 
308
    ADMUX  = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
 
309
    ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
 
310
}
 
311
 
 
312
inline void ADC_off() {
 
313
    ADCSRA &= ~(1<<7); //ADC off
 
314
}
 
315
 
 
316
#ifdef VOLTAGE_MON
 
317
uint8_t get_voltage() {
 
318
    // Start conversion
 
319
    ADCSRA |= (1 << ADSC);
 
320
    // Wait for completion
 
321
    while (ADCSRA & (1 << ADSC));
 
322
    // Return the raw value, caller can decide what to do with it
 
323
    return ADCH;
 
324
}
 
325
#endif
 
326
 
 
327
#ifdef ENABLE_WDT
 
328
ISR(WDT_vect) {
 
329
    // Even if this function does nothing,
 
330
    // having the WDT enabled will at least wake up the main loop
 
331
    // and force it to do voltage monitoring plus optional step-down
 
332
    // (then again, doing a _delay_ms() instead of sleep() in the main loop
 
333
    //  can achieve the same purpose, and makes fast PWM=0 moon mode work)
 
334
    static uint8_t ticks = 0;
 
335
    if (ticks < 255) ticks++;
 
336
 
 
337
    // do a turbo step-down (NOTE: untested)
 
338
    if ((mode_idx == SOLID_MODES-TURBO_MODES) && (ticks > TURBO_TIMEOUT/100)) {
 
339
        mode_idx --;
 
340
        ticks = 0;
 
341
    }
 
342
    /*
 
343
    // FIXME: this is short-cycle memory, remove it
 
344
    // (we use off-time memory instead)
 
345
    if (ticks == WDT_TIMEOUT) {
 
346
#if memory
 
347
        store_mode_idx();
 
348
#else
 
349
        // Reset the mode to the start for next time
 
350
        uint8_t foo = mode_idx;
 
351
        mode_idx = 0;
 
352
        store_mode_idx();
 
353
        mode_idx = foo;
 
354
#endif
 
355
    }
 
356
    */
 
357
}
 
358
#endif
 
359
 
 
360
int main(void)
 
361
{
 
362
    // All ports default to input, but turn pull-up resistors on for the stars
 
363
    // (not the ADC input!  Made that mistake already)
 
364
    // (stars not used)
 
365
    //PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN) | (1 << STAR4_PIN);
 
366
 
 
367
    // Set PWM pin to output
 
368
    DDRB = (1 << PWM_PIN);
 
369
 
 
370
    // Set timer to do PWM for correct output pin and set prescaler timing
 
371
    // defaulting to PHASE allows PWM=0 to mean "off"
 
372
    // will override later for solid modes where PWM=0 means "moon"
 
373
    TCCR0A = PHASEPWM;
 
374
#ifdef USE_PFM
 
375
    // 0x08 is for variable-speed PWM
 
376
    TCCR0B = 0x08 | 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
377
    CEIL_LVL = 255; // default
 
378
#else
 
379
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
380
#endif
 
381
 
 
382
    // Determine what mode we should fire up
 
383
    // Read the last mode that was saved
 
384
    read_mode_idx();
 
385
 
 
386
    // Start up ADC for capacitor pin
 
387
    // disable digital input on ADC pin to reduce power consumption
 
388
    DIDR0 |= (1 << CAP_DIDR);
 
389
    // 1.1v reference, left-adjust, ADC3/PB3
 
390
    ADMUX  = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL;
 
391
    // enable, start, prescale
 
392
    ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;
 
393
 
 
394
    // Wait for completion
 
395
    while (ADCSRA & (1 << ADSC));
 
396
    // Start again as datasheet says first result is unreliable
 
397
    ADCSRA |= (1 << ADSC);
 
398
    // Wait for completion
 
399
    while (ADCSRA & (1 << ADSC));
 
400
    if (ADCH > CAP_SHORT) {
 
401
        // Indicates they did a short press, go to the next mode
 
402
        next_mode(); // Will handle wrap arounds
 
403
        store_mode_idx();
 
404
    } else if (ADCH > CAP_MED) {
 
405
        // User did a medium press, go back one mode
 
406
        prev_mode(); // Will handle "negative" modes and wrap-arounds
 
407
        store_mode_idx();
 
408
    } else {
 
409
        // Long press
 
410
#if memory
 
411
        // Keep the same mode
 
412
#else
 
413
        // Reset to the first mode
 
414
        mode_idx = 0;
 
415
        store_mode_idx();
 
416
#endif
 
417
    }
 
418
    // Turn off ADC
 
419
    ADC_off();
 
420
 
 
421
    // Charge up the capacitor by setting CAP_PIN to output
 
422
    DDRB  |= (1 << CAP_PIN);  // Output
 
423
    PORTB |= (1 << CAP_PIN);  // High
 
424
 
 
425
    // Turn features on or off as needed
 
426
    #ifdef VOLTAGE_MON
 
427
    ADC_on();
 
428
    #else
 
429
    //ADC_off();  // was already off
 
430
    #endif
 
431
    ACSR  |=  (1<<7); //AC off
 
432
 
 
433
#ifdef ENABLE_WDT
 
434
    // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
 
435
    // Will allow us to go idle between WDT interrupts
 
436
    // (not necessary if we're staying awake during all modes, such as to
 
437
    //  enable fast PWM=0 for moon mode)
 
438
    set_sleep_mode(SLEEP_MODE_IDLE);
 
439
 
 
440
    // enable turbo step-down timer, if there is one
 
441
    // also makes voltage monitor work, by interrupting sleep
 
442
    WDT_on();
 
443
#endif
 
444
 
 
445
    // Convert the "negative" mode into its actual (positive) mode
 
446
    //if (mode_idx < 0) {
 
447
    // The 0x40 bit is used, because I had issues getting eeprom to store
 
448
    // and retrieve negative values on a signed integer.  So, I'm setting
 
449
    // a "negative" bit manually.
 
450
    if (mode_idx & 0x40) {
 
451
        mode_idx = neg_modes[(mode_idx&0x3f)-1];
 
452
    }
 
453
 
 
454
    // Hey look, variable declarations after executable code
 
455
    // (we must not be using a strict or ancient C compiler)
 
456
    uint8_t i = 0;
 
457
    uint8_t j = 0;  // only used for strobes
 
458
#ifdef NON_WDT_TURBO
 
459
    uint16_t ontime_ticks = 0;
 
460
#endif
 
461
    uint8_t strobe_len = 0;
 
462
#ifdef VOLTAGE_MON
 
463
    uint8_t lowbatt_cnt = 0;
 
464
    uint8_t voltage;
 
465
    // Make sure voltage reading is running for later
 
466
    //ADCSRA |= (1 << ADSC);
 
467
    // ... and prime the battery check for more accurate first reading
 
468
    voltage = get_voltage();
 
469
#endif
 
470
 
 
471
    while(1) {
 
472
        if(mode_idx < SOLID_MODES) { // Just stay on at a given brightness
 
473
            TCCR0A = FASTPWM;
 
474
            PWM_LVL = modes[mode_idx];
 
475
#ifdef USE_PFM
 
476
            if (mode_idx == 0) {
 
477
                //CEIL_LVL = MOON_PFM_LVL;
 
478
                voltage = get_voltage();
 
479
                for (i=0; i<sizeof(moon_ceilings); i+=2) {
 
480
                    if(voltage > pgm_read_byte(moon_ceilings + i)) {
 
481
                        //CEIL_LVL = pgm_read_byte(moon_ceilings + i+1);
 
482
                        //CEIL_LVL = (CEIL_LVL + pgm_read_byte(moon_ceilings + i+1)) >> 2;
 
483
                        j = CEIL_LVL;
 
484
                        if (j < pgm_read_byte(moon_ceilings + i+1)) {
 
485
                            CEIL_LVL = j + 1;
 
486
                        } else {
 
487
                            CEIL_LVL = j - 1;
 
488
                        }
 
489
                        break;
 
490
                    }
 
491
                }
 
492
            } /* else {  // was already set to 255
 
493
                CEIL_LVL = 255;
 
494
            } */
 
495
#endif
 
496
            /*
 
497
            if (modes[mode_idx] < 3) {
 
498
                // use phase-correct for really low modes
 
499
                TCCR0A = 0x21; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
 
500
            }
 
501
            */
 
502
#ifdef ENABLE_WDT
 
503
            // Saves a little power, but makes fast PWM=0 moon mode not work
 
504
            sleep_mode();
 
505
#else
 
506
            _delay_ms(5); // can't sleep, fast PWM=0 will eat me
 
507
#ifdef NON_WDT_TURBO
 
508
            if (ontime_ticks < 65535) { ontime_ticks ++; }
 
509
            if ((mode_idx >= SOLID_MODES-TURBO_MODES)
 
510
                    && (ontime_ticks > TURBO_TIMEOUT)) {
 
511
                // step down one level
 
512
                mode_idx -= 1;
 
513
                // reset in case there's more than one level of turbo
 
514
                ontime_ticks = 0;
 
515
                // save, so we can short-press to go back up
 
516
                store_mode_idx();
 
517
            }
 
518
#endif
 
519
#endif
 
520
        } else if (mode_idx < DUAL_BEACON_MODES) { // two-level fast strobe pulse at about 1 Hz
 
521
            TCCR0A = FASTPWM;
 
522
            for(i=0; i<4; i++) {
 
523
                PWM_LVL = modes[mode_idx-SOLID_MODES+2];
 
524
                _delay_ms(5);
 
525
                PWM_LVL = modes[mode_idx];
 
526
                _delay_ms(65);
 
527
            }
 
528
            _delay_ms(720);
 
529
        } else if (mode_idx < SINGLE_BEACON_MODES) { // heartbeat flasher
 
530
            PWM_LVL = modes[SOLID_MODES-1];
 
531
            _delay_ms(1);
 
532
            PWM_LVL = 0;
 
533
            _delay_ms(249);
 
534
            PWM_LVL = modes[SOLID_MODES-1];
 
535
            _delay_ms(1);
 
536
            PWM_LVL = 0;
 
537
            _delay_ms(749);
 
538
        } else if (mode_idx < FIXED_STROBE_MODES) { // strobe mode, fixed-speed
 
539
#if 0
 
540
            // bigger, better-looking version
 
541
            j = modes[mode_idx]; // look up only once, saves a few bytes
 
542
            strobe_len = 1;
 
543
            if (j < 50) { strobe_len = 0; }
 
544
            // loop to make timing more consistent
 
545
            // (voltage check messes with timing)
 
546
            for(i=0; i<30; i++) {
 
547
                PWM_LVL = modes[SOLID_MODES-1];
 
548
                _delay_ms(strobe_len);
 
549
                PWM_LVL = 0;
 
550
                _delay_ms(j);
 
551
            }
 
552
#else
 
553
            // minimal version to save space
 
554
            PWM_LVL = modes[SOLID_MODES-1];
 
555
            _delay_ms(0);
 
556
            PWM_LVL = 0;
 
557
            _delay_ms(modes[mode_idx]);
 
558
#endif
 
559
        } else if (mode_idx == VARIABLE_STROBE_MODES-1) {
 
560
            // strobe mode, smoothly oscillating frequency ~10 Hz to ~24 Hz
 
561
            for(j=0; j<60; j++) {
 
562
                PWM_LVL = modes[SOLID_MODES-1];
 
563
                _delay_ms(1);
 
564
                PWM_LVL = 0;
 
565
                if (j<30) { strobe_len = j; }
 
566
                else { strobe_len = 60-j; }
 
567
                _delay_ms(2*(strobe_len+20));
 
568
            }
 
569
        } else if (mode_idx < BATT_CHECK_MODE) {
 
570
            uint8_t blinks = 0;
 
571
            //PWM_LVL = MODE_MED;  // brief flash at start of measurement
 
572
            voltage = get_voltage();
 
573
            // turn off and wait one second before showing the value
 
574
            // (or not, uses extra space)
 
575
            //PWM_LVL = 0;
 
576
            //_delay_ms(1000);
 
577
 
 
578
            // division takes too much flash space
 
579
            //voltage = (voltage-ADC_LOW) / (((ADC_42 - 15) - ADC_LOW) >> 2);
 
580
            // a table uses less space than 5 logic clauses
 
581
            for (i=0; i<sizeof(voltage_blinks); i++) {
 
582
                if (voltage > pgm_read_byte(voltage_blinks + i)) {
 
583
                    blinks ++;
 
584
                }
 
585
            }
 
586
 
 
587
            // blink up to five times to show voltage
 
588
            // (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
 
589
            for(i=0; i<blinks; i++) {
 
590
                PWM_LVL = MODE_MED;
 
591
                _delay_ms(100);
 
592
                PWM_LVL = 0;
 
593
                _delay_ms(400);
 
594
            }
 
595
            _delay_ms(2000);  // wait at least 2 seconds between readouts
 
596
        }
 
597
#ifdef VOLTAGE_MON
 
598
        if (ADCSRA & (1 << ADIF)) {  // if a voltage reading is ready
 
599
            voltage = ADCH; // get_voltage();
 
600
            // See if voltage is lower than what we were looking for
 
601
            if (voltage < ((mode_idx <= 1) ? ADC_CRIT : ADC_LOW)) {
 
602
                ++lowbatt_cnt;
 
603
            } else {
 
604
                lowbatt_cnt = 0;
 
605
            }
 
606
            // See if it's been low for a while, and maybe step down
 
607
            if (lowbatt_cnt >= 3) {
 
608
                if (mode_idx >= SOLID_MODES) {
 
609
                    // step down from blinky modes to medium
 
610
                    mode_idx = 2;
 
611
                } else if (mode_idx > 1) {
 
612
                    // step down from solid modes one at a time
 
613
                    // (ignore mode 0 because it's probably invisible anyway
 
614
                    //  if the voltage is this low)
 
615
                    mode_idx -= 1;
 
616
                } else { // Already at the lowest mode
 
617
                    mode_idx = 0;
 
618
                    // Turn off the light
 
619
                    PWM_LVL = 0;
 
620
#ifdef ENABLE_WDT
 
621
                    // Disable WDT so it doesn't wake us up
 
622
                    WDT_off();
 
623
#endif
 
624
                    // Power down as many components as possible
 
625
                    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
626
                    sleep_mode();
 
627
                }
 
628
                store_mode_idx();
 
629
                lowbatt_cnt = 0;
 
630
                // Wait at least 2 seconds before lowering the level again
 
631
                _delay_ms(2000);  // this will interrupt blinky modes
 
632
            }
 
633
 
 
634
            // Make sure conversion is running for next time through
 
635
            ADCSRA |= (1 << ADSC);
 
636
        }
 
637
#endif
 
638
    }
 
639
}