#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServerSecure.h>
#include <ESP8266mDNS.h>
#include <FS.h>
extern const char* serverIndex;
extern const char* serverCert;
extern const char* serverKey;
const char* host PROGMEM = "esp8266-webupdate";
const char *ssid PROGMEM = "my_ssid",
*password PROGMEM = "ma_password";
const char *user_name PROGMEM = "username",
*user_password PROGMEM = "password";
BearSSL::ESP8266WebServerSecure server(443);
FSInfo fs_info;
File update_file;
bool HashEqualsFirmware(const String chash) {
MD5Builder md5h;
md5h.begin();
uint8_t *fbuff = new uint8_t[512];
update_file = SPIFFS.open("/frmwupdate/nfw.bin", "r");
uint16_t readlen = update_file.read(fbuff, 512);
while(readlen) {
md5h.add(fbuff, readlen);
readlen = update_file.read(fbuff, 512);
}
update_file.close();
delete[] fbuff;
md5h.calculate();
return chash == md5h.toString();
}
void getHTTPserverIndex(){
if (!server.authenticate(user_name, user_password))
return server.requestAuthentication();
server.sendHeader("Connection", "close");
server.send(200, "text/html", serverIndex);
}
bool nerrfile; byte callfsinfo;
void uploadUpdateFile(){
if (!server.authenticate(user_name, user_password))
return server.requestAuthentication();
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
if (strcmp(upload.type.c_str(),"application/octet-stream")) nerrfile = false;
else {
nerrfile = true;
callfsinfo = 3;
SPIFFS.remove("/frmwupdate/nfw.bin");
update_file = SPIFFS.open("/frmwupdate/nfw.bin", "w");
}
} else if (upload.status == UPLOAD_FILE_WRITE && nerrfile) {
if (callfsinfo) {
--callfsinfo;
} else {
callfsinfo = 3;
SPIFFS.info(fs_info);
//8192 == 0x2000; 0xFFFFE000 - mask for the number without 0x1FFF (8191) mask inside
//fs_info.usedBytes == fs_info.usedBytes & 0xFFFFE000 mean, that 0x1FFF no role in number
//fs_info.usedBytes & 0x3FFF - get number by mask. If number < 0x2000, then clear it (fs_info.usedBytes & 0xFFFFC000) and add 0x2000 (rounding to greater)
//If number > 0x2000, then clear it (fs_info.usedBytes & 0xFFFFC000) and add 0x2000*2 (0x4000) (rounding to greater)
// < 0x2000 mean that free space less, than need to write block 8192 (size of chunk)
if (fs_info.totalBytes - ((fs_info.usedBytes & 0xFFFFE000) == fs_info.usedBytes ? fs_info.usedBytes : ((fs_info.usedBytes & 0x3FFF) > 0x2000 ? (fs_info.usedBytes & 0xFFFFC000) + 0x4000 : (fs_info.usedBytes & 0xFFFFC000) + 0x2000)) < 0x2000) {
update_file.close();
nerrfile = false;
return;
}
}
update_file.write(upload.buf, upload.currentSize);
} else if (upload.status == UPLOAD_FILE_END && nerrfile) {
update_file.close();
}
yield();
}
void uploadUpdateFileEnd(){
if (!server.authenticate(user_name, user_password))
return server.requestAuthentication();
server.sendHeader("Connection", "close");
if (nerrfile) {
if (HashEqualsFirmware(server.arg(String("md5")))) {
WiFiUDP::stopAll();
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if (!Update.begin(maxSketchSpace)) { //start with max available size
server.send(200, "text/plain", "File uploaded successfully!\nError on begining of writing firmware!");
return;
}
uint8_t *fbuff = new uint8_t[512];
update_file = SPIFFS.open("/frmwupdate/nfw.bin", "r");
uint16_t readlen = update_file.read(fbuff, 512);
while(readlen) {
if (Update.write(fbuff, readlen) != readlen) {
server.send(200, "text/plain", "File uploaded successfully!\nError in firmware writing process!");
return;
}
readlen = update_file.read(fbuff, 512);
}
update_file.close();
delete[] fbuff;
if (!Update.end(true)) { //true to set the size to the current progress
server.send(200, "text/plain", "File uploaded successfully!\nError on ending of writing firmware!");
return;
}
server.send(200, "text/plain", "File uploaded successfully!\nFirmware written successfully!\nRebooting after 1 second...");
delay(1000);
ESP.restart();
} else {
server.send(412, "text/plain", "File is damaged or invalid hash!");
}
} else server.send(415, "text/plain", "Wrong file type of size!");
}
void setup(void) {
Serial.begin(115200);
Serial.println("Booting Sketch...");
SPIFFS.begin();
Dir dir = SPIFFS.openDir("/");
while (dir.next()) {
Serial.print(dir.fileName());
if(dir.fileSize()) {
File f = dir.openFile("r");
Serial.printf(" %d bytes",f.size());
}
Serial.println("");
}
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);
while(WiFi.waitForConnectResult() != WL_CONNECTED){
Serial.println("WiFi failed, retrying.");
delay(1000);
WiFi.begin(ssid, password);
}
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
MDNS.begin(host);
server.setRSACert(new BearSSLX509List(serverCert), new BearSSLPrivateKey(serverKey));
server.on("/", HTTP_GET, getHTTPserverIndex);
server.on("/updatefirmware", HTTP_POST, uploadUpdateFileEnd, uploadUpdateFile);
server.begin();
MDNS.addService("https", "tcp", 443);
Serial.printf("Open https://%s.local", host);
}
void loop(void) {
server.handleClient();
delay(1);
}