set_os
ReferenceAudience: API consumerUse: initiate an over-the-air firmware update on the reader
Overview
set_os initiates an over-the-air (OTA) firmware update — it points the reader at a firmware .DAT image at a URL and triggers the download-and-flash cycle. It is a state-changing command on the device-management (MGMT) endpoint, and it is both asynchronous and session-severing: the device accepts the payload, downloads and flashes the firmware, and reboots — dropping off the network for the duration. The authoritative confirmation of the outcome is an out-of-band get_version read-back.
set_os reflashes and reboots the reader. The reply code 1 is an accept (returned for success and failure alike), and the reader rejects every other command with code 4 while the update runs. See the danger callout in Notes and Caveats; confirm the result with get_version.
Details
| Property | Value |
|---|---|
| Interaction Pattern | Asynchronous Request → Accept → Reboot → Verify (completion confirmed out-of-band) |
| State Change | STATE-CHANGING (downloads and flashes firmware; reboots the reader) |
| Reversibility | IRREVERSIBLE (no single-step undo; re-flash a prior build with another set_os to roll back) |
| Required Request Fields | command, requestId, OSUpdateDetails (with url and authenticationType) |
| Applies To | Endpoint: Management (MGMT). Reader: RFD40 series, RFD90 series |
| API Version | V1.1 (API Version Reporting) |
| Related | get_version, install_certificate, reboot, get_status |
When to Use
Use set_os to update the reader's firmware — during commissioning, a fleet migration, or a security/feature rollout.
- Host the image — place the firmware
.DATat a URL the reader can reach. - Choose authentication —
NONEfor an open/internal server, orCERTIFICATE(with a CA reference or inline PEM) plus averificationTypefor a secured HTTPS download. - Check preconditions — ensure adequate battery and flash first (low battery → code
14, insufficient flash → code8). - Verify afterward — always confirm the new build with
get_version.
Topic Mapping
| Message | Direction | Topic | Note |
|---|---|---|---|
| Request | App → Reader | <tenantId>/MGMT/clients/cmnd/<serial> | On the management endpoint. |
| Response | Reader → App | <tenantId>/MGMT/clients/resp/<serial> | The code 1 accept, and the code 4 lock returned to other commands during the install. |
| Event | Reader → App | <tenantId>/MGMT/clients/event/<serial> | The FIRMWARE_UPDATE notifications (start, progress, outcome). See alerts. |
Topics follow the pattern <tenantId>/<topic>/<serial> — the tenant id assigned to you at provisioning (a slot you fill in, never a literal), the base topic you configure on the endpoint, and the reader's serial number, which the reader appends itself in its model-prefixed form (for example RFD40-212735201D0053). App → Reader means your app publishes and the reader subscribes; Reader → App means the reader publishes and your app subscribes.
See Topics and Channels.
Key Fields
OSUpdateDetails.url(request) — the firmware.DATdownload URL (a bad/unreachable URL → code9).OSUpdateDetails.authenticationType(request) —NONEorCERTIFICATE; forCERTIFICATE, supplycaCertificateFile(installed reference) orcaCertificateFileContent(inline PEM).OSUpdateDetails.verificationType(request) — TLS verification of the download server; we recommend setting it for HTTPS downloads (see Request Semantics).response.code(response) —1means accepted, not updated; confirm withget_version. See Response Codes.
Request Payload
The envelope is command, requestId, and the OSUpdateDetails payload (url + authenticationType). Pick an update source below; the Example and Schema update together. Use the Schema tab for the full field reference and the Example tab for the concrete payload.
- Example
- Schema
| Field | Type | Enum / Units | Example | Description |
|---|---|---|---|---|
command* | string | — | set_os | Fixed command name. |
requestId* | string | — | abc123 | A unique identifier for the request; echoed back in the response. |
OSUpdateDetails* | object | — | — | The firmware update source and authentication. Required by the contract, which also requires url and authenticationType within it. |
url*, nesting level 2 | string | uri | https://192.168.29.39:8000/Build-3.10.27/Firmware | URL of the firmware .DAT image to download and flash. Required. Bad/unreachable URL → code 9. |
authenticationType*, nesting level 2 | enum | NONECERTIFICATE | CERTIFICATE | Authentication to the firmware source. NONE = no auth; CERTIFICATE = inline or pre-installed CA certificate. Required. |
verificationType, nesting level 2 | enum | VERIFY_NONEVERIFY_HOST_AND_PEER | VERIFY_NONE | TLS verification of the download server. Optional (no default). Use VERIFY_HOST_AND_PEER for an HTTPS download; VERIFY_NONE must not be used in production. See the transport-security note in Request Semantics. |
caCertificateFileContent, nesting level 2 | string | — | -----BEGIN CERTIFICATE-----\nMIIDPTCCAiWgAwIBAgIUHtj1WDceNR9Nf+vZBB1Ne7sFmHkwDQYJKoZIhvcNAQEL\nBQAwLjEPMA0GA1UEAwwGVGVzdENBMQ4wDAYDVQQKDAVaZWJyYTELMAkGA1UEBhMC\nVVMwHhcNMjYwNTExMTMxNDQ3WhcNMzYwNTA4MTMxNDQ3WjAuMQ8wDQYDVQQDDAZU\nZXN0Q0ExDjAMBgNVBAoMBVplYnJhMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBANR8E/mxoQ5uTPW/muWnGoWDM7+YH5lIJiCM/n92\ndMLIQ+TVgb9mcOz80VnpgkQqomhbZaIG4CtnBufyIJvNKid+mgrrFh18HtMnE78d\nlGoPC4M469Rm+0oEN9GkUHgtgF2N4Qt9bYomySux3yyPlFuUOC+uaYRCCn3Hm8ko\nXE/a7XWdXeJkpIB0Mzzf7MoNq14AfX431RKgvpMgAqZTnDWtm4TVnK9Hq0t64tP7\naKIAJV2avJd87dF8x+K80kLBErDnKtuNFpXN7KoC7BBwdTzbG8VWYNUZeG8eCqBh\neL9MY5Ik7POmR0AM/TxRBLAXyoZHSPdEpEoaQZjSzJ7IHv0CAwEAAaNTMFEwHQYD\nVR0OBBYEFJHms5XPEyFYnVcf2++X9N8UlkahMB8GA1UdIwQYMBaAFJHms5XPEyFY\nnVcf2++X9N8UlkahMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB\nAL4HEEMhuQ9sQLFE9wiGw4BIILxnX30FbphWM8/xpHKuYNsH/NOWpjulG+ZAxe8n\n+QUhaU9hruBEajT7mYjKvZ9NtUumcAmAh3s29KD4NpjwglqUYfKQpzT4QxkcMXuW\nzCmqlAqGVzK3ve/Fuurgdbk3kApOZTTyqFw629dmmRhoP+2sdCYMAukyf348/4Gl\nY6ZtwwAJygC4/WCfWg/7XaeQ+6kqKtY6ijSJFfCNwEPPY7CW3XAqP+kZ0e2taL5a\nrvm5UUPRMGCs20JOucntSF8uPOICNWUEr3DbqAJ4jXKhSHdsG6fNDyJIOzX1zn0j\nVqh5mX6k2PZe0eylcukyu/s=\n-----END CERTIFICATE----- | Inline CA certificate (PEM) used to authenticate the HTTPS download (for CERTIFICATE auth). If empty, an already-installed filestore certificate is used. Public — not a secret. |
caCertificateFile, nesting level 2 | string | — | /apps/ca_cert.pem | Reference to a CA certificate already installed via install_certificate (for CERTIFICATE auth). One of caCertificateFile / caCertificateFileContent. |
Request Semantics
Modes. authenticationType selects how the reader authenticates to the firmware source: NONE for an unauthenticated download (an open or internal server), or CERTIFICATE to authenticate the HTTPS download against a CA — supplied either as a pre-installed reference (caCertificateFile, installed via install_certificate) or inline (caCertificateFileContent), and paired with a verificationType for TLS server verification. Both CERTIFICATE forms are in the Request Payload selector: Pre-installed CA certificate HTTPS and Inline CA certificate HTTPS.
- Authentication & transport security. For
authenticationType: CERTIFICATE, supply the CA either as an installed reference (caCertificateFile, pre-installed viainstall_certificate) or inline (caCertificateFileContent); ifcaCertificateFileContentis empty, the reader uses an already installed filestore certificate.authenticationType: NONEmeans no authentication is needed to access the URL.verificationTypesets how far the reader validates the download server's TLS certificate:VERIFY_HOST_AND_PEERchecks the certificate chain and that the certificate matches the host;VERIFY_NONEdoes no verification and must not be used in production. OSUpdateDetailsis required. The contract requires it, so a request without it is invalid rather than merely unable to update. A bad or unreachableurlreturns code9.
Response Payload
The immediate reply is an accept — command, requestId, apiVersion, and response with code 1. It confirms receipt, not a completed update.
- Example
- Schema
| Field | Type | Enum / Units | Example | Description |
|---|---|---|---|---|
command* | string | — | set_os | Echoes the executed command. |
requestId* | string | — | abc123 | Echoes the request's requestId — pairs reply to request. |
apiVersion* | enum | V1.0V1.1V1.2V1.21V1.22 | V1.1 | API version reported by this command. Emitted per-handler, not device-wide. |
response | object | — | — | Accept/status envelope. Not in the contract’s required set for this reply, although every published example carries it. This is an accept, not a terminal result (see the lifecycle below). |
code*, nesting level 2 | integer | 0–101 | 0 | 1 = the payload was accepted (the accept code for this command — not 0). See Response Codes below. |
description*, nesting level 2 | string | — | Success | Human-readable status. |
Lifecycle
set_os is an accept-then-verify cycle, not a single request/response.
Acknowledgment
The device replies response.code 1 ("Command payload is accepted") on the response topic. This confirms receipt only, and is identical for success and failure.
Progress
A FIRMWARE_UPDATE notification appears on the event topic as the download/flash begins. The block below is the example the schema declares for it, not an observed one:
Notifications continue on the same topic while the install runs. Each of the five recorded during one update observed on an RFD40 carried alertDetails.fwUpdateStatus.overallProgress — percent of the update completed — beside a human-readable updateStatus. overallProgress is optional and the start example above carries none, so treat its absence as unknown rather than as zero. Three of those five notifications are shown here and under Confirmation below, across the 15.265 s in which overallProgress ran from 87% to 100%; the earlier part of that update was not recorded.
The identical In progress value arrived twice — at 87%, then at 95% 10.014 s later — so only the first is shown. The notification after it carried a status string naming a component file inside the firmware package:
updateStatusis display text, not a machine token. It carries whatever the firmware shows an operator, file names included, and four of the five recorded notifications carried a value that is not one of the fivefwUpdateStatus.updateStatusdeclares — only the terminalFW update successmatched. The declared lowercase valuesupdating,skippingandfailedappeared in none of them: the reader sent sentences such asUpdating file …andSkipping file …instead, so the obvious testupdateStatus === 'updating'matches nothing this reader sends. Render the string as opaque text and drive logic fromoverallProgressand from the terminal literal under Confirmation below. The schema remains normative; this is an observed device deviation, cataloged in Known device deviations from the schema.- A status string can arrive incomplete. The notification at 99% reported
Skipping file …, naming a path inside the firmware package, and the value ended mid-name, its final character recorded as an encoding-error artifact rather than as text. Whether the reader truncated the string or the recording chain corrupted it cannot be told from a single sample, and no maximum length can be inferred from one either: do not assume a length limit, and do not substring-match.overallProgressin that same notification was a well-formed99, which is the practical case for branching on the number rather than on the text. - The stream's timing is not a contract. Across the recorded window the gap between notifications ran from 10.014 s down to 0.284 s while
overallProgressadvanced by 8, then 3, then 1, then 1 percentage points — neither a fixed interval nor a fixed increment, so do not derive a silence timeout from it. Emission is gated byeventConfiguration.firmwareUpdateinconfig_eventsand can be switched off altogether. The stream therefore gives you no deadline, so bound the wait on your own clock: set a wall-clock budget for the whole update from your own fleet's measurements, and on expiry pollget_versionuntil the reader answers. Treat silence as unknown, never as failure.
Session Impact
While the update runs, the reader rejects every other command on the response topic with code 4 ("Firmware update in progress"), echoing the blocked command's name:
The reader reflashes and reboots, and is briefly off the network.
Confirmation
The reader also reports the outcome on the event topic. The terminal notification carries updateStatus: FW update success with overallProgress: 100:
That literal is the one updateStatus value worth an equality test — it is a declared enum member and the observed notification matched it exactly — with overallProgress: 100 as corroboration rather than as the test, because a single observation cannot establish that 100 always means success. Every notification recorded, the terminal one included, carried state: ONESHOT, so the terminal signal sits in fwUpdateStatus and not in the envelope: do not wait for a CLEAR.
Gate on the read-back, not on the notification. Run get_version and compare readerVersion.firmwareVersion against the target build (see the version note below). The notification tells you when to stop waiting; the read-back tells you whether the firmware changed. A failed update leaves the prior version in place; terminal failure is reported as fwUpdateStatus.updateStatus: failed in the FIRMWARE_UPDATE event (which carries no response code), not on the set_os accept. failed is the value the schema declares for that case; no failure arose in the traffic behind the examples above, and because two of the three bare-stem values the schema declares arrive as sentences on this firmware, a real failure may well arrive as a sentence too — so detect failure by the read-back, not by matching failed.
Response Codes
set_os returns response.code. The codes applicable to this command are below; for the full table see the Response Codes and Correlation and Request/Response Pairing.
| Code | Description |
|---|---|
1 | Command payload is accepted (IOT_STATUS_CMD_PAYLOAD_ACCEPTED) — the accept code for this command. The update proceeds asynchronously; verify with get_version. |
0 | Success — terminal success; in practice set_os acks with 1, and completion is confirmed via the get_version read-back. |
4 | Firmware update in progress (IOT_STATUS_FW_UPDATE_IN_PROGRESS) — returned to other commands while an update is running (the device-wide lock). Pause orchestration until the device returns. |
8 | Insufficient flash size (IOT_ERROR_LOW_FLASH_SIZE) — not enough free flash to download the firmware. Free space or use a smaller package. |
9 | File not found (IOT_STATUS_FILE_NOT_FOUND) — the firmware url is wrong or unreachable. |
13 | Firmware update Failed (IOT_ERROR_FW_UPDATE_FAIL) — the defined failure code. In practice set_os acks only with 1; a failed update is reported as fwUpdateStatus.updateStatus: failed in the FIRMWARE_UPDATE event (which carries no response code), not on the set_os response — but do not equality-test that literal; see Confirmation. |
14 | Battery is low, Cannot update firmware (IOT_ERROR_FW_UPDATE_FAIL_LOW_BATTERY) — charge or connect external power, then retry. |
101 | Default error — generic catch-all. |
Gate success strictly on the get_version read-back, not on the set_os response code — the reply code 1 acknowledges only that the payload was accepted, and is returned for both success and failure.
Examples
Publish set_os; the device acks with the code 1 envelope under Response Payload, then runs the Lifecycle. The CERTIFICATE variants are in the Request Payload selector; the FIRMWARE_UPDATE notifications — start, in-flight progress, and terminal success — and the code 4 lock are shown under the Lifecycle above.
Notes and Caveats
The device goes dark and locks out other commands. While the update runs, the reader rejects every other command with code 4 ("Firmware update in progress") and is then unreachable across the reboot. Do not retry-storm — pause orchestration until the device re-attaches. The reply code 1 is returned for both success and failure, and there is no terminal result on the response topic: confirm the outcome with get_version and compare the firmware version.
- Preconditions. Ensure the device is ready before issuing: low battery → code
14; insufficient flash → code8. - Confirm by full version string.
readerVersion.firmwareVersionis a free-form string. Compare the whole value against the target build — the build-variant prefix can change (e.g.PAAFKS00-013-R02→SAAFKS00-013-R02), so match the full string rather than a substring. Seeget_version.
Sequence a firmware rollout as: pre-check with get_status (power/battery) → set_os → wait out the reboot (expect code 4 on other commands) → confirm with get_version. For the concept-level view, see Device Lifecycle.
Related: 📕 Device Lifecycle · 📕 get_version · 📕 install_certificate · 📕 reboot · 📕 Response Codes