Mistake on this page? Email us

Uploading the firmware image

Tip: With a commercial account, you can have up to 1,000 firmware images at a time, instead of 10. See Choosing your account type for more information about the other benefits of commercial accounts and how to upgrade a free account to a commercial account.

In Portal: Uploading the firmware image

When you upload a new firmware image to Device Management, Device Management provides you with a URL to put in the manifests you send to deployed devices.

Devices use this URL to download the candidate firmware image.

To upload the firmware binary to Device Management and obtain the candidate image URL:

  1. Log in to Device Management Portal for your region:

  2. Select Firmware update > Images from the left menu.

  3. Click the Upload image button.

  4. Enter a name and a description for the image.

  5. Toggle the Image file encryption switch to encrypt the image you are uploading.

  6. Click the Choose file button and select the file.

  7. Click the Upload firmware image button.

You can now find the new image on the Firmware image management screen.

Using the API: Uploading a small firmware image


If your image is smaller than 100 MiB (one hundred mebibytes) in total, upload your full firmware or delta update image using the POST v3/firmware-images/ API.

To encrypt the image, set datafile_encryption to true.

Using the API: Uploading a large firmware image


Device Management Update lets you upload large firmware images in multiple parts (chunks) where each request is less than or equal to 100 MiB (one hundred mebibytes). This is necessary if the final firmware image resource is greater than 100 MiB. You can do this using the Update Service APIs.

The minimum chunk size is 5 MiB (five mebibytes). If your firmware chunk size is under 5 MiB, the upload process will not fail until the last packet is sent. This is because firmware images are stored in an S3 bucket, and the S3 APIs don't send a message of failure until the last packet.

Summary of API interfaces


You can click on each operation to see the full API reference.

Operation Explanation
POST /v3/firmware-images/upload-jobs Create a new upload job.
GET /v3/firmware-images/upload-jobs List upload jobs.
GET /v3/firmware-images/upload-jobs/{upload_job_id} View a specific upload job.
DELETE /v3/firmware-images/upload-jobs/{upload_job_id} Delete an upload job.
PUT /v3/firmware-images/upload-jobs/{upload_job_id} Update an upload job.
GET /v3/firmware-images/upload-jobs/{upload_job_id}/chunks View metadata for uploaded chunks.
POST /v3/firmware-images/upload-jobs/{upload_job_id}/chunks Append a chunk to an upload job, or mark an upload job as complete.
GET /v3/firmware-images/upload-jobs/{upload_job_id}/chunks/{chunk_id} View metadata about a chunk.

See the troubleshooting information on this page if you encounter any difficulty during an upload with multiple parts.

Creating an upload job


To create an upload job with multiple parts, use POST /v3/firmware-images/upload-jobs with header Content-Type: application/json:

    {
      "name": "<name>",
      "description": "<description>"
    }

The field name must be unique for each account, and the field description is optional. Both default to an empty string.

Note: The response bodies in this tutorial are for demonstration purposes only. Your response bodies will differ.

The POST response consists of:

  • A 201 Created status.
  • A Location header containing the URI for the new firmware image upload job resource.
  • A Content-Location header containing the same URI.
  • A response body like:
    {
        "completed": false,
        "created_at": "2018-08-31T10:49:41.736515Z",
        "description": "<description>",
        "etag": "2018-08-31T10:49:41.736515Z",
        "firmware_image_id": "",
        "id": "01658f9a586800000000000100100023",
        "name": "<name>",
        "object": "upload-job",
        "status": "not_started",
        "updated_at": "2018-08-31T10:49:41.736515Z",
    }

The status is not_started because you haven't yet uploaded any chunks for this job. The completed field is false until the upload job is finished.

The firmware_image_id is empty because you haven't finished the upload. There is no corresponding firmware image resource until the job is complete.

Returning information on existing upload jobs


To see information on upload jobs, GET /v3/firmware-images/upload-jobs, which returns a 200 OK status and a response like:

{
      "after": None,
      "data": [
          {   
              "completed": false,
              "created_at": "2018-08-31T15:09:06.659124Z",
              "description": "<description>",
              "etag": "2018-08-31T15:09:06.659124Z",
              "firmware_image_id": "",
              "id": "01659087d8e300000000000100100015",
              "name": "<name>",
              "object": "upload-job",
              "status": "in_progress",
              "updated_at": "2018-08-31T15:09:06.659124Z"
          },
          {
              "completed": false,
              "created_at": "2018-08-31T15:09:06.663841Z",
              "description": "<description>",
              "etag": "2018-08-31T15:09:06.663841Z",
              "firmware_image_id": "",
              "id": "01659087d8e900000000000100100016",
              "name": "<another name>",
              "object": "upload-job",
              "status": "not_started",
              "updated_at": "2018-08-31T15:09:06.663841Z"
          }
      ],
      "has_more": False,
      "limit": 50,
      "object": "list",
      "order": "ASC"
}

Uploading a new chunk


You must call the API each time you want to add a new chunk. The service appends data onto an existing upload.

Note: Because there is no way to specify chunk index, you can't upload chunks in parallel. Doing so results in a corrupted firmware image. Wait for a 201 Created status before proceeding to the next chunk.

To do this, POST /v3/firmware-images/upload-jobs/{upload_job_id}/chunks with headers:

  • Content-Type: binary/octet-stream.
  • Content-Length: <data-length>.
  • Content-MD5: <per-RFC1864>.

The body must contain the binary data for the chunk.

The response consists of:

  • A 201 Created status.

  • A Location header containing the URI for the new firmware image upload chunk resource.

  • A Content-Location header containing the same URI.

  • JSON like:

        {
            "created_at": "2018-08-31T10:49:41.742352Z",
            "etag": "2018-08-31T10:49:41.742352Z",
            "hash": "e3237bc98b00f204e8800999ecff316e",
            "id": 1,
            "length": 14,
            "updated_at": "2018-08-31T10:49:41.742352Z"
        }
    

The id is the chunk index, starting at 1 for the first chunk posted to the firmware image upload job and increasing by one with each uploaded chunk.

The length reflects the number of bytes in the binary data. The binary data in a chunk must be at least 5MB, except for the last chunk of the sequence (and ignoring the zero-length chunk that finishes the upload).

The hash field is the hexadecimal form of the Content-MD5 message integrity check.

Viewing metadata for all uploaded chunks


To view metadata for all uploaded chunks in a job, use GET /v3/firmware-images/upload-jobs/{upload_job_id}/chunks, which returns a 200 OK status and a response body like the one below.

This example shows two uploaded firmware image chunks. See Posting a chunk for more information on the various fields for each chunk below:

    {
        "after": None,
        "data": [
            {
                "created_at": "2018-08-31T14:44:25.023959Z",
                "etag": "2018-08-31T14:44:25.023959Z",
                "hash": "e3237bc98b00f204e8800999ecff316e",
                "id": 1,
                "length": 14,
                "updated_at": "2018-08-31T14:44:25.023959Z"
            },
            {
                "created_at": "2018-08-31T14:44:25.033691Z",
                "etag": "2018-08-31T14:44:25.033691Z",
                "hash": "d41d8cd98f00b204e9800998ecf8427e",
                "id": 2,
                "length": 19,
                "updated_at": "2018-08-31T14:44:25.033691Z"
            }
        ],
        "has_more": False,
        "limit": 50,
        "object": "list",
        "order": "ASC"
    }

Alternatively, if there are no chunks (either because you haven't uploaded any yet, or you completed an upload):

    {
        "after": None,
        "data": [],
        "has_more": False,
        "limit": 50,
        "object": "list",
        "order": "ASC"
    }

Viewing metadata for a specific chunk


To view metadata for a specific uploaded chunk, use GET /v3/firmware-images/upload-jobs/{upload_job_id}/chunks/{chunk_id}:

  {   
      "created_at": "2018-08-31T15:23:55.912834Z",
      "etag": "2018-08-31T15:23:55.912834Z",
      "hash": "d41d8cd98f00b204e9800998ecf8427e",
      "id": 1,
      "length": 14,
      "updated_at": "2018-08-31T15:23:55.912834Z"
  }

This returns a 200 OK status.

Ending an upload with multiple parts


Sending a chunk with zero data length ends the upload job and creates a record of the firmware image (firmware_image_id).

To do this, use POST /v3/firmware-images/upload-jobs/{upload_job_id}/chunks with headers:

  • Content-Length: 0.
  • Content-MD5: <per-RFC1864>.

Note: You still need to include headers when posting an empty response body.

This returns a 201 Created response and Location and Content-Location headers as for any other chunk.

For example, if you upload four data chunks, then the POST of a zero-length chunk returns JSON like:

    {
        "created_at": "2018-08-31T10:49:41.742352Z",
        "etag": "2018-08-31T10:49:41.742352Z",
        "hash": "d41d8cd98f00b204e9800998ecf8427e",
        "id": 5,
        "length": 0,
        "updated_at": "2018-08-31T10:49:41.742352Z"
    }

Note: You can't complete an upload that has no data. The first chunk uploaded can't be of length zero. Attempting this gives a 400 Bad Request response. To delete an upload job, see Delete a firmware image upload job.

You can then view information about the completed firmware image upload job using GET /v3/firmware-images/upload-jobs/{upload_job_id}, which returns JSON like:

  {
     "completed": true,
     "created_at": "2018-08-31T10:49:41.736515Z",
     "description": "<description>",
     "etag": "2018-08-31T10:49:41.756045Z",
     "firmware_image_id": "01658f9a587800000000000100100024",
     "id": "01658f9a586800000000000100100023",
     "name": "<name>",
     "object": "upload-job",
     "status": "completed",
     "updated_at": "2018-08-31T10:49:41.756045Z"
  }

View the newly created firmware image using GET /v3/firmware-images/{image_id}/. For example, GET /v3/firmware-images/01658f9a587800000000000100100024 returns the data for the firmware image resource, with the location of the image in the datafile field:

  {
     "completed:" true,
     "created_at": "2018-08-31T10:49:41.751725Z",
     "datafile": "http://example.com/00000000000000000000000000000001",
     "datafile_checksum": "1234",
     "datafile_size": 27,
     "description": "<description>",
     "etag": "2018-08-31T10:49:41.751725Z",
     "id": "01658f9a587800000000000100100024",
     "name": "<name>",
     "object": "firmware-image",
     "updated_at": "2018-08-31T10:49:41.755303Z"
  }

Deleting a firmware image upload job


Deleting an upload job cancels any ongoing upload job and removes the upload job resource. You can't undo or continue the job after this.

To do this, use DELETE /v3/firmware-images/upload-jobs/{upload_job_id}. The response is a 204 No Content status and no response body.

Troubleshooting

Posting to a completed upload job

If you try to POST to a firmware image upload job that has already finished (with status completed), the result is a 400 Bad Request.

409 Conflict

This conflict happens when you use POST /v3/firmware-images/upload-jobs but already have either a /v3/firmware-images/:id with the same name as given, or a /v3/firmware-images/upload-jobs/:id with the same name as given.