2018-10-09 19:45:45

by Sylvain Leroux

[permalink] [raw]
Subject: Serial communication over BT using the DBus API

Hi everyone,

I'm trying to establish a serial communication between two hosts over
Bluetooth.

Things go well if I start the `bluetoothd` on the server in
_compatibility mode_. That way, I can attach the "serial port" profile
to the channel. Then, using `rfcomm watch` and `rfcomm connect` to
establish the connection between the server and the client, I can
receive "raw" data sent by the server:

----
# Server side
/usr/lib/bluetooth/bluetoothd -C
sdptool add --channel=1 SP
rfcomm watch hci0 1 bash -c 'cat somefile > /dev/rfcomm0'
----

----
#Client side
sudo rfcomm connect /dev/rfcomm0 BB:CC:DD:EE:FF:11 1
sudo cat /dev/rfcomm0
-----

If I do _not_ run the `bluetoothd` server in compatibility mode, SDP is
no longer supported and the data I receive seems to modified by the BT
stack, leading to my data being mixed with garbage--exactly like when I
don't set the "serial port" profile while running in compatibility mode.


I don't like the idea of depending on a deprecated interface. And from
what I read, I understand the new way to go is to use the DBus API. Any
solution using bluetoothctl, busctl, dbus-send or similar tools would do
the trick. But I can't manage to understand the sequence of operations
required to transmit raw data between the server and the client *from a
shell script* using that new API.

Could you give me some example or point me to the relevant documentation?

Regards,
- Sylvain Leroux


2018-10-09 20:31:03

by Barry Byford

[permalink] [raw]
Subject: Re: Serial communication over BT using the DBus API

Hello Sylvain,

On Tue, 9 Oct 2018 at 20:50, Sylvain Leroux <[email protected]> wrote:
>
> Hi everyone,
>
> I'm trying to establish a serial communication between two hosts over
> Bluetooth.
>
> Things go well if I start the `bluetoothd` on the server in
> _compatibility mode_. That way, I can attach the "serial port" profile
> to the channel. Then, using `rfcomm watch` and `rfcomm connect` to
> establish the connection between the server and the client, I can
> receive "raw" data sent by the server:
>
> ----
> # Server side
> /usr/lib/bluetooth/bluetoothd -C
> sdptool add --channel=1 SP
> rfcomm watch hci0 1 bash -c 'cat somefile > /dev/rfcomm0'
> ----
>
> ----
> #Client side
> sudo rfcomm connect /dev/rfcomm0 BB:CC:DD:EE:FF:11 1
> sudo cat /dev/rfcomm0
> -----
>
> If I do _not_ run the `bluetoothd` server in compatibility mode, SDP is
> no longer supported and the data I receive seems to modified by the BT
> stack, leading to my data being mixed with garbage--exactly like when I
> don't set the "serial port" profile while running in compatibility mode.
>
>
> I don't like the idea of depending on a deprecated interface. And from
> what I read, I understand the new way to go is to use the DBus API. Any
> solution using bluetoothctl, busctl, dbus-send or similar tools would do
> the trick. But I can't manage to understand the sequence of operations
> required to transmit raw data between the server and the client *from a
> shell script* using that new API.
>
> Could you give me some example or point me to the relevant documentation?

You might want to take a look at the Bluedot library that has used the
DBus API to communicate over the Serial Port Profile.
Server:
https://github.com/martinohanlon/BlueDot/blob/master/bluedot/btcomm.py#L141
Client:
https://github.com/martinohanlon/BlueDot/blob/master/bluedot/btcomm.py#L466

The main section using DBus is in:
https://github.com/martinohanlon/BlueDot/blob/master/bluedot/utils.py


Hope that helps.

>
> Regards,
> - Sylvain Leroux

2018-10-09 21:10:03

by Barry Byford

[permalink] [raw]
Subject: Re: Serial communication over BT using the DBus API

