A place users can post their projects. If you have a small project and would like your own dedicated place to post and have others chat about it then this is your spot.
User avatar
By bobbycomelately
#70743 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.
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
  }
}