Skip to main content

Bracelet Initialization

From SDK 2.0.0 and FW 8.48.

Starting with 2.0.0, a bracelet must be initialized on each connection before it will accept commands. Initialization authenticates the connection and applies the plan and sampling rates in one sequence.

Warning — breaking change in 2.0.0. A command sent to a connected but not-yet-initialized bracelet is now rejected with NotInitializedException. This includes downloads and ordinary reads such as get serial number, get battery or get plan. An app upgrading from 1.x that does nothing else will see every command fail until the device is initialized. See Migrating from 1.x below.

The command gate

Every request carries a requiresInitialization flag, true by default. When the bracelet is connected but not initialized, any such request fails through the normal error path with a NotInitializedException (in com.corsano.sdk.ble.exceptions).

The commands that make up the initialization sequence itself set the flag to false, so the sequence can run on a fresh connection.

Initialization is tracked per connection:

val ready: Boolean = bleDevice.isInitialized

isInitialized resets to false on every disconnection, because firmware authentication does not survive a disconnect. A bracelet must therefore be re-initialized each time it reconnects — which the automatic path below handles for you.

From SDK 2.0.0.

Enable it once, early — Application.onCreate() is the right place — and every bracelet is initialized as soon as it connects, with no further call from your app. Reconnections are handled too.

DataSdk.getInstance().getManager().enableAutoInitialization(
InitializationConfig(
braceletPlan = DevicePlan.CONTINUOUS_GREEN_INTERMITTENT_MULTICOLOR,
samplingRate = SampleRate.ONE_MIN,
)
)

With auto-initialization on, downloads initialize the bracelet on their own before transferring, so a periodic or triggered file transfer needs no extra work.

InitializationConfig

Bundles the parameters of the initialization sequence.

FieldTypeDefaultDescription
braceletPlanDevicePlanThe plan to apply. See Bracelet Plans
samplingRateActivitySampleRateActivity sampling rate
samplingRatePulseRateSampleRatePulse rate sampling rate
samplingRateRespirationRateSampleRateRespiration rate sampling rate
samplingRateSp02SampleRateSpO2 sampling rate
samplingRateTemperatureSampleRateTemperature sampling rate
ppg2FrequencyPpg2Frequency?nullPPG2 frequency (B2)
isNibpEnabledBooleanfalseEnable NIBP
isAccEnabledBooleanfalseEnable raw accelerometer
isBiozOrEmographyEnabledBooleanfalseEnable BioZ / EmoGraphy

A secondary constructor applies a single SampleRate to all five vital parameters, which is what most integrations need:

InitializationConfig(
braceletPlan = DevicePlan.CONTINUOUS_GREEN_INTERMITTENT_MULTICOLOR,
samplingRate = SampleRate.ONE_MIN,
isNibpEnabled = true,
)

The sample rates supported are:

  • 30 seconds
  • 1 minute
  • 5 minutes
  • 30 minutes

List of PPG frequencies

