So you're a Noob? Post your questions here until you graduate! Don't be shy.

User avatar
By Narfel
#70948 Hi all, i think i wrote myself into a corner here. I am trying to have a manual pushbutton override for my web-enabled relay.
My goal is to have the physical button work even if there is no wifi, so i thought a simple webserver would handle the request but if all fails the button code still gets evaluated. The code below runs the loop for the physical button while listening for the webbutton... or so i thought :|
I'm not sure if it is simple and i just can't see it or if i am missing a concept here.
Can anybody help?

Code: Select all#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#define RELAY 5  // D1(GPIO5)
#define BUTTON 4 // D2(GPIO4)

ESP8266WebServer server;

char* ssid = "xxxxxxxx";
char* password = "xxxxxxxx";
bool currentState = false;
bool lastButtonState = false;
bool relayState = false;
String HTML_Root = "<!doctype html><html><body>\<a href=\'toggle\'><button type=\"button\">On/Off</button>\</body></html>";

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON, INPUT);
  pinMode(RELAY, OUTPUT);
  // wifi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println(WiFi.localIP());
  // webserver
  server.on ("/", handleRoot);
  server.on("/toggle", handleWebButton);
  server.begin();
}

void loop() {
  server.handleClient();
  handlePhysicalButton();
}

void handleRoot() {
  server.send ( 200, "text/html", HTML_Root );
}

void handlePhysicalButton() {
  currentState = digitalRead(BUTTON);
  if (currentState != lastButtonState)
    lastButtonState = currentState;
    if (currentState) {
      relayState = !relayState;
    }
  }
  if (relayState) {
    Serial.print("digitalRead(BUTTON)result -> HIGH!\n");
    digitalWrite(RELAY, HIGH);
    delay(500);
  } else {
    Serial.print("digitalRead(BUTTON)result -> LOW!\n");
    digitalWrite(RELAY, LOW);
    delay(500);
  }
}

void handleWebButton() {
  currentState = digitalRead(BUTTON);
  lastButtonState = currentState;
  Serial.print("Web button pressed!\n");
  server.send(200, "text/html", HTML_Root);
}
User avatar
By rudy
#70949 I only looked at your code for about a minute. I think you need to handle switch bounce. Either your own routines, or get a library that takes care of it. Even if your logic might be okay (I don't know that) it would get screwed up badly if you are getting a lot of transitions due to switch bounce.
User avatar
By rudy
#70952 Having looked at it more carefully I see that you had long delays (500) that would have handled and switch bounce. But the logic didn't work. I modified it so that it would work the way I thought you wanted. I assumed that since you used the term "Button" that the switch was a momentary push button switch, and that is the way my changes treat it. I also assumed that you only wanted the relay to change on a press and not on a release of the button.

The code will toggle the relay with a press of the button or a click from the web.

There are other ways to deal with this problem, the following is just the simplest that I though of at this time. Note that I enabled pull-up on the switch pin as that is what I needed for my setup.

Code: Select all#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#define RELAY 5  //
#define BUTTON 4 //

ESP8266WebServer server;

char* ssid = "HappyGoLucky";
char* password = "upupandaway";
bool currentState = false;
bool lastButtonState = false;
bool relayState = false;

const bool pressedState = 0;     // 0 if pressed gives a low

//====================================================
String HTML_Root = "<!doctype html><html><body>\<a href=\'toggle\'><button type=\"button\">On/Off</button>\</body></html>";
//====================================================
void handleRoot() {
  server.send ( 200, "text/html", HTML_Root );
}

//====================================================
void handlePhysicalButton() {
  currentState = digitalRead(BUTTON);

  if (currentState != lastButtonState) {
    lastButtonState = currentState;

    if (currentState == pressedState)
    {
      relayState = !relayState;
    }
    digitalWrite(RELAY, relayState);
  }
  delay(50);
}

//====================================================
void handleWebButton() {
  relayState = !relayState;
  digitalWrite(RELAY, relayState);
  Serial.print("Web button pressed!\n");
  server.send(200, "text/html", HTML_Root);
}

//====================================================
void setup() {
  Serial.begin(115200);
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(RELAY, OUTPUT);
  // wifi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println(WiFi.localIP());
  // webserver
  server.on ("/", handleRoot);
  server.on("/toggle", handleWebButton);
  server.begin();
}

//====================================================
void loop() {
  server.handleClient();
  handlePhysicalButton();
}
User avatar
By jarnoldbrown
#70955 If I were doing this, I would use a ticker, and attach my function to it at the start of setup().
I did, in fact do exactly this when writing the code for the temperature control on my homebrew refrigerator. I wanted to be sure that the temperature control would still run, even if the MQTT connection, or the WiFi connection went down. Using a ticker also allows you to do fairly easy debouncing without using delay()
which is also a good thing, in my estimation.

With a single switch, I tend to left shift the switch reading into a 16 bit value, this makes it fairly easy to check for the 1st switch closure after n open reading. For example, if your ticker runs every 20mS, and your switch goes to 0V, then:
if((switchreg & 0x1f) == 0x1e)
would be true if there had been 4 consecutive open switch readings(80mS), followed by one closed reading.