Effect switcher and relays - EEPROM question

Hi everyone! I have a project that I'm sort of working backwards on... I started it 7 or 8 years ago, and am finally getting to finishing it up. I'm good at understanding and figuring out, but am a total noob with code.

Using an Arduino Mega, this project uses 9 DPDT single coil latched relays to create 9 "loops" to effect pedals when engaged, and just have the audio pass through when not engaged.

I found the initial code in an instructable - and it works! But it used the Arduino relay modules, which I'm not using - I've created my own relay setup using TQ2-L-5V relays. To engage the relay, it needs positive to one side / negative to the other, but to disengage the relay, I have to flip the voltage. I've attached my method for doing that, which utilizes a transistor setup for NOT gate. (I've attached that schematic). I've tested that, and it works.

My issue is that the code, as is, which uses the keypad library/setup and sets each relay's status from EEPROM preset, only has one pin for engaging the relays. (In the code, lines 100 to 107 are where the relays are engaged, I think).

void readPreset(int addr, int pcNum, int led)
{
  for(int i=0; i<numberOfPedal; i++)
  {
    digitalWrite(relayPin[i], EEPROM.read((addr)+i));
    digitalWrite(ledPin[i], HIGH);
    digitalWrite(ledPin[led], LOW);
  }
  midiProg(0xC0 | midiChannel , pcNum);  /* send midi change program 1 */
}

With my relay setup, I need a "resetPin" to go HIGH while the relayPin goes LOW - and I'd like that to be more of a "pulse" - 100ms maybe; enough to engage the relayPin LOW (then HIGH after 100ms) / resetPin HIGH, and when it's time to disengage relayPin HIGH / resetPin LOW (then high after 100ms).

I hope that makes sense... Basically, instead of one pin for the relay just being ON - I need it to pulse on and then off - and I need another pin to work opposite the relays.

Any thoughts? I appreciate it in advance!

#include <EEPROM.h>
#include <Keypad.h>
const byte rows = 3;
const byte cols = 3;
char keys[rows][cols] = {
{'a','f','k'}, 
{'b','g','l'},
{'c','h','m'}
};
byte rowPins[rows] = {2,3,4}; /* buttons */
byte colPins[cols] = {5,6,7}; /* rotary switch */
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);
int relayPin[3] = {8,9,10};
int resetPin[3] = {11,12,13};
int ledPin[3] = {A0,A1,A2};
byte midiChannel = 0;

int numberOfPedal = 3;

/******************************************************/
void setup()
{
  for(int c=0; c<numberOfPedal; c++) /* setup test phase */
  {
    pinMode(relayPin[c], OUTPUT);
    pinMode(resetPin[c], OUTPUT);
    pinMode(ledPin[c], OUTPUT);
    digitalWrite(resetPin[c], HIGH);
    digitalWrite(relayPin[c], LOW);
    digitalWrite(ledPin[c], LOW);
  }
  delay(2000);
  for(int d=0; d<numberOfPedal; d++) 
  {
    digitalWrite(relayPin[d], HIGH);    
    digitalWrite(ledPin[d], HIGH);
  }
readPreset(11, 1, 0); /* initiate  default mode */
Serial.begin(31250); /* for midi communication - pin 1 TX */
/*for (int i = 0; i < 10; i++) // erase eeprom (optional)
   // EEPROM.write(i, 0); */

   
}
/*********************************************************/
void midiProg(byte status, int data) 
 {
  Serial.write(status);
  Serial.write(data);
 }
 /*********************************************************/
void memory(int addr, int led)
{
  for(int i=0; i<numberOfPedal; i++)
  {
    EEPROM.write((addr) + i, digitalRead(relayPin[i]));
    digitalWrite(ledPin[i], LOW); // all leds reset
  }
  delay(100);
    digitalWrite(ledPin[led], HIGH);
    delay(100); 
  digitalWrite(ledPin[led], LOW);
  delay(100); 
  digitalWrite(ledPin[led], HIGH);
  delay(100);
  digitalWrite(ledPin[led], LOW);
  delay(100); 
  digitalWrite(ledPin[led], HIGH);
  delay(100);
  digitalWrite(ledPin[led], LOW);
  delay(100); 
  digitalWrite(ledPin[led], HIGH);
}

/*********************************************************/
void resetAllRelays()
{
  for(int i=0; i<numberOfPedal; i++)
  {
    digitalWrite(relayPin[i], HIGH);
  }
}
/*********************************************************/
void resetAllLeds()
{
  for(int i=0; i<numberOfPedal; i++)
  {
    digitalWrite(ledPin[i], HIGH);
  }
}
/*********************************************************/
void writeOut(int relay)
{
  resetAllLeds();
  digitalWrite(relayPin[relay], !digitalRead(relayPin[relay]));
  /*digitalWrite(ledPin[relay], !digitalRead(relayPin[relay]));
  /* thanks to  anton.efremoff.1 for this tip */
}
/*********************************************************/
void readPreset(int addr, int pcNum, int led)
{
  for(int i=0; i<numberOfPedal; i++)
  {
    digitalWrite(relayPin[i], EEPROM.read((addr)+i));
    digitalWrite(ledPin[i], HIGH);
    digitalWrite(ledPin[led], LOW);
  }
  midiProg(0xC0 | midiChannel , pcNum);  /* send midi change program 1 */
}
/*********************************************************/
void loop()
{
  
  char key = keypad.getKey();
  if(key)  // Check for a valid key.
  {
   switch (key)
      { 
    case 'a':  // a to x 
      writeOut(0); // relay
      break; 
    case 'b': 
      writeOut(1);
      break;
    case 'c': 
      writeOut(2);
      break;
    case 'd': 
      writeOut(3);
      break;
    case 'e': 
      writeOut(4);
      break;  
    /****************************** STORE PRESET MODE */       
    case 'f': 
      memory(11,0);  //addr, led
      break; 
    case 'g': 
      memory(21,1);
      break;
    case 'h': 
      memory(31,2);
      break;
    case 'i': 
      memory(41,3);
      break;
    case 'j': 
      memory(51,4);
      break;
    /****************************** READ PRESET MODE */      
    case 'k':  
      readPreset(11, 1, 0); // addr, pcNum, relay
      break; 
    case 'l':  
      readPreset(21, 2, 1);
      break;   
    case 'm': 
      readPreset(31, 3, 2);
      break;
    case 'n': 
      readPreset(41, 4, 3);
      break;
    case 'o': 
      readPreset(51, 5, 4);
      break;   
      }
   }
}

Interesting that should work with the correct values. If you have a higher voltage available for the relays that would help as you will get less then 5V because of the drop across the 100 Ohm Resistor.

it's easy enough to turn one of 2 pins ON for 100 msec

void
pulsePin (
    int  pin )
{
    digitalWrite (pin, HIGH);
    delay        (100);
    digitalWrite (pin, LOW);
}

but you need to define 2 pins for each pedal(?) and decode which one to pulse. probably need to track the state of each pedal to know which pin to pulse

@greysun will also need to consider what that 100ms delay does to the appearance of the "effect". If he wants to change the state of all "pedals" in a loop "simultaneously" (at least to the human eye), then the delay may be noticeable. In which case, non-block techniques would be required.

.... Or, set the state of all relays at once followed by a single (common) delay, followed by removal of the pulse.

Wouldn't it be easier to use the TQ2-L2-5V relays with the 2 set and reset coils? 1 pin turns it on and the other turns it off.

I understand the benefit of using a latching relay. And of course you have the ones you have, which are single coil.

Either single or double coil latching relays will obvsly need more pain to deal with than one regular non-latching.

I assume that to get the relay in one state you write HIGH to one of the driver circuit sides and LOW to the other, and vice versa to toggle it back. Otherwise both sides are LOW and the relay doesn't change.

For now let's also say you have enough output pins to do this directly.

Every

     digitalWrite(relayPin, HIGH);

or LOW can be replace with a function call

    switchRelay(N, 1);   // 1 for true

or 0 false for off… now referred to by number, with the actual output pins stored in arrays.

(edited to fix small issue with timing)

int leftPin[4] = {2,4,6,8};
int rightPin[4] = {3,5,7,9};

void switchRelay(int relayNumber, int onOff)
{
  if (onOff) {  // close the contacts
    digitalWrite(leftPin[relayNumber], LOW);
    digitalWrite(rightPin[relayNumber], HIGH);
    delay(100);
  }
  else {  // open the contacts
    digitalWrite(rightPin[relayNumber], LOW);
    digitalWrite(leftPin[relayNumber], HIGH);
    delay(100);
  }

  delay(100);  // lose this, or both the delays in the if/else statement.

  digitalWrite(leftPin[relayNumber], LOW);
  digitalWrite(rightPin[relayNumber], LOW);
}

This will take time, but it may not matter. The 100 seems very conservative, I have relays that switch in 5 milliseconds. Check the spec sheet and maybe your drive circuitry is slowing it down if you've needed 100 milliseconds.

Yes, the data sheet sez 3 milliseconds. If you aren't seeing that performance, you needa rethink the drive circuit.

a7

Yes. It would eliminate any question about how to drive the coils.

Each relay would still, however, need in any simple approach two pins, and a functional cover for dealing with the two sides.

I think the same function would work or be close.

If new hardware is in the picture, perhaps relays isn't even the best f/x switching method.

a7

It would cost about $30USD to replace 9 TQ2-L-5V with the TQ2-L2-5V. The simple solution.

Or you could use an H-Bridge such as the L298 or roll your own similar to

