Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ULCP: Device Domain

The device domain is everything that belongs to the radio itself: its own node identity, the settings that make it behave the way its operator commissioned it, and the telemetry it can report about its own hardware. None of it is keyed by the attached host, and none of it is disturbed when one host replaces another (see State Classes).

Commissioning a repeater is exactly this: writing device-domain state and saving it. It is also the reason a host that is merely administering a device writes nothing in the host domain — see Two Kinds of Attach.

The Device Identity

The device hosts a node belonging to the device itself, used for in-band management, diagnostics, repeater forwarding (see PROP_MAC_REPEATER_ENABLED), and (in future revisions) periodic advertisement behavior. Its Ed25519 private key is held by the device and is never readable through this protocol.

A device identity always exists. A device advertising CAP_DEV_IDENTITY that finds no stored keypair at power-on MUST generate one from a cryptographic random source and persist it before processing any host command; PROP_DEV_KEY therefore never reports an empty value on a running device. Provisioning an identity is not a commissioning step: a factory-fresh radio is already a node, and PROP_DEV_PRIVATE_KEY exists to install a particular identity — restoring a known repeater onto replacement hardware — not to bring one into being.

The corollary is that a radio holds a throwaway identity from first power-on until a specific one is installed. This is safe because it never reaches the air: PROP_PHY_ENABLED is false post-reset, and a radio with nothing saved boots with the PHY disabled. It is not safe automatically on the restore path — see CMD_RESTORE.

Because the device holds this identity’s private key, it performs its own key agreement and needs only peer public keys (see PROP_DEV_PEERS). That is the opposite of the host identity, for which the device holds no private key and every pairwise key must be provisioned explicitly (see Tethered Host Services).

Capabilities

CodeNameRequiresGrants
37CAP_DEV_IDENTITYThe device identity: PROP_DEV_KEY, PROP_DEV_PRIVATE_KEY, PROP_DEV_CHANNEL_KEYS, PROP_DEV_PEERS
38CAP_DEV_NAMEPROP_DEV_NAME
39CAP_BATTERYBattery-powered operation and PROP_BATTERY
40CAP_REPEATERCAP_DEV_IDENTITYAutonomous repeater forwarding by the device identity: PROP_MAC_REPEATER_ENABLED, PROP_MAC_REPEATER_REGIONS, PROP_MAC_REPEATER_DEFAULT_REGION, PROP_MAC_REPEATER_MIN_RSSI, PROP_MAC_REPEATER_MIN_SNR
41CAP_IDENTCAP_DEV_IDENTITYPROP_IDENT, PROP_IDENT_ROLE, PROP_IDENT_MOBILE — serving and configuring the device identity’s advertised node identity
42CAP_ALERTSome means of making the device physically conspicuous on demand, and PROP_ALERT
44CAP_TIMEA wall clock: PROP_TIME, PROP_TZ_OFFSET
45CAP_GNSSCAP_TIMEA GNSS receiver: PROP_GNSS_ENABLED, PROP_GNSS_LOCATION, PROP_GNSS_ALTITUDE, PROP_GNSS_FIX, PROP_GNSS_PRECISION, PROP_GNSS_SATELLITES, PROP_GNSS_IDENT_UPDATE, PROP_GNSS_IDENT_PRECISION, PROP_GNSS_TIME_TRUST
46CAP_ADVERTCAP_DEV_IDENTITYAnnouncing itself on a schedule of its own: PROP_ADVERT_INTERVAL, PROP_BEACON_INTERVAL, PROP_STARTUP_BEACON
47CAP_ILLUMINANCEAn ambient light sensor and PROP_ILLUMINANCE

CAP_ADVERT requires CAP_DEV_IDENTITY because what a scheduled advertisement carries is the device identity, and a beacon’s source address names it.

CAP_TIME states that the device keeps a wall clock and nothing else. It says nothing about where the time comes from, how accurate it is, or how much of a power cycle it survives — a device that has one and does not currently know what time it is is a normal state, reported by the empty PROP_TIME.

CAP_GNSS requires CAP_TIME because a receiver is, among other things, a clock: a device advertising one without the other would be claiming a time source for a clock it does not have.

Properties

The device domain occupies property identifiers 64–95. Identifiers 70–95 are the device-behavior range: 70–78 are the repeater policy and advertised node identity settings, 79 is the locate alert, 80–87 are the advertisement policy — 80–82 allocated, 83–87 reserved — 88–93 are positioning (88 the receiver switch, 89–93 the fix telemetry), and 94–95 are environmental sensing: 94 illuminance, 95 reserved.

A single-octet identifier is the scarce resource, so the positioning range holds the properties a host reads and the device announces continually. The positioning configuration — which is written during commissioning and rarely again — lives at 4868–4870 in the extended device range, alongside the wall clock at 4866–4867.

IdMnemonicCommandsDescription
64PROP_DEV_KEYGetDevice identity public key
65PROP_DEV_PRIVATE_KEYSetDevice identity private key (write-only)
66PROP_DEV_CHANNEL_KEYSGet, Set, Insert, RemoveDevice identity channel keys
67PROP_DEV_PEERSGet, Set, Insert, RemoveDevice identity peer list
68PROP_DEV_NAMEGet, SetHuman-readable device name
69PROP_BATTERYGet, IsBattery status snapshot
70PROP_MAC_REPEATER_ENABLEDGet, SetAutonomous repeater forwarding enable
71PROP_IDENTGetSigned node identity of the device identity
72PROP_IDENT_ROLEGet, SetAdvertised node role, or empty to derive it
73PROP_IDENT_MOBILEGet, SetAdvertise the mobile capability bit
74PROP_MAC_REPEATER_REGIONSGet, SetRegion codes the device forwards for
75PROP_MAC_REPEATER_DEFAULT_REGIONGet, SetRegion code inserted into untagged flood packets
76PROP_MAC_REPEATER_MIN_RSSIGet, SetMinimum received RSSI for flood forwarding
77PROP_MAC_REPEATER_MIN_SNRGet, SetMinimum received SNR for flood forwarding
78PROP_DEV_DISCOVERABLEGet, SetWhether the device identity answers Identity Requests
79PROP_ALERTGet, Set, IsLocate alert state
80PROP_ADVERT_INTERVALGet, SetSeconds between unsolicited advertisements
81PROP_BEACON_INTERVALGet, SetSeconds between unsolicited beacons
82PROP_STARTUP_BEACONGet, SetWhether a beacon goes out at bring-up
88PROP_GNSS_ENABLEDGet, SetWhether the GNSS receiver is powered
89PROP_GNSS_LOCATIONGet, IsPosition of the last fix
90PROP_GNSS_ALTITUDEGetAltitude of the last fix
91PROP_GNSS_FIXGet, IsFix quality
92PROP_GNSS_PRECISIONGetEstimated horizontal accuracy of the last fix
93PROP_GNSS_SATELLITESGetSatellites used, and optionally in view
94PROP_ILLUMINANCEGetAmbient illuminance in millilux
4866PROP_TIMEGet, Set, IsWall clock, or empty when unknown
4867PROP_TZ_OFFSETGet, SetLocal time-zone offset from UTC
4868PROP_GNSS_IDENT_UPDATEGet, SetWhether fixes update the advertised node identity
4869PROP_GNSS_IDENT_PRECISIONGet, SetPrecision the advertised location is clamped to
4870PROP_GNSS_TIME_TRUSTGet, SetWhether receiver-derived time may set the clock

The RF configuration is also device-domain state, but is specified in Radio Control; so is the transport configuration in ULCP over BLE.

PROP 64: PROP_DEV_KEY

  • Type: Single-Value, Read-Only
  • Asynchronous Updates: No
  • Required: CAP_DEV_IDENTITY
  • Value Type: 32 octets, or empty
  • Post-Reset Value: Persisted

The Ed25519 public key of the device identity (see The Device Identity). The public key is also emitted as the success response when the private key is installed or generated (see PROP_DEV_PRIVATE_KEY).

An empty value means the device has no device identity. A conforming device does not report one in normal operation — an identity is generated at first boot if none is stored — so hosts SHOULD treat an empty value as a fault to surface rather than as an invitation to provision one.

Frames addressed to the device identity are processed by the device itself. They are additionally delivered or queued to the host only if they independently match the host’s receive filtering (see Receive Filtering).

PROP 65: PROP_DEV_PRIVATE_KEY

  • Type: Single-Value, Write-Only
  • Asynchronous Updates: No
  • Required: CAP_DEV_IDENTITY
  • Value Type: 32 octets, or empty
  • Post-Reset Value: Persisted

Installs or generates the device identity private key. An identity always exists already (see The Device Identity), so both forms replace one:

  • Setting a 32-octet value installs it as the device identity’s Ed25519 private key. This is the recovery path — moving a known repeater’s identity onto replacement hardware — not a commissioning step.
  • Setting an empty value commands the device to generate a fresh private key entirely on-device from its cryptographically secure random number generator. On-device generation is RECOMMENDED over installation, since a generated key never exists anywhere but the radio.

In both cases, success is reported by emitting CMD_PROP_IS for PROP_DEV_KEY — carrying the resulting public key — with the command’s TID. The private key itself is never emitted. Success MUST NOT be reported before the new identity is in effect and durably stored. Replacing an existing device identity is permitted; implementations SHOULD treat the device identity’s peer list and channel keys as still valid, since they are not derived from the identity key.

This property is write-only: CMD_PROP_GET MUST fail with STATUS_UNIMPLEMENTED and MUST NOT disclose the value or whether an identity is configured (use PROP_DEV_KEY for that).

The device identity is not part of the saved snapshot (see Saved State): it is durably persisted as soon as it is installed or generated, and it is changed only by another set of this property or by CMD_CLEAR. CMD_RESTORE never reverts it — though it does read the identity a snapshot was taken under, and refuses to enable the PHY when it does not match (see CMD_RESTORE).

Replacing the identity takes effect for the property surface immediately and for anything the device built around the old key at the next boot. The old key stops being one the device claims at once, so a device running a device node MUST stop originating traffic under it rather than continue until the reboot.

Installing a private key is subject to the same transport security requirements as all key provisioning (see Provisioning Security).

PROP 66: PROP_DEV_CHANNEL_KEYS

  • Type: Multiple-Value, Read-Write
  • Has Item Length Prefix: No
  • Asynchronous Updates: No
  • Required: CAP_DEV_IDENTITY
  • Item Form: 32 octets (the channel key)
  • Digest Form: 2 octets (the derived channel identifier)
  • Remove Selector: the 32-octet channel key
  • Post-Reset Value: Empty, or restored from saved state

