From 26ab724410dba00fadd643197d55b5a0cb1baa5e Mon Sep 17 00:00:00 2001 From: Graham Jones Date: Wed, 10 Apr 2024 20:51:09 +0100 Subject: [PATCH] enabled new BLE2 data source - not working yet! --- app/build.gradle | 2 +- .../openseizuredetector/SdDataSourceBLE2.java | 271 ++++++++++++++++++ .../uk/org/openseizuredetector/SdServer.java | 5 + app/src/main/res/values/datasource_list.xml | 2 + 4 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/uk/org/openseizuredetector/SdDataSourceBLE2.java diff --git a/app/build.gradle b/app/build.gradle index 061d7e7..7b98881 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -81,7 +81,7 @@ dependencies { implementation 'com.techyourchance:threadposter:1.0.1' implementation 'com.google.android.material:material' implementation "com.github.RideBeeline:android-bluetooth-current-time-service:0.1.2" - + implementation 'com.github.weliem:blessed-android:2.5.0' } repositories { diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSourceBLE2.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSourceBLE2.java new file mode 100644 index 0000000..8d5f853 --- /dev/null +++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSourceBLE2.java @@ -0,0 +1,271 @@ +/* + Android_Pebble_sd - Android alarm client for openseizuredetector.. + + See http://openseizuredetector.org for more information. + + Copyright Graham Jones, 2015, 2016 + + This file is part of pebble_sd. + + Android_Pebble_sd 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. + + Android_Pebble_sd 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 Android_pebble_sd. If not, see . + +*/ +package uk.org.openseizuredetector; + +import static com.welie.blessed.BluetoothBytesParser.FORMAT_SINT16; +import static com.welie.blessed.BluetoothBytesParser.FORMAT_UINT16; +import static com.welie.blessed.BluetoothBytesParser.FORMAT_UINT8; +import static com.welie.blessed.BluetoothBytesParser.asHexString; +import static java.lang.Math.abs; + +import android.Manifest; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.le.ScanResult; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.os.Looper; +import android.text.format.Time; +import android.util.Log; + +import androidx.core.app.ActivityCompat; + +import com.welie.blessed.BluetoothBytesParser; +import com.welie.blessed.BluetoothCentralManager; +import com.welie.blessed.BluetoothCentralManagerCallback; +import com.welie.blessed.BluetoothPeripheral; +import com.welie.blessed.BluetoothPeripheralCallback; +import com.welie.blessed.ConnectionPriority; +import com.welie.blessed.GattStatus; +import com.welie.blessed.PhyOptions; +import com.welie.blessed.PhyType; +import com.welie.blessed.WriteType; + +import org.jetbrains.annotations.NotNull; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; +import java.util.UUID; + +import co.beeline.android.bluetooth.currenttimeservice.CurrentTimeService; + + +/** + * A data source that registers for BLE GATT notifications from a device and + * waits to be notified of data being available. + * SdDataSourceBLE2 uses the BLESSED library for the BLE access rather than native Android + * BLE methods to try to improve start-up/shutdown reliability. + */ +public class SdDataSourceBLE2 extends SdDataSource { + private int MAX_RAW_DATA = 125; // 5 seconds at 25 Hz. + private String TAG = "SdDataSourceBLE2"; + private BluetoothManager mBluetoothManager; + private BluetoothAdapter mBluetoothAdapter; + private String mBluetoothDeviceAddress; + private BluetoothGatt mBluetoothGatt; + private int mConnectionState = STATE_DISCONNECTED; + + private int nRawData = 0; + private double[] rawData = new double[MAX_RAW_DATA]; + private double[] rawData3d = new double[MAX_RAW_DATA * 3]; + private int mAccFmt = 0; + private boolean waitForDescriptorWrite = false; + + private static final int STATE_DISCONNECTED = 0; + private static final int STATE_CONNECTING = 1; + private static final int STATE_CONNECTED = 2; + + + public static String SERV_DEV_INFO = "0000180a-0000-1000-8000-00805f9b34fb"; + public static String SERV_HEART_RATE = "0000180d-0000-1000-8000-00805f9b34fb"; + public static String CHAR_HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb"; + + public static String SERV_OSD = "000085e9-0000-1000-8000-00805f9b34fb"; + public static String CHAR_OSD_ACC_DATA = "000085e9-0001-1000-8000-00805f9b34fb"; + public static String CHAR_OSD_BATT_DATA = "000085e9-0002-1000-8000-00805f9b34fb"; + public static String CHAR_OSD_WATCH_ID = "000085e9-0003-1000-8000-00805f9b34fb"; + public static String CHAR_OSD_WATCH_FW = "000085e9-0004-1000-8000-00805f9b34fb"; + public static String CHAR_OSD_ACC_FMT = "000085e9-0005-1000-8000-00805f9b34fb"; + // Valid values are 0: 8 bit vector magnitude scaled so 1g=44 + public final static int ACC_FMT_8BIT = 0; + public final static int ACC_FMT_16BIT = 1; + public final static int ACC_FMT_3D = 3; + public static String CHAR_OSD_STATUS = "000085e9-0006-1000-8000-00805f9b34fb"; + + public static String SERV_INFINITIME_MOTION = "00030000-78fc-48fe-8e23-433b3a1942d0"; + public static String CHAR_INFINITIME_ACC_DATA = "00030002-78fc-48fe-8e23-433b3a1942d0"; + public static String CHAR_INFINITIME_OSD_STATUS = "00030078-78fc-48fe-8e23-433b3a1942d0"; + + public static String CHAR_BATT_DATA = "00002a19-0000-1000-8000-00805f9b34fb"; + public static String SERV_BATT = "0000180f-0000-1000-8000-00805f9b34fb"; + + // public static String CHAR_MANUF_NAME = "00002a29-0000-1000-8000-00805f9b34fb"; + // public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; + private BluetoothGatt mGatt; + private BluetoothGattCharacteristic mOsdChar; + private BluetoothGattCharacteristic mStatusChar; + private BluetoothCentralManager mBluetoothCentralManager; + + + public SdDataSourceBLE2(Context context, Handler handler, + SdDataReceiver sdDataReceiver) { + super(context, handler, sdDataReceiver); + mName = "BLE2"; + } + + + /** + * Start the datasource updating - initialises from sharedpreferences first to + * make sure any changes to preferences are taken into account. + */ + public void start() { + super.start(); + Log.i(TAG, "start() - mBleDeviceAddr="+mBleDeviceAddr); + mUtil.writeToSysLogFile("SdDataSourceBLE.start() - mBleDeviceAddr=" + mBleDeviceAddr); + + if (mBleDeviceAddr == "" || mBleDeviceAddr == null) { + final Intent intent = new Intent(this.mContext, BLEScanActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + + // Note, these values are set in BleScanActivity and written to shared preferences, which + // ae read in SdDataSource.java + // FIXME: Read the shared preferences in this class so SdDataSource does not need to know + // FIXME: about BLE details. + Log.i(TAG, "mBLEDevice is " + mBleDeviceName + ", Addr=" + mBleDeviceAddr); + mSdData.watchSdName = mBleDeviceName; + mSdData.watchPartNo = mBleDeviceAddr; + + boolean success = CurrentTimeService.startServer(mContext); + + bleConnect(); + + } + + private void bleConnect() { + + // Create BluetoothCentral and receive callbacks on the main thread + mBluetoothCentralManager = new BluetoothCentralManager(mContext, + mBluetoothCentralManagerCallback, + new Handler(Looper.getMainLooper()) + ); + // Look for the specified device + mBluetoothCentralManager.scanForPeripheralsWithAddresses(new String[]{mBleDeviceAddr}); + } + + + private final BluetoothCentralManagerCallback mBluetoothCentralManagerCallback = new BluetoothCentralManagerCallback() { + @Override + public void onDiscoveredPeripheral(BluetoothPeripheral peripheral, ScanResult scanResult) { + mBluetoothCentralManager.stopScan(); + mBluetoothCentralManager.connectPeripheral(peripheral, peripheralCallback); + } + }; + + // Callback for peripherals + private final BluetoothPeripheralCallback peripheralCallback = new BluetoothPeripheralCallback() { + @Override // BluetoothPeripheralCallback + public void onServicesDiscovered(@NotNull BluetoothPeripheral peripheral) { + // Request a higher MTU, iOS always asks for 185 + peripheral.requestMtu(185); + // Request a new connection priority + peripheral.requestConnectionPriority(ConnectionPriority.HIGH); + peripheral.setPreferredPhy(PhyType.LE_2M, PhyType.LE_2M, PhyOptions.S2); + peripheral.readPhy(); + + + // Try to turn on notifications for other characteristics + peripheral.readCharacteristic(UUID.fromString(SERV_BATT), UUID.fromString(CHAR_BATT_DATA)); + peripheral.setNotify(UUID.fromString(SERV_BATT), UUID.fromString(CHAR_BATT_DATA), true); + } + + @Override + public void onNotificationStateUpdate(@NotNull BluetoothPeripheral peripheral, @NotNull BluetoothGattCharacteristic characteristic, @NotNull GattStatus status) { + if (status == GattStatus.SUCCESS) { + final boolean isNotifying = peripheral.isNotifying(characteristic); + Log.i(TAG, String.format("SUCCESS: Notify set to '%s' for %s", isNotifying, characteristic.getUuid())); + + } else { + Log.e(TAG, String.format("ERROR: Changing notification state failed for %s (%s)", + characteristic.getUuid(), status)); + } + } + + @Override + public void onCharacteristicWrite(@NotNull BluetoothPeripheral peripheral, @NotNull byte[] value, @NotNull BluetoothGattCharacteristic characteristic, @NotNull GattStatus status) { + if (status == GattStatus.SUCCESS) { + Log.i(TAG, String.format("SUCCESS: Writing <%s> to <%s>", asHexString(value), characteristic.getUuid())); + } else { + Log.i(TAG, String.format("ERROR: Failed writing <%s> to <%s> (%s)", asHexString(value), characteristic.getUuid(), status)); + } + } + + @Override + public void onCharacteristicUpdate(@NotNull BluetoothPeripheral peripheral, @NotNull byte[] value, @NotNull BluetoothGattCharacteristic characteristic, @NotNull GattStatus status) { + if (status != GattStatus.SUCCESS) return; + + UUID characteristicUUID = characteristic.getUuid(); + BluetoothBytesParser parser = new BluetoothBytesParser(value); + + if (characteristicUUID.equals(UUID.fromString(CHAR_HEART_RATE_MEASUREMENT))) { + Log.d(TAG, String.format("%s", "HR Measurement")); + } else if (characteristicUUID.equals(UUID.fromString(CHAR_BATT_DATA))) { + int batteryLevel = parser.getIntValue(FORMAT_UINT8); + Log.i(TAG, String.format("Received battery level %d%%", batteryLevel)); + } + } + + @Override + public void onMtuChanged(@NotNull BluetoothPeripheral peripheral, int mtu, @NotNull GattStatus status) { + Log.i(TAG, String.format("new MTU set: %d", mtu)); + } + + + }; + + + private void bleDisconnect() { + } + + /** + * Stop the datasource from updating + */ + public void stop() { + Log.i(TAG, "stop()"); + mUtil.writeToSysLogFile("SDDataSourceBLE.stop()"); + + bleDisconnect(); + CurrentTimeService.stopServer(); + super.stop(); + } + + + + +} diff --git a/app/src/main/java/uk/org/openseizuredetector/SdServer.java b/app/src/main/java/uk/org/openseizuredetector/SdServer.java index 48845a4..3d2f82f 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdServer.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdServer.java @@ -291,6 +291,11 @@ public class SdServer extends Service implements SdDataReceiver { mUtil.writeToSysLogFile("SdServer.onStartCommand() - creating SdDataSourceBLE"); mSdDataSource = new SdDataSourceBLE(this.getApplicationContext(), mHandler, this); break; + case "BLE2": + Log.v(TAG, "Selecting BLE2 DataSource"); + mUtil.writeToSysLogFile("SdServer.onStartCommand() - creating SdDataSourceBLE2"); + mSdDataSource = new SdDataSourceBLE2(this.getApplicationContext(), mHandler, this); + break; case "Phone": Log.v(TAG, "Selecting Phone Sensor DataSource"); mUtil.writeToSysLogFile("SdServer.onStartCommand() - creating SdDataSourcePhone"); diff --git a/app/src/main/res/values/datasource_list.xml b/app/src/main/res/values/datasource_list.xml index f095243..dc73409 100644 --- a/app/src/main/res/values/datasource_list.xml +++ b/app/src/main/res/values/datasource_list.xml @@ -3,6 +3,7 @@ "Garmin Watch" "Bluetooth Device" + "BLE2" "Pebble Watch" "Phone Sensor (for testing)" "Network (for Wifi Alerts)" @@ -10,6 +11,7 @@ "Garmin" "BLE" + "BLE2" "Pebble" "Phone" "Network"