Alto777 has the logic right (I'll reply to the other posts shortly - thank you to everyone that posted, this has been very helpful)

I have some questions first, hehe...

I assume that to get the relay in one state you write HIGH to one of the driver circuit sides and LOW to the other, and vice versa to toggle it back. Otherwise both sides are LOW and the relay doesn't change.

Correct - a complete reversal is needed. It only engages when the voltage is inverted - 5v+0v to engage, 0v+5v to disengage. Once the relay is engaged, it will stay in that state until it's disengaged, so I don't have to have constant power to it - just enough to switch it on, and then just enough to switch it off.

In the current code, the relay is triggered based on what's saved to the Arduino, or EEPROM.read (so if addr has something stored, that is what triggers the relevant relays, while leaving the others off, I assume).

void readPreset(int addr, int pcNum, int led)
{
  for(int i=0; i<numberOfPedal; i++)
  {
    digitalWrite(relayPin[i], EEPROM.read((addr)+i));
    digitalWrite(ledPin[i], HIGH);
    digitalWrite(ledPin[led], LOW);
  }
  midiProg(0xC0 | midiChannel , pcNum);  /* send midi change program 1 */
}

So in the code you made above (and, btw, THANK YOU for taking the time to do that!), is the switchRelay something that works whenever left pin is engaged in the code I already have, or does this code replace my EEPROM.read code in some fashion? I'm just trying to understand the logic and am somewhat noob-ish on this stuff.

Regarding the delay - yes, 100 is very conservative. While I'm testing with LEDs, it's very long - like 1000 - so I can see that it's functioning. In situ, I would be decreasing to probably 10ish to ensure enough time between the OPTO and NOT gate engage.

Let me know about the above when you get a moment - again, thank you so much for the reply!

(as for why I have all these relays - I built a pedalsync system and it was not quite right. I have all the parts needed for this and would like to use them if I can. The Opto/NOT gate circuit works well and gets me about 4.25v, while the relays only need 3.75v to engage, so I have a good buffer there while not taking too much mA (I think around 50 per relay). Regarding needing an extra pin for each relay, I have plenty of pins on the Arduino, so I'm not concerned on that front).

My function is… agnostic. It neither can know nor care who is calling it with what arguments.

So the above, which presumably ends up writing HIGH or LOW to one relay pin, can be directly replaced with

    switchRelay(i, EEPROM.read((addr)+i));

provided you have arranged the ledtPin[] and rightPin[] arrays to hold the appropriate real pin numbers.

Instead of writing directly to relay number i by using digitalWrite() with the i index into one array of pins (relayPin[i]), we send the relay number for the function to use as an index to access the guide arrays.

BTW i is never a good variable name no matter how locally used. In a loop where it is all close by, even ii is better.

I understand about the delay() needing to be larger during development so you can see what is going on. I didn't worry too much about using delay() as it didn't look like a deal break or anything.

When you get along a bit more, you'll want to start losing all calls to delay(). Then a nice trick can be used to make an LED stay on for a good visible stab like 50 or 100 milliseconds, even if the thing the LED is telling tales about is only on for 5 ms.

a7

This makes sense, and in reading it, you've sparked an idea... I was going to add a loopLED pin, which mirrors the relayPin so I can see which effects are engaged - your if/else logic might be able to work with that. I'll give it a test at lunch and keep you posted.

In looking at my more expansive code from 2017 (the code I shared here is sort of a "start from scratch, build back into my more robust code" sort of thing), I just used different letters for each "for" statement, avoiding "i". Is that sufficient, or should I be using double letters like you've suggested?

Thank you, again!

Part of the point is to use something that won't be found with a text search in many many many places that aren't what you are looking for.

So ii would at least restrict hits to uses of that as a variable.

Better (not very good at it myself) would be to use meaningful variable names. Many ppl don't bother with loop index variables and use ii, jj and so forth.

I don't like posting untested code and I do like running other ppl's code to see it working (or not). So I placed your code in the wokwi simulator.

Instead of a keypad, enter characters in the serial input area at the bottom of the diagram window (right hand side). I only wired one single coil latching relay, using LEDs as proxy so you can see the pulses, and added a tellTale[ ] array of LEDs to reflect the current state.

I just called through to the switchRelay() fubnction from your writeOut() function.

Oh, I also stepped around the MIDI stuff.

I only tried typing 'a' and it seems to work. Since I only placed components for channel 'a'.

Play with it here:


Wokwi_badge Umbrella Academy Latching Relay Thing


I've finished this just in time. She who must not be kept waiting has texted, we off to the beach!

a7

I played with it and saw that I did get the function wrong as far as where the delays are exact.

This is what runs in the wokwi and is fixed:

void switchRelay(int relayNumber, int onOff)
{
  if (onOff) {  // close the contacts
    digitalWrite(leftPin[relayNumber], LOW);
    digitalWrite(rightPin[relayNumber], HIGH);
    delay(500);

    digitalWrite(tellTale[relayNumber], HIGH);
  }
  else {  // open the contacts
    digitalWrite(rightPin[relayNumber], LOW);
    digitalWrite(leftPin[relayNumber], HIGH);
    delay(500);

    digitalWrite(tellTale[relayNumber], LOW);
  }

  delay(100);

  digitalWrite(leftPin[relayNumber], LOW);
  digitalWrite(rightPin[relayNumber], LOW);
}

Sets the two pins, then delays.

a7

This is what I would do to make the pulse generation non-blocking and abstract all the relay details into a class. The main methods of the class are:

cycleAll() – called on every pass through loop to handle the non-blocking pulse generation.

set() – called to send a pulse for the “set” relay position.

reset() – called to send a pulse for the “reset” relay position.

class Pedal {
  public:
    Pedal(uint8_t set, uint8_t reset) : setPin(set), resetPin(reset) {
      for (auto &ptr : pedalArray) {
        if (ptr == nullptr) {
          ptr = this;
          break;
        }
      }
    }

    ~Pedal() {
      for (auto &ptr : pedalArray) {
        if (ptr == this) {
          ptr = nullptr;
          break;
        }
      }
    }

    Pedal(const Pedal & rhs) = delete;
    Pedal &operator=(const Pedal & rhs) = delete;

    void begin() {
      pinMode(setPin, OUTPUT);
      pinMode(resetPin, OUTPUT);
      clear();
    }

    void clear() {
      digitalWrite(setPin, LOW);
      digitalWrite(resetPin, LOW);
      active = false;
    }

    bool inline isActive() const {
      return active;
    }

    bool set() {
      if (active) {
        return false;
      }
      active = true;
      digitalWrite(setPin, HIGH);
      timer = millis();
      return true;
    }

    bool reset() {
      if (active) {
        return false;
      }
      active = true;
      digitalWrite(resetPin, HIGH);
      timer = millis();
      return true;
    }

    static void cycleAll() {
      uint32_t now = millis();
      for (auto &ptr : pedalArray) {
        if ((ptr != nullptr) && ptr->active) {
          if (now - ptr->timer >= pulseWidth) {
            ptr->clear();
          }
        }
      }
    }

  private:
    static constexpr uint32_t pulseWidth = 100;
    static constexpr uint8_t maxPedals = 10;
    static Pedal *pedalArray[maxPedals];
    const uint8_t setPin;
    const uint8_t resetPin;
    uint32_t timer;
    bool active = false;
};

Pedal * Pedal::pedalArray[] = {nullptr};

Pedal pedals[] = {
  {8, 11},
  {9, 12},
  {10, 13}
};

void setup() {
  Serial.begin(115200);
  delay(1000);

  for (auto &p : pedals) {
    p.begin();
  }
}

void loop() {
  Pedal::cycleAll();  // Call this function on every pass through loop()

  if (I want to set Relay x) {
    pedals[x].set();
  }

  if (I want to reset Relay x) {
    pedals[x].reset();
  }
}

Thank you again, this was MASSIVELY helpful. Your code worked on its own in that test, but I had a hard time getting it to work with my buttons and programming, so I used your if/then setup within my current code and got it working!

https://wokwi.com/projects/386646738777324545

(I love that tool, btw - thank you for that!)

To program it and see for yourself, it's a little tricky with the 2 switch setup (there wasn't a rotary switch option for that emulator) - you can toggle the first (left) switch to the right, then press the buttons (or effects) that you want (yellow LEDs denote "effects"). Flip the second switch (right) to the right, then press one of the buttons again. The green LED flashes, showing that you've stored something for that button/preset. Repeat for the other 2 presets, and then swap the first toggle (left) to the left, and you're back in read/use mode.

Everything operates as it should - the relays engage properly based on what effects are stored in the preset, and all of the LEDs are operating properly and as expected.

I made sure to comment what each line does much better this time around so I can figure it out a little quicker next time, too.

I still have a ways to go on this (adding a bank button, LCD readout, and figuring out a better solution for the programming buttons), but for this specific relay issue, I think we can call solved?

#include <EEPROM.h>
#include <Keypad.h>
const byte rows = 3;
const byte cols = 3;
char keys[rows][cols] = {
{'a','f','k'}, 
{'b','g','l'},
{'c','h','m'}
};
byte rowPins[rows] = {2,3,4}; /* buttons or momentary switches */
byte colPins[cols] = {5,6,7}; /* rotary switch - pin 7 is read/use, pin 5 is program/choose effects, pin 6 is save to a preset. */
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);
int OneRelayPin[3] = {8,9,10}; /* pin 1 on relay - reset/default has pin 1 at 0v - RED */
int TenRelayPin[3] = {11,12,13}; /* pin 10 on relay - reset/default has pin 10 at 5v - BLUE */
int presetLEDPin[3] = {A0,A1,A2}; /* shows which preset is engaged - GREEN */
int effectLEDPin[3] = {A3,A4,A5}; /* shows which effects are engaged - YELLOW */
byte midiChannel = 0;

