Reverse Engineering Sphero R2D2 with Javascript
I bought my Sphero R2D2 two years ago, it was a cool toy for a Star Wars fans like me and a great pal for my cat. anyway after some time I started thinking to make some programming project with this beautiful cluster of electronics! I wanted to find a way to script my robot but I didn’t find anything well documented and maintained.
The only thing I knew about R2D2 is that it works using BLE technology and you can drive it using the official Sphero Droids app. After some search, I found only this article, a good starting point and some documentation about protocol communication … It wasn’t enough, the article and the script attached looked unfinished and official documentation had no specification about the messages that make R2D2 moving and dancing.
That’s why I decided to write some Javascript code to discover how to communicate with R2D2! In this article I’ll show you my personal experience on reverse engineering this droid: you can use this approach to any BLE device you want to hack.
TL;DR
Using an Android phone, Wireshark and the protocol documentation I discovered how the app communicates with R2D2 and wrote the driver using Node: you can jump to this repository and use the code to communicate with R2D2. The final result is in this video 📺
Setup
For this experiment is necessary:
- Basic knowledge of BLE protocol (a tutorial for beginners)
- A computer with BLE support (I’m using a MacBook Pro)
- An Android phone (I’m using an old Motorola with an Android 6)
- A Sphero R2D2 droid! (Amazon 📦)
The first thing to do is installing Wireshark and Android Developer tools on the PC:
- Wireshark is a network protocol analyzer useful for inspecting Bluetooth messages and can be downloaded from the official site.
- Android Developer tools contain adb executable to communicate with your Android phone from the PC, visit the official site for more informations.
On the Android phone install Sphero Droids app and enable Bluetooth HCI Spoofing feature under Developer options.
Using this feature, I’m able to obtain a file with all Bluetooth communication packets sent and received between devices.
Capturing data
Now, with BLE HCI Spoofing enabled, open the Sphero Droids app, connect R2D2 and play with it for some time.
After that, close the app and download the file generated on your disk using adb.
adb pull /sdcard/btsnoop_hci.log /dest/path
This file is generally saved under /sdcard/btsnoop_hci.log and can be opened with Wireshark.
Wireshark inspection
This is the most interesting part of the project: opening the file with Wireshark reveals a lot of useful information for reverse engineering the droid. This is what I got after my first session: there are a lot of information request packets sent between the Android device (localhost) and the droid (mine is labeled with the address d7:1b:52:17:7b:d6) and, after some scrolling, there’s the first write request!
As you can see in the bytes inspector the payload is quite eloquent: “usetheforce. ..band”. Sounds good :)
Another useful information is Service UUID and Characteristic UUID (handle 0x0015), annotate them to know where to send “usetheforce. ..band” message!
Now it’s time to read some documentation, starting from the Packet structure. This is the structure of a packet in Sphero’s protocol:
Every packet has a SOP (Start of packet) byte and an EOP (End of packet) byte, both equal to 0x8D and 0xD8, so it’s necessary to search for all those packets starting with SOP and ending with EOP.
Other interesting bytes are:
SEQ (Sequence Number): The token used to link commands with responses
DATA (Message Data): Zero or more bytes of payload data
CHK (Checksum): The sum of all bytes (excluding SOP and EOP) mod 256, bit-inverted
The first packet sent from the app is this:
0x8D 0x0A 0x13 0x0D 0x00 0xD5 0xD8
The SEQ byte here is 0x00 according to the packet structure schema: this is the first packet the app sends to the droid! Let’s call it the Init packet
As you can see, there’s another Service UUID and another Characteristic UUID (handle 0x001c) that will receive the next messages.
Another useful message to get is the last one at the end of the log file, sent from the app before closing, the packet to turn off the droid:
0x8D 0x0A 0x13 0x01 0x20 0xC1 0xD8
It’s time to annotate services, characteristics and messages (without SOP, EOP and other bytes) in some constants.
Let’s write some code
The final script will be composed by:
- a function to build a packet
- a function to connect R2D2 droid
- a function to write packets and wait for a response
- a function to turn off the droid
Building a packet
Building a packet is very straightforward because it’s just an array of bytes, starting with an SOP byte and ending with an EOP byte. There are two bytes that must be generated at runtime:
- SEQ byte: it’s just a variable initialized to 0x00 and incremented by 1 every time a packet is built.
- CHK byte: according to the documentation, CHK byte is the sum of all bytes (excluding SOP & EOP) mod 256, bit-inverted, really easy to generate:
There are other special bytes used in communication beyond SOP and EOP:
When the ESC, SOP, or EOP bytes are needed in the payload, they are encoded into two-byte escape sequences as follows:
This is the final code to build a valid packet for R2D2:
Connect our droid
In this example, to connect R2D2 with the PC using BLE technology, I use Noble library. I installed a special fork to make Noble working on macOS Catalina
yarn add git://github.com/lzever/noble.git
With Noble is really easy implementing a function to get the main characteristic used to communicate with the droid.
This script starts scanning all the devices around and select the device with the specific address provided, gets the connection service and sends “usetheforce. ..band” (MSG_CONNECTION) message to its characteristic (CONNECT_CHAR). After that, it’s time to get the “Main characteristic” to send commands to the droid! To do that, it’s better creating some code for writing and reading because I need to wait for some responses.
Write packets and read responses
This is the core part of the experiment: create a function to write commands and… wait for a response to read! When the app sends a message to the droid, it receives one or more response packets, as you can see from logs and/or read from the documentation:
Responses echo the DID, CID, and SEQ to help the sender fully identify the corresponding command packet.
If ERR byte is 0x00 no error happened during the command execution, else you need to check this link to know what happened.
Inspecting the Wireshark log you can see that there are some commands that receive another response after the echo-response and other commands that require a timeout (e.g. the bipod/tripod transformation).
To satisfy all these cases, the final write function must work in this way:
- Receives the characteristic object, the command to execute, a boolean to specify if it has to wait for another response after the echo and a timeout
- Sends the command to the characteristic
- Waits for the response, check if there are some errors and then resolve a promise (after some time if the timeout is greater than 0)
To enable ‘data’ receive handler, the function needs to subscribe to the main characteristic and read from it. The data packet has the same structure of a packet used to send commands, but now we have to check if there are some errors in the Error byte.
Supported types for payload data
Following the same process, I tried to know how to rotate the top. There are a lot of similar messages to make the top rotating starting with 0x0A 0x17 0x0F
I tried to rotate the top to near to 90 degrees and I got 32 bit of payload (0x42 0xb2 0x31 0x98, for simplicity 0x42b23198) with no value near to 90 so… this number must be not represented with an integer. Fortunately, there’s no special secret code in the droid protocol, following the documentation there are other types supported for the payload data
32 bits payload 0x42b23198 is very similar to a number encoded using IEEE754! Converting this value using an online tool I get 89.09686.
Adding a simple function to convert degrees to hex, this is the final code to rotate R2D2 top:
I tried to make a full rotation of the top but it’s not possible, droid replies with error 0x07 (data parameter invalid).
In the next episode, I’ll try to move R2D2, for now, you can check this repository containing some other functions like animations and bipod/tripod transformations.
In the final episode, I’ll try to write a real library, rewriting the code (maybe using some queue?) and adding support for other droids 🤖 Stay tuned.
That’s all folks 👋