ESP32 OTA update with Web Browser: upload in HTTPS (SSL/TLS) with self-signed certificate – 2

Spread the love

In this article, we will explore how to perform OTA updates on the ESP32 using a web browser and HTTPS protocol with a self-signed certificate. HTTPS provides a secure way to transfer data over the internet and is essential for any OTA update process that involves sensitive information. A self-signed certificate can be used to provide encryption and authentication without the need for a third-party certificate authority, making it a cost-effective solution for small-scale projects.

ESP32 OTA update with Web Browser: HTTPS (SSL/TLS) and self signed certificate
ESP32 OTA update with Web Browser: HTTPS (SSL/TLS) and self signed certificate

We will start by setting up the ESP32 for OTA updates, then generate a self-signed certificate and configure the HTTPS server. Finally, we will test the OTA update process by uploading a new firmware version using a web browser. By the end of this article, you will have a working OTA update process for your ESP32 project that uses HTTPS protocol and a self-signed certificate.

OTA (Over the Air) update is the process of uploading firmware to an ESP32 module using a Wi-Fi connection rather than a serial port. Such functionality becomes extremely useful in case of limited or no physical access to the module.

OTA may be done using:

  • Arduino IDE
  • Web Browser
  • HTTP Server

First of all, check the tutorial “ESP32: flash compiled firmware (.bin)“.

Introduction

First, we look that the core component of ESP32 core needs python installed, and when installing It remember to add to the path (for windows)

ESP Tools Install Python and add It to path
ESP Tools Install Python and add It to path

Then go to read how to create a binary file from this article “Compiled (.bin), signed and filesystem binary on esp8266”.

SSL/TLS encrypted, password-protected firmware update

First, I think It’s nonsense to use a self-signed certificate in this case because if you want to use it internally (in the private LAN network), you don’t add a certificate, you are already inside a secure network, and if you want to export to the internet a better solution is to use a proxy with a single entry point with a complete certificate (like myhostname.org) and a set of endpoints that point to the device.

HTTPS TLS SSL encryption Arduino OTA
HTTPS TLS SSL encryption Arduino OTA

Generate the self-signed certificate

If you want to use a self-signed certificate, you can use OpenSSL for the generation of the private keys and certificate.

You can get openssl by downloading the Linux version from all packet managers, or download it for windows from here, to use It with more simplicity add It to the PATH.

openssl req -new -x509 -sha256 -newkey rsa:2048 -nodes -keyout key.pem -days 365 -out cert.pem -subj "/CN=esp32-webupdate.local"

and here is the response

Generating a RSA private key
................................+++++
..........................................................+++++
writing new private key to 'key.pem'
-----

Here is the content of key.pem

-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDHgkVGDdBv32Ba
MCgjTyw/dolIeix2YxsBPIvWVNqjq3nTwjGXrXUcr8Z2kJ7NVTXE3UVkHnMZmPeU
m4Gii9z5UbLk8tPhd1ZpQOKPZdioTFAZ6qKxspm4xVJqIW+gBRY849ofkP6DcDec
6dCYj8eJdQgARSfggDHL+Oc+0qh9YEZkRjyYIZWl3R5uVEi51AF5s7rWstuhvIUa
P/kSvvyIB1RRH4sgi8ARpY4uv9v/8EklRuh+eSYxz0U4aYcIqjSHBiG2U3utncxz
wKyz/T4G7Fbp1LAJhJqzPN2N1DznM6gFLlXvHJk8c4sJCHJpd7rgOvJEfte1xTuP
XKE1YNX3AgMBAAECggEACXMCFO9Sj/nkdmERhZqaXecfWW9v66uF++kCNoSOVnIo
sv7fmm98vH8Wx0bSPqdqhIh2YOeQTAYSs9cprKkzLhvQfUeDAYrFbPbFxETungn/
QTr7ua7b2QR/gsdSoiGbjAa+rgAQDemiuKvaVV958+i3GEwuN1PbB+4iBbe0gzuW
jUNjyHLw6TqlmnSS6Vf6JiKJU0czIjWW5lwJE30iyJp03LGbZDCmvyWGqN3zdW8y
PwI8GgrWHX2sgttWfWLlCZV0D582cWLc0ZKEkRyAOuhkUJ8Y/MwUKj/M/frB3Ii+
eEdhKNLUYlQY/Nh3QHL3ohjnzQ7/O+dLRf+6gBYgAQKBgQD9hhD4mt8NYuKRDnu5
4douvEmKLN18hJANFJjkDwQzRJsVnaGIEEJVZ7LpDwTy5+lNw+lvn3D5AVB++f+8
cXkwDIcEZbwk+/eOWz3SEPo1KwGhs8EQM7IzfRlXrkUsXMvu8Up4paueU4W5Pf7c
yXgnNbs0Fu9l90D/mV7OiNR/dwKBgQDJdSQGT9bfq5et8XsVnQQXg6BWc6/k75aa
qlvpgeXQtSKOmzTEkJz34Fu7sIEm1dQ0RUjDEz5EQVZcog34Dn/+K8aRBRrMFn17
wCdL0m3PgXlJlM42m1eaJ0vVO0p1jCtvGK+fP+AORz1hjOWeIHmhw4bYE+1w2F6x
oKFpH0z9gQKBgQCLJ39BNaCgtFovzIdU7AbaCDdFRIL9ybVXuKqC40sm6M1G/BKh
oGsIfbbR/ZB1051XNeV3g0h7JKGOUKJySMZ//SBO5ZhzpGmpFaPFHdR4QnbOzt91
iSqS4GN8oQcO5pB5Qq/hsO2WJboMh17QyTTOMMvkN4KHsUNYCFLlgm1A2QKBgQCS
7ov6yopmkilLpX9nMSEF4Wu1AiV87T3DypLEyYgLY8Ezj4G6B9tkcs+VIdPgtj5S
bWH3XXaho6HQaCWWEUVK5TSrGrUDuzeEZY6Dn5OVr9H9V7nbXAtVlGmbuOXCvBwF
qFSW6qiI4W6kUnY6kWcFE62qtUYNctIF7aksGbc2gQKBgQDK9tuP4pR1WBcK4kDa
r7EYWs5G83ceXVCTMf0XFJPgwNm0RduJ/ntRSWTj2jc5rENMEiFQpMIKz1h/rKkx
c/m7ZT9AbedAUxXOm9a/LKaTsJ11R/XO1MQE3QDZIDIq7C9Osd2Ap2WXdPazCtRR
D9eczO3iHYoTPvDYNWxDf45oig==
-----END PRIVATE KEY-----

and cert.pem

-----BEGIN CERTIFICATE-----
MIIDITCCAgmgAwIBAgIUdZdm1T/ibN+bQ5JB0+c+rnFPOZcwDQYJKoZIhvcNAQEL
BQAwIDEeMBwGA1UEAwwVZXNwMzItd2VidXBkYXRlLmxvY2FsMB4XDTIxMDkyNDEw
MjUwMloXDTIyMDkyNDEwMjUwMlowIDEeMBwGA1UEAwwVZXNwMzItd2VidXBkYXRl
LmxvY2FsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx4JFRg3Qb99g
WjAoI08sP3aJSHosdmMbATyL1lTao6t508Ixl611HK/GdpCezVU1xN1FZB5zGZj3
lJuBoovc+VGy5PLT4XdWaUDij2XYqExQGeqisbKZuMVSaiFvoAUWPOPaH5D+g3A3
nOnQmI/HiXUIAEUn4IAxy/jnPtKofWBGZEY8mCGVpd0eblRIudQBebO61rLbobyF
Gj/5Er78iAdUUR+LIIvAEaWOLr/b//BJJUbofnkmMc9FOGmHCKo0hwYhtlN7rZ3M
c8Css/0+BuxW6dSwCYSaszzdjdQ85zOoBS5V7xyZPHOLCQhyaXe64DryRH7XtcU7
j1yhNWDV9wIDAQABo1MwUTAdBgNVHQ4EFgQULJvBhUDPFKnnSkWxNSH24EVWZbgw
HwYDVR0jBBgwFoAULJvBhUDPFKnnSkWxNSH24EVWZbgwDwYDVR0TAQH/BAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAQEAVc6IOt7zUDmy4sX2hcKnhzmpi5O9M0MqL9wx
46ocbTbFZfc5MecyyKQJ7vEk013f+tD9Vdj/OFBXXyffkBEe0JU5cpbzPMZb22Nu
24dP1hGt/kdsJEbXg3ZUGJbpJgfmF7mgMn6vp0ggp1Yprn3794Xh+QtnaVLnfGIc
dPH4qMLclwoZyA6xKeJjaGz6zQcHITzA6Eb9xX+HJKn91AOEJluUB+RubJBQlNpC
BeI1DvXhssiknKx4e/axcbbRxsW7VBeSK+5CR6y8yTHFJqXPJ0JWkby0NOi6C4es
UhplRTTHcxfOONyYWUIHas659U9yrEu8t8sTOyNQZPJ2ckdW5A==
-----END CERTIFICATE-----

Convert to byte array

Now the SSLCert needs the certificate and key in byte array format,

// Create an SSL certificate object from the files above
SSLCert cert = SSLCert(
    (unsigned char *)cert_pem, cert_pem_len,
    (unsigned char *)key_pem, key_pem_len
);

you can use my online converter, to convert.

Convert certificate and key self signed to bytearray
Convert certificate and key self-signed to byte array

HTTPS example sketch

Get a library for the HTTPS server

To execute this example, you need an external library that you can find here on GitHub.

Arduino IDE sketchbook location library base path
Arduino IDE sketchbook location library base path

You can DOWNLOAD the library zip, next you extract the content and copy It under <sketchbook location>\libraries.

Example sketch

Now we add the certificates to the example code

/**
 * Example for the ESP32 HTTP(S) OTA Update Webserver
 *
 * Check the guide to how to generate key and cert
 * and convert in bytearray with this tool
 * https://mischianti.org/online-converter-file-to-cpp-gzip-byte-array-3/
 *
 * Renzo Mischianti <www.mischianti.org>
 *
 * https://mischianti.org/
 *
 */

#define WIFI_SSID "<YOUR-SSID>"
#define WIFI_PSK  "<YOUR-PASSWD>"

// We will use wifi
#include <WiFi.h>

// Includes for the server
#include <HTTPSServer.hpp>
#include <SSLCert.hpp>
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>
#include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp>
#include <HTTPURLEncodedBodyParser.hpp>
#include <Update.h>
#include <SPIFFS.h>

