We use some essential cookies to make our website work.

We use optional cookies, as detailed in our cookie policy, to remember your settings and understand how you use our website.

Ventran
Posts: 142
Joined: Wed Dec 16, 2020 5:22 pm

Bluetooth pairing in Python code

Mon Feb 14, 2022 8:24 pm

Hello,
It is possible to establish connection between two devices (RPi and custom sensor node based on nRF52840) in Python program? The sensor node require Pairing (entering 6-digits code) during establishing conection.

I can connect to the sensor using:

Code: Select all

bluetoothctl
in RPi Linux command line. But I do not know how I can do this in Python.


Ventran
Posts: 142
Joined: Wed Dec 16, 2020 5:22 pm

Re: Bluetooth pairing in Python code

Tue Feb 15, 2022 8:42 am

I try use PyBluez library. I can scan and print device address. But I can not connect/pair to the device and read data.

However when I type in terminal:

Code: Select all

sudo bluetoothctl
scan on
pair CC:C8:41:10:2C:3b
trust CC:C8:41:10:2C:3b
in other terminal window:

Code: Select all

sudo rfcomm watch hci0
I see only "Waiting for connection on channel 1". My device (sensor) can not initialize connection like smarthpone with app Serial Bluetooth Terminal 1.36.

Using phone with android app I can read data from phone:

Code: Select all

sudo rfcomm watch hci0
Waiting for connection on channel 1
Connection from BC:A5:8B:8E:Df:Fc to /dev/rfcomm0
Press CTRL+C for hangup
and then

Code: Select all

minicom -b 9600 -o -D /dev/rfcomm0
MyData
MyData

User avatar
Douglas6
Posts: 5228
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, USA

Re: Bluetooth pairing in Python code

Tue Feb 15, 2022 5:42 pm

The nRF52840 is strictly a BLE device. PyBluez and rfcomm will be of no help in connecting to it.

Does your phone connect to the sensor?

Ventran
Posts: 142
Joined: Wed Dec 16, 2020 5:22 pm

Re: Bluetooth pairing in Python code

Wed Feb 16, 2022 5:42 am

Douglas6 wrote:
Tue Feb 15, 2022 5:42 pm
Does your phone connect to the sensor?
Yes. I using nRF Connect app.

Also I tested Bleak Lib. The RPi only connects to the sensor for about one second and disconnects suddenly. Before running the code below, the devices were paired by the bluetoothctl tool.

Code: Select all

import asyncio
import bleak
from bleak import BleakClient

address = "CC:C8:41:10:2C:3B" # Sensor MAC
MODEL_NBR_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e" # Sensor TX Characteristic

async def main(address):
    client = bleak.backends.bluezdbus.client.BleakClientBlueZDBus(address)
    try:
        await client.connect()
       
        #paired = await client.pair(protection_level=2)
        #print(f"Paired: {paired}")
       
        model_number = await client.read_gatt_char(MODEL_NBR_UUID)
        print("Data: {0}".format("".join(map(chr, model_number))))
    except Exception as e:
        print(e)
    finally:
        #await client.disconnect()
        print("Done")

asyncio.run(main(address))

I thinking about write wrapper in Python for bluetoothctl...

Ventran
Posts: 142
Joined: Wed Dec 16, 2020 5:22 pm

Re: Bluetooth pairing in Python code

Thu Feb 17, 2022 1:44 pm

Ventran wrote:
Wed Feb 16, 2022 5:42 am
I thinking about write wrapper in Python for bluetoothctl...
Currently I try pair Bluetooth devices in Python using sh module.

Code: Select all

from sh import bluetoothctl

print( bluetoothctl("power","on") )
print( bluetoothctl("discoverable","on") )
print( bluetoothctl("pairable","on") )
print( bluetoothctl("agent","NoInputNoOutput") )
print( bluetoothctl("default-agent") )
The last line of code give "No agent is registred".:

Code: Select all

pi@raspberrypi:~/Desktop/Bluetoothctl $ python test.py 
Changing power on succeeded

Changing discoverable on succeeded

Changing pairable on succeeded


Traceback (most recent call last):
  File "/home/pi/Desktop/Bluetoothctl/test.py", line 11, in <module>
    print(bluetoothctl("default-agent"))
  File "/home/pi/.local/lib/python3.9/site-packages/sh.py", line 1566, in __call__
    return RunningCommand(cmd, call_args, stdin, stdout, stderr)
  File "/home/pi/.local/lib/python3.9/site-packages/sh.py", line 822, in __init__
    self.wait()
  File "/home/pi/.local/lib/python3.9/site-packages/sh.py", line 879, in wait
    self.handle_command_exit_code(exit_code)
  File "/home/pi/.local/lib/python3.9/site-packages/sh.py", line 905, in handle_command_exit_code
    raise exc
sh.ErrorReturnCode_1: 

  RAN: /usr/bin/bluetoothctl default-agent

  STDOUT:
No agent is registered


  STDERR:

pi@raspberrypi:~/Desktop/Bluetoothctl $
How Can I registred bluetooth agent in Python code using sh module?

User avatar
Douglas6
Posts: 5228
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, USA

Re: Bluetooth pairing in Python code

Thu Feb 17, 2022 5:06 pm

