So you're a Noob? Post your questions here until you graduate! Don't be shy.
User avatar
By Narfel
#71767 Bashing my head against another wall here. The below code is my current relayswitch implementation by using the websockets example from this thread. It works rather well, except that the handlePhysicalButton() in the main loop is causing problems by continuously firing at the websocket server no matter if it's running or not. Or, just to make it complicated for giggles, sometimes it refuses to fire at all until i reload the page. That's what i want eventually, but i don't know why and when it happens.

I'm looking for
a) an quick assessment if the concept of how i coded this makes sense
b) a way to have the physical button routine only send a message when pressed and checking if the websocket connection is up. I was able to do within the javascript with if (websock.readyState === WebSocket.OPEN) .

Code: Select all#include <Arduino.h>
#include <Hash.h>

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <ESP8266mDNS.h>

const char *website =
R"0(
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable=0">
  <title>Switcheroo</title>

  <body>

<script>
  var websock;

  function start() {
    websock = new WebSocket('ws://' + window.location.hostname + ':81/');
    websock.onopen = function(evt) {
      console.log('websock open');
    };
    websock.onclose = function(evt) {
      console.log('websock close');
    };
    websock.onerror = function(evt) {
      console.log(evt);
    };
    websock.onmessage = function(evt) {
      console.log(evt);
      var a = document.getElementById('cbStyle');
      var e = document.getElementById('ledstatus');
      if (evt.data === 'ledon') {
        e.style.color = 'red';
        a.checked = true;
      } else if (evt.data === 'ledoff') {
        e.style.color = 'black';
        a.checked = false;
      } else {
        console.log('unknown event');
      }
    };
  }

  function checkboxClick(e) {
    if (e.checked) {
        if (websock.readyState === WebSocket.OPEN) {
          websock.send('ledon');
      }
    } else {
      if (websock.readyState === WebSocket.OPEN) {
        websock.send('ledoff');
      }
    }
    e.checked = !e.checked;
  }

  function buttonClick(e) {
    if (websock.readyState === WebSocket.OPEN) {
      websock.send(e.id);
    }
  }

  window.addEventListener("load", start, false);
</script>
</head>

<body>
  <h1>Switcheroo111</h1>
  <div id="ledstatus"><b>LED</b></div>
  <button id="ledon" type="button" onclick="buttonClick(this);">On</button>
  <button id="ledoff" type="button" onclick="buttonClick(this);">Off</button>
  <div id="switchCB" class="switch">
    <input id="cbStyle" class="cbStyle cbStyle-round" type="checkbox" onclick="checkboxClick(this)">
    <label for="cbStyle"></label>
  </div>
</body>

</html>
)0";

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, unsigned int length), handleRoot(), handleNotFound(), handlePhysicalButton();
static void writeLED(bool LEDon);

#define RELAY 5  //D1
#define BUTTON 4 //D2

MDNSResponder mdns;
ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);

const char* ssid = "xxxxxxxxxxx";
const char* password = "xxxxxxxxxxx";

// Commands sent through Web Socket
const char LEDON[] = "ledon";
const char LEDOFF[] = "ledoff";

bool LEDStatus = false;
bool currentState = false;
bool lastButtonState = false;
bool pressedState = false;

//----------------------------------------------------------------------
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, unsigned int length)
{
        Serial.printf("webSocketEvent(%d, %d, ...)\r\n", num, type);
        switch(type) {
        case WStype_DISCONNECTED:
                Serial.printf("[%u] Disconnected!\r\n", num);
                break;

        case WStype_CONNECTED:
        {
                IPAddress ip = webSocket.remoteIP(num);
                Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\r\n", num, ip[0], ip[1], ip[2], ip[3], payload);
                // Send the current LED status
                if (LEDStatus) { webSocket.sendTXT(num, LEDON, strlen(LEDON)); }
                else { webSocket.sendTXT(num, LEDOFF, strlen(LEDOFF)); }
        }
        break;

        case WStype_TEXT:
                Serial.printf("[%u] get Text: %s\r\n", num, payload);
                if (strcmp(LEDON, (const char *)payload) == 0) {
                        writeLED(true);
                } else if (strcmp(LEDOFF, (const char *)payload) == 0) {
                        writeLED(false);
                }
                else { Serial.println("Unknown command"); }
                // send data to all connected clients
                webSocket.broadcastTXT(payload, length);
                break;

        case WStype_BIN:
                Serial.printf("[%u] get binary length: %u\r\n", num, length);
                hexdump(payload, length);
                // echo data back to browser
                webSocket.sendBIN(num, payload, length);
                break;

        default:
                Serial.printf("Invalid WStype [%d]\r\n", type);
                break;
        }
}

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

