Bracelet init and setup
To set up the bracelet and the Android SDK here are the main steps to implement:
- Pair and connect to the bracelet
- Set up the bracelet parameter
- Set up the automatic data transfer between the Bracelet to the Android SDK database
- Retrieve the data
Step 2: Set up the bracelet parameters
To get the right data or metrics, the bracelet needs specific settings. Prior to download the data, please follow these settings steps. Perform these commands after each reconnection of the bracelet to your app.
Set time
The bracelet needs to be initialised with the time.
If not set, the metrics will have a corrupted timestamp.
Please use the SetTimeRequest, here is an example:
bleDevice.enqueueCommand(
request = SetTimeRequest(),
onError = { result("SetTimeRequest error: $it") },
onSuccess = { result("SetTimeRequest success: $it") }
)
Set Bracelet plan
The bracelet has different plans depending on the type of measurement intended. Data can be measure intensively with a high rate (1 seconds), or with a slower rate (10 seconds) to increase the battery life. Please set the plan to customise the use of the bracelet to your best needs.
The bracelet plan is communicated by our team, please contact them to know which plan to use.
Set Plan
Enqueue the following command
SetPlanRequest(DevicePlan.xxx)
Or use the default plan
SetPlanRequest(DevicePlan.getDefault(hardwareId)
The plan will be set accordingly:
Plan for B1: HOSPITAL for B2: HOSPITAL_MULTICOLOR
Available plans:
STAND_BY(0, 16f, "Stand by"),
MAX_BATTERY(1, 15f, "Max Battery"),
TYPICAL(2, 7.5f, "Typical"),
HOSPITAL(3, 6.5f, "High Resolution BPM"),
HZ(4, 5f, "25 Hz"), // 25 Hz
HOSPITAL_RAW(5, 3.5f, "Hospital Raw"),
RESERVED(6, 20f, "Disable"), // unused
TYPICAL_MULTICOLOR(7, 6f, "Typical multicolor"),
HOSPITAL_MULTICOLOR(8, 4f, "High Resolution Multicolor"),
CONTINUOUS_GREEN_INTERMITTENT_MULTICOLOR_RAW(9, 4f, "Continuous Green Intermittent Multicolor Raw"),
CONTINUOUS_GREEN_INTERMITTENT_MULTICOLOR(10, 5f, "Continuous Green Intermittent Multicolor")
Example
bleDevice.enqueueCommand(
request = SetPlanRequest(DevicePlan.TYPICAL),
onError = { result("SetPlanRequest error: $it") },
Set PPG2 frequency (B2 only)
For B2 bracelet, and when PPG2 is activated: plans HOSPITAL and HOSPITAL_MULTICOLOR
If desired, it is possible to specify in the SetPlanRequest the PPG2 frequency
Example, set to 128 Hz:
SetPlanRequest(DevicePlan.HOSPITAL_RAW, Ppg2Frequency.HUNDRED_TWENTY_HEIGHT_HZ, 10)
By default the PPG2 frequency is 32 Hz
List of 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);
Default is 32 Hz
Full code sample in the sample app file:
DataStartFragment.kt
Set Sampling rate
The sample rate is explained here
Set sample for lastest SDK version (from 0.9.13)
The sample rate of the following metrics have to be set:
- Activity (steps, calories)
- Temperature
- Sp02
- Respiration rate
- Heart rate
The request is SetVitalParameterSamplingRateRequest
Here is an example to set the Heart rate sample rate to 1 minute
SetVitalParameterSamplingRateRequest(VitalParameterWithSamplingRateType.PULSE_RATE, 60)
To request the Get of the sample rate, here is the request:
GetVitalParameterSamplingRateRequest(VitalParameterWithSamplingRateType.PULSE_RATE)
Set sample for previous SDK version (before 0.9.13)
The sample rate can be customized in the SetPlanRequest
There is a third parameter called interval
The metrics will come every xxx seconds according to this interval
Example, set to 60 second:
SetPlanRequest(DevicePlan.HOSPITAL_RAW, Ppg2Frequency.HUNDRED_TWENTY_HEIGHT_HZ, 60)
Default is 10 seconds, steps come every 10 seconds for example
User Profile
Steps depends on the user height, weight, the user profile needs to be set to get the most accurate data: It is important to set the user profile: (height, weight), because this will be send to the bracelet and be used in calculations.
Weight / Height
- Weight
Use the following commands:
SetHeightRequest(height)
SetWeightRequest(weight)
Weight in kg
Height in cm
Get commands are available as well, if necessary:
GetWeightRequest()
GetHeightRequest()
Bedtime / risetime
The sleep monitoring uses the bracelet battery, and to avoid a high consumption, the sleep is not monitored all day long. This is why it is important to set a theoretical bedtime and risetime to the bracelet. The sleep will be monitored starting 2 hours before the theoretical bedtime and 3h maximum after the rise time (sleep stop conditions bellow).
SET Bedtime / risetime
SetBedtimeRequest(bedtimeGmt)
SetRisetimeRequest(risetimeGmt)
These commands use the following structure
data class HourMinuteRecord(val hour: Int, val minute: Int)
⚠️ Pass the time in GMT time
Example: user in France GMT+1 7:00 risetime local time -> send to bracelet 6:00 22:30 bedtime local time -> send to bracelet 21:30
Example 2: user in Iran GMT +3:30E 7:00 risetime local time -> send to bracelet 03:30 22:30 bedtime local time -> send to bracelet 19:00
Transform to GMT time: You can use the function
val bedtimeGmt = HourMinuteRecord(22,30).transformToGmtTime()
val risetimeGmt = HourMinuteRecord(7,0).transformToGmtTime()
Full code sample in the sample app file:
UserProfileFragment.kt
Get Bedtime / risetime
Get commands are available as well, if necessary:
GetBedtimeRequest()
GetRisetimeRequest()
Full code sample in the sample app file:
UserProfileFragment.kt
Profile: birthdate / gender / wrist
The bracelet needs to be initialised with the user's profile. The birthdate and gender are used mostly for the activity data (steps, workout) but the wrist is mandatory for every metric Please use the UserProfileSetRecord, here is an example:
device.enqueueCommands(
SetUserProfileRequest(UserProfileSetRecord(1960, 1, 22, Gender.MALE, Wrist.LEFT)),
Full code sample in the sample app file:
UserProfileFragment.kt
Sleep settings
Sleep monitoring needs bedtime / risetime settings:
Prerequis
Bracelet Plan
The sleep monitoring is enabled according to the plan set to the bracelet.
Bracelet plan documentation here
Bedtime / Risetime
The sleep monitoring uses the bracelet battery, and to avoid a high consumption, the sleep is not monitored all day long. This is why it is important to set a theoretical bedtime and risetime to the bracelet. The sleep will be monitored starting 2 hours before the theoretical bedtime and 3h maximum after the rise time (sleep stop conditions bellow).
Sleep measurement
Sleep Stop
The sleep is stopped automatically in the morning after 250 steps. If the users doesn’t reached 250 steps it will be stopped automatically 3h after the rise time by the bracelet.
Sleep session
The sleep monitoring provides a sleep session (start, stop, 30 seconds slots), then the sleep session is filtered to obtain the best user friendly result. Here are the filtering steps:
- Remove awake blocks at the beginning and the end of the night.
- The sleep will always start and end with a sleep stage (light / REM / deep)
- Noice filtering: the graph is flattened, the slots have to last at least 2 min straight
- Example: REM sleep during 20 min, then one 30 seconds awake slot, and after 10 more minutes of REM sleep: the 30s awake will be filtered and will not appear
- 1 minute slots
- Slots have 0 seconds (22:01:00, 22:02:00, etc)
- Start and stop time have 0 seconds too
Sleep process technically:
When the sleep monitoring is actif on the bracelet, data is generated, we collect this raw data in the app and build a .wiff file
When the sleep is ready (after 250 steps), the wiff file is processed by the Philips Library (in the app) and will give a sleep session.
A wiff file can contain multiple nights, and multiple sleep sessions.
The app tries to process the wiff file 4 times maximum, if a sleep session cannot be identified (bracelet not worn during the night for example), the wiff file will be deleted. The first trial comes after 250 steps, then we wait 2 minutes between each trials.
Enable EmoGraphy & BioZ data & Accelerometer
Enable / Disable metrics EmoGraphy & BioZ
Please follow the steps below to start getting EmoGraphy and/or BioZ data from the bracelet.
⚠️ Warning
PPG 128HZ can not be enabled when BIOZ or Emography is enabled.
First set the right plan for the bracelet. Please follow the steps in the "Set Plan" section
EmoGraphy & BioZ are paired together in the same command, use the command SetEmographyAndBiozPlan
The best place to call this function is after setting the plan.
class SetEmographyAndBiozPlan(emographyValue: VitalParameterValue, biozValue: VitalParameterValue)
Possible values
val braceletValue: Int) {
DISABLE(1),
BEDTIME_RISETIME(2),
CONTINUOUS(3);
Use BEDTIME_RISETIME to get data between bedtime and risetime
Use CONTINUOUS to get data continuously
Use DISABLE to stop monitoring
Exemple:
Emography and BioZ are enabled
bleDevice.enqueueCommand(SetEmographyAndBiozPlan(VitalParameterValue.CONTINUOUS, VitalParameterValue.CONTINUOUS),{
result("Set EmoGraphy $emographyValue OK")}, {
result("Set EmoGraphy $emographyValue error $it")})
Enable Emography and Disable BioZ
bleDevice.enqueueCommand(SetEmographyAndBiozPlan(VitalParameterValue.CONTINUOUS, VitalParameterValue.DISABLE),{
result("Set EmoGraphy $emographyValue OK")}, {
result("Set EmoGraphy $emographyValue error $it")})
Disable Emography and Enable BioZ
bleDevice.enqueueCommand(SetEmographyAndBiozPlan(VitalParameterValue.DISABLE, VitalParameterValue.CONTINUOUS),{
result("Set EmoGraphy $emographyValue OK")}, {
result("Set EmoGraphy $emographyValue error $it")})
Disable bothe Emography and BioZ
bleDevice.enqueueCommand(SetEmographyAndBiozPlan(VitalParameterValue.DISABLE, VitalParameterValue.DISABLE),{
result("Set EmoGraphy $emographyValue OK")}, {
result("Set EmoGraphy $emographyValue error $it")})
Get command
Use the command GetEmoGraphyAndBiozPlan
bleDevice.enqueueCommand(
request = GetEmoGraphyAndBiozPlan(),
...
Enable / Disable Accelerometer
Please follow the steps below to start getting Accelerometer data from the bracelet.
First set the right plan for the bracelet. Please follow the steps in the "Set Plan" section
Use the command SetAccelerometerPlan
The best place to call this function is after setting the plan.
kotlin class SetAccelerometerPlan(value: VitalParameterValue)
Possible values
val braceletValue: Int) {
DISABLE(1),
BEDTIME_RISETIME(2),
CONTINUOUS(3);
Use BEDTIME_RISETIME to get data between bedtime and risetime
Use CONTINUOUS to get data continuously
Use DISABLE to stop monitoring
Exemple
bleDevice.enqueueCommand(SetAccelerometerPlan(VitalParameterValue.CONTINUOUS),{
Get command
Use the command GetAccelerometerPlan()
bleDevice.enqueueCommand(
request = GetAccelerometerPlan(),
...
PPG2 / BioZ / Accelerometer / ECG transfer & export
Location of the files: PPG2, EmoGraphy, BioZ, Accelerometer
All the files are saved in the internal app folder on the phone.
The folders are not accessible externally, here is the list of the folders per type:
enum class RawFileType(val directory: String) {
UNKNOWN(""),
SLEEP("sleep"),
PPG2("ppg2"),
ECG("ecg"),
BIOZ("bioz"),
ACC("acc");
}
The list of files is accessible in the table RawFileChunk
Exportable files have the type <type>_FULL, example for PPG2: PPG2_FULL
Once exported, the files are deleted
- Get the exportable files
Use the following code:
DataSdk.getInstance().getManager().rawFileRepository?.getExportabledWiffFiles(watchAddress, RawFileType.PPG2)
- Get notified when a new .wiff file is ready (starting SDK 0.8.0)
private val fullWiffFileListener = object : ValueUpdated.Listener<RawFileChunk> {
override fun onValueUpdated(model: RawFileChunk) {
if (model.type.contains(type.name, true)) {
this@DataMeasurementFragment.view?.findViewById<TextView>(R.id.action_result)?.text =
"New File saved: ${model.fileName} size ${model.size / 1024} ko at ${Date(model.timestamp)}"
}
}
}
override fun onStart() {
super.onStart()
dataSdkManager?.wiffFileUpdated?.addListener(fullWiffFileListener)
}
override fun onStop() {
// Unsubscribe updated listener.
dataSdkManager?.wiffFileUpdated?.removeListener(fullWiffFileListener)
super.onStop()
}
Export of the files
Method 1: export to the phone’s internal storage (Document, Download, etc)
The user selects the directory, and then the files are copied to the directory. The directory is a folder from the phone’s internal storage, for exemple the Download folder.
After the export, the files are deleted from the app.
Code example (DataMeasurementFragment.kt in the sample app):
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
val mainActivity = requireActivity() as MainActivity
mainActivity.startActivityForResult(intent) { a, b ->
b?.data?.also { uri -> val docFile = DocumentFile.fromTreeUri(requireContext(), uri) ?: return@startActivityForResult dataSdkManager?.wiffFileExporter?.exportWiffFiles(address, type, docFile, {
DocFile is obtained by asking the user to select a local folder and give access to the app.
Method 2: export to the app folder (hidden from the user)
It is possible to export the files to the hidden app folder using the method
exportWiffFilesToAppDirectory
Files will be copied to Android/data/com.corsano.sdk.sample/files
Example using the sample app with “My_directory”
Code example (DataMeasurementFragment.kt in the sample app):
val directory =
mainActivity.getExternalFilesDir("My_directory") ?: return@setOnClickListener
if (!directory.exists())
directory.mkdir()
dataSdkManager?.wiffFileExporter?.exportWiffFilesToAppDirectory(
watchAddress, RawFileType.PPG2,
directory
- Data interpretation
The exported files can be parsed using our sample code, available in Python or PHP.
Please request access by writing to our email: info@corsano.com
ECG Measurement
Available starting version 0.8.2
The ECG feature is meant for quick measurement of a couple minutes, it is not meant to stay activated all day, because the measurement of the other metrics are deactivated during the ECG measurement
Start ECG measurement
Start ECG using the code bellow:
DataSdk.getInstance().getManager().getEcgManager().startEcg(bleDevice, {
result("Start Ecg success")
updateUI()
}, {
result("Start Ecg error: $it")
})
The start ECG command will be sent to the bracelet, and a row will be created in the Measurement table
A blue light is visible on the bracelet when the ECG is running:
Stop ECG measurement
Stop ECG using the code bellow:
DataSdk.getInstance().getManager().getEcgManager().stopEcg(bleDevice, {
result("Stop Ecg success")
updateUI()
}, {
result("Stop Ecg error: $it")
})
The stop ECG command will be sent to the bracelet, and the row in the Measurement table will be updated with the stop timestamp
The blue light on the bracelet is turned off when the ECG is stopped.
ECG in the database
The ECG are saved into the table EcgMeasurementModel
open class EcgMeasurementModel : RealmObject() {
@PrimaryKey
var startTimestamp: Long = 0L // start time of the ECG on the bracelet
var endTimestamp: Long? = null // end time of the ECG on the bracelet
var wiffFilename: String? = null // filename of the full .wiff file of the measurement when the measurement is finished and after a succesfull sync
Retrieve data of the ECG measurement
In order to retrieve the ECG data, please make sure the bracelet is syncing regularly using the Data Download
- Small .wiff.part files at each synchronisation
The ECG data from the bracelet is synced at each regular sync. At each sync, the ECG data is synced then written in a file, the file name is “ECG-watch address-timestamp.wiff.part”Ex:The .wiff.part files are stored in the app internal folder, in the ECG directory
- Merge to a .wiff files at the end of the ECG
When the ECG is stopped, after the next sync with the bracelet, when we are sure that there is no remaining ECG data on the bracelet, the .wiff.part files are merges to a .wiff file.The filename contains the stop data of the ECG.This is the file that is available for export.After the merge, the .wiff.part are deleted
Export ECG files
Export to phone storage
Use the function exportWiffFiles
, this function will:
- Get in the DB the ECG with a wiff file to export
- Check that the file exist
- Export it
- If the flag
deleteAfterExport
is true, the file will be deleted from the app storage (recommanded)
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
val mainActivity = requireActivity() as MainActivity
mainActivity.startActivityForResult(intent) { a, b ->
b?.data?.also { uri ->
val docFile =
DocumentFile.fromTreeUri(requireContext(), uri)
?: return@startActivityForResult
DataSdk.getInstance().getManager().getEcgManager().exportWiffFiles(
requireContext(),
docFile,
true,
{
Toast.makeText(
requireContext(),
"${it?.size} files saved successfully",
Toast.LENGTH_SHORT
).show()
},
{
result("Export ECG error $it")
})
}
}
Export to another directory in the app folder
Use the function exportWiffFilesToAppDirectory
, this function will:
- Get in the DB the ECG with a wiff file to export
- Check that the file exist
- Copy it to the destination directory
- If the flag
deleteAfterExport
is true, the file will be deleted from the app storage (recommanded)