181 lines
5.2 KiB
Dart
181 lines
5.2 KiB
Dart
|
|
import 'dart:async';
|
||
|
|
import 'dart:convert';
|
||
|
|
import 'dart:typed_data';
|
||
|
|
|
||
|
|
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||
|
|
import 'package:get/get.dart';
|
||
|
|
import 'package:nmea/nmea.dart';
|
||
|
|
import 'package:terepi_seged/gnss_sentences/gngga.dart';
|
||
|
|
|
||
|
|
class BluetoothTestViewController extends GetxController {
|
||
|
|
Rx<BluetoothState> bluetoothState = BluetoothState.UNKNOWN.obs;
|
||
|
|
RxList<BluetoothDevice> devices = RxList<BluetoothDevice>([]);
|
||
|
|
Rx<BluetoothDevice>? selectedDevice;
|
||
|
|
final NmeaDecoder nmeaDecoder = NmeaDecoder();
|
||
|
|
|
||
|
|
BluetoothConnection? connection;
|
||
|
|
StreamSubscription<BluetoothDiscoveryResult>? _bluetoothDiscoveryStream;
|
||
|
|
RxList<BluetoothDiscoveryResult> bluetoothDiscoveryResults =
|
||
|
|
RxList<BluetoothDiscoveryResult>.empty(growable: true);
|
||
|
|
|
||
|
|
// bool get isConnected => connection != null && connection!.isConnected;
|
||
|
|
bool isConnected = false;
|
||
|
|
int count = 0;
|
||
|
|
String _messageBuffer = '';
|
||
|
|
bool hasGpsData = false;
|
||
|
|
DateTime lastGpsRefreshTime = DateTime.now();
|
||
|
|
RxString utcOfPositionFix = "".obs;
|
||
|
|
|
||
|
|
@override
|
||
|
|
void onInit() {
|
||
|
|
super.onInit();
|
||
|
|
|
||
|
|
print("Call BluetoothTestController.OnInit");
|
||
|
|
|
||
|
|
FlutterBluetoothSerial.instance.state.then((state) {
|
||
|
|
bluetoothState.value = state;
|
||
|
|
if (bluetoothState.value.isEnabled) {
|
||
|
|
//_listBoundedDevices();
|
||
|
|
_startDiscovery();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
nmeaDecoder.registerTalkerSentence("GGA", (line) => Gngga(raw: line));
|
||
|
|
|
||
|
|
//Loading, Success, Error handle with 1 line of code
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
void onClose() {
|
||
|
|
super.onClose();
|
||
|
|
FlutterBluetoothSerial.instance.setPairingRequestHandler(null);
|
||
|
|
if (isConnected) {
|
||
|
|
connection?.close();
|
||
|
|
// connection = null;
|
||
|
|
print("BluetoothTestController dispose ....");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
_listBoundedDevices() {
|
||
|
|
FlutterBluetoothSerial.instance
|
||
|
|
.getBondedDevices()
|
||
|
|
.then((List<BluetoothDevice> boundedDevices) {
|
||
|
|
devices.value = boundedDevices;
|
||
|
|
devices.refresh();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void setSelectedDevice(BluetoothDevice device) {
|
||
|
|
selectedDevice = Rx<BluetoothDevice>(device);
|
||
|
|
|
||
|
|
if (!isConnected) {
|
||
|
|
BluetoothConnection.toAddress(selectedDevice?.value.address)
|
||
|
|
.then((value) {
|
||
|
|
connection = value;
|
||
|
|
isConnected = true;
|
||
|
|
print("Bleutooth device connected .... ${connection!.isConnected}");
|
||
|
|
|
||
|
|
connection!.input!.listen(_onDataReceived);
|
||
|
|
});
|
||
|
|
|
||
|
|
// connection!.input!.listen((data) {
|
||
|
|
// print("Data -> $data");
|
||
|
|
// });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void _startDiscovery() {
|
||
|
|
_bluetoothDiscoveryStream =
|
||
|
|
FlutterBluetoothSerial.instance.startDiscovery().listen((event) {
|
||
|
|
final existingIndex = bluetoothDiscoveryResults.indexWhere(
|
||
|
|
(element) => element.device.address == event.device.address);
|
||
|
|
if (existingIndex > 0) {
|
||
|
|
bluetoothDiscoveryResults[existingIndex] = event;
|
||
|
|
update();
|
||
|
|
} else {
|
||
|
|
bluetoothDiscoveryResults.add(event);
|
||
|
|
update();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
_bluetoothDiscoveryStream!.onDone(() {});
|
||
|
|
}
|
||
|
|
|
||
|
|
void _onDataReceived(Uint8List data) {
|
||
|
|
// Allocate buffer for parsed data
|
||
|
|
int backspacesCounter = 0;
|
||
|
|
String sentence = '';
|
||
|
|
|
||
|
|
// print("Data: $data");
|
||
|
|
|
||
|
|
for (var byte in data) {
|
||
|
|
if (byte == 8 || byte == 127) {
|
||
|
|
backspacesCounter++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Uint8List buffer = Uint8List(data.length - backspacesCounter);
|
||
|
|
int bufferIndex = buffer.length;
|
||
|
|
|
||
|
|
// Apply backspace control character
|
||
|
|
backspacesCounter = 0;
|
||
|
|
for (int i = data.length - 1; i >= 0; i--) {
|
||
|
|
if (data[i] == 8 || data[i] == 127) {
|
||
|
|
backspacesCounter++;
|
||
|
|
} else {
|
||
|
|
if (backspacesCounter > 0) {
|
||
|
|
backspacesCounter--;
|
||
|
|
} else {
|
||
|
|
buffer[--bufferIndex] = data[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create message if there is new line character
|
||
|
|
String dataString = String.fromCharCodes(buffer);
|
||
|
|
int index = buffer.indexOf(13);
|
||
|
|
if (~index != 0) {
|
||
|
|
sentence = backspacesCounter > 0
|
||
|
|
? _messageBuffer.substring(
|
||
|
|
0, _messageBuffer.length - backspacesCounter)
|
||
|
|
: _messageBuffer + dataString.substring(0, index);
|
||
|
|
_messageBuffer = dataString.substring(index);
|
||
|
|
} else {
|
||
|
|
_messageBuffer = (backspacesCounter > 0
|
||
|
|
? _messageBuffer.substring(
|
||
|
|
0, _messageBuffer.length - backspacesCounter)
|
||
|
|
: _messageBuffer + dataString);
|
||
|
|
}
|
||
|
|
|
||
|
|
count++;
|
||
|
|
// print("Message ($count): $sentence");
|
||
|
|
_processGnssMessage(sentence);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _processGnssMessage(String message) {
|
||
|
|
LineSplitter lineSplitter = const LineSplitter();
|
||
|
|
List<String> lines = lineSplitter.convert(message);
|
||
|
|
|
||
|
|
if (lines.isEmpty) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (String line in lines) {
|
||
|
|
if (line.trim().isEmpty) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (line.startsWith("\$GNGGA")) {
|
||
|
|
final sentence = nmeaDecoder.decode(line);
|
||
|
|
if (sentence!.valid && sentence is Gngga) {
|
||
|
|
hasGpsData = sentence.gpsQualityIndicator > 0;
|
||
|
|
if (hasGpsData) {
|
||
|
|
if (DateTime.now().difference(lastGpsRefreshTime).inSeconds >= 4) {
|
||
|
|
lastGpsRefreshTime = DateTime.now();
|
||
|
|
utcOfPositionFix.value = sentence.utcOfPositionFix;
|
||
|
|
}
|
||
|
|
print("Last update: $utcOfPositionFix");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|