MQTT client to switch digital pins high

Hi guys,

I’m after a little help please? I’m in testing phase at the moment but basically I have an EtherTen with a Security Sensor shield and two PIRs connected (sketch for this publishes states to MQTT broker - this work fine).

I have another EtherTen with a couple of Relay 8 driver shields on (uses I2C). The following codes subscribes to the topics fine and I see this in my Serial monitor. What I can’t now figure out is how to use the value from the topic, typically “1”, to then execute the “SetLatchChannelOn” function using my if statements.

Anyone got any ideas please?

// 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, 2, 110);
IPAddress server(192, 168, 2, 149);

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();

    // Switch on the LED if an 1 was received as first character
  if (((char)payload[0] == '1') && ((topic) == "sensor1")) {
    setLatchChannelOn(1);   // Turn on Relay Driver Shield Channel 1
    Serial.println("Sensor 1 triggered");
  }
  if (((char)payload[0] == '1') && ((topic) == "sensor2")) {
    setLatchChannelOn(2);   // Turn on Relay Driver Shield Channel 2
    Serial.println("Sensor 2 triggered");
  }
  if (((char)payload[0] == '1') && ((topic) == "sensor3")) {
    setLatchChannelOn(3);   // Turn on Relay Driver Shield Channel 3
    Serial.println("Sensor 3 triggered");
  }
  if (((char)payload[0] == '1') && ((topic) == "sensor4")) {
    setLatchChannelOn(4);   // Turn on Relay Driver Shield Channel 4
    Serial.println("Sensor 4 triggered");
  }
}

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

void setup()
{
  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
  
  if (client.connect("arduinoClient")) {
    client.subscribe("sensor1");
    client.subscribe("sensor2");
    client.subscribe("sensor3");
    client.subscribe("sensor4");
  }
}

void loop()
{
  client.loop();
}

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 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;
}

Ok well I've had a little bit of progress by implementing the following if statement to replace my original ones:

if (strcmp(topic, "sensor1") == 0) {
    if ((char)payload[0] == '1')
    {
      setLatchChannelOn(1);
      delay(100);
      Serial.println("Sensor 1 triggered");
      client.publish("sensor1", "On");
    }
    else if ((char)payload[0] == '0')
    {
      setLatchChannelOff(1);
      client.publish("sensor1", "Off");
    }
  }

This is now working however it is really really slow. By slow I mean it takes ages for it to read from the MQTT broker (up to 10 seconds at times), then seems to just lock up.

Had to remove the client.publish("sensor1", "On");lines because they were causing havoc and I realised I didn't need them anyway.

Still not that stable because it's really slow to subscribe so still have some work ahead of me.

Still not that stable because it's really slow to subscribe so still have some work ahead of me.

You should post your revised code, in a single code block. Don't expect us to sew them together.

You should probably separate the topic handling from the payload handling, with the payload handling happen in the block that deals with a specific topic.

if(strcmp(topic) == something)
{
   dealWithPayloadForSomething();
}
else if(strcmp(topic) == "sunny weather forecast for today")
{
   dealWithPayloadForSUnnyWeather();
}

This way, you avoid repeated comparisons of the topic.

Looks like I've narrowed it down a little. It seems that if I disconnect the EtherTen running the security sensor shield, then any switching done through Home Assistant (via MQTT broker) occurs straight away. Once I connect the EtherTen back up to my network, I get delays again.