void handleNotFound()
{
        String message = "File Not Found\n\n";
        message += "URI: ";
        message += server.uri();
        message += "\nMethod: ";
        message += (server.method() == HTTP_GET) ? "GET" : "POST";
        message += "\nArguments: ";
        message += server.args();
        message += "\n";
        for (uint8_t i=0; i<server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
        server.send(404, "text/plain", message);
}

void handlePhysicalButton() {
  delay(100); //just slow down a bit
  currentState = digitalRead(BUTTON);
  if (currentState != lastButtonState) {
    lastButtonState = currentState;
    if (currentState == pressedState) {
      LEDStatus = !LEDStatus;
    }
    digitalWrite(RELAY, LEDStatus);
  }
  delay(50); //poor mans debounce
  if (LEDStatus) { webSocket.sendTXT(0, LEDON, strlen(LEDON)); }
  else { webSocket.sendTXT(0, LEDOFF, strlen(LEDOFF)); }
}

static void writeLED(bool LEDon)
{
        LEDStatus = LEDon;
        if (LEDon) { digitalWrite(RELAY, 1); }
        else { digitalWrite(RELAY, 0); }
}
//----------------------------------------------------------------------
void setup() {
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(RELAY, OUTPUT);
  writeLED(false);
  char HostName[32];
  sprintf(HostName, "Narfel-%06X", ESP.getChipId()); //create hostname name+chip-id
  WiFi.mode(WIFI_STA);
  WiFi.hostname(HostName);
  Serial.begin(115200);
  //Serial.setDebugOutput(true);
  Serial.println();
  for(uint8_t t = 4; t > 0; t--) {
          Serial.printf("[SETUP] BOOT WAIT %d...\r\n", t);
          Serial.flush();
          delay(1000);
  }

  Serial.printf("%s, ChipID: ", HostName);
  Serial.println(ESP.getChipId());

  WiFiMulti.addAP(ssid, password);
  while(WiFiMulti.run() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print(WiFi.SSID());
  Serial.print(" (");
  Serial.print(WiFi.RSSI());
  Serial.println(")");

  if (mdns.begin("esprelay", WiFi.localIP())) {
          Serial.println("MDNS responder started");
          mdns.addService("http", "tcp", 80);
          mdns.addService("ws", "tcp", 81);
  } else { Serial.println("MDNS.begin failed"); }

  Serial.print("Connect to http://esprelay.local or http://");
  Serial.println(WiFi.localIP());
  // webserver
  server.on("/", handleRoot);
  server.onNotFound(handleNotFound);
  server.begin();
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
}

void loop() {
  handlePhysicalButton();
  webSocket.loop();
  server.handleClient();
}
User avatar
By philbowles
#71770 The concept is reasonably sound, but how does the button know when the socket disconnects? I had a lot of problems in the early days. Tbh I don't think the socket class is very good at detecting failure and disconnects. Point is, if you don't want the button sending socket messages, you have to have a volatile global which it set and cleared when socket connects and disconnects and only send when set.

Also, you might need to install fiddler to watch the traffic to and from the socket if you get weird errors
User avatar
By Narfel
#71774 Thanks, and here I was hoping for that code to be simple enough to not having to watch the traffic :/ The dev tools in chrome give me an idea, but well.

Just out of curiousity: Is it so esoteric to want a physical switch control the same thing? Nearly every tutorial or example is about reading data and displaying it in all forms. Or am i just oldfashioned and its totally normal that your off the shelf iot nuclear reactor of today can only be controlled by mobile? :? :lol:

About the volatile global. The button doesn't know anything about the socket because i don't want him to, he should be able to work even if the network is down. That's why i have him send the exact same message as the button on the website. And ... according to my theory ... that message in function buttonClick() gets tested for if (websock.readyState === WebSocket.OPEN). However that's up to chance if that happens or not.

Maybe i'm better off just having the button do whatever he pleases and put a on-demand status check in the website. But then i could have stopped at the simple GET request and be done with it for weeks. :shock: :|
User avatar
By rudy
#71777
Narfel wrote:Is it so esoteric to want a physical switch control the same thing?

I consider it to be a requirement in most cases.

The button doesn't know anything about the socket because i don't want him to, he should be able to work even if the network is down.

I think that makes a lot of sense. It is how I would do it. (so of course it makes sense to me)

Maybe i'm better off just having the button do whatever he pleases and put a on-demand status check in the website. But then i could have stopped at the simple GET request and be done with it for weeks. :shock: :|

But you wouldn't have learned anything. ;)

I tried your code and I had no problems with it so far. I had to do other things so I have not had time to work it much. I hope to later today.