#BLE extended advertising
1 messages · Page 1 of 1 (latest)
back in arduino bluefruit circa 2020 I had to give up on going over 31 bytes at all it seems.
In all casses I had been setting the the entire advertisement data with a byte array all at once directly with Bluefruit.Advertising.setData() there is no direct equivalent of that in CircuitPython's adafruit_ble library right?
Second problem is if I set up the rest of the data, long name, short name using the interfaces of Bluefruit.Advertising how can I set the advert data type 0x07 to a particular 16 byte array
used to do it with bluefruit set data
I attempted to use the low level _BLEIO api directly but got weird errors like "_bleio.BluetoothError: Unknown system firmware error: 0009"
The BLE library uses class variables that describe the different types of advertising fields. advertising/__init__.py only defines short_name, complete_name, and tx_power, but it provides the mechanism for building up other fields. Look for example in advertising/adafruit.py, which defines manufacturer_data, and color.
I think this is a use of the Python "descriptor" style of programming.
There is NO documentation about this, very unfortunately. I didn't write it myself, and spent quite a while scratching my head over it several years ago.
lol I know, isn't it fun working in the world of NO documentation. I found the most help digging arround the Bluetooth SIG protocol spec docs
which were very enlightening
in any case, I wouldn't advise you to use _bleio yourself. Take a look at other examples in Adafruit_CircuitPython_BLE_Beacon, Adafruit_CircuitPython_BLE_BroadcastNet, and Adafruit_CircuitPython_BLE_Eddystone
back in 2020 I had conseridered extending the library to create what I needed based off the short_name or complete_name calls. at the end of the day its the same thing with one difference in the 0xBlaBla type
it was the library structure itself, not the BLE specs that I spent more time head-scratching over. The headaches I had over BLE itself were a couple of years before that 🙂
the libraries I listed above all extend the base Advertisement class with additional fields that are constructed using the advertisement construction classes in __init__.py. you can see their structure along with the type code
again, each new field is a class variable that specifies the field. It's a specification, really. The same technique is used to specify service charcteristics.
from the eddystone code I can see that this is already interesting, https://learn.adafruit.com/circuitpython-ble-advertising-beacons/simple-beacon-code-in-circuitpython
# Reuse the BLE address as our Eddystone instance id.
eddystone_uid = uid.EddystoneUID(radio.address_bytes)
eddystone_url = url.EddystoneURL("https://adafru.it/discord")
while True:
# Alternate between advertising our ID and our URL.
radio.start_advertising(eddystone_uid)
Almost like if I set my byte array right in place of radio.address_bytes I could get somewhere
the beacon stuff is pretty dead in the BLE world now (I wrote it very early on)
See for instance https://github.com/adafruit/Adafruit_CircuitPython_BLE_BroadcastNet/blob/7e061a1d201ef6cecefdae9a57c5a46169cf18e8/adafruit_ble_broadcastnet.py#L70 and below, which defines a whole bunch of specizlied advertising fields.
(all ManufacturerDataFields0
the service ids you are listing should not need a new custom field, though. regular advertisements already handle a list of service ids
I wonder if that translates to 0xFF "manufacturere specific data" in the advertismetn itself or not.
i think ti does, then there are subtype fields
are you using ProvideServicesAdvertisement? That is in standard.py
I was looking at that but then the only docs I could find were here: https://docs.circuitpython.org/projects/ble/en/stable/advertising.html#adafruit_ble.advertising.standard.ProvideServicesAdvertisement and I dont know why but that particular description seemed about as clear as mud to me lol
services
List of services the device can provide. paricularly this
does this imply I can stick my 128 bit custom service UUID right in there and it would just work?
yes!
omg. lets give it a shot
it's designed to make it really easy to construct advertisements. It's just that the "really easy" was never written down.
or aparently demonstrated in an easy to find example lol
we thought we would have a lot of people who wanted to do this, but everyone just wanted to talk as a central to existing devices, or use the UART service.
just use bleio if you know the bytes you want to advertise: https://docs.circuitpython.org/en/latest/shared-bindings/_bleio/index.html#bleio.Adapter.start_advertising
some examples: https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/main/examples/ble_demo_periph.py
https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/main/examples/ble_json_peripheral.py
https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/main/examples/ble_uart_echo_test.py
lol makes sense and yeah am someone overhere who actually bought an adafruit sniffer and had been recreating my own modules that connect to existing central devices.
this is regular ProvideServicesAdvertisement, shouldn't need low level
@rustic zenith am I right that this is python descriptors? I keep forgetting
trust me, as of yesterday i had this line of code already in there, but ran out of examples to go by.
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
yes, it uses data descriptors to define the fields of the advertisement. The root class then packs and unpacks to buffers for you
and I'm over here trying to stick shift it all.
because before I understood what I was doing it was easier to copy the raw advertisment and spit that back out of my code to get these commercial off the shelf central device remotes to connect to me.
barely supported by the arduino bluefruit library and appartently not at all in the circuitpython one
to do it raw, just use the low level bleio that takes in a byte buffer
the library is trying to help pack and unpack automatically
I tried going low and got errors like this AttributeError: 'module' object has no attribute 'start_advertising'
data descriptors (AdvertisingDataField) are used to pack and unpack values to an underlying dict that is then packed and unpacked into a buffer
it is on the adapter, so _bleio.adapter.start_advertising()
ooooh
packing and unpacking is done here: https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/main/adafruit_ble/advertising/__init__.py
i have threatened to write a guide but have not followed through on that threat
LOL could have saved my bacon
good thing I do this for fun
Plus I am better at reading c libraries than python ones, the lack of hard typing by default throws my C brain for a loop.
that and the lack of code completion when I use notepad++ for CircuitPython leaves me a lot more high and dry than the arduino editor. Class name.(tell me what my options are mister IDE please)
I get the vibe I could install the library on my pc and use vscode and some plug ins to get similar results with CircuitPython but as far as I know there arent any how to guides for setting up vscode for CircuitPython development. Heck, I'm not even sure what everyone else is using out there.
I'm living it rough with nothing but notepad++ and putty on the serial port, though I just remembered now that I had used MU in the past.
lol of couse there would be an extension already, nice.
get the V2 extension. There is a dead extension that is broken with the same prefix name
ah yes, I do also have pycharm installed but had actually forgotten all about it.
and even a library manager?! not bad
autocomplete on all files in teh adafruit bundle. say no more
for on the board
for all the piles of feathers I have, no I didn't even know of circup. I've just been dragging the specific libraries I needed out of the latest bundle zips
Like a caveman apparently
after calling _bleio.adapter.start_advertising(advertising_data) I get:
_bleio.BluetoothError: Unknown system firmware error: 0009
would that be because I failed to init the radio or something?
are you using the BLE library at all?
and actually I had gotten that far yesterday when I tried doing radio = _bleio.adapter and calling start_advertising on there. So I was on the right trail of low level.
I backed up and tried without. I didn't realize I could make the low level call with the library in place. Let me try uncommenting the library init stuff
such as ble = BLERadio()
I'd use the normal radio from the library and just call radio._adapter.start_advertising() directly
radio just gets the advertising object packed up first: https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/1acb303cc7f63a752c9fb87655d2ec478e564be2/adafruit_ble/__init__.py#L168-L213
So I had tried
ble = BLERadio()
ble._adapter.start_advertising(advertising_data) and got the same firmware unkown error, are you refering to another radio from the library?
with this sample advert data
advertising_data = bytes([
0x02, # Length of the following data
0x01, # AD Type: Flags
0x06, # Flags: LE General Discoverable Mode, BR/EDR Not Supported
0x08, # Length of the following data
0x09, # AD Type: Complete Local Name
ord('M'), ord('y'), ord('D'), ord('e'), ord('v'), ord('i'), ord('c'), ord('e')
])
which I got from some example, but my byte array is a little less easy to read by hand.
should the 0x08 be 0x09 to include the ad type? I don't remember
what platform are you on?
since there are 8 letters in the name, including the AD type, the length should indeed be 9
ooh your referring to the firmware error number
I was focusing on the advertising type ids
the 8 should be a 9 and yes the error 9 is invalid length
the size value should include the adt byte(s)
ooh snap, yeah, I knew that but I think that sample advert might have come from google's not so smart AI
welp with that fixed the sample works! let me try my longer custom advert next
NRF connect dump of this broadcast by the way
so that was picked up by my phone. Imagine if the error could have included that detail
with the short name and the service it totally worked with the low level manual advertisment, and the physical (central) ble remote conected to the nRF via BLE running circuitpython for the first time, YAY, but once I added the long name, I did get hit with the a new error _bleio.BluetoothError: Unknown system firmware error: 000B
#define NRF_ERROR_INVALID_DATA (NRF_ERROR_BASE_NUM + 11) ///< Invalid Data
I'm going to mess around with this long name to see if I can't see what its problem is.
But in theory, using the low level directly to set the advertisment shouldnt interfear witht the rest of the library right?
I don't think so
maybe double check the library start_advertising impl to see if it has any extra state
this is so strange, my hang up is on setting anything in type 0x09 Complete Local Name at the same time as also having a type 0x08 Shortened Local Name, I dont get it. Even if I make the two names match same error 000B
long name then short name or short name then long name same results, Very strange.
Out of curiosity I wonder if I can go though the library to set this custom service like @danh suggested.
I did end up trying that but so far if I just feed it my byte array it complains with an attribute error: 'bytes' object has no attribute 'uuid'.
So I tried using it:
uart = UARTService()
UARTadvertisement = ProvideServicesAdvertisement(uart)
print(UARTadvertisement)```
which gets me this
<ProvideServicesAdvertisement services=<BoundServiceList: UUID('6e400001-b5a3-f393-e0a9-e50e24dcca9e')> flags=<AdvertisingFlags general_discovery le_only > >
a very nice and formed UUID.
but if I try to get my byte array uuid in there, I had to make my own object, since I wasn't sure what other object to instantiate for this.
ADV_ServiceUUID = bytes([0x11, 0x07, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45])
obj = type('', (), {})()
obj.uuid = ADV_ServiceUUID
advertisement = ProvideServicesAdvertisement(obj)
print(advertisement)
which gets me
<ProvideServicesAdvertisement services=<BoundServiceList: > flags=<AdvertisingFlags general_discovery le_only > >
No error message, and it even starts advertising with this(default board name shows up by the way)
but obviously missing an actual UUID..... so not sure what the issue there is.
Maybe if I new the path, I could take the UUID that was generated for the UART and replace it with mine.
Also I dont even think it would work since when I checked the advertisement being sent out for the uart, it's going out as an advertisement type 0x06 Incomplete List of 128-bit Service Class UUIDs SO not even a type 0x07 complete list. So good chance the ProvideServicesAdvertisement only sends out "incomplete type 0x06" adverts. Which brings me back to low level advert I guess? and firmware that gets very upset when I use low level to provide both a full name and a shortened name... (not an issue with over length by the way since just my service id and long name alone get us to 36 bytes which the nRF52840 happily broadcasts.)
good time to take a break for the night I guess
I feel like my ultimate fix would be I could set my complete services UUID type 0x07 as easily as I can set advertisment.complete_name
either that or if I could figure out how to setup my custom service by UUID (while ensuring that it is advertised on type 0x07) Because it appears that ProvideServicesAdvertisement isnt it at the moment.
There is support for 0x07 in ProvideServicesAdvertisement: https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/1acb303cc7f63a752c9fb87655d2ec478e564be2/adafruit_ble/advertising/standard.py#L176 .You could create a different subclass similar to ProvideServicesAdvertisement. It could be a bug.
These classes are used both to construct advertisements and to parse incoming advertisements.
So this is very interesting, I was able to use the library to create a service using the ProviceServicesAdvertisement interface but I still got the SAME 0x000b firmware error, so when I did some digging I found that even if I create a simple advertisement, the circuitpy complete name is being added on top of my custom complete name.
And interestingly the board doesn't mind that. But I get the error as soon as we try to advertise with two 0x09 names AND a 0x08 shortened name at the same time.
This method, worked to get my custom 0x07 service uuid going using the library, and it even shows up as the only complete name 0x09 but as soon as I add the shortened name, I get hit with firmware error 0x000b
you can set _bleio.adapter.name, I think. It fills in the complete_name automatically.
setting adapter.name should be the same as setting complete_name
It certainly SHOULD be, using the API to set the name like that actually did work. This marks the first time I was able to get my custom type 0x07, 0x08 and 0x09 to work all at once!
At the same time I would say we have officially found a bug somewhere in here, I am going to attach two different ways I can setup the advertising that both hit the same firmware 0x000b error. The only thing that works around it so far is going low level to set the adapter name directly after using a short name.
from adafruit_ble import BLERadio
from adafruit_ble import _bleio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement,ServiceList
from adafruit_ble.services.nordic import UARTService
from adafruit_ble.advertising import Advertisement#, ServiceData
def AdvertiseMethod1():
ble = BLERadio()
print(ble.name) #display board's current name
ADV_DATA = bytes([
0x02, 0x01, 0x06, #Flags: LE General Discoverable Mode, BR/EDR Not Supported
#0x09, 0x08,ord('M'), ord('y'), ord('D'), ord('e'), ord('v'), ord('i'), ord('c'), ord('e'),#uncomment this one to get the error 0x000b
0x11, 0x07, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,# COMPLETE SERVICE UUID
0x11, 0x09, ord('M'), ord('y'), ord('-'), ord('C'), ord('o'), ord('m'), ord('p'), ord('e'), ord('t'), ord('e'), ord('-'), ord('N'), ord('a'), ord('m'), ord('e'), ord('!'),
]) # I should be able to run all 4 lines uncommented, but setting a long and short name results in 0x000b
obj = type('', (), {})()
obj.advertisement_bytes = ADV_DATA
obj.address = None
obj.rssi = None
obj.connectable = True
obj.scan_response = False
advertisement = ProvideServicesAdvertisement(entry=obj)
#you can also mix and match using these name setting methods
#UARTadvertisement.short_name = "shortnm"
#UARTadvertisement.complete_name = "Complete_name"
############_bleio.adapter.name = "Complete-Name" #WORKS only if ADV_DATA lines 1,2,3 are uncommented
ble.start_advertising(advertisement)
##_bleio.adapter.name = "API Complete Name"
print(ble.name) #display board's current name
print("Advertising...")
AdvertiseMethod2()
while True:
# Normally other work would be done here after connecting.
pass
from adafruit_ble import BLERadio
from adafruit_ble import _bleio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement,ServiceList
from adafruit_ble.services.nordic import UARTService
from adafruit_ble.advertising import Advertisement
def AdvertiseMethod2():
ble = BLERadio()
print(ble.name) #display board's current name
adv = Advertisement()#.__bytes__=advertising_data
adv.connectable = True
#adv.short_name = "shortnm" #comment this and uncomment the next line to get two "complete" names
adv.complete_name = "Complete_name" #uncomment this one at the same time as above to get the error 0x000b
ble.start_advertising(adv)
##_bleio.adapter.name = "API Complete Name"
print(ble.name) #display board's current name
print("Advertising...")
AdvertiseMethod2()
while True:
# Normally other work would be done here after connecting.
pass
Method 2 is a very compact way to see the issue