Details on the Sphero API

Protocol Definition

All Sphero API packets share a single structure defined below. Packets start/end with the special SOP/EOP control bytes. These control byte values are not allowed unencoded anywhere else in the packet (see below). Packets are classified as either commands or responses, distinguished by a bit flag. Commands may be sent at any time by any node. Commands may optionally request a response from the receiver using a bit flag.

Packets can be directed to particular nodes using the “target ID” (TID) and “source ID” (SID). The TID and SID are each composed of two parts, a “port ID” and a “node ID” (See: Sending Information via Channels).

All packets have a two-byte ID code, separated into the “device ID” (DID) and “command ID” (CID). These bytes specify the command to execute. When sending a command, the sender uses the “sequence number” (SEQ) as a handle to link a particular command packet to its response. Responses echo the DID, CID, and SEQ to help the sender fully identify the corresponding command packet.

Responses contain a special “error code” field in the header that expresses the result of the corresponding command (see: Error Codes).

Both commands and responses may contain a data payload. In principle, this payload may be any size, though in practice the payload is limited by the receiver’s buffers. For forwarded commands, the data size may also be limited by intermediate nodes’ buffer sizes, depending on the implementation.

Finally, all packets contain a single byte checksum, which is computed over all other bytes in the packet (excluding SOP and EOP) before payload encoding.


Packet Structure

The Sphero API packet structure is shown below. All items are a single byte, except DATA, which is zero or more bytes. 

API Information

Packet Encoding

To avoid misinterpretations of the packet structure, the SOP and EOP bytes are never allowed in the rest of the packet. If these byte values are necessary in the payload, they are encoded before transmission and decoded by the receiving parser. The encoding method is an extension of SLIP encoding, using a two-byte escape sequence to replace special characters. This method necessitates a third special character, “escape” (ESC). The three special characters are encoded by prepending the ESC character and changing the original values to corresponding “escaped” values. These six values are shown in the table below. 

API Information

When the ESC, SOP, or EOP bytes are needed in the payload, they are encoded into (or decoded from) two-byte escape sequences as follows: 

API Information

The values for ESC, SOP, and EOP were specifically selected to minimize the cost of encoding. Additionally, these values were selected to provide a simple relationship between control values and their corresponding escaped value (unencoded value = escaped value | 0x88). 

Flags

API flags are used to modify the behavior of the API system. The available API flags are listed below. Bits are listed 0 to 7, where 0 is the least significant bit. 

API Information

Error Codes

API Information

Payload Data

Supported Types

API Information

Endianness

In the Sphero API, all multi-byte words are big-endian. Byte arrays and strings are sent in index-order (lowest indexed item first). 

Standard  (Preferred)  Units

We aspire towards using the standard units below to make it easier on both you and our own team to be able to develop with confidence and consistency across our commands. We'll do our best not to let you down on this front and to adhere to these units for all of the API commands we all have the pleasure of utilizing.


Packet Addressing and Routing

This API utilizes in-protocol packet routing, allowing peers to directly target each other’s API nodes. In addition to more clearly specifying communication paths, this feature lets processors host identical commands (e.g., “Get Main App Version” or “Echo”).

The Key Elements

The Sphero API is built on a system of nodes, which send and receive information. Each node is contained in a system and a system can contain more than one node. As an example, the RVR+/RVR is a system that contains two nodes, (1) the Bluetooth SOC, which also handles the power system, indications, the color sensor, and the ambient light sensor and (2) a general-purpose microcontroller which handles the control system, IR communication, USB, and the IMU.

Connections Between Key Elements

Nodes (yellow ovals below) can communicate with other nodes within their own system (blue rounded rectangle below) OR with nodes in systems directly connected to their own, however, you cannot talk to a system (blue rounded rectangle below) through another system (only systems with direct connections (“channels”) can communicate).

As an example, you can mount a board, such as a Raspberry Pi onto the top of RVR+/RVR and connect the Raspberry Pi to the RVR+/RVR via the robot's UART port (creating a direct connection/channel between the two). You can also use the Sphero Edu App on your phone to control your RVR+/RVR. Both the Raspberry Pi and the phone are connected to and can send information back and forth with the RVR+/RVR, but the phone and the Raspberry Pi cannot communicate with one another (at least not through the RVR+/RVR).


Sending Information via Channels

When information is sent between nodes (yellow ovals), there is a “source” node which sends the command and a “target” node which receives the command. In the Sphero API, when the target node (yellow oval) receives a command, it sends a response back to the original source node (yellow oval); the sending of this response packet causes the target and source to switch places (as the original target is now the sender (source) and the original source is now the receiver (target)).

The addressing system for each node (yellow oval) uses a two-part address, such that the first part of the address is the id of the port (red diamonds below) the packet leaves the system (blue rounded rectangle) through in order to get to the target node from the source node’s perspective and the second part of the address is the id of the node (yellow oval) that is the source or target (depending on whether we are reporting the address of the source or the address of the target). A first (port) value of “0” means we are staying within the system (blue rounded rectangle).


Just like port “0”, the internal port, is reserved to address nodes on the same device, node “0”, the wildcard node, is reserved for commands that do not target a particular node on a device. In this case, the target system (system containing the target node) decides which node will service the command, if any (when a node receives a packet with an empty target node id, if it can handle it, it does; otherwise, it passes the packet down the line and each node goes through the same decision-making process) .

