Untitled diff

Created Diff never expires
24 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
736 lines
18 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
733 lines
/*
/*
* "Bistro" firmware
* "Bistro" firmware
* This code runs on a single-channel or dual-channel driver (FET+7135)
* This code runs on a single-channel or dual-channel driver (FET+7135)
* with an attiny25/45/85 MCU and a capacitor to measure offtime (OTC).
* with an attiny25/45/85 MCU and a capacitor to measure offtime (OTC).
*
*
* Copyright (C) 2015 Selene Scriven
* Copyright (C) 2015 Selene Scriven
*
*
* This program is free software: you can redistribute it and/or modify
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*
*
* ATTINY25/45/85 Diagram
* ATTINY25/45/85 Diagram
* ----
* ----
* -|1 8|- VCC
* -|1 8|- VCC
* OTC -|2 7|- Voltage ADC
* OTC -|2 7|- Voltage ADC
* Star 3 -|3 6|- PWM (FET, optional)
* Star 3 -|3 6|- PWM (FET, optional)
* GND -|4 5|- PWM (1x7135)
* GND -|4 5|- PWM (1x7135)
* ----
* ----
*
*
* FUSES
* FUSES
* I use these fuse settings on attiny25
* I use these fuse settings on attiny25
* Low: 0xd2
* Low: 0xd2
* High: 0xde
* High: 0xde
* Ext: 0xff
* Ext: 0xff
*
*
* For more details on these settings:
* For more details on these settings:
* http://www.engbedded.com/cgi-bin/fcx.cgi?P_PREV=ATtiny25&P=ATtiny25&M_LOW_0x3F=0x12&M_HIGH_0x07=0x06&M_HIGH_0x20=0x00&B_SPIEN=P&B_SUT0=P&B_CKSEL3=P&B_CKSEL2=P&B_CKSEL0=P&B_BODLEVEL0=P&V_LOW=E2&V_HIGH=DE&V_EXTENDED=FF
* http://www.engbedded.com/cgi-bin/fcx.cgi?P_PREV=ATtiny25&P=ATtiny25&M_LOW_0x3F=0x12&M_HIGH_0x07=0x06&M_HIGH_0x20=0x00&B_SPIEN=P&B_SUT0=P&B_CKSEL3=P&B_CKSEL2=P&B_CKSEL0=P&B_BODLEVEL0=P&V_LOW=E2&V_HIGH=DE&V_EXTENDED=FF
*
*
* STARS
* STARS
* Star 3 = unused
* Star 3 = unused
*
*
* CALIBRATION
* CALIBRATION
*
*
* To find out what values to use, flash the driver with battcheck.hex
* To find out what values to use, flash the driver with battcheck.hex
* and hook the light up to each voltage you need a value for. This is
* and hook the light up to each voltage you need a value for. This is
* much more reliable than attempting to calculate the values from a
* much more reliable than attempting to calculate the values from a
* theoretical formula.
* theoretical formula.
*
*
* Same for off-time capacitor values. Measure, don't guess.
* Same for off-time capacitor values. Measure, don't guess.
*/
*/
// Choose your MCU here, or in the build script
// Choose your MCU here, or in the build script
//#define ATTINY 13
//#define ATTINY 13
//#define ATTINY 25
//#define ATTINY 25
// FIXME: make 1-channel vs 2-channel power a single #define option
// FIXME: make 1-channel vs 2-channel power a single #define option
#define FET_7135_LAYOUT // specify an I/O pin layout
#define FET_7135_LAYOUT // specify an I/O pin layout
// Also, assign I/O pins in this file:
// Also, assign I/O pins in this file:
#include "tk-attiny.h"
#include "tk-attiny.h"


/*
/*
* =========================================================================
* =========================================================================
* Settings to modify per driver
* Settings to modify per driver
*/
*/


// FIXME: make 1-channel vs 2-channel power a single #define option
// FIXME: make 1-channel vs 2-channel power a single #define option
//#define FAST 0x23 // fast PWM channel 1 only
//#define FAST 0x23 // fast PWM channel 1 only
//#define PHASE 0x21 // phase-correct PWM channel 1 only
//#define PHASE 0x21 // phase-correct PWM channel 1 only
#define FAST 0xA3 // fast PWM both channels
#define FAST 0xA3 // fast PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels


#define VOLTAGE_MON // Comment out to disable LVP
#define VOLTAGE_MON // Comment out to disable LVP


#define OFFTIM3 // Use short/med/long off-time presses
#define OFFTIM3 // Use short/med/long off-time presses
// instead of just short/long
// instead of just short/long


// ../../bin/level_calc.py 64 1 10 1400 y 3 0.25 140
// ../../bin/level_calc.py 64 1 10 1300 y 3 0.23 140
#define RAMP_SIZE 64
#define RAMP_SIZE 64
// log curve
// log curve
//#define RAMP_7135 3,3,3,3,3,3,4,4,4,4,4,5,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,21,23,27,30,34,39,44,50,57,65,74,85,97,111,127,145,166,190,217,248,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_7135 3,3,3,3,3,3,4,4,4,4,4,5,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,21,23,27,30,34,39,44,50,57,65,74,85,97,111,127,145,166,190,217,248,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,11,17,23,30,39,48,59,72,86,103,121,143,168,197,255
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,11,17,23,30,39,48,59,72,86,103,121,143,168,197,255
// x**2 curve
// x**2 curve
//#define RAMP_7135 3,5,8,12,17,24,32,41,51,63,75,90,105,121,139,158,178,200,223,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_7135 3,5,8,12,17,24,32,41,51,63,75,90,105,121,139,158,178,200,223,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,6,9,12,16,19,22,26,30,33,37,41,45,50,54,59,63,68,73,78,84,89,94,100,106,111,117,123,130,136,142,149,156,162,169,176,184,191,198,206,214,221,255
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,6,9,12,16,19,22,26,30,33,37,41,45,50,54,59,63,68,73,78,84,89,94,100,106,111,117,123,130,136,142,149,156,162,169,176,184,191,198,206,214,221,255
// x**3 curve
// x**3 curve
#define RAMP_7135 3,3,4,5,7,8,10,13,16,20,25,30,36,42,50,59,68,78,90,103,116,131,148,165,184,204,226,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define RAMP_7135 3,3,4,5,6,8,10,12,15,19,23,28,33,40,47,55,63,73,84,95,108,122,137,153,171,190,210,232,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,7,10,13,16,20,24,28,32,36,41,46,51,56,61,67,73,80,86,93,100,107,115,123,131,139,148,157,166,176,186,196,207,218,255
#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,8,11,14,18,22,26,30,34,39,44,49,54,59,65,71,77,84,91,98,105,113,121,129,137,146,155,164,174,184,194,205,216,255
// x**5 curve
// x**5 curve
//#define RAMP_7135 3,3,3,4,4,5,5,6,7,8,10,11,13,15,18,21,24,28,33,38,44,50,57,66,75,85,96,108,122,137,154,172,192,213,237,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_7135 3,3,3,4,4,5,5,6,7,8,10,11,13,15,18,21,24,28,33,38,44,50,57,66,75,85,96,108,122,137,154,172,192,213,237,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,9,13,17,21,25,30,35,41,47,53,60,67,75,83,91,101,111,121,132,144,156,169,183,198,213,255
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,9,13,17,21,25,30,35,41,47,53,60,67,75,83,91,101,111,121,132,144,156,169,183,198,213,255