The set of channel keys belonging to the device identity — channels the radio’s own node participates in (for example, a site-infrastructure management channel). These are independent of the host domain: they survive host replacement and are distinct from PROP_HOST_CHANNEL_KEYS.

For each key the device derives the 2-byte channel identifier and the channel’s K_enc/K_mic (see Multicast Packet Keys). The digest form reported for each entry is that derived channel identifier; the key itself is never read back.

Device channel keys do not create implicit host receive filters: frames on these channels are consumed by the device node and reach the host only through the host’s own filtering.

PROP 67: PROP_DEV_PEERS

  • Type: Multiple-Value, Read-Write
  • Has Item Length Prefix: No
  • Asynchronous Updates: No
  • Required: CAP_DEV_IDENTITY
  • Item Form: 32 octets (the peer’s Ed25519 public key)
  • Remove Selector: the 32-octet public key
  • Post-Reset Value: Empty, or restored from saved state

The device identity’s peer list: the set of peer public keys the device node recognizes and may communicate with securely. Because the device holds the device identity’s private key, it performs its own key agreement (Unicast Key Agreement) for these peers — no symmetric keys are provisioned, and the entries contain no secret material.

How the device node uses this list (management access control, secure diagnostics, and so on) is application behavior outside the scope of this protocol.

PROP 68: PROP_DEV_NAME

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_DEV_NAME
  • Value Type: 1–64 octets of UTF-8, without U+0000
  • Post-Reset Value: Implementation-defined default, or restored from saved state

The operator-assigned, human-readable name of the physical device. It is independent of the device and host cryptographic identities and MUST NOT be derived from a bonded host or other host-domain state.

Setting the property changes the live name immediately. Like other ordinary device-domain configuration, it is included in a CMD_SAVE snapshot but is not independently persisted merely by being set. Applications and transports that present the device to a person SHOULD use this value when practical. They MAY shorten it to fit a constrained presentation, but MUST NOT split a UTF-8 code point when doing so.

The name is intentionally public metadata. Operators should assume that any value used in discovery advertisements can be observed by nearby devices.

PROP 69: PROP_BATTERY

  • Type: Single-Value, Read-Only
  • Asynchronous Updates: Yes
  • Required: CAP_BATTERY
  • Value Type: Battery status snapshot (see below), or empty
  • Post-Reset Value: Current measurement, or empty if reporting is unsupported

A device advertising CAP_BATTERY has a battery capable of powering its operation and recognizes this property. The capability does not require the hardware to support reporting any measurement: an implementation that cannot report battery status at all answers CMD_PROP_GET successfully with an empty value.

A non-empty value is a snapshot of the battery measurements the platform supports, taken as one measurement event:

OctetsField
1Field flags
0 or 2Battery voltage, UINT16_LE, millivolts
0 or 1Battery level, UINT8, percent (0–100)
0+Charge state, PUI

Bits 0 (voltage), 1 (level), and 2 (charge state) of the field flags octet indicate which fields are present; present fields follow in the order above. Bits 3–7 are reserved and MUST be zero; a host MUST treat a value with a reserved bit set, or whose length does not match its field flags, as malformed.

Which fields a platform can report is fixed for a given hardware and firmware configuration; an individual snapshot carries those it can currently substantiate. A field is absent either because the implementation never reports that measurement, or because the value is not derivable in the device’s present state — a level estimated from resting terminal voltage is not obtainable while the pack is charging, and a charger that reports no completion signal offers no moment at which to recalibrate one. An implementation MUST NOT report a value it knows to be unreliable in place of omitting the field.

Absence MUST NOT be used to indicate a depleted or disconnected battery, and it is not how a failed measurement is reported: an implementation whose attempt to take a reading fails answers CMD_PROP_GET with STATUS_FAILURE.

A host MUST treat an absent field as unknown at that instant, and MUST NOT carry a value forward from an earlier snapshot in its place.

The value returned by CMD_PROP_GET reflects a measurement performed when the request is serviced, not a previously cached reading; concurrent requests MAY share one measurement. How each field is produced is platform-defined — in particular, the level estimate is not necessarily derived from the voltage measurement, and a platform with a fuel gauge may report a level without reporting a voltage at all.

The fields:

Battery voltage
The measured voltage at the battery terminals, in millivolts. This is the battery voltage, not an external-power input or regulated system voltage; it may therefore reflect the normal voltage elevation that occurs while the battery is charging.
Battery level
The implementation’s estimate of the battery’s state of charge, as an integer percentage from 0 through 100 inclusive. A host MUST NOT derive this value from the voltage field or assume that successive estimates change monotonically.
Charge state
The current battery charge state:
ValueName
0BATTERY_CHARGE_STATE_DISCHARGING
1BATTERY_CHARGE_STATE_CHARGING
2BATTERY_CHARGE_STATE_CHARGED
BATTERY_CHARGE_STATE_DISCHARGING
The charging system reports neither active charging nor charge completion. This is the charge state used for a disconnected battery when the implementation can detect that condition; an absent field never carries that meaning.
BATTERY_CHARGE_STATE_CHARGING
The charging system reports that the battery is actively receiving charge.
BATTERY_CHARGE_STATE_CHARGED
External power is present and the charging system reports that charging has completed. A battery at 100 percent while operating without external power remains in BATTERY_CHARGE_STATE_DISCHARGING.

The property contains live, read-only state. It is never included in a saved snapshot and is not changed by CMD_RESTORE. A device MAY emit unsolicited CMD_PROP_IS updates when the reported snapshot changes. Such updates SHOULD be coalesced or rate-limited so that measurement noise does not produce excessive ULCP traffic.

PROP 70: PROP_MAC_REPEATER_ENABLED

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_REPEATER
  • Value Type: BOOL
  • Post-Reset Value: Persisted

The first of the device-behavior settings (property identifiers 70–78), and the master switch for the repeater policy in PROP_MAC_REPEATER_REGIONS, PROP_MAC_REPEATER_DEFAULT_REGION, PROP_MAC_REPEATER_MIN_RSSI, and PROP_MAC_REPEATER_MIN_SNR, which are configurable while forwarding is disabled and take effect when it is enabled. When true, the device identity acts as an autonomous mesh repeater: its on-board node forwards overheard routable frames according to Repeater Operation, and it sets the repeater capability bit in its node identity. When false, the device identity does not forward and the bit is clear.

The capability bit is a statement of fact and MUST track the live forwarding state. The advertised role is a separate matter: it is configuration, set through PROP_IDENT_ROLE, and defaults to being derived from this flag rather than being fixed by it. A mobile repeater and a fixed tracker are both expressible.

This property governs only the forwarding behavior of the device identity. It is independent of PROP_MAC_PROMISCUOUS (a session-scoped host-delivery mode) and of the host identity, which never forwards.

The flag is device-domain state: it is part of the saved snapshot, so a CMD_SAVE arms an unattended repeater across power cycles, and it survives a change of host.

Flood-contention tuning — the forwarding delay window, deferral count, and similar timing parameters — is not exposed; a repeater applies its local defaults.

PROP 71: PROP_IDENT

  • Type: Single-Value, Read-Only
  • Asynchronous Updates: No
  • Required: CAP_IDENT
  • Value Type: Signed node-identity payload

The device identity’s complete signed node identity: the canonical payload encoding — role, capabilities, and the descriptive options the device advertises — followed by its 64-octet detached EdDSA signature over that encoding.

This is the same statement the device makes over the air, in its standalone framing. A device MUST build it from the same values it would advertise in an Identity Request response, so a host reading it locally and a peer hearing it on the mesh cannot disagree about what the device is. It differs from that response in exactly two ways, both structural: it carries no request nonce, and it is authenticated by the signature rather than by an enclosing authenticated unicast.

The contents are nonce-free and timestamp-free, so the value is a function of the device’s configuration alone. A device MAY cache it, but is not required to: reading this property is an operator-scale event.

PROP 72: PROP_IDENT_ROLE

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_IDENT
  • Value Type: UINT8, or empty
  • Post-Reset Value: Empty, or restored from saved state

The ROLE byte the device identity advertises (see Node Primary Role).

An empty value — the factory default — means the device derives the role from what it is actually doing: Repeater while PROP_MAC_REPEATER_ENABLED is set, Tracker otherwise. Any other value is advertised verbatim.

Role and forwarding are deliberately separate. Forwarding is a fact, reported through the repeater capability bit; the role is how the device presents itself, which is the operator’s choice. Deriving it by default keeps the common cases right without a configuration step, and setting it explicitly expresses the ones derivation cannot reach — a repeater that is also mobile, a fixed node that is not a repeater.

Tethering does not appear here, or anywhere in a node identity. Whether some host is currently attached over the local control link is a transient local relationship, not a durable characteristic of the node, and the mesh has no business knowing it.

PROP 73: PROP_IDENT_MOBILE

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_IDENT
  • Value Type: BOOL
  • Post-Reset Value: 0 (false), or restored from saved state

Whether the device identity advertises the mobile capability bit: true for a device that moves, false for one installed in a fixed location.

Orthogonal to PROP_IDENT_ROLE and to PROP_MAC_REPEATER_ENABLED, and orthogonal to whether a host is tethered. A hand-carried repeater and a pole-mounted sensor are both ordinary configurations.

PROP 74: PROP_MAC_REPEATER_REGIONS

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_REPEATER
  • Value Type: Concatenated 2-octet region codes
  • Post-Reset Value: Empty, or restored from saved state

The set of regions the device identity flood-forwards for, as the codes themselves concatenated with no delimiter — byte-for-byte the encoding of the Supported Regions identity option. The value length is therefore always even; a device MUST reject an odd-length write with STATUS_INVALID_ARGUMENT, and MAY reject a write that exceeds the number of entries it can hold.

The list is the filter applied at the region-policy step of the forwarding procedure: a flood packet carrying region codes is forwarded only if at least one of them appears here. An empty list — the factory default — imposes no regional restriction, so a tagged packet is forwarded whatever its region.

A device with forwarding enabled and a non-empty list SHOULD advertise the same codes in its node identity, so that a peer choosing a route can see what a repeater will carry. A device that is not forwarding makes no such claim and omits the option.

