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

User avatar
By Narfel
#71356 I worked a bit on my led switch sketch and got a couple of new concepts working ...kinda. The problem where i think i'm conceptionally stuck again is having the status of the switch reported to the website and back.

My idea for that working was create a checkbox that is acting as a button with an onclick event and at the same time indicating the status. Since i use a simple GET on the route "/toggle" of my server i used a XMLHttpRequest to have the checkbox just basically "touch" it to not have a navigation event in the browser. That works (more or less) fine to switch the led on or off from the browser. I can't seem to wrap my head around the other way, though.

my sketch so far:
Code: Select all#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ArduinoOTA.h>
#define RELAY 5
#define BUTTON 4

ESP8266WebServer server;

char* ssid = "xxxxxx";
char* password = "xxxxxxx";
bool currentState = false;
bool lastButtonState = false;
bool relayState = false;
const bool pressedState = 0;

const char *website =
#include "website.h"
;

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

void handlePhysicalButton() {
  currentState = digitalRead(BUTTON);
  if (currentState != lastButtonState) {
    lastButtonState = currentState;
    if (currentState == pressedState) {
      relayState = !relayState;
    }
    digitalWrite(RELAY, relayState);
  }
  delay(50); //poor mans debounce
  server.send ( 200, "text/html", "" );
}

void handleWebButton() {
  relayState = !relayState;
  digitalWrite(RELAY, relayState);
  Serial.print("Web button pressed!\n");
}

void setup() {
  char HostName[32];
  sprintf(HostName, "Narfel-%06X", ESP.getChipId()); //create hostname name+chip-id
  WiFi.mode(WIFI_STA);
  WiFi.hostname(HostName);
  Serial.begin(115200);
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(RELAY, OUTPUT);
  Serial.print("(Re)booting ");
  Serial.printf("%s, ChipID: ", HostName);
  Serial.println(ESP.getChipId());
 
  // ======================================= wifi =====================
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }
  Serial.println(WiFi.localIP());
 
  // ======================================= webserver ================
  server.on ("/", handleRoot);
  server.on("/toggle", handleWebButton);
  server.begin();
 
  // ======================================= OTA ======================
  ArduinoOTA.setPassword((const char *)"xxxxxxx");
  ArduinoOTA.onStart([]() {
    Serial.println("Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    Serial.printf("Done!");
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
}

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

Website.h:
Code: Select allR"0(
<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title></title>
</head>
<body>
<script type="text/javascript">
  function handleClick() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/toggle');
    xhr.send('');
  }
</script>
  <div class="switch">
    <input type="checkbox" onclick="handleClick()">
  </div>
 </body>
</html>
)0"
User avatar
By rudy
#71358 I don't want to distract you from learning how to work with your current approach, but I wanted you to have a look at the following. It allows control of an output from a web page and shows the status of that output. Also you can have multiple browsers access the web page and each can control the output and will update the other connected pages.

Change the value of LEDPIN for your output.

Code: Select all/*
 * ESP8266 Web server with Web Socket to control an LED.
 *
 * The web server keeps all clients' LED status up to date and any client may
 * turn the LED on or off.
 *
 * For example, clientA connects and turns the LED on. This changes the word
 * "LED" on the web page to the color red. When clientB connects, the word
 * "LED" will be red since the server knows the LED is on.  When clientB turns
 * the LED off, the word LED changes color to black on clientA and clientB web
 * pages.
 *
 * References:
 *
 * https://github.com/Links2004/arduinoWebSockets
 *
 */

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

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

//-----------------------------------------------------------------
const char* ssid = "network ssid";
const char* password = "password";

//-----------------------------------------------------------------
//MDNSResponder mdns;

static void writeLED(bool);

ESP8266WiFiMulti WiFiMulti;

ESP8266WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);

//----------------------------------------------------------------------
static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable=0">
<title>ESP8266 WebSocket Demo</title>

<body style="background-color: #808080; font-family: Arial, Helvetica, Sans-Serif; Color: #000000;">

<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 e = document.getElementById('ledstatus');
    if (evt.data === 'ledon') {
      e.style.color = 'red';
    }
    else if (evt.data === 'ledoff') {
      e.style.color = 'black';
    }
    else {
      console.log('unknown event');
    }
  };
}
function buttonclick(e) {
  websock.send(e.id);
}
</script>
</head>
<body onload="javascript:start();">
<h1>ESP8266 WebSocket Demo</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>
</body>
</html>
)rawliteral";

//----------------------------------------------------------------------

// GPIO#0 is for Adafruit ESP8266 HUZZAH board. Your board LED might be on 13.
const int LEDPIN = 12;                      // *****************************************************************************
// Current LED status
bool LEDStatus;

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

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t 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", INDEX_HTML);
}

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);
}

static void writeLED(bool LEDon)
{
  LEDStatus = LEDon;

  if (LEDon) {
    digitalWrite(LEDPIN, 1);
  }
  else {
    digitalWrite(LEDPIN, 0);
  }
}

void setup()
{
  pinMode(LEDPIN, OUTPUT);
  writeLED(false);

  Serial.begin(115200);

  //Serial.setDebugOutput(true);

  Serial.println();
  Serial.println();
  Serial.println();

  for(uint8_t t = 4; t > 0; t--) {
    Serial.printf("[SETUP] BOOT WAIT %d...\r\n", t);
    Serial.flush();
    delay(1000);
  }

  WiFiMulti.addAP(ssid, password);

  while(WiFiMulti.run() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }


  Serial.println("");
  Serial.println("ESPWebSock.ino");
 
  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("espWebSock", 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://espWebSock.local or http://");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.onNotFound(handleNotFound);

  server.begin();

  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
}

void loop()
{
  webSocket.loop();
  server.handleClient();
}
User avatar
By Narfel
#71359 Many thanks, i will have a look. While deciding my approach i came across websockets and thought it to be even more daunting for a simple implementation at my level. Now i start to think it is more like a superset than a different approach. These days i have to treat carefully, every little thing opens to a cosmos of new (interesting) concepts. Oh, those halcyon days when i thought a lightswitch, yeah, that's gotta be easy :lol: