~toykeeper/flashlight-firmware/trunk

« back to all changes in this revision

Viewing changes to Tom_E/RampingIOS/RampingIOS.c

  • Committer: Selene Scriven
  • Date: 2014-07-24 06:46:30 UTC
  • Revision ID: ubuntu@toykeeper.net-20140724064630-k1fk2o5ppso2owhj
Started adding contributions from DrJones.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
//-------------------------------------------------------------------------------------
2
 
// RampingIOS - simple ramping UI for the Atmel ATTiny85/ATTiny85V
3
 
// ==========
4
 
//
5
 
// Copyright (C) 2017 Tom Elfers and Selene Scriven
6
 
//
7
 
// This program is free software: you can redistribute it and/or modify
8
 
// it under the terms of the GNU General Public License as published by
9
 
// the Free Software Foundation, either version 3 of the License, or
10
 
// (at your option) any later version.
11
 
//
12
 
// This program is distributed in the hope that it will be useful,
13
 
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
// GNU General Public License for more details.
16
 
//
17
 
// You should have received a copy of the GNU General Public License
18
 
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 
//
20
 
//
21
 
//  originally based on STAR_momentary.c v1.6 from JonnyC as of Feb 14th, 2015, Modified by Tom E
22
 
//
23
 
// Capabilities:
24
 
//  - full e-switch "only" support with a low standby power mode (low parasitic drain)
25
 
//  - lock-out feature - uses a special sequence to lock out the e-switch (prevent accidental turn-on's)
26
 
//  - tactical mode, beacon mode
27
 
//  - LVP and temperature regulation using the internal temp sensor
28
 
//  - no voltage divider resistors, very low parasitic drain
29
 
//
30
 
// Change History
31
 
// --------------
32
 
// Vers 1.1.0 2017-07-22:  (ToyKeeper)
33
 
//   - added full thermal regulation with user-calibrated ceiling
34
 
//   - added mode memory on click-from-off (default 100% 7135)
35
 
//   - made beacon use current ramp level
36
 
//   - made double-click toggle turbo (not just one-way any more)
37
 
//   - made LVP drop down in smaller steps
38
 
//   - calibrated moon to ~0.3 lm on Emisar D4-219c hardware
39
 
//   - blink when passing the 100% 7135 level, for reference
40
 
//   - fixed display of "zero" digits
41
 
//   - fixed potential eeprom corruption
42
 
//   - some code refactoring
43
 
//   - cleaned whitespace, pruned dead code
44
 
//
45
 
// Vers 1.0.1 2017-05-03:
46
 
//   - changed ramping table to start at 7135 PWM value of 10 rather than 4 (4 was too low for the IOS 7135's)
47
 
//   - delay a double-click action in ramping so that all multi-clicks don't flash MAX brightness
48
 
//   - bug fixes in tactical: all multi clicks were not available, they are now
49
 
//   - bug fixes in tactical: 4 extra blinks happened when powering up in tactical mode -- removed that
50
 
//   - bug fixes in tactical: from a lockout, ramping got resotred when tactical should have
51
 
//   - for the 10 clicks and 11th hold for thermal step-down, ensure no extra blinks
52
 
//
53
 
// Vers 1.0 2017-03-21/27:
54
 
//   - original release
55
 
//-------------------------------------------------------------------------------------
56
 
 
57
 
/*
58
 
 * ATtiny 25/45/85 Diagram
59
 
 *              ---
60
 
 *   Reset  1 -|   |- 8  VCC
61
 
 *  switch  2 -|   |- 7  Voltage ADC
62
 
 * Ind.LED  3 -|   |- 6  FET PWM
63
 
 *     GND  4 -|   |- 5  7135 PWM
64
 
 *              ---
65
 
 *
66
 
 * FUSES
67
 
 *  See this for fuse settings:
68
 
 *    http://www.engbedded.com/cgi-bin/fcx.cgi?P_PREV=ATtiny85&P=ATtiny85&M_LOW_0x3F=0x22&M_HIGH_0x07=0x07&M_HIGH_0x20=0x00&B_SPIEN=P&B_SUT0=P&B_CKSEL3=P&B_CKSEL2=P&B_CKSEL0=P&V_LOW=E2&V_HIGH=DE&V_EXTENDED=FF&O_HEX=Apply+values
69
 
 *
70
 
 *  Following is the command options for the fuses used for BOD enabled (Brown-Out Detection), recommended:
71
 
 *      -Ulfuse:w:0xe2:m -Uhfuse:w:0xde:m -Uefuse:w:0xff:m
72
 
 *    or BOD disabled:
73
 
 *      -Ulfuse:w:0xe2:m -Uhfuse:w:0xdf:m -Uefuse:w:0xff:m
74
 
 *
75
 
 *      Low: 0xE2 - 8 MHz CPU without a divider, 15.67kHz phase-correct PWM
76
 
 *     High: 0xDE - enable serial prog/dnld, BOD enabled (or 0xDF for no BOD)
77
 
 *    Extra: 0xFF - self programming not enabled
78
 
 *
79
 
 *  --> Note: BOD enabled fixes lockups intermittently occurring on power up fluctuations, but adds slightly to parasitic drain
80
 
 *
81
 
 * STARS  (not used)
82
 
 *
83
 
 * VOLTAGE
84
 
 *      Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
85
 
 *
86
 
 *
87
 
 */
88
 
 
89
 
#define ATTINY 85
90
 
#include "tk-attiny.h"
91
 
 
92
 
#define byte uint8_t
93
 
#define word uint16_t
94
 
 
95
 
#define PHASE 0xA1          // phase-correct PWM both channels
96
 
#define FAST 0xA3           // fast PWM both channels
97
 
 
98
 
// Ignore a spurious warning, we did the cast on purpose
99
 
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
100
 
 
101
 
//------------- Driver Board Settings -------------------------------------
102
 
//
103
 
#define VOLTAGE_MON         // Comment out to disable - ramp down and eventual shutoff when battery is low
104
 
//#define VOLT_MON_PIN7       // Use voltage divider on pin7 instead of raw voltage on VCC pin.
105
 
#define VOLT_MON_INTREF     // uses the 1.1V internal reference
106
 
 
107
 
//
108
 
//------------- End Driver Board Settings ---------------------------------
109
 
 
110
 
 
111
 
//-------------------------------------------------------------------------
112
 
//                  Settings to modify per driver
113
 
//-------------------------------------------------------------------------
114
 
//-------------------------------------------------------------------------
115
 
 
116
 
// v1.1 (in bits: mmm.nnnnn, mm=major @, nnnnn=minor#)
117
 
#define FIRMWARE_VERS ((1<<5) | 1)
118
 
 
119
 
// Choose one or the other below. Neither set will be normal power-up in OFF state
120
 
//#define STARTUP_LIGHT_ON        // powerup/startup at max level
121
 
#define STARTUP_2BLINKS         // enables the powerup/startup two blinks
122
 
 
123
 
#define USE_LVP                 // enables Low Voltage Protection
124
 
 
125
 
// Temperature monitoring setup
126
 
//-----------------------------
127
 
// Temperature Calibration Offset - tiny85 chips vary per-chip
128
 
#define TEMP_CAL_OFFSET (0)     // err on the safe side for factory production purposes
129
 
//   -12  is about right on the DEL DDm-L4 board in the UranusFire C818 light
130
 
//   -11  On the TA22 board in SupFire M2-Z, it's bout 11-12C too high,reads 35C at room temp, 23C=73.4F
131
 
//   -8   For the Manker U11 - at -11, reads 18C at 71F room temp (22C)
132
 
//   -2   For the Lumintop SD26 - at -2, reading a solid 19C-20C (66.2F-68F for 67F room temp)
133
 
 
134
 
#define MAX_THERM_CEIL 70
135
 
#define DEFAULT_THERM_CEIL 45
136
 
#define TEMP_CRITICAL (DEFAULT_THERM_CEIL)      // Temperature in C to step the light down
137
 
// (40C=104F, 45C=113F, 50C=122F, 55C=131F, 60C=140F)
138
 
// use 50C for smaller size hosts, or a more conservative level (SD26, U11, etc.)
139
 
// use 55C for larger size hosts, maybe C8 and above, or for a more aggressive setting
140
 
 
141
 
#define TEMP_ADJ_PERIOD 32     // Thermal regulation frequency: 0.5 sec(s) (in 16 msec ticks)
142
 
//-----------------------------
143
 
 
144
 
//#define USING_3807135_BANK    // (default OFF) sets up ramping for 380 mA 7135's instead of a FET
145
 
 
146
 
#define RAMPING_REVERSE         // (default ON) reverses ramping direction for every click&hold
147
 
 
148
 
#define RAMP_SWITCH_TIMEOUT 75  // make the up/dn ramp switch timeout 1.2 secs (same as IDLE_TIME)
149
 
 
150
 
//#define ADV_RAMP_OPTIONS        // In ramping, enables "mode set" additional method for lock-out and battery voltage display, comment out to disable
151
 
 
152
 
#define TRIPLE_CLICK_BATT       // enable a triple-click to display Battery status
153
 
 
154
 
#define SHORT_CLICK_DUR 18      // Short click max duration - for 0.288 secs
155
 
#define RAMP_MOON_PAUSE 23      // this results in a 0.368 sec delay, paused in moon mode
156
 
 
157
 
#define D1 2                    // Drop over reverse polarity protection diode = 0.2 V
158
 
 
159
 
// ----- 2/14 TE: One-Click Turn OFF option --------------------------------------------
160
 
#define IDLE_TIME         75    // make the time-out 1.2 seconds (Comment out to disable)
161
 
 
162
 
 
163
 
// Switch handling:
164
 
#define LONG_PRESS_DUR    24    // Prev Mode time - long press, 24=0.384s (1/3s: too fast, 0.5s: too slow)
165
 
#define XLONG_PRESS_DUR   68    // strobe mode entry hold time - 68=1.09s (any slower it can happen unintentionally too much)
166
 
#define CONFIG_ENTER_DUR 160    // Config mode entry hold time - 160=2.5s, 128=2s
167
 
 
168
 
#define DBL_CLICK_TICKS   15    //  fast click time for enable/disable of Lock-Out, batt check,
169
 
                                // and double/triple click timing (15=0.240s, was 14=0.224s)
170
 
 
171
 
#ifdef VOLT_MON_PIN7
172
 
// 1S cell configuration:
173
 
#define BATT_LOW   30  // Cell voltage to step light down = 3.0 V
174
 
#define BATT_CRIT  28  // Cell voltage to shut the light off = 2.8 V
175
 
// measure values for a full and empty battery, then plug them in
176
 
