mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-12 01:53:07 +03:00
Add MD5 to core, Fix OTA examples and Digest Authentication to OTA and espota.py
This commit is contained in:
@ -2,119 +2,235 @@
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include "ArduinoOTA.h"
|
||||
#include "MD5Builder.h"
|
||||
|
||||
ArduinoOTA::ArduinoOTA(const char *mdns_host_prefix, int port, bool serial_debug)
|
||||
//#define OTA_DEBUG 1
|
||||
|
||||
ArduinoOTAClass::ArduinoOTAClass()
|
||||
{
|
||||
_port = port;
|
||||
_mdns_host = new String(mdns_host_prefix);
|
||||
*_mdns_host += String(ESP.getChipId(), HEX);
|
||||
_udp_ota = new WiFiUDP();
|
||||
_serial_debug = serial_debug;
|
||||
|
||||
_password = 0;
|
||||
_hostname = 0;
|
||||
_port = 0;
|
||||
_nonce = 0;
|
||||
_state = OTA_IDLE;
|
||||
|
||||
_size = 0;
|
||||
_cmd = 0;
|
||||
_ota_port = 0;
|
||||
_ota_ip = (uint32_t)0;
|
||||
_md5 = new char[33];
|
||||
|
||||
_start_callback = NULL;
|
||||
_end_callback = NULL;
|
||||
_progress_callback = NULL;
|
||||
_error_callback = NULL;
|
||||
}
|
||||
|
||||
void ArduinoOTA::onStart(OTA_CALLBACK(fn)){
|
||||
void ArduinoOTAClass::onStart(OTA_CALLBACK(fn)){
|
||||
_start_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTA::onEnd(OTA_CALLBACK(fn)){
|
||||
void ArduinoOTAClass::onEnd(OTA_CALLBACK(fn)){
|
||||
_end_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTA::onProgress(OTA_CALLBACK_PROGRESS(fn)){
|
||||
void ArduinoOTAClass::onProgress(OTA_CALLBACK_PROGRESS(fn)){
|
||||
_progress_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTA::onError(OTA_CALLBACK(fn)){
|
||||
void ArduinoOTAClass::onError(OTA_CALLBACK_ERROR(fn)){
|
||||
_error_callback = fn;
|
||||
}
|
||||
|
||||
ArduinoOTA::~ArduinoOTA(){
|
||||
ArduinoOTAClass::~ArduinoOTAClass(){
|
||||
delete _udp_ota;
|
||||
delete _mdns_host;
|
||||
}
|
||||
|
||||
void ArduinoOTA::setup() {
|
||||
_udp_ota->begin(_port);
|
||||
if (_mdns_host) {
|
||||
if (_serial_debug)
|
||||
Serial.printf("OTA server at: %s:%u\n",
|
||||
_mdns_host->c_str(),
|
||||
_port);
|
||||
MDNS.begin(_mdns_host->c_str());
|
||||
MDNS.addService("arduino", "tcp", _port);
|
||||
void ArduinoOTAClass::setPort(uint16_t port){
|
||||
if(!_initialized && !_port && port){
|
||||
_port = port;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTA::handle() {
|
||||
|
||||
void ArduinoOTAClass::setHostname(const char * hostname){
|
||||
if(!_initialized && !_hostname && hostname){
|
||||
_hostname = new char[strlen(hostname)];
|
||||
sprintf(_hostname, "%s", hostname);
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setPassword(const char * password){
|
||||
if(!_initialized && !_password && password){
|
||||
_password = new char[strlen(password)];
|
||||
sprintf(_password, "%s", password);
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::begin() {
|
||||
if(_initialized)
|
||||
return;
|
||||
_initialized = true;
|
||||
if(!_hostname){
|
||||
_hostname = new char[15];
|
||||
sprintf(_hostname, "esp8266-%02x", ESP.getChipId());
|
||||
}
|
||||
if(!_port)
|
||||
_port = 8266;
|
||||
|
||||
_udp_ota->begin(_port);
|
||||
MDNS.begin(_hostname);
|
||||
if(_password){
|
||||
_nonce = new char[33];
|
||||
MDNS.enableArduino(_port, true);
|
||||
} else
|
||||
MDNS.enableArduino(_port);
|
||||
_state = OTA_IDLE;
|
||||
#if OTA_DEBUG
|
||||
Serial.printf("OTA server at: %s.local:%u\n", _hostname, _port);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::_runUpdate(){
|
||||
if(!Update.begin(_size, _cmd)){
|
||||
#if OTA_DEBUG
|
||||
Serial.println("Update Begin Error");
|
||||
#endif
|
||||
if (_error_callback) _error_callback(OTA_BEGIN_ERROR);
|
||||
_udp_ota->begin(_port);
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
Update.setMD5(_md5);
|
||||
WiFiUDP::stopAll();
|
||||
WiFiClient::stopAll();
|
||||
|
||||
|
||||
if (_start_callback) _start_callback();
|
||||
if (_progress_callback) _progress_callback(0, _size);
|
||||
|
||||
WiFiClient client;
|
||||
if (!client.connect(_ota_ip, _ota_port)) {
|
||||
#if OTA_DEBUG
|
||||
Serial.printf("Connect Failed\n");
|
||||
#endif
|
||||
_udp_ota->begin(_port);
|
||||
if (_error_callback) _error_callback(OTA_CONNECT_ERROR);
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
|
||||
uint32_t written, total = 0;
|
||||
while(!Update.isFinished() && client.connected()){
|
||||
int waited = 1000;
|
||||
while(!client.available() && waited--)
|
||||
delay(1);
|
||||
if(!waited){
|
||||
#if OTA_DEBUG
|
||||
Serial.printf("Recieve Failed\n");
|
||||
#endif
|
||||
_udp_ota->begin(_port);
|
||||
if (_error_callback) _error_callback(OTA_RECIEVE_ERROR);
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
written = Update.write(client);
|
||||
if(written > 0){
|
||||
client.print(written, DEC);
|
||||
total += written;
|
||||
if(_progress_callback) _progress_callback(total, _size);
|
||||
}
|
||||
}
|
||||
|
||||
if(Update.end()){
|
||||
client.print("OK");
|
||||
#if OTA_DEBUG
|
||||
Serial.printf("Update Success\nRebooting...\n");
|
||||
#endif
|
||||
if(_end_callback) _end_callback();
|
||||
ESP.restart();
|
||||
} else {
|
||||
_udp_ota->begin(_port);
|
||||
if (_error_callback) _error_callback(OTA_END_ERROR);
|
||||
Update.printError(client);
|
||||
#if OTA_DEBUG
|
||||
Update.printError(Serial);
|
||||
#endif
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::handle() {
|
||||
if (!*_udp_ota) {
|
||||
_udp_ota->begin(_port);
|
||||
if (_serial_debug) {
|
||||
Serial.println("OTA restarted");
|
||||
}
|
||||
#if OTA_DEBUG
|
||||
Serial.println("OTA restarted");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!_udp_ota->parsePacket()) return;
|
||||
|
||||
IPAddress remote = _udp_ota->remoteIP();
|
||||
int cmd = _udp_ota->parseInt();
|
||||
int port = _udp_ota->parseInt();
|
||||
int size = _udp_ota->parseInt();
|
||||
if(_state == OTA_IDLE){
|
||||
_ota_ip = _udp_ota->remoteIP();
|
||||
_cmd = _udp_ota->parseInt();
|
||||
_ota_port = _udp_ota->parseInt();
|
||||
_size = _udp_ota->parseInt();
|
||||
_udp_ota->read();
|
||||
sprintf(_md5, "%s", _udp_ota->readStringUntil('\n').c_str());
|
||||
|
||||
if (_serial_debug){
|
||||
Serial.print("Update Start: ip:");
|
||||
Serial.print(remote);
|
||||
Serial.printf(", port:%d, size:%d\n", port, size);
|
||||
}
|
||||
|
||||
WiFiUDP::stopAll();
|
||||
|
||||
if(!Update.begin(size, cmd)){
|
||||
if (_serial_debug)
|
||||
Serial.println("Update Begin Error");
|
||||
if (_error_callback) _error_callback();
|
||||
_udp_ota->begin(_port);
|
||||
return;
|
||||
}
|
||||
if (_start_callback) _start_callback();
|
||||
if (_progress_callback) _progress_callback(0, size);
|
||||
|
||||
WiFiClient client;
|
||||
if (!client.connect(remote, port)) {
|
||||
if (_serial_debug)
|
||||
Serial.printf("Connect Failed\n");
|
||||
_udp_ota->begin(_port);
|
||||
if (_error_callback) _error_callback();
|
||||
}
|
||||
|
||||
uint32_t written;
|
||||
while(!Update.isFinished() && client.connected()){
|
||||
// TODO(mangelajo): enhance the Update.write(client) to
|
||||
// accept a progress callback
|
||||
written = Update.write(client);
|
||||
if(written > 0) client.print(written, DEC);
|
||||
if(_progress_callback) _progress_callback(written, size);
|
||||
}
|
||||
|
||||
Serial.setDebugOutput(false);
|
||||
|
||||
if(Update.end()){
|
||||
client.println("OK");
|
||||
if (_serial_debug)
|
||||
Serial.printf("Update Success\nRebooting...\n");
|
||||
if(_end_callback) _end_callback();
|
||||
ESP.restart();
|
||||
} else {
|
||||
// Update failed: listen UDP again, callback and print
|
||||
_udp_ota->begin(_port);
|
||||
if (_error_callback) _error_callback();
|
||||
Update.printError(client);
|
||||
if (_serial_debug)
|
||||
Update.printError(Serial);
|
||||
#if OTA_DEBUG
|
||||
Serial.print("Update Start: ip:");
|
||||
Serial.print(_ota_ip);
|
||||
Serial.printf(", port:%d, size:%d, md5:%s\n", _ota_port, _size, _md5);
|
||||
#endif
|
||||
|
||||
_udp_ota->beginPacket(_ota_ip, _udp_ota->remotePort());
|
||||
if(_password){
|
||||
MD5Builder nonce_md5;
|
||||
nonce_md5.begin();
|
||||
nonce_md5.add(String(micros()));
|
||||
nonce_md5.calculate();
|
||||
nonce_md5.getChars(_nonce);
|
||||
_udp_ota->printf("AUTH %s", _nonce);
|
||||
_udp_ota->endPacket();
|
||||
_state = OTA_WAITAUTH;
|
||||
return;
|
||||
} else {
|
||||
_udp_ota->print("OK");
|
||||
_udp_ota->endPacket();
|
||||
_state = OTA_RUNUPDATE;
|
||||
}
|
||||
} else if(_state == OTA_WAITAUTH){
|
||||
String cnonce = _udp_ota->readStringUntil(' ');
|
||||
String response = _udp_ota->readStringUntil('\n');
|
||||
|
||||
MD5Builder _passmd5;
|
||||
_passmd5.begin();
|
||||
_passmd5.add(_password);
|
||||
_passmd5.calculate();
|
||||
String passmd5 = _passmd5.toString();
|
||||
|
||||
String challenge = passmd5 + ":" + String(_nonce) + ":" + cnonce;
|
||||
MD5Builder _challengemd5;
|
||||
_challengemd5.begin();
|
||||
_challengemd5.add(challenge);
|
||||
_challengemd5.calculate();
|
||||
String result = _challengemd5.toString();
|
||||
|
||||
if(result.equals(response)){
|
||||
_udp_ota->beginPacket(_ota_ip, _udp_ota->remotePort());
|
||||
_udp_ota->print("OK");
|
||||
_udp_ota->endPacket();
|
||||
_state = OTA_RUNUPDATE;
|
||||
} else {
|
||||
_udp_ota->beginPacket(_ota_ip, _udp_ota->remotePort());
|
||||
_udp_ota->print("Authentication Failed");
|
||||
_udp_ota->endPacket();
|
||||
if (_error_callback) _error_callback(OTA_AUTH_ERROR);
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
if(_state == OTA_RUNUPDATE)
|
||||
_runUpdate();
|
||||
}
|
||||
|
||||
ArduinoOTAClass ArduinoOTA;
|
||||
|
@ -5,31 +5,58 @@ class WiFiUDP;
|
||||
|
||||
#define OTA_CALLBACK(callback) void (*callback)()
|
||||
#define OTA_CALLBACK_PROGRESS(callback) void (*callback)(unsigned int, unsigned int)
|
||||
#define OTA_CALLBACK_ERROR(callback) void (*callback)(ota_error_t)
|
||||
|
||||
class ArduinoOTA
|
||||
typedef enum {
|
||||
OTA_IDLE,
|
||||
OTA_WAITAUTH,
|
||||
OTA_RUNUPDATE
|
||||
} ota_state_t;
|
||||
|
||||
typedef enum {
|
||||
OTA_AUTH_ERROR,
|
||||
OTA_BEGIN_ERROR,
|
||||
OTA_CONNECT_ERROR,
|
||||
OTA_RECIEVE_ERROR,
|
||||
OTA_END_ERROR
|
||||
} ota_error_t;
|
||||
|
||||
class ArduinoOTAClass
|
||||
{
|
||||
private:
|
||||
int _port;
|
||||
String* _mdns_host;
|
||||
char *_password;
|
||||
char * _hostname;
|
||||
char * _nonce;
|
||||
WiFiUDP* _udp_ota;
|
||||
bool _serial_debug;
|
||||
bool _initialized;
|
||||
|
||||
ota_state_t _state;
|
||||
int _size, _cmd, _ota_port;
|
||||
IPAddress _ota_ip;
|
||||
char * _md5;
|
||||
|
||||
OTA_CALLBACK(_start_callback);
|
||||
OTA_CALLBACK(_end_callback);
|
||||
OTA_CALLBACK(_error_callback);
|
||||
|
||||
OTA_CALLBACK_ERROR(_error_callback);
|
||||
OTA_CALLBACK_PROGRESS(_progress_callback);
|
||||
|
||||
void _runUpdate(void);
|
||||
|
||||
public:
|
||||
ArduinoOTA(const char *mdns_host="ESP8266-OTA-",
|
||||
int port=8266,
|
||||
bool serial_debug=true);
|
||||
~ArduinoOTA();
|
||||
void setup();
|
||||
void handle();
|
||||
void onStart(OTA_CALLBACK(fn));
|
||||
void onEnd(OTA_CALLBACK(fn));
|
||||
void onProgress(OTA_CALLBACK_PROGRESS(fn));
|
||||
void onError(OTA_CALLBACK (fn));
|
||||
public:
|
||||
ArduinoOTAClass();
|
||||
~ArduinoOTAClass();
|
||||
void setPort(uint16_t port);
|
||||
void setHostname(const char *hostname);
|
||||
void setPassword(const char *password);
|
||||
void onStart(OTA_CALLBACK(fn));
|
||||
void onEnd(OTA_CALLBACK(fn));
|
||||
void onProgress(OTA_CALLBACK_PROGRESS(fn));
|
||||
void onError(OTA_CALLBACK_ERROR (fn));
|
||||
void begin();
|
||||
void handle();
|
||||
};
|
||||
|
||||
extern ArduinoOTAClass ArduinoOTA;
|
||||
|
||||
#endif /* __ARDUINO_OTA_H */
|
||||
|
@ -6,26 +6,36 @@
|
||||
const char* ssid = "...";
|
||||
const char* password = "...";
|
||||
|
||||
ArduinoOTA ota_server;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("Booting");
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
/* try the flash stored password first */
|
||||
WiFi.begin();
|
||||
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED){
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("Retrying connection...");
|
||||
Serial.begin(115200);
|
||||
Serial.println("Booting");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED){
|
||||
Serial.println("Connection Failed! Rebooting...");
|
||||
delay(5000);
|
||||
ESP.reset();
|
||||
}
|
||||
ota_server.setup();
|
||||
//ArduinoOTA.setPort(8266);//Defaults to 8266
|
||||
//ArduinoOTA.setHostname((const char *)"myesp8266");//Defaults to esp8266-[ChipID]
|
||||
//ArduinoOTA.setPassword((const char *)"123");//defaults to no authentication
|
||||
ArduinoOTA.onStart([]() { Serial.println("Start"); });
|
||||
ArduinoOTA.onEnd([]() { Serial.println("End"); });
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\n", (progress/(total/100)));
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if(error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
|
||||
else if(error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
|
||||
else if(error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
|
||||
else if(error == OTA_RECIEVE_ERROR) Serial.println("Recieve Failed");
|
||||
else if(error == OTA_END_ERROR) Serial.println("End Failed");
|
||||
});
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ota_server.handle();
|
||||
yield();
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
|
@ -5,9 +5,7 @@
|
||||
|
||||
const char* ssid = "...";
|
||||
const char* password = "...";
|
||||
const char* host_prefix = "OTA-LEDS-";
|
||||
|
||||
ArduinoOTA ota_server(host_prefix, 8266, /* debug_serial= */ true);
|
||||
const char* host = "OTA-LEDS";
|
||||
|
||||
int led_pin = 13;
|
||||
#define N_DIMMERS 3
|
||||
@ -23,8 +21,7 @@ void setup() {
|
||||
Serial.println("Booting");
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
/* try the flash stored password first */
|
||||
WiFi.begin();
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED){
|
||||
WiFi.begin(ssid, password);
|
||||
@ -33,10 +30,6 @@ void setup() {
|
||||
/* switch off led */
|
||||
digitalWrite(led_pin, HIGH);
|
||||
|
||||
/* setup the OTA server */
|
||||
ota_server.setup();
|
||||
Serial.println("Ready");
|
||||
|
||||
/* configure dimmers, and OTA server events */
|
||||
analogWriteRange(1000);
|
||||
analogWrite(led_pin,990);
|
||||
@ -47,13 +40,14 @@ void setup() {
|
||||
analogWrite(dimmer_pin[i],50);
|
||||
}
|
||||
|
||||
ota_server.onStart([]() { // switch off all the PWMs during upgrade
|
||||
ArduinoOTA.setHostname(host);
|
||||
ArduinoOTA.onStart([]() { // switch off all the PWMs during upgrade
|
||||
for(int i=0; i<N_DIMMERS;i++)
|
||||
analogWrite(dimmer_pin[i], 0);
|
||||
analogWrite(led_pin,0);
|
||||
});
|
||||
|
||||
ota_server.onEnd([]() { // do a fancy thing with our board led at end
|
||||
ArduinoOTA.onEnd([]() { // do a fancy thing with our board led at end
|
||||
for (int i=0;i<30;i++)
|
||||
{
|
||||
analogWrite(led_pin,(i*100) % 1001);
|
||||
@ -61,11 +55,14 @@ void setup() {
|
||||
}
|
||||
});
|
||||
|
||||
ota_server.onError([]() { ESP.restart(); });
|
||||
ArduinoOTA.onError([](ota_error_t error) { ESP.restart(); });
|
||||
|
||||
/* setup the OTA server */
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ota_server.handle();
|
||||
yield();
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
|
Reference in New Issue
Block a user