// uncomment to ramp up/down to a mode instead of jumping directly
// uncomment to ramp up/down to a mode instead of jumping directly
#define SOFT_START
#define SOFT_START


// Enable battery indicator mode?
// Enable battery indicator mode?
#define USE_BATTCHECK
#define USE_BATTCHECK
// Choose a battery indicator style
// Choose a battery indicator style
//#define BATTCHECK_4bars // up to 4 blinks
//#define BATTCHECK_4bars // up to 4 blinks
//#define BATTCHECK_8bars // up to 8 blinks
//#define BATTCHECK_8bars // up to 8 blinks
#define BATTCHECK_VpT // Volts + tenths
#define BATTCHECK_VpT // Volts + tenths


// output to use for blinks on battery check (and other modes)
// output to use for blinks on battery check (and other modes)
#define BLINK_BRIGHTNESS RAMP_SIZE/4
#define BLINK_BRIGHTNESS RAMP_SIZE/4
// ms per normal-speed blink
// ms per normal-speed blink
#define BLINK_SPEED 500
#define BLINK_SPEED 500


// Hidden modes are *before* the lowest (moon) mode, and should be specified
// Hidden modes are *before* the lowest (moon) mode, and should be specified
// in reverse order. So, to go backward from moon to turbo to strobe to
// in reverse order. So, to go backward from moon to turbo to strobe to
// battcheck, use BATTCHECK,STROBE,TURBO .
// battcheck, use BATTCHECK,STROBE,TURBO .
#define HIDDENMODES BIKING_STROBE,BATTCHECK,POLICE_STROBE,TURBO
#define HIDDENMODES BIKING_STROBE,BATTCHECK,POLICE_STROBE,TURBO


#define TURBO RAMP_SIZE // Convenience code for turbo mode
#define TURBO RAMP_SIZE // Convenience code for turbo mode
#define BATTCHECK 254 // Convenience code for battery check mode
#define BATTCHECK 254 // Convenience code for battery check mode
#define GROUP_SELECT_MODE 253
#define GROUP_SELECT_MODE 253
#define TEMP_CAL_MODE 252
#define TEMP_CAL_MODE 252
// Uncomment to enable tactical strobe mode
// Uncomment to enable tactical strobe mode
//#define STROBE 251 // Convenience code for strobe mode
//#define STROBE 251 // Convenience code for strobe mode
// Uncomment to unable a 2-level stutter beacon instead of a tactical strobe
// Uncomment to unable a 2-level stutter beacon instead of a tactical strobe
#define BIKING_STROBE 250 // Convenience code for biking strobe mode
#define BIKING_STROBE 250 // Convenience code for biking strobe mode
// comment out to use minimal version instead (smaller)
// comment out to use minimal version instead (smaller)
#define FULL_BIKING_STROBE
#define FULL_BIKING_STROBE
//#define RAMP 249 // ramp test mode for tweaking ramp shape
//#define RAMP 249 // ramp test mode for tweaking ramp shape
#define POLICE_STROBE 248
#define POLICE_STROBE 248
//#define RANDOM_STROBE 247
//#define RANDOM_STROBE 247


// thermal step-down
// thermal step-down
#define TEMPERATURE_MON
#define TEMPERATURE_MON


// Calibrate voltage and OTC in this file:
// Calibrate voltage and OTC in this file:
#include "tk-calibration.h"
#include "tk-calibration.h"


/*
/*
* =========================================================================
* =========================================================================
*/
*/


// Ignore a spurious warning, we did the cast on purpose
// Ignore a spurious warning, we did the cast on purpose
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"


#include <avr/pgmspace.h>
#include <avr/pgmspace.h>
//#include <avr/io.h>
//#include <avr/io.h>
//#include <avr/interrupt.h>
//#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <avr/eeprom.h>
#include <avr/sleep.h>
#include <avr/sleep.h>
//#include <avr/power.h>
//#include <avr/power.h>
#include <string.h>
#include <string.h>


#define OWN_DELAY // Don't use stock delay functions.
#define OWN_DELAY // Don't use stock delay functions.
#define USE_DELAY_S // Also use _delay_s(), not just _delay_ms()
#define USE_DELAY_S // Also use _delay_s(), not just _delay_ms()
#include "tk-delay.h"
#include "tk-delay.h"


#include "tk-voltage.h"
#include "tk-voltage.h"


#ifdef RANDOM_STROBE
#ifdef RANDOM_STROBE
#include "tk-random.h"
#include "tk-random.h"
#endif
#endif


/*
/*
* global variables
* global variables
*/
*/


