Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions ShimmerBLE/Maui/ShimmerBLEMauiAPI/Communications/RadioPluginBLEv3.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
using shimmer.Communications;
using System;
using System.Linq;
using System.Threading.Tasks;
using Plugin.BLE;
using Plugin.BLE.Abstractions.Contracts;
using Plugin.BLE.Abstractions.EventArgs;
using System.Threading;

namespace ShimmerBLEMauiAPI.Communications
{
internal class RadioPluginBLEv3 : IVerisenseByteCommunication
{


private IBluetoothLE bluetoothLE = CrossBluetoothLE.Current; // ✅ Should now work
public static bool ShowRXB = false;

public Guid Asm_uuid { get; set; }

public event EventHandler<ByteLevelCommunicationEvent> CommunicationEvent;

private IAdapter _adapter;
private IDevice _device;
private IService _serviceTXRX;
private ICharacteristic _uartTX;
private ICharacteristic _uartRX;

private ConnectivityState State = ConnectivityState.Unknown;
private TaskCompletionSource<bool> ConnectionStatusTCS;

private readonly IBluetoothLE _bluetoothLE;

public RadioPluginBLEv3()
{
_bluetoothLE = CrossBluetoothLE.Current;
_adapter = CrossBluetoothLE.Current.Adapter;
}

public async Task<ConnectivityState> Connect()
{
try
{
ConnectionStatusTCS = new TaskCompletionSource<bool>();

var knownDevices = _adapter.GetSystemConnectedOrPairedDevices(new Guid[] { Asm_uuid });

_device = knownDevices.FirstOrDefault(d => d.Id == Asm_uuid);
if (_device == null)
{
State = ConnectivityState.Disconnected;
return State;
}

// With this corrected implementation:
_adapter.DeviceDisconnected += Device_Disconnected;

await _adapter.ConnectToDeviceAsync(_device);

var services = await _device.GetServicesAsync();
_serviceTXRX = services.FirstOrDefault(s => s.Id == new Guid("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"));
if (_serviceTXRX == null)
{
throw new Exception("TXRX Service not found");
}

_uartTX = (await _serviceTXRX.GetCharacteristicsAsync())
.FirstOrDefault(c => c.Id == new Guid("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"));

_uartRX = (await _serviceTXRX.GetCharacteristicsAsync())
.FirstOrDefault(c => c.Id == new Guid("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"));

if (_uartRX != null)
{
_uartRX.ValueUpdated += UartRX_ValueUpdated;
await _uartRX.StartUpdatesAsync();
}

State = ConnectivityState.Connected;
return State;
}
catch (Exception ex)
{
Console.WriteLine("RadioPluginBLE Connection Error: " + ex.Message);
Disconnect();
return ConnectivityState.Disconnected;
}
}

public async Task<ConnectivityState> Disconnect()
{
try
{
if (_uartRX != null)
{
_uartRX.ValueUpdated -= UartRX_ValueUpdated;
await _uartRX.StopUpdatesAsync();
}

if (_device != null)
{
// With this corrected implementation:
_adapter.DeviceDisconnected += Device_Disconnected;
await _adapter.DisconnectDeviceAsync(_device);
}

_uartTX = null;
_uartRX = null;
_serviceTXRX = null;
_device = null;

State = ConnectivityState.Disconnected;
GC.Collect();
Thread.Sleep(3000);
}
catch (Exception ex)
{
Console.WriteLine("Error during Disconnect: " + ex.Message);
}

return State;
}

private void Device_Disconnected(object sender, DeviceEventArgs e)
{
State = ConnectivityState.Disconnected;
CommunicationEvent?.Invoke(this, new ByteLevelCommunicationEvent
{
Event = ByteLevelCommunicationEvent.CommEvent.Disconnected
});
ConnectionStatusTCS?.TrySetResult(true);
}

public ConnectivityState GetConnectivityState()
{
return State;
}

public async Task<bool> WriteBytes(byte[] bytes)
{
if (_uartTX != null)
{
// Replace WriteWithoutResponseAsync with WriteAsync as per the ICharacteristic interface
await _uartTX.WriteAsync(bytes);
return true;
}

return false;
}

private void UartRX_ValueUpdated(object sender, CharacteristicUpdatedEventArgs e)
{
if (ShowRXB)
{
Console.WriteLine("RXB:" + BitConverter.ToString(e.Characteristic.Value).Replace("-", ""));
}

CommunicationEvent?.Invoke(this, new ByteLevelCommunicationEvent
{
Bytes = e.Characteristic.Value,
Event = ByteLevelCommunicationEvent.CommEvent.NewBytes
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using shimmer.Communications;
using ShimmerBLEAPI.Devices;
using ShimmerBLEMauiAPI.Communications;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ShimmerBLEMauiAPI.Devices
{
public class VerisensePluginBLEDevice : VerisenseBLEDevice
{
public static string path;
public VerisensePluginBLEDevice(string uuid, string name, string comport, CommunicationType commtype) : base(uuid, name)
{
ComPort = comport;
CommType = commtype;
}

public VerisensePluginBLEDevice(string uuid, string name) : base(uuid, name)
{

}

protected override void InitializeRadio()
{
if (BLERadio != null)
BLERadio.CommunicationEvent -= UartRX_ValueUpdated;
if (CommType == CommunicationType.BLE)
{
BLERadio = new RadioPluginBLEv3();
}

}
}
}
17 changes: 17 additions & 0 deletions ShimmerBLE/Maui/ShimmerBLEMauiAPI/ShimmerBLEMauiAPI.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\ShimmerBLEAPI\ShimmerBLEAPI.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Plugin.BLE" Version="3.1.0" />
</ItemGroup>

</Project>
38 changes: 38 additions & 0 deletions ShimmerBLE/Maui/VerisensePasskey/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Passkey Configuration App

This App allows you to set the passkey options for bonding/pairing with the Verisense device. There are three options
- No Passkey
- Default Passkey
- Clinical Passkey

The API has a fully configurable advertising ids, passkey ids, and passkey values, but for the purpose of limiting user error, the App is limited to the above. It will not be Shimmers responsibility should you use a configuration that ends up bricking the Verisense sensor.

The following steps are how to configure the device
1) Pair the Verisense device (if required)
2) Scan
3) Pick Verisense device from the list of scanned devices
4) Connect
5) Select the passkey configuration from the list of choices
6) Click Write Passkey
7) Disconnect
8) Power cycle the Verisense device and unpair it

