Skip to content

Commit c67c107

Browse files
committed
Added OTA and MDNS
Added OTA update functionality and MDNS transponder.
1 parent 0f7e57c commit c67c107

File tree

3 files changed

+210
-8
lines changed

3 files changed

+210
-8
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* Ported the ESP8266HTTPUpdateServer published by Arduino-core to
3+
* provide the web browser based OTA update on the ESP32 platform.
4+
* @file HTTPUpdateServer.cpp
5+
6+
* @version 0.9.10
7+
* @date 2019-06-06
8+
* @copyright MIT license.
9+
*/
10+
11+
#ifdef ARDUINO_ARCH_ESP32
12+
// This class will available only EPS32 actually.
13+
14+
#include <Arduino.h>
15+
#include <WiFiClient.h>
16+
#include <WiFiServer.h>
17+
#include <WebServer.h>
18+
#include <WiFiUdp.h>
19+
#include <Update.h>
20+
#include "StreamString.h"
21+
#include "HTTPUpdateServer.h"
22+
23+
static const char serverIndex[] PROGMEM = R"(
24+
<html><body>
25+
<form method='POST' action='' enctype='multipart/form-data'>
26+
<input type='file' name='update'>
27+
<input type='submit' value='Update'>
28+
</form>
29+
</body></html>
30+
)";
31+
32+
static const char successResponse[] PROGMEM =
33+
"<meta http-equiv=\"refresh\" content=\"15;URL=/\">Update Success! Rebooting...\n";
34+
35+
/**
36+
* Setup for the web update. Register the authentication procedure and
37+
* binary file upload handler required to update the actual sketch binary by OTA.
38+
* @param server A pointer to the WebServer instance
39+
* @param path URI of the update handler
40+
* @param username Username for authentication
41+
* @arama password Password for authentication
42+
*/
43+
void HTTPUpdateServer::setup(WebServer* server, const String& path, const String& username, const String& password) {
44+
_server = server;
45+
_username = username;
46+
_password = password;
47+
48+
// handler for the /update form page
49+
_server->on(path.c_str(), HTTP_GET, [&] () {
50+
if (_username != _emptyString && _password != _emptyString && !_server->authenticate(_username.c_str(), _password.c_str()))
51+
return _server->requestAuthentication();
52+
_server->send_P(200, PSTR("text/html"), serverIndex);
53+
});
54+
55+
// handler for the /update form POST (once file upload finishes)
56+
_server->on(path.c_str(), HTTP_POST, [&] () {
57+
if(!_authenticated)
58+
return _server->requestAuthentication();
59+
if (Update.hasError()) {
60+
_server->send(200, F("text/html"), String(F("Update error: ")) + _updaterError);
61+
}
62+
else {
63+
_server->client().setNoDelay(true);
64+
_server->send_P(200, PSTR("text/html"), successResponse);
65+
delay(100);
66+
_server->client().stop();
67+
ESP.restart();
68+
}
69+
}, [&] () {
70+
// handler for the file upload, get's the sketch bytes, and writes
71+
// them through the Update object
72+
HTTPUpload& upload = _server->upload();
73+
74+
if (upload.status == UPLOAD_FILE_START) {
75+
_updaterError = String();
76+
if (_serial_output)
77+
Serial.setDebugOutput(true);
78+
79+
_authenticated = (_username == _emptyString || _password == _emptyString || _server->authenticate(_username.c_str(), _password.c_str()));
80+
if (!_authenticated) {
81+
if (_serial_output)
82+
Serial.println("Unauthenticated Update");
83+
return;
84+
}
85+
86+
if (_serial_output)
87+
Serial.printf("Update: %s\n", upload.filename.c_str());
88+
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
89+
if (!Update.begin(maxSketchSpace)) { //start with max available size
90+
_setUpdaterError();
91+
}
92+
}
93+
else if (_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()) {
94+
if (_serial_output)
95+
Serial.print('.');
96+
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize)
97+
_setUpdaterError();
98+
}
99+
else if (_authenticated && upload.status == UPLOAD_FILE_END && !_updaterError.length()) {
100+
if (Update.end(true)) { //true to set the size to the current progress
101+
if (_serial_output)
102+
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
103+
}
104+
else {
105+
_setUpdaterError();
106+
}
107+
if (_serial_output)
108+
Serial.setDebugOutput(false);
109+
}
110+
else if (_authenticated && upload.status == UPLOAD_FILE_ABORTED) {
111+
Update.end();
112+
if (_serial_output)
113+
Serial.println("Update was aborted");
114+
}
115+
delay(0);
116+
});
117+
}
118+
119+
/**
120+
* Convert the update error code to string for notation.
121+
*/
122+
void HTTPUpdateServer::_setUpdaterError() {
123+
if (_serial_output)
124+
Update.printError(Serial);
125+
StreamString str;
126+
Update.printError(str);
127+
_updaterError = str.c_str();
128+
}
129+
130+
/**
131+
* Shared empty String instance
132+
*/
133+
const String HTTPUpdateServer::_emptyString;
134+
135+
#endif // !ARDUINO_ARCH_ESP32
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Ported the ESP8266HTTPUpdateServer published by Arduino-core to
3+
* provide the web browser based OTA update on the ESP32 platform.
4+
* @file HTTPUpdateServer.h
5+
6+
* @version 0.9.10
7+
* @date 2019-06-06
8+
* @copyright MIT license.
9+
*/
10+
11+
#ifndef __HTTP_UPDATE_SERVER_H
12+
#define __HTTP_UPDATE_SERVER_H
13+
14+
#ifdef ARDUINO_ARCH_ESP32
15+
// This class will available only EPS32 actually.
16+
17+
class WebServer;
18+
19+
class HTTPUpdateServer {
20+
public:
21+
explicit HTTPUpdateServer(bool serial_debug = false) : _serial_output(serial_debug), _server(nullptr), _username(_emptyString), _password(_emptyString), _authenticated(false) {}
22+
~HTTPUpdateServer() {}
23+
void setup(WebServer* server) { setup(server, _emptyString, _emptyString); }
24+
void setup(WebServer* server, const String& path) { setup(server, path, _emptyString, _emptyString); }
25+
void setup(WebServer* server, const String& username, const String& password) { setup(server, "/update", username, password); }
26+
void setup(WebServer* server, const String& path, const String& username, const String& password);
27+
void updateCredentials(const String& username, const String& password) { _username = username; _password = password; }
28+
29+
protected:
30+
void _setUpdaterError();
31+
32+
private:
33+
bool _serial_output;
34+
WebServer* _server;
35+
String _username;
36+
String _password;
37+
bool _authenticated;
38+
String _updaterError;
39+
static const String _emptyString;
40+
};
41+
42+
#endif // !ARDUINO_ARCH_ESP32
43+
#endif // !__HTTP_UPDATE_SERVER_H

Node main module/src/main.cpp

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
#include <WiFi.h>
88
#include <HTTPClient.h>
99
#include <WebServer.h>
10+
#include <ESPmDNS.h>
1011
#include <AutoConnect.h>
12+
#include "HTTPUpdateServer.h"
1113
#include <AutoConnectCredential.h>
1214
#include <ArduinoJson.h>
1315

@@ -16,16 +18,16 @@
1618

1719
// Time is in milliseconds
1820
#define LED_TICKER 33
21+
#define LED_BUILTIN 2
1922
#define BUTTON_PIN 32
2023
#define DEFAULT_UPDATE_INTERVAL 60000
2124
#define LIVE_SENSOR_INTERVAL 1000
2225
#define SETTINGS_FILE "/settings.txt"
23-
#define DATA_TRANSMISSION_TIMEOUT 20 // arbitrary number
26+
#define DATA_TRANSMISSION_TIMEOUT 32 // arbitrary number
2427
#define REBOOT_BUTTON_HOLD_DURATION 3000
2528
#define FACTORY_RESET_BUTTON_HOLD_DURATION 10000
2629

2730

28-
2931
byte modules[MAX_SENSORS]; // array with address listings of connected sensor modules
3032
AsyncDelay delay_sensor_update; // delay timer for asynchronous update interval
3133
AsyncDelay delay_sensor_view; // 1 second delay for real time sensor viewing
@@ -44,6 +46,8 @@ String nodeLEDSetting = "On";
4446
unsigned long currentUpdateRate = DEFAULT_UPDATE_INTERVAL;
4547

4648
WebServer server; // HTTP server to serve web UI
49+
HTTPUpdateServer updateServer(true); // OTA update handler, true param is for serial debug
50+
AutoConnectAux update("/update", "Update");
4751
AutoConnect Portal(server); // AutoConnect handler object
4852
AutoConnectConfig portalConfig("MainModuleAP", "12345678");
4953

@@ -130,17 +134,17 @@ void asyncBlink(unsigned long ms = 0)
130134

131135
// Button input checking function
132136
void checkButton(){
133-
static unsigned long pushedDownTime = NULL;
134-
if (pushedDownTime == NULL && digitalRead(BUTTON_PIN) == LOW){ // Button being pressed
137+
static unsigned long pushedDownTime = 0;
138+
if (pushedDownTime == 0 && digitalRead(BUTTON_PIN) == LOW){ // Button being pressed
135139
pushedDownTime = millis();
136140

137-
}else if (pushedDownTime != NULL && digitalRead(BUTTON_PIN) == LOW){ // Pressing the button, change the LED light according to the pressing time.
141+
}else if (pushedDownTime != 0 && digitalRead(BUTTON_PIN) == LOW){ // Pressing the button, change the LED light according to the pressing time.
138142
unsigned int pressingDuration = millis() - pushedDownTime;
139143
if (pressingDuration > REBOOT_BUTTON_HOLD_DURATION && pressingDuration < FACTORY_RESET_BUTTON_HOLD_DURATION){
140144
asyncBlink(FACTORY_RESET_BUTTON_HOLD_DURATION - pressingDuration);
141145
}
142146
}
143-
else if (pushedDownTime != NULL && digitalRead(BUTTON_PIN) == HIGH ){ // Button released, check the pressed duration and peform action
147+
else if (pushedDownTime != 0 && digitalRead(BUTTON_PIN) == HIGH ){ // Button released, check the pressed duration and peform action
144148
unsigned int pressingDuration = millis() - pushedDownTime;
145149

146150
if (pressingDuration > FACTORY_RESET_BUTTON_HOLD_DURATION){
@@ -153,7 +157,7 @@ void checkButton(){
153157
ESP.restart();
154158
}
155159

156-
pushedDownTime = NULL;
160+
pushedDownTime = 0;
157161
}
158162
}
159163

@@ -618,6 +622,10 @@ void setup()
618622
server.on("/save_settings", handle_SaveSettings);
619623
server.on("/getJSON", handle_getSensorJSON);
620624

625+
// setup update server
626+
updateServer.setup(&server);
627+
Serial.println("OTA update server started.");
628+
621629
// setup AutoConnect with a configuration
622630
portalConfig.title = "Main Module v1.0";
623631
portalConfig.apid = "MainModule-" + String((uint32_t)(ESP.getEfuseMac() >> 32), HEX);
@@ -628,6 +636,8 @@ void setup()
628636
portalConfig.tickerPort = LED_TICKER;
629637
portalConfig.tickerOn = HIGH;
630638
Portal.config(portalConfig);
639+
// add update page aux
640+
Portal.join({update});
631641

632642
// load custom page JSON and build pages into memory
633643
if(!Portal.load(customPageJSON))
@@ -653,10 +663,24 @@ void setup()
653663
Serial.print("IP: ");
654664
Serial.println(WiFi.localIP());
655665
Serial.println();
666+
// initialize MDNS
667+
String mdnshostname = nodeName;
668+
mdnshostname.toLowerCase();
669+
if(MDNS.begin(mdnshostname.c_str()))
670+
{
671+
MDNS.addService("http","tcp",80);
672+
Serial.println("MDNS transponder started.");
673+
Serial.println("Access at http://" + mdnshostname + ".local");
674+
}
675+
else
676+
{
677+
Serial.println("MDNS Initialization failed. Service will not be available.");
678+
}
679+
656680
}
657681
else
658682
{
659-
Serial.println("Connection failed, rebooting.");
683+
Serial.println("Portal initialization failed, rebooting.");
660684
ESP.restart();
661685
}
662686

0 commit comments

Comments
 (0)