Build a Wearable Motion Tracker (BLE From Arduino to a Custom Android Studio App)

Introduction: Build a Wearable Motion Tracker (BLE From Arduino to a Custom Android Studio App)

About: Biomedical Engineer

Bluetooth Low Energy (BLE) is a form of low power Bluetooth communication. Wearable devices, like the smart garments I help design at Predictive Wear, must limit power consumption wherever possible to extend battery life, and frequently utilize BLE. The Bluetooth Special Interest Group (SIG) defines several specifications a device should implement to interact with a Bluetooth device, which they term "profiles". Most application profiles utilize the General Attribute Profile (GATT) to send data over a BLE link. There are three fundamental concepts in BLE: profiles, services, and attributes.

Bluetooth SIG has standardized many common profiles, services, and attributes. However, when creating custom hardware there is often a need to create custom services and attributes and there aren't many tutorials available. To make matters more difficult, Adafruit does not provide any guidance on designing mobile applications to pair with their BLE modules and the source code for their applications is difficult to reverse engineer.

This tutorial aims to explain:

  1. How to design custom GATT services and characteristics
  2. How to program the Adafruit Bluefruit LE SPI Friend to act as a GATT server for these custom services & characteristics
  3. How to program an Android device to act as the GATT client to read the data from the GATT server

This tutorial is not intended to be translatable into a production-ready application - this is simply an introduction to BLE.

Background Reading:


Step 1: Design Custom Services & Characteristics


This article does a great job explaining how to design custom services & characteristics. I highly recommend reading through this article. I provide a very simple overview below that neglects the subtleties in favor of simplicity.

GATT Services are a collection of characteristics.

GATT Characteristics contain a property, a value, and zero or more descriptors.

  • Property: how the data should be handled by the client (Android App) e.g. read, write, write without response, notify, and indicate.
  • Value: the actual value of the characteristic e.g. 1089
  • Descriptors: this is information about the value e.g. the unit, milliseconds


Okay, now you know what services & characteristics are, we need to figure out how to design some services & characteristics to get our custom data and send it from our GATT server (Arduino) to the client (Android App). Let's consider an Arduino device that is collecting data from an accelerometer-gyroscope module (AGM). We want to collect gyroscope and acceleration measurements from three spatial axes and the time these measurements were taken and transmit this data to our mobile application. We also want to know when we need to charge the device, so we want to read the battery level and transmit that to our mobile application.

1. Can we use any of the standard services & characteristics?

Bluetooth SIG has standardized many common services and characteristics. First, check these to see if you can co-opt any of the standardized services and characteristics. The standard services and characteristics can utilize much smaller data packets as the Universally Unique Identifier (UUID) are 16 bits while custom services and characteristics must utilize 128 bits for their UUIDs. More on UUIDs later. From our search, we found a standardized "battery service" which contains one characteristic "battery level".

2. Separate all data values you want to send over BLE into characteristics and services

We can break our custom data points down into seven custom characteristics within one custom service. We will call this service the "AGM service". It will contain 7 characteristics: x-acceleration, y-acceleration, z-acceleration, x-gyroscope, y-gyroscope, z-gyroscope, and a time reference.

3. Determine the properties needed for each characteristic

There are several properties a characteristic may have.

  1. Read: the Client (Android App) can read a value from the GATT Server (Arduino)
  2. Write: the Client can change a value from the GATT Server
  3. Indicate: the Client will be notified if a value changes from the GATT Server and the Client is expected to send confirmation to the GATT Server
  4. Notify: the Client will be notified if a value changes from the GATT Server and the Client is not expected to send confirmation to the GATT Server

For this tutorial, we will set all our characteristics to read, with the exception of the battery level which will have both notify and read properties.

4. Generate UUIDs for custom services & characteristics and find standard UUIDs

As I briefly mentioned earlier, Bluetooth SIG standardized services and characteristics utilize a 16 bit UUID while custom services and characteristics utilize 128 bit UUIDs. For example, see the battery service assigned number on the Bluetooth SIG. The assigned number 0x180F represents the 128 bit UUID "0000180F-0000-1000-8000-00805F9B34FB". The four digits (16 bits) in bold are unique to the particular standardized service or characteristic while the other characters are conserved between all standardized services and characteristics. Since both the client and GATT server know that standardized services and characteristics only vary by the bolded digits, the packet sizes of data can be substantially reduced. However, custom services and characteristics cannot operate under this same assumption.

