Trying to control I2C Relays

Hi guys,

I’ve read one of the posts on the I2C 4 relay control, but doesn’t give me the answers I need.
I have a Photon with a “ControlEverything.com” I2C 2-Relay expander.
I’m really-really new to the whole Particle thing and I’m enjoying it thoroughly, but as any noob, I need help.
The exact items I’m using are:
Photon
https://store.ncd.io/product/particle-electron-i2c-shield-screw-terminals-2-amp-power-supply/
https://store.ncd.io/product/2-channel-signal-relay-1a-spdt-i2c-mini-module/

I’ve created a new project and I’ve imported the NCD2Relay libraries, but this is how far my knowledge stretches.
I can do basics like triggering the Photon relay with a push-button input, so the bare-basics I’m familiar with, but using the relays on this unit is a bit tricky.
Any help will do

You should be able to get all the help you need at https://community.ncd.io/

The Product Page for your relay board references this Code for Particle:
https://github.com/ControlEverythingCommunity/PCA9536_R11/blob/master/Particle/PCA9536_R11.ino

Try it out and Post any questions on NCD.io

@Rftop,
Thanks for the referral, nothing there that’s really worth using. I’ve found some code but I’m having some difficulty turning individual relays off. I can turn them on individually and all on at the same time, I can even turn them off at the same time, but I cannot turn specific ones off.
Its frustrating.

Could you post here the code you are using to turn on and off the relays? We might be able to help you better that way? [update] I had a look at the specification sheet for the PCA9536 and the code on github. To turn on port 0 on the I2C GPIO IC you need to send command (0x01) then the data to write to the output register (0x01) for bit 0 value = 1. Port 1 ON would be 0x02. To turn both port 0 and port 1 ON would be 0x03. To turn OFF port 1 but leave port 0 ON would require a write of 0x01. Generally, the way this is done is that you read the output register value and then set (1) or reset (0) the bit associated with the port and write the changed value back to the output register. Maybe this was why you were turning off both ports?

1 Like

So in short:
Turning on relay 1 - Works
Turning on relay 2 - Works
Turning on all relays - Works
Turning off relay 1 - Turns off both
Turning off relay 2 - Turns off both
Turn off both relays - Works

#include <NCD2Relay.h>
#include <application.h>
#include <spark_wiring_i2c.h>
#define Addr 0x41

NCD2Relay relayController;

void setup()
{
    initRelays();
    relaysToLow();
    Particle.function("relayOne", relayOne);
    Particle.function("relayTwo", relayTwo);
    Particle.function("allRelays", allRelays);
}

int relayOne(String relay1State)
{
    Wire.beginTransmission(Addr);
    if (relay1State == "On" || relay1State == "on" || relay1State == "ON" || relay1State == "oN" || relay1State == "1")
    {
        Wire.write(0x01);
        Wire.write(0x01);
        Wire.endTransmission();
        Serial.println ("Relay-1 state is : HIGH");
    }
    else if (relay1State == "Off" || relay1State == "off" || relay1State == "OFF" || relay1State == "0")
    {
        Wire.write(0x01);
        Wire.write(0x00);
        Wire.endTransmission();
        Serial.println ("Relay-1 state is : LOW");
    }
    return 1;
}

int relayTwo(String relay2State)
{
    Wire.beginTransmission(Addr);
    if (relay2State == "On" || relay2State == "on" || relay2State == "ON" || relay2State == "oN" || relay2State == "1")
    {
        Wire.write(0x01);
        Wire.write(0x02);
        Wire.endTransmission();
        Serial.println ("Relay-2 state is : HIGH");
    }
    else if (relay2State == "Off" || relay2State == "off" || relay2State == "OFF" || relay2State == "0")
    {
        Wire.write(0x01);
        Wire.write(0x00);
        Wire.endTransmission();
        Serial.println ("Relay-2 state is : LOW");
    }
    return 1;
}