is an example of changing the sensor to default passkey from clinical passkey, note that the sensor has to be paired prior. Once the settings have been changed remember to power cycle and unpair --> pair the device.
We are defaulting to the following for the advertising ID, and we would recommend customers do the same. In particular paying attention to the Passkey ID definitions below

Verisense-00-XXXXXXXXXXXX : No Passkey

Verisense-01-XXXXXXXXXXXX : Default Passkey (123456)

Verisense-XXXXXXXXXXXX : Clinical Passkey

Verisense-02-XXXXXXXXXXXX : Clinical Passkey (reserved for future if required)

The Clinical Passkey can be retrieved by contacting Shimmer if required.

_Note: Scanning can take a moment, and there are currently no UI indicators that it is indeed scanning_

## Requirements
Verisense FW Version >=1.2.99, using an incorrect version will lead to an error being displayed.
If you require updating the firmware on your Verisense device, please contact Shimmer.

Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.companyname.verisensepasskey" android:versionCode="1" android:versionName="1.0">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:label="@string/app_name" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset".

These files will be deployed with your package and will be accessible using Android's
AssetManager, like this:

public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);

InputStream input = Assets.Open ("my_asset.txt");
}
}

Additionally, some Android functions will automatically load asset files:

Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Android.App;
using Android.Content.PM;
using Android.OS;

namespace VerisensePasskey.Droid
{
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Android.App;
using Android.Runtime;

namespace VerisensePasskey.Droid
{
[Application]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}

protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace VerisensePasskey.Droid
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();

builder
.UseSharedMauiApp();

return builder.Build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.

For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:

Resources/
drawable/
icon.png

layout/
main.xml

values/
strings.xml

In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called "Resource"
(this is an Android convention) that contains the tokens for each one of the resources
included. For example, for the above Resources layout, this is what the Resource class would expose:

public class Resource {
public class Drawable {
public const int icon = 0x123;
}

public class Layout {
public const int main = 0x456;
}

public class Strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}

You would then use Resource.Drawable.icon to reference the drawable/icon.png file, or
Resource.Layout.main to reference the layout/main.xml file, or Resource.Strings.first_string
to reference the first string in the dictionary file values/strings.xml.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/appicon_background" />
<foreground android:drawable="@mipmap/appicon_foreground" />
</adaptive-icon>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/appicon_background" />
<foreground android:drawable="@mipmap/appicon_foreground" />
</adaptive-icon>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading