ESP8266XXX hardware OR software issue?

I would appreciate some education regarding this matter. I have project with the code shown that will be used to run a handheld remote control for controlling a sonoff basic switch. With some help from others on this forum I got the software working but have now run into another issue that I am not sure what to blame it on, hardware or software. I have the same issue when using either of these two devices. An ESP8266-12E NodeMCU or a Wemos D1 mini pro. The issue is the same on either device. The sketch compiles and uploads with no issues. The serial monitor reports connected to wifi and MQTT. And when pushing the button, everything works fine on and off for both the onboard LED and the remote sonoff switch. BUT after 30~ seconds of NO button activity, system goes dead. Pushing the button will change the LED state but the MQTT message is not being sent and of course the remote switch does not work. I have verified that the MQTT messages are not there using MQTTLens app. Simply pushing the reset button on the associated esp device brings operation back, but only if you start pushing the button right away.
Any and all assistance greatly appreciated. I am extremely new to this...
KentM

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
 
const char* ssid = "xx";
const char* password =  "xxx";
const char* mqttServer = "192.168.1.x";
const int mqttPort = 1883;

#define LED 5 // D1(gpio5)
#define BUTTON 4 //D2(gpio4)
//Let's say you have your push button on pin 4
int switchState = 0; // actual read value from pin4
int oldSwitchState = 0; // last read value from pin4
int lightsOn = 0; // is the switch on = 1 or off = 0

WiFiClient espClient;
PubSubClient client(espClient);
 
void setup() {

  Serial.begin(115200);
   WiFi.begin(ssid, password);
   while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print("Connecting to WiFi..");
    Serial.println("Connected to ");
    Serial.print(ssid);
  }
  
 
  client.setServer(mqttServer, mqttPort);
 // client.setCallback(callback);
 
  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
 
    if (client.connect("ESP8266Client" )) {
 
      Serial.print("connected");  
 
    } else {
 
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
 
    }
  }
 
  client.publish("home/office/sonoff1", "Remote");
  client.subscribe("home/office/sonoff1");


 pinMode(BUTTON, INPUT); // push button 
 pinMode(LED, OUTPUT); // anything you want to control using a switch e.g. a Led
}
void loop () 
{
    switchState = digitalRead(BUTTON); // read the pushButton State
    if (switchState != oldSwitchState) // catch change
    {
        oldSwitchState = switchState;
        if (switchState == HIGH)
        {
            // toggle
            lightsOn = !lightsOn;
        }
        
        if(lightsOn)
        {
            digitalWrite(LED, HIGH); // set the LED on
            client.publish("home/office/sonoff1", "on");
        } 
        else 
        {
            digitalWrite(LED, LOW); // set the LED off
            client.publish("home/office/sonoff1", "off");
        }
    }
    
}

Do you have pullup/pulldown resistors on the button and is it connected to
pin-button-ground
or
pin-button-resistor-vcc.

You should not assume the ESP is still connected to Wi-Fi or the MQTT broker and check both before trying to publish the button state.

Thank you taking the time to reply. I am a really new at this and appreciate any assistance offered. I am an old guy and I am afraid I will die before I cut and paste enough stuff that makes it work!!!!
First the pin (gpio5) has a 10k pulldown attached and the button applies VCC (3.3v) to that pin when closed.
Can you point me to some information on how to check the wifi and MQTT at the source rather than the receive end of this chain?
Thank you
KentM

A better way of doing buttons on MCU's is to connect one side of the button to ground and the other side to a pin on the MCU that is set as input with internal pullup. This reduces the external component count but reverses the logic in code as a button press then pulls the pin low instead of high.

I use some ESP8266 devices connected to BME280 sensors to periodically send values to MQTT. I have posted the part of my code that does the sending below. It's sends a lot more data than your needing but it gives the idea of how it checks if wifi and MQTT is connected before publishing the results.