#define VOLTS_MAX  42  // 4.2V
177
 
#define ADC_MAX   172
178
 
#define VOLTS_MIN  28  // 2.8V
179
 
#define ADC_MIN   116
180
 
// 2S cell configuration:
181
 
//#define BATT_LOW   61  // Cell voltage to step light down = 6.1 V
182
 
//#define BATT_CRIT  59  // Cell voltage to shut the light off = 5.9 V
183
 
// measure values for a full and empty battery, then plug them in
184
 
//#define VOLTS_MAX  84  // 8.4V
185
 
//#define ADC_MAX   225
186
 
//#define VOLTS_MIN  50  // 5.0V
187
 
//#define ADC_MIN   134
188
 
#else
189
 
#define BATT_LOW          30    // Cell voltage to step light down = 3.0 V
190
 
#define BATT_CRIT         28    // Cell voltage to shut the light off = 2.8 V
191
 
#endif
192
 
 
193
 
#define ADC_DELAY        312    // Delay in ticks between low-batt ramp-downs (312=5secs, 250=4secs, was: 188 ~= 3s)
194
 
 
195
 
// output to use for blinks on battery check mode (primary PWM level, alt PWM level)
196
 
// Use 20,0 for a single-channel driver or 0,40 for a two-channel driver
197
 
#define BLINK_BRIGHTNESS  (RAMP_SIZE/5)
198
 
#define BEACON_BRIGHTNESS (RAMP_SIZE/4)
199
 
 
200
 
// States (in modeState):
201
 
#define RAMPING_ST      (0)
202
 
#define TACTICAL_ST     (1)
203
 
#define LOCKOUT_ST      (2)
204
 
#define BEACON_ST       (3)
205
 
#define THERMAL_REG_ST  (4)
206
 
#define THERMAL_CAL_ST  (5)
207
 
#define BATT_READ_ST    (6)
208
 
#define TEMP_READ_ST    (7)
209
 
 
210
 
 
211
 
#define FIRM_VERS_MODE  81
212
 
 
213
 
//-------------------------------------------------------------------------
214
 
//-------------------------------------------------------------------------
215
 
#include <avr/pgmspace.h>
216
 
#include <avr/io.h>
217
 
//#include <util/delay.h>
218
 
#include <avr/interrupt.h>
219
 
#include <avr/wdt.h>
220
 
#include <avr/eeprom.h>
221
 
#include <avr/sleep.h>
222
 
 
223
 
#define OWN_DELAY           // Don't use stock delay functions.
224
 
#define USE_DELAY_S         // Also use _delay_s(), not just _delay_ms()
225
 
 
226
 
#include "tk-delay.h"
227
 
 
228
 
// MCU I/O pin assignments (most are now in tk-attiny.h):
229
 
#define SWITCH_PIN PB3      // Star 4,  MCU pin #2 - pin the switch is connected to
230
 
 
231
 
#define ADCMUX_TEMP 0b10001111  // ADCMUX register setup to read temperature
232
 
#ifdef VOLT_MON_PIN7
233
 
// ADCMUX register setup to read PIN7 referenced divider
234
 
//#define ADC_CHANNEL 0x01  // MUX 01 corresponds with PB2 (pin 7)
235
 
//#define ADCMUX_VCC    ((1 << V_REF) | (0 << ADLAR) | ADC_CHANNEL)
236
 
#define ADCMUX_VCC      0b10000001
237
 
#else
238
 
#define ADCMUX_VCC  0b00001100  // ADCMUX register setup to read Vbg referenced to Vcc
239
 
#endif
240
 
 
241
 
#define DEBOUNCE_BOTH          // Comment out if you don't want to debounce the PRESS along with the RELEASE
242
 
                               // PRESS debounce is only needed in special cases where the switch can experience errant signals
243
 
#define DB_PRES_DUR 0b00000001 // time before we consider the switch pressed (after first realizing it was pressed)
244
 
#define DB_REL_DUR  0b00001111 // time before we consider the switch released
245
 
                               // each bit of 1 from the right equals 16ms, so 0x0f = 64ms
246
 
 
247
 
 
248
 
//---------------------------------------------------------------------------------------
249
 
#define RAMP_SIZE  sizeof(ramp_7135)
250
 
#define TURBO_DROP_MIN 115
251
 
// min level in ramping the turbo timeout will engage,
252
 
//    level 115 = 106 PWM, this is ~43%
253
 
#define TURBO_DROP_SET 102
254
 
// the level turbo timeout will set,
255
 
//    level 102 = 71 PWM, this is ~32%
256
 
 
257
 
//----------------------------------------------------
258
 
// Ramping Mode Levels, 150 total entries (2.4 secs)
259
 
//
260
 
// For FET+1: FET and one 350 mA 7135 (tested/verified):
261
 
//    level_calc.py 2 150 7135 10 0.3 150 FET 1 1 1500
262
 
//
263
 
// Note: PWM value of 4 should be ~5.5 mA, but a value of 10 actually measures ~5 ma
264
 
//----------------------------------------------------
265
 
 
266
 
PROGMEM const byte ramp_7135[] = {
267
 
    // original ramp, 1.7 lm to 133 lm
268
 
    //10,10,10,11,11,11,12,12,            13,13,14,15,15,16,17,18,
269
 
    //19,21,22,23,25,27,28,30,            32,34,37,39,42,44,47,50,
270
 
    //53,56,60,63,67,71,75,79,            84,88,93,98,103,108,114,120,
271
 
    //126,132,138,145,152,159,166,174,    182,190,198,206,215,224,234,243,
272
 
    //253,255,255,255,255,255,255,255,    255,255,255,255,255,255,255,255,
273
 
    // lower lows (0.2-0.4 lm moon), exact 100% 7135 at level=65
274
 
    4,4,5,5,5,6,6,7,                    8,8,9,10,11,12,13,14,
275
 
    15,17,18,20,22,23,25,27,            30,32,34,37,40,42,45,48,
276
 
    52,55,59,62,66,70,74,79,            83,88,93,98,104,109,115,121,
277
 
    127,133,140,146,153,160,168,175,    183,191,200,208,217,226,236,245,
278
 
    255,255,255,255,255,255,255,255,    255,255,255,255,255,255,255,255,
279
 
    255,255,255,255,255,255,255,255,    255,255,255,255,255,255,255,255,
280
 
    255,255,255,255,255,255,255,255,    255,255,255,255,255,255,255,255,
281
 
    255,255,255,255,255,255,255,255,    255,255,255,255,255,255,255,255,
282
 
    255,255,255,255,255,255,255,255,    255,255,255,255,255,255,255,255,
283
 
    255,255,255,255,255,0
284
 
};
285
 
PROGMEM const byte ramp_FET[]  = {
286
 
    0,0,0,0,0,0,0,0,                    0,0,0,0,0,0,0,0,
287
 
    0,0,0,0,0,0,0,0,                    0,0,0,0,0,0,0,0,
288
 
    0,0,0,0,0,0,0,0,                    0,0,0,0,0,0,0,0,
289
 
    0,0,0,0,0,0,0,0,                    0,0,0,0,0,0,0,0,
290
 
 
291
 
// 100%
292
 
    0,2,3,4,5,7,8,9,                    11,12,14,15,17,18,20,22,
293
 
    23,25,27,29,30,32,34,36,            38,40,42,44,47,49,51,53,
294
 
    56,58,60,63,66,68,71,73,            76,79,82,85,87,90,93,96,
295
 
    100,103,106,109,113,116,119,123,    126,130,134,137,141,145,149,153,
296
 
    157,161,165,169,173,178,182,186,    191,196,200,205,210,214,219,224,
297
 
    229,234,239,244,250,255
298
 
 
299
 
// 95%
300
 
//    0,1,2,3,4,6,7,8,10,11,13,14,16,17,19,20,21,23,25,27,28,30,32,34,36,38,39,41,44,46,48,50,53,55,57,59,62,64,67,69,72,75,77,80,82,85,88,91,95,97,100,103,107,110,113,116,119,123,127,130,133,137,141,145,149,152,156,160,164,169,172,176,181,186,190,194,199,203,208,212,217,222,227,231,237,242
301
 
 
302
 
// 90%
303
 
//    0,1,2,3,4,6,7,8,9,10,12,13,15,16,18,19,20,22,24,26,27,28,30,32,34,36,37,39,42,44,45,47,50,52,54,56,59,61,63,65,68,71,73,76,78,81,83,86,90,92,95,98,101,104,107,110,113,117,120,123,126,130,134,137,141,144,148,152,155,160,163,167,171,176,180,184,189,192,197,201,206,210,215,219,225,229
304
 
 
305
 
// 85%
306
 
//    0,1,2,3,4,5,6,7,9,10,11,12,14,15,17,18,19,21,22,24,25,27,28,30,32,34,35,37,39,41,43,45,47,49,51,53,56,57,60,62,64,67,69,72,73,76,79,81,85,87,90,92,96,98,101,104,107,110,113,116,119,123,126,130,133,136,140,143,147,151,154,158,162,166,170,174,178,181,186,190,194,198,203,207,212,216
307
 
 
308
 
// 80%
309
 
//    0,1,2,3,4,5,6,7,8,9,11,12,13,14,16,17,18,20,21,23,24,25,27,28,30,32,33,35,37,39,40,42,44,46,48,50,52,54,56,58,60,63,65,68,69,72,74,76,80,82,84,87,90,92,95,98,100,104,107,109,112,116,119,122,125,128,132,135,138,142,145,148,152,156,160,164,168,171,175,179,183,187,191,195,200,204
310
 
 
311
 
// 75%
312
 
//    0,1,2,3,3,5,6,6,8,9,10,11,12,13,15,16,17,18,20,21,22,24,25,27,28,30,31,33,35,36,38,39,42,43,45,47,49,51,53,54,57,59,61,63,65,67,69,72,75,77,79,81,84,87,89,92,94,97,100,102,105,108,111,114,117,120,123,126,129,133,136,139,143,147,150,153,157,160,164,168,171,175,179,183,187,191
313
 
 
314
 
// 70%
315
 
//    0,1,2,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,20,21,22,23,25,26,28,29,30,32,34,35,37,39,40,42,44,46,47,49,51,53,55,57,59,60,62,65,67,70,72,74,76,79,81,83,86,88,91,93,95,98,101,104,107,109,112,115,118,121,124,127,130,133,137,140,143,147,149,153,156,160,163,167,170,175,178
316
 
 
317
 
// 66.66%
318
 
//    0,1,1,2,3,4,5,5,7,7,9,9,11,11,13,14,15,16,17,19,19,21,22,23,25,26,27,29,31,32,33,35,37,38,39,41,43,45,47,48,50,52,54,56,57,59,61,63,66,68,70,72,75,77,79,81,83,86,89,91,93,96,99,101,104,107,109,112,115,118,121,123,127,130,133,136,139,142,145,149,152,155,159,162,166,169
319
 
};
320
 
 
321
 
