I am trying to create a simple web interface to control a NeoPixel strip from an ESP32 hosting a client over an access point. Every example I've found on the web so far demonstrates how you can toggle LED's ON/OFF by appending the state to the URL header and then using that info on the server-side to then call a function that toggles the physical LEDs, however, I'm trying to do something a little more unique.
I cannot find a way to pass the #hex value retrieved from my HTML color picker over the HTTP back to the server and use this to set the NEOPIXEL color then.
One thing I'm doing differently than many examples out there is I'm letting the server handle the client by sending the HTML page using this function, which I understand takes my index.h file which contains the HTML code and sends it over the server to the client. But I'm not sure how to "reverse" this process, especially for a "text/variable"
This is my server code:
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <Adafruit_NeoPixel.h>
//HTML code header for the web page
#include "index.h"
#define PIN 13
//Initialize the web client server
WebServer server(80);
//Initialize NeoPixels
Adafruit_NeoPixel strip = Adafruit_NeoPixel(12, PIN, NEO_GRB + NEO_KHZ800);
const char* ssid = "ESP32NeoPixelInterface";
const char* password = "password";
//temporary string to hold the HEX value of the color picker received from the web client
String header = "";
//Current time
unsigned long currentTime = millis();
//Previous time
unsigned long previousTime = 0;
//Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
//===============================================================
// This routine is executed when you open its IP in browser
//===============================================================
void handleRoot() {
String s = MAIN_page; //Read HTML contents
server.send(200, "text/html", s); //Send web page
}
void UpdateNeoPixels() {
}
void setup(void) {
Serial.begin(115200);
Serial.println();
Serial.println("Booting Sketch...");
strip.begin();
strip.setBrightness(25);
//Connect to Wi-Fi network with SSID and password
Serial.print("Setting AP (Access Point)…");
//Remove the password parameter, if you want the AP (Access Point) to be open
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
//Start server
server.on("/", handleRoot); //This is display page
server.begin();
Serial.println("HTTP server started");
//Set pixel #1 to green to show that an active access point connection has been made
strip.setPixelColor(1, 100, 0, 0);
strip.show();
}
//===============================================================
// This routine is executed when you open its IP in browser
//===============================================================
void loop(void) {
server.handleClient();
}
and this is the header file for the client:
const char MAIN_page[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<head>
<title>ESP32 NeoPixel Web Control</title>
</head>
<style>
.button {
display: inline;
padding: 10px 10px;
font-size: 2em;
cursor: pointer;
text-align: center;
text-decoration: none;
outline: none;
color: #fff;
background-color: #4CAF50;
border: none;
border-radius: 10px;
box-shadow: 0px 2px 10px -2px rgba(0,0,0,0.5);
}
.button:hover {background-color: #3e8e41}
.button:active {
background-color: #3e8e41;
box-shadow: 0px 2px 15px -2px rgba(0,0,0,0.75);
transform: translateY(2px);
}
.p1 {
font-family: "Monaco", monospace;
color: white;
font-size: 1em;
}
.container{
position: absolute;
}
.center1{
margin: auto;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.center2{
margin: auto;
position: absolute;
top: 60%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
</style>
<body bgcolor="#282c34">
<h1 class="p1">This web page is hosted remotley by an ESP32 server, change the color of the LED NeoPixels using the color picker and press "SUBMIT".</STYLE></h1>
<hr height="10px"/>
<div class="containter">
<div class="center1">
<input type="color" id="myColor" value="#ff0080">
</div>
<div class="center2">
<button class="button button1" align="center" onclick=SendColorValue()>SUBMIT</button>
</div>
</div>
<script>
function SendColorValue() {
var x = document.getElementById("myColor");
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
};
//Send a request from client to server saying "hey update the color of the NeoPixels!"
xhttp.open("POST", "ColorValue", true);
xhttp.send(x.value);
}
</script>
</body>
</html>
)=====";
I basically need to find a way to send x.value which is the color value retrieved from the color picker box back to the server and use this value to set the neopixel color.
Most examples I've seen only deal with sending "boolean" type data to the server from the client such as this:
// turns the GPIOs on and off
if (header.indexOf("GET /26/on") >= 0) {
Serial.println("GPIO 26 on");
output26State = "on";
digitalWrite(output26, HIGH);
} else if (header.indexOf("GET /26/off") >= 0) {
Serial.println("GPIO 26 off");
output26State = "off";
digitalWrite(output26, LOW);
}
For processing the POST route you send from your client you need something on the server like:
server.on("/ColorValue", HTTP_POST, []() { ... });
then you have to process the argument (the hex value)
String newColorValue = server.arg("ColorValue");
hand it over to a routine for splitting the hex and setting the Led
setLedColor(newColorValue);
and respond to the client like
server.send(200, "text/plain", "Color changed");
so the whole thing would look like:
server.on("/ColorValue", HTTP_POST, []() {
String newColorValue = server.arg("ColorValue");
setLedColor(newColorValue);
server.send(200, "text/plain", "Color changed");
});
The sub-routine setLedColor looks something like
void setLedColor (String rgbHexValue) {
char hexValue[3] = {'\0'};
uint8_t tenV;
uint8_t oneV;
// split and convert the String to uint8_t
// code -> get first two letters via substring, process and convert to uint8_t
rgbHexValue.substring(0, 2).toCharArray(hexValue, 2);
tenV = (hexValue[0] <= '9') ? hexValue[0] - '0' : hexValue[0] - '7';
oneV = (hexValue[1] <= '9') ? hexValue[1] - '0' : hexValue[1] - '7';
uint8_t greenValue = (16 * tenV) + oneV;
//pseudocode pretty much the same as above changes in the comments
uint8_t redValue = ... //pseudocode -> second two letters via rgbHexValue.substring(2, 4) and from hex to uint see above
uint8_t blueValue = ... //pseudocode -> last two letters via rgbHexValue.substring(4) and from hex to uint see above
// Set pixel #1 to new color
strip.setPixelColor(1, greenValue, redValue, blueValue); // check if correct
strip.show();
}
Comments (also for other readers): I personally do not use String class with ESP8266/ESP32 because on the long run (which is sometimes a few minutes) it defragments the memory and crashes the device. So you should learn from the start to use char arrays and char handling for stable usage so I would propose the following code:
Define a global char array before setup():
static char newColorValue[7] = {'\0'};
and getting the value from the client like
strcpy(newColorValue, webServer.arg("ColorValue").c_str());
which leads to the following route:
server.on("/ColorValue", HTTP_POST, []() {
strcpy(newColorValue, webServer.arg("ColorValue").c_str());
setLedColor(newColorValue);
server.send(200, "text/plain", "Color changed");
});
and the setLedColor is then defined as follows:
void setLedColor () { // Convert routine for rgbHex to uint values
char hexValue[3] = {'\0'}; // holds a 2 digit hex value eg A9
uint8_t tenV; // first digit value eg A
uint8_t oneV; // second digit value eg 9
// split and process the char array newColorValue
// split and convert the first two digits
strncpy(hexValue,newColorValue, 2);
tenV = (hexValue[0] <= '9') ? hexValue[0] - '0' : hexValue[0] - '7';
oneV = (hexValue[1] <= '9') ? hexValue[1] - '0' : hexValue[1] - '7';
uint8_t greenValue = (16 * tenV) + oneV;
// second two digits
strncpy(hexValue,newColorValue + 2, 2);
...
uint8_t redValue = ...
// Last two digits
strncpy(hexValue,newColorValue + 4, 2);
...
uint8_t blueValue = ...
}
Hope this solution helps your project - and remember "Strings are evil" ;-)