2022年1月1日 星期六

ESP32 Web Server (WebSocket) with Multiple Sliders & MQTT : Control LEDs Brightness (PWM)

ESP32 Web Server (WebSocket) with Multiple Sliders & MQTT : Control LEDs Brightness (PWM)

參考來源https://randomnerdtutorials.com/esp32-web-server-websocket-sliders/




















#include <Arduino.h>

#include <WiFi.h>

#include <AsyncTCP.h>

#include <ESPAsyncWebServer.h>

#include "SPIFFS.h"

#include <Arduino_JSON.h>

//================MQTT=====================

#include <PubSubClient.h>  //MQTT


// Replace with your network credentials

//const char* ssid = "REPLACE_WITH_YOUR_SSID";

//const char* password = "REPLACE_WITH_YOUR_PASSWORD";

const char* ssid     = "TOTOLINK_A3002MU";

const char* password = "24063173";



// Create AsyncWebServer object on port 80

AsyncWebServer server(80);

// Create a WebSocket object

AsyncWebSocket ws("/ws");


//================MQTT=====================

const char* mqtt_server = "broker.mqtt-dashboard.com" ; //MQTT


// Set LED GPIO

const int ledPin1 = 12;

const int ledPin2 = 13;

const int ledPin3 = 14;


String message = "";

String sliderValue1 = "0";

String sliderValue2 = "0";

String sliderValue3 = "0";


int dutyCycle1;

int dutyCycle2;

int dutyCycle3;


// setting PWM properties

const int freq = 5000;

const int ledChannel1 = 0;

const int ledChannel2 = 1;

const int ledChannel3 = 2;


const int resolution = 8;


//================MQTT=====================

WiFiClient espClient;

PubSubClient client(espClient);

long lastMsg = 0;

char msg[50];


//Json Variable to Hold Slider Values

JSONVar sliderValues;


//=========================================

//Get Slider Values

String getSliderValues(){

  sliderValues["sliderValue1"] = String(sliderValue1);

  sliderValues["sliderValue2"] = String(sliderValue2);

  sliderValues["sliderValue3"] = String(sliderValue3);


  String jsonString = JSON.stringify(sliderValues);

  return jsonString;

}


//=========================================

// Initialize SPIFFS

void initFS() {

  if (!SPIFFS.begin()) {

    Serial.println("An error has occurred while mounting SPIFFS");

  }

  else{

   Serial.println("SPIFFS mounted successfully");

  }

}

//=========================================

// Initialize WiFi

void initWiFi() {

  WiFi.mode(WIFI_STA);

  WiFi.begin(ssid, password);

  Serial.print("Connecting to WiFi ..");

  while (WiFi.status() != WL_CONNECTED) {

    Serial.print('.');

    delay(1000);

  }

  Serial.println(WiFi.localIP());

}

//=========================================

void notifyClients(String sliderValues) {

  ws.textAll(sliderValues);

}

//=========================================

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {

  AwsFrameInfo *info = (AwsFrameInfo*)arg;

  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {

    data[len] = 0;

    message = (char*)data;

    if (message.indexOf("1s") >= 0) {

      sliderValue1 = message.substring(2);

      dutyCycle1 = map(sliderValue1.toInt(), 0, 100, 0, 255);

      Serial.println(dutyCycle1);

      Serial.print(getSliderValues());

      notifyClients(getSliderValues());

    }

    if (message.indexOf("2s") >= 0) {

      sliderValue2 = message.substring(2);

      dutyCycle2 = map(sliderValue2.toInt(), 0, 100, 0, 255);

      Serial.println(dutyCycle2);

      Serial.print(getSliderValues());

      notifyClients(getSliderValues());

    }    

    if (message.indexOf("3s") >= 0) {

      sliderValue3 = message.substring(2);

      dutyCycle3 = map(sliderValue3.toInt(), 0, 100, 0, 255);

      Serial.println(dutyCycle3);

      Serial.print(getSliderValues());

      notifyClients(getSliderValues());

    }

    if (strcmp((char*)data, "getValues") == 0) {

      notifyClients(getSliderValues());

    }

  }

}

//=========================================

void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {

  switch (type) {

    case WS_EVT_CONNECT:

      Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());

      break;

    case WS_EVT_DISCONNECT:

      Serial.printf("WebSocket client #%u disconnected\n", client->id());

      break;

    case WS_EVT_DATA:

      handleWebSocketMessage(arg, data, len);

      break;

    case WS_EVT_PONG:

    case WS_EVT_ERROR:

      break;

  }

}

//=========================================

void initWebSocket() {

  ws.onEvent(onEvent);

  server.addHandler(&ws);

}


//================MQTT=====================

void callback(char* topic, byte* message, unsigned int length) {

  Serial.print("Message arrived on topic: ");

  Serial.print(topic);

  Serial.print(". Message: ");

  String messageTemp;

  

  for (int i = 0; i < length; i++) {

    Serial.print((char)message[i]);

    messageTemp += (char)message[i];

  }

  Serial.println();


  // Feel free to add more if statements to control more GPIOs with MQTT


  // If a message is received on the topic esp32/output, you check if the message is either "on" or "off". 

  // Changes the output state according to the message

  if (String(topic) == "alex9ufo/esp32/Multipleslider/input/LED12") {

    Serial.print("Changing output to ");

    sliderValue1 = messageTemp;

    dutyCycle1 = map(sliderValue1.toInt(), 0, 100, 0, 255);

    Serial.println(dutyCycle1);

    Serial.print(getSliderValues());

    notifyClients(getSliderValues());

    }

  if (String(topic) == "alex9ufo/esp32/Multipleslider/input/LED13") {

    Serial.print("Changing output to ");

    sliderValue2 = messageTemp;

    dutyCycle2 = map(sliderValue2.toInt(), 0, 100, 0, 255);

    Serial.println(dutyCycle2);

    Serial.print(getSliderValues());

    notifyClients(getSliderValues());

    

    }

  if (String(topic) == "alex9ufo/esp32/Multipleslider/input/LED14") {

    Serial.print("Changing output to ");

    sliderValue3 = messageTemp;

    dutyCycle3 = map(sliderValue3.toInt(), 0, 100, 0, 255);

    Serial.println(dutyCycle3);

    Serial.print(getSliderValues());

    notifyClients(getSliderValues());    

    }

    loop_mqtt();

 }

//================MQTT=====================

void reconnect() {

  // Loop until we're reconnected

  while (!client.connected()) {

    Serial.print("Attempting MQTT connection...");

    // Attempt to connect

    if (client.connect("ESP32Client")) {

      Serial.println("connected");

      // Subscribe

      client.subscribe("alex9ufo/esp32/Multipleslider/input/LED12");

      client.subscribe("alex9ufo/esp32/Multipleslider/input/LED13");

      client.subscribe("alex9ufo/esp32/Multipleslider/input/LED14");            

    } else {

      Serial.print("failed, rc=");

      Serial.print(client.state());

      Serial.println(" try again in 5 seconds");

      // Wait 5 seconds before retrying

      delay(5000);

    }

  }

}

//================MQTT===================== 

void loop_mqtt(){

  if (!client.connected()) {

    reconnect();

  }

  client.loop();

  long now = millis();

  if (now - lastMsg > 5000) {

    lastMsg = now;

    client.publish("alex9ufo/esp32/Multipleslider/output/LED12", String(sliderValue1).c_str());

    client.publish("alex9ufo/esp32/Multipleslider/output/LED13", String(sliderValue2).c_str());  

    client.publish("alex9ufo/esp32/Multipleslider/output/LED14", String(sliderValue3).c_str());    

  }  

}

//=========================================

void setup() {

  Serial.begin(115200);

  pinMode(ledPin1, OUTPUT);

  pinMode(ledPin2, OUTPUT);

  pinMode(ledPin3, OUTPUT);

  initFS();

  initWiFi();


  // configure LED PWM functionalitites

  ledcSetup(ledChannel1, freq, resolution);

  ledcSetup(ledChannel2, freq, resolution);

  ledcSetup(ledChannel3, freq, resolution);


  // attach the channel to the GPIO to be controlled

  ledcAttachPin(ledPin1, ledChannel1);

  ledcAttachPin(ledPin2, ledChannel2);

  ledcAttachPin(ledPin3, ledChannel3);



  initWebSocket();

  

  // Web Server Root URL

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){

    request->send(SPIFFS, "/index.html", "text/html");

  });

  

  server.serveStatic("/", SPIFFS, "/");


  // Start server

  server.begin();

  //================MQTT===================== 

  client.setServer(mqtt_server, 1883);

  client.setCallback(callback);

}


void loop() {

  ledcWrite(ledChannel1, dutyCycle1);

  ledcWrite(ledChannel2, dutyCycle2);

  ledcWrite(ledChannel3, dutyCycle3);


  ws.cleanupClients();

 

  loop_mqtt();

}


  • Arduino sketch that handles the web server;
  • index.html: to define the content of the web page;
  • sytle.css: to style the web page;
  • script.js: to program the behavior of the web page—handle what happens when you move the slider, send, receive and interpret the messages received via WebSocket protocol.
Organizing your Files arduino sketch index html style css script js

