274 lines
7.6 KiB
C++
274 lines
7.6 KiB
C++
/*
|
|
esp32 firmware OTA
|
|
Date: December 2018
|
|
Author: Chris Joyce <https://chrisjoyce911/esp32FOTA>
|
|
Purpose: Perform an OTA update from a bin located on a webserver (HTTP Only)
|
|
*/
|
|
|
|
#include "esp32fota.h"
|
|
#include "Arduino.h"
|
|
#include <WiFi.h>
|
|
#include <HTTPClient.h>
|
|
#include <Update.h>
|
|
#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;
|
|
} |