Skip to main content

BLE commands

A command is a single request/response exchange with the bracelet. You build a request object, enqueue it on a connected BleDevice, and receive a typed result.

This page is the command reference. For how commands are executed, queued and how errors surface, see BLE SDK.

Note: DeviceManager in these examples is a sample-app helper, not SDK API. See Pairing for what to use instead.

Sending a command

Callback style:

DeviceManager.getCurrentDevice()?.enqueueCommand(
request = ResetWatchRequest(),
onSuccess = { Log.d("Corsano-SDK", "Reset successful, the bracelet will restart") },
onError = { Log.e("Corsano-SDK", "Reset error $it") }
)

Coroutine style (recommended — see BLE SDK):

val result: Result<Boolean> = bleDevice.awaitCommandV2(ResetWatchRequest(), timeout = 10_000)

Sample app: DataOthersFragment.kt in the sample module (sdk/sample/src/main/java/com/corsano/sdk/sample/screens/data/tabs/) drives reset, shutdown, erase-all, battery and bracelet status from buttons — the closest thing to a command playground.

Return value conventions

Three conventions run through the whole command set, and they are worth knowing before reading the tables:

  • Set commands return Boolean. The bracelet acknowledges with the value 100, which the SDK maps to true. A false means the bracelet replied but did not acknowledge — it is not an error and onError will not fire.
  • Some Get commands return a nullable type. GetPlanRequest, GetBraceletStatusRequest, GetSessionStatusRequest and the vital-parameter getters return null when the bracelet's reply does not match the request (for example a different parameter than the one asked for). Treat null as "no usable answer", not as an error.
  • A failed command surfaces through onError / a failed Result, never as a false or null.

Device information

RequestTypeParametersReturns
GetFwVersionRequest()GetFirmwareVersion
GetHwIdRequest()GetHardwareId
GetModelIdRequest()GetInt
GetSerialNumberRequest()GetString
GetBrandRequest()GetString
GetDeviceNameRequest()GetString
GetBatteryLevelRequest()GetBatteryLevel
GetActiveModeRequest()GetDeviceMode
GetBraceletStatusRequest()GetResponse?
GetSessionStatusRequest()GetResponse?

BatteryLevel

data class BatteryLevel(
val level: Int, // percentage
val voltage: Int,
val isCharging: Boolean
)

Sample app: DataOthersFragment.kt, and DeviceImpl.kt which polls it to keep the device model up to date.

GetBraceletStatusRequest.Response

Wearing, charging and motion state in one call.

data class Response(
val wearingState: Int, // 0..4. 0 = not wearing, 4 = good wearing.
// 0, 1 and 2 are worth warning the user about.
val modeState: Int, // 1 = sleep-idle mode to save battery, 0 = active
val batteryPercentage: Int,
val isCharging: Boolean,
val idleState: Int // 0..4. 0 = completely still, 4 = in motion.
)

Returns null if the bracelet's reply cannot be interpreted.

Sample app: DataOthersFragment.kt.

GetSessionStatusRequest.Response

data class Response(
val mode: Int,
val sessionNumber: Int,
val startTime: Int,
val durationSecs: Int,
val status: SessionStatus // NOT_STARTED, ON_GOING, FINISHED
)

DeviceMode

Returned by GetActiveModeRequest.

enum class DeviceMode(val id: Int) {
SHIPPING(0), POWER_SAVING(1), HRM(2), SLEEP_TRACKING(3),
PREVENTICUS(4), PREVENTICUS_SLEEP(5), WORKOUT(6),
NORMAL(7), CHARGING(8), MAX_BATTERY(9)
}

HardwareId

Returned by GetHwIdRequest. See DFU SDK for the values and which bracelets are supported.

Time

