Imagine writing a software package that sends a single rpc_request node once every five minutes. You could easily track the status of your RPC request by simply waiting for the next rpc_ok or rpc_error node from the device.

However, what if we are required to send 100 requests in a one-minute timeframe? Identifying success or failure is no longer a trivial task. This is especially true for operations that result in partial failure, where only a portion of the requests succeed.

When using the HTTP protocol, the solution is simple - open a connection, start a request, and then close the connection when finished. The situation is more complicated over MQTT, which is a persistent, session-based protocol where many requests are sent over the same TCP socket.

CeleryScript authors can solve this problem by tagging each outbound request with a unique label arg. You can think of this as a sort of request ID.

Multiplexing requests with the label arg

FarmBot has the ability to send many commands over a single MQTT channel. Messages do not follow a call/response cycle seen with HTTP servers. Unlike HTTP, MQTT connections are persistent, full duplex connections. If you send three commands to a FarmBot, they might not come back in the order that they were received.

Request order != response order:

     | BROWSER  Request "ABC"             DEVICE
     |        ------------------------->
     |          Request "DEF"
Time |        ------------------------->
     |          Request "GHI"
     |        ------------------------->
     |          Response to "DEF"
     |        <-------------------------
     v          Response to "ABC"
              <-------------------------
                Response to "GHI"
              <-------------------------

To make sense of which messages have been acknowledged, the rpc_request, rpc_ok, and rpc_error nodes all contain a label argument. The label serves as a unique identifier for a message. Assigning unique IDs to each RPC message allows re-assembly of message order on arrival.

In the case of an rpc_error, it allows us to specify not only that an error has occurred, but also which command caused the error to occur.

Example

Here is an example of a move_relative command sent to a device over MQTT. First, a message is published to MQTT channel /bot/device_123/from_clients:

{
  "kind": "rpc_request",
  "args": { "label": "adslkjhfalskdha" },
  "body": [
    { "kind": "move_relative", "args": { "x": 0, "y": 0, "z": 0, "speed": 100 } }
  ]
}

After some time the bot will respond on MQTT channel /bot/device_123/from_device:

{
  "kind": "rpc_ok",
  "args": { "label": "adslkjhfalskdha" }
}

Notice that the label in the response matches the label in the request. When sending requests, any unique identifier can be used as a label. UUIDs are highly recommended.

Out-of-band responses

Not every piece of data that FarmBot works with can be transmitted as CeleryScript. When an RPC creates data that is not formatted as CeleryScript, the response is said to be “out of band”, meaning that it relays the information through a channel that does not support CeleryScript (typically the REST API or status channel on the Message Broker).

take_photo Creates an image resource in the REST API .
read_pin Updates the bot state tree and broadcasts over the appropriate status channel on the Message Broker.
read_status Forces the bot to re-transmit the entire status tree over the appropriate channel on the Message Broker.
sync Causes the bot to upload and download newly created resources on the REST API.

This list is not exhaustive

If you have questions about a specific RPC, please raise an issue on Github or the forum.