// Config option variables
// Config option variables
#define FIRSTBOOT 0b01010101
#define FIRSTBOOT 0b01010101
uint8_t firstboot = FIRSTBOOT; // detect initial boot or factory reset
uint8_t firstboot = FIRSTBOOT; // detect initial boot or factory reset
uint8_t modegroup = 5; // which mode group (set above in #defines)
uint8_t modegroup = 5; // which mode group (set above in #defines)
uint8_t enable_moon = 1; // Should we add moon to the set of modes?
uint8_t enable_moon = 1; // Should we add moon to the set of modes?
uint8_t reverse_modes = 0; // flip the mode order?
uint8_t reverse_modes = 0; // flip the mode order?
uint8_t memory = 0; // mode memory, or not (set via soldered star)
uint8_t memory = 0; // mode memory, or not (set via soldered star)
#ifdef OFFTIM3
#ifdef OFFTIM3
uint8_t offtim3 = 1; // enable medium-press?
uint8_t offtim3 = 1; // enable medium-press?
#endif
#endif
#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
uint8_t maxtemp = 79; // temperature step-down threshold
uint8_t maxtemp = 79; // temperature step-down threshold
#endif
#endif
uint8_t muggle_mode = 0; // simple mode designed for muggles
uint8_t muggle_mode = 0; // simple mode designed for muggles
// Other state variables
// Other state variables
uint8_t mode_override = 0; // do we need to enter a special mode?
uint8_t mode_override = 0; // do we need to enter a special mode?
uint8_t mode_idx = 0; // current or last-used mode number
uint8_t mode_idx = 0; // current or last-used mode number
uint8_t eepos = 0;
uint8_t eepos = 0;
// counter for entering config mode
// counter for entering config mode
// (needs to be remembered while off, but only for up to half a second)
// (needs to be remembered while off, but only for up to half a second)
uint8_t fast_presses __attribute__ ((section (".noinit")));
uint8_t fast_presses __attribute__ ((section (".noinit")));


// total length of current mode group's array
// total length of current mode group's array
uint8_t mode_cnt;
uint8_t mode_cnt;
// number of regular non-hidden modes in current mode group
// number of regular non-hidden modes in current mode group
uint8_t solid_modes;
uint8_t solid_modes;
// number of hidden modes in the current mode group
// number of hidden modes in the current mode group
// (hardcoded because both groups have the same hidden modes)
// (hardcoded because both groups have the same hidden modes)
//uint8_t hidden_modes = NUM_HIDDEN; // this is never used
//uint8_t hidden_modes = NUM_HIDDEN; // this is never used




PROGMEM const uint8_t hiddenmodes[] = { HIDDENMODES };
PROGMEM const uint8_t hiddenmodes[] = { HIDDENMODES };
// default values calculated by group_calc.py
// default values calculated by group_calc.py
// Each group must be 8 values long, but can be cut short with a zero.
// Each group must be 8 values long, but can be cut short with a zero.
#define NUM_MODEGROUPS 8 // don't count muggle mode
#define NUM_MODEGROUPS 9 // don't count muggle mode
PROGMEM const uint8_t modegroups[] = {
PROGMEM const uint8_t modegroups[] = {
64, 0, 0, 0, 0, 0, 0, 0,
64, 0, 0, 0, 0, 0, 0, 0,
10, 64, 0, 0, 0, 0, 0, 0,
11, 64, 0, 0, 0, 0, 0, 0,
10, 37, 64, 0, 0, 0, 0, 0,
11, 35, 64, 0, 0, 0, 0, 0,
10, 28, 46, 64, 0, 0, 0, 0,
11, 26, 46, 64, 0, 0, 0, 0,
10, 23, 37, 50, 64, 0, 0, 0,
11, 23, 36, 50, 64, 0, 0, 0,
10, 20, 31, 42, 53, 64, 0, 0,
11, 20, 31, 41, 53, 64, 0, 0,
10, 19, 28, 37, 46, 54, 64, 0,
29, 64,POLICE_STROBE,0,0,0,0,0, // 7: special group A
10, 17, 25, 33, 40, 48, 56, 64,
BIKING_STROBE,BATTCHECK,11,29,64,0,0,0, // 8: special group B
10, 30, 50, 0, // muggle mode, exception to "must be 8 bytes long"
9, 18, 29, 46, 64, 0, 0, 0, // 9: special group C
11, 29, 50, 0, // muggle mode, exception to "must be 8 bytes long"
};
};
uint8_t modes[] = { 1,2,3,4,5,6,7,8,9, HIDDENMODES }; // make sure this is long enough...
uint8_t modes[] = { 1,2,3,4,5,6,7,8,9, HIDDENMODES }; // make sure this is long enough...


// Modes (gets set when the light starts up based on saved config values)
// Modes (gets set when the light starts up based on saved config values)
PROGMEM const uint8_t ramp_7135[] = { RAMP_7135 };
PROGMEM const uint8_t ramp_7135[] = { RAMP_7135 };
PROGMEM const uint8_t ramp_FET[] = { RAMP_FET };
PROGMEM const uint8_t ramp_FET[] = { RAMP_FET };


void save_mode() { // save the current mode index (with wear leveling)
void save_mode() { // save the current mode index (with wear leveling)
uint8_t oldpos=eepos;
uint8_t oldpos=eepos;


eepos = (eepos+1) & ((EEPSIZE/2)-1); // wear leveling, use next cell
eepos = (eepos+1) & ((EEPSIZE/2)-1); // wear leveling, use next cell


eeprom_write_byte((uint8_t *)(eepos), mode_idx); // save current state
eeprom_write_byte((uint8_t *)(eepos), mode_idx); // save current state
eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state
eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state
}
}