RequestTypeParametersReturns
GetTimeRequest()GetDate
SetTimeRequest()SetBoolean
GetBedtimeRequest()GetHourMinuteRecord
GetRisetimeRequest()GetHourMinuteRecord
SetBedtimeRequest(bedtimeRecord)SetbedtimeRecord: HourMinuteRecordBoolean
SetRisetimeRequest(risetimeRecord)SetrisetimeRecord: HourMinuteRecordBoolean

SetTimeRequest takes no parameter — it writes the phone's current time to the bracelet.

Bedtime and risetime are GMT, and they gate sleep monitoring. See Sleep Settings.

data class HourMinuteRecord(val hour: Int, val minute: Int)

Sample app: DataStartFragment.kt sends SetTimeRequest as part of bracelet setup.

User profile

RequestTypeParametersReturns
GetUserProfileRequest()GetUserProfileGetRecord
SetUserProfileRequest(userProfile)SetuserProfile: UserProfileSetRecordBoolean
GetBirthdayRequest()GetResponse
GetHeightRequest()GetInt (cm)
SetHeightRequest(weight)Setweight: Int — the height in cm, see the warning belowBoolean
GetWeightRequest()GetInt (kg)
SetWeightRequest(weight)Setweight: Int (kg)Boolean
data class UserProfileSetRecord(
val birthdayYear: Int,
val birthdayMonth: Int,
val birthdayDay: Int,
val gender: Gender, // MALE, FEMALE, UNSPECIFIED
val wrist: Wrist, // LEFT, RIGHT
val skinType: SkinType? = null
)

data class UserProfileGetRecord(
val age: Int,
val gender: Gender,
val wrist: Wrist,
val skinType: SkinType? = null
)

Note the asymmetry: you set a birthdate but get an age.

GetBirthdayRequest returns Response(year, month, day).

Note: Height and weight go through a shared base that requires the value to be in 0..255. Passing anything outside that range throws IllegalArgumentException when the request is constructed, before it ever reaches the bracelet.

Warning: SetHeightRequest's constructor parameter is named weight, not height — a naming slip in the SDK. It does set the height. If you use named arguments you have to write SetHeightRequest(weight = 180); positional arguments are unaffected.

Skin type and wrist matter for measurement accuracy — see Wearing Optimization.

Sample app: UserProfileViewModel.kt.

Plan and sampling rate

RequestTypeParametersReturns
GetPlanRequest()GetResponse?
SetPlanRequest(plan, ppg2Frequency, sampleRateV1)Setsee belowBoolean
GetVitalParameterSamplingRateRequest(type)Gettype: VitalParameterWithSamplingRateTypeSetVitalParameterSamplingRateRequest?
SetVitalParameterSamplingRateRequest(type, samplingRate)Settype, samplingRate: SampleRateBoolean

SetPlanRequest

class SetPlanRequest(
val plan: DevicePlan,
val ppg2Frequency: Ppg2Frequency = Ppg2Frequency.THIRTY_TWO_HZ,
val sampleRateV1: Int = 60 // only used by FW < 5.67
)
ParameterTypeDefaultDescription
planDevicePlanThe measurement plan. See Bracelet Plans
ppg2FrequencyPpg2FrequencyTHIRTY_TWO_HZPPG2 sampling frequency. B2 only
sampleRateV1Int60Legacy sampling rate, only honoured by firmware older than 5.67
bleDevice.enqueueCommand(
request = SetPlanRequest(DevicePlan.HOSPITAL_MULTICOLOR),
onSuccess = { Log.d("Corsano-SDK", "Plan set: $it") },
onError = { Log.e("Corsano-SDK", "SetPlanRequest error: $it") }
)

GetPlanRequest returns Response(devicePlan, ppg2Frequency, sampleRateV1), all but the first nullable, or null if the reply is unusable.

Sample app: DataStartFragment.kt.

Sampling rates

GetVitalParameterSamplingRateRequest has an unusual signature: it returns a SetVitalParameterSamplingRateRequest — the setter object — rather than a plain value. Read .type and .samplingRate off it. It returns null if the bracelet answers about a different parameter, or reports a rate that does not correspond to a known SampleRate.

