@@ -55,8 +55,10 @@ type AdvertisementOptions struct {
55
55
Interval Duration
56
56
57
57
// ManufacturerData stores Advertising Data.
58
- // Keys are the Manufacturer ID to associate with the data.
59
58
ManufacturerData []ManufacturerDataElement
59
+
60
+ // ServiceData stores Advertising Data.
61
+ ServiceData []ServiceDataElement
60
62
}
61
63
62
64
// Manufacturer data that's part of an advertisement packet.
@@ -73,6 +75,17 @@ type ManufacturerDataElement struct {
73
75
Data []byte
74
76
}
75
77
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
+
76
89
// Duration is the unit of time used in BLE, in 0.625µs units. This unit of time
77
90
// is used throughout the BLE stack.
78
91
type Duration uint16
@@ -124,9 +137,13 @@ type AdvertisementPayload interface {
124
137
// if this data is not available.
125
138
Bytes () []byte
126
139
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.
129
142
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
130
147
}
131
148
132
149
// AdvertisementFields contains advertisement fields in structured form.
@@ -142,6 +159,9 @@ type AdvertisementFields struct {
142
159
143
160
// ManufacturerData is the manufacturer data of the advertisement.
144
161
ManufacturerData []ManufacturerDataElement
162
+
163
+ // ServiceData is the service data of the advertisement.
164
+ ServiceData []ServiceDataElement
145
165
}
146
166
147
167
// advertisementFields wraps AdvertisementFields to implement the
@@ -179,6 +199,11 @@ func (p *advertisementFields) ManufacturerData() []ManufacturerDataElement {
179
199
return p .AdvertisementFields .ManufacturerData
180
200
}
181
201
202
+ // ServiceData returns the underlying ServiceData field.
203
+ func (p * advertisementFields ) ServiceData () []ServiceDataElement {
204
+ return p .AdvertisementFields .ServiceData
205
+ }
206
+
182
207
// rawAdvertisementPayload encapsulates a raw advertisement packet. Methods to
183
208
// get the data (such as LocalName()) will parse just the needed field. Scanning
184
209
// the data should be fast as most advertisement packets only have a very small
@@ -288,6 +313,40 @@ func (buf *rawAdvertisementPayload) ManufacturerData() []ManufacturerDataElement
288
313
return manufacturerData
289
314
}
290
315
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
+
291
350
// reset restores this buffer to the original state.
292
351
func (buf * rawAdvertisementPayload ) reset () {
293
352
// The data is not reset (only the length), because with a zero length the
@@ -322,6 +381,12 @@ func (buf *rawAdvertisementPayload) addFromOptions(options AdvertisementOptions)
322
381
}
323
382
}
324
383
384
+ for _ , element := range options .ServiceData {
385
+ if ! buf .addServiceData (element .UUID , element .Data ) {
386
+ return false
387
+ }
388
+ }
389
+
325
390
return true
326
391
}
327
392
@@ -344,6 +409,57 @@ func (buf *rawAdvertisementPayload) addManufacturerData(key uint16, value []byte
344
409
return true
345
410
}
346
411
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
+
347
463
// addFlags adds a flags field to the advertisement buffer. It returns true on
348
464
// success (the flags can be added) and false on failure.
349
465
func (buf * rawAdvertisementPayload ) addFlags (flags byte ) (ok bool ) {
0 commit comments