void sendResults()                                          // Publish readings to MQTT
{
  if(WiFi.status() != WL_CONNECTED)                         // Wifi connected?
  {
    WiFi.disconnect();                                      // If not then drop current connection
    delay(100);
    WiFi.begin(ssid, password);                             // Try to restart Wifi
    while (WiFi.waitForConnectResult() != WL_CONNECTED) {   // Wait until something happens
      Serial.println(F("Connection Failed! Rebooting...")); // Failed to connect
      delay(5000);                                          // So wait a bit
      ESP.restart();                                        // And then reboot
    }
  }
  
  if (!client.connected())                                  // Are we connected to MQTT server?
  {
    Serial.println(F("Attempting MQTT connection..."));
    if (client.connect(MQTTCLIENTID, MQTTUSER, MQTTPASSWORD)) 
    {
      Serial.println(F("Connected"));
      client.subscribe(PREAMBLE1 T_COMMAND, 1);              // Subscribe to MQTT input
    } 
    else 
    {
      Serial.print(F("Failed, rc="));
      Serial.print(client.state());
    }
  }
  
  if (client.connected())
  {
    dtostrf(temperature,2,2,chrBuffer);
    client.publish(PREAMBLE1 T_TEMPERATURE, chrBuffer);
    delay(500);
    
    dtostrf(pressure,2,2,chrBuffer);
    client.publish(PREAMBLE1 T_PRESSURE, chrBuffer);
    delay(500);
    
    dtostrf(altitude,2,2,chrBuffer);
    client.publish(PREAMBLE1 T_ALTITUDE, chrBuffer);
    delay(500);
    
    dtostrf(humidity,2,2,chrBuffer);
    client.publish(PREAMBLE1 T_HUMIDITY, chrBuffer);
    delay(500);
    
    dtostrf(vcc,2,2,chrBuffer);
    client.publish(PREAMBLE1 T_VCC, chrBuffer);
    delay(500);
    //***********************************    
    uint32_t free = ESP.getFreeHeap();
    itoa(free, chrBuffer, 10);
    client.publish(PREAMBLE1 "FREE", chrBuffer);
    delay(500);
    //***********************************    
    
    convert2Json();
    packet.toCharArray(chrBuffer, sizeof(chrBuffer));
    client.publish(PREAMBLE2 T_JSON, chrBuffer);
    
  }
}

Are you programming the Sonoffs with custom firmware or have you found a way to control stock devices without the need of the EWelink app?

There are a lot of old folks on here (me included).

Thanks for the information. I appreciate your supplying some code to study. In my case, it appears to me that the Wemos D1 mini pro(V1.0) is going into sleep mode. I am not sure exactly which mode it is but I can bring it back to life by pulling the RST pin low momentarily. And then the system works fine for a period and then goes dark again. It is not completely dead because I can still make the LED turn off and on with a button push. I would really like to avoid the requirement for a separate switch just to "awaken" the device. I have the button wired to put a LOW(gnd) on the BUTTON PIN to cause the system to toggle. The drawback here is that trying to use the same button would cause the whole system to restart every time it was pushed.
So maybe I will have to add a wake up button to the system.
I enrolled in one of the online courses about Home Automation. The one from RandomNerdTutorials. I really learned a lot but it really is like so many courses in that it simply teaches how to use examples and the theory gets only lightly touched. But I am not complaining about that. I did learn a lot from it and managed to get through the whole group of devices with the exception of one that I never did figure out and they could not seem to find the issue either.
One of the things that I did get from the course was a section on flashing the sonoff basic to allow use with my own MQTT server. I flashed two of them and am using them to work out this remote control device. I have about ten of those switches in the house, mostly controlled by the eWelink. But my goal is to get completely away from that and on to my own server. Right now I have most of the devices controlled with time settings over the eWelink or via Google and Alexa. I know the voice control will present another issue but that is on the back burner for now.
This handheld remote device will be used to operate a switch that may need to be operated at a time other than what is set by the timer.

Lot of ideas, so little time!!
https://rntlab.com/how-to-use-sonoff-with-node-red/

Data on the wemos D1 mini show that there are some neat features regarding sleep in the later versions of the board V(2.0 and V3.0)

Can you supply a schematic of what your circuit looks like, also supply the current code (after removing sensitive data like usernames/passwords etc) and describe exactly what your trying to do.
Currently I read it as this ESP8266 is trying to be a switch that publishes a button state to MQTT and it is picked up by a subscriber and acted on.

Thanks for your interest. The code is as shown in the OP. The basic plan is to have a handheld, battery powered device equipped with a spst momentary switch that will cause a remote sonoff switch to turn on with one button push and then turn off with the next button push. The sonoff has been flashed to work with my own MQTT server running on a RPi. The same RPi also runs Node Red. The system works perfectly with one exception.

