Help with Arduino Mega With Relay8 Boards

Hi Guys, im having trouble with getting my Arduino to actually communicate over the i2c bus to toggle relay outputs.

This is the code i currently have,

Relay Controller MQTT Code

#include <SPI.h>
#include “Ethernet.h”
#include “Wire.h”
#include <PubSubClient.h>
#include <stdlib.h>

#define SHIELD_1_I2C_ADDRESS 0x20 // 0x20 is the address with all jumpers removed
#define SHIELD_2_I2C_ADDRESS 0x21 // 0x21 is the address with a jumper on position A0

#define MAC_I2C_ADDRESS 0x50 // Microchip 24AA125E48 I2C ROM address

/* If channelInterlocks is set to true, the channels are grouped into

  • pairs starting at 1 (ie: channels 1 & 2, 3 & 4, etc) and only one
  • channel in each pair can be on at any time. For example, if channel
  • 1 is on and 2 is set to on, channel 1 will be turned off first and
  • vice versa. This is to allow control of dual-active devices such as
  • electric curtain motors which must only be driven in one direction
  • at a time. */
    const byte channelInterlocks = false;

/* CHANGE THIS TO YOUR OWN UNIQUE VALUE. The MAC number should be

  • different from any other devices on your network or you’ll have
  • problems receiving packets. Can be replaced automatically below
  • using a MAC address ROM. */
    static uint8_t mac[] = { 0xDE, 0xA7, 0x0E, 0xEF, 0xFE, 0xED };

/* CHANGE THIS TO MATCH YOUR HOST NETWORK. Most home networks are in

  • the 192.168.0.XXX or 192.168.1.XXX subrange. Pick an address
  • that’s not in use and isn’t going to be automatically allocated by
  • DHCP from your router. Can be replaced automatically using DHCP. */
    static uint8_t ip[] = { 192, 168, 1, 182 };

static uint8_t serverIP[] = { 192, 168, 1, 108 };

char* outputTopic="/output";
char topic[37];
char message[22];

byte shield1BankA = 0; // Current status of all outputs on first shield, one bit per output
byte shield2BankA = 0; // Current status of all outputs on second shield, one bit per output
EthernetClient ethClient;
PubSubClient client(serverIP, 1883, ethClient);

// New message receive
void callback(char* topic, byte* payload, unsigned int length) {
String strPayload= String((char*)payload).substring(0,length);

Serial.print(“Receive message”);
Serial.println(strPayload);

// topic will be something like /relay/power/1/01

char channel[]=" ";
channel[0]= *(topic+13);
channel[1]= *(topic+14);

unsigned short int relay = atoi(channel);

if (strPayload.equals(“on”))
{
setLatchChannelOn( relay );
publishState(atoi(channel),1,outputTopic);
Serial.print("Turning on ");
Serial.println(channel);
}

  if (strPayload.equals("off"))

  {
    setLatchChannelOff( relay );
    publishState(atoi(channel),0,outputTopic);
            Serial.print("Turning off ");
             Serial.println(channel);
  }

}

