Skip to content

Commit ce87cb7

Browse files
committed
Improve conection stability & reconnection logic
Closes #3 Closes #4
1 parent ae216aa commit ce87cb7

File tree

4 files changed

+110
-41
lines changed

4 files changed

+110
-41
lines changed

BluetoothHeartrateModule/BluetoothHeartrateModule.cs

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ public BluetoothHeartrateModule()
1919

2020
protected override BluetoothHeartrateProvider CreateProvider()
2121
{
22-
watcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active };
2322
var provider = new BluetoothHeartrateProvider(this);
2423
provider.OnHeartrateUpdate += SendWebcoketHeartrate;
2524
return provider;
@@ -44,15 +43,21 @@ protected override void CreateAttributes()
4443

4544
protected override async void OnModuleStart()
4645
{
47-
ResetWatcher();
46+
CreateWatcher();
4847
base.OnModuleStart();
49-
await wsServer.Start();
48+
if (GetWebocketEnabledSetting())
49+
{
50+
await wsServer.Start();
51+
}
5052
}
5153

5254
protected override void OnModuleStop()
5355
{
54-
ResetWatcher();
55-
wsServer.Stop();
56+
StopWatcher();
57+
if (GetWebocketEnabledSetting())
58+
{
59+
wsServer.Stop();
60+
}
5661
base.OnModuleStop();
5762
}
5863

@@ -73,30 +78,71 @@ internal int GetWebocketPortSetting()
7378
return GetSetting<int>(BluetoothHeartrateSetting.WebsocketServerPort);
7479
}
7580

76-
internal void StopWatcher()
77-
{
78-
watcher?.Stop();
79-
}
80-
8181
internal void SetDeviceName(string deviceName)
8282
{
8383
SetVariableValue(BluetoothHeartratevariable.DeviceName, deviceName);
8484
}
8585

8686
private async void SendWebcoketHeartrate(int heartrate)
8787
{
88+
if (!GetWebocketEnabledSetting())
89+
{
90+
return;
91+
}
92+
8893
await wsServer.SendIntMessage(heartrate);
8994
}
95+
internal BluetoothLEAdvertisementWatcher CreateWatcher()
96+
{
97+
if (watcher == null)
98+
{
99+
watcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active };
100+
watcher.Stopped += Watcher_Stopped;
101+
}
102+
return watcher;
103+
}
104+
105+
private void Watcher_Stopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)
106+
{
107+
string scanStatus;
108+
bool invokeDisconnect = true;
109+
switch (args.Error)
110+
{
111+
case Windows.Devices.Bluetooth.BluetoothError.RadioNotAvailable:
112+
scanStatus = "Bluetooth adapter/module is disconnected";
113+
break;
114+
case Windows.Devices.Bluetooth.BluetoothError.Success:
115+
scanStatus = "device found";
116+
invokeDisconnect = false;
117+
break;
118+
default:
119+
scanStatus = args.Error.ToString();
120+
break;
121+
}
122+
Log($"Stopped scanning for devices ({scanStatus})");
123+
if (invokeDisconnect)
124+
{
125+
HeartrateProvider?.OnDisconnected?.Invoke();
126+
}
127+
}
90128

91-
private void ResetWatcher()
129+
internal void StartWatcher()
92130
{
93131
if (watcher != null)
94132
{
95-
StopWatcher();
96-
watcher = null;
133+
if (watcher.Status != BluetoothLEAdvertisementWatcherStatus.Started)
134+
{
135+
watcher.Start();
136+
Log("Scanning for devices");
137+
}
97138
}
98139
}
99140

141+
internal void StopWatcher()
142+
{
143+
watcher?.Stop();
144+
}
145+
100146
internal enum BluetoothHeartrateSetting
101147
{
102148
DeviceMac,

BluetoothHeartrateModule/BluetoothHeartrateModule.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
<TargetFramework>net6.0-windows10.0.22621.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
7-
<AssemblyVersion>1.3.2</AssemblyVersion>
8-
<FileVersion>1.3.2</FileVersion>
7+
<AssemblyVersion>1.3.3</AssemblyVersion>
8+
<FileVersion>1.3.3</FileVersion>
99
<Authors>DJDavid98</Authors>
1010
<Product>Bluetooth Heartrate</Product>
1111
<ApplicationIcon>logo\logo.ico</ApplicationIcon>

BluetoothHeartrateModule/BluetoothHeartrateProvider.cs

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@ namespace BluetoothHeartrateModule
99
public class BluetoothHeartrateProvider : HeartrateProvider
1010
{
1111
private Dictionary<string, string?> deviceNames = new();
12+
private GattDeviceService? heartRateService;
1213
private GattCharacteristic? heartRateCharacteristic;
1314
private HashSet<string> missingCharacteristicDevices = new();
1415
private bool processingData = false;
1516
BluetoothLEDevice? currentDevice;
1617
private readonly BluetoothHeartrateModule module;
17-
public override bool IsConnected => currentDevice != null && heartRateCharacteristic != null;
18+
public override bool IsConnected => currentDevice != null && heartRateCharacteristic != null && currentDevice.ConnectionStatus == BluetoothConnectionStatus.Connected;
1819

1920
public BluetoothHeartrateProvider(BluetoothHeartrateModule module)
2021
{
2122
this.module = module;
23+
2224
}
2325

2426
public override void Initialise()
@@ -35,15 +37,7 @@ public override void Initialise()
3537
}
3638

3739
module.watcher.Received += Watcher_Received;
38-
module.watcher.Stopped += Watcher_Stopped;
39-
switch (module.watcher.Status)
40-
{
41-
case BluetoothLEAdvertisementWatcherStatus.Stopped:
42-
case BluetoothLEAdvertisementWatcherStatus.Created:
43-
module.watcher.Start();
44-
Log("Watching for devices");
45-
break;
46-
}
40+
module.StartWatcher();
4741
}
4842

