Skip to content

Commit

Permalink
Improve conection stability & reconnection logic
Browse files Browse the repository at this point in the history
Closes #3
Closes #4
  • Loading branch information
DJDavid98 committed Oct 6, 2023
1 parent ae216aa commit ce87cb7
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 41 deletions.
72 changes: 59 additions & 13 deletions BluetoothHeartrateModule/BluetoothHeartrateModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public BluetoothHeartrateModule()

protected override BluetoothHeartrateProvider CreateProvider()
{
watcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active };
var provider = new BluetoothHeartrateProvider(this);
provider.OnHeartrateUpdate += SendWebcoketHeartrate;
return provider;
Expand All @@ -44,15 +43,21 @@ protected override void CreateAttributes()

protected override async void OnModuleStart()
{
ResetWatcher();
CreateWatcher();
base.OnModuleStart();
await wsServer.Start();
if (GetWebocketEnabledSetting())
{
await wsServer.Start();
}
}

protected override void OnModuleStop()
{
ResetWatcher();
wsServer.Stop();
StopWatcher();
if (GetWebocketEnabledSetting())
{
wsServer.Stop();
}
base.OnModuleStop();
}

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

internal void StopWatcher()
{
watcher?.Stop();
}

internal void SetDeviceName(string deviceName)
{
SetVariableValue(BluetoothHeartratevariable.DeviceName, deviceName);
}

private async void SendWebcoketHeartrate(int heartrate)
{
if (!GetWebocketEnabledSetting())
{
return;
}

await wsServer.SendIntMessage(heartrate);
}
internal BluetoothLEAdvertisementWatcher CreateWatcher()
{
if (watcher == null)
{
watcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active };
watcher.Stopped += Watcher_Stopped;
}
return watcher;
}

private void Watcher_Stopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)
{
string scanStatus;
bool invokeDisconnect = true;
switch (args.Error)
{
case Windows.Devices.Bluetooth.BluetoothError.RadioNotAvailable:
scanStatus = "Bluetooth adapter/module is disconnected";
break;
case Windows.Devices.Bluetooth.BluetoothError.Success:
scanStatus = "device found";
invokeDisconnect = false;
break;
default:
scanStatus = args.Error.ToString();
break;
}
Log($"Stopped scanning for devices ({scanStatus})");
if (invokeDisconnect)
{
HeartrateProvider?.OnDisconnected?.Invoke();
}
}

private void ResetWatcher()
internal void StartWatcher()
{
if (watcher != null)
{
StopWatcher();
watcher = null;
if (watcher.Status != BluetoothLEAdvertisementWatcherStatus.Started)
{
watcher.Start();
Log("Scanning for devices");
}
}
}

internal void StopWatcher()
{
watcher?.Stop();
}

internal enum BluetoothHeartrateSetting
{
DeviceMac,
Expand Down
4 changes: 2 additions & 2 deletions BluetoothHeartrateModule/BluetoothHeartrateModule.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<TargetFramework>net6.0-windows10.0.22621.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyVersion>1.3.2</AssemblyVersion>
<FileVersion>1.3.2</FileVersion>
<AssemblyVersion>1.3.3</AssemblyVersion>
<FileVersion>1.3.3</FileVersion>
<Authors>DJDavid98</Authors>
<Product>Bluetooth Heartrate</Product>
<ApplicationIcon>logo\logo.ico</ApplicationIcon>
Expand Down
69 changes: 49 additions & 20 deletions BluetoothHeartrateModule/BluetoothHeartrateProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ namespace BluetoothHeartrateModule
public class BluetoothHeartrateProvider : HeartrateProvider
{
private Dictionary<string, string?> deviceNames = new();
private GattDeviceService? heartRateService;
private GattCharacteristic? heartRateCharacteristic;
private HashSet<string> missingCharacteristicDevices = new();
private bool processingData = false;
BluetoothLEDevice? currentDevice;
private readonly BluetoothHeartrateModule module;
public override bool IsConnected => currentDevice != null && heartRateCharacteristic != null;
public override bool IsConnected => currentDevice != null && heartRateCharacteristic != null && currentDevice.ConnectionStatus == BluetoothConnectionStatus.Connected;

public BluetoothHeartrateProvider(BluetoothHeartrateModule module)
{
this.module = module;

}

public override void Initialise()
Expand All @@ -35,15 +37,7 @@ public override void Initialise()
}

module.watcher.Received += Watcher_Received;
module.watcher.Stopped += Watcher_Stopped;
switch (module.watcher.Status)
{
case BluetoothLEAdvertisementWatcherStatus.Stopped:
case BluetoothLEAdvertisementWatcherStatus.Created:
module.watcher.Start();
Log("Watching for devices");
break;
}
module.StartWatcher();
}