//File: cert.pem, Size: 1170
#define cert_pem_len 1170
const uint8_t cert_pem[] PROGMEM = {
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49,
0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0D, 0x0A, 0x4D, 0x49, 0x49,
0x44, 0x4A, 0x54, 0x43, 0x43, 0x41, 0x67, 0x32, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49,
0x55, 0x49, 0x62, 0x54, 0x73, 0x6D, 0x66, 0x2F, 0x50, 0x55, 0x4C, 0x4C, 0x70, 0x75, 0x75, 0x51,
0x50, 0x41, 0x77, 0x61, 0x39, 0x7A, 0x30, 0x32, 0x79, 0x79, 0x78, 0x41, 0x77, 0x44, 0x51, 0x59,
0x4A, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x63, 0x4E, 0x41, 0x51, 0x45, 0x4C, 0x0D, 0x0A, 0x42,
0x51, 0x41, 0x77, 0x49, 0x6A, 0x45, 0x67, 0x4D, 0x42, 0x34, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41,
0x77, 0x77, 0x58, 0x5A, 0x58, 0x4E, 0x77, 0x4F, 0x44, 0x49, 0x32, 0x4E, 0x69, 0x31, 0x33, 0x5A,
0x57, 0x4A, 0x31, 0x63, 0x47, 0x52, 0x68, 0x64, 0x47, 0x55, 0x75, 0x62, 0x47, 0x39, 0x6A, 0x59,
0x57, 0x77, 0x77, 0x48, 0x68, 0x63, 0x4E, 0x4D, 0x6A, 0x45, 0x77, 0x4F, 0x54, 0x41, 0x32, 0x0D,
0x0A, 0x4D, 0x54, 0x55, 0x7A, 0x4F, 0x54, 0x55, 0x33, 0x57, 0x68, 0x63, 0x4E, 0x4D, 0x6A, 0x49,
0x77, 0x4F, 0x54, 0x41, 0x32, 0x4D, 0x54, 0x55, 0x7A, 0x4F, 0x54, 0x55, 0x33, 0x57, 0x6A, 0x41,
0x69, 0x4D, 0x53, 0x41, 0x77, 0x48, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x44, 0x44, 0x42, 0x64,
0x6C, 0x63, 0x33, 0x41, 0x34, 0x4D, 0x6A, 0x59, 0x32, 0x4C, 0x58, 0x64, 0x6C, 0x59, 0x6E, 0x56,
0x77, 0x0D, 0x0A, 0x5A, 0x47, 0x46, 0x30, 0x5A, 0x53, 0x35, 0x73, 0x62, 0x32, 0x4E, 0x68, 0x62,
0x44, 0x43, 0x43, 0x41, 0x53, 0x49, 0x77, 0x44, 0x51, 0x59, 0x4A, 0x4B, 0x6F, 0x5A, 0x49, 0x68,
0x76, 0x63, 0x4E, 0x41, 0x51, 0x45, 0x42, 0x42, 0x51, 0x41, 0x44, 0x67, 0x67, 0x45, 0x50, 0x41,
0x44, 0x43, 0x43, 0x41, 0x51, 0x6F, 0x43, 0x67, 0x67, 0x45, 0x42, 0x41, 0x4D, 0x64, 0x35, 0x4A,
0x51, 0x72, 0x55, 0x0D, 0x0A, 0x4E, 0x61, 0x43, 0x79, 0x43, 0x72, 0x66, 0x57, 0x72, 0x56, 0x30,
0x56, 0x50, 0x58, 0x6D, 0x37, 0x63, 0x31, 0x59, 0x67, 0x6F, 0x56, 0x49, 0x75, 0x58, 0x57, 0x37,
0x4C, 0x72, 0x74, 0x4D, 0x67, 0x4B, 0x33, 0x33, 0x5A, 0x62, 0x78, 0x64, 0x4F, 0x6F, 0x4B, 0x49,
0x4E, 0x63, 0x2B, 0x61, 0x72, 0x78, 0x54, 0x4E, 0x6C, 0x68, 0x41, 0x67, 0x39, 0x53, 0x32, 0x50,
0x6A, 0x32, 0x52, 0x65, 0x59, 0x0D, 0x0A, 0x35, 0x4B, 0x67, 0x32, 0x61, 0x67, 0x70, 0x37, 0x39,
0x70, 0x64, 0x30, 0x2B, 0x2F, 0x76, 0x57, 0x34, 0x54, 0x69, 0x47, 0x6F, 0x6E, 0x39, 0x6B, 0x37,
0x42, 0x45, 0x55, 0x38, 0x65, 0x77, 0x64, 0x44, 0x44, 0x4F, 0x38, 0x64, 0x65, 0x37, 0x4A, 0x52,
0x62, 0x63, 0x32, 0x30, 0x56, 0x49, 0x38, 0x42, 0x52, 0x33, 0x72, 0x51, 0x52, 0x61, 0x6C, 0x75,
0x6F, 0x70, 0x35, 0x5A, 0x79, 0x44, 0x43, 0x0D, 0x0A, 0x62, 0x4D, 0x48, 0x63, 0x4B, 0x5A, 0x56,
0x61, 0x39, 0x58, 0x71, 0x68, 0x32, 0x57, 0x4A, 0x45, 0x47, 0x73, 0x6C, 0x75, 0x57, 0x70, 0x56,
0x79, 0x67, 0x74, 0x4E, 0x6A, 0x5A, 0x41, 0x73, 0x62, 0x55, 0x56, 0x69, 0x34, 0x32, 0x6A, 0x48,
0x32, 0x69, 0x70, 0x6F, 0x74, 0x53, 0x69, 0x68, 0x65, 0x56, 0x79, 0x31, 0x68, 0x68, 0x56, 0x4A,
0x75, 0x63, 0x63, 0x52, 0x43, 0x55, 0x4D, 0x6A, 0x75, 0x0D, 0x0A, 0x72, 0x6B, 0x42, 0x76, 0x35,
0x55, 0x45, 0x5A, 0x6D, 0x73, 0x33, 0x6B, 0x55, 0x42, 0x47, 0x68, 0x41, 0x7A, 0x66, 0x74, 0x48,
0x48, 0x31, 0x63, 0x31, 0x6D, 0x70, 0x58, 0x34, 0x41, 0x7A, 0x51, 0x5A, 0x4F, 0x75, 0x78, 0x32,
0x53, 0x65, 0x6A, 0x4F, 0x57, 0x4D, 0x64, 0x65, 0x75, 0x42, 0x4D, 0x30, 0x53, 0x68, 0x58, 0x30,
0x53, 0x78, 0x39, 0x46, 0x31, 0x64, 0x73, 0x37, 0x51, 0x61, 0x4E, 0x0D, 0x0A, 0x44, 0x56, 0x54,
0x73, 0x42, 0x74, 0x67, 0x77, 0x67, 0x75, 0x4A, 0x6F, 0x4C, 0x48, 0x36, 0x31, 0x64, 0x56, 0x63,
0x4A, 0x34, 0x33, 0x5A, 0x65, 0x53, 0x47, 0x65, 0x37, 0x4E, 0x39, 0x71, 0x57, 0x71, 0x50, 0x74,
0x5A, 0x53, 0x48, 0x2B, 0x4F, 0x52, 0x50, 0x64, 0x4C, 0x2F, 0x6D, 0x4B, 0x2F, 0x58, 0x78, 0x4D,
0x6C, 0x71, 0x71, 0x54, 0x75, 0x6A, 0x53, 0x48, 0x34, 0x61, 0x64, 0x31, 0x71, 0x0D, 0x0A, 0x36,
0x70, 0x62, 0x62, 0x77, 0x49, 0x43, 0x54, 0x36, 0x4B, 0x31, 0x57, 0x54, 0x51, 0x30, 0x43, 0x41,
0x77, 0x45, 0x41, 0x41, 0x61, 0x4E, 0x54, 0x4D, 0x46, 0x45, 0x77, 0x48, 0x51, 0x59, 0x44, 0x56,
0x52, 0x30, 0x4F, 0x42, 0x42, 0x59, 0x45, 0x46, 0x48, 0x59, 0x35, 0x37, 0x74, 0x63, 0x56, 0x6A,
0x54, 0x51, 0x70, 0x35, 0x74, 0x69, 0x70, 0x57, 0x66, 0x67, 0x51, 0x54, 0x31, 0x70, 0x65, 0x0D,
0x0A, 0x45, 0x72, 0x45, 0x5A, 0x4D, 0x42, 0x38, 0x47, 0x41, 0x31, 0x55, 0x64, 0x49, 0x77, 0x51,
0x59, 0x4D, 0x42, 0x61, 0x41, 0x46, 0x48, 0x59, 0x35, 0x37, 0x74, 0x63, 0x56, 0x6A, 0x54, 0x51,
0x70, 0x35, 0x74, 0x69, 0x70, 0x57, 0x66, 0x67, 0x51, 0x54, 0x31, 0x70, 0x65, 0x45, 0x72, 0x45,
0x5A, 0x4D, 0x41, 0x38, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, 0x45, 0x42, 0x2F, 0x77, 0x51,
0x46, 0x0D, 0x0A, 0x4D, 0x41, 0x4D, 0x42, 0x41, 0x66, 0x38, 0x77, 0x44, 0x51, 0x59, 0x4A, 0x4B,
0x6F, 0x5A, 0x49, 0x68, 0x76, 0x63, 0x4E, 0x41, 0x51, 0x45, 0x4C, 0x42, 0x51, 0x41, 0x44, 0x67,
0x67, 0x45, 0x42, 0x41, 0x49, 0x38, 0x32, 0x6F, 0x44, 0x32, 0x41, 0x36, 0x4D, 0x51, 0x48, 0x78,
0x78, 0x79, 0x35, 0x39, 0x31, 0x58, 0x36, 0x56, 0x52, 0x2F, 0x45, 0x49, 0x58, 0x39, 0x72, 0x57,
0x46, 0x32, 0x54, 0x0D, 0x0A, 0x58, 0x68, 0x65, 0x6E, 0x34, 0x49, 0x6D, 0x53, 0x79, 0x6D, 0x71,
0x6D, 0x35, 0x56, 0x46, 0x73, 0x51, 0x44, 0x41, 0x71, 0x52, 0x4C, 0x70, 0x62, 0x42, 0x71, 0x57,
0x4F, 0x36, 0x74, 0x30, 0x42, 0x67, 0x69, 0x41, 0x66, 0x62, 0x51, 0x4E, 0x47, 0x37, 0x6C, 0x33,
0x6B, 0x6B, 0x58, 0x51, 0x78, 0x41, 0x33, 0x73, 0x42, 0x2B, 0x6C, 0x49, 0x5A, 0x67, 0x4A, 0x65,
0x76, 0x67, 0x52, 0x4B, 0x74, 0x0D, 0x0A, 0x65, 0x37, 0x74, 0x65, 0x79, 0x31, 0x48, 0x7A, 0x64,
0x59, 0x75, 0x50, 0x76, 0x46, 0x46, 0x42, 0x65, 0x31, 0x58, 0x6F, 0x54, 0x38, 0x72, 0x52, 0x2F,
0x6C, 0x7A, 0x2B, 0x41, 0x63, 0x74, 0x51, 0x54, 0x67, 0x49, 0x61, 0x46, 0x42, 0x4E, 0x50, 0x45,
0x66, 0x74, 0x6F, 0x57, 0x65, 0x55, 0x70, 0x4F, 0x4B, 0x4A, 0x48, 0x58, 0x5A, 0x37, 0x4E, 0x61,
0x57, 0x69, 0x41, 0x76, 0x6D, 0x70, 0x43, 0x0D, 0x0A, 0x6A, 0x69, 0x7A, 0x78, 0x30, 0x6E, 0x72,
0x64, 0x70, 0x61, 0x6E, 0x6E, 0x47, 0x65, 0x67, 0x77, 0x39, 0x63, 0x69, 0x46, 0x42, 0x70, 0x6E,
0x36, 0x2B, 0x38, 0x44, 0x6F, 0x4D, 0x56, 0x71, 0x2B, 0x47, 0x75, 0x51, 0x34, 0x45, 0x48, 0x76,
0x78, 0x6E, 0x56, 0x58, 0x50, 0x35, 0x4F, 0x59, 0x7A, 0x44, 0x50, 0x36, 0x47, 0x6D, 0x79, 0x75,
0x43, 0x73, 0x7A, 0x76, 0x47, 0x6D, 0x4B, 0x4E, 0x54, 0x0D, 0x0A, 0x34, 0x2B, 0x2F, 0x4B, 0x78,
0x7A, 0x38, 0x32, 0x46, 0x48, 0x6A, 0x71, 0x79, 0x6B, 0x67, 0x41, 0x32, 0x41, 0x76, 0x44, 0x2F,
0x39, 0x6D, 0x4F, 0x4C, 0x53, 0x61, 0x4F, 0x68, 0x4B, 0x6E, 0x66, 0x73, 0x38, 0x43, 0x64, 0x58,
0x78, 0x2B, 0x30, 0x72, 0x33, 0x4A, 0x31, 0x78, 0x58, 0x4D, 0x55, 0x38, 0x30, 0x37, 0x64, 0x6A,
0x57, 0x42, 0x4C, 0x38, 0x4B, 0x42, 0x55, 0x67, 0x54, 0x54, 0x66, 0x0D, 0x0A, 0x62, 0x4A, 0x79,
0x50, 0x50, 0x6C, 0x6D, 0x30, 0x57, 0x6C, 0x66, 0x79, 0x37, 0x64, 0x79, 0x57, 0x55, 0x78, 0x54,
0x38, 0x50, 0x46, 0x6D, 0x6F, 0x58, 0x45, 0x52, 0x38, 0x71, 0x75, 0x36, 0x71, 0x68, 0x66, 0x6D,
0x77, 0x39, 0x6F, 0x63, 0x4C, 0x43, 0x70, 0x34, 0x6C, 0x61, 0x42, 0x75, 0x36, 0x66, 0x46, 0x63,
0x49, 0x34, 0x50, 0x6F, 0x3D, 0x0D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20,
0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D,
0x0D, 0x0A
};
//File: key.pem, Size: 1732
#define key_pem_len 1732
const uint8_t key_pem[] PROGMEM = {
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41,
0x54, 0x45, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0D, 0x0A, 0x4D, 0x49, 0x49,
0x45, 0x76, 0x67, 0x49, 0x42, 0x41, 0x44, 0x41, 0x4E, 0x42, 0x67, 0x6B, 0x71, 0x68, 0x6B, 0x69,
0x47, 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x53, 0x43, 0x42, 0x4B, 0x67,
0x77, 0x67, 0x67, 0x53, 0x6B, 0x41, 0x67, 0x45, 0x41, 0x41, 0x6F, 0x49, 0x42, 0x41, 0x51, 0x44,
0x48, 0x65, 0x53, 0x55, 0x4B, 0x31, 0x44, 0x57, 0x67, 0x73, 0x67, 0x71, 0x33, 0x0D, 0x0A, 0x31,
0x71, 0x31, 0x64, 0x46, 0x54, 0x31, 0x35, 0x75, 0x33, 0x4E, 0x57, 0x49, 0x4B, 0x46, 0x53, 0x4C,
0x6C, 0x31, 0x75, 0x79, 0x36, 0x37, 0x54, 0x49, 0x43, 0x74, 0x39, 0x32, 0x57, 0x38, 0x58, 0x54,
0x71, 0x43, 0x69, 0x44, 0x58, 0x50, 0x6D, 0x71, 0x38, 0x55, 0x7A, 0x5A, 0x59, 0x51, 0x49, 0x50,
0x55, 0x74, 0x6A, 0x34, 0x39, 0x6B, 0x58, 0x6D, 0x4F, 0x53, 0x6F, 0x4E, 0x6D, 0x6F, 0x4B, 0x0D,
0x0A, 0x65, 0x2F, 0x61, 0x58, 0x64, 0x50, 0x76, 0x37, 0x31, 0x75, 0x45, 0x34, 0x68, 0x71, 0x4A,
0x2F, 0x5A, 0x4F, 0x77, 0x52, 0x46, 0x50, 0x48, 0x73, 0x48, 0x51, 0x77, 0x7A, 0x76, 0x48, 0x58,
0x75, 0x79, 0x55, 0x57, 0x33, 0x4E, 0x74, 0x46, 0x53, 0x50, 0x41, 0x55, 0x64, 0x36, 0x30, 0x45,
0x57, 0x70, 0x62, 0x71, 0x4B, 0x65, 0x57, 0x63, 0x67, 0x77, 0x6D, 0x7A, 0x42, 0x33, 0x43, 0x6D,
0x56, 0x0D, 0x0A, 0x57, 0x76, 0x56, 0x36, 0x6F, 0x64, 0x6C, 0x69, 0x52, 0x42, 0x72, 0x4A, 0x62,
0x6C, 0x71, 0x56, 0x63, 0x6F, 0x4C, 0x54, 0x59, 0x32, 0x51, 0x4C, 0x47, 0x31, 0x46, 0x59, 0x75,
0x4E, 0x6F, 0x78, 0x39, 0x6F, 0x71, 0x61, 0x4C, 0x55, 0x6F, 0x6F, 0x58, 0x6C, 0x63, 0x74, 0x59,
0x59, 0x56, 0x53, 0x62, 0x6E, 0x48, 0x45, 0x51, 0x6C, 0x44, 0x49, 0x37, 0x71, 0x35, 0x41, 0x62,
0x2B, 0x56, 0x42, 0x0D, 0x0A, 0x47, 0x5A, 0x72, 0x4E, 0x35, 0x46, 0x41, 0x52, 0x6F, 0x51, 0x4D,
0x33, 0x37, 0x52, 0x78, 0x39, 0x58, 0x4E, 0x5A, 0x71, 0x56, 0x2B, 0x41, 0x4D, 0x30, 0x47, 0x54,
0x72, 0x73, 0x64, 0x6B, 0x6E, 0x6F, 0x7A, 0x6C, 0x6A, 0x48, 0x58, 0x72, 0x67, 0x54, 0x4E, 0x45,
0x6F, 0x56, 0x39, 0x45, 0x73, 0x66, 0x52, 0x64, 0x58, 0x62, 0x4F, 0x30, 0x47, 0x6A, 0x51, 0x31,
0x55, 0x37, 0x41, 0x62, 0x59, 0x0D, 0x0A, 0x4D, 0x49, 0x4C, 0x69, 0x61, 0x43, 0x78, 0x2B, 0x74,
0x58, 0x56, 0x58, 0x43, 0x65, 0x4E, 0x32, 0x58, 0x6B, 0x68, 0x6E, 0x75, 0x7A, 0x66, 0x61, 0x6C,
0x71, 0x6A, 0x37, 0x57, 0x55, 0x68, 0x2F, 0x6A, 0x6B, 0x54, 0x33, 0x53, 0x2F, 0x35, 0x69, 0x76,
0x31, 0x38, 0x54, 0x4A, 0x61, 0x71, 0x6B, 0x37, 0x6F, 0x30, 0x68, 0x2B, 0x47, 0x6E, 0x64, 0x61,
0x75, 0x71, 0x57, 0x32, 0x38, 0x43, 0x41, 0x0D, 0x0A, 0x6B, 0x2B, 0x69, 0x74, 0x56, 0x6B, 0x30,
0x4E, 0x41, 0x67, 0x4D, 0x42, 0x41, 0x41, 0x45, 0x43, 0x67, 0x67, 0x45, 0x42, 0x41, 0x4B, 0x64,
0x67, 0x73, 0x41, 0x52, 0x32, 0x4C, 0x4D, 0x6D, 0x36, 0x6D, 0x2B, 0x50, 0x47, 0x37, 0x35, 0x47,
0x37, 0x30, 0x4F, 0x6E, 0x6B, 0x73, 0x4E, 0x4D, 0x45, 0x62, 0x6C, 0x6C, 0x58, 0x4C, 0x6D, 0x39,
0x5A, 0x32, 0x6B, 0x69, 0x75, 0x4A, 0x64, 0x42, 0x6B, 0x0D, 0x0A, 0x4D, 0x79, 0x4F, 0x4C, 0x68,
0x70, 0x35, 0x30, 0x53, 0x31, 0x79, 0x7A, 0x70, 0x62, 0x57, 0x4B, 0x36, 0x55, 0x39, 0x41, 0x6E,
0x35, 0x49, 0x69, 0x44, 0x6E, 0x67, 0x4F, 0x67, 0x30, 0x2F, 0x69, 0x32, 0x50, 0x67, 0x54, 0x4C,
0x4D, 0x66, 0x4B, 0x45, 0x65, 0x63, 0x77, 0x56, 0x36, 0x76, 0x6D, 0x7A, 0x56, 0x4F, 0x34, 0x69,
0x35, 0x32, 0x4A, 0x78, 0x62, 0x59, 0x71, 0x61, 0x61, 0x4B, 0x2F, 0x0D, 0x0A, 0x38, 0x50, 0x56,
0x39, 0x54, 0x6C, 0x4A, 0x2B, 0x58, 0x51, 0x5A, 0x7A, 0x39, 0x70, 0x63, 0x30, 0x59, 0x46, 0x6C,
0x77, 0x6C, 0x79, 0x61, 0x36, 0x32, 0x42, 0x35, 0x56, 0x42, 0x6F, 0x31, 0x6C, 0x41, 0x44, 0x2B,
0x6A, 0x49, 0x33, 0x6A, 0x54, 0x64, 0x6D, 0x56, 0x4F, 0x59, 0x47, 0x76, 0x46, 0x66, 0x5A, 0x30,
0x4A, 0x69, 0x56, 0x31, 0x33, 0x65, 0x4F, 0x73, 0x59, 0x38, 0x52, 0x6C, 0x51, 0x0D, 0x0A, 0x42,
0x77, 0x47, 0x52, 0x2B, 0x38, 0x74, 0x67, 0x56, 0x7A, 0x53, 0x39, 0x55, 0x45, 0x71, 0x2F, 0x36,
0x2B, 0x50, 0x44, 0x35, 0x77, 0x75, 0x6C, 0x73, 0x79, 0x6F, 0x6A, 0x4D, 0x2B, 0x70, 0x39, 0x78,
0x2F, 0x72, 0x6D, 0x71, 0x36, 0x31, 0x6A, 0x63, 0x36, 0x6A, 0x63, 0x66, 0x7A, 0x71, 0x45, 0x75,
0x70, 0x7A, 0x55, 0x64, 0x69, 0x5A, 0x4D, 0x65, 0x45, 0x69, 0x75, 0x50, 0x7A, 0x4C, 0x71, 0x0D,
0x0A, 0x77, 0x5A, 0x64, 0x32, 0x65, 0x46, 0x68, 0x50, 0x51, 0x46, 0x43, 0x76, 0x64, 0x58, 0x6F,
0x55, 0x69, 0x61, 0x78, 0x53, 0x67, 0x4A, 0x2B, 0x47, 0x31, 0x54, 0x69, 0x6B, 0x44, 0x4D, 0x58,
0x61, 0x51, 0x58, 0x67, 0x55, 0x53, 0x41, 0x4D, 0x43, 0x65, 0x49, 0x42, 0x74, 0x53, 0x58, 0x4C,
0x41, 0x64, 0x4D, 0x65, 0x68, 0x70, 0x43, 0x75, 0x6C, 0x51, 0x35, 0x6C, 0x33, 0x41, 0x73, 0x38,
0x30, 0x0D, 0x0A, 0x77, 0x6B, 0x48, 0x64, 0x36, 0x72, 0x42, 0x46, 0x78, 0x6C, 0x71, 0x69, 0x74,
0x4D, 0x47, 0x4C, 0x4A, 0x30, 0x65, 0x4D, 0x47, 0x6F, 0x54, 0x58, 0x79, 0x67, 0x51, 0x77, 0x2F,
0x7A, 0x58, 0x4C, 0x6F, 0x5A, 0x62, 0x4C, 0x7A, 0x72, 0x6A, 0x66, 0x4F, 0x4D, 0x45, 0x43, 0x67,
0x59, 0x45, 0x41, 0x38, 0x30, 0x6A, 0x4E, 0x59, 0x75, 0x2F, 0x61, 0x41, 0x6B, 0x69, 0x52, 0x77,
0x4A, 0x72, 0x71, 0x0D, 0x0A, 0x74, 0x2F, 0x72, 0x6D, 0x7A, 0x30, 0x49, 0x74, 0x39, 0x33, 0x44,
0x36, 0x59, 0x6C, 0x50, 0x44, 0x32, 0x59, 0x51, 0x4B, 0x35, 0x75, 0x47, 0x52, 0x71, 0x7A, 0x76,
0x4F, 0x33, 0x6E, 0x6F, 0x6F, 0x54, 0x6B, 0x4E, 0x48, 0x65, 0x73, 0x35, 0x59, 0x54, 0x4A, 0x2B,
0x6F, 0x2B, 0x4F, 0x38, 0x61, 0x4E, 0x38, 0x61, 0x37, 0x61, 0x36, 0x73, 0x4F, 0x6B, 0x4E, 0x55,
0x66, 0x6B, 0x64, 0x64, 0x31, 0x0D, 0x0A, 0x74, 0x2B, 0x68, 0x52, 0x63, 0x63, 0x66, 0x70, 0x30,
0x6C, 0x58, 0x61, 0x61, 0x69, 0x4C, 0x77, 0x65, 0x64, 0x59, 0x67, 0x2F, 0x36, 0x2B, 0x38, 0x77,
0x64, 0x75, 0x6F, 0x47, 0x53, 0x34, 0x4C, 0x58, 0x68, 0x46, 0x52, 0x56, 0x2F, 0x47, 0x41, 0x32,
0x4B, 0x59, 0x64, 0x6C, 0x4E, 0x65, 0x74, 0x39, 0x4D, 0x6A, 0x76, 0x54, 0x6E, 0x35, 0x6C, 0x68,
0x52, 0x4B, 0x78, 0x6B, 0x44, 0x77, 0x45, 0x0D, 0x0A, 0x56, 0x36, 0x67, 0x34, 0x45, 0x2B, 0x74,
0x37, 0x59, 0x34, 0x73, 0x4F, 0x79, 0x32, 0x32, 0x74, 0x6D, 0x70, 0x54, 0x45, 0x37, 0x49, 0x30,
0x56, 0x66, 0x47, 0x73, 0x43, 0x67, 0x59, 0x45, 0x41, 0x30, 0x65, 0x59, 0x6A, 0x75, 0x74, 0x74,
0x74, 0x6A, 0x37, 0x6F, 0x70, 0x39, 0x35, 0x59, 0x76, 0x37, 0x2F, 0x57, 0x6F, 0x7A, 0x64, 0x77,
0x53, 0x48, 0x72, 0x6E, 0x4C, 0x79, 0x56, 0x32, 0x55, 0x0D, 0x0A, 0x79, 0x36, 0x52, 0x6F, 0x33,
0x79, 0x33, 0x66, 0x56, 0x71, 0x55, 0x49, 0x6A, 0x61, 0x66, 0x74, 0x4F, 0x65, 0x78, 0x41, 0x6E,
0x6E, 0x49, 0x4E, 0x53, 0x5A, 0x62, 0x56, 0x6D, 0x69, 0x6B, 0x38, 0x63, 0x51, 0x64, 0x44, 0x70,
0x49, 0x6B, 0x47, 0x46, 0x63, 0x51, 0x4C, 0x78, 0x6F, 0x69, 0x66, 0x38, 0x4A, 0x67, 0x68, 0x6D,
0x77, 0x79, 0x4A, 0x43, 0x6E, 0x53, 0x4C, 0x34, 0x58, 0x33, 0x73, 0x0D, 0x0A, 0x55, 0x6E, 0x50,
0x2B, 0x59, 0x58, 0x39, 0x57, 0x76, 0x2B, 0x67, 0x34, 0x63, 0x4B, 0x53, 0x58, 0x51, 0x48, 0x57,
0x30, 0x65, 0x4F, 0x32, 0x6E, 0x33, 0x63, 0x49, 0x71, 0x63, 0x50, 0x33, 0x4C, 0x33, 0x70, 0x67,
0x6F, 0x42, 0x31, 0x42, 0x68, 0x43, 0x77, 0x41, 0x71, 0x56, 0x6F, 0x65, 0x56, 0x42, 0x39, 0x49,
0x58, 0x71, 0x6A, 0x66, 0x33, 0x6F, 0x55, 0x51, 0x6F, 0x48, 0x63, 0x6E, 0x64, 0x0D, 0x0A, 0x68,
0x63, 0x55, 0x70, 0x35, 0x31, 0x4C, 0x44, 0x4F, 0x6D, 0x63, 0x43, 0x67, 0x59, 0x42, 0x33, 0x6C,
0x49, 0x42, 0x48, 0x73, 0x69, 0x64, 0x52, 0x57, 0x67, 0x51, 0x79, 0x54, 0x4C, 0x4D, 0x30, 0x62,
0x57, 0x49, 0x48, 0x32, 0x37, 0x71, 0x6D, 0x56, 0x6D, 0x54, 0x72, 0x38, 0x63, 0x70, 0x68, 0x58,
0x78, 0x4C, 0x51, 0x75, 0x32, 0x30, 0x54, 0x59, 0x70, 0x6C, 0x6A, 0x4A, 0x31, 0x34, 0x71, 0x0D,
0x0A, 0x59, 0x63, 0x31, 0x2F, 0x6F, 0x6D, 0x38, 0x71, 0x63, 0x6F, 0x76, 0x6D, 0x6C, 0x2F, 0x67,
0x6D, 0x46, 0x45, 0x76, 0x4A, 0x4E, 0x31, 0x49, 0x34, 0x68, 0x46, 0x6B, 0x35, 0x49, 0x56, 0x4F,
0x65, 0x61, 0x74, 0x57, 0x6C, 0x66, 0x4B, 0x4D, 0x45, 0x6E, 0x4F, 0x68, 0x33, 0x70, 0x4F, 0x62,
0x59, 0x72, 0x42, 0x53, 0x61, 0x4F, 0x50, 0x49, 0x70, 0x77, 0x32, 0x4D, 0x54, 0x78, 0x56, 0x73,
0x58, 0x0D, 0x0A, 0x43, 0x4B, 0x49, 0x58, 0x2F, 0x50, 0x2F, 0x63, 0x2F, 0x59, 0x47, 0x71, 0x79,
0x48, 0x79, 0x62, 0x78, 0x69, 0x78, 0x43, 0x51, 0x38, 0x52, 0x34, 0x72, 0x57, 0x43, 0x54, 0x79,
0x62, 0x78, 0x45, 0x72, 0x63, 0x32, 0x71, 0x6C, 0x4F, 0x4F, 0x59, 0x59, 0x53, 0x43, 0x4C, 0x67,
0x30, 0x53, 0x43, 0x70, 0x37, 0x39, 0x48, 0x6D, 0x6D, 0x31, 0x47, 0x4D, 0x77, 0x4B, 0x42, 0x67,
0x51, 0x44, 0x41, 0x0D, 0x0A, 0x70, 0x55, 0x38, 0x70, 0x30, 0x76, 0x63, 0x74, 0x73, 0x52, 0x6D,
0x50, 0x69, 0x61, 0x4B, 0x5A, 0x78, 0x49, 0x69, 0x58, 0x32, 0x47, 0x49, 0x78, 0x48, 0x53, 0x6D,
0x79, 0x75, 0x56, 0x63, 0x31, 0x79, 0x4D, 0x49, 0x51, 0x42, 0x46, 0x51, 0x31, 0x65, 0x2B, 0x62,
0x72, 0x7A, 0x4C, 0x4A, 0x6F, 0x4E, 0x54, 0x50, 0x68, 0x75, 0x4B, 0x41, 0x46, 0x61, 0x4A, 0x44,
0x48, 0x6D, 0x77, 0x31, 0x45, 0x0D, 0x0A, 0x56, 0x2F, 0x78, 0x33, 0x61, 0x4F, 0x50, 0x53, 0x32,
0x6B, 0x70, 0x4D, 0x66, 0x31, 0x7A, 0x38, 0x50, 0x30, 0x53, 0x6F, 0x76, 0x38, 0x71, 0x50, 0x64,
0x41, 0x52, 0x6D, 0x67, 0x32, 0x4B, 0x42, 0x45, 0x73, 0x44, 0x6F, 0x7A, 0x79, 0x7A, 0x5A, 0x6D,
0x64, 0x4D, 0x6A, 0x58, 0x34, 0x30, 0x4B, 0x74, 0x6E, 0x31, 0x62, 0x65, 0x42, 0x52, 0x58, 0x37,
0x6A, 0x74, 0x78, 0x31, 0x52, 0x35, 0x6D, 0x0D, 0x0A, 0x51, 0x4A, 0x53, 0x4D, 0x6B, 0x66, 0x4E,
0x66, 0x57, 0x31, 0x6D, 0x41, 0x62, 0x5A, 0x51, 0x55, 0x78, 0x33, 0x69, 0x38, 0x32, 0x2F, 0x31,
0x46, 0x58, 0x2F, 0x37, 0x4A, 0x79, 0x46, 0x71, 0x75, 0x79, 0x68, 0x54, 0x35, 0x6F, 0x50, 0x5A,
0x61, 0x42, 0x51, 0x4B, 0x42, 0x67, 0x47, 0x77, 0x44, 0x79, 0x64, 0x35, 0x34, 0x36, 0x58, 0x62,
0x77, 0x4D, 0x65, 0x33, 0x77, 0x43, 0x31, 0x69, 0x5A, 0x0D, 0x0A, 0x42, 0x4A, 0x76, 0x70, 0x6C,
0x68, 0x4A, 0x53, 0x6E, 0x43, 0x34, 0x32, 0x50, 0x61, 0x70, 0x4F, 0x4F, 0x58, 0x46, 0x55, 0x6B,
0x63, 0x67, 0x32, 0x63, 0x63, 0x65, 0x37, 0x69, 0x59, 0x50, 0x6B, 0x37, 0x5A, 0x50, 0x4F, 0x67,
0x57, 0x72, 0x57, 0x54, 0x70, 0x39, 0x53, 0x52, 0x41, 0x68, 0x49, 0x45, 0x54, 0x66, 0x75, 0x41,
0x6E, 0x67, 0x47, 0x38, 0x57, 0x71, 0x52, 0x36, 0x37, 0x71, 0x63, 0x0D, 0x0A, 0x7A, 0x4C, 0x44,
0x52, 0x42, 0x51, 0x67, 0x42, 0x73, 0x66, 0x30, 0x71, 0x41, 0x2B, 0x50, 0x6C, 0x66, 0x66, 0x39,
0x45, 0x62, 0x63, 0x31, 0x71, 0x7A, 0x6A, 0x31, 0x79, 0x51, 0x32, 0x64, 0x53, 0x31, 0x61, 0x76,
0x72, 0x55, 0x75, 0x72, 0x78, 0x4F, 0x4F, 0x5A, 0x55, 0x63, 0x53, 0x7A, 0x71, 0x57, 0x56, 0x4C,
0x5A, 0x2F, 0x7A, 0x33, 0x4F, 0x4E, 0x32, 0x4E, 0x4E, 0x45, 0x58, 0x76, 0x53, 0x0D, 0x0A, 0x67,
0x51, 0x32, 0x70, 0x62, 0x4A, 0x7A, 0x61, 0x75, 0x78, 0x4F, 0x71, 0x59, 0x57, 0x56, 0x74, 0x36,
0x41, 0x7A, 0x6F, 0x74, 0x7A, 0x6B, 0x4E, 0x0D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E,
0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D,
0x2D, 0x2D, 0x0D, 0x0A
};

