mirror of
https://github.com/oliexdev/openScale.git
synced 2025-09-02 04:42:42 +02:00
changed custom openScale scale from Bluetooth 3.x to Bluetooth 4.x to comply with openScale MCU version 1.1
This commit is contained in:
@@ -476,6 +476,8 @@ public abstract class BluetoothCommunication {
|
|||||||
|
|
||||||
bluetoothGatt.close();
|
bluetoothGatt.close();
|
||||||
bluetoothGatt = null;
|
bluetoothGatt = null;
|
||||||
|
|
||||||
|
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,28 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package com.health.openscale.core.bluetooth;
|
package com.health.openscale.core.bluetooth;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothGatt;
|
||||||
import android.bluetooth.BluetoothSocket;
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||||
private final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // Standard SerialPortService ID
|
private final UUID WEIGHT_MEASUREMENT_SERVICE = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb");
|
||||||
|
private final UUID WEIGHT_MEASUREMENT_CHARACTERISTIC = UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"); // Bluetooth Modul HM-10
|
||||||
|
private final UUID WEIGHT_MEASUREMENT_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
|
||||||
|
|
||||||
private BluetoothSocket btSocket = null;
|
private String string_data = new String();
|
||||||
private BluetoothDevice btDevice = null;
|
|
||||||
|
|
||||||
private BluetoothConnectedThread btConnectThread = null;
|
|
||||||
|
|
||||||
public BluetoothCustomOpenScale(Context context) {
|
public BluetoothCustomOpenScale(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -44,12 +42,33 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String driverName() {
|
public String driverName() {
|
||||||
return "Custom Open Scale";
|
return "Custom openScale";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean nextInitCmd(int stateNr) {
|
protected boolean nextInitCmd(int stateNr) {
|
||||||
return false;
|
switch (stateNr) {
|
||||||
|
case 0:
|
||||||
|
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC, WEIGHT_MEASUREMENT_CONFIG);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
|
||||||
|
String date_time = String.format(Locale.US, "2%1d,%1d,%1d,%1d,%1d,%1d,",
|
||||||
|
cal.get(Calendar.YEAR)-2000,
|
||||||
|
cal.get(Calendar.MONTH) + 1,
|
||||||
|
cal.get(Calendar.DAY_OF_MONTH),
|
||||||
|
cal.get(Calendar.HOUR_OF_DAY),
|
||||||
|
cal.get(Calendar.MINUTE),
|
||||||
|
cal.get(Calendar.SECOND));
|
||||||
|
|
||||||
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC, date_time.getBytes());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -62,211 +81,94 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connect(String hwAddress) {
|
|
||||||
|
|
||||||
if (btAdapter == null) {
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_NO_DEVICE_FOUND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
btDevice = btAdapter.getRemoteDevice(hwAddress);
|
|
||||||
try {
|
|
||||||
// Get a BluetoothSocket to connect with the given BluetoothDevice
|
|
||||||
btSocket = btDevice.createRfcommSocketToServiceRecord(uuid);
|
|
||||||
} catch (IOException e) {
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Can't get a bluetooth socket");
|
|
||||||
btDevice = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread socketThread = new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
if (!btSocket.isConnected()) {
|
|
||||||
// Connect the device through the socket. This will block
|
|
||||||
// until it succeeds or throws an exception
|
|
||||||
btSocket.connect();
|
|
||||||
|
|
||||||
// Bluetooth connection was successful
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_ESTABLISHED);
|
|
||||||
|
|
||||||
btConnectThread = new BluetoothConnectedThread();
|
|
||||||
btConnectThread.start();
|
|
||||||
}
|
|
||||||
} catch (IOException connectException) {
|
|
||||||
// Unable to connect; close the socket and get out
|
|
||||||
disconnect(false);
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_NO_DEVICE_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
socketThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disconnect(boolean doCleanup) {
|
|
||||||
if (btSocket != null) {
|
|
||||||
if (btSocket.isConnected()) {
|
|
||||||
try {
|
|
||||||
btSocket.close();
|
|
||||||
btSocket = null;
|
|
||||||
} catch (IOException closeException) {
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Can't close bluetooth socket");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btConnectThread != null) {
|
|
||||||
btConnectThread.cancel();
|
|
||||||
btConnectThread = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
btDevice = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearEEPROM()
|
public void clearEEPROM()
|
||||||
{
|
{
|
||||||
sendBtData("9");
|
byte[] cmd = {(byte)'9'};
|
||||||
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean sendBtData(String data) {
|
@Override
|
||||||
if (btSocket.isConnected()) {
|
public void onBluetoothDataChange(BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic gattCharacteristic) {
|
||||||
btConnectThread = new BluetoothConnectedThread();
|
final byte[] data = gattCharacteristic.getValue();
|
||||||
btConnectThread.write(data.getBytes());
|
|
||||||
|
|
||||||
btConnectThread.cancel();
|
if (data != null) {
|
||||||
return true;
|
for (byte character : data) {
|
||||||
}
|
string_data += (char) (character & 0xFF);
|
||||||
|
|
||||||
return false;
|
if (character == '\n') {
|
||||||
}
|
parseBtString(string_data);
|
||||||
|
string_data = new String();
|
||||||
private class BluetoothConnectedThread extends Thread {
|
|
||||||
private InputStream btInStream;
|
|
||||||
private OutputStream btOutStream;
|
|
||||||
private volatile boolean isCancel;
|
|
||||||
|
|
||||||
public BluetoothConnectedThread() {
|
|
||||||
|
|
||||||
isCancel = false;
|
|
||||||
|
|
||||||
// Get the input and output bluetooth streams
|
|
||||||
try {
|
|
||||||
btInStream = btSocket.getInputStream();
|
|
||||||
btOutStream = btSocket.getOutputStream();
|
|
||||||
} catch (IOException e) {
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Can't get bluetooth input or output stream " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
final StringBuilder btLine = new StringBuilder();
|
|
||||||
|
|
||||||
// Keep listening to the InputStream until an exception occurs (e.g. device partner goes offline)
|
|
||||||
while (!isCancel) {
|
|
||||||
try {
|
|
||||||
// stream read is a blocking method
|
|
||||||
char btChar = (char) btInStream.read();
|
|
||||||
|
|
||||||
btLine.append(btChar);
|
|
||||||
|
|
||||||
if (btLine.charAt(btLine.length() - 1) == '\n') {
|
|
||||||
ScaleMeasurement scaleMeasurement = parseBtString(btLine.toString());
|
|
||||||
|
|
||||||
if (scaleMeasurement != null) {
|
|
||||||
addScaleData(scaleMeasurement);
|
|
||||||
}
|
|
||||||
|
|
||||||
btLine.setLength(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
cancel();
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScaleMeasurement parseBtString(String btString) throws IOException {
|
|
||||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
|
||||||
btString = btString.substring(0, btString.length() - 1); // delete newline '\n' of the string
|
|
||||||
|
|
||||||
if (btString.charAt(0) != '$' && btString.charAt(2) != '$') {
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Parse error of bluetooth string. String has not a valid format");
|
|
||||||
}
|
|
||||||
|
|
||||||
String btMsg = btString.substring(3, btString.length()); // message string
|
|
||||||
|
|
||||||
switch (btString.charAt(1)) {
|
|
||||||
case 'I':
|
|
||||||
Timber.i("MCU Information: %s", btMsg);
|
|
||||||
break;
|
|
||||||
case 'E':
|
|
||||||
Timber.e("MCU Error: %s", btMsg);
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
Timber.i("MCU stored data size: %s", btMsg);
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
String[] csvField = btMsg.split(",");
|
|
||||||
|
|
||||||
try {
|
|
||||||
int checksum = 0;
|
|
||||||
|
|
||||||
checksum ^= Integer.parseInt(csvField[0]);
|
|
||||||
checksum ^= Integer.parseInt(csvField[1]);
|
|
||||||
checksum ^= Integer.parseInt(csvField[2]);
|
|
||||||
checksum ^= Integer.parseInt(csvField[3]);
|
|
||||||
checksum ^= Integer.parseInt(csvField[4]);
|
|
||||||
checksum ^= Integer.parseInt(csvField[5]);
|
|
||||||
checksum ^= (int) Float.parseFloat(csvField[6]);
|
|
||||||
checksum ^= (int) Float.parseFloat(csvField[7]);
|
|
||||||
checksum ^= (int) Float.parseFloat(csvField[8]);
|
|
||||||
checksum ^= (int) Float.parseFloat(csvField[9]);
|
|
||||||
|
|
||||||
int btChecksum = Integer.parseInt(csvField[10]);
|
|
||||||
|
|
||||||
if (checksum == btChecksum) {
|
|
||||||
scaleBtData.setId(-1);
|
|
||||||
scaleBtData.setUserId(Integer.parseInt(csvField[0]));
|
|
||||||
String date_string = csvField[1] + "/" + csvField[2] + "/" + csvField[3] + "/" + csvField[4] + "/" + csvField[5];
|
|
||||||
scaleBtData.setDateTime(new SimpleDateFormat("yyyy/MM/dd/HH/mm").parse(date_string));
|
|
||||||
|
|
||||||
scaleBtData.setWeight(Float.parseFloat(csvField[6]));
|
|
||||||
scaleBtData.setFat(Float.parseFloat(csvField[7]));
|
|
||||||
scaleBtData.setWater(Float.parseFloat(csvField[8]));
|
|
||||||
scaleBtData.setMuscle(Float.parseFloat(csvField[9]));
|
|
||||||
|
|
||||||
return scaleBtData;
|
|
||||||
} else {
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error calculated checksum (" + checksum + ") and received checksum (" + btChecksum + ") is different");
|
|
||||||
}
|
|
||||||
} catch (ParseException e) {
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")");
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while decoding a number of bluetooth string (" + e.getMessage() + ")");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error unknown MCU command");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(byte[] bytes) {
|
|
||||||
try {
|
|
||||||
btOutStream.write(bytes);
|
|
||||||
} catch (IOException e) {
|
|
||||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while writing to bluetooth socket " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancel() {
|
|
||||||
isCancel = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parseBtString(String btString) {
|
||||||
|
btString = btString.substring(0, btString.length() - 1); // delete newline '\n' of the string
|
||||||
|
|
||||||
|
if (btString.charAt(0) != '$' && btString.charAt(2) != '$') {
|
||||||
|
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Parse error of bluetooth string. String has not a valid format: " + btString);
|
||||||
|
}
|
||||||
|
|
||||||
|
String btMsg = btString.substring(3, btString.length()); // message string
|
||||||
|
|
||||||
|
switch (btString.charAt(1)) {
|
||||||
|
case 'I':
|
||||||
|
Timber.d("MCU Information: %s", btMsg);
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
Timber.e("MCU Error: %s", btMsg);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
Timber.d("MCU stored data size: %s", btMsg);
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
Timber.d("All data sent");
|
||||||
|
clearEEPROM();
|
||||||
|
disconnect(false);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
String[] csvField = btMsg.split(",");
|
||||||
|
|
||||||
|
try {
|
||||||
|
int checksum = 0;
|
||||||
|
|
||||||
|
checksum ^= Integer.parseInt(csvField[0]);
|
||||||
|
checksum ^= Integer.parseInt(csvField[1]);
|
||||||
|
checksum ^= Integer.parseInt(csvField[2]);
|
||||||
|
checksum ^= Integer.parseInt(csvField[3]);
|
||||||
|
checksum ^= Integer.parseInt(csvField[4]);
|
||||||
|
checksum ^= Integer.parseInt(csvField[5]);
|
||||||
|
checksum ^= (int) Float.parseFloat(csvField[6]);
|
||||||
|
checksum ^= (int) Float.parseFloat(csvField[7]);
|
||||||
|
checksum ^= (int) Float.parseFloat(csvField[8]);
|
||||||
|
checksum ^= (int) Float.parseFloat(csvField[9]);
|
||||||
|
|
||||||
|
int btChecksum = Integer.parseInt(csvField[10]);
|
||||||
|
|
||||||
|
if (checksum == btChecksum) {
|
||||||
|
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||||
|
|
||||||
|
String date_string = csvField[1] + "/" + csvField[2] + "/" + csvField[3] + "/" + csvField[4] + "/" + csvField[5];
|
||||||
|
scaleBtData.setDateTime(new SimpleDateFormat("yyyy/MM/dd/HH/mm").parse(date_string));
|
||||||
|
|
||||||
|
scaleBtData.setWeight(Float.parseFloat(csvField[6]));
|
||||||
|
scaleBtData.setFat(Float.parseFloat(csvField[7]));
|
||||||
|
scaleBtData.setWater(Float.parseFloat(csvField[8]));
|
||||||
|
scaleBtData.setMuscle(Float.parseFloat(csvField[9]));
|
||||||
|
|
||||||
|
addScaleData(scaleBtData);
|
||||||
|
} else {
|
||||||
|
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error calculated checksum (" + checksum + ") and received checksum (" + btChecksum + ") is different");
|
||||||
|
}
|
||||||
|
} catch (ParseException e) {
|
||||||
|
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")");
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while decoding a number of bluetooth string (" + e.getMessage() + ")");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error unknown MCU command : " + btString);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,7 @@ public class BluetoothFactory {
|
|||||||
if (name.startsWith("BEURER BF710".toLowerCase(Locale.US))) {
|
if (name.startsWith("BEURER BF710".toLowerCase(Locale.US))) {
|
||||||
return new BluetoothBeurerSanitas(context, BluetoothBeurerSanitas.DeviceType.BEURER_BF710);
|
return new BluetoothBeurerSanitas(context, BluetoothBeurerSanitas.DeviceType.BEURER_BF710);
|
||||||
}
|
}
|
||||||
if (name.equals("openScale_MCU".toLowerCase(Locale.US))) {
|
if (name.equals("openScale".toLowerCase(Locale.US))) {
|
||||||
return new BluetoothCustomOpenScale(context);
|
return new BluetoothCustomOpenScale(context);
|
||||||
}
|
}
|
||||||
if (name.equals("Mengii".toLowerCase(Locale.US))) {
|
if (name.equals("Mengii".toLowerCase(Locale.US))) {
|
||||||
|
Reference in New Issue
Block a user