Added HTTP OTA updater for ESP32

Added Easter Egg
Improved ArduinoOTA stability on ESP8266
Added function that starts the AP if you press the button for 7 secs
Added yet unused methods for color conversion
This commit is contained in:
cschwinne 2018-01-15 00:20:23 +01:00
parent 3db38726ca
commit e97a739f03
8 changed files with 233 additions and 19 deletions

View File

@ -15,7 +15,7 @@ Now also with experimental ESP32 support.
- Notifier function (multiple ESPs sync color via UDP broadcast) - Notifier function (multiple ESPs sync color via UDP broadcast)
- Support for power pushbutton - Support for power pushbutton
- Custom Theater Chase - Custom Theater Chase
- Full OTA software update capability (only ESP8266) - Full OTA software update capability (HTTP and ArduinoOTA)
- Password protected OTA page for added security (OTA lock) - Password protected OTA page for added security (OTA lock)
- Alexa smart home device server (including dimming) - Alexa smart home device server (including dimming)
- NTP and experimental analog clock function - NTP and experimental analog clock function

View File

@ -0,0 +1,104 @@
#include <Arduino.h>
#include <WiFiClient.h>
#include <WiFiServer.h>
#ifdef ARDUINO_ARCH_ESP32
#include "WebServer.h"
#include <Update.h>
#else
#include <ESP8266WebServer.h>
#endif
#include <WiFiUdp.h>
#include "ESP8266HTTPUpdateServer.h"
const char* ESP8266HTTPUpdateServer::_serverIndex =
R"(<html><body><h2>WLED Software Update</h2><br>Get the latest binaries on the <a href="https://github.com/Aircoookie/WLED/tree/master/bin">project GitHub page</a>!<br>
<i>Unsure which binary is correct? Go to the <a href="./build">/build subpage</a> to find the details of this version.</i><br>
<b>Double check to be sure to upload a valid .bin firmware file for your ESP! Otherwise you'll need USB recovery!</b><br><br>
<form method='POST' action='' enctype='multipart/form-data'>
<input type='file' name='update'>
<input type='submit' value='Update!'>
</form>
</body></html>)";
const char* ESP8266HTTPUpdateServer::_failedResponse = R"(Update Failed!)";
const char* ESP8266HTTPUpdateServer::_successResponse = "<META http-equiv=\"refresh\" content=\"15;URL=./\">Update Successful! Rebooting, please wait for redirect...";
ESP8266HTTPUpdateServer::ESP8266HTTPUpdateServer(bool serial_debug)
{
_serial_output = serial_debug;
_server = NULL;
_username = NULL;
_password = NULL;
_authenticated = false;
}
#ifdef ARDUINO_ARCH_ESP32
void ESP8266HTTPUpdateServer::setup(WebServer *server, const char * path, const char * username, const char * password)
#else
void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server, const char * path, const char * username, const char * password)
#endif
{
_server = server;
_username = (char *)username;
_password = (char *)password;
// handler for the /update form page
_server->on(path, HTTP_GET, [&](){
if(_username != NULL && _password != NULL && !_server->authenticate(_username, _password))
return _server->requestAuthentication();
_server->send(200, "text/html", _serverIndex);
});
// handler for the /update form POST (once file upload finishes)
_server->on(path, HTTP_POST, [&](){
if(!_authenticated)
return _server->requestAuthentication();
_server->send(200, "text/html", Update.hasError() ? _failedResponse : _successResponse);
ESP.restart();
},[&](){
// handler for the file upload, get's the sketch bytes, and writes
// them through the Update object
HTTPUpload& upload = _server->upload();
if(upload.status == UPLOAD_FILE_START){
if (_serial_output)
Serial.setDebugOutput(true);
_authenticated = (_username == NULL || _password == NULL || _server->authenticate(_username, _password));
if(!_authenticated){
if (_serial_output)
Serial.printf("Unauthenticated Update\n");
return;
}
#ifndef ARDUINO_ARCH_ESP32
WiFiUDP::stopAll();
#endif
if (_serial_output)
Serial.printf("Update: %s\n", upload.filename.c_str());
#ifdef ARDUINO_ARCH_ESP32
uint32_t maxSketchSpace = 0x100000; //dirty workaround, limit to 1MB
#else
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
#endif
if(!Update.begin(maxSketchSpace)){//start with max available size
if (_serial_output) Update.printError(Serial);
}
} else if(_authenticated && upload.status == UPLOAD_FILE_WRITE){
if (_serial_output) Serial.printf(".");
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
if (_serial_output) Update.printError(Serial);
}
} else if(_authenticated && upload.status == UPLOAD_FILE_END){
if(Update.end(true)){ //true to set the size to the current progress
if (_serial_output) Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
} else {
if (_serial_output) Update.printError(Serial);
}
if (_serial_output) Serial.setDebugOutput(false);
} else if(_authenticated && upload.status == UPLOAD_FILE_ABORTED){
Update.end();
if (_serial_output) Serial.println("Update was aborted");
}
delay(0);
});
}

View File