void setup()
{
Wire.begin(); // Wake up I2C bus
Serial.begin( 38400 );
Serial.println(“Strting MQTT Relay 8 board”);

Serial.print("Getting MAC address from ROM: ");
// mac[0] = readRegister(0xFA);
// mac[1] = readRegister(0xFB);
// mac[2] = readRegister(0xFC);
// mac[3] = readRegister(0xFD);
// mac[4] = readRegister(0xFE);
// mac[5] = readRegister(0xFF);
char tmpBuf[17];
sprintf(tmpBuf, “%02X:%02X:%02X:%02X:%02X:%02X”, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Serial.println(tmpBuf);

// setup the Ethernet library to talk to the Wiznet board
Ethernet.begin(mac, ip); // Use static address defined above
//Ethernet.begin(mac); // Use DHCP

// Print IP address:
Serial.print(“My IP: “);
for (byte thisByte = 0; thisByte < 4; thisByte++) {
// print the value of each byte of the IP address:
Serial.print(Ethernet.localIP()[thisByte], DEC);
if( thisByte < 3 )
{
Serial.print(”.”);
}
}

/* Set up the Relay8 shields */
initialiseShield(SHIELD_1_I2C_ADDRESS);
sendRawValueToLatch1(0); // If we don’t do this, channel 6 turns on! I don’t know why

initialiseShield(SHIELD_2_I2C_ADDRESS);
sendRawValueToLatch2(0); // If we don’t do this, channel 6 turns on! I don’t know why

Serial.println(“Ready.”);
}

void connect() {
if (client.connect(“arduinoClient”, “openhabian”, “bedrock”)) {
Serial.println(“Connected”);
client.subscribe("/relay/power/1/#");
}
}

void loop()
{
if (!client.connected()) {
connect();
}

client.loop();
}

void publishState(int channel,int state,char* theTopic) {
sprintf(topic,theTopic,channel);
sprintf(message,"{channel:%1d,state:%1d}",channel,state);
client.publish(topic,message);
}

void initialiseShield(int shieldAddress)
{
// Set addressing style
Wire.beginTransmission(shieldAddress);
Wire.write(0x12);
Wire.write(0x20); // use table 1.4 addressing
Wire.endTransmission();

// Set I/O bank A to outputs
Wire.beginTransmission(shieldAddress);
Wire.write(0x00); // IODIRA register
Wire.write(0x00); // Set all of bank A to outputs
Wire.endTransmission();
}

/**
*/
void toggleLatchChannel(byte channelId)
{
if( channelId >= 1 && channelId <= 8 )
{
byte shieldOutput = channelId;
byte channelMask = 1 << (shieldOutput - 1);
shield1BankA = shield1BankA ^ channelMask;
sendRawValueToLatch1(shield1BankA);
}
else if( channelId >= 9 && channelId <= 16 )
{
byte shieldOutput = channelId - 8;
byte channelMask = 1 << (shieldOutput - 1);
shield2BankA = shield2BankA ^ channelMask;
sendRawValueToLatch2(shield2BankA);
}
}

/**
*/
void setLatchChannelOn (byte channelId)
{
if( channelInterlocks == true )
{
if ( (channelId % 2) == 0) // This is an even number channel, so turn off the channel before it
{
setLatchChannelOff( channelId - 1 );
} else { // This is an odd number channel, so turn off the channel after it
setLatchChannelOff( channelId + 1 );
}
}

if( channelId >= 1 && channelId <= 8 )
{
byte shieldOutput = channelId;
byte channelMask = 1 << (shieldOutput - 1);
shield1BankA = shield1BankA | channelMask;
sendRawValueToLatch1(shield1BankA);
}
else if( channelId >= 9 && channelId <= 16 )
{
byte shieldOutput = channelId - 8;
byte channelMask = 1 << (shieldOutput - 1);
shield2BankA = shield2BankA | channelMask;
sendRawValueToLatch2(shield2BankA);
}
}

/**
*/
void setLatchChannelOff (byte channelId)
{
if( channelId >= 1 && channelId <= 8 )
{
byte shieldOutput = channelId;
byte channelMask = 255 - ( 1 << (shieldOutput - 1));
shield1BankA = shield1BankA & channelMask;
sendRawValueToLatch1(shield1BankA);
}
else if( channelId >= 9 && channelId <= 16 )
{
byte shieldOutput = channelId - 8;
byte channelMask = 255 - ( 1 << (shieldOutput - 1));
shield2BankA = shield2BankA & channelMask;
sendRawValueToLatch2(shield2BankA);
}
}

/**
*/
void sendRawValueToLatch1(byte rawValue)
{
Wire.beginTransmission(SHIELD_1_I2C_ADDRESS);
Wire.write(0x12); // Select GPIOA
Wire.write(rawValue); // Send value to bank A
shield1BankA = rawValue;
Wire.endTransmission();
}

/**
*/
void sendRawValueToLatch2(byte rawValue)
{
Wire.beginTransmission(SHIELD_2_I2C_ADDRESS);
Wire.write(0x12); // Select GPIOA
Wire.write(rawValue); // Send value to bank A
shield2BankA = rawValue;
Wire.endTransmission();
}

/**

  • Required to read the MAC address ROM
    */
    byte readRegister(byte r)
    {
    unsigned char v;
    Wire.beginTransmission(MAC_I2C_ADDRESS);
    Wire.write®; // Register to read
    Wire.endTransmission();

Wire.requestFrom(MAC_I2C_ADDRESS, 1); // Read a byte
while(!Wire.available())
{
// Wait
}
v = Wire.read();
return v;
}

not all of it is mine so im trying to piece this together. i should be able to publish into /relay/power/1/01 ‘on’ or ‘off’ and that should trigger the relay. am i missing something here?

@chris something you could possibly help me with?
Thanks again for your help

I’ll have a proper look this weekend, that’s a lot of code!

1 Like

Rewrote my code @chris

after spending the last 3hrs bashing my head against a wall its finally working

turns out
Wire.begin(); // Wake up the I2C Bus

is actually needed, i just forgot that part somehow.

Hopefully me bashing my head against a wall helps others who may need this

   // This subscribes to MQTT IOT get the PIR sensor states //

#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
#include <Wire.h>

#define SHIELD_1_I2C_ADDRESS  0x20  // 0x20 is the address with all jumpers removed
#define SHIELD_2_I2C_ADDRESS  0x21  // 0x21 is the address with a jumper on position A0
#define MAC_I2C_ADDRESS       0x50  // Microchip 24AA125E48 I2C ROM address

// Update these with values suitable for your network.
byte mac[]    = {  0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 67);
IPAddress server(192, 168, 1, 108);

byte shield1BankA = 0; // Current status of all outputs on first shield, one bit per output
byte shield2BankA = 0; // Current status of all outputs on second shield, one bit per output

void callback(char* topic, byte* payload, unsigned int length) {
  // handle message arrived
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();


  if (strcmp(topic, "relay1") == 0) {
    if ((char)payload[0] == '1')
    {
      setLatchChannelOn(1);
      delay(100);   // not actually needed, but i added this in here for how my lights work
      Serial.println("Relay 1 triggered ON");
      
    }
    else if ((char)payload[0] == '0')
    {
      setLatchChannelOff(1);
      Serial.println("Relay 1 triggered OFF");
    }
  }
}



EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);

void setup()
{
  Wire.begin(); // Wake up the I2C Bus
  Serial.begin(38400);
  Ethernet.begin(mac, ip);
  // Note - the default maximum packet size is 128 bytes. If the
  // combined length of clientId, username and password exceed this,
  // you will need to increase the value of MQTT_MAX_PACKET_SIZE in
  // PubSubClient.h

/* Set up the Relay8 shields */
  initialiseShield(SHIELD_1_I2C_ADDRESS);
  sendRawValueToLatch1(0);  // If we don't do this, channel 6 turns on! I don't know why
  
  initialiseShield(SHIELD_2_I2C_ADDRESS);
  sendRawValueToLatch2(0);  // If we don't do this, channel 6 turns on! I don't know why
 Serial.println("Ready.");
}

void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
  Serial.print("Attempting MQTT connection...");
  // Attempt to connect
  if (client.connect("arduinoClient", "openhabian", "openhabian")) {
    Serial.println("connected");
    client.subscribe("relay1");

  } else {
    Serial.print("failed, rc=");
    Serial.print(client.state());
    Serial.println(" try again in 5 seconds");
    // Wait 5 seconds before retrying
    delay(5000);
   }
 }
}
void loop()
{
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

void initialiseShield(int shieldAddress)
{
  // Set addressing style
  Wire.beginTransmission(shieldAddress);
  Wire.write(0x12);
  Wire.write(0x20); // use table 1.4 addressing
  Wire.endTransmission();

  // Set I/O bank A to outputs
  Wire.beginTransmission(shieldAddress);
  Wire.write(0x00); // IODIRA register
  Wire.write(0x00); // Set all of bank A to outputs
  Wire.endTransmission();
}

void setLatchChannelOn (byte channelId)
{
  if ( channelId >= 1 && channelId <= 8 )
  {
    byte shieldOutput = channelId;
    byte channelMask = 1 << (shieldOutput - 1);
    shield1BankA = shield1BankA | channelMask;
    sendRawValueToLatch1(shield1BankA);
  }
  else if ( channelId >= 9 && channelId <= 16 )
  {
    byte shieldOutput = channelId - 8;
    byte channelMask = 1 << (shieldOutput - 1);
    shield2BankA = shield2BankA | channelMask;
    sendRawValueToLatch2(shield2BankA);
  }
}

void setLatchChannelOff (byte channelId)
{
  if( channelId >= 1 && channelId <= 8 )
  {
    byte shieldOutput = channelId;
    byte channelMask = 255 - ( 1 << (shieldOutput - 1));
    shield1BankA = shield1BankA & channelMask;
    sendRawValueToLatch1(shield1BankA);
  }
  else if( channelId >= 9 && channelId <= 16 )
  {
    byte shieldOutput = channelId - 8;
    byte channelMask = 255 - ( 1 << (shieldOutput - 1));
    shield2BankA = shield2BankA & channelMask;
    sendRawValueToLatch2(shield2BankA);
  }
}

void sendRawValueToLatch1(byte rawValue)
{
  Wire.beginTransmission(SHIELD_1_I2C_ADDRESS);
  Wire.write(0x12);        // Select GPIOA
  Wire.write(rawValue);    // Send value to bank A
  shield1BankA = rawValue;
  Wire.endTransmission();
}

void sendRawValueToLatch2(byte rawValue)
{
  Wire.beginTransmission(SHIELD_2_I2C_ADDRESS);
  Wire.write(0x12);        // Select GPIOA
  Wire.write(rawValue);    // Send value to bank A
  shield2BankA = rawValue;
  Wire.endTransmission();
}

/** Required to read the MAC address ROM */
byte readRegister(byte r)
{
  unsigned char v;
  Wire.beginTransmission(MAC_I2C_ADDRESS);
  Wire.write(r);  // Register to read
  Wire.endTransmission();

  Wire.requestFrom(MAC_I2C_ADDRESS, 1); // Read a byte
  while (!Wire.available())
  {
    // Wait
  }
  v = Wire.read();
  return v;
}

At least you wont need to read all that code now :heart:

3 Likes

Hi Bedrock,

In your code - how do you send channelID to the arduino?
If I send it like this from my client:
mosquitto_pub -h 10.10.10.100 -t “relay1” -m “1” -u username -P password

In serial monitor I can see that message 1 is arrived, but relay isn’t triggered. Cannot fully understand how do you pass values to relay 2, 3, etc.

Thanks.

if its appearing in the serial monitor sounds like its not initializing the I2C bus
you do have Wire.begine(); at the start of void setup()?

as for the relays the way i have done mine is a but different this is all you need for the triggering of the relay

if (strcmp(topic, “relay1”) == 0) {
if ((char)payload[0] == ‘1’)
{
setLatchChannelOn(1);
delay(100); // not actually needed, but i added this in here for how my lights work
Serial.println(“Relay 1 triggered ON”);
}
else if ((char)payload[0] == ‘0’)
{
setLatchChannelOff(1);
Serial.println(“Relay 1 triggered OFF”);
}
}

so if you had 2 relays it would looks like this
if its appearing in the serial monitor sounds like its not initializing the I2C bus
you do have Wire.begin(); at the start of void setup()?

as for the relays the way i have done mine is a but different this is all you need for the triggering of the relay

if (strcmp(topic, “relay1”) == 0) {
if ((char)payload[0] == ‘1’)
{
setLatchChannelOn(1);
delay(100); // not actually needed, but i added this in here for how my lights work
Serial.println(“Relay 1 triggered ON”);
}
else if ((char)payload[0] == ‘0’)
{
setLatchChannelOff(1);
Serial.println(“Relay 1 triggered OFF”);
}
}

so if you had 2 relays it would looks like this

if (strcmp(topic, “relay1”) == 0) {
if ((char)payload[0] == ‘1’)
{
setLatchChannelOn(1);
delay(100); // not actually needed, but i added this in here for how my lights work
Serial.println(“Relay 1 triggered ON”);
}
else if ((char)payload[0] == ‘0’)
{
setLatchChannelOff(1);
Serial.println(“Relay 1 triggered OFF”);
}
}
if (strcmp(topic, “relay2”) == 0) {
if ((char)payload[0] == ‘1’)
{
setLatchChannelOn(2);
delay(100); // not actually needed, but i added this in here for how my lights work
Serial.println(“Relay 2 triggered ON”);
}
else if ((char)payload[0] == ‘0’)
{
setLatchChannelOff(2);
Serial.println(“Relay 2 triggered OFF”);
}
}

the in Void Reconnect()

add another line for Relay2
client.subscribe("relay2");

i recommend getting MQTT.fx

it will show you more on what is happening on your MQTT server.

hopefully that answers your question a bit more

1 Like