Instead, custom services and characteristics must utilize unabbreviated 128 bit UUIDs. Here is an online UUID generator. Any UUID other than the standardized UUID is acceptable for a custom UUID. However, a typical naming convention is to denote a custom service 00000001-... and characteristics within that custom service 00000002-...

Here is a summary spreadsheet of the services & characteristics we will be implementing along with their UUIDs.

Step 2: Arduino Code


First, hook up the Adafruit Bluefruit LE SPI Friend as they specify in their hookup guide and power up the Arduino device. Ensure you can find the Adafruit Bluefruit LE SPI Friend on your android device when scanning for Bluetooth devices. Download the Bluefruit Connect app, connect to the Adafruit Bluefruit LE SPI Friend and allow it to update the firmware on the device. This step is important. If you don't update the firmware, the commands you issue the device via Arduino will likely fail and there will not be an obvious error for you to discover what the problem is.

Here my repo for this project. You can see the full Arduino code here.


A few important things to note:

  1. In the setup() method, all custom UUIDs must contain "-" between every two characters. For example, "AT+GATTADDCHAR=UUID128=00-00-00-05-62-7E-47-E5-A3-FC-DD-AB-D9-7A-A9-66" will work. "AT+GATTADDCHAR=UUID128=00000005-627E-47E5-A3fCDDABD97AA966" will not work.
  2. Note that in the setup() method, "battery.begin(true);" calls "ble.reset();" automatically. If you don't use the battery service as I have, you need to reset the ble module (use "ble.reset();") where I have the command "battery.begin(true);".
  3. In the setup() method, change "if ( !ble.begin(false) )" to "if ( !ble.begin(true) )" if you want to debug.

This code is pretty self-explanatory. I included descriptions of each custom method. The setup method gets the BLE module ready to act as the GATT server. The loop method goes through a fake sweep of the accelerometer gyroscope module (AGM) and generates a random number from 1 to 100 for these values. The battery is drained by 1% to simulate battery use. You can replace this code with the actual sensor values easily. This code assumes you will transmit an array of AGM data, 6 measurements long, rather than a single measurement as analyzing a window of AGM data is likely more useful than a single point of data. If you alter the array size, note that there will be changes required in the android studio code. To capture an array of data, you must pass a counter along with the data you intend to send. This counter allows you to find where you are at in the window from the android studio application so you can wait to listen to the missing data points in the window. Without the counter or with a different sized array, the android studio project will either miss data points or will get stuck in a loop waiting for the remaining data points it expects.

Step 3: Android Studio Code

Here my repo for this project. You can see the full Android Studio code here.


I will keep updating this with a more thorough overview of how the arduino and android code work in detail... The app is fully functional so feel free to look at the code yourself in the meantime.

Step 4: Final Application

Congratulations! Your application is downloaded to your phone and your wearable device is charged and transmitting data.


To get started, click on the launcher icon for the application.


You will need to approve the use of some permissions for the app to work properly.


Next, click on the "Scan" button in the top left corner of the app.


Next, select your wearable device from the list of available BLE devices. Its name is "BLE Arduino Hardware".
Wait while the app gets the AGM data and determines where the user is still or moving.
Check out the results on the screen! Click the sync button to get another reading of data.

Battery Powered Contest

Participated in the
Battery Powered Contest

Be the First to Share


    • Anything Goes Contest

      Anything Goes Contest
    • Rice & Grains Challenge

      Rice & Grains Challenge
    • CNC and 3D Printing Contest

      CNC and 3D Printing Contest



    Question 9 months ago on" is doing? Telling the App that we are going to use a set of custom UUID's and associating these with each sensor value being sent by the Bluefruit hardware. Is my understanding correct?

    Thanks again!


    Answer 9 months ago

    Sorry - I have adding a more thorough commentary on the Android side on my TODO list.

    The UUID's are present in the advertising (which is why custom UUID's have larger data packet sizes than SIG UUID's). However, your mobile application still needs to know what those UUID's define so you can properly parse the data. The custom UUID's are defined in The BLE service then uses those values when parsing the data. So, your understanding is correct - change the UUID's in to match your own custom UUID's and then change the constants' names in the BLE service if you change the UUID name to something unique to your project.