Community /

Cyton BLE code now in alpha!

In July of 2017, Push The World was contacted by NEBA Health, LLC with the goal of developing firmware to stream data from the Cyton over BLE. As of today, the code base is not stable enough for a reliable medical grade platform, test systems show 1% packet loss. We want to make the Cyton over BLE firmware rock solid; so today we are open sourcing two new repositories to support Cyton over BLE.

An image of the reba health logo

Neba Health sponsored this awesome project!

The goal we set was to leverage the ADS1299 on the Cyton and aims to get two channels of data from the Cyton over BLE with zero packet loss. I, AJ Keller of Push The World, will present background research into the current state of the Ganglion (which uses BLE already) and Cyton, explain where the pitfalls were in getting a reliable data stream, and finally present new firmware and drivers to actually get BLE working on your device. Most of the links will point to actual code, gotta love open source. The intended audience is people looking to use BLE with the OpenBCI Cyton.



To get started, I looked at how the Ganglion works. The Ganglion single core micro controller is the Simblee, it’s a small device that can acquire data from the custom analog frontend and send that data over BLE to mobile phones or computers. The Ganglion by default uses a 19 bit delta compression or 18 bit delta compression when sending accelerometer data too. The Simblee uses the native BLE stack that BLE devices are ready to connect with.

Simblee module

Simblee module that powers the Ganglion


Taking a look at the Cyton, there are 3 micro controllers! The Pic32 micro controller gets brainwave data from the TI ADS1299 analog front end and sends data through a wire to an RFduino micro controller that then wirelessly sends data to another RFduino attached to a computer through USB. The RFduino on the Cyton is called the Device, and using a proprietary Nordic Gazelle stack, to wirelessly send data to the RFduino on the Dongle called the Host. BLE removes the Dongle from the pipeline.

Dongle on top of Cyton

The Dongle sits on top of the Cyton

Every 4ms the ADS1299 simultaneously samples 8 channels. Each channels voltage is recorded and in the field of neurotech, we call this 250 samples be second or 250Hz. Each channel is digitally represented by 24 bits (aka 3 bytes), adding the 6 auxiliary bytes, sample number, start and stop bytes to each sample, the Pic32 will go on to assemble a 33 byte packet every 4ms. If you have ever worked with OpenBCI raw data before, then you have seen this 33 byte code before. The Pic32 must then send data to the RFduino micro controller. This is where the problems began.

The Pic32 to RFduino pipeline is the biggest barrier to success.

The Pic32 sends serial data to the RFDuino at a serial baud rate of 115200. Sending data faster then 115200 results in catastrophic loss of data. At 115200, to send one byte from Pic32 to RFDuino takes 78 micro seconds ((8 data bits + 1 stop bit)/115200baud). That 33byte packet takes 2.6ms to travel from Pic32 to RFduino. Since new data is produced every 4ms, the RFduino has only 1.4ms to send the data to the Dongle before getting interrupted again. I completely rewrote the Device and Host firmware in 2016. I transformed the system, and drastically refactored the entire data pipeline. The current system is physically pushing the limits of the RFduinos. Several years before my time, the RFduino team built a custom fork for the RFduino to help OpenBCI get the system up to speed. The custom RFduino code is why we still have to use Arduino 1.5.8beta, and is included in the RFduino firmware by RFduinoGZLL.h.


To get the RFduino running a BLE stack that conforms to BLE standards, so as to be able to stream direct to smartphones and tablets, the RFduino firmware must import RFduinoBLE.h instead of RFduinoGZLL.h. Immediately the RFduino throws an error and fails to boot!

BLE + UART > 9600 baud not permitted due to critical BLE timing requirements.

To override, add: override_uart_limit = true; to the top of setup() in your sketch.

Take this warning seriously. Horrible data loss occurs at higher than 9600 baud. I tested at: 115200, 57600, 38400, 28800, 19200,  14400 and no matter what, data is lost. Instead of just changing the RFduino firmware, I had to change the Pic32 firmware to send data at 9600 baud to the RFduino instead of 115200 baud. Putting everything in perspective, to send 33 bytes at 9600 baud takes 31ms, meaning to send one byte takes a little under 1ms. Keep in mind the Pic32 must get new data from the ADS1299 every 4ms, meaning the single threaded Pic32 must read from the ADS1299 and store the new data to a buffer.

At 9600 baud, the Pic32 can only send 4 bytes every 4ms to the RFduino

There are 20 bytes per BLE packet as the standards dictate. In other words, the RFduino in BLE mode will have to wait a minimum of 20ms just to get 20 bytes from the Pic32. I downsampled the data rate to 125Hz and buffered three samples together on the Cyton to drastically reduce the number of times the Pic32 must send the RFduino data to just 42 times a second from 250 times a second in the other mode. The only last tricky part was the fact that the Pic32 must stop sending serial data every 4ms to get new data from the ADS1299, therefore, only four bytes are sent at a time from the Pic32 to the RFduino.

The Pic32 runs the BoardWithBLE.ino from the examples folder on GitHub. The Device RFduino runs DefaultRadio.ino and the computer for testing runs a NodeJS driver. The new custom RFduino firmware sends data as soon as it gets the full data packet from the Pic32.


Running with a Windows 10 system using a CSR BLE dongle sees a less than 1% data packet loss. I evaluated the packet loss using the NodeJS driver and using the packets received to count packet dropped, e.g. if a skip in sample number occurs between consecutive packet. The link is almost stable and small optimizations may lead to a powerful new way to send brainwave data.


I have presented firmware that will enable 2 channels of 24bit lossless data sampled at 250Hz, downsampled to 125Hz, and sent over BLE from the Cyton. As soon as the link has stabilized and the community is happy with it, I want to add compression to the Pic32 to send 4 channels instead of 2. The hope now is you, the community will embrace this much requested code and help us develop a strong and stable link of the Cyton over BLE.

A picture of AJ Keller

AJ Keller

This article and code presented was written by AJ Keller of Push The World LLC. Follow me on twitter and GitHub.

Leave a Reply