#if 0       // ------------------   Do Not Use   --------------------
322
 
// level_calc.py 2 150 7135 4 0.3 150 FET 1 1 1500
323
 
PROGMEM const byte ramp_7135[] = {
324
 
    4,4,4,5,5,5,6,6,                  7,7,8,9,10,10,11,13,
325
 
    14,15,16,18,19,21,23,25,          27,29,31,34,36,39,42,45,
326
 
    48,51,55,58,62,66,70,75,          79,84,89,94,99,105,111,116,
327
 
    123,129,136,142,149,157,164,172,  180,188,197,205,214,224,233,243,  // 49-64
328
 
    253,255,255,255,255,255,255,255,  255,255,255,255,255,255,255,255,
329
 
    255,255,255,255,255,255,255,255,  255,255,255,255,255,255,255,255,
330
 
    255,255,255,255,255,255,255,255,  255,255,255,255,255,255,255,255,
331
 
    255,255,255,255,255,255,255,255,  255,255,255,255,255,255,255,255,
332
 
    255,255,255,255,255,255,255,255,  255,255,255,255,255,255,255,255,
333
 
    255,255,255,255,255,0                                               // 145-150
334
 
};
335
 
PROGMEM const byte ramp_FET[]  = {
336
 
    0,0,0,0,0,0,0,0,                  0,0,0,0,0,0,0,0,
337
 
    0,0,0,0,0,0,0,0,                  0,0,0,0,0,0,0,0,
338
 
    0,0,0,0,0,0,0,0,                  0,0,0,0,0,0,0,0,
339
 
    0,0,0,0,0,0,0,0,                  0,0,0,0,0,0,0,0,                  // 49-64
340
 
    0,2,3,4,5,7,8,9,                  11,12,14,15,17,18,20,22,          // 65
341
 
    23,25,27,29,30,32,34,36,          38,40,42,44,47,49,51,53,          // 81
342
 
    56,58,60,63,66,68,71,73,          76,79,82,85,87,90,93,96,          // 97
343
 
    100,103,106,109,113,116,119,123,  126,130,134,137,141,145,149,153,  // 113-128
344
 
    157,161,165,169,173,178,182,186,  191,196,200,205,210,214,219,224,  // 129-144
345
 
    229,234,239,244,250,255                                             // 145-150
346
 
};
347
 
#endif  // -----------------------------------------------------
348
 
 
349
 
//---------------------------------------------------------------------------------------
350
 
 
351
 
// Turbo timeout values, 16 msecs each: 30secs, 60 secs, 90 secs, 120 secs, 3 mins, 5 mins, 10 mins
352
 
// unused
353
 
//PROGMEM const word turboTimeOutVals[] = {0, 1875, 3750, 5625, 7500, 11250, 18750, 37500};
354
 
 
355
 
 
356
 
//----------------------------------------------------------------
357
 
// Config Settings via UI, with default values:
358
 
//----------------------------------------------------------------
359
 
byte tacticalSet = 0;       // 1: system is in Tactical Mode
360
 
//byte tempAdjEnable = 1;     // 1: enable active temperature adjustments, 0: disable
361
 
// thermal regulation ceiling
362
 
byte tempCeil = DEFAULT_THERM_CEIL;
363
 
// unused
364
 
//byte turboTimeoutMode = 0;  // 0=disabled, 1=30s,2=60s,3=90s, 4=120s, 5=3min,6=5min,7=10min (2 mins is good for production)
365
 
 
366
 
//----------------------------------------------------------------
367
 
// Global state options
368
 
//----------------------------------------------------------------
369
 
 
370
 
// Ramping :
371
 
#define MAX_RAMP_LEVEL (RAMP_SIZE)
372
 
#define MAX_7135_LEVEL 65
373
 
 
374
 
volatile byte rampingLevel = 0;     // 0=OFF, 1..MAX_RAMP_LEVEL is the ramping table index, 255=moon mode
375
 
volatile byte TargetLevel = 0;      // what the user requested (may be higher than actual level, due to thermal regulation)
376
 
volatile byte ActualLevel;          // current brightness (may differ from rampingLevel or TargetLevel)
377
 
byte memorizedLevel = 65;           // mode memory (ish, not saved across battery changes)
378
 
byte preTurboLevel = 65;            // only used to return from double-click turbo
379
 
byte rampState = 0;                 // 0=OFF, 1=in lowest mode (moon) delay, 2=ramping Up, 3=Ramping Down, 4=ramping completed (Up or Dn)
380
 
byte rampLastDirState = 0;          // last ramping state's direction: 2=up, 3=down
381
 
byte dontToggleDir = 0;             // flag to not allow the ramping direction to switch//toggle
382
 
//volatile byte byDelayRamping = 0;   // when the ramping needs to be temporarily disabled
383
 
byte rampPauseCntDn;                // count down timer for ramping support
384
 
 
385
 
 
386
 
// State and count vars:
387
 
 
388
 
volatile byte modeState;            // Current operating state: 0=Ramping, 1=Tactical, 2=Lock Out, 3=Beacon mode, 4=Battery check, 5=Temp check
389
 
 
390
 
word wPressDuration = 0;
391
 
 
392
 
volatile byte fastClicks = 0;       // fast click counts for dbl-click, triple-click, etc.
393
 
 
394
 
volatile word wIdleTicks = 0;
395
 
 
396
 
volatile byte LowBattSignal = 0;    // a low battery has been detected - trigger/signal it
397
 
 
398
 
byte byStartupDelayTime;            // Used to delay the WDT from doing anything for a short period at power up
399
 
 
400
 
// Keep track of cell voltage in ISRs, 10-bit resolution required for impedance check
401
 
volatile int16_t voltageTenths = 0; // in volts * 10
402
 
volatile int16_t temperature = 0;   // in degC
403
 
volatile byte adc_step = 0;         // steps 0, 1 read voltage, steps 2, 3 reads temperature - only use steps 1, 3 values
404
 
 
405
 
volatile word wTempTicks = 0;       // set to TEMP_ADJ_PERIOD, and decremented
406
 
 
407
 
byte b10Clicks = 0;                 // special 10 click state for turning thermal reg ON/OFF
408
 
 
409
 
// Configuration settings storage in EEPROM
410
 
word eepos = 0; // (0..n) position of mode/settings stored in EEPROM (128/256/512)
411
 
 
412
 
 
413
 
/**************************************************************************************
414
 
* SetOutput - sets the PWM output value directly
415
 
* =========
416
 
* Don't use this directly; use SetLevel or SetActualLevel instead.
417
 
**************************************************************************************/
418
 
// TODO: add support for 1-channel and 3-channel drivers
419
 
void SetOutput(byte pwm1, byte pwm2)
420
 
{
421
 
    PWM_LVL = pwm1;
422
 
    ALT_PWM_LVL = pwm2;
423
 
}
424
 
 
425
 
/**************************************************************************************
426
 
* SetActualLevel - uses the ramping levels to set the PWM output
427
 
* ========      (0 is OFF, 1..RAMP_SIZE is the ramping index level)
428
 
**************************************************************************************/
429
 
void SetActualLevel(byte level)
430
 
{
431
 
    ActualLevel = level;
432
 
    if (level == 0)
433
 
    {
434
 
        SetOutput(0,0);
435
 
    }
436
 
    else
437
 
    {
438
 
        level -= 1;  // make it 0 based
439
 
        SetOutput(pgm_read_byte(ramp_FET  + level), pgm_read_byte(ramp_7135 + level));
440
 
    }
441
 
}
442
 
 
443
 
// Use this one if you also want to adjust the maximum brightness it'll step up to when cold.
444
 
void SetLevel(byte level)
445
 
{
446
 
    TargetLevel = level;
447
 
    SetActualLevel(level);
448
 
}
449
 
 
450
 
#define SetLevelSoft SetLevel
451
 
#ifndef SetLevelSoft
452
 
// FIXME: this interferes with handling double-clicks
453
 
// FIXME: looks bad when turning off, if ramp has repeated values at the bottom
454
 
// Like SetLevel, but with a ramp-up / ramp-down transition
455
 
void SetLevelSoft(byte level)
456
 
{
457
 
    uint8_t target_level = level;
458
 
    int8_t shift_amount;
459
 
    int16_t diff;
460
 
    do {
461
 
        diff = target_level - ActualLevel;
462
 
        shift_amount = (diff >> 2) | (diff!=0);
463
 
        ActualLevel += shift_amount;
464
 
        SetLevel(ActualLevel);
465
 
        _delay_ms(RAMP_SIZE/8);  // fast ramp
466
 
    } while (target_level != ActualLevel);
467
 
}
468
 
#endif
469
 
 
470
 
/**************************************************************************************
471
 
* Strobe
472
 
* ======
473
 
**************************************************************************************/
474
 
void Strobe(byte ontime, byte offtime)
475
 
{
476
 
    SetLevel(MAX_RAMP_LEVEL);
477
 
    _delay_ms(ontime);
478
 
    SetLevel(0);
479
 
    _delay_ms(offtime);
480
 
}
481
 
 
482
 
/**************************************************************************************
483
 
* Blink - do a # of blinks with a speed in msecs
484
 
* =====
485
 
**************************************************************************************/
486
 
void Blink(byte val, word speed)
487
 
{
488
 
    for (; val>0; val--)
489
 
    {
490
 
        SetLevel(BLINK_BRIGHTNESS);
491
 
        _delay_ms(speed);
492
 
 
493
 
        SetLevel(0);
494
 
        _delay_ms(speed<<2);    // 4X delay OFF
495
 
    }
496
 
}
497
 
 
498
 
/**************************************************************************************
499
 
* delay_unless_modeState_changed - like _delay_ms(), but aborts on mode change
500
 
* =========
501
 
* ms       : milliseconds to delay
502
 
* expected : abort if/when modeState doesn't match this value
503
 
**************************************************************************************/
504
 
uint8_t delay_unless_modeState_changed(uint16_t ms, uint8_t expected)
505
 
{
506
 
    //uint8_t expected = modeState;  // automatic mode; buggy
507
 
    while (ms > 0) {
508
 
        _delay_ms(1);
509
 
        ms --;
510
 
        if (modeState != expected) return 0;
511
 
    }
512
 
    return 1;
513
 
}
514
 
 
515
 
/**************************************************************************************
516
 
* DigitBlink - do a # of blinks with a speed in msecs
517
 
* =========
518
 
**************************************************************************************/
519
 
byte DigitBlink(byte val, byte blinkModeState)
520
 
{
521
 
    if (modeState != blinkModeState)    // if the mode changed, bail out
522
 
        return 0;
523
 
 
524
 
    // zeroes get only a short blink
525
 
    if (val == 0) {
526
 
        SetLevel(BLINK_BRIGHTNESS);
527
 
        delay_unless_modeState_changed(8, blinkModeState);
528
 
        SetLevel(0);
529
 
        if(!delay_unless_modeState_changed(375, blinkModeState)) return 0;
530
 
    }
531
 
 
532
 
    for (; val>0; val--)
533
 
    {
534
 
        SetLevel(BLINK_BRIGHTNESS);
535
 
        delay_unless_modeState_changed(250, blinkModeState);
536
 
        SetLevel(0);
537
 
        if(!delay_unless_modeState_changed(375, blinkModeState)) return 0;
538
 
    }
539
 
    return 1;   // Ok, not terminated
540
 
}
541
 
 
542
 
/**************************************************************************************
543
 
* BlinkOutNumber - blinks out a 2 digit #
544
 
* ==============
545
 
**************************************************************************************/
546
 
void BlinkOutNumber(byte num, byte blinkModeState)
547
 
{
548
 
    // FIXME: handle 1-digit and 3-digit numbers
549
 
    // FIXME: don't use "/" or "%"; this MCU doesn't have those
550
 
    if (!DigitBlink(num / 10, blinkModeState))
551
 
        return;
552
 
    _delay_ms(800);
553
 
    if (!DigitBlink(num % 10, blinkModeState))
554
 
        return;
555
 
    _delay_ms(2000);
556
 
}
557
 
 
558
 
/**************************************************************************************
559
 
* BlinkLVP - blinks the specified time for use by LVP
560
 
* ========
561
 
*  Supports both ramping and mode set modes.
562
 
**************************************************************************************/
563
 
void BlinkLVP(byte BlinkCnt, byte level)
564
 
{
565
 
    int nMsecs = 200;
566
 
    if (BlinkCnt > 5)
567
 
        nMsecs = 125;
568
 
 
569
 
    // Flash 'n' times before lowering
570
 
    while (BlinkCnt-- > 0)
571
 
    {
572
 
        SetLevel(0);
573
 
        _delay_ms(nMsecs);
574
 
        SetLevel(level);
575
 
        _delay_ms(nMsecs<<1);
576
 
    }
577
 
}
578
 
 
579
 
/**************************************************************************************
580
 
* IsPressed - debounce the switch release, not the switch press
581
 
* ==========
582
 
**************************************************************************************/
583
 
// Debounce switch press value
584
 
#ifdef DEBOUNCE_BOTH
585
 
int IsPressed()
586
 
{
587
 
    static byte pressed = 0;
588
 
    // Keep track of last switch values polled
589
 
    static byte buffer = 0x00;
590
 
    // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
591
 
    buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
592
 
 
593
 
    if (pressed) {
594
 
        // Need to look for a release indicator by seeing if the last switch status has been 0 for n number of polls
595
 
        pressed = (buffer & DB_REL_DUR);
596
 
    } else {
597
 
        // Need to look for pressed indicator by seeing if the last switch status was 1 for n number of polls
598
 
        pressed = ((buffer & DB_PRES_DUR) == DB_PRES_DUR);
599
 
    }
600
 
 
601
 
    return pressed;
602
 
}
603
 
#else
604
 
static int IsPressed()
605
 
{
606
 
    // Keep track of last switch values polled
607
 
    static byte buffer = 0x00;
608
 
    // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
609
 
    buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
610
 
 
611
 
    return (buffer & DB_REL_DUR);
612
 
}
613
 
#endif
614
 
 
615
 
 
616
 
/**************************************************************************************
617
 
* PCINT_on - Enable pin change interrupts
618
 
* ========
619
 
**************************************************************************************/
620
 
inline void PCINT_on()
621
 
{
622
 
    // Enable pin change interrupts
623
 
    GIMSK |= (1 << PCIE);
624
 
}
625
 
 
626
 
/**************************************************************************************
627
 
* PCINT_off - Disable pin change interrupts
628
 
* =========
629
 
**************************************************************************************/
630
 
inline void PCINT_off()
631
 
{
632
 
    // Disable pin change interrupts
633
 
    GIMSK &= ~(1 << PCIE);
634
 
}
635
 
 
636
 
// Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
637
 
// All logic of what to do when we wake up will be handled in the main loop.
638
 
EMPTY_INTERRUPT(PCINT0_vect);
639
 
 
640
 
/**************************************************************************************
641
 
* WDT_on - Setup watchdog timer to only interrupt, not reset, every 16ms
642
 
* ======
643
 
**************************************************************************************/
644
 
void WDT_on()
645
 
{
646
 
    // Setup watchdog timer to only interrupt, not reset, every 16ms.
647
 
    cli();                          // Disable interrupts
648
 
    wdt_reset();                    // Reset the WDT
649
 
    WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
650
 
    WDTCR = (1<<WDIE);              // Enable interrupt every 16ms (was 1<<WDTIE)
651
 
    sei();                          // Enable interrupts
652
 
}
653
 
 
654
 
/**************************************************************************************
655
 
* WDT_off - turn off the WatchDog timer
656
 
* =======
657
 
**************************************************************************************/
658
 
void WDT_off()
659
 
{
660
 
    wdt_reset();                    // Reset the WDT
661
 
    MCUSR &= ~(1<<WDRF);            // Clear Watchdog reset flag
662
 
    WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
663
 
    WDTCR = 0x00;                   // Disable WDT
664
 
}
665
 
 
666
 
/**************************************************************************************
667
 
* ADC_on - Turn the AtoD Converter ON
668
 
* ======
669
 
**************************************************************************************/
670
 
void ADC_on()
671
 
{
672
 
    // Turn ADC on (13 CLKs required for conversion, go max 200 kHz for 10-bit resolution)
673
 
    ADMUX  = ADCMUX_VCC;                         // 1.1 V reference, not left-adjust, Vbg
674
 
    DIDR0 |= (1 << ADC1D);                       // disable digital input on ADC1 pin to reduce power consumption
675
 
    ADCSRA = (1 << ADEN ) | (1 << ADSC ) | 0x07; // enable, start, ADC clock prescale = 128 for 62.5 kHz
676
 
}
677
 
 
678
 
/**************************************************************************************
679
 
* ADC_off - Turn the AtoD Converter OFF
680
 
* =======
681
 
**************************************************************************************/
682
 
inline void ADC_off()
683
 
{
684
 
    ADCSRA &= ~(1<<ADEN); //ADC off
685
 
}
686
 
 
687
 
/**************************************************************************************
688
 
* SleepUntilSwitchPress - only called with the light OFF
689
 
* =====================
690
 
**************************************************************************************/
691
 
inline void SleepUntilSwitchPress()
692
 
{
693
 
    // This routine takes up a lot of program memory :(
694
 
 
695
 
    cli();          // Disable interrupts
696
 
 
697
 
    //  Turn the WDT off so it doesn't wake us from sleep. Will also ensure interrupts
698
 
    // are on or we will never wake up.
699
 
    WDT_off();
700
 
 
701
 
    ADC_off();      // Save more power -- turn the AtoD OFF
702
 
 
703
 
    // Need to reset press duration since a button release wasn't recorded
704
 
    wPressDuration = 0;
705
 
 
706
 
    sei();          // Enable interrupts
707
 
 
708
 
    //  Enable a pin change interrupt to wake us up. However, we have to make sure the switch
709
 
    // is released otherwise we will wake when the user releases the switch
710
 
    while (IsPressed()) {
711
 
        _delay_ms(16);
712
 
    }
713
 
 
714
 
    PCINT_on();
715
 
 
716
 
    //-----------------------------------------
717
 
    sleep_enable();
718
 
    sleep_bod_disable();  // try to disable Brown-Out Detection to reduce parasitic drain
719
 
    sleep_cpu();          // go to sleepy beddy bye
720
 
    sleep_disable();
721
 
    //-----------------------------------------
722
 
 
723
 
    // Hey, to get here, someone must have pressed the switch!!
724
 
 
725
 
    PCINT_off();    // Disable pin change interrupt because it's only used to wake us up
726
 
 
727
 
    ADC_on();       // Turn the AtoD back ON
728
 
 
729
 
    WDT_on();       // Turn the WDT back on to check for switch presses
730
 
 
731
 
}   // Go back to main program
732
 
 
733
 
/**************************************************************************************
734
 
* LoadConfig - gets the configuration settings from EEPROM
735
 
* ==========
736
 
*  config1 - 1st byte of stored configuration settings:
737
 
*   bits   0: tactical mode
738
 
*  config2 - 2nd byte of stored configuration settings
739
 
*   all bits: user-configured thermal ceiling value
740
 
*
741
 
**************************************************************************************/
742
 
inline void LoadConfig()
743
 
{
744
 
    uint8_t found = 0;
745
 
    uint8_t config1, config2;
746
 
 
747
 
    // find the config data
748
 
    for (eepos=0; (!found) && (eepos < 127); eepos++)
749
 
    {
750
 
        config1 = eeprom_read_byte((const byte *)eepos);
751
 
        config2 = eeprom_read_byte((const byte *)eepos+1);
752
 
 
753
 
        // Only use the data if a valid marker is found
754
 
        if (config1 < 0x80) {
755
 
            found = 1;
756
 
            break;
757
 
        }
758
 
    }
759
 
 
760
 
    // unpack the config data
761
 
    if (found)
762
 
    {
763
 
        tacticalSet = modeState = config1 & 0x01;   // ramping or tactical
764
 
        //tempAdjEnable = (config1 >> 1) & 0x01;      // temp regulation
765
 
        tempCeil = config2;                         // temperature limit
766
 
        // make sure nothing crazy in eeprom can cause a fire
767
 
        if (tempCeil > MAX_THERM_CEIL) {
768
 
            tempCeil = DEFAULT_THERM_CEIL;
769
 
        }
770
 
    }
771
 
    else
772
 
    {
773
 
        tacticalSet = 0;
774
 
        modeState = RAMPING_ST;
775
 
        eepos = 0;
776
 
    }
777
 
}
778
 
 
779
 
/**************************************************************************************
780
 
* SaveConfig - save the current mode with config settings
781
 
* ==========
782
 
*  Central method for writing (with wear leveling)
783
 
*
784
 
*  config1 - 1st byte of stored configuration settings:
785
 
*   bits   0: tactical mode
786
 
*  config2 - 2nd byte of stored configuration settings
787
 
*   all bits: user-configured thermal ceiling value
788
 
*
789
 
**************************************************************************************/
790
 
void SaveConfig()
791
 
{
792
 
    uint8_t config1;
793
 
    uint8_t config2;
794
 
    // Pack all settings into one byte
795
 
    //config1 = (byte)(tacticalSet | (tempAdjEnable << 1));
796
 
    config1 = (byte)(tacticalSet);
797
 
    config2 = tempCeil;
798
 
 
799
 
    byte oldpos = eepos;
800
 
 
801
 
    eepos = (eepos+1) & 127;  // wear leveling, use next cell
802
 
 
803
 
    cli();  // don't interrupt while writing eeprom; that's bad
804
 
 
805
 
    // Erase the last settings
806
 
    eeprom_write_byte((uint8_t *)(oldpos), 0xff);
807
 
    eeprom_write_byte((uint8_t *)(oldpos+1), 0xff);
808
 
 
809
 
    // Write the current settings
810
 
    eeprom_write_byte((uint8_t *)(eepos), config1);
811
 
    eeprom_write_byte((uint8_t *)(eepos+1), config2);
812
 
 
813
 
    sei();  // okay, interrupts are fine now
814
 
}
815
 
 
816
 
/****************************************************************************************
817
 
* ADC_vect - ADC done interrupt service routine
818
 
* ========
819
 
* This should execute every 64ms  (every 4 WDT ticks)
820
 
* So, voltage and temperature are each measured 4X per second.
821
 
****************************************************************************************/
822
 
ISR(ADC_vect)
823
 
{
824
 
    // (probably 4 values, since this happens 4X per second)
825
 
    #define NUM_TEMP_VALUES 4
826
 
    static int16_t temperature_values[NUM_TEMP_VALUES];  // for lowpass filter
827
 
 
828
 
    // Read in the ADC 10 bit value (0..1023)
829
 
    int16_t adcin = ADC;
830
 
 
831
 
    // Voltage Monitoring
832
 
    if (adc_step == 1)                          // ignore first ADC value from step 0
833
 
    {
834
 
        // Calculate cell voltage from ADC value
835
 
        #ifdef VOLT_MON_PIN7
836
 
        // ADC / 1.1V : ADC goes up with voltage
837
 
        // requires voltage divider on driver, to adjust volts to 0-1.1V range
838
 
        // ADC_MIN and ADC_MAX are 8-bit values, but we need 10-bit
839
 
        adcin = VOLTS_MIN + \
840
 
                 ((adcin - (ADC_MIN<<2)) * (VOLTS_MAX - VOLTS_MIN) / ((ADC_MAX<<2) - (ADC_MIN<<2)));
841
 
        voltageTenths = adcin;
842
 
        #else
843
 
        // 1.1V / ADC : ADC goes down with voltage
844
 
        // requires raw battery voltage on VCC pin, so 5.5V or less
845
 
        adcin = (11264 + adcin/2)/adcin + D1;   // in volts * 10: 10 * 1.1 * 1024 / ADC + D1, rounded
846
 
        if (voltageTenths > 0)
847
 
        {
848
 
            if (voltageTenths < adcin)          // crude low pass filter
849
 
                ++voltageTenths;
850
 
            if (voltageTenths > adcin)
851
 
                --voltageTenths;
852
 
        }
853
 
        else
854
 
            voltageTenths = adcin;              // prime on first read
855
 
        #endif
856
 
    }
857
 
 
858
 
    // Temperature monitoring
859
 
    if (adc_step == 3)                          // ignore first ADC value from step 2
860
 
    {
861
 
        //----------------------------------------------------------------------------------
862
 
        // Read the MCU temperature, applying a calibration offset value
863
 
        //----------------------------------------------------------------------------------
864
 
        // cancelled: adjust temperature values to fit in 0-255 range
865
 
        //            (should be able to cover about -40 C to 100 C)
866
 
        //            (cancelled because 13.2 fixed-point precision works better)
867
 
        adcin = adcin - 275 + TEMP_CAL_OFFSET;  // 300 = 25 degC
868
 
 
869
 
        // average the last few values to reduce noise
870
 
        // (noise will be amplified by predictive thermal algorithm, so noise is bad)
871
 
        if (temperature != 0)
872
 
        {
873
 
            uint8_t i;
874
 
            uint16_t total=0;
875
 
 
876
 
            // rotate array
877
 
            for(i=0; i<NUM_TEMP_VALUES-1; i++) {
878
 
                temperature_values[i] = temperature_values[i+1];
879
 
                total += temperature_values[i];
880
 
            }
881
 
            temperature_values[i] = adcin;
882
 
            total += adcin;
883
 
 
884
 
            // Original method, divide back to original range:
885
 
            //temperature = total >> 2;
886
 
            // More precise method: use noise as extra precision
887
 
            // (values are now basically fixed-point, signed 13.2)
888
 
            temperature = total;
889
 
        }
890
 
        else {  // prime on first read
891
 
            uint8_t i;
892
 
            for(i=0; i<NUM_TEMP_VALUES; i++)
893
 
                temperature_values[i] = adcin;
894
 
            // convert to 13.2 fixed-point
895
 
            //temperature = adcin<<2;
896
 
            // ... or not; all that matters here is that it's non-zero
897
 
            temperature = adcin;
898
 
        }
899
 
    }
900
 
 
901
 
    adc_step = (adc_step +1) & 0x03;             // increment but keep in range of 0..3
902
 
 
903
 
    if (adc_step < 2)                           // steps 0, 1 read voltage, steps 2, 3 read temperature
904
 
        ADMUX  = ADCMUX_VCC;
905
 
    else
906
 
        ADMUX  = ADCMUX_TEMP;
907
 
}
908
 
 
909
 
 
910
 
/**************************************************************************************
911
 
* WDT_vect - The watchdog timer - this is invoked every 16ms
912
 
* ========
913
 
**************************************************************************************/
914
 
ISR(WDT_vect)
915
 
