1
//-------------------------------------------------------------------------------------
2
// RampingIOS - simple ramping UI for the Atmel ATTiny85/ATTiny85V
5
// Copyright (C) 2017 Tom Elfers and Selene Scriven
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.
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.
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/>.
21
// originally based on STAR_momentary.c v1.6 from JonnyC as of Feb 14th, 2015, Modified by Tom E
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
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
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
53
// Vers 1.0 2017-03-21/27:
55
//-------------------------------------------------------------------------------------
58
* ATtiny 25/45/85 Diagram
61
* switch 2 -| |- 7 Voltage ADC
62
* Ind.LED 3 -| |- 6 FET PWM
63
* GND 4 -| |- 5 7135 PWM
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
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
73
* -Ulfuse:w:0xe2:m -Uhfuse:w:0xdf:m -Uefuse:w:0xff:m
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
79
* --> Note: BOD enabled fixes lockups intermittently occurring on power up fluctuations, but adds slightly to parasitic drain
84
* Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
90
#include "tk-attiny.h"
95
#define PHASE 0xA1 // phase-correct PWM both channels
96
#define FAST 0xA3 // fast PWM both channels
98
// Ignore a spurious warning, we did the cast on purpose
99
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
101
//------------- Driver Board Settings -------------------------------------
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
108
//------------- End Driver Board Settings ---------------------------------
111
//-------------------------------------------------------------------------
112
// Settings to modify per driver
113
//-------------------------------------------------------------------------
114
//-------------------------------------------------------------------------
116
// v1.1 (in bits: mmm.nnnnn, mm=major @, nnnnn=minor#)
117
#define FIRMWARE_VERS ((1<<5) | 1)
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
123
#define USE_LVP // enables Low Voltage Protection
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)
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
141
#define TEMP_ADJ_PERIOD 32 // Thermal regulation frequency: 0.5 sec(s) (in 16 msec ticks)
142
//-----------------------------
144
//#define USING_3807135_BANK // (default OFF) sets up ramping for 380 mA 7135's instead of a FET
146
#define RAMPING_REVERSE // (default ON) reverses ramping direction for every click&hold
148
#define RAMP_SWITCH_TIMEOUT 75 // make the up/dn ramp switch timeout 1.2 secs (same as IDLE_TIME)
150
//#define ADV_RAMP_OPTIONS // In ramping, enables "mode set" additional method for lock-out and battery voltage display, comment out to disable
152
#define TRIPLE_CLICK_BATT // enable a triple-click to display Battery status
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
157
#define D1 2 // Drop over reverse polarity protection diode = 0.2 V
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)
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
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)
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
178
#define VOLTS_MIN 28 // 2.8V
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
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
193
#define ADC_DELAY 312 // Delay in ticks between low-batt ramp-downs (312=5secs, 250=4secs, was: 188 ~= 3s)
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)
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)
211
#define FIRM_VERS_MODE 81
213
//-------------------------------------------------------------------------
214
//-------------------------------------------------------------------------
215
#include <avr/pgmspace.h>
217
//#include <util/delay.h>
218
#include <avr/interrupt.h>
220
#include <avr/eeprom.h>
221
#include <avr/sleep.h>
223
#define OWN_DELAY // Don't use stock delay functions.
224
#define USE_DELAY_S // Also use _delay_s(), not just _delay_ms()
226
#include "tk-delay.h"
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
231
#define ADCMUX_TEMP 0b10001111 // ADCMUX register setup to read temperature
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
238
#define ADCMUX_VCC 0b00001100 // ADCMUX register setup to read Vbg referenced to Vcc
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
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%
257
//----------------------------------------------------
258
// Ramping Mode Levels, 150 total entries (2.4 secs)
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
263
// Note: PWM value of 4 should be ~5.5 mA, but a value of 10 actually measures ~5 ma
264
//----------------------------------------------------
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
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,
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
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
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
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
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
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
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
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
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
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
347
#endif // -----------------------------------------------------
349
//---------------------------------------------------------------------------------------
351
// Turbo timeout values, 16 msecs each: 30secs, 60 secs, 90 secs, 120 secs, 3 mins, 5 mins, 10 mins
353
//PROGMEM const word turboTimeOutVals[] = {0, 1875, 3750, 5625, 7500, 11250, 18750, 37500};
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;
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)
366
//----------------------------------------------------------------
367
// Global state options
368
//----------------------------------------------------------------
371
#define MAX_RAMP_LEVEL (RAMP_SIZE)
372
#define MAX_7135_LEVEL 65
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
386
// State and count vars:
388
volatile byte modeState; // Current operating state: 0=Ramping, 1=Tactical, 2=Lock Out, 3=Beacon mode, 4=Battery check, 5=Temp check
390
word wPressDuration = 0;
392
volatile byte fastClicks = 0; // fast click counts for dbl-click, triple-click, etc.
394
volatile word wIdleTicks = 0;
396
volatile byte LowBattSignal = 0; // a low battery has been detected - trigger/signal it
398
byte byStartupDelayTime; // Used to delay the WDT from doing anything for a short period at power up
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
405
volatile word wTempTicks = 0; // set to TEMP_ADJ_PERIOD, and decremented
407
byte b10Clicks = 0; // special 10 click state for turning thermal reg ON/OFF
409
// Configuration settings storage in EEPROM
410
word eepos = 0; // (0..n) position of mode/settings stored in EEPROM (128/256/512)
413
/**************************************************************************************
414
* SetOutput - sets the PWM output value directly
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)
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)
438
level -= 1; // make it 0 based
439
SetOutput(pgm_read_byte(ramp_FET + level), pgm_read_byte(ramp_7135 + level));
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)
447
SetActualLevel(level);
450
#define SetLevelSoft SetLevel
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)
457
uint8_t target_level = level;
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);
470
/**************************************************************************************
473
**************************************************************************************/
474
void Strobe(byte ontime, byte offtime)
476
SetLevel(MAX_RAMP_LEVEL);
482
/**************************************************************************************
483
* Blink - do a # of blinks with a speed in msecs
485
**************************************************************************************/
486
void Blink(byte val, word speed)
490
SetLevel(BLINK_BRIGHTNESS);
494
_delay_ms(speed<<2); // 4X delay OFF
498
/**************************************************************************************
499
* delay_unless_modeState_changed - like _delay_ms(), but aborts on mode change
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)
506
//uint8_t expected = modeState; // automatic mode; buggy
510
if (modeState != expected) return 0;
515
/**************************************************************************************
516
* DigitBlink - do a # of blinks with a speed in msecs
518
**************************************************************************************/
519
byte DigitBlink(byte val, byte blinkModeState)
521
if (modeState != blinkModeState) // if the mode changed, bail out
524
// zeroes get only a short blink
526
SetLevel(BLINK_BRIGHTNESS);
527
delay_unless_modeState_changed(8, blinkModeState);
529
if(!delay_unless_modeState_changed(375, blinkModeState)) return 0;
534
SetLevel(BLINK_BRIGHTNESS);
535
delay_unless_modeState_changed(250, blinkModeState);
537
if(!delay_unless_modeState_changed(375, blinkModeState)) return 0;
539
return 1; // Ok, not terminated
542
/**************************************************************************************
543
* BlinkOutNumber - blinks out a 2 digit #
545
**************************************************************************************/
546
void BlinkOutNumber(byte num, byte blinkModeState)
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))
553
if (!DigitBlink(num % 10, blinkModeState))
558
/**************************************************************************************
559
* BlinkLVP - blinks the specified time for use by LVP
561
* Supports both ramping and mode set modes.
562
**************************************************************************************/
563
void BlinkLVP(byte BlinkCnt, byte level)
569
// Flash 'n' times before lowering
570
while (BlinkCnt-- > 0)
575
_delay_ms(nMsecs<<1);
579
/**************************************************************************************
580
* IsPressed - debounce the switch release, not the switch press
582
**************************************************************************************/
583
// Debounce switch press value
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);
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);
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);
604
static int IsPressed()
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);
611
return (buffer & DB_REL_DUR);
616
/**************************************************************************************
617
* PCINT_on - Enable pin change interrupts
619
**************************************************************************************/
620
inline void PCINT_on()
622
// Enable pin change interrupts
623
GIMSK |= (1 << PCIE);
626
/**************************************************************************************
627
* PCINT_off - Disable pin change interrupts
629
**************************************************************************************/
630
inline void PCINT_off()
632
// Disable pin change interrupts
633
GIMSK &= ~(1 << PCIE);
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);
640
/**************************************************************************************
641
* WDT_on - Setup watchdog timer to only interrupt, not reset, every 16ms
643
**************************************************************************************/
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
654
/**************************************************************************************
655
* WDT_off - turn off the WatchDog timer
657
**************************************************************************************/
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
666
/**************************************************************************************
667
* ADC_on - Turn the AtoD Converter ON
669
**************************************************************************************/
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
678
/**************************************************************************************
679
* ADC_off - Turn the AtoD Converter OFF
681
**************************************************************************************/
682
inline void ADC_off()
684
ADCSRA &= ~(1<<ADEN); //ADC off
687
/**************************************************************************************
688
* SleepUntilSwitchPress - only called with the light OFF
689
* =====================
690
**************************************************************************************/
691
inline void SleepUntilSwitchPress()
693
// This routine takes up a lot of program memory :(
695
cli(); // Disable interrupts
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.
701
ADC_off(); // Save more power -- turn the AtoD OFF
703
// Need to reset press duration since a button release wasn't recorded
706
sei(); // Enable interrupts
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()) {
716
//-----------------------------------------
718
sleep_bod_disable(); // try to disable Brown-Out Detection to reduce parasitic drain
719
sleep_cpu(); // go to sleepy beddy bye
721
//-----------------------------------------
723
// Hey, to get here, someone must have pressed the switch!!
725
PCINT_off(); // Disable pin change interrupt because it's only used to wake us up
727
ADC_on(); // Turn the AtoD back ON
729
WDT_on(); // Turn the WDT back on to check for switch presses
731
} // Go back to main program
733
/**************************************************************************************
734
* LoadConfig - gets the configuration settings from EEPROM
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
741
**************************************************************************************/
742
inline void LoadConfig()
745
uint8_t config1, config2;
747
// find the config data
748
for (eepos=0; (!found) && (eepos < 127); eepos++)
750
config1 = eeprom_read_byte((const byte *)eepos);
751
config2 = eeprom_read_byte((const byte *)eepos+1);
753
// Only use the data if a valid marker is found
754
if (config1 < 0x80) {
760
// unpack the config data
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;
774
modeState = RAMPING_ST;
779
/**************************************************************************************
780
* SaveConfig - save the current mode with config settings
782
* Central method for writing (with wear leveling)
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
789
**************************************************************************************/
794
// Pack all settings into one byte
795
//config1 = (byte)(tacticalSet | (tempAdjEnable << 1));
796
config1 = (byte)(tacticalSet);
801
eepos = (eepos+1) & 127; // wear leveling, use next cell
803
cli(); // don't interrupt while writing eeprom; that's bad
805
// Erase the last settings
806
eeprom_write_byte((uint8_t *)(oldpos), 0xff);
807
eeprom_write_byte((uint8_t *)(oldpos+1), 0xff);
809
// Write the current settings
810
eeprom_write_byte((uint8_t *)(eepos), config1);
811
eeprom_write_byte((uint8_t *)(eepos+1), config2);
813
sei(); // okay, interrupts are fine now
816
/****************************************************************************************
817
* ADC_vect - ADC done interrupt service routine
819
* This should execute every 64ms (every 4 WDT ticks)
820
* So, voltage and temperature are each measured 4X per second.
821
****************************************************************************************/
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
828
// Read in the ADC 10 bit value (0..1023)
831
// Voltage Monitoring
832
if (adc_step == 1) // ignore first ADC value from step 0
834
// Calculate cell voltage from ADC value
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;
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)
848
if (voltageTenths < adcin) // crude low pass filter
850
if (voltageTenths > adcin)
854
voltageTenths = adcin; // prime on first read
858
// Temperature monitoring
859
if (adc_step == 3) // ignore first ADC value from step 2
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
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)
877
for(i=0; i<NUM_TEMP_VALUES-1; i++) {
878
temperature_values[i] = temperature_values[i+1];
879
total += temperature_values[i];
881
temperature_values[i] = adcin;
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)
890
else { // prime on first read
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
901
adc_step = (adc_step +1) & 0x03; // increment but keep in range of 0..3
903
if (adc_step < 2) // steps 0, 1 read voltage, steps 2, 3 read temperature
910
/**************************************************************************************
911
* WDT_vect - The watchdog timer - this is invoked every 16ms
913
**************************************************************************************/
916
//static word wTurboTicks = 0;
918
//static byte byBattCheck = 0;
919
static byte byModeForMultiClicks = 0; // the mode that originally was running before the 1st click
922
static byte byInitADCTimer = 0;
923
static word adc_ticks = ADC_DELAY;
924
static byte lowbatt_cnt = 0;
927
// Enforce a start-up delay so no switch actions processed initially
928
if (byStartupDelayTime > 0)
930
--byStartupDelayTime;
934
if (wTempTicks > 0) // decrement each tick if active
937
//---------------------------------------------------------------------------------------
938
//---------------------------------------------------------------------------------------
940
//---------------------------------------------------------------------------------------
941
//---------------------------------------------------------------------------------------
944
if (wPressDuration < 2000)
948
// Enter thermal calibration mode
951
if (wPressDuration >= 62) // if held more than 1 second
954
// enter thermal calibration mode
955
modeState = THERMAL_REG_ST;
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))
964
rampingLevel = MAX_RAMP_LEVEL;
965
SetLevel(rampingLevel);
968
// long-press for ramping
969
else if ((wPressDuration >= SHORT_CLICK_DUR) && (modeState == RAMPING_ST)) // also in there was: && !byDelayRamping
974
case 0: // ramping not initialized yet
975
if (rampingLevel == 0)
978
rampPauseCntDn = RAMP_MOON_PAUSE; // delay a little on moon
980
// set this to the 1st level
984
dontToggleDir = 0; // clear it in case it got set from a timeout
988
#ifdef RAMPING_REVERSE
991
rampState = rampLastDirState; // keep it in the same
992
dontToggleDir = 0; // clear it so it can't happen again til another timeout
995
rampState = 5 - rampLastDirState; // 2->3, or 3->2
997
rampState = 2; // lo->hi
999
if (rampingLevel == MAX_RAMP_LEVEL)
1001
rampState = 3; // hi->lo
1002
SetLevel(MAX_RAMP_LEVEL);
1004
else if (rampingLevel == 255) // If stopped in ramping moon mode, start from lowest
1006
rampState = 2; // lo->hi
1008
SetLevel(rampingLevel);
1010
else if (rampingLevel == 1)
1011
rampState = 2; // lo->hi
1015
case 1: // in moon mode
1016
if (--rampPauseCntDn == 0)
1018
rampState = 2; // lo->hi
1023
rampLastDirState = 2;
1024
if (rampingLevel < MAX_RAMP_LEVEL)
1032
if ((rampingLevel == MAX_RAMP_LEVEL) || (rampingLevel == MAX_7135_LEVEL)) {
1033
SetActualLevel(0); // Do a quick blink
1036
SetLevel(rampingLevel);
1041
rampLastDirState = 3;
1042
if (rampingLevel > 1)
1050
if ((rampingLevel == 1) || (rampingLevel == MAX_7135_LEVEL)) {
1051
SetActualLevel(0); // Do a quick blink
1054
SetLevel(rampingLevel);
1058
case 4: // ramping ended - 0 or max reached
1061
} // switch on rampState
1062
// ensure turbo toggle will return to ramped level, even if the ramp was at turbo
1063
preTurboLevel = rampingLevel;
1065
} // switch held longer than single click
1066
} // else (not b10Clicks)
1068
//wTurboTicks = 0; // Just always reset turbo timer whenever the button is pressed
1071
adc_ticks = ADC_DELAY; // Same with the ramp down delay
1075
//---------------------------------------------------------------------------------------
1076
//---------------------------------------------------------------------------------------
1077
// Not pressed (debounced qualified)
1078
//---------------------------------------------------------------------------------------
1079
//---------------------------------------------------------------------------------------
1083
// Was previously pressed - button released
1084
if (wPressDuration > 0)
1087
rampState = 0; // reset state to not ramping
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;
1096
// Momentary / tactical mode
1097
if (modeState == TACTICAL_ST)
1099
rampingLevel = 0; // turn off output as soon as the user lets the switch go
1103
// normal short click processing
1104
if (wPressDuration < SHORT_CLICK_DUR)
1107
++fastClicks; // track fast clicks in a row from OFF or ON (doesn't matter in ramping)
1109
//-----------------------------------------------------------------------------------
1110
//-----------------------------------------------------------------------------------
1114
if (fastClicks == 1)
1116
byModeForMultiClicks = modeState; // save current mode
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;
1131
// if we were on, turn off
1133
memorizedLevel = rampingLevel;
1136
SetLevelSoft(rampingLevel);
1138
//byDelayRamping = 1; // don't act on ramping button presses
1140
else if (fastClicks == 10) // --> 10 clicks: flip thermal regulation control
1142
else // --> 2+ clicks: turn off the output
1144
// prevent flashes while entering long click sequences
1146
SetLevel(rampingLevel);
1151
if (fastClicks == 1)
1152
byModeForMultiClicks = modeState; // save current mode
1153
else if (fastClicks == 10) // --> 10 clicks: flip thermal regulation control
1160
if (fastClicks == 1)
1162
byModeForMultiClicks = modeState; // save current mode
1164
modeState = tacticalSet; // set to Ramping or Tactical
1166
SetLevel(rampingLevel);
1167
//byDelayRamping = 1; // don't act on ramping button presses
1175
fastClicks = 0; // reset fast clicks for long holds
1176
b10Clicks = 0; // reset special 10 click flag
1181
//byDelayRamping = 0; // restore acting on ramping button presses, if disabled
1183
wIdleTicks = 0; // reset idle time
1185
} // previously pressed
1188
//---------------------------------------------------------------------------------------
1189
//---------------------------------------------------------------------------------------
1190
// Not previously pressed
1191
//---------------------------------------------------------------------------------------
1192
//---------------------------------------------------------------------------------------
1193
if (++wIdleTicks > 30000)
1194
wIdleTicks = 30000; // max it out at 30,000 (8 minutes)
1196
if (wIdleTicks > DBL_CLICK_TICKS) // Too much delay between clicks - time out the fast clicks
1198
//-----------------------------------------------------------------------------------
1199
// Process multi clicks here, after it times out
1200
//-----------------------------------------------------------------------------------
1204
// FIXME: disable extra states from tactical/momentary mode
1205
// (only do this in ramping mode, or from off)
1208
if (fastClicks == 2) // --> double click
1210
// --> battcheck to tempcheck
1211
// FIXME: why is this handled here instead of below under battcheck state?
1212
if (byModeForMultiClicks == BATT_READ_ST)
1214
modeState = TEMP_READ_ST;
1216
// --> ramp direct to MAX/turbo (or back)
1217
else if (modeState == RAMPING_ST)
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;
1225
preTurboLevel = memorizedLevel;
1226
rampingLevel = MAX_RAMP_LEVEL;
1228
SetLevelSoft(rampingLevel);
1232
#ifdef TRIPLE_CLICK_BATT
1233
else if (fastClicks == 3) // --> triple click: display battery check/status
1235
modeState = BATT_READ_ST;
1236
//byDelayRamping = 1; // don't act on ramping button presses
1240
else if (fastClicks == 4) // --> 4 clicks: set toggle Ramping/Tactical Mode
1242
if (modeState == RAMPING_ST)
1243
modeState = TACTICAL_ST;
1245
modeState = RAMPING_ST;
1246
//byDelayRamping = 1; // don't act on ramping button presses
1249
else if (fastClicks == 6) // --> 6 clicks: set Lockout Mode
1251
modeState = LOCKOUT_ST;
1252
//byDelayRamping = 1; // don't act on ramping button presses
1255
else if (fastClicks == 8) // --> 8 clicks: set Beacon Mode
1257
modeState = BEACON_ST;
1258
//byDelayRamping = 1; // don't act on ramping button presses
1263
if (fastClicks == 6) // --> 6 clicks: clear Lock Out Mode
1265
modeState = tacticalSet; // set to Ramping or Tactical
1266
//byDelayRamping = 1; // don't act on ramping button presses
1281
// For ramping, timeout the direction toggling
1282
if ((rampingLevel > 1) && (rampingLevel < MAX_RAMP_LEVEL))
1284
if (wIdleTicks > RAMP_SWITCH_TIMEOUT)
1288
//------------------------------------------------------------------------------
1289
// Do voltage monitoring when the switch isn't pressed
1290
//------------------------------------------------------------------------------
1296
// See if conversion is done
1299
atLowLimit = ((ActualLevel == 1));
1301
// See if voltage is lower than what we were looking for
1302
if (voltageTenths < (atLowLimit ? BATT_CRIT : BATT_LOW))
1308
// See if it's been low for a while
1309
if (lowbatt_cnt >= 4)
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;
1322
} // not previously pressed
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
1332
/**************************************************************************************
1333
* main - main program loop. This is where it all happens...
1335
**************************************************************************************/
1339
// Set all ports to input, and turn pull-up resistors on for the inputs we are using
1341
//PORTB = (1 << SWITCH_PIN) | (1 << STAR3_PIN);
1343
PORTB = (1 << SWITCH_PIN); // Only the switch is an input
1345
// Set the switch as an interrupt for when we turn pin change interrupts on
1346
PCMSK = (1 << SWITCH_PIN);
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);
1352
TCCR0A = PHASE; // set this once here - don't use FAST anymore
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...)
1357
// Turn features on or off as needed
1364
ACSR |= (1<<7); // Analog Comparator off
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);
1369
// Shut things off now, just in case we come up in a partial power state
1370
cli(); // Disable interrupts
1375
// Load config settings: tactical mode and temperature regulation
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;
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
1397
byte byPrevModeState = modeState;
1399
byStartupDelayTime = 25; // 400 msec delay in the WDT interrupt handler
1401
WDT_on(); // Turn it on now (mode can be non-zero on startup)
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
1412
//---------------------------------------------------------------------------------------
1413
// State Changes and Main Mode Handling
1414
//---------------------------------------------------------------------------------------
1415
if (byPrevModeState != modeState)
1417
byte savedPrevMode = byPrevModeState;
1418
byPrevModeState = modeState;
1423
// user released button during thermal calibration:
1424
if ((savedPrevMode == THERMAL_REG_ST) || (savedPrevMode == THERMAL_CAL_ST)) {
1425
// set limit to whatever they requested
1427
// ... and show them the new value
1430
BlinkOutNumber(tempCeil, RAMPING_ST);
1432
else if (savedPrevMode == TACTICAL_ST)
1438
tacticalSet = 0; // Save the state for ramping
1441
else if (savedPrevMode == LOCKOUT_ST)
1449
case TACTICAL_ST: // Entering Tactical
1450
if (savedPrevMode == RAMPING_ST)
1459
// FIXME: We can go from lockout to tactical???
1460
// (this code should probably be removed)
1461
else if (savedPrevMode == LOCKOUT_ST)
1469
case LOCKOUT_ST: // Entering Lock Out
1474
//byDelayRamping = 0; // restore acting on ramping button presses
1478
_delay_ms(300); // pause a little initially
1479
while (modeState == BEACON_ST) // Beacon mode
1481
SetLevel(memorizedLevel); // allow user to set level
1482
// on for 1/4 second
1483
delay_unless_modeState_changed(250, BEACON_ST);
1487
delay_unless_modeState_changed(2250, BEACON_ST);
1491
case THERMAL_REG_ST:
1493
// First part of thermal calibration mode:
1494
// check current value, option to set maximum,
1495
// then proceed to actual calibration
1497
// show previous ceiling value
1498
byte oldCeil = tempCeil;
1500
SetLevel(0); // blink ON/OFF
1502
BlinkOutNumber(oldCeil, THERMAL_REG_ST);
1504
aborted = ! IsPressed();
1506
// exit with no changes
1507
modeState = RAMPING_ST;
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);
1516
SetLevel(MAX_RAMP_LEVEL/7);
1518
aborted = ! IsPressed();
1523
modeState = RAMPING_ST;
1527
// else calibrate normally
1529
modeState = THERMAL_CAL_ST;
1534
_delay_ms(400); // delay a little here to give the user a chance to see a full blink sequence
1536
//byDelayRamping = 0; // restore acting on ramping button presses
1538
while (modeState == BATT_READ_ST) // Battery Check
1540
// blink out volts and tenths
1541
BlinkOutNumber(voltageTenths, BATT_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
1549
// blink out temperature in 2 digits
1550
// (divide by 4 to get an integer because the value is 13.2 fixed-point)
1552
// don't try to show negative numbers
1553
if (temperature > 0) {
1554
temp = temperature>>2;
1556
BlinkOutNumber(temp, TEMP_READ_ST);
1564
//---------------------------------------------------------------------
1565
// Perform low battery indicator tests
1566
//---------------------------------------------------------------------
1570
if (ActualLevel > 0)
1572
if (ActualLevel == 1)
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
1578
// TODO: test if it actually shuts off as far as possible after LVP
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; }
1588
SetLevelSoft(ActualLevel); // soft ramp down
1592
#endif // ifdef USE_LVP
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);
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)) {
1605
if (first_temp_reading) { // initial setup
1606
first_temp_reading = 0;
1608
for (i=0; i<THERM_HISTORY_SIZE; i++)
1609
temperatures[i] = temperature;
1612
// should happen every half-second or so
1613
if (wTempTicks == 0) {
1615
wTempTicks = TEMP_ADJ_PERIOD;
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!
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
1639
// rotate measurements and add a new one
1640
for (i=0; i<THERM_HISTORY_SIZE-1; i++) {
1641
temperatures[i] = temperatures[i+1];
1643
temperatures[THERM_HISTORY_SIZE-1] = temperature;
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);
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;
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) {
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;
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;
1681
// really, don't try to regulate below the floor
1682
if (ActualLevel > THERM_FLOOR) {
1683
SetActualLevel(stepdown);
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
1705
} // thermal regulation
1707
//---------------------------------------------------------------------
1708
// Be sure switch is not pressed and light is OFF for at least 5 secs
1709
//---------------------------------------------------------------------
1710
word wWaitTicks = 300; // ~5 secs
1712
if ((ActualLevel == 0) && !IsPressed() && (wIdleTicks > wWaitTicks))
1715
_delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
1716
SleepUntilSwitchPress(); // Go to sleep
1721
return 0; // Standard Return Code