You should run those commands in bluetoothctl to get more information. You should aso tell us the results of 'bluetoothd - v'.

But I think it has little to do with your problem. You see, the nRF52840 is strictly a BLE device. You don't need to (can't) pair classically with it.

Ventran
Posts: 142
Joined: Wed Dec 16, 2020 5:22 pm

Re: Bluetooth pairing in Python code

Fri Feb 18, 2022 7:13 am

Douglas6 wrote:
Thu Feb 17, 2022 5:06 pm
You should run those commands in bluetoothctl to get more information. You should aso tell us the results of 'bluetoothd - v'.

But I think it has little to do with your problem. You see, the nRF52840 is strictly a BLE device. You don't need to (can't) pair classically with it.

I ran the commands in terminal. bluetooth d -v give 5.55.

Moreover, I can pair and read characteristic using bluetoothctl utility in terminal. I want to do exactly the same in Python
bluetoothctl-min.png
bluetoothctl-min.png (108.18 KiB) Viewed 17518 times

Ventran
Posts: 142
Joined: Wed Dec 16, 2020 5:22 pm

Re: Bluetooth pairing in Python code

Fri Feb 18, 2022 10:35 am

I prepared another mini programm for controll bluetoothctl from python. This time I decided use subprocess module. However, the result is exacly the same when i use sh python module. Called command bluetoothctl default-agent from Python code return "No agent is registred".

Code: Select all

import subprocess
import time

def run_cmd(command: str):
        """ Execute shell commands and return STDOUT """
        process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = process.communicate()
   
        return stdout.decode("utf-8")
   
print ( run_cmd("bluetoothctl agent on") )
print ( run_cmd("bluetoothctl default-agent") )
print ( run_cmd("timeout 10s bluetoothctl scan on") )
print ( run_cmd("bluetoothctl pair CC:C8:41:10:2C:3B") )
print ( run_cmd("bluetoothctl connect CC:C8:41:10:2C:3B") )
print ( run_cmd("bluetoothctl info CC:C8:41:10:2C:3B") )
I know what the problem is. When I run bluetoothctl program in Linux terminal this program run another sub-programm [bluetooth]#. Something like parent-child hierarchy.
shell_sub-programm.png
shell_sub-programm.png (44.63 KiB) Viewed 17487 times

So, how can I interact with shell sub-program (child) from Python script?

User avatar
Douglas6
Posts: 5228
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, USA

Re: Bluetooth pairing in Python code

Sat Feb 19, 2022 1:26 am

1. You can continue to pipe commands through bluetoothctl (remove the pairing and agent commands), but remember each connection will cause bluetoothctl to start, load a default agent, perform your command, and then stop, ending any agent also. You can stack up commands, it's not the best way.

2. In Python you can use the pexpect library, a much more elegant way to interact with a CLI. Google knows about it.

3. Or use the Bluepy library to scan, connect, and read/write characteristics directly in Python.

There are other ways. I prefer #3.

By the way, your device is using the nRF(Nordic) UART service, I don' t recall seeing a Python library for sending/receiving that, but you should look for one. It wiil give you a large boost. Or rewrite various C implementations.

[EDIT: I did find this: https://scribles.net/tag/nordic-uart-service/]

Ventran
Posts: 142
Joined: Wed Dec 16, 2020 5:22 pm

Re: Bluetooth pairing in Python code

Mon Feb 21, 2022 12:51 pm

I found temporary solution. I prepared bash script for establish Bluetooth connection with authentication (passkey):

Code: Select all

#!/usr/bin/expect -f

set prompt "#"
set address [lindex $argv 0]

spawn sudo bluetoothctl 
expect -re $prompt
send "disconnect $address\r"
sleep 3
send "remove $address\r"
sleep 1
expect -re $prompt
send "scan on\r"
send_user "\nSleeping\r"
sleep 5
send_user "\nDone sleeping\r"
send "scan off\r"
expect "Controller"
send "trust $address\r"
sleep 2
send "pair $address\r"
sleep 2
send "123456\r"
sleep 3
send_user "\nShould be paired now.\r"
send "info $address\r"
expect eof
Function in Python call bash script with param:

Code: Select all

import subprocess

#==================================================================
#   FUNCTION:   Run Shell Command.
#   PURPOSE:    Call Bash script from Python code.
#               Input paramter should be a string.
#------------------------------------------------------------------
def run_cmd(command: str):
    process = subprocess.Popen(command, shell=True, 
              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = process.communicate()

    return stdout.decode("utf-8")
#------------------------------------------------------------------


#==================================================================
#                 BLUETOOTH MODULE CONTROLL COMMANDS
#==================================================================

#==================================================================
#   FUNCTION:   Bluetooth Connect
#   PURPOSE:    Disconnect and remove already connected device.
#               Trust and Pair with authentication BL device.
#------------------------------------------------------------------
def ble_connect(command: str):
    out = (run_cmd(command))
    if out.find("Pairing successful") != -1:
        print ("The Device connected successfully.")
        return True
    else:
        print ("The Device is not connected.")
        return False
#------------------------------------------------------------------
Start connection:

Code: Select all

ble_connect(f"./connect.sh {BL_DEVICE_MAC}")

Return to “Python”