import 'dart:async'; import 'dart:convert'; import 'dart:core'; import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; import 'package:buffer/buffer.dart'; import 'package:flutter/material.dart'; import 'package:flutter_blue/flutter_blue.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:sport/db/step_db.dart'; import 'package:sport/services/api/inject_api.dart'; import 'package:sport/utils/toast.dart'; class Bluetooth with ChangeNotifier, InjectApi { static final String PREF_KEY = "SHOES"; factory Bluetooth() => _getInstance(); static Bluetooth _instance; // 获取对象 static Bluetooth _getInstance() { if (_instance == null) { // 使用私有的构造方法来创建对象 _instance = Bluetooth._internal(); } return _instance; } Bluetooth._internal() { //初始化(设置一些默认的)... } FlutterBlue flutterBlue = FlutterBlue.instance; StreamSubscription listenSubscription, periodicSubscription, characteristicSubscription, deviceSubscription, connectedDevicesSubscription; String _mac; BluetoothDevice _device; BluetoothCharacteristic _writeCharacteristic; bool _founded = false; // ignore: close_sinks final StreamController _queryController = StreamController.broadcast(); Stream get queryStream => _queryController.stream; // ignore: close_sinks final StreamController _stateController = StreamController.broadcast(); Stream get stateStream => _stateController.stream; BluetoothDevice get device => _device; bool _online = false; final ValueNotifier infoNotifier = ValueNotifier({}); final ValueNotifier dataNotifier = ValueNotifier({}); final ValueNotifier electricityNotifier = ValueNotifier(0); final ValueNotifier stepNotifier = ValueNotifier(0); final ValueNotifier stepTotalNotifier = ValueNotifier(0); final ValueNotifier actionNotifier = ValueNotifier(0); final ValueNotifier> byteNotifier = ValueNotifier>([]); final ValueNotifier vibrateNotifier = ValueNotifier(400); final ValueNotifier stepRealtimeNotifier = ValueNotifier(0); final ValueNotifier stepRealtimeSwitchNotifier = ValueNotifier(false); PartInfo _partInfo; bool get isConnected => _online; resetData() { stepNotifier.value = 0; stepTotalNotifier.value = 0; actionNotifier.value = 0; } set device(BluetoothDevice device) { _device = device; notifyListeners(); } Future saveDevice(BluetoothDevice device) async { var id = device.id.toString(); var prefs = await SharedPreferences.getInstance(); prefs.setString(PREF_KEY, id); _mac = id; await connect(device); return id; } Future getHistoryDevice() async { var prefs = await SharedPreferences.getInstance(); return _mac = prefs.getString(PREF_KEY); } Future listen() async { var prefs = await SharedPreferences.getInstance(); if (!prefs.containsKey("token")) { return; } FlutterBlue flutterBlue = FlutterBlue.instance; if (listenSubscription != null) return; print("bluetooth -- listen"); listenSubscription = flutterBlue.state.listen((state) async { print("bluetooth -- state $state"); if (state == BluetoothState.on) { // connectedDevicesSubscription = Stream.periodic(Duration(seconds: 10)).asyncMap((_) => FlutterBlue.instance.connectedDevices).listen((event) { // BluetoothDevice d = event.firstWhere((element) => element.id.toString() == _mac, orElse: () => null); // if (d != null) { // device = d; // } // }); await connectDevice(); } else { connectedDevicesSubscription?.cancel(); } }); } void timer() { // 定时读取设备信息 periodicSubscription = Stream.periodic(Duration(seconds: 60)).listen((event) async { if (device == null) return; if (!_online) return; await queryDeviceData(); await queryDeviceStep(); }); } void disposeDevice() { deviceSubscription?.cancel(); characteristicSubscription?.cancel(); _online = false; device?.disconnect(); print("bluetooth -- disconnect $device"); _writeCharacteristic = null; } Future disposeBluetooth() async { // 实时计步中不主动断开 if(stepRealtimeSwitchNotifier.value == true) return; connectedDevicesSubscription?.cancel(); periodicSubscription?.cancel(); listenSubscription?.cancel(); listenSubscription = null; stepRealtimeSwitchNotifier?.value = false; disposeDevice(); FlutterBlue.instance?.stopScan(); _founded = false; } Future disconnectDevice() async { device?.disconnect(); device = null; } Future connectDevice() async { var mac = await getHistoryDevice(); print("bluetooth -- connect $mac $device"); if (mac?.isEmpty == true) { return; } if (device != null) { connect(device); return; } // Start scanning flutterBlue.startScan(timeout: Duration(seconds: 10)); flutterBlue.scanResults.listen((results) { for (ScanResult r in results) { if (r.device.id.toString() == mac) { if (_founded) return; _founded = true; flutterBlue.stopScan(); connect(r.device); print('${r.device.name} found! connect: ${r.rssi} ${r.device.id}'); break; } print('${r.device.name} found! rssi: ${r.rssi}'); } }); } Future connect(BluetoothDevice newDevice) async { if (device != null) { if (!(device == newDevice)) { _online = false; disposeDevice(); } } device = newDevice; deviceSubscription?.cancel(); deviceSubscription = device.state.listen((event) async { print("device: $_mac --> state $event"); if (event == BluetoothDeviceState.disconnected) { if (_online == true) { ToastUtil.show("${device?.name} 已断开"); } _online = false; characteristicSubscription?.cancel(); _writeCharacteristic = null; periodicSubscription?.cancel(); } else if (event == BluetoothDeviceState.connected) { _online = true; flutterBlue.stopScan(); ToastUtil.show("${device?.name} 已连接"); _stateController.add(true); await discoverServices(device); await Future.delayed(Duration(seconds: 2)); await queryDeviceInfo(); await Future.delayed(Duration(seconds: 1)); await queryDeviceData(); await Future.delayed(Duration(seconds: 1)); await queryDeviceStep(); timer(); } }); device.connect(autoConnect: false); print("device: $_mac --> connect $device"); } Future discoverServices(BluetoothDevice device) async { characteristicSubscription?.cancel(); var services = await device.discoverServices(); for (var service in services) { print('${device.name} found! service: $service'); for (var characteristic in service.characteristics) { print('${device.name} found! characteristic: $characteristic ${characteristic.uuid.toString()}'); if (characteristic.properties.write && characteristic.properties.writeWithoutResponse) { _writeCharacteristic = characteristic; } if (characteristic.properties.notify) { characteristic.setNotifyValue(true).then((succ) { print("characteristic notify --> $succ"); if (succ) { characteristicSubscription = characteristic.value.listen((event) { print("characteristic change --> $event"); byteNotifier.value = event; parse(event); }); } }); } } } } void parse(List event) { if (event?.isEmpty == true) return; ByteDataReader reader = ByteDataReader(); reader.add(event); int flagAA = reader.readUint8(); // int flagBB = reader.readUint8(); // int flagCC = reader.readUint8(); int len = reader.readUint8(); int len1 = 0xFF - reader.readUint8(); // int index = reader.readUint8(); int cmd = reader.readUint8(); print(" change --> ${flagAA} ${len} ${~len} ${cmd}"); // print(" change --> ${len} ${~len} ${len1}"); // print(" change --> ${index} ${cmd}"); if (!(flagAA == 0xAA)) return; if (len != len1) return; parseCmd(cmd, reader.read(event.length - 5)); } void parseCmd(int cmd, Uint8List byteArray) { print(" cmd $cmd data --> $byteArray"); switch (cmd) { case 0x01: _parseAction(byteArray); break; case 0xA0: break; case 0xA1: _parseQuery(byteArray); break; case 0xA5: _parseStepRealtime(byteArray); break; } } void _parseAction(Uint8List byteArray) { ByteDataReader reader = ByteDataReader(); reader.add(byteArray); int action = reader.readUint8(); int t = reader.readUint16(); print("action: $action "); actionNotifier.value = action; } void _parseQuery(Uint8List byteArray) { ByteDataReader reader = ByteDataReader(); reader.add(byteArray); int cmd = reader.readUint8(); switch (cmd) { case 0x00: // 设备基本信息 Map info = {}; List name = []; for (var i = 0; i < 64; i++) { name.add(reader.readUint8()); } info['name'] = String.fromCharCodes(name); info['softwareVer'] = "${reader.readUint8()}.${reader.readUint8()}"; info['hardwareVer'] = "${reader.readUint8()}.${reader.readUint8()}"; infoNotifier.value = info; break; case 0x01: // 设备数据(左鞋,右鞋) Map info = {}; for (var i = 0; i < 2; i++) { info['${i}_electricity'] = reader.readUint8(); info['${i}_endurance'] = reader.readUint8(); info['${i}_temperature'] = reader.readUint8(); info['${i}_pressure'] = reader.readUint(4); } // [170, 187, 204, 23, 232, 0, 161, 1, 50, 0, 33, 0, 0, 156, 41, 51, 0, 34, 0, 0, 150, 19, 232] dataNotifier.value = info; electricityNotifier.value = min(info['0_electricity'] ?? 0, info['1_electricity'] ?? 0); // _testElectricity(info); break; case 0x02: // 查询步数 if (reader.remainingLength == 1) { print("step end ... "); print(_partInfo?.items); _partInfo = null; return; } int serial = reader.readUint8(); if (serial == 0) { int start = reader.readUint64(); int serialCount = reader.readUint8(); print("step start ... $start $serial $serialCount"); if (serialCount > 0) { if (_partInfo == null) _partInfo = new PartInfo(start ~/ 1000, serialCount); } else { _partInfo = null; } } else { if (_partInfo != null) { if (_partInfo.serial < serial) { _partInfo.serial = serial; while (reader.remainingLength > 0) { int step = reader.readUint16(); int distance = reader.readUint16(); _partInfo.add(step, distance); print("step read $serial --> ${reader.offsetInBytes} ... ${reader.remainingLength} = info: $step $distance"); } if (serial > 0 && _partInfo.serialCount == serial) { print("step end ... "); _saveInfo(_partInfo); stepTotalNotifier.value = _partInfo.items.map((e) => e.step).reduce((value, element) => value + element); _partInfo = null; } } } ByteDataWriter writer = ByteDataWriter(); writer.writeUint8(0x02); writer.writeUint8(serial); write(0xA1, writer.toBytes()); } break; case 0x03: // 步数回调 int step = reader.readUint8(); stepNotifier.value = stepNotifier.value + step; break; } } void _parseStepRealtime(Uint8List byteArray) { ByteDataReader reader = ByteDataReader(); reader.add(byteArray); int cmd = reader.readUint8(); if (cmd == 0) { stepRealtimeSwitchNotifier.value = false; } else if (cmd == 1) { stepRealtimeSwitchNotifier.value = true; } else { stepRealtimeNotifier.value = reader.readUint16(); } } Future _saveInfo(PartInfo partInfo) async { if (partInfo?.items?.isEmpty == true) return; List _items = []; for (var item in partInfo.items) { if (item.step == 0 && item.distance == 0) continue; _items.add(item); } await StepDB().insertAll(_items); await uploadInfo(); _queryController.add(true); } Future uploadInfo() async { for (var i = 0; i < 100; i++) { var list = await StepDB().find(); if (list.isEmpty) break; List> data = List(); int last = 0; int step = 0; for (var item in list) { last = item['time']; data.add([item['st'], item['di'], item['time']]); step += item['st']; } if (step > 5) { await api.addDaily(data: json.encode(data)); await StepDB().delete(last); } } // Directory appDocDir = await getApplicationDocumentsDirectory(); // String appDocPath = appDocDir.path; // Directory saveDir = Directory("$appDocPath/step"); // var files = saveDir.listSync(); // for(var file in files){ // var f= new File(file.path); // var content = f.readAsStringSync(); // await api.addDaily(data: content); // file.delete(); // } } Future syncDeviceInfo() async { DateTime d = DateTime.now(); int now = d.millisecondsSinceEpoch; ByteDataWriter writer = ByteDataWriter(); writer.writeUint8(0x00); writer.writeUint64(now); await write(0xA0, writer.toBytes()); } // 查询 设备基本信息 Future queryDeviceInfo() async { await write(0xA1, Uint8List.fromList([0x00])); } // 查询 设备数据 Future queryDeviceData() async { await write(0xA1, Uint8List.fromList([0x01])); } // 查询 查询步数 Future queryDeviceStep() async { // await Future.delayed(Duration(seconds: 3)); // _queryController.add(true); if (_partInfo != null) { if ((_partInfo.now?.difference(DateTime.now())?.inSeconds ?? 0) < 60) { return; } } await write(0xA1, createTime(0x02)); } Future setupGameMode(bool mode) async { await write(0xA2, mode ? Uint8List.fromList([0x01]) : Uint8List.fromList([0x00])); } Future vibrate(int vibrate) async { ByteDataWriter writer = ByteDataWriter(); writer.writeUint16(vibrate.round()); await write(0xA4, writer.toBytes()); } Future stepRealTime(bool mode) async { stepRealtimeNotifier.value = 0; await write(0xA5, mode ? Uint8List.fromList([0x01]) : Uint8List.fromList([0x00])); } Uint8List createTime(int cmd) { DateTime now = DateTime.now(); DateTime offset = DateTime(now.year, now.month, now.day, now.hour); print("create now .. $now - $offset"); ByteDataWriter writer = ByteDataWriter(); writer.writeUint8(cmd); writer.writeUint8(0); writer.writeUint64(offset.millisecondsSinceEpoch); writer.writeUint8(max(0, min(60, now.minute - offset.minute))); return writer.toBytes(); } Future write(int cmd, Uint8List data) async { BluetoothDevice _device = device; if (_device == null) return; if (_writeCharacteristic == null) { await discoverServices(_device); } if (_writeCharacteristic == null) return; int length = data.length + 5; ByteDataWriter writer = ByteDataWriter(); writer.writeUint8(0xAA); // writer.writeUint8(0xBB); // writer.writeUint8(0xCC); writer.writeUint8(length); writer.writeUint8(0xFF - length); // writer.writeUint8(0x00); writer.writeUint8(cmd); if (data.isNotEmpty) writer.write(data); int ver = writer.toBytes().reduce((value, element) => value + element); writer.writeUint8(ver); Uint8List out = writer.toBytes(); print("out $out"); for(var i =0; i< 10;i++){ try { await _writeCharacteristic?.write(out, withoutResponse: true); break; } catch (e) { print(e); await Future.delayed(Duration(milliseconds: 10)); } } } void _testElectricity(Map info) async { var now = DateTime.now(); Directory dir = await getExternalStorageDirectory(); File file = File("${dir.path}/temp/${now.year}_${now.month}_${now.day}.cvs"); print("log file: $file"); if (!file.parent.existsSync()) { file.parent.createSync(); } file.writeAsStringSync("${now.hour},${now.minute},${now.second},${info['0_electricity']},${info['1_electricity']}\n", mode: FileMode.append, flush: true); } } class PartItem { int step; int distance; int time; PartItem(this.step, this.distance, this.time); Map toJson() { final Map data = new Map(); data['step'] = this.step; data['distance'] = this.distance; data['time'] = this.time; return data; } } class PartInfo { int start; int serial = 0; int serialCount; List items = []; DateTime now; PartInfo(this.start, this.serialCount) { now = DateTime.now(); } void add(int step, int distance) { items.add(PartItem(step, distance, start + items.length * 60 * 60)); } }