You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
531 lines
14 KiB
C++
531 lines
14 KiB
C++
4 years ago
|
#include <SPIFFS.h>
|
||
4 years ago
|
#include <Update.h>
|
||
4 years ago
|
#include "app.h"
|
||
4 years ago
|
#include "ESPAsyncWebServer.h"
|
||
4 years ago
|
#include "ArduinoJson.h"
|
||
4 years ago
|
#include "settings.h"
|
||
4 years ago
|
#include "device.h"
|
||
4 years ago
|
#include "playlist.h"
|
||
4 years ago
|
#include "image.h"
|
||
4 years ago
|
#include "display.h"
|
||
4 years ago
|
#include "datetime.h"
|
||
4 years ago
|
#include "faceWeather.h"
|
||
|
#include "faceCalendar.h"
|
||
4 years ago
|
#include "download.h"
|
||
4 years ago
|
|
||
|
AsyncWebServer server(80);
|
||
|
|
||
4 years ago
|
void setupSettingsGet();
|
||
|
void setupSettingsPost();
|
||
|
void setupWifiScan();
|
||
|
void setupWifiConnect();
|
||
4 years ago
|
void setupCurrentImage();
|
||
4 years ago
|
void setupApiFace();
|
||
4 years ago
|
void setupApiUpdate();
|
||
4 years ago
|
void setupOTA();
|
||
|
|
||
|
//flag to use from web update to update display
|
||
4 years ago
|
bool updateDisplayRequired = false;
|
||
4 years ago
|
|
||
4 years ago
|
//flag to use from web update to reboot the ESP
|
||
|
bool shouldReboot = false;
|
||
|
|
||
4 years ago
|
// bmp
|
||
|
void write16(AsyncResponseStream &f, uint16_t v);
|
||
|
void write32(AsyncResponseStream &f, uint32_t v);
|
||
|
uint8_t filldata2[] = {0x0, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xFF, 0xFF, 0x0};
|
||
|
// bmp
|
||
|
|
||
4 years ago
|
void setupApp()
|
||
4 years ago
|
{
|
||
4 years ago
|
Serial.println("setup configure");
|
||
|
|
||
4 years ago
|
// @see https://github.com/me-no-dev/ESPAsyncWebServer
|
||
4 years ago
|
// @see https://arduinojson.org/v6/assistant/
|
||
4 years ago
|
|
||
|
// serve static files
|
||
|
server
|
||
|
.serveStatic("/", SPIFFS, "/dist/")
|
||
4 years ago
|
.setDefaultFile("index.html")
|
||
4 years ago
|
//.setCacheControl("max-age=600")
|
||
|
;
|
||
4 years ago
|
server.serveStatic("/fs/", SPIFFS, "/");
|
||
4 years ago
|
|
||
4 years ago
|
setupSettingsGet();
|
||
|
setupSettingsPost();
|
||
|
setupWifiScan();
|
||
|
setupWifiConnect();
|
||
4 years ago
|
setupCurrentImage();
|
||
4 years ago
|
setupApiFace();
|
||
4 years ago
|
setupApiUpdate();
|
||
4 years ago
|
setupOTA();
|
||
4 years ago
|
|
||
4 years ago
|
server.onNotFound([](AsyncWebServerRequest *request) {
|
||
|
request->send(404);
|
||
|
});
|
||
|
|
||
4 years ago
|
// TODO response
|
||
4 years ago
|
server.on("/stats", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||
4 years ago
|
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||
4 years ago
|
DynamicJsonDocument doc(668); // https://arduinojson.org/v6/assistant/
|
||
4 years ago
|
|
||
4 years ago
|
doc["wifi"]["mac"] = WiFi.macAddress();
|
||
4 years ago
|
doc["wifi"]["ssid"] = WiFi.SSID();
|
||
|
doc["wifi"]["connected"] = WiFi.isConnected();
|
||
4 years ago
|
doc["wifi"]["rssi"] = WiFi.RSSI();
|
||
4 years ago
|
doc["wifi"]["ip"] = WiFi.localIP().toString();
|
||
4 years ago
|
doc["wifi"]["channel"] = WiFi.channel();
|
||
4 years ago
|
doc["wifi"]["dns"] = WiFi.dnsIP().toString();
|
||
|
doc["wifi"]["gateway"] = WiFi.gatewayIP().toString();
|
||
4 years ago
|
|
||
4 years ago
|
doc["device"]["id"] = DeviceId;
|
||
4 years ago
|
doc["device"]["time"] = time(NULL);
|
||
4 years ago
|
doc["device"]["bootCycle"] = deviceGetBootCount();
|
||
|
doc["device"]["screen"]["width"] = 640;
|
||
4 years ago
|
doc["device"]["screen"]["height"] = 384;
|
||
4 years ago
|
|
||
4 years ago
|
doc["device"]["fs"]["total"] = SPIFFS.totalBytes();
|
||
|
doc["device"]["fs"]["used"] = SPIFFS.usedBytes();
|
||
|
doc["device"]["fs"]["free"] = SPIFFS.totalBytes() - SPIFFS.usedBytes();
|
||
4 years ago
|
|
||
|
doc["device"]["heap"]["total"] = ESP.getHeapSize();
|
||
|
doc["device"]["heap"]["free"] = ESP.getFreeHeap();
|
||
|
doc["device"]["psram"]["total"] = ESP.getPsramSize();
|
||
|
doc["device"]["psram"]["free"] = ESP.getFreePsram();
|
||
4 years ago
|
|
||
|
doc["playlist"]["current"] = PlaylistGetCurrentFace();
|
||
|
doc["playlist"]["remaining"] = PlaylistGetRemainingTimeMs() / 1000;
|
||
4 years ago
|
|
||
|
JsonArray capability = doc.createNestedArray("capability");
|
||
|
capability.add("png");
|
||
|
capability.add("wbmp");
|
||
|
|
||
|
doc["cloud"]["sleep"] = deviceGetSleepInterval();
|
||
|
|
||
|
serializeJson(doc, *response);
|
||
4 years ago
|
request->send(response);
|
||
4 years ago
|
});
|
||
4 years ago
|
|
||
4 years ago
|
// CORS
|
||
4 years ago
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||
|
//DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "content-type"));
|
||
|
|
||
4 years ago
|
server.begin();
|
||
|
|
||
|
Serial.println("setup configure - done");
|
||
|
}
|
||
|
|
||
4 years ago
|
void loopApp()
|
||
|
{
|
||
4 years ago
|
if (shouldReboot)
|
||
|
{
|
||
|
Serial.println("Rebooting...");
|
||
|
delay(100);
|
||
|
ESP.restart();
|
||
|
}
|
||
|
|
||
4 years ago
|
if (updateDisplayRequired)
|
||
|
{
|
||
|
Serial.println("loop app update display");
|
||
|
|
||
|
// stop playlist to show the new image
|
||
|
PlaylistResetTimer();
|
||
|
|
||
|
updateDisplayRequired = false;
|
||
|
display.nextPage();
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
void setupSettingsGet()
|
||
|
{
|
||
4 years ago
|
server.on("/api/settings", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||
4 years ago
|
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||
|
DynamicJsonDocument root(1024);
|
||
4 years ago
|
|
||
4 years ago
|
root["system"]["country"] = NVS.getString("system.country");
|
||
|
root["system"]["language"] = NVS.getString("system.language");
|
||
|
root["system"]["timezone"] = NVS.getString("system.timezone");
|
||
|
root["system"]["utc"] = NVS.getInt("system.utc");
|
||
|
root["system"]["dst"] = NVS.getInt("system.dst");
|
||
4 years ago
|
root["system"]["wifi"] = NVS.getString("wifi.ssid");
|
||
|
|
||
4 years ago
|
// gmtOffset_sec
|
||
|
// daylightOffset_sec
|
||
|
|
||
|
//root["device"]["angle"] = NVS.getInt("device.angle");
|
||
4 years ago
|
root["device"]["theme"] = NVS.getString("device.theme");
|
||
4 years ago
|
root["device"]["name"] = NVS.getString("device.name");
|
||
4 years ago
|
|
||
|
root["playlist"]["timer"] = NVS.getInt("playlist.timer");
|
||
|
|
||
4 years ago
|
root["weather"]["api"] = NVS.getString("weather.api");
|
||
|
root["weather"]["location"] = NVS.getInt("weather.loc");
|
||
|
root["weather"]["lang"] = NVS.getString("weather.lang");
|
||
|
root["weather"]["unit"] = NVS.getString("weather.unit");
|
||
4 years ago
|
|
||
4 years ago
|
//root["cloud"]["mode"] = NVS.getString("cloud.mode");
|
||
|
//root["cloud"]["url"] = NVS.getString("cloud.url");
|
||
|
//root["cloud"]["token"] = NVS.getString("cloud.token");
|
||
4 years ago
|
|
||
|
serializeJson(root, *response);
|
||
|
request->send(response);
|
||
4 years ago
|
});
|
||
4 years ago
|
}
|
||
|
|
||
|
void setupSettingsPost()
|
||
|
{
|
||
4 years ago
|
server.on(
|
||
|
"/api/settings", HTTP_PUT, [](AsyncWebServerRequest *request) { /* nothing and dont remove it */ }, NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
||
4 years ago
|
DynamicJsonDocument doc(2048);
|
||
4 years ago
|
|
||
4 years ago
|
DeserializationError error = deserializeJson(doc, data);
|
||
|
if (error) {
|
||
|
Serial.print(F("deserializeJson() failed with code "));
|
||
|
Serial.println(error.c_str());
|
||
4 years ago
|
|
||
4 years ago
|
request->send(404, "text/plain", "");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
4 years ago
|
JsonVariant system = doc["system"];
|
||
|
if (!system.isNull()) {
|
||
|
NVS.setString("system.country", system["country"]);
|
||
|
NVS.setString("system.language", system["language"]);
|
||
|
NVS.setString("system.timezone", system["timezone"]);
|
||
|
NVS.setInt("system.utc", system["utc"].as<unsigned int>());
|
||
|
NVS.setInt("system.dst", system["dst"].as<unsigned int>());
|
||
|
}
|
||
4 years ago
|
|
||
4 years ago
|
JsonVariant device = doc["device"];
|
||
|
if (!device.isNull()) {
|
||
|
NVS.setInt("device.angle", device["angle"].as<unsigned int>());
|
||
|
NVS.setString("device.theme", device["theme"]);
|
||
|
NVS.setString("device.name", device["name"]);
|
||
|
}
|
||
4 years ago
|
|
||
4 years ago
|
JsonVariant playlist = doc["playlist"];
|
||
|
if (!playlist.isNull()) {
|
||
|
NVS.setInt("playlist.timer", playlist["timer"].as<unsigned int>());
|
||
|
}
|
||
4 years ago
|
|
||
4 years ago
|
JsonVariant weather = doc["weather"];
|
||
|
if (!doc["weather"].isNull()) {
|
||
|
NVS.setString("weather.api", weather["api"]);
|
||
|
NVS.setInt("weather.loc", weather["location"].as<unsigned int>());
|
||
|
NVS.setString("weather.lang", weather["lang"]);
|
||
|
NVS.setString("weather.unit", weather["unit"]);
|
||
|
}
|
||
|
/*
|
||
4 years ago
|
NVS.setString("cloud.mode", doc["cloud"]["mode"]);
|
||
|
NVS.setString("cloud.url", doc["cloud"]["url"]);
|
||
|
NVS.setString("cloud.token", doc["cloud"]["token"]);
|
||
4 years ago
|
*/
|
||
4 years ago
|
|
||
|
NVS.commit();
|
||
4 years ago
|
|
||
4 years ago
|
request->send(200, "application/ld+json; charset=utf-8", "{}");
|
||
|
} });
|
||
4 years ago
|
}
|
||
4 years ago
|
|
||
4 years ago
|
/**
|
||
4 years ago
|
* get current screen
|
||
4 years ago
|
*/
|
||
4 years ago
|
void setupCurrentImage()
|
||
|
{
|
||
4 years ago
|
server.on("/current-image", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||
4 years ago
|
uint8_t *bitmap = display.getBuffer();
|
||
|
int16_t w = display.width();
|
||
|
int16_t h = display.height();
|
||
|
|
||
|
uint16_t depth = 1;
|
||
|
uint32_t rowSizeCode = (w + 8 - depth) * depth / 8;
|
||
|
|
||
|
// BMP rows are padded (if needed) to 4-byte boundary
|
||
|
uint32_t rowSizeBMP = (w * depth / 8 + 3) & ~3;
|
||
|
uint32_t headerSize = 40;
|
||
|
uint32_t imageOffset = 62;
|
||
|
uint32_t fileSize = imageOffset + h * rowSizeBMP;
|
||
|
|
||
|
AsyncResponseStream *response = request->beginResponseStream("image/bmp", w * h / 8 + imageOffset);
|
||
|
|
||
|
write16(*response, 0x4D42); // BMP signature
|
||
4 years ago
|
write32(*response, fileSize); // fileSize
|
||
4 years ago
|
write32(*response, 0); // creator bytes
|
||
|
write32(*response, imageOffset); // image offset
|
||
4 years ago
|
write32(*response, headerSize); // Header size
|
||
4 years ago
|
write32(*response, w); // image width
|
||
|
write32(*response, h); // image height
|
||
|
write16(*response, 1); // # planes
|
||
4 years ago
|
write16(*response, depth); // bits per pixel
|
||
4 years ago
|
write32(*response, 0); // format uncompressed
|
||
|
|
||
|
uint32_t j = 0;
|
||
|
for (uint32_t i = 34; i < imageOffset; i++)
|
||
|
{
|
||
|
response->write(filldata2[j++]); // remaining header bytes
|
||
|
}
|
||
|
|
||
4 years ago
|
uint32_t rowidx = w * h / 8;
|
||
4 years ago
|
for (uint16_t row = 0; row < h; row++) // for each line
|
||
|
{
|
||
4 years ago
|
rowidx -= rowSizeCode;
|
||
|
|
||
4 years ago
|
uint32_t colidx;
|
||
|
for (colidx = 0; colidx < rowSizeCode; colidx++)
|
||
|
{
|
||
|
uint8_t data = pgm_read_byte(&bitmap[rowidx + colidx]);
|
||
|
response->write(data);
|
||
|
}
|
||
4 years ago
|
|
||
4 years ago
|
while (colidx++ < rowSizeBMP)
|
||
|
{
|
||
|
response->write(uint8_t(0)); // padding
|
||
|
}
|
||
|
}
|
||
|
|
||
|
request->send(response);
|
||
4 years ago
|
});
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
/**
|
||
4 years ago
|
* scan for wifi
|
||
4 years ago
|
*/
|
||
4 years ago
|
void setupWifiScan()
|
||
|
{
|
||
4 years ago
|
server.on("/api/wifi/scan", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||
4 years ago
|
String json = "[";
|
||
4 years ago
|
|
||
|
int n = WiFi.scanNetworks();
|
||
|
for (int i = 0; i < n; ++i)
|
||
4 years ago
|
{
|
||
4 years ago
|
if (i)
|
||
4 years ago
|
{
|
||
4 years ago
|
json += ",";
|
||
4 years ago
|
}
|
||
4 years ago
|
|
||
4 years ago
|
json += "{";
|
||
|
json += "\"rssi\":" + String(WiFi.RSSI(i));
|
||
|
json += ",\"ssid\":\"" + WiFi.SSID(i) + "\"";
|
||
|
json += ",\"bssid\":\"" + WiFi.BSSIDstr(i) + "\"";
|
||
|
json += ",\"channel\":" + String(WiFi.channel(i));
|
||
|
json += ",\"secure\":" + String(WiFi.encryptionType(i));
|
||
|
json += "}";
|
||
4 years ago
|
}
|
||
4 years ago
|
|
||
4 years ago
|
WiFi.scanDelete();
|
||
|
|
||
4 years ago
|
json += "]";
|
||
|
request->send(200, "application/json", json);
|
||
|
json = String();
|
||
|
});
|
||
|
}
|
||
|
|
||
4 years ago
|
/**
|
||
|
* @todo
|
||
|
*/
|
||
4 years ago
|
void setupWifiConnect()
|
||
|
{
|
||
4 years ago
|
server.on(
|
||
|
"/api/wifi/connect", HTTP_POST, [](AsyncWebServerRequest *request) { /* nothing and dont remove it */ }, NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
||
4 years ago
|
DynamicJsonDocument doc(1024);
|
||
4 years ago
|
|
||
4 years ago
|
DeserializationError error = deserializeJson(doc, data);
|
||
|
if (error) {
|
||
|
Serial.print(F("deserializeJson() failed with code "));
|
||
|
Serial.println(error.c_str());
|
||
4 years ago
|
|
||
4 years ago
|
request->send(404, "application/ld+json; charset=utf-8", "{}");
|
||
4 years ago
|
}
|
||
|
else
|
||
|
{
|
||
4 years ago
|
JsonVariant ssid = doc["ssid"];
|
||
|
if (!ssid.isNull()) {
|
||
|
NVS.setString("wifi.ssid", ssid);
|
||
|
Serial.println(ssid.as<char*>());
|
||
|
}
|
||
|
|
||
|
JsonVariant password = doc["password"];
|
||
|
if (!password.isNull()) {
|
||
|
NVS.setString("wifi.password", password);
|
||
|
Serial.println(password.as<char*>());
|
||
|
}
|
||
|
|
||
|
/*
|
||
4 years ago
|
if (doc.containsKey("ssid")) {
|
||
|
NVS.setString("wifi_ssid", doc["ssid"]);
|
||
|
Serial.println(doc["ssid"].as<char*>());
|
||
4 years ago
|
}
|
||
|
if (doc.containsKey("password")) {
|
||
4 years ago
|
NVS.setString("wifi_password", doc["password"]);
|
||
|
Serial.println(doc["password"].as<char*>());
|
||
4 years ago
|
}
|
||
4 years ago
|
*/
|
||
4 years ago
|
|
||
4 years ago
|
request->send(200, "application/ld+json; charset=utf-8", "{}");
|
||
4 years ago
|
|
||
|
ESP.restart();
|
||
4 years ago
|
} });
|
||
4 years ago
|
}
|
||
|
|
||
|
static void handle_update_progress_cb(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
|
||
|
{
|
||
|
if (!index)
|
||
|
{
|
||
|
Serial.printf("UploadStart: %s\n", filename.c_str());
|
||
4 years ago
|
bool dither = strcmp(filename.c_str(), "dithering") == 0;
|
||
|
|
||
|
ImageNew(0, 0, 0, 0, dither);
|
||
4 years ago
|
PlaylistResetTimer();
|
||
4 years ago
|
}
|
||
|
|
||
|
ImageWriteBuffer(data, len);
|
||
|
|
||
|
if (final)
|
||
|
{
|
||
|
Serial.printf("UploadEnd: %s, %u B\n", filename.c_str(), index + len);
|
||
|
ImageFlushBuffer();
|
||
4 years ago
|
|
||
|
updateDisplayRequired = true;
|
||
4 years ago
|
}
|
||
|
}
|
||
|
|
||
|
void setupApiFace()
|
||
|
{
|
||
4 years ago
|
server.on(
|
||
|
"/api/face", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||
4 years ago
|
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||
|
DynamicJsonDocument doc(117); // https://arduinojson.org/v6/assistant/
|
||
|
|
||
|
// todo
|
||
|
doc["status"] = true;
|
||
|
doc["image"]["format"] = "xxx";
|
||
|
doc["image"]["width"] = 0;
|
||
|
doc["image"]["height"] = 0;
|
||
|
|
||
|
//doc["jpg"]["comps"] = 0;
|
||
|
|
||
|
/*
|
||
|
Serial.println(JpegDec.width);
|
||
|
Serial.println(JpegDec.height);
|
||
|
|
||
|
Serial.print(F("Components :"));
|
||
|
Serial.println(JpegDec.comps);
|
||
|
Serial.print(F("MCU / row :"));
|
||
|
Serial.println(JpegDec.MCUSPerRow);
|
||
|
Serial.print(F("MCU / col :"));
|
||
|
Serial.println(JpegDec.MCUSPerCol);
|
||
|
Serial.print(F("Scan type :"));
|
||
|
Serial.println(JpegDec.scanType);
|
||
|
Serial.print(F("MCU width :"));
|
||
|
Serial.println(JpegDec.MCUWidth);
|
||
|
Serial.print(F("MCU height :"));
|
||
|
Serial.println(JpegDec.MCUHeight);
|
||
|
*/
|
||
|
|
||
|
serializeJson(doc, *response);
|
||
|
request->send(response);
|
||
|
|
||
|
//request->send(200, "application/ld+json; charset=utf-8", "{}");
|
||
4 years ago
|
},
|
||
|
handle_update_progress_cb);
|
||
4 years ago
|
}
|
||
|
|
||
|
void setupApiUpdate()
|
||
|
{
|
||
|
server.on("/api/update", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||
4 years ago
|
if (request->hasParam("datetime"))
|
||
|
{
|
||
|
Serial.println("update datetime...");
|
||
|
|
||
|
updateDateTime();
|
||
|
}
|
||
4 years ago
|
|
||
|
if (request->hasParam("weather"))
|
||
|
{
|
||
|
Serial.println("update weather data...");
|
||
|
|
||
|
updateWeatherData();
|
||
|
}
|
||
|
|
||
|
if (request->hasParam("calendar"))
|
||
|
{
|
||
|
Serial.println("update calendar data...");
|
||
|
|
||
|
updateCalendarData();
|
||
|
}
|
||
|
|
||
4 years ago
|
if (request->getParam("url") && request->hasParam("file"))
|
||
4 years ago
|
{
|
||
|
Serial.println("file...");
|
||
|
|
||
|
downloadFile(request->getParam("url")->value().c_str(), request->getParam("file")->value().c_str());
|
||
|
}
|
||
|
|
||
4 years ago
|
request->send(200, "application/ld+json; charset=utf-8", "{}");
|
||
|
});
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
void setupOTA()
|
||
|
{
|
||
|
// Simple Firmware Update Form
|
||
|
/*
|
||
|
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||
|
// TODO in die pwa auslagern
|
||
|
request->send(200, "text/html", "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>");
|
||
|
});
|
||
|
*/
|
||
|
|
||
|
server.on(
|
||
|
"/update", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||
|
shouldReboot = !Update.hasError();
|
||
|
AsyncWebServerResponse *response = request->beginResponse(200, "application/ld+json; charset=utf-8", shouldReboot ? "{\"success\": true}" : "{\"success\": false}");
|
||
|
|
||
|
response->addHeader("Connection", "close");
|
||
4 years ago
|
request->send(response); },
|
||
4 years ago
|
[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||
|
if (!index)
|
||
|
{
|
||
|
Serial.printf("Update Start: %s\n", filename.c_str());
|
||
|
// bool canBegin = Update.begin(contentLength, U_FLASH);
|
||
|
// bool canBegin = Update.begin(contentLength, U_SPIFFS);
|
||
|
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000))
|
||
|
{
|
||
|
Update.printError(Serial);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!Update.hasError())
|
||
|
{
|
||
|
if (Update.write(data, len) != len)
|
||
|
{
|
||
|
Update.printError(Serial);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (final)
|
||
|
{
|
||
|
if (Update.end(true))
|
||
|
{
|
||
|
Serial.printf("Update Success: %uB\n", index + len);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Update.printError(Serial);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
4 years ago
|
void write16(AsyncResponseStream &f, uint16_t v)
|
||
|
{
|
||
|
f.write(uint8_t(v));
|
||
|
f.write(uint8_t(v >> 8));
|
||
|
}
|
||
|
|
||
|
void write32(AsyncResponseStream &f, uint32_t v)
|
||
|
{
|
||
|
f.write(uint8_t(v));
|
||
|
f.write(uint8_t(v >> 8));
|
||
|
f.write(uint8_t(v >> 16));
|
||
|
f.write(uint8_t(v >> 24));
|
||
4 years ago
|
}
|