Neopixel

Based on the ESP8266 template i wrote a script for driving the ws2812 neopixel. I must say a small snippet of the code comes from Adafruit neopixel demo script (rainbow colours). The commands can be given by your browser. The urls for the commands you can also use for program a switch in Domoticz.

There's also a embedded timer to switch to a specific colour on a specific time. You can choose between 3 diffrent modes, Fading colours, Rainbow colours and of course a static colour from the table. A slider at the bottom acts like a dimmer.

I used in this case a Wemos D1. Because the scripts stores information it's important to take a esp8266 model with a small ammount of SPIFFS.



neopixel-version0.11.ino

// *****************************************************************
// * Wennekes' Template *
// * for ESP8266 *
// * *
// * # Wifi connected once by wps. *
// * # Hostname & port adjustable *
// * # All configurations save on spiffs. *
// * # Secured web acces based on cookie-level. *
// * # Example of svg webpage showing current time . *
// * # Update esp8266 by upload bin file without *
// * dismounting esp8266 just thru webpage. *
// * *
// * This script is free of use *
// * if you find this script useful *
// * consider to buy me a cup of coffee *
// * see http://www.wennekes.info *
// * or like my video on *
// * https://www.youtube.com/c/ReneWennekes *
// *****************************************************************


#include <Wire.h>
#include "TimeLib.h"
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <FS.h>
#include <Adafruit_NeoPixel.h>
//Neopixel
#ifdef __AVR__
#include <avr/power.h>
#endif

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1
#define PIN 0

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 240

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
//Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN);
//Adafruit_NeoPixel pixels;

String strTotalpixels = "1";
uint16_t totalpixels = 1;
uint8_t red = 0;
uint8_t grn = 0;
uint8_t blu = 0;
uint8_t bri= 255;
uint8_t newred = 0;
uint8_t newgrn = 0;
uint8_t newblu = 0;
uint8_t modus = 0;

uint8_t j;

boolean timer_enable[6];
uint8_t timer_day[6]; //Day of the week (1-7), Sunday is day 1 / 0=all days
uint8_t timer_hrs[6];
uint8_t timer_min[6];
uint8_t timer_red[6] = {16, 32, 48, 64, 80, 96};
uint8_t timer_grn[6] = {16, 32, 48, 64, 48, 32};
uint8_t timer_blu[6] = {96, 80, 64, 48, 32, 16};

uint8_t setbuttonPin = 2; // D4 of ESP8266 (Button)


// Determine version for administration, display it on a webpage and/or output device during boot.
// Use always this format: <your projectname>version<version>
// Increase version each time you update the sketch
String strFilepath =__FILE__;
String strVersion = strFilepath.substring(strFilepath.lastIndexOf("version")+7, strFilepath.lastIndexOf(".ino"));

// ********** wifi ************
String strSSID;
String strPASS;

// ********** webserver ************
String strChipID = String(ESP.getChipId(), HEX); //Unique number of each esp8266 device
String strHostname = "esp8266-" + strChipID; //default
uint16_t port = 80; //default
String strPort = String(port);
// CSS style (themes) example
//String strDefaulttheme = "";
String strDefaulttheme = \
"<style>\n" \
"body {background: #333; color: #EEE; margin: 20px; font-family: Arial;}\n" \
"a {color: #EEE; vertical-align: bottom;}\n" \
"label, input, button {vertical-align: middle; display: inline-block; width: 400px; height: 32px; margin: 2px; padding: 0px 20px; font-size: 16pt; color: #EEE; border: 0px; background: #555;}\n" \
"label {width: 200px;}\n" \
"button {background: transparent; border: 2px solid orange; width: 100px; height: 100px; border-radius: 50%; cursor: pointer;}\n" \
"div {text-decoration: none; background: #222; display: table-cell; text-align: center; margin: 4px; padding: 0px;}\n" \
"</style>\n";