// The HTTPS Server comes in a separate namespace. For easier use, include it here.
using namespace httpsserver;

// Create an SSL certificate object from the files included above
SSLCert cert = SSLCert(
    (unsigned char *)cert_pem, cert_pem_len,
    (unsigned char *)key_pem, key_pem_len
);

// Create an SSL-enabled server that uses the certificate
// The contstructor takes some more parameters, but we go for default values here.
HTTPSServer secureServer = HTTPSServer(&cert);

void handleRoot(HTTPRequest * req, HTTPResponse * res);
void handleFormUpload(HTTPRequest * req, HTTPResponse * res);
void handle404(HTTPRequest * req, HTTPResponse * res);

void setup() {
  // For logging
  Serial.begin(115200);
  // Connect to WiFi
  Serial.println("Setting up WiFi");
  WiFi.begin(WIFI_SSID, WIFI_PSK);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.print("Connected. IP=");
  Serial.println(WiFi.localIP());

  ResourceNode * nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
  ResourceNode * nodeForm = new ResourceNode("/upload", "GET", &handleRoot);

  // 404 node has no URL as it is used for all requests that don't match anything else
  ResourceNode * node404  = new ResourceNode("", "GET", &handle404);

  // Add all nodes to the server so they become accessible:
  secureServer.registerNode(nodeForm);
  secureServer.registerNode(nodeFormUpload);
  secureServer.setDefaultNode(node404);

  Serial.println("Starting server...");
  secureServer.start();
  if (secureServer.isRunning()) {
    Serial.println("Server ready.");
  }
}

