Skip to main content

control_operation

ReferenceAudience: API consumerUse: start or stop the RFID radio (or the scanner) on demand

Overview​

control_operation starts or stops a reader subsystem on demand. You pick a controlType (RFID — the radio inventory engine, or SCANNER — the barcode scanner) and an operation (START or STOP). For RFID, START begins an inventory cycle using the currently configured operating mode and the device streams tag reads as dataEVT events until you STOP (or the operating-mode stop conditions are met). It is a state-changing control-plane (CTRL) command, and it is reversible — a START is undone by a STOP.

This is a CTRL-endpoint command, published on the control endpoint (<tenantId>/CTRL/clients/cmnd/<serial>) and answered only there (not on the MGMT/MDM channel). Returning a response requires an active CTRL endpoint (config_endpoint, epType: CTRL, activate: true). Receiving the tag stream that START triggers also requires an active DATA1 endpoint (<tenantId>/DATA/clients/data1event/<serial>; see the data endpoint topic mapping).

Set the regulatory region first. Before you start an RFID inventory, make sure the reader’s regulatory region is configured: the reader does not transmit until it is, and the region is set outside MQTT with 123RFID Desktop or 123RFID Mobile. Use get_current_region to verify the current region.

A START executes whatever the operating mode has armed — including destructive access operations. See the danger callout in Notes and Caveats.

A first inventory, once the region is set, the operating mode is configured and you have subscribed to the DATA1 topic:

Diagram

Details​

PropertyValue
Interaction PatternRequest / Response (app publishes a command; device publishes the correlated reply)
State ChangeSTATE-CHANGING (starts / stops the radio or scanner)
ReversibilityREVERSIBLE (a START is undone by a STOP)
Supported OperationsSTART, STOP
Required Request Fieldscommand, requestId (ctrlOprPayload is optional in the contract; inside it, controlType and operation are required)
Applies ToEndpoint: Control (CTRL). Reader: RFD40 series, RFD90 series
API VersionV1.1 (API Version Reporting)
Relatedset_operating_mode, get_operating_mode, get_status, reboot

When to Use​

Use control_operation to drive the radio (or scanner) on demand:

  • Start an inventory — controlType: RFID, operation: START begins reading tags with the current operating mode; the device streams dataEVT events over the DATA endpoint.
  • Stop an inventory — operation: STOP halts the radio; the tag stream ceases.
  • Control the scanner — controlType: SCANNER starts / stops the barcode scanner instead of the radio.

Configure how the reader reads with set_operating_mode first, then START — control_operation itself has no tuning parameters. If the inventory state is uncertain, check it with get_status before sending START.

Topic Mapping​

control_operation is the one command that drives both planes: the command travels over CTRL, while the device publishes the tag events a START triggers over DATA.

PlaneMessageDirectionTopicNote
CONTROLRequestApp → Reader<tenantId>/CTRL/clients/cmnd/<serial>On the control endpoint. Requires an active CTRL endpoint.
CONTROLResponseReader → App<tenantId>/CTRL/clients/resp/<serial>Correlated reply; the in-payload requestId links the twin topics.
DATAEventReader → App<tenantId>/DATA/clients/data1event/<serial>The dataEVT stream a RFID START triggers. Requires an active DATA1 endpoint to receive it.

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.

The command is answered only on the CTRL endpoint; the tag stream is published on the DATA1 endpoint's configured publish topic (data1event by default). DATA2 is currently not supported.

See Topics and Channels.

Key Fields​

  • ctrlOprPayload.controlType (request) — RFID (radio inventory engine) or SCANNER (barcode scanner).
  • ctrlOprPayload.operation (request) — START (begin) or STOP (halt).
  • response.code (response) — read this to determine success vs failure. The contract requires it; note the reported success-path omission (see Success without a code). See Response Codes.

Request Payload​

The envelope is command, requestId, and the ctrlOprPayload (controlType + operation). Pick an operation below; the Example and Schema update together. Use the Schema tab for the full field reference and the Example tab for the concrete payload.

Request payload
Example: RFID START
{
"command": "control_operation",
"requestId": "abcd1432",
"ctrlOprPayload": {
"controlType": "RFID",
"operation": "START"
}
}

Request Semantics​

Modes. ctrlOprPayload.operation selects the action for the chosen controlType:

  • START — begin the operation. For RFID, this starts an inventory cycle using the current operating mode and streams dataEVT events over the DATA endpoint.
  • STOP — halt the active operation. For RFID, the tag stream ceases (a single in-flight read may still be flushed shortly after — see Notes).

A STOP when no operation is active returns code 12 ("No Radio Operation in Progress") — this is not a failure, the device is already idle. A START while an inventory is already running returns code 11 ("Inventory in progress").

  • SCANNER uses the same request envelope as RFID. ctrlOprPayload declares one controlType (RFID | SCANNER) and one operation (START | STOP), both required, with no scanner-specific field and no separate payload — a scanner request differs from an RFID request only in the controlType value. Decoded barcodes are declared on the DATA plane, not in this command's response: the dataEVT data object carries barcodeData[] (decodedBarcode, plus symbology, the barcode type as a hexadecimal string such as 0x03) alongside tagData[]. Codes 11 and 12 above are radio-scoped — the contract describes them as an RFID inventory already running and no radio operation active — and the schema defines no scanner-specific response code, leaving code 23 for a controlType or operation outside its enum. Physical-trigger behavior and per-model scanner coverage are outside the schema and are not documented here — see Scan Barcodes.