You should save the HTML, CSS, and JavaScript files inside a folder called data inside the Arduino sketch folder, as shown in the previous diagram. We’ll upload these files to the ESP32 filesystem (SPIFFS).

You can download all project files:

  • Download All the Arduino Project Files



[{"id":"7c1e54cf7314f1b2","type":"ui_slider","z":"3c452c5da4098e56","name":"","label":"slider  LED12","tooltip":"","group":"fc89dc38.347898","order":35,"width":0,"height":0,"passthru":true,"outs":"end","topic":"topic","topicType":"msg","min":0,"max":"100","step":1,"className":"","x":410,"y":120,"wires":[["31ae40b4841323cc"]]},{"id":"31ae40b4841323cc","type":"mqtt out","z":"3c452c5da4098e56","name":"","topic":"alex9ufo/esp32/Multipleslider/input/LED12","qos":"1","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"3d43a51a.c133a2","x":780,"y":120,"wires":[]},{"id":"60d22383bd62c768","type":"ui_slider","z":"3c452c5da4098e56","name":"","label":"slider LED13","tooltip":"","group":"fc89dc38.347898","order":35,"width":0,"height":0,"passthru":true,"outs":"end","topic":"topic","topicType":"msg","min":0,"max":"100","step":1,"className":"","x":410,"y":200,"wires":[["17807f322eace2c4"]]},{"id":"17807f322eace2c4","type":"mqtt out","z":"3c452c5da4098e56","name":"","topic":"alex9ufo/esp32/Multipleslider/input/LED13","qos":"1","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"3d43a51a.c133a2","x":780,"y":200,"wires":[]},{"id":"dba05cd4c6d9102c","type":"ui_slider","z":"3c452c5da4098e56","name":"","label":"slider LED14","tooltip":"","group":"fc89dc38.347898","order":35,"width":0,"height":0,"passthru":true,"outs":"end","topic":"topic","topicType":"msg","min":0,"max":"100","step":1,"className":"","x":410,"y":280,"wires":[["1a03ba63031a53de"]]},{"id":"1a03ba63031a53de","type":"mqtt out","z":"3c452c5da4098e56","name":"","topic":"alex9ufo/esp32/Multipleslider/input/LED14","qos":"1","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"3d43a51a.c133a2","x":780,"y":280,"wires":[]},{"id":"2e8b59ca9d5e8430","type":"ui_gauge","z":"3c452c5da4098e56","name":"","group":"fc89dc38.347898","order":38,"width":"5","height":"4","gtype":"gage","title":"LED12 Current Value","label":"units","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","className":"","x":860,"y":360,"wires":[]},{"id":"d31eda43d20e36f9","type":"mqtt in","z":"3c452c5da4098e56","name":"","topic":"alex9ufo/esp32/Multipleslider/output/LED12","qos":"2","datatype":"auto","broker":"3d43a51a.c133a2","nl":false,"rap":true,"rh":0,"x":500,"y":360,"wires":[["2e8b59ca9d5e8430"]]},{"id":"387ebfb9277ef2b5","type":"mqtt in","z":"3c452c5da4098e56","name":"","topic":"alex9ufo/esp32/Multipleslider/output/LED13","qos":"2","datatype":"auto","broker":"3d43a51a.c133a2","nl":false,"rap":true,"rh":0,"x":500,"y":420,"wires":[["3ac1528e8ce5df47"]]},{"id":"6b1fcb7db98a5766","type":"mqtt in","z":"3c452c5da4098e56","name":"","topic":"alex9ufo/esp32/Multipleslider/output/LED14","qos":"2","datatype":"auto","broker":"3d43a51a.c133a2","nl":false,"rap":true,"rh":0,"x":500,"y":480,"wires":[["38d953e53e68e604"]]},{"id":"3ac1528e8ce5df47","type":"ui_gauge","z":"3c452c5da4098e56","name":"","group":"fc89dc38.347898","order":38,"width":"5","height":"4","gtype":"gage","title":"LED13 Current Value","label":"units","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","className":"","x":860,"y":420,"wires":[]},{"id":"38d953e53e68e604","type":"ui_gauge","z":"3c452c5da4098e56","name":"","group":"fc89dc38.347898","order":38,"width":"5","height":"4","gtype":"gage","title":"LED12 Current Value","label":"units","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","className":"","x":860,"y":480,"wires":[]},{"id":"fc89dc38.347898","type":"ui_group","name":"LED show","tab":"ec4c23e9246d7836","order":1,"disp":true,"width":16,"collapse":false,"className":""},{"id":"3d43a51a.c133a2","type":"mqtt-broker","name":"broker.mqtt-dashboard.com","broker":"broker.mqtt-dashboard.com","port":"1883","clientid":"","usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"sessionExpiry":""},{"id":"ec4c23e9246d7836","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}]


Copy the following to the index.html file.

<!-- Complete project details: https://randomnerdtutorials.com/esp32-web-server-websocket-sliders/ -->

<!DOCTYPE html>
<html>
<head>
    <title>ESP IOT DASHBOARD</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/png" href="favicon.png">
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div class="topnav">
        <h1>Multiple Sliders</h1>
    </div>
    <div class="content">
        <div class="card-grid">
            <div class="card">
                <p class="card-title">Fader 1</p>
                <p class="switch">
                    <input type="range" onchange="updateSliderPWM(this)" id="slider1" min="0" max="100" step="1" value ="0" class="slider">
                </p>
                <p class="state">Brightness: <span id="sliderValue1"></span> &percnt;</p>
            </div>
            <div class="card">
                <p class="card-title"> Fader 2</p>
                <p class="switch">
                    <input type="range" onchange="updateSliderPWM(this)" id="slider2" min="0" max="100" step="1" value ="0" class="slider">
                </p>
                <p class="state">Brightness: <span id="sliderValue2"></span> &percnt;</p>
            </div>
            <div class="card">
                <p class="card-title"> Fader 3</p>
                <p class="switch">
                    <input type="range" onchange="updateSliderPWM(this)" id="slider3" min="0" max="100" step="1" value ="0" class="slider">
                </p>
                <p class="state">Brightness: <span id="sliderValue3"></span> &percnt;</p>
            </div>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

CSS File

Copy the following to the style.css file.

/*  Complete project details: https://randomnerdtutorials.com/esp32-web-server-websocket-sliders/  */

html {
    font-family: Arial, Helvetica, sans-serif;
    display: inline-block;
    text-align: center;
  }
  h1 {
    font-size: 1.8rem;
    color: white;
  }
  p {
    font-size: 1.4rem;
  }
  .topnav {
    overflow: hidden;
    background-color: #0A1128;
  }
  body {
    margin: 0;
  }
  .content {
    padding: 30px;
  }
  .card-grid {
    max-width: 700px;
    margin: 0 auto;
    display: grid;
    grid-gap: 2rem;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  }
  .card {
    background-color: white;
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
  }
  .card-title {
    font-size: 1.2rem;
    font-weight: bold;
    color: #034078
  }
  .state {
    font-size: 1.2rem;
    color:#1282A2;
  }
  .slider {
    -webkit-appearance: none;
    margin: 0 auto;
    width: 100%;
    height: 15px;
    border-radius: 10px;
    background: #FFD65C;
    outline: none;
  }
  .slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: #034078;
    cursor: pointer;
  }
  .slider::-moz-range-thumb {
    width: 30px;
    height: 30px;
    border-radius: 50% ;
    background: #034078;
    cursor: pointer;
  }
  .switch {
    padding-left: 5%;
    padding-right: 5%;
  }

JavaScript File

Copy the following to the script.js file.

// Complete project details: https://randomnerdtutorials.com/esp32-web-server-websocket-sliders/

var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
window.addEventListener('load', onload);

function onload(event) {
    initWebSocket();
}

function getValues(){
    websocket.send("getValues");
}

function initWebSocket() {
    console.log('Trying to open a WebSocket connection…');
    websocket = new WebSocket(gateway);
    websocket.onopen = onOpen;
    websocket.onclose = onClose;
    websocket.onmessage = onMessage;
}

function onOpen(event) {
    console.log('Connection opened');
    getValues();
}

function onClose(event) {
    console.log('Connection closed');
    setTimeout(initWebSocket, 2000);
}

function updateSliderPWM(element) {
    var sliderNumber = element.id.charAt(element.id.length-1);
    var sliderValue = document.getElementById(element.id).value;
    document.getElementById("sliderValue"+sliderNumber).innerHTML = sliderValue;
    console.log(sliderValue);
    websocket.send(sliderNumber+"s"+sliderValue.toString());
}

function onMessage(event) {
    console.log(event.data);
    var myObj = JSON.parse(event.data);
    var keys = Object.keys(myObj);

    for (var i = 0; i < keys.length; i++){
        var key = keys[i];
        document.getElementById(key).innerHTML = myObj[key];
        document.getElementById("slider"+ (i+1).toString()).value = myObj[key];
    }
}

沒有留言:

張貼留言

2024_09 作業3 以Node-Red 為主

 2024_09 作業3  (以Node-Red 為主  Arduino 可能需要配合修改 ) Arduino 可能需要修改的部分 1)mqtt broker  2) 主題Topic (發行 接收) 3) WIFI ssid , password const char br...