String strFavicon = "";
//String strFavicon = "<link rel=\"shortcut icon\" href=\"data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABMLAAATCwAAAAAAAAAAAAAxTTH/LVAt/y9RL/8uUS//LlAu/y9RL/8uUC7/L1Av/y9QL/8uTy7/L1Av/y5PLv8uTy7/L1Av/y5PLv8xUDH/ODU4/zUxNf82NDj/NjI1/zUyNf83NDf/NTI1/zYzNv83Mzf/NTE1/zczN/82Mjb/NjI2/zczN/81MTX/ODQ4/zU2Nf8zNTX/NjIu/zM3N/80NDP/NDU0/zQ1NP80NTT/NDU0/zQ1NP80NTT/NDU0/zM0M/81NjX/NDQ0/zU2Nf8zNjf/Niwm/yNWcf8EnO7/LzxD/zQwLv8zMzT/MzMz/zMzM/8zMjL/MzMz/zMzM/8kJCT/Kysr/zQ0NP80NDT/NjIv/y87Q/8Cp/n/FHyv/zUvLP8yMzT/MzMz/zMyMv80Nzj/Lj9I/zIzNP80NDP/Kysr/yAgIP8wMDD/NTU1/zkqJP8XdKL/BJ/u/zQwL/80MTD/NTY3/zU1Nf83Nzf/OTk5/zQ2N/83Nzf/NDQ0/zQ0NP8lJSb/Jico/zU2Nv81MzH/Bpvo/xptmv83KiP/MjY5/zo7O/85Ojr/Ojo7/z4/P/86Ozv/Ozs7/zQ1Nv80NDX/Li4t/yMcGP8zMjH/L0BI/wGk+/8lU2z/Nyki/zQxMP80MC7/NTIw/zQxMP80MjH/NTAu/zM0NP80MC3/NTAu/zExMv8bPlD/LjY6/y5ET/8Apf3/KEte/y5ASP8rRlT/LENP/y4+Rv8wOkD/MDpA/yxEUf81Lyv/K0ZU/y5AS/8vO0D/Aab4/yZUbP8yOj7/AqL3/yJZdv8tQUr/Il1+/yFfgf8uQEr/KUxe/yZUbf8jWnn/LUNP/yVWcv8wPET/KUpb/wCm/f8rSln/OC0o/wyNzv8Qg77/MjY4/ydRZ/8nUGb/MzQ1/ylMXf8mU2v/Kkpc/ytIWv8nT2X/MDpB/xptmP8FnOr/NTQz/zgtJ/8jV3H/ALD//ypIWf84Jx7/MzMy/zI0Nf80MTD/NS8s/zQxMP8zMzT/Nism/zA5Pv8Bqfz/G2qT/zgrJP80NTb/NS0p/xCFv/8DpvT/LEJP/zgpIf80MC7/MzMz/zM0NP80MTD/Nyoj/zE3PP8ImN7/CZfc/zMyM/81NDP/NDU2/zMxMP8zNDX/DYzM/wCt//8abJj/LkBJ/zMzMv8zMjH/LzxC/x5hhv8Cpfb/B5rj/y88RP80Ly3/NDY2/zQ0NP8yMzT/NDEw/zQwLf8baZP/AqH2/wCm/v8Em+3/BZrr/wGk+/8BpPz/FXSn/zI1Nv80Ly3/MjQ1/zQ0NP82Njb/NDQ0/zQ1Nv80NDT/OCsk/zM3Of8mVnD/HGuV/xtsmP8jWnf/MTxB/zgrJP81MzL/NDY3/zQ0NP81Njb/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\" />";

String strCookie;

// ********** security ************
String strUser = "admin"; //default username
String strPass = "esp8266"; //default password
boolean localauth = false; //default authentification for external network only, set to true to add internal network

//Set to European (if other region disable or adjust settings in adjustDstEurope() function in case summer/wintertime)
uint16_t timeZone = 7200;

String strTime;

unsigned int localPort = 2390; // local port to listen for UDP packets

/* Don't hardwire the IP address or we won't get the benefits of the pool.
* Lookup the IP address for the host name instead */
//IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";

const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;

//Webserver
ESP8266WebServer server(port);

//WiFiClient client
HTTPClient http;


void setup() {
Serial.begin(9600);

// initialize the pushbutton pins as an input:
pinMode(setbuttonPin, INPUT);

//Display firmware version
//Consider display version briefly on output device during startup
//and one or more webpages.
Serial.println("Firmware Version: " + strVersion);

//Display Chip ID
Serial.println("Chip ID: " + strChipID);
//Serial.println(ESP.getChipId());

//Initialize File System
if(SPIFFS.begin()){
Serial.println("SPIFFS Initialize....ok");
} else{
Serial.println("SPIFFS Initialization...failed");
}

//Display directory of filesystem
Dir dir = SPIFFS.openDir("");
while (dir.next()) {
Serial.print(dir.fileName());
File f = dir.openFile("r");
Serial.print("\t");
Serial.println(f.size());
}

//Press the button to erase config files
//if (digitalRead(setbuttonPin) == HIGH) {factory_reset();}


//Restore webserver configuration from config.txt
//------------------------------------------------------------------------
if (SPIFFS.exists("/config.txt")) {
File f = SPIFFS.open("/config.txt", "r");
strHostname = f.readStringUntil('\n');
strPort = f.readStringUntil('\n');
strUser = f.readStringUntil('\n');
strPass = f.readStringUntil('\n');
strTotalpixels = f.readStringUntil('\n');
//totalpixels = atoi(f.readStringUntil('\n').c_str ());
//strTotalpixels = String(totalpixels, DEC);
strHostname.trim();
strPort.trim();
strUser.trim();
strPass.trim();
strTotalpixels.trim();
Serial.println("Hostname=" + strHostname);
Serial.println("Port=" + strPort);
Serial.println("User=" + strUser);
Serial.println("Pass=" + strPass);
Serial.println("Total pixels=" + strTotalpixels);
f.close();
Serial.println("Data read from config.txt.");
totalpixels = strTotalpixels.toInt();
//totalpixels = 8;
}

//totalpixels = 24;
//totalpixels = strTotalpixels.toInt();
//Serial.println(totalpixels);
//pixels = Adafruit_NeoPixel(totalpixels, PIN);
pixels.begin(); // This initializes the NeoPixel library.


//Restore time table arrays from file in spiffs if exist
//------------------------------------------------------------------------
if (SPIFFS.exists("/timer.txt")) {
File f = SPIFFS.open("/timer.txt", "r");
for (int i=0; i< 6; i++) {
timer_enable[i] = atoi(f.readStringUntil('\n').c_str ());
timer_day[i] = atoi(f.readStringUntil('\n').c_str ());
timer_hrs[i] = atoi(f.readStringUntil('\n').c_str ());
timer_min[i] = atoi(f.readStringUntil('\n').c_str ());
timer_red[i] = atoi(f.readStringUntil('\n').c_str ());
timer_grn[i] = atoi(f.readStringUntil('\n').c_str ());
timer_blu[i] = atoi(f.readStringUntil('\n').c_str ());
}
f.close();
Serial.println("Data read from timer.txt.");
}


//Restore SSID & WPA2 password from wifi.txt
//------------------------------------------------------------------------
if (SPIFFS.exists("/wifi.txt")) {
File f = SPIFFS.open("/wifi.txt", "r");
strSSID = f.readStringUntil('\n');
strPASS = f.readStringUntil('\n');
strSSID.trim();
strPASS.trim();
Serial.println("SSID=" + strSSID);
Serial.println("PASS=" + strPASS);
f.close();
Serial.println("Data read from wifi.txt.");

// ********************** Wifi ***********************
//Convert String to char

char charSSID[strSSID.length()];
char charPASS[strPASS.length()];
strSSID.toCharArray(charSSID, strSSID.length()+1);
strPASS.toCharArray(charPASS, strPASS.length()+1);
WiFi.hostname(strHostname);
WiFi.begin(charSSID, charPASS);
WiFi.mode(WIFI_STA);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
//if (digitalRead(setbuttonPin) == HIGH) {factory_reset();}
}
Serial.println("");
Serial.println("Connecting to:");
Serial.println(strSSID);
Serial.println(strPASS);
Serial.println("WiFi connected");

} else {

//-----------------Retrieve credentials by wps----------------------------
//------------------------------------------------------------------------
Serial.println("wifi.txt not exist.");
Serial.println("Let's make a wps connection now.");
Serial.println("Press the WPS button on your router now ...");
if (startWPSPBC()) {
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Connecting to:");
strSSID = WiFi.SSID();
strPASS = WiFi.psk();
Serial.println("SSID retrieved by wps="+strSSID);
Serial.println("PASS retrieved by wps="+strPASS);
Serial.println("WiFi connected");
//-----------------Store SSID & WPA2 password in wifi.txt----------------------
//-----------------------------------------------------------------------------
File f = SPIFFS.open("/wifi.txt", "w");
f.println(strSSID);
f.println(strPASS);
f.close();
Serial.println("Data written to wifi.txt.");

} else {

//WPS failed
Serial.println();
Serial.println("WPS failed ...");
}
}


server.on ( "/", showMain );
server.on ( "/program", showProgram );
server.on ( "/config", showConfig );
server.on ( "/login", showLogin );

//********* webform firmware upload ************************
server.on("/update", HTTP_GET, [](){
Serial.print(strTime);
Serial.print(" /update\trequested by ");
Serial.print(server.client().remoteIP());
Serial.print("\t");
if (!is_authentified()){
Serial.println("not authentified, redirect to login");
server.sendHeader("Location","/login");
server.sendHeader("Cache-Control","no-cache");
server.send(301);
return;
}
Serial.println("authentified");
server.sendHeader("Connection", "close");
String strHTML;
strHTML += "<!DOCTYPE HTML>\n<html>\n<head>\n";
strHTML += "<title>" + strHostname + "</title>\n";
strHTML += strDefaulttheme + "\n"; //Inline css
strHTML += strFavicon + "\n"; //Favicon
strHTML += "</head>\n";
strHTML += "<body>\n";
strHTML += "<form method='POST' action='/flash' enctype='multipart/form-data'>\n";
strHTML += "<center>\n";
strHTML += "<input type='file' name='update'><br />\n";
strHTML += "Current firmware: " + strVersion + "<br /><br />\n";
//strHTML += "<input type='submit' value='Update'>\n";
strHTML += "<button type='submit' name='Update'>Update</button><br /><br />\n";
strHTML += "</center>\n";
strHTML += "</form>\n";
strHTML += "<center>\n";
strHTML += "<a href=\"/\">Back to panel</a>\n";
strHTML += "</center>\n";
strHTML += "</body>\n</html>";
server.send(200, "text/html", strHTML);
});

//********* upload the flash file ***************************
server.on("/flash", HTTP_POST, [](){
server.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
ESP.restart();
},[](){
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START){
Serial.setDebugOutput(true);
WiFiUDP::stopAll();
Serial.printf("Update: %s\n", upload.filename.c_str());
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if(!Update.begin(maxSketchSpace)){//start with max available size
Update.printError(Serial);
}
} else if(upload.status == UPLOAD_FILE_WRITE){
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
Update.printError(Serial);
}
} else if(upload.status == UPLOAD_FILE_END){
if(Update.end(true)){ //true to set the size to the current progress
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); //
} else {
Update.printError(Serial);
}
Serial.setDebugOutput(false);
}
//******************************************************************************************
yield();
});

server.on ( "/inline", []() {
server.send ( 200, "text/plain", "this works as well" );
} );

//server.onNotFound ( showNotFound );

//!!!!!!!These lines are important to work with cookies !!!!!!!!!!
//here the list of headers to be recorded
const char * headerkeys[] = {"User-Agent","Cookie"} ;
size_t headerkeyssize = sizeof(headerkeys)/sizeof(char*);
//ask server to track these headers
server.collectHeaders(headerkeys, headerkeyssize );


char host[strHostname.length()];
strHostname.toCharArray(host, strHostname.length()+1);
port = strPort.toInt();

// Start the server
Serial.println("Starting webserver");
MDNS.begin(host);
server.begin();
MDNS.addService("http", "tcp", port);
Serial.print("http://");
Serial.print(WiFi.localIP()); //Consider to display ip on output device.
Serial.print(":");
Serial.println(port); //Consider to display ip on output device.
Serial.println(WiFi.subnetMask()); //Consider to display ip on output device.
Serial.println(WiFi.gatewayIP()); //Consider to display ip on output device.

