Skip to content

Commit 911eead

Browse files
author
Tzu-Jung Lee
committed
gatt: add ble.Profile and modify API accordingly.
A profile is simply the top level of grouping hierachy. 1. Rename group.go to profile.go 2. Add DiscoverProfile() to discover the whole hierachy. 3. modify explorer to use the new API.
1 parent 161dded commit 911eead

File tree

5 files changed

+114
-51
lines changed

5 files changed

+114
-51
lines changed

Diff for: darwin/client.go

+35-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package darwin
22

33
import (
4+
"fmt"
5+
46
"github.com/currantlabs/ble"
57
"github.com/raff/goble/xpc"
68
)
79

810
// A Client is a GATT client.
911
type Client struct {
10-
svcs []*ble.Service
11-
name string
12+
profile *ble.Profile
13+
name string
1214

1315
id xpc.UUID
1416
conn *conn
@@ -33,9 +35,34 @@ func (cln *Client) Name() string {
3335
return cln.name
3436
}
3537

36-
// Services returns discovered services.
37-
func (cln *Client) Services() []*ble.Service {
38-
return cln.svcs
38+
// Profile returns the discovered profile.
39+
func (cln *Client) Profile() *ble.Profile {
40+
return cln.profile
41+
}
42+
43+
// DiscoverProfile discovers the whole hierachy of a server.
44+
func (cln *Client) DiscoverProfile(force bool) (*ble.Profile, error) {
45+
if cln.profile != nil && !force {
46+
return cln.profile, nil
47+
}
48+
ss, err := cln.DiscoverServices(nil)
49+
if err != nil {
50+
return nil, fmt.Errorf("can't discover services: %s\n", err)
51+
}
52+
for _, s := range ss {
53+
cs, err := cln.DiscoverCharacteristics(nil, s)
54+
if err != nil {
55+
return nil, fmt.Errorf("can't discover characteristics: %s\n", err)
56+
}
57+
for _, c := range cs {
58+
_, err := cln.DiscoverDescriptors(nil, c)
59+
if err != nil {
60+
return nil, fmt.Errorf("can't discover descriptors: %s\n", err)
61+
}
62+
}
63+
}
64+
cln.profile = &ble.Profile{Services: ss}
65+
return cln.profile, nil
3966
}
4067

4168
// DiscoverServices finds all the primary services on a server. [Vol 3, Part G, 4.4.1]
@@ -57,7 +84,9 @@ func (cln *Client) DiscoverServices(ss []ble.UUID) ([]*ble.Service, error) {
5784
EndHandle: uint16(xs.serviceEndHandle()),
5885
})
5986
}
60-
cln.svcs = svcs
87+
if cln.profile == nil {
88+
cln.profile = &ble.Profile{Services: svcs}
89+
}
6190
return svcs, nil
6291
}
6392

Diff for: examples/explorer/main.go