Whether an untagged packet is tagged on the way out is a separate decision, governed by PROP_MAC_REPEATER_DEFAULT_REGION.

PROP 75: PROP_MAC_REPEATER_DEFAULT_REGION

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_REPEATER
  • Value Type: One 2-octet region code, or empty
  • Post-Reset Value: Empty, or restored from saved state

The region code the device inserts into a flood packet that carries none, as permitted at the region-policy step of the forwarding procedure. An empty value — the factory default — means the device never tags: untagged packets are forwarded untagged. Any other value MUST be exactly two octets; a device rejects other lengths with STATUS_INVALID_ARGUMENT.

Tagging is opt-in because it is a claim about where the packet is, not merely about where the repeater is willing to forward. A repeater that filters on a region list without asserting one leaves the decision to whoever originated the packet.

The configured code SHOULD be one of the codes in PROP_MAC_REPEATER_REGIONS when that list is non-empty, so that the repeater will itself forward what it tags. A device does not enforce this across the two writes, and the two properties may be set in either order.

Only untagged packets are affected: an already-tagged packet is forwarded with its codes unchanged, and a second code is never added.

PROP 76: PROP_MAC_REPEATER_MIN_RSSI

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_REPEATER
  • Value Type: INT16 in dBm, or empty
  • Post-Reset Value: Empty, or restored from saved state

The weakest signal the device will flood-forward, in dBm. An empty value — the factory default — imposes no threshold. Any other value MUST be exactly two octets.

The threshold is the repeater’s half of step 7 of the forwarding procedure: where the packet also carries a minimum, the higher of the two applies. Raising it trades reach for a quieter mesh, which is what a dense deployment wants from a repeater sitting at the edge of everyone’s range.

Applies to flood forwarding only. Source-routed packets are forwarded on the strength of the route, not the link.

PROP 77: PROP_MAC_REPEATER_MIN_SNR

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_REPEATER
  • Value Type: INT8 in whole dB, or empty
  • Post-Reset Value: Empty, or restored from saved state

The lowest signal-to-noise ratio the device will flood-forward, in whole dB. An empty value — the factory default — imposes no threshold. Any other value MUST be exactly one octet.

The threshold is the repeater’s half of step 8 of the forwarding procedure, combined with any packet-imposed minimum the same way PROP_MAC_REPEATER_MIN_RSSI is. On spreading factors that decode well below the noise floor, this is the more meaningful of the two thresholds.

Applies to flood forwarding only.

PROP 78: PROP_DEV_DISCOVERABLE

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_DEV_IDENTITY
  • Value Type: BOOL
  • Post-Reset Value: 1 (true), or restored from saved state

Whether the device identity answers Identity Requests addressed to it, including broadcast solicitations whose filters select it. When false, the device identity ignores every Identity Request.

Discoverability defaults on: a deployed device is infrastructure, and being askable is most of what makes it administrable in the field. The property is the opt-out for deployments where the device should not volunteer its identity to arbitrary nearby askers.

Affects only Identity Request responses. Unsolicited advertisements and beacons are governed by the advertisement policy, and the device’s participation in forwarding by PROP_MAC_REPEATER_ENABLED; neither is changed by this property. A device that is not discoverable still advertises on its own schedule if it has one — declining to answer strangers and declining to speak are different decisions.

PROP 79: PROP_ALERT

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: Yes
  • Required: CAP_ALERT
  • Value Type: PUI
  • Post-Reset Value: 0 (ALERT_NONE)

What the device is currently doing to draw a person’s attention to where it physically is. A radio that has been set down in the wrong place is found by making it announce itself.

ValueName
0ALERT_NONE
1ALERT_LOCATE
ALERT_NONE
The nominal state. The device draws no attention to itself beyond whatever its ordinary operation involves.
ALERT_LOCATE
The device makes itself as conspicuous as its hardware allows, and keeps doing so until the alert is cleared.

Values other than these are rejected with STATUS_INVALID_ARGUMENT.

The presentation is board-defined. The property carries intent, not presentation: a device with a buzzer sounds it, a device with only an indicator LED flashes it, a device with a display can say so on the screen. CAP_ALERT states that the device has some means of making itself conspicuous and nothing more, so a host MUST NOT assume that an alert is audible, or that two devices alert alike. Because the alert runs unattended on a device that may already be low, it is expected to be intermittent rather than continuous, and it does not defer or inhibit a protective shutdown.

The alert overrides local quiet settings. A device whose buzzer has been silenced through a local control still sounds ALERT_LOCATE: locating a misplaced radio is precisely the case that silencing must not defeat. The alert suspends the local setting rather than changing it, so clearing the alert leaves the device as quiet as it was before.

A device returns to ALERT_NONE three ways:

  1. The host writes ALERT_NONE.
  2. Local user input cancels it. A device with any user input at all MUST offer a way to cancel an alert from the device itself — whoever finds the radio is rarely holding the phone that set it off. The input that cancels performs none of its other functions, so that fumbling for a beeping radio cannot change its configuration; a deliberate gesture such as hold-to-power-off MAY remain reachable while an alert is active.
  3. The deadline expires. A device MUST bound how long it will remain in ALERT_LOCATE; a few minutes is RECOMMENDED. Writing ALERT_LOCATE while it is already in effect succeeds and restarts the deadline, which is how a host holds an alert open for a longer search.

PROP 80: PROP_ADVERT_INTERVAL

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_ADVERT
  • Value Type: UINT32
  • Post-Reset Value: 14400 (four hours), or restored from saved state

Seconds between unsolicited advertisements — broadcasts carrying the device’s signed node identity. Zero sends none.

An automatic advertisement is sent with no flood hops and no source route, so it reaches the nodes that can hear the device directly and stops there. It is the largest frame the device originates, and what it carries is a standing statement rather than news; repeating that statement across the whole mesh every interval would spend airtime out of all proportion to what a distant listener learns. A device that wants to be findable further away publishes a path with PROP_BEACON_INTERVAL instead, which costs a fraction as much.

Because the advertisement is a signed broadcast, it carries its source in full-key form (§Node Identity).

A device MUST reject a non-zero interval outside 1200 seconds (twenty minutes) to 86400 seconds (twenty-four hours) with STATUS_INVALID_ARGUMENT. Neither bound is an airtime control — the duty limit is that — but the two ends fail differently. Below the floor a device spends the mesh’s airtime restating what it already said; above the ceiling the schedule has stopped being one, and zero says so more plainly.

The interval is a minimum rather than an exact cadence: each period is scattered later by a random fraction of it, never earlier, so the configured value is the shortest gap between two unsolicited announcements.

Scheduled sends are subject to the same duty accounting and channel access as any other transmission: a send the device cannot make when it falls due is skipped, not queued.

PROP 81: PROP_BEACON_INTERVAL

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_ADVERT
  • Value Type: UINT32
  • Post-Reset Value: 3600 (one hour), or restored from saved state

Seconds between unsolicited beacons — broadcasts with no payload at all. Zero sends none.

A beacon is sent with a flood budget and both the Trace Route and Trace Signal options, so what arrives at a distant node is a usable path back to the device and the signal quality of every hop along it. What it does not carry is any statement of who the device is: that is what an advertisement is for, and a listener that has never met this device learns only that something with a given source hint is reachable.

The two intervals are independent because they announce different things at very different costs. A mesh usually wants the path refreshed often and the identity restated rarely.

The accepted range, the per-period scatter, and the duty accounting are as described for PROP_ADVERT_INTERVAL.

PROP 82: PROP_STARTUP_BEACON

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_ADVERT
  • Value Type: BOOL
  • Post-Reset Value: 1 (true), or restored from saved state

Whether the device emits one beacon once it has come up. On by default: a node that has just restarted is exactly the node whose neighbours hold the stalest paths to it, and a single empty broadcast is the cheapest correction available.

The beacon is emitted after the device’s own configuration has been applied, so it reflects the device as it will actually run rather than as it booted. Unlike a scheduled period it is not scattered: devices do not restart in unison, so bring-up is already spread out by whatever staggered it.

PROP 88: PROP_GNSS_ENABLED

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: Yes
  • Required: CAP_GNSS
  • Value Type: BOOL
  • Post-Reset Value: device-defined, conventionally 0 (false), or restored from saved state

Whether the GNSS receiver is powered.

Asynchronous because a device MAY offer the receiver as a control the operator can reach — a button, a menu entry — and a switch someone can flip is a value that moves without the host asking. A device that flips it locally MUST publish the new value like any other transition the host did not command.

False means the lowest power state the board can put the receiver in, not merely an idle one: on a battery-powered node the receiver is typically the largest continuous load there is, and a property that only stopped reporting would be a property that solved nothing.

The post-reset value is the device’s to choose, and it SHOULD be false: a device that has never been told to care where it is should not be spending a battery finding out. A device whose purpose is to know where it is — a fixed outdoor node with a panel rather than a pocket tracker on a cell — MAY default it true instead, and SHOULD document that it does. Either way the value is only a starting point: saved state overrides it in both directions, and a host that wants a particular state sets it rather than assuming one.

Disabling the receiver does not clear the wall clock. Time already obtained stays as good as the device’s oscillator keeps it, which is the whole point of having acquired it.

One exception is permitted, and only for boards where the receiver’s own real-time-clock domain is the only clock the board has: that domain MAY remain powered while this property is false, and the device MAY briefly power the receiver at boot to read the time back out of it. That is a clock operation, not a positioning one — position data observed during it is discarded, and it is governed by PROP_GNSS_TIME_TRUST rather than by this property.

PROP 89: PROP_GNSS_LOCATION

  • Type: Single-Value, Read-Only
  • Asynchronous Updates: Yes
  • Required: CAP_GNSS
  • Value Type: 0–7 octets
  • Post-Reset Value: Empty

The position of the most recent fix, in the variable-precision location encoding — the same nibble-interleaved grid code node identities carry, so a host never has to convert between two position formats.

An empty value means no fix has been obtained since the receiver was last powered. The device reports the position it actually has rather than the last one it remembers across a power cycle: a position that was true somewhere else is worse than no position at all.

The length is the device’s own honest precision for that fix and MAY vary between reads. A host MUST NOT read more precision into a value than its length carries; the encoding’s truncation property means a shorter value is a correct lower-precision statement of the same position, never a different one.

