Establishing Connections
Aidlab SDK lets you discover and connect to Aidlab, Aidlab 2, and Aidmed One. Typical workflow: import the SDK, initialize the manager and delegate, scan for available devices, then establish a connection and start collecting data.
Imports
Import the SDK into the file that will manage device connections and handle events.
from aidlab import AidlabManager, DeviceDelegate, DataTypeimport com.aidlab.sdk.*import Aidlab
import Foundationimport 'dart:async';
import 'package:aidlab_sdk/aidlab_sdk.dart';using UnityEngine;
using Aidlab;Create the manager
The manager discovers nearby devices. Your delegate or event handlers receive connection and data events. Implement the connection, disconnection, and error callbacks; all other callbacks are optional. In Flutter, mix in or extend DeviceDelegate to inherit the optional callbacks.
class MainManager(DeviceDelegate):
async def did_connect(self, device):
print("Connected to:", device.address)
def did_disconnect(self, device, reason):
print("Disconnected from:", device.address, reason)
def did_receive_error(self, device, error):
print("Error:", error)class MainActivity : ComponentActivity(), DeviceDelegate, AidlabManagerDelegate {
private lateinit var aidlabManager: AidlabManager
private var device: Device? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
aidlabManager = AidlabManager(this, this)
}
override fun didConnect(device: Device) {
println("Connected to: ${device.address()}")
}
override fun didDisconnect(device: Device, disconnectReason: DisconnectReason) {
if (this.device === device) this.device = null
}
override fun didReceiveError(device: Device, error: AidlabError) {
println("Error: $error")
}
}final class DeviceController: AidlabManagerDelegate, DeviceDelegate {
private lazy var aidlabManager = AidlabManager(delegate: self)
private var device: Device?
func didConnect(_ device: Device) {
print("Connected to: \(device.address)")
}
func didDisconnect(_ device: Device, reason: DisconnectReason) {
if self.device === device { self.device = nil }
}
func didReceiveError(_ device: Device, error: AidlabError) {
print("Error: \(error)")
}
}class _HomePageState extends State<HomePage>
with DeviceDelegate, AidlabManagerDelegate {
late final AidlabManager aidlabManager = AidlabManager(this);
Device? device;
@override
Future<void> didConnect(Device device) async {
print('Connected to: ${device.address}');
}
@override
void didDisconnect(Device device, DisconnectReason reason) {
if (identical(this.device, device)) this.device = null;
}
@override
void didReceiveError(Device device, AidlabError error) {
print('Error: $error');
}
}// Android, iOS, macOS, and Windows:
Aidlab.AidlabSDK.init(); // connects to the first Aidlab, Aidlab 2 or Aidmed One
// Aidlab.AidlabSDK.init("Aidlab 2"); // or only to a device with this nameIf your app already handles Bluetooth discovery or connections, see Custom Transport to use that setup with the SDK.
Callback threads
| Binding | Where callbacks run | Calling SDK methods from a callback |
|---|---|---|
| Python | The asyncio loop that called connect(). | did_connect may be async def. From other callbacks, use asyncio.create_task(). |
| Android | The main thread. | Launch a coroutine, for example with lifecycleScope.launch. |
| Apple platforms | The main queue. | Start a Task. |
| Flutter | The isolate that called connect(). | didConnect may be async. |
| Unity | The main thread. | Call AidlabSDK methods directly. |
Return quickly from callbacks and move slow work elsewhere: Aidlab 2 disconnects when the SDK acknowledges its data late.
Scan and connect
With Bluetooth enabled and the required permissions granted, scan for nearby devices and connect to one of the results. Wait for the connection event before starting data collection.
A scan can also report other heart-rate devices, so match the advertised name: Aidlab 2 for Aidlab 2, Aidlab for Aidlab 1 and Aidmed One. Devices of one model share a name; to pick one of them, use its address, see Connecting to a Specified Device.
import asyncio
class MainManager(DeviceDelegate):
async def run(self):
devices = await AidlabManager().scan()
device = next((d for d in devices if d.name in ("Aidlab", "Aidlab 2")), None)
if device:
print("Connecting to:", device.address)
await device.connect(self)
while True:
await asyncio.sleep(1)
asyncio.run(MainManager().run())private val aidlabNames = setOf("Aidlab", "Aidlab 2")
fun onBluetoothRequestsGranted() {
aidlabManager.scan()
}
override fun didDiscover(device: Device, rssi: Int) {
// Every advertisement reports a new Device: connect only to the first match.
if (this.device != null || device.name() !in aidlabNames) return
this.device = device
aidlabManager.stopScan()
lifecycleScope.launch {
try {
device.connect(this@MainActivity)
} catch (error: IllegalStateException) {
this@MainActivity.device = null
println("Connection failed: ${error.message}")
}
}
}
override fun onDeviceScanFailed(errorCode: Int) {
println("Scan failed: $errorCode")
}
// Ensure Bluetooth permissions are granted before calling scan()private let aidlabNames: Set = ["Aidlab", "Aidlab 2"]
func startScan() {
aidlabManager.scan()
}
func didDiscover(_ device: Device) {
// Every advertisement reports the device again: connect only once.
guard self.device == nil, let name = device.name, aidlabNames.contains(name) else { return }
self.device = device
aidlabManager.stopScan()
Task {
do {
try await device.connect(delegate: self)
} catch {
self.device = nil
print("Connection failed: \(error)")
}
}
}static const aidlabNames = {'Aidlab', 'Aidlab 2'};
Future<void> startScan() async {
await aidlabManager.scan(ScanMode.aggressive);
}
@override
void didDiscover(Device device, int rssi) {
// Every advertisement reports a new Device: connect only to the first match.
if (this.device != null || !aidlabNames.contains(device.name)) return;
this.device = device;
unawaited(connectTo(device));
}
@override
void onScanFailed(int errorCode) {
print('Scan failed: $errorCode');
}
Future<void> connectTo(Device device) async {
try {
await aidlabManager.stopScan();
await device.connect(this);
} catch (error) {
if (identical(this.device, device)) this.device = null;
print('Connection failed: $error');
}
}void Start() {
Aidlab.AidlabSDK.init();
// The Android backend requests runtime BLE permission before scanning.
}connect throws when the connection cannot be established. To reconnect, scan again and connect a newly discovered Device.
Device discovery
Bluetooth scanning relies on radio signals, so immediate discovery is not guaranteed. Keep the scan active and report scan failures instead of assuming that a device will appear immediately.
After connecting, firmwareRevision, hardwareRevision and serialNumber (firmware_revision, hardware_revision, serial_number in Python) identify the device.
To connect to a specific device, see Connecting to a Specified Device.
Disconnect
Call disconnect() when you no longer need the connection. The delegate then receives the disconnection callback. In Unity, disable the AidlabSDK component; enabling it scans and connects again.
try:
await device.connect(self)
await asyncio.sleep(60) # receive data for one minute
finally:
await device.disconnect()override fun onDestroy() {
device?.disconnect()
super.onDestroy()
}func stop() {
device?.disconnect()
}@override
void dispose() {
unawaited(device?.disconnect());
super.dispose();
}public void Disconnect() {
var sdk = FindAnyObjectByType<Aidlab.AidlabSDK>();
if (sdk != null) sdk.enabled = false;
}