Serial.println("Starting UDP");
udp.begin(localPort);
Serial.print("Local port: ");
Serial.println(udp.localPort());

//Clear all leds.
// For a set of NeoPixels the first NeoPixel is 0, second is 1, all the way up to the count of pixels minus one.
for(int i=0;i<NUMPIXELS;i++){
// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
pixels.setPixelColor(i, pixels.Color(red,grn,blu)); // Moderately bright green color.
}
pixels.show(); // This sends the updated pixel color to the hardware.


} //EOF setup


void showMain() {
Serial.print(strTime);
Serial.print(" /\t\trequested by ");
Serial.print(server.client().remoteIP());
Serial.print("\t");

if (server.hasArg("red") or server.hasArg("grn") or server.hasArg("blu")){
newred = server.arg("red").toInt();
newgrn = server.arg("grn").toInt();
newblu = server.arg("blu").toInt();
Serial.print("Red="); Serial.print(newred);
Serial.print("\tGreen="); Serial.print(newgrn);
Serial.print("\tBlue="); Serial.print(newblu);
modus = 0;
} //eof serverarg

if (server.hasArg("modus")){
modus = server.arg("modus").toInt();
Serial.print("\tModus="); Serial.print(modus);
} //eof serverarg

if (server.hasArg("bri")){
bri = server.arg("bri").toInt();
Serial.print("\tBrightness="); Serial.print(bri);
pixels.setBrightness(bri);
pixels.show();
} //eof serverarg

if (is_local()) {Serial.println("local ip adres");}
if (!is_authentified() && !is_local()){
//if (!is_authentified() && !is_local() && !localauth ){
Serial.println("not authentified, redirect to login");
server.sendHeader("Location","/login");
server.sendHeader("Cache-Control","no-cache");
server.send(301);
return;
}
Serial.print("\t");
Serial.println("authentified");

String strHTML;
strHTML += "<!DOCTYPE HTML>\n<html>\n<head>\n";
strHTML += "<title>" + strHostname + "</title>\n";
strHTML += strDefaulttheme + "\n"; //Inline css
strHTML += "</head>\n";
//strHTML += "<body onload=\"JavaScript:setTimeout(\'window.location = window.location.pathname;',60000);\">\n";
strHTML += "<body>\n";
strHTML += "<center>\n";
//strHTML += "<svg style=\"background: #333333; width: 100%; height: 100%; position:fixed; top:0; bottom:0; left:0; right:0; border: 5px red;\" viewBox=\"0 0 600 600\">\n";
//strHTML += "<circle cx=\"300\" cy=\"300\" r=\"250\" fill=\"none\" stroke= \"orange\" stroke-width=\"30\" />\n";
//strHTML += "<text x=\"300\" y=\"300\" text-anchor=\"middle\" dominant-baseline=\"middle\" fill=\"orange\" font-family=\"Arial\" font-size=\"150\" stroke-width=\"1px\">"+strTime.substring(0, 5)+"</text>\n";
//strHTML += "<a href= \"login\"><text x=\"0\" y=\"550\" text-anchor=\"left\" fill= \"orange\" font-family=\"Arial\" font-size=\"12\" stroke-width=\"1px\">Logout</text></a>\n";
//strHTML += "<a href= \"config\"><text x=\"600\" y=\"550\" text-anchor=\"end\" fill= \"orange\" font-family=\"Arial\" font-size=\"12\" stroke-width=\"1px\">Settings</text></a>\n";
//strHTML += "<a href= \"update\"><text x=\"600\" y=\"570\" text-anchor=\"end\" fill= \"orange\" font-family=\"Arial\" font-size=\"12\" stroke-width=\"1px\">v"+strVersion+"</text></a>\n";
//strHTML += "</svg>\n";

for(uint16_t r=0;r<256;r=r+85){
for(uint16_t g=0;g<256;g=g+85){
for(uint16_t b=0;b<256;b=b+85){
//strHTML += "<a style= \"text-decoration: none; color: black;\" href=\"?red="+String(r, DEC)+"&grn="+String(g, DEC)+"&blu="+String(b, DEC)+"\"> <div style=\"border: 0px solid red; background: rgb("+String(r)+","+String(g)+","+String(b)+"); margin: 4px; padding: 0px; width: 80px; height: 80px; display: table-cell; vertical-align: middle; text-align: center; color: white;\">"+String(r, HEX)+","+String(g, HEX)+","+String(b, HEX)+"</div></a>\n";
strHTML += "<a href=\"?red="+String(r, DEC)+"&grn="+String(g, DEC)+"&blu="+String(b, DEC)+"\"><div style=\"background: rgb("+String(r)+","+String(g)+","+String(b)+"); width: 40px; height: 40px;\"></div></a>\n";
}
//strHTML += "<br /><br />\n";
}
strHTML += "<br />\n";
}

strHTML += "<br />\n";
strHTML += "<a href=\"?modus=2\"><div style=\"vertical-align: middle; width: 80px; height: 80px;\">Fading colours</div></a>\n";
strHTML += "<a href=\"?modus=3\"><div style=\"vertical-align: middle; width: 80px; height: 80px;\">Rainbow colours</div></a>\n";
strHTML += "<a href=\"program\"><div style=\"vertical-align: middle; width: 80px; height: 80px;\">Program table</div></a>\n";
strHTML += "<a href=\"config\"><div style=\"vertical-align: middle; width: 80px; height: 80px;\">Settings</div></a>\n";
strHTML += "<a href=\"update\"><div style=\"vertical-align: middle; width: 80px; height: 80px;\">Firmware v"+strVersion+"</div></a>\n";
strHTML += "<a href=\"login\"><div style=\"vertical-align: middle; width: 80px; height: 80px;\">Logout</div></a>\n";

strHTML += "<br /><br />\n";
strHTML += "\n";
strHTML += "<script>function updateSliderValue(val) {document.getElementById('sliderValue').innerHTML=val;}</script>\n";
strHTML += "<form action=\"/\" method=\"GET\">\n";
strHTML += "<input type=\"range\" style=\"border-radius: 5px; width: 300px; height: 10px; border: 0px solid red; background: orange; transform: rotate(0deg);\" name=\"bri\" min=\"0\" max=\"255\" value=\"" + String(bri) + "\" oninput=\"updateSliderValue(this.value);\" onchange=\"this.form.submit()\" />\n";
strHTML += "<br /><br />\n";
strHTML += "<span id=\"sliderValue\" style=\"font-size: 48px;\">" + String(bri) + "</span>\n";
strHTML += "</form>\n";
strHTML += "\n";

strHTML += "</center>\n";
strHTML += "</body>\n</html>";
server.send ( 200, "text/html", strHTML );
}

void showProgram() {
Serial.print(strTime);
Serial.print(" /program\trequested by ");
Serial.print(server.client().remoteIP());
Serial.print("\t");
if (!is_authentified()){
Serial.println("not authentified, redirect to login");
server.sendHeader("Location","/login");
server.sendHeader("Cache-Control","no-cache");
server.send(301);
return;
}
Serial.println("authentified");

if (server.hasArg("timer_day0")){
for (int i=0; i< 6; i++) {
//timer_enable[i] = server.arg("timer_enable"+String(i, DEC)).toInt();
if (server.arg("timer_enable"+String(i, DEC)) == "1") {timer_enable[i] = 1;} else {timer_enable[i] = 0;}
//Serial.print(server.arg("timer_enable"+String(i, DEC)));Serial.print("\t");
timer_day[i] = server.arg("timer_day"+String(i, DEC)).toInt();
timer_hrs[i] = server.arg("timer_hrs"+String(i, DEC)).toInt();
timer_min[i] = server.arg("timer_min"+String(i, DEC)).toInt();

//String strTimer_color = server.arg("timer_color"+String(i, DEC)); //#ff00ff
//Serial.print(strTimer_color); Serial.print("\t");

String strRed = server.arg("timer_color"+String(i, DEC)).substring(1,3); //Serial.print(strRed + "\t");
String strGrn = server.arg("timer_color"+String(i, DEC)).substring(3,5); //Serial.print(strGrn + "\t");
String strBlu = server.arg("timer_color"+String(i, DEC)).substring(5,7); //Serial.print(strBlu + "\t");
char charRed[2]; strRed.toCharArray(charRed, 3); //Serial.print(charRed); Serial.print("\t");
char charGrn[2]; strGrn.toCharArray(charGrn, 3); //Serial.print(charGrn); Serial.print("\t");
char charBlu[2]; strBlu.toCharArray(charBlu, 3); //Serial.print(charBlu); Serial.print("\t");

timer_red[i] = strtoul(charRed, NULL, 16);
timer_grn[i] = strtoul(charGrn, NULL, 16);
timer_blu[i] = strtoul(charBlu, NULL, 16);

//timer_red[i] = server.arg("timer_color"+String(i, DEC)).substring(1,3).toInt();
//timer_grn[i] = server.arg("timer_color"+String(i, DEC)).substring(3,5).toInt();
//timer_blu[i] = server.arg("timer_color"+String(i, DEC)).substring(5,7).toInt();

Serial.print(timer_enable[i]); Serial.print("\t");
Serial.print(timer_day[i]); Serial.print("\t");
Serial.print(timer_hrs[i]); Serial.print("\t");
Serial.print(timer_min[i]); Serial.print("\t");
Serial.print(timer_red[i]); Serial.print("\t");
Serial.print(timer_grn[i]); Serial.print("\t");
Serial.print(timer_blu[i]); Serial.print("\t");
Serial.println();
}
//Store time table arrays to file in spiffs on 23.59:40
//------------------------------------------------------------------------
File f = SPIFFS.open("/timer.txt", "w");
for (int i=0; i< 6; i++) {
f.println(timer_enable[i]);
f.println(timer_day[i]);
f.println(timer_hrs[i]);
f.println(timer_min[i]);
f.println(timer_red[i]);
f.println(timer_grn[i]);
f.println(timer_blu[i]);
}
f.close();
Serial.println("Data written to timer.txt.");
}

String strHTML;
strHTML += "<!DOCTYPE HTML>\n<html>\n<head>\n";
strHTML += "<title>" + strHostname + "</title>\n";
//strHTML += strDefaulttheme + "\n"; //Inline css
strHTML += "<style>label, input {display: inline-block; width: 12%; margin: 2px; color: #EEE; border: 0px; background: #444;} input {background: sienna;} div {color: #EEE; display: inline-block; border-radius: 50%;}</style>\n";
strHTML += strFavicon + "\n"; //Favicon
strHTML += "</head>\n";
strHTML += "<body style=\"background: #333; color: #EEE; margin: 20px; font-family: Arial;\">\n";

strHTML += "<br /><br />\n";
strHTML += "<center>\n";

//strHTML += "<form action=\"\" method=\"get\">\n";
strHTML += "<form action=\"\" method=\"POST\">\n";
strHTML += "<fieldset style=\"width: 80%;\">\n";
strHTML += "<legend>Automated Task Table</legend>\n";

strHTML += "<label>Task</label>\n";
strHTML += "<label>Enabled</label>\n";
strHTML += "<label>Day<sup>1</sup></label>\n";
strHTML += "<label>Hour</label>\n";
strHTML += "<label>Mins</label>\n";
strHTML += "<label>Color</label>\n";
strHTML += "<br />\n";

for (int i=0; i< 6; i++) {
strHTML += "<label>"+String(i, DEC)+"</label>\n";
//strHTML += "<input type=\"text\" name=\"timer_enable"+String(i, DEC)+"\" value=\"" + timer_enable[i] + "\" />\n";
strHTML += "<input type=\"checkbox\" name=\"timer_enable"+String(i, DEC)+"\" value=\"1\" ";
if (timer_enable[i] == 1) {strHTML += "checked=\"checked\" ";}
strHTML += "/>\n";
strHTML += "<input type=\"number\" min=\"0\" max=\"7\" name=\"timer_day"+String(i, DEC)+"\" value=\"" + timer_day[i] + "\" />\n";
strHTML += "<input type=\"number\" min=\"0\" max=\"23\" name=\"timer_hrs"+String(i, DEC)+"\" value=\"" + timer_hrs[i] + "\" />\n";
strHTML += "<input type=\"number\" min=\"0\" max=\"59\" name=\"timer_min"+String(i, DEC)+"\" value=\"" + timer_min[i] + "\" />\n";
strHTML += "<input type=\"color\" style=\"background: #333; vertical-align: top; padding: 0px; height: 20px;\" name=\"timer_color"+String(i, DEC)+"\" value=\"#" + String(timer_red[i], HEX) + String(timer_grn[i], HEX) + String(timer_blu[i], HEX) + "\" />\n";
strHTML += "<br />\n";
}

strHTML += "</fieldset><br /> <sup>1</sup>0=Every day, 1=Sunday, 2=Monday, 3=Tuesday, 4=Wednesday, 5=Thursday, 6=Friday, 7=Saturday.<br />\n";
strHTML += "<br /><br />\n";
strHTML += "<input type=\"submit\" value=\"Save\" style=\"background: transparent; border: 2px solid orange; width: 100px; height: 100px; border-radius: 50%; cursor: pointer;\" />\n";
strHTML += "</form>\n";
strHTML += "<br />\n";
strHTML += "<a href=\"config\" style=\"color: #EEE;\">Settings</a>\n";
strHTML += "<br /><br />\n";
strHTML += "<a href=\"/\" style=\"color: #EEE;\">Back to panel</a>\n";
strHTML += "</center>\n";

strHTML += "</body>\n</html>";
server.send ( 200, "text/html", strHTML );
}

void showConfig() {
Serial.print(strTime);
Serial.print(" /config\trequested by ");
Serial.print(server.client().remoteIP());
Serial.print("\t");
if (!is_authentified()){
Serial.println("not authentified, redirect to login");
server.sendHeader("Location","/login");
server.sendHeader("Cache-Control","no-cache");
server.send(301);
return;
}
Serial.println("authentified");

if (server.hasArg("newhostname") or server.hasArg("newport") or server.hasArg("newusername") or server.hasArg("newtotalpixels")){
strHostname = server.arg("newhostname");
strPort = server.arg("newport");
strUser = server.arg("newusername");
if (server.arg("newpassword") != "") {
strPass = server.arg("newpassword"); //if empty keep old password !!!
} else {
Serial.println("Password not changed.");
}
strTotalpixels = server.arg("newtotalpixels");
//totalpixels = strTotalpixels.toInt();
Serial.println("New hostname="+strHostname);
Serial.println("New port="+strPort);
Serial.println("New username="+strUser);
Serial.println("New password="+strPass);
Serial.println("New totalpixels="+strTotalpixels);
//Store configuration in config.txt
//------------------------------------------------------------------------
File f = SPIFFS.open("/config.txt", "w");
f.println(strHostname);
f.println(strPort);
f.println(strUser);
f.println(strPass);
f.println(strTotalpixels);
//f.println(totalpixels); //Store bytes
//f.println(">End of file<");
f.close();
Serial.println("Data written to config.txt.");
}
String strHTML;
strHTML += "<!DOCTYPE HTML>\n<html>\n<head>\n";
//strHTML += "<html>\n";
//strHTML += "<head>\n";
strHTML += "<title>" + strHostname + "</title>\n";
strHTML += strDefaulttheme + "\n"; //Inline css
strHTML += "</head>\n";
strHTML += "<body>\n";
strHTML += "<form action='/config' method='POST'>\n";
strHTML += "<center>\n";
strHTML += "<label>Hostname</label><input type='text' name='newhostname' value=\"" + strHostname + "\" /><br />\n";
strHTML += "<label>Port</label><input type='text' name='newport' value=\"" + strPort + "\" /><br />\n";
strHTML += "<label>Username</label><input type='text' name='newusername' value=\"" + strUser + "\" /><br />\n";
strHTML += "<label>Password</label><input type='password' name='newpassword' value=\"\" /><br />\n";
strHTML += "<br />\n";
strHTML += "<label>Total pixels</label><input type='text' name='newtotalpixels' value=\"" + strTotalpixels + "\" /><br />\n";
strHTML += "<span style=\"font-size: 10pt;\">*** The Hostname, Port & Total Pixels only applied after restart. ***</span><br /><br />\n";
strHTML += "<button type='submit' name='submit'>Modify</button><br /><br />\n";
strHTML += "</center>\n";
strHTML += "</form>\n";
strHTML += "<center>\n";
strHTML += "<a href=\"/\">Back to panel</a>\n";
strHTML += "</center>\n";
strHTML += "</body>\n</html>";
//strHTML += "</html>";
server.send ( 200, "text/html", strHTML );
}