#define OPT_firstboot (EEPSIZE-1)
#define OPT_firstboot (EEPSIZE-1)
#define OPT_modegroup (EEPSIZE-2)
#define OPT_modegroup (EEPSIZE-2)
#define OPT_memory (EEPSIZE-3)
#define OPT_memory (EEPSIZE-3)
#define OPT_offtim3 (EEPSIZE-4)
#define OPT_offtim3 (EEPSIZE-4)
#define OPT_maxtemp (EEPSIZE-5)
#define OPT_maxtemp (EEPSIZE-5)
#define OPT_mode_override (EEPSIZE-6)
#define OPT_mode_override (EEPSIZE-6)
#define OPT_moon (EEPSIZE-7)
#define OPT_moon (EEPSIZE-7)
#define OPT_revmodes (EEPSIZE-8)
#define OPT_revmodes (EEPSIZE-8)
#define OPT_muggle (EEPSIZE-9)
#define OPT_muggle (EEPSIZE-9)
void save_state() { // central method for writing complete state
void save_state() { // central method for writing complete state
save_mode();
save_mode();
eeprom_write_byte((uint8_t *)OPT_firstboot, firstboot);
eeprom_write_byte((uint8_t *)OPT_firstboot, firstboot);
eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup);
eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup);
eeprom_write_byte((uint8_t *)OPT_memory, memory);
eeprom_write_byte((uint8_t *)OPT_memory, memory);
#ifdef OFFTIM3
#ifdef OFFTIM3
eeprom_write_byte((uint8_t *)OPT_offtim3, offtim3);
eeprom_write_byte((uint8_t *)OPT_offtim3, offtim3);
#endif
#endif
#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
eeprom_write_byte((uint8_t *)OPT_maxtemp, maxtemp);
eeprom_write_byte((uint8_t *)OPT_maxtemp, maxtemp);
#endif
#endif
eeprom_write_byte((uint8_t *)OPT_mode_override, mode_override);
eeprom_write_byte((uint8_t *)OPT_mode_override, mode_override);
eeprom_write_byte((uint8_t *)OPT_moon, enable_moon);
eeprom_write_byte((uint8_t *)OPT_moon, enable_moon);
eeprom_write_byte((uint8_t *)OPT_revmodes, reverse_modes);
eeprom_write_byte((uint8_t *)OPT_revmodes, reverse_modes);
eeprom_write_byte((uint8_t *)OPT_muggle, muggle_mode);
eeprom_write_byte((uint8_t *)OPT_muggle, muggle_mode);
}
}


void restore_state() {
void restore_state() {
uint8_t eep;
uint8_t eep;


// check if this is the first time we have powered on
// check if this is the first time we have powered on
eep = eeprom_read_byte((uint8_t *)OPT_firstboot);
eep = eeprom_read_byte((uint8_t *)OPT_firstboot);
if (eep != FIRSTBOOT) {
if (eep != FIRSTBOOT) {
// not much to do; the defaults should already be set
// not much to do; the defaults should already be set
// while defining the variables above
// while defining the variables above
save_state();
save_state();
return;
return;
}
}


// find the mode index data
// find the mode index data
for(eepos=0; eepos<(EEPSIZE/2); eepos++) {
for(eepos=0; eepos<(EEPSIZE/2); eepos++) {
eep = eeprom_read_byte((const uint8_t *)eepos);
eep = eeprom_read_byte((const uint8_t *)eepos);
if (eep != 0xff) {
if (eep != 0xff) {
mode_idx = eep;
mode_idx = eep;
break;
break;
}
}
}
}


// load other config values
// load other config values
modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup);
modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup);
memory = eeprom_read_byte((uint8_t *)OPT_memory);
memory = eeprom_read_byte((uint8_t *)OPT_memory);
#ifdef OFFTIM3
#ifdef OFFTIM3
offtim3 = eeprom_read_byte((uint8_t *)OPT_offtim3);
offtim3 = eeprom_read_byte((uint8_t *)OPT_offtim3);
#endif
#endif
#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
maxtemp = eeprom_read_byte((uint8_t *)OPT_maxtemp);
maxtemp = eeprom_read_byte((uint8_t *)OPT_maxtemp);
#endif
#endif
mode_override = eeprom_read_byte((uint8_t *)OPT_mode_override);
mode_override = eeprom_read_byte((uint8_t *)OPT_mode_override);
enable_moon = eeprom_read_byte((uint8_t *)OPT_moon);
enable_moon = eeprom_read_byte((uint8_t *)OPT_moon);
reverse_modes = eeprom_read_byte((uint8_t *)OPT_revmodes);
reverse_modes = eeprom_read_byte((uint8_t *)OPT_revmodes);
muggle_mode = eeprom_read_byte((uint8_t *)OPT_muggle);
muggle_mode = eeprom_read_byte((uint8_t *)OPT_muggle);


// unnecessary, save_state handles wrap-around
// unnecessary, save_state handles wrap-around
// (and we don't really care about it skipping cell 0 once in a while)
// (and we don't really care about it skipping cell 0 once in a while)
//else eepos=0;
//else eepos=0;
}
}


inline void next_mode() {
inline void next_mode() {
mode_idx += 1;
mode_idx += 1;
if (mode_idx >= solid_modes) {
if (mode_idx >= solid_modes) {
// Wrap around, skipping the hidden modes
// Wrap around, skipping the hidden modes
// (note: this also applies when going "forward" from any hidden mode)
// (note: this also applies when going "forward" from any hidden mode)
// FIXME? Allow this to cycle through hidden modes?
// FIXME? Allow this to cycle through hidden modes?
mode_idx = 0;
mode_idx = 0;
}
}
}
}


#ifdef OFFTIM3
#ifdef OFFTIM3
inline void prev_mode() {
inline void prev_mode() {
// simple mode has no reverse
// simple mode has no reverse
if (muggle_mode) { return next_mode(); }
if (muggle_mode) { return next_mode(); }


if (mode_idx == solid_modes) {
if (mode_idx == solid_modes) {
// If we hit the end of the hidden modes, go back to moon
// If we hit the end of the hidden modes, go back to moon
mode_idx = 0;
mode_idx = 0;
} else if (mode_idx > 0) {
} else if (mode_idx > 0) {
// Regular mode: is between 1 and TOTAL_MODES
// Regular mode: is between 1 and TOTAL_MODES
mode_idx -= 1;
mode_idx -= 1;
} else {
} else {
// Otherwise, wrap around (this allows entering hidden modes)
// Otherwise, wrap around (this allows entering hidden modes)
mode_idx = mode_cnt - 1;
mode_idx = mode_cnt - 1;
}
}
}
}
#endif
#endif


void count_modes() {
void count_modes() {
/*
/*
* Determine how many solid and hidden modes we have.
* Determine how many solid and hidden modes we have.
*
*
* (this matters because we have more than one set of modes to choose
* (this matters because we have more than one set of modes to choose
* from, so we need to count at runtime)
* from, so we need to count at runtime)
*/
*/
// copy config to local vars to avoid accidentally overwriting them in muggle mode
// copy config to local vars to avoid accidentally overwriting them in muggle mode
// (also, it seems to reduce overall program size)
// (also, it seems to reduce overall program size)
uint8_t my_modegroup = modegroup;
uint8_t my_modegroup = modegroup;
uint8_t my_enable_moon = enable_moon;
uint8_t my_enable_moon = enable_moon;
uint8_t my_reverse_modes = reverse_modes;
uint8_t my_reverse_modes = reverse_modes;


// override config if we're in simple mode
// override config if we're in simple mode
if (muggle_mode) {
if (muggle_mode) {
my_modegroup = NUM_MODEGROUPS;
my_modegroup = NUM_MODEGROUPS;
my_enable_moon = 0;
my_enable_moon = 0;
my_reverse_modes = 0;
my_reverse_modes = 0;
}
}


uint8_t *dest;
uint8_t *dest;
const uint8_t *src = modegroups + (my_modegroup<<3);
const uint8_t *src = modegroups + (my_modegroup<<3);
dest = modes;
dest = modes;


// Figure out how many modes are in this group
// Figure out how many modes are in this group
//solid_modes = modegroup + 1; // Assume group N has N modes
//solid_modes = modegroup + 1; // Assume group N has N modes
// No, how about actually counting the modes instead?
// No, how about actually counting the modes instead?
// (in case anyone changes the mode groups above so they don't form a triangle)
// (in case anyone changes the mode groups above so they don't form a triangle)
for(solid_modes=0;
for(solid_modes=0;
(solid_modes<8) && pgm_read_byte(src + solid_modes);
(solid_modes<8) && pgm_read_byte(src + solid_modes);
solid_modes++ ) {}
solid_modes++ ) {}


// add moon mode (or not) if config says to add it
// add moon mode (or not) if config says to add it
if (my_enable_moon) {
if (my_enable_moon) {
modes[0] = 1;
modes[0] = 1;
dest ++;
dest ++;
}
}


// add regular modes
// add regular modes
memcpy_P(dest, src, solid_modes);
memcpy_P(dest, src, solid_modes);
// add hidden modes
// add hidden modes
memcpy_P(dest + solid_modes, hiddenmodes, sizeof(hiddenmodes));
memcpy_P(dest + solid_modes, hiddenmodes, sizeof(hiddenmodes));
// final count
// final count
mode_cnt = solid_modes + sizeof(hiddenmodes);
mode_cnt = solid_modes + sizeof(hiddenmodes);
if (my_reverse_modes) {
if (my_reverse_modes) {
// TODO: yuck, isn't there a better way to do this?
// TODO: yuck, isn't there a better way to do this?
int8_t i;
int8_t i;
src += solid_modes;
src += solid_modes;
dest = modes;
dest = modes;
for(i=0; i<solid_modes; i++) {
for(i=0; i<solid_modes; i++) {
src --;
src --;
*dest = pgm_read_byte(src);
*dest = pgm_read_byte(src);
dest ++;
dest ++;
}
}
if (my_enable_moon) {
if (my_enable_moon) {
*dest = 1;
*dest = 1;
}
}
mode_cnt --; // get rid of last hidden mode, since it's a duplicate turbo
mode_cnt --; // get rid of last hidden mode, since it's a duplicate turbo
}
}
if (my_enable_moon) {
if (my_enable_moon) {
mode_cnt ++;
mode_cnt ++;
solid_modes ++;
solid_modes ++;
}
}
}
}


inline void set_output(uint8_t pwm1, uint8_t pwm2) {
inline void set_output(uint8_t pwm1, uint8_t pwm2) {
/* This is no longer needed since we always use PHASE mode.
/* This is no longer needed since we always use PHASE mode.
// Need PHASE to properly turn off the light
// Need PHASE to properly turn off the light
if ((pwm1==0) && (pwm2==0)) {
if ((pwm1==0) && (pwm2==0)) {
TCCR0A = PHASE;
TCCR0A = PHASE;
}
}
*/
*/
PWM_LVL = pwm1;
PWM_LVL = pwm1;
ALT_PWM_LVL = pwm2;
ALT_PWM_LVL = pwm2;
}
}


void set_level(uint8_t level) {
void set_level(uint8_t level) {
if (level == 0) {
if (level == 0) {
set_output(0,0);
set_output(0,0);
} else {
} else {
level -= 1;
level -= 1;
set_output(pgm_read_byte(ramp_FET + level),
set_output(pgm_read_byte(ramp_FET + level),
pgm_read_byte(ramp_7135 + level));
pgm_read_byte(ramp_7135 + level));
}
}
}
}


void set_mode(uint8_t mode) {
void set_mode(uint8_t mode) {
#ifdef SOFT_START
#ifdef SOFT_START
static uint8_t actual_level = 0;
static uint8_t actual_level = 0;
uint8_t target_level = mode;
uint8_t target_level = mode;
int8_t shift_amount;
int8_t shift_amount;
int8_t diff;
int8_t diff;
do {
do {
diff = target_level - actual_level;
diff = target_level - actual_level;
shift_amount = (diff >> 2) | (diff!=0);
shift_amount = (diff >> 2) | (diff!=0);
actual_level += shift_amount;
actual_level += shift_amount;
set_level(actual_level);
set_level(actual_level);
//_delay_ms(RAMP_SIZE/20); // slow ramp
//_delay_ms(RAMP_SIZE/20); // slow ramp
_delay_ms(RAMP_SIZE/4); // fast ramp
_delay_ms(RAMP_SIZE/4); // fast ramp
} while (target_level != actual_level);
} while (target_level != actual_level);
#else
#else
set_level(mode);
set_level(mode);
#endif // SOFT_START
#endif // SOFT_START
}
}


void blink(uint8_t val, uint16_t speed)
void blink(uint8_t val, uint16_t speed)
{
{
for (; val>0; val--)
for (; val>0; val--)
{
{
set_level(BLINK_BRIGHTNESS);
set_level(BLINK_BRIGHTNESS);
_delay_ms(speed);
_delay_ms(speed);
set_level(0);
set_level(0);
_delay_ms(speed<<2);
_delay_ms(speed<<2);
}
}
}
}


void strobe(uint8_t ontime, uint8_t offtime) {
void strobe(uint8_t ontime, uint8_t offtime) {
set_level(RAMP_SIZE);
set_level(RAMP_SIZE);
_delay_ms(ontime);
_delay_ms(ontime);
set_level(0);
set_level(0);
_delay_ms(offtime);
_delay_ms(offtime);
}
}


void toggle(uint8_t *var, uint8_t num) {
void toggle(uint8_t *var, uint8_t num) {
// Used for config mode
// Used for config mode
// Changes the value of a config option, waits for the user to "save"
// Changes the value of a config option, waits for the user to "save"
// by turning the light off, then changes the value back in case they
// by turning the light off, then changes the value back in case they
// didn't save. Can be used repeatedly on different options, allowing
// didn't save. Can be used repeatedly on different options, allowing
// the user to change and save only one at a time.
// the user to change and save only one at a time.
blink(num, BLINK_SPEED/8); // indicate which option number this is
blink(num, BLINK_SPEED/8); // indicate which option number this is
*var ^= 1;
*var ^= 1;
save_state();
save_state();
// "buzz" for a while to indicate the active toggle window
// "buzz" for a while to indicate the active toggle window
for(uint8_t i=0; i<32; i++) {
for(uint8_t i=0; i<32; i++) {
set_level(BLINK_BRIGHTNESS * 3 / 4);
set_level(BLINK_BRIGHTNESS * 3 / 4);
_delay_ms(20);
_delay_ms(20);
set_level(0);
set_level(0);
_delay_ms(20);
_delay_ms(20);
}
}
// if the user didn't click, reset the value and return
// if the user didn't click, reset the value and return
*var ^= 1;
*var ^= 1;
save_state();
save_state();
_delay_s();
_delay_s();
}
}


#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
uint8_t get_temperature() {
uint8_t get_temperature() {
ADC_on_temperature();
ADC_on_temperature();
// average a few values; temperature is noisy
// average a few values; temperature is noisy
uint16_t temp = 0;
uint16_t temp = 0;
uint8_t i;
uint8_t i;
get_voltage();
get_voltage();
for(i=0; i<16; i++) {
for(i=0; i<16; i++) {
temp += get_voltage();
temp += get_voltage();
_delay_ms(5);
_delay_ms(5);
}
}
temp >>= 4;
temp >>= 4;
return temp;
return temp;
}
}
#endif // TEMPERATURE_MON
#endif // TEMPERATURE_MON


inline uint8_t read_otc() {
inline uint8_t read_otc() {
// Read and return the off-time cap value
// Read and return the off-time cap value
// Start up ADC for capacitor pin
// Start up ADC for capacitor pin
// disable digital input on ADC pin to reduce power consumption
// disable digital input on ADC pin to reduce power consumption
DIDR0 |= (1 << CAP_DIDR);
DIDR0 |= (1 << CAP_DIDR);
// 1.1v reference, left-adjust, ADC3/PB3
// 1.1v reference, left-adjust, ADC3/PB3
ADMUX = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL;
ADMUX = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL;
// enable, start, prescale
// enable, start, prescale
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;


// Wait for completion
// Wait for completion
while (ADCSRA & (1 << ADSC));
while (ADCSRA & (1 << ADSC));
// Start again as datasheet says first result is unreliable
// Start again as datasheet says first result is unreliable
ADCSRA |= (1 << ADSC);
ADCSRA |= (1 << ADSC);
// Wait for completion
// Wait for completion
while (ADCSRA & (1 << ADSC));
while (ADCSRA & (1 << ADSC));


// ADCH should have the value we wanted
// ADCH should have the value we wanted
return ADCH;
return ADCH;
}
}


