diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a2ca8e7..00d8318a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ set(LIBRARY_SRCS libraries/SPI/src/SPI.cpp libraries/Ticker/src/Ticker.cpp libraries/Update/src/Updater.cpp + libraries/Update/src/HttpsOTAUpdate.cpp libraries/WebServer/src/WebServer.cpp libraries/WebServer/src/Parsing.cpp libraries/WebServer/src/detail/mimetable.cpp diff --git a/libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update.ino b/libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update.ino new file mode 100644 index 00000000..6e32ee5b --- /dev/null +++ b/libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update.ino @@ -0,0 +1,98 @@ +// This sketch provide the functionality of OTA Firmware Upgrade +#include "WiFi.h" +#include "HttpsOTAUpdate.h" +// This sketch shows how to implement HTTPS firmware update Over The Air. +// Please provide your WiFi credentials, https URL to the firmware image and the server certificate. + +static const char *ssid = "your-ssid"; // your network SSID (name of wifi network) +static const char *password = "your-password"; // your network password + +static const char *url = "https://example.com/firmware.bin"; //state url of your firmware image + +static const char *server_certificate = "-----BEGIN CERTIFICATE-----\n" \ + "MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \ + "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \ + "DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \ + "SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \ + "GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \ + "AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \ + "q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \ + "SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \ + "Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \ + "a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \ + "/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \ + "AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \ + "CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \ + "bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \ + "c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \ + "VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \ + "ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \ + "MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \ + "Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \ + "AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \ + "uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \ + "wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \ + "X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \ + "PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \ + "KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \ + "-----END CERTIFICATE-----"; + +static HttpsOTAStatus_t otastatus; + +void HttpEvent(HttpEvent_t *event) +{ + switch(event->event_id) { + case HTTP_EVENT_ERROR: + Serial.println("Http Event Error"); + break; + case HTTP_EVENT_ON_CONNECTED: + Serial.println("Http Event On Connected"); + break; + case HTTP_EVENT_HEADER_SENT: + Serial.println("Http Event Header Sent"); + break; + case HTTP_EVENT_ON_HEADER: + Serial.printf("Http Event On Header, key=%s, value=%s\n", event->header_key, event->header_value); + break; + case HTTP_EVENT_ON_DATA: + break; + case HTTP_EVENT_ON_FINISH: + Serial.println("Http Event On Finish"); + break; + case HTTP_EVENT_DISCONNECTED: + Serial.println("Http Event Disconnected"); + break; + } +} + +void setup(){ + + Serial.begin(115200); + Serial.print("Attempting to connect to SSID: "); + WiFi.begin(ssid, password); + + // attempt to connect to Wifi network: + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(1000); + } + + Serial.print("Connected to "); + Serial.println(ssid); + + HttpsOTA.onHttpEvent(HttpEvent); + Serial.println("Starting OTA"); + HttpsOTA.begin(url, server_certificate); + + Serial.println("Please Wait it takes some time ..."); +} + +void loop(){ + otastatus = HttpsOTA.status(); + if(otastatus == HTTPS_OTA_SUCCESS) { + Serial.println("Firmware written successfully. To reboot device, call API ESP.restart() or PUSH restart button on device"); + } else if(otastatus == HTTPS_OTA_FAIL) { + Serial.println("Firmware Upgrade Fail"); + } + delay(1000); +} diff --git a/libraries/Update/examples/HTTPS_OTA_Update/Readme.md b/libraries/Update/examples/HTTPS_OTA_Update/Readme.md new file mode 100644 index 00000000..27fdb596 --- /dev/null +++ b/libraries/Update/examples/HTTPS_OTA_Update/Readme.md @@ -0,0 +1,32 @@ +# OTA Firmware Upgrade for Arduino +This sketch allows Arduino user to perform Over The Air (OTA) firmware upgrade. It uses HTTPS. + +# API introduced for OTA + +## HttpsOTA.begin(const char * url, const char * server_certificate, bool skip_cert_common_name_check) + +Main API which starts firmware upgrade + +### Parameters +* url : URL for the uploaded firmware image +* server_certificate : Provide the ota server certificate for authentication via HTTPS +* skip_cert_common_name_check : Skip any validation of server certificate CN field + +The default value provided to skip_cert_common_name_check is true + +## HttpsOTA.onHttpEvent(function) + +This API exposes HTTP Events to the user + +### Parameter +Function passed has following signature +void HttpEvent (HttpEvent_t * event); + +# HttpsOTA.otaStatus() + +It tracks the progress of OTA firmware upgrade. +* HTTPS_OTA_IDLE : OTA upgrade have not started yet. +* HTTPS_OTA_UPDATNG : OTA upgarde is in progress. +* HTTPS_OTA_SUCCESS : OTA upgrade is successful. +* HTTPS_OTA_FAIL : OTA upgrade failed. +* HTTPS_OTA_ERR : Error occured while creating xEventGroup(). diff --git a/libraries/Update/src/HttpsOTAUpdate.cpp b/libraries/Update/src/HttpsOTAUpdate.cpp new file mode 100644 index 00000000..574b054c --- /dev/null +++ b/libraries/Update/src/HttpsOTAUpdate.cpp @@ -0,0 +1,107 @@ +/* OTA task + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include + +#include +#include + +#include "esp32-hal-log.h" +#include "esp_http_client.h" +#include "esp_https_ota.h" + +#include "HttpsOTAUpdate.h" +#include "Esp.h" +#define OTA_TASK_STACK_SIZE 9216 + +typedef void (*HttpEventCb)(HttpEvent_t*); + +static esp_http_client_config_t config; +static HttpEventCb cb; +static EventGroupHandle_t ota_status = NULL;//check for ota status +static EventBits_t set_bit; + +const int OTA_IDLE_BIT = BIT0; +const int OTA_UPDATING_BIT = BIT1; +const int OTA_SUCCESS_BIT = BIT2; +const int OTA_FAIL_BIT = BIT3; + +esp_err_t http_event_handler(esp_http_client_event_t *event) +{ + cb(event); + return ESP_OK; +} + +void https_ota_task(void *param) +{ + if(ota_status) { + xEventGroupSetBits(ota_status, OTA_UPDATING_BIT); + xEventGroupClearBits(ota_status, OTA_IDLE_BIT); + } + esp_err_t ret = esp_https_ota((const esp_http_client_config_t *)param); + if(ret == ESP_OK) { + if(ota_status) { + xEventGroupClearBits(ota_status, OTA_UPDATING_BIT); + xEventGroupSetBits(ota_status, OTA_SUCCESS_BIT); + } + } else { + if(ota_status) { + xEventGroupClearBits(ota_status, OTA_UPDATING_BIT); + xEventGroupSetBits(ota_status, OTA_FAIL_BIT); + } + } + vTaskDelete(NULL); +} + +HttpsOTAStatus_t HttpsOTAUpdateClass::status() +{ + if(ota_status) { + set_bit = xEventGroupGetBits(ota_status); + if(set_bit == OTA_IDLE_BIT) { + return HTTPS_OTA_IDLE; + } + if(set_bit == OTA_UPDATING_BIT) { + return HTTPS_OTA_UPDATING; + } + if(set_bit == OTA_SUCCESS_BIT) { + return HTTPS_OTA_SUCCESS; + } + if(set_bit == OTA_FAIL_BIT) { + return HTTPS_OTA_FAIL; + } + } + return HTTPS_OTA_ERR; +} + +void HttpsOTAUpdateClass::onHttpEvent(HttpEventCb cbEvent) +{ + cb = cbEvent; +} + +void HttpsOTAUpdateClass::begin(const char *url, const char *cert_pem, bool skip_cert_common_name_check) +{ + config.url = url; + config.cert_pem = cert_pem; + config.skip_cert_common_name_check = skip_cert_common_name_check; + config.event_handler = http_event_handler; + + if(!ota_status) { + ota_status = xEventGroupCreate(); + if(!ota_status) { + log_e("OTA Event Group Create Failed"); + } + xEventGroupSetBits(ota_status, OTA_IDLE_BIT); + } + + if (xTaskCreate(&https_ota_task, "https_ota_task", OTA_TASK_STACK_SIZE, &config, 5, NULL) != pdPASS) { + log_e("Couldn't create ota task\n"); + } +} + +HttpsOTAUpdateClass HttpsOTA; diff --git a/libraries/Update/src/HttpsOTAUpdate.h b/libraries/Update/src/HttpsOTAUpdate.h new file mode 100644 index 00000000..bc52a305 --- /dev/null +++ b/libraries/Update/src/HttpsOTAUpdate.h @@ -0,0 +1,21 @@ +#include "esp_http_client.h" +#define HttpEvent_t esp_http_client_event_t + +typedef enum +{ + HTTPS_OTA_IDLE, + HTTPS_OTA_UPDATING, + HTTPS_OTA_SUCCESS, + HTTPS_OTA_FAIL, + HTTPS_OTA_ERR +}HttpsOTAStatus_t; + +class HttpsOTAUpdateClass { + + public: + void begin(const char *url, const char *cert_pem, bool skip_cert_common_name_check = true); + void onHttpEvent(void (*http_event_cb_t)(HttpEvent_t *)); + HttpsOTAStatus_t status(); +}; + +extern HttpsOTAUpdateClass HttpsOTA;