void loop() {
  // This call will let the server do its work
  secureServer.loop();
  delay(1);
}

static const char serverIndex[] PROGMEM =
R"(<!DOCTYPE html>
     <html lang='en'>
     <head>
         <meta charset='utf-8'>
         <meta name='viewport' content='width=device-width,initial-scale=1'/>
     </head>
     <body>
     <form method='POST' action='' enctype='multipart/form-data'>
         Firmware:<br>
         <input type='file' accept='.bin,.bin.gz' name='firmware'>
         <input type='submit' value='Update Firmware'>
     </form>
     <form method='POST' action='' enctype='multipart/form-data'>
         FileSystem:<br>
         <input type='file' accept='.bin,.bin.gz,.image' name='filesystem'>
         <input type='submit' value='Update FileSystem'>
     </form>
     </body>
     </html>)";
static const char successResponse[] PROGMEM =
"<p>Update Success! Reset device and restart webpage.</p>";

void handleRoot(HTTPRequest * req, HTTPResponse * res) {
  res->setHeader("Content-Type", "text/html");

  // Just the regular HTML document structure, nothing special to forms here....
  res->println(serverIndex);
}

void handleFormUpload(HTTPRequest * req, HTTPResponse * res) {
  HTTPBodyParser *parser;
  std::string contentType = req->getHeader("Content-Type");

  size_t semicolonPos = contentType.find(";");
  if (semicolonPos != std::string::npos) {
    contentType = contentType.substr(0, semicolonPos);
  }

  // Now, we can decide based on the content type:
  if (contentType == "multipart/form-data") {
    parser = new HTTPMultipartBodyParser(req);
  } else {
    Serial.printf("Unknown POST Content-Type: %s\n", contentType.c_str());
    return;
  }

  while(parser->nextField()) {
    std::string name = parser->getFieldName();
    std::string filename = parser->getFieldFilename();
    std::string mimeType = parser->getFieldMimeType();

    Serial.printf("handleFormUpload: field name='%s', filename='%s', mimetype='%s'\n", name.c_str(), filename.c_str(), mimeType.c_str());

    if (name != "firmware" && name != "filesystem" ) {
      Serial.println("Skipping unexpected field");
      break;
    }

    if (name == "filesystem") {
      Serial.println(F("Filesystem update found!"));
        if (!Update.begin(SPIFFS.totalBytes(), U_SPIFFS)) {//start with max available size
            Update.printError(Serial);
        }
    }
    else {
      Serial.println(F("Firmware update found!"));
        uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
        if (!Update.begin(maxSketchSpace, U_FLASH)) {//start with max available size
          Update.printError(Serial);
        }
    }

  Serial.println(F("Starting update!"));
    size_t fileLength = 0;

  while (!parser->endOfField()) {
    byte buf[512];
    size_t readLength = parser->read(buf, 512);

    if (Update.write(buf, readLength) != readLength) {
      Update.printError(Serial);
    }

    fileLength += readLength;

    Serial.printf("Upload and updated %u byte\n", fileLength);
  }
    if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\n", fileLength);
        // res->println("<p>Update Success: \nRebooting...\n</p>");
        res->println(successResponse);
    }
    else {
      Update.printError(Serial);
      res->println(Update.getError());
    }
  }

  delete parser;
}

