@ -0,0 +1,325 @@ | |||
#ifndef LWIP_OPEN_SRC | |||
#define LWIP_OPEN_SRC | |||
#endif | |||
#include <functional> | |||
#include <WiFiUdp.h> | |||
#include "ArduinoOTA.h" | |||
#include "MD5Builder.h" | |||
extern "C" { | |||
#include "osapi.h" | |||
#include "ets_sys.h" | |||
#include "user_interface.h" | |||
} | |||
#include "lwip/opt.h" | |||
#include "lwip/udp.h" | |||
#include "lwip/inet.h" | |||
#include "lwip/igmp.h" | |||
#include "lwip/mem.h" | |||
#include "include/UdpContext.h" | |||
#include <ESP8266mDNS.h> | |||
#ifdef DEBUG_ESP_OTA | |||
#ifdef DEBUG_ESP_PORT | |||
#define OTA_DEBUG DEBUG_ESP_PORT | |||
#endif | |||
#endif | |||
ArduinoOTAClass::ArduinoOTAClass() | |||
: _port(0) | |||
, _udp_ota(0) | |||
, _initialized(false) | |||
, _state(OTA_IDLE) | |||
, _size(0) | |||
, _cmd(0) | |||
, _ota_port(0) | |||
, _start_callback(NULL) | |||
, _end_callback(NULL) | |||
, _error_callback(NULL) | |||
, _progress_callback(NULL) | |||
{ | |||
} | |||
ArduinoOTAClass::~ArduinoOTAClass(){ | |||
if(_udp_ota){ | |||
_udp_ota->unref(); | |||
_udp_ota = 0; | |||
} | |||
} | |||
void ArduinoOTAClass::onStart(OTA_CALLBACK(fn)) { | |||
_start_callback = fn; | |||
} | |||
void ArduinoOTAClass::onEnd(OTA_CALLBACK(fn)) { | |||
_end_callback = fn; | |||
} | |||
void ArduinoOTAClass::onProgress(OTA_CALLBACK_PROGRESS(fn)) { | |||
_progress_callback = fn; | |||
} | |||
void ArduinoOTAClass::onError(OTA_CALLBACK_ERROR(fn)) { | |||
_error_callback = fn; | |||
} | |||
void ArduinoOTAClass::setPort(uint16_t port) { | |||
if (!_initialized && !_port && port) { | |||
_port = port; | |||
} | |||
} | |||
void ArduinoOTAClass::setHostname(const char * hostname) { | |||
if (!_initialized && !_hostname.length() && hostname) { | |||
_hostname = hostname; | |||
} | |||
} | |||
void ArduinoOTAClass::setPassword(const char * password) { | |||
if (!_initialized && !_password.length() && password) { | |||
_password = password; | |||
} | |||
} | |||
void ArduinoOTAClass::begin() { | |||
if (_initialized) | |||
return; | |||
if (!_hostname.length()) { | |||
char tmp[15]; | |||
sprintf(tmp, "esp8266-%06x", ESP.getChipId()); | |||
_hostname = tmp; | |||
} | |||
if (!_port) { | |||
_port = 8266; | |||
} | |||
if(_udp_ota){ | |||
_udp_ota->unref(); | |||
_udp_ota = 0; | |||
} | |||
_udp_ota = new UdpContext; | |||
_udp_ota->ref(); | |||
if(!_udp_ota->listen(*IP_ADDR_ANY, _port)) | |||
return; | |||
_udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this)); | |||
MDNS.begin(_hostname.c_str()); | |||
if (_password.length()) { | |||
MDNS.enableArduino(_port, true); | |||
} else { | |||
MDNS.enableArduino(_port); | |||
} | |||
_initialized = true; | |||
_state = OTA_IDLE; | |||
#ifdef OTA_DEBUG | |||
OTA_DEBUG.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port); | |||
#endif | |||
} | |||
int ArduinoOTAClass::parseInt(){ | |||
char data[16]; | |||
uint8_t index = 0; | |||
char value; | |||
while(_udp_ota->peek() == ' ') _udp_ota->read(); | |||
while(true){ | |||
value = _udp_ota->peek(); | |||
if(value < '0' || value > '9'){ | |||
data[index++] = '\0'; | |||
return atoi(data); | |||
} | |||
data[index++] = _udp_ota->read(); | |||
} | |||
return 0; | |||
} | |||
String ArduinoOTAClass::readStringUntil(char end){ | |||
String res = ""; | |||
char value; | |||
while(true){ | |||
value = _udp_ota->read(); | |||
if(value == '\0' || value == end){ | |||
return res; | |||
} | |||
res += value; | |||
} | |||
return res; | |||
} | |||
void ArduinoOTAClass::_onRx(){ | |||
if(!_udp_ota->next()) return; | |||
ip_addr_t ota_ip; | |||
if (_state == OTA_IDLE) { | |||
int cmd = parseInt(); | |||
if (cmd != U_FLASH && cmd != U_SPIFFS) | |||
return; | |||
_ota_ip = _udp_ota->getRemoteAddress(); | |||
_cmd = cmd; | |||
_ota_port = parseInt(); | |||
_size = parseInt(); | |||
_udp_ota->read(); | |||
_md5 = readStringUntil('\n'); | |||
_md5.trim(); | |||
if(_md5.length() != 32) | |||
return; | |||
ota_ip.addr = (uint32_t)_ota_ip; | |||
if (_password.length()){ | |||
MD5Builder nonce_md5; | |||
nonce_md5.begin(); | |||
nonce_md5.add(String(micros())); | |||
nonce_md5.calculate(); | |||
_nonce = nonce_md5.toString(); | |||
char auth_req[38]; | |||
sprintf(auth_req, "AUTH %s", _nonce.c_str()); | |||
_udp_ota->append((const char *)auth_req, strlen(auth_req)); | |||
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort()); | |||
_state = OTA_WAITAUTH; | |||
return; | |||
} else { | |||
_udp_ota->append("OK", 2); | |||
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort()); | |||
_state = OTA_RUNUPDATE; | |||
} | |||
} else if (_state == OTA_WAITAUTH) { | |||
int cmd = parseInt(); | |||
if (cmd != U_AUTH) { | |||
_state = OTA_IDLE; | |||
return; | |||
} | |||
_udp_ota->read(); | |||
String cnonce = readStringUntil(' '); | |||
String response = readStringUntil('\n'); | |||
if (cnonce.length() != 32 || response.length() != 32) { | |||
_state = OTA_IDLE; | |||
return; | |||
} | |||
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(); | |||
ota_ip.addr = (uint32_t)_ota_ip; | |||
if(result.equals(response)){ | |||
_udp_ota->append("OK", 2); | |||
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort()); | |||
_state = OTA_RUNUPDATE; | |||
} else { | |||
_udp_ota->append("Authentication Failed", 21); | |||
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort()); | |||
if (_error_callback) _error_callback(OTA_AUTH_ERROR); | |||
_state = OTA_IDLE; | |||
} | |||
} | |||
while(_udp_ota->next()) _udp_ota->flush(); | |||
} | |||
void ArduinoOTAClass::_runUpdate() { | |||
if (!Update.begin(_size, _cmd)) { | |||
#ifdef OTA_DEBUG | |||
OTA_DEBUG.println("Update Begin Error"); | |||
#endif | |||
if (_error_callback) { | |||
_error_callback(OTA_BEGIN_ERROR); | |||
} | |||
_udp_ota->listen(*IP_ADDR_ANY, _port); | |||
_state = OTA_IDLE; | |||
return; | |||
} | |||
Update.setMD5(_md5.c_str()); | |||
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)) { | |||
#ifdef OTA_DEBUG | |||
OTA_DEBUG.printf("Connect Failed\n"); | |||
#endif | |||
_udp_ota->listen(*IP_ADDR_ANY, _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){ | |||
#ifdef OTA_DEBUG | |||
OTA_DEBUG.printf("Receive Failed\n"); | |||
#endif | |||
_udp_ota->listen(*IP_ADDR_ANY, _port); | |||
if (_error_callback) { | |||
_error_callback(OTA_RECEIVE_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); | |||
#ifdef OTA_DEBUG | |||
OTA_DEBUG.printf("Update Success\nRebooting...\n"); | |||
#endif | |||
if (_end_callback) { | |||
_end_callback(); | |||
} | |||
ESP.restart(); | |||
} else { | |||
_udp_ota->listen(*IP_ADDR_ANY, _port); | |||
if (_error_callback) { | |||
_error_callback(OTA_END_ERROR); | |||
} | |||
Update.printError(client); | |||
#ifdef OTA_DEBUG | |||
Update.printError(OTA_DEBUG); | |||
#endif | |||
_state = OTA_IDLE; | |||
} | |||
} | |||
void ArduinoOTAClass::handle() { | |||
if (_state == OTA_RUNUPDATE) { | |||
_runUpdate(); | |||
_state = OTA_IDLE; | |||
} | |||
} | |||
ArduinoOTAClass ArduinoOTA; |
@ -0,0 +1,69 @@ | |||
#ifndef __ARDUINO_OTA_H | |||
#define __ARDUINO_OTA_H | |||
#include <ESP8266WiFi.h> | |||
#include <WiFiUdp.h> | |||
class UdpContext; | |||
#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) | |||
typedef enum { | |||
OTA_IDLE, | |||
OTA_WAITAUTH, | |||
OTA_RUNUPDATE | |||
} ota_state_t; | |||
typedef enum { | |||
OTA_AUTH_ERROR, | |||
OTA_BEGIN_ERROR, | |||
OTA_CONNECT_ERROR, | |||
OTA_RECEIVE_ERROR, | |||
OTA_END_ERROR | |||
} ota_error_t; | |||
class ArduinoOTAClass | |||
{ | |||
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(); | |||
private: | |||
int _port; | |||
String _password; | |||
String _hostname; | |||
String _nonce; | |||
UdpContext *_udp_ota; | |||
bool _initialized; | |||
ota_state_t _state; | |||
int _size; | |||
int _cmd; | |||
int _ota_port; | |||
IPAddress _ota_ip; | |||
String _md5; | |||
OTA_CALLBACK(_start_callback); | |||
OTA_CALLBACK(_end_callback); | |||
OTA_CALLBACK_ERROR(_error_callback); | |||
OTA_CALLBACK_PROGRESS(_progress_callback); | |||
void _runUpdate(void); | |||
void _onRx(void); | |||
int parseInt(void); | |||
String readStringUntil(char end); | |||
}; | |||
extern ArduinoOTAClass ArduinoOTA; | |||
#endif /* __ARDUINO_OTA_H */ |
@ -0,0 +1,54 @@ | |||
#include <ESP8266WiFi.h> | |||
#include <ESP8266mDNS.h> | |||
#include <WiFiUdp.h> | |||
#include <ArduinoOTA.h> | |||
const char* ssid = ".........."; | |||
const char* password = ".........."; | |||
void setup() { | |||
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.restart(); | |||
} | |||
// Port defaults to 8266 | |||
// ArduinoOTA.setPort(8266); | |||
// Hostname defaults to esp8266-[ChipID] | |||
// ArduinoOTA.setHostname("myesp8266"); | |||
// No authentication by default | |||
// ArduinoOTA.setPassword((const char *)"123"); | |||
ArduinoOTA.onStart([]() { | |||
Serial.println("Start"); | |||
}); | |||
ArduinoOTA.onEnd([]() { | |||
Serial.println("\nEnd"); | |||
}); | |||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { | |||
Serial.printf("Progress: %u%%\r", (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_RECEIVE_ERROR) Serial.println("Receive Failed"); | |||
else if (error == OTA_END_ERROR) Serial.println("End Failed"); | |||
}); | |||
ArduinoOTA.begin(); | |||
Serial.println("Ready"); | |||
Serial.print("IP address: "); | |||
Serial.println(WiFi.localIP()); | |||
} | |||
void loop() { | |||
ArduinoOTA.handle(); | |||
} |
@ -0,0 +1,68 @@ | |||
#include <ESP8266WiFi.h> | |||
#include <ESP8266mDNS.h> | |||
#include <WiFiUdp.h> | |||
#include <ArduinoOTA.h> | |||
const char* ssid = "..."; | |||
const char* password = "..."; | |||
const char* host = "OTA-LEDS"; | |||
int led_pin = 13; | |||
#define N_DIMMERS 3 | |||
int dimmer_pin[] = {14, 5, 15}; | |||
void setup() { | |||
Serial.begin(115200); | |||
/* switch on led */ | |||
pinMode(led_pin, OUTPUT); | |||
digitalWrite(led_pin, LOW); | |||
Serial.println("Booting"); | |||
WiFi.mode(WIFI_STA); | |||
WiFi.begin(ssid, password); | |||
while (WiFi.waitForConnectResult() != WL_CONNECTED){ | |||
WiFi.begin(ssid, password); | |||
Serial.println("Retrying connection..."); | |||
} | |||
/* switch off led */ | |||
digitalWrite(led_pin, HIGH); | |||
/* configure dimmers, and OTA server events */ | |||
analogWriteRange(1000); | |||
analogWrite(led_pin,990); | |||
for (int i=0; i<N_DIMMERS; i++) | |||
{ | |||
pinMode(dimmer_pin[i], OUTPUT); | |||
analogWrite(dimmer_pin[i],50); | |||
} | |||
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); | |||
}); | |||
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); | |||
delay(50); | |||
} | |||
}); | |||
ArduinoOTA.onError([](ota_error_t error) { ESP.restart(); }); | |||
/* setup the OTA server */ | |||
ArduinoOTA.begin(); | |||
Serial.println("Ready"); | |||
} | |||
void loop() { | |||
ArduinoOTA.handle(); | |||
} |
@ -0,0 +1,26 @@ | |||
####################################### | |||
# Syntax Coloring Map For Ultrasound | |||
####################################### | |||
####################################### | |||
# Datatypes (KEYWORD1) | |||
####################################### | |||
ArduinoOTA KEYWORD1 | |||
####################################### | |||
# Methods and Functions (KEYWORD2) | |||
####################################### | |||
begin KEYWORD2 | |||
setup KEYWORD2 | |||
handle KEYWORD2 | |||
onStart KEYWORD2 | |||
onEnd KEYWORD2 | |||
onError KEYWORD2 | |||
onProgress KEYWORD2 | |||
####################################### | |||
# Constants (LITERAL1) | |||
####################################### | |||
@ -0,0 +1,9 @@ | |||
name=ArduinoOTA | |||
version=1.0 | |||
author=Ivan Grokhotkov and Miguel Angel Ajo | |||
maintainer=Ivan Grokhtkov <ivan@esp8266.com> | |||
sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download. | |||
paragraph=With this library you can enable your sketch to be upgraded over network. Includes mdns anounces to get discovered by the arduino IDE. | |||
category=Communication | |||
url= | |||
architectures=esp8266 |
@ -0,0 +1,78 @@ | |||
/* | |||
Debounce buttons and trigger events | |||
Copyright (C) 2015 by Xose Pérez <xose dot perez at gmail dot com> | |||
This program is free software: you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation, either version 3 of the License, or | |||
(at your option) any later version. | |||
This program 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 General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <Arduino.h> | |||
#include "DebounceEvent.h" | |||
DebounceEvent::DebounceEvent(uint8_t pin, callback_t callback, uint8_t defaultStatus, unsigned long delay) { | |||
// store configuration | |||
_pin = pin; | |||
_status = _defaultStatus = defaultStatus; | |||
_delay = delay; | |||
_callback = callback; | |||
// set up button | |||
if (_defaultStatus == LOW) { | |||
pinMode(_pin, INPUT); | |||
} else { | |||
pinMode(_pin, INPUT_PULLUP); | |||
} | |||
} | |||
bool DebounceEvent::loop() { | |||
// holds whether status has changed or not | |||
bool changed = false; | |||
if (digitalRead(_pin) != _status) { | |||
delay(_delay); | |||
uint8_t newStatus = digitalRead(_pin); | |||
if (newStatus != _status) { | |||
changed = true; | |||
_status = newStatus; | |||
// raise events if callback defined | |||
if (_callback) { | |||
// raise change event | |||
_callback(_pin, EVENT_CHANGED); | |||
if (_status == _defaultStatus) { | |||
// raise released event | |||
_callback(_pin, EVENT_RELEASED); | |||
} else { | |||
// raise pressed event | |||
_callback(_pin, EVENT_PRESSED); | |||
} | |||
} | |||
} | |||
} | |||
return changed; | |||
} | |||
bool DebounceEvent::pressed() { | |||
return (_status != _defaultStatus); | |||
} |
@ -0,0 +1,49 @@ | |||
/* | |||
Debounce buttons and trigger events | |||
Copyright (C) 2015 by Xose Pérez <xose dot perez at gmail dot com> | |||
This program is free software: you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation, either version 3 of the License, or | |||
(at your option) any later version. | |||
This program 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 General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef _DEBOUNCE_EVENT_h | |||
#define _DEBOUNCE_EVENT_h | |||
#define DEBOUNCE_DELAY 100 | |||
#define EVENT_CHANGED 0 | |||
#define EVENT_PRESSED 1 | |||
#define EVENT_RELEASED 2 | |||
typedef void(*callback_t)(uint8_t pin, uint8_t event); | |||
class DebounceEvent { | |||
private: | |||
uint8_t _pin; | |||
uint8_t _status; | |||
uint8_t _defaultStatus; | |||
unsigned long _delay; | |||
callback_t _callback; | |||
public: | |||
DebounceEvent(uint8_t pin, callback_t callback = false, uint8_t defaultStatus = HIGH, unsigned long delay = DEBOUNCE_DELAY); | |||
bool pressed(); | |||
bool loop(); | |||
}; | |||
#endif |