Response Payload​

control_operation returns a status-only envelope — command, requestId, apiVersion, and response. The tag reads arrive separately as dataEVT events on the DATA endpoint; the response carries no data echo.

Response payload
{
"command": "control_operation",
"requestId": "abc123",
"apiVersion": "V1.1",
"response": {
"code": 0,
"description": "Success"
}
}

The contract requires response.code. Readers have been reported to omit it on a successful reply, returning just response: { "description": "Success" } — treat that as success too. See Success without a code.

As reported, a successful START or STOP can arrive with code omitted. This form does not validate against the contract; the reply with code: 0 above is the documented form:

Response — success reported without code (not accepted by the contract)
{
"command": "control_operation",
"requestId": "abc123",
"apiVersion": "V1.1",
"response": {
"description": "Success"
}
}

Response Codes​

control_operation 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.

CodeDescription
0Success — the radio (or scanner) started or stopped. (Readers have been reported to omit code on this reply — see above.)
11Inventory in progress (IOT_ERROR_INVENTORY_IN_PROGRESS) — a START was sent while an RFID inventory is already running. Stop the current inventory first; if the state is uncertain, check get_status.
12No Radio Operation in Progress (IOT_ERROR_NO_RADIO_OP_IN_PROGRESS) — a STOP was sent while the radio is already idle. Not a failure — no action required. Unique to this command.
23Invalid enum value (IOT_ERROR_INVALID_ENUM) — controlType or operation is outside its allowed set.
101Default error — generic catch-all.

Examples​

Publish control_operation over the CTRL endpoint; the device replies with the status envelope under Response Payload. The Operation dropdown under Request Payload shows the START and STOP requests. The examples below show what the dropdown does not: the tag stream that START triggers, and the two common error responses.

A RFID START triggers a dataEVT stream on the DATA endpoint. Each event's type echoes the active operating-mode profile, and the mode's tagMetaDataToEnable flags gate which tagData fields appear (here the default mode reports EPCid / eventNum / peakRssi / seenCount only):

DATA plane — dataEVT triggered by START (abridged)
{
"type": "BALANCED_PERFORMANCE",
"timestamp": "2026-06-16T06:03:36.894Z",
"data": {
"tagData": [
{
"EPCid": "E28011B0A506007D3DD07C0F",
"eventNum": 41,
"peakRssi": -73,
"seenCount": 256
},
{
"EPCid": "311100000000000000000000",
"eventNum": 42,
"peakRssi": -50,
"seenCount": 256
},
{
"EPCid": "11113333880600890630DBEC",
"eventNum": 43,
"peakRssi": -53,
"seenCount": 256
},
{
"EPCid": "000011C1A5000062F7952926",
"eventNum": 44,
"peakRssi": -65,
"seenCount": 256
},
{
"EPCid": "E200001B880600870630DBEB",
"eventNum": 45,
"peakRssi": -44,
"seenCount": 256
},
{
"EPCid": "1111333300004001B043ACB2",
"eventNum": 46,
"peakRssi": -36,
"seenCount": 256
},
{
"EPCid": "11113333333344445555AAAA",
"eventNum": 47,
"peakRssi": -32,
"seenCount": 256
},
{
"EPCid": "11113333A5000062F798E5B6",
"eventNum": 48,
"peakRssi": -34,
"seenCount": 256
},
{
"EPCid": "11113333333344445555AAAA",
"eventNum": 49,
"peakRssi": -36,
"seenCount": 256
},
{
"EPCid": "11115555222233334444A9B6",
"eventNum": 50,
"peakRssi": -43,
"seenCount": 256
},
{
"EPCid": "111133332222333344445555",
"eventNum": 51,
"peakRssi": -44,
"seenCount": 256
},
{
"EPCid": "111133332222333344445550",
"eventNum": 52,
"peakRssi": -43,
"seenCount": 256
},
{
"EPCid": "FFFFEEEE3435363742426666",
"eventNum": 53,
"peakRssi": -51,
"seenCount": 256
},
{
"EPCid": "11113333333344445555AAAA",
"eventNum": 54,
"peakRssi": -46,
"seenCount": 256
},
{
"EPCid": "11113333880600900630DBF1",
"eventNum": 55,
"peakRssi": -52,
"seenCount": 256
},
{
"EPCid": "E200001B880600840630DBE2",
"eventNum": 56,
"peakRssi": -57,
"seenCount": 256
},
{
"EPCid": "111133332222333344445555",
"eventNum": 57,
"peakRssi": -63,
"seenCount": 256
},
{
"EPCid": "111133332222333344445555",
"eventNum": 58,
"peakRssi": -63,
"seenCount": 256
},
{
"EPCid": "111133332222333344445555",
"eventNum": 59,
"peakRssi": -62,
"seenCount": 256
},
{
"EPCid": "E200001B880600870630DBEB",
"eventNum": 60,
"peakRssi": -44,
"seenCount": 256
},
{
"EPCid": "000011C1A5000062F7952926",
"eventNum": 61,
"peakRssi": -66,
"seenCount": 256
},
{
"EPCid": "11113333B22FB22FB22FB22F",
"eventNum": 62,
"peakRssi": -65,
"seenCount": 256
},
{
"EPCid": "AAAABBBB11112222CCCCDDDD",
"eventNum": 63,
"peakRssi": -60,
"seenCount": 256
},
{
"EPCid": "BEDD11112222333344445555",
"eventNum": 64,
"peakRssi": -45,
"seenCount": 256
},
{
"EPCid": "311100000000000000000000",
"eventNum": 65,
"peakRssi": -51,
"seenCount": 256
},
{
"EPCid": "11113333880600890630DBEC",
"eventNum": 66,
"peakRssi": -53,
"seenCount": 256
},
{
"EPCid": "1111FB63AC1F3841DADA00000000CAFEDADACAFEDADACAFE00001111",
"eventNum": 67,
"peakRssi": -57,
"seenCount": 256
},
{
"EPCid": "E28011B0A506007D3DD07D01",
"eventNum": 68,
"peakRssi": -72,
"seenCount": 256
},
{
"EPCid": "E28011B0A506007D3DD07C0F",
"eventNum": 69,
"peakRssi": -69,
"seenCount": 256
},
{
"EPCid": "E28011B0A506007D3DD07D00",
"eventNum": 70,
"peakRssi": -70,
"seenCount": 256
},
{
"EPCid": "11115555222233334444A9B6",
"eventNum": 71,
"peakRssi": -41,
"seenCount": 256
},
{
"EPCid": "111133332222333344445550",
"eventNum": 72,
"peakRssi": -45,
"seenCount": 256
},
{
"EPCid": "311100000000000000000000",
"eventNum": 73,
"peakRssi": -65,
"seenCount": 256
},
{
"EPCid": "111133332222333344445555",
"eventNum": 74,
"peakRssi": -61,
"seenCount": 256
},
{
"EPCid": "A1B22222BBBB",
"eventNum": 75,
"peakRssi": -65,
"seenCount": 256
}
]
}
}

