Skip to content

Instantly share code, notes, and snippets.

@brennanMKE
Last active January 29, 2024 21:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brennanMKE/2512e940b48d315f46def9e65d8639fd to your computer and use it in GitHub Desktop.
Save brennanMKE/2512e940b48d315f46def9e65d8639fd to your computer and use it in GitHub Desktop.
OTA support for ESP32

OTA support for ESP32

Please show me code which would check a server which has the new firmware along with a checksum which is then downloaded, verified and installed with fallback support. Ideally a single JSON file would list the latest release to install related to hardware models. (sent to ChatGPT)


Implementing OTA updates on an ESP32 that checks a server for new firmware, along with a checksum for verification, and then downloads, verifies, and installs it with fallback support, involves several steps. I'll provide a simplified example in Arduino code. This example assumes you have a server hosting a JSON file indicating the latest firmware and its checksum, and the firmware binary itself.

Assumptions:

  • A server hosts the firmware binary and a JSON file.
  • The JSON file contains fields like version, hardware_model, url (firmware download URL), and checksum.
  • The ESP32 is connected to a Wi-Fi network.
  • The ESP32 has an appropriate partition scheme for OTA updates.

Example Code:

First, include the necessary libraries and define constants for WiFi credentials and server URLs.

#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClient.h>
#include <Update.h>

const char* ssid = "yourSSID";
const char* password = "yourPASSWORD";

const char* info_url = "http://yourserver.com/firmware_info.json"; // URL to the JSON file
const char* firmware_version = "1.0"; // Current firmware version

Next, define a function to connect to WiFi:

void connectToWiFi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");
}

Define a function to parse the JSON file from the server and check if an update is required:

bool checkForUpdates() {
  HTTPClient http;
  http.begin(info_url);
  int httpCode = http.GET();

  if (httpCode == 200) {
    String payload = http.getString();
    // Parse the JSON and extract information
    // For simplicity, pseudo-code is used here
    String new_version = parseJSON(payload, "version");
    String model = parseJSON(payload, "hardware_model");
    String url = parseJSON(payload, "url");
    String checksum = parseJSON(payload, "checksum");

    if (new_version != firmware_version && model == "YourModel") {
      return updateFirmware(url, checksum);
    }
  } else {
    Serial.println("Error fetching update info");
  }
  http.end();
  return false;
}

Define the function to update the firmware:

bool updateFirmware(String url, String expected_checksum) {
  HTTPClient http;
  http.begin(url);
  int httpCode = http.GET();

  if (httpCode == 200) {
    int contentLength = http.getSize();
    WiFiClient &client = http.getStream();

    if (Update.begin(contentLength)) {
      size_t written = Update.writeStream(client);
      if (written == contentLength) {
        Serial.println("Written : " + String(written) + " successfully");
      } else {
        Serial.println("Write failed. Written only : " + String(written));
        return false;
      }

      if (Update.end()) {
        if (Update.verifyChecksum(expected_checksum.c_str())) {
          Serial.println("OTA done!");
          if (Update.isFinished()) {
            Serial.println("Update successfully completed. Rebooting.");
            ESP.restart();
            return true;
          } else {
            Serial.println("Update not finished? Something went wrong!");
          }
        } else {
          Serial.println("Checksum mismatch. Update failed.");
        }
      } else {
        Serial.println("Error Occurred. Error #: " + String(Update.getError()));
      }
    } else {
      Serial.println("Not enough space to begin OTA");
    }
  } else {
    Serial.println("Failed to fetch firmware");
  }
  http.end();
  return false;
}

Finally, in your setup() and loop() functions, you would call these functions appropriately:

void setup() {
  Serial.begin(115200);
  connectToWiFi();
  if (checkForUpdates()) {
    Serial.println("Update successful");
  } else {
    Serial.println("No update required or update failed");
  }
}

void loop() {
  // Your regular code here
}

Important Notes:

  • Error Handling: This code needs more robust error handling for production use.
  • JSON Parsing: You'll need a JSON parsing library like ArduinoJson to parse the JSON response.
  • Security: Consider using HTTPS for your server communication and validate the server's SSL certificate.
  • Checksum Verification: The checksum verification part is simplified; ensure you use an appropriate method to verify the integrity of the downloaded firmware.
  • Testing: Always thoroughly test OTA update mechanisms to prevent bricking devices.

This example provides a starting point, but real-world applications may require more detailed implementation, especially regarding error handling and security.


Please show me how to validate the SSL certificate. Can pinning be used?


Validating the SSL certificate and implementing certificate pinning are crucial for securing HTTPS connections, especially in scenarios like OTA (Over-The-Air) updates for ESP32 devices. This ensures that the ESP32 is communicating with the genuine server and not an imposter, thus preventing Man-In-The-Middle (MITM) attacks.

Here's how you can validate the SSL certificate and implement certificate pinning in your ESP32 project:

1. Obtain the Server's SSL Certificate or Public Key

First, you need to obtain the SSL certificate or the public key of your server. This can typically be done using a browser or command-line tools like openssl.

For example, using openssl:

openssl s_client -showcerts -connect yourserver.com:443 </dev/null

This command will print the server's certificate. You can either:

  • Use the entire certificate (beginning with -----BEGIN CERTIFICATE----- and ending with -----END CERTIFICATE-----).
  • Extract the public key from the certificate.

2. Convert the Certificate to DER Format

ESP32 requires the certificate in DER format. You can convert it using openssl:

openssl x509 -in mycert.pem -outform der -out mycert.der

3. Load the Certificate in Your ESP32 Code

If Using the Full Certificate:

You can include the certificate in your Arduino sketch. It's common to convert it to a C byte array. Tools like xxd can be used for this conversion:

xxd -i mycert.der > mycert.h

Then, include mycert.h in your sketch and use it for SSL verification.

If Using Public Key Pinning:

Extract the public key in a format like Base64 and hardcode it in your Arduino sketch.

4. Implementing SSL Verification in Your ESP32 Code

Here's an example using the WiFiClientSecure library, which supports SSL:

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "mycert.h" // Include the certificate header file

WiFiClientSecure client;

void setup() {
  WiFi.begin(ssid, password);

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }

  // Use the CA cert
  client.setCACert(mycert); // If using the full certificate

  // OR use public key pinning
  // client.setFingerprint(fingerprint); // Where fingerprint is the SHA-1 fingerprint of the certificate

  // Proceed with making a secure request
}

void loop() {
  // Your code for making secure requests
}

Security Considerations

  • Certificate Expiry: Be aware that SSL certificates expire. You'll need to update your ESP32 firmware if the server's certificate changes.
  • Certificate Revocation: There's no easy way to handle certificate revocation checks in this setup.
  • Public Key Pinning: Pinning the public key can be more flexible than pinning the entire certificate, as it doesn't change with every certificate renewal. However, it's less secure against certain advanced attacks.

Certificate pinning enhances security but must be implemented with care. It can make your system more robust against certain types of attacks but can also lead to challenges in managing and updating certificates. Always test thoroughly to ensure reliability and security in your application.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment