Trouble with Sonoff and MQTT - state not changing

I’ve been trying for weeks to get the Sonoff switch to change states.

Here is the sketch I used (only changed my broker details and what was asked to change:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>

#define BUTTON          0                                    // (Don't Change for Sonoff)
#define RELAY           12                                   // (Don't Change for Sonoff)
#define LED             13                                   // (Don't Change for Sonoff)

#define MQTT_CLIENT     "Sonoff_Living_Room_v1.0p"           // mqtt client_id (Must be unique for each Sonoff)
#define MQTT_SERVER     "192.168.0.100"                      // mqtt server
#define MQTT_PORT       1883                                 // mqtt port
#define MQTT_TOPIC      "home/sonoff/living_room/1"          // mqtt topic (Must be unique for each Sonoff)
#define MQTT_USER       "user"                               // mqtt user
#define MQTT_PASS       "pass"                               // mqtt password

#define WIFI_SSID       "homewifi"                           // wifi ssid
#define WIFI_PASS       "homepass"                           // wifi password

#define VERSION    "\n\n------------------  Sonoff Powerpoint v1.0p  -------------------"

extern "C" { 
  #include "user_interface.h" 
}

bool sendStatus = false;
bool requestRestart = false;

int kUpdFreq = 1;
int kRetries = 10;

unsigned long TTasks;
unsigned long count = 0;

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient, MQTT_SERVER, MQTT_PORT);
Ticker btn_timer;

void callback(const MQTT::Publish& pub) {
  if (pub.payload_string() == "stat") {
  }
  else if (pub.payload_string() == "on") {
    digitalWrite(LED, LOW);
    digitalWrite(RELAY, HIGH);
  }
  else if (pub.payload_string() == "off") {
    digitalWrite(LED, HIGH);
    digitalWrite(RELAY, LOW);
  }
  else if (pub.payload_string() == "reset") {
    requestRestart = true;
  }
  sendStatus = true;
}

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(RELAY, OUTPUT);
  pinMode(BUTTON, INPUT);

  digitalWrite(LED, HIGH);
  digitalWrite(RELAY, LOW);
  
  btn_timer.attach(0.05, button);
  
  mqttClient.set_callback(callback);
  
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  Serial.begin(115200);
  Serial.println(VERSION);
  Serial.print("\nESP ChipID: ");
  Serial.print(ESP.getChipId(), HEX);
  Serial.print("\nConnecting to "); Serial.print(WIFI_SSID); Serial.print(" Wifi"); 
  while ((WiFi.status() != WL_CONNECTED) && kRetries --) {
    delay(500);
    Serial.print(" .");
  }
  if (WiFi.status() == WL_CONNECTED) {  
    Serial.println(" DONE");
    Serial.print("IP Address is: "); Serial.println(WiFi.localIP());
    Serial.print("Connecting to ");Serial.print(MQTT_SERVER);Serial.print(" Broker . .");
    delay(500);
    while (!mqttClient.connect(MQTT::Connect(MQTT_CLIENT).set_keepalive(90).set_auth(MQTT_USER, MQTT_PASS)) && kRetries --) {
      Serial.print(" .");
      delay(1000);
    }
    if(mqttClient.connected()) {
      Serial.println(" DONE");
      Serial.println("\n----------------------------  Logs  ----------------------------");
      Serial.println();
      mqttClient.subscribe(MQTT_TOPIC);
      blinkLED(LED, 40, 8);
      digitalWrite(LED, HIGH);
    }
    else {
      Serial.println(" FAILED!");
      Serial.println("\n----------------------------------------------------------------");
      Serial.println();
    }
  }
  else {
    Serial.println(" WiFi FAILED!");
    Serial.println("\n----------------------------------------------------------------");
    Serial.println();
  }
}

void loop() { 
  mqttClient.loop();
  timedTasks();
  checkStatus();
}

void blinkLED(int pin, int duration, int n) {             
  for(int i=0; i<n; i++)  {  
    digitalWrite(pin, HIGH);        
    delay(duration);
    digitalWrite(pin, LOW);
    delay(duration);
  }
}

void button() {
  if (!digitalRead(BUTTON)) {
    count++;
  } 
  else {
    if (count > 1 && count <= 40) {   
      digitalWrite(LED, !digitalRead(LED));
      digitalWrite(RELAY, !digitalRead(RELAY));
      sendStatus = true;
    } 
    else if (count >40){
      Serial.println("\n\nSonoff Rebooting . . . . . . . . Please Wait"); 
      requestRestart = true;
    } 
    count=0;
  }
}

