Task is to automate pairing and connection process between Arduino and Raspberry Pi over Bluetooth using D-BUS API based on python script.
The Bluetooth module connected to Arduino is: Grove - Serial Bluetooth v3.0.
I am able to automate pairing process. The pairing script does the following in order:
- It looks for an Bluetooth module named Slave by creating adapter object and using StartDiscovery method.(naming is done in the Arduino).
- Registers Bluetooth agent.
- Creates device object and pairs via Pair method if the device is not already paired.
The part of the code that does above steps given below:
register_agent()
start_discovery()
time.sleep(10)
for i in range(len(address_list)):
new_dbus_device = get_device(address_list[i])
dev_path = new_dbus_device.object_path
device_properties = dbus.Interface(new_dbus_device, "org.freedesktop.DBus.Properties")
pair_status = device_properties.Get("org.bluez.Device1", "Paired")
if not pair_status:
new_dbus_device.Pair(reply_handler=pair_reply, error_handler=pair_error, timeout=60000)
Here is what register_agent() does as requested:
def register_agent():
agent = Agent(bus, path)
capability = "NoInputNoOutput"
obj = bus.get_object(BUS_NAME, "/org/bluez");
manager = dbus.Interface(obj, "org.bluez.AgentManager1")
manager.RegisterAgent(path, capability)
However when I try to call Connect method as documented in device-api of Bluez, it always fails. I have created a custom serial port profile and tried ConnectProfile method but it failed again.
The communication over Bluetooth works if I use deprecated rfcomm tool, or it works if I use python socket module. However I want to avoid using rfcomm due to being deprecated. Regarding using python socket library, the connection works only in rfcomm channel 1, others channels produce Connection Refused error.
Adding MRE, to clarify the question better:
import dbus
import dbus.service
import dbus.mainloop.glib
import sys
import time
import subprocess
from bluezutils import *
from bluetooth import *
from gi.repository import GObject, GLib
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
path = "/test/agent"
AGENT_INTERFACE = 'org.bluez.Agent1'
BUS_NAME = 'org.bluez'
bus = dbus.SystemBus()
device_obj = None
dev_path = None
def set_trusted(path2):
props = dbus.Interface(bus.get_object("org.bluez", path2),
"org.freedesktop.DBus.Properties")
props.Set("org.bluez.Device1", "Trusted", True)
class Rejected(dbus.DBusException):
_dbus_error_name = "org.bluez.Error.Rejected"
class Agent(dbus.service.Object):
exit_on_release = True
def set_exit_on_release(self, exit_on_release):
self.exit_on_release = exit_on_release
@dbus.service.method(AGENT_INTERFACE,
in_signature="", out_signature="")
def Release(self):
print("Release")
if self.exit_on_release:
mainloop.quit()
@dbus.service.method(AGENT_INTERFACE,
in_signature="os", out_signature="")
def AuthorizeService(self, device, uuid):
print("AuthorizeService (%s, %s)" % (device, uuid))
return
@dbus.service.method(AGENT_INTERFACE,
in_signature="o", out_signature="s")
def RequestPinCode(self, device):
set_trusted(device)
return "0000"
@dbus.service.method(AGENT_INTERFACE,
in_signature="o", out_signature="u")
def RequestPasskey(self, device):
set_trusted(device)
return dbus.UInt32("0000")
@dbus.service.method(AGENT_INTERFACE,
in_signature="ou", out_signature="")
def RequestConfirmation(self, device, passkey):
set_trusted(device)
return
@dbus.service.method(AGENT_INTERFACE,
in_signature="o", out_signature="")
def RequestAuthorization(self, device):
return
@dbus.service.method(AGENT_INTERFACE,
in_signature="", out_signature="")
def Cancel(self):
print("Cancel")
def pair_reply():
print("Device paired and trusted")
set_trusted(dev_path)
def pair_error(error):
err_name = error.get_dbus_name()
if err_name == "org.freedesktop.DBus.Error.NoReply" and device_obj:
print("Timed out. Cancelling pairing")
device_obj.CancelPairing()
else:
print("Creating device failed: %s" % (error))
mainloop.quit()
def register_agent():
agent = Agent(bus, path)
capability = "NoInputNoOutput"
obj = bus.get_object(BUS_NAME, "/org/bluez");
manager = dbus.Interface(obj, "org.bluez.AgentManager1")
manager.RegisterAgent(path, capability)
def start_discovery():
global pi_adapter
pi_adapter = find_adapter()
scan_filter = dict({"DuplicateData": False})
pi_adapter.SetDiscoveryFilter(scan_filter)
pi_adapter.StartDiscovery()
def stop_discovery():
pi_adapter.StopDiscovery()
def get_device(dev_str):
# use [Service] and [Object path]:
device_proxy_object = bus.get_object("org.bluez","/org/bluez/hci0/dev_"+dev_str)
# use [Interface]:
device1 = dbus.Interface(device_proxy_object,"org.bluez.Device1")
return device1
def char_changer(text):
text = text.replace(':', r'_')
return text
def slave_finder(device_name):
global sublist_normal
sublist_normal = []
sublist= []
address = []
edited_address = None
sub = subprocess.run(["hcitool scan"], text = True, shell = True, capture_output=True)
print(sub.stdout) #string type
sublist = sub.stdout.split()
for i in range(len(sublist)):
if sublist[i] == device_name:
print(sublist[i-1])
sublist_normal.append(sublist[i-1])
edited_address = char_changer(sublist[i-1])
address.append(edited_address)
return address
def remove_all_paired():
for i in range(len(sublist_normal)):
sub = subprocess.run(["bluetoothctl remove " + sublist_normal[i]], text = True, shell = True, capture_output=True)
time.sleep(1)
if __name__ == '__main__':
pair_status = None
address_list = slave_finder('Slave') #Arduino bluetooth module named as "Slave", here we are finding it.
time.sleep(2)
remove_all_paired() #rfcomm requires repairing after release
print(sublist_normal)
if address_list:
register_agent()
start_discovery()
time.sleep(10)
for i in range(len(address_list)):
new_dbus_device = get_device(address_list[i])
dev_path = new_dbus_device.object_path
device_properties = dbus.Interface(new_dbus_device, "org.freedesktop.DBus.Properties")
pair_status = device_properties.Get("org.bluez.Device1", "Paired")
if not pair_status:
new_dbus_device.Pair(reply_handler=pair_reply, error_handler=pair_error, timeout=60000)
mainloop = GLib.MainLoop()
mainloop.run()
sudo btmon output:
Bluetooth monitor ver 5.50
= Note: Linux version 5.4.83-v7l+ (armv7l) 0.832473
= Note: Bluetooth subsystem version 2.22 0.832478
= New Index: DC:A6:32:58:FE:13 (Primary,UART,hci0) [hci0] 0.832481
= Open Index: DC:A6:32:58:FE:13 [hci0] 0.832484
= Index Info: DC:A6:32:5.. (Cypress Semiconductor Corporation) [hci0] 0.832487
@ MGMT Open: bluetoothd (privileged) version 1.14 {0x0001} 0.832490
@ MGMT Open: btmon (privileged) version 1.14 {0x0002} 0.832540
So the question is why Connect and ConnectProfile methods are always failing, what do I need to do establish bluetooth communication based on D-BUS API between Arduino and Raspberry Pi?
register_agent()
is doing. I am assuming your are registering your own agent; something along the lines of simple-agent? – ukBazAgent
code? What is inpair_reply
andpair_error
? What outputs are you getting in the terminal and fromsudo btmon
? Can I draw your attention to How to create a Minimal, Reproducible Example – ukBaz