Add Ota and mdns (#257)
* Add Sketch Update Library * Add MDNS Library * Add Arduino OTA Library * add missing library file * Add library files for Update * Add missing headers * fix ota command * Add espota binary * remove bad example * PlatformIO does not auto forward declare methods like Arduino Builder
This commit is contained in:
		
							parent
							
								
									2f5efed220
								
							
						
					
					
						commit
						fa1716e73e
					
				
							
								
								
									
										65
									
								
								libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | ||||
| #include <WiFi.h> | ||||
| #include <ESPmDNS.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 3232
 | ||||
|   // ArduinoOTA.setPort(3232);
 | ||||
| 
 | ||||
|   // Hostname defaults to esp3232-[MAC]
 | ||||
|   // ArduinoOTA.setHostname("myesp32");
 | ||||
| 
 | ||||
|   // No authentication by default
 | ||||
|   // ArduinoOTA.setPassword("admin");
 | ||||
| 
 | ||||
|   // Password can be set with it's md5 value as well
 | ||||
|   // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
 | ||||
|   // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
 | ||||
| 
 | ||||
|   ArduinoOTA.onStart([]() { | ||||
|     String type; | ||||
|     if (ArduinoOTA.getCommand() == U_FLASH) | ||||
|       type = "sketch"; | ||||
|     else // U_SPIFFS
 | ||||
|       type = "filesystem"; | ||||
| 
 | ||||
|     // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
 | ||||
|     Serial.println("Start updating " + type); | ||||
|   }); | ||||
|   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(); | ||||
| } | ||||
							
								
								
									
										26
									
								
								libraries/ArduinoOTA/keywords.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								libraries/ArduinoOTA/keywords.txt
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||
| ####################################### | ||||
| 
 | ||||
							
								
								
									
										9
									
								
								libraries/ArduinoOTA/library.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								libraries/ArduinoOTA/library.properties
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| name=ArduinoOTA | ||||
| version=1.0 | ||||
| author=Ivan Grokhotkov and Hristo Gochkov | ||||
| maintainer=Hristo Gochkov <hristo@espressif.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=esp32 | ||||
							
								
								
									
										367
									
								
								libraries/ArduinoOTA/src/ArduinoOTA.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										367
									
								
								libraries/ArduinoOTA/src/ArduinoOTA.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,367 @@ | ||||
| #ifndef LWIP_OPEN_SRC | ||||
| #define LWIP_OPEN_SRC | ||||
| #endif | ||||
| #include <functional> | ||||
| #include <WiFiUdp.h> | ||||
| #include "ArduinoOTA.h" | ||||
| #include "ESPmDNS.h" | ||||
| #include "MD5Builder.h" | ||||
| #include "Update.h" | ||||
| 
 | ||||
| 
 | ||||
| //#define OTA_DEBUG Serial
 | ||||
| 
 | ||||
| ArduinoOTAClass::ArduinoOTAClass() | ||||
| : _port(0) | ||||
| , _initialized(false) | ||||
| , _rebootOnSuccess(true) | ||||
| , _mdnsEnabled(true) | ||||
| , _state(OTA_IDLE) | ||||
| , _size(0) | ||||
| , _cmd(0) | ||||
| , _ota_port(0) | ||||
| , _start_callback(NULL) | ||||
| , _end_callback(NULL) | ||||
| , _error_callback(NULL) | ||||
| , _progress_callback(NULL) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| ArduinoOTAClass::~ArduinoOTAClass(){ | ||||
|     _udp_ota.stop(); | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::onStart(THandlerFunction fn) { | ||||
|     _start_callback = fn; | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::onEnd(THandlerFunction fn) { | ||||
|     _end_callback = fn; | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::onProgress(THandlerFunction_Progress fn) { | ||||
|     _progress_callback = fn; | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::onError(THandlerFunction_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; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| String ArduinoOTAClass::getHostname() { | ||||
|     return _hostname; | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::setPassword(const char * password) { | ||||
|     if (!_initialized && !_password.length() && password) { | ||||
|         MD5Builder passmd5; | ||||
|         passmd5.begin(); | ||||
|         passmd5.add(password); | ||||
|         passmd5.calculate(); | ||||
|         _password = passmd5.toString(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::setPasswordHash(const char * password) { | ||||
|     if (!_initialized && !_password.length() && password) { | ||||
|         _password = password; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::setRebootOnSuccess(bool reboot){ | ||||
|     _rebootOnSuccess = reboot; | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::setMdnsEnabled(bool enabled){ | ||||
|     _mdnsEnabled = enabled; | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::begin() { | ||||
|     if (_initialized){ | ||||
|         log_w("already initialized"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!_port) { | ||||
|         _port = 3232; | ||||
|     } | ||||
| 
 | ||||
|     if(!_udp_ota.begin(_port)){ | ||||
|         log_e("udp bind failed"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     if (!_hostname.length()) { | ||||
|         char tmp[20]; | ||||
|         uint8_t mac[6]; | ||||
|         WiFi.macAddress(mac); | ||||
|         sprintf(tmp, "esp32-%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); | ||||
|         _hostname = tmp; | ||||
|     } | ||||
|     if(_mdnsEnabled){ | ||||
|         MDNS.begin(_hostname.c_str()); | ||||
|         MDNS.enableArduino(_port, (_password.length() > 0)); | ||||
|     } | ||||
|     _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 (_state == OTA_IDLE) { | ||||
|         int cmd = parseInt(); | ||||
|         if (cmd != U_FLASH && cmd != U_SPIFFS) | ||||
|             return; | ||||
|         _cmd  = cmd; | ||||
|         _ota_port = parseInt(); | ||||
|         _size = parseInt(); | ||||
|         _udp_ota.read(); | ||||
|         _md5 = readStringUntil('\n'); | ||||
|         _md5.trim(); | ||||
|         if(_md5.length() != 32){ | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (_password.length()){ | ||||
|             MD5Builder nonce_md5; | ||||
|             nonce_md5.begin(); | ||||
|             nonce_md5.add(String(micros())); | ||||
|             nonce_md5.calculate(); | ||||
|             _nonce = nonce_md5.toString(); | ||||
| 
 | ||||
|             _udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort()); | ||||
|             _udp_ota.printf("AUTH %s", _nonce.c_str()); | ||||
|             _udp_ota.endPacket(); | ||||
|             _state = OTA_WAITAUTH; | ||||
|             return; | ||||
|         } else { | ||||
|             _udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort()); | ||||
|             _udp_ota.print("OK"); | ||||
|             _udp_ota.endPacket(); | ||||
|             _ota_ip = _udp_ota.remoteIP(); | ||||
|             _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; | ||||
|         } | ||||
| 
 | ||||
|         String challenge = _password + ":" + String(_nonce) + ":" + cnonce; | ||||
|         MD5Builder _challengemd5; | ||||
|         _challengemd5.begin(); | ||||
|         _challengemd5.add(challenge); | ||||
|         _challengemd5.calculate(); | ||||
|         String result = _challengemd5.toString(); | ||||
| 
 | ||||
|         if(result.equals(response)){ | ||||
|             _udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort()); | ||||
|             _udp_ota.print("OK"); | ||||
|             _udp_ota.endPacket(); | ||||
|             _ota_ip = _udp_ota.remoteIP(); | ||||
|             _state = OTA_RUNUPDATE; | ||||
|         } else { | ||||
|             _udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort()); | ||||
|             _udp_ota.print("Authentication Failed"); | ||||
|             _udp_ota.endPacket(); | ||||
|             if (_error_callback) _error_callback(OTA_AUTH_ERROR); | ||||
|             _state = OTA_IDLE; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::_runUpdate() { | ||||
|     if (!Update.begin(_size, _cmd)) { | ||||
| #ifdef OTA_DEBUG | ||||
|         Update.printError(OTA_DEBUG); | ||||
| #endif | ||||
|         if (_error_callback) { | ||||
|             _error_callback(OTA_BEGIN_ERROR); | ||||
|         } | ||||
|         _state = OTA_IDLE; | ||||
|         return; | ||||
|     } | ||||
|     Update.setMD5(_md5.c_str()); | ||||
| 
 | ||||
|     if (_start_callback) { | ||||
|         _start_callback(); | ||||
|     } | ||||
|     if (_progress_callback) { | ||||
|         _progress_callback(0, _size); | ||||
|     } | ||||
| 
 | ||||
|     WiFiClient client; | ||||
|     if (!client.connect(_ota_ip, _ota_port)) { | ||||
|         if (_error_callback) { | ||||
|             _error_callback(OTA_CONNECT_ERROR); | ||||
|         } | ||||
|         _state = OTA_IDLE; | ||||
|     } | ||||
| 
 | ||||
|     uint32_t written = 0, total = 0, tried = 0; | ||||
|     while (!Update.isFinished() && client.connected()) { | ||||
|         size_t waited = 1000; | ||||
|         size_t available = client.available(); | ||||
|         while (!available && waited){ | ||||
|             delay(1); | ||||
|             waited -=1 ; | ||||
|             available = client.available(); | ||||
|         } | ||||
|         if (!waited){ | ||||
|             if(written && tried++ < 3){ | ||||
| #ifdef OTA_DEBUG | ||||
|                 OTA_DEBUG.printf("Try[%u]: %u\n", tried, written); | ||||
| #endif | ||||
|                 if(!client.printf("%u", written)){ | ||||
| #ifdef OTA_DEBUG | ||||
|                     OTA_DEBUG.printf("failed to respond\n"); | ||||
| #endif | ||||
|                     _state = OTA_IDLE; | ||||
|                     break; | ||||
|                 } | ||||
|                 continue; | ||||
|             } | ||||
| #ifdef OTA_DEBUG | ||||
|             OTA_DEBUG.printf("Receive Failed\n"); | ||||
| #endif | ||||
|             if (_error_callback) { | ||||
|                 _error_callback(OTA_RECEIVE_ERROR); | ||||
|             } | ||||
|             _state = OTA_IDLE; | ||||
|             Update.abort(); | ||||
|             return; | ||||
|         } | ||||
|         if(!available){ | ||||
| #ifdef OTA_DEBUG | ||||
|             OTA_DEBUG.printf("No Data: %u\n", waited); | ||||
| #endif | ||||
|             _state = OTA_IDLE; | ||||
|             break; | ||||
|         } | ||||
|         tried = 0; | ||||
|         static uint8_t buf[1460]; | ||||
|         if(available > 1460){ | ||||
|             available = 1460; | ||||
|         } | ||||
|         size_t r = client.read(buf, available); | ||||
|         if(r != available){ | ||||
|             log_w("didn't read enough! %u != %u", r, available); | ||||
|         } | ||||
| 
 | ||||
|         written = Update.write(buf, r); | ||||
|         if (written > 0) { | ||||
|             if(written != r){ | ||||
|                 log_w("didn't write enough! %u != %u", written, r); | ||||
|             } | ||||
|             if(!client.printf("%u", written)){ | ||||
| #ifdef OTA_DEBUG | ||||
|                 OTA_DEBUG.printf("failed to respond\n"); | ||||
| #endif | ||||
|             } | ||||
|             total += written; | ||||
|             if(_progress_callback) { | ||||
|                 _progress_callback(total, _size); | ||||
|             } | ||||
|         } else { | ||||
| #ifdef OTA_DEBUG | ||||
|             Update.printError(OTA_DEBUG); | ||||
| #endif | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (Update.end()) { | ||||
|         client.print("OK"); | ||||
|         client.stop(); | ||||
|         delay(10); | ||||
|         if (_end_callback) { | ||||
|             _end_callback(); | ||||
|         } | ||||
|         if(_rebootOnSuccess){ | ||||
|             //let serial/network finish tasks that might be given in _end_callback
 | ||||
|             delay(100); | ||||
|             ESP.restart(); | ||||
|         } | ||||
|     } else { | ||||
|         if (_error_callback) { | ||||
|             _error_callback(OTA_END_ERROR); | ||||
|         } | ||||
|         Update.printError(client); | ||||
|         client.stop(); | ||||
|         delay(10); | ||||
| #ifdef OTA_DEBUG | ||||
|         OTA_DEBUG.print("Update ERROR: "); | ||||
|         Update.printError(OTA_DEBUG); | ||||
| #endif | ||||
|         _state = OTA_IDLE; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ArduinoOTAClass::handle() { | ||||
|     if (_state == OTA_RUNUPDATE) { | ||||
|         _runUpdate(); | ||||
|         _state = OTA_IDLE; | ||||
|     } | ||||
|     if(_udp_ota.parsePacket()){ | ||||
|         _onRx(); | ||||
|         _udp_ota.flush(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int ArduinoOTAClass::getCommand() { | ||||
|     return _cmd; | ||||
| } | ||||
| 
 | ||||
| #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA) | ||||
| ArduinoOTAClass ArduinoOTA; | ||||
| #endif | ||||
							
								
								
									
										103
									
								
								libraries/ArduinoOTA/src/ArduinoOTA.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								libraries/ArduinoOTA/src/ArduinoOTA.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | ||||
| #ifndef __ARDUINO_OTA_H | ||||
| #define __ARDUINO_OTA_H | ||||
| 
 | ||||
| #include <WiFi.h> | ||||
| #include <functional> | ||||
| #include "Update.h" | ||||
| 
 | ||||
| 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: | ||||
| 	typedef std::function<void(void)> THandlerFunction; | ||||
| 	typedef std::function<void(ota_error_t)> THandlerFunction_Error; | ||||
| 	typedef std::function<void(unsigned int, unsigned int)> THandlerFunction_Progress; | ||||
| 
 | ||||
|     ArduinoOTAClass(); | ||||
|     ~ArduinoOTAClass(); | ||||
| 
 | ||||
|     //Sets the service port. Default 8266
 | ||||
|     void setPort(uint16_t port); | ||||
| 
 | ||||
|     //Sets the device hostname. Default esp8266-xxxxxx
 | ||||
|     void setHostname(const char *hostname); | ||||
|     String getHostname(); | ||||
| 
 | ||||
|     //Sets the password that will be required for OTA. Default NULL
 | ||||
|     void setPassword(const char *password); | ||||
| 
 | ||||
|     //Sets the password as above but in the form MD5(password). Default NULL
 | ||||
|     void setPasswordHash(const char *password); | ||||
| 
 | ||||
|     //Sets if the device should be rebooted after successful update. Default true
 | ||||
|     void setRebootOnSuccess(bool reboot); | ||||
| 
 | ||||
|     //Sets if the device should advertise itself to Arduino IDE. Default true
 | ||||
|     void setMdnsEnabled(bool enabled); | ||||
| 
 | ||||
|     //This callback will be called when OTA connection has begun
 | ||||
|     void onStart(THandlerFunction fn); | ||||
| 
 | ||||
|     //This callback will be called when OTA has finished
 | ||||
|     void onEnd(THandlerFunction fn); | ||||
| 
 | ||||
|     //This callback will be called when OTA encountered Error
 | ||||
|     void onError(THandlerFunction_Error fn); | ||||
| 
 | ||||
|     //This callback will be called when OTA is receiving data
 | ||||
|     void onProgress(THandlerFunction_Progress fn); | ||||
| 
 | ||||
|     //Starts the ArduinoOTA service
 | ||||
|     void begin(); | ||||
| 
 | ||||
|     //Call this in loop() to run the service
 | ||||
|     void handle(); | ||||
| 
 | ||||
|     //Gets update command type after OTA has started. Either U_FLASH or U_SPIFFS
 | ||||
|     int getCommand(); | ||||
| 
 | ||||
|   private: | ||||
|     int _port; | ||||
|     String _password; | ||||
|     String _hostname; | ||||
|     String _nonce; | ||||
|     WiFiUDP _udp_ota; | ||||
|     bool _initialized; | ||||
|     bool _rebootOnSuccess; | ||||
|     bool _mdnsEnabled; | ||||
|     ota_state_t _state; | ||||
|     int _size; | ||||
|     int _cmd; | ||||
|     int _ota_port; | ||||
|     IPAddress _ota_ip; | ||||
|     String _md5; | ||||
| 
 | ||||
|     THandlerFunction _start_callback; | ||||
|     THandlerFunction _end_callback; | ||||
|     THandlerFunction_Error _error_callback; | ||||
|     THandlerFunction_Progress _progress_callback; | ||||
| 
 | ||||
|     void _runUpdate(void); | ||||
|     void _onRx(void); | ||||
|     int parseInt(void); | ||||
|     String readStringUntil(char end); | ||||
| }; | ||||
| 
 | ||||
| #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA) | ||||
| extern ArduinoOTAClass ArduinoOTA; | ||||
| #endif | ||||
| 
 | ||||
| #endif /* __ARDUINO_OTA_H */ | ||||
| @ -0,0 +1,80 @@ | ||||
| /*
 | ||||
|   ESP8266 mDNS-SD responder and query sample | ||||
| 
 | ||||
|   This is an example of announcing and finding services. | ||||
|    | ||||
|   Instructions: | ||||
|   - Update WiFi SSID and password as necessary. | ||||
|   - Flash the sketch to two ESP8266 boards | ||||
|   - The last one powered on should now find the other. | ||||
|  */ | ||||
| 
 | ||||
| #include <WiFi.h> | ||||
| #include <ESPmDNS.h> | ||||
| 
 | ||||
| const char* ssid     = "..."; | ||||
| const char* password = "..."; | ||||
| 
 | ||||
| void setup() { | ||||
|     Serial.begin(115200); | ||||
|     WiFi.begin(ssid, password); | ||||
|     while (WiFi.status() != WL_CONNECTED) { | ||||
|         delay(250); | ||||
|         Serial.print("."); | ||||
|     } | ||||
|     Serial.println(""); | ||||
|     Serial.print("Connected to "); | ||||
|     Serial.println(ssid); | ||||
|     Serial.print("IP address: "); | ||||
|     Serial.println(WiFi.localIP()); | ||||
| 
 | ||||
|     if (!MDNS.begin("ESP32_Browser")) { | ||||
|         Serial.println("Error setting up MDNS responder!"); | ||||
|         while(1){ | ||||
|             delay(1000); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void loop() { | ||||
|     browseService("http", "tcp"); | ||||
|     delay(1000); | ||||
|     browseService("arduino", "tcp"); | ||||
|     delay(1000); | ||||
|     browseService("workstation", "tcp"); | ||||
|     delay(1000); | ||||
|     browseService("smb", "tcp"); | ||||
|     delay(1000); | ||||
|     browseService("afpovertcp", "tcp"); | ||||
|     delay(1000); | ||||
|     browseService("ftp", "tcp"); | ||||
|     delay(1000); | ||||
|     browseService("ipp", "tcp"); | ||||
|     delay(1000); | ||||
|     browseService("printer", "tcp"); | ||||
|     delay(10000); | ||||
| } | ||||
| 
 | ||||
| void browseService(const char * service, const char * proto){ | ||||
|     Serial.printf("Browsing for service _%s._%s.local. ... ", service, proto); | ||||
|     int n = MDNS.queryService(service, proto); | ||||
|     if (n == 0) { | ||||
|         Serial.println("no services found"); | ||||
|     } else { | ||||
|         Serial.print(n); | ||||
|         Serial.println(" service(s) found"); | ||||
|         for (int i = 0; i < n; ++i) { | ||||
|             // Print details for each service found
 | ||||
|             Serial.print("  "); | ||||
|             Serial.print(i + 1); | ||||
|             Serial.print(": "); | ||||
|             Serial.print(MDNS.hostname(i)); | ||||
|             Serial.print(" ("); | ||||
|             Serial.print(MDNS.IP(i)); | ||||
|             Serial.print(":"); | ||||
|             Serial.print(MDNS.port(i)); | ||||
|             Serial.println(")"); | ||||
|         } | ||||
|     } | ||||
|     Serial.println(); | ||||
| } | ||||
							
								
								
									
										120
									
								
								libraries/ESPmDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								libraries/ESPmDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | ||||
| /*
 | ||||
|   ESP32 mDNS responder sample | ||||
| 
 | ||||
|   This is an example of an HTTP server that is accessible | ||||
|   via http://esp32.local URL thanks to mDNS responder.
 | ||||
| 
 | ||||
|   Instructions: | ||||
|   - Update WiFi SSID and password as necessary. | ||||
|   - Flash the sketch to the ESP32 board | ||||
|   - Install host software: | ||||
|     - For Linux, install Avahi (http://avahi.org/).
 | ||||
|     - For Windows, install Bonjour (http://www.apple.com/support/bonjour/).
 | ||||
|     - For Mac OSX and iOS support is built in through Bonjour already. | ||||
|   - Point your browser to http://esp32.local, you should see a response.
 | ||||
| 
 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #include <WiFi.h> | ||||
| #include <ESPmDNS.h> | ||||
| #include <WiFiClient.h> | ||||
| 
 | ||||
| const char* ssid = "............"; | ||||
| const char* password = ".............."; | ||||
| 
 | ||||
| // TCP server at port 80 will respond to HTTP requests
 | ||||
| WiFiServer server(80); | ||||
| 
 | ||||
| void setup(void) | ||||
| {   | ||||
|     Serial.begin(115200); | ||||
| 
 | ||||
|     // Connect to WiFi network
 | ||||
|     WiFi.begin(ssid, password); | ||||
|     Serial.println(""); | ||||
| 
 | ||||
|     // Wait for connection
 | ||||
|     while (WiFi.status() != WL_CONNECTED) { | ||||
|         delay(500); | ||||
|         Serial.print("."); | ||||
|     } | ||||
|     Serial.println(""); | ||||
|     Serial.print("Connected to "); | ||||
|     Serial.println(ssid); | ||||
|     Serial.print("IP address: "); | ||||
|     Serial.println(WiFi.localIP()); | ||||
| 
 | ||||
|     // Set up mDNS responder:
 | ||||
|     // - first argument is the domain name, in this example
 | ||||
|     //   the fully-qualified domain name is "esp8266.local"
 | ||||
|     // - second argument is the IP address to advertise
 | ||||
|     //   we send our IP address on the WiFi network
 | ||||
|     if (!MDNS.begin("esp32")) { | ||||
|         Serial.println("Error setting up MDNS responder!"); | ||||
|         while(1) { | ||||
|             delay(1000); | ||||
|         } | ||||
|     } | ||||
|     Serial.println("mDNS responder started"); | ||||
| 
 | ||||
|     // Start TCP (HTTP) server
 | ||||
|     server.begin(); | ||||
|     Serial.println("TCP server started"); | ||||
| 
 | ||||
|     // Add service to MDNS-SD
 | ||||
|     MDNS.addService("http", "tcp", 80); | ||||
| } | ||||
| 
 | ||||
| void loop(void) | ||||
| { | ||||
|     // Check if a client has connected
 | ||||
|     WiFiClient client = server.available(); | ||||
|     if (!client) { | ||||
|         return; | ||||
|     } | ||||
|     Serial.println(""); | ||||
|     Serial.println("New client"); | ||||
| 
 | ||||
|     // Wait for data from client to become available
 | ||||
|     while(client.connected() && !client.available()){ | ||||
|         delay(1); | ||||
|     } | ||||
| 
 | ||||
|     // Read the first line of HTTP request
 | ||||
|     String req = client.readStringUntil('\r'); | ||||
| 
 | ||||
|     // First line of HTTP request looks like "GET /path HTTP/1.1"
 | ||||
|     // Retrieve the "/path" part by finding the spaces
 | ||||
|     int addr_start = req.indexOf(' '); | ||||
|     int addr_end = req.indexOf(' ', addr_start + 1); | ||||
|     if (addr_start == -1 || addr_end == -1) { | ||||
|         Serial.print("Invalid request: "); | ||||
|         Serial.println(req); | ||||
|         return; | ||||
|     } | ||||
|     req = req.substring(addr_start + 1, addr_end); | ||||
|     Serial.print("Request: "); | ||||
|     Serial.println(req); | ||||
|     client.flush(); | ||||
| 
 | ||||
|     String s; | ||||
|     if (req == "/") | ||||
|     { | ||||
|         IPAddress ip = WiFi.localIP(); | ||||
|         String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]); | ||||
|         s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>Hello from ESP32 at "; | ||||
|         s += ipStr; | ||||
|         s += "</html>\r\n\r\n"; | ||||
|         Serial.println("Sending 200"); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         s = "HTTP/1.1 404 Not Found\r\n\r\n"; | ||||
|         Serial.println("Sending 404"); | ||||
|     } | ||||
|     client.print(s); | ||||
| 
 | ||||
|     Serial.println("Done with client"); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										25
									
								
								libraries/ESPmDNS/keywords.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								libraries/ESPmDNS/keywords.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| ####################################### | ||||
| # Syntax Coloring Map For Ultrasound | ||||
| ####################################### | ||||
| 
 | ||||
| ####################################### | ||||
| # Datatypes (KEYWORD1) | ||||
| ####################################### | ||||
| 
 | ||||
| ESPmDNS	KEYWORD1 | ||||
| MDNS	KEYWORD1 | ||||
| 
 | ||||
| ####################################### | ||||
| # Methods and Functions (KEYWORD2) | ||||
| ####################################### | ||||
| 
 | ||||
| begin	KEYWORD2 | ||||
| end KEYWORD2 | ||||
| addService	KEYWORD2 | ||||
| enableArduino	KEYWORD2 | ||||
| disableArduino   KEYWORD2 | ||||
| 
 | ||||
| ####################################### | ||||
| # Constants (LITERAL1) | ||||
| ####################################### | ||||
| 
 | ||||
							
								
								
									
										9
									
								
								libraries/ESPmDNS/library.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								libraries/ESPmDNS/library.properties
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| name=ESPmDNS | ||||
| version=1.0 | ||||
| author=Hristo Gochkov, Ivan Grokhtkov | ||||
| maintainer=Hristo Gochkov <hristo@espressif.com> | ||||
| sentence=ESP32 mDNS Library | ||||
| paragraph= | ||||
| category=Communication | ||||
| url= | ||||
| architectures=esp32 | ||||
							
								
								
									
										187
									
								
								libraries/ESPmDNS/src/ESPmDNS.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								libraries/ESPmDNS/src/ESPmDNS.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,187 @@ | ||||
| /*
 | ||||
| 
 | ||||
| ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) | ||||
| Version 1.1 | ||||
| Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) | ||||
| ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) | ||||
| MDNS-SD Suport 2015 Hristo Gochkov (hristo@espressif.com) | ||||
| Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) | ||||
| 
 | ||||
| 
 | ||||
| License (MIT license): | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
| 
 | ||||
|   The above copyright notice and this permission notice shall be included in | ||||
|   all copies or substantial portions of the Software. | ||||
| 
 | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|   THE SOFTWARE. | ||||
| 
 | ||||
|  */ | ||||
| 
 | ||||
| // Important RFC's for reference:
 | ||||
| // - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt
 | ||||
| // - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt
 | ||||
| // - MDNS-SD: https://tools.ietf.org/html/rfc6763
 | ||||
| 
 | ||||
| #ifndef LWIP_OPEN_SRC | ||||
| #define LWIP_OPEN_SRC | ||||
| #endif | ||||
| 
 | ||||
| #include "ESPmDNS.h" | ||||
| #include <functional> | ||||
| #include "esp_wifi.h" | ||||
| 
 | ||||
| MDNSResponder::MDNSResponder() : mdns(NULL), _if(TCPIP_ADAPTER_IF_STA) {} | ||||
| MDNSResponder::~MDNSResponder() { | ||||
|     end(); | ||||
| } | ||||
| 
 | ||||
| bool MDNSResponder::begin(const char* hostName, tcpip_adapter_if_t tcpip_if, uint32_t ttl){ | ||||
|     _if = tcpip_if; | ||||
|     if(!mdns && mdns_init(_if, &mdns)){ | ||||
|         log_e("Failed starting MDNS"); | ||||
|         return false; | ||||
|     } | ||||
|     _hostname = hostName; | ||||
|     if(mdns_set_hostname(mdns, hostName)) { | ||||
|         log_e("Failed setting MDNS hostname"); | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void MDNSResponder::end() { | ||||
|     if(!mdns){ | ||||
|         return; | ||||
|     } | ||||
|     mdns_free(mdns); | ||||
|     mdns = NULL; | ||||
| } | ||||
| 
 | ||||
| void MDNSResponder::setInstanceName(String name) { | ||||
|     if (name.length() > 63) return; | ||||
|     if(mdns_set_instance(mdns, name.c_str())){ | ||||
|         log_e("Failed setting MDNS instance"); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MDNSResponder::enableArduino(uint16_t port, bool auth){ | ||||
|     const char * arduTxtData[4] = { | ||||
|             "board=" ARDUINO_BOARD, | ||||
|             "tcp_check=no", | ||||
|             "ssh_upload=no", | ||||
|             "auth_upload=no" | ||||
|     }; | ||||
|     if(auth){ | ||||
|         arduTxtData[3] = "auth_upload=yes"; | ||||
|     } | ||||
| 
 | ||||
|     if(mdns_service_add(mdns, "_arduino", "_tcp", port)) { | ||||
|         log_e("Failed adding Arduino service"); | ||||
|     } else if(mdns_service_txt_set(mdns, "_arduino", "_tcp", 4, arduTxtData)) { | ||||
|         log_e("Failed setting Arduino service TXT"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MDNSResponder::disableArduino(){ | ||||
|     if(mdns_service_remove(mdns, "_arduino", "_tcp")) { | ||||
|         log_w("Failed removing Arduino service"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MDNSResponder::enableWorkstation(){ | ||||
|     char winstance[21+_hostname.length()]; | ||||
|     uint8_t mac[6]; | ||||
|     esp_wifi_get_mac((wifi_interface_t)_if, mac); | ||||
|     sprintf(winstance, "%s [%02x:%02x:%02x:%02x:%02x:%02x]", _hostname.c_str(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); | ||||
| 
 | ||||
|     if(mdns_service_add(mdns, "_workstation", "_tcp", 9)) { | ||||
|         log_e("Failed adding Workstation service"); | ||||
|     } else if(mdns_service_instance_set(mdns, "_workstation", "_tcp", winstance)) { | ||||
|         log_e("Failed setting Workstation service instance name"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MDNSResponder::disableWorkstation(){ | ||||
|     if(mdns_service_remove(mdns, "_workstation", "_tcp")) { | ||||
|         log_w("Failed removing Workstation service"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MDNSResponder::addService(char *name, char *proto, uint16_t port){ | ||||
|     if(mdns_service_add(mdns, name, proto, port)) { | ||||
|         log_e("Failed adding service %s.%s.\n", name, proto); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool MDNSResponder::addServiceTxt(char *name, char *proto, char *key, char *value){ | ||||
|     //ToDo: implement it in IDF. This will set the TXT to one record currently
 | ||||
|     String txt = String(key) + "=" + String(value); | ||||
|     const char * txt_chr[1] = {txt.c_str()}; | ||||
|     if(mdns_service_txt_set(mdns, name, proto, 1, txt_chr)) { | ||||
|         log_e("Failed setting service TXT"); | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| int MDNSResponder::queryService(char *service, char *proto) { | ||||
|     mdns_result_free(mdns); | ||||
|     if(proto){ | ||||
|         char srv[strlen(service)+2]; | ||||
|         char prt[strlen(proto)+2]; | ||||
|         sprintf(srv, "_%s", service); | ||||
|         sprintf(prt, "_%s", proto); | ||||
|         return mdns_query(mdns, srv, prt, 2000); | ||||
|     } | ||||
|     return mdns_query(mdns, service, NULL, 2000); | ||||
| } | ||||
| 
 | ||||
| IPAddress MDNSResponder::queryHost(char *host){ | ||||
|     mdns_result_free(mdns); | ||||
|     if(!mdns_query(mdns, host, NULL, 2000)){ | ||||
|         return IPAddress(); | ||||
|     } | ||||
|     return IP(0); | ||||
| } | ||||
| 
 | ||||
| String MDNSResponder::hostname(int idx) { | ||||
|     const mdns_result_t * result = mdns_result_get(mdns, idx); | ||||
|     if(!result){ | ||||
|         log_e("Result %d not found", idx); | ||||
|         return String(); | ||||
|     } | ||||
|     return String(result->host); | ||||
| } | ||||
| 
 | ||||
| IPAddress MDNSResponder::IP(int idx) { | ||||
|     const mdns_result_t * result = mdns_result_get(mdns, idx); | ||||
|     if(!result){ | ||||
|         log_e("Result %d not found", idx); | ||||
|         return IPAddress(); | ||||
|     } | ||||
|     return IPAddress(result->addr.addr); | ||||
| } | ||||
| 
 | ||||
| uint16_t MDNSResponder::port(int idx) { | ||||
|     const mdns_result_t * result = mdns_result_get(mdns, idx); | ||||
|     if(!result){ | ||||
|         log_e("Result %d not found", idx); | ||||
|         return 0; | ||||
|     } | ||||
|     return result->port; | ||||
| } | ||||
| 
 | ||||
| MDNSResponder MDNS; | ||||
							
								
								
									
										117
									
								
								libraries/ESPmDNS/src/ESPmDNS.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								libraries/ESPmDNS/src/ESPmDNS.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,117 @@ | ||||
| /*
 | ||||
| ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) | ||||
| Version 1.1 | ||||
| Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) | ||||
| ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) | ||||
| MDNS-SD Suport 2015 Hristo Gochkov (hristo@espressif.com) | ||||
| Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) | ||||
| Rewritten for ESP32 by Hristo Gochkov (hristo@espressif.com) | ||||
| 
 | ||||
| This is a simple implementation of multicast DNS query support for an Arduino | ||||
| running on ESP32 chip.  | ||||
| 
 | ||||
| Usage: | ||||
| - Include the ESP32 Multicast DNS library in the sketch. | ||||
| - Call the begin method in the sketch's setup and provide a domain name (without | ||||
|   the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the | ||||
|   Adafruit CC3000 class instance.  Optionally provide a time to live (in seconds) | ||||
|   for the DNS record--the default is 1 hour. | ||||
| - Call the update method in each iteration of the sketch's loop function. | ||||
| 
 | ||||
| License (MIT license): | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
| 
 | ||||
|   The above copyright notice and this permission notice shall be included in | ||||
|   all copies or substantial portions of the Software. | ||||
| 
 | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|   THE SOFTWARE. | ||||
| 
 | ||||
| */ | ||||
| #ifndef ESP32MDNS_H | ||||
| #define ESP32MDNS_H | ||||
| 
 | ||||
| #include "Arduino.h" | ||||
| #include "mdns.h" | ||||
| 
 | ||||
| //this should be defined at build time
 | ||||
| #ifndef ARDUINO_BOARD | ||||
| #define ARDUINO_BOARD "esp32" | ||||
| #endif | ||||
| 
 | ||||
| class MDNSResponder { | ||||
| public: | ||||
|   MDNSResponder(); | ||||
|   ~MDNSResponder(); | ||||
|   bool begin(const char* hostName, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_STA, uint32_t ttl=120); | ||||
|   void end(); | ||||
| 
 | ||||
|   void setInstanceName(String name); | ||||
|   void setInstanceName(const char * name){ | ||||
|     setInstanceName(String(name)); | ||||
|   } | ||||
|   void setInstanceName(char * name){ | ||||
|     setInstanceName(String(name)); | ||||
|   } | ||||
| 
 | ||||
|   void addService(char *service, char *proto, uint16_t port); | ||||
|   void addService(const char *service, const char *proto, uint16_t port){ | ||||
|     addService((char *)service, (char *)proto, port); | ||||
|   } | ||||
|   void addService(String service, String proto, uint16_t port){ | ||||
|     addService(service.c_str(), proto.c_str(), port); | ||||
|   } | ||||
|    | ||||
|   bool addServiceTxt(char *name, char *proto, char * key, char * value); | ||||
|   void addServiceTxt(const char *name, const char *proto, const char *key,const char * value){ | ||||
|     addServiceTxt((char *)name, (char *)proto, (char *)key, (char *)value); | ||||
|   } | ||||
|   void addServiceTxt(String name, String proto, String key, String value){ | ||||
|     addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str()); | ||||
|   } | ||||
| 
 | ||||
|   void enableArduino(uint16_t port=3232, bool auth=false); | ||||
|   void disableArduino(); | ||||
| 
 | ||||
|   void enableWorkstation(); | ||||
|   void disableWorkstation(); | ||||
| 
 | ||||
|   IPAddress queryHost(char *host); | ||||
|   IPAddress queryHost(const char *host){ | ||||
|     return queryHost((char *)host); | ||||
|   } | ||||
|   IPAddress queryHost(String host){ | ||||
|     return queryHost(host.c_str()); | ||||
|   } | ||||
|    | ||||
|   int queryService(char *service, char *proto); | ||||
|   int queryService(const char *service, const char *proto){ | ||||
|     return queryService((char *)service, (char *)proto); | ||||
|   } | ||||
|   int queryService(String service, String proto){ | ||||
|     return queryService(service.c_str(), proto.c_str()); | ||||
|   } | ||||
| 
 | ||||
|   String hostname(int idx); | ||||
|   IPAddress IP(int idx); | ||||
|   uint16_t port(int idx); | ||||
|    | ||||
| private: | ||||
|   mdns_server_t * mdns; | ||||
|   tcpip_adapter_if_t _if; | ||||
|   String _hostname; | ||||
| }; | ||||
| 
 | ||||
| extern MDNSResponder MDNS; | ||||
| 
 | ||||
| #endif //ESP32MDNS_H
 | ||||
| @ -16,51 +16,6 @@ | ||||
| #include "SD.h" | ||||
| #include "SPI.h" | ||||
| 
 | ||||
| void setup(){ | ||||
|     Serial.begin(115200); | ||||
|     if(!SD.begin()){ | ||||
|         Serial.println("Card Mount Failed"); | ||||
|         return; | ||||
|     } | ||||
|     uint8_t cardType = SD.cardType(); | ||||
| 
 | ||||
|     if(cardType == CARD_NONE){ | ||||
|         Serial.println("No SD card attached"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     Serial.print("SD Card Type: "); | ||||
|     if(cardType == CARD_MMC){ | ||||
|         Serial.println("MMC"); | ||||
|     } else if(cardType == CARD_SD){ | ||||
|         Serial.println("SDSC"); | ||||
|     } else if(cardType == CARD_SDHC){ | ||||
|         Serial.println("SDHC"); | ||||
|     } else { | ||||
|         Serial.println("UNKNOWN"); | ||||
|     } | ||||
| 
 | ||||
|     uint64_t cardSize = SD.cardSize() / (1024 * 1024); | ||||
|     Serial.printf("SD Card Size: %lluMB\n", cardSize); | ||||
| 
 | ||||
|     listDir(SD, "/", 0); | ||||
|     createDir(SD, "/mydir"); | ||||
|     listDir(SD, "/", 0); | ||||
|     removeDir(SD, "/mydir"); | ||||
|     listDir(SD, "/", 2); | ||||
|     writeFile(SD, "/hello.txt", "Hello "); | ||||
|     appendFile(SD, "/hello.txt", "World!\n"); | ||||
|     readFile(SD, "/hello.txt"); | ||||
|     deleteFile(SD, "/foo.txt"); | ||||
|     renameFile(SD, "/hello.txt", "/foo.txt"); | ||||
|     readFile(SD, "/foo.txt"); | ||||
|     testFileIO(SD, "/test.txt"); | ||||
| } | ||||
| 
 | ||||
| void loop(){ | ||||
|      | ||||
| } | ||||
| 
 | ||||
| void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ | ||||
|     Serial.printf("Listing directory: %s\n", dirname); | ||||
| 
 | ||||
| @ -214,3 +169,48 @@ void testFileIO(fs::FS &fs, const char * path){ | ||||
|     Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); | ||||
|     file.close(); | ||||
| } | ||||
| 
 | ||||
| void setup(){ | ||||
|     Serial.begin(115200); | ||||
|     if(!SD.begin()){ | ||||
|         Serial.println("Card Mount Failed"); | ||||
|         return; | ||||
|     } | ||||
|     uint8_t cardType = SD.cardType(); | ||||
| 
 | ||||
|     if(cardType == CARD_NONE){ | ||||
|         Serial.println("No SD card attached"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     Serial.print("SD Card Type: "); | ||||
|     if(cardType == CARD_MMC){ | ||||
|         Serial.println("MMC"); | ||||
|     } else if(cardType == CARD_SD){ | ||||
|         Serial.println("SDSC"); | ||||
|     } else if(cardType == CARD_SDHC){ | ||||
|         Serial.println("SDHC"); | ||||
|     } else { | ||||
|         Serial.println("UNKNOWN"); | ||||
|     } | ||||
| 
 | ||||
|     uint64_t cardSize = SD.cardSize() / (1024 * 1024); | ||||
|     Serial.printf("SD Card Size: %lluMB\n", cardSize); | ||||
| 
 | ||||
|     listDir(SD, "/", 0); | ||||
|     createDir(SD, "/mydir"); | ||||
|     listDir(SD, "/", 0); | ||||
|     removeDir(SD, "/mydir"); | ||||
|     listDir(SD, "/", 2); | ||||
|     writeFile(SD, "/hello.txt", "Hello "); | ||||
|     appendFile(SD, "/hello.txt", "World!\n"); | ||||
|     readFile(SD, "/hello.txt"); | ||||
|     deleteFile(SD, "/foo.txt"); | ||||
|     renameFile(SD, "/hello.txt", "/foo.txt"); | ||||
|     readFile(SD, "/foo.txt"); | ||||
|     testFileIO(SD, "/test.txt"); | ||||
| } | ||||
| 
 | ||||
| void loop(){ | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -16,51 +16,6 @@ | ||||
| #include "FS.h" | ||||
| #include "SD_MMC.h" | ||||
| 
 | ||||
| void setup(){ | ||||
|     Serial.begin(115200); | ||||
|     if(!SD_MMC.begin()){ | ||||
|         Serial.println("Card Mount Failed"); | ||||
|         return; | ||||
|     } | ||||
|     uint8_t cardType = SD_MMC.cardType(); | ||||
| 
 | ||||
|     if(cardType == CARD_NONE){ | ||||
|         Serial.println("No SD_MMC card attached"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     Serial.print("SD_MMC Card Type: "); | ||||
|     if(cardType == CARD_MMC){ | ||||
|         Serial.println("MMC"); | ||||
|     } else if(cardType == CARD_SD){ | ||||
|         Serial.println("SDSC"); | ||||
|     } else if(cardType == CARD_SDHC){ | ||||
|         Serial.println("SDHC"); | ||||
|     } else { | ||||
|         Serial.println("UNKNOWN"); | ||||
|     } | ||||
| 
 | ||||
|     uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024); | ||||
|     Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize); | ||||
| 
 | ||||
|     listDir(SD_MMC, "/", 0); | ||||
|     createDir(SD_MMC, "/mydir"); | ||||
|     listDir(SD_MMC, "/", 0); | ||||
|     removeDir(SD_MMC, "/mydir"); | ||||
|     listDir(SD_MMC, "/", 2); | ||||
|     writeFile(SD_MMC, "/hello.txt", "Hello "); | ||||
|     appendFile(SD_MMC, "/hello.txt", "World!\n"); | ||||
|     readFile(SD_MMC, "/hello.txt"); | ||||
|     deleteFile(SD_MMC, "/foo.txt"); | ||||
|     renameFile(SD_MMC, "/hello.txt", "/foo.txt"); | ||||
|     readFile(SD_MMC, "/foo.txt"); | ||||
|     testFileIO(SD_MMC, "/test.txt"); | ||||
| } | ||||
| 
 | ||||
| void loop(){ | ||||
|      | ||||
| } | ||||
| 
 | ||||
| void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ | ||||
|     Serial.printf("Listing directory: %s\n", dirname); | ||||
| 
 | ||||
| @ -214,3 +169,48 @@ void testFileIO(fs::FS &fs, const char * path){ | ||||
|     Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); | ||||
|     file.close(); | ||||
| } | ||||
| 
 | ||||
| void setup(){ | ||||
|     Serial.begin(115200); | ||||
|     if(!SD_MMC.begin()){ | ||||
|         Serial.println("Card Mount Failed"); | ||||
|         return; | ||||
|     } | ||||
|     uint8_t cardType = SD_MMC.cardType(); | ||||
| 
 | ||||
|     if(cardType == CARD_NONE){ | ||||
|         Serial.println("No SD_MMC card attached"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     Serial.print("SD_MMC Card Type: "); | ||||
|     if(cardType == CARD_MMC){ | ||||
|         Serial.println("MMC"); | ||||
|     } else if(cardType == CARD_SD){ | ||||
|         Serial.println("SDSC"); | ||||
|     } else if(cardType == CARD_SDHC){ | ||||
|         Serial.println("SDHC"); | ||||
|     } else { | ||||
|         Serial.println("UNKNOWN"); | ||||
|     } | ||||
| 
 | ||||
|     uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024); | ||||
|     Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize); | ||||
| 
 | ||||
|     listDir(SD_MMC, "/", 0); | ||||
|     createDir(SD_MMC, "/mydir"); | ||||
|     listDir(SD_MMC, "/", 0); | ||||
|     removeDir(SD_MMC, "/mydir"); | ||||
|     listDir(SD_MMC, "/", 2); | ||||
|     writeFile(SD_MMC, "/hello.txt", "Hello "); | ||||
|     appendFile(SD_MMC, "/hello.txt", "World!\n"); | ||||
|     readFile(SD_MMC, "/hello.txt"); | ||||
|     deleteFile(SD_MMC, "/foo.txt"); | ||||
|     renameFile(SD_MMC, "/hello.txt", "/foo.txt"); | ||||
|     readFile(SD_MMC, "/foo.txt"); | ||||
|     testFileIO(SD_MMC, "/test.txt"); | ||||
| } | ||||
| 
 | ||||
| void loop(){ | ||||
| 
 | ||||
| } | ||||
|  | ||||
							
								
								
									
										24
									
								
								libraries/Update/keywords.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								libraries/Update/keywords.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| ####################################### | ||||
| # Syntax Coloring Map For Ultrasound | ||||
| ####################################### | ||||
| 
 | ||||
| ####################################### | ||||
| # Datatypes (KEYWORD1) | ||||
| ####################################### | ||||
| 
 | ||||
| Update	KEYWORD1 | ||||
| 
 | ||||
| ####################################### | ||||
| # Methods and Functions (KEYWORD2) | ||||
| ####################################### | ||||
| 
 | ||||
| begin	KEYWORD2 | ||||
| end KEYWORD2 | ||||
| write	KEYWORD2 | ||||
| writeStream	KEYWORD2 | ||||
| printError   KEYWORD2 | ||||
| 
 | ||||
| ####################################### | ||||
| # Constants (LITERAL1) | ||||
| ####################################### | ||||
| 
 | ||||
							
								
								
									
										9
									
								
								libraries/Update/library.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								libraries/Update/library.properties
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| name=Update | ||||
| version=1.0 | ||||
| author=Hristo Gochkov | ||||
| maintainer=Hristo Gochkov <hristo@espressif.com> | ||||
| sentence=ESP32 Sketch Update Library | ||||
| paragraph= | ||||
| category=Other | ||||
| url= | ||||
| architectures=esp32 | ||||
							
								
								
									
										161
									
								
								libraries/Update/src/Update.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								libraries/Update/src/Update.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,161 @@ | ||||
| #ifndef ESP8266UPDATER_H | ||||
| #define ESP8266UPDATER_H | ||||
| 
 | ||||
| #include <Arduino.h> | ||||
| #include <MD5Builder.h> | ||||
| #include "esp_partition.h" | ||||
| 
 | ||||
| #define UPDATE_ERROR_OK                 (0) | ||||
| #define UPDATE_ERROR_WRITE              (1) | ||||
| #define UPDATE_ERROR_ERASE              (2) | ||||
| #define UPDATE_ERROR_READ               (3) | ||||
| #define UPDATE_ERROR_SPACE              (4) | ||||
| #define UPDATE_ERROR_SIZE               (5) | ||||
| #define UPDATE_ERROR_STREAM             (6) | ||||
| #define UPDATE_ERROR_MD5                (7) | ||||
| #define UPDATE_ERROR_MAGIC_BYTE         (8) | ||||
| #define UPDATE_ERROR_ACTIVATE           (9) | ||||
| #define UPDATE_ERROR_NO_PARTITION       (10) | ||||
| #define UPDATE_ERROR_BAD_ARGUMENT       (11) | ||||
| #define UPDATE_ERROR_ABORT              (12) | ||||
| 
 | ||||
| #define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF | ||||
| 
 | ||||
| #define U_FLASH   0 | ||||
| #define U_SPIFFS  100 | ||||
| #define U_AUTH    200 | ||||
| 
 | ||||
| class UpdateClass { | ||||
|   public: | ||||
|     UpdateClass(); | ||||
|     /*
 | ||||
|       Call this to check the space needed for the update | ||||
|       Will return false if there is not enough space | ||||
|     */ | ||||
|     bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH); | ||||
| 
 | ||||
|     /*
 | ||||
|       Writes a buffer to the flash and increments the address | ||||
|       Returns the amount written | ||||
|     */ | ||||
|     size_t write(uint8_t *data, size_t len); | ||||
| 
 | ||||
|     /*
 | ||||
|       Writes the remaining bytes from the Stream to the flash | ||||
|       Uses readBytes() and sets UPDATE_ERROR_STREAM on timeout | ||||
|       Returns the bytes written | ||||
|       Should be equal to the remaining bytes when called | ||||
|       Usable for slow streams like Serial | ||||
|     */ | ||||
|     size_t writeStream(Stream &data); | ||||
| 
 | ||||
|     /*
 | ||||
|       If all bytes are written | ||||
|       this call will write the config to eboot | ||||
|       and return true | ||||
|       If there is already an update running but is not finished and !evenIfRemainanig | ||||
|       or there is an error | ||||
|       this will clear everything and return false | ||||
|       the last error is available through getError() | ||||
|       evenIfRemaining is helpfull when you update without knowing the final size first | ||||
|     */ | ||||
|     bool end(bool evenIfRemaining = false); | ||||
| 
 | ||||
|     /*
 | ||||
|       Aborts the running update | ||||
|     */ | ||||
|     void abort(); | ||||
| 
 | ||||
|     /*
 | ||||
|       Prints the last error to an output stream | ||||
|     */ | ||||
|     void printError(Stream &out); | ||||
| 
 | ||||
|     /*
 | ||||
|       sets the expected MD5 for the firmware (hexString) | ||||
|     */ | ||||
|     bool 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
 | ||||
|     uint8_t getError(){ return _error; } | ||||
|     void clearError(){ _error = UPDATE_ERROR_OK; } | ||||
|     bool hasError(){ return _error != UPDATE_ERROR_OK; } | ||||
|     bool isRunning(){ return _size > 0; } | ||||
|     bool isFinished(){ return _progress == _size; } | ||||
|     size_t size(){ return _size; } | ||||
|     size_t progress(){ return _progress; } | ||||
|     size_t remaining(){ return _size - _progress; } | ||||
| 
 | ||||
|     /*
 | ||||
|       Template to write from objects that expose | ||||
|       available() and read(uint8_t*, size_t) methods | ||||
|       faster than the writeStream method | ||||
|       writes only what is available | ||||
|     */ | ||||
|     template<typename T> | ||||
|     size_t write(T &data){ | ||||
|       size_t written = 0; | ||||
|       if (hasError() || !isRunning()) | ||||
|         return 0; | ||||
| 
 | ||||
|       size_t available = data.available(); | ||||
|       while(available) { | ||||
|         if(_bufferLen + available > remaining()){ | ||||
|           available = remaining() - _bufferLen; | ||||
|         } | ||||
|         if(_bufferLen + available > 4096) { | ||||
|           size_t toBuff = 4096 - _bufferLen; | ||||
|           data.read(_buffer + _bufferLen, toBuff); | ||||
|           _bufferLen += toBuff; | ||||
|           if(!_writeBuffer()) | ||||
|             return written; | ||||
|           written += toBuff; | ||||
|         } else { | ||||
|           data.read(_buffer + _bufferLen, available); | ||||
|           _bufferLen += available; | ||||
|           written += available; | ||||
|           if(_bufferLen == remaining()) { | ||||
|             if(!_writeBuffer()) { | ||||
|               return written; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         if(remaining() == 0) | ||||
|           return written; | ||||
|         available = data.available(); | ||||
|       } | ||||
|       return written; | ||||
|     } | ||||
| 
 | ||||
|   private: | ||||
|     void _reset(); | ||||
|     void _abort(uint8_t err); | ||||
|     bool _writeBuffer(); | ||||
|     bool _verifyHeader(uint8_t data); | ||||
|     bool _verifyEnd(); | ||||
| 
 | ||||
|     uint8_t _error; | ||||
|     uint8_t *_buffer; | ||||
|     size_t _bufferLen; | ||||
|     size_t _size; | ||||
|     uint32_t _progress; | ||||
|     uint32_t _command; | ||||
|     const esp_partition_t* _partition; | ||||
| 
 | ||||
|     String _target_md5; | ||||
|     MD5Builder _md5; | ||||
| }; | ||||
| 
 | ||||
| extern UpdateClass Update; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										278
									
								
								libraries/Update/src/Updater.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								libraries/Update/src/Updater.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,278 @@ | ||||
| #include "Update.h" | ||||
| #include "Arduino.h" | ||||
| #include "esp_spi_flash.h" | ||||
| #include "esp_ota_ops.h" | ||||
| #include "esp_image_format.h" | ||||
| 
 | ||||
| static const char * _err2str(uint8_t _error){ | ||||
|     if(_error == UPDATE_ERROR_OK){ | ||||
|         return ("No Error"); | ||||
|     } else if(_error == UPDATE_ERROR_WRITE){ | ||||
|         return ("Flash Write Failed"); | ||||
|     } else if(_error == UPDATE_ERROR_ERASE){ | ||||
|         return ("Flash Erase Failed"); | ||||
|     } else if(_error == UPDATE_ERROR_READ){ | ||||
|         return ("Flash Read Failed"); | ||||
|     } else if(_error == UPDATE_ERROR_SPACE){ | ||||
|         return ("Not Enough Space"); | ||||
|     } else if(_error == UPDATE_ERROR_SIZE){ | ||||
|         return ("Bad Size Given"); | ||||
|     } else if(_error == UPDATE_ERROR_STREAM){ | ||||
|         return ("Stream Read Timeout"); | ||||
|     } else if(_error == UPDATE_ERROR_MD5){ | ||||
|         return ("MD5 Check Failed"); | ||||
|     } else if(_error == UPDATE_ERROR_MAGIC_BYTE){ | ||||
|         return ("Wrong Magic Byte"); | ||||
|     } else if(_error == UPDATE_ERROR_ACTIVATE){ | ||||
|         return ("Could Not Activate The Firmware"); | ||||
|     } else if(_error == UPDATE_ERROR_NO_PARTITION){ | ||||
|         return ("Partition Could Not be Found"); | ||||
|     } else if(_error == UPDATE_ERROR_BAD_ARGUMENT){ | ||||
|         return ("Bad Argument"); | ||||
|     } else if(_error == UPDATE_ERROR_ABORT){ | ||||
|         return ("Aborted"); | ||||
|     } | ||||
|     return ("UNKNOWN"); | ||||
| } | ||||
| 
 | ||||
| UpdateClass::UpdateClass() | ||||
| : _error(0) | ||||
| , _buffer(0) | ||||
| , _bufferLen(0) | ||||
| , _size(0) | ||||
| , _progress(0) | ||||
| , _command(U_FLASH) | ||||
| , _partition(NULL) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void UpdateClass::_reset() { | ||||
|     if (_buffer) | ||||
|         delete[] _buffer; | ||||
|     _buffer = 0; | ||||
|     _bufferLen = 0; | ||||
|     _progress = 0; | ||||
|     _size = 0; | ||||
|     _command = U_FLASH; | ||||
| } | ||||
| 
 | ||||
| bool UpdateClass::begin(size_t size, int command) { | ||||
|     if(_size > 0){ | ||||
|         log_w("already running"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     _reset(); | ||||
|     _error = 0; | ||||
| 
 | ||||
|     if(size == 0) { | ||||
|         _error = UPDATE_ERROR_SIZE; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (command == U_FLASH) { | ||||
|         _partition = esp_ota_get_next_update_partition(NULL); | ||||
|         if(!_partition){ | ||||
|             _error = UPDATE_ERROR_NO_PARTITION; | ||||
|             return false; | ||||
|         } | ||||
|         log_d("OTA Partition: %s", _partition->label); | ||||
|     } | ||||
|     else if (command == U_SPIFFS) { | ||||
|         _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); | ||||
|         if(!_partition){ | ||||
|             _error = UPDATE_ERROR_NO_PARTITION; | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     else { | ||||
|         _error = UPDATE_ERROR_BAD_ARGUMENT; | ||||
|         log_e("bad command %u", command); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if(size == UPDATE_SIZE_UNKNOWN){ | ||||
|         size = _partition->size; | ||||
|     } else if(size > _partition->size){ | ||||
|         _error = UPDATE_ERROR_SIZE; | ||||
|         log_e("too large %u > %u", size, _partition->size); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     //initialize
 | ||||
|     _buffer = (uint8_t*)malloc(SPI_FLASH_SEC_SIZE); | ||||
|     if(!_buffer){ | ||||
|         log_e("malloc failed"); | ||||
|         return false; | ||||
|     } | ||||
|     _size = size; | ||||
|     _command = command; | ||||
|     _md5.begin(); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void UpdateClass::_abort(uint8_t err){ | ||||
|     _reset(); | ||||
|     _error = err; | ||||
| } | ||||
| 
 | ||||
| void UpdateClass::abort(){ | ||||
|     _abort(UPDATE_ERROR_ABORT); | ||||
| } | ||||
| 
 | ||||
| bool UpdateClass::_writeBuffer(){ | ||||
|     if(!ESP.flashEraseSector((_partition->address + _progress)/SPI_FLASH_SEC_SIZE)){ | ||||
|         _abort(UPDATE_ERROR_ERASE); | ||||
|         return false; | ||||
|     } | ||||
|     if (!ESP.flashWrite(_partition->address + _progress, (uint32_t*)_buffer, _bufferLen)) { | ||||
|         _abort(UPDATE_ERROR_WRITE); | ||||
|         return false; | ||||
|     } | ||||
|     _md5.add(_buffer, _bufferLen); | ||||
|     _progress += _bufferLen; | ||||
|     _bufferLen = 0; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool UpdateClass::_verifyHeader(uint8_t data) { | ||||
|     if(_command == U_FLASH) { | ||||
|         if(data != ESP_IMAGE_HEADER_MAGIC) { | ||||
|             _abort(UPDATE_ERROR_MAGIC_BYTE); | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } else if(_command == U_SPIFFS) { | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool UpdateClass::_verifyEnd() { | ||||
|     if(_command == U_FLASH) { | ||||
|         uint8_t buf[4]; | ||||
|         if(!ESP.flashRead(_partition->address, (uint32_t*)buf, 4)) { | ||||
|             _abort(UPDATE_ERROR_READ); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if(buf[0] != ESP_IMAGE_HEADER_MAGIC) { | ||||
|             _abort(UPDATE_ERROR_MAGIC_BYTE); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if(esp_ota_set_boot_partition(_partition)){ | ||||
|             _abort(UPDATE_ERROR_ACTIVATE); | ||||
|             return false; | ||||
|         } | ||||
|         _reset(); | ||||
|         return true; | ||||
|     } else if(_command == U_SPIFFS) { | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool UpdateClass::setMD5(const char * expected_md5){ | ||||
|     if(strlen(expected_md5) != 32) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     _target_md5 = expected_md5; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool UpdateClass::end(bool evenIfRemaining){ | ||||
|     if(hasError() || _size == 0){ | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if(!isFinished() && !evenIfRemaining){ | ||||
|         log_e("premature end: res:%u, pos:%u/%u\n", getError(), progress(), _size); | ||||
|         _abort(UPDATE_ERROR_ABORT); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if(evenIfRemaining) { | ||||
|         if(_bufferLen > 0) { | ||||
|             _writeBuffer(); | ||||
|         } | ||||
|         _size = progress(); | ||||
|     } | ||||
| 
 | ||||
|     _md5.calculate(); | ||||
|     if(_target_md5.length()) { | ||||
|         if(_target_md5 != _md5.toString()){ | ||||
|             _abort(UPDATE_ERROR_MD5); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return _verifyEnd(); | ||||
| } | ||||
| 
 | ||||
| size_t UpdateClass::write(uint8_t *data, size_t len) { | ||||
|     if(hasError() || !isRunning()){ | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     if(len > remaining()){ | ||||
|         _abort(UPDATE_ERROR_SPACE); | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     size_t left = len; | ||||
| 
 | ||||
|     while((_bufferLen + left) > SPI_FLASH_SEC_SIZE) { | ||||
|         size_t toBuff = SPI_FLASH_SEC_SIZE - _bufferLen; | ||||
|         memcpy(_buffer + _bufferLen, data + (len - left), toBuff); | ||||
|         _bufferLen += toBuff; | ||||
|         if(!_writeBuffer()){ | ||||
|             return len - left; | ||||
|         } | ||||
|         left -= toBuff; | ||||
|     } | ||||
|     memcpy(_buffer + _bufferLen, data + (len - left), left); | ||||
|     _bufferLen += left; | ||||
|     if(_bufferLen == remaining()){ | ||||
|         if(!_writeBuffer()){ | ||||
|             return len - left; | ||||
|         } | ||||
|     } | ||||
|     return len; | ||||
| } | ||||
| 
 | ||||
| size_t UpdateClass::writeStream(Stream &data) { | ||||
|     size_t written = 0; | ||||
|     size_t toRead = 0; | ||||
|     if(hasError() || !isRunning()) | ||||
|         return 0; | ||||
| 
 | ||||
|     if(!_verifyHeader(data.peek())) { | ||||
|         _reset(); | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     while(remaining()) { | ||||
|         toRead = data.readBytes(_buffer + _bufferLen,  (SPI_FLASH_SEC_SIZE - _bufferLen)); | ||||
|         if(toRead == 0) { //Timeout
 | ||||
|             delay(100); | ||||
|             toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen)); | ||||
|             if(toRead == 0) { //Timeout
 | ||||
|                 _abort(UPDATE_ERROR_STREAM); | ||||
|                 return written; | ||||
|             } | ||||
|         } | ||||
|         _bufferLen += toRead; | ||||
|         if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer()) | ||||
|             return written; | ||||
|         written += toRead; | ||||
|     } | ||||
|     return written; | ||||
| } | ||||
| 
 | ||||
| void UpdateClass::printError(Stream &out){ | ||||
|     out.println(_err2str(_error)); | ||||
| } | ||||
| 
 | ||||
| UpdateClass Update; | ||||
| @ -6,6 +6,9 @@ runtime.tools.xtensa-esp32-elf-gcc.path={runtime.platform.path}/tools/xtensa-esp | ||||
| tools.esptool.cmd="{runtime.platform.path}/tools/esptool" | ||||
| tools.esptool.cmd.windows="{runtime.platform.path}/tools/esptool.exe" | ||||
| 
 | ||||
| tools.esptool.network_cmd=python "{runtime.platform.path}/tools/espota.py" | ||||
| tools.esptool.network_cmd.windows="{runtime.platform.path}/tools/espota.exe" | ||||
| 
 | ||||
| tools.gen_esp32part.cmd=python "{runtime.platform.path}/tools/gen_esp32part.py" | ||||
| tools.gen_esp32part.cmd.windows="{runtime.platform.path}/tools/gen_esp32part.exe" | ||||
| 
 | ||||
| @ -17,7 +20,7 @@ compiler.warning_flags.all=-Wall -Werror=all -Wextra | ||||
| 
 | ||||
| compiler.path={runtime.tools.xtensa-esp32-elf-gcc.path}/bin/ | ||||
| compiler.sdk.path={runtime.platform.path}/tools/sdk | ||||
| compiler.cpreprocessor.flags=-DESP_PLATFORM -DMBEDTLS_CONFIG_FILE="mbedtls/esp_config.h" -DHAVE_CONFIG_H "-I{compiler.sdk.path}/include/config" "-I{compiler.sdk.path}/include/bluedroid" "-I{compiler.sdk.path}/include/bt" "-I{compiler.sdk.path}/include/driver" "-I{compiler.sdk.path}/include/esp32" "-I{compiler.sdk.path}/include/ethernet" "-I{compiler.sdk.path}/include/fatfs" "-I{compiler.sdk.path}/include/freertos" "-I{compiler.sdk.path}/include/log" "-I{compiler.sdk.path}/include/mdns" "-I{compiler.sdk.path}/include/mbedtls" "-I{compiler.sdk.path}/include/mbedtls_port" "-I{compiler.sdk.path}/include/vfs" "-I{compiler.sdk.path}/include/ulp" "-I{compiler.sdk.path}/include/newlib" "-I{compiler.sdk.path}/include/nvs_flash" "-I{compiler.sdk.path}/include/spi_flash" "-I{compiler.sdk.path}/include/sdmmc" "-I{compiler.sdk.path}/include/openssl" "-I{compiler.sdk.path}/include/app_update" "-I{compiler.sdk.path}/include/tcpip_adapter" "-I{compiler.sdk.path}/include/xtensa-debug-module" "-I{compiler.sdk.path}/include/newlib" "-I{compiler.sdk.path}/include/coap" "-I{compiler.sdk.path}/include/wpa_supplicant" "-I{compiler.sdk.path}/include/expat" "-I{compiler.sdk.path}/include/json" "-I{compiler.sdk.path}/include/nghttp" "-I{compiler.sdk.path}/include/lwip" | ||||
| compiler.cpreprocessor.flags=-DESP_PLATFORM -DMBEDTLS_CONFIG_FILE="mbedtls/esp_config.h" -DHAVE_CONFIG_H "-I{compiler.sdk.path}/include/config" "-I{compiler.sdk.path}/include/bluedroid" "-I{compiler.sdk.path}/include/bootloader_support" "-I{compiler.sdk.path}/include/bt" "-I{compiler.sdk.path}/include/driver" "-I{compiler.sdk.path}/include/esp32" "-I{compiler.sdk.path}/include/ethernet" "-I{compiler.sdk.path}/include/fatfs" "-I{compiler.sdk.path}/include/freertos" "-I{compiler.sdk.path}/include/log" "-I{compiler.sdk.path}/include/mdns" "-I{compiler.sdk.path}/include/mbedtls" "-I{compiler.sdk.path}/include/mbedtls_port" "-I{compiler.sdk.path}/include/vfs" "-I{compiler.sdk.path}/include/ulp" "-I{compiler.sdk.path}/include/newlib" "-I{compiler.sdk.path}/include/nvs_flash" "-I{compiler.sdk.path}/include/spi_flash" "-I{compiler.sdk.path}/include/sdmmc" "-I{compiler.sdk.path}/include/openssl" "-I{compiler.sdk.path}/include/app_update" "-I{compiler.sdk.path}/include/tcpip_adapter" "-I{compiler.sdk.path}/include/xtensa-debug-module" "-I{compiler.sdk.path}/include/newlib" "-I{compiler.sdk.path}/include/coap" "-I{compiler.sdk.path}/include/wpa_supplicant" "-I{compiler.sdk.path}/include/expat" "-I{compiler.sdk.path}/include/json" "-I{compiler.sdk.path}/include/nghttp" "-I{compiler.sdk.path}/include/lwip" | ||||
| 
 | ||||
| compiler.c.cmd=xtensa-esp32-elf-gcc | ||||
| compiler.c.flags=-std=gnu99 -Os -g3 -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -mlongcalls -nostdlib -Wpointer-arith {compiler.warning_flags} -Wno-error=unused-function -Wno-error=unused-but-set-variable -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-sign-compare -Wno-old-style-declaration -MMD -c | ||||
| @ -88,3 +91,4 @@ tools.esptool.upload.protocol=esp32 | ||||
| tools.esptool.upload.params.verbose= | ||||
| tools.esptool.upload.params.quiet= | ||||
| tools.esptool.upload.pattern={cmd} --chip esp32 --port "{serial.port}" --baud {upload.speed}  --before default_reset --after hard_reset write_flash -z --flash_freq {build.flash_freq} --flash_mode {build.flash_mode} --flash_size {build.flash_size} 0x1000 "{runtime.platform.path}/tools/sdk/bin/bootloader.bin" 0x8000 "{build.path}/{build.project_name}.partitions.bin" 0xe000 "{runtime.platform.path}/tools/partitions/boot_app0.bin" 0x10000 "{build.path}/{build.project_name}.bin" | ||||
| tools.esptool.upload.network_pattern={network_cmd} -i "{serial.port}" -p "{network.port}" "--auth={network.password}" -f "{build.path}/{build.project_name}.bin" | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								tools/espota.exe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tools/espota.exe
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										321
									
								
								tools/espota.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										321
									
								
								tools/espota.py
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,321 @@ | ||||
| #!/usr/bin/env python | ||||
| # | ||||
| # Original espota.py by Ivan Grokhotkov: | ||||
| # https://gist.github.com/igrr/d35ab8446922179dc58c | ||||
| # | ||||
| # 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) | ||||
| # Modified since 2016-01-03 from Matthew O'Gorman (https://githumb.com/mogorman) | ||||
| # | ||||
| # This script will push an OTA update to the ESP | ||||
| # use it like: python espota.py -i <ESP_IP_address> -I <Host_IP_address> -p <ESP_port> -P <Host_port> [-a password] -f <sketch.bin> | ||||
| # Or to upload SPIFFS image: | ||||
| # python espota.py -i <ESP_IP_address> -I <Host_IP_address> -p <ESP_port> -P <HOST_port> [-a password] -s -f <spiffs.bin> | ||||
| # | ||||
| # Changes | ||||
| # 2015-09-18: | ||||
| # - Add option parser. | ||||
| # - Add logging. | ||||
| # - Send command to controller to differ between flashing and transmitting SPIFFS image. | ||||
| # | ||||
| # Changes | ||||
| # 2015-11-09: | ||||
| # - Added digest authentication | ||||
| # - Enhanced error tracking and reporting | ||||
| # | ||||
| # Changes | ||||
| # 2016-01-03: | ||||
| # - Added more options to parser. | ||||
| # | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| import socket | ||||
| import sys | ||||
| import os | ||||
| import optparse | ||||
| import logging | ||||
| import hashlib | ||||
| import random | ||||
| 
 | ||||
| # Commands | ||||
| FLASH = 0 | ||||
| SPIFFS = 100 | ||||
| AUTH = 200 | ||||
| PROGRESS = False | ||||
| # update_progress() : Displays or updates a console progress bar | ||||
| ## Accepts a float between 0 and 1. Any int will be converted to a float. | ||||
| ## A value under 0 represents a 'halt'. | ||||
| ## A value at 1 or bigger represents 100% | ||||
| def update_progress(progress): | ||||
|   if (PROGRESS): | ||||
|     barLength = 60 # Modify this to change the length of the progress bar | ||||
|     status = "" | ||||
|     if isinstance(progress, int): | ||||
|       progress = float(progress) | ||||
|     if not isinstance(progress, float): | ||||
|       progress = 0 | ||||
|       status = "error: progress var must be float\r\n" | ||||
|     if progress < 0: | ||||
|       progress = 0 | ||||
|       status = "Halt...\r\n" | ||||
|     if progress >= 1: | ||||
|       progress = 1 | ||||
|       status = "Done...\r\n" | ||||
|     block = int(round(barLength*progress)) | ||||
|     text = "\rUploading: [{0}] {1}% {2}".format( "="*block + " "*(barLength-block), int(progress*100), status) | ||||
|     sys.stderr.write(text) | ||||
|     sys.stderr.flush() | ||||
|   else: | ||||
|     sys.stderr.write('.') | ||||
|     sys.stderr.flush() | ||||
| 
 | ||||
| def serve(remoteAddr, localAddr, remotePort, localPort, password, filename, command = FLASH): | ||||
|   # Create a TCP/IP socket | ||||
|   sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||||
|   server_address = (localAddr, localPort) | ||||
|   logging.info('Starting on %s:%s', str(server_address[0]), str(server_address[1])) | ||||
|   try: | ||||
|     sock.bind(server_address) | ||||
|     sock.listen(1) | ||||
|   except: | ||||
|     logging.error("Listen Failed") | ||||
|     return 1 | ||||
| 
 | ||||
|   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) | ||||
|   message = '%d %d %d %s\n' % (command, localPort, content_size, file_md5) | ||||
| 
 | ||||
|   # Wait for a connection | ||||
|   inv_trys = 0 | ||||
|   data = '' | ||||
|   msg = 'Sending invitation to %s ' % (remoteAddr) | ||||
|   sys.stderr.write(msg) | ||||
|   sys.stderr.flush() | ||||
|   while (inv_trys < 10): | ||||
|     inv_trys += 1 | ||||
|     sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||||
|     remote_address = (remoteAddr, int(remotePort)) | ||||
|     sent = sock2.sendto(message.encode(), remote_address) | ||||
|     sock2.settimeout(1) | ||||
|     try: | ||||
|       data = sock2.recv(37).decode() | ||||
|       break; | ||||
|     except: | ||||
|       sys.stderr.write('.') | ||||
|       sys.stderr.flush() | ||||
|       sock2.close() | ||||
|   sys.stderr.write('\n') | ||||
|   sys.stderr.flush() | ||||
|   if (inv_trys == 10): | ||||
|     logging.error('No response from the ESP') | ||||
|     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.encode()).hexdigest() | ||||
|       passmd5 = hashlib.md5(password.encode()).hexdigest() | ||||
|       result_text = '%s:%s:%s' % (passmd5 ,nonce, cnonce) | ||||
|       result = hashlib.md5(result_text.encode()).hexdigest() | ||||
|       sys.stderr.write('Authenticating...') | ||||
|       sys.stderr.flush() | ||||
|       message = '%d %s %s\n' % (AUTH, cnonce, result) | ||||
|       sock2.sendto(message.encode(), remote_address) | ||||
|       sock2.settimeout(10) | ||||
|       try: | ||||
|         data = sock2.recv(32).decode() | ||||
|       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() | ||||
| 
 | ||||
|   logging.info('Waiting for device...') | ||||
|   try: | ||||
|     sock.settimeout(10) | ||||
|     connection, client_address = sock.accept() | ||||
|     sock.settimeout(None) | ||||
|     connection.settimeout(None) | ||||
|   except: | ||||
|     logging.error('No response from device') | ||||
|     sock.close() | ||||
|     return 1 | ||||
| 
 | ||||
|   try: | ||||
|     f = open(filename, "rb") | ||||
|     if (PROGRESS): | ||||
|       update_progress(0) | ||||
|     else: | ||||
|       sys.stderr.write('Uploading') | ||||
|       sys.stderr.flush() | ||||
|     offset = 0 | ||||
|     while True: | ||||
|       chunk = f.read(1460) | ||||
|       if not chunk: break | ||||
|       offset += len(chunk) | ||||
|       update_progress(offset/float(content_size)) | ||||
|       connection.settimeout(10) | ||||
|       try: | ||||
|         connection.sendall(chunk) | ||||
|         res = connection.recv(5) | ||||
|       except: | ||||
|         sys.stderr.write('\n') | ||||
|         logging.error('Error Uploading') | ||||
|         connection.close() | ||||
|         f.close() | ||||
|         sock.close() | ||||
|         return 1 | ||||
| 
 | ||||
|     sys.stderr.write('\n') | ||||
|     logging.info('Waiting for result...') | ||||
|     try: | ||||
|       connection.settimeout(60) | ||||
|       data = connection.recv(32).decode() | ||||
|       logging.info('Result: %s' ,data) | ||||
|       connection.close() | ||||
|       f.close() | ||||
|       sock.close() | ||||
|       if (data != "OK"): | ||||
|         sys.stderr.write('\n') | ||||
|         logging.error('%s', data) | ||||
|         return 1; | ||||
|       return 0 | ||||
|     except: | ||||
|       logging.error('No Result!') | ||||
|       connection.close() | ||||
|       f.close() | ||||
|       sock.close() | ||||
|       return 1 | ||||
| 
 | ||||
|   finally: | ||||
|     connection.close() | ||||
|     f.close() | ||||
| 
 | ||||
|   sock.close() | ||||
|   return 1 | ||||
| # end serve | ||||
| 
 | ||||
| 
 | ||||
| def parser(unparsed_args): | ||||
|   parser = optparse.OptionParser( | ||||
|     usage = "%prog [options]", | ||||
|     description = "Transmit image over the air to the esp8266 module with OTA support." | ||||
|   ) | ||||
| 
 | ||||
|   # destination ip and port | ||||
|   group = optparse.OptionGroup(parser, "Destination") | ||||
|   group.add_option("-i", "--ip", | ||||
|     dest = "esp_ip", | ||||
|     action = "store", | ||||
|     help = "ESP8266 IP Address.", | ||||
|     default = False | ||||
|   ) | ||||
|   group.add_option("-I", "--host_ip", | ||||
|     dest = "host_ip", | ||||
|     action = "store", | ||||
|     help = "Host IP Address.", | ||||
|     default = "0.0.0.0" | ||||
|   ) | ||||
|   group.add_option("-p", "--port", | ||||
|     dest = "esp_port", | ||||
|     type = "int", | ||||
|     help = "ESP8266 ota Port. Default 8266", | ||||
|     default = 8266 | ||||
|   ) | ||||
|   group.add_option("-P", "--host_port", | ||||
|     dest = "host_port", | ||||
|     type = "int", | ||||
|     help = "Host server ota Port. Default random 10000-60000", | ||||
|     default = random.randint(10000,60000) | ||||
|   ) | ||||
|   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 | ||||
|   group = optparse.OptionGroup(parser, "Image") | ||||
|   group.add_option("-f", "--file", | ||||
|     dest = "image", | ||||
|     help = "Image file.", | ||||
|     metavar="FILE", | ||||
|     default = None | ||||
|   ) | ||||
|   group.add_option("-s", "--spiffs", | ||||
|     dest = "spiffs", | ||||
|     action = "store_true", | ||||
|     help = "Use this option to transmit a SPIFFS image and do not flash the module.", | ||||
|     default = False | ||||
|   ) | ||||
|   parser.add_option_group(group) | ||||
| 
 | ||||
|   # output group | ||||
|   group = optparse.OptionGroup(parser, "Output") | ||||
|   group.add_option("-d", "--debug", | ||||
|     dest = "debug", | ||||
|     help = "Show debug output. And override loglevel with debug.", | ||||
|     action = "store_true", | ||||
|     default = False | ||||
|   ) | ||||
|   group.add_option("-r", "--progress", | ||||
|     dest = "progress", | ||||
|     help = "Show progress output. Does not work for ArduinoIDE", | ||||
|     action = "store_true", | ||||
|     default = False | ||||
|   ) | ||||
|   parser.add_option_group(group) | ||||
| 
 | ||||
|   (options, args) = parser.parse_args(unparsed_args) | ||||
| 
 | ||||
|   return options | ||||
| # end parser | ||||
| 
 | ||||
| 
 | ||||
| def main(args): | ||||
|   options = parser(args) | ||||
|   loglevel = logging.WARNING | ||||
|   if (options.debug): | ||||
|     loglevel = logging.DEBUG | ||||
| 
 | ||||
|   logging.basicConfig(level = loglevel, format = '%(asctime)-8s [%(levelname)s]: %(message)s', datefmt = '%H:%M:%S') | ||||
|   logging.debug("Options: %s", str(options)) | ||||
| 
 | ||||
|   # check options | ||||
|   global PROGRESS | ||||
|   PROGRESS = options.progress | ||||
|   if (not options.esp_ip or not options.image): | ||||
|     logging.critical("Not enough arguments.") | ||||
|     return 1 | ||||
| 
 | ||||
|   command = FLASH | ||||
|   if (options.spiffs): | ||||
|     command = SPIFFS | ||||
| 
 | ||||
|   return serve(options.esp_ip, options.host_ip, options.esp_port, options.host_port, options.auth, options.image, command) | ||||
| # end main | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|   sys.exit(main(sys.argv)) | ||||
										
											Binary file not shown.
										
									
								
							| @ -51,6 +51,7 @@ env.Prepend( | ||||
|     CPPPATH=[ | ||||
|         join(FRAMEWORK_DIR, "tools", "sdk", "include", "config"), | ||||
|         join(FRAMEWORK_DIR, "tools", "sdk", "include", "bluedroid"), | ||||
|         join(FRAMEWORK_DIR, "tools", "sdk", "include", "bootloader_support"), | ||||
|         join(FRAMEWORK_DIR, "tools", "sdk", "include", "bt"), | ||||
|         join(FRAMEWORK_DIR, "tools", "sdk", "include", "driver"), | ||||
|         join(FRAMEWORK_DIR, "tools", "sdk", "include", "esp32"), | ||||
|  | ||||
							
								
								
									
										56
									
								
								tools/sdk/include/bootloader_support/esp_efuse.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								tools/sdk/include/bootloader_support/esp_efuse.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| 
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| #ifndef _ESP_EFUSE_H | ||||
| #define _ESP_EFUSE_H | ||||
| 
 | ||||
| #include "soc/efuse_reg.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /* @brief Permanently update values written to the efuse write registers
 | ||||
|  * | ||||
|  * After updating EFUSE_BLKx_WDATAx_REG registers with new values to | ||||
|  * write, call this function to permanently write them to efuse. | ||||
|  * | ||||
|  * @note Setting bits in efuse is permanent, they cannot be unset. | ||||
|  * | ||||
|  * @note Due to this restriction you don't need to copy values to | ||||
|  * Efuse write registers from the matching read registers, bits which | ||||
|  * are set in the read register but unset in the matching write | ||||
|  * register will be unchanged when new values are burned. | ||||
|  * | ||||
|  * @note This function is not threadsafe, if calling code updates | ||||
|  * efuse values from multiple tasks then this is caller's | ||||
|  * responsibility to serialise. | ||||
|  * | ||||
|  * After burning new efuses, the read registers are updated to match | ||||
|  * the new efuse values. | ||||
|  */ | ||||
| void esp_efuse_burn_new_values(void); | ||||
| 
 | ||||
| /* @brief Reset efuse write registers
 | ||||
|  * | ||||
|  * Efuse write registers are written to zero, to negate | ||||
|  * any changes that have been staged here. | ||||
|  */ | ||||
| void esp_efuse_reset(void); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif /* __ESP_EFUSE_H */ | ||||
| 
 | ||||
							
								
								
									
										102
									
								
								tools/sdk/include/bootloader_support/esp_flash_encrypt.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								tools/sdk/include/bootloader_support/esp_flash_encrypt.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | ||||
| // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| 
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| #ifndef __ESP32_FLASH_ENCRYPT_H | ||||
| #define __ESP32_FLASH_ENCRYPT_H | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include "esp_attr.h" | ||||
| #include "esp_err.h" | ||||
| #include "esp_spi_flash.h" | ||||
| #include "soc/efuse_reg.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * @file esp_partition.h | ||||
|  * @brief Support functions for flash encryption features | ||||
|  * | ||||
|  * Can be compiled as part of app or bootloader code. | ||||
|  */ | ||||
| 
 | ||||
| /** @brief Is flash encryption currently enabled in hardware?
 | ||||
|  * | ||||
|  * Flash encryption is enabled if the FLASH_CRYPT_CNT efuse has an odd number of bits set. | ||||
|  * | ||||
|  * @return true if flash encryption is enabled. | ||||
|  */ | ||||
| static inline /** @cond */ IRAM_ATTR /** @endcond */ bool esp_flash_encryption_enabled(void) { | ||||
|     uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_RD_FLASH_CRYPT_CNT); | ||||
|     /* __builtin_parity is in flash, so we calculate parity inline */ | ||||
|     bool enabled = false; | ||||
|     while(flash_crypt_cnt) { | ||||
|         if (flash_crypt_cnt & 1) { | ||||
|             enabled = !enabled; | ||||
|         } | ||||
|         flash_crypt_cnt >>= 1; | ||||
|     } | ||||
|     return enabled; | ||||
| } | ||||
| 
 | ||||
| /* @brief Update on-device flash encryption
 | ||||
|  * | ||||
|  * Intended to be called as part of the bootloader process if flash | ||||
|  * encryption is enabled in device menuconfig. | ||||
|  * | ||||
|  * If FLASH_CRYPT_CNT efuse parity is 1 (ie odd number of bits set), | ||||
|  * then return ESP_OK immediately (indicating flash encryption is enabled | ||||
|  * and functional). | ||||
|  * | ||||
|  * If FLASH_CRYPT_CNT efuse parity is 0 (ie even number of bits set), | ||||
|  * assume the flash has just been written with plaintext that needs encrypting. | ||||
|  * | ||||
|  * The following regions of flash are encrypted in place: | ||||
|  * | ||||
|  * - The bootloader image, if a valid plaintext image is found.[*] | ||||
|  * - The partition table, if a valid plaintext table is found. | ||||
|  * - Any app partition that contains a valid plaintext app image. | ||||
|  * - Any other partitions with the "encrypt" flag set. [**] | ||||
|  * | ||||
|  * After the re-encryption process completes, a '1' bit is added to the | ||||
|  * FLASH_CRYPT_CNT value (setting the parity to 1) and the EFUSE is re-burned. | ||||
|  * | ||||
|  * [*] If reflashing bootloader with secure boot enabled, pre-encrypt | ||||
|  * the bootloader before writing it to flash or secure boot will fail. | ||||
|  * | ||||
|  * [**] For this reason, if serial re-flashing a previous flashed | ||||
|  * device with secure boot enabled and using FLASH_CRYPT_CNT to | ||||
|  * trigger re-encryption, you must simultaneously re-flash plaintext | ||||
|  * content to all partitions with the "encrypt" flag set or this | ||||
|  * data will be corrupted (encrypted twice). | ||||
|  * | ||||
|  * @note The post-condition of this function is that all | ||||
|  * partitions that should be encrypted are encrypted. | ||||
|  * | ||||
|  * @note Take care not to power off the device while this function | ||||
|  * is running, or the partition currently being encrypted will be lost. | ||||
|  * | ||||
|  * @return ESP_OK if all operations succeeded, ESP_ERR_INVALID_STATE | ||||
|  * if a fatal error occured during encryption of all partitions. | ||||
|  */ | ||||
| esp_err_t esp_flash_encrypt_check_and_update(void); | ||||
| 
 | ||||
| 
 | ||||
| /** @brief Encrypt-in-place a block of flash sectors
 | ||||
|  * | ||||
|  * @param src_addr Source offset in flash. Should be multiple of 4096 bytes. | ||||
|  * @param data_length Length of data to encrypt in bytes. Will be rounded up to next multiple of 4096 bytes. | ||||
|  * | ||||
|  * @return ESP_OK if all operations succeeded, ESP_ERR_FLASH_OP_FAIL | ||||
|  * if SPI flash fails, ESP_ERR_FLASH_OP_TIMEOUT if flash times out. | ||||
|  */ | ||||
| esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										39
									
								
								tools/sdk/include/bootloader_support/esp_flash_partitions.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								tools/sdk/include/bootloader_support/esp_flash_partitions.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| 
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| #ifndef __ESP_FLASH_PARTITIONS_H | ||||
| #define __ESP_FLASH_PARTITIONS_H | ||||
| 
 | ||||
| #include "esp_err.h" | ||||
| #include "esp_flash_data_types.h" | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| /* Pre-partition table fixed flash offsets */ | ||||
| #define ESP_BOOTLOADER_DIGEST_OFFSET 0x0 | ||||
| #define ESP_BOOTLOADER_OFFSET 0x1000 /* Offset of bootloader image. Has matching value in bootloader KConfig.projbuild file. */ | ||||
| #define ESP_PARTITION_TABLE_OFFSET 0x8000 /* Offset of partition table. Has matching value in partition_table Kconfig.projbuild file. */ | ||||
| 
 | ||||
| #define ESP_PARTITION_TABLE_MAX_LEN 0xC00 /* Maximum length of partition table data */ | ||||
| #define ESP_PARTITION_TABLE_MAX_ENTRIES (ESP_PARTITION_TABLE_MAX_LEN / sizeof(esp_partition_info_t)) /* Maximum length of partition table data, including terminating entry */ | ||||
| 
 | ||||
| /* @brief Verify the partition table (does not include verifying secure boot cryptographic signature)
 | ||||
|  * | ||||
|  * @param partition_table Pointer to at least ESP_PARTITION_TABLE_MAX_ENTRIES of potential partition table data. (ESP_PARTITION_TABLE_MAX_LEN bytes.) | ||||
|  * @param log_errors Log errors if the partition table is invalid. | ||||
|  * @param num_partitions If result is ESP_OK, num_partitions is updated with total number of partitions (not including terminating entry). | ||||
|  * | ||||
|  * @return ESP_OK on success, ESP_ERR_INVALID_STATE if partition table is not valid. | ||||
|  */ | ||||
| esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										143
									
								
								tools/sdk/include/bootloader_support/esp_image_format.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								tools/sdk/include/bootloader_support/esp_image_format.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | ||||
| // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| 
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| #ifndef __ESP32_IMAGE_FORMAT_H | ||||
| #define __ESP32_IMAGE_FORMAT_H | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <esp_err.h> | ||||
| 
 | ||||
| #define ESP_ERR_IMAGE_BASE       0x2000 | ||||
| #define ESP_ERR_IMAGE_FLASH_FAIL (ESP_ERR_IMAGE_BASE + 1) | ||||
| #define ESP_ERR_IMAGE_INVALID    (ESP_ERR_IMAGE_BASE + 2) | ||||
| 
 | ||||
| /* Support for app/bootloader image parsing
 | ||||
|    Can be compiled as part of app or bootloader code. | ||||
| */ | ||||
| 
 | ||||
| /* SPI flash mode, used in esp_image_header_t */ | ||||
| typedef enum { | ||||
|     ESP_IMAGE_SPI_MODE_QIO, | ||||
|     ESP_IMAGE_SPI_MODE_QOUT, | ||||
|     ESP_IMAGE_SPI_MODE_DIO, | ||||
|     ESP_IMAGE_SPI_MODE_DOUT, | ||||
|     ESP_IMAGE_SPI_MODE_FAST_READ, | ||||
|     ESP_IMAGE_SPI_MODE_SLOW_READ | ||||
| } esp_image_spi_mode_t; | ||||
| 
 | ||||
| /* SPI flash clock frequency */ | ||||
| enum { | ||||
|     ESP_IMAGE_SPI_SPEED_40M, | ||||
|     ESP_IMAGE_SPI_SPEED_26M, | ||||
|     ESP_IMAGE_SPI_SPEED_20M, | ||||
|     ESP_IMAGE_SPI_SPEED_80M = 0xF | ||||
| } esp_image_spi_freq_t; | ||||
| 
 | ||||
| /* Supported SPI flash sizes */ | ||||
| typedef enum { | ||||
|     ESP_IMAGE_FLASH_SIZE_1MB = 0, | ||||
|     ESP_IMAGE_FLASH_SIZE_2MB, | ||||
|     ESP_IMAGE_FLASH_SIZE_4MB, | ||||
|     ESP_IMAGE_FLASH_SIZE_8MB, | ||||
|     ESP_IMAGE_FLASH_SIZE_16MB, | ||||
|     ESP_IMAGE_FLASH_SIZE_MAX | ||||
| } esp_image_flash_size_t; | ||||
| 
 | ||||
| #define ESP_IMAGE_HEADER_MAGIC 0xE9 | ||||
| 
 | ||||
| /* Main header of binary image */ | ||||
| typedef struct { | ||||
|     uint8_t magic; | ||||
|     uint8_t segment_count; | ||||
|     uint8_t spi_mode;      /* flash read mode (esp_image_spi_mode_t as uint8_t) */ | ||||
|     uint8_t spi_speed: 4;  /* flash frequency (esp_image_spi_freq_t as uint8_t) */ | ||||
|     uint8_t spi_size: 4;   /* flash chip size (esp_image_flash_size_t as uint8_t) */ | ||||
|     uint32_t entry_addr; | ||||
|     uint8_t encrypt_flag;    /* encrypt flag */ | ||||
|     uint8_t extra_header[15]; /* ESP32 additional header, unused by second bootloader */ | ||||
| }  esp_image_header_t; | ||||
| 
 | ||||
| /* Header of binary image segment */ | ||||
| typedef struct { | ||||
|     uint32_t load_addr; | ||||
|     uint32_t data_len; | ||||
| } esp_image_segment_header_t; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Read an ESP image header from flash. | ||||
|  * | ||||
|  * If encryption is enabled, data will be transparently decrypted. | ||||
|  * | ||||
|  * @param src_addr Address in flash to load image header. Must be 4 byte aligned. | ||||
|  * @param log_errors Log error output if image header appears invalid. | ||||
|  * @param[out] image_header Pointer to an esp_image_header_t struture to be filled with data. If the function fails, contents are undefined. | ||||
|  * | ||||
|  * @return ESP_OK if image header was loaded, ESP_ERR_IMAGE_FLASH_FAIL | ||||
|  * if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header | ||||
|  * appears invalid. | ||||
|  */ | ||||
| esp_err_t esp_image_load_header(uint32_t src_addr, bool log_errors, esp_image_header_t *image_header); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Read the segment header and data offset of a segment in the image. | ||||
|  * | ||||
|  * If encryption is enabled, data will be transparently decrypted. | ||||
|  * | ||||
|  * @param index Index of the segment to load information for. | ||||
|  * @param src_addr Base address in flash of the image. | ||||
|  * @param[in] image_header Pointer to the flash image header, already loaded by @ref esp_image_load_header(). | ||||
|  * @param log_errors Log errors reading the segment header. | ||||
|  * @param[out] segment_header Pointer to a segment header structure to be filled with data. If the function fails, contents are undefined. | ||||
|  * @param[out] segment_data_offset Pointer to the data offset of the segment. | ||||
|  * | ||||
|  * @return ESP_OK if segment_header & segment_data_offset were loaded successfully, ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header appears invalid, ESP_ERR_INVALID_ARG if the index is invalid. | ||||
|  */ | ||||
| esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, bool log_errors, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset); | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Non-cryptographically validate app image integrity. On success, length of image is provided to caller. | ||||
|  * | ||||
|  * If the image has a secure boot signature appended, the signature is not checked and this length is not included in the | ||||
|  * output value. | ||||
|  * | ||||
|  * Image validation checks: | ||||
|  * - Magic byte | ||||
|  * - No single segment longer than 16MB | ||||
|  * - Total image no longer than 16MB | ||||
|  * - 8 bit image checksum is valid | ||||
|  * | ||||
|  * If flash encryption is enabled, the image will be tranpsarently decrypted. | ||||
|  * | ||||
|  * @param src_addr Offset of the start of the image in flash. Must be 4 byte aligned. | ||||
|  * @param allow_decrypt If true and flash encryption is enabled, the image will be transparently decrypted. | ||||
|  * @param log_errors Log errors verifying the image. | ||||
|  * @param[out] length Length of the image, set to a value if the image is valid. Can be null. | ||||
|  * | ||||
|  * @return ESP_OK if image is valid, ESP_FAIL or ESP_ERR_IMAGE_INVALID on errors. | ||||
|  * | ||||
|  */ | ||||
| esp_err_t esp_image_basic_verify(uint32_t src_addr, bool log_errors, uint32_t *length); | ||||
| 
 | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t drom_addr; | ||||
|     uint32_t drom_load_addr; | ||||
|     uint32_t drom_size; | ||||
|     uint32_t irom_addr; | ||||
|     uint32_t irom_load_addr; | ||||
|     uint32_t irom_size; | ||||
| } esp_image_flash_mapping_t; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										91
									
								
								tools/sdk/include/bootloader_support/esp_secure_boot.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								tools/sdk/include/bootloader_support/esp_secure_boot.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | ||||
| // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| 
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| #ifndef __ESP32_SECUREBOOT_H | ||||
| #define __ESP32_SECUREBOOT_H | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <esp_err.h> | ||||
| #include "soc/efuse_reg.h" | ||||
| 
 | ||||
| /* Support functions for secure boot features.
 | ||||
| 
 | ||||
|    Can be compiled as part of app or bootloader code. | ||||
| */ | ||||
| 
 | ||||
| /** @brief Is secure boot currently enabled in hardware?
 | ||||
|  * | ||||
|  * Secure boot is enabled if the ABS_DONE_0 efuse is blown. This means | ||||
|  * that the ROM bootloader code will only boot a verified secure | ||||
|  * bootloader digest from now on. | ||||
|  * | ||||
|  * @return true if secure boot is enabled. | ||||
|  */ | ||||
| static inline bool esp_secure_boot_enabled(void) { | ||||
|     return REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /** @brief Enable secure boot if it is not already enabled.
 | ||||
|  * | ||||
|  * @important If this function succeeds, secure boot is permanently | ||||
|  * enabled on the chip via efuse. | ||||
|  * | ||||
|  * @important This function is intended to be called from bootloader code only. | ||||
|  * | ||||
|  * If secure boot is not yet enabled for bootloader, this will | ||||
|  * generate the secure boot digest and enable secure boot by blowing | ||||
|  * the EFUSE_RD_ABS_DONE_0 efuse. | ||||
|  * | ||||
|  * This function does not verify secure boot of the bootloader (the | ||||
|  * ROM bootloader does this.) | ||||
|  * | ||||
|  * Will fail if efuses have been part-burned in a way that indicates | ||||
|  * secure boot should not or could not be correctly enabled. | ||||
|  * | ||||
|  * | ||||
|  * @return ESP_ERR_INVALID_STATE if efuse state doesn't allow | ||||
|  * secure boot to be enabled cleanly. ESP_OK if secure boot | ||||
|  * is enabled on this chip from now on. | ||||
|  */ | ||||
| esp_err_t esp_secure_boot_permanently_enable(void); | ||||
| 
 | ||||
| /** @brief Verify the secure boot signature (determinstic ECDSA w/ SHA256) appended to some binary data in flash.
 | ||||
|  * | ||||
|  * Public key is compiled into the calling program. See docs/security/secure-boot.rst for details. | ||||
|  * | ||||
|  * @param src_addr Starting offset of the data in flash. | ||||
|  * @param length Length of data in bytes. Signature is appended -after- length bytes. | ||||
|  * | ||||
|  * If flash encryption is enabled, the image will be transparently decrypted while being verified. | ||||
|  * | ||||
|  * @return ESP_OK if signature is valid, ESP_ERR_INVALID_STATE if | ||||
|  * signature fails, ESP_FAIL for other failures (ie can't read flash). | ||||
|  */ | ||||
| esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length); | ||||
| 
 | ||||
| /** @brief Secure boot verification block, on-flash data format. */ | ||||
| typedef struct { | ||||
|     uint32_t version; | ||||
|     uint8_t signature[64]; | ||||
| } esp_secure_boot_sig_block_t; | ||||
| 
 | ||||
| #define FLASH_OFFS_SECURE_BOOT_IV_DIGEST 0 | ||||
| 
 | ||||
| /** @brief Secure boot IV+digest header */ | ||||
| typedef struct { | ||||
|     uint8_t iv[128]; | ||||
|     uint8_t digest[64]; | ||||
| } esp_secure_boot_iv_digest_t; | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user