A STOP issued while the radio is already idle returns code 12 (not a failure):

Response — STOP while idle (code 12)
{
"command": "control_operation",
"requestId": "abc123",
"apiVersion": "V1.1",
"response": {
"code": 12,
"description": "No Radio Operation in Progress"
}
}

A START issued while an inventory is already running returns code 11:

Response — START while inventory running (code 11)
{
"command": "control_operation",
"requestId": "abc123",
"apiVersion": "V1.1",
"response": {
"code": 11,
"description": "Inventory in progress"
}
}

Notes and Caveats​

Armed access operations execute on START

control_operation has no tuning parameters; a START runs whatever set_operating_mode last configured. If destructive access operations (WRITE / LOCK / KILL) are armed, START executes them on every tag in range — KILL and PERMANENT_LOCK are irreversible. Confirm the operating mode is read-only (no armed accessOperations) before starting a production inventory. See set_operating_mode § Notes and Caveats.

  • START drives two planes; the tag stream requires a DATA endpoint. The command round-trip is on the CTRL endpoint, but the tag reads a RFID START produces are published as dataEVT events on the DATA endpoint (<tenantId>/DATA/clients/data1event/<serial> for DATA1_EP). To receive them, a DATA1 endpoint must be active — activate one with config_endpoint. dataEVT.type echoes the active operating-mode profile, and the mode's tagMetaDataToEnable flags gate which tagData fields appear (EPCid is always present).
  • STOP is immediate at the radio, but a trailing read may still flush. A STOP stops the radio right away, yet a single in-flight / buffered read may still be delivered on the DATA plane a couple of seconds after the STOP (≈2.9 s observed on RFD40) before the stream fully ceases. Do not treat one trailing dataEVT after a STOP as continued inventory.
  • Configure the mode before you START. Set profiles, power, triggers, select filters, and access operations with set_operating_mode first, then START; control_operation itself sets none of these. The operating mode is volatile: it resets to the default (BALANCED_PERFORMANCE, no armed access operations) on reboot, so re-apply it after a reboot before starting.
  • An attached terminal has priority. Apps on a terminal attached to the reader, whether Android, iOS or Windows, always have priority over MQTT commands, and a user can start an inventory from the terminal while the reader is connected to the broker. What the reader replies to a control_operation that a terminal app overrides is not specified in the available sources. See The Reader Is Its Own MQTT Client.

control_operation is reversible (START ↔ STOP) and carries no credentials — the tag EPCid values in the resulting dataEVT stream are identifiers read off physical tags, not secrets. SCANNER control follows the same request/response shape as RFID (verified paths are RFID START/STOP on RFD40). For the concept-level view, see Inventory Control.

Related: 📕 set_operating_mode · 📕 get_operating_mode · 📕 get_status · 📕 dataEVT · 📕 Inventory Control · 📘 Scan Barcodes · 📕 Response Codes

Was this helpful?