void handle404(HTTPRequest * req, HTTPResponse * res) {
  req->discardRequestBody();
  res->setStatusCode(404);
  res->setStatusText("Not Found");
  res->setHeader("Content-Type", "text/html");
  res->println("<!DOCTYPE html>");
  res->println("<html>");
  res->println("<head><title>Not Found</title></head>");
  res->println("<body><h1>404 Not Found</h1><p>The requested resource was not found on this server.</p></body>");
  res->println("</html>");
}

In this sketch, we create the certificate with cert and key content.

// Create an SSL certificate object from the files above
SSLCert cert = SSLCert(
    (unsigned char *)cert_pem, cert_pem_len,
    (unsigned char *)key_pem, key_pem_len
);

// Create an SSL-enabled server that uses the certificate
// The contstructor takes some more parameters, but we go for default values here.
HTTPSServer secureServer = HTTPSServer(&cert);

And we are going to publish the upload endpoint in GET for the page and in POST to upload the firmware.

  ResourceNode * nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
  ResourceNode * nodeForm = new ResourceNode("/upload", "GET", &handleRoot);

  // 404 node has no URL as it is used for all requests that don't match anything else
  ResourceNode * node404  = new ResourceNode("", "GET", &handle404);

  // Add all nodes to the server so they become accessible:
  secureServer.registerNode(nodeForm);
  secureServer.registerNode(nodeFormUpload);
  secureServer.setDefaultNode(node404);

I reuse the page of HTTPUpdateServer.

static const char serverIndex[] PROGMEM =
R"(<!DOCTYPE html>
     <html lang='en'>
     <head>
         <meta charset='utf-8'>
         <meta name='viewport' content='width=device-width,initial-scale=1'/>
     </head>
     <body>
     <form method='POST' action='' enctype='multipart/form-data'>
         Firmware:<br>
         <input type='file' accept='.bin,.bin.gz' name='firmware'>
         <input type='submit' value='Update Firmware'>
     </form>
     <form method='POST' action='' enctype='multipart/form-data'>
         FileSystem:<br>
         <input type='file' accept='.bin,.bin.gz,.image' name='filesystem'>
         <input type='submit' value='Update FileSystem'>
     </form>
     </body>
     </html>)";

Next, when we receive the POST form data, we are going to check if It’s firmware or filesystem and call the begin of the Update class with the correct parameter U_FLASH or U_FS.

    if (name == "filesystem") {
      Serial.println(F("Filesystem update found!"));
        if (!Update.begin(SPIFFS.totalBytes(), U_SPIFFS)) {//start with max available size
            Update.printError(Serial);
        }
    }
    else {
      Serial.println(F("Firmware update found!"));
        uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
        if (!Update.begin(maxSketchSpace, U_FLASH)) {//start with max available size
          Update.printError(Serial);
        }
    }

