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.

mkdir ble-uart-peripheral && cd ble-uart-peripheral

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

cp ~/bluez-5.50/test/example-advertisement ./example_advertisement.py
cp ~/bluez-5.50/test/example-gatt-server ./example_gatt_server.py

Note: The file name of python module must have the suffix “.py” and should not have “-” (hyphen) in it.

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.

import sys
import dbus, dbus.mainloop.glib
from gi.repository import GLib
from example_advertisement import Advertisement
from example_advertisement import register_ad_cb, register_ad_error_cb
from example_gatt_server import Service, Characteristic
from example_gatt_server import register_app_cb, register_app_error_cb

BLUEZ_SERVICE_NAME =           'org.bluez'
DBUS_OM_IFACE =                'org.freedesktop.DBus.ObjectManager'
LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
GATT_MANAGER_IFACE =           'org.bluez.GattManager1'
GATT_CHRC_IFACE =              'org.bluez.GattCharacteristic1'
UART_SERVICE_UUID =            '6e400001-b5a3-f393-e0a9-e50e24dcca9e'
UART_RX_CHARACTERISTIC_UUID =  '6e400002-b5a3-f393-e0a9-e50e24dcca9e'
UART_TX_CHARACTERISTIC_UUID =  '6e400003-b5a3-f393-e0a9-e50e24dcca9e'
LOCAL_NAME =                   'rpi-gatt-server'
mainloop = None

class TxCharacteristic(Characteristic):
    def __init__(self, bus, index, service):
        Characteristic.__init__(self, bus, index, UART_TX_CHARACTERISTIC_UUID,
                                ['notify'], service)
        self.notifying = False
        GLib.io_add_watch(sys.stdin, GLib.IO_IN, self.on_console_input)

    def on_console_input(self, fd, condition):
        s = fd.readline()
        if s.isspace():
            pass
        else:
            self.send_tx(s)
        return True

    def send_tx(self, s):
        if not self.notifying:
            return
        value = []
        for c in s:
            value.append(dbus.Byte(c.encode()))
        self.PropertiesChanged(GATT_CHRC_IFACE, {'Value': value}, [])

    def StartNotify(self):
        if self.notifying:
            return
        self.notifying = True

    def StopNotify(self):
        if not self.notifying:
            return
        self.notifying = False

class RxCharacteristic(Characteristic):
    def __init__(self, bus, index, service):
        Characteristic.__init__(self, bus, index, UART_RX_CHARACTERISTIC_UUID,
                                ['write'], service)

    def WriteValue(self, value, options):
        print('remote: {}'.format(bytearray(value).decode()))

class UartService(Service):
    def __init__(self, bus, index):
        Service.__init__(self, bus, index, UART_SERVICE_UUID, True)
        self.add_characteristic(TxCharacteristic(bus, 0, self))
        self.add_characteristic(RxCharacteristic(bus, 1, self))

class Application(dbus.service.Object):
    def __init__(self, bus):
        self.path = '/'
        self.services = []
        dbus.service.Object.__init__(self, bus, self.path)

    def get_path(self):
        return dbus.ObjectPath(self.path)

    def add_service(self, service):
        self.services.append(service)

    @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
    def GetManagedObjects(self):
        response = {}
        for service in self.services:
            response[service.get_path()] = service.get_properties()
            chrcs = service.get_characteristics()
            for chrc in chrcs:
                response[chrc.get_path()] = chrc.get_properties()
        return response

class UartApplication(Application):
    def __init__(self, bus):
        Application.__init__(self, bus)
        self.add_service(UartService(bus, 0))

class UartAdvertisement(Advertisement):
    def __init__(self, bus, index):
        Advertisement.__init__(self, bus, index, 'peripheral')
        self.add_service_uuid(UART_SERVICE_UUID)
        self.add_local_name(LOCAL_NAME)
        self.include_tx_power = True

def find_adapter(bus):
    remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
                               DBUS_OM_IFACE)
    objects = remote_om.GetManagedObjects()
    for o, props in objects.items():
        if LE_ADVERTISING_MANAGER_IFACE in props and GATT_MANAGER_IFACE in props:
            return o
        print('Skip adapter:', o)
    return None

def main():
    global mainloop
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    bus = dbus.SystemBus()
    adapter = find_adapter(bus)
    if not adapter:
        print('BLE adapter not found')
        return

    service_manager = dbus.Interface(
                                bus.get_object(BLUEZ_SERVICE_NAME, adapter),
                                GATT_MANAGER_IFACE)
    ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
                                LE_ADVERTISING_MANAGER_IFACE)

    app = UartApplication(bus)
    adv = UartAdvertisement(bus, 0)

    mainloop = GLib.MainLoop()

    service_manager.RegisterApplication(app.get_path(), {},
                                        reply_handler=register_app_cb,
                                        error_handler=register_app_error_cb)
    ad_manager.RegisterAdvertisement(adv.get_path(), {},
                                     reply_handler=register_ad_cb,
                                     error_handler=register_ad_error_cb)
    try:
        mainloop.run()
    except KeyboardInterrupt:
        adv.Release()

if __name__ == '__main__':
    main()

 

 

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

python uart_peripheral.py

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.
* See Troubleshoot when you get error.

$ python uart_peripheral.py 
GATT application registered
GetAll
returning props
Advertisement registered

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.

remote: hello from iphone!

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

hello from Raspberry Pi!

3-9. Check the app screen.

 

 

 

 

 

 

 

 

 

 

 

 

 

Troubleshoot
If you are getting “Failed to register advertisement” error below, it’s most likely because the advertisement wasn’t unregistered when the script exited previously.

Failed to register advertisement: org.bluez.Error.Failed: Failed to register advertisement

An easy way to recover is to restart bluetooth service.

sudo systemctl restart bluetooth.service

 

 

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

74 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`

          1. yes , it worked as explained above. However it would be nicer to use the d-bus api.

  3. Hi max great article. I’ve tested it my Pi Zero W (Raspbian Stretch & Bluez 5.50) and after removing -C flag from dbus-org.bluez.services (ExecStart=/usr/libexec/bluetooth/bluetoothd -C) it worked as expected and I was receiving UART characters from my Android phone, nRF Connect also saw my Pi’s UART Advertisement. Unfortunately since that initial success the script won’t work even after reboot, when I try to execute it I get:
    sudo python3 /home/pi/ble/uart_peripheral.py
    ERROR:dbus.connection:Unable to set arguments (dbus.ObjectPath(‘/’), {}) according to signature None: : Unable to guess signature from an empty dict
    Traceback (most recent call last):
    File “/usr/lib/python3/dist-packages/dbus/connection.py”, line 604, in msg_reply_handler
    reply_handler(*message.get_args_list(**get_args_opts))
    File “/usr/lib/python3/dist-packages/dbus/proxies.py”, line 401, in _introspect_reply_handler
    self._introspect_execute_queue()
    File “/usr/lib/python3/dist-packages/dbus/proxies.py”, line 387, in _introspect_execute_queue
    proxy_method(*args, **keywords)
    File “/usr/lib/python3/dist-packages/dbus/proxies.py”, line 137, in __call__
    **keywords)
    File “/usr/lib/python3/dist-packages/dbus/connection.py”, line 584, in call_async
    message.append(signature=signature, *args)
    ValueError: Unable to guess signature from an empty dict
    ERROR:dbus.connection:Unable to set arguments (dbus.ObjectPath(‘/org/bluez/example/advertisement0’), {}) according to signature None: : Unable to guess signature from an empty dict
    Traceback (most recent call last):
    File “/usr/lib/python3/dist-packages/dbus/connection.py”, line 604, in msg_reply_handler
    reply_handler(*message.get_args_list(**get_args_opts))
    File “/usr/lib/python3/dist-packages/dbus/proxies.py”, line 401, in _introspect_reply_handler
    self._introspect_execute_queue()
    File “/usr/lib/python3/dist-packages/dbus/proxies.py”, line 387, in _introspect_execute_queue
    proxy_method(*args, **keywords)
    File “/usr/lib/python3/dist-packages/dbus/proxies.py”, line 137, in __call__
    **keywords)
    File “/usr/lib/python3/dist-packages/dbus/connection.py”, line 584, in call_async
    message.append(signature=signature, *args)
    ValueError: Unable to guess signature from an empty dict
    ———————————————————————————————

    BTW example-advertisement still works.
    I would appreciate any suggestions what might be the cause of this error.

    1. I have the same problem. Maybe it’s because of a new version of one of the libraries used ?

      1. Hi Tom, AntoineB

        Thank you for your feedback. I quickly tested and found that the same error happens (not persistent but random) when I run the script with python3. Can you try it with python (i.e. python2.7) and let me know the result?

        1. Yes, it works with python2.7. As you said, I can make it work with python3 too but I often have this random error.

          I still have the problem when I stop/restart the script whereas I didn’t disconnect the Bluetooth on my iPhone or didn’t remove the mac of my iPhone on the Raspi.

          I think there is a problem of shutdown and deinitialization when you exit the script with ctrl+c.

          Thank you for the script anyway, it’s really great to understand ble/uart !

        2. I’ve also checked with python 2.7 but it’s also random, sometimes it works few times but then the error returns.
          AntoineB might be right, when I’ve worked with Bluetooth Classic I’ve used exit_handler(signalNumber) to catch termination signals. Not an expert on DBUS it’s shot in the dark.
          I might try with a fresh Rasbian.

        3. I came across the same issue and was able to resolve it by altering the find_adapter method for the custom Advertisement to:
          def find_adapter(bus):
          remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, ‘/’),
          DBUS_OM_IFACE)
          objects = remote_om.GetManagedObjects()

          for o, props in objects.items():
          if LE_ADVERTISING_MANAGER_IFACE in props:
          return o

          return None

          It’s basically what the example_advertisement class used but replaces objects.items() instead of objects.iteritems()

          1. I forgot to mention that I also added

            mainloop.quit()

            to the except KeyboardInterrupt in the main method.

          2. Hi Kyle, Tom, AntoineB,
            I’ve updated find_adapter() based on Michael’s comment. Now the code should work both with Python2 and 3! Thank you guys for your feedback!

    2. Have you solved this issue? My script still starts randomly, f.e. at first attempt or maybe at 10th

  4. Hi !
    Great tutorial !

    Is this possible to secure the connection ? What I mean is add a password so not every phone can connect to the Raspberry Pi server.

  5. Hi

    This is very nice post and very helpful for me to setup and create ble gatt server and uart service on raspberrypi.Both gatt server and uart service successfully running on my raspberrypi and i can connect to raspberrypi over ble with phone and send command message to raspberrypi and it working but currently in script TX characteristic

    class TxCharacteristic(Characteristic):
    def __init__(self, bus, index, service):
    Characteristic.__init__(self, bus, index, UART_TX_CHARACTERISTIC_UUID,
    [‘notify’], service)
    self.notifying = False
    GObject.io_add_watch(sys.stdin, GObject.IO_IN, self.on_console_input)

    i can see in TxCharacteristic class if i write something in console and after press enter raspberrypi transmit message to phone but i dont want to transmit message after write in console. i want to transmit message programmatically from python script so what changes i need to make in script for the same please help me.

    1. Hi kamlesh,
      Thank you for your comment! In order to transmit data programmatically, you can remove line 25 and call send_tx() from your code.

      1. Hi Max

        Thanks i can transmit data programmatically using send_tx() function but i want to use send_tx function outside txcharacterisitcs class for transmit data programatically from main function when i try to use send_tx fucntion outside txcharacterisitics class its not working please give me solution how i can i perform this.

        Thanks.

        1. I solved this issue by recreating the send_tx function somewhere else. I placed it in my new function by creating a global variable named ‘send’ and assigning that to the self value belonging to the TxCharacteristic class:

          class TxCharacteristic(Characteristic):
          def __init__(self, bus, index, service):
          Characteristic.__init__(self, bus, index, UART_TX_CHARACTERISTIC_UUID,
          [‘notify’], service)
          self.notifying = False
          global send
          send = self
          #GObject.io_add_watch(sys.stdin, GObject.IO_IN, self.on_console_input)

          I then used this send variable inside of the new function in order to send a value:

          #pretend obj is the string that you wish to send back
          def send(obj):
          s = obj
          global send
          value = []
          for c in s:
          value.append(dbus.Byte(c.encode()))
          send.PropertiesChanged(GATT_CHRC_IFACE, {‘Value’: value}, [])

  6. There’s something odd about find_adapter:
    “`
    def find_adapter(bus):
    remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, ‘/’),
    DBUS_OM_IFACE)
    objects = remote_om.GetManagedObjects()
    for o, props in objects.items():
    for iface in (LE_ADVERTISING_MANAGER_IFACE, GATT_MANAGER_IFACE):
    if iface not in props:
    continue
    return o
    return None
    “`
    Perhaps the `return o` is not indented to the right indentation level?
    The `for iface` apparently has no effect — this function always returns the first of the objects.items().

  7. ok, I see… find_adapter is wrong.
    The intention was apparently to find the first adapter that has both LE_ADVERTISING_MANAGER_IFACE and GATT_MANAGER_IFACE properties.
    But it always actually just returns the first adapter regardless.
    If that happens to be the right adapter, you’re fine; otherwise you get errors like “Unable to guess signature from an empty dict” that people have reported in earlier comments.

    Here’s a fix:
    “`
    +–
    |def find_adapter(bus):
    | remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, ‘/’),
    | DBUS_OM_IFACE)
    | objects = remote_om.GetManagedObjects()
    | for o, props in objects.items():
    | if LE_ADVERTISING_MANAGER_IFACE in props and GATT_MANAGER_IFACE in props:
    | return o
    | print(‘Skip adapter:’, o)
    | return None
    +–
    “`

  8. The comment mechanism on this web page strips leading spaces, which makes it hard to post Python snippets…
    “`
    def find_adapter(bus):
    ….remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, ‘/’), DBUS_OM_IFACE)
    ….objects = remote_om.GetManagedObjects()
    ….for o, props in objects.items():
    ……..if LE_ADVERTISING_MANAGER_IFACE in props and GATT_MANAGER_IFACE in props:
    …………return o
    ……..print(‘Skip adapter:’, o)
    ….return None
    “`

    1. Hi Michael,
      Thank you so much for pointing out my mistake and providing the fix! I updated the code in 2-3. Now it works both with Python 2 and 3! Again, I really appreciated your feedback!

  9. Thank you so much!! This really is a great Tutorial and it finally helped me to connect my iOS App with the Raspberry PI 4 <3

  10. Can you also describe how to connect from rPi to BLE module like HM-11/HM-17 and to add a device (/dev) which will act as a standard Bluetooth Serial (SPP)?

  11. Hello,

    Thank you for such a great tutorial. I tried going through the Bluez Documentation, but it’s too vast. I wanted a way to detect when a device is connected to the RPi BLE. Can you guide me through this?

    1. Hi Eric,
      By “ack or nak”, do you mean “Write Response” and “Error Response”? If so, it’s done under the hood by using “write” flag on line 56. When you use “write” flag, the other side (i.e. phone) uses “Write Request” to write the value, and RPi needs to respond back with “Write Response”. If you use “write-without-response” flag, phone uses “Write Command” and RPi doesn’t need to reply back to the command.

  12. I have a question that why my raspberry pi can connect with more than one iPhone at a time.

    1. Why not? 😉
      If you want to avoid multiple connections, you can do so by disabling the advertisement upon the first connection.

      1. How we can check the first connection happened and disable the advertisement? Can anyone provide dbus solution in this front?

  13. Hey! Liked your tutorial and it worked very well. Now am working on a project with nrf52 where nrf will take uart input from pc and send to RPI using BLE . The channel has established but RPI is missing to receive some of the packets. I have put a delay in sending end(nrf) and it works fine.. but the prblm is i need max data rate. I have read somewhere that the connecton interval can be changed to adjust the data rate. How to do that in rpi? Thanks

    1. I am sorry i didnt get u completely . If u meant in nrf side , yah its possible. But could u elaborate what can i do with that and how its gonna help.
      Also in RPI side i changed the max and min conn_interval in bluez reffering this https://unix.stackexchange.com/questions/288978/how-to-configure-a-connection-interval-in-a-ble-connection
      And it helped by reducing skipping rate of packets but still i lose 20% even in 4800 bps uart input.
      Thanks for the response.

    2. I am sorry i didnt get u completely . If u meant in nrf side , yah its possible. But could u elaborate what can i do with that and how its gonna help.
      Also in RPI side i changed the max and min conn_interval in bluez reffering this https://unix.stackexchange.com/questions/288978/how-to-configure-a-connection-interval-in-a-ble-connection
      And it helped by reducing skipping rate of packets but still i lose 20% even in 4800 bps uart input.
      Thanks for the response.

      1. Hi ANANTHAN,

        Did you manage to solve the issue? I also have a lot of packets lost. Please help.

        Lidia

  14. Does anyone get the issue where the connection is disconnected after 5-6 seconds? The airlog show rpi sends out Connection Termination command after a while. I tried with bluez 5.5/5.52, iPhone/Android but the issue happens all the times. What might be wrong?

  15. Hi, i’ve checked your code and it works with iOS but for some reason, it is not receiving messages from Android or sent messages are not visible on Android. Android part of code works since it is connecting correctly to other devices. I’m testing it on Android 9

  16. Hello,

    I am using this code to work with the Bluetooth gatt services on raspberry pi.
    I am facing some problems while calling send_tx() of TxCharacteristic into the RxCharacteristic.
    will you please help me to know how can I use this function in another class?

  17. Hello Community,

    I am using this code to work with the Bluetooth gatt services on raspberry pi.
    I would like to know how do I set multiple characteristics (like read, write, notify) to one service using the following function.
    Characteristic.__init__(self, bus, index, UART_TX_CHARACTERISTIC_UUID, [‘notify’], service)

  18. Hello Community,

    I am using this example Code and with the nRF Toolbox App the MTU is always set to 260. I don’t know where this 260 is even coming from.. Anybody knows how to change the mtu for this code and on the Raspberry Pi that I got a larger mtu?

  19. Hi Folks,
    I’m trying to use this code to create a connection between a Raspberry Pi 3 and Raspberry Pi Zero. I’ve been able to run the server on the first Pi and have been able to communicate via the CLI on the second Pi. Is there a way to have python create a UART client on the Pi Zero?

  20. Hello everybody, I trying implement this BLE example but I have this error…
    ModuleNotFoundError: no module named ‘example_advertisemnt’

    How much any problem if run this code on python3 or python2?

    Can you help me? Thanks!

  21. Hi max,
    Thanks for this awesome article. It works like a charm on my PiZero W. I’ve tried to run it on a Raspberry Pi 4 as well and it is hardly working. I have to try to connect multiple times (5-10) until I can can connect successfully. Any Idea what I can change in order to improve my situation on the Pi4?

    Thanks mate, and have a good one.
    Michael

  22. Thanks for this great info! I have been using your GATT example code for my project on RasPi 3B+ with Raspian Buster and Bluez 5.50. Only recently, when connecting to the GATT service from my iOS device, after connecting it asks for “Bluetooth Pairing Request” with a PIN on a popup after connection (using BluetoothLE app). I have tried “agent NoInputNoOutput” from command line before running but still no luck. How do I enable simple pairing for Bluetooth on the RasPi always? Or another way to fix / accept in code?

    1. I’ve run into the same issue. Trying to follow several guides to connect a phone to my RPi4B and I can’t connect. From the phones bluetooth menu I can pair but not connect. From the nRF app, it wants me to accept a Bluetooth Pairing Request” but it fails to complete the bond/connect process.

      If I find a solution, I’ll make sure to post it.

      1. So far, my Android 9 device (Samsung) can connect without Pairing/Bonding but my Anrdoid 10 device (Samsung) will try to Pair/Bond every time it connects. I know that Android 10 made some changes to how its handling bluetooth & security. So the question still remains, how do we make it work?

  23. Great info!
    Also want to call send_tx after receiving, never having used python before not sure how to do that, any examples ?
    There still also seems to be issues if ctrlC, the nRF52 I have does not disconnect automatically.

  24. Hey max, thank you for this. This script worked on iOS like a wonder 3 months ago but now every time I run a GATT client on my phone to communicate with the server it asks for a “Pairing Request” repeatedly. Even if I select “Pair”, it keeps asking me and the BLE connection keeps disconnecting and reconnecting over and over again. I’d really appreciate some help.

  25. All, just for completion:
    With the lastest raspberry pi os you have BlueZ 5.50 for free, so no need to update.

  26. Just tried this on my RPi3B and it works like a charm. I have my own Android app, and was able to get this going quickly. Thank you so much.

  27. Hello Max. Thank you very much for this. I’m experimenting an issue with this:

    Some of the messages that are sent through BLE (I can see all of them with btmon), are lost and the GATT server does not receive them. My system is sending multiple BLE packets per connection period (normally 2 or three) and in many ocasions I only see 1 of them.

    I’m using Raspberry Pi Zero W with internal Bluetooth chip. Do you think I might have some performance issues. Is the Raspi Zero too slow to receive all the packets?

    Thank you in advance,
    Lidia

  28. Hi community,

    I’m having an issue with this implementation. I’m receiving data from a mobile app into my Raspberry Pi Zero W by bluetooth through a GATT server like the one described in this post. If I check the bluetooth messages with btmon utility, all the expected packets are being received (I usually have more than 1 20MB packet per connection period). However, the UART receiver is only seeing part of the packets. Usually I see only the first packet sent and the others are lost. And eventually after some repetitions, there is one period in which I receive all the packets of the period (1 out of 20 periods or something like that).

    I’m suspecting that the Zero is too slow to be able to process all the packets. What do you think?

    Thank you,
    Lidia

  29. Hi, i think that i saw you visited my website so
    i came to “return the favor”.I am trying to find things to enhance my site!I suppose its
    ok to use a few of your ideas!!

    Also visit my web blog :: Xe888 apk

  30. Hi there. I’m a bluetooth noob, and trying to see if I can get iOS talking to a Raspberry Pi for simple messaging. This article seems like the closest thing I’ve found so far, but it seems a bit outdated now, so I’m having trouble getting it working. I’m on a new RPi4, using Python 3. It came with Bluez 5.50 installed by default, so I didn’t have to upgrade that, but the example files you mentioned don’t exist. I found some files at https://github.com/RadiusNetworks/bluez/tree/master/test but still having trouble. Not sure if the versions are wrong or if it’s a Python version issue. After fixing the “print” parentheses issues, the example file is throwing “ModuleNotFoundError: No module named ‘gobject'” and I don’t know what gobject is.

    I’d love to see an updated version of this article for Python 3 and anything else that’s new since it was written. Thanks!

    1. add this line
      #!/usr/bin/env python3

      to the top of the python file uart_peripheral.py

  31. Followup to my previous comment… I was able to find the correct version of the example files at https://github.com/hadess/bluez/tree/master/test . And the nRF Toolbox app does see the server from my iPhone 12. But when I touch to connect, I get the pairing prompt, which I touch “Pair”, the UI changes like it’s going to work, then it just disconnects after 1 second. No idea why it would be doing that. 🙁

    1. Another followup. I got this all working after a fresh Raspberry Pi install. I guess I had broken something on the bluetooth config on the pi while trying to figure things out. Now that I got it going, I turned your code into an easily reusable class that can be integrated into other apps and be useful for sending/receiving messages.

  32. Max, Absolutely magnificent stuff here great help. I’ve been trying different coding and looking for this information on how to create and set UUID Advertisement with set SERVICE and CHARACTERISTIC UUID.

    There is a Proprietry Android and IOS app that sends data to the Characteristic when data changes. I can send and received data after connecting via BLE Connect app on my phone. But can’t show or manipulate data from the app.

    I just don’t know what to do from here. I’ve asked for input from the developers of the app but don’t expect to get it as they no longer support it.

    So I basically want to code something in python that will show what I am receiving from the Server.

    Any help will be much appreciated and rewarded. Ian

  33. Hello,

    I have installed all the scripts on my Linux System and apps on Smartphones.

    I have follwoing topic:
    The UART connection on an Android-Smartphone works great.
    The UART connection on an iPhone is not really working.
    When I select UART at iphone, I can find the device at scanning.
    But the app can’t connect to the device.
    Screen on the App: Connecting…

    What can be the cause at iphone?

  34. Hello,
    How could I take the text displayed after the word remote and use it in another python script?
    Sorry to bother you, but I’m a beginner in python.

Comments are closed.