void checkConnection() {
  if (WiFi.status() == WL_CONNECTED)  {
    if (mqttClient.connected()) {
      Serial.println("mqtt broker connection . . . . . . . . . . OK");
    } 
    else {
      Serial.println("mqtt broker connection . . . . . . . . . . LOST");
      requestRestart = true;
    }
  }
  else { 
    Serial.println("WiFi connection . . . . . . . . . . LOST");
    requestRestart = true;
  }
}

void checkStatus() {
  if (sendStatus) {
    if(digitalRead(LED) == LOW)  {
      mqttClient.publish(MQTT::Publish(MQTT_TOPIC"/stat", "on").set_retain().set_qos(1));
      Serial.println("Relay . . . . . . . . . . . . . . . . . . ON");
    } else {
      mqttClient.publish(MQTT::Publish(MQTT_TOPIC"/stat", "off").set_retain().set_qos(1));
      Serial.println("Relay . . . . . . . . . . . . . . . . . . OFF");
    }
    sendStatus = false;
  }
  if (requestRestart) {
    blinkLED(LED, 400, 4);
    ESP.restart();
  }
}

void timedTasks() {
  if ((millis() > TTasks + (kUpdFreq*60000)) || (millis() < TTasks)) { 
    TTasks = millis();
    checkConnection();
  }
}

Here is my HA Yaml code (changed the original code from switch to light):

light:
  platform: mqtt
  name: "Living Room"
  state_topic: "home/sonoff/living_room/1/stat"
  command_topic: "home/sonoff/living_room/1"
  qos: 1
  payload_on: "on"
  payload_off: "off"
  retain: true

Here is the strange thing. When check what message is being sent when trying to change the state via MQTTfx. Its show that the payload is “True” when Publishing to command_topic “home/sonoff/living_room/1”. The payload is not setting “on”

I can Publishing to command_topic “home/sonoff/living_room/1” with payload “on” via MQTTfx on the light turns on.

Please assist in anyway possible.

Cheers

Just out of curiosity, try reversing your topics in the Home Assistant configuration to:

state_topic: "home/sonoff/living_room/1"
command_topic: "home/sonoff/living_room/1/stat"

I know it sounds counter productive, but from what I am seeing in your sketch, it appears like the sonoff is viewing the state topic and the command topic in the reverse of how Home Assistant is viewing them. This would account for sending directly to the home/sonoff/living_room/1 and having it successfully turn on the light, because based on the sketch, that action would bypass the stat check it is doing and simply forcing the state.

The sketch seems to be publishing to the home/sonoff/living_room/1/stat topic after reading the state from the home/sonoff/living_room/1 topic while Home Assistant also seems to be publishing to the home/sonoff/living_room/1/stat topic after reading from the home/sonoff/living_room/1 topic. Sonoff should actually be doing the opposite of Home Assistant, but it appears they are both doing the same thing. Nobody is publishing to the topic that Sonoff is looking at to turn on the light.

Just try reversing them and see if anything changes.

It’s late, and sometimes I get turned around when my mind isn’t operating on all cylinders, so I could be way off base on this.

Thank you for your quick response.

A tried to publish the “home/sonoff/living_room/1/stat” via MQTT and the state changed on HA.

I think you are 100% correct.

Do you think I should change the sketch or will it be fine to continue like this after doing the above change?

If that does indeed solve the problem, I wouldn’t mess with the sketch and just leave them switched in HA. It will take more reworking the sketch than is necessary to achieve the same result.

I may have gotten that wrong… Been looking again and things might be right in the sketch after all. Writing directly to the stat topic tells HA that sonoff already turned on the switch, that is why you see the state change in HA. So HA is in fact writing to the command topic and waiting for sonoff to publish to the stat topic, while the sketch is listening to the command and updating the stat.

I was wrong, so there must be another problem.

Something that might be helpful…

In your sketch, subscribe to the topic “#”. That’s a wildcard, it will subscribe you to all topics. Then in your callback function print out the topic and payload strings. That will show you all incoming MQTT messages and you can determine the correct topic and payload to use.

The command topic is the one HA uses to send commands to the device. The state topic is for the device to communicate its status back to HA.