int numberOfPedal = 3;

/******************************************************/
void setup()
{
  for(int cc=0; cc<numberOfPedal; cc++) /* setup device */
  {
    pinMode(OneRelayPin[cc], OUTPUT);
    pinMode(TenRelayPin[cc], OUTPUT);
    pinMode(presetLEDPin[cc], OUTPUT);
    pinMode(effectLEDPin[cc], OUTPUT);
    digitalWrite(OneRelayPin[cc], LOW);
    digitalWrite(TenRelayPin[cc], LOW);
    digitalWrite(presetLEDPin[cc], LOW);
    digitalWrite(effectLEDPin[cc], LOW);
    delay(100);
  }
  delay(100);
  for(int dd=0; dd<numberOfPedal; dd++) 
  {
    digitalWrite(OneRelayPin[dd], HIGH);
    delay(100);  
  }
  delay(100);
  for(int ee=0; ee<numberOfPedal; ee++)
  {
    digitalWrite(TenRelayPin[ee], HIGH);
    digitalWrite(presetLEDPin[ee], HIGH);
    digitalWrite(effectLEDPin[ee], HIGH);
    delay(100);
  }
  delay(1000);

  readPreset(11, 1, 0); /* pulls first saved preset in memory */
  Serial.begin(31250); /* for midi communication - pin 1 TX */
  /*for (int i = 0; i < 10; i++) // erase eeprom (optional)
   // EEPROM.write(i, 0); */
   
}
/*********************************************************/
void midiProg(byte status, int data) 
 {
  Serial.write(status);
  Serial.write(data);
 }
 /*********************************************************/
void memory(int addr, int led)
{
  for(int ff=0; ff<numberOfPedal; ff++)
  {
    EEPROM.write((addr) + ff, digitalRead(effectLEDPin[ff]));
    digitalWrite(presetLEDPin[ff], HIGH); // turns off all preset LEDs
  }
  /* Preset LED will flash when saving effects loops */
  delay(100); 
  digitalWrite(presetLEDPin[led], LOW);
  delay(100); 
  digitalWrite(presetLEDPin[led], HIGH);
  delay(100);
  digitalWrite(presetLEDPin[led], LOW);
  delay(100); 
  digitalWrite(presetLEDPin[led], HIGH);
  delay(100);
  digitalWrite(presetLEDPin[led], LOW);
  delay(100); 
  digitalWrite(presetLEDPin[led], HIGH);
  delay(100);
  digitalWrite(presetLEDPin[led], LOW);
  delay(100);
  digitalWrite(presetLEDPin[led], HIGH);
}

/*********************************************************/
void resetAllRelays()
{
  for(int gg=0; gg<numberOfPedal; gg++)
  {
    digitalWrite(OneRelayPin[gg], HIGH);
    digitalWrite(TenRelayPin[gg], LOW);
  }
  delay(1000);
  for(int hh=0; hh<numberOfPedal; hh++)
  {
    digitalWrite(TenRelayPin[hh], HIGH);
  }
}
/*********************************************************/
void resetAllLeds()
{
  for(int ii=0; ii<numberOfPedal; ii++)
  {
    digitalWrite(presetLEDPin[ii], HIGH);
  }
}
/*********************************************************/
void writeOut(int relay)
{
  resetAllLeds();
  digitalWrite(effectLEDPin[relay], !digitalRead(effectLEDPin[relay]));
  /*digitalWrite(ledPin[relay], !digitalRead(relayPin[relay]));
  /* thanks to  anton.efremoff.1 for this tip */
}
/*********************************************************/
void readPreset(int addr, int pcNum, int led)
{
  for(int jj=0; jj<numberOfPedal; jj++)
  {
    digitalWrite(effectLEDPin[jj], EEPROM.read((addr)+jj));
    digitalWrite(presetLEDPin[jj], HIGH);
    digitalWrite(presetLEDPin[led], LOW);
    if (EEPROM.read((addr)+jj)) {  
      digitalWrite(OneRelayPin[jj], HIGH);
      digitalWrite(TenRelayPin[jj], LOW);
    }
    else {  
      digitalWrite(OneRelayPin[jj], LOW);
      digitalWrite(TenRelayPin[jj], HIGH);
    }
  }
  delay(1000);
  for(int kk=0; kk<numberOfPedal; kk++)
  {
    digitalWrite(OneRelayPin[kk], HIGH);
    digitalWrite(TenRelayPin[kk], HIGH);
  }
}
  
