Creating BLE GATT Server (UART Service) on Raspberry Pi


In this post, I will create BLE GATT server on Raspberry Pi 3 using BlueZ dbus interface with Python. I will reuse BlueZ example code as much as possible. As an example of GATT service, I’ll create UART service (a.k.a. Nordic UART Service/NUS [1] ), so that I can test it with Nordic’s smartphone app [2].

 

Assumptions

 

Steps
1. BlueZ Update
Raspbian Stretch comes with BlueZ 5.43. BlueZ’s Advertising Manager has been officially supported from 5.48. So, let’s update BlueZ by following this post.

 

2. UART Service Implementation
I’ll use “example-advertisement” and “example-gatt-server” from the downloaded source code in Step 1. Some of the classes and functions will be reused.

2-1. First, let’s create a working directory.

 

2-2. Copy the example code with new names so that they can be imported as modules.

 

2-3. Create a new file (e.g. “uart_peripheral.py”). I’ll reuse ‘Advertisement’ class, ‘Service’ class, ‘Characteristic’ class, and callback functions from the example code. Also, combine the both main() functions from the example code into one and modify the sub-classes of Advertisement, Application, Service, and Characteristic to realize the UART service.

 

 

3. Test
3-1. Run the UART service on Raspberry Pi.

If all goes well, the output should be like this. Now, the service is running on Raspberry Pi and it’s broadcasting BLE advertising message.

3-2. Lauch nRF Toolbox app on the smartphone and tap on “UART”.

3-3. Tap on “CONNECT” button. Then the app will start scanning for nearby BLE devices.

3-4. Select your Raspberry Pi from the detected device list. It triggers the connection between the Raspberry Pi and the app.


 

 

 

 

 

* In case of iPhone, Raspberry Pi’s host name may be displayed instead of LOCAL_NAME in the code.

3-5. Tap on “Show Log”, enter some strings and tap on “Send”.

 

 

 

 

 

 

 

 

 

 

 

3-7. Check the console on Raspberry Pi. You should be able to see what you sent from the app like below.

3-8. Let’s send back something from Raspberry Pi by entering below on Raspberry Pi’s console.

3-9. Check the app screen.

 

 

 

 

 

 

 

 

 

 

 

 

References
[1] UART/Serial Port Emulation over BLE – Nordic Semiconductor 
[2] nRF Toolbox App – Nordic Semiconductor
[3] Turning a Raspberry Pi 3 into a Bluetooth Low Energy peripheral

 

 

Sponsor Link

8 Comments

  1. Hello first of all, congratulations! and thank you for your work. It has really helped me a lot.

    I have created a ble peripheral on a RPi using the same methodology as you. I have a service with two characteristics, temperature and humidity values. I can figure out how to specify that my characteristics publish double values… I use

    def updateSensorValue(self, sensorValue):
    print(‘Updating char. value’)
    self.value = sensorValue
    self.PropertiesChanged(
    GATT_CHRC_IFACE,
    {‘Value’: [dbus.Double(self.value)]}, [])

    to notify and this:

    def ReadValue(self, options):
    print(‘Reading data’)
    return [dbus.Double(self.value)]

    to let the value reading….

    I’m testing it with nRF Connect app from Nordic Semiconductor but allways read a unique byte…. Do you know what I’m missing? Thank you in advance !!!

    1. Hi Raúl, thank you for your comment!
      I think nRF Connect app interprets the receiving data as string… What happens if you convert the data (self.value) into a string before sending it to nRF Connect?

    1. Hi Enzo, thank you for your comment!

      Do you want to change “flags” value from 0x01 to 0x06? If so, 0x06 is not defined in the spec (i.e. Core Specification Supplement, Part A, Section 1.3).

      Or, are you trying to set AD type 0x06, which indicates 128-bit Service Class UUIDs? If that’s the case, the example above already is sending 128-bit UUID (i.e. UART_SERVICE_UUID), so I believe the advertising packet already has 0x06 AD type following the first AD structure (AD type: 0x01/”flags”). BTW, “flags” AD type is an essential part of BLE advertising packet. You need to have one in every advertising packet.

  2. I have an existing device with Raw advertising:

    02 01 06 02 0A 00 11 06 9ECADC240EE5A9E093F3A3B50100406E 0C 09 4D6F6F6E626F6172642041
    | l , t , v | l , t , v | l , t , v | l , t , v
    (length=l Type=t value=v)

    So, it does set 0x01 (flags) to 0x06. According to the supplement you referred this mean “LE General Discoverable Mode” and “BR/EDR Not Supported”

    According to
    https://stackoverflow.com/a/25039980/10657404
    set 0x01 to 0x06 can help to get discovered by Android. However they start from a flag 0x1a which according to the specification does not have any mean.

    Another think I am missing is how to set the advertising interval
    Thanks a lot

      1. Hi I setup the advertising as follow

        – set adv:
        sudo hcitool -i hci0 cmd 0x08 0x0008 adv packet

        – set scan response:
        sudo hcitool -i hci0 cmd 0x08 0x0009 {adv}
        [scan response](https://stackoverflow.com/questions/46431843/linux-bluez-custom-manufacturing-scan-response-data)

        – start adv:
        [info](https://stackoverflow.com/questions/16151360/use-bluez-stack-as-a-peripheral-advertiser)
        sudo hcitool -i hci0 cmd 0x08 0x000a 01
        In Bluetooth specification 5.0 (Core_v5.0.pdf), 7.8.5 LE Set Advertising Parameters command p.1321

        – adv time:
        [Adv time](https://stackoverflow.com/questions/21124993/is-there-a-way-to-increase-ble-advertisement-frequency-in-bluez)
        sudo hcitool -i hci0 cmd 0x08 0x0006 min max connectable (2bytes 2bytes 1bytes) 00 00 00 00 00 00 00 00 07 00

Leave a Reply

Your email address will not be published. Required fields are marked *