2016-07-14 12:45:45

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ] test/example-gatt-server: Fix error on Property.GetAll

From: Luiz Augusto von Dentz <[email protected]>

get_properties is a method not a dictionary which cause the following
error:
TypeError: 'method' object is not subscriptable
---
test/example-gatt-server | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/test/example-gatt-server b/test/example-gatt-server
index 71aeb1b..84905f3 100755
--- a/test/example-gatt-server
+++ b/test/example-gatt-server
@@ -117,7 +117,7 @@ class Service(dbus.service.Object):
if interface != GATT_SERVICE_IFACE:
raise InvalidArgsException()

- return self.get_properties[GATT_SERVICE_IFACE]
+ return self.get_properties()[GATT_SERVICE_IFACE]


class Characteristic(dbus.service.Object):
@@ -164,7 +164,7 @@ class Characteristic(dbus.service.Object):
if interface != GATT_CHRC_IFACE:
raise InvalidArgsException()

- return self.get_properties[GATT_CHRC_IFACE]
+ return self.get_properties()[GATT_CHRC_IFACE]

@dbus.service.method(GATT_CHRC_IFACE,
in_signature='a{sv}',
@@ -222,7 +222,7 @@ class Descriptor(dbus.service.Object):
if interface != GATT_DESC_IFACE:
raise InvalidArgsException()

- return self.get_properties[GATT_CHRC_IFACE]
+ return self.get_properties()[GATT_CHRC_IFACE]

@dbus.service.method(GATT_DESC_IFACE,
in_signature='a{sv}',
--
2.7.4



2016-07-27 23:34:33

by Travis Griggs

[permalink] [raw]
Subject: Re: Disappointing BLE throughput scheme/results


> On Jul 19, 2016, at 4:48 AM, Luiz Augusto von Dentz <[email protected]> wrote:
>
> Try using l2test:
>
> /* Central */
> ./l2test -i 0 -s -V le_public -P 128 <peripheral bdaddr>
>
> /* Peripheral */
> - Make sure it is advertising an it is connectable
> ./l2test -t -V le_public -P 128
>
> The results for me is the following:
> l2test[1532]: Options [imtu 672, omtu 672, flush_to 0, mode 0, handle
> 74, class 0x000000, priority 0, rcvbuf 212992]
> l2test[1532]: Sending ...
> l2test[1533]: Receiving ...
> l2test[1533]: 672 bytes in 0.70 sec, 0.94 kB/s
> l2test[1533]: 672 bytes in 0.49 sec, 1.34 kB/s
> l2test[1533]: 672 bytes in 0.42 sec, 1.55 kB/s
> l2test[1533]: 672 bytes in 0.63 sec, 1.05 kB/s
> l2test[1533]: 672 bytes in 0.63 sec, 1.04 kB/s
> l2test[1533]: 672 bytes in 0.49 sec, 1.34 kB/s
> l2test[1533]: 672 bytes in 0.56 sec, 1.17 kB/s
> l2test[1533]: 672 bytes in 0.63 sec, 1.04 kB/s
> l2test[1533]: 672 bytes in 0.49 sec, 1.35 kB/s
> l2test[1533]: 672 bytes in 0.70 sec, 0.93 kB/s
> l2test[1533]: 672 bytes in 0.49 sec, 1.35 kB/s
> l2test[1533]: 672 bytes in 0.49 sec, 1.34 kB/s
> l2test[1533]: 672 bytes in 0.77 sec, 0.86 kB/s
> l2test[1533]: 672 bytes in 0.43 sec, 1.54 kB/s
> l2test[1533]: 672 bytes in 0.77 sec, 0.86 kB/s
> l2test[1533]: 672 bytes in 0.56 sec, 1.17 kB/s
> l2test[1533]: 672 bytes in 0.63 sec, 1.04 kB/s
>
> So very slow indeed, but that is using the default connection
> parameters which are probably not good for transfer speed.


Finally got to the point where I could run this. I ran the test between two of my SBCs. I got numbers similar to yours, but it only seemed to do about half as many before having connection issues. Not sure what that was about.

Being a neophyte at this, here’s what a little googling has led me to believe. Please correct as appropriate.

L2CAP is a lower level layer that works over both BTClassic as well as BLE? If you have it, you can create channels and stream data between the two.

This l2test gives an idea of what the current bluez stack, my particular usb ble dongle and host hardware MIGHT be able to achieve. It shows that ~1kB/s is achievable in some situation. It may be possible to go faster, but that is unknown. And I’ve already proved I can go slower. :)

It does not appear that iOS (or Android?) have any (or at least strong) support for L2CAP, so while this might give a demonstration, I wouldn’t be able to use it between handhelds and the linux SBC running bluez as a peripheral. So I’ll need to figure out a way to make the bluez gatt characteristics go as fast as possible.

Any useful hints on where I might instrument what to see where the time is being spent?

2016-07-19 11:48:02

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: Disappointing BLE throughput scheme/results

Hi Travis,

On Thu, Jul 14, 2016 at 10:37 PM, Travis Griggs <[email protected]> wrote:
> I’m working on an app that uses bluez on an embedded linux SBC, acting as a peripheral. I connect to it with an iOS central app for the purpose of configuration/query. I have to do some limited “streaming” of arbitrary byte lengths between the two to pull this off. So based on some of the feedback I received here, stack overflow, and some experimentation, I came up with a scheme for doing so (described briefly below). I’ve used this same scheme between a different iOS app and an embedded stmicro bluenrg chip, where I’m able to push 26KB firmware upgrades in 9.5 seconds. Unfortunately, I’m getting nowhere this kind of performance between my iOS app and my little linux device.
>
> The basic scheme involves using a control characteristic with verified writes/indications, and then a separate characteristic without verifications (e.g. write-without-response and notify) to send the actual data. This was based on the understanding that these unverified transactions can happen very fast and basically be buffered by the hardware/stack. I found through experimentation that iOS seems to have enough buffers for almost 32 writes before the data seems to disappear into the ether. So I use a scheme of bursts of 16.
>
> To be bidirectional, I have the following 3 characteristics set up
>
> Control (“write”, “indicate”)
> Request(“write-without-response”)
> Response(“notify”)
>
> As an example then, the central wants to make a 49 byte request, that will result in a 63 byte response. It would go down something like (in pseudo code-ish terms):
>
> iOS.Control.WriteValue(RequestSize: 49)
> bluez.Control.WriteValue()
> notes that 49 bytes are coming
> bluez.Control.ValueChanged(ReceivedRequest: 0)
> iOS.Control.ValueChanged()
> notes that 0 have been received so far, initiates a burst of up to 16 writes
> ios.Request.WriteValue(request[0:20])
> ios.Request.WriteValue(request[20:40])
> ios.Request.WriteValue(request[40:49])
> bluez.Request.WriteValue()
> appends first 20 bytes
> bluez.Request.WriteValue()
> appends second 20 bytes
> bluez.Request.WriteValue()
> appends last 9 bytes, notes that expected byte count has now been received and processes the request. derives the 63 byte response
> bluez.Control.ValueChanged(ResponseSize: 63)
> ios.Control.ValueChanged()
> notes that 63 bytes are coming
> iOS.Control.WriteValue(ReceivedResponse: 0)
> bluez.Control.WriteValue()
> notes that 0 have been received so far, initiates a burst of up to 16 writes
> bluez.Response.ValueChanged(response[0:20])
> bluez.Response.ValueChanged(response[20:40])
> bluez.Response.ValueChanged(response[40:60])
> bluez.Response.ValueChanged(response[60:63])
> ios.Response.ValueChanged()
> appends first 20 bytes
> ios.Response.ValueChanged()
> appends second 20 bytes
> ios.Response.ValueChanged()
> appends third 20 bytes
> ios.Response.ValueChanged()
> appends last 3 bytes, notes that the expected byte count has now been received and does its thing with the response
>
>
> I’ve been disappointed with this. I am using a python program on the bluez side, running on a 351 bogomip ARM7. I know there’s some overhead involved in the various layers as data makes its way from my blue tooth dongle through the kernel, bluetoothd, and over dbus to my python program. Placing some timing statements in the flow hasn’t been entirely conclusive, but one area that seems to be pretty regular, is that hand off where one side says “I want to send you X bytes” and then waits to hear back from the other side hearing the initial “so far I’ve got 0 bytes” which is the sending sides queue to burst up to 16 writes. That often takes ~110 ms. I’m not sure why that’s taking so long. I’m not sure if it’s split evenly between the write and the indicate, or if one is dominating. Any insights/suggestions/hints anyone has on this would be much appreciated.--

Try using l2test:

/* Central */
./l2test -i 0 -s -V le_public -P 128 <peripheral bdaddr>

/* Peripheral */
- Make sure it is advertising an it is connectable
./l2test -t -V le_public -P 128

The results for me is the following:
l2test[1532]: Options [imtu 672, omtu 672, flush_to 0, mode 0, handle
74, class 0x000000, priority 0, rcvbuf 212992]
l2test[1532]: Sending ...
l2test[1533]: Receiving ...
l2test[1533]: 672 bytes in 0.70 sec, 0.94 kB/s
l2test[1533]: 672 bytes in 0.49 sec, 1.34 kB/s
l2test[1533]: 672 bytes in 0.42 sec, 1.55 kB/s
l2test[1533]: 672 bytes in 0.63 sec, 1.05 kB/s
l2test[1533]: 672 bytes in 0.63 sec, 1.04 kB/s
l2test[1533]: 672 bytes in 0.49 sec, 1.34 kB/s
l2test[1533]: 672 bytes in 0.56 sec, 1.17 kB/s
l2test[1533]: 672 bytes in 0.63 sec, 1.04 kB/s
l2test[1533]: 672 bytes in 0.49 sec, 1.35 kB/s
l2test[1533]: 672 bytes in 0.70 sec, 0.93 kB/s
l2test[1533]: 672 bytes in 0.49 sec, 1.35 kB/s
l2test[1533]: 672 bytes in 0.49 sec, 1.34 kB/s
l2test[1533]: 672 bytes in 0.77 sec, 0.86 kB/s
l2test[1533]: 672 bytes in 0.43 sec, 1.54 kB/s
l2test[1533]: 672 bytes in 0.77 sec, 0.86 kB/s
l2test[1533]: 672 bytes in 0.56 sec, 1.17 kB/s
l2test[1533]: 672 bytes in 0.63 sec, 1.04 kB/s

So very slow indeed, but that is using the default connection
parameters which are probably not good for transfer speed.

--
Luiz Augusto von Dentz

2016-07-15 07:39:27

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ] test/example-gatt-server: Fix error on Property.GetAll

Hi,

On Thu, Jul 14, 2016 at 3:45 PM, Luiz Augusto von Dentz
<[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> get_properties is a method not a dictionary which cause the following
> error:
> TypeError: 'method' object is not subscriptable
> ---
> test/example-gatt-server | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/test/example-gatt-server b/test/example-gatt-server
> index 71aeb1b..84905f3 100755
> --- a/test/example-gatt-server
> +++ b/test/example-gatt-server
> @@ -117,7 +117,7 @@ class Service(dbus.service.Object):
> if interface != GATT_SERVICE_IFACE:
> raise InvalidArgsException()
>
> - return self.get_properties[GATT_SERVICE_IFACE]
> + return self.get_properties()[GATT_SERVICE_IFACE]
>
>
> class Characteristic(dbus.service.Object):
> @@ -164,7 +164,7 @@ class Characteristic(dbus.service.Object):
> if interface != GATT_CHRC_IFACE:
> raise InvalidArgsException()
>
> - return self.get_properties[GATT_CHRC_IFACE]
> + return self.get_properties()[GATT_CHRC_IFACE]
>
> @dbus.service.method(GATT_CHRC_IFACE,
> in_signature='a{sv}',
> @@ -222,7 +222,7 @@ class Descriptor(dbus.service.Object):
> if interface != GATT_DESC_IFACE:
> raise InvalidArgsException()
>
> - return self.get_properties[GATT_CHRC_IFACE]
> + return self.get_properties()[GATT_CHRC_IFACE]
>
> @dbus.service.method(GATT_DESC_IFACE,
> in_signature='a{sv}',
> --
> 2.7.4

Applied.


--
Luiz Augusto von Dentz

2016-07-14 19:45:16

by Travis Griggs

[permalink] [raw]
Subject: Request for Long Write/Read details

I noticed vestiges of the new options parameter in the 5.40 APIs being able to support long reads/writes. There doesn’t seem to be anything in test directory that demonstrates how these can be utilized.

I’m only vaguely familiar with how these long read/writes work. I’m curious which parts I as the programmer do, and which parts the stack does for me. If I just issue a ValueChanged with an array longer than 20, does it automatically break it up into multiple writes with the start/stop values appropriately attached? I’d be curious what I do in my iOS app to see the WriteValue light up with those options indicating the start/stop of each segment.

I’m particularly curious about this, given the performance I’m seeing using a scheme I concocted myself for streaming arbitrary length packets over the characteristics.


2016-07-14 19:37:40

by Travis Griggs

[permalink] [raw]
Subject: Disappointing BLE throughput scheme/results

I’m working on an app that uses bluez on an embedded linux SBC, acting as a peripheral. I connect to it with an iOS central app for the purpose of configuration/query. I have to do some limited “streaming” of arbitrary byte lengths between the two to pull this off. So based on some of the feedback I received here, stack overflow, and some experimentation, I came up with a scheme for doing so (described briefly below). I’ve used this same scheme between a different iOS app and an embedded stmicro bluenrg chip, where I’m able to push 26KB firmware upgrades in 9.5 seconds. Unfortunately, I’m getting nowhere this kind of performance between my iOS app and my little linux device.

The basic scheme involves using a control characteristic with verified writes/indications, and then a separate characteristic without verifications (e.g. write-without-response and notify) to send the actual data. This was based on the understanding that these unverified transactions can happen very fast and basically be buffered by the hardware/stack. I found through experimentation that iOS seems to have enough buffers for almost 32 writes before the data seems to disappear into the ether. So I use a scheme of bursts of 16.

To be bidirectional, I have the following 3 characteristics set up

Control (“write”, “indicate”)
Request(“write-without-response”)
Response(“notify”)

As an example then, the central wants to make a 49 byte request, that will result in a 63 byte response. It would go down something like (in pseudo code-ish terms):

iOS.Control.WriteValue(RequestSize: 49)
bluez.Control.WriteValue()
notes that 49 bytes are coming
bluez.Control.ValueChanged(ReceivedRequest: 0)
iOS.Control.ValueChanged()
notes that 0 have been received so far, initiates a burst of up to 16 writes
ios.Request.WriteValue(request[0:20])
ios.Request.WriteValue(request[20:40])
ios.Request.WriteValue(request[40:49])
bluez.Request.WriteValue()
appends first 20 bytes
bluez.Request.WriteValue()
appends second 20 bytes
bluez.Request.WriteValue()
appends last 9 bytes, notes that expected byte count has now been received and processes the request. derives the 63 byte response
bluez.Control.ValueChanged(ResponseSize: 63)
ios.Control.ValueChanged()
notes that 63 bytes are coming
iOS.Control.WriteValue(ReceivedResponse: 0)
bluez.Control.WriteValue()
notes that 0 have been received so far, initiates a burst of up to 16 writes
bluez.Response.ValueChanged(response[0:20])
bluez.Response.ValueChanged(response[20:40])
bluez.Response.ValueChanged(response[40:60])
bluez.Response.ValueChanged(response[60:63])
ios.Response.ValueChanged()
appends first 20 bytes
ios.Response.ValueChanged()
appends second 20 bytes
ios.Response.ValueChanged()
appends third 20 bytes
ios.Response.ValueChanged()
appends last 3 bytes, notes that the expected byte count has now been received and does its thing with the response


I’ve been disappointed with this. I am using a python program on the bluez side, running on a 351 bogomip ARM7. I know there’s some overhead involved in the various layers as data makes its way from my blue tooth dongle through the kernel, bluetoothd, and over dbus to my python program. Placing some timing statements in the flow hasn’t been entirely conclusive, but one area that seems to be pretty regular, is that hand off where one side says “I want to send you X bytes” and then waits to hear back from the other side hearing the initial “so far I’ve got 0 bytes” which is the sending sides queue to burst up to 16 writes. That often takes ~110 ms. I’m not sure why that’s taking so long. I’m not sure if it’s split evenly between the write and the indicate, or if one is dominating. Any insights/suggestions/hints anyone has on this would be much appreciated.