{
916
 
    //static word wTurboTicks = 0;
917
 
 
918
 
    //static byte byBattCheck = 0;
919
 
    static byte byModeForMultiClicks = 0;   // the mode that originally was running before the 1st click
920
 
 
921
 
    #ifdef VOLTAGE_MON
922
 
    static byte byInitADCTimer = 0;
923
 
    static word adc_ticks = ADC_DELAY;
924
 
    static byte lowbatt_cnt = 0;
925
 
    #endif
926
 
 
927
 
    // Enforce a start-up delay so no switch actions processed initially
928
 
    if (byStartupDelayTime > 0)
929
 
    {
930
 
        --byStartupDelayTime;
931
 
        return;
932
 
    }
933
 
 
934
 
    if (wTempTicks > 0) // decrement each tick if active
935
 
        --wTempTicks;
936
 
 
937
 
    //---------------------------------------------------------------------------------------
938
 
    //---------------------------------------------------------------------------------------
939
 
    // Button is pressed
940
 
    //---------------------------------------------------------------------------------------
941
 
    //---------------------------------------------------------------------------------------
942
 
    if (IsPressed())
943
 
    {
944
 
        if (wPressDuration < 2000)
945
 
            wPressDuration++;
946
 
 
947
 
 
948
 
        // Enter thermal calibration mode
949
 
        if (b10Clicks)
950
 
        {
951
 
            if (wPressDuration >= 62)   // if held more than 1 second
952
 
            {
953
 
                b10Clicks = 0;
954
 
                // enter thermal calibration mode
955
 
                modeState = THERMAL_REG_ST;
956
 
            }
957
 
        }
958
 
 
959
 
        // For Tactical, turn on MAX while button is depressed
960
 
        // FIXME: tactical mode acts weird when user fast-clicks 3 times
961
 
        //        (goes to battcheck mode, then next click goes back to tactical mode)
962
 
        else if ((modeState == TACTICAL_ST) && (wPressDuration == 1) && (fastClicks < 2))
963
 
        {
964
 
            rampingLevel = MAX_RAMP_LEVEL;
965
 
            SetLevel(rampingLevel);
966
 
        }
967
 
 
968
 
        // long-press for ramping
969
 
        else if ((wPressDuration >= SHORT_CLICK_DUR) && (modeState == RAMPING_ST))  // also in there was:  && !byDelayRamping
970
 
        {
971
 
            {
972
 
                switch (rampState)
973
 
                {
974
 
                    case 0:        // ramping not initialized yet
975
 
                        if (rampingLevel == 0)
976
 
                        {
977
 
                            rampState = 1;
978
 
                            rampPauseCntDn = RAMP_MOON_PAUSE;   // delay a little on moon
979
 
 
980
 
                            // set this to the 1st level
981
 
                            rampingLevel = 1;
982
 
                            SetLevel(1);
983
 
 
984
 
                            dontToggleDir = 0;                  // clear it in case it got set from a timeout
985
 
                        }
986
 
                        else
987
 
                        {
988
 
                            #ifdef RAMPING_REVERSE
989
 
                                if (dontToggleDir)
990
 
                                {
991
 
                                    rampState = rampLastDirState;      // keep it in the same
992
 
                                    dontToggleDir = 0;                 // clear it so it can't happen again til another timeout
993
 
                                }
994
 
                                else
995
 
                                    rampState = 5 - rampLastDirState;  // 2->3, or 3->2
996
 
                            #else
997
 
                                rampState = 2;  // lo->hi
998
 
                            #endif
999
 
                            if (rampingLevel == MAX_RAMP_LEVEL)
1000
 
                            {
1001
 
                                rampState = 3;  // hi->lo
1002
 
                                SetLevel(MAX_RAMP_LEVEL);
1003
 
                            }
1004
 
                            else if (rampingLevel == 255)  // If stopped in ramping moon mode, start from lowest
1005
 
                            {
1006
 
                                rampState = 2; // lo->hi
1007
 
                                rampingLevel = 1;
1008
 
                                SetLevel(rampingLevel);
1009
 
                            }
1010
 
                            else if (rampingLevel == 1)
1011
 
                                rampState = 2;  // lo->hi
1012
 
                        }
1013
 
                        break;
1014
 
 
1015
 
                    case 1:        // in moon mode
1016
 
                        if (--rampPauseCntDn == 0)
1017
 
                        {
1018
 
                            rampState = 2; // lo->hi
1019
 
                        }
1020
 
                        break;
1021
 
 
1022
 
                    case 2:        // lo->hi
1023
 
                        rampLastDirState = 2;
1024
 
                        if (rampingLevel < MAX_RAMP_LEVEL)
1025
 
                        {
1026
 
                            rampingLevel ++;
1027
 
                        }
1028
 
                        else
1029
 
                        {
1030
 
                            rampState = 4;
1031
 
                        }
1032
 
                        if ((rampingLevel == MAX_RAMP_LEVEL) || (rampingLevel == MAX_7135_LEVEL)) {
1033
 
                            SetActualLevel(0);  // Do a quick blink
1034
 
                            _delay_ms(7);
1035
 
                        }
1036
 
                        SetLevel(rampingLevel);
1037
 
                        dontToggleDir = 0;
1038
 
                        break;
1039
 
 
1040
 
                    case 3:        // hi->lo
1041
 
                        rampLastDirState = 3;
1042
 
                        if (rampingLevel > 1)
1043
 
                        {
1044
 
                            rampingLevel --;
1045
 
                        }
1046
 
                        else
1047
 
                        {
1048
 
                            rampState = 4;
1049
 
                        }
1050
 
                        if ((rampingLevel == 1) || (rampingLevel == MAX_7135_LEVEL)) {
1051
 
                            SetActualLevel(0);  // Do a quick blink
1052
 
                            _delay_ms(7);
1053
 
                        }
1054
 
                        SetLevel(rampingLevel);
1055
 
                        dontToggleDir = 0;
1056
 
                        break;
1057
 
 
1058
 
                    case 4:        // ramping ended - 0 or max reached
1059
 
                        break;
1060
 
 
1061
 
                } // switch on rampState
1062
 
                // ensure turbo toggle will return to ramped level, even if the ramp was at turbo
1063
 
                preTurboLevel = rampingLevel;
1064
 
 
1065
 
            } // switch held longer than single click
1066
 
        } // else (not b10Clicks)
1067
 
 
1068
 
        //wTurboTicks = 0;      // Just always reset turbo timer whenever the button is pressed
1069
 
 
1070
 
        #ifdef VOLTAGE_MON
1071
 
        adc_ticks = ADC_DELAY;  // Same with the ramp down delay
1072
 
        #endif
1073
 
    }
1074
 
 
1075
 
    //---------------------------------------------------------------------------------------
1076
 
    //---------------------------------------------------------------------------------------
1077
 
    // Not pressed (debounced qualified)
1078
 
    //---------------------------------------------------------------------------------------
1079
 
    //---------------------------------------------------------------------------------------
1080
 
    else
1081
 
    {
1082
 
 
1083
 
        // Was previously pressed - button released
1084
 
        if (wPressDuration > 0)
1085
 
        {
1086
 
 
1087
 
            rampState = 0;  // reset state to not ramping
1088
 
 
1089
 
            // user got too hot and let go of button in thermal calibration mode
1090
 
            // (or otherwise aborted that mode)
1091
 
            if ((modeState == THERMAL_REG_ST) || (modeState == THERMAL_CAL_ST)) {
1092
 
                // change modeState to let main() know it can save the calibration
1093
 
                modeState = RAMPING_ST;
1094
 
            }
1095
 
 
1096
 
            // Momentary / tactical mode
1097
 
            if (modeState == TACTICAL_ST)
1098
 
            {
1099
 
                rampingLevel = 0;  // turn off output as soon as the user lets the switch go
1100
 
                SetLevel(0);
1101
 
            }
1102
 
 
1103
 
            // normal short click processing
1104
 
            if (wPressDuration < SHORT_CLICK_DUR)
1105
 
            {
1106
 
 
1107
 
                ++fastClicks;   // track fast clicks in a row from OFF or ON (doesn't matter in ramping)
1108
 
 
1109
 
                //-----------------------------------------------------------------------------------
1110
 
                //-----------------------------------------------------------------------------------
1111
 
                switch (modeState)
1112
 
                {
1113
 
                    case RAMPING_ST:
1114
 
                        if (fastClicks == 1)
1115
 
                        {
1116
 
                            byModeForMultiClicks = modeState;       // save current mode
1117
 
 
1118
 
                            // if we were off, turn on at memorized level
1119
 
                            if (rampingLevel == 0) {
1120
 
                                rampingLevel = memorizedLevel;
1121
 
                                // bugfix: make off->turbo work when memorizedLevel == turbo
1122
 
                                preTurboLevel = memorizedLevel;
1123
 
                                // also set up other ramp vars to make sure ramping will work
1124
 
                                rampState = 2;  // lo->hi
1125
 
                                rampLastDirState = 2;
1126
 
                                if (rampingLevel == MAX_RAMP_LEVEL) {
1127
 
                                    rampState = 3;  // hi->lo
1128
 
                                    rampLastDirState = 3;
1129
 
                                }
1130
 
                                dontToggleDir = 0;
1131
 
                            // if we were on, turn off
1132
 
                            } else {
1133
 
                                memorizedLevel = rampingLevel;
1134
 
                                rampingLevel = 0;
1135
 
                            }
1136
 
                            SetLevelSoft(rampingLevel);
1137
 
 
1138
 
                            //byDelayRamping = 1;       // don't act on ramping button presses
1139
 
                        }
1140
 
                        else if (fastClicks == 10)      // --> 10 clicks: flip thermal regulation control
1141
 
                            b10Clicks = 1;
1142
 
                        else                            // --> 2+ clicks: turn off the output
1143
 
                        {
1144
 
                            // prevent flashes while entering long click sequences
1145
 
                            rampingLevel = 0;
1146
 
                            SetLevel(rampingLevel);
1147
 
                        }
1148
 
                        break;
1149
 
 
1150
 
                    case TACTICAL_ST:
1151
 
                        if (fastClicks == 1)
1152
 
                            byModeForMultiClicks = modeState;   // save current mode
1153
 
                        else if (fastClicks == 10)              // --> 10 clicks: flip thermal regulation control
1154
 
                            b10Clicks = 1;
1155
 
                        break;
1156
 
 
1157
 
                    case BEACON_ST:
1158
 
                    case BATT_READ_ST:
1159
 
                    case TEMP_READ_ST:
1160
 
                        if (fastClicks == 1)
1161
 
                        {
1162
 
                            byModeForMultiClicks = modeState;   // save current mode
1163
 
 
1164
 
                            modeState = tacticalSet;            // set to Ramping or Tactical
1165
 
                            rampingLevel = 0;
1166
 
                            SetLevel(rampingLevel);
1167
 
                            //byDelayRamping = 1;                 // don't act on ramping button presses
1168
 
                        }
1169
 
                        break;
1170
 
                } // switch
1171
 
 
1172
 
            } // short click
1173
 
            else
1174
 
            {
1175
 
                fastClicks = 0; // reset fast clicks for long holds
1176
 
                b10Clicks = 0;  // reset special 10 click flag
1177
 
            }
1178
 
 
1179
 
            wPressDuration = 0;
1180
 
 
1181
 
            //byDelayRamping = 0;   // restore acting on ramping button presses, if disabled
1182
 
 
1183
 
            wIdleTicks = 0; // reset idle time
1184
 
 
1185
 
        } // previously pressed
1186
 
        else
1187
 
        {
1188
 
            //---------------------------------------------------------------------------------------
1189
 
            //---------------------------------------------------------------------------------------
1190
 
            // Not previously pressed
1191
 
            //---------------------------------------------------------------------------------------
1192
 
            //---------------------------------------------------------------------------------------
1193
 
            if (++wIdleTicks > 30000)
1194
 
                wIdleTicks = 30000;     // max it out at 30,000 (8 minutes)
1195
 
 
1196
 
            if (wIdleTicks > DBL_CLICK_TICKS)   // Too much delay between clicks - time out the fast clicks
1197
 
            {
1198
 
                //-----------------------------------------------------------------------------------
1199
 
                // Process multi clicks here, after it times out
1200
 
                //-----------------------------------------------------------------------------------
1201
 
                switch (modeState)
1202
 
                {
1203
 
 
1204
 
                    // FIXME: disable extra states from tactical/momentary mode
1205
 
                    //        (only do this in ramping mode, or from off)
1206
 
                    case RAMPING_ST:
1207
 
                    case TACTICAL_ST:
1208
 
                        if (fastClicks == 2)            // --> double click
1209
 
                        {
1210
 
                            // --> battcheck to tempcheck
1211
 
                            // FIXME: why is this handled here instead of below under battcheck state?
1212
 
                            if (byModeForMultiClicks == BATT_READ_ST)
1213
 
                            {
1214
 
                                modeState = TEMP_READ_ST;
1215
 
                            }
1216
 
                            // --> ramp direct to MAX/turbo (or back)
1217
 
                            else if (modeState == RAMPING_ST)
1218
 
                            {
1219
 
                                // make double-click toggle turbo, not a one-way trip
1220
 
                                // Note: rampingLevel is zero here,
1221
 
                                //       because double-click turns the light off and back on
1222
 
                                if (memorizedLevel == MAX_RAMP_LEVEL) {
1223
 
                                    rampingLevel = preTurboLevel;
1224
 
                                } else {
1225
 
                                    preTurboLevel = memorizedLevel;
1226
 
                                    rampingLevel = MAX_RAMP_LEVEL;
1227
 
                                }
1228
 
                                SetLevelSoft(rampingLevel);
1229
 
                            }
1230
 
                        }
1231
 
 
1232
 
                        #ifdef TRIPLE_CLICK_BATT
1233
 
                        else if (fastClicks == 3)       // --> triple click: display battery check/status
1234
 
                        {
1235
 
                            modeState = BATT_READ_ST;
1236
 
                            //byDelayRamping = 1;         // don't act on ramping button presses
1237
 
                        }
1238
 
                        #endif
1239
 
 
1240
 
                        else if (fastClicks == 4)       // --> 4 clicks: set toggle Ramping/Tactical Mode
1241
 
                        {
1242
 
                            if (modeState == RAMPING_ST)
1243
 
                                modeState = TACTICAL_ST;
1244
 
                            else
1245
 
                                modeState = RAMPING_ST;
1246
 
                            //byDelayRamping = 1;         // don't act on ramping button presses
1247
 
                        }
1248
 
 
1249
 
                        else if (fastClicks == 6)       // --> 6 clicks: set Lockout Mode
1250
 
                        {
1251
 
                            modeState = LOCKOUT_ST;
1252
 
                            //byDelayRamping = 1;         // don't act on ramping button presses
1253
 
                        }
1254
 
 
1255
 
                        else if (fastClicks == 8)       // --> 8 clicks: set Beacon Mode
1256
 
                        {
1257
 
                            modeState = BEACON_ST;
1258
 
                            //byDelayRamping = 1;         // don't act on ramping button presses
1259
 
                        }
1260
 
                        break;
1261
 
 
1262
 
                    case LOCKOUT_ST:
1263
 
                        if (fastClicks == 6)            // --> 6 clicks: clear Lock Out Mode
1264
 
                        {
1265
 
                            modeState = tacticalSet;    // set to Ramping or Tactical
1266
 
                            //byDelayRamping = 1;         // don't act on ramping button presses
1267
 
                        }
1268
 
                        break;
1269
 
 
1270
 
                    case BEACON_ST:
1271
 
                        break;
1272
 
 
1273
 
                    case BATT_READ_ST:
1274
 
                    case TEMP_READ_ST:
1275
 
                        break;
1276
 
                }
1277
 
 
1278
 
                fastClicks = 0;
1279
 
            }
1280
 
 
1281
 
            // For ramping, timeout the direction toggling
1282
 
            if ((rampingLevel > 1) && (rampingLevel < MAX_RAMP_LEVEL))
1283
 
            {
1284
 
                if (wIdleTicks > RAMP_SWITCH_TIMEOUT)
1285
 
                    dontToggleDir = 1;
1286
 
            }
1287
 
 
1288
 
            //------------------------------------------------------------------------------
1289
 
            // Do voltage monitoring when the switch isn't pressed
1290
 
            //------------------------------------------------------------------------------
1291
 
            #ifdef VOLTAGE_MON
1292
 
            if (adc_ticks > 0)
1293
 
                --adc_ticks;
1294
 
            if (adc_ticks == 0)
1295
 
            {
1296
 
                // See if conversion is done
1297
 
                {
1298
 
                    byte atLowLimit;
1299
 
                    atLowLimit = ((ActualLevel == 1));
1300
 
 
1301
 
                    // See if voltage is lower than what we were looking for
1302
 
                    if (voltageTenths < (atLowLimit ? BATT_CRIT : BATT_LOW))
1303
 
                        ++lowbatt_cnt;
1304
 
                    else
1305
 
                        lowbatt_cnt = 0;
1306
 
                }
1307
 
 
1308
 
                // See if it's been low for a while
1309
 
                if (lowbatt_cnt >= 4)
1310
 
                {
1311
 
                    LowBattSignal = 1;
1312
 
 
1313
 
                    lowbatt_cnt = 0;
1314
 
                    // If we reach 0 here, main loop will go into sleep mode
1315
 
                    // Restart the counter to when we step down again
1316
 
                    adc_ticks = ADC_DELAY;
1317
 
                }
1318
 
 
1319
 
            }
1320
 
 
1321
 
            #endif
1322
 
        } // not previously pressed
1323
 
 
1324
 
        wPressDuration = 0;
1325
 
    } // Not pressed
1326
 
 
1327
 
    // Schedule an A-D conversion every 4th timer (64 msecs)
1328
 
    if ((++byInitADCTimer & 0x03) == 0)
1329
 
        ADCSRA |= (1 << ADSC) | (1 << ADIE);    // start next ADC conversion and arm interrupt
1330
 
}
1331
 
 
1332
 
/**************************************************************************************
1333
 
* main - main program loop. This is where it all happens...
1334
 
* ====
1335
 
**************************************************************************************/
1336
 
int main(void)
1337
 
{
1338
 
 
1339
 
    // Set all ports to input, and turn pull-up resistors on for the inputs we are using
1340
 
    DDRB = 0x00;
1341
 
    //PORTB = (1 << SWITCH_PIN) | (1 << STAR3_PIN);
1342
 
 
1343
 
    PORTB = (1 << SWITCH_PIN);  // Only the switch is an input
1344
 
 
1345
 
    // Set the switch as an interrupt for when we turn pin change interrupts on
1346
 
    PCMSK = (1 << SWITCH_PIN);
1347
 
 
1348
 
    // Set primary and alternate PWN pins for output
1349
 
    // FIXME: add support for 1-channel and 3-channel drivers
1350
 
    DDRB = (1 << PWM_PIN) | (1 << ALT_PWM_PIN);
1351
 
 
1352
 
    TCCR0A = PHASE;  // set this once here - don't use FAST anymore
1353
 
 
1354
 
    // Set timer to do PWM for correct output pin and set prescaler timing
1355
 
    TCCR0B = 0x01;  // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
1356
 
 
1357
 
    // Turn features on or off as needed
1358
 
    #ifdef VOLTAGE_MON
1359
 
    ADC_on();
1360
 
    #else
1361
 
    ADC_off();
1362
 
    #endif
1363
 
 
1364
 
    ACSR |= (1<<7);  // Analog Comparator off
1365
 
 
1366
 
    // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
1367
 
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
1368
 
 
1369
 
    // Shut things off now, just in case we come up in a partial power state
1370
 
    cli();  // Disable interrupts
1371
 
    PCINT_off();
1372
 
 
1373
 
    WDT_off();
1374
 
 
1375
 
    // Load config settings: tactical mode and temperature regulation
1376
 
    LoadConfig();
1377
 
 
1378
 
    // Not really intended to be configurable;
1379
 
    // only #defined for convenience
1380
 
    #define THERM_HISTORY_SIZE 8
1381
 
    // this allows us to measure change over time,
1382
 
    // which allows predicting future state
1383
 
    uint16_t temperatures[THERM_HISTORY_SIZE];
1384
 
    // track whether array has been initialized yet
1385
 
    uint8_t first_temp_reading = 1;
1386
 
 
1387
 
 
1388
 
    #ifdef STARTUP_LIGHT_ON
1389
 
    rampingLevel = MAX_RAMP_LEVEL;
1390
 
    SetLevel(MAX_RAMP_LEVEL);
1391
 
    #elif defined STARTUP_2BLINKS
1392
 
    Blink(2, 80);           // Blink twice upon power-up
1393
 
    #else
1394
 
    SetLevel(0);
1395
 
    #endif
1396
 
 
1397
 
    byte byPrevModeState = modeState;
1398
 
 
1399
 
    byStartupDelayTime = 25;  // 400 msec delay in the WDT interrupt handler
1400
 
 
1401
 
    WDT_on();  // Turn it on now (mode can be non-zero on startup)
1402
 
 
1403
 
    //------------------------------------------------------------------------------------
1404
 
    //------------------------------------------------------------------------------------
1405
 
    //  We will never leave this loop.  The WDT will interrupt to check for switch presses
1406
 
    // and will change the mode if needed. If this loop detects that the mode has changed,
1407
 
    // run the logic for that mode while continuing to check for a mode change.
1408
 
    //------------------------------------------------------------------------------------
1409
 
    //------------------------------------------------------------------------------------
1410
 
    while(1)  // run forever
1411
 
    {
1412
 
        //---------------------------------------------------------------------------------------
1413
 
        // State Changes and Main Mode Handling
1414
 
        //---------------------------------------------------------------------------------------
1415
 
        if (byPrevModeState != modeState)
1416
 
        {
1417
 
            byte savedPrevMode = byPrevModeState;
1418
 
            byPrevModeState = modeState;
1419
 
 
1420
 
            switch (modeState)
1421
 
            {
1422
 
                case RAMPING_ST:
1423
 
                    // user released button during thermal calibration:
1424
 
                    if ((savedPrevMode == THERMAL_REG_ST) || (savedPrevMode == THERMAL_CAL_ST)) {
1425
 
                        // set limit to whatever they requested
1426
 
                        SaveConfig();
1427
 
                        // ... and show them the new value
1428
 
                        SetLevel(0);
1429
 
                        _delay_ms(500);
1430
 
                        BlinkOutNumber(tempCeil, RAMPING_ST);
1431
 
                    }
1432
 
                    else if (savedPrevMode == TACTICAL_ST)
1433
 
                    {
1434
 
                        SetLevel(0);
1435
 
                        _delay_ms(250);
1436
 
                        Blink(2, 60);
1437
 
 
1438
 
                        tacticalSet = 0;    // Save the state for ramping
1439
 
                        SaveConfig();
1440
 
                    }
1441
 
                    else if (savedPrevMode == LOCKOUT_ST)
1442
 
                    {
1443
 
                        SetLevel(0);
1444
 
                        _delay_ms(250);
1445
 
                        Blink(2, 60);
1446
 
                    }
1447
 
                    break;
1448
 
 
1449
 
                case TACTICAL_ST:  // Entering Tactical
1450
 
                    if (savedPrevMode == RAMPING_ST)
1451
 
                    {
1452
 
                        SetLevel(0);
1453
 
                        _delay_ms(250);
1454
 
                        Blink(4, 60);
1455
 
 
1456
 
                        tacticalSet = 1;
1457
 
                        SaveConfig();
1458
 
                    }
1459
 
                    // FIXME: We can go from lockout to tactical???
1460
 
                    //        (this code should probably be removed)
1461
 
                    else if (savedPrevMode == LOCKOUT_ST)
1462
 
                    {
1463
 
                        SetLevel(0);
1464
 
                        _delay_ms(250);
1465
 
                        Blink(2, 60);
1466
 
                    }
1467
 
                    break;
1468
 
 
1469
 
                case LOCKOUT_ST:       // Entering Lock Out
1470
 
                    SetLevel(0);
1471
 
                    _delay_ms(250);
1472
 
                    Blink(4, 60);
1473
 
 
1474
 
                    //byDelayRamping = 0;       // restore acting on ramping button presses
1475
 
                    break;
1476
 
 
1477
 
                case BEACON_ST:
1478
 
                    _delay_ms(300); // pause a little initially
1479
 
                    while (modeState == BEACON_ST)  // Beacon mode
1480
 
                    {
1481
 
                        SetLevel(memorizedLevel);   // allow user to set level
1482
 
                        // on for 1/4 second
1483
 
                        delay_unless_modeState_changed(250, BEACON_ST);
1484
 
                        SetLevel(0);
1485
 
 
1486
 
                        // 2.5 s per cycle
1487
 
                        delay_unless_modeState_changed(2250, BEACON_ST);
1488
 
                    }
1489
 
                    break;
1490
 
 
1491
 
                case THERMAL_REG_ST:
1492
 
                    {
1493
 
                        // First part of thermal calibration mode:
1494
 
                        // check current value, option to set maximum,
1495
 
                        // then proceed to actual calibration
1496
 
 
1497
 
                        // show previous ceiling value
1498
 
                        byte oldCeil = tempCeil;
1499
 
                        byte aborted = 0;
1500
 
                        SetLevel(0);     // blink ON/OFF
1501
 
                        _delay_ms(500);
1502
 
                        BlinkOutNumber(oldCeil, THERMAL_REG_ST);
1503
 
                        _delay_ms(500);
1504
 
                        aborted = ! IsPressed();
1505
 
                        if (aborted) {
1506
 
                            // exit with no changes
1507
 
                            modeState = RAMPING_ST;
1508
 
                            break;
1509
 
                        }
1510
 
 
1511
 
                        // buzz at a low level to give user time to set maximum level
1512
 
                        tempCeil = MAX_THERM_CEIL;
1513
 
                        for(uint8_t i=0; i<20; i++) {
1514
 
                            SetLevel(MAX_RAMP_LEVEL/8);
1515
 
                            _delay_ms(50);
1516
 
                            SetLevel(MAX_RAMP_LEVEL/7);
1517
 
                            _delay_ms(50);
1518
 
                            aborted = ! IsPressed();
1519
 
                            if (aborted) break;
1520
 
                        }
1521
 
                        if (aborted) {
1522
 
                            // save and exit
1523
 
                            modeState = RAMPING_ST;
1524
 
                            break;
1525
 
                        }
1526
 
 
1527
 
                        // else calibrate normally
1528
 
                        tempCeil = oldCeil;
1529
 
                        modeState = THERMAL_CAL_ST;
1530
 
                    }
1531
 
                    break;
1532
 
 
1533
 
                case BATT_READ_ST:
1534
 
                    _delay_ms(400);  // delay a little here to give the user a chance to see a full blink sequence
1535
 
 
1536
 
                    //byDelayRamping = 0;       // restore acting on ramping button presses
1537
 
 
1538
 
                    while (modeState == BATT_READ_ST)  // Battery Check
1539
 
                    {
1540
 
                        // blink out volts and tenths
1541
 
                        BlinkOutNumber(voltageTenths, BATT_READ_ST);
1542
 
                    }
1543
 
                    break;
1544
 
 
1545
 
                case TEMP_READ_ST:
1546
 
                    _delay_ms(400);  // delay a little here to give the user a chance to see a full blink sequence
1547
 
                    while (modeState == TEMP_READ_ST)  // Temperature Check
1548
 
                    {
1549
 
                        // blink out temperature in 2 digits
1550
 
                        // (divide by 4 to get an integer because the value is 13.2 fixed-point)
1551
 
                        uint8_t temp = 0;
1552
 
                        // don't try to show negative numbers
1553
 
                        if (temperature > 0) {
1554
 
                            temp = temperature>>2;
1555
 
                        }
1556
 
                        BlinkOutNumber(temp, TEMP_READ_ST);
1557
 
                    }
1558
 
                    break;
1559
 
            } // switch
1560
 
 
1561
 
        } // mode changed
1562
 
 
1563
 
 
1564
 
        //---------------------------------------------------------------------
1565
 
        // Perform low battery indicator tests
1566
 
        //---------------------------------------------------------------------
1567
 
        #ifdef USE_LVP
1568
 
        if (LowBattSignal)
1569
 
        {
1570
 
            if (ActualLevel > 0)
1571
 
            {
1572
 
                if (ActualLevel == 1)
1573
 
                {
1574
 
                    // Reached critical battery level
1575
 
                    BlinkLVP(8, RAMP_SIZE/6);    // blink more, brighter, and quicker (to get attention)
1576
 
                    ActualLevel = rampingLevel = 0;    // Shut it down
1577
 
                    rampState = 0;
1578
 
                    // TODO: test if it actually shuts off as far as possible after LVP
1579
 
                }
1580
 
                else
1581
 
                {
1582
 
                    // Drop the output level when voltage is low
1583
 
                    BlinkLVP(3, ActualLevel);  // 3 blinks as a warning
1584
 
                    // decrease by half current ramp level
1585
 
                    ActualLevel = (ActualLevel>>1);
1586
 
                    if (ActualLevel < 1) { ActualLevel = 1; }
1587
 
                }
1588
 
                SetLevelSoft(ActualLevel);  // soft ramp down
1589
 
            }
1590
 
            LowBattSignal = 0;
1591
 
        }
1592
 
        #endif  // ifdef USE_LVP
1593
 
 
1594
 
        //---------------------------------------------------------------------
1595
 
        // Temperature monitoring - step it down if too hot!
1596
 
        //---------------------------------------------------------------------
1597
 
        // Init thermal calibration mode, if we're in that mode.
1598
 
        if (modeState == THERMAL_CAL_ST) {
1599
 
            SetLevelSoft(MAX_RAMP_LEVEL);
1600
 
        }
1601
 
        // don't run unless a measurement has completed
1602
 
        // don't run unless the light is actually on!
1603
 
        if ((ActualLevel > 0) && (temperature > 0)) {
1604
 
 
1605
 
            if (first_temp_reading) {  // initial setup
1606
 
                first_temp_reading = 0;
1607
 
                uint8_t i;
1608
 
                for (i=0; i<THERM_HISTORY_SIZE; i++)
1609
 
                    temperatures[i] = temperature;
1610
 
            }
1611
 
 
1612
 
            // should happen every half-second or so
1613
 
            if (wTempTicks == 0) {
1614
 
                // reset timer
1615
 
                wTempTicks = TEMP_ADJ_PERIOD;
1616
 
 
1617
 
                uint8_t i;
1618
 
                // only adjust if several readings in a row are outside desired range
1619
 
                static uint8_t overheat_count = 0;
1620
 
                static uint8_t underheat_count = 0;
1621
 
                int16_t projected;  // Fight the future!
1622
 
                int16_t diff;
1623
 
 
1624
 
                // algorithm tweaking; not really intended to be modified
1625
 
                // how far ahead should we predict?
1626
 
                #define THERM_PREDICTION_STRENGTH 4
1627
 
                // how proportional should the adjustments be?
1628
 
                #define THERM_DIFF_ATTENUATION 4
1629
 
                // how low is the lowpass filter?
1630
 
                #define THERM_LOWPASS 8
1631
 
                // lowest ramp level; never go below this (sanity check)
1632
 
                #define THERM_FLOOR (MAX_RAMP_LEVEL/8)
1633
 
                // highest temperature allowed
1634
 
                // (convert configured value to 13.2 fixed-point)
1635
 
                #define THERM_CEIL (tempCeil<<2)
1636
 
                // acceptable temperature window size in C
1637
 
                #define THERM_WINDOW_SIZE 8
1638
 
 
1639
 
                // rotate measurements and add a new one
1640
 
                for (i=0; i<THERM_HISTORY_SIZE-1; i++) {
1641
 
                    temperatures[i] = temperatures[i+1];
1642
 
                }
1643
 
                temperatures[THERM_HISTORY_SIZE-1] = temperature;
1644
 
 
1645
 
                // guess what the temp will be several seconds in the future
1646
 
                diff = temperatures[THERM_HISTORY_SIZE-1] - temperatures[0];
1647
 
                projected = temperatures[THERM_HISTORY_SIZE-1] + (diff<<THERM_PREDICTION_STRENGTH);
1648
 
 
1649
 
                // never step down in thermal calibration mode
1650
 
                if (modeState == THERMAL_CAL_ST) {
1651
 
                    // use the current temperature as the new ceiling value
1652
 
                    //tempCeil = projected >> 2;
1653
 
                    // less aggressive prediction
1654
 
                    tempCeil = (temperatures[THERM_HISTORY_SIZE-1]
1655
 
                                + (diff<<(THERM_PREDICTION_STRENGTH-1))) >> 2;
1656
 
                    //tempCeil = (temperature + (diff<<(THERM_PREDICTION_STRENGTH-1))) >> 2;
1657
 
                    // Don't let user exceed maximum limit
1658
 
                    if (tempCeil > MAX_THERM_CEIL) {
1659
 
                        tempCeil = MAX_THERM_CEIL;
1660
 
                    }
1661
 
                }
1662
 
                // too hot, step down (maybe)
1663
 
                else if (projected >= THERM_CEIL) {
1664
 
                    underheat_count = 0;  // we're definitely not too cold
1665
 
                    if (overheat_count > THERM_LOWPASS) {
1666
 
                        overheat_count = 0;
1667
 
 
1668
 
                        // how far above the ceiling?
1669
 
                        int16_t exceed = (projected - THERM_CEIL) >> THERM_DIFF_ATTENUATION;
1670
 
                        if (exceed < 1) { exceed = 1; }
1671
 
                        uint8_t stepdown = ActualLevel - exceed;
1672
 
                        // never go under the floor; zombies in the cellar
1673
 
                        if (stepdown < THERM_FLOOR) {
1674
 
                            stepdown = THERM_FLOOR;
1675
 
                        }
1676
 
                        // avoid a bug: stepping "down" from moon to THERM_FLOOR
1677
 
                        // if user turned light down to moon during lowpass period
1678
 
                        if (stepdown > TargetLevel) {
1679
 
                            stepdown = TargetLevel;
1680
 
                        }
1681
 
                        // really, don't try to regulate below the floor
1682
 
                        if (ActualLevel > THERM_FLOOR) {
1683
 
                            SetActualLevel(stepdown);
1684
 
                        }
1685
 
                    } else {
1686
 
                        overheat_count ++;
1687
 
                    }
1688
 
                }
1689
 
                else {  // not too hot
1690
 
                    overheat_count = 0;  // we're definitely not too hot
1691
 
                    // too cold?  step back up?
1692
 
                    if (projected < (THERM_CEIL - (THERM_WINDOW_SIZE<<2))) {
1693
 
                        if (underheat_count > (THERM_LOWPASS/2)) {
1694
 
                            underheat_count = 0;
1695
 
                            // never go above the user's requested level
1696
 
                            if (ActualLevel < TargetLevel) {
1697
 
                                SetActualLevel(ActualLevel + 1);   // step up slowly
1698
 
                            }
1699
 
                        } else {
1700
 
                            underheat_count ++;
1701
 
                        }
1702
 
                    }
1703
 
                }
1704
 
            }
1705
 
        }  // thermal regulation
1706
 
 
1707
 
        //---------------------------------------------------------------------
1708
 
        // Be sure switch is not pressed and light is OFF for at least 5 secs
1709
 
        //---------------------------------------------------------------------
1710
 
        word wWaitTicks = 300;   // ~5 secs
1711
 
 
1712
 
        if ((ActualLevel == 0) && !IsPressed() && (wIdleTicks > wWaitTicks))
1713
 
        {
1714
 
            wIdleTicks = 0;
1715
 
            _delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
1716
 
            SleepUntilSwitchPress();  // Go to sleep
1717
 
        }
1718
 
 
1719
 
    } // while(1)
1720
 
 
1721
 
   return 0;  // Standard Return Code
1722
 
}