A device MUST generally avoid announcing this property, and a host that wants a position MUST be prepared to read one. A receiver produces a fix about once a second, and at fine precision the readings of a receiver standing perfectly still still differ from each other, so a device that published every change would transmit continuously — and wake its host every time — on behalf of a host that may not be looking. No threshold rescues this: the one that would be quiet enough to be worth having is coarse enough that the announcements it does send are too late to be the point.

A host SHOULD nonetheless accept an announcement that arrives. The value carries what a read would have returned, a device may have its own reason to volunteer one, and a host that treats it as a protocol error gains nothing for the strictness.

Read-only in this revision. A manually-placed fixed node is a real use, but writing a position requires a rule for which source wins when the receiver also has one, and this revision does not define that rule.

PROP 90: PROP_GNSS_ALTITUDE

  • Type: Single-Value, Read-Only
  • Asynchronous Updates: No
  • Required: CAP_GNSS
  • Value Type: INT32_LE, or empty
  • Post-Reset Value: Empty

Altitude of the most recent fix, in meters, in the same units and reference as the node identity’s altitude option, so the two are directly interchangeable.

Empty when there is no three-dimensional fix — including while the receiver holds a two-dimensional one, which has a position but no altitude. Read-only for the same reason as PROP_GNSS_LOCATION.

PROP 91: PROP_GNSS_FIX

  • Type: Single-Value, Read-Only
  • Asynchronous Updates: Yes
  • Required: CAP_GNSS
  • Value Type: UINT8
  • Post-Reset Value: 0

The quality of the current position solution.

ValueMeaning
0No fix
1Two-dimensional fix — position without altitude
2Three-dimensional fix

Unlike the three properties that describe a position, this one is never empty: a device that is not fixed knows it is not fixed, so it reports 0. A receiver that is switched off reports 0 for the same reason. This is the distinction the whole positioning surface rests on — zero for the facts the device is sure of, empty for the position it does not have.

This is the positioning property a device SHOULD announce, and the reason it is the exception to PROP_GNSS_LOCATION’s silence is that it is not a measurement. It changes when the receiver acquires or loses a solution, which is a few times in a session rather than a few times a second, and it is what tells a host whether reading a position is worth anything at all.

PROP 92: PROP_GNSS_PRECISION

  • Type: Single-Value, Read-Only
  • Asynchronous Updates: No
  • Required: CAP_GNSS
  • Value Type: UINT16_LE, or empty
  • Post-Reset Value: Empty

Estimated horizontal accuracy of the current fix, in decimeters. Empty when there is no fix.

An estimate, not a measured error bound. Receivers generally report a dilution of precision, which becomes a distance only after multiplying by an assumed range error; a device that has a real accuracy figure SHOULD report that instead. Hosts MUST treat the value as indicative and MUST NOT present it as a guarantee.

Deliberately distinct from the length of PROP_GNSS_LOCATION: that is how precisely the device is willing to say where it is, and this is how precisely it knows. The two move independently.

PROP 93: PROP_GNSS_SATELLITES

  • Type: Single-Value, Read-Only
  • Asynchronous Updates: No
  • Required: CAP_GNSS
  • Value Type: UINT8, optionally followed by a second UINT8
  • Post-Reset Value: 0

The number of satellites contributing to the current solution, optionally followed by the number the receiver can see at all.

A device that cannot distinguish the two reports only the first octet. As with PROP_GNSS_FIX, a receiver that is off or searching reports 0 rather than the empty value.

Chiefly a diagnostic: it is what distinguishes an antenna fault from a sky that is simply obstructed, which is otherwise invisible to anyone not holding the device.

PROP 94: PROP_ILLUMINANCE

  • Type: Single-Value, Read-Only
  • Asynchronous Updates: No
  • Required: CAP_ILLUMINANCE
  • Value Type: UINT32_LE, or empty
  • Post-Reset Value: Not applicable

The ambient illuminance at the device, in millilux.

Millilux rather than lux because the readings that matter are at the dark end: a full moon is around 0.3 lux and starlight two orders of magnitude below that, so a device deciding how bright to make an indicator at night is working entirely inside what whole lux would round to zero. The unsigned 32-bit range still reaches past direct sunlight.

The value is a measurement taken when the property is read, not stored state, so a device samples on each get rather than answering from a cache. It follows that nothing about it is saved or reset: CMD_RST leaves it alone because there is nothing to leave.

An empty value means the device has no reading — the sensor did not answer, or is unavailable for as long as some other part of the device holds the hardware it shares. This is the same “we do not know” that PROP_TIME reports for an unset clock, and is not an error: a host that asked for the light level and got no answer has learned what it needed to.

A sensor that saturates reports its clamped maximum rather than an extrapolation past the point where it stopped responding to light. The alternative — a number derived from a transfer function outside the range it was fitted in — is indistinguishable at the host from a real reading.

PROP 4866: PROP_TIME

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: Yes
  • Required: CAP_TIME
  • Value Type: UINT32_LE, or empty
  • Post-Reset Value: Unchanged

The device’s wall clock, as seconds since the Unix epoch, UTC.

The count is unsigned, which puts the end of the representable range at 2106-02-07T06:28:15Z rather than at the 2038 rollover of the signed encoding. Nothing in this revision needs to handle a wrap.

An empty value means the device does not know what time it is. This is a normal state, not a fault: a device with no receiver, no battery-backed clock, and no host to ask has genuinely never been told. A device MUST report the empty value rather than an invented one, a zero, or a build timestamp.

A device that does not know the time MUST NOT display a clock, or any other indication of the current time, on any local user interface. A plausible-looking wrong time is worse than a blank space: an operator reads a displayed clock as a fact about the device, and a device with a screen is exactly the device somebody will trust.

Setting the property sets the clock. Setting the empty value returns the device to not knowing — the operator’s way of saying that whatever the device believes is wrong. A host-supplied time outranks every receiver-derived one, including while PROP_GNSS_TIME_TRUST is clear: the operator is the more authoritative source by definition.

The clock is not part of the saved snapshot. An epoch written to flash accumulates unbounded error while the device is off, so a clock is restored from a real time source — a receiver, a battery-backed real-time clock, or a host — or not at all. CMD_RST does not clear it; only an empty write does.

Devices announce this asynchronously. The transition from not knowing to knowing is the one worth announcing; a routine re-synchronization that agrees with the current value is not, and a device SHOULD NOT publish one.

PROP 4867: PROP_TZ_OFFSET

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_TIME
  • Value Type: INT16_LE
  • Post-Reset Value: 0, or restored from saved state

The local time-zone offset from UTC, in minutes east of UTC. Negative values are west.

Unlike PROP_TIME this always has a value. Where a device is meant to be is configuration and is known from the moment it is commissioned; what time it is is a measurement and may not be. Separating them is what lets a device render a local time the instant it acquires a clock, with no second round trip.

A minute offset rather than an hour one, because several real zones are not whole hours. Values outside the range of real civil offsets (−720 through +840) are rejected with STATUS_INVALID_ARGUMENT: outside it, a value is a unit or byte-order mistake, and a device that accepted one would display a confidently wrong local time.

Carries an offset and not a zone identifier. Devices do not carry a zone database, so daylight-saving transitions are the host’s business: whatever adjusts the device’s clock adjusts its offset.

PROP 4868: PROP_GNSS_IDENT_UPDATE

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_GNSS
  • Value Type: BOOL
  • Post-Reset Value: 0 (false), or restored from saved state

Whether position fixes refresh the location the device identity advertises in its node identity.

Off by default. Broadcasting where you are is a decision, and a device that started doing it because a receiver was switched on would be making that decision on the operator’s behalf.

When on, the device clamps each fix to PROP_GNSS_IDENT_PRECISION before advertising it. A device SHOULD act on a fix only when the clamped position actually changes, rather than on every fix: at a coarse precision a stationary node’s fixes all land in the same cell, and re-advertising each one spends airtime to say nothing.

Switching it off retracts the advertised position rather than freezing the last one. So does switching the receiver off. A position that nothing is refreshing any more is a claim the device cannot support, and it ages into a false one at whatever speed the device moves; a device therefore drops the location and the altitude together when it stops updating them.

Note that the Unix Timestamp option dates the identity payload, not the position — a node that has been stationary for a day still stamps each payload with the moment it was built.

PROP 4869: PROP_GNSS_IDENT_PRECISION

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_GNSS
  • Value Type: UINT8
  • Post-Reset Value: 5, or restored from saved state

How many octets of variable-precision location the advertised position is clamped to, 1 (coarsest) through 7 (finest).

The default of 5 is a cell of roughly 38 × 19 m at the equator: fine enough to place a node on a street, coarse enough not to place it in a room. That trade — not the receiver’s accuracy — is what this property exists to control, which is why it is separate from PROP_GNSS_PRECISION.

0 is rejected with STATUS_INVALID_ARGUMENT rather than read as “advertise nothing”: PROP_GNSS_IDENT_UPDATE is how the advertisement is switched off, and a precision that silently meant the opposite of a precision would be a trap. Values above 7 are rejected for the same reason the encoding stops there.

Writable while auto-update is off, so a whole positioning policy can be staged and enabled last.

PROP 4870: PROP_GNSS_TIME_TRUST

  • Type: Single-Value, Read-Write
  • Asynchronous Updates: No
  • Required: CAP_GNSS
  • Value Type: BOOL
  • Post-Reset Value: 1 (true), or restored from saved state

Whether time derived from the GNSS receiver may set the wall clock.

On by default: the sky is normally the best clock a device of this class has, and a node that sets itself needs no operator at all.

When clear, no receiver-derived time touches PROP_TIME — not a fix’s time, and not a read of the receiver’s own real-time clock at boot. This is the opt-out for a receiver whose time cannot be trusted: a jammed or spoofed sky can carry a plausible and badly wrong time, and a clock silently reset to it is worse than a clock that has stopped, because everything downstream will believe it.

Position reporting is unaffected. The two are separable, and an operator who distrusts the time may still want to know where the device thinks it is — including in order to notice that it is wrong.

Every transition to ALERT_NONE that the host did not command MUST be reported with an unsolicited CMD_PROP_IS.

The deadline is the only bound. In particular, the alert is not cleared on detach: the link to the searching host drops as soon as the searcher walks out of range, which is the moment the alert becomes most useful. It is likewise unaffected by CMD_RST, which resets session state and not the physical behavior of the device.

The property is live device-domain state. It is never included in a saved snapshot, is not changed by CMD_RESTORE, and is ALERT_NONE after every reset — a device that loses power mid-alert comes back quiet.