On Tue, 9 Oct 2018 at 21:56, Sylvain Leroux <[email protected]> wrote:
>
> Hi Barry,
>
> Thanks for the reply:
>
> On 10/09/2018 10:30 PM, Barry Byford wrote:
> > You might want to take a look at the Bluedot library that has used the
> > DBus API to communicate over the Serial Port Profile.
> > Server:
> > https://github.com/martinohanlon/BlueDot/blob/master/bluedot/btcomm.py#L141
> > Client:
> > https://github.com/martinohanlon/BlueDot/blob/master/bluedot/btcomm.py#L466
> >
> > The main section using DBus is in:
> > https://github.com/martinohanlon/BlueDot/blob/master/bluedot/utils.py
>
> I started to explore the bluedot sources. From what I understand, I need
> in the shell something like the `register_spp` function
> (https://github.com/martinohanlon/BlueDot/blob/master/bluedot/utils.py#L101)
>
>
> I'm currently investigating how I could use `busctl` to reproduce that
> behavior.

Not my area of expertise but it seems like it might be difficult
because there would be no event loop.
Hopefully someone else can jump in on that point.

>
> I noticed the function mostly send some XML description to the
> `org.bluez.ProfileManager1` interface (see code below).

The author has decided to do it that way. You don't have to. Here is a
different example:
https://gist.github.com/ukBaz/217875c83c2535d22a16ba38fc8f2a91#file-spp-profile

> There are a
> couple of hard coded constants in the code, including the UUID
> "00001101-0000-1000-8000-00805f9b34fb". Are those "well known" values I
> can blindly use?

That UUID refers to the Serial Port Profile. You can see the full list
of assigned numbers at:
https://www.bluetooth.com/specifications/assigned-numbers/service-discovery


>
>
> - Sylvain
>
>
> ----
> def register_spp(port):
>
> service_record = """
> <?xml version="1.0" encoding="UTF-8" ?>
> <record>
> <attribute id="0x0001">
> <sequence>
> <uuid value="0x1101"/>
> </sequence>
> </attribute>
> <attribute id="0x0004">
> <sequence>
> <sequence>
> <uuid value="0x0100"/>
> </sequence>
> <sequence>
> <uuid value="0x0003"/>
> <uint8 value="{}" name="channel"/>
> </sequence>
> </sequence>
> </attribute>
> <attribute id="0x0100">
> <text value="Serial Port" name="name"/>
> </attribute>
> </record>
> """.format(port)
>
> bus = dbus.SystemBus()
>
> manager = dbus.Interface(bus.get_object(SERVICE_NAME, "/org/bluez"),
> PROFILE_MANAGER)
>
> path = "/bluez"
> uuid = "00001101-0000-1000-8000-00805f9b34fb"
> opts = {
> # "AutoConnect" : True,
> "ServiceRecord" : service_record
> }
>
> try:
> manager.RegisterProfile(path, uuid, opts)
> except dbus.exceptions.DBusException as e:
> #the spp profile has already been registered, ignore
> if str(e) != "org.bluez.Error.AlreadyExists: Already Exists":
> raise(e)
> ----

2018-10-09 21:34:21

by Sylvain Leroux

[permalink] [raw]
Subject: Re: Serial communication over BT using the DBus API

Hi Barry,

Thanks for the reply:

On 10/09/2018 10:30 PM, Barry Byford wrote:
> You might want to take a look at the Bluedot library that has used the
> DBus API to communicate over the Serial Port Profile.
> Server:
> https://github.com/martinohanlon/BlueDot/blob/master/bluedot/btcomm.py#L141
> Client:
> https://github.com/martinohanlon/BlueDot/blob/master/bluedot/btcomm.py#L466
>
> The main section using DBus is in:
> https://github.com/martinohanlon/BlueDot/blob/master/bluedot/utils.py

I started to explore the bluedot sources. From what I understand, I need
in the shell something like the `register_spp` function
(https://github.com/martinohanlon/BlueDot/blob/master/bluedot/utils.py#L101)


I'm currently investigating how I could use `busctl` to reproduce that
behavior.

I noticed the function mostly send some XML description to the
`org.bluez.ProfileManager1` interface (see code below). There are a
couple of hard coded constants in the code, including the UUID
"00001101-0000-1000-8000-00805f9b34fb". Are those "well known" values I
can blindly use?


- Sylvain


----
def register_spp(port):

service_record = """
<?xml version="1.0" encoding="UTF-8" ?>
<record>
<attribute id="0x0001">
<sequence>
<uuid value="0x1101"/>
</sequence>
</attribute>
<attribute id="0x0004">
<sequence>
<sequence>
<uuid value="0x0100"/>
</sequence>
<sequence>
<uuid value="0x0003"/>
<uint8 value="{}" name="channel"/>
</sequence>
</sequence>
</attribute>
<attribute id="0x0100">
<text value="Serial Port" name="name"/>
</attribute>
</record>
""".format(port)

bus = dbus.SystemBus()

manager = dbus.Interface(bus.get_object(SERVICE_NAME, "/org/bluez"),
PROFILE_MANAGER)

path = "/bluez"
uuid = "00001101-0000-1000-8000-00805f9b34fb"
opts = {
# "AutoConnect" : True,
"ServiceRecord" : service_record
}

try:
manager.RegisterProfile(path, uuid, opts)
except dbus.exceptions.DBusException as e:
#the spp profile has already been registered, ignore
if str(e) != "org.bluez.Error.AlreadyExists: Already Exists":
raise(e)
----

2018-10-10 15:21:13

by Sylvain Leroux

[permalink] [raw]
Subject: Re: Serial communication over BT using the DBus API



On 10/09/2018 11:09 PM, Barry Byford wrote:
>> I'm currently investigating how I could use `busctl` to reproduce that
>> behavior.
>
> Not my area of expertise but it seems like it might be difficult
> because there would be no event loop.

I tried something like that:


----
busctl call \
org.bluez /org/bluez org.bluez.ProfileManager1 RegisterProfile \
'osa{sv}' \
/bluez \
"00001101-0000-1000-8000-00805f9b34fb" \
3 'Role' s server 'Channel' u 1 Name s 'SerialPort'
----

I'm not 100% confident with the syntax. I also tried using
`bluetoothctl` for that, but I'm even less confident with the syntax:

----
bluetoothctl << EOF
register-profile 'osa{sv}' /bluez "00001101-0000-1000-8000-00805f9b34fb"
3 'Role' s server 'Channel' u 1 Name s 'SerialPort'
EOF
----


Nevertheless, as you suggested, each of these solutions seems doomed
since, according to the documentation:


(from https://github.com/pauloborges/bluez/blob/master/doc/profile-api.txt)
> If an application disconnects from the bus all
> its registered profiles will be removed.


If I understand it well, that means even if I was able to register a new
profile, it will be removed as soon as `busctl` or `bluetoothctl`
terminate. Or did I misunderstand the whole thing?