/* esp32 firmware OTA Date: December 2018 Author: Chris Joyce Purpose: Perform an OTA update from a bin located on a webserver (HTTP Only) */ #include "esp32fota.h" #include "Arduino.h" #include #include #include #include "ArduinoJson.h" extern void forceBootInit(); esp32FOTA::esp32FOTA(String firwmareType, int firwmareVersion) { _firwmareType = firwmareType; _firwmareVersion = firwmareVersion; useDeviceID = false; } // Utility to extract header value from headers String esp32FOTA::getHeaderValue(String header, String headerName) { return header.substring(strlen(headerName.c_str())); } // OTA Logic void esp32FOTA::execOTA() { WiFiClient client; int contentLength = 0; bool isValidContentType = false; Serial.println("Connecting to: " + String(_host)); // Connect to Webserver if (client.connect(_host.c_str(), _port)) { // Connection Succeed. // Fecthing the bin Serial.println("Fetching Bin: " + String(_bin)); // Get the contents of the bin file client.print(String("GET ") + _bin + " HTTP/1.1\r\n" + "Host: " + _host + "\r\n" + "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; } } 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 (!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 webserver failed // May be try? // Probably a choppy network? Serial.println("Connection to " + String(_host) + " failed. Please check your setup"); // retry?? // execOTA(); } // Check what is the contentLength and if content type is `application/octet-stream` Serial.println("contentLength : " + String(contentLength) + ", isValidContentType : " + String(isValidContentType)); // check contentLength and content type if (contentLength && isValidContentType) { // Check if there is enough to OTA Update bool canBegin = Update.begin(contentLength); // If yes, begin if (canBegin) { 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 size_t written = Update.writeStream(client); if (written == contentLength) { Serial.println("Written : " + String(written) + " successfully"); } else { Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?"); // retry?? // execOTA(); } if (Update.end()) { Serial.println("OTA done!"); if (Update.isFinished()) { Serial.println("Update successfully completed. Rebooting."); forceBootInit(); ESP.restart(); } else { Serial.println("Update not finished? Something went wrong!"); } } else { Serial.println("Error Occurred. Error #: " + String(Update.getError())); } } else { // not enough space to begin OTA // Understand the partitions and // space availability Serial.println("Not enough space to begin OTA"); client.flush(); } } else { Serial.println("There was no content in the response"); client.flush(); } } bool esp32FOTA::execHTTPcheck() { String useURL; if (useDeviceID) { // String deviceID = getDeviceID() ; useURL = checkURL + "?id=" + getDeviceID(); } else { useURL = checkURL; } WiFiClient client; _port = 80; Serial.println("Getting HTTP"); Serial.println(useURL); Serial.println("------"); if ((WiFi.status() == WL_CONNECTED)) { //Check the current connection status HTTPClient http; http.begin(useURL); //Specify the URL int httpCode = http.GET(); //Make the request if (httpCode == 200) { //Check is a file was returned String payload = http.getString(); int str_len = payload.length() + 1; char* JSONMessage = new char[str_len]; payload.toCharArray(JSONMessage, str_len); StaticJsonBuffer<300> JSONBuffer; //Memory pool JsonObject &parsed = JSONBuffer.parseObject(JSONMessage); //Parse message if (!parsed.success()) { //Check for errors in parsing delete[] JSONMessage; Serial.println("Parsing failed"); delay(5000); return false; } const char *pltype = parsed["type"]; int plversion = parsed["version"]; const char *plhost = parsed["host"]; _port = parsed["port"]; const char *plbin = parsed["bin"]; String jshost(plhost); String jsbin(plbin); _host = jshost; _bin = jsbin; String fwtype(pltype); delete[] JSONMessage; if (plversion > _firwmareVersion && fwtype == _firwmareType) { return true; } else { return false; } } else { Serial.println("Error on HTTP request"); return false; } http.end(); //Free the resources } return false; } String esp32FOTA::getDeviceID() { char deviceid[21]; uint64_t chipid; chipid = ESP.getEfuseMac(); sprintf(deviceid, "%" PRIu64, chipid); String thisID(deviceid); return thisID; }