int allRelays(String command)
{
    if (command == "On" || command == "on" || command == "ON" || command == "oN" || command == "1")
    {
        relaysToHigh();
    }
    else if (command == "Off" || command == "off" || command == "OFF" || command == "0")
    {
        relaysToLow();
    }
    return 1;
}

void initRelays()
{
    Serial.begin(9600);
    Wire.begin();
    Wire.beginTransmission(Addr);
    Wire.write(0x03);
    Wire.write(0x00);
    Wire.endTransmission();\
    delay(300);
}

void relaysToLow()
{   
    Wire.beginTransmission(Addr);
    Wire.write(0x01);
    Wire.write(0x00);
    Wire.endTransmission();
    Serial.println ("All Relay states are : LOW");
}

void relaysToHigh()
{
    Wire.beginTransmission(Addr);
    Wire.write(0x01);
    Wire.write(0x03);
    Wire.endTransmission();
    Serial.println ("All Relay states are : HIGH");
}

void loop()
{

}

A couple of quick observations.

  1. Serial.begin(9600); does not make sense in initRelays() better in setup(). Ditto Wire.begin();
  2. A spurious \ after the Wire.endTransmission();
  3. You are turning both ports and relays off in each “OFF” command with Wire.write(0x00);

The solution to issue 3 is to read the port state set the port required off and then write the value back.

Wire.beginTransmission(Addr);
Wire.write(0x01);
byte val = Wire.read(); //get the output register value
val = val & 0x02; //bitwise AND with 00000010 clears bit 0 only
OR
val = val | 0x01; //bitwise OR with 00000001 sets bit 0 only
Wire.write(val); //write back the changed output register value
Wire.endTransmission();
1 Like

Thanks for the pointers, but honestly I’m more confused now than ever.
I’ve rectified points 1 & 2 in your list however your code is making funny things.
Either way I use the & or the |, it still doesn’t do what I want it to do.
I now have a flip-flop issue. Let’s say both relays were off, and I click relay one 1, it goes on, then when I say relay 2 go on, relay 1 goes off and relay 2 goes on, then it’s a never ending flip-flop from there.
Both code methods give me some form of similar result.

Possibly (without seeing your code) you need the following for each individual relay ON and OFF command.

for relay 1 ON only

Wire.write(0x01);
byte val = Wire.read(); //get the output register value
val = val | 0x01; //bitwise OR with 00000001 sets bit 0 only
Wire.write(val); //write back the changed output register value
Wire.endTransmission();

for relay 1 OFF only

Wire.write(0x01);
byte val = Wire.read(); //get the output register value
val = val & 0x02; //bitwise AND with 00000010 clears bit 0 only
Wire.write(val); //write back the changed output register value
Wire.endTransmission();

for relay 2 ON only

Wire.write(0x01);
byte val = Wire.read(); //get the output register value
val = val | 0x02; //bitwise OR with 00000002 sets bit 1 only
Wire.write(val); //write back the changed output register value
Wire.endTransmission();

for relay 2 OFF only

Wire.write(0x01);
byte val = Wire.read(); //get the output register value
val = val & 0x01; //bitwise AND with 00000001 clears bit 1 only
Wire.write(val); //write back the changed output register value
Wire.endTransmission();

Okay, so implementing your code which is what I had:

Your relay 1 ON only - Switches on both relays
Your relay 1 OFF only - Switches on relay 2
Your relay 2 ON only - Switches on both relays
Your relay 2 OFF only - Switches on relay 1

This is proving to be more effort than necessary. I’m almost at the point of buying a separate relay circuit to trigger with the on-board outputs.

I recently started using these relay boards and I love them. They have many different options for relay ratings.

With the Library, the commands are:
relayController.turnOffRelay(1);
relayController.turnOffRelay(2);
There are Several other neat features shown Here.

Hey guys, just an update.
I managed to get it working with very similar code as @armor
@Rftop your suggestion does work, however I'm trying to understand the logic behind the Texas Instrument chip. Although your much shorter version works, the library of course still makes use of other logic in the back-end.
After finally getting come feedback from the NCD community, it all makes sense now, their explanation as follows:

