diff --git a/src/HAP.cpp b/src/HAP.cpp index 19f998e..636d69c 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -40,13 +40,13 @@ void HAPClient::init(){ size_t len; // not used but required to read blobs from NVS -// if(strlen(homeSpan.spanOTA.otaPwd)==0){ // OTA password has not been specified in sketch -// if(!nvs_get_str(homeSpan.otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS... -// nvs_get_str(homeSpan.otaNVS,"OTADATA",homeSpan.spanOTA.otaPwd,&len); // ...retrieve data. -// } else { // otherwise... -// homeSpan.spanOTA.setPassword(DEFAULT_OTA_PASSWORD); // ...use default password -// } -// } + if(strlen(homeSpan.spanOTA.otaPwd)==0){ // OTA password has not been specified in sketch + if(!nvs_get_str(homeSpan.otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS... + nvs_get_str(homeSpan.otaNVS,"OTADATA",homeSpan.spanOTA.otaPwd,&len); // ...retrieve data. + } else { // otherwise... + homeSpan.spanOTA.setPassword(DEFAULT_OTA_PASSWORD); // ...use default password + } + } if(!strlen(homeSpan.qrID)){ // if Setup ID has not been specified in sketch if(!nvs_get_str(homeSpan.hapNVS,"SETUPID",NULL,&len)){ // check for saved value diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index ec90e43..5b6681c 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "HomeSpan.h" #include "HAP.h" @@ -155,11 +156,11 @@ void Span::begin(Category catID, const char *_displayName, const char *_hostName nvs_get_u8(otaNVS,"OTA_REQUIRED",&otaRequired); nvs_set_u8(otaNVS,"OTA_REQUIRED",0); nvs_commit(otaNVS); -// if(otaRequired && !spanOTA.enabled){ -// LOG0("\n\n*** OTA SAFE MODE ALERT: OTA REQUIRED BUT NOT ENABLED. ROLLING BACK TO PREVIOUS APPLICATION ***\n\n"); -// delay(100); -// esp_ota_mark_app_invalid_rollback_and_reboot(); -// } + if(otaRequired && !spanOTA.enabled){ + LOG0("\n\n*** OTA SAFE MODE ALERT: OTA REQUIRED BUT NOT ENABLED. ROLLING BACK TO PREVIOUS APPLICATION ***\n\n"); + delay(100); + esp_ota_mark_app_invalid_rollback_and_reboot(); + } } // begin @@ -268,8 +269,8 @@ void Span::pollTask() { HAPClient::checkNotifications(); HAPClient::checkTimedWrites(); -//D if(spanOTA.enabled) -//D ArduinoOTA.handle(); + if(spanOTA.enabled) + ArduinoOTA.handle(); if(controlButton && controlButton->primed()) STATUS_UPDATE(start(LED_ALERT),HS_ENTERING_CONFIG_MODE) @@ -476,20 +477,20 @@ void Span::checkConnect(){ mbedtls_base64_encode((uint8_t *)setupHash,9,&len,hashOutput,4); // Step 3: Encode the first 4 bytes of hashOutput in base64, which results in an 8-character, null-terminated, setupHash mdns_service_txt_item_set("_hap","_tcp","sh",setupHash); // Step 4: broadcast the resulting Setup Hash -// if(spanOTA.enabled){ -// ArduinoOTA.setHostname(hostName); -// -// if(spanOTA.auth) -// ArduinoOTA.setPasswordHash(spanOTA.otaPwd); -// -// ArduinoOTA.onStart(spanOTA.start).onEnd(spanOTA.end).onProgress(spanOTA.progress).onError(spanOTA.error); -// -// ArduinoOTA.begin(); -// LOG0("Starting OTA Server: %s at %s\n",displayName,WiFi.localIP().toString().c_str()); -// LOG0("Authorization Password: %s",spanOTA.auth?"Enabled\n\n":"DISABLED!\n\n"); -// } + if(spanOTA.enabled){ + ArduinoOTA.setHostname(hostName); + + if(spanOTA.auth) + ArduinoOTA.setPasswordHash(spanOTA.otaPwd); + + ArduinoOTA.onStart(spanOTA.start).onEnd(spanOTA.end).onProgress(spanOTA.progress).onError(spanOTA.error); + + ArduinoOTA.begin(); + LOG0("Starting OTA Server: %s at %s\n",displayName,WiFi.localIP().toString().c_str()); + LOG0("Authorization Password: %s",spanOTA.auth?"Enabled\n\n":"DISABLED!\n\n"); + } -//D mdns_service_txt_item_set("_hap","_tcp","ota",spanOTA.enabled?"yes":"no"); // OTA status (info only - NOT used by HAP) + mdns_service_txt_item_set("_hap","_tcp","ota",spanOTA.enabled?"yes":"no"); // OTA status (info only - NOT used by HAP) if(webLog.isEnabled){ mdns_service_txt_item_set("_hap","_tcp","logURL",webLog.statusURL.c_str()+4); // Web Log status (info only - NOT used by HAP) @@ -608,34 +609,34 @@ void Span::processSerialCommand(const char *c){ } break; -// case 'O': { -// -// char textPwd[34]="\0"; -// -// LOG0("\n>>> New OTA Password, or to cancel request: "); -// readSerial(textPwd,33); -// -// if(strlen(textPwd)==0){ -// LOG0("(cancelled)\n\n"); -// return; -// } -// -// if(strlen(textPwd)==33){ -// LOG0("\n*** Sorry, 32 character limit - request cancelled\n\n"); -// return; -// } -// -// LOG0("%s\n",mask(textPwd,2).c_str()); -// spanOTA.setPassword(textPwd); -// nvs_set_str(otaNVS,"OTADATA",spanOTA.otaPwd); // update data -// nvs_commit(otaNVS); -// -// LOG0("... Accepted! Password change will take effect after next restart.\n"); -// if(!spanOTA.enabled) -// LOG0("... Note: OTA has not been enabled in this sketch.\n"); -// LOG0("\n"); -// } -// break; + case 'O': { + + char textPwd[34]="\0"; + + LOG0("\n>>> New OTA Password, or to cancel request: "); + readSerial(textPwd,33); + + if(strlen(textPwd)==0){ + LOG0("(cancelled)\n\n"); + return; + } + + if(strlen(textPwd)==33){ + LOG0("\n*** Sorry, 32 character limit - request cancelled\n\n"); + return; + } + + LOG0("%s\n",mask(textPwd,2).c_str()); + spanOTA.setPassword(textPwd); + nvs_set_str(otaNVS,"OTADATA",spanOTA.otaPwd); // update data + nvs_commit(otaNVS); + + LOG0("... Accepted! Password change will take effect after next restart.\n"); + if(!spanOTA.enabled) + LOG0("... Note: OTA has not been enabled in this sketch.\n"); + LOG0("\n"); + } + break; case 'S': { @@ -2457,89 +2458,89 @@ void SpanWebLog::vLog(boolean sysMsg, const char *fmt, va_list ap){ // SpanOTA // /////////////////////////////// -//int SpanOTA::init(boolean _auth, boolean _safeLoad, const char *pwd){ -// if(esp_ota_get_running_partition()==esp_ota_get_next_update_partition(NULL)){ -// LOG0("\n*** WARNING: Can't start OTA Server - Partition table used to compile this sketch is not configured for OTA.\n\n"); -// return(-1); -// } -// -// enabled=true; -// safeLoad=_safeLoad; -// auth=_auth; -// if(pwd==NULL) -// return(0); -// return(setPassword(pwd)); -//} -// -///////////////////////////////// -// -//int SpanOTA::setPassword(const char *pwd){ -// if(strlen(pwd)<1 || strlen(pwd)>32){ -// LOG0("\n*** WARNING: Cannot change OTA password to '%s'. Password length must be between 1 and 32 characters.\n\n",pwd); -// return(-1); -// } -// -// MD5Builder otaPwdHash; -// otaPwdHash.begin(); -// otaPwdHash.add(pwd); -// otaPwdHash.calculate(); -// otaPwdHash.getChars(homeSpan.spanOTA.otaPwd); -// return(0); -//} -// -///////////////////////////////// -// -//void SpanOTA::start(){ -// LOG0("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..", -// esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); -// otaPercent=0; -// STATUS_UPDATE(start(LED_OTA_STARTED),HS_OTA_STARTED) -//} -// -///////////////////////////////// -// -//void SpanOTA::end(){ -// nvs_set_u8(homeSpan.otaNVS,"OTA_REQUIRED",safeLoad); -// nvs_commit(homeSpan.otaNVS); -// LOG0(" DONE! Rebooting...\n"); -// homeSpan.reboot(); -//} -// -///////////////////////////////// -// -//void SpanOTA::progress(uint32_t progress, uint32_t total){ -// int percent=progress*100/total; -// if(percent/10 != otaPercent/10){ -// otaPercent=percent; -// LOG0("%d%%..",progress*100/total); -// } -// -// if(safeLoad && progress==total){ -// SpanPartition newSpanPartition; -// esp_partition_read(esp_ota_get_next_update_partition(NULL), sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t), &newSpanPartition, sizeof(newSpanPartition)); -// LOG0("Checking for HomeSpan Magic Cookie: %s..",newSpanPartition.magicCookie); -// if(strcmp(newSpanPartition.magicCookie,spanPartition.magicCookie)) -// Update.abort(); -// } -//} -// -///////////////////////////////// -// -//void SpanOTA::error(ota_error_t err){ -// LOG0("*** OTA Error[%u]: ", err); -// if (err == OTA_AUTH_ERROR) LOG0("Auth Failed\n\n"); -// else if (err == OTA_BEGIN_ERROR) LOG0("Begin Failed\n\n"); -// else if (err == OTA_CONNECT_ERROR) LOG0("Connect Failed\n\n"); -// else if (err == OTA_RECEIVE_ERROR) LOG0("Receive Failed\n\n"); -// else if (err == OTA_END_ERROR) LOG0("End Failed\n\n"); -//} -// -///////////////////////////////// -// -//int SpanOTA::otaPercent; -//boolean SpanOTA::safeLoad; -//boolean SpanOTA::enabled=false; -//boolean SpanOTA::auth; +int SpanOTA::init(boolean _auth, boolean _safeLoad, const char *pwd){ + if(esp_ota_get_running_partition()==esp_ota_get_next_update_partition(NULL)){ + LOG0("\n*** WARNING: Can't start OTA Server - Partition table used to compile this sketch is not configured for OTA.\n\n"); + return(-1); + } + + enabled=true; + safeLoad=_safeLoad; + auth=_auth; + if(pwd==NULL) + return(0); + return(setPassword(pwd)); +} + +/////////////////////////////// + +int SpanOTA::setPassword(const char *pwd){ + if(strlen(pwd)<1 || strlen(pwd)>32){ + LOG0("\n*** WARNING: Cannot change OTA password to '%s'. Password length must be between 1 and 32 characters.\n\n",pwd); + return(-1); + } + + MD5Builder otaPwdHash; + otaPwdHash.begin(); + otaPwdHash.add(pwd); + otaPwdHash.calculate(); + otaPwdHash.getChars(homeSpan.spanOTA.otaPwd); + return(0); +} + +/////////////////////////////// + +void SpanOTA::start(){ + LOG0("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..", + esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); + otaPercent=0; + STATUS_UPDATE(start(LED_OTA_STARTED),HS_OTA_STARTED) +} + +/////////////////////////////// + +void SpanOTA::end(){ + nvs_set_u8(homeSpan.otaNVS,"OTA_REQUIRED",safeLoad); + nvs_commit(homeSpan.otaNVS); + LOG0(" DONE! Rebooting...\n"); + homeSpan.reboot(); +} + +/////////////////////////////// + +void SpanOTA::progress(uint32_t progress, uint32_t total){ + int percent=progress*100/total; + if(percent/10 != otaPercent/10){ + otaPercent=percent; + LOG0("%d%%..",percent); + } + + if(safeLoad && progress==total){ + SpanPartition newSpanPartition; + esp_partition_read(esp_ota_get_next_update_partition(NULL), sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t), &newSpanPartition, sizeof(newSpanPartition)); + LOG0("Checking for HomeSpan Magic Cookie: %s..",newSpanPartition.magicCookie); + if(strcmp(newSpanPartition.magicCookie,spanPartition.magicCookie)) + Update.abort(); + } +} + +/////////////////////////////// + +void SpanOTA::error(ota_error_t err){ + LOG0("*** OTA Error[%u]: ", err); + if (err == OTA_AUTH_ERROR) LOG0("Auth Failed\n\n"); + else if (err == OTA_BEGIN_ERROR) LOG0("Begin Failed\n\n"); + else if (err == OTA_CONNECT_ERROR) LOG0("Connect Failed\n\n"); + else if (err == OTA_RECEIVE_ERROR) LOG0("Receive Failed\n\n"); + else if (err == OTA_END_ERROR) LOG0("End Failed\n\n"); +} + +/////////////////////////////// + +int SpanOTA::otaPercent; +boolean SpanOTA::safeLoad; +boolean SpanOTA::enabled=false; +boolean SpanOTA::auth; /////////////////////////////// // SpanPoint // diff --git a/src/HomeSpan.h b/src/HomeSpan.h index d708218..c860b01 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -37,7 +37,7 @@ #include #include #include -//D#include +#include #include #include @@ -186,22 +186,22 @@ struct SpanWebLog{ // optional web status/log data /////////////////////////////// -//struct SpanOTA{ // manages OTA process -// -// char otaPwd[33]=""; // MD5 Hash of OTA password, represented as a string of hexidecimal characters -// -// static boolean enabled; // enables OTA - default if not enabled -// static boolean auth; // indicates whether OTA password is required -// static int otaPercent; -// static boolean safeLoad; // indicates whether OTA update should reject any application update that is not another HomeSpan sketch -// -// int init(boolean auth, boolean safeLoad, const char *pwd); -// int setPassword(const char *pwd); -// static void start(); -// static void end(); -// static void progress(uint32_t progress, uint32_t total); -// static void error(ota_error_t err); -//}; +struct SpanOTA{ // manages OTA process + + char otaPwd[33]=""; // MD5 Hash of OTA password, represented as a string of hexidecimal characters + + static boolean enabled; // enables OTA - default if not enabled + static boolean auth; // indicates whether OTA password is required + static int otaPercent; + static boolean safeLoad; // indicates whether OTA update should reject any application update that is not another HomeSpan sketch + + int init(boolean auth, boolean safeLoad, const char *pwd); + int setPassword(const char *pwd); + static void start(); + static void end(); + static void progress(uint32_t progress, uint32_t total); + static void error(ota_error_t err); +}; ////////////////////////////////////// // USER API CLASSES BEGINS HERE // @@ -273,7 +273,7 @@ class Span{ TaskHandle_t loopTaskHandle; // Arduino Loop Task handle boolean verboseWifiReconnect = true; // set to false to not print WiFi reconnect attempts messages -//D SpanOTA spanOTA; // manages OTA process + SpanOTA spanOTA; // manages OTA process SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found list> hapList; // linked-list of HAPClient structures containing HTTP client connections, parsing routines, and state variables @@ -370,8 +370,8 @@ class Span{ Span& setHostNameSuffix(const char *suffix){asprintf(&hostNameSuffix,"%s",suffix);return(*this);} // sets the hostName suffix to be used instead of the 6-byte AccessoryID -//D int enableOTA(boolean auth=true, boolean safeLoad=true){return(spanOTA.init(auth, safeLoad, NULL));} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password -//D int enableOTA(const char *pwd, boolean safeLoad=true){return(spanOTA.init(true, safeLoad, pwd));} // enables Over-the-Air updates, with custom authorization password (overrides any password stored with the 'O' command) + int enableOTA(boolean auth=true, boolean safeLoad=true){return(spanOTA.init(auth, safeLoad, NULL));} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password + int enableOTA(const char *pwd, boolean safeLoad=true){return(spanOTA.init(true, safeLoad, pwd));} // enables Over-the-Air updates, with custom authorization password (overrides any password stored with the 'O' command) Span& enableWebLog(uint16_t maxEntries=0, const char *serv=NULL, const char *tz="UTC", const char *url=DEFAULT_WEBLOG_URL){ // enable Web Logging webLog.init(maxEntries, serv, tz, url); diff --git a/src/src.ino b/src/src.ino index 830aa31..bb646af 100644 --- a/src/src.ino +++ b/src/src.ino @@ -32,7 +32,8 @@ void setup() { Serial.begin(115200); homeSpan.enableWebLog(50); - homeSpan.begin(Category::Lighting,"HomeSpan LightBulb"); + homeSpan.enableOTA(); + homeSpan.begin(Category::Lighting,"HomeSpan OTA Test"); new SpanAccessory(); new Service::AccessoryInformation();