1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-08-12 11:44:19 +02:00

added reversed engineered mi scale v2 library to support body measurements

This commit is contained in:
oliexdev
2019-03-04 18:41:23 +01:00
parent 5e685ef94f
commit d2678fca85
2 changed files with 202 additions and 34 deletions

View File

@@ -21,16 +21,10 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.health.openscale.core.OpenScale;
import com.health.openscale.core.bodymetric.EstimatedFatMetric;
import com.health.openscale.core.bodymetric.EstimatedLBMMetric;
import com.health.openscale.core.bodymetric.EstimatedWaterMetric;
import com.health.openscale.core.bluetooth.lib.MiScaleLib;
import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.health.openscale.core.datatypes.ScaleUser;
import com.health.openscale.core.utils.Converters;
import com.health.openscale.gui.views.FatMeasurementView;
import com.health.openscale.gui.views.LBMMeasurementView;
import com.health.openscale.gui.views.MeasurementViewSettings;
import com.health.openscale.gui.views.WaterMeasurementView;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -143,31 +137,39 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
return true;
}
private void parseBytes(byte[] weightBytes) {
private void parseBytes(byte[] data) {
try {
final byte ctrlByte0 = weightBytes[0];
final byte ctrlByte1 = weightBytes[1];
final byte ctrlByte0 = data[0];
final byte ctrlByte1 = data[1];
final boolean isWeightRemoved = isBitSet(ctrlByte1, 7);
final boolean isDateInvalid = isBitSet(ctrlByte1, 6);
final boolean isStabilized = isBitSet(ctrlByte1, 5);
final boolean isLBSUnit = isBitSet(ctrlByte0, 0);
final boolean isCattyUnit = isBitSet(ctrlByte1, 6);
final boolean isImpedance = isBitSet(ctrlByte1, 1);
if (isStabilized && !isWeightRemoved && !isDateInvalid) {
final int year = ((weightBytes[3] & 0xFF) << 8) | (weightBytes[2] & 0xFF);
final int month = (int) weightBytes[4];
final int day = (int) weightBytes[5];
final int hours = (int) weightBytes[6];
final int min = (int) weightBytes[7];
final int sec = (int) weightBytes[8];
final int year = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
final int month = (int) data[4];
final int day = (int) data[5];
final int hours = (int) data[6];
final int min = (int) data[7];
final int sec = (int) data[8];
float weight;
float impedance = 0.0f;
if (isLBSUnit || isCattyUnit) {
weight = (float) (((weightBytes[12] & 0xFF) << 8) | (weightBytes[11] & 0xFF)) / 100.0f;
weight = (float) (((data[12] & 0xFF) << 8) | (data[11] & 0xFF)) / 100.0f;
} else {
weight = (float) (((weightBytes[12] & 0xFF) << 8) | (weightBytes[11] & 0xFF)) / 200.0f;
weight = (float) (((data[12] & 0xFF) << 8) | (data[11] & 0xFF)) / 200.0f;
}
if (isImpedance) {
impedance = ((data[10] & 0xFF) << 8) | (data[9] & 0xFF);
Timber.d("impedance value is " + impedance);
}
String date_string = year + "/" + month + "/" + day + "/" + hours + "/" + min;
@@ -175,29 +177,31 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
// Is the year plausible? Check if the year is in the range of 20 years...
if (validateDate(date_time, 20)) {
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
final ScaleUser scaleUser = OpenScale.getInstance().getSelectedScaleUser();
ScaleMeasurement scaleBtData = new ScaleMeasurement();
scaleBtData.setWeight(Converters.toKilogram(weight, selectedUser.getScaleUnit()));
scaleBtData.setWeight(Converters.toKilogram(weight, scaleUser.getScaleUnit()));
scaleBtData.setDateTime(date_time);
// estimate fat, water and LBM until library is reversed engineered
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
int sex;
MeasurementViewSettings settings = new MeasurementViewSettings(prefs, WaterMeasurementView.KEY);
EstimatedWaterMetric waterMetric = EstimatedWaterMetric.getEstimatedMetric(
EstimatedWaterMetric.FORMULA.valueOf(settings.getEstimationFormula()));
scaleBtData.setWater(waterMetric.getWater(selectedUser, scaleBtData));
if (scaleUser.getGender() == Converters.Gender.MALE) {
sex = 1;
} else {
sex = 0;
}
settings = new MeasurementViewSettings(prefs, FatMeasurementView.KEY);
EstimatedFatMetric fatMetric = EstimatedFatMetric.getEstimatedMetric(
EstimatedFatMetric.FORMULA.valueOf(settings.getEstimationFormula()));
scaleBtData.setFat(fatMetric.getFat(selectedUser, scaleBtData));
if (impedance != 0.0f) {
MiScaleLib miScaleLib = new MiScaleLib(sex, scaleUser.getAge(), scaleUser.getBodyHeight());
settings = new MeasurementViewSettings(prefs, LBMMeasurementView.KEY);
EstimatedLBMMetric lbmMetric = EstimatedLBMMetric.getEstimatedMetric(
EstimatedLBMMetric.FORMULA.valueOf(settings.getEstimationFormula()));
scaleBtData.setLbm(lbmMetric.getLBM(selectedUser, scaleBtData));
scaleBtData.setWater(miScaleLib.getWater(weight, impedance));
scaleBtData.setVisceralFat(miScaleLib.getVisceralFat(weight));
scaleBtData.setFat(miScaleLib.getBodyFat(weight, impedance));
scaleBtData.setMuscle(miScaleLib.getMuscle(weight, impedance));
scaleBtData.setBone(miScaleLib.getBoneMass(weight, impedance));
} else {
Timber.d("Impedance value is zero");
}
addScaleMeasurement(scaleBtData);
} else {

View File

@@ -0,0 +1,164 @@
/* Copyright (C) 2019 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
/**
* based on https://github.com/prototux/MIBCS-reverse-engineering by prototux
*/
package com.health.openscale.core.bluetooth.lib;
public class MiScaleLib {
private int sex; // male = 1; female = 0
private int age;
private float height;
public MiScaleLib(int sex, int age, float height) {
this.sex = sex;
this.age = age;
this.height = height;
}
private float getLBMCoefficient(float weight, float impedance) {
float lbm = (height * 9.058f / 100.0f) * (height / 100.0f);
lbm += weight * 0.32f + 12.226f;
lbm -= impedance * 0.0068f;
lbm -= age * 0.0542f;
return lbm;
}
public float getBMI(float weight) {
return weight / (((height * height) / 100.0f) / 100.0f);
}
public float getMuscle(float weight, float impedance) {
float muscleMass = weight - ((getBodyFat(weight, impedance) * 0.01f) * weight) - getBoneMass(weight, impedance);
if (sex == 0 && muscleMass >= 84.0f) {
muscleMass = 120.0f;
}
else if (sex == 1 && muscleMass >= 93.5f) {
muscleMass = 120.0f;
}
return muscleMass;
}
public float getWater(float weight, float impedance) {
float coeff;
float water = (100.0f - getBodyFat(weight, impedance)) * 0.7f;
if (water < 50) {
coeff = 1.02f;
} else {
coeff = 0.98f;
}
return coeff * water;
}
public float getBoneMass(float weight, float impedance) {
float boneMass;
float base;
if (sex == 0) {
base = 0.245691014f;
}
else {
base = 0.18016894f;
}
boneMass = (base - (getLBMCoefficient(weight, impedance) * 0.05158f)) * -1.0f;
if (boneMass > 2.2f) {
boneMass += 0.1f;
}
else {
boneMass -= 0.1f;
}
if (sex == 0 && boneMass > 5.1f) {
boneMass = 8.0f;
}
else if (sex == 1 && boneMass > 5.2f) {
boneMass = 8.0f;
}
return boneMass;
}
public float getVisceralFat(float weight) {
float visceralFat = 0.0f;
if (sex == 0) {
if (weight > (13.0f - (height * 0.5f)) * -1.0f) {
float subsubcalc = ((height * 1.45f) + (height * 0.1158f) * height) - 120.0f;
float subcalc = weight * 500.0f / subsubcalc;
visceralFat = (subcalc - 6.0f) + (age * 0.07f);
}
else {
float subcalc = 0.691f + (height * -0.0024f) + (height * -0.0024f);
visceralFat = (((height * 0.027f) - (subcalc * weight)) * -1.0f) + (age * 0.07f) - age;
}
}
else {
if (height < weight * 1.6f) {
float subcalc = ((height * 0.4f) - (height * (height * 0.0826f))) * -1.0f;
visceralFat = ((weight * 305.0f) / (subcalc + 48.0f)) - 2.9f + (age * 0.15f);
}
else {
float subcalc = 0.765f + height * -0.0015f;
visceralFat = (((height * 0.143f) - (weight * subcalc)) * -1.0f) + (age * 0.15f) - 5.0f;
}
}
return visceralFat;
}
public float getBodyFat(float weight, float impedance) {
float bodyFat = 0.0f;
float lbmSub = 0.8f;
if (sex == 0 && age <= 49) {
lbmSub = 9.25f;
} else if (sex == 1 && age > 49) {
lbmSub = 7.25f;
}
float lbmCoeff = getLBMCoefficient(weight, impedance);
if (sex == 1 && weight < 61) {
bodyFat = (1.0f - (((lbmCoeff - lbmSub) * 0.98f) / weight)) * 100.0f;
}
else if (sex == 0 && weight < 50) {
bodyFat = (1.0f - (((lbmCoeff - lbmSub) * 1.02f) / weight)) * 100.0f;
}
else if (sex == 0 && weight < 60) {
bodyFat = (1.0f - (((lbmCoeff - lbmSub) * 0.96f) / weight)) * 100.0f;
}
else if (sex == 0 && height < 160) {
bodyFat = (1.0f - (((lbmCoeff - lbmSub) * 1.03f) / weight)) * 100.0f;
} else {
bodyFat = (1.0f - ((lbmCoeff - lbmSub) / weight)) * 100.0f;
}
if (bodyFat > 63.0f) {
bodyFat = 75.0f;
}
return bodyFat;
}
}