The command to set the relay state controls all channels at once, if you break the byte into bits you can see what is happening by viewing the two lowest bits:

(both relays off) 0x00 - 00000000
(relay 1 on, 2 off) 0x01 - 00000001
(relay 2 on, 1 off) 0x02 - 00000010
(both relays on) 0x03 - 00000011

You cannot set the state of one relay at a time, so generally you would read the current status first and then adjust the value accordingly before setting it again.

The 0x02 register is the polarity inversion register, that is to say that it acts as a mask to reverse the bit control (or readout) of particular channels. Typically a set bit will turn the relay on, so when you write 0x01 the last two bits are 01, based on the normal polarity settings this means relay 1 is on, writing a 2 to the polarity register says you’d like to invert the second lowest bit, so now that bit will turn on the associated relay when it is cleared rather than set. If you wrote 0x03 to the polarity register, then sending 0x00 to register 0x01 would actually turn on both relays.

My rectified code below for those who are interested:

#include <application.h>
#include <spark_wiring_i2c.h>
#define Addr 0x41


byte relayState{0x01};
void setup()
{
    initRelays();
    relaysToLow();
    Particle.function("relayOne", relayOne);
    Particle.function("relayTwo", relayTwo);
    Particle.function("allRelays", allRelays);
}

int relayOne(String relay1State)
{
    
    Wire.beginTransmission(Addr);
    if (relay1State == "On" || relay1State == "on" || relay1State == "ON" || relay1State == "oN" || relay1State == "1")
    {
        relayState = relayState | 1;
        Wire.write(0x01);
        Wire.write(relayState);
        Wire.endTransmission();
        Serial.println ("Relay-1 state is : HIGH");
        
    }
    else if (relay1State == "Off" || relay1State == "off" || relay1State == "OFF" || relay1State == "0")
    {
        relayState = relayState & ~1;
        Wire.write(0x01);
        Wire.write(relayState);
        Wire.endTransmission();
        Serial.println ("Relay-1 state is : LOW");
    }
    return 1;
}

int relayTwo(String relay2State)
{
    //byte test{0x02};
    Wire.beginTransmission(Addr);
    if (relay2State == "On" || relay2State == "on" || relay2State == "ON" || relay2State == "oN" || relay2State == "1")
    {
        relayState = relayState | 2;
        Wire.write(0x01);
        Wire.write(relayState);
       Wire.endTransmission();
        Serial.println ("Relay-2 state is : HIGH");
    }
    else if (relay2State == "Off" || relay2State == "off" || relay2State == "OFF" || relay2State == "0")
    {
        relayState = relayState & ~2;
        Wire.write(0x01);
        Wire.write(relayState);
        Wire.endTransmission();
        Serial.println ("Relay-2 state is : LOW");
       
    }
    return 1;
}

int allRelays(String command)
{
    if (command == "On" || command == "on" || command == "ON" || command == "oN" || command == "1")
    {
        relaysToHigh();
    }
    else if (command == "Off" || command == "off" || command == "OFF" || command == "0")
    {
        relaysToLow();
    }
    return 1;
}

void initRelays()
{
    Serial.begin(9600);
    Wire.begin();
    Wire.beginTransmission(Addr);
    Wire.write(0x03);
    Wire.write(0x00);
    Wire.endTransmission();
    delay(300);
}

void relaysToLow()
{   
    Wire.beginTransmission(Addr);
    Wire.write(0x01);
    Wire.write(0x00);
    Wire.endTransmission();
    Serial.println ("All Relay states are : LOW");
    relayState = 0x00;

}

void relaysToHigh()
{
    Wire.beginTransmission(Addr);
    Wire.write(0x01);
    Wire.write(0x03);
    Wire.endTransmission();
    Serial.println ("All Relay states are : HIGH");
    relayState = 0x03;
}


void loop()
{

}

Thanks for all the interest and help, much appreciated!

1 Like