+4-12
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,14 @@ var (
2020
func explorer(cln ble.Client) error {
2121
fmt.Printf("Exploring Peripheral [ %s ] ...\n", cln.Address())
2222

23-
ss, err := cln.DiscoverServices(nil)
23+
p, err := cln.DiscoverProfile(true)
2424
if err != nil {
2525
return fmt.Errorf("can't discover services: %s\n", err)
2626
}
27-
for _, s := range ss {
27+
for _, s := range p.Services {
2828
fmt.Printf("Service: %s %s, Handle (0x%02X)\n", s.UUID.String(), ble.Name(s.UUID), s.Handle)
2929

30-
cs, err := cln.DiscoverCharacteristics(nil, s)
31-
if err != nil {
32-
return fmt.Errorf("can't discover characteristics: %s\n", err)
33-
}
34-
for _, c := range cs {
30+
for _, c := range s.Characteristics {
3531
fmt.Printf(" Characteristic: %s, Property: 0x%02X (%s), %s, Handle(0x%02X), VHandle(0x%02X)\n",
3632
c.UUID, c.Property, propString(c.Property), ble.Name(c.UUID), c.Handle, c.ValueHandle)
3733
if (c.Property & ble.CharRead) != 0 {
@@ -43,11 +39,7 @@ func explorer(cln ble.Client) error {
4339
fmt.Printf(" Value %x | %q\n", b, b)
4440
}
4541

46-
ds, err := cln.DiscoverDescriptors(nil, c)
47-
if err != nil {
48-
return fmt.Errorf("can't discover descriptors: %s\n", err)
49-
}
50-
for _, d := range ds {
42+
for _, d := range c.Descriptors {
5143
fmt.Printf(" Descriptor: %s, %s, Handle(0x%02x)\n", d.UUID, ble.Name(d.UUID), d.Handle)
5244
b, err := cln.ReadDescriptor(d)
5345
if err != nil {

Diff for: gatt.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ type Client interface {
99
// This can be the advertised name, if exists, or the GAP device name, which takes priority.
1010
Name() string
1111

12-
// Services returns discovered services.
13-
Services() []*Service
12+
// Profile returns discovered profile.
13+
Profile() *Profile
1414

15+
// DiscoverProfile discovers the whole hierachy of a server.
16+
DiscoverProfile(force bool) (*Profile, error)
1517
// DiscoverServices finds all the primary services on a server. [Vol 3, Part G, 4.4.1]
1618
// If filter is specified, only filtered services are returned.
1719
DiscoverServices(filter []UUID) ([]*Service, error)

Diff for: linux/gatt/client.go

+62-27
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@ package gatt
22

33
import (
44
"encoding/binary"
5+
"fmt"
56
"log"
67
"sync"
78

8-
"github.com/currantlabs/ble/linux/att"
99
"github.com/currantlabs/ble"
10+
"github.com/currantlabs/ble/linux/att"
1011
)
1112

1213
const (
1314
cccNotify = 0x0001
1415
cccIndicate = 0x0002
1516
)
1617

17-
// NewClient ...
18+
// NewClient returns a GATT Client.
1819
func NewClient(conn ble.Conn) (*Client, error) {
1920
p := &Client{
2021
subs: make(map[uint16]*sub),
@@ -25,48 +26,77 @@ func NewClient(conn ble.Conn) (*Client, error) {
2526
return p, nil
2627
}
2728

28-
// Client ...
29+
// A Client is a GATT Client.
2930
type Client struct {
3031
sync.RWMutex
3132

32-
svcs []*ble.Service
33-
name string
34-
subs map[uint16]*sub
33+
profile *ble.Profile
34+
name string
35+
subs map[uint16]*sub
3536

3637
ac *att.Client
3738
conn ble.Conn
3839
}
3940

40-
// Address ...
41+
// Address returns the address of the client.
4142
func (p *Client) Address() ble.Addr {
4243
p.RLock()
4344
defer p.RUnlock()
4445
return p.conn.RemoteAddr()
4546
}
4647

47-
// Name ...
48+
// Name returns the name of the client.
4849
func (p *Client) Name() string {
4950
p.RLock()
5051
defer p.RUnlock()
5152
return p.name
5253
}
5354

54-
// Services ...
55-
func (p *Client) Services() []*ble.Service {
55+
// Profile returns the discovered profile.
56+
func (p *Client) Profile() *ble.Profile {
5657
p.RLock()
5758
defer p.RUnlock()
58-
return p.svcs
59+
return p.profile
60+
}
61+
62+
// DiscoverProfile discovers the whole hierachy of a server.
63+
func (p *Client) DiscoverProfile(force bool) (*ble.Profile, error) {
64+
if p.profile != nil && !force {
65+
return p.profile, nil
66+
}
67+
ss, err := p.DiscoverServices(nil)
68+
if err != nil {
69+
return nil, fmt.Errorf("can't discover services: %s\n", err)
70+
}
71+
for _, s := range ss {
72+
cs, err := p.DiscoverCharacteristics(nil, s)
73+
if err != nil {
74+
return nil, fmt.Errorf("can't discover characteristics: %s\n", err)
75+
}
76+
for _, c := range cs {
77+
_, err := p.DiscoverDescriptors(nil, c)
78+
if err != nil {
79+
return nil, fmt.Errorf("can't discover descriptors: %s\n", err)
80+
}
81+
}
82+
}
83+
p.profile = &ble.Profile{Services: ss}
84+
return p.profile, nil
5985
}
6086

61-
// DiscoverServices ...
87+
// DiscoverServices finds all the primary services on a server. [Vol 3, Part G, 4.4.1]
88+
// If filter is specified, only filtered services are returned.
6289
func (p *Client) DiscoverServices(filter []ble.UUID) ([]*ble.Service, error) {
6390
p.Lock()
6491
defer p.Unlock()
92+
if p.profile == nil {
93+
p.profile = &ble.Profile{}
94+
}
6595
start := uint16(0x0001)
6696
for {
6797
length, b, err := p.ac.ReadByGroupType(start, 0xFFFF, ble.PrimaryServiceUUID)
6898
if err == ble.ErrAttrNotFound {
69-
return p.svcs, nil
99+
return p.profile.Services, nil
70100
}
71101
if err != nil {
72102
return nil, err
@@ -81,25 +111,27 @@ func (p *Client) DiscoverServices(filter []ble.UUID) ([]*ble.Service, error) {
81111
Handle: h,
82112
EndHandle: endh,
83113
}
84-
p.svcs = append(p.svcs, s)
114+
p.profile.Services = append(p.profile.Services, s)
85115
}
86116
if endh == 0xFFFF {
87-
return p.svcs, nil
117+
return p.profile.Services, nil
88118
}
89119
start = endh + 1
90120
b = b[length:]
91121
}
92122
}
93123
}
94124

95-
// DiscoverIncludedServices ...
125+
// DiscoverIncludedServices finds the included services of a service. [Vol 3, Part G, 4.5.1]
126+
// If filter is specified, only filtered services are returned.
96127
func (p *Client) DiscoverIncludedServices(ss []ble.UUID, s *ble.Service) ([]*ble.Service, error) {
97128
p.Lock()
98129
defer p.Unlock()
99130
return nil, nil
100131
}
101132

102-
// DiscoverCharacteristics ...
133+
// DiscoverCharacteristics finds all the characteristics within a service. [Vol 3, Part G, 4.6.1]
134+
// If filter is specified, only filtered characteristics are returned.
103135
func (p *Client) DiscoverCharacteristics(filter []ble.UUID, s *ble.Service) ([]*ble.Characteristic, error) {
104136
p.Lock()
105137
defer p.Unlock()
@@ -138,7 +170,8 @@ func (p *Client) DiscoverCharacteristics(filter []ble.UUID, s *ble.Service) ([]*
138170
return s.Characteristics, nil
139171
}
140172

141-
// DiscoverDescriptors ...
173+
// DiscoverDescriptors finds all the descriptors within a characteristic. [Vol 3, Part G, 4.7.1]
174+
// If filter is specified, only filtered descriptors are returned.
142175
func (p *Client) DiscoverDescriptors(filter []ble.UUID, c *ble.Characteristic) ([]*ble.Descriptor, error) {
143176
p.Lock()
144177
defer p.Unlock()
@@ -171,21 +204,21 @@ func (p *Client) DiscoverDescriptors(filter []ble.UUID, c *ble.Characteristic) (
171204
return c.Descriptors, nil
172205
}
173206

174-
// ReadCharacteristic ...
207+
// ReadCharacteristic reads a characteristic value from a server. [Vol 3, Part G, 4.8.1]
175208
func (p *Client) ReadCharacteristic(c *ble.Characteristic) ([]byte, error) {
176209
p.Lock()
177210
defer p.Unlock()
178211
return p.ac.Read(c.ValueHandle)
179212
}
180213

181-
// ReadLongCharacteristic ...
214+
// ReadLongCharacteristic reads a characteristic value which is longer than the MTU. [Vol 3, Part G, 4.8.3]
182215
func (p *Client) ReadLongCharacteristic(c *ble.Characteristic) ([]byte, error) {
183216
p.Lock()
184217
defer p.Unlock()
185218
return nil, nil
186219
}
187220

188-
// WriteCharacteristic ...
221+
// WriteCharacteristic writes a characteristic value to a server. [Vol 3, Part G, 4.9.3]
189222
func (p *Client) WriteCharacteristic(c *ble.Characteristic, v []byte, noRsp bool) error {
190223
p.Lock()
191224
defer p.Unlock()
@@ -196,21 +229,21 @@ func (p *Client) WriteCharacteristic(c *ble.Characteristic, v []byte, noRsp bool
196229
return p.ac.Write(c.ValueHandle, v)
197230
}
198231

199-
// ReadDescriptor ...
232+
// ReadDescriptor reads a characteristic descriptor from a server. [Vol 3, Part G, 4.12.1]
200233
func (p *Client) ReadDescriptor(d *ble.Descriptor) ([]byte, error) {
201234
p.Lock()
202235
defer p.Unlock()
203236
return p.ac.Read(d.Handle)
204237
}
205238

206-
// WriteDescriptor ...
239+
// WriteDescriptor writes a characteristic descriptor to a server. [Vol 3, Part G, 4.12.3]
207240
func (p *Client) WriteDescriptor(d *ble.Descriptor, v []byte) error {
208241
p.Lock()
209242
defer p.Unlock()
210243
return p.ac.Write(d.Handle, v)
211244
}
212245

213-
// ReadRSSI ...
246+
// ReadRSSI retrieves the current RSSI value of remote peripheral. [Vol 2, Part E, 7.5.4]
214247
func (p *Client) ReadRSSI() int {
215248
p.Lock()
216249
defer p.Unlock()
@@ -226,7 +259,8 @@ func (p *Client) ExchangeMTU(mtu int) (int, error) {
226259
return p.ac.ExchangeMTU(mtu)
227260
}
228261

229-
// Subscribe ...
262+
// Subscribe subscribes to indication (if ind is set true), or notification of a
263+
// characteristic value. [Vol 3, Part G, 4.10 & 4.11]
230264
func (p *Client) Subscribe(c *ble.Characteristic, ind bool, h ble.NotificationHandler) error {
231265
p.Lock()
232266
defer p.Unlock()
@@ -236,7 +270,8 @@ func (p *Client) Subscribe(c *ble.Characteristic, ind bool, h ble.NotificationHa
236270
return p.setHandlers(c.CCCD.Handle, c.ValueHandle, cccNotify, h)
237271
}
238272

239-
// Unsubscribe ...
273+
// Unsubscribe unsubscribes to indication (if ind is set true), or notification
274+
// of a specified characteristic value. [Vol 3, Part G, 4.10 & 4.11]
240275
func (p *Client) Unsubscribe(c *ble.Characteristic, ind bool) error {
241276
p.Lock()
242277
defer p.Unlock()
@@ -273,7 +308,7 @@ func (p *Client) setHandlers(cccdh, vh, flag uint16, h ble.NotificationHandler)
273308
return p.ac.Write(s.cccdh, v)
274309
}
275310

276-
// ClearSubscriptions ...
311+
// ClearSubscriptions clears all subscriptions to notifications and indications.
277312
func (p *Client) ClearSubscriptions() error {
278313
p.Lock()
279314
defer p.Unlock()

Diff for: group.go renamed to profile.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,17 @@ const (
3030
CharExtended Property = 0x80 // supports extended properties
3131
)
3232

33+
// A Profile is composed of one or more services necessary to fulfill a use case.
34+
type Profile struct {
35+
Services []*Service
36+
}
37+
3338
// A Service is a BLE service.
3439
type Service struct {
3540
UUID UUID
3641
Characteristics []*Characteristic
3742

38-
Handle uint16
43+
Handle uint16
3944
EndHandle uint16
4045
}
4146

@@ -72,9 +77,9 @@ type Characteristic struct {
7277
NotifyHandler NotifyHandler
7378
IndicateHandler NotifyHandler
7479

75-
Handle uint16
76-
ValueHandle uint16
77-
EndHandle uint16
80+
Handle uint16
81+
ValueHandle uint16
82+
EndHandle uint16
7883
}
7984

8085
// AddDescriptor adds a descriptor to a characteristic.

0 commit comments

Comments
 (0)