From ef90bc3f34d34e280a853c415f8750af4ef86293 Mon Sep 17 00:00:00 2001 From: "Hansi, dl9rdz" Date: Thu, 16 Sep 2021 20:38:35 +0200 Subject: [PATCH] OTA also for files (www/js/css) --- RX_FSK/RX_FSK.ino | 249 ++++++++++++++++++++++-------------- RX_FSK/src/ShFreqImport.cpp | 7 +- RX_FSK/version.h | 2 +- 3 files changed, 155 insertions(+), 103 deletions(-) diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index c4f526f..d3c87db 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -41,11 +41,13 @@ AXP20X_Class axp; SemaphoreHandle_t axpSemaphore; bool pmu_irq = false; -String updateHost = "rdzsonde.mooo.com"; +const char *updateHost = "rdzsonde.mooo.com"; int updatePort = 80; -String updateBinM = "/master/update.ino.bin"; -String updateBinD = "/devel/update.ino.bin"; -String *updateBin = &updateBinM; + +const char *updatePrefixM = "/master/"; +const char *updatePrefixD = "/devel/"; +const char *updatePrefix = updatePrefixM; + #define LOCALUDPPORT 9002 //Get real UTC time from NTP server @@ -1076,14 +1078,14 @@ const char *handleUpdatePost(AsyncWebServerRequest *request) { Serial.println(param.c_str()); if (param.equals("devel")) { Serial.println("equals devel"); - updateBin = &updateBinD; + updatePrefix = updatePrefixD; } else if (param.equals("master")) { Serial.println("equals master"); - updateBin = &updateBinM; + updatePrefix = updatePrefixM; } } - Serial.println("Updating: " + *updateBin); + Serial.printf("Updating: %supdate.ino.bin\n", updatePrefix); enterMode(ST_UPDATE); return ""; } @@ -2901,27 +2903,81 @@ void execOTA() { if ( ISOLED(sonde.config) ) { disp.rdis->setFont(FONT_SMALL); dispxs = dispys = 1; + char uh[17]; + strncpy(uh, updateHost, 17); + uh[16]=0; + disp.rdis->drawString(0, 0, uh); } else { disp.rdis->setFont(5); dispxs = 18; dispys = 20; + disp.rdis->drawString(0, 0, updateHost); } - String dispHost = updateHost.substring(0, 16); - disp.rdis->drawString(0, 0, dispHost.c_str()); - - Serial.println("Connecting to: " + updateHost); + Serial.print("Connecting to: "); Serial.println(updateHost); // Connect to Update host - if (client.connect(updateHost.c_str(), updatePort)) { + if (!client.connect(updateHost, updatePort)) { + Serial.println("Connection to " + String(updateHost) + " failed. Please check your setup"); + return; + } + + // First, update file system + Serial.println("Fetching fs update"); + disp.rdis->drawString(0, 1 * dispys, "Fetching fs..."); + client.printf("GET %supdate.fs.bin HTTP/1.1\r\n" + "Host: %s\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n\r\n", updatePrefix, updateHost); + // see if we get some data.... + + int type = 0; + int res = fetchHTTPheader(&type); + if(res < 0) { return; } + // process data... + while(client.available()) { + // get header... + char fn[128]; + fn[0] = '/'; + client.readBytesUntil('\n', fn+1, 128); + char *sz = strchr(fn, ' '); + if(!sz) { client.stop(); return; } + *sz = 0; + int len = atoi(sz+1); + Serial.printf("Updating file %s (%d bytes)\n", fn, len); + char fnstr[17]; + memset(fnstr, ' ', 16); + strncpy(fnstr, fn, strlen(fn)); + fnstr[16]=0; + disp.rdis->drawString(0, 2 * dispys, fnstr); + File f = SPIFFS.open(fn, FILE_WRITE); + // read sz bytes........ + while(len>0) { + unsigned char buf[1024]; + int r = client.read(buf, len>1024? 1024:len); + if(r==-1) { client.stop(); return; } + f.write(buf, r); + len -= r; + } + } + client.stop(); + + Serial.print("Connecting to: "); Serial.println(updateHost); + // Connect to Update host + if (!client.connect(updateHost, updatePort)) { + Serial.println("Connection to " + String(updateHost) + " failed. Please check your setup"); + return; + } + // Connection succeeded, fecthing the bin - Serial.println("Fetching bin: " + String(*updateBin)); - disp.rdis->drawString(0, 1 * dispys, "Fetching update"); + Serial.printf("Fetching bin: %supdate.ino.bin\n", updatePrefix); + disp.rdis->drawString(0, 3 * dispys, "Fetching update"); // Get the contents of the bin file - client.print(String("GET ") + *updateBin + " HTTP/1.1\r\n" + - "Host: " + updateHost + "\r\n" + - "Cache-Control: no-cache\r\n" + - "Connection: close\r\n\r\n"); + client.printf("GET %supdate.ino.bin HTTP/1.1\r\n" + "Host: %s\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n\r\n", + updatePrefix, updateHost); // Check what is being sent // Serial.print(String("GET ") + bin + " HTTP/1.1\r\n" + @@ -2929,101 +2985,27 @@ void execOTA() { // "Cache-Control: no-cache\r\n" + // "Connection: close\r\n\r\n"); - unsigned long timeout = millis(); - while (client.available() == 0) { - if (millis() - timeout > 5000) { - Serial.println("Client Timeout !"); - client.stop(); - return; - } - } - // Once the response is available, - // check stuff - - /* - Response Structure - HTTP/1.1 200 OK - x-amz-id-2: NVKxnU1aIQMmpGKhSwpCBh8y2JPbak18QLIfE+OiUDOos+7UftZKjtCFqrwsGOZRN5Zee0jpTd0= - x-amz-request-id: 2D56B47560B764EC - Date: Wed, 14 Jun 2017 03:33:59 GMT - Last-Modified: Fri, 02 Jun 2017 14:50:11 GMT - ETag: "d2afebbaaebc38cd669ce36727152af9" - Accept-Ranges: bytes - Content-Type: application/octet-stream - Content-Length: 357280 - Server: AmazonS3 - - {{BIN FILE CONTENTS}} - - */ - while (client.available()) { - // read line till /n - String line = client.readStringUntil('\n'); - // remove space, to check if the line is end of headers - line.trim(); - - // if the the line is empty, - // this is end of headers - // break the while and feed the - // remaining `client` to the - // Update.writeStream(); - if (!line.length()) { - //headers ended - break; // and get the OTA started - } - - // Check if the HTTP Response is 200 - // else break and Exit Update - if (line.startsWith("HTTP/1.1")) { - if (line.indexOf("200") < 0) { - Serial.println("Got a non 200 status code from server. Exiting OTA Update."); - break; - } - } - - // extract headers here - // Start with content length - if (line.startsWith("Content-Length: ")) { - contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str()); - Serial.println("Got " + String(contentLength) + " bytes from server"); - } - - // Next, the content type - if (line.startsWith("Content-Type: ")) { - String contentType = getHeaderValue(line, "Content-Type: "); - Serial.println("Got " + contentType + " payload."); - if (contentType == "application/octet-stream") { - isValidContentType = true; - } - } - } - } else { - // Connect to updateHost failed - // May be try? - // Probably a choppy network? - Serial.println("Connection to " + String(updateHost) + " failed. Please check your setup"); - // retry?? - // execOTA(); - } + int validType = 0; + contentLength = fetchHTTPheader( &validType ); + if(validType==1) isValidContentType = true; // Check what is the contentLength and if content type is `application/octet-stream` Serial.println("contentLength : " + String(contentLength) + ", isValidContentType : " + String(isValidContentType)); - disp.rdis->drawString(0, 2 * dispys, "Len: "); + disp.rdis->drawString(0, 4 * dispys, "Len: "); String cls = String(contentLength); - disp.rdis->drawString(5 * dispxs, 2 * dispys, cls.c_str()); + disp.rdis->drawString(5 * dispxs, 4 * dispys, cls.c_str()); // check contentLength and content type if (contentLength && isValidContentType) { // Check if there is enough to OTA Update bool canBegin = Update.begin(contentLength); - disp.rdis->drawString(0, 4 * dispys, "Starting update"); // If yes, begin if (canBegin) { + disp.rdis->drawString(0, 5 * dispys, "Starting update"); Serial.println("Begin OTA. This may take 2 - 5 mins to complete. Things might be quite for a while.. Patience!"); // No activity would appear on the Serial monitor // So be patient. This may take 2 - 5mins to complete - disp.rdis->drawString(0, 5 * dispys, "Please wait!"); size_t written = Update.writeStream(client); if (written == contentLength) { @@ -3062,6 +3044,77 @@ void execOTA() { enterMode(ST_DECODER); } +int fetchHTTPheader(int *validType) { + int contentLength = -1; + unsigned long timeout = millis(); + while (client.available() == 0) { + if (millis() - timeout > 5000) { + Serial.println("Client Timeout !"); + client.stop(); + return -1; + } + } + // Once the response is available, check stuff + + /* + Response Structure + HTTP/1.1 200 OK + x-amz-id-2: NVKxnU1aIQMmpGKhSwpCBh8y2JPbak18QLIfE+OiUDOos+7UftZKjtCFqrwsGOZRN5Zee0jpTd0= + x-amz-request-id: 2D56B47560B764EC + Date: Wed, 14 Jun 2017 03:33:59 GMT + Last-Modified: Fri, 02 Jun 2017 14:50:11 GMT + ETag: "d2afebbaaebc38cd669ce36727152af9" + Accept-Ranges: bytes + Content-Type: application/octet-stream + Content-Length: 357280 + Server: AmazonS3 + + {{BIN FILE CONTENTS}} + + */ + while (client.available()) { + // read line till \n + String line = client.readStringUntil('\n'); + // remove space, to check if the line is end of headers + line.trim(); + + // if the the line is empty, + // this is end of headers + // break the while and feed the + // remaining `client` to the + // Update.writeStream(); + if (!line.length()) { + //headers ended + break; // and get the OTA started + } + + // Check if the HTTP Response is 200 + // else break and Exit Update + if (line.startsWith("HTTP/1.1")) { + if (line.indexOf("200") < 0) { + Serial.println("Got a non 200 status code from server. Exiting OTA Update."); + return -1; + } + } + + // extract headers here + // Start with content length + if (line.startsWith("Content-Length: ")) { + contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str()); + Serial.println("Got " + String(contentLength) + " bytes from server"); + } + + // Next, the content type + if (line.startsWith("Content-Type: ")) { + String contentType = getHeaderValue(line, "Content-Type: "); + Serial.println("Got " + contentType + " payload."); + if (contentType == "application/octet-stream") { + if(validType) *validType = 1; + } + } + } + return contentLength; +} diff --git a/RX_FSK/src/ShFreqImport.cpp b/RX_FSK/src/ShFreqImport.cpp index 68db393..baa04c1 100644 --- a/RX_FSK/src/ShFreqImport.cpp +++ b/RX_FSK/src/ShFreqImport.cpp @@ -46,7 +46,6 @@ void ShFreqImport::setLabel(int idx, char *id, float lat, float lon) { } void ShFreqImport::usekeyvalue() { - printf("\nUSEKEYVALUE: key %s => value %s\n", keyword, value); if(strcmp(keyword,"lat")==0) lat = atof(value); if(strcmp(keyword,"lon")==0) lon = atof(value); if(strcmp(keyword,"frequency")==0) freq = atof(value); @@ -56,7 +55,7 @@ void ShFreqImport::usekeyvalue() { /* populate qrg.txt with frequency of near sonde */ void ShFreqImport::populate(char *id, float lat, float lon, float freq, const char *type) { - printf(" ID %s: %.5f, %.5f f=%.3f, type=%s \n", id, lat, lon, freq, type); + //printf(" ID %s: %.5f, %.5f f=%.3f, type=%s \n", id, lat, lon, freq, type); // Skip if freq already exists int stype = stringToStype(type); if(stype<0) return; // unsupported type @@ -101,7 +100,7 @@ void ShFreqImport::populate(char *id, float lat, float lon, float freq, const ch // clears all remaining automatically filled slots (no longer in SH data) void ShFreqImport::cleanup() { - Serial.println("Cleanup called ********"); + //Serial.println("Cleanup called ********"); for(int i=0; i>(i&7))&1) == 0) && *sonde.sondeList[i].launchsite=='@' ) { Serial.printf("removing #%d\n", i); @@ -176,7 +175,7 @@ int ShFreqImport::handleChar(char c) { if(c=='}') { // we have an ID and all key/value pairs, check if its good.... if( !isnan(lat) && !isnan(lon) && !isnan(freq) && type[0] ) { - printf("populate %s %f %f %f %s\n", id, lat, lon, freq, type); + printf("SondeHub import: populate %s %f %f %f %s\n", id, lat, lon, freq, type); populate(id, lat, lon, freq, type); } else { printf("Skipping incomplete %s\n", id); diff --git a/RX_FSK/version.h b/RX_FSK/version.h index 6067d3c..8f8e660 100644 --- a/RX_FSK/version.h +++ b/RX_FSK/version.h @@ -1,4 +1,4 @@ const char *version_name = "rdzTTGOsonde"; -const char *version_id = "devel20210915"; +const char *version_id = "devel20210916"; const int SPIFFS_MAJOR=2; const int SPIFFS_MINOR=16;