2
* This is intended for use on flashlights with a clicky switch.
3
* Ideally, a Nichia 219B running at 1900mA in a Convoy S-series host.
4
* It's mostly based on JonnyC's STAR on-time firmware and ToyKeeper's
7
* Original author: JonnyC
8
* Modifications: ToyKeeper / Selene Scriven
13
* Star 4 -| |- Voltage ADC
18
* CPU speed is 4.8Mhz without the 8x divider when low fuse is 0x79
20
* define F_CPU 4800000 CPU: 4.8MHz PWM: 9.4kHz ####### use low fuse: 0x79 #######
22
* Above PWM speeds are for phase-correct PWM. This program uses Fast-PWM,
23
* which when the CPU is 4.8MHz will be 18.75 kHz
26
* I use these fuse settings
29
* (flash-noinit.sh has an example)
34
* Resistor values for voltage divider (reference BLF-VLD README for more info)
35
* Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
39
* Vd (~.25 v drop from protection diode)
41
* 1912 (R1 19,100 ohms)
45
* 4701 (R2 4,700 ohms)
49
* ADC = ((V_bat - V_diode) * R2 * 255) / ((R1 + R2 ) * V_ref)
50
* 125 = ((3.0 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
51
* 121 = ((2.9 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
53
* Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
56
* To find out what value to use, plug in the target voltage (V) to this equation
57
* value = (V * 4700 * 255) / (23800 * 1.1)
60
#define F_CPU 4800000UL
63
* =========================================================================
64
* Settings to modify per driver
67
#define VOLTAGE_MON // Comment out to disable
68
#define OWN_DELAY // Should we use the built-in delay or our own?
70
// Lumen measurements used a Nichia 219B at 1900mA in a Convoy S7 host
71
#define MODE_MOON 6 // 6: 0.14 lm (6 through 9 may be useful levels)
72
#define MODE_LOW 14 // 14: 7.3 lm
73
#define MODE_MED 39 // 39: 42 lm
74
#define MODE_HIGH 120 // 120: 155 lm
75
#define MODE_HIGHER 255 // 255: 342 lm
76
// If you change these, you'll probably want to change the "modes" array below
77
#define SOLID_MODES 5 // How many non-blinky modes will we have?
78
#define DUAL_BEACON_MODES 5+3 // How many beacon modes will we have (with background light on)?
79
#define SINGLE_BEACON_MODES 5+3+1 // How many beacon modes will we have (without background light on)?
80
#define FIXED_STROBE_MODES 5+3+1+3 // How many constant-speed strobe modes?
81
#define VARIABLE_STROBE_MODES 5+3+1+3+2 // How many variable-speed strobe modes?
82
#define BATT_CHECK_MODE 5+3+1+3+2+1 // battery check mode index
83
// Note: don't use more than 32 modes, or it will interfere with the mechanism used for mode memory
84
#define TOTAL_MODES BATT_CHECK_MODE
86
//#define ADC_LOW 130 // When do we start ramping
87
//#define ADC_CRIT 120 // When do we shut the light off
89
#define ADC_42 185 // the ADC value we expect for 4.20 volts
90
#define ADC_100 185 // the ADC value for 100% full (4.2V resting)
91
#define ADC_75 175 // the ADC value for 75% full (4.0V resting)
92
#define ADC_50 164 // the ADC value for 50% full (3.8V resting)
93
#define ADC_25 154 // the ADC value for 25% full (3.6V resting)
94
#define ADC_0 139 // the ADC value for 0% full (3.3V resting)
95
#define ADC_LOW 123 // When do we start ramping down
96
#define ADC_CRIT 113 // When do we shut the light off
99
* =========================================================================
103
#include <util/delay_basic.h>
104
// Having own _delay_ms() saves some bytes AND adds possibility to use variables as input
105
static void _delay_ms(uint16_t n)
107
// TODO: make this take tenths of a ms instead of ms,
108
// for more precise timing?
109
// (would probably be better than the if/else here for a special-case
110
// sub-millisecond delay)
111
if (n==0) { _delay_loop_2(300); }
118
#include <util/delay.h>
121
#include <avr/pgmspace.h>
122
#include <avr/interrupt.h>
123
#include <avr/eeprom.h>
124
#include <avr/sleep.h>
126
#define STAR2_PIN PB0
127
#define STAR3_PIN PB4
128
#define STAR4_PIN PB3
130
#define VOLTAGE_PIN PB2
131
#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
132
#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
133
#define ADC_PRSCL 0x06 // clk/64
135
#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
142
// store in uninitialized memory so it will not be overwritten and
143
// can still be read at startup after short (<500ms) power off
144
// decay used to tell if user did a short press.
145
volatile uint8_t noinit_decay __attribute__ ((section (".noinit")));
146
volatile uint8_t noinit_mode __attribute__ ((section (".noinit")));
148
// change to 1 if you want on-time mode memory instead of "short-cycle" memory
149
// (actually, don't. It's not supported any more, and doesn't work)
152
// Modes (hardcoded to save space)
153
static uint8_t modes[TOTAL_MODES] = { // high enough to handle all
154
MODE_MOON, MODE_LOW, MODE_MED, MODE_HIGH, MODE_HIGHER, // regular solid modes
155
MODE_MOON, MODE_LOW, MODE_MED, // dual beacon modes (this level and this level + 2)
156
MODE_HIGHER, // heartbeat beacon
157
79, 41, 15, // constant-speed strobe modes (12.5 Hz, 24 Hz, 60 Hz)
158
MODE_HIGHER, MODE_HIGHER, // variable-speed strobe modes
159
MODE_MED, // battery check mode
161
volatile uint8_t mode_idx = 0;
162
// 1 or -1. Do we increase or decrease the idx when moving up to a higher mode?
163
// Is set by checking stars in the original STAR firmware, but that's removed to save space.
165
PROGMEM const uint8_t voltage_blinks[] = {
166
ADC_0, // 1 blink for 0%-25%
167
ADC_25, // 2 blinks for 25%-50%
168
ADC_50, // 3 blinks for 50%-75%
169
ADC_75, // 4 blinks for 75%-100%
170
ADC_100, // 5 blinks for >100%
173
inline void next_mode() {
174
mode_idx += mode_dir;
175
if (mode_idx > (TOTAL_MODES - 1)) {
181
inline void ADC_on() {
182
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
183
DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
184
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
187
inline void ADC_off() {
188
ADCSRA &= ~(1<<7); //ADC off
192
uint8_t get_voltage() {
194
ADCSRA |= (1 << ADSC);
195
// Wait for completion
196
while (ADCSRA & (1 << ADSC));
197
// See if voltage is lower than what we were looking for
204
// All ports default to input, but turn pull-up resistors on for the stars
205
// (not the ADC input! Made that mistake already)
207
//PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN) | (1 << STAR4_PIN);
209
// Set PWM pin to output
210
DDRB = (1 << PWM_PIN);
212
// Set timer to do PWM for correct output pin and set prescaler timing
213
TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
214
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
216
// Turn features on or off as needed
222
ACSR |= (1<<7); //AC off
224
// Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
225
// Will allow us to go idle between WDT interrupts (which we're not using anyway)
226
set_sleep_mode(SLEEP_MODE_IDLE);
228
// Determine what mode we should fire up
229
// Read the last mode that was saved
230
if (noinit_decay) // not short press, forget mode
234
} else { // short press, advance to next mode
235
mode_idx = noinit_mode;
237
noinit_mode = mode_idx;
239
// set noinit data for next boot
242
// Now just fire up the mode
243
PWM_LVL = modes[mode_idx];
247
uint8_t strobe_len = 0;
249
uint8_t lowbatt_cnt = 0;
253
if(mode_idx < SOLID_MODES) { // Just stay on at a given brightness
255
} else if (mode_idx < DUAL_BEACON_MODES) { // two-level fast strobe pulse at about 1 Hz
257
PWM_LVL = modes[mode_idx-SOLID_MODES+2];
259
PWM_LVL = modes[mode_idx];
263
} else if (mode_idx < SINGLE_BEACON_MODES) { // heartbeat flasher
264
PWM_LVL = modes[SOLID_MODES-1];
268
PWM_LVL = modes[SOLID_MODES-1];
272
} else if (mode_idx < FIXED_STROBE_MODES) { // strobe mode, fixed-speed
274
if (modes[mode_idx] < 50) { strobe_len = 0; }
275
PWM_LVL = modes[SOLID_MODES-1];
276
_delay_ms(strobe_len);
278
_delay_ms(modes[mode_idx]);
279
} else if (mode_idx == VARIABLE_STROBE_MODES-2) {
280
// strobe mode, smoothly oscillating frequency ~7 Hz to ~18 Hz
281
for(j=0; j<66; j++) {
282
PWM_LVL = modes[SOLID_MODES-1];
285
if (j<33) { strobe_len = j; }
286
else { strobe_len = 66-j; }
287
_delay_ms(2*(strobe_len+33-6));
289
} else if (mode_idx == VARIABLE_STROBE_MODES-1) {
290
// strobe mode, smoothly oscillating frequency ~16 Hz to ~100 Hz
291
for(j=0; j<100; j++) {
292
PWM_LVL = modes[SOLID_MODES-1];
293
_delay_ms(0); // less than a millisecond
295
if (j<50) { strobe_len = j; }
296
else { strobe_len = 100-j; }
297
_delay_ms(strobe_len+9);
299
} else if (mode_idx < BATT_CHECK_MODE) {
301
// turn off and wait one second before showing the value
302
// (also, ensure voltage is measured while not under load)
305
voltage = get_voltage();
306
voltage = get_voltage(); // the first one is unreliable
307
// division takes too much flash space
308
//voltage = (voltage-ADC_LOW) / (((ADC_42 - 15) - ADC_LOW) >> 2);
309
// a table uses less space than 5 logic clauses
310
for (i=0; i<sizeof(voltage_blinks); i++) {
311
if (voltage > pgm_read_byte(voltage_blinks + i)) {
316
// blink up to five times to show voltage
317
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
318
for(i=0; i<blinks; i++) {
325
_delay_ms(1000); // wait at least 1 second between readouts
328
if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready
329
voltage = get_voltage();
330
// See if voltage is lower than what we were looking for
331
if (voltage < ((mode_idx == 0) ? ADC_CRIT : ADC_LOW)) {
336
// See if it's been low for a while, and maybe step down
337
if (lowbatt_cnt >= 3) {
340
} else { // Already at the lowest mode
341
// Turn off the light
343
// Power down as many components as possible
344
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
348
// Wait at least 1 second before lowering the level again
349
_delay_ms(1000); // this will interrupt blinky modes
352
// Make sure conversion is running for next time through
353
ADCSRA |= (1 << ADSC);