int main(void)
int main(void)
{
{
// check the OTC immediately before it has a chance to charge or discharge
// check the OTC immediately before it has a chance to charge or discharge
uint8_t cap_val = read_otc(); // save it for later
uint8_t cap_val = read_otc(); // save it for later


// Set PWM pin to output
// Set PWM pin to output
DDRB |= (1 << PWM_PIN); // enable main channel
DDRB |= (1 << PWM_PIN); // enable main channel
DDRB |= (1 << ALT_PWM_PIN); // enable second channel
DDRB |= (1 << ALT_PWM_PIN); // enable second channel


// Set timer to do PWM for correct output pin and set prescaler timing
// Set timer to do PWM for correct output pin and set prescaler timing
//TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
//TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
//TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
//TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
TCCR0A = PHASE;
TCCR0A = PHASE;
// Set timer to do PWM for correct output pin and set prescaler timing
// Set timer to do PWM for correct output pin and set prescaler timing
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)


// Read config values and saved state
// Read config values and saved state
restore_state();
restore_state();


// Enable the current mode group
// Enable the current mode group
count_modes();
count_modes();




// TODO: Enable this? (might prevent some corner cases, but requires extra room)
// TODO: Enable this? (might prevent some corner cases, but requires extra room)
// memory decayed, reset it
// memory decayed, reset it
// (should happen on med/long press instead
// (should happen on med/long press instead
// because mem decay is *much* slower when the OTC is charged
// because mem decay is *much* slower when the OTC is charged
// so let's not wait until it decays to reset it)
// so let's not wait until it decays to reset it)
//if (fast_presses > 0x20) { fast_presses = 0; }
//if (fast_presses > 0x20) { fast_presses = 0; }


// check button press time, unless the mode is overridden
// check button press time, unless the mode is overridden
if (! mode_override) {
if (! mode_override) {
if (cap_val > CAP_SHORT) {
if (cap_val > CAP_SHORT) {
// Indicates they did a short press, go to the next mode
// Indicates they did a short press, go to the next mode
// We don't care what the fast_presses value is as long as it's over 15
// We don't care what the fast_presses value is as long as it's over 15
fast_presses = (fast_presses+1) & 0x1f;
fast_presses = (fast_presses+1) & 0x1f;
next_mode(); // Will handle wrap arounds
next_mode(); // Will handle wrap arounds
#ifdef OFFTIM3
#ifdef OFFTIM3
} else if (cap_val > CAP_MED) {
} else if (cap_val > CAP_MED) {
// User did a medium press, go back one mode
// User did a medium press, go back one mode
fast_presses = 0;
fast_presses = 0;
if (offtim3) {
if (offtim3) {
prev_mode(); // Will handle "negative" modes and wrap-arounds
prev_mode(); // Will handle "negative" modes and wrap-arounds
} else {
} else {
next_mode(); // disabled-med-press acts like short-press
next_mode(); // disabled-med-press acts like short-press
// (except that fast_presses isn't reliable then)
// (except that fast_presses isn't reliable then)
}
}
#endif
#endif
} else {
} else {
// Long press, keep the same mode
// Long press, keep the same mode
// ... or reset to the first mode
// ... or reset to the first mode
fast_presses = 0;
fast_presses = 0;
if (muggle_mode || (! memory)) {
if (muggle_mode || (! memory)) {
// Reset to the first mode
// Reset to the first mode
mode_idx = 0;
mode_idx = 0;
}
}
}
}
}
}
save_mode();
save_mode();


// Charge up the capacitor by setting CAP_PIN to output
// Charge up the capacitor by setting CAP_PIN to output
DDRB |= (1 << CAP_PIN); // Output
DDRB |= (1 << CAP_PIN); // Output
PORTB |= (1 << CAP_PIN); // High
PORTB |= (1 << CAP_PIN); // High


// Turn features on or off as needed
// Turn features on or off as needed
#ifdef VOLTAGE_MON
#ifdef VOLTAGE_MON
ADC_on();
ADC_on();
#else
#else
ADC_off();
ADC_off();
#endif
#endif


uint8_t output;
uint8_t output;
uint8_t actual_level;
uint8_t actual_level;
#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
uint8_t overheat_count = 0;
uint8_t overheat_count = 0;
#endif
#endif
#ifdef VOLTAGE_MON
#ifdef VOLTAGE_MON
uint8_t lowbatt_cnt = 0;
uint8_t lowbatt_cnt = 0;
uint8_t i = 0;
uint8_t i = 0;
uint8_t voltage;
uint8_t voltage;
// Make sure voltage reading is running for later
// Make sure voltage reading is running for later
ADCSRA |= (1 << ADSC);
ADCSRA |= (1 << ADSC);
#endif
#endif
//output = pgm_read_byte(modes + mode_idx);
//output = pgm_read_byte(modes + mode_idx);
output = modes[mode_idx];
output = modes[mode_idx];
actual_level = output;
actual_level = output;
// handle mode overrides, like mode group selection and temperature calibration
// handle mode overrides, like mode group selection and temperature calibration
if (mode_override) {
if (mode_override) {
// do nothing; mode is already set
// do nothing; mode is already set
//mode_idx = mode_override;
//mode_idx = mode_override;
fast_presses = 0;
fast_presses = 0;
output = mode_idx;
output = mode_idx;
}
}
while(1) {
while(1) {
if (fast_presses > 0x0f) { // Config mode
if (fast_presses > 0x0f) { // Config mode
_delay_s(); // wait for user to stop fast-pressing button
_delay_s(); // wait for user to stop fast-pressing button
fast_presses = 0; // exit this mode after one use
fast_presses = 0; // exit this mode after one use
mode_idx = 0;
mode_idx = 0;


// Enter or leave "muggle mode"?
// Enter or leave "muggle mode"?
toggle(&muggle_mode, 1);
toggle(&muggle_mode, 1);
if (muggle_mode) { continue; }; // don't offer other options in muggle mode
if (muggle_mode) { continue; }; // don't offer other options in muggle mode


toggle(&memory, 2);
toggle(&memory, 2);


toggle(&enable_moon, 3);
toggle(&enable_moon, 3);


toggle(&reverse_modes, 4);
toggle(&reverse_modes, 4);


// Enter the mode group selection mode?
// Enter the mode group selection mode?
mode_idx = GROUP_SELECT_MODE;
mode_idx = GROUP_SELECT_MODE;
toggle(&mode_override, 5);
toggle(&mode_override, 5);
mode_idx = 0;
mode_idx = 0;


#ifdef OFFTIM3
#ifdef OFFTIM3
toggle(&offtim3, 6);
toggle(&offtim3, 6);
#endif
#endif


#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
// Enter temperature calibration mode?
// Enter temperature calibration mode?
mode_idx = TEMP_CAL_MODE;
mode_idx = TEMP_CAL_MODE;
toggle(&mode_override, 7);
toggle(&mode_override, 7);
mode_idx = 0;
mode_idx = 0;
#endif
#endif


toggle(&firstboot, 8);
toggle(&firstboot, 8);


//output = pgm_read_byte(modes + mode_idx);
//output = pgm_read_byte(modes + mode_idx);
output = modes[mode_idx];
output = modes[mode_idx];
actual_level = output;
actual_level = output;
}
}
#ifdef STROBE
#ifdef STROBE
else if (output == STROBE) {
else if (output == STROBE) {
// 10Hz tactical strobe
// 10Hz tactical strobe
strobe(50,50);
strobe(50,50);
}
}
#endif // ifdef STROBE
#endif // ifdef STROBE
#ifdef POLICE_STROBE
#ifdef POLICE_STROBE
else if (output == POLICE_STROBE) {
else if (output == POLICE_STROBE) {
// police-like strobe
// police-like strobe
for(i=0;i<8;i++) {
for(i=0;i<8;i++) {
strobe(20,40);
strobe(20,40);
}
}
for(i=0;i<8;i++) {
for(i=0;i<8;i++) {
strobe(40,80);
strobe(40,80);
}
}
}
}
#endif // ifdef POLICE_STROBE
#endif // ifdef POLICE_STROBE
#ifdef RANDOM_STROBE
#ifdef RANDOM_STROBE
else if (output == RANDOM_STROBE) {
else if (output == RANDOM_STROBE) {
// pseudo-random strobe
// pseudo-random strobe
uint8_t ms = 34 + (pgm_rand() & 0x3f);
uint8_t ms = 34 + (pgm_rand() & 0x3f);
strobe(ms, ms);
strobe(ms, ms);
strobe(ms, ms);
strobe(ms, ms);
}
}
#endif // ifdef RANDOM_STROBE
#endif // ifdef RANDOM_STROBE
#ifdef BIKING_STROBE
#ifdef BIKING_STROBE
else if (output == BIKING_STROBE) {
else if (output == BIKING_STROBE) {
// 2-level stutter beacon for biking and such
// 2-level stutter beacon for biking and such
#ifdef FULL_BIKING_STROBE
#ifdef FULL_BIKING_STROBE
// normal version
// normal version
for(i=0;i<4;i++) {
for(i=0;i<4;i++) {
set_output(255,0);
set_output(255,0);
_delay_ms(5);
_delay_ms(5);
set_output(0,255);
set_output(0,255);
_delay_ms(65);
_delay_ms(65);
}
}
_delay_ms(720);
_delay_ms(720);
#else
#else
// small/minimal version
// small/minimal version
set_output(255,0);
set_output(255,0);
_delay_ms(10);
_delay_ms(10);
set_output(0,255);
set_output(0,255);
_delay_s();
_delay_s();
#endif
#endif
}
}
#endif // ifdef BIKING_STROBE
#endif // ifdef BIKING_STROBE
#ifdef RAMP
#ifdef RAMP
else if (output == RAMP) {
else if (output == RAMP) {
int8_t r;
int8_t r;
// simple ramping test
// simple ramping test
for(r=1; r<=RAMP_SIZE; r++) {
for(r=1; r<=RAMP_SIZE; r++) {
set_level(r);
set_level(r);
_delay_ms(25);
_delay_ms(25);
}
}
for(r=RAMP_SIZE; r>0; r--) {
for(r=RAMP_SIZE; r>0; r--) {
set_level(r);
set_level(r);
_delay_ms(25);
_delay_ms(25);
}
}
}
}
#endif // ifdef RAMP
#endif // ifdef RAMP
#ifdef BATTCHECK
#ifdef BATTCHECK
else if (output == BATTCHECK) {
else if (output == BATTCHECK) {
#ifdef BATTCHECK_VpT
#ifdef BATTCHECK_VpT
// blink out volts and tenths
// blink out volts and tenths
uint8_t result = battcheck();
uint8_t result = battcheck();
blink(result >> 5, BLINK_SPEED/8);
blink(result >> 5, BLINK_SPEED/8);
_delay_ms(BLINK_SPEED);
_delay_ms(BLINK_SPEED);
blink(1,5);
blink(1,5);
_delay_ms(BLINK_SPEED*3/2);
_delay_ms(BLINK_SPEED*3/2);
blink(result & 0b00011111, BLINK_SPEED/8);
blink(result & 0b00011111, BLINK_SPEED/8);
#else // ifdef BATTCHECK_VpT
#else // ifdef BATTCHECK_VpT
// blink zero to five times to show voltage
// blink zero to five times to show voltage
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
blink(battcheck(), BLINK_SPEED/8);
blink(battcheck(), BLINK_SPEED/8);
#endif // ifdef BATTCHECK_VpT
#endif // ifdef BATTCHECK_VpT
// wait between readouts
// wait between readouts
_delay_s(); _delay_s();
_delay_s(); _delay_s();
}
}
#endif // ifdef BATTCHECK
#endif // ifdef BATTCHECK
else if (output == GROUP_SELECT_MODE) {
else if (output == GROUP_SELECT_MODE) {
// exit this mode after one use
// exit this mode after one use
mode_idx = 0;
mode_idx = 0;
mode_override = 0;
mode_override = 0;


for(i=0; i<NUM_MODEGROUPS; i++) {
for(i=0; i<NUM_MODEGROUPS; i++) {
modegroup = i;
modegroup = i;
save_state();
save_state();


blink(1, BLINK_SPEED/3);
blink(1, BLINK_SPEED/3);
}
}
_delay_s(); _delay_s();
_delay_s(); _delay_s();
}
}
#ifdef TEMP_CAL_MODE
#ifdef TEMP_CAL_MODE
else if (output == TEMP_CAL_MODE) {
else if (output == TEMP_CAL_MODE) {
// make sure we don't stay in this mode after button press
// make sure we don't stay in this mode after button press
mode_idx = 0;
mode_idx = 0;
mode_override = 0;
mode_override = 0;


// Allow the user to turn off thermal regulation if they want
// Allow the user to turn off thermal regulation if they want
maxtemp = 255;
maxtemp = 255;
save_state();
save_state();
set_mode(RAMP_SIZE/4); // start somewhat dim during turn-off-regulation mode
set_mode(RAMP_SIZE/4); // start somewhat dim during turn-off-regulation mode
_delay_s(); _delay_s();
_delay_s(); _delay_s();


// run at highest output level, to generate heat
// run at highest output level, to generate heat
set_mode(RAMP_SIZE);

// measure, save, wait... repeat
while(1) {
ma