Discovering and Connecting to Nix Devices
Device discovery
Regardless of device type, each unique Nix device can be represented by an object that conforms to the IDeviceCompat
interface. To obtain instances of the device object, they need to first be discovered using the DeviceScanner
. They can also be recalled at a later time directly using their Id
property (for Bluetooth connected devices only).
The DeviceScanner
implements the IDeviceScanner
interface and searches for nearby Nix devices using Bluetooth and USB. Scanner events include:
ScannerCreated
- Invoked when the scanner has finished initializing after calling
InitializeAsync()
- State is provided via
ScannerCreatedEventArgs
arguments.
- Invoked when the scanner has finished initializing after calling
ScannerStarted
- Invoked when the scanner has started searching for devices
ScannerStopped
- Invoked when the scanner has stopped searching for devices
ScanResult
- Invoked each time that an
IDeviceCompat
instance has been found - Device information is provided via
ScanResultEventArgs
arguments. - Note that this will be called both when a new device is found and when the RSSI/signal strength of a previously discovered device has changed. It is possible to use the
Id
parameter as a means to uniquely identify discovered devices.
- Invoked each time that an
Note
Devices can only be discovered or recalled while your SDK licence is Active
(see LicenseManager.State
). Only device types supported by your license can be discovered and connected (see LicenseManager.AllowedDeviceTypes
).
To use the DeviceScanner
:
- Create a new scanner instance and define event handlers.
- Call
InitializeAsync()
and await its completion. - Check the scanner
State
, which will beIdle
if properly initialized.- See a list possible states here.
- If the state is
ErrorLicense
, check theLicenseManager.State
for further details.
- The scanner can be started using
Start()
- By default, the scanner will search for a period
DefaultGeneralScanPeriodMs
- This interval can be overridden by providing an argument for
scanPeriodMs
.
- By default, the scanner will search for a period
- Discovered devices will be reported via the
ScanResult
event.- At this stage, it is valid to check a limited number of parameters:
Id
: Device identifier stringRssi
: Device signal strength (-127 to 0). Will be 0 for USB connected devices.InterfaceType
: Indicates whether the device was found via Bluetooth or USB connection.Type
: Indicates the specific device type.Name
: Full name of the device (e.g. 'Nix Spectro 2')
- Note that USB connected devices will be reported only once, immediately after
Start()
is called
- At this stage, it is valid to check a limited number of parameters:
- The device search will run for the specified duration, after which the
ScannerStopped
event is invoked. The device search can also be manually stopped by callingStop()
. - To recall a specific Bluetooth connected device in a later app session, you can await
SearchForIdAsync()
. This will run theDeviceScanner
searching for a specific device by its identifier.
// DeviceScanner event handlers
void OnScannerCreated(
object sender,
ScannerCreatedEventArgs args)
{
// Scanner has initialized
// ...
}
void OnScannerStarted(
object sender,
EventArgs args)
{
// Scanner has started
// ...
}
void OnScannerStopped(
object sender,
EventArgs args)
{
// Scanner has stopped
// ...
}
void OnScanResult(
object sender,
ScanResultEventArgs args)
{
if (args.Device is IDeviceCompat device)
{
// Found a device
// ...
Debug.WriteLine(
$"Found {device?.Id} " +
"({device?.Name}) " +
"at RSSI {device?.Rssi}");
}
}
// New DeviceScanner instance
IDeviceScanner scanner = new DeviceScanner();
scanner.ScannerCreated += OnScannerCreated;
scanner.ScannerStarted += OnScannerStarted;
scanner.ScannerStopped += OnScannerStopped;
scanner.ScanResult += OnScanResult;
// Initialize the scanner
// Can await state here, or check via event handler
var state = await scanner.InitializeAsync();
// Start the scanner
if (state == DeviceScannerState.Idle)
{
scanner.Start();
}
else
{
// Check the error state and handle accordingly ...
}
Tip
If Bluetooth is disabled or unavailable, it will not be possible to run the device scanner normally. However, USB connected devices can separately be listed by calling ListUsbDevicesAsync()
. This task provides a list of USB connected devices and does not invoke the ScanResult
event.
Opening a connection
Once an IDeviceCompat
instance has been obtained, a connection can be opened by calling ConnectAsync()
. Connection state changes are provided via the following events:
Connected
- Invoked when the connection process has completed successfully
Disconnected
- Invoked when the Nix device has disconnected from the host
- Provides a status indicating the reason for disconnecting via
DeviceStatusArgs
BatteryStateChanged
- Invoked when the device battery level has updated
- New battery state is provided via
BatteryStateEventArgs
ExtPowerStateChanged
- Invoked when external power has been connected or disconnected from the Nix device
- New external power state is provided via
ExtPowerStateEventArgs
The steps necessary for connecting include:
- Implement device event handlers in your class
- Stop the
DeviceScanner
before opening a connection, if still running.- It is safe to call
Stop()
at any time, even if already stopped
- It is safe to call
- Start the connection process by calling by calling
ConnectAsync()
- The connection process is asynchronous.
ConnectAsync()
returns a task that can be awaited. - A call to
ConnectAsync()
will always result in either theConnected
orDisconnected
event being invoked. - Device operations are not possible until
ConnectAsync()
provides aSuccess
result (or equivalently, until theConnected
event is invoked). - If the connection process fails, the
Disconnected
event will be invoked with an appropriate status code indicating the cause of the error. Disconnected
will also be called later whenever the device is disconnected. This can occur normally (i.e. - if the host device callsDisconnect()
), or abnormally (i.e. - if the connection is dropped due to an error or low Bluetooth signal strength).- Your license is linked to a specific allocation of Nix devices and will not operate with devices from another allocation.
- At connection time, the SDK will read an allocation code stored on the Nix device and compare to the license information.
- If this check does not pass, the SDK will contact a Nix authentication server to check if that device serial number is authorized.
- If the device cannot be authenticated (i.e. – an unknown allocation code was found and an internet connection is unavailable), the device will be disconnected with the status
ErrorUnauthorized
. - The internet connection is required only once every 30 days – once authorized, this status is saved, and connections can be made offline for this time period.
- The connection process is asynchronous.
- When the user has finished using the device, the connection can be closed by calling
Disconnect()
.- This method call can also be used to cancel an ongoing connection operation.
// Using a device instance previously found by `DeviceScanner`
IDeviceCompat Device;
// Define device state event handlers
void OnConnected(
object sender,
EventArgs args)
{
// Device has connected successfully
// ...
}
void OnDisconnected(
object sender,
DeviceStatusArgs args)
{
// Device has disconnected
DeviceStatus status = args.Status;
Debug.WriteLine($"OnDisconnected() with status {status}");
// Remove device event handlers
RemoveDeviceEvents();
// Handle status codes here, if desired in your application
// At minimum, should check for ErrorUnauthorized and ErrorLicense cases
switch (status)
{
case DeviceStatus.ErrorUnauthorized:
// Device not authorized for this license UUID
// ...
break;
case DeviceStatus.ErrorLicense:
// There is an issue with the LicenseManager
// Check LicenseManager.State
break;
case DeviceStatus.Success:
// Normal disconnect, triggered by device.Disconnect()
// ...
break;
case DeviceStatus.ErrorDroppedConnection:
// Nix device dropped the connection
// ...
break;
case DeviceStatus.ErrorTimeout:
case DeviceStatus.ErrorMaxAttempts:
// Connection to Nix device timed out
// ...
break;
default:
// Other internal errors
// ...
break;
}
// Other steps as necessary in your application
// ...
}
void OnBatteryStateChanged(
object sender,
BatteryStateEventArgs args)
{
// Battery level has updated
// ...
int newState = args.NewState;
Debug.WriteLine($"OnBatteryStateChanged to {newState}");
}
void OnExtPowerStateChanged(
object sender,
ExtPowerStateEventArgs args)
{
// External power has been connected or disconnected
// ...
int newState = args.NewState;
Debug.WriteLine($"OnExtPowerStateChanged to {newState}");
}
// Helpers to add/remove device event handlers
void AddDeviceEvents()
{
if (Device != null)
{
Device.Connected += OnConnected;
Device.Disconnected += OnDisconnected;
Device.BatteryStateChanged += OnBatteryStateChanged;
Device.ExtPowerStateChanged += OnExtPowerStateChanged;
}
}
void RemoveDeviceEvent()
{
if (device != null)
{
Device.Connected -= OnConnected;
Device.Disconnected -= OnDisconnected;
Device.BatteryStateChanged -= OnBatteryStateChanged;
Device.ExtPowerStateChanged -= OnExtPowerStateChanged;
}
}
// Add device handlers
AddDeviceEvents()
// Start connection process
var connectTask = Device.ConnectAsync();
// Show progress indicator here if desired
// ...
// Wait for completion
var state = await connectTask;
Bluetooth compatibility notes
- Availability of Bluetooth functions may depend on your application being built natively for the computer architecture used at runtime. An architecture mismatch may result in the
IDeviceScanner
initializing with statusErrorBluetoothUnavailable
, even if Bluetooth hardware is present in the system. This is related to an underlying limitation in the Windows Runtime API as described here.- When the
NixUniversalSDK
is called from a UWP or WinUI application, there is no architecture requirement.- Builds made for AnyCPU, x86, x64, and ARM64 should support Bluetooth device discovery regardless of the computer architecture
- When the
NixUniversalSDK
is called from a desktop application, Bluetooth functions are only available when running on native architecture.- x86 application builds support Bluetooth functions when running on x86 hardware
- x64 application builds support Bluetooth functions when running on x64 hardware
- x86 application builds do not support Bluetooth functions when running on x64 hardware
- If the
IDeviceScanner
initializes to an error state, it cannot be used to discover Bluetooth devices. However, USB attached devices can still be listed by callingListUsbDevicesAsync()
even when theIDeviceScanner
is in an error state.
- When the
- Bluetooth connection speed is greatly improved when running your application on Windows 11 as compared to Windows 10. This is related to enhancements that were made in the Windows Runtime API for Windows 11. The
NixUniversalSDK
automatically requests the highest Bluetooth connection speeds available from the system at runtime.