GITHUB OTA for ESP32 devices¶
Automate your OTA and CI/CD pipeline with Github Actions to update your ESP32 devices in the field direct from github releases
Features¶
Uses the esp_htps_ota library under the hood to update firmware images
Can also update spiffs/littlefs/fatfs partitions
Uses SemVer to compare versions and only update if a newer version is available
Plays nicely with App rollback and anti-rollback features of the esp-idf bootloader
Download firmware and partitiion images from the github release page directly
Supports multiple devices with different firmware images
Includes a sample Github Actions that builds and releases images when a new tag is pushed
Updates can be triggered manually, or via a interval timer
Uses a streaming JSON parser for to reduce memory usage (Github API responses can be huge)
Supports Private Repositories (Github API token required*)
Supports Github Enterprise
Supports Github Personal Access Tokens to overcome Github API Ratelimits
Sends progress of Updates via the esp_event_loop
Note: You should be careful with your GitHub PAT and putting it in the source code. I would suggest that you store the PAT in NVS, and the user enters it when running, as otherwise the PAT would be easily extractable from your firmware images.
Usage¶
via the Espressif Component Registry:
idf.py add-dependency Fishwaldo/ghota^1.0.0
Example¶
After Initilizing Network Access, Start a timer to periodically check for new releases:
ghota_config_t ghconfig = {
.filenamematch = "GithubOTA-esp32.bin", // Glob Pattern to match against the Firmware file
.storagenamematch = "storage-esp32.bin", // Glob Pattern to match against the storage firmware file
.storagepartitionname = "storage", // Update the storage partition
.updateInterval = 60, // Check for updates every 60 minuites
};
ghota_client_handle_t *ghota_client = ghota_init(&ghconfig);
if (ghota_client == NULL) {
ESP_LOGE(TAG, "ghota_client_init failed");
return;
}
esp_event_handler_register(GHOTA_EVENTS, ESP_EVENT_ANY_ID, &ghota_event_callback, ghota_client); // Register a handler to get updates on progress
ESP_ERROR_CHECK(ghota_start_update_timer(ghota_client)); // Start the timer to check for updates
Manually Checking for updates:
ghota_config_t ghconfig = {
.filenamematch = "GithubOTA-esp32.bin",
.storagenamematch = "storage-esp32.bin",
.storagepartitionname = "storage",
.updateInterval = 60,
};
ghota_client_handle_t *ghota_client = ghota_init(&ghconfig);
if (ghota_client == NULL) {
ESP_LOGE(TAG, "ghota_client_init failed");
return;
}
esp_event_handler_register(GHOTA_EVENTS, ESP_EVENT_ANY_ID, &ghota_event_callback, ghota_client);
ESP_ERROR_CHECK(ghota_check(ghota_client));
semver_t *cur = ghota_get_current_version(ghota_client);
if (cur) {
ESP_LOGI(TAG, "Current version: %d.%d.%d", cur->major, cur->minor, cur->patch);
semver_free(cur);
}
semver_t *new = ghota_get_latest_version(ghota_client);
if (new) {
ESP_LOGI(TAG, "New version: %d.%d.%d", new->major, new->minor, new->patch);
semver_free(new);
}
ESP_ERROR_CHECK(ghota_update(ghota_client));
ESP_ERROR_CHECK(ghota_free(ghota_client));
Configuration¶
The following configuration options are available:
* config.filenamematch <- Glob pattern to match against the firmware file from the Github Releases page.
* config.storagenamematch <- Glob pattern to match against the storage file from the Github Releases page.
* config.storagepartitionname <- Name of the storage partition to update (as defined in partitions.csv)
* config.hostname <- Hostname of the Github API (default: api.github.com)
* config.orgname <- Name of the Github User or Organization
* config.reponame <- Name of the Github Repository
* config.updateInterval <- Interval in minutes to check for updates
Github Actions¶
The Github Actions included in this repository can be used to build and release firmware images to Github Releases. This is a good way to automate your CI/CD pipeline, and update your devices in the field. In this example, we build two variants of the Firmware - on for a ESP32 and one for a ESP32-S3 device Using the filenamematch and storagenamematch config options, we can match against the correct firmware image for the device.
on:
push:
pull_request:
branches: [master]
permissions:
contents: write
name: Build
jobs:
build:
strategy:
fail-fast: true
matrix:
targets: [esp32, esp32s3]
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: esp-idf build
uses: espressif/esp-idf-ci-action@v1
with:
esp_idf_version: v4.4
target: ${{ matrix.targets }}
- name: Rename artifact
run: |
cp build/GithubOTA.bin GithubOTA-${{ matrix.targets }}.bin
cp build/storage.bin storage-${{ matrix.targets }}.bin
- name: Archive Firmware Files
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.targets }}-firmware
path: "*-${{ matrix.targets }}.bin"
release:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download Firmware Files
uses: actions/download-artifact@v2
with:
path: release
- name: Release Firmware
uses: ncipollo/release-action@v1
if: startsWith(github.ref, 'refs/tags/')
with:
artifacts: release/*/*.bin
generateReleaseNotes: true
allowUpdates: true
token: ${{ secrets.GITHUB_TOKEN }}