@@ -55,8 +55,10 @@ type AdvertisementOptions struct {
5555 Interval Duration
5656
5757 // ManufacturerData stores Advertising Data.
58- // Keys are the Manufacturer ID to associate with the data.
5958 ManufacturerData []ManufacturerDataElement
59+
60+ // ServiceData stores Advertising Data.
61+ ServiceData []ServiceDataElement
6062}
6163
6264// Manufacturer data that's part of an advertisement packet.
@@ -73,6 +75,17 @@ type ManufacturerDataElement struct {
7375 Data []byte
7476}
7577
78+ // ServiceDataElement strores a uuid/byte-array pair used as ServiceData advertisment elements
79+ type ServiceDataElement struct {
80+ // service uuid or company uuid
81+ // The list can also be viewed here:
82+ // https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml
83+ // https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/uuids/service_uuids.yaml
84+ UUID UUID
85+ // the data byte array
86+ Data []byte
87+ }
88+
7689// Duration is the unit of time used in BLE, in 0.625µs units. This unit of time
7790// is used throughout the BLE stack.
7891type Duration uint16
@@ -124,9 +137,13 @@ type AdvertisementPayload interface {
124137 // if this data is not available.
125138 Bytes () []byte
126139
127- // ManufacturerData returns a map with all the manufacturer data present in the
128- //advertising. IT may be empty.
140+ // ManufacturerData returns a slice with all the manufacturer data present in the
141+ // advertising. It may be empty.
129142 ManufacturerData () []ManufacturerDataElement
143+
144+ // ServiceData returns a slice with all the service data present in the
145+ // advertising. It may be empty.
146+ ServiceData () []ServiceDataElement
130147}
131148
132149// AdvertisementFields contains advertisement fields in structured form.
@@ -142,6 +159,9 @@ type AdvertisementFields struct {
142159
143160 // ManufacturerData is the manufacturer data of the advertisement.
144161 ManufacturerData []ManufacturerDataElement
162+
163+ // ServiceData is the service data of the advertisement.
164+ ServiceData []ServiceDataElement
145165}
146166
147167// advertisementFields wraps AdvertisementFields to implement the
@@ -179,6 +199,11 @@ func (p *advertisementFields) ManufacturerData() []ManufacturerDataElement {
179199 return p .AdvertisementFields .ManufacturerData
180200}
181201
202+ // ServiceData returns the underlying ServiceData field.
203+ func (p * advertisementFields ) ServiceData () []ServiceDataElement {
204+ return p .AdvertisementFields .ServiceData
205+ }
206+
182207// rawAdvertisementPayload encapsulates a raw advertisement packet. Methods to
183208// get the data (such as LocalName()) will parse just the needed field. Scanning
184209// the data should be fast as most advertisement packets only have a very small
@@ -288,6 +313,40 @@ func (buf *rawAdvertisementPayload) ManufacturerData() []ManufacturerDataElement
288313 return manufacturerData
289314}
290315
316+ // ServiceData returns the service data in the advertisment payload
317+ func (buf * rawAdvertisementPayload ) ServiceData () []ServiceDataElement {
318+ var serviceData []ServiceDataElement
319+ for index := 0 ; index < int (buf .len )+ 4 ; index += int (buf .data [index ]) + 1 {
320+ fieldLength := int (buf .data [index + 0 ])
321+ if fieldLength < 3 { // field has only length and type and no data
322+ continue
323+ }
324+ fieldType := buf .data [index + 1 ]
325+ switch fieldType {
326+ case 0x16 : // 16-bit uuid
327+ serviceData = append (serviceData , ServiceDataElement {
328+ UUID : New16BitUUID (uint16 (buf .data [index + 2 ]) + (uint16 (buf .data [index + 3 ]) << 8 )),
329+ Data : buf .data [index + 4 : index + fieldLength + 1 ],
330+ })
331+ case 0x20 : // 32-bit uuid
332+ serviceData = append (serviceData , ServiceDataElement {
333+ UUID : New32BitUUID (uint32 (buf .data [index + 2 ]) + (uint32 (buf .data [index + 3 ]) << 8 ) + (uint32 (buf .data [index + 4 ]) << 16 ) + (uint32 (buf .data [index + 5 ]) << 24 )),
334+ Data : buf .data [index + 6 : index + fieldLength + 1 ],
335+ })
336+ case 0x21 : // 128-bit uuid
337+ var uuidArray [16 ]byte
338+ copy (uuidArray [:], buf .data [index + 2 :index + 18 ])
339+ serviceData = append (serviceData , ServiceDataElement {
340+ UUID : NewUUID (uuidArray ),
341+ Data : buf .data [index + 18 : index + fieldLength + 1 ],
342+ })
343+ default :
344+ continue
345+ }
346+ }
347+ return serviceData
348+ }
349+
291350// reset restores this buffer to the original state.
292351func (buf * rawAdvertisementPayload ) reset () {
293352 // The data is not reset (only the length), because with a zero length the
@@ -322,6 +381,12 @@ func (buf *rawAdvertisementPayload) addFromOptions(options AdvertisementOptions)
322381 }
323382 }
324383
384+ for _ , element := range options .ServiceData {
385+ if ! buf .addServiceData (element .UUID , element .Data ) {
386+ return false
387+ }
388+ }
389+
325390 return true
326391}
327392
@@ -344,6 +409,57 @@ func (buf *rawAdvertisementPayload) addManufacturerData(key uint16, value []byte
344409 return true
345410}
346411
412+ // addServiceData adds service data ([]byte) entries to the advertisement payload.
413+ func (buf * rawAdvertisementPayload ) addServiceData (uuid UUID , data []byte ) (ok bool ) {
414+ switch {
415+ case uuid .Is16Bit ():
416+ // check if it fits
417+ fieldLength := 1 + 1 + 2 + len (data ) // 1 byte length, 1 byte ad type, 2 bytes uuid, actual service data
418+ if int (buf .len )+ fieldLength > len (buf .data ) {
419+ return false
420+ }
421+ // Add the data.
422+ buf .data [buf .len + 0 ] = byte (fieldLength - 1 )
423+ buf .data [buf .len + 1 ] = 0x16
424+ buf .data [buf .len + 2 ] = byte (uuid .Get16Bit ())
425+ buf .data [buf .len + 3 ] = byte (uuid .Get16Bit () >> 8 )
426+ copy (buf .data [buf .len + 4 :], data )
427+ buf .len += uint8 (fieldLength )
428+
429+ case uuid .Is32Bit ():
430+ // check if it fits
431+ fieldLength := 1 + 1 + 4 + len (data ) // 1 byte length, 1 byte ad type, 4 bytes uuid, actual service data
432+ if int (buf .len )+ fieldLength > len (buf .data ) {
433+ return false
434+ }
435+ // Add the data.
436+ buf .data [buf .len + 0 ] = byte (fieldLength - 1 )
437+ buf .data [buf .len + 1 ] = 0x20
438+ buf .data [buf .len + 2 ] = byte (uuid .Get32Bit ())
439+ buf .data [buf .len + 3 ] = byte (uuid .Get32Bit () >> 8 )
440+ buf .data [buf .len + 4 ] = byte (uuid .Get32Bit () >> 16 )
441+ buf .data [buf .len + 5 ] = byte (uuid .Get32Bit () >> 24 )
442+ copy (buf .data [buf .len + 6 :], data )
443+ buf .len += uint8 (fieldLength )
444+
445+ default : // must be 128-bit uuid
446+ // check if it fits
447+ fieldLength := 1 + 1 + 16 + len (data ) // 1 byte length, 1 byte ad type, 16 bytes uuid, actual service data
448+ if int (buf .len )+ fieldLength > len (buf .data ) {
449+ return false
450+ }
451+ // Add the data.
452+ buf .data [buf .len + 0 ] = byte (fieldLength - 1 )
453+ buf .data [buf .len + 1 ] = 0x21
454+ uuid_bytes := uuid .Bytes ()
455+ copy (buf .data [buf .len + 2 :], uuid_bytes [:])
456+ copy (buf .data [buf .len + 2 + 16 :], data )
457+ buf .len += uint8 (fieldLength )
458+
459+ }
460+ return true
461+ }
462+
347463// addFlags adds a flags field to the advertisement buffer. It returns true on
348464// success (the flags can be added) and false on failure.
349465func (buf * rawAdvertisementPayload ) addFlags (flags byte ) (ok bool ) {
0 commit comments