enum class VitalParameterWithSamplingRateType {
ACTIVITY, PULSE_RATE, RESPIRATION_RATE, SPO2, TEMPERATURE, NIBP
}

enum class SampleRate(val rateSecs: Int, val friendlyName: String) {
OFF(0, "OFF"),
THIRTY_SECS(30, "1/30 secs"),
ONE_MIN(60, "1/minute"),
FIVE_MIN(5 * 60, "1/5 minutes"),
THIRTY_MIN(30 * 60, "1/30 minutes")
}

See Set Sampling rate.

Raw stream enablement

These control whether EmoGraphy, BioZ, accelerometer and raw temperature are recorded. All of them are VitalParameterWatchType variants of the same underlying pair of commands.

RequestTypeParametersReturns
GetEmoGraphyAndBiozPlan()GetVitalParametersRequest?
SetEmographyAndBiozPlan(emographyValue, biozValue)Settwo VitalParameterValueBoolean
GetAccelerometerPlan()GetVitalParametersRequest?
SetAccelerometerPlan(value)Setvalue: VitalParameterValueBoolean
GetTemperatureRawPlan()GetVitalParametersRequest?
SetTemperatureRawPlan(value)Setvalue: VitalParameterValueBoolean
enum class VitalParameterValue { DISABLE, CONTINUOUS, BEDTIME_RISETIME, UNKNOWN }

data class VitalParametersRequest(
val type: VitalParameterWatchType, // ACCELEROMETER, BIOZ, TEMPERATURE_RAW, UNKNOWN
val param1: VitalParameterValue,
val param2: VitalParameterValue?
)

The getters return null when the bracelet replies about a different parameter than the one requested.

Full walkthrough: Enable EmoGraphy, BioZ & Accelerometer.

Files

RequestTypeParametersReturns
GetFileSizeRequest(file)Getfile: FileOnWatchFileSize
EraseFileRequest(file, size)Setfile: FileOnWatch, size: LongBoolean
EraseAllRequest()SetBoolean

FileOnWatch values: ACTIVITY, PPG, PPG2, HRV, SLEEP, ECG, BIOZ, ACC_FILE, STRESS_FILE, GREENTEG, WORKOUT, LOGS.

FileSize

class FileSize(val file: FileOnWatch, val sizeInBytes: Long) {
fun getCapacity(hardwareId: HardwareId): Double?
}

getCapacity gives how full the file is as a percentage: 0.0 when empty, 100.0 when full, and null when not applicable — for instance PPG2 on a B1, which has no such file. Note it returns a Double, so format it before displaying.

bleDevice.enqueueCommand(
request = GetFileSizeRequest(FileOnWatch.ACTIVITY),
onSuccess = {
val hardwareId = DeviceManager.getCurrentDevice()?.deviceInfo?.hardwareId ?: HardwareId.UNKNOWN
Log.d("Corsano-SDK", "File size $it and capacity ${it.getCapacity(hardwareId)}")
},
onError = { }
)

EraseAllRequest erases every data file on the bracelet — activity, sleep, hrv, ppg and the rest.

Sample app: DataActivityFragment.kt and DataMeasurementFragment.kt for file size; DataOthersFragment.kt for erase-all.

Note: In normal use you do not send file commands yourself. The automatic file transfer reads and erases files for you.

Measurements

Each of these has a dedicated page that covers the full sequence — the commands are listed here only for completeness.

RequestTypeReturnsDocumented in
StartEcgMeasurement()ActionBooleanECG
StopEcgMeasurement()ActionBooleanECG
StartNibpCalibrationMeasurement()ActionBooleanNIBP
StopNibpCalibrationMeasurement()ActionBooleanNIBP
ResetNibpCalibrationRequest()ActionBooleanNIBP
GetNibpTrainingInfoRequest()GetResponseNIBP
SetBpCuffCalibrationRequest(...)SetSetBpCuffCalibrationRequestModelNIBP
StartWearingOptimizationMeasurement()ActionBooleanWearing Optimization
StopWearingOptimizationMeasurement()ActionBooleanWearing Optimization
GetWearingOptimizationStatusRequest()GetResponseWearing Optimization
StartSpotMeasurementCmd()ActionBooleanSpO2 spot measurement
SetSpotMeasurementRequest(value)SetBooleanSpO2 spot measurement

