MQTT Keeps crashing

Hi,
I have an arduino which has an ethernet shield and a freetronics relay8 shield on it for part of my home automation system.

I am using openhab to publish over MQTT to this arduino node to turn various things in my house on and off.

I have a strange issue with this setup in that occasionally the arduino completely stops responding to MQTT messages and then won't respond again until it is rebooted. This started off by happening very infrequently (it had run for months before it first happened, then a few weeks, now it happens every few days). I assume the arduino itself is still running as I am still able to ping it.

I am using pubsubclient for MQTT and have tried upgrading to the latest version, which actually coincided with it happening much more frequently (almost daily).

Below is the code, any suggestions would be greatly appreciated.

James

#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, 185, 21 };

static uint8_t serverIP[] = { 192, 168, 185, 12 };

char* outputTopic="/gfc/power/1/state%2d";
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, callback, 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 /gfc/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")) {
    Serial.println("Connected");
    client.subscribe("/gfc/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(r);  // Register to read
  Wire.endTransmission();

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

I'm not sure whether "ping" will really indicate that the Arduino is still running, this command may be handled entirely by the Ethernet shield. I'd add some different life indicator, like blinking a LED, to be sure that the Arduino is running. Better were a status command that returns some data, to verify that the Ardunio can react on Ethernet input.

The degradation over time could indicate some faulty component (capacitor...), that should be replaced. Control the supply voltages, best with a scope or transient recorder to catch glitches. Or it might be some electrical interference from outside...

Hi,
Thank you for the pointers, I will add a blinking led indicator as suggested. I guess I just assumed that the ethernet shield relied on the arduino itself functioning to operate.