void showLogin() {
Serial.print(strTime);
Serial.print(" /login\trequested by ");
Serial.print(server.client().remoteIP());
Serial.print("\t");
String strHTML;
strHTML += "<!DOCTYPE HTML>\n<html>\n<head>\n";
strHTML += "<title>" + strHostname + "</title>\n";
strHTML += strDefaulttheme + "\n"; //Inline css
strHTML += "</head>\n";
strHTML += "<body>\n";
//**** Login ****
if (server.hasArg("login")){
if (server.arg("user") == strUser && server.arg("pass") == strPass){
server.sendHeader("Location","/login");
server.sendHeader("Cache-Control","no-cache");
server.sendHeader("Set-Cookie","authlevel=1"); //Set browsercookie to 1
server.send(301);
Serial.print("Login succeeded ");
strHTML += "<center>\n";
strHTML += "Username and Password are correct<br /><br />\n";
strHTML += "</center>\n";
} else {
Serial.print("Login failed, try again ");
strHTML += "<center>\n";
strHTML += "Wrong username or password<br /><br />\n";
strHTML += "</center>\n";
}
}
//**** Logout ****
if (server.hasArg("logout")){
server.sendHeader("Location","/login");
server.sendHeader("Cache-Control","no-cache");
server.sendHeader("Set-Cookie","authlevel=0"); //Set browsercookie to 0
server.send(301);
Serial.print("logged out succesfully ");
}
if (!is_authentified()){
Serial.println("not authentified");
//********** login form & button ************
strHTML += "<form action='/login' method='POST'>\n";
strHTML += "<center>\n";
strHTML += "<input type='text' name='user' placeholder='User name' /><br />\n";
strHTML += "<input type='password' name='pass' placeholder='Password' /><br /><br />\n";
strHTML += "<button type='submit' name='login'>login</button>\n";
strHTML += "</center>\n";
strHTML += "</form>\n";
} else {
Serial.println("authentified");
//*********** logout button *************
strHTML += "<form action='/login' method='POST'>\n";
strHTML += "<center>\n";
strHTML += "You are succesfully logged in<br /><br />\n";
strHTML += "<a href=\"/\">Main panel</a><br /><br />\n";
strHTML += "<a href=\"config\">Settings</a><br /><br />\n";
strHTML += "<button type='submit' name='logout'>logout</button>\n";
strHTML += "</center>\n";
strHTML += "</form>\n";
}

strHTML += "</body>\n</html>";
server.send ( 200, "text/html", strHTML );
}


void loop() {
//sync once a hour on xx:00:25
if (minute() == 0 && second() == 25) {
long t = ntpSync();
if (adjustDstEurope(t)) {timeZone = 7200;} else {timeZone = 3600;}
if (t) {setTime(t+timeZone);}
//Serial.println(t);
Serial.println("Synced now.");
}

unsigned long Hour = hour();
unsigned long Minute = minute();
unsigned long Second = second();
unsigned long Year = year();
unsigned long Month = month();
unsigned long Day = day();
unsigned long Dayofweek = weekday();

int hour1 = (Hour%10);
int hour10 = (Hour/10);
int minute1 = (Minute%10);
int minute10 = (Minute/10);
int second1 = (Second%10);
int second10 = (Second/10);
int year1 = (Year%10);
int year10 = ((Year/10)%10);
int year100 = ((Year/100)%10);
int year1000 = (Year/1000);
int month1 = (Month%10);
int month10 = (Month/10);
int day1 = (Day%10);
int day10 = (Day/10);

strTime = String(hour10, DEC)+String(hour1, DEC)+":"+String(minute10, DEC)+String(minute1, DEC)+":"+String(second10, DEC)+String(second1, DEC); //for serial output log

//if modus = 1 random colours.
//----------------------------
if (modus == 1 && second1 == 0) {
newred = random(0, 255);
newgrn = random(0, 255);
newblu = random(0, 255);
Serial.print("Red="); Serial.print(newred);
Serial.print("\tGreen="); Serial.print(newgrn);
Serial.print("\tBlue="); Serial.println(newblu);
delay(1000);
}

//pixels.setBrightness(bri);

//if modus = 2 fading rainbow colours.
//-----------------------------
if (modus == 2) {
//uint8_t j;
if (j < 255) {j = j + 1;} else {j = 0;}
for(int i=0; i<totalpixels; i++) {pixels.setPixelColor(i, Wheel((i+j) & 255));}
pixels.show();
//delay(100);
}

//if modus = 3 rainbow colours cycling.
//-------------------------------------
if (modus == 3) {
//uint8_t j;
if (j < 255) {j = j + 1;} else {j = 0;}
for(int i=0; i<totalpixels; i++) {pixels.setPixelColor(i, Wheel(((i * 256 / totalpixels) + j) & 255));}
pixels.show();
//delay(100);
}


//Timer
for (int i=0; i< 6; i++) {
if (timer_enable[i] && (timer_day[i] == Dayofweek or timer_day[i] == 0) && timer_hrs[i] == Hour && timer_min[i] == Minute && Second == 0) {
newred= timer_red[i];
newgrn= timer_grn[i];
newblu= timer_blu[i];
modus = 0;
Serial.println(strTime+"\t\tTask nr. " + String(i, DEC) + " set to new color.");
delay(1000);
}
}

//Slowly transition to new color each step during loop
//====================================================
////while (newred != red or newgrn != grn or newblu != blu) {
if (newred != red or newgrn != grn or newblu != blu) {
red = colortransit(newred, red);
grn = colortransit(newgrn, grn);
blu = colortransit(newblu, blu);

// For a set of NeoPixels the first NeoPixel is 0, second is 1, all the way up to the count of pixels minus one.
for(int i=0;i<totalpixels;i++){
// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
pixels.setPixelColor(i, pixels.Color(red,grn,blu)); // Moderately bright green color.
//Serial.print(".");
}
pixels.show(); // This sends the updated pixel color to the hardware.
//// delay(25);
} //eof while / if


//Webserver
//-----------------------------------------------------------------
// Check if a client has connected
server.handleClient();


delay(100);
} //EOF loop


uint8_t colortransit(uint8_t newval, uint8_t oldval)
{
if (newval < oldval) {oldval = oldval -1;}
if (newval > oldval) {oldval = oldval +1;}
return oldval;
}


int ntpSync()
{
//get a random server from the pool
WiFi.hostByName(ntpServerName, timeServerIP);

sendNTPpacket(timeServerIP); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);

int cb = udp.parsePacket();
if (cb) {
//Serial.print("packet received, length=");
//Serial.println(cb);
// We've received a packet, read the data from it
udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:

unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
//Serial.print("Seconds since Jan 1 1900 = " );
//Serial.println(secsSince1900);

// now convert NTP time into everyday time:
//Serial.print("Unix time = ");
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
// print Unix time:
//Serial.println(epoch);
return epoch;
} else {
//Serial.println("no packet yet");
return false;
}
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
//Serial.println("sending NTP packet...");
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;

// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
udp.beginPacket(address, 123); //NTP requests are to port 123
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
}


long adjustDstEurope(long epoch)
{
// last sunday of march
int beginDSTDate= (31 - (5* year(epoch) /4 + 4) % 7);
int beginDSTMonth=3;
//last sunday of october
int endDSTDate= (31 - (5 * year(epoch) /4 + 1) % 7);
int endDSTMonth=10;
// DST is valid as:
if (((month(epoch) > beginDSTMonth) && (month(epoch) < endDSTMonth))
|| ((month(epoch) == beginDSTMonth) && (day(epoch) >= beginDSTDate))
|| ((month(epoch) == endDSTMonth) && (day(epoch) < endDSTDate)))
return true; // DST europe = GMT +2
else return false; // nonDST europe = GMT +1
}


bool startWPSPBC() {
// from https://gist.github.com/copa2/fcc718c6549721c210d614a325271389
// wpstest.ino
//Serial.println("WPS config start");
WiFi.mode(WIFI_STA);
bool wpsSuccess = WiFi.beginWPSConfig();
String newSSID = WiFi.SSID();
String newPASS = WiFi.psk();
if (wpsSuccess) {
if(newSSID.length() > 0) {return wpsSuccess;} else {wpsSuccess = false;}
}
return wpsSuccess;
} // EOF startWPSPBC()


String httprequest(String urlrequest) {
String payload = "";
http.begin(urlrequest); //Specify request destination
int httpCode = http.GET(); //Send the request
//Serial.println(httpCode);

if (httpCode > 0) { //Check the returning code
payload = http.getString(); //Get the request response payload
//Serial.println(payload); //Print the response payload
}
http.end(); //Close connection
return payload;
} // EOF httprequest()


void factory_reset() {
Serial.println("Factory reset start in 5 seconds ...");
for (int i = 0; i < 5; i++) {
Serial.print("*");
//display output device
delay(1000);
}
Serial.println();
//***** Actually clearing *****
if (SPIFFS.exists("/config.txt")) {SPIFFS.remove("/config.txt");}
if (SPIFFS.exists("/wifi.txt")) {SPIFFS.remove("/wifi.txt");}

while (digitalRead(setbuttonPin) == HIGH) {
//Do nothing until button is released.
}
delay(5000);
} // EOF factory_reset()


bool is_authentified() {
if (server.hasHeader("Cookie")){
//Serial.print("Found cookie: ");
strCookie = server.header("Cookie");
//Serial.println(strCookie);
if (strCookie.indexOf("authlevel=1") != -1) {return true;} else {return false;}
}
} // EOF is_authentified()


bool is_local() {
Serial.println("is_local test");
Serial.println(WiFi.localIP());
Serial.println(WiFi.subnetMask());
Serial.println(WiFi.gatewayIP());
//uint8_t ip0 = WiFi.localIP()[0];
//uint8_t ip1 = WiFi.localIP()[1];
//uint8_t ip2 = WiFi.localIP()[2];
//uint8_t ip3 = WiFi.localIP()[3];
Serial.println(server.client().remoteIP());

if (WiFi.localIP()[0] != server.client().remoteIP()[0]) {return false;}
if (WiFi.subnetMask()[1] != 0) {
if (WiFi.localIP()[1] != server.client().remoteIP()[1]) {return false;}
if (WiFi.subnetMask()[2] != 0) {
if (WiFi.localIP()[2] != server.client().remoteIP()[2]) {return false;}
}
}

return true;
} // EOF is_local()


// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}


Buy me a cup of coffee

If you like this project, keep in mind that a huge amount of coffee was consumed in the process of making this script. Please donate a cup of coffee if you want me to keep going.