Supports AutoConnectOTA

pull/197/head
Hieromon Ikasamo 5 years ago
parent 62b53a31f7
commit 52e03c81fd
  1. 25
      examples/MyLabels/mylabels.h
  2. 21
      src/AutoConnect.cpp
  3. 11
      src/AutoConnect.h
  4. 12
      src/AutoConnectAux.cpp
  5. 4
      src/AutoConnectDefs.h
  6. 29
      src/AutoConnectLabels.h
  7. 213
      src/AutoConnectOTA.cpp
  8. 86
      src/AutoConnectOTA.h
  9. 39
      src/AutoConnectOTAPage.h
  10. 2
      src/AutoConnectUpload.h
  11. 12
      src/AutoConnectUploadImpl.h

@ -62,6 +62,11 @@
//#define AUTOCONNECT_BUTTONLABEL_RESET "NEW_STRING_YOU_WISH" //#define AUTOCONNECT_BUTTONLABEL_RESET "NEW_STRING_YOU_WISH"
//#endif // !AUTOCONNECT_BUTTONLABEL_RESET //#endif // !AUTOCONNECT_BUTTONLABEL_RESET
// Button label: UPDATE
//#ifndef AUTOCONNECT_BUTTONLABEL_UPDATE
//#define AUTOCONNECT_BUTTONLABEL_UPDATE "NEW_STRING_YOU_WISH"
//#endif // !AUTOCONNECT_BUTTONLABEL_UPDATE
// Page title: Page not found // Page title: Page not found
//#ifdef AUTOCONNECT_PAGETITLE_NOTFOUND //#ifdef AUTOCONNECT_PAGETITLE_NOTFOUND
//#undef AUTOCONNECT_PAGETITLE_NOTFOUND //#undef AUTOCONNECT_PAGETITLE_NOTFOUND
@ -212,6 +217,26 @@
//#define AUTOCONNECT_PAGETITLE_CREDENTIALS "NEW_STRING_YOU_WISH" //#define AUTOCONNECT_PAGETITLE_CREDENTIALS "NEW_STRING_YOU_WISH"
//#endif // !AUTOCONNECT_PAGETITLE_CREDENTIALS //#endif // !AUTOCONNECT_PAGETITLE_CREDENTIALS
// Text: The update page caption
//#ifndef AUTOCONNECT_TEXT_UPDATINGFIRMWARE
//#define AUTOCONNECT_TEXT_UPDATINGFIRMWARE "NEW_STRING_YOU_WISH"
//#endif // !AUTOCONNECT_TEXT_UPDATINGFIRMWARE
// Text: The update page's file selection button label
//#ifndef AUTOCONNECT_TEXT_SELECTFIRMWARE
//#define AUTOCONNECT_TEXT_SELECTFIRMWARE "NEW_STRING_YOU_WISH"
//#endif // !AUTOCONNECT_TEXT_SELECTFIRMWARE
// Text: OTA success
//#ifndef AUTOCONNECT_TEXT_OTASUCCESS
//#define AUTOCONNECT_TEXT_OTASUCCESS "NEW_STRING_YOU_WISH"
//#endif // !AUTOCONNECT_TEXT_OTASUCCESS
// Text: OTA failure
//#ifndef AUTOCONNECT_TEXT_OTAFAILURE
//#define AUTOCONNECT_TEXT_OTAFAILURE "NEW_STRING_YOU_WISH"
//#endif // !AUTOCONNECT_TEXT_OTAFAILURE
// Page title: AutoConnect connecting // Page title: AutoConnect connecting
//#ifdef AUTOCONNECT_PAGETITLE_CONNECTING //#ifdef AUTOCONNECT_PAGETITLE_CONNECTING
//#undef AUTOCONNECT_PAGETITLE_CONNECTING //#undef AUTOCONNECT_PAGETITLE_CONNECTING

@ -95,6 +95,13 @@ bool AutoConnect::begin(const char* ssid, const char* passphrase, unsigned long
_ticker->start(AUTOCONNECT_FLICKER_PERIODDC, (uint8_t)AUTOCONNECT_FLICKER_WIDTHDC); _ticker->start(AUTOCONNECT_FLICKER_PERIODDC, (uint8_t)AUTOCONNECT_FLICKER_WIDTHDC);
} }
// Attach AutoConnectOTA if OTA is available.
if (_apConfig.ota) {
_ota.reset(new AutoConnectOTA());
_ota->attach(*this);
_ota->setTicker(_apConfig.tickerPort, _apConfig.tickerOn);
}
// Advance configuration for STA mode. Restore previous configuration of STA. // Advance configuration for STA mode. Restore previous configuration of STA.
station_config_t current; station_config_t current;
if (_getConfigSTA(&current)) { if (_getConfigSTA(&current)) {
@ -377,6 +384,9 @@ void AutoConnect::home(const String& uri) {
void AutoConnect::end(void) { void AutoConnect::end(void) {
_responsePage.reset(); _responsePage.reset();
_currentPageElement.reset(); _currentPageElement.reset();
_ticker.reset();
_update.reset();
_ota.reset();
_stopPortal(); _stopPortal();
_dnsServer.reset(); _dnsServer.reset();
@ -583,6 +593,15 @@ void AutoConnect::handleRequest(void) {
// Handle the update behaviors for attached AutoConnectUpdate. // Handle the update behaviors for attached AutoConnectUpdate.
if (_update) if (_update)
_update->handleUpdate(); _update->handleUpdate();
// Indicate the reboot at the next handleClient turn
// with on completion of the update via OTA.
if (_ota) {
if (_ota->status() == AutoConnectOTA::OTA_RIP) {
_webServer->client().setNoDelay(true);
_rfReset = true;
}
}
} }
/** /**
@ -850,7 +869,7 @@ String AutoConnect::_induceConnect(PageArgument& args) {
// that occurs at connection establishment. // that occurs at connection establishment.
// [WiFiClient.cpp:463] connected(): Disconnected: RES: 0, ERR: 128 // [WiFiClient.cpp:463] connected(): Disconnected: RES: 0, ERR: 128
// When connecting as a station, TCP reset caused by switching of the // When connecting as a station, TCP reset caused by switching of the
// radio channel occurs. Although the Espressif's view is true. However, // radio channel occurs. Although the Espressif view is true. However,
// the actual TCP reset occurs not at the time of switching the channel. // the actual TCP reset occurs not at the time of switching the channel.
// It occurs at the connection from the ESP32 to the AP is established // It occurs at the connection from the ESP32 to the AP is established
// and it is possible that TCP reset is occurring in other situations. // and it is possible that TCP reset is occurring in other situations.

@ -31,8 +31,12 @@ using WebServerClass = WebServer;
#include "AutoConnectDefs.h" #include "AutoConnectDefs.h"
#include "AutoConnectPage.h" #include "AutoConnectPage.h"
#include "AutoConnectCredential.h" #include "AutoConnectCredential.h"
#include "AutoConnectAux.h"
#include "AutoConnectTicker.h" #include "AutoConnectTicker.h"
#include "AutoConnectAux.h"
// The realization of AutoConnectOTA is effective only by the explicit
#include "AutoConnectOTA.h"
class AutoConnectOTA; // Reference to avoid circular
// The realization of AutoConnectUpdate is effective only by the explicit // The realization of AutoConnectUpdate is effective only by the explicit
// definition of AUTOCONNECT_USE_UPDATE // definition of AUTOCONNECT_USE_UPDATE
@ -125,6 +129,7 @@ class AutoConnectConfig {
ticker(false), ticker(false),
tickerPort(AUTOCONNECT_TICKER_PORT), tickerPort(AUTOCONNECT_TICKER_PORT),
tickerOn(LOW), tickerOn(LOW),
ota(false),
hostName(String("")), hostName(String("")),
homeUri(AUTOCONNECT_HOMEURI), homeUri(AUTOCONNECT_HOMEURI),
title(AUTOCONNECT_MENU_TITLE), title(AUTOCONNECT_MENU_TITLE),
@ -158,6 +163,7 @@ class AutoConnectConfig {
ticker = o.ticker; ticker = o.ticker;
tickerPort = o.tickerPort; tickerPort = o.tickerPort;
tickerOn = o.tickerOn; tickerOn = o.tickerOn;
ota = o.ota;
hostName = o.hostName; hostName = o.hostName;
homeUri = o.homeUri; homeUri = o.homeUri;
title = o.title; title = o.title;
@ -190,6 +196,7 @@ class AutoConnectConfig {
bool ticker; /**< Drives LED flicker according to WiFi connection status. */ bool ticker; /**< Drives LED flicker according to WiFi connection status. */
uint8_t tickerPort; /**< GPIO for flicker */ uint8_t tickerPort; /**< GPIO for flicker */
uint8_t tickerOn; /**< A signal for flicker turn on */ uint8_t tickerOn; /**< A signal for flicker turn on */
bool ota; /**< Attach built-in OTA */
String hostName; /**< host name */ String hostName; /**< host name */
String homeUri; /**< A URI of user site */ String homeUri; /**< A URI of user site */
String title; /**< Menu title */ String title; /**< Menu title */
@ -304,6 +311,8 @@ class AutoConnect {
String _prevUri; /**< Previous generated page uri */ String _prevUri; /**< Previous generated page uri */
/** Available updater, only reset by AutoConnectUpdate::attach is valid */ /** Available updater, only reset by AutoConnectUpdate::attach is valid */
std::unique_ptr<AutoConnectUpdate> _update; std::unique_ptr<AutoConnectUpdate> _update;
/** OTA updater */
std::unique_ptr<AutoConnectOTA> _ota;
/** Saved configurations */ /** Saved configurations */
AutoConnectConfig _apConfig; AutoConnectConfig _apConfig;

@ -53,14 +53,18 @@ const char AutoConnectAux::_PAGE_AUX[] PROGMEM = {
"</div></div>" "</div></div>"
"</div>" "</div>"
"<script>" "<script>"
"function _sa(url) {" "function _bu(url) {"
"var uri=document.createElement('input');" "var uri=document.createElement('input');"
"uri.setAttribute('type','hidden');" "uri.setAttribute('type','hidden');"
"uri.setAttribute('name','" AUTOCONNECT_AUXURI_PARAM "');" "uri.setAttribute('name','" AUTOCONNECT_AUXURI_PARAM "');"
"uri.setAttribute('value','{{AUX_URI}}');" "uri.setAttribute('value','{{AUX_URI}}');"
"document.getElementById('_aux').appendChild(uri);" "var fm=document.getElementById('_aux');"
"document.getElementById('_aux').action=url;" "fm.appendChild(uri);"
"document.getElementById('_aux').submit();" "fm.action=url;"
"return fm;"
"}"
"function _sa(url) {"
"_bu(url).submit();"
"}" "}"
"</script>" "</script>"
"</body>" "</body>"

@ -185,7 +185,7 @@
#define AUTOCONNECT_JSONPSRAM_SIZE (16* 1024) #define AUTOCONNECT_JSONPSRAM_SIZE (16* 1024)
#endif // !AUTOCONNECT_JSONPSRAM_SIZE #endif // !AUTOCONNECT_JSONPSRAM_SIZE
// Available HTTP port number for the update [ms] // Available HTTP port number for the update
#ifndef AUTOCONNECT_UPDATE_PORT #ifndef AUTOCONNECT_UPDATE_PORT
#define AUTOCONNECT_UPDATE_PORT 8000 #define AUTOCONNECT_UPDATE_PORT 8000
#endif // !AUTOCONNECT_UPDATE_PORT #endif // !AUTOCONNECT_UPDATE_PORT
@ -207,7 +207,7 @@
// Wait timer for rebooting after updated // Wait timer for rebooting after updated
#ifndef AUTOCONNECT_UPDATE_WAITFORREBOOT #ifndef AUTOCONNECT_UPDATE_WAITFORREBOOT
#define AUTOCONNECT_UPDATE_WAITFORREBOOT 9000 #define AUTOCONNECT_UPDATE_WAITFORREBOOT 15000
#endif // !AUTOCONNECT_UPDATE_WAITFORREBOOT #endif // !AUTOCONNECT_UPDATE_WAITFORREBOOT
// A signal value that the board dependent LED turns on. // A signal value that the board dependent LED turns on.

@ -2,8 +2,8 @@
* AutoConnect proper menu label constant definition. * AutoConnect proper menu label constant definition.
* @file AutoConnectLabels.h * @file AutoConnectLabels.h
* @author hieromon@gmail.com * @author hieromon@gmail.com
* @version 1.1.4 * @version 1.1.5
* @date 2020-02-13 * @date 2020-04-09
* @copyright MIT license. * @copyright MIT license.
*/ */
@ -72,6 +72,11 @@
//#define AUTOCONNECT_BUTTONLABEL_RESET "Reboot" //#define AUTOCONNECT_BUTTONLABEL_RESET "Reboot"
#endif // !AUTOCONNECT_BUTTONLABEL_RESET #endif // !AUTOCONNECT_BUTTONLABEL_RESET
// Button label: UPDATE
#ifndef AUTOCONNECT_BUTTONLABEL_UPDATE
#define AUTOCONNECT_BUTTONLABEL_UPDATE "UPDATE"
#endif // !AUTOCONNECT_BUTTONLABEL_UPDATE
// Page title: Page not found // Page title: Page not found
#ifndef AUTOCONNECT_PAGETITLE_NOTFOUND #ifndef AUTOCONNECT_PAGETITLE_NOTFOUND
#define AUTOCONNECT_PAGETITLE_NOTFOUND "Page not found" #define AUTOCONNECT_PAGETITLE_NOTFOUND "Page not found"
@ -222,6 +227,26 @@
#define AUTOCONNECT_TEXT_NOSAVEDCREDENTIALS "No saved credentials." #define AUTOCONNECT_TEXT_NOSAVEDCREDENTIALS "No saved credentials."
#endif // !AUTOCONNECT_TEXT_NOSAVEDCREDENTIALS #endif // !AUTOCONNECT_TEXT_NOSAVEDCREDENTIALS
// Text: The update page caption
#ifndef AUTOCONNECT_TEXT_UPDATINGFIRMWARE
#define AUTOCONNECT_TEXT_UPDATINGFIRMWARE "Updating firmware via OTA"
#endif // !AUTOCONNECT_TEXT_UPDATINGFIRMWARE
// Text: The update page's file selection button label
#ifndef AUTOCONNECT_TEXT_SELECTFIRMWARE
#define AUTOCONNECT_TEXT_SELECTFIRMWARE "Select firmware: "
#endif // !AUTOCONNECT_TEXT_SELECTFIRMWARE
// Text: OTA success
#ifndef AUTOCONNECT_TEXT_OTASUCCESS
#define AUTOCONNECT_TEXT_OTASUCCESS "Successfully updated, rebooting..."
#endif // !AUTOCONNECT_TEXT_OTASUCCESS
// Text: OTA failure
#ifndef AUTOCONNECT_TEXT_OTAFAILURE
#define AUTOCONNECT_TEXT_OTAFAILURE "Failed to update: "
#endif // !AUTOCONNECT_TEXT_OTAFAILURE
// Menu Text: Connecting // Menu Text: Connecting
#ifndef AUTOCONNECT_MENUTEXT_CONNECTING #ifndef AUTOCONNECT_MENUTEXT_CONNECTING
#define AUTOCONNECT_MENUTEXT_CONNECTING "Connecting" #define AUTOCONNECT_MENUTEXT_CONNECTING "Connecting"

@ -0,0 +1,213 @@
/**
* AutoConnectOTA class implementation.
* @file AutoConnectOTA.cpp
* @author hieromon@gmail.com
* @version 1.1.5
* @date 2020-04-09
* @copyright MIT license.
*/
#include <functional>
#include "AutoConnectOTA.h"
#include "AutoConnectOTAPage.h"
#include <StreamString.h>
/**
* A destructor. Release the OTA operation pages.
*/
AutoConnectOTA::~AutoConnectOTA() {
_auxUpdate.reset(nullptr);
_auxResult.reset(nullptr);
}
/**
* Attach the AutoConnectOTA to hosted AutoConnect which constitutes
* the update process. This function creates an OTA operation page as
* AutoConnectAux instance and allows it to receive binary updates.
* @param portal A reference of AutoConnect
*/
void AutoConnectOTA::attach(AutoConnect& portal) {
AutoConnectAux* updatePage;
updatePage = new AutoConnectAux(String(FPSTR(_pageUpdate.uri)), String(FPSTR(_pageUpdate.title)), _pageUpdate.menu);
_buildAux(updatePage, &_pageUpdate, lengthOf(_elmUpdate));
_auxUpdate.reset(updatePage);
updatePage = new AutoConnectAux(String(FPSTR(_pageResult.uri)), String(FPSTR(_pageResult.title)), _pageResult.menu);
_buildAux(updatePage, &_pageResult, lengthOf(_elmResult));
_auxResult.reset(updatePage);
_auxResult->on(std::bind(&AutoConnectOTA::_updated, this, std::placeholders::_1, std::placeholders::_2));
_auxResult->onUpload<AutoConnectOTA>(*this);
portal.join(*_auxUpdate.get());
portal.join(*_auxResult.get());
}
/**
* Create the update operation pages using a predefined page structure
* with two structures as ACPage_t and ACElementProp_t which describe
* for AutoConnectAux configuration.
* This function receives instantiated AutoConnectAux, instantiates
* defined AutoConnectElements by ACPage_t, and configures it into
* received AutoConnectAux.
* @param aux An instantiated AutoConnectAux that will configure according to ACPage_t.
* @param page Pre-defined ACPage_t
* @param elementNum Number of AutoConnectElements to configure.
*/
void AutoConnectOTA::_buildAux(AutoConnectAux* aux, const AutoConnectOTA::ACPage_t* page, const size_t elementNum) {
for (size_t n = 0; n < elementNum; n++) {
if (page->element[n].type == AC_Button) {
AutoConnectButton* element = new AutoConnectButton;
element->name = String(FPSTR(page->element[n].name));
if (page->element[n].value)
element->value = String(FPSTR(page->element[n].value));
if (page->element[n].peculiar)
element->action = String(FPSTR(page->element[n].peculiar));
aux->add(reinterpret_cast<AutoConnectElement&>(*element));
}
else if (page->element[n].type == AC_Element) {
AutoConnectElement* element = new AutoConnectElement;
element->name = String(FPSTR(page->element[n].name));
if (page->element[n].value)
element->value = String(FPSTR(page->element[n].value));
aux->add(reinterpret_cast<AutoConnectElement&>(*element));
}
else if (page->element[n].type == AC_File) {
AutoConnectFile* element = new AutoConnectFile;
element->name = String(FPSTR(page->element[n].name));
element->label = String(FPSTR(page->element[n].peculiar));
element->store = ACFile_t::AC_File_Extern;
aux->add(reinterpret_cast<AutoConnectElement&>(*element));
}
else if (page->element[n].type == AC_Style) {
AutoConnectStyle* element = new AutoConnectStyle;
element->name = String(FPSTR(page->element[n].name));
if (page->element[n].value)
element->value = String(FPSTR(page->element[n].value));
aux->add(reinterpret_cast<AutoConnectElement&>(*element));
}
else if (page->element[n].type == AC_Text) {
AutoConnectText* element = new AutoConnectText;
element->name = String(FPSTR(page->element[n].name));
if (page->element[n].value)
element->value = String(FPSTR(page->element[n].value));
if (page->element[n].peculiar)
element->style = String(FPSTR(page->element[n].peculiar));
aux->add(reinterpret_cast<AutoConnectText&>(*element));
}
}
}
/**
* Check the space needed for the update
* This function overrides AutoConnectUploadHandler::_open.
* @param filename An updater bin file name
* @param mode File access mode, but it is not be used.
* @return true Ready for update
* @return false Not enough FLASH space to update.
*/
bool AutoConnectOTA::_open(const char* filename, const char* mode) {
AC_UNUSED(mode);
_binName = String(strchr(filename, '/') + sizeof(char));
WiFiUDP::stopAll();
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
// It only supports FLASH as a sketch area for updating.
if (Update.begin(maxSketchSpace, U_FLASH, _tickerPort, _tickerOn)) {
_status = OTA_START;
AC_DBG("%s updating start\n", filename);
return true;
}
_setError();
return false;
}
/**
* Writes received updater to the flash.
* This function overrides AutoConnectUploadHandler::_write.
* @param buf Buffer address where received update file was stored.
* @param size Size to be written.
* @return the amount written
*/
size_t AutoConnectOTA::_write(const uint8_t *buf, const size_t size) {
size_t wsz = 0;
if (!_err.length()) {
_status = OTA_PROGRESS;
wsz = Update.write(const_cast<uint8_t*>(buf), size);
if (wsz != size)
_setError();
}
return wsz;
}
/**
* All bytes are written, this call writes the config to reboot.
* If there is an error this will clear everything.
* This function overrides AutoConnectUploadHandler::_close.
* @param status Updater binary upload completion status.
*/
void AutoConnectOTA::_close(const HTTPUploadStatus status) {
if (!_err.length()) {
AC_DBG("OTA update ");
if (status == UPLOAD_FILE_END) {
if (Update.end(true)) {
_status = OTA_SUCCESS;
AC_DBG_DUMB("succeeds, turn to reboot.\n");
}
else
_setError();
}
else {
Update.end(false);
AC_DBG_DUMB(" aborted\n");
}
}
}
/**
* Callback of the update operation page as AutoConnectAux.
* Reflect the flash result of Update class to the page.
* @param result Upload post-process page
* @param args Unused
* @return none
*/
String AutoConnectOTA::_updated(AutoConnectAux& result, PageArgument& args) {
AC_UNUSED(args);
PGM_P stColor;
String st;
// Build an updating result caption.
// Change the color of the bin name depending on the result of the update.
if (_status == OTA_SUCCESS) {
st = String(F(AUTOCONNECT_TEXT_OTASUCCESS));
stColor = PSTR("3d7e9a");
// Notify to the handleClient of loop() thread that it can reboot.
_status = OTA_RIP;
}
else {
st = String(F(AUTOCONNECT_TEXT_OTAFAILURE)) + _err;
stColor = PSTR("e66157");
}
result["bin"].as<AutoConnectText>().value = _binName;
result["bin"].as<AutoConnectText>().style += String(stColor);
result["result"].as<AutoConnectText>().value = st;
// The rc element on the result page has an update return code
// which is a hidden field.
// By setting the error code of the Update class into its field,
// the homepage after reboot will automatically GET by the JavaScript
// on the result page.
result["rc"].value.replace("%d", String(Update.getError()));
return String("");
}
/**
* Save the last error
*/
void AutoConnectOTA::_setError(void) {
StreamString eStr;
Update.printError(eStr);
_err = String(eStr.c_str());
AC_DBG("%s\n", _err.c_str());
_status = OTA_FAIL;
}

@ -0,0 +1,86 @@
/**
* Declaration of AutoConnectOTA class.
* The AutoConnecOTA class is a class for web updating a Sketch binary
* via OTA and implements with an AutoConnectAux page handler that
* inherits from AutoConnectUploadHandler.
* By overriding the _write function of AutoConnectUploadHandler to
* write the executable binary using the Update class, it can update
* the module firmware in synchronization with the upload of the sketch
* binary file.
* @file AutoConnectOTA.h
* @author hieromon@gmail.com
* @version 1.1.5
* @date 2020-04-09
* @copyright MIT license.
*/
#ifndef _AUTOCONNECTOTA_H_
#define _AUTOCONNECTOTA_H_
#include <memory>
#include "AutoConnect.h"
#include "AutoConnectUpload.h"
class AutoConnectOTA : public AutoConnectUploadHandler {
public:
// Updating process status
typedef enum {
OTA_IDLE, /**< Update process has not started */
OTA_START, /**< Update process has started */
OTA_PROGRESS, /**< Update process in progress */
OTA_SUCCESS, /**< A binary updater has uploaded fine */
OTA_RIP, /**< Ready for module restart */
OTA_FAIL /**< Failed to save binary updater by Update class */
} AC_OTAStatus_t;
AutoConnectOTA() : _status(OTA_IDLE), _tickerPort(-1), _tickerOn(LOW) {}
~AutoConnectOTA();
void attach(AutoConnect& portal);
String error(void) const { return _err; }
AC_OTAStatus_t status(void) const { return _status; }
void setTicker(uint8_t pin, uint8_t on) { _tickerPort = pin, _tickerOn = on; }
protected:
// Attribute definition of the element to be placed on the update page.
typedef struct {
const ACElement_t type;
const char* name; /**< Name to assign to AutoConnectElement */
const char* value; /**< Value owned by an element */
const char* peculiar; /**< Specific ornamentation for the element */
} ACElementProp_t;
// Attributes to treat included update pages as AutoConnectAux.
typedef struct {
const char* uri; /**< URI for the page */
const char* title; /**< Menu title of update page */
const bool menu; /**< Whether to display in menu */
const ACElementProp_t* element;
} ACPage_t;
template<typename T, size_t N> constexpr
size_t lengthOf(T(&)[N]) noexcept { return N; }
void _buildAux(AutoConnectAux* aux, const AutoConnectOTA::ACPage_t* page, const size_t elementNum);
bool _open(const char* filename, const char* mode) override;
size_t _write(const uint8_t *buf, const size_t size) override;
void _close(const HTTPUploadStatus status) override;
String _updated(AutoConnectAux& result, PageArgument& args);
std::unique_ptr<AutoConnectAux> _auxUpdate; /**< An update operation page */
std::unique_ptr<AutoConnectAux> _auxResult; /**< An update result page */
private:
void _setError(void);
AC_OTAStatus_t _status; /**< Status for update progress */
uint8_t _tickerPort; /**< GPIO for flicker */
uint8_t _tickerOn; /**< A signal for flicker turn on */
String _binName; /**< An updater file name */
String _err; /**< Occurred error stamp */
static const ACPage_t _pageUpdate PROGMEM;
static const ACElementProp_t _elmUpdate[] PROGMEM;
static const ACPage_t _pageResult PROGMEM;
static const ACElementProp_t _elmResult[] PROGMEM;
};
#endif // !_AUTOCONNECTOTA_H_

@ -0,0 +1,39 @@
/**
* Define pages to operate updates using the AutoConnectUpdate class.
* @file AutoConnectOTAPage.h
* @author hieromon@gmail.com
* @version 1.1.5
* @date 2020-04-09
* @copyright MIT license.
*/
#ifndef _AUTOCONNECTOTAPAGE_H_
#define _AUTOCONNECTOTAPAGE_H_
const AutoConnectOTA::ACElementProp_t AutoConnectOTA::_elmUpdate[] PROGMEM = {
{ AC_Style, "s_rc", ".s_rc{display:none}", nullptr },
{ AC_Text, "caption", "<h3>" AUTOCONNECT_TEXT_UPDATINGFIRMWARE "<h3>", nullptr },
{ AC_File, "bin", nullptr, AUTOCONNECT_TEXT_SELECTFIRMWARE },
{ AC_Button, "update", AUTOCONNECT_BUTTONLABEL_UPDATE, "_upd(this, 'bin', '" AUTOCONNECT_URI_UPDATE_ACT "')" },
{ AC_Element, "js", "<script type=\"text/javascript\">function _upd(e,t,n){var r=document.getElementById(t);if(r.files.length>0){par=e.parentNode,par.removeChild(e),pb=document.createElement(\"progress\"),pb.setAttribute(\"id\",\"pb\"),pb.setAttribute(\"style\",\"margin-top:1.0em\"),pb.setAttribute(\"value\",\"0\"),pb.setAttribute(\"max\",r.files[0].size),par.appendChild(pb);var p=new FormData(_bu(n)),o=new XMLHttpRequest;o.upload.addEventListener(\"progress\",function(e){pb.setAttribute(\"value\",e.loaded)},!1),o.addEventListener(\"load\",function(){document.body.innerHTML=o.response.body.innerHTML,\"0\"==document.getElementById(\"rc\").innerText&&setTimeout(function(){location.href=\"" AUTOCONNECT_HOMEURI "\"}," AUTOCONNECT_STRING_DEPLOY(AUTOCONNECT_UPDATE_WAITFORREBOOT) ")},!1),o.open(\"POST\",n),o.responseType=\"document\",o.send(p)}}var par,pb;</script>", nullptr }
};
// The definition of the OTA update operation page, which will be located to AUTOCONNECT_URI_UPDATE.
const AutoConnectOTA::ACPage_t AutoConnectOTA::_pageUpdate PROGMEM = {
AUTOCONNECT_URI_UPDATE, AUTOCONNECT_MENULABEL_UPDATE, true, AutoConnectOTA::_elmUpdate
};
const AutoConnectOTA::ACElementProp_t AutoConnectOTA::_elmResult[] PROGMEM = {
{ AC_Text, "bin", nullptr, "margin-bottom:0.5em;font-size:1.2em;font-weight:bold;color:#" },
{ AC_Text, "result", nullptr, nullptr },
{ AC_Element, "rc", "<div class=\"s_rc\" id=\"rc\">%d</div>", nullptr }
};
// The definition of the OTA update result display page.
// This page is assigned to AUTOCONNECT_URI_UPDATE_ACT, but the actual
// HTML document is dynamically rewritten on AUTOCONNECT_URI_UPDATE page
// by the JavaScript function included in the _pageUpdate AutoConnectAux.
const AutoConnectOTA::ACPage_t AutoConnectOTA::_pageResult PROGMEM = {
AUTOCONNECT_URI_UPDATE_ACT, AUTOCONNECT_MENULABEL_UPDATE, false, AutoConnectOTA::_elmResult
};
#endif // !_AUTOCONNECTOTAPAGE_H_

@ -32,7 +32,7 @@ class AutoConnectUploadHandler {
protected: protected:
virtual bool _open(const char* filename, const char* mode) = 0; virtual bool _open(const char* filename, const char* mode) = 0;
virtual size_t _write(const uint8_t *buf, const size_t size) = 0; virtual size_t _write(const uint8_t *buf, const size_t size) = 0;
virtual void _close(void) = 0; virtual void _close(const HTTPUploadStatus status) = 0;
}; };
#endif // !_AUTOCONNECTUPLOAD_H_ #endif // !_AUTOCONNECTUPLOAD_H_

@ -74,7 +74,7 @@ void AutoConnectUploadHandler::upload(const String& requestUri, const HTTPUpload
break; break;
case UPLOAD_FILE_ABORTED: case UPLOAD_FILE_ABORTED:
case UPLOAD_FILE_END: case UPLOAD_FILE_END:
_close(); _close(upload.status);
break; break;
} }
} }
@ -83,7 +83,7 @@ void AutoConnectUploadHandler::upload(const String& requestUri, const HTTPUpload
class AutoConnectUploadFS : public AutoConnectUploadHandler { class AutoConnectUploadFS : public AutoConnectUploadHandler {
public: public:
explicit AutoConnectUploadFS(SPIFFST& media) : _media(&media) {} explicit AutoConnectUploadFS(SPIFFST& media) : _media(&media) {}
~AutoConnectUploadFS() { _close(); } ~AutoConnectUploadFS() { _close(HTTPUploadStatus::UPLOAD_FILE_END); }
protected: protected:
bool _open(const char* filename, const char* mode) override { bool _open(const char* filename, const char* mode) override {
@ -106,7 +106,8 @@ class AutoConnectUploadFS : public AutoConnectUploadHandler {
return -1; return -1;
} }
void _close(void) override { void _close(const HTTPUploadStatus status) override {
AC_UNUSED(status);
if (_file) if (_file)
_file.close(); _file.close();
_media->end(); _media->end();
@ -142,7 +143,7 @@ class AutoConnectUploadFS : public AutoConnectUploadHandler {
class AutoConnectUploadSD : public AutoConnectUploadHandler { class AutoConnectUploadSD : public AutoConnectUploadHandler {
public: public:
explicit AutoConnectUploadSD(SDClassT& media, const uint8_t cs = AUTOCONNECT_SD_CS, const uint32_t speed = AUTOCONNECT_SD_SPEED) : _media(&media), _cs(cs), _speed(speed) {} explicit AutoConnectUploadSD(SDClassT& media, const uint8_t cs = AUTOCONNECT_SD_CS, const uint32_t speed = AUTOCONNECT_SD_SPEED) : _media(&media), _cs(cs), _speed(speed) {}
~AutoConnectUploadSD() { _close(); } ~AutoConnectUploadSD() { _close(HTTPUploadStatus::UPLOAD_FILE_END); }
protected: protected:
bool _open(const char* filename, const char* mode) override { bool _open(const char* filename, const char* mode) override {
@ -205,7 +206,8 @@ class AutoConnectUploadSD : public AutoConnectUploadHandler {
return -1; return -1;
} }
void _close(void) override { void _close(const HTTPUploadStatus status) override {
AC_UNUSED(status);
if (_file) if (_file)
_file.close(); _file.close();
AutoConnectUtil::end<SDClassT>(_media); AutoConnectUtil::end<SDClassT>(_media);

Loading…
Cancel
Save