MQTT sketch for HomeAssistant - callback failing
Posted: Mon Oct 09, 2017 11:57 am
Seeking help in desperation. All suggestions greatly appreciated.
I've hacked together a sketch based on other people's excellent work to allow me to monitor sensors and control relays and an IR blaster on a variety of nodes around my home, connected via MQTT to HomeAssistant (home-assistant.io).
Everything works...for a while. But then with no obvious reason, callbacks for the actuators stop working. I still get the 'heartbeat' (part of debugging attempts) and readings from the sensors, but I can no longer control the relays or IR Blaster. No error messages (as you can see, I've tried sticking in messages for debugging all over the place).
FYI it still fails with debugging and the serial connection turned off.
Full code below. Thanks in advance. Sorry if my horrible code offends your eyes.
I've hacked together a sketch based on other people's excellent work to allow me to monitor sensors and control relays and an IR blaster on a variety of nodes around my home, connected via MQTT to HomeAssistant (home-assistant.io).
Everything works...for a while. But then with no obvious reason, callbacks for the actuators stop working. I still get the 'heartbeat' (part of debugging attempts) and readings from the sensors, but I can no longer control the relays or IR Blaster. No error messages (as you can see, I've tried sticking in messages for debugging all over the place).
FYI it still fails with debugging and the serial connection turned off.
Full code below. Thanks in advance. Sorry if my horrible code offends your eyes.
Code: Select all
// Utilises:
// - PubSubClient by Nick ‘O Leary
// - DHT sensor library by Adafruit
// Name this node - sets mqtt addresses
#define NODE_NAME "livingroom"
// Activate components - comment out to deactivate
//#define PIR_ACTIVE
//#define DHT_ACTIVE
//#define RELAY_ACTIVE
#define IRBLASTER_ACTIVE
#define HEARTBEAT
//#define SWITCH
// Debug mode ?
#define DEBUG
// Core library definitions and settings
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#define wifi_ssid "xxxxxxxxxx"
#define wifi_password "xxxxxxxxxx"
#define mqtt_server "192.168.xx.xx"
#define mqtt_user "xxxxxxxxxx"
#define mqtt_password "xxxxxxxxxx"
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
float diff = 1.0;
// Temp and humidity sensor
#ifdef DHT_ACTIVE
#include <DHT.h>
#define humidity_topic NODE_NAME "/sensor/humidity"
#define temperature_topic NODE_NAME "/sensor/temperature"
#define DHTTYPE DHT22
#define DHTPIN 14 // D5
float temp = 0.0;
float hum = 0.0;
DHT dht(DHTPIN, DHTTYPE, 11); // 11 works fine for ESP8266
#endif
// PIR
#ifdef PIR_ACTIVE
#define pir_topic "livingroom/sensor/pir"
#define PIRPIN D6
#define LEDPIN 13
int pirState = LOW; // we start, assuming no motion detected
int pir = 0; // variable for reading the pin status
#endif
// IR Blaster
#ifdef IRBLASTER_ACTIVE
#include <IRremoteESP8266.h>
#include <IRsend.h>
#define ir_message NODE_NAME "/control/irb"
#define ir_state NODE_NAME "/control/irb/state"
int khz = 38; // 38kHz carrier frequency for both NEC and Samsung
IRsend irsend(2); //an IR led is connected to GPI0 2 (pin D4 on NodeMCU)
#endif
// 4-way Relay
#ifdef RELAY_ACTIVE
#define relay_zero NODE_NAME "/control/relay/0"
#define zero_state NODE_NAME "/control/relay/0/state"
#define relay_one NODE_NAME "/control/relay/1"
#define one_state NODE_NAME "/control/relay/1/state"
#define relay_two NODE_NAME "/control/relay/2"
#define two_state NODE_NAME "/control/relay/2/state"
#define relay_three NODE_NAME "/control/relay/3"
#define three_state NODE_NAME "/control/relay/3/state"
int r_zero_state = 0;
int r_one_state = 0;
int r_two_state = 0;
int r_three_state = 0;
int r_zero_test = 0;
int r_one_test = 0;
int r_two_test = 0;
int r_three_test = 0;
#endif
// Heartbeat
#ifdef HEARTBEAT
#define heartbeat NODE_NAME "/heartbeat"
#endif
// Switch
#ifdef SWITCH
#define switchchan NODE_NAME "/sensor/switch"
int switchstate = 0;
int switchread = 0;
#endif
void setup() {
Serial.begin(115200);
#ifdef DHT_ACTIVE
dht.begin(); // start the temp and humidity sensor
#endif
#ifdef IRBLASTER_ACTIVE
irsend.begin(); // start the irblaster
#endif
#ifdef PIR_ACTIVE
pinMode(PIRPIN, INPUT); // set the PIR Pin as an inout
#endif
// prepare GPIO for relay
#ifdef RELAY_ACTIVE
pinMode(D0, OUTPUT); // GPIO 16
pinMode(D1, OUTPUT); // GPIO 5
pinMode(D2, OUTPUT); // GPIO 4
pinMode(D3, OUTPUT); // GPIO 0
digitalWrite(D0, 1);
digitalWrite(D1, 1);
digitalWrite(D2, 1);
digitalWrite(D3, 1);
#endif
// Switch
#ifdef SWITCH
pinMode(D7, INPUT);
#endif
// get the core stuff started
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
#ifdef RELAY_ACTIVE
// Share current relay status
client.publish(zero_state, "1", true);
client.publish(one_state, "1", true);
client.publish(two_state, "1", true);
client.publish(three_state, "1", true);
#endif
}
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
#ifdef DEBUG
Serial.println();
Serial.print("Connecting to ");
Serial.println(wifi_ssid);
#endif
WiFi.mode(WIFI_STA);
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
#ifdef DEBUG
Serial.print(".");
#endif
}
#ifdef DEBUG
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
#endif
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
#ifdef DEBUG
Serial.print("Attempting MQTT connection...");
#endif
// Attempt to connect
if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) {
#ifdef RELAY_ACTIVE
client.subscribe(relay_zero);
client.subscribe(relay_one);
client.subscribe(relay_two);
client.subscribe(relay_three);
#endif
#ifdef IRBLASTER_ACTIVE
client.subscribe(ir_message);
#endif
} else {
#ifdef DEBUG
Serial.println("");
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
#endif
delay(5000);
}
}
}
#ifdef DHT_ACTIVE
bool checkBound(float newValue, float prevValue, float maxDiff) {
return !isnan(newValue) &&
(newValue < prevValue - maxDiff || newValue > prevValue + maxDiff);
}
#endif
#ifdef IRBLASTER_ACTIVE
void blastir(byte* payload) {
String statmessage = "unset";
int val = payload[0];
int instr = payload[0] - '0';
switch (instr) {
case 0:
irsend.sendNEC(0xFF02FD,32);
#ifdef DEBUG
statmessage = "Lights On/Off Command sent";
#endif
delay(1);
break;
case 1:
irsend.sendNEC(0xFF1AE5,32);
#ifdef DEBUG
statmessage = "Red light command sent";
#endif
delay(1);
break;
case 2:
irsend.sendNEC(0xFF9A65,32);
#ifdef DEBUG
statmessage = "Green lights command sent";
#endif
delay(1);
break;
case 3:
irsend.sendNEC(0xFF22DD,32);
#ifdef DEBUG
statmessage = "White lights command sent";
#endif
delay(1);
break;
case 4:
irsend.sendNEC(0xFF30CF,32);
#ifdef DEBUG
statmessage = "DIY 1 command sent";
#endif
delay(1);
break;
default:
#ifdef DEBUG
statmessage = "Could not match string";
#endif
break;
}
#ifdef DEBUG
client.publish(ir_state, String(statmessage).c_str(), true);
Serial.println(statmessage);
#endif
delay(10);
}
#endif
#ifdef RELAY_ACTIVE
void relayswitch(byte* payload, String topicstring) {
#ifdef DEBUG
Serial.println("Relayswitch function called");
#endif
int val = int(payload[0]);
char port;
int portloc = topicstring.length() - 1;
#ifdef DEBUG
Serial.print("Port location: ");
Serial.println(portloc);
#endif
port = topicstring.charAt(portloc);
int iport = port - '0';
int ival = val - '0';
#ifdef DEBUG
Serial.print("Port: ");
Serial.println(port);
Serial.print("iPort: ");
Serial.println(iport);
#endif
switch(iport) {
case 0:
digitalWrite(D0, ival);
r_zero_state = ival;
#ifdef DEBUG
Serial.print("Switching Relay One to:");
Serial.println(ival);
#endif
client.publish(zero_state, String(ival).c_str(), true);
delay(1);
break;
case 1:
digitalWrite(D1, ival);
r_one_state = ival;
#ifdef DEBUG
Serial.print("Switching Relay Two to:");
Serial.println(ival);
#endif
client.publish(one_state, String(ival).c_str(), true);
delay(1);
break;
case 2 :
digitalWrite(D2, ival);
r_two_state = ival;
#ifdef DEBUG
Serial.print("Switching Relay Three to:");
Serial.println(ival);
#endif
client.publish(two_state, String(ival).c_str(), true);
delay(1);
break;
case 3 :
digitalWrite(D3, ival);
r_three_state = ival;
#ifdef DEBUG
Serial.print("Switching Relay Four to:");
Serial.println(ival);
#endif
client.publish(three_state, String(ival).c_str(), true);
delay(1);
break;
default:
Serial.println("Could not match string");
break;
}
}
#endif
void callback(char* topic, byte* payload, unsigned int length) {
#ifdef DEBUG
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
#endif
String topicstring(topic);
int stripstart = String(NODE_NAME).length()+1;
int stripstop = String(NODE_NAME).length()+4;
#ifdef DEBUG
Serial.println(topicstring.substring(stripstart,stripstop));
#endif
if(topicstring.substring(stripstart,stripstop) == "con") {
int funcstart = String(NODE_NAME).length()+9;
int funcstop = String(NODE_NAME).length()+12;
String func = topicstring.substring(funcstart,funcstop);
#ifdef DEBUG
Serial.print("Func = ");
Serial.println(func);
#endif
if(func == "rel") {
#ifdef RELAY_ACTIVE
relayswitch(payload, topicstring);
#endif
delay(1);
}
if(func == "irb") {
#ifdef IRBLASTER_ACTIVE
blastir(payload);
#endif
delay(1);
}
}
delay(10);
}
#ifdef PIR_ACTIVE
void pir_state() {
pir = digitalRead(PIRPIN);
if (pir == HIGH) { // check if the input is HIGH
digitalWrite(LEDPIN, HIGH); // turn LED ON
if (pirState == LOW) {
// we have just turned on
#ifdef DEBUG
Serial.println("Motion detected!");
#endif
client.publish(pir_topic, "1", true);
// We only want to print on the output change, not state
pirState = HIGH;
}
} else {
digitalWrite(LEDPIN, LOW); // turn LED OFF
if (pirState == HIGH){
// we have just turned off
#ifdef DEBUG
Serial.println("Motion ended!");
#endif
client.publish(pir_topic, "0", true);
// We only want to print on the output change, not state
pirState = LOW;
}
}
delay(10);
}
#endif
#ifdef DHT_ACTIVE
void dhtread() {
float newTemp = dht.readTemperature();
float newHum = dht.readHumidity();
if (checkBound(newTemp, temp, diff)) {
temp = newTemp;
#ifdef DEBUG
Serial.print("New temperature:");
Serial.println(String(temp).c_str());
#endif
client.publish(temperature_topic, String(temp).c_str(), false);
}
if (checkBound(newHum, hum, diff)) {
hum = newHum;
#ifdef DEBUG
Serial.print("New humidity:");
Serial.println(String(hum).c_str());
#endif
client.publish(humidity_topic, String(hum).c_str(), false);
}
delay(10);
}
#endif
#ifdef SWITCH
void switchfunc(){
switchread = digitalRead(D7);
if(switchread != switchstate) {
client.publish(switchchan, String(switchstate).c_str(), true);
#ifdef DEBUG
Serial.print("Switch flipped to ");
Serial.println(switchstate);
#endif
switchstate = switchread;
delay(10);
}
}
#endif
void loop() {
if (!client.connected()) {
reconnect();
}
delay(1000);
client.loop();
// Serial.println("In loop");
long now = millis();
if (now - lastMsg > 10000) {
lastMsg = now;
#ifdef HEARTBEAT
client.publish(heartbeat, "1", false);
#endif
#ifdef DEBUG
Serial.println("Reading...");
#endif
#ifdef PIR_ACTIVE
pir_state();
#endif
#ifdef DHT_ACTIVE
dhtread();
#endif
#ifdef SWITCH
switchfunc();
#endif
}
}