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 } }
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. }