#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
Expand All @@ -59,24 +53,50 @@ private void Reset()
if (module.watcher != null)
{
module.watcher.Received -= Watcher_Received;
module.watcher.Stopped -= Watcher_Stopped;
}
deviceNames.Clear();
missingCharacteristicDevices.Clear();
ResetDevice();
processingData = false;
}

private void ResetDevice()
private async void ResetDevice()
{
if (heartRateService != null)
{
try
{
heartRateService.Dispose();
}
catch (ObjectDisposedException)
{
// Ignore if object is already disposed
}
heartRateService = null;
}
if (heartRateCharacteristic != null)
{
heartRateCharacteristic.ValueChanged -= HeartRateCharacteristic_ValueChanged;
try
{
heartRateCharacteristic.ValueChanged -= HeartRateCharacteristic_ValueChanged;
}
catch (ObjectDisposedException)
{
// Ignore if object is already disposed
}
heartRateCharacteristic = null;
}
if (currentDevice != null)
{
currentDevice.Dispose();
try
{
currentDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged;
currentDevice.Dispose();
}
catch (ObjectDisposedException)
{
// Ignore if object is already disposed
}
currentDevice = null;
}
}
Expand Down Expand Up @@ -128,13 +148,15 @@ private async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, Blue
var currentDeviceName = deviceNames[advertisementMac] ?? "Unknown";
Log($"Found device named {currentDeviceName} for MAC {advertisementMac}");
module.SetDeviceName(currentDeviceName);
currentDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged;
}

var missungUnknown = !missingCharacteristicDevices.Contains(deviceMacSetting);
var services = await currentDevice.GetGattServicesForUuidAsync(GattServiceUuids.HeartRate, BluetoothCacheMode.Uncached);
if (services.Services.Count > 0)
{
var firstService = services.Services[0];
IEnumerable<GattDeviceService> cleanupServices = services.Services;
var firstService = cleanupServices.First();
if (missungUnknown)
{
Log("Found heartrate service");
Expand All @@ -144,6 +166,7 @@ private async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, Blue
{
if (heartRateCharacteristic == null)
{
heartRateService = firstService;
heartRateCharacteristic = characteristics.Characteristics[0];
Log("Found heartrate measurement characteristic");

Expand All @@ -162,16 +185,19 @@ private async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, Blue
}
OnConnected?.Invoke();
Log("Connection successful");
module.StopWatcher();
cleanupServices = services.Services.Skip(1);
}
else
{
Log($"Failed to enable heart rate notifications. Status: {status}");
}
}
}
else

if (cleanupServices.Any())
{
foreach (var service in services.Services)
foreach (var service in cleanupServices)
service.Dispose();
}
}
Expand Down Expand Up @@ -201,10 +227,13 @@ private void HeartRateCharacteristic_ValueChanged(GattCharacteristic sender, Gat
OnHeartrateUpdate?.Invoke(data[1]);
}

private void Watcher_Stopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)
private async void Device_ConnectionStatusChanged(BluetoothLEDevice sender, object args)
{
Log("Watcher stopped");
OnDisconnected?.Invoke();
if (sender.ConnectionStatus == BluetoothConnectionStatus.Disconnected)
{
Log("Current device disconected");
OnDisconnected?.Invoke();
}
}
}
}
6 changes: 0 additions & 6 deletions BluetoothHeartrateModule/WebsocketHeartrateServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,6 @@ private void StopHttpListener()
// Public method to send an int message to all clients
internal async Task SendIntMessage(int message)
{
if (!module.GetWebocketEnabledSetting())
{
Stop();
return;
}

var messageBuffer = new ArraySegment<byte>(Converter.GetAsciiStringInt(message));
foreach (var clientId in connectedClients.Keys)
{
Expand Down

0 comments on commit ce87cb7

Please sign in to comment.