#include <avr/io.h>
#include <util/delay.h>
#include <avr/power.h>
#include <avr/sleep.h>

// The simulation diagram is for non-latching relay and use of the control feature.
// The button on the right in the diagram is the foot switch.
// The button on the left in the diagram represents an incoming control signal being asserted.
// The simulator doesn't have a latching relay. Only regular non-latching.
// Pin assignment was for ease of drawing the diagram. Choose what ever you need for your platform.

// There are two primary features included here:
//    Temporary Change:
//        If the foot switch is held for more than a half second, the relay toggles back to previous position when released.
//        This allows for a short use of a some foot pedal instead of needing to tap-on and tap-off again.
//
//    Control group:
//        Multiple modules can be connected together via the control line.
//        Only one module can be engaged at anytime.
//        When a module is engaged, it asserts the control line high.
//        Any other module hooked to the control line will disengage when it sees that.
//        This allows for creating a multi-channel effect where only one channel is to be active at a time (or none).
//
//    Both features also work together. If a channel is temporarily engaged, 
//    any module disengaged by the control feature will re-engage.

#define USE_CTRL            true    // Choose between control feature or a second LED.
#define USE_LATCHING_RELAY  false   // Using a latching relay? Simulator only has a regular relay.
#define NORMALLY_CLOSED     false   // Is the foot switch normally closed?
#define TEMPORARY_SWITCH    true    // Use Temporary change feature?
#define SLOW_CLOCK          false   // Slow the clock way down to save power? Simulator does not handle this correctly.
#define LEVEL_SENSITIVE     false   // Intended for monitoring a signal instead of a foot switch.
#define ENABLE_SLEEP        false   // Put the CPU to sleep while waiting for input. Doesn't quite work right in the simulator.

#define TEMPORARY_SWITCH_TIME_ms  500 // Foot switch held this long is a temporary change.
#define RELAY_SWITCH_TIME_ms      40  // Time needed to switch the relay.
#define DEBOUNCE_DELAY_ms         14  // Debounce time of the switch.
#define POWER_ON_DELAY_ms         0   // Hold in bypass for this long on power up.

#define PIN_LED    PB0  // LED is on PB0 on sparkfun programmer.
#define PIN_ENGAGE PB1  // Drives the engage pin of a latching relay or the coil of a non-latching relay.
#define PIN_BYPASS PB2  // Drives the disengage pin of a latching relay.
#define PIN_LED2   PB3  // For a second LED if the control feature is not use.
#define PIN_CTRL   PB3  // Not enough pins for control feature and a 2nd LED. Shared pin.
#define PIN_SW     PB4  // Connect to a momentary foot switch to ground.

#if SLOW_CLOCK == false
#define DELAY(ms) delay(ms)
#else // SLOW_CLOCK == true
// Try something much slower... 125kHz.
#define CLOCK_PRESCALE clock_div_128        // Divides 1Mhz down to 125kHz..
#define DELAY_DIVIDER  8                    // 1/8th the speed.
#define DELAY(ms) delay(ms/DELAY_DIVIDER)
#endif // SLOW_CLOCK

// Helper macros

#define ENABLE_PCIE   GIMSK |= (1 << PCIE)
#define DISABLE_PCIE  GIMSK &= ~(1 << PCIE)

#define LED_ON   PORTB |= (1 << PIN_LED); // high
#define LED_OFF  PORTB &= ~(1 << PIN_LED); // low

// CTRL and LED2 usually share the same pin. Can't use both features at the same time.
#if USE_CTRL == false
  #define LED2_ON  PORTB |= (1 << PIN_LED2); // high
  #define LED2_OFF PORTB &= ~(1 << PIN_LED2); // low
  #define CTRL_INPUT
  #define CTRL_OUTPUT
  #define CTRL_HIGH
  #define CTRL_LOW
#else 
  #define LED2_ON
  #define LED2_OFF
  #define CTRL_INPUT  DDRB &= ~(1 << PIN_CTRL);  // Input most of the time.
  #define CTRL_OUTPUT DDRB |= (1 << PIN_CTRL);   // Output some of the time.
  #define CTRL_HIGH  PORTB |= (1 << PIN_CTRL);   // high
  #define CTRL_LOW   PORTB &= ~(1 << PIN_CTRL);  // low
#endif // USE_CTRL == false

#define SW_PRESSED      false
#define SW_NOT_PRESSED  true

// Global state variables.
bool is_bypassed = true;          // Is relay in bypass state?
bool is_sw_pressed = false;       // Log the state of the switch in the ISR and get out of the ISR.
bool is_ctrl_asserted = false;    // Log the state of the control line in the ISR and get out of the ISR.

#if USE_CTRL == true
  bool honor_ctrl_signal = true; // Should we watch the CTRL line or not?
#endif // USE_CTRL

#if LEVEL_SENSITIVE == true
  bool sw_last_loop, sw_state = SW_NOT_PRESSED;
#endif // LEVEL_SENSITIVE

void setup() 
{
  #if SLOW_CLOCK == true
    clock_prescale_set(CLOCK_PRESCALE);
  #endif // SLOW_CLOCK

  // Configure the ports.
  DDRB &= ~(1 << PIN_SW);     // Switch pin is an input.
  PORTB |= (1 << PIN_SW);     // activate pull-up resistor for the switch.
  DDRB |= (1 << PIN_BYPASS);  // output
  DDRB |= (1 << PIN_ENGAGE);  // output
  DDRB |= (1 << PIN_LED);     // output
  DDRB |= (1 << PIN_LED2);    // output for 2nd LED
  CTRL_INPUT;                 // CTRL is an input in the idle state.

  PORTB &= ~(1 << PIN_BYPASS); // low
  PORTB &= ~(1 << PIN_ENGAGE); // low

  // wait for the pull-up on the switch pin to do its job,
  // otherwise PIN_SW can sometimes get false LOW readings.
  DELAY(5);

  // Set relay to be bypassed initially.
  is_bypassed  = true;
  write_bypass();

  DELAY(POWER_ON_DELAY_ms);

  PCMSK = (1 << PIN_SW); // Add the foot switch pin to the pin-change interrupt mask.
  #if USE_CTRL == true
    // Add the CTRL pin to the pin change interrupt mask.
    PCMSK |= (honor_ctrl_signal << PIN_CTRL);
  #endif // USE_CTRL

  // Low power mode...
  ADCSRA = 0;   // disable ADC
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  noInterrupts ();           // timed sequence follows
  sleep_enable();
 
  // turn off brown-out enable in software
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 
  interrupts ();             // guarantees next instruction executed
  ENABLE_PCIE;              // Enable the pin change interrupt.
}


void sleep()
{
  ENABLE_PCIE;              // Enable the pin change interrupt.
#if ENABLE_SLEEP == true
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sei();
  sleep_cpu();              // Sleep until interrupt.
  cli();
  sleep_disable();
#endif // ENABLE_SLEEP
}

///////////////////////////////////////////////////////////////
// The loop will sleep between interrupts.
#if LEVEL_SENSITIVE == true
void loop() 
{
  sleep();              // Sleep until interrupt.

  sw_state = read_switch();

  // Did the switch change?
  if (sw_state != sw_last_loop)
  {
    // If the switch is closed, set relay to bypass.
    // To reverse this, set NORMALLY_CLOSED to true.
    is_bypassed = (sw_state == SW_PRESSED);
    write_bypass();
  }

  sw_last_loop = sw_state;
  DELAY(DEBOUNCE_DELAY_ms);
}
#else // LEVEL_SENSITIVE == false
void loop() 
{
  sleep();              // Sleep until interrupt.

  if(is_sw_pressed == true) 
  {
    switchPressed();
  }
  #if USE_CTRL == true
    // We will honor the CTRL signal if we booted into the proper state.
    else if(honor_ctrl_signal == true)
    {
      // Has some other module issued a CTRL signal?
      if (is_ctrl_asserted == true) // Is CTRL pin high?
      {
          // Blocking call until the control signal is no longer active.
          ctrlState();
      }
    }
  #endif // USE_CTRL == true
}
#endif // LEVEL_SENSITIVE

// Interrupt driven to save power.
// This is invoked when the switch is pressed or the control signal is asserted.
// Figure out what caused the interrupt and get out.
ISR (PCINT0_vect)
{
  DISABLE_PCIE; // Disable further interrupt.

  #if USE_CTRL == true
    // We will honor the CTRL signal if we booted into the proper state.
    if( (honor_ctrl_signal == true) &&
        (bool(PINB & (1 << PIN_CTRL)) == true) ) // Has some other module issued a CTRL signal?
    {
      is_ctrl_asserted = true;
    } 
    else
  #endif // USE_CTRL == true
    //if(read_switch() == SW_PRESSED)
    {
      // Assume the switch is pressed, else why did we get an interrupt?
      is_sw_pressed = true;
    }
}

/////////////////////////////////////////////////////////////////////////////////////
// Handle the state of a pressed switch. It's pressed when this is called.
void switchPressed()
{
  // Need to know how long the switch is pressed.
  unsigned long hold_time = DEBOUNCE_DELAY_ms;

  is_sw_pressed = false; // Reset the flag from the ISR.

  // The switch must remain pressed (debounced) to be valid.
  DELAY(DEBOUNCE_DELAY_ms);
  if(read_switch() == SW_PRESSED) 
  {
    #if USE_CTRL == true
      // Issue a ctrl pulse to tell other modules we're engaging.
      if (is_bypassed ==  true)
      {
          CTRL_OUTPUT; // Change the pin to output.
          CTRL_HIGH;   // High
      }
    #endif // USE_CTRL

    // Change the relay state.
    toggle_bypass_state();

    // Relay switching in toggle_bypass_state takes this amount of time.
    hold_time += RELAY_SWITCH_TIME_ms;

    // Now wait for the switch to release and track it's hold time.
    while (read_switch() == SW_PRESSED)
    {
      DELAY(DEBOUNCE_DELAY_ms);
      hold_time += DEBOUNCE_DELAY_ms;
    }

    #if USE_CTRL == true
      // End the ctrl pulse. Doesn't matter if we started one or not.
      CTRL_LOW;   // Low
      CTRL_INPUT; // Change the pin to input.
    #endif // USE_CTRL

    #if TEMPORARY_SWITCH == true
      // Was it a long press?
      if (hold_time > TEMPORARY_SWITCH_TIME_ms)
      {
        // Change the relay state again.
        toggle_bypass_state();
      }
    #endif // TEMPORARY_SWITCH == true

    // To get here, the switch is no longer pressed but perhaps not stable.
    // Wait for stabilzation to leave the Switch-Pressed state.
    DELAY(RELAY_SWITCH_TIME_ms);
  }
  ENABLE_PCIE;              // Enable the pin change interrupt.
}

/////////////////////////////////////////////////////////////////////////////////////
// Handle the state of an incoming CTRL signal. It's active when this is called.
#if USE_CTRL == true
void ctrlState()
{
  is_ctrl_asserted = false; // Reset the flag from the ISR.

  // If engaged, change to disengaged.
  if (is_bypassed == false)
  {
    toggle_bypass_state();
    unsigned long hold_time = RELAY_SWITCH_TIME_ms;

    // Wait for the CTRL signal to go away.
    while( bool(PINB & (1 << PIN_CTRL)) == true )
    {
      DELAY(DEBOUNCE_DELAY_ms);
      hold_time += DEBOUNCE_DELAY_ms;
    }

    #if TEMPORARY_SWITCH  == true
      // Was it a long press?
      if (hold_time > TEMPORARY_SWITCH_TIME_ms)
      {
        // Change the relay state back to engaged if this was temporary.
        toggle_bypass_state();
      }
    #endif // TEMPORARY_SWITCH == true
  }
  ENABLE_PCIE;              // Enable the pin change interrupt.
}
#endif // USE_CTRL == true

void toggle_bypass_state() 
{
  is_bypassed = !is_bypassed;
  write_bypass();
}

void write_bypass() 
{
  set_led();

#if USE_LATCHING_RELAY == true

  if (is_bypassed) 
  {
    PORTB |= (1 << PIN_BYPASS); // high
    DELAY(RELAY_SWITCH_TIME_ms);
    PORTB &= ~(1 << PIN_BYPASS); // low
  } 
  else 
  {
    PORTB |= (1 << PIN_ENGAGE); // high
    DELAY(RELAY_SWITCH_TIME_ms);
    PORTB &= ~(1 << PIN_ENGAGE); // low
  }

#else // USE_LATCHING_RELAY= false

  // Use static levels to drive devices other than the latching relay.
  if (is_bypassed) 
  {
    PORTB |= (1 << PIN_BYPASS); // high
    PORTB &= ~(1 << PIN_ENGAGE); // low
  }
  else
  {
    PORTB |= (1 << PIN_ENGAGE); // high
    PORTB &= ~(1 << PIN_BYPASS); // low   
  }
  DELAY(RELAY_SWITCH_TIME_ms);

#endif // USE_LATCHING_RELAY
}

void set_led() 
{
  if (is_bypassed) 
  {
    LED_OFF;
    LED2_ON;
  } 
  else 
  {
    LED_ON;
    LED2_OFF;
  }
}

// Finds switch state.
// "false" is a 0 on the pin, which is a pressed switch.
// "true" is a 1 on the pin, which is an open switch (not pressed).
bool read_switch() 
{
  bool rv = bool(PINB & (1 << PIN_SW));
#if NORMALLY_CLOSED == true
  rv = !rv;
#endif
  return rv;
}
ATTINY8520PU