Problem with receiving MySensors messages for relays from Home Assistant

Hi.

I have 9 relays attached to an Arduino. I have MySensors set up on the Arduino to receive messages from Home Assistant to switch the relays on and off.

I wanted the relays to be switched on and off whenever I switched them on and off from Home Assistant.

I also wanted a heartbeat sent every minute, to keep the node alive.

For some reason, the messages from Home Assistant only get received when the heartbeat is sent.

What seems to happen, is the following:

  1. In Home Assistant’s screen, I switch a relay on.
  2. The relay in Home Assistant switches itself off.
  3. Then nothing happens for a while.
  4. Then suddenly the relay switches on, and in Home Assistant the relay switches itself on.

As you can imagine, this is far from ideal, and makes it difficult to tell if the network is down.

From what I can see from the Log viewer in Home Assistant, nothing comes through, and then suddenly HA receives messages for ALL of the 9 relays with their current states, followed immediately by the heartbeat.

This is the code that I’ve been running:

    // Main settings
    #define SERIAL_ON 1 // Switch for relay
    #define MSG_LOOP 1 // Switch for relay

    // Set blinking period (in milliseconds)
    #define MY_DEFAULT_LED_BLINK_PERIOD 100

    #define MY_DEFAULT_ERR_LED_PIN 4
    #define MY_DEFAULT_TX_LED_PIN 5
    #define MY_DEFAULT_RX_LED_PIN 6

    // Enable debug prints to serial monitor
    #ifdef SERIAL_ON
      #define MY_DEBUG
      #define MY_DEBUG_VERBOSE
    #endif

    // Define the node id for the ESP8266
    #define MY_NODE_ID 20
    //#define NODE_SENSOR_ID 1
    //#define MY_NODE_ID   (AUTO)
    //#define MY_PARENT_NODE_ID 0
    //#define MY_PARENT_NODE_ID   (AUTO)
    //#define MY_PARENT_NODE_IS_STATIC
    //#define MY_SIGNAL_REPORT_ENABLED
    //#define MY_TRANSPORT_SANITY_CHECK

    // Define CS and CE pins for RF24 according to the web page https://www.mysensors.org/build/connect_radio
    #define MY_RF24_CE_PIN 49
    #define MY_RF24_CS_PIN 53
    #define MY_RF24_IRQ_PIN 48

    // Enable and select radio type attached
    #define MY_RADIO_RF24
    //#define MY_RADIO_NRF5_ESB
    //#define MY_RADIO_RFM69
    //#define MY_RADIO_RFM95

    //#define MY_DEBUG_VERBOSE_RF24

    // Enable repeater functionality for this node
    //#define MY_REPEATER_FEATURE

    //#define MY_MQTT_CLIENT_ID "*************"

    #include <MySensors.h>
    //#include <SPI.h>

    #define SN "Arduino Mega"
    #define SV "2.0"

    // Invert on/off (high/low) for relay.
    #define RELAY_ON 0
    #define RELAY_OFF 1

    // Add two new defines for reporting state of relay to home assistant.
    #define RELAY_MSG_ON 1
    #define RELAY_MSG_OFF 0

    // Heartbeat info
    #define HEARTBEAT_INTERVAL_MILLIS 2000
    long lLastHeartbeatMillis = - HEARTBEAT_INTERVAL_MILLIS;

    // Base info
    #define NUMBER_OF_RELAYS 9
    int ChildIDs[NUMBER_OF_RELAYS] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int RelayPins[NUMBER_OF_RELAYS] = { 32, 34, 36, 38, 40, 42, 44, 46, 10 };

    // Variables for when relays last checked to see if initialised
    //#define RELAYS_CHECK_INITIALISED_INTERVAL_MILLIS 500
    //long lLastRelaysCheckInitialisedMillis = 0;

    // Info to hold sensor data
    #define RELAYS_RECEIVE_INITIALISED_TIMEOUT_MILLIS 120000
    typedef struct {
      int iSensorID;
      int iRelayPin;
      bool bCurrentState;

      MyMessage *msg;
      bool bHomeAssistantRequestedStateChange;
      bool bNewState;

      bool bRelayInitialisationMessageSent;
      long lLastRelayInitialisationMessageSentMillis;
      bool bRelayInitialisationMessageReceived;

    } Relay;
    Relay relays[NUMBER_OF_RELAYS];
    bool bAllRelaysInitialised = false;

    //////////////////////////////////////////////////////////
    // Main routines
    //////////////////////////////////////////////////////////

    // Set up relay variables
    void before() {
      #ifdef SERIAL_ON
        Serial.println("BEFORE function started.");
      #endif

      for(int iRelayID=0; iRelayID<NUMBER_OF_RELAYS; iRelayID++) {
        relays[iRelayID] = { ChildIDs[iRelayID], RelayPins[iRelayID],
          false, NULL, false, false, false, 0, false };
      }

      #ifdef SERIAL_ON
        Serial.println("BEFORE function ended.");
      #endif
    }

    // Send the sketch and present the relays
    void presentation() {
      #ifdef SERIAL_ON
        Serial.println("PRESENTATION function started.");
      #endif

      sendSketchInfo(SN, SV);

      for(int iRelayID=0; iRelayID<NUMBER_OF_RELAYS; iRelayID++) {
        int iSensorID = relays[iRelayID].iSensorID;
        present(iSensorID, S_BINARY);
      }

      #ifdef SERIAL_ON
        Serial.println("PRESENTATION function ended.");
      #endif
    }

    void setup()
    {
      #ifdef SERIAL_ON
        Serial.println("SETUP function started.");
      #endif

      for(int iRelayID=0; iRelayID<NUMBER_OF_RELAYS; iRelayID++) {
        // Initialise the MyMessage classes
        int iSensorID = relays[iRelayID].iSensorID;
        relays[iRelayID].msg = new MyMessage(iSensorID, V_STATUS);

        // Initialize the digital pin as an output.
        int iRelayPin = relays[iRelayID].iRelayPin;
        pinMode(iRelayPin, OUTPUT);  // set relay pins in output

        // Restore current states from EPROM
        // and set relay
        int bCurrentState = readEPROMState(iRelayID, iSensorID);
        writeRelayPin(iRelayID, bCurrentState);

        // Write to serial monitor
        // Added debug message for loadState function 
        #ifdef SERIAL_ON
          Serial.print("Restored state for relay ");
          Serial.print(iSensorID);
          Serial.print(", current state = ");
          Serial.println(bCurrentState);
        #endif

        // Send initialisation message to Home Assistant
        sendMessage(iRelayID, bCurrentState);

        // Write to serial monitor
        #ifdef SERIAL_ON
          Serial.print("Requesting initial value from controller for relay ");
          Serial.println(iSensorID);
        #endif

        // Request message from Home Assistant
        request(iSensorID, V_STATUS);
        // smartSleep(2000, C_SET, V_STATUS);

        // Save for timeout
        relays[iRelayID].bRelayInitialisationMessageSent = true;
        relays[iRelayID].lLastRelayInitialisationMessageSentMillis = millis();
      }

      // Send heartbeat
      // smartSleep(500);
      #ifdef SERIAL_ON
        Serial.println("HEARTBEAT started.");
      #endif
      //sendHeartbeat();
      // Write to serial monitor
      #ifdef SERIAL_ON
        Serial.println("HEARTBEAT finished.");
      #endif
      lLastHeartbeatMillis = millis();

      #ifdef SERIAL_ON
        Serial.println("SETUP function ended.");
      #endif
    }

    void loop()
    {
      #ifdef SERIAL_ON
        Serial.println("LOOP function started.");
      #endif

      // Send heartbeat every 60 seconds
      //wait(60000);
      long now = millis();
      while(millis() < now + 60000) {
        _process();
      }

      // Write to serial monitor
      #ifdef SERIAL_ON
        Serial.println("HEARTBEAT started.");
      #endif

      sendHeartbeat();

      // Write to serial monitor
      #ifdef SERIAL_ON
        Serial.println("HEARTBEAT finished.");
      #endif

      #ifdef SERIAL_ON
        Serial.println("LOOP function ended.");
      #endif
    }

    // Receive message from Home Assistant
    void receive(const MyMessage &message)
    {
      #ifdef SERIAL_ON
        Serial.println("RECEIVE function started.");
      #endif

      #ifdef SERIAL_ON
        Serial.println("message type=" + (String)(message.type)
        + " sensor=" + (String)(message.sensor)
        + " state=" + (message.getBool() ? "true" : "false"));
      #endif

      // Only for status change messages
      if(message.type != V_STATUS) return;

      // Read message info
      int iSensorID = message.sensor;
      bool bNewState = message.getBool();

      // Get current state
      int iRelayID = iSensorID - 1; // Array index
      bool bCurrentState = relays[iRelayID].bCurrentState;

      // Write to serial monitor
      #ifdef SERIAL_ON
        Serial.print("Received request from Home Assistant to change sensor ID ");
        Serial.print(iSensorID);
        Serial.print(" from ");
        Serial.println(bCurrentState ? "on" : "off");
        Serial.print(" to ");
        Serial.println(bNewState ? "on" : "off");
      #endif

      // Request received from Home Assistant.
      // Initialisation sent, but not received.
      // So initialise.
      if(!relays[iRelayID].bRelayInitialisationMessageReceived) {
        relays[iRelayID].bRelayInitialisationMessageReceived = true;

        // Write to serial monitor
        #ifdef SERIAL_ON
          Serial.print("Relay with sensor ID ");
          Serial.print(iSensorID);
          Serial.println(" initialised.");
        #endif
      }

      // State not changed
      if(bNewState == bCurrentState) {
        #ifdef SERIAL_ON
          Serial.print("Hass sent unchanged state: ");
          Serial.print(bNewState);
          Serial.print(" for Relay ID: ");
          Serial.println(iSensorID);
        #endif

      // State changed.
      // So update relay, EPROM and message Home Assistant that state updated.
      } else {
        // Change digital state
        writeRelayPin(iRelayID, bNewState);

        // Send message to Home Assistant
        sendMessage(iRelayID, bNewState);

        // Store state in eeprom and RAM
        writeEPROMState(iRelayID, iSensorID, bNewState);

        #ifdef SERIAL_ON
          Serial.print("Changed state for relay ID = ");
          Serial.print(iSensorID);
          Serial.print(" from state = ");
          Serial.print(bCurrentState);
          Serial.print(" to state = ");
          Serial.println(bNewState);
        #endif
      }

      #ifdef SERIAL_ON
        Serial.println("RECEIVE function ended.");
      #endif
    }

    //////////////////////////////////////////////////////////
    // Utilities
    //////////////////////////////////////////////////////////

    // Write to digital pin for relay
    static inline void writeRelayPin(int iRelayID, bool bNewState) {
      // Power relay - Sensor ID 9
      if(iRelayID == 8) {
        int iRelayPin = relays[iRelayID].iRelayPin;
        int iDigitalState = bNewState?HIGH:LOW;
        digitalWrite(iRelayPin, iDigitalState);
        
      // Regular relays
      } else {
        int iRelayPin = relays[iRelayID].iRelayPin;
        int iDigitalState = bNewState?RELAY_ON:RELAY_OFF;
        digitalWrite(iRelayPin, iDigitalState);
      }
    }

    // Send message to Home Assistant
    static inline void sendMessage(int iRelayID, bool bNewState) {
      int iMessageState = bNewState?RELAY_MSG_ON:RELAY_MSG_OFF;
      send(relays[iRelayID].msg->set(iMessageState), false);
    }

    // Read from EPROM memory
    static inline int readEPROMState(int iRelayID, int iSensorID) {
      int bCurrentState = loadState(iSensorID); // State restored
      relays[iRelayID].bCurrentState = bCurrentState; // Save to RAM
      return bCurrentState;
    }

    // Write to EPROM memory
    static inline void writeEPROMState(int iRelayID, int iSensorID, bool bNewState) {
      saveState(iSensorID , bNewState);
      relays[iRelayID].bCurrentState = bNewState;
    }

Suggestions?

I have the same problem that i think started in 0.92.0.
I just noticed it when I was recreating my Lovelace dashboard.
What version are you using? I also have the same problem in 0.93.2
I opened an issue on githtub.

This is expected and documented behavior:

Let me know if you think there’s something not working as described in docs.