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:
- In Home Assistant’s screen, I switch a relay on.
- The relay in Home Assistant switches itself off.
- Then nothing happens for a while.
- 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?