For the address of the target node (yellow oval), the port (red diamond) through which the packet leaves the source system (blue rounded rectangle) to get to the target node is the first part of the address, and the id of the node where the packet is being sent is the second part of the address. For the address of the source, (again, from the source’s perspective) the source does not have to leave its system (blue rounded rectangle) to get to itself, so the value of the port is always “0” and the node value of the address is just the id of the source node (yellow oval).

To continue on with our example, below is a diagram of the two systems of the Raspberry Pi and the RVR+/RVR. The Raspberry Pi system (blue rounded rectangle) contains just one node (yellow oval) for its processor and the RVR+/RVR system (blue rounded rectangle) contains two nodes (yellow ovals), one each for the Bluetooth SOC and the general-purpose microcontroller mentioned above. The Raspberry Pi has a node id of “a” and the port (red diamond) to the Raspberry Pi system has an id of “1”. The Bluetooth SOC has a node id of “b”, the general-purpose microcontroller has a node id of “c” and the port (red diamond) to the RVR+/RVR system has an id of “2”. (Note: these ids are arbitrary and may or may not happen to coincide with the ids used in your individual systems.)


If a command is sent from the Raspberry Pi node (yellow oval) to the general-purpose microcontroller node (yellow oval), the addresses would be as such:

Now for the fun part: the response. As we noted above, the response, while it contains the same information (to allow us to check that everything arrived correctly), is a separate packet transmission, so (in our example) the source of the response is the general-purpose microcontroller node (yellow oval) and the target of the response is the Raspberry Pi node (yellow oval). This means that the addresses are now from the perspective of the general-purpose microcontroller. As such, the addresses for the response are:

Behind the Scenes

Note: The information (address system) above is all you truly need to understand in order to successfully use the API; this section is purely to dive a little deeper into how all of this is happening, for those who are curious.

In reality, a node only really knows about what is going on in its own system (so the Raspberry Pi node doesn’t know about RVR+/RVR system’s port 2 and the general-purpose microcontroller doesn’t know about the Raspberry Pi system’s port 1) and, furthermore, each node only knows the first step toward getting to another node (inside or outside of its own system (blue rounded rectangle)). Let’s dive into each of those concepts individually….

Nodes (yellow ovals) don’t know about other systems’ ports (red diamonds):

As was noted earlier, the addresses used to send the packets back and forth are sent from the source node for any given packet transfer exactly as we wrote them above. With the nodes only being familiar with their own systems, however, the addresses in a packet in transit must be translated before arriving at the target node, so that the target node knows where, from its own perspective, to send the response, once it is the source.

Using our same example as before:

As the command packet is crossing the channel between the two systems the address of the source is translated from 0a to 2a (so the port portion of the address changes to reflect which of the RVR+/RVR system’s ports the packet came through, but the node (yellow oval) it came from is still the same) and the address of the target is translated from 1c to 0c (so the port portion of the address changes to reflect that the target node (yellow oval) would not have to leave its own system (blue rounded rectangle) to find itself, but the id of the node (yellow oval) the packet is going to is still the same).

When a response is sent back, it, too, is translated so that the new target (the original source, Raspberry Pi) can understand the packet it has received and from where it received it. The response’s source address is translated from 0c to 1c (again, just the port portion of the address changes to one that the receiving node (yellow oval) will understand) and the response’s target address is translated from 2a to 0a (again, just the port portion of the address changes to one that the receiving node (yellow oval) will understand).

Again, you do not need to be an expert on this translation concept to successfully utilize the Sphero API; this concept is just a fun deep-dive for curious minds.

Nodes only know the first step towards their target:

To keep things simple and keep any node from having to do too much heavy lifting, each one has a “map”, of sorts, of its own system (blue rounded rectangle), which tells it which pathway to follow out of itself to get to each of the nodes (yellow oval) and ports (red diamonds) in its system. This “map” is known as a “table map” and is literally just a table that lists a corresponding direction to head in for each of the given targets a node has access to. Using our same example:


If, as in the cross-system examples above, node “c” wants to send information through port “2” (now do you see why we use the port (red diamond) through which the packet exits the system in the addresses we plug into the API?), it knows it must go through node “b”, and only that it must go through node “b”; there could be 7 more steps after node “b”, but node “c” knows only that, if it wants to get information to port “2”, it must pass that information to node “b” (per the dotted line in the above diagram). Node “b” has its own table that tells it what it needs to do in order to get information in its possession to the designated target. If node “b” receives a packet that is not addressed to node “b”, it checks its table to determine where that packet must go next. The above system is fairly simple, but this process really comes in handy for more complex systems, so that each node (yellow oval) does not have to store entire pathways for each of the targets it can access.

Optimizing Transmission

Nodes are required to respect two optimizing rules:

These rules allow senders to omit addressing in the following cases:

Together, these optimizations mean that the follow configurations never need to transmit source or target IDs:

Packet Routing

Packet routing may be implemented in any way that is convenient for a platform as long as commands are guaranteed to reach their targets, and targets produce valid responses. Specifically, if a target is invalid or unavailable, the sending node must receive the “Bad Target ID” or “Target Unavailable” from some node in the system.

Example Routing Strategy

Every node has a table that lists all the ports and nodes on its device. Each entry in the table maps to the channel that a packet should go through to reach that port or node.

When a packet is received, every node follows these rules:

Glossary


General Docs

Study up on the basics.

SDK Docs

Methods, Parameters, Return Values, Oh My!