void sleep_until_eswitch_pressed()
{
    WDT_off();
    ADC_off();

    // make sure switch isn't currently pressed
    while (button_is_pressed()) {}
    empty_event_sequence();  // cancel pending input on suspend
    //PCINT_since_WDT = 0;  // ensure PCINT won't ignore itself

    PCINT_on();  // wake on e-switch event

    // configure sleep mode
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);

    sleep_enable();
    sleep_bod_disable();
    sleep_cpu();  // wait here

    // something happened; wake up
    sleep_disable();

    #ifdef USE_THERMAL_REGULATION
    // forget what the temperature was last time we were on
    reset_thermal_history = 1;
    #endif

    // go back to normal running mode
    //PCINT_on();  // should be on already
    // FIXME? if button is down, make sure a button press event is added to the current sequence
    ADC_on();
    WDT_on();
}
void sleep_until_switch_press()
{
	// This routine takes up a lot of program memory :(
	// Turn the WDT off so it doesn't wake us from sleep
	// Will also ensure interrupts are on or we will never wake up
	WDT_off();
	// Need to reset press duration since a button release wasn't recorded
	press_duration = 0;
	// Enable a pin change interrupt to wake us up
	// However, we have to make sure the switch is released otherwise we will wake when the user releases the switch
	while (is_pressed()) {
		_delay_ms(16);
	}
	PCINT_on();
	// Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
	ADC_off();//ADC switched off to reduce power consumptionADC_off();
	set_sleep_mode(SLEEP_MODE_PWR_DOWN);
	// Now go to sleep
	sleep_mode();
	// Hey, someone must have pressed the switch!!
	// Disable pin change interrupt because it's only used to wake us up
    ADC_on();
	PCINT_off();
	// Turn the WDT back on to check for switch presses
	WDT_on();
	// Go back to main program
}
int main(void)
{	
	// Set all ports to input, and turn pull-up resistors on for the inputs we are using
	DDRB = 0x00;
	PORTB = (1 << SWITCH_PIN) | (1 << STAR2_PIN) | (1 << STAR3_PIN);

	// Set the switch as an interrupt for when we turn pin change interrupts on
	PCMSK = (1 << SWITCH_PIN);
	
    // Set PWM pin to output
    DDRB = (1 << PWM_PIN);

    // 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
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
	
	// Turn features on or off as needed
	#ifdef VOLTAGE_MON
	ADC_on();
	#else
	ADC_off();
	#endif
	ACSR   |=  (1<<7); //AC off
	
	// Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
	set_sleep_mode(SLEEP_MODE_PWR_DOWN);
	sleep_until_switch_press();
	
	uint8_t last_mode_idx = 0;
	
	while(1) {
		// We will never leave this loop.  The WDT will interrupt to check for switch presses and 
		// will change the mode if needed.  If this loop detects that the mode has changed, run the
		// logic for that mode while continuing to check for a mode change.
		if (mode_idx != last_mode_idx) {
			last_mode_idx = mode_idx;
			// The WDT changed the mode.
			PWM_LVL = pgm_read_byte(&modes[mode_idx]);
			if (PWM_LVL == 0)
			  {
				_delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
				// Go to sleep
				sleep_until_switch_press();
			  }
			 else
			   { set_sleep_mode(SLEEP_MODE_IDLE); //in every other mode just reduce current consumption
		         sleep_mode();
			   }
		}
	}

    return 0; // Standard Return Code
}
int main(void)
{	
	// Set all ports to input, and turn pull-up resistors on for the inputs we are using
	DDRB = 0x00;
	PORTB = (1 << SWITCH_PIN) | (1 << STAR2_PIN) | (1 << STAR3_PIN);

	// Set the switch as an interrupt for when we turn pin change interrupts on
	PCMSK = (1 << SWITCH_PIN);
	
    // Set PWM pin to output
    DDRB = (1 << PWM_PIN);

    // 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
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
	
	// Turn features on or off as needed
	#ifdef VOLTAGE_MON
	ADC_on();
	#else
	ADC_off();
	#endif
	ACSR   |=  (1<<7); //AC off
	
	// Determine if we are going L-H, or H-L based on STAR 2
	if ((PINB & (1 << STAR2_PIN)) == 0) {
		// High to Low
		low_to_high = 0;
	} else {
		low_to_high = 1;
	}
	// Not soldered (1) should enable memory
	memory = ((PINB & (1 << STAR3_PIN)) > 0) ? 1 : 0;
	
	// Don't think we want to ever go to sleep
	// Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
	//set_sleep_mode(SLEEP_MODE_PWR_DOWN);
	//sleep_until_switch_press();
	
	WDT_on();
	
	// Determine what mode we should fire up
	// Read the last mode that was saved
	if (memory) {
		read_mode_idx();
	} else {
		if (low_to_high) {
			mode_idx = 0;
		} else {
			mode_idx = sizeof(modes);
		}		
	}
	
	PWM_LVL = modes[mode_idx];
	
	uint8_t last_mode_idx = mode_idx;
	
	while(1) {
		// We will never leave this loop.  The WDT will interrupt to check for switch presses and 
		// will change the mode if needed.  If this loop detects that the mode has changed, run the
		// logic for that mode while continuing to check for a mode change.
		if (mode_idx != last_mode_idx) {
			// Save the new mode
			last_mode_idx = mode_idx;
			// The WDT changed the mode.
			PWM_LVL = modes[mode_idx];
		}
	}

    return 0; // Standard Return Code
}
int main(void)
{	
	// Set all ports to input, and turn pull-up resistors on for the inputs we are using
	DDRB = 0x00;
	PORTB = (1 << SWITCH_PIN) | (1 << STAR3_PIN);

	// Set the switch as an interrupt for when we turn pin change interrupts on
	PCMSK = (1 << SWITCH_PIN);
	
    // Set PWM pin to output
	#ifdef ALT_MODES
    DDRB = (1 << PWM_PIN) | (1 << ALT_PWM_PIN);
	#else
	DDRB = (1 << PWM_PIN);
	#endif

    // 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
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
	
	// Turn features on or off as needed
	#ifdef VOLTAGE_MON
	ADC_on();
	#else
	ADC_off();
	#endif
	ACSR   |=  (1<<7); //AC off
	
	// Determine if we are going L-H, or H-L based on Star 3
	if ((PINB & (1 << STAR3_PIN)) == 0) {
		// High to Low
		low_to_high = 0;
	} else {
		low_to_high = 1;
	}
	
	// Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
	set_sleep_mode(SLEEP_MODE_PWR_DOWN);
	sleep_until_switch_press();
	
	uint8_t last_mode_idx = 0;
	
	while(1) {
		// We will never leave this loop.  The WDT will interrupt to check for switch presses and 
		// will change the mode if needed.  If this loop detects that the mode has changed, run the
		// logic for that mode while continuing to check for a mode change.
		if (mode_idx != last_mode_idx) {
			// The WDT changed the mode.
			if (mode_idx > 0) {
				// No need to change the mode if we are just turning the light off
				// Check if the PWM mode is different
				if (mode_pwm[last_mode_idx] != mode_pwm[mode_idx]) {
					#ifdef ALT_MODES
					TCCR0A = mode_pwm[mode_idx] | 0b10100000;  // Use both outputs
					#else
					TCCR0A = mode_pwm[mode_idx] | 0b00100000;  // Only use the normal output
					#endif
				}
			}
			PWM_LVL     = modes[mode_idx];
			#ifdef ALT_MODES
			ALT_PWM_LVL = alt_modes[mode_idx];
			#endif
			last_mode_idx = mode_idx;
			if (mode_pwm[mode_idx] == 0) {
				_delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
				// Go to sleep
				sleep_until_switch_press();
			}
		}
	}

    return 0; // Standard Return Code
}
int main(void)
{	
	uint8_t short_click = 0;
	
	// All ports default to input, but turn pull-up resistors on for the stars (not the ADC input!  Made that mistake already)
	PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN);
	
	// Start up ADC for capacitor pin
	DIDR0 |= (1 << CAP_DIDR);							// disable digital input on ADC pin to reduce power consumption
	ADMUX  = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3
	ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
	
	// Wait for completion
	while (ADCSRA & (1 << ADSC))
		;
	// Start again as datasheet says first result is unreliable
	ADCSRA |= (1 << ADSC);
	// Wait for completion
	while (ADCSRA & (1 << ADSC))
		;

	if(ADCH > CAP_THRESHOLD) 
		short_click = 1;

	// Turn off ADC
	ADC_off();

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

    // Set PWM pin to output
    DDRB = (1 << PWM_PIN);

    // 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
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
	
	// Turn features on or off as needed
#ifdef ENABLE_VOLTAGE_MONITORING
	ADC_on();
#else
	ADC_off();
#endif
	ACSR   |=  (1<<7); //AC off
	
#ifdef ENABLE_TURNING_OFF_TURBO_TIMER
	turbo_timer_enabled = ((PINB & (1 << STAR2_PIN)) > 0) ? 1 : 0;
#endif

#ifdef ENABLE_SINGLE_MODE
	if((PINB & (1 << STAR2_PIN)) == 0)
	{
			modes[0] = modes[1] = 
#ifdef FOUR_MODES
			modes[2] = 
#endif
			MODE_TURBO;

			// Last basic mode is already MODE_TURBO - no need to waste bytes changing that
	}		
#endif

#ifdef ENABLE_HIGH_TO_LOW
#ifdef HIGH_TO_LOW_ON_BY_DEFAULT
	if((PINB & (1 << STAR3_PIN)) > 0)
#else
	if((PINB & (1 << STAR3_PIN)) == 0)
#endif
		revert_modes();
#endif
	
#ifdef ENABLE_TACTICAL_MODE
#ifdef TACTICAL_MODE_ON_BY_DEFAULT
	if((PINB & (1 << STAR2_PIN)) > 0)
#else
	if((PINB & (1 << STAR2_PIN)) == 0)
#endif
	{
		modes[1] = MODE_TURBO;

		if(modes[0] == MODE_TURBO) // Single mode
		{
			modes[2] = 
#ifdef FOUR_MODES
			modes[3] = 
#endif
			MODE_TURBO;
		}
		else
		{
			modes[0] = MODE_STROBE;
			modes[2] = MODE_LOW;
#ifdef FOUR_MODES
			modes[3] = MODE_LOWLOW;
#endif
		}
	}
#endif


	// Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
	// Will allow us to go idle between WDT interrupts
	set_sleep_mode(SLEEP_MODE_IDLE);
	
	// Mode memory handling block
	{
		uint8_t n_short_clicks = 0;

		// Determine what mode we should fire up
		// Read the last mode that was saved
		mode_idx = read_stored_idx();

		// Handle short press counter
		n_short_clicks = (mode_idx & 0xf0);
		mode_idx &= 0x0f;

		if(short_click) // This click was short
		{
			if(n_short_clicks == MODE_INDEX_TURBO_RAMPDOWN) // TODO: Test if this logic works in practice, or do we need to use always double tap from turbo?
				n_short_clicks = 0; // Keep turbo, reset counter
			else
				next_mode(n_short_clicks); // Will handle wrap arounds

			store_mode_idx(mode_idx | ((n_short_clicks < HIDDEN_MODE_THRESHOLD) ? n_short_clicks+0x10 : n_short_clicks));
		} 
		else // Didn't have a short press, keep the same mode, stored without short click counter
		{
			if((PINB & (1 << STAR2_PIN)) == 0) // Tactical or Single or No memory, reset to 1st mode
				mode_idx = 0;

			store_mode_idx(mode_idx);
		}
	}		
	
	// Start watchdog timer (used for storing the mode after delay, turbo timer, and voltage monitoring)
	WDT_on();
	
	// Now just fire up the mode
	selected_pwm = modes[mode_idx]; // Note: Actual PWM can be less than selected PWM (e.g. in case of low voltage)
	
	if(selected_pwm < PWM_MIN) // Hidden blinky modes
		adjusted_pwm = PWM_MAX; // All blinky modes initially with full power
	else
		adjusted_pwm = selected_pwm;

	// block for main loop
	{
		uint8_t ii = 0; // Loop counter, used by multiple branches
#ifdef ENABLE_BEACONS
		uint8_t beacon_background = PWM_OFF;
#endif
	
		while(1)
		{
#ifdef ENABLE_VOLTAGE_MONITORING
			if(adjusted_pwm == PWM_OFF) // Voltage monitoring signaled us to turn off the light -> break out of the main loop
				break;
#endif

			PWM_LVL = adjusted_pwm; // must be set inside loop, is volatile & might have changed because of voltage monitoring

			switch(selected_pwm)
			{
			case MODE_STROBE: // Disorienting alternating strobe
#ifdef NORMAL_STROBE
				// 51ms = ~19.5Hz, ~60% DC
				_delay_ms(25);
				PWM_LVL = 0;
				_delay_ms(26);
#endif
#ifdef ALTERNATING_STROBE
				_delay_ms(31);
				PWM_LVL = 0;
				if(ii < 19) // 51ms = ~19.5Hz, ~60% DC
					_delay_ms(20);
				else if(ii < 32) // 77ms = ~13Hz, ~40% DC
					_delay_ms(46);
				else
					ii = 255;
#endif
#ifdef RANDOM_STROBE 
				{   // 77ms = 13Hz, 51ms = 19.5Hz / 40-60% DC
					ii = (5 * ii) + 128;
					_delay_ms(31);
					PWM_LVL = 0;
					_delay_ms(ii > 127 ? 46 : 20);
				}					
#endif
				break;
			case MODE_MOTION_STOPPING_STROBE: // 8Hz, 1.6% DC
				_delay_ms(2);
				PWM_LVL = 0;
				_delay_ms(123);
				break;
#ifdef ENABLE_SOS
			case MODE_SOS:
				if(ii / 3 == 1)
					_delay_ms(600);  // Dash for 'O' (3xDot)
				else
					_delay_ms(200);  // Dot for 'S'

				PWM_LVL = 0;

				switch(ii)
				{
				default:
					_delay_ms(200);  // Pause inside a letter (1xDot)
					break;
				case 2:
				case 5:
					_delay_ms(600); // Pause between letters (3xDot)
					break;
				case 8:
					_delay_ms(2500); // Pause between "words" (should be 7xDot, but I like it longer)
					ii = 255;
					break;
				}
				break;
#endif
#ifdef ENABLE_BEACONS
			case MODE_BEACON_WITH_BACKGROUND:
				beacon_background = PWM_BEACON_BACKGROUND;
				goto beacon_common;
			case MODE_SLOW_BEACON_WITH_BACKGROUND:
				beacon_background = PWM_SLOW_BEACON_BACKGROUND;
				// no break - fall through to beacon code
			case MODE_BEACON:
			case MODE_ALPINE_DISTRESS_BEACON:
			beacon_common:
				_delay_ms(50);
				PWM_LVL = beacon_background;
				_delay_ms(950);
				
				if(selected_pwm == MODE_ALPINE_DISTRESS_BEACON)
				{
					if(ii > 5)
					{
						_delay_ms(59000);
						ii = 255;
					}
					else
						_delay_ms(9000);
				}
				else if(selected_pwm == MODE_SLOW_BEACON_WITH_BACKGROUND)
					_delay_ms(1500);
#endif
				break;
			default:
				sleep_mode();
				break;
			}

			ii++; // Loop counter, used by multiple branches
		}
	}

#ifdef ENABLE_VOLTAGE_MONITORING
	//
	// Critically low voltage -> Turn off the light
	// 
		
	// Disable WDT so it doesn't wake us up from sleep
	WDT_off();

	// Would be nice to blink a couple of times with lowest brightness to notify the user, but not implemented due space restrictions

	// Turn the light off
	PWM_LVL = 0;

	// Disable ADC so it doesn't consume power
	ADC_off();

	// Power down as many components as possible
	set_sleep_mode(SLEEP_MODE_PWR_DOWN);

	// Deep sleep until powered off - consumes ~110uA during the deep sleep
	while(1)
		sleep_mode();
#endif

    return 0; // Standard Return Code -> would return to idle loop with interrupts disabled.
}
int main(void)
{
    // Set all ports to input, and turn pull-up resistors on for the inputs we are using
    DDRB = 0x00;
    PORTB = (1 << SWITCH_PIN);

    // Set the switch as an interrupt for when we turn pin change interrupts on
    PCMSK = (1 << SWITCH_PIN);

    // Set PWM pin to output
    DDRB = (1 << PWM_PIN);

    // Set timer to do PWM for correct output pin and set prescaler timing
    TCCR0A = 0x20 | PWM_MODE; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
    #ifdef USE_PFM
    // 0x08 is for variable-speed PWM
    TCCR0B = 0x08 | 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
    #else
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
    #endif

    // Turn features on or off as needed
    #ifdef VOLTAGE_MON
    ADC_on();
    #else
    ADC_off();
    #endif
    ACSR   |=  (1<<7); //AC off

    #ifdef BLINK_ON_POWER
    // blink once to let the user know we have power
    //TCCR0A = PHASE | 0b00100000;  // Only use the normal output
    #ifdef USE_PFM
    CEIL_LVL = 255;
    #endif
    PWM_LVL = 255;
    _delay_ms(3);
    PWM_LVL = 0;
    _delay_ms(1);
    #endif

    // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_until_switch_press();

    uint8_t last_mode_idx = 0;

    while(1) {
        // We will never leave this loop.  The WDT will interrupt to check for switch presses and 
        // will change the mode if needed.  If this loop detects that the mode has changed, run the
        // logic for that mode while continuing to check for a mode change.
        if (mode_idx != last_mode_idx) {
            // The WDT changed the mode.
            PWM_LVL     = pgm_read_byte(modes + mode_idx);
            last_mode_idx = mode_idx;
            _delay_ms(1);
            if (mode_idx == 0) {
                // Finish executing instructions for PWM level change
                // and/or voltage readout mode before shutdown.
                do {
                    _delay_ms(1);
                } while (0); // FIXME: stay on for a while to catch doubleclicks
                // Go to sleep
                sleep_until_switch_press();
                // FIXME: lock mode can result in fast PWM=0 staying lit
                // (MCU wakes up and stays awake, should go back to sleep)
            }
        }
    }

    return 0; // Standard Return Code
}
int main(void)
{

	DDRB |= (1 << PWM_PIN);	 // Set PWM pin to output, enable main channel
	TCCR0A = FAST; // Set timer to do PWM for correct output pin and set prescaler timing
	TCCR0B = 0x01; // Set timer to do PWM for correct output pin and set prescaler timing

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

	count_modes(); // Enable the current mode group
	 // check button press time, unless we're in group selection mode
	if (mode_idx != GROUP_SELECT_MODE) {
		if ( we_did_a_fast_press() ) { // sram hasn't decayed yet, must have been a short press
			increment_fast_presses();
			next_mode(); // Will handle wrap arounds
		} else { // Long press, keep the same mode
			reset_fast_presses();
			if( !memory_is_enabled() ) {mode_idx = 0;}  // if memory is turned off, set mode_idx to 0
		}
	}
	save_mode();

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

	uint8_t output;
	uint8_t i = 0;
	uint16_t ticks = 0;
#ifdef TURBO_RAMP_DOWN
	uint8_t adj_output = 255;
#endif
	
#ifdef VOLTAGE_MON
	uint8_t lowbatt_cnt = 0;
	uint8_t voltage;
	ADCSRA |= (1 << ADSC); // Make sure voltage reading is running for later
#endif

	if(mode_idx > num_modes) { output = mode_idx; }  // special modes, override output
	else { output = modes[mode_idx]; }
	
	while(1) {
		if (fast_presses[0] >= 12) {  // Config mode if 12 or more fast presses
			_delay_s();	   // wait for user to stop fast-pressing button
			reset_fast_presses(); // exit this mode after one use

			toggle_mode(GROUP_SELECT_MODE, 1); // Enter the mode group selection mode?
			toggle_options((options ^ 0b00010000), 2); // memory
			toggle_options((options ^ 0b00100000), 3); // hidden blinkies
			toggle_options((options ^ 0b01000000), 4); // hidden battcheck
			toggle_options((options ^ 0b10000000), 5); // turbo timer
			toggle_options(DEFAULTS, 6); // reset to defaults
		}
		else if (output == STROBE) { // 10Hz tactical strobe
			for(i=0;i<8;i++) {
				set_level(RAMP_SIZE);
				_delay_ms(33);
				set_output(0);
				_delay_ms(67);
			}
		}
		else if (output == BEACON) {
			set_level(RAMP_SIZE);
			_delay_ms(10);
			set_output(0);
			_delay_s(); _delay_s();
		}
		else if (output == SOS) {  // dot = 1 unit, dash = 3 units, space betwen parts of a letter = 1 unit, space between letters = 3 units
			#define SOS_SPEED 200  // these speeds aren't perfect, but they'll work for the [never] times they'll actually get used
			blink(3, SOS_SPEED); // 200 on, 400 off, 200 on, 400 off, 200 on, 400 off
			_delay_ms(SOS_SPEED);  // wait for 200
			blink(3, SOS_SPEED*5/2);  // 500 on, 1000 off, 500 on, 1000 off, 500 on, 1000 off (techically too long on that last off beat, but oh well)
			blink(3, SOS_SPEED);  // 200 on, 400 off, 200 on, 400 off, 200 on, 400 off
			_delay_s(); _delay_s();
		}
		else if (output == BATT_CHECK) {
			 blink(battcheck(), BLINK_SPEED/4);
			 _delay_s(); _delay_s();
		}
		else if (output == GROUP_SELECT_MODE) {
			mode_idx = 0; // exit this mode after one use

			for(i=0; i<NUM_MODEGROUPS; i++) {
				toggle_options(((options & 0b11110000) | i), i+1);
			}
			_delay_s();
		}
		else {
			if ((output == TURBO) && ( ttimer_is_enabled() ) && (ticks > (TURBO_MINUTES * TICKS_PER_MINUTE))) {
	#ifdef TURBO_RAMP_DOWN
				if (adj_output > TURBO_LOWER) { adj_output = adj_output - 2; }
				set_output(adj_output);
	#else
				set_output(TURBO_LOWER);
	#endif
			}
			else {
				ticks ++; // count ticks for turbo timer
				set_level(output);
			}

			_delay_ms(500);  // Otherwise, just sleep.

		}
		reset_fast_presses();
#ifdef VOLTAGE_MON
		if (ADCSRA & (1 << ADIF)) {  // if a voltage reading is ready
			voltage = ADCH;  // get the waiting value
	
			if (voltage < ADC_LOW) { // See if voltage is lower than what we were looking for
				lowbatt_cnt ++;
			} else {
				lowbatt_cnt = 0;
			}
			
			if (lowbatt_cnt >= 8) {  // See if it's been low for a while, and maybe step down
				//set_output(0);  _delay_ms(100); // blink on step-down:

				if (output > RAMP_SIZE) {  // blinky modes 
					output = RAMP_SIZE / 2; // step down from blinky modes to medium
				} else if (output > 1) {  // regular solid mode
					output = output - 1; // step down from solid modes somewhat gradually
				} else { // Already at the lowest mode
					set_output(0); // Turn off the light
					set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Power down as many components as possible
					sleep_mode();
				}
				set_level(output);
				lowbatt_cnt = 0;
				_delay_s(); // Wait before lowering the level again
			}

			ADCSRA |= (1 << ADSC); // Make sure conversion is running for next time through
		}
#endif  // ifdef VOLTAGE_MON
	}
}
Beispiel #9
0
int main(void)
{
    // All ports default to input, but turn pull-up resistors on for the stars
    // (not the ADC input!  Made that mistake already)
    // (stars not used)
    //PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN) | (1 << STAR4_PIN);

    // Set PWM pin to output
    DDRB = (1 << PWM_PIN);

    // Turn features on or off as needed
    #ifdef VOLTAGE_MON
    ADC_on();
    #else
    ADC_off();
    #endif
    ACSR   |=  (1<<7); //AC off

    // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
    // Will allow us to go idle between WDT interrupts (which we're not using anyway)
    set_sleep_mode(SLEEP_MODE_IDLE);

    // Determine what mode we should fire up
    // Read the last mode that was saved
    if (noinit_decay) // not short press, forget mode
    {
        noinit_mode = 0;
        mode_idx = 0;
    } else { // short press, advance to next mode
        mode_idx = noinit_mode;
        next_mode();
        noinit_mode = mode_idx;
    }
    // set noinit data for next boot
    noinit_decay = 0;

    // set PWM mode
    if (modes[mode_idx] < FAST_PWM_START) {
        // Set timer to do PWM for correct output pin and set prescaler timing
        TCCR0A = 0x21; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
    } else {
        // 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
    }
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)

    // Now just fire up the mode
    PWM_LVL = modes[mode_idx];

    uint8_t i = 0;
    uint8_t j = 0;
    uint8_t strobe_len = 0;
