diff --git a/build.gradle b/build.gradle index 31f292a8..f4a7015f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.7.2' + classpath 'com.android.tools.build:gradle:8.7.3' classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.2' classpath 'com.google.gms:google-services:4.4.2' diff --git a/common/src/main/java/com/platypii/baseline/bluetooth/Flysight2Protocol.java b/common/src/main/java/com/platypii/baseline/bluetooth/Flysight2Protocol.java index a7567ac2..7ff7228e 100644 --- a/common/src/main/java/com/platypii/baseline/bluetooth/Flysight2Protocol.java +++ b/common/src/main/java/com/platypii/baseline/bluetooth/Flysight2Protocol.java @@ -12,8 +12,10 @@ import androidx.annotation.Nullable; import com.welie.blessed.BluetoothPeripheral; import com.welie.blessed.GattStatus; +import com.welie.blessed.WriteType; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.Arrays; import java.util.UUID; public class Flysight2Protocol extends BleProtocol { @@ -24,11 +26,15 @@ public class Flysight2Protocol extends BleProtocol { private static final UUID flysightService0 = UUID.fromString("00000000-cc7a-482a-984a-7f2ed5b3e58f"); private static final UUID flysightService1 = UUID.fromString("00000001-cc7a-482a-984a-7f2ed5b3e58f"); private static final UUID flysightService2 = UUID.fromString("00000002-cc7a-482a-984a-7f2ed5b3e58f"); + // Flysight characteristics private static final UUID flysightCharacteristicGNSS = UUID.fromString("00000000-8e22-4541-9d4c-21edae82ed19"); private static final UUID flysightCharacteristicTX = UUID.fromString("00000001-8e22-4541-9d4c-21edae82ed19"); private static final UUID flysightCharacteristicRX = UUID.fromString("00000002-8e22-4541-9d4c-21edae82ed19"); + // Flysight commands + private static final byte[] flysightCommandHeartbeat = new byte[]{(byte) 0xfe}; + private static final long gpsEpochMilliseconds = 315964800000L - 18000L; // January 6, 1980 - 18s private static final long millisecondsPerWeek = 604800000L; @@ -46,6 +52,8 @@ public boolean canParse(@NonNull BluetoothPeripheral peripheral, @Nullable ScanR public void onServicesDiscovered(@NonNull BluetoothPeripheral peripheral) { Log.i(TAG, "flysight services discovered " + peripheral.getCurrentMtu()); peripheral.requestMtu(256); + // Start heartbeat thread + startHeartbeat(peripheral, flysightService0, flysightCharacteristicRX); } @Override @@ -58,6 +66,10 @@ public void onMtuChanged(@NonNull BluetoothPeripheral peripheral, int mtu, @NonN @Override public void processBytes(@NonNull BluetoothPeripheral peripheral, @NonNull byte[] value) { try { + // FlySight2 RC pre-2024-11-11 didn't have flag byte + if (value.length == 29 && value[0] == -80) { // 0xb0 + value = Arrays.copyOfRange(value, 1, value.length); + } final ByteBuffer buf = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN); final int tow = buf.getInt(0); // gps time of week final double lng = buf.getInt(4) * 1e-7; @@ -92,4 +104,23 @@ public void processBytes(@NonNull BluetoothPeripheral peripheral, @NonNull byte[ Exceptions.report(e); } } + + private void startHeartbeat(@NonNull BluetoothPeripheral peripheral, @NonNull UUID service, @NonNull UUID characteristic) { + // Start heartbeat thread + new Thread(() -> { + try { + while (true) { + // Send heartbeat every 14.5 seconds + if (peripheral.writeCharacteristic(service, characteristic, flysightCommandHeartbeat, WriteType.WITHOUT_RESPONSE)) { + Thread.sleep(14500); + } else { + Log.w(TAG, "Failed to send heartbeat, stopping heartbeat thread"); + break; + } + } + } catch (InterruptedException e) { + Log.i(TAG, "Heartbeat thread interrupted"); + } + }).start(); + } }