@ -0,0 +1,74 @@
#ifndef __HTTP_UPDATE_SERVER_H
#define __HTTP_UPDATE_SERVER_H
#ifdef ARDUINO_ARCH_ESP32
class WebServer;
class ESP8266HTTPUpdateServer
{
private:
bool _serial_output;
WebServer *_server;
static const char *_serverIndex;
static const char *_failedResponse;
static const char *_successResponse;
char * _username;
char * _password;
bool _authenticated;
public:
ESP8266HTTPUpdateServer(bool serial_debug=false);
void setup(WebServer *server)
{
setup(server, NULL, NULL);
}
void setup(WebServer *server, const char * path)
{
setup(server, path, NULL, NULL);
}
void setup(WebServer *server, const char * username, const char * password)
{
setup(server, "/update", username, password);
}
void setup(WebServer *server, const char * path, const char * username, const char * password);
};
#else
class ESP8266WebServer;
class ESP8266HTTPUpdateServer
{
private:
bool _serial_output;
ESP8266WebServer *_server;
static const char *_serverIndex;
static const char *_failedResponse;
static const char *_successResponse;
char * _username;
char * _password;
bool _authenticated;
public:
ESP8266HTTPUpdateServer(bool serial_debug=false);
void setup(ESP8266WebServer *server)
{
setup(server, NULL, NULL);
}
void setup(ESP8266WebServer *server, const char * path)
{
setup(server, path, NULL, NULL);
}
void setup(ESP8266WebServer *server, const char * username, const char * password)
{
setup(server, "/update", username, password);
}
void setup(ESP8266WebServer *server, const char * path, const char * username, const char * password);
};
#endif
#endif

View File

@ -1,3 +1,5 @@
Notice by Aircoookie: Port of the ESP8266HTTPUpdateServer for ESP32 is also included in this directory.
# WebServer # WebServer
ESP8266/ESP32 WebServer library ESP8266/ESP32 WebServer library

View File

@ -16,11 +16,11 @@
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESP8266mDNS.h> #include <ESP8266mDNS.h>
#include <ESP8266WebServer.h> #include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#endif #endif
#include <EEPROM.h> #include <EEPROM.h>
#include <ArduinoOTA.h> #include <ArduinoOTA.h>
#include <WiFiUDP.h> #include <WiFiUDP.h>
#include "src/dependencies/webserver/ESP8266HTTPUpdateServer.h"
#include "src/dependencies/time/Time.h" #include "src/dependencies/time/Time.h"
#include "src/dependencies/time/TimeLib.h" #include "src/dependencies/time/TimeLib.h"
#include "src/dependencies/timezone/Timezone.h" #include "src/dependencies/timezone/Timezone.h"
@ -29,7 +29,7 @@
#include "WS2812FX.h" #include "WS2812FX.h"
//version in format yymmddb (b = daily build) //version in format yymmddb (b = daily build)
#define VERSION 1801140 #define VERSION 1801150
const String versionName = "WLED 0.5dev"; const String versionName = "WLED 0.5dev";
//AP and OTA default passwords (change them!) //AP and OTA default passwords (change them!)
@ -171,6 +171,7 @@ byte bri_it = 0;
byte bri_last = 127; byte bri_last = 127;
boolean transitionActive = false; boolean transitionActive = false;
boolean buttonPressedBefore = false; boolean buttonPressedBefore = false;
long buttonPressedTime = 0;
boolean nightlightActive = false; boolean nightlightActive = false;
boolean nightlightActive_old = false; boolean nightlightActive_old = false;
int nightlightDelayMs; int nightlightDelayMs;
@ -234,8 +235,8 @@ String escapedMac;
WebServer server(80); WebServer server(80);
#else #else
ESP8266WebServer server(80); ESP8266WebServer server(80);
ESP8266HTTPUpdateServer httpUpdater; //only for ESP8266
#endif #endif
ESP8266HTTPUpdateServer httpUpdater;
WiFiUDP notifierUdp; WiFiUDP notifierUdp;
WiFiUDP ntpUdp; WiFiUDP ntpUdp;

View File