enum class Ppg2Frequency(val braceletValue: Int, val frequency: Int) { THIRTY_TWO_HZ(1, 32),

SIXTY_FOUR_HZ(2, 64),

HUNDRED_TWENTY_HEIGHT_HZ(3, 128),

TWO_HUNDRED_FIFTY_SIX_HZ(4, 256),

FIVE_HUNDRED_TWELVE_HZ(5, 512);

enableAutoInitialization / disableAutoInitialization

fun enableAutoInitialization(config: InitializationConfig, listener: BraceletInitializationListener? = null)
fun disableAutoInitialization()

The optional listener observes the initialization progress and errors of every device. See BraceletInitializationListener.

Note: disableAutoInitialization() stops future automatic runs but does not turn off the command gate. Already-initialized connections keep working; once disabled, initializing a device becomes your responsibility again via braceletInitialization.

ensureInitialized

suspend fun ensureInitialized(bleDevice: BleDevice, listener: BraceletInitializationListener? = null): Result<Boolean>

Initializes the device if it is not already initialized on its current connection, using the config passed to enableAutoInitialization. It is idempotent and safe to call from several places: concurrent callers — the on-connect trigger and a starting download, for instance — are serialized and share one initialization run.

Returns success if the device is (or becomes) initialized, and failure if it does not — in particular, failure if auto-initialization was never enabled.

Call it before issuing reads right after a connection:

manager.ensureInitialized(bleDevice)
.onSuccess {
// safe to read now
bleDevice.awaitCommandV2(GetSerialNumberRequest())
}
.onFailure { Log.e("Corsano-SDK", "Init failed: $it") }

updateConfiguration

From SDK 2.1.0.

Changes the bracelet configuration at runtime — a new plan, different sampling rates, NIBP on or off — and re-applies it immediately.

suspend fun updateConfiguration(bleDevice: BleDevice, config: InitializationConfig, listener: BraceletInitializationListener? = null): Result<Boolean>

Unlike ensureInitialized, this does not short-circuit when the device is already initialized: it always re-runs the sequence, which is exactly what you need when the user changes the configuration on an already-set-up bracelet. The new config also replaces the one stored by enableAutoInitialization, so a later reconnection re-applies the new configuration rather than the previous one.

manager.updateConfiguration(
bleDevice,
InitializationConfig(
braceletPlan = DevicePlan.HOSPITAL_MULTICOLOR,
samplingRate = SampleRate.FIVE_MIN,
),
).onSuccess { Log.d("Corsano-SDK", "New configuration applied") }
.onFailure { Log.e("Corsano-SDK", "Reconfigure failed: $it") }

The run is serialized with any concurrent initialization of the same device — the on-connect trigger, a starting download, or another updateConfiguration call — so the change is applied atomically. It returns failure if the sequence does not complete, for example if the bracelet is not connected.

Manual initialization

If you do not use the automatic path, run the sequence yourself once per connection.

suspend fun braceletInitialization(
bleDevice: BleDevice,
config: InitializationConfig,
listener: BraceletInitializationListener? = null
): Result<Boolean>
val result = manager.braceletInitialization(
bleDevice,
InitializationConfig(
braceletPlan = DevicePlan.CONTINUOUS_GREEN_INTERMITTENT_MULTICOLOR,
samplingRate = SampleRate.ONE_MIN,
),
)

Overloads accept the plan and a single SampleRate directly, or every parameter individually, instead of an InitializationConfig. The sequence runs: set time → get FW version → get/set authentication → set plan → set the five vital sampling rates → NIBP / ACC / BioZ toggles.

Warning: You must re-run this after every reconnection, because isInitialized resets on disconnect. The automatic path does this for you; if you initialize manually, hook it to the connection state listener yourself.

BraceletInitializationListener

interface BraceletInitializationListener {
fun onStatusChanged(status: BraceletInitializationStatus)
fun onError(t: Throwable? = null)
}

onStatusChanged reports each step of the sequence — useful for a progress UI. BraceletInitializationStatus runs from STARTED through the per-step *_STARTED / *_DONE values to DONE_WITH_SUCCESS:

STARTED, SET_TIME_STARTED, SET_TIME_SUCCESS, GET_FW_VERSION_STARTED, GET_FW_VERSION_SUCCESS, GET_AUTHENTICATION_STATUS_STARTED, GET_AUTHENTICATION_STATUS_DONE, SET_AUTHENTICATION_STATUS_STARTED, SET_AUTHENTICATION_STATUS_DONE, SET_PLAN_STARTED, SET_PLAN_DONE, the SET_SAMPLING_RATE_* pairs (activity, PR, RR, SpO2, temp, NIBP), SET_ACC_FREQUENCY_*, SET_BIOZ_EMOGRAPHY_FREQUENCY_*, and finally DONE_WITH_SUCCESS.

onError fires with the failure if any step fails; the Result from the initialization call is also a failure in that case.

Firmware authentication

Authentication is one step of the initialization sequence, so most apps never call it directly. It is documented here because it defines what "initialized" protects.

FW 8.48 requires the connection to be authenticated before it will act on commands. The SDK authenticates using a stable per-phone identifier.

RequestCommandReturnsPurpose
SetAuthenticationRequest(phoneId)250 (APP_CMD_AUTHENTICATE)BooleanAuthenticate the connection
GetAuthenticationRequest()251 (APP_CMD_GET_AUTH_STATUS)BooleanWhether the connection is already authenticated

The phoneId is any stable per-phone string. Pass Settings.Secure.ANDROID_ID:

val phoneId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
SetAuthenticationRequest(phoneId)

ANDROID_ID is a 16-hex-character value and maps onto the firmware's 8-byte field directly; any other string is SHA-256 hashed down to 8 bytes, so it is still stable and well distributed. Both SetAuthenticationRequest and GetAuthenticationRequest set requiresInitialization = false, so they run on a fresh connection.

Authentication does not survive a disconnection — which is why isInitialized resets and the bracelet must be re-authenticated on every reconnect.

Migrating from 1.x

The command gate is on by default in 2.0.0, so an app that neither enables auto-initialization nor calls braceletInitialization gets NotInitializedException on every non-initialization command.

To migrate:

  1. Enable auto-initialization once at startup with your plan and sampling rates:

    class MyApp : Application() {
    override fun onCreate() {
    super.onCreate()
    CorsanoSdk.initialize(this, config)
    DataSdk.getInstance().getManager().enableAutoInitialization(
    InitializationConfig(
    braceletPlan = DevicePlan.CONTINUOUS_GREEN_INTERMITTENT_MULTICOLOR,
    samplingRate = SampleRate.ONE_MIN,
    )
    )
    }
    }
  2. Sequence any reads that ran right after connection — get serial number, battery, plan — after initialization instead of firing them from the connection callback:

    // Before (1.x): read straight after connecting
    // After (2.0.0):
    manager.ensureInitialized(bleDevice).onSuccess {
    bleDevice.awaitCommandV2(GetBatteryLevelRequest())
    }
  3. If you set the plan or sampling rates manually after connecting, remove that code — the initialization sequence now does it. In 2.0.0 the sample app's DataStartFragment dropped its manual plan / ACC / BioZ / EmoGraphy setup for exactly this reason.

Once auto-initialization is enabled, existing download and summary code keeps working unchanged: downloads initialize the bracelet themselves before transferring.