Skip to content

OTA Updates

Since GPIO 1/3 (the programming UART) is used for LoRa in production, OTA updates are the primary method for field firmware updates.

The firmware uses a custom dual-slot partition table (partitions_ota.csv):

# Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000
ota_0, app, ota_0, 0x10000, 0x180000 (1.5MB)
ota_1, app, ota_1, 0x190000,0x180000 (1.5MB)

Each app slot is 1.5MB. Typical firmware is ~200KB, leaving plenty of room.

Hold the K1 button (GPIO0) for 3 seconds during boot. The firmware detects this and enters OTA mode instead of the normal pipeline:

  1. Connects to WiFi (credentials must be provisioned via BLE first)
  2. Starts the OTA HTTP client
  3. Downloads firmware from the configured URL
  4. Writes to the inactive partition slot
  5. Sets the boot partition and restarts

If the OTA update fails or the new firmware crashes, the device falls back to the previous partition on the next boot.

The firmware expects a binary firmware file served over HTTP:

// Configure the OTA URL (in ota_example.h or via NVS)
#define OTA_UPDATE_URL "http://192.168.1.100:8080/firmware.bin"

Serve the built binary:

8080/esp32cam_qemu_test.bin
cd build
python -m http.server 8080
┌──────────┐ ┌───────────┐ ┌──────────┐
│ Boot │──K1──▶│ WiFi │──OK──▶│ HTTP GET │
│ check │ held │ connect │ │ firmware │
└──────────┘ └───────────┘ └────┬─────┘
┌────────▼────────┐
│ Write to ota_1 │
│ (inactive slot) │
└────────┬────────┘
┌────────▼────────┐
│ Set boot slot │
│ → Restart │
└─────────────────┘

OTA requires an active WiFi connection. If the device has never been provisioned:

  1. Power on without holding K1
  2. The firmware detects no saved WiFi credentials
  3. BLE provisioning starts automatically
  4. Use the ESP SoftAP Provisioning app to configure WiFi
  5. After provisioning, reboot and hold K1 for OTA mode