mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-21 10:26:06 +03:00
commit
450718b4a4
43
cores/esp8266/MD5Builder.cpp
Normal file
43
cores/esp8266/MD5Builder.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include "Arduino.h"
|
||||||
|
#include "md5.h"
|
||||||
|
#include "MD5Builder.h"
|
||||||
|
|
||||||
|
#define hex_char_to_byte(c) (((c)>='a'&&(c)<='f')?((c)-87):((c)>='A'&&(c)<='F')?((c)-55):((c)>='0'&&(c)<='9')?((c)-48):0)
|
||||||
|
|
||||||
|
void MD5Builder::begin(void){
|
||||||
|
memset(_buf, 0x00, 16);
|
||||||
|
MD5Init(&_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5Builder::add(uint8_t * data, uint16_t len){
|
||||||
|
MD5Update(&_ctx, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5Builder::addHexString(const char * data){
|
||||||
|
uint16_t i, len = strlen(data);
|
||||||
|
uint8_t * tmp = (uint8_t*)malloc(len/2);
|
||||||
|
if(tmp == NULL)
|
||||||
|
return;
|
||||||
|
for(i=0; i<len; i+=2) tmp[i/2] = (hex_char_to_byte(data[i]) & 0x0F) << 4 | (hex_char_to_byte(data[i+1]) & 0x0F);
|
||||||
|
add(tmp, len/2);
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5Builder::calculate(void){
|
||||||
|
MD5Final(_buf, &_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5Builder::getBytes(uint8_t * output){
|
||||||
|
memcpy(output, _buf, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5Builder::getChars(char * output){
|
||||||
|
for(uint8_t i = 0; i < 16; i++)
|
||||||
|
sprintf(output + (i * 2), "%02x", _buf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
String MD5Builder::toString(void){
|
||||||
|
char out[32];
|
||||||
|
getChars(out);
|
||||||
|
return String(out);
|
||||||
|
}
|
47
cores/esp8266/MD5Builder.h
Normal file
47
cores/esp8266/MD5Builder.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
md5.h - exposed md5 ROM functions for esp8266
|
||||||
|
|
||||||
|
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||||
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#ifndef __ESP8266_MD5_BUILDER__
|
||||||
|
#define __ESP8266_MD5_BUILDER__
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "md5.h"
|
||||||
|
|
||||||
|
class MD5Builder {
|
||||||
|
private:
|
||||||
|
md5_context_t _ctx;
|
||||||
|
uint8_t _buf[16];
|
||||||
|
public:
|
||||||
|
void begin(void);
|
||||||
|
void add(uint8_t * data, uint16_t len);
|
||||||
|
void add(const char * data){ add((uint8_t*)data, strlen(data)); }
|
||||||
|
void add(char * data){ add((const char*)data); }
|
||||||
|
void add(String data){ add(data.c_str()); }
|
||||||
|
void addHexString(const char * data);
|
||||||
|
void addHexString(char * data){ addHexString((const char*)data); }
|
||||||
|
void addHexString(String data){ addHexString(data.c_str()); }
|
||||||
|
void calculate(void);
|
||||||
|
void getBytes(uint8_t * output);
|
||||||
|
void getChars(char * output);
|
||||||
|
String toString(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -32,6 +32,7 @@ void UpdaterClass::_reset() {
|
|||||||
_currentAddress = 0;
|
_currentAddress = 0;
|
||||||
_size = 0;
|
_size = 0;
|
||||||
_command = U_FLASH;
|
_command = U_FLASH;
|
||||||
|
_target_md5 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UpdaterClass::begin(size_t size, int command) {
|
bool UpdaterClass::begin(size_t size, int command) {
|
||||||
@ -96,10 +97,17 @@ bool UpdaterClass::begin(size_t size, int command) {
|
|||||||
_size = size;
|
_size = size;
|
||||||
_buffer = new uint8_t[FLASH_SECTOR_SIZE];
|
_buffer = new uint8_t[FLASH_SECTOR_SIZE];
|
||||||
_command = command;
|
_command = command;
|
||||||
|
|
||||||
|
_target_md5 = new char[64];
|
||||||
|
_md5.begin();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UpdaterClass::setMD5(const char * expected_md5){
|
||||||
|
if(strlen(expected_md5) != 32) return;
|
||||||
|
strcpy(_target_md5, expected_md5);
|
||||||
|
}
|
||||||
|
|
||||||
bool UpdaterClass::end(bool evenIfRemaining){
|
bool UpdaterClass::end(bool evenIfRemaining){
|
||||||
if(_size == 0){
|
if(_size == 0){
|
||||||
#ifdef DEBUG_UPDATER
|
#ifdef DEBUG_UPDATER
|
||||||
@ -123,7 +131,21 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
|||||||
}
|
}
|
||||||
_size = progress();
|
_size = progress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_md5.calculate();
|
||||||
|
if(_target_md5 && strlen(_target_md5) == 32){
|
||||||
|
if(strcmp(_target_md5, _md5.toString().c_str()) != 0){
|
||||||
|
_error = UPDATE_ERROR_MD5;
|
||||||
|
#ifdef DEBUG_UPDATER
|
||||||
|
DEBUG_UPDATER.printf("MD5 Failed: expected:%s, calculated:%s\n", _target_md5, _md5.toString().c_str());
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_UPDATER
|
||||||
|
else DEBUG_UPDATER.printf("MD5 Success: %s\n", _md5.toString().c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if (_command == U_FLASH) {
|
if (_command == U_FLASH) {
|
||||||
eboot_command ebcmd;
|
eboot_command ebcmd;
|
||||||
ebcmd.action = ACTION_COPY_RAW;
|
ebcmd.action = ACTION_COPY_RAW;
|
||||||
@ -157,6 +179,7 @@ bool UpdaterClass::_writeBuffer(){
|
|||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
_md5.add(_buffer, _bufferLen);
|
||||||
_currentAddress += _bufferLen;
|
_currentAddress += _bufferLen;
|
||||||
_bufferLen = 0;
|
_bufferLen = 0;
|
||||||
return true;
|
return true;
|
||||||
@ -232,6 +255,8 @@ void UpdaterClass::printError(Stream &out){
|
|||||||
out.println("Bad Size Given");
|
out.println("Bad Size Given");
|
||||||
} else if(_error == UPDATE_ERROR_STREAM){
|
} else if(_error == UPDATE_ERROR_STREAM){
|
||||||
out.println("Stream Read Timeout");
|
out.println("Stream Read Timeout");
|
||||||
|
} else if(_error == UPDATE_ERROR_MD5){
|
||||||
|
out.println("MD5 Check Failed");
|
||||||
} else {
|
} else {
|
||||||
out.println("UNKNOWN");
|
out.println("UNKNOWN");
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "flash_utils.h"
|
#include "flash_utils.h"
|
||||||
|
#include "MD5Builder.h"
|
||||||
|
|
||||||
#define UPDATE_ERROR_OK 0
|
#define UPDATE_ERROR_OK 0
|
||||||
#define UPDATE_ERROR_WRITE 1
|
#define UPDATE_ERROR_WRITE 1
|
||||||
@ -10,11 +11,12 @@
|
|||||||
#define UPDATE_ERROR_SPACE 3
|
#define UPDATE_ERROR_SPACE 3
|
||||||
#define UPDATE_ERROR_SIZE 4
|
#define UPDATE_ERROR_SIZE 4
|
||||||
#define UPDATE_ERROR_STREAM 5
|
#define UPDATE_ERROR_STREAM 5
|
||||||
|
#define UPDATE_ERROR_MD5 6
|
||||||
|
|
||||||
#define U_FLASH 0
|
#define U_FLASH 0
|
||||||
#define U_SPIFFS 100
|
#define U_SPIFFS 100
|
||||||
|
|
||||||
//#define DEBUG_UPDATER Serial1
|
//#define DEBUG_UPDATER Serial
|
||||||
|
|
||||||
class UpdaterClass {
|
class UpdaterClass {
|
||||||
public:
|
public:
|
||||||
@ -56,7 +58,22 @@ class UpdaterClass {
|
|||||||
Prints the last error to an output stream
|
Prints the last error to an output stream
|
||||||
*/
|
*/
|
||||||
void printError(Stream &out);
|
void printError(Stream &out);
|
||||||
|
|
||||||
|
/*
|
||||||
|
sets the expected MD5 for the firmware (hexString)
|
||||||
|
*/
|
||||||
|
void setMD5(const char * expected_md5);
|
||||||
|
|
||||||
|
/*
|
||||||
|
returns the MD5 String of the sucessfully ended firmware
|
||||||
|
*/
|
||||||
|
String md5String(void){ return _md5.toString(); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
populated the result with the md5 bytes of the sucessfully ended firmware
|
||||||
|
*/
|
||||||
|
void md5(uint8_t * result){ return _md5.getBytes(result); }
|
||||||
|
|
||||||
//Helpers
|
//Helpers
|
||||||
uint8_t getError(){ return _error; }
|
uint8_t getError(){ return _error; }
|
||||||
void clearError(){ _error = UPDATE_ERROR_OK; }
|
void clearError(){ _error = UPDATE_ERROR_OK; }
|
||||||
@ -103,7 +120,7 @@ class UpdaterClass {
|
|||||||
}
|
}
|
||||||
if(remaining() == 0)
|
if(remaining() == 0)
|
||||||
return written;
|
return written;
|
||||||
yield();
|
delay(1);
|
||||||
available = data.available();
|
available = data.available();
|
||||||
}
|
}
|
||||||
return written;
|
return written;
|
||||||
@ -120,6 +137,9 @@ class UpdaterClass {
|
|||||||
uint32_t _startAddress;
|
uint32_t _startAddress;
|
||||||
uint32_t _currentAddress;
|
uint32_t _currentAddress;
|
||||||
uint32_t _command;
|
uint32_t _command;
|
||||||
|
|
||||||
|
char *_target_md5;
|
||||||
|
MD5Builder _md5;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern UpdaterClass Update;
|
extern UpdaterClass Update;
|
||||||
|
44
cores/esp8266/md5.h
Normal file
44
cores/esp8266/md5.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
md5.h - exposed md5 ROM functions for esp8266
|
||||||
|
|
||||||
|
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||||
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
|
||||||
|
original C source from https://github.com/morrissinger/ESP8266-Websocket/raw/master/MD5.h
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#ifndef __ESP8266_MD5__
|
||||||
|
#define __ESP8266_MD5__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t state[4];
|
||||||
|
uint32_t count[2];
|
||||||
|
uint8_t buffer[64];
|
||||||
|
} md5_context_t;
|
||||||
|
|
||||||
|
extern void MD5Init (md5_context_t *);
|
||||||
|
extern void MD5Update (md5_context_t *, uint8_t *, uint16_t);
|
||||||
|
extern void MD5Final (uint8_t [16], md5_context_t *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -2,119 +2,254 @@
|
|||||||
#include <ESP8266mDNS.h>
|
#include <ESP8266mDNS.h>
|
||||||
#include <WiFiUdp.h>
|
#include <WiFiUdp.h>
|
||||||
#include "ArduinoOTA.h"
|
#include "ArduinoOTA.h"
|
||||||
|
#include "MD5Builder.h"
|
||||||
|
|
||||||
ArduinoOTA::ArduinoOTA(const char *mdns_host_prefix, int port, bool serial_debug)
|
//#define OTA_DEBUG 1
|
||||||
|
|
||||||
|
#define U_AUTH 200
|
||||||
|
|
||||||
|
ArduinoOTAClass::ArduinoOTAClass()
|
||||||
{
|
{
|
||||||
_port = port;
|
|
||||||
_mdns_host = new String(mdns_host_prefix);
|
|
||||||
*_mdns_host += String(ESP.getChipId(), HEX);
|
|
||||||
_udp_ota = new WiFiUDP();
|
_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;
|
_start_callback = NULL;
|
||||||
_end_callback = NULL;
|
_end_callback = NULL;
|
||||||
_progress_callback = NULL;
|
_progress_callback = NULL;
|
||||||
_error_callback = NULL;
|
_error_callback = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArduinoOTA::onStart(OTA_CALLBACK(fn)){
|
void ArduinoOTAClass::onStart(OTA_CALLBACK(fn)){
|
||||||
_start_callback = fn;
|
_start_callback = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArduinoOTA::onEnd(OTA_CALLBACK(fn)){
|
void ArduinoOTAClass::onEnd(OTA_CALLBACK(fn)){
|
||||||
_end_callback = fn;
|
_end_callback = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArduinoOTA::onProgress(OTA_CALLBACK_PROGRESS(fn)){
|
void ArduinoOTAClass::onProgress(OTA_CALLBACK_PROGRESS(fn)){
|
||||||
_progress_callback = fn;
|
_progress_callback = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArduinoOTA::onError(OTA_CALLBACK(fn)){
|
void ArduinoOTAClass::onError(OTA_CALLBACK_ERROR(fn)){
|
||||||
_error_callback = fn;
|
_error_callback = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArduinoOTA::~ArduinoOTA(){
|
ArduinoOTAClass::~ArduinoOTAClass(){
|
||||||
delete _udp_ota;
|
delete _udp_ota;
|
||||||
delete _mdns_host;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArduinoOTA::setup() {
|
void ArduinoOTAClass::setPort(uint16_t port){
|
||||||
_udp_ota->begin(_port);
|
if(!_initialized && !_port && port){
|
||||||
if (_mdns_host) {
|
_port = port;
|
||||||
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 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");
|
||||||
|
client.stop();
|
||||||
|
delay(10);
|
||||||
|
#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) {
|
if (!*_udp_ota) {
|
||||||
_udp_ota->begin(_port);
|
_udp_ota->begin(_port);
|
||||||
if (_serial_debug) {
|
#if OTA_DEBUG
|
||||||
Serial.println("OTA restarted");
|
Serial.println("OTA restarted");
|
||||||
}
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_udp_ota->parsePacket()) return;
|
if (!_udp_ota->parsePacket()) return;
|
||||||
|
|
||||||
IPAddress remote = _udp_ota->remoteIP();
|
if(_state == OTA_IDLE){
|
||||||
int cmd = _udp_ota->parseInt();
|
int cmd = _udp_ota->parseInt();
|
||||||
int port = _udp_ota->parseInt();
|
if(cmd != U_FLASH && cmd != U_SPIFFS)
|
||||||
int size = _udp_ota->parseInt();
|
return;
|
||||||
|
_ota_ip = _udp_ota->remoteIP();
|
||||||
|
_cmd = cmd;
|
||||||
|
_ota_port = _udp_ota->parseInt();
|
||||||
|
_size = _udp_ota->parseInt();
|
||||||
|
_udp_ota->read();
|
||||||
|
sprintf(_md5, "%s", _udp_ota->readStringUntil('\n').c_str());
|
||||||
|
if(strlen(_md5) != 32)
|
||||||
|
return;
|
||||||
|
|
||||||
if (_serial_debug){
|
#if OTA_DEBUG
|
||||||
Serial.print("Update Start: ip:");
|
Serial.print("Update Start: ip:");
|
||||||
Serial.print(remote);
|
Serial.print(_ota_ip);
|
||||||
Serial.printf(", port:%d, size:%d\n", port, size);
|
Serial.printf(", port:%d, size:%d, md5:%s\n", _ota_port, _size, _md5);
|
||||||
}
|
#endif
|
||||||
|
|
||||||
WiFiUDP::stopAll();
|
_udp_ota->beginPacket(_ota_ip, _udp_ota->remotePort());
|
||||||
|
if(_password){
|
||||||
if(!Update.begin(size, cmd)){
|
MD5Builder nonce_md5;
|
||||||
if (_serial_debug)
|
nonce_md5.begin();
|
||||||
Serial.println("Update Begin Error");
|
nonce_md5.add(String(micros()));
|
||||||
if (_error_callback) _error_callback();
|
nonce_md5.calculate();
|
||||||
_udp_ota->begin(_port);
|
nonce_md5.getChars(_nonce);
|
||||||
return;
|
_udp_ota->printf("AUTH %s", _nonce);
|
||||||
}
|
_udp_ota->endPacket();
|
||||||
if (_start_callback) _start_callback();
|
_state = OTA_WAITAUTH;
|
||||||
if (_progress_callback) _progress_callback(0, size);
|
return;
|
||||||
|
} else {
|
||||||
WiFiClient client;
|
_udp_ota->print("OK");
|
||||||
if (!client.connect(remote, port)) {
|
_udp_ota->endPacket();
|
||||||
if (_serial_debug)
|
_state = OTA_RUNUPDATE;
|
||||||
Serial.printf("Connect Failed\n");
|
}
|
||||||
_udp_ota->begin(_port);
|
} else if(_state == OTA_WAITAUTH){
|
||||||
if (_error_callback) _error_callback();
|
int cmd = _udp_ota->parseInt();
|
||||||
}
|
if(cmd != U_AUTH){
|
||||||
|
_state = OTA_IDLE;
|
||||||
uint32_t written;
|
return;
|
||||||
while(!Update.isFinished() && client.connected()){
|
}
|
||||||
// TODO(mangelajo): enhance the Update.write(client) to
|
_udp_ota->read();
|
||||||
// accept a progress callback
|
String cnonce = _udp_ota->readStringUntil(' ');
|
||||||
written = Update.write(client);
|
String response = _udp_ota->readStringUntil('\n');
|
||||||
if(written > 0) client.print(written, DEC);
|
if(cnonce.length() != 32 || response.length() != 32){
|
||||||
if(_progress_callback) _progress_callback(written, size);
|
_state = OTA_IDLE;
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
Serial.setDebugOutput(false);
|
|
||||||
|
MD5Builder _passmd5;
|
||||||
if(Update.end()){
|
_passmd5.begin();
|
||||||
client.println("OK");
|
_passmd5.add(_password);
|
||||||
if (_serial_debug)
|
_passmd5.calculate();
|
||||||
Serial.printf("Update Success\nRebooting...\n");
|
String passmd5 = _passmd5.toString();
|
||||||
if(_end_callback) _end_callback();
|
|
||||||
ESP.restart();
|
String challenge = passmd5 + ":" + String(_nonce) + ":" + cnonce;
|
||||||
} else {
|
MD5Builder _challengemd5;
|
||||||
// Update failed: listen UDP again, callback and print
|
_challengemd5.begin();
|
||||||
_udp_ota->begin(_port);
|
_challengemd5.add(challenge);
|
||||||
if (_error_callback) _error_callback();
|
_challengemd5.calculate();
|
||||||
Update.printError(client);
|
String result = _challengemd5.toString();
|
||||||
if (_serial_debug)
|
|
||||||
Update.printError(Serial);
|
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(callback) void (*callback)()
|
||||||
#define OTA_CALLBACK_PROGRESS(callback) void (*callback)(unsigned int, unsigned int)
|
#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;
|
int _port;
|
||||||
String* _mdns_host;
|
char *_password;
|
||||||
|
char * _hostname;
|
||||||
|
char * _nonce;
|
||||||
WiFiUDP* _udp_ota;
|
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(_start_callback);
|
||||||
OTA_CALLBACK(_end_callback);
|
OTA_CALLBACK(_end_callback);
|
||||||
OTA_CALLBACK(_error_callback);
|
OTA_CALLBACK_ERROR(_error_callback);
|
||||||
|
|
||||||
OTA_CALLBACK_PROGRESS(_progress_callback);
|
OTA_CALLBACK_PROGRESS(_progress_callback);
|
||||||
|
|
||||||
|
void _runUpdate(void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ArduinoOTA(const char *mdns_host="ESP8266-OTA-",
|
ArduinoOTAClass();
|
||||||
int port=8266,
|
~ArduinoOTAClass();
|
||||||
bool serial_debug=true);
|
void setPort(uint16_t port);
|
||||||
~ArduinoOTA();
|
void setHostname(const char *hostname);
|
||||||
void setup();
|
void setPassword(const char *password);
|
||||||
void handle();
|
void onStart(OTA_CALLBACK(fn));
|
||||||
void onStart(OTA_CALLBACK(fn));
|
void onEnd(OTA_CALLBACK(fn));
|
||||||
void onEnd(OTA_CALLBACK(fn));
|
void onProgress(OTA_CALLBACK_PROGRESS(fn));
|
||||||
void onProgress(OTA_CALLBACK_PROGRESS(fn));
|
void onError(OTA_CALLBACK_ERROR (fn));
|
||||||
void onError(OTA_CALLBACK (fn));
|
void begin();
|
||||||
|
void handle();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern ArduinoOTAClass ArduinoOTA;
|
||||||
|
|
||||||
#endif /* __ARDUINO_OTA_H */
|
#endif /* __ARDUINO_OTA_H */
|
||||||
|
@ -6,26 +6,36 @@
|
|||||||
const char* ssid = "...";
|
const char* ssid = "...";
|
||||||
const char* password = "...";
|
const char* password = "...";
|
||||||
|
|
||||||
ArduinoOTA ota_server;
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
Serial.println("Booting");
|
||||||
Serial.println("Booting");
|
WiFi.mode(WIFI_STA);
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.begin(ssid, password);
|
||||||
|
while (WiFi.waitForConnectResult() != WL_CONNECTED){
|
||||||
/* try the flash stored password first */
|
Serial.println("Connection Failed! Rebooting...");
|
||||||
WiFi.begin();
|
delay(5000);
|
||||||
|
ESP.reset();
|
||||||
while (WiFi.waitForConnectResult() != WL_CONNECTED){
|
|
||||||
WiFi.begin(ssid, password);
|
|
||||||
Serial.println("Retrying connection...");
|
|
||||||
}
|
}
|
||||||
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");
|
Serial.println("Ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
ota_server.handle();
|
ArduinoOTA.handle();
|
||||||
yield();
|
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
|
|
||||||
const char* ssid = "...";
|
const char* ssid = "...";
|
||||||
const char* password = "...";
|
const char* password = "...";
|
||||||
const char* host_prefix = "OTA-LEDS-";
|
const char* host = "OTA-LEDS";
|
||||||
|
|
||||||
ArduinoOTA ota_server(host_prefix, 8266, /* debug_serial= */ true);
|
|
||||||
|
|
||||||
int led_pin = 13;
|
int led_pin = 13;
|
||||||
#define N_DIMMERS 3
|
#define N_DIMMERS 3
|
||||||
@ -23,8 +21,7 @@ void setup() {
|
|||||||
Serial.println("Booting");
|
Serial.println("Booting");
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
||||||
/* try the flash stored password first */
|
WiFi.begin(ssid, password);
|
||||||
WiFi.begin();
|
|
||||||
|
|
||||||
while (WiFi.waitForConnectResult() != WL_CONNECTED){
|
while (WiFi.waitForConnectResult() != WL_CONNECTED){
|
||||||
WiFi.begin(ssid, password);
|
WiFi.begin(ssid, password);
|
||||||
@ -33,10 +30,6 @@ void setup() {
|
|||||||
/* switch off led */
|
/* switch off led */
|
||||||
digitalWrite(led_pin, HIGH);
|
digitalWrite(led_pin, HIGH);
|
||||||
|
|
||||||
/* setup the OTA server */
|
|
||||||
ota_server.setup();
|
|
||||||
Serial.println("Ready");
|
|
||||||
|
|
||||||
/* configure dimmers, and OTA server events */
|
/* configure dimmers, and OTA server events */
|
||||||
analogWriteRange(1000);
|
analogWriteRange(1000);
|
||||||
analogWrite(led_pin,990);
|
analogWrite(led_pin,990);
|
||||||
@ -47,13 +40,14 @@ void setup() {
|
|||||||
analogWrite(dimmer_pin[i],50);
|
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++)
|
for(int i=0; i<N_DIMMERS;i++)
|
||||||
analogWrite(dimmer_pin[i], 0);
|
analogWrite(dimmer_pin[i], 0);
|
||||||
analogWrite(led_pin,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++)
|
for (int i=0;i<30;i++)
|
||||||
{
|
{
|
||||||
analogWrite(led_pin,(i*100) % 1001);
|
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() {
|
void loop() {
|
||||||
ota_server.handle();
|
ArduinoOTA.handle();
|
||||||
yield();
|
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ static const IPAddress MDNS_MULTICAST_ADDR(224, 0, 0, 251);
|
|||||||
static const int MDNS_MULTICAST_TTL = 1;
|
static const int MDNS_MULTICAST_TTL = 1;
|
||||||
static const int MDNS_PORT = 5353;
|
static const int MDNS_PORT = 5353;
|
||||||
|
|
||||||
MDNSResponder::MDNSResponder() : _conn(0) { _services = 0; }
|
MDNSResponder::MDNSResponder() : _conn(0) { _services = 0; _arduinoAuth = false; }
|
||||||
MDNSResponder::~MDNSResponder() {}
|
MDNSResponder::~MDNSResponder() {}
|
||||||
|
|
||||||
bool MDNSResponder::begin(const char* domain){
|
bool MDNSResponder::begin(const char* domain){
|
||||||
@ -101,8 +101,6 @@ bool MDNSResponder::begin(const char* domain){
|
|||||||
_hostName[i] = tolower(domain[i]);
|
_hostName[i] = tolower(domain[i]);
|
||||||
_hostName[n] = '\0';
|
_hostName[n] = '\0';
|
||||||
|
|
||||||
os_strcpy(_boardName, ARDUINO_BOARD);
|
|
||||||
|
|
||||||
// Open the MDNS socket if it isn't already open.
|
// Open the MDNS socket if it isn't already open.
|
||||||
if (!_conn) {
|
if (!_conn) {
|
||||||
uint32_t ourIp = _getOurIp();
|
uint32_t ourIp = _getOurIp();
|
||||||
@ -372,7 +370,10 @@ void MDNSResponder::_parsePacket(){
|
|||||||
return _reply(responseMask, (serviceName), (protoName), servicePort);
|
return _reply(responseMask, (serviceName), (protoName), servicePort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MDNSResponder::enableArduino(uint16_t port, bool auth){
|
||||||
|
_arduinoAuth = auth;
|
||||||
|
addService("arduino", "tcp", port);
|
||||||
|
}
|
||||||
|
|
||||||
void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint16_t port){
|
void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint16_t port){
|
||||||
int i;
|
int i;
|
||||||
@ -439,24 +440,53 @@ void MDNSResponder::_reply(uint8_t replyMask, char * service, char *proto, uint1
|
|||||||
|
|
||||||
// TXT Response
|
// TXT Response
|
||||||
if(replyMask & 0x4){
|
if(replyMask & 0x4){
|
||||||
if(replyMask & 0x8){//send the name
|
if(replyMask & 0x8){
|
||||||
uint8_t txtHead[2] = {0xC0, (uint8_t)(36 + serviceLen)};
|
uint8_t txtHead[10] = {
|
||||||
_conn->append(reinterpret_cast<const char*>(txtHead), 2);
|
0xC0, (uint8_t)(36 + serviceLen),//send the name
|
||||||
|
0x00, 0x10, //Type TXT
|
||||||
|
0x80, 0x01, //Class IN, with cache flush
|
||||||
|
0x00, 0x00, 0x11, 0x94, //TTL 4500
|
||||||
|
};
|
||||||
|
_conn->append(reinterpret_cast<const char*>(txtHead), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t boardNameLen = os_strlen(_boardName);
|
if(strcmp(reinterpret_cast<const char*>("arduino"), service) == 0){
|
||||||
|
//arduino
|
||||||
uint8_t txt[24] = {
|
//arduino service dependance should be removed and properties abstracted
|
||||||
0x00, 0x10, //Type TXT
|
const char *tcpCheckExtra = "tcp_check=no";
|
||||||
0x80, 0x01, //Class IN, with cache flush
|
uint8_t tcpCheckExtraLen = os_strlen(tcpCheckExtra);
|
||||||
0x00, 0x00, 0x11, 0x94, //TTL 4500
|
|
||||||
0x00, 0x0e, //DATA LEN
|
const char *sshUploadExtra = "ssh_upload=no";
|
||||||
(uint8_t)(6 + boardNameLen), //strlen(board=) + strlen(boardName)
|
uint8_t sshUploadExtraLen = os_strlen(sshUploadExtra);
|
||||||
0x62, 0x6f, 0x61, 0x72, 0x64, 0x3d //board=
|
|
||||||
};
|
char boardName[64];
|
||||||
_conn->append(reinterpret_cast<const char*>(txt), 17);
|
const char *boardExtra = "board=";
|
||||||
_conn->append(reinterpret_cast<const char*>(_boardName), boardNameLen);
|
os_sprintf(boardName, "%s%s\0", boardExtra, ARDUINO_BOARD);
|
||||||
|
uint8_t boardNameLen = os_strlen(boardName);
|
||||||
|
|
||||||
|
char authUpload[16];
|
||||||
|
const char *authUploadExtra = "auth_upload=";
|
||||||
|
os_sprintf(authUpload, "%s%s\0", authUploadExtra, reinterpret_cast<const char*>((_arduinoAuth)?"yes":"no"));
|
||||||
|
uint8_t authUploadLen = os_strlen(authUpload);
|
||||||
|
|
||||||
|
uint16_t textDataLen = (1 + boardNameLen) + (1 + tcpCheckExtraLen) + (1 + sshUploadExtraLen) + (1 + authUploadLen);
|
||||||
|
uint8_t txt[2] = {(uint8_t)(textDataLen >> 8), (uint8_t)(textDataLen)}; //DATA LEN
|
||||||
|
_conn->append(reinterpret_cast<const char*>(txt), 2);
|
||||||
|
|
||||||
|
_conn->append(reinterpret_cast<const char*>(&boardNameLen), 1);
|
||||||
|
_conn->append(reinterpret_cast<const char*>(boardName), boardNameLen);
|
||||||
|
_conn->append(reinterpret_cast<const char*>(&authUploadLen), 1);
|
||||||
|
_conn->append(reinterpret_cast<const char*>(authUpload), authUploadLen);
|
||||||
|
_conn->append(reinterpret_cast<const char*>(&tcpCheckExtraLen), 1);
|
||||||
|
_conn->append(reinterpret_cast<const char*>(tcpCheckExtra), tcpCheckExtraLen);
|
||||||
|
_conn->append(reinterpret_cast<const char*>(&sshUploadExtraLen), 1);
|
||||||
|
_conn->append(reinterpret_cast<const char*>(sshUploadExtra), sshUploadExtraLen);
|
||||||
|
} else {
|
||||||
|
//not arduino
|
||||||
|
//we should figure out an API so TXT properties can be added for services
|
||||||
|
uint8_t txt[2] = {0,0};
|
||||||
|
_conn->append(reinterpret_cast<const char*>(txt), 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRV Response
|
// SRV Response
|
||||||
|
@ -77,12 +77,14 @@ public:
|
|||||||
void addService(String service, String proto, uint16_t port){
|
void addService(String service, String proto, uint16_t port){
|
||||||
addService(service.c_str(), proto.c_str(), port);
|
addService(service.c_str(), proto.c_str(), port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enableArduino(uint16_t port, bool auth=false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct MDNSService * _services;
|
struct MDNSService * _services;
|
||||||
UdpContext* _conn;
|
UdpContext* _conn;
|
||||||
char _hostName[128];
|
char _hostName[128];
|
||||||
char _boardName[64];
|
bool _arduinoAuth;
|
||||||
|
|
||||||
uint32_t _getOurIp();
|
uint32_t _getOurIp();
|
||||||
uint16_t _getServicePort(char *service, char *proto);
|
uint16_t _getServicePort(char *service, char *proto);
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESP8266mDNS.h>
|
|
||||||
#include <WiFiUdp.h>
|
|
||||||
|
|
||||||
const char* host = "esp8266-ota";
|
|
||||||
const char* ssid = "**********";
|
|
||||||
const char* pass = "**********";
|
|
||||||
const uint16_t aport = 8266;
|
|
||||||
|
|
||||||
WiFiServer TelnetServer(aport);
|
|
||||||
WiFiClient Telnet;
|
|
||||||
WiFiUDP OTA;
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
Serial.println("");
|
|
||||||
Serial.println("Arduino OTA Test");
|
|
||||||
|
|
||||||
Serial.printf("Sketch size: %u\n", ESP.getSketchSize());
|
|
||||||
Serial.printf("Free size: %u\n", ESP.getFreeSketchSpace());
|
|
||||||
|
|
||||||
WiFi.begin(ssid, pass);
|
|
||||||
if(WiFi.waitForConnectResult() == WL_CONNECTED){
|
|
||||||
MDNS.begin(host);
|
|
||||||
MDNS.addService("arduino", "tcp", aport);
|
|
||||||
OTA.begin(aport);
|
|
||||||
TelnetServer.begin();
|
|
||||||
TelnetServer.setNoDelay(true);
|
|
||||||
Serial.print("IP address: ");
|
|
||||||
Serial.println(WiFi.localIP());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
//OTA Sketch
|
|
||||||
if (OTA.parsePacket()) {
|
|
||||||
IPAddress remote = OTA.remoteIP();
|
|
||||||
int cmd = OTA.parseInt();
|
|
||||||
int port = OTA.parseInt();
|
|
||||||
int size = OTA.parseInt();
|
|
||||||
|
|
||||||
Serial.print("Update Start: ip:");
|
|
||||||
Serial.print(remote);
|
|
||||||
Serial.printf(", port:%d, size:%d\n", port, size);
|
|
||||||
uint32_t startTime = millis();
|
|
||||||
|
|
||||||
WiFiUDP::stopAll();
|
|
||||||
|
|
||||||
if(!Update.begin(size)){
|
|
||||||
Serial.println("Update Begin Error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
WiFiClient client;
|
|
||||||
if (client.connect(remote, port)) {
|
|
||||||
|
|
||||||
uint32_t written;
|
|
||||||
while(!Update.isFinished()){
|
|
||||||
written = Update.write(client);
|
|
||||||
if(written > 0) client.print(written, DEC);
|
|
||||||
}
|
|
||||||
Serial.setDebugOutput(false);
|
|
||||||
|
|
||||||
if(Update.end()){
|
|
||||||
client.println("OK");
|
|
||||||
Serial.printf("Update Success: %u\nRebooting...\n", millis() - startTime);
|
|
||||||
ESP.restart();
|
|
||||||
} else {
|
|
||||||
Update.printError(client);
|
|
||||||
Update.printError(Serial);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Serial.printf("Connect Failed: %u\n", millis() - startTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//IDE Monitor (connected to Serial)
|
|
||||||
if (TelnetServer.hasClient()){
|
|
||||||
if (!Telnet || !Telnet.connected()){
|
|
||||||
if(Telnet) Telnet.stop();
|
|
||||||
Telnet = TelnetServer.available();
|
|
||||||
} else {
|
|
||||||
WiFiClient toKill = TelnetServer.available();
|
|
||||||
toKill.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Telnet && Telnet.connected() && Telnet.available()){
|
|
||||||
while(Telnet.available())
|
|
||||||
Serial.write(Telnet.read());
|
|
||||||
}
|
|
||||||
if(Serial.available()){
|
|
||||||
size_t len = Serial.available();
|
|
||||||
uint8_t * sbuf = (uint8_t *)malloc(len);
|
|
||||||
Serial.readBytes(sbuf, len);
|
|
||||||
if (Telnet && Telnet.connected()){
|
|
||||||
Telnet.write((uint8_t *)sbuf, len);
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
free(sbuf);
|
|
||||||
}
|
|
||||||
delay(1);
|
|
||||||
}
|
|
@ -38,10 +38,6 @@ const char* ap_default_psk = "esp8266esp8266"; ///< Default PSK.
|
|||||||
/// Uncomment the next line for verbose output over UART.
|
/// Uncomment the next line for verbose output over UART.
|
||||||
//#define SERIAL_VERBOSE
|
//#define SERIAL_VERBOSE
|
||||||
|
|
||||||
/// OTA server handle.
|
|
||||||
ArduinoOTA ota_server;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read WiFi connection information from file system.
|
* @brief Read WiFi connection information from file system.
|
||||||
* @param ssid String pointer for storing SSID.
|
* @param ssid String pointer for storing SSID.
|
||||||
@ -244,7 +240,8 @@ void setup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start OTA server.
|
// Start OTA server.
|
||||||
ota_server.setup();
|
ArduinoOTA.setHostname((const char *)hostname.c_str());
|
||||||
|
ArduinoOTA.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -254,7 +251,7 @@ void setup()
|
|||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
// Handle OTA server.
|
// Handle OTA server.
|
||||||
ota_server.handle();
|
ArduinoOTA.handle();
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ MDNS KEYWORD1
|
|||||||
begin KEYWORD2
|
begin KEYWORD2
|
||||||
update KEYWORD2
|
update KEYWORD2
|
||||||
addService KEYWORD2
|
addService KEYWORD2
|
||||||
|
enableArduino KEYWORD2
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Constants (LITERAL1)
|
# Constants (LITERAL1)
|
||||||
|
14
platform.txt
14
platform.txt
@ -92,18 +92,18 @@ recipe.size.regex.data=^(?:\.data|\.rodata|\.bss)\s+([0-9]+).*
|
|||||||
tools.esptool.cmd=esptool
|
tools.esptool.cmd=esptool
|
||||||
tools.esptool.cmd.windows=esptool.exe
|
tools.esptool.cmd.windows=esptool.exe
|
||||||
tools.esptool.path={runtime.platform.path}/tools/esptool
|
tools.esptool.path={runtime.platform.path}/tools/esptool
|
||||||
#runtime.tools.esptool.path
|
tools.esptool.network_cmd=python
|
||||||
|
tools.esptool.network_cmd.windows=python.exe
|
||||||
tools.mkspiffs.cmd=mkspiffs
|
|
||||||
tools.mkspiffs.cmd.windows=mkspiffs.exe
|
|
||||||
tools.mkspiffs.path={runtime.platform.path}/tools/mkspiffs
|
|
||||||
#runtime.tools.mkspiffs.path
|
|
||||||
|
|
||||||
tools.esptool.upload.protocol=esp
|
tools.esptool.upload.protocol=esp
|
||||||
tools.esptool.upload.params.verbose=-vv
|
tools.esptool.upload.params.verbose=-vv
|
||||||
tools.esptool.upload.params.quiet=
|
tools.esptool.upload.params.quiet=
|
||||||
tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" -ca 0x00000 -cf "{build.path}/{build.project_name}.bin"
|
tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" -ca 0x00000 -cf "{build.path}/{build.project_name}.bin"
|
||||||
tools.esptool.network.pattern=python "{path}/espota.py" -i "{serial.port}" -p "{network.port}" -f "{build.path}/{build.project_name}.bin"
|
tools.esptool.upload.network_pattern="{network_cmd}" "{runtime.platform.path}/tools/espota.py" -i "{serial.port}" -p "{network.port}" "--auth={network.password}" -f "{build.path}/{build.project_name}.bin"
|
||||||
|
|
||||||
|
tools.mkspiffs.cmd=mkspiffs
|
||||||
|
tools.mkspiffs.cmd.windows=mkspiffs.exe
|
||||||
|
tools.mkspiffs.path={runtime.platform.path}/tools/mkspiffs
|
||||||
|
|
||||||
tools.espota.cmd=python
|
tools.espota.cmd=python
|
||||||
tools.espota.cmd.windows=python.exe
|
tools.espota.cmd.windows=python.exe
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
# https://gist.github.com/igrr/d35ab8446922179dc58c
|
# https://gist.github.com/igrr/d35ab8446922179dc58c
|
||||||
#
|
#
|
||||||
# Modified since 2015-09-18 from Pascal Gollor (https://github.com/pgollor)
|
# Modified since 2015-09-18 from Pascal Gollor (https://github.com/pgollor)
|
||||||
|
# Modified since 2015-11-09 from Hristo Gochkov (https://github.com/me-no-dev)
|
||||||
#
|
#
|
||||||
# This script will push an OTA update to the ESP
|
# This script will push an OTA update to the ESP
|
||||||
# use it like: python espota.py -i <ESP_IP_address> -p <ESP_port> -f <sketch.bin>
|
# use it like: python espota.py -i <ESP_IP_address> -p <ESP_port> [-a password] -f <sketch.bin>
|
||||||
# Or to upload SPIFFS image:
|
# Or to upload SPIFFS image:
|
||||||
# python espota.py -i <ESP_IP_address> -p <ESP_port> -s -f <spiffs.bin>
|
# python espota.py -i <ESP_IP_address> -p <ESP_port> [-a password] -s -f <spiffs.bin>
|
||||||
#
|
#
|
||||||
# Changes
|
# Changes
|
||||||
# 2015-09-18:
|
# 2015-09-18:
|
||||||
@ -16,6 +17,11 @@
|
|||||||
# - Add logging.
|
# - Add logging.
|
||||||
# - Send command to controller to differ between flashing and transmitting SPIFFS image.
|
# - Send command to controller to differ between flashing and transmitting SPIFFS image.
|
||||||
#
|
#
|
||||||
|
# Changes
|
||||||
|
# 2015-11-09:
|
||||||
|
# - Added digest authentication
|
||||||
|
# - Enchanced error tracking and reporting
|
||||||
|
#
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import socket
|
import socket
|
||||||
@ -23,13 +29,15 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import optparse
|
import optparse
|
||||||
import logging
|
import logging
|
||||||
|
import hashlib
|
||||||
|
|
||||||
# Commands
|
# Commands
|
||||||
FLASH = 0
|
FLASH = 0
|
||||||
SPIFFS = 100
|
SPIFFS = 100
|
||||||
|
AUTH = 200
|
||||||
|
|
||||||
|
|
||||||
def serve(remoteAddr, remotePort, filename, command = FLASH):
|
def serve(remoteAddr, remotePort, password, filename, command = FLASH):
|
||||||
# Create a TCP/IP socket
|
# Create a TCP/IP socket
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
serverPort = 48266
|
serverPort = 48266
|
||||||
@ -43,17 +51,58 @@ def serve(remoteAddr, remotePort, filename, command = FLASH):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
content_size = os.path.getsize(filename)
|
content_size = os.path.getsize(filename)
|
||||||
|
f = open(filename,'rb')
|
||||||
|
file_md5 = hashlib.md5(f.read()).hexdigest()
|
||||||
|
f.close()
|
||||||
logging.info('Upload size: %d', content_size)
|
logging.info('Upload size: %d', content_size)
|
||||||
message = '%d %d %d\n' % (command, serverPort, content_size)
|
message = '%d %d %d %s\n' % (command, serverPort, content_size, file_md5)
|
||||||
|
|
||||||
# Wait for a connection
|
# Wait for a connection
|
||||||
logging.info('Sending invitation to: %s', remoteAddr)
|
logging.info('Sending invitation to: %s', remoteAddr)
|
||||||
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
remote_address = (remoteAddr, int(remotePort))
|
remote_address = (remoteAddr, int(remotePort))
|
||||||
sent = sock2.sendto(message, remote_address)
|
sent = sock2.sendto(message, remote_address)
|
||||||
|
sock2.settimeout(10)
|
||||||
|
try:
|
||||||
|
data = sock2.recv(37)
|
||||||
|
except:
|
||||||
|
logging.error('No Answer')
|
||||||
|
sock2.close()
|
||||||
|
return 1
|
||||||
|
if (data != "OK"):
|
||||||
|
if(data.startswith('AUTH')):
|
||||||
|
nonce = data.split()[1]
|
||||||
|
cnonce_text = '%s%u%s%s' % (filename, content_size, file_md5, remoteAddr)
|
||||||
|
cnonce = hashlib.md5(cnonce_text).hexdigest()
|
||||||
|
passmd5 = hashlib.md5(password).hexdigest()
|
||||||
|
result_text = '%s:%s:%s' % (passmd5 ,nonce, cnonce)
|
||||||
|
result = hashlib.md5(result_text).hexdigest()
|
||||||
|
sys.stderr.write('Authenticating...')
|
||||||
|
sys.stderr.flush()
|
||||||
|
message = '%d %s %s\n' % (AUTH, cnonce, result)
|
||||||
|
sock2.sendto(message, remote_address)
|
||||||
|
sock2.settimeout(10)
|
||||||
|
try:
|
||||||
|
data = sock2.recv(32)
|
||||||
|
except:
|
||||||
|
sys.stderr.write('FAIL\n')
|
||||||
|
logging.error('No Answer to our Authentication')
|
||||||
|
sock2.close()
|
||||||
|
return 1
|
||||||
|
if (data != "OK"):
|
||||||
|
sys.stderr.write('FAIL\n')
|
||||||
|
logging.error('%s', data)
|
||||||
|
sock2.close()
|
||||||
|
sys.exit(1);
|
||||||
|
return 1
|
||||||
|
sys.stderr.write('OK\n')
|
||||||
|
else:
|
||||||
|
logging.error('Bad Answer: %s', data)
|
||||||
|
sock2.close()
|
||||||
|
return 1
|
||||||
sock2.close()
|
sock2.close()
|
||||||
|
|
||||||
logging.info('Waiting for device...\n')
|
logging.info('Waiting for device...')
|
||||||
try:
|
try:
|
||||||
sock.settimeout(10)
|
sock.settimeout(10)
|
||||||
connection, client_address = sock.accept()
|
connection, client_address = sock.accept()
|
||||||
@ -78,13 +127,15 @@ def serve(remoteAddr, remotePort, filename, command = FLASH):
|
|||||||
connection.sendall(chunk)
|
connection.sendall(chunk)
|
||||||
res = connection.recv(4)
|
res = connection.recv(4)
|
||||||
except:
|
except:
|
||||||
logging.error('\nError Uploading')
|
sys.stderr.write('\n')
|
||||||
|
logging.error('Error Uploading')
|
||||||
connection.close()
|
connection.close()
|
||||||
f.close()
|
f.close()
|
||||||
sock.close()
|
sock.close()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
logging.info('\nWaiting for result...\n')
|
sys.stderr.write('\n')
|
||||||
|
logging.info('Waiting for result...')
|
||||||
try:
|
try:
|
||||||
connection.settimeout(60)
|
connection.settimeout(60)
|
||||||
data = connection.recv(32)
|
data = connection.recv(32)
|
||||||
@ -92,9 +143,13 @@ def serve(remoteAddr, remotePort, filename, command = FLASH):
|
|||||||
connection.close()
|
connection.close()
|
||||||
f.close()
|
f.close()
|
||||||
sock.close()
|
sock.close()
|
||||||
|
if (data != "OK"):
|
||||||
|
sys.stderr.write('\n')
|
||||||
|
logging.error('%s', data)
|
||||||
|
return 1;
|
||||||
return 0
|
return 0
|
||||||
except:
|
except:
|
||||||
logging.error('Result: No Answer!')
|
logging.error('No Result!')
|
||||||
connection.close()
|
connection.close()
|
||||||
f.close()
|
f.close()
|
||||||
sock.close()
|
sock.close()
|
||||||
@ -131,6 +186,16 @@ def parser():
|
|||||||
)
|
)
|
||||||
parser.add_option_group(group)
|
parser.add_option_group(group)
|
||||||
|
|
||||||
|
# auth
|
||||||
|
group = optparse.OptionGroup(parser, "Authentication")
|
||||||
|
group.add_option("-a", "--auth",
|
||||||
|
dest = "auth",
|
||||||
|
help = "Set authentication password.",
|
||||||
|
action = "store",
|
||||||
|
default = ""
|
||||||
|
)
|
||||||
|
parser.add_option_group(group)
|
||||||
|
|
||||||
# image
|
# image
|
||||||
group = optparse.OptionGroup(parser, "Image")
|
group = optparse.OptionGroup(parser, "Image")
|
||||||
group.add_option("-f", "--file",
|
group.add_option("-f", "--file",
|
||||||
@ -190,7 +255,7 @@ def main(args):
|
|||||||
command = SPIFFS
|
command = SPIFFS
|
||||||
# end if
|
# end if
|
||||||
|
|
||||||
return serve(options.esp_ip, options.esp_port, options.image, command)
|
return serve(options.esp_ip, options.esp_port, options.auth, options.image, command)
|
||||||
# end main
|
# end main
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user