Note: Prefer the managers — EcgManager, NibpCalibrationManager, WearingOptimizationManager, SpotMeasurementManager — over sending these commands directly. They handle the ordering, retries and database writes that a raw command sequence does not.

Sleep

RequestTypeParametersReturns
SetStopSleepRequest()SetInt

Stops the current sleep session early. Note this one returns an Int, not the Boolean that Set commands normally return, because it is built on the raw single-byte base class.

See Sleep Settings.

Real-time ping

RequestTypeParametersReturns
GetRealTimeEnabledRequest()GetBoolean
SetRealTimePingEnableRequest(value)Setvalue: BooleanBoolean
GetRealTimePeriodRequest()GetInt (seconds)
SetRealTimePingPeriodRequest(periodSecs)SetperiodSecs: IntBoolean

Real-time pings push live vitals without the app asking. The pings themselves arrive as notifications — see Instant data.

Note: SetRealTimePingPeriodRequest shares the 0..255 constraint described under User profile; a period outside that range throws IllegalArgumentException at construction.

Device actions

RequestTypeParametersReturns
ResetWatchRequest()SetBoolean
ShutdownWatchRequest()SetBoolean
LedTestWatchRequestV2(ledNumber, blinking, durationSecs)Setsee belowBoolean
Led1TestWatchRequest()SetBoolean
Led2TestWatchRequest()SetBoolean

Reset

Restarts the firmware. The LEDs turn off, then on.

Warning: Resend your setup commands after a reset — time, user profile and bracelet plan. The firmware may restore them itself, but it does not always, so do not rely on it.

Shutdown

Turns the bracelet off. It stops measuring and disappears from scans, since it no longer advertises over BLE. Connecting the charger brings it back, and its LEDs turn on when it restarts.

LED test

Turns on an LED on the side of the bracelet for a few seconds.

LedTestWatchRequestV2 — from SDK 1.1.5 and FW 6.87:

ParameterTypeDescription
ledNumberInt0 = green, 1 = orange, 2 = blue
blinkingBooleantrue to blink, false for continuous
durationSecsIntDuration in seconds

Led1TestWatchRequest and Led2TestWatchRequest are the older, parameterless equivalents.

Sample app: DataStartFragment.kt for the LED test; DataOthersFragment.kt for reset and shutdown.

File transfer requests

File transfers are enqueued with enqueueFileTransfer rather than enqueueCommand, and each takes the file size in bytes obtained from GetFileSizeRequest. See BLE SDK.

RequestReturns
ActivityFileTransferRequest(sizeInBytes)StructFileData<ActivityDataRecord>
PpgFileTransferRequest(sizeInBytes)StructFileData<PpgChunk>
HrvFileTransferRequest(sizeInBytes)StructFileData<HrvDataRecord>
EmographyFileTransferRequest(sizeInBytes)StructFileData<EmographyDataRecord>
TemperatureRawFileTransferRequest(sizeInBytes)StructFileData<TemperatureRawDataRecord>
LogFileTransferRequest(size)StructFileData<String>
Ppg2FileTransferRequest(size)ByteArray
AccFileTransferRequest(size)ByteArray
BiozFileTransferRequest(size)ByteArray
EcgFileTransferRequest(size)ByteArray
SleepFileTransferRequest(size)ByteArray
data class StructFileData<T>(
val items: List<T>,
val totalBytes: Int,
val processedBytes: Int
)

The transfers returning ByteArray deliver raw .wiff content, which the SDK writes to disk. To decode it into samples, see Parsing Raw Files.