4943
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
@@ -59,24 +53,50 @@ private void Reset()
5953
if (module.watcher != null)
6054
{
6155
module.watcher.Received -= Watcher_Received;
62-
module.watcher.Stopped -= Watcher_Stopped;
6356
}
6457
deviceNames.Clear();
6558
missingCharacteristicDevices.Clear();
6659
ResetDevice();
6760
processingData = false;
6861
}
6962

70-
private void ResetDevice()
63+
private async void ResetDevice()
7164
{
65+
if (heartRateService != null)
66+
{
67+
try
68+
{
69+
heartRateService.Dispose();
70+
}
71+
catch (ObjectDisposedException)
72+
{
73+
// Ignore if object is already disposed
74+
}
75+
heartRateService = null;
76+
}
7277
if (heartRateCharacteristic != null)
7378
{
74-
heartRateCharacteristic.ValueChanged -= HeartRateCharacteristic_ValueChanged;
79+
try
80+
{
81+
heartRateCharacteristic.ValueChanged -= HeartRateCharacteristic_ValueChanged;
82+
}
83+
catch (ObjectDisposedException)
84+
{
85+
// Ignore if object is already disposed
86+
}
7587
heartRateCharacteristic = null;
7688
}
7789
if (currentDevice != null)
7890
{
79-
currentDevice.Dispose();
91+
try
92+
{
93+
currentDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged;
94+
currentDevice.Dispose();
95+
}
96+
catch (ObjectDisposedException)
97+
{
98+
// Ignore if object is already disposed
99+
}
80100
currentDevice = null;
81101
}
82102
}
@@ -128,13 +148,15 @@ private async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, Blue
128148
var currentDeviceName = deviceNames[advertisementMac] ?? "Unknown";
129149
Log($"Found device named {currentDeviceName} for MAC {advertisementMac}");
130150
module.SetDeviceName(currentDeviceName);
151+
currentDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged;
131152
}
132153

133154
var missungUnknown = !missingCharacteristicDevices.Contains(deviceMacSetting);
134155
var services = await currentDevice.GetGattServicesForUuidAsync(GattServiceUuids.HeartRate, BluetoothCacheMode.Uncached);
135156
if (services.Services.Count > 0)
136157
{
137-
var firstService = services.Services[0];
158+
IEnumerable<GattDeviceService> cleanupServices = services.Services;
159+
var firstService = cleanupServices.First();
138160
if (missungUnknown)
139161
{
140162
Log("Found heartrate service");
@@ -144,6 +166,7 @@ private async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, Blue
144166
{
145167
if (heartRateCharacteristic == null)
146168
{
169+
heartRateService = firstService;
147170
heartRateCharacteristic = characteristics.Characteristics[0];
148171
Log("Found heartrate measurement characteristic");
149172

@@ -162,16 +185,19 @@ private async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, Blue
162185
}
163186
OnConnected?.Invoke();
164187
Log("Connection successful");
188+
module.StopWatcher();
189+
cleanupServices = services.Services.Skip(1);
165190
}
166191
else
167192
{
168193
Log($"Failed to enable heart rate notifications. Status: {status}");
169194
}
170195
}
171196
}
172-
else
197+
198+
if (cleanupServices.Any())
173199
{
174-
foreach (var service in services.Services)
200+
foreach (var service in cleanupServices)
175201
service.Dispose();
176202
}
177203
}
@@ -201,10 +227,13 @@ private void HeartRateCharacteristic_ValueChanged(GattCharacteristic sender, Gat
201227
OnHeartrateUpdate?.Invoke(data[1]);
202228
}
203229

204-
private void Watcher_Stopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)
230+
private async void Device_ConnectionStatusChanged(BluetoothLEDevice sender, object args)
205231
{
206-
Log("Watcher stopped");
207-
OnDisconnected?.Invoke();
232+
if (sender.ConnectionStatus == BluetoothConnectionStatus.Disconnected)
233+
{
234+
Log("Current device disconected");
235+
OnDisconnected?.Invoke();
236+
}
208237
}
209238
}
210239
}

BluetoothHeartrateModule/WebsocketHeartrateServer.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,6 @@ private void StopHttpListener()
109109
// Public method to send an int message to all clients
110110
internal async Task SendIntMessage(int message)
111111
{
112-
if (!module.GetWebocketEnabledSetting())
113-
{
114-
Stop();
115-
return;
116-
}
117-
118112
var messageBuffer = new ArraySegment<byte>(Converter.GetAsciiStringInt(message));
119113
foreach (var clientId in connectedClients.Keys)
120114
{

0 commit comments

Comments
 (0)