/*********************************************************/
void loop()
{
  
  char key = keypad.getKey();
  if(key)  // Check for a valid key.
  {
   switch (key)
      { 
    case 'a':  // a to x 
      writeOut(0); // relay
      break; 
    case 'b': 
      writeOut(1);
      break;
    case 'c': 
      writeOut(2);
      break;
    case 'd': 
      writeOut(3);
      break;
    case 'e': 
      writeOut(4);
      break;  
    /****************************** STORE PRESET MODE */       
    case 'f': 
      memory(11,0);  //addr, led
      break; 
    case 'g': 
      memory(21,1);
      break;
    case 'h': 
      memory(31,2);
      break;
    case 'i': 
      memory(41,3);
      break;
    case 'j': 
      memory(51,4);
      break;
    /****************************** READ PRESET MODE */      
    case 'k':  
      readPreset(11, 1, 0); // addr, pcNum, relay
      break; 
    case 'l':  
      readPreset(21, 2, 1);
      break;   
    case 'm': 
      readPreset(31, 3, 2);
      break;
    case 'n': 
      readPreset(41, 4, 3);
      break;
    case 'o': 
      readPreset(51, 5, 4);
      break;   
      }
   }
}

This is true, but I already have what I have - While $30 for new ones isn't gonna break the bank, I wanted to see if I could use what I have. I think it's technically the same number of pins and same setup for each relay with the TQ2-L2, as well. I was able to get it figured out with the relays I have for now, but if they don't work in actual testing, I may be getting the TQ2-L2s soon.

Benefit to that is I wouldn't need to convert anything to 5v (could just get the TQ2-L2-9v), but we'll cross that bridge if/when necessary, hehe.

This is so thorough! I'm not sure how to finagle this in with what I've done - unless you're suggesting this replaces the keypad stuff I've been working with?

I was able to get what I needed working, but I think I need to study this code to learn a bit more in-depth about this platform!

My 5v signal from the LM7805 is 5.8v - when it goes through the 100r and into the coil, it's at about 4.25v. The minimum to set and reset the relay is ~3.75v, so I have a little wiggle room depending on my power source, and it's pulling about 50mA per relay.

the MAX voltage for the relay is 7.5v, so it could handle more voltage, but I think it's good where it is for now!

I'll definitely set the timing to be sure not all of them go off at once, hehe.

I don't argue with success. That said, it is too bad the function wasn't able to simlpy replace the calls to digitalWrite() what would have controlled regular relays. That was sorta the point, to change out the relay type with minimally invasive surgery.

I see you leave both sides HIGH, which does the same thing as leaving both sides LOW, I think, if I understand the interface circuitry that is between the output pins and the physical relay. LOW might be better if only to not slow down the reader - it seems to say off or unenergized at an intuitive level.

One thing it (the if/then code copied out to do the relay) is something you've said you would avoid, or hinted at. With the function, the relays will switch one at a time. With your code, the first loop will energize the appropriate direction all attached relays boom! then pause before it turns those driven coils off. I do not think it is a problem, just wanted to show that there can be unintended consequences to innocent changes.

When I get to the lab, I will be compelled to write at least one example of how to use just the function. Partly because it should have just worked, and partly because as sketches get larger, you will begin to appreciate that functions can make code more readable at the high level, and more easily written, fixed, modified or enhanced at the low level.

With the function, once you've made the pin arrays you can put the hole mess below the fold, so to speak, and never tax your eyes with reading through the energize dance which is switching the relay on or off. Multiply that by several other things that might be similarly directly coded in several places in you sketch and you might see what I mean.

I'd even put a relaySetup() function down there which would set the pin modes and do the initial state of the relay and so forth, leaving just one line, the function call, in setup().

@gfvalvo's version is the next big step or two - there's more that could be done with language features you should know about but are not so advanced. The C++ version does the same thing you do know, and what I did, and does more - like taking care of finishing out the relay pulses automagically behind the scenes in a totally non-blocking (delay()-less) manner.

If that class was lifted out and placed in a library or even tab in your sketch it would be even easier to forget all about its innards.

I can read that code. So far writing like that is something of a slog, so know that no matter where you are on the learning curve there will always be more to learn. If I am being honest, I figure I still have way more to learn than I already have. About so many things…

a7

No, it's only a class that provides an abstract representation of a Latching Relay and handles generation of the required coil pulses in a non-blocking way. It's left as an exercise for the reader to work it into the framework of a larger code.