Skip to main content

OTA Firmware update

Customization

Connhex is dedicated to hardware manufacturers: their firmware update strategies, albeit with some commonalities, vary a lot. This is why, for the time being, Connhex offers basic support for OTA updates: you can contact us to see how your current firmware update process fits with Connhex.

OS updates

As of today, Connhex only supports user-space software updates.

Pull update strategy

Broadly speaking, IoT firmware updates can be separated in:

  • push updates: updates are split into chunks, then sent to the device - typically as MQTT(s) packets, if the protocol is available. The device then recreates the entire update, checks its integrity and applies it.
  • pull updates: the device requests for available updates. If at least one is present, it is obtained as a file download through the HTTP(s) protocol.

By default, Connhex only implements pull updates, since they are better suited for heavy updates. Should your situation be better suited for push updates, you can contact us as usual!

Flow

The firmware update process begins with building and uploading the firmware package. Each firmware can have multiple versions, with each version identified by a unique version. However, only one version can be active at a time. The active version is the one that will be downloaded and applied by the device during an update if no other constraints are in place.

Devices can periodically check for updates by providing the current version they are running or the timestamp of the last update check. If a new firmware version is available, the server will return the new binary for download.

Downloading Updates

There are two main methods for checking and downloading firmware updates:

  • Check Only: Determines if a new firmware version is available without downloading it.
  • Check and Download: Determines if a new firmware version is available and downloads it if so.

The API supports both behaviors depending on the HTTP method used:

  • Use the HEAD method to check for updates without downloading.
  • Use the GET method to check for updates and download the firmware.

The GET method also supports partial downloads using the Range header, allowing for efficient handling of large files, resuming interrupted downloads, and more.

Authentication

The device must authenticate using init_id and init_key credentials. Only initialized devices are permitted to access OTA updates, so other credentials, such as migration_key, cannot be used for this purpose.

Checking for Updates

To check if a new firmware version is available, the device can send a HEAD request to the server.

It is recommended to include the If-Modified-Since (or the complementary If-Unmodified-Since) header with the timestamp of the last firmware check. This allows the server to respond with a 304 Not Modified status if the firmware has not been updated since the provided date, saving bandwidth and processing time.

Other conditional headers, such as If-None-Match and If-Match, can also be used to check for updates based on the ETag value, for example to check if a specific version is available.

Example Request filtering by last update check

HEAD /iot/edge/ota/<init_id> HTTP/1.1
Host: <tenant>.connhex.com
Authorization: Thing <init_key>
If-Modified-Since: <date>
  • <init_id>: The unique identifier of the device.
  • <tenant>: The tenant's domain for the server.
  • <init_key>: The key used for authenticating the device.
  • <date>: The timestamp of the last firmware check in HTTP-date format (e.g., Wed, 21 Oct 2023 07:28:00 GMT).

Example with curl

curl -I -H "Authorization: Thing <init_key>" \
-H "If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT" \
https://<tenant>.connhex.com/iot/edge/ota/<init_id>

Expected Response

  • 200 OK: A new firmware version is available.
  • 304 Not Modified: The firmware has not been updated since the provided date.
  • 404 Not Found: No firmware binary is available.

If a new firmware is available, the response will include the following headers:

  • Last-Modified: The timestamp of the latest available firmware version.
  • Content-Length: The size of the firmware binary in bytes.
  • Content-Type: The MIME type of the firmware binary.
  • Content-Disposition: The suggested filename for the firmware binary.
  • Content-Digest: The checksum of the firmware binary in the format <algo>=<checksum>.
  • Version: The version of the firmware binary.
  • Min-Version: The minimum version required to apply the firmware binary (if applicable).
  • Max-Version: The maximum version allowed to apply the firmware binary (if applicable).
  • ETag: Unique identifier for the firmware binary (same as the version) to be used with the If-Range header.

Example Request filtering by version

HEAD /iot/edge/ota/<init_id> HTTP/1.1
Host: <tenant>.connhex.com
Authorization: Thing <init_key>
If-None-Match: "<version>"
  • <version>: The version of the current firmware running on the device.

The server will respond with a 304 Not Modified status if the firmware version is the same as the one provided in the If-None-Match header, or a 200 OK status if a new version is available.

The expected response is the same as in the example above.

Downloading the Firmware Binary

To download the firmware binary, use the GET method. The binary can be downloaded in chunks using the Range header, which is especially useful for large files. If the Range header is not specified, the full binary will be returned.

Example Request

GET /iot/edge/ota/<init_id> HTTP/1.1
Host: <tenant>.connhex.com
Authorization: Thing <init_key>
Range: bytes=<start>-<end>
  • <start>: The starting byte position of the range.
  • <end>: The ending byte position of the range. This value is optional and, if omitted, the end of the resource is used as the end of the range.

Notes:

  • The Range header uses a zero-based index, so the first byte is at position 0.
  • The <end> value is inclusive, meaning the byte at that position is included in the range.
  • When the <end> is omitted, the - character must still be present (e.g., bytes=512-).

Example with curl

curl -H "Authorization: Thing <init_key>" \
-H "Range: bytes=0-1023" \
-o firmware_part_1.bin \
https://<tenant>.connhex.com/iot/edge/ota/<init_id>

This command downloads the first 1024 bytes of the firmware binary and saves it as firmware_part_1.bin.

Downloading the Entire Binary

If you prefer to download the entire binary at once:

curl -H "Authorization: Thing <init_key>" \
-o firmware.bin \
https://<tenant>.connhex.com/iot/edge/ota/<init_id>

This will download the complete firmware binary and save it as firmware.bin.

Expected Response

  • 200 OK: The full firmware binary is returned (if no Range header is provided).
  • 206 Partial Content: The requested byte range of the firmware binary is returned.
  • 416 Range Not Satisfiable: The requested byte range is invalid or out of bounds.
  • 404 Not Found: The firmware binary is not available.

The response will include the same headers as the update check response. In the case of partial content, the Content-Range header will also be included, indicating the range of bytes being returned.

Using If-Range for resuming downloads

The If-Range header is useful when resuming a partially downloaded firmware binary. It ensures that the server sends only the missing portion of the binary if it hasn't been modified since you last checked. If the firmware has been updated, the server will ignore the Range header and send the entire binary instead.

Note that the If-Range header can only be used together with the Range header.

Example Request

GET /iot/edge/ota/<init_id> HTTP/1.1
Host: <tenant>.connhex.com
Authorization: Thing <init_key>
Range: bytes=1024-2047
If-Range: "<etag>"
  • <etag>: The ETag value received from the previous GET or HEAD request, indicating the active version of the firmware.

Example with curl

curl -H "Authorization: Thing <init_key>" \
-H "Range: bytes=1024-2047" \
-H 'If-Range: "<etag>"' \
-o firmware_part_2.bin \
https://<tenant>.connhex.com/iot/edge/ota/<init_id>

This command requests the next 1024 bytes of the firmware binary, but only if the binary hasn't changed since the last download, as indicated by the ETag.

Expected Response

  • 206 Partial Content: The requested byte range of the firmware binary is returned.
  • 200 OK: The full firmware binary is returned because the binary was updated, rendering the range request invalid.
  • 416 Range Not Satisfiable: The requested byte range is invalid or out of bounds.
  • 404 Not Found: The firmware binary is not available.

Verifying the Download

After downloading the firmware, verify its integrity by comparing the checksum provided in the Content-Digest header with the checksum you calculate for the downloaded file. For example, using sha256sum:

sha256sum firmware.bin

Ensure the checksum matches the one provided by the server to confirm the binary is intact and uncorrupted.

Here is an example script that downloads the firmware binary and verifies its checksum:

#!/bin/bash

URL="https://<tenant>.connhex.com/iot/edge/ota/<init_id>"
AUTH="Thing <init_key>"
OUTPUT_FILE="firmware.bin"

# Make the HTTP GET request to download the file and capture the Content-Digest header
CONTENT_DIGEST=$(curl -s -H "Authorization: $AUTH" -D - $URL -o $OUTPUT_FILE | grep -i "Content-Digest" | awk '{print $2}' | tr -d '\r')

# Extract the algorithm and checksum from the Content-Digest header
ALGO=$(echo $CONTENT_DIGEST | cut -d '=' -f 1)
EXPECTED_CHECKSUM=$(echo $CONTENT_DIGEST | cut -d '=' -f 2)

# Calculate the checksum of the downloaded file using the specified algorithm
if [[ "$ALGO" == "sha256" ]]; then
ACTUAL_CHECKSUM=$(sha256sum $OUTPUT_FILE | awk '{print $1}')
elif [[ "$ALGO" == "sha1" ]]; then
ACTUAL_CHECKSUM=$(sha1sum $OUTPUT_FILE | awk '{print $1}')
elif [[ "$ALGO" == "md5" ]]; then
ACTUAL_CHECKSUM=$(md5sum $OUTPUT_FILE | awk '{print $1}')
else
echo "Unsupported checksum algorithm: $ALGO"
exit 1
fi

# Compare the checksums
if [[ "$EXPECTED_CHECKSUM" == "$ACTUAL_CHECKSUM" ]]; then
echo "Checksum matches!"
else
echo "Checksum does not match!"
exit 1
fi

Applying the Update

Once the update is available, a re_init message can be sent to any device - either manually or programmatically1. Upon receiving the re-initialization message2, Connhex Edge will:

  • download the update, by authenticating with the usual init_id and init_key credentials
  • overwrite the update folder (typically /opt/connhex/updates)
  • perform the initialization process once again

For additional security, upon the receival of a download request, a token can be generated and exchanged before downloading the file.


  1. This depends on the type of rollout. A common practice is to select a few devices to start and then progressively extend to more.
  2. Here's where the custom logic is usually implemented. Want to wait for user approval? Automatically update if inside a given time window?