As a complete novice at this, I can only assume that the esp8266 devices have some sort sleep function. It appears that the thing that has shut off is the wifi. I am basing that assumption on the fact that the LED keeps tuning on and off normally but the messages to NodeRed debug window stop.

I added the code you sent earlier and as I understand it, it checks for the wifi connection, then the MQTT connection. If required, it starts everything over again. I ran the code but after the initial connection messages on the sermon, I never see anything else. I don't understand what tells the code to check again for a connection. It appears to me that wifi may not be the problem. I need to find some way to check for that i/p in real time to see if it is still on the air.

I know that pulling RST low will recover the whole operation. I am just about resolved to adding another switch to wake it up or just turn the power on/off.

The circuit is nothing but an LED with 330ohm to ground. The button pin is pulled up with a 10k resistor and pulled to ground with the switch. My ultimate goal is to get the sonoff switch to publish back to the remote to tell me that it did indeed turn on or off.

Please don't waste a lot of time on this. My immediate goal can be achieved with a second switch but I was hoping to avoid that.

Regards

Hi,

As you going to run this from a battery I would suggest you think of a better idea as the ESP8266 will not last long on battery if it's powered and connected to wifi all the time.
You could put the ESP into deep sleep and use the button to power it up etc but there will be a bit of a delay between pressing the button and a response as the ESP wakes up, connects to wifi & MQTT and publishes.

I think you main problem with the code is it did not have a client.loop() within loop() to allow MQTT to do housekeeping.

I have hacked around your code (below)and tested it here on my MQTT/Node-Red setup and it is working okay. I removed your Client ID from the connect code and also setup my Node-Red MQTT with no client ID so one is auto generated for me as I could not get the data in NR otherwise.

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

const char* ssid = "***";
const char* password =  "***";
const char* mqttServer = "192.168.1.***";
const int mqttPort = 1883;

#define LED 5 // D1(gpio5)
#define BUTTON 4 //D2(gpio4)

bool lightsOn = false;

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  
  Serial.begin(115200);
  delay(500);
  Serial.print("Connecting to WiFi...");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
  }
  Serial.print("Connected to ");
  Serial.println(ssid);
  
  client.setServer(mqttServer, mqttPort);
  // client.setCallback(callback);
  
  Serial.print("Connecting to MQTT...");
  while (!client.connected()) 
  {
    
    if (client.connect("", "username", "password" )) 
    {
      Serial.println("connected"); 
    } 
    else 
    {
      Serial.print("failed with state ");
      Serial.println(client.state());
      delay(2000);
      
    }
  }
  
  client.publish("test/sonoff1", "Remote");
  //client.subscribe("home/office/sonoff1");
  
  pinMode(BUTTON, INPUT_PULLUP); // push button
  pinMode(LED, OUTPUT); // anything you want to control using a switch e.g. a Led
}
void loop ()
{
  client.loop();                                            // MQTT houskeeping
  
  if(digitalRead(BUTTON) == LOW)                            // Button pressed (LOW)
  {
    lightsOn = !lightsOn;                                   // Toggle light state
    
    digitalWrite(LED, lightsOn);
    if(lightsOn)
    {
      client.publish("test/office/sonoff1", "on");
    }
    else
    {
      client.publish("test/office/sonoff1", "off");
    }
    
    delay(50);                                              // Switch debounce
    while(digitalRead(BUTTON) == LOW)                       // Wait for button to be released
    {
      delay(10);                                            // Short delay to yield else WDT reset will happen
    }
  }
  delay(100);
}

Well my friend, you have nailed it. It runs forever now and hasn't missed a beat. The client.loop did the trick. I also noticed that you declared the state of "lightsOn" rather than test for it in the void loop.
I do intend to run this with a battery. I have researched the deep sleep function and I don't think the delay would affect the operation abnormally. The immediate problem is whether or not my "shaky" hands can solder a wire to GPIO16 on the esp8266-01. There was a time but that has passed!!! One solution is to change to the Wemos D1 miniPro V2.0. It not only has a battery connector but also a set of solder pads that can put the device into deep sleep. I also plan to mount a connector on the box to allow charging via one of the many phone chargers laying around the house.
I will keep you posted with the progress.
I can't thank you enough for your assistance.
Kentm

merkelck:
I also noticed that you declared the state of "lightsOn" rather than test for it in the void loop.

To do the job properly you would need to add MQTT callback code and set the LED based on this instead of on a local state. That way you can be sure the LED reflects what got sent.

