Skip to content

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.

python
from aidlab import AidlabManager, DeviceDelegate, DataType
kotlin
import com.aidlab.sdk.*
swift
import Aidlab
import Foundation
dart
import 'dart:async';

import 'package:aidlab_sdk/aidlab_sdk.dart';
csharp
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.

python
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)
kotlin
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")
    }
}
swift
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)")
    }
}
dart
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');
  }
}
csharp
// 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 name

If your app already handles Bluetooth discovery or connections, see Custom Transport to use that setup with the SDK.

Callback threads ​

BindingWhere callbacks runCalling SDK methods from a callback
PythonThe asyncio loop that called connect().did_connect may be async def. From other callbacks, use asyncio.create_task().
AndroidThe main thread.Launch a coroutine, for example with lifecycleScope.launch.
Apple platformsThe main queue.Start a Task.
FlutterThe isolate that called connect().didConnect may be async.
UnityThe 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.

python
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())
kotlin
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()
swift
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)")
        }
    }
}
dart
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');
  }
}
csharp
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.

python
try:
    await device.connect(self)
    await asyncio.sleep(60)  # receive data for one minute
finally:
    await device.disconnect()
kotlin
override fun onDestroy() {
    device?.disconnect()
    super.onDestroy()
}
swift
func stop() {
    device?.disconnect()
}
dart
@override
void dispose() {
  unawaited(device?.disconnect());
  super.dispose();
}
csharp
public void Disconnect() {
    var sdk = FindAnyObjectByType<Aidlab.AidlabSDK>();
    if (sdk != null) sdk.enabled = false;
}