Skip to content

Commit 8cea8a6

Browse files
authored
ble: fix some issues in connect and disconnect (hybridgroup#1161)
1 parent 02818ce commit 8cea8a6

File tree

6 files changed

+284
-71
lines changed

6 files changed

+284
-71
lines changed

platforms/bleclient/ble_client_adaptor.go

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import (
1212
)
1313

1414
type configuration struct {
15-
scanTimeout time.Duration
16-
sleepAfterDisconnect time.Duration
17-
debug bool
15+
scanTimeout time.Duration
16+
sleepAfterDisconnect time.Duration
17+
dropCharacteristicsOnDisconnect bool
18+
debug bool
1819
}
1920

2021
// Adaptor represents a Client Connection to a BLE Peripheral
@@ -38,8 +39,9 @@ type Adaptor struct {
3839
//
3940
// Supported options:
4041
//
41-
// "WithAdaptorDebug"
42-
// "WithAdaptorScanTimeout"
42+
// "WithDebug"
43+
// "WithDropCharacteristicsOnDisconnect"
44+
// "WithScanTimeout"
4345
func NewAdaptor(identifier string, opts ...optionApplier) *Adaptor {
4446
cfg := configuration{
4547
scanTimeout: 10 * time.Minute,
@@ -67,6 +69,13 @@ func WithDebug() debugOption {
6769
return debugOption(true)
6870
}
6971

72+
// WithWithDropCharacteristicsOnDisconnect leads to clean all discovered services from last connect command if a
73+
// disconnect command happen. Also all subscriptions will be cleaned. A new discover of services and characteristics is
74+
// done on next connect and the subscriptions needs to be done again afterwards by the caller.
75+
func WithDropCharacteristicsOnDisconnect() dropCharacteristicsOnDisconnect {
76+
return dropCharacteristicsOnDisconnect(true)
77+
}
78+
7079
// WithScanTimeout substitute the default scan timeout of 10 min.
7180
func WithScanTimeout(timeout time.Duration) scanTimeoutOption {
7281
return scanTimeoutOption(timeout)
@@ -104,6 +113,10 @@ func (a *Adaptor) Connect() error {
104113
a.mutex.Lock()
105114
defer a.mutex.Unlock()
106115

116+
if a.connected {
117+
return fmt.Errorf("%s is already connected", a.name)
118+
}
119+
107120
var err error
108121

109122
if a.cfg.debug {
@@ -139,28 +152,30 @@ func (a *Adaptor) Connect() error {
139152
a.rssi = int(result.RSSI)
140153
a.btDevice = dev
141154

142-
if a.cfg.debug {
143-
fmt.Println("[Connect]: get all services/characteristics...")
144-
}
145-
services, err := a.btDevice.discoverServices(nil)
146-
if err != nil {
147-
return err
148-
}
149-
for _, service := range services {
155+
if len(a.characteristics) == 0 {
150156
if a.cfg.debug {
151-
fmt.Printf("[Connect]: service found: %s\n", service)
157+
fmt.Println("[Connect]: get all services/characteristics...")
152158
}
153-
chars, err := service.DiscoverCharacteristics(nil)
159+
services, err := a.btDevice.discoverServices(nil)
154160
if err != nil {
155-
log.Println(err)
156-
continue
161+
return err
157162
}
158-
for _, char := range chars {
163+
for _, service := range services {
159164
if a.cfg.debug {
160-
fmt.Printf("[Connect]: characteristic found: %s\n", char)
165+
fmt.Printf("[Connect]: service found: %s\n", service)
166+
}
167+
chars, err := service.DiscoverCharacteristics(nil)
168+
if err != nil {
169+
log.Println(err)
170+
continue
171+
}
172+
for _, char := range chars {
173+
if a.cfg.debug {
174+
fmt.Printf("[Connect]: characteristic found: %s\n", char)
175+
}
176+
c := char // to prevent implicit memory aliasing in for loop, before go 1.22
177+
a.characteristics[char.UUID().String()] = &c
161178
}
162-
c := char // to prevent implicit memory aliasing in for loop, before go 1.22
163-
a.characteristics[char.UUID().String()] = &c
164179
}
165180
}
166181

@@ -187,6 +202,26 @@ func (a *Adaptor) Disconnect() error {
187202
if a.cfg.debug {
188203
fmt.Println("[Disconnect]: disconnect...")
189204
}
205+
206+
if a.cfg.dropCharacteristicsOnDisconnect {
207+
if a.cfg.debug {
208+
fmt.Println("[Disconnect]: unsubscribe...")
209+
}
210+
211+
for id, chara := range a.characteristics {
212+
if err := adjustNotificationsForCharacteristic(chara, nil); err != nil {
213+
fmt.Printf("[Disconnect]: error on unsubscribe characteristic %s: %v\n", id, err)
214+
}
215+
}
216+
217+
if a.cfg.debug {
218+
fmt.Println("[Disconnect]: drop characteristics...")
219+
}
220+
a.characteristics = make(map[string]bluetoothExtCharacteristicer)
221+
} else if a.cfg.debug {
222+
fmt.Println("[Disconnect]: as configured, characteristics not dropped")
223+
}
224+
190225
err := a.btDevice.disconnect()
191226
time.Sleep(a.cfg.sleepAfterDisconnect)
192227
a.connected = false
@@ -252,7 +287,26 @@ func (a *Adaptor) Subscribe(cUUID string, f func(data []byte)) error {
252287
}
253288

254289
if chara, ok := a.characteristics[cUUID]; ok {
255-
return enableNotificationsForCharacteristic(chara, f)
290+
return adjustNotificationsForCharacteristic(chara, f)
291+
}
292+
293+
return fmt.Errorf("unknown characteristic: %s", cUUID)
294+
}
295+
296+
// Unsubscribe remove subscription to notifications from the BLE device for the requested characteristic UUID.
297+
// The UUID can be given as 16-bit or 128-bit (with or without dashes) value.
298+
func (a *Adaptor) Unsubscribe(cUUID string) error {
299+
if !a.connected {
300+
return fmt.Errorf("cannot unsubscribe from BLE device until connected")
301+
}
302+
303+
cUUID, err := convertUUID(cUUID)
304+
if err != nil {
305+
return err
306+
}
307+
308+
if chara, ok := a.characteristics[cUUID]; ok {
309+
return adjustNotificationsForCharacteristic(chara, nil)
256310
}
257311

258312
return fmt.Errorf("unknown characteristic: %s", cUUID)

platforms/bleclient/ble_client_adaptor_options.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@ type optionApplier interface {
77
apply(cfg *configuration)
88
}
99

10+
// dropCharacteristicsOnDisconnect is the type for applying drop feature.
11+
type dropCharacteristicsOnDisconnect bool
12+
1013
// debugOption is the type for applying the debug switch on or off.
1114
type debugOption bool
1215

1316
// scanTimeoutOption is the type for applying another timeout than the default 10 min.
1417
type scanTimeoutOption time.Duration
1518

19+
func (o dropCharacteristicsOnDisconnect) String() string {
20+
return "drop characteristics on disconnect option for BLE client adaptors"
21+
}
22+
1623
func (o debugOption) String() string {
1724
return "debug option for BLE client adaptors"
1825
}
@@ -21,6 +28,10 @@ func (o scanTimeoutOption) String() string {
2128
return "scan timeout option for BLE client adaptors"
2229
}
2330

31+
func (o dropCharacteristicsOnDisconnect) apply(cfg *configuration) {
32+
cfg.dropCharacteristicsOnDisconnect = bool(o)
33+
}
34+
2435
func (o debugOption) apply(cfg *configuration) {
2536
cfg.debug = bool(o)
2637
}

platforms/bleclient/ble_client_adaptor_options_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ func TestWithDebug(t *testing.T) {
1616
assert.True(t, a.cfg.debug)
1717
}
1818

19+
func TestWithDropCharacteristicsOnDisconnect(t *testing.T) {
20+
// arrange
21+
cfg := &configuration{dropCharacteristicsOnDisconnect: false}
22+
// act
23+
WithDropCharacteristicsOnDisconnect().apply(cfg)
24+
// assert
25+
assert.True(t, cfg.dropCharacteristicsOnDisconnect)
26+
}
27+
1928
func TestWithScanTimeout(t *testing.T) {
2029
// arrange
2130
newTimeout := 2 * time.Second

0 commit comments

Comments
 (0)