I'm a bit busy but have knocked up a quick version with callback below. It's not elegant or properly bug tested but hopefully it will give you an idea. The NR test Flow is simple and just publishes the received topic to the reply topic.

Good luck in your project.

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

const char* ssid = "***";
const char* password =  "***";
const char* mqttServer = "192.168.1.***";
const int mqttPort = 1883;

#define mqtt_publish    "pub/office/sonoff1"
#define mqtt_subscribe  "sub/office/sonoff1"
#define mqtt_sub1       "on"
#define mqtt_sub2       "off"

#define LED 5 // D1(gpio5)
#define BUTTON 4 //D2(gpio4)

bool lightsOn = false;

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  
  Serial.begin(115200);
  pinMode(BUTTON, INPUT_PULLUP); // push button
  pinMode(LED, OUTPUT); // anything you want to control using a switch e.g. a Led
  delay(500);
  
  Serial.print("Connecting to WiFi...");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
  }
  Serial.print("Connected to ");
  Serial.println(ssid);
  
  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
  
  Serial.print("Connecting to MQTT...");
  while (!client.connected()) 
  {
    
    if (client.connect("", "***", "***" )) 
    {
      Serial.println("connected"); 
    } 
    else 
    {
      Serial.print("failed with state ");
      Serial.println(client.state());
      delay(2000);
      
    }
  }
  
  client.subscribe(mqtt_subscribe);
  client.publish(mqtt_publish, "Remote");
}
void loop ()
{
  client.loop();                                            // MQTT houskeeping
  
  if(digitalRead(BUTTON) == LOW)                            // Button pressed (LOW)
  {
    lightsOn = !lightsOn;                                   // Toggle light state
    
    if(lightsOn)
    {
      client.publish(mqtt_publish, mqtt_sub1);
    }
    else
    {
      client.publish(mqtt_publish, mqtt_sub2);
    }
    
    delay(50);                                              // Switch debounce
    while(digitalRead(BUTTON) == LOW)                       // Wait for button to be released
    {
      delay(10);                                            // Short delay to yield else WDT reset will happen
    }
  }
  delay(100);
}


void callback(char* topic, byte * data, unsigned int length)// Callback for subscribed MQTT topics
{
  Serial.print(topic);
  Serial.print(": ");
  for (int i = 0; i < length; i++)
  {
    Serial.print((char)data[i]);
  }
  Serial.println();
  
  
  if (strncmp(topic, mqtt_subscribe, strlen(mqtt_subscribe)) == 0)    // Check it's the right topic
  {
    if (strncmp((char*)data, mqtt_sub1, strlen(mqtt_sub1)) == 0)             // Check it's the right data
    {
      lightsOn = true;
    }
    
    if (strncmp((char*)data, mqtt_sub2, strlen(mqtt_sub2)) == 0)             // Check it's the right data
    {
      lightsOn = false;
    }
  }
  digitalWrite(LED, lightsOn);
  
}

Once again, it works right out of the box. I am proceeding now to study the deep sleep method.
Thank you again for your interest and help with this project.

Thanks for all your help with this project. I took the easy way out as I need this to be working NOW. It works fine and does what I wanted in the first place. I elected to add a power switch because the unit will sit for days requiring no attention until I want to use it. When the battery eventually depletes, I just use a phone charger to get it back in shape.

My next effort is going to be working on these units so that the LED state of the sonoff switch actually sends a message back to the remote unit LED.
Regards
merkelck

The code I posted in #9 subscribes to an MQTT topic to possibly light an LED

A simple link between the MQTT pub & sub topics (see below) will do but better if the code controlling the Sonoff publishes a result based on the state.

[
    {
        "id": "13a3a465.498e0c",
        "type": "mqtt in",
        "z": "36036aeb.47c9b6",
        "name": "",
        "topic": "pub/office/sonoff1",
        "qos": "2",
        "broker": "",
        "x": 250,
        "y": 160,
        "wires": [
            [
                "f08ef228.765c"
            ]
        ]
    },
    {
        "id": "f08ef228.765c",
        "type": "mqtt out",
        "z": "36036aeb.47c9b6",
        "name": "",
        "topic": "sub/office/sonoff1",
        "qos": "",
        "retain": "",
        "broker": "",
        "x": 500,
        "y": 160,
        "wires": []
    }
]