@ -122,6 +122,9 @@ void wledInit()
val += "mA currently\nNotice: This is just an estimate which does not take into account several factors (like effects and wire resistance). It is NOT an accurate measurement!"; val += "mA currently\nNotice: This is just an estimate which does not take into account several factors (like effects and wire resistance). It is NOT an accurate measurement!";
server.send(200, "text/plain", val); server.send(200, "text/plain", val);
}); });
server.on("/teapot", HTTP_GET, [](){
server.send(418, "text/plain", "418. I'm a teapot. (Tangible Embedded Advanced Project Of Twinkling)");
});
server.on("/build", HTTP_GET, [](){ server.on("/build", HTTP_GET, [](){
String info = "hard-coded build info:\r\n\n"; String info = "hard-coded build info:\r\n\n";
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
@ -166,6 +169,7 @@ void wledInit()
#endif #endif
server.send(200, "text/plain", info); server.send(200, "text/plain", info);
}); });
//if OTA is allowed
if (!otaLock){ if (!otaLock){
server.on("/edit", HTTP_GET, [](){ server.on("/edit", HTTP_GET, [](){
if(!handleFileRead("/edit.htm")) server.send(200, "text/html", PAGE_edit); if(!handleFileRead("/edit.htm")) server.send(200, "text/html", PAGE_edit);
@ -179,14 +183,14 @@ void wledInit()
server.on("/down", HTTP_GET, down); server.on("/down", HTTP_GET, down);
server.on("/cleareeprom", HTTP_GET, clearEEPROM); server.on("/cleareeprom", HTTP_GET, clearEEPROM);
//init ota page //init ota page
#ifndef ARDUINO_ARCH_ESP32
httpUpdater.setup(&server); //only for ESP8266 httpUpdater.setup(&server); //only for ESP8266
#else
server.on("/update", HTTP_GET, [](){
server.send(200, "text/plain", "OTA update is not supported on ESP32 at this time. You may want to use ArduinoOTA.");
});
#endif
//init ArduinoOTA //init ArduinoOTA
ArduinoOTA.onStart([]() {
#ifndef ARDUINO_ARCH_ESP32
wifi_set_sleep_type(NONE_SLEEP_T);
#endif
DEBUG_PRINTLN("Start ArduinoOTA");
});
ArduinoOTA.begin(); ArduinoOTA.begin();
} else } else
{ {
@ -233,7 +237,7 @@ void wledInit()
strip.setBrightness(255); strip.setBrightness(255);
strip.start(); strip.start();
pinMode(buttonPin, INPUT_PULLUP);
#ifdef CRONIXIE #ifdef CRONIXIE
strip.driverModeCronixie(true); strip.driverModeCronixie(true);
strip.setCronixieBacklight(cronixieBacklight); strip.setCronixieBacklight(cronixieBacklight);
@ -241,11 +245,18 @@ void wledInit()
#endif #endif
if (bootPreset>0) applyPreset(bootPreset, turnOnAtBoot, true, true); if (bootPreset>0) applyPreset(bootPreset, turnOnAtBoot, true, true);
colorUpdated(0); colorUpdated(0);
pinMode(buttonPin, INPUT_PULLUP); if(digitalRead(buttonPin) == LOW) buttonEnabled = false; //disable button if it is "pressed" unintentionally
} }
void initAP(){ void initAP(){
String save = apssid;
#ifdef CRONIXIE
if (apssid.length() <1) apssid = "CRONIXIE-AP";
#else
if (apssid.length() <1) apssid = "WLED-AP";
#endif
WiFi.softAP(apssid.c_str(), appass.c_str(), apchannel, aphide); WiFi.softAP(apssid.c_str(), appass.c_str(), apchannel, aphide);
apssid = save;
} }
void initCon() void initCon()
@ -261,15 +272,8 @@ void initCon()
{ {
WiFi.disconnect(); WiFi.disconnect();
DEBUG_PRINTLN("Can't connect. Opening AP..."); DEBUG_PRINTLN("Can't connect. Opening AP...");
String save = apssid;
onlyAP = true; onlyAP = true;
#ifdef CRONIXIE
if (apssid.length() <1) apssid = "CRONIXIE-AP";
#else
if (apssid.length() <1) apssid = "WLED-AP";
#endif
initAP(); initAP();
apssid = save;
return; return;
} }
} }

View File

@ -8,6 +8,7 @@ void handleButton()
{ {
if (digitalRead(buttonPin) == LOW && !buttonPressedBefore) if (digitalRead(buttonPin) == LOW && !buttonPressedBefore)
{ {
buttonPressedTime = millis();
buttonPressedBefore = true; buttonPressedBefore = true;
if (buttonMacro == 255) if (buttonMacro == 255)
{ {
@ -29,6 +30,7 @@ void handleButton()
delay(15); //debounce delay(15); //debounce
if (digitalRead(buttonPin) == HIGH) if (digitalRead(buttonPin) == HIGH)
{ {
if (millis() - buttonPressedTime > 7000) initAP();
buttonPressedBefore = false; buttonPressedBefore = false;
} }
} }

27
wled00/wled14_colors.ino Normal file
View File

@ -0,0 +1,27 @@
/*
* Color conversion methods
*/
void colorCTtoRGB(uint16_t mired, uint8_t* rgb) //white spectrum to rgb
{
}
void colorHSBtoRGB(uint16_t hue, uint8_t sat, uint8_t bri, uint8_t* rgb) //hue, sat, bri to rgb
{
}
void colorXYtoRGB(float x, float y, uint8_t* rgb) //coordinates to rgb (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)
{
float z = 1.0f - x - y;
//float Y = 1.0f; // Brightness, we handle this separately
float X = (1.0f / y) * x;
float Z = (1.0f / y) * z;
rgb[0] = (int)(X * 1.656492f - 0.354851f - Z * 0.255038f);
rgb[1] = (int)(-X * 0.707196f + 1.655397f + Z * 0.036152f);
rgb[2] = (int)(X * 0.051713f - 0.121364f + Z * 1.011530f);
}
void colorRGBtoXY(uint8_t* rgb, float* xy){} //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)
void colorRGBtoRGBW(uint8_t* rgb, uint8_t* rgbw){} //rgb to rgbw, not imlemented yet