#BLE extended advertising

1 messages · Page 1 of 1 (latest)

rigid ledge
#

and the high level adafruit_ble library just says 31 bytes max on advertising data.
so are you saying when you try to give more data than fits in 31 bytes it complains?

rustic summit
#

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"

rigid ledge
#

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.

rustic summit
#

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

rigid ledge
#

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

rustic summit
#

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

rigid ledge
#

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.

rustic summit
#

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
rigid ledge
#

the beacon stuff is pretty dead in the BLE world now (I wrote it very early on)

#

(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

rustic summit
#

I wonder if that translates to 0xFF "manufacturere specific data" in the advertismetn itself or not.

rigid ledge
#

i think ti does, then there are subtype fields

#

are you using ProvideServicesAdvertisement? That is in standard.py

rustic summit
#

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?

rigid ledge
#

yes!

rustic summit
#

omg. lets give it a shot

rigid ledge
#

it's designed to make it really easy to construct advertisements. It's just that the "really easy" was never written down.

rustic summit
#

or aparently demonstrated in an easy to find example lol

rigid ledge
#

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.

rustic zenith
rustic summit
#

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.

rigid ledge
#

@rustic zenith am I right that this is python descriptors? I keep forgetting

rustic summit
#

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

rustic zenith
#

yes, it uses data descriptors to define the fields of the advertisement. The root class then packs and unpacks to buffers for you

rustic summit
#

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

rustic zenith
#

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

rustic summit
#

I tried going low and got errors like this AttributeError: 'module' object has no attribute 'start_advertising'

rustic zenith
#

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()

rustic summit
#

ooooh

rustic zenith
rigid ledge
#

i have threatened to write a guide but have not followed through on that threat

rustic summit
#

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.

rustic summit
#

lol of couse there would be an extension already, nice.

rigid ledge
#

get the V2 extension. There is a dead extension that is broken with the same prefix name

rustic summit
#

ah yes, I do also have pycharm installed but had actually forgotten all about it.

#

and even a library manager?! not bad

rustic summit
#

autocomplete on all files in teh adafruit bundle. say no more

rigid ledge
#

for on the board

rustic summit
#

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

rustic summit
rustic zenith
#

are you using the BLE library at all?

rustic summit
#

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()

rustic zenith
#

I'd use the normal radio from the library and just call radio._adapter.start_advertising() directly

rustic summit
#

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.

rustic zenith
#

should the 0x08 be 0x09 to include the ad type? I don't remember

#

what platform are you on?

rustic summit
#

nRF52840

#

and 0x01 are the type flags, 0x09 is the long name

#

0x08 is short name

rustic summit
#

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

rustic zenith
#

the 8 should be a 9 and yes the error 9 is invalid length

#

the size value should include the adt byte(s)

rustic summit
#

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

rustic summit
#

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?

rustic zenith
#

I don't think so

#

maybe double check the library start_advertising impl to see if it has any extra state

rustic summit
#

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

rustic summit
#

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.

rustic summit
#

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.

rigid ledge
rustic summit
#

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

rigid ledge
#

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

rustic summit
#

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.

rustic summit
#
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

rigid ledge
#

See #welcome for how to post code. It would be good to open an issue on the BLE library to get this recorded more permanently. Thanks.