-
Notifications
You must be signed in to change notification settings - Fork 142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add ServiceData advertising element #243
Conversation
…ervicedata this was PR tinygo-org#123
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you looked at the documentation here?
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/org.bluez.LEAdvertisement.rst
gap_linux.go
Outdated
var serviceData = make(map[string][]byte) | ||
for uuid, data := range options.ServiceData { | ||
serviceData[New16BitUUID(uuid).String()] = data.([]byte) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My guess would be that you need to use map[string]interface{}
instead of map[string][]byte
(like manufacturer data but with a different key type).
gap_linux.go
Outdated
serviceData := make(map[uint16][]byte) | ||
if sdata, ok := props["ServiceData"].Value().(map[string]dbus.Variant); ok { | ||
for k, v := range sdata { | ||
// looks like bluez delivers the long-128bit-uuid even for 16-bit uuid ad-element and it needs to be converted |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes 128-bit UUIDs are commonly used in BlueZ.
YES thank you. It now works also on linux! |
I'm new to Bluetooth, so please excuse my ignorance. Should the list of service UUIDs include the 128-bit UUIDs for the service data? |
well yes. there is 16, 32 and 128 Bit uuid for service data. see the exhaustive list of possible advertising element types That beeing said, i recon the larger uuids are uncommon since there are only 31 bytes total space in the advertisment. |
Looking at it, there's a problem: the service data is specified as a map and maps are unordered in Go. This means the output won't be consistent. I think that's a problem. It should really be something that keeps order, like a slice. |
Also, I'm thinking we should perhaps use Sorry for all the back-and-forth, I really appreciate your work on this but would like to get the API right. |
Hi all, |
two questions: Shouldn't
Then we could reuse this Type also for Service Data (and call it more generically). Maybe for ManufacturerData we check if it is 16-Bit only. I saw this error declaration
I think |
88d7297
to
f0fb62b
Compare
right. I merged changes from #244 and did similar changes to ServiceData. I had problems with vscode working on the mac and windows files, as they were not recognized properly and were not parsed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't
CompanyID
be also aUUID
Type?
No. CompanyID
is always 16 bits, it's not a UUID (and in fact in a different namespace compared to other BLE UUIDs). It's just a 16-bit number representing a particular company.
I think
addFromOptions
should return this error instead of false if the payload does not fit?
It's returned at the caller side, so I don't think there's a problem right now. Though the code might be a bit cleaner if the error is generated inside addFromOptions
(so that it can return other kinds of errors too).
That said, I think it mostly makes sense in addFromOptions
, in other methods like addFlags
it doesn't make much sense to me.
gap_linux.go
Outdated
@@ -53,6 +53,10 @@ func (a *Advertisement) Configure(options AdvertisementOptions) error { | |||
for _, uuid := range options.ServiceUUIDs { | |||
serviceUUIDs = append(serviceUUIDs, uuid.String()) | |||
} | |||
var serviceData = make(map[string]interface{}) | |||
for uuid, data := range options.ServiceData { | |||
serviceData[New16BitUUID(uuid).String()] = data.([]byte) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type assert doesn't seem to be necessary.
serviceData[New16BitUUID(uuid).String()] = data.([]byte) | |
serviceData[New16BitUUID(uuid).String()] = data |
gap.go
Outdated
func (buf *rawAdvertisementPayload) ManufacturerData() []AdvertismentDataElement { | ||
var manufacturerData []AdvertismentDataElement | ||
for _, data := range buf.findAllFields(0xFF) { | ||
manufacturerData = append(manufacturerData, AdvertismentDataElement{ | ||
UUID: New16BitUUID(uint16(data[0])+(uint16(data[1])<<8)), | ||
Data: data[2:], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old code has been written in a way that avoids heap allocations. The rewrite introduces new heap allocations, which I think is a regression.
I agree the code is somewhat hard to read. It might be possible to make it easier to read using function iterators, though I'm not entirely sure about that.
gap.go
Outdated
var serviceData []AdvertismentDataElement | ||
for _, data := range buf.findAllFields(0x16) { //16-Bit uuid | ||
serviceData = append(serviceData, AdvertismentDataElement{ | ||
UUID: New16BitUUID(uint16(data[0])+(uint16(data[1])<<8)), | ||
Data: data[2:], | ||
}) | ||
} | ||
for _, data := range buf.findAllFields(0x20) { //32-Bit uuid | ||
serviceData = append(serviceData, AdvertismentDataElement{ | ||
UUID: New32BitUUID(uint32(data[0])+(uint32(data[1])<<8)+(uint32(data[2])<<16)+(uint32(data[3])<<24)), | ||
Data: data[4:], | ||
}) | ||
} | ||
for _, data := range buf.findAllFields(0x21) { //128-Bit uuid | ||
var uuidArray [16]byte | ||
copy(uuidArray[:], data[:16]) | ||
serviceData = append(serviceData, AdvertismentDataElement{ | ||
UUID: NewUUID(uuidArray), | ||
Data: data[16:], | ||
}) | ||
} | ||
return serviceData |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With a loop like the one that was previously in ManufacturerData
, you could make a single loop that loops through all fields only once (and avoids lots of unnecessary heap allocations!).
gap.go
Outdated
buf.data[buf.len+0] = uint8(fieldLength - 1) | ||
buf.data[buf.len+1] = 0xff | ||
buf.data[buf.len+2] = uint8(key) | ||
buf.data[buf.len+3] = uint8(key >> 8) | ||
copy(buf.data[buf.len+4:], value) | ||
buf.data[buf.len+0] = byte(fieldLength - 1) | ||
buf.data[buf.len+1] = 0xFF | ||
buf.data[buf.len+2] = byte(uuid.Get16Bit()) | ||
buf.data[buf.len+3] = byte(uuid.Get16Bit() >> 8) | ||
copy(buf.data[buf.len+4:], data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With all these extra (unrelated!) changes it becomes a bit hard to review this PR. I appreciate the effort but I'd prefer to keep PRs small and focused on a single thing.
The larger a PR, the harder it is to review and the longer it'll take to merge the features you actually care about.
gap.go
Outdated
// addServiceData adds service data ([]byte) entries to the advertisement payload. | ||
func (buf *rawAdvertisementPayload) addServiceData(uuid UUID, data []byte) (err error) { | ||
switch { | ||
case uuid.Is16Bit(): { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is Go, I'm pretty sure you don't need the extra curly braces.
gap.go
Outdated
// addFlags adds a flags field to the advertisement buffer. It returns true on | ||
// success (the flags can be added) and false on failure. | ||
func (buf *rawAdvertisementPayload) addFlags(flags byte) (ok bool) { | ||
func (buf *rawAdvertisementPayload) addFlags(flags byte) (err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think returning a boolean is fine here? It's an internal function, and the only way it could fail is when there's not enough space in the buffer.
(Same goes for other similar methods).
Hi, |
@aykevl any further feedback before we squash/merge? |
@dnlwgnd that has now been corrected. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me! Just two minor comments.
Also, can you run go fmt
on this package? It looks like some files aren't formatted correctly (normally IDEs do this for you).
adapter_darwin.go
Outdated
for _, svcData := range advFields.ServiceData { | ||
cbgoUUID := svcData.UUID | ||
uuid, err := ParseUUID(cbgoUUID.String()) | ||
if err != nil || uuid.String() != cbgoUUID.String() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the extra check whether the UUID matches? (if it doesn't, it means the UUID either is formatted differently for example due to capitalization, or there's a bug in ParseUUID
which isn't the responsibility of the calling code).
if err != nil || uuid.String() != cbgoUUID.String() { | |
if err != nil { |
(Also, there should never be an error here but as it's an external API it doesn't hurt to check for the error response anyway).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually that piece came with the other PR in the beginning. but sure that check is redundant.
// service uuid or company uuid | ||
// The list can also be viewed here: | ||
// https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml | ||
// https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/uuids/service_uuids.yaml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is always the service UUID (which can be 16/32/128-bit) - not a company ID.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought so as well, but indeed in case of bthome which is my usecase, they use a company id (0xFCD2
) with service data. Hence my comment on the function .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It really is a service UUID, not a company ID. You can check for yourself here: https://bitbucket.org/bluetooth-SIG/public/src/413977fdb5a21c25947f3abc4d7cf26947cd0e63/assigned_numbers/uuids/member_uuids.yaml#lines-1149
Company IDs are listed here, and don't include 0xFCD2: https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok fine. i didnt think that members weren't companies. please go ahead and change that comment to suit you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will change in a subsequent PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is a PR to fix the comment: #275
Looks like all of the feedback has been addressed, except for the one comment that I can handle in a subsequent PR. @radhus and @dnlwgnd thank you both so very much for all of the effort on this, and to @aykevl for your patient coaching to get this feature added. I will have to squash/merge due to the convoluted commit history. But at least we are getting it completed. Thanks again everyone! |
* gap: fix comment * gap: expose ServiceData() in AdvertisementFields * macos: include ServiceData in AdvertisementFields * gap/linux: include ServiceData in AdvertisementFields * gap: add unimplemented ServiceData() to raw advertisement * added ServiceData advertising element also to the sending pieces * more explicitly use the ad element type ids * added a test case for ServiceData * linux: added ServiceData advertising element * sd: fix: handle no servicedata present * linux: bluez uses string uuids for service data * linux: fix: correct datatype for advertise with ServiceData * uuid: add 32-Bit functions * ServiceData now also uses a slice instead of a map as in #244 * Revert unnessesary changes * formatting * remove extra check --------- Co-authored-by: William Johansson <[email protected]>
* gap: fix comment * gap: expose ServiceData() in AdvertisementFields * macos: include ServiceData in AdvertisementFields * gap/linux: include ServiceData in AdvertisementFields * gap: add unimplemented ServiceData() to raw advertisement * added ServiceData advertising element also to the sending pieces * more explicitly use the ad element type ids * added a test case for ServiceData * linux: added ServiceData advertising element * sd: fix: handle no servicedata present * linux: bluez uses string uuids for service data * linux: fix: correct datatype for advertise with ServiceData * uuid: add 32-Bit functions * ServiceData now also uses a slice instead of a map as in #244 * Revert unnessesary changes * formatting * remove extra check --------- Co-authored-by: William Johansson <[email protected]>
this PR add support for ServiceData advertising elements (0x16: Service Data - 16-bit UUID)
It is based on the work of #123 of which I merged all commits.
fixes #241
I added
I tested successfully
There is an error I can not figure out when advertising with ServiceData on linux. I keep getting:
Maybe s.th. is wrong with the formating of the ServiceData fields. Do you know what this should look like? Could you test on your end?