74HC595 with Nano 33 iot and 8 relay module. Trouble controlling a single relay while state of others remains the same

Hi Everyone,

In order to move a project from a Mega2560 to a Nano 33 IoT I've set up a SN74HC595N.
I've connected Qa to Qh on the relay pins and put a 104 nF capacitor on VCC and GND after reading this thread about de-coupling, although I'm not 100% I have the right capacitor.
The 595 is powered from an external 5V and as far as I can tell I've done eveything right. (examples work as intented)

I've used these guides on how it works:
Guide to ShiftOut
595 Adruino tutorial
595 and Arduino full guide
ESP32 with 595 and relay modules
more pins with 595
And a bunch of topics with various applications. There was an example by sending a 0b00001000 bit to the shiftOut function directly to operate a single relay, but from what i understand this means all the others are off. Making an array of 255 possible combinations doesnt sound like a good idea.

All the examples work just fine after being uploaded, but I'm not looking for the LEDs on the relay module to show me a pretty pattern. I want to operate individual relays, and do so while the state of the other relays remains unchanged, as I did when the relay module was connected to my mega, with lots of pins.

leds = 0;        // Initially turns all the LEDs off, by giving the variable 'leds' the value 0
  updateShiftRegister();
  delay(500);
  for (int i = 0; i < 8; i++)        // Turn all the LEDs ON one by one.
  {
    bitSet(leds, i);                // Set the bit that controls that LED in the variable 'leds'
    updateShiftRegister();
    delay(500);
  }

This is great and all but I'm looking for a way to be able to use a direct value corresponding to a certain relay, and use that instead of "i"

To summarize: I don't fully understand what commands to send to the 595 to control it properly. :persevere:

You don't send commands, you send data!
Have a byte variable to remember the 8 relay states and set/clear the bits in it on each relay state change, then always send the updated byte in updateShiftRegister.

You can simply use

    bitWrite(leds, i, v);                // Set the bit that controls that LED in the variable 'leds'
    updateShiftRegister();

where i is the number of the relay and v is either 0 or 1.

I think your capacitor is not "104 nf". You are reading it incorrectly. I suspect it says only "104" on it, and what this means is 10 x 10^4 pf (pico-farads) = 100,000 pf = 100 nf (nano-farads) = 0.1 uf (micro-farads), which is an appropriate value for a bypass capacitor.

1 Like

Thank you @DrDiettrich,
You are right, data. What I meant was a function to translate my intentions into data :wink:
It is my assumption that 0b00000001 will turn relay 1 on and the rest off, and if I send 0b10000000 afterwards this wil turn 8 on and 1 off. Is my assumption correct?

And thank you @PaulRB for the bitWrite suggestion. With that suggestion I've started to try to create a program that lets me enter a number into the serial monitor and use that input to turn a relay on or off. That was a struggle on it's own as I'm just not very experienced. You were right about the pF as well, now I understand the 4, thanks!

At the moment I think I have the input and output of the serial monitor working with

  String num = String(receivedChars);
  int number = num.toInt();

to be able to use number in bitWrite, however it's not working at all.

When I upload the sketch, all relays go LOW (on) and stay there. with bitWrite(leds, number, 0); I'm trying to set one HIGH.

Here is the whole sketch:

int latchPin = 5;  // Latch pin of 74HC595 is connected to Digital pin 5
int clockPin = 6;  // Clock pin of 74HC595 is connected to Digital pin 6
int dataPin = 4;   // Data pin of 74HC595 is connected to Digital pin 4

byte leds = 0;  // Variable to hold the pattern of which LEDs are currently turned on or off
const byte numChars = 32;
char receivedChars[numChars];  // an array to store the received data
int number = 0;
boolean newData = false;

void setup() {
  // Set all the pins of 74HC595 as OUTPUT
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  relayControl();
  recvWithEndMarker();
  showNewData();
}

void relayControl() {

  String num = String(receivedChars);
  int number = num.toInt();

  
  leds = 0;  // Initially turns all the LEDs off, by giving the variable 'leds' the value 0
  updateShiftRegister();
  delay(500);

  if (number == 3) {
    bitWrite(leds, number, 0);  // Set the bit that controls that LED in the variable 'leds'
    updateShiftRegister();
  }
}

void updateShiftRegister() {
//updateShiftRegister() - This function sets the latchPin to low, then calls the Arduino function 'shiftOut' to shift out contents of variable 'leds' in the shift register before putting the 'latchPin' high again.
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, leds);
  digitalWrite(latchPin, HIGH);
}

void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (rc != endMarker) {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    } else {
      receivedChars[ndx] = '\0';  // terminate the string
      ndx = 0;
      newData = true;
    }
  }
}

void showNewData() {

  if (newData == true) {
    Serial.println(receivedChars);
    newData = false;
  }
}

Can you point me in the right direction?

So your relays are "active-low"? This is often true for relay boards that have opto-isolators.

That's a strange idea. Don't you think maybe writing a 1 would result in the output pin being HIGH?

Let's see if we can simplify this a little

const int latchPin = 5;  // Latch pin of 74HC595 is connected to Digital pin 5
const int clockPin = 6;  // Clock pin of 74HC595 is connected to Digital pin 6
const int dataPin = 4;   // Data pin of 74HC595 is connected to Digital pin 4

byte leds = 0;  // Variable to hold the pattern of which LEDs are currently turned on or off

void setup() {
  // Set all the pins that control 74HC595 as OUTPUT
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() > 0) {
    int number = Serial.parseInt();
    Serial.print("Relay ");
    Serial.print(number);
    Serial.print(" is now ");
    if (bitRead(leds, number) == 1) {
      Serial.println("off");
      bitWrite(leds, number, 0);
    }
    else {
      Serial.println("on");
      bitWrite(leds, number, 1);
    }
    updateShiftRegister();
  }
}

void updateShiftRegister() {
//updateShiftRegister() - This function sets the latchPin to low, then calls the Arduino function 'shiftOut' to shift out contents of variable 'leds' in the shift register before putting the 'latchPin' high again.
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, leds);
  digitalWrite(latchPin, HIGH);
}
1 Like

Please excuse my shortsightedness :sweat_smile:

Having known this existed would have saved me 2 very frustrating hours of ASCII conversion madness.

Other than that, the example you posted works almost perfectly!
The only thing strange is typing 1 switches relay 2, typing 2 switches 3 etc. And also every time any relay switches state, the relay on 1 switches as well. So with every action of another relay regardless of numer or state, relay 1 will switch states and report "Relay 0 is now off/on" as well.

In any case thank you so much for the help! It was not as complicated a I had feared!

Then you can change
Serial.print(number);
to
Serial.print(number + 1);

I tried it with int number = Serial.parseInt()+1;

However the only change is now typing 2 will result in "Relay 3 is now on/off" and relay 1 switching state.

It's "zero based". They're relays 0 - 7, the low order bit is bit 0 and the high order bit is bit 7. You can adopt and adapt or try and re-arrange, re-order and back compensate for the "1 - 8" paradigm.

Don't think of it as "relay 2". Think of it as "the relay connected to Q1" according to this pinout:

Ah... Try setting serial monitor to "no line ending".

Beautiful!

Thanks again Paul, now I can start integrating it into the bigger project.

The only hurdle left is (I think) the high-impedance. From what I've read here and here is to add a 10K pullup to the OE pin, and possibly to the outputs. But the relay module uses optocouplers so that shouldn't be the case.

My assumption is the fast glitching high/low the module does when uploading a sketch, and random state the relays end up in after a reset, are fixed with the pullup.

You don't need the '595 output pins to be high impedance, so you can just connect OE to ground (not to an Arduino pin, if that is what you are doing).

Yes, a 10K pull-up or pull-down on the latch pin should fix that.

Indeed it has :slight_smile:

So case closed.... or not quite. After connecting the latch (pin5) with a 10K to ground everything looks fine. Until I hit a number and things go upside down.
I could have sworn your example worked perfectly last night, @PaulRB.... Then again it was around 2 AM and I couldn't sleep.

At the start all relays are off, but when I hit a number all other relays go on (LOW) and the one correcsponding with the number stays off (HIGH)
After considering my relay module with optocouplers are off when HIGH I changed the code to reflect this. I also added a line to setup to write "shiftOut(dataPin, clockPin, LSBFIRST, 0b11111111);"

So now I'm looking into inverting the bit, planning to write 0b11111110 instead of 0b00000001.

it works with ~leds in shiftOut, is this acceptable?

complete code here:

const int latchPin = 5;  // Latch pin of 74HC595 is connected to Digital pin 5
const int clockPin = 6;  // Clock pin of 74HC595 is connected to Digital pin 6
const int dataPin = 4;   // Data pin of 74HC595 is connected to Digital pin 4

byte leds = 0;  // Variable to hold the pattern of which LEDs are currently turned on or off

void setup() {
  // Set all the pins that control 74HC595 as OUTPUT
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  Serial.begin(9600);

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, 0b11111111);
  digitalWrite(latchPin, HIGH);
}

void loop() {
  if (Serial.available() > 0) {
    int number = Serial.parseInt();
    Serial.print("Relay ");
    Serial.print(number);
    Serial.print(" is now ");
    if (bitRead(leds, number) == 0) {
      Serial.println("off");
      bitWrite(leds, number, 1);
    } else {
      Serial.println("on");
      bitWrite(leds, number, 0);
    }
    updateShiftRegister();
  }
}

void updateShiftRegister() {

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, ~leds);
  digitalWrite(latchPin, HIGH);
}

edit: I took the 3 lines in setup out again, no longer needed.

revised:

const int latchPin = 5;  // Latch pin of 74HC595 is connected to Digital pin 5
const int clockPin = 6;  // Clock pin of 74HC595 is connected to Digital pin 6
const int dataPin = 4;   // Data pin of 74HC595 is connected to Digital pin 4

byte leds = 0;  // Variable to hold the pattern of which LEDs are currently turned on or off

void setup() {
  // Set all the pins that control 74HC595 as OUTPUT
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() > 0) {
    int number = Serial.parseInt();
    Serial.print("Relay ");
    Serial.print(number);
    Serial.print(" is now ");
    if (bitRead(leds, number) == 0) {
      Serial.println("on");
      bitWrite(leds, number, 1);
    } else {
      Serial.println("off");
      bitWrite(leds, number, 0);
    }
    updateShiftRegister();
  }
}

void updateShiftRegister() {

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, ~leds);
  digitalWrite(latchPin, HIGH);
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.