Untitled diff

Created Diff never expires
/*
/*
* "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