Created
April 12, 2025 01:14
-
-
Save SebastianBitsch/567e03edc98bc6ccd1bfec8419d0b30e to your computer and use it in GitHub Desktop.
Update ESP boards over-the-air
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* ESP32 HTTP OTA Update Example | |
* This sketch demonstrates how to check for and download firmware updates | |
* from an HTTP server. | |
*/ | |
#include <WiFi.h> | |
#include <HTTPClient.h> | |
#include <HTTPUpdate.h> | |
#define LED_BUILTIN 2 | |
// WiFi credentials | |
const char* ssid = "..."; | |
const char* password = "..."; | |
// Device information | |
const char* DEVICE_TYPE = "esp32"; | |
const char* FIRMWARE_VERSION = "1.0.1"; | |
// Update server details | |
const char* API_SERVER = "http://...:5001"; | |
const char* API_KEY = "..."; | |
const unsigned long CHECK_INTERVAL = 24 * 60 * 60 * 1000; // Once per day, ms | |
unsigned long lastCheckTime = 0; | |
void setup() { | |
Serial.begin(115200); | |
Serial.println("\n\n--------------------------"); | |
Serial.println("ESP32 OTA Update Client"); | |
Serial.println("--------------------------"); | |
Serial.print("Current firmware version: "); | |
Serial.println(FIRMWARE_VERSION); | |
// Connect to WiFi | |
WiFi.mode(WIFI_STA); | |
WiFi.begin(ssid, password); | |
Serial.print("Connecting to WiFi"); | |
while (WiFi.status() != WL_CONNECTED) { | |
delay(500); | |
Serial.print("."); | |
} | |
Serial.println(); | |
Serial.print("Connected to WiFi. IP address: "); | |
Serial.println(WiFi.localIP()); | |
// Check for updates on startup | |
checkForUpdates(); | |
// Your regular setup code here | |
// .. | |
pinMode(LED_BUILTIN, OUTPUT); | |
} | |
void loop() { | |
// Periodically check for updates | |
if (millis() - lastCheckTime > CHECK_INTERVAL || lastCheckTime == 0) { | |
if (WiFi.status() == WL_CONNECTED) { | |
checkForUpdates(); | |
} | |
lastCheckTime = millis(); | |
} | |
// Your regular code here | |
// .. | |
// Print status periodically | |
static unsigned long lastMsg = 0; | |
if (millis() - lastMsg > 10000) { // Every 10 seconds | |
lastMsg = millis(); | |
Serial.printf("Device running firmware v%s, IP: %s\n", FIRMWARE_VERSION, WiFi.localIP().toString().c_str()); | |
} | |
} | |
void checkForUpdates() { | |
Serial.println("Checking for firmware updates..."); | |
if (WiFi.status() != WL_CONNECTED) { | |
Serial.println("WiFi not connected. Skipping update check."); | |
return; | |
} | |
HTTPClient http; | |
// Prepare URL with query parameters | |
String url = String(API_SERVER) + "/firmware/version?device_type=" + DEVICE_TYPE + "¤t_version=" + FIRMWARE_VERSION; | |
Serial.print("Connecting to: "); | |
Serial.println(url); | |
http.begin(url); | |
http.addHeader("X-API-Key", API_KEY); | |
// Send GET request | |
int httpCode = http.GET(); | |
if (httpCode == HTTP_CODE_OK) { | |
String payload = http.getString(); | |
Serial.println("Response: " + payload); | |
// Manual JSON parsing to avoid dependency on ArduinoJson | |
// Looking for "update_available":true | |
bool updateAvailable = (payload.indexOf("\"update_available\":true") > -1); | |
if (updateAvailable) { | |
// Extract version - simple method, assumes format like "latest_version":"1.0.1" | |
int verStart = payload.indexOf("\"latest_version\":\"") + 18; // Length of "latest_version":" | |
int verEnd = payload.indexOf("\"", verStart); | |
String latestVersion = ""; | |
if (verStart > 18 && verEnd > verStart) { | |
latestVersion = payload.substring(verStart, verEnd); | |
Serial.println("New version available: " + latestVersion); | |
Serial.println("Current version: " + String(FIRMWARE_VERSION)); | |
// Download and install update | |
updateFirmware(latestVersion); | |
} else { | |
Serial.println("Found update but couldn't parse version"); | |
} | |
} else { | |
Serial.println("Firmware is up to date!"); | |
} | |
} else { | |
Serial.print("Failed to check version, HTTP error code: "); | |
Serial.println(httpCode); | |
Serial.println(http.errorToString(httpCode)); | |
} | |
http.end(); | |
} | |
// Custom function to handle firmware updates with the API key properly included | |
void updateFirmware(String version) { | |
Serial.println("Starting firmware update..."); | |
// Prepare URL for firmware download | |
String url = String(API_SERVER) + "/firmware/download?device_type=" + DEVICE_TYPE + "&version=" + version; | |
Serial.print("Downloading from: "); | |
Serial.println(url); | |
// Create HTTP client | |
HTTPClient http; | |
http.begin(url); | |
// Add API key header | |
http.addHeader("X-API-Key", API_KEY); | |
// Start the request | |
int httpCode = http.GET(); | |
if (httpCode != HTTP_CODE_OK) { | |
Serial.printf("HTTP GET failed, error: %d, %s\n", httpCode, http.errorToString(httpCode).c_str()); | |
http.end(); | |
return; | |
} | |
// Get content length for progress tracking | |
int contentLength = http.getSize(); | |
if (contentLength <= 0) { | |
Serial.println("Error: Content length is invalid"); | |
http.end(); | |
return; | |
} | |
Serial.printf("Update size: %d bytes\n", contentLength); | |
// Check if we have enough space | |
if (!Update.begin(contentLength)) { | |
Serial.println("Error: Not enough space for update"); | |
http.end(); | |
return; | |
} | |
// Create buffer for reading data | |
uint8_t buff[1024] = { 0 }; | |
size_t bytesRead = 0; | |
size_t totalRead = 0; | |
// Get tcp stream | |
WiFiClient * stream = http.getStreamPtr(); | |
// Read all data from server and write it to the update partition | |
Serial.println("Downloading firmware..."); | |
// Turn on LED to indicate update in progress | |
digitalWrite(LED_BUILTIN, HIGH); | |
// Read data and write it to the Update object | |
while (http.connected() && (totalRead < contentLength)) { | |
// Read data from HTTP stream | |
size_t avail = stream->available(); | |
if (avail) { | |
bytesRead = stream->readBytes(buff, min(avail, sizeof(buff))); | |
// Write data to the update partition | |
if (Update.write(buff, bytesRead) != bytesRead) { | |
Serial.println("Error: Update write failed"); | |
http.end(); | |
Update.abort(); | |
return; | |
} | |
totalRead += bytesRead; | |
// Print progress | |
int progress = (totalRead * 100) / contentLength; | |
static int lastProgress = 0; | |
if (progress % 10 == 0 && progress != lastProgress) { | |
Serial.printf("Progress: %d%%\n", progress); | |
lastProgress = progress; | |
// Blink LED for visual feedback | |
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); | |
} | |
} | |
delay(1); // Small delay to prevent WDT issues | |
} | |
// Check if we got all data | |
if (totalRead != contentLength) { | |
Serial.println("Error: Size mismatch"); | |
http.end(); | |
Update.abort(); | |
return; | |
} | |
// End the update | |
if (!Update.end()) { | |
Serial.printf("Error: Update end failed! (%d)\n", Update.getError()); | |
http.end(); | |
return; | |
} | |
// Cleanup | |
http.end(); | |
Serial.println("Update successful!"); | |
Serial.println("Rebooting..."); | |
delay(1000); | |
ESP.restart(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment