The Problem
I've noticed during my testing that I'm getting to many false positives about my curved posture during walking.
Objective
Ignore curved posture detection during walking.
Solution
I've decided to use a built-in pedometer to detect walking, so the app can receive movement information from MCU using the new BLE pedometer characteristic.
IMU of LSM6DS3 has a built-in capability to measure steps. It shouldn't increase significantly MCU power consumption and memory footprint.
And to get access to this low level function I've switched to another Arduino library and start using SparkFunLSM6DS3 library. The library comes with a pedometer example https://github.com/sparkfun/SparkFun_LSM6DS3_Arduino_Library/tree/master/examples/Pedometer. It made my work a bit simpler.
I've added a function to configure IMU to enable the pedometer function:
void setupIMU() { //Call .beginCore() to configure the IMU if( myIMU.beginCore() != 0 ) { Serial.println("Error at beginCore() configure the IMU."); } else { Serial.println("IMU beginCore() passed."); } //Error accumulation variable uint8_t errorAccumulator = 0; uint8_t dataToWrite = 0; //Temporary variable //Setup the accelerometer****************************** dataToWrite = 0; //Start Fresh! // dataToWrite |= LSM6DS3_ACC_GYRO_BW_XL_200Hz; dataToWrite |= LSM6DS3_ACC_GYRO_FS_XL_2g; dataToWrite |= LSM6DS3_ACC_GYRO_ODR_XL_26Hz; // //Now, write the patched together data errorAccumulator += myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, dataToWrite); //Set the ODR bit errorAccumulator += myIMU.readRegister(&dataToWrite, LSM6DS3_ACC_GYRO_CTRL4_C); dataToWrite &= ~((uint8_t)LSM6DS3_ACC_GYRO_BW_SCAL_ODR_ENABLED); // Enable embedded functions -- ALSO clears the pdeo step count errorAccumulator += myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL10_C, 0x3E); // Enable pedometer algorithm errorAccumulator += myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x40); // Step Detector interrupt driven to INT1 pin errorAccumulator += myIMU.writeRegister( LSM6DS3_ACC_GYRO_INT1_CTRL, 0x10 ); if( errorAccumulator ) { Serial.println("Problem configuring the IMU."); } else { Serial.println("IMU O.K."); } }
I've added the new BLE characteristic :
// Custom BLE Posture Position Characteristic BLEUnsignedCharCharacteristic postureChar("b7469627-3ad1-4b96-8684-ff4d6cb73014", // custom 128-bit characteristic UUID BLERead | BLENotify); // remote clients will be able to get notifications if this characteristic changes int oldPosture = EI_STRAIGHT; // last posture position reading from EI inference int oldPedometer = 0; // last pedometer reading from IMU
And registered it:
postureService.addCharacteristic(postureChar); // add the posture position characteristic
I've added a new function to update this characteristic using reading from the pedometer:
void updatePedometer() { uint8_t readDataByte = 0; uint16_t stepsTaken = 0; //Read the 16bit value by two 8bit operations myIMU.readRegister(&readDataByte, LSM6DS3_ACC_GYRO_STEP_COUNTER_H); stepsTaken = ((uint16_t)readDataByte) << 8; myIMU.readRegister(&readDataByte, LSM6DS3_ACC_GYRO_STEP_COUNTER_L); stepsTaken |= readDataByte; //Display steps taken Serial.print("Steps taken: "); Serial.println(stepsTaken); if (stepsTaken != oldPedometer) { // if the pedometer value has changed pedometerChar.writeValue(stepsTaken); // and update the battery level characteristic oldPedometer = stepsTaken; // save the level for next comparison Serial.println("Pedometer notification sent"); } }
Than I've added additional code in my Android app to read and process this new characteristic. And I added vibration (line 14) , so you can feel when your posture need correction.
var data = new Uint8Array(buffer);
console.log("Data value " + data[0]);
if (data[0] == trakcore.STRAIGHT) {
resultDiv.innerHTML = "Good Posture";
resultDiv.style.backgroundColor = "initial";
navigator.vibrate(0);
}
else if (pedometerSteps == oldPedometerSteps) { // no movement - most likely sitting
resultDiv.innerHTML = "WARNING: Adjust Posture";
resultDiv.style.backgroundColor = "red";
// Vibrate for 1s, wait 1s, vibrate for 2s, wait 1s, vibrate for 3s
navigator.vibrate([1000, 1000, 2000, 1000, 3000]);
} else {
oldPedometerSteps = pedometerSteps; // moving??
const start = Date.now();
setTimeout(() => { // trigger verification of posture in 5 seconds
const millis = Date.now() - start;
ble.read(deviceId, trakcore.serviceUUID, trakcore.postureCharUUID, app.onData, app.onError1);
console.log(`seconds elapsed = ${Math.floor(millis / 1000)}`);
}, 5000); //check in 5 seconds
}
if (subscribedPedometer == false) {
ble.startNotification(deviceId, trakcore.serviceUUID, trakcore.pedometerCharUUID, app.onMovement, app.onError2);
}
},
onMovement: function (buffer) {
subscribedPedometer = true;
var data = new Uint16Array(buffer);
pedometerSteps = data[0];
console.log("Move value " + pedometerSteps);
moveDiv.innerHTML = "Steps: " + pedometerSteps;
moveDiv.style.backgroundColor = "initial";
},
Result
False positives of curved posture has been eliminated and I've added additional field on the app dashboard to show steps:
Challenges
- Cordova BLE plugin function ble.startNotification on Android requires a special attention as it may not get registered on the first attempt when more than one characteristic needs to be registered. It took me some time to find a workaround.
- After I switch LSM6DS3 IMU library my anomaly score for the posture went up significantly. So I temporary removed it from my rules. I need to identify why it went up.