JTEX
Well-known member
This code uses momentary, SPST, normally-open pushbuttons (or stompswitches) to toggle On/Off some dual coil latching relays. I'm not a seasoned Arduino programmer by any stretch, so it took me quite a while to come up with it. Especially since I had to figure out a way to cut down on power as much as possible, for battery operation, so I had to use sleep mode and hardware interrupts when buttons are pressed. First time using either... 
Hope it helps someone. Do whatever you want with it.
 
	
	
	
		
				
			Hope it helps someone. Do whatever you want with it.
 
		C:
	
	// ****** 3 momentary pushbuttons (tactile switches) toggle 3 dual coil DPDT latching relays (push on/push off) ******
// Coded by Jerry Catanescu (jtex.ca)
// VERSION 0.1, 2022-12-28
//
// Works with Digispark Pro (ATtiny167 Pro). Optimised for minimal power use
// (go to sleep mode whenever idle, use interrupts to wake up only when a button is pressed)
//
// I used Kemet EA2-5TNJ dual coil latching relays. A 5V-powered ATtiny167/87 can drive them directly. Not even discrete flyback diodes are needed.
// The momentary pushbuttons are NO (normally open).
// One pin of every pushbutton, as well as the negative pins of all relay coils, are connected to ground.
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
const byte rly_setpin[3] = { 6, 8, 10 };  // relay +SET pins
const byte rly_rstpin[3] = { 7, 9, 11 };  // relay +RESET pins
const byte btn_pin[3] = { 0, 1, 2 };      // pushbutton pins (note: there's an LED on pin 1 Digispark Pro. I removed it. You can leave it alone and use another pin if you prefer.)
bool rly_state[3] = { 0, 0, 0 };          // array containing the current relay states:  0 = RESET, 1 = SET
// setup() runs only once when program starts:
void setup() {
  for (byte i = 0; i < 12; i++) {
    pinMode(i, INPUT_PULLUP);  // initially set all pins to INPUT_PULLUP, which reduces current draw
  }
  // iterate through all relays, set up their pins and initialize
  for (byte i = 0; i < 3; i++) {
    pinMode(rly_setpin[i], OUTPUT);  // configure all SET coil pins as OUTPUT
    pinMode(rly_rstpin[i], OUTPUT);  // configure all RESET coil pins as OUTPUT
    digitalWrite(rly_setpin[i], 0);  // initialize SET pins to 0
    digitalWrite(rly_rstpin[i], 1);
    delay(10);
    digitalWrite(rly_rstpin[i], 0);  // send pulse to latch the relays in the RESET state
  }
  // Turn off unused ATtiny167 subsystems to save power
  power_adc_disable();  // Analog to Digital Converter
  power_lin_disable();  // LIN module
  power_spi_disable();  // Serial Peripheral Interface
  power_usi_disable();  // Universal Serial Interface
  power_timer1_disable();
  // Pin Change Mask Register 1. Applies to Port B pins (PB0, 1...). See ATtiny167 datasheet
  PCMSK1 = 0b00000111;  // Turn on interrupt triggering for the specified pin(s) (logic 1)
  // Pin Change Interrupt Control Register. See ATtiny167 datasheet.
  PCICR = 0b00000010;   // Second bit from the right is the Port B bank.
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
}
// Interrupt handler fires whenever there is a logic change on the designated Port B interrupt pin(s) (where the pushbuttons are wired)
ISR(PCINT1_vect) {
  delayMicroseconds(5000);      // just wait a bit to avoid firing multiple times due to bouncy button
                                // then we simply exit and return control to the main loop()
}
// latch relays in the SET or RESET state
void latchRelay() {
  noInterrupts();  // disable interrupts, to ignore any button activity (glitches, double-presses etc) that may happen until we're done latching the relay (debounce)
  bool btnLastState[3] = { HIGH, HIGH, HIGH };    // for starters, initialize all button states as "up" (logic HIGH)
  do {
    for (byte i = 0; i < 3; i++) {
      if (digitalRead(btn_pin[i]) == LOW) {       // if this button # was pushed
        // delayMicroseconds() works even while the interrupts are turned off, unlike delay()
        delayMicroseconds(3000);                      // wait a bit to account for bouncy contacts.
        if (digitalRead(btn_pin[i]) == LOW) {         // read button again after a delay, to insure it's still down (no glitches)
          delayMicroseconds(2000);
          if (digitalRead(btn_pin[i]) == LOW) {       // read button state the 3rd time. If it's still low, we accept it as stable
            if (btnLastState[i] == HIGH) {
              rly_state[i] = !rly_state[i];           // toggle the relay state
              btnLastState[i] = LOW;                  // record the fact that the button was pressed
              if (rly_state[i]) {                     // if the relay should be latched to the SET state
                digitalWrite(rly_setpin[i], 1);
                delayMicroseconds(5000);              // Hold the logic "1" long enough to latch the relay. 10ms shoud do it.
                digitalWrite(rly_setpin[i], 0); 
              } else {                                // if the relay should be RESET
                digitalWrite(rly_rstpin[i], 1);
                delayMicroseconds(5000);
                digitalWrite(rly_rstpin[i], 0);
              }
            }
          }
        }
      } else {                                    // if this button # wasn't pushed
        btnLastState[i] = HIGH;
      }
      // optional: do something if all 3 buttons are pressed simulaneously
      if (!digitalRead(btn_pin[0]) && !digitalRead(btn_pin[1]) && !digitalRead(btn_pin[2])) {  // all three buttons pressed
        // do something awesome, maybe? Such as, true bypass? TBD.
      } else {
        // placeholder
      }
      
    }
  } while (!digitalRead(btn_pin[0]) || !digitalRead(btn_pin[1]) || !digitalRead(btn_pin[2]));  // stay in this loop as long as at least one button is pressed
  interrupts();     // re-enable interrupts before exiting function
}
/////////////////////////  MAIN PROGRAM LOOP ///////////////////////////////////
void loop() {
  sleep_cpu();   // Sleep all the time except when woken by an interrupt (meaning that a button was pressed)
  latchRelay();  // If we ever get here, we got kicked out of sleep by a button press, so we need to handle the relays
}
			
				Last edited: 
			
		
	
								
								
									
	
								
							
							 
	
 
 
		 
 
		 
 
		