#ifdef VOLTAGE_MON
    uint8_t lowbatt_cnt = 0;
    uint8_t voltage;
#endif
    while(1) {
        if(mode_idx < SOLID_MODES) { // Just stay on at a given brightness
            sleep_mode();
        } else if (mode_idx < BATT_CHECK_MODE) {
            PWM_LVL = 0;
            get_voltage();  _delay_ms(200);  // the first reading is junk
#ifdef BATTCHECK_VpT
            uint8_t result = battcheck();
            blink(result >> 5, BLINK_SPEED/8);
            _delay_ms(BLINK_SPEED);
            blink(1,5);
            _delay_ms(BLINK_SPEED*3/2);
            blink(result & 0b00011111, BLINK_SPEED/8);
#else
            blink(battcheck());
#endif  // BATTCHECK_VpT
            _delay_ms(2000);  // wait at least 2 seconds between readouts
        } else if (mode_idx < SINGLE_BEACON_MODES) { // heartbeat flasher
int main(void)
{	
	// All ports default to input, but turn pull-up resistors on for the stars (not the ADC input!  Made that mistake already)
	PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN) | (1 << STAR4_PIN);
	
    // Set PWM pin to output
    DDRB = (1 << PWM_PIN);

    // 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
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
	
	// Turn features on or off as needed
	#ifdef ENABLE_VOLTAGE_MONITORING
	ADC_on();
	#else
	ADC_off();
	#endif
	ACSR   |=  (1<<7); //AC off
	
#ifdef ENABLE_TURNING_OFF_MEMORY
	// Soldering Star 4 disables memory
#ifdef MEMORY_ON_BY_DEFAULT
	memory_enabled = ((PINB & (1 << STAR4_PIN)) > 0) ? 1 : 0;
#else
	memory_enabled = ((PINB & (1 << STAR4_PIN)) == 0) ? 1 : 0;
#endif
#endif

#ifdef ENABLE_TURNING_OFF_TURBO_TIMER
	turbo_timer_enabled = ((PINB & (1 << STAR2_PIN)) > 0) ? 1 : 0;
#endif

#ifdef ENABLE_SINGLE_MODE
	if((PINB & (1 << STAR2_PIN)) == 0)
	{
			modes[0] = modes[1] = 
#ifdef FOUR_MODES
			modes[2] = 
#endif
			MODE_TURBO;

			// Last basic mode is already MODE_TURBO - no need to waste bytes changing that
	}		
#endif

#ifdef ENABLE_TACTICAL_MODE
#ifdef TACTICAL_MODE_ON_BY_DEFAULT
	if((PINB & (1 << STAR2_PIN)) > 0)
#else
	if((PINB & (1 << STAR2_PIN)) == 0)
#endif
	{
#ifdef ENABLE_TURNING_OFF_MEMORY
		if(memory_enabled) // Single mode
		{
			modes[0] = modes[1] = 
#ifdef FOUR_MODES
			modes[2] = 
#endif
			MODE_TURBO;

			// Last basic mode is already MODE_TURBO - no need to waste bytes changing that
		}
		else
#endif
		{
			modes[0] = MODE_STROBE;
			modes[1] = MODE_TURBO;
			modes[2] = MODE_LOW;
#ifdef FOUR_MODES
			modes[3] = MODE_LOWLOW;
#endif
			wdt_timeout = 1;
		}
	}
#endif


#ifdef ENABLE_HIGH_TO_LOW
#ifdef HIGH_TO_LOW_ON_BY_DEFAULT
	if((PINB & (1 << STAR3_PIN)) > 0)
#else
	if((PINB & (1 << STAR3_PIN)) == 0)
#endif
		revert_modes();
#endif
	
	// Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
	// Will allow us to go idle between WDT interrupts
	set_sleep_mode(SLEEP_MODE_IDLE);
	
	// Mode memory handling block
	{
		uint8_t short_clicks = 0;

		// Determine what mode we should fire up
		// Read the last mode that was saved
		mode_idx = read_stored_idx();

		// Handle short press indicator
		short_clicks = (mode_idx & 0xf0);
		mode_idx &= 0x0f;
		
		if(short_clicks) // One or more short clicks
		{
			// Indicates we did a short press last time, go to the next mode
			next_mode(short_clicks); // Will handle wrap arounds
		} 
		// else: Didn't have a short press, keep the same mode, nothing to do
	
		// Store mode with short press indicator
		store_mode_idx(mode_idx | ((short_clicks < HIDDEN_MODE_THRESHOLD) ? short_clicks+0x10 : short_clicks));
	}		
	
	// Start watchdog timer (used for storing the mode after delay, turbo timer, and voltage monitoring)
	WDT_on();
	
	// Now just fire up the mode
	selected_pwm = modes[mode_idx]; // Note: Actual PWM can be less than selected PWM (e.g. in case of low voltage)
	
	if(selected_pwm < PWM_MIN) // Hidden blinky modes
		adjusted_pwm = PWM_MAX; // All blinky modes initially with full power
	else
		adjusted_pwm = selected_pwm;

	// block for main loop
	{
		uint8_t ii = 0; // Loop counter, used by multiple branches
#ifdef ENABLE_BEACONS
		uint8_t beacon_background = PWM_OFF;
#endif
	
		while(1)
		{
#ifdef ENABLE_VOLTAGE_MONITORING
			if(adjusted_pwm == PWM_OFF) // Voltage monitoring signaled us to turn off the light -> break out of the main loop
				break;
#endif

			PWM_LVL = adjusted_pwm; // must be set inside loop, is volatile & might have changed because of voltage monitoring

			switch(selected_pwm)
			{
			case MODE_STROBE: // Disorienting alternating strobe
#ifdef NORMAL_STROBE
				_delay_ms(20);
				PWM_LVL = 0;
				_delay_ms(40);
#endif
#ifdef ALTERNATING_STROBE
				_delay_ms(20);
				PWM_LVL = 0;
				if(ii < 22) // 60ms = ~16.7Hz, ~33% DC
					_delay_ms(40);
				else if(ii < 36) // 111ms = ~9Hz, ~18% DC
					_delay_ms(90);
				else
					ii = 255;
#endif
#ifdef RANDOM_STROBE 
				{   // 77ms = 13Hz, 51ms = 19.5Hz / 40-60% DC
					ii = (5 * ii) + 128;
					_delay_ms(31);
					PWM_LVL = 0;
					_delay_ms(ii > 127 ? 46 : 20);
				}					
#endif
				break;
			case MODE_MOTION_STOPPING_STROBE: // 10Hz, 2% DC
				_delay_ms(2);
				PWM_LVL = 0;
				_delay_ms(98);
				break;
#ifdef ENABLE_SOS
			case MODE_SOS:
				if(ii / 3 == 1)
					_delay_ms(600);  // Dash for 'O' (3xDot)
				else
					_delay_ms(200);  // Dot for 'S'

				PWM_LVL = 0;

				switch(ii)
				{
				default:
					_delay_ms(200);  // Pause inside a letter (1xDot)
					break;
				case 2:
				case 5:
					_delay_ms(600); // Pause between letters (3xDot)
					break;
				case 8:
					_delay_ms(2500); // Pause between "words" (should be 7xDot, but I like it longer)
					ii = 255;
					break;
				}
				break;
#endif
#ifdef ENABLE_BEACONS
			case MODE_BEACON_WITH_BACKGROUND:
			case MODE_SLOW_BEACON_WITH_BACKGROUND:
#ifdef FOUR_MODES
				beacon_background = PWM_SLOW_BEACON_BACKGROUND;
#else
				switch(selected_pwm)
				{
				case MODE_BEACON_WITH_BACKGROUND:
					beacon_background = PWM_BEACON_BACKGROUND;
					break;
				case MODE_SLOW_BEACON_WITH_BACKGROUND:
					beacon_background = PWM_SLOW_BEACON_BACKGROUND;
					break;
				}
#endif
				// no break - fall through to beacon code
			case MODE_BEACON:
			case MODE_ALPINE_DISTRESS_BEACON:
				_delay_ms(50);
				PWM_LVL = beacon_background;

				_delay_ms(950);
				if(selected_pwm == MODE_ALPINE_DISTRESS_BEACON)
				{
					if(ii > 5)
					{
						_delay_ms(59000);
						ii = 255;
					}
					else
						_delay_ms(9000);
				}
				else if(selected_pwm == MODE_SLOW_BEACON_WITH_BACKGROUND)
					_delay_ms(1500);
#endif
				break;
			default:
				sleep_mode();
				break;
			}

			ii++; // Loop counter, used by multiple branches
		}
	}

#ifdef ENABLE_VOLTAGE_MONITORING
	//
	// Critically low voltage -> Turn off the light
	// 
		
	// Disable WDT so it doesn't wake us up from sleep
	WDT_off();

	// Would be nice to blink a couple of times with lowest brightness to notify the user, but not implemented due space restrictions

	// Turn the light off
	PWM_LVL = 0;

	// Disable ADC so it doesn't consume power
	ADC_off();

	// Power down as many components as possible
	set_sleep_mode(SLEEP_MODE_PWR_DOWN);

	// Deep sleep until powered off - consumes ~110uA during the deep sleep
	while(1)
		sleep_mode();
#endif

    return 0; // Standard Return Code -> would return to idle loop with interrupts disabled.
}