When the stream start, I’m going to use Update.write

  while (!parser->endOfField()) {
    byte buf[512];
    size_t readLength = parser->read(buf, 512);

    if (Update.write(buf, readLength) != readLength) {
      Update.printError(Serial);
    }

    fileLength += readLength;

    Serial.printf("Upload and updated %u byte\n", fileLength);
  }

And finally, the end

    if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\n", fileLength);
        // res->println("<p>Update Success: \nRebooting...\n</p>");
        res->println(successResponse);
    }
    else {
      Update.printError(Serial);
      res->println(Update.getError());
    }

With a self-signed certificate without a real certification authority, when you try to open the browser sends you a message to inform you of potential risk, but the connection is encrypted.

self signed certificate on Web Arduino OTA browser message
self-signed certificate on Web Arduino OTA browser message

Thanks

  1. ESP32: pinout, specs and Arduino IDE configuration
  2. ESP32: integrated SPIFFS Filesystem
  3. ESP32: manage multiple Serial and logging
  4. ESP32 practical power saving
    1. ESP32 practical power saving: manage WiFi and CPU
    2. ESP32 practical power saving: modem and light sleep
    3. ESP32 practical power saving: deep sleep and hibernation
    4. ESP32 practical power saving: preserve data, timer and touch wake up
    5. ESP32 practical power saving: external and ULP wake up
    6. ESP32 practical power saving: UART and GPIO wake up
  5. ESP32: integrated LittleFS FileSystem
  6. ESP32: integrated FFat (Fat/exFAT) FileSystem
  7. ESP32-wroom-32
    1. ESP32-wroom-32: flash, pinout, specs and IDE configuration
  8. ESP32-CAM
    1. ESP32-CAM: pinout, specs and Arduino IDE configuration
    2. ESP32-CAM: upgrade CamerWebServer with flash features
  9. ESP32: use ethernet w5500 with plain (HTTP) and SSL (HTTPS)
  10. ESP32: use ethernet enc28j60 with plain (HTTP) and SSL (HTTPS)
  11. How to use SD card with esp32
  12. esp32 and esp8266: FAT filesystem on external SPI flash memory
  1. Firmware and OTA update management
    1. Firmware management
      1. ESP32: flash compiled firmware (.bin)
      2. ESP32: flash compiled firmware and filesystem (.bin) with GUI tools
    2. OTA update with Arduino IDE
      1. ESP32 OTA update with Arduino IDE: filesystem, firmware, and password
    3. OTA update with Web Browser
      1. ESP32 OTA update with Web Browser: firmware, filesystem, and authentication
      2. ESP32 OTA update with Web Browser: upload in HTTPS (SSL/TLS) with self-signed certificate
      3. ESP32 OTA update with Web Browser: custom web interface
    4. Self OTA uptate from HTTP server
      1. ESP32 self OTA update firmware from the server
      2. ESP32 self OTA update firmware from the server with version check
      3. ESP32 self-OTA update in HTTPS (SSL/TLS) with trusted self-signed certificate
    5. Non-standard Firmware update
      1. ESP32 firmware and filesystem update from SD card
      2. ESP32 firmware and filesystem update with FTP client
  1. Integrating LAN8720 with ESP32 for Ethernet Connectivity with plain (HTTP) and SSL (HTTPS)

Spread the love

Leave a Reply

Your email address will not be published. Required fields are marked *