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.
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> %</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> %</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> %</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];
}
}
沒有留言:
張貼留言