Turning a Raspberry Pi 3 into a Bluetooth Low Energy peripheral

The 3rd version of the Raspberry Pi is able to communicate with other devices over classic Bluetooth and Bluetooth Low Energy (BLE). Combined with its GPIO pins this makes an attractive device to control whatever is connected to it with a smartphone over BLE. I will point out some helpful sources and tipps I found while I was trying to use a Raspberry Pi 3 to control an LED matrix over BLE.

First you need to learn some things about BLE. You will not get far without knowing what advertising, services and characteristics are. I read the book “Getting Started with Bluetooth Low Energy: Tools and Techniques for Low-Power Networking” to learn about these things. It teaches you everything you need to know to start developing BLE devices on less than 80 pages. If you do not want to read a book, there is also a short introduction to BLE on Adafruit.

Like many other Linux distributions Raspian uses BlueZ as its Bluetooth stack. Because some BLE features were just added recently to BlueZ, you should update it. There is a step-by-step tutorial on Adafruit, just make sure that you download the latest version.

After successfully updating BlueZ you should take a look at the documentation for the advertising and GATT API. The python scripts example-advertisement and example-gatt-server show how these APIs can be used to create and advertise a fake BLE heartrate monitor.

The easiest way to create your own BLE peripheral is to take the classes for services, characteristics, desriptors, advertisements, etc. from the examples and use them for your own project. Using this approach I created a peripheral that allows me to control an LED matrix over BLE.


The Android app to control the matrix is shown below. Using Android’s native BLE API can be annoying because it requires you to implement a lot of callback functions. Instead of using the native API directly, the app uses the RxAndroidBle library, which makes communicating with BLE devices much easier.

16 Gedanken zu „Turning a Raspberry Pi 3 into a Bluetooth Low Energy peripheral“

  1. Hi Tobias,

    Can you provide a sample of Your android application also. I am doing something same using BLE on PI3 and Android application.
    Thanks in Advance. 🙂

    1. Hi. Sorry it took so long.

      The app’s source code is available on Github: https://github.com/WIStudent/Bluetooth-LED-Matrix-App
      I can’t tell you anything about Android’s own BLE API because I didn’t use it. Instead I used the RxAndroidBle library. You can find it with code examples here: https://github.com/Polidea/RxAndroidBle
      If you want to use it too and if are new to the ReactiveX approach, you probably should look first into ReactiveX and it’s Java imlementation RxJava: http://reactivex.io/intro.html
      I used these exercises to get familiar with it: https://github.com/mutexkid/rxjava-koans

  2. Great tutorial.
    I’m trying to run an adapted version of your code with Bluez V5.44.
    When I execute the led_matrix.py script as root, the Advertisement is registered as so is the GATT application. I also see a “raspberry” pi device in Android’s BLE scanner (removing the advertising from your code, I see no “raspberry pi” device). But if I try to connect to the device to list the supported GATT services and characteristics, I get CONNECTION FAILED.

    I also tried using BlueZ example-gat-server with pretty similar results.

    Do you have any idea about what the problem might be?

    Thanks in advance.

    1. If you want to try the example-gat-server script, make sure to start the example-advertisement script as well. The first one does only create the GATT services, the second one will start sending a corresponding advertisement. As far as I know it is not possible to connect to a BLE device that is not advertising.

      There is also the possibility that it is an issue with Android itself. Android’s BLE implementation is not the best. For example, Android expects a service/characteristic to always be reachable under the same handle. But BlueZ will most likely register your service under different handles if you start your script, terminate it, and then start it again. The only solution I found for this issue is to force close the BLE App on Android, to deactivate and then to activate Bluetooth again.

      I would advise you to first test your raspberry pi ble peripheral with something that is not an Android device, like a laptop or an iOS device (from what I have read iOS’ BLE implementation is much less buggy than Android’s). If you have a laptop with linux/blueZ, this tutorial might help you.

  3. Hi I downloaded the advertising part of it works well, but the service fails. I suspect that its a bluez version issue. What version of bluez are you using on the pi?

  4. Hi Tobias

    I get the software running on the pi. I.e. I can see it print out the registration and advertising and I can see it advertising from another device, but I can’t connect to it with anything. Am I missing something?

    1. What kind of device are you using to test the connection? If you are using an Android device, I noticed that Android can be quite buggy when it comes to Bluetooth Low Energy.

      You could also try running example-advertisement.py and example-gatt-server.py simultaneously. The files are in the test directory of the BlueZ source code and simulate a heart rate monitor. If they work, the issue must be in your own code.

      Another option is to check the state of your bluetooth device. Execute bluetoothctl to start the interactive console. Then use list to show the address of your bluetooth controller and then show <addr> to show its information. Use help to see what else you can do with this tool.

    1. I am not exactly sure what your problem is. You need to run the python code on the device that should act as a Bluetooth LE peripheral. Keep in mind that you need to customize the python code for your needs if you don’t have the same LED matrix I used. You could also try running example-advertisement.py and example-gatt-server.py first. They are included in BlueZ’s source code.

  5. How do you handle security between the devices? such as hogging. As when one device is connected to the bluetooth module, no other device can connect to it.

    1. As for security, you can set the corresponding flags on a characteristic to enforce authentication and/or encryption. To read/write a characteristic the devices must then not only be connected, but also be paired. Another option is to create a separate challenge-response service and only returning actual data on your other services if the central device responded correctly to your challenge.

      As for hogging, your peripheral device itself must detect that the connected central device is just blocking the connection for other devices right know. The peripheral could for example disconnect the connection if the challenge was not answered correctly or not answered at all in a specific time frame. It could also disconnect itself if the central device didn’t make read or write requests in a certain time.

      Unfortunately I can’t help you with the actual implementation of these features because I haven’t looked into theses topics that deeply. I think a good start would be to look into the BlueZ DBus-API documentation and figuring out how you can detect as a peripheral when a central device connected and how you can disconnect as a peripheral.

  6. Thank for your post.
    I updated bluez to v5.48 after that I run led_matrix.py on my raspberry and see the log below:
    GATT application registered
    returning props
    Advertisement registered
    But when I use a BLE scanner application on my IOS device, I can not see the Bluetooth Low Energy peripheral on my rasp.
    Could you help?

    1. It is possible that some things in the Bluez API changed between version 5.41 and 5.48. Especially in the advertisement API since it was still marked as experimental when I wrote the code. Have you tried running the example-advertisement and example-gatt-server from the BlueZ repository? (I linked the files from the master branch, if there were changes since version 5.48 you might need check out the appropriate version of these scripts) If these work you should compare the classes in bluez_components.py with the classes in the example scripts to check if there were any API changes.

      1. I found that with Bluez v5.48 we don’t need to add “–experimental” at the end of “ExecStart” line.
        So everything is work perfect now.

        Thank you for quick response!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.