bluetooth.dart 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:core';
  4. import 'dart:io';
  5. import 'dart:math';
  6. import 'dart:typed_data';
  7. import 'package:buffer/buffer.dart';
  8. import 'package:flutter/material.dart';
  9. import 'package:flutter_blue/flutter_blue.dart';
  10. import 'package:path_provider/path_provider.dart';
  11. import 'package:shared_preferences/shared_preferences.dart';
  12. import 'package:sport/db/step_db.dart';
  13. import 'package:sport/services/api/inject_api.dart';
  14. import 'package:sport/utils/toast.dart';
  15. class Bluetooth with ChangeNotifier, InjectApi {
  16. static final String PREF_KEY = "SHOES";
  17. factory Bluetooth() => _getInstance();
  18. static Bluetooth _instance;
  19. // 获取对象
  20. static Bluetooth _getInstance() {
  21. if (_instance == null) {
  22. // 使用私有的构造方法来创建对象
  23. _instance = Bluetooth._internal();
  24. }
  25. return _instance;
  26. }
  27. Bluetooth._internal() {
  28. //初始化(设置一些默认的)...
  29. }
  30. FlutterBlue flutterBlue = FlutterBlue.instance;
  31. StreamSubscription listenSubscription, periodicSubscription, characteristicSubscription, deviceSubscription, connectedDevicesSubscription;
  32. String _mac;
  33. BluetoothDevice _device;
  34. BluetoothCharacteristic _writeCharacteristic;
  35. bool _founded = false;
  36. // ignore: close_sinks
  37. final StreamController<bool> _queryController = StreamController.broadcast();
  38. Stream<bool> get queryStream => _queryController.stream;
  39. // ignore: close_sinks
  40. final StreamController<bool> _stateController = StreamController.broadcast();
  41. Stream<bool> get stateStream => _stateController.stream;
  42. BluetoothDevice get device => _device;
  43. bool _online = false;
  44. final ValueNotifier<Map> infoNotifier = ValueNotifier<Map>({});
  45. final ValueNotifier<Map> dataNotifier = ValueNotifier<Map>({});
  46. final ValueNotifier<int> electricityNotifier = ValueNotifier<int>(0);
  47. final ValueNotifier<int> stepNotifier = ValueNotifier<int>(0);
  48. final ValueNotifier<int> stepTotalNotifier = ValueNotifier<int>(0);
  49. final ValueNotifier<int> actionNotifier = ValueNotifier<int>(0);
  50. final ValueNotifier<List<int>> byteNotifier = ValueNotifier<List<int>>([]);
  51. final ValueNotifier<int> vibrateNotifier = ValueNotifier<int>(400);
  52. final ValueNotifier<int> stepRealtimeNotifier = ValueNotifier<int>(0);
  53. final ValueNotifier<bool> stepRealtimeSwitchNotifier = ValueNotifier<bool>(false);
  54. PartInfo _partInfo;
  55. bool get isConnected => _online;
  56. resetData() {
  57. stepNotifier.value = 0;
  58. stepTotalNotifier.value = 0;
  59. actionNotifier.value = 0;
  60. }
  61. set device(BluetoothDevice device) {
  62. _device = device;
  63. notifyListeners();
  64. }
  65. Future<String> saveDevice(BluetoothDevice device) async {
  66. var id = device.id.toString();
  67. var prefs = await SharedPreferences.getInstance();
  68. prefs.setString(PREF_KEY, id);
  69. _mac = id;
  70. await connect(device);
  71. return id;
  72. }
  73. Future<String> getHistoryDevice() async {
  74. var prefs = await SharedPreferences.getInstance();
  75. return _mac = prefs.getString(PREF_KEY);
  76. }
  77. Future listen() async {
  78. var prefs = await SharedPreferences.getInstance();
  79. if (!prefs.containsKey("token")) {
  80. return;
  81. }
  82. FlutterBlue flutterBlue = FlutterBlue.instance;
  83. if (listenSubscription != null) return;
  84. print("bluetooth -- listen");
  85. listenSubscription = flutterBlue.state.listen((state) async {
  86. print("bluetooth -- state $state");
  87. if (state == BluetoothState.on) {
  88. // connectedDevicesSubscription = Stream.periodic(Duration(seconds: 10)).asyncMap((_) => FlutterBlue.instance.connectedDevices).listen((event) {
  89. // BluetoothDevice d = event.firstWhere((element) => element.id.toString() == _mac, orElse: () => null);
  90. // if (d != null) {
  91. // device = d;
  92. // }
  93. // });
  94. await connectDevice();
  95. } else {
  96. connectedDevicesSubscription?.cancel();
  97. }
  98. });
  99. }
  100. void timer() {
  101. // 定时读取设备信息
  102. periodicSubscription = Stream.periodic(Duration(seconds: 60)).listen((event) async {
  103. if (device == null) return;
  104. if (!_online) return;
  105. await queryDeviceData();
  106. await queryDeviceStep();
  107. });
  108. }
  109. void disposeDevice() {
  110. deviceSubscription?.cancel();
  111. characteristicSubscription?.cancel();
  112. _online = false;
  113. device?.disconnect();
  114. print("bluetooth -- disconnect $device");
  115. _writeCharacteristic = null;
  116. }
  117. Future disposeBluetooth() async {
  118. // 实时计步中不主动断开
  119. if(stepRealtimeSwitchNotifier.value == true)
  120. return;
  121. connectedDevicesSubscription?.cancel();
  122. periodicSubscription?.cancel();
  123. listenSubscription?.cancel();
  124. listenSubscription = null;
  125. stepRealtimeSwitchNotifier?.value = false;
  126. disposeDevice();
  127. FlutterBlue.instance?.stopScan();
  128. _founded = false;
  129. }
  130. Future disconnectDevice() async {
  131. device?.disconnect();
  132. device = null;
  133. }
  134. Future connectDevice() async {
  135. var mac = await getHistoryDevice();
  136. print("bluetooth -- connect $mac $device");
  137. if (mac?.isEmpty == true) {
  138. return;
  139. }
  140. if (device != null) {
  141. connect(device);
  142. return;
  143. }
  144. // Start scanning
  145. flutterBlue.startScan(timeout: Duration(seconds: 10));
  146. flutterBlue.scanResults.listen((results) {
  147. for (ScanResult r in results) {
  148. if (r.device.id.toString() == mac) {
  149. if (_founded) return;
  150. _founded = true;
  151. flutterBlue.stopScan();
  152. connect(r.device);
  153. print('${r.device.name} found! connect: ${r.rssi} ${r.device.id}');
  154. break;
  155. }
  156. print('${r.device.name} found! rssi: ${r.rssi}');
  157. }
  158. });
  159. }
  160. Future connect(BluetoothDevice newDevice) async {
  161. if (device != null) {
  162. if (!(device == newDevice)) {
  163. _online = false;
  164. disposeDevice();
  165. }
  166. }
  167. device = newDevice;
  168. deviceSubscription?.cancel();
  169. deviceSubscription = device.state.listen((event) async {
  170. print("device: $_mac --> state $event");
  171. if (event == BluetoothDeviceState.disconnected) {
  172. if (_online == true) {
  173. ToastUtil.show("${device?.name} 已断开");
  174. }
  175. _online = false;
  176. characteristicSubscription?.cancel();
  177. _writeCharacteristic = null;
  178. periodicSubscription?.cancel();
  179. } else if (event == BluetoothDeviceState.connected) {
  180. _online = true;
  181. flutterBlue.stopScan();
  182. ToastUtil.show("${device?.name} 已连接");
  183. _stateController.add(true);
  184. await discoverServices(device);
  185. await Future.delayed(Duration(seconds: 2));
  186. await queryDeviceInfo();
  187. await Future.delayed(Duration(seconds: 1));
  188. await queryDeviceData();
  189. await Future.delayed(Duration(seconds: 1));
  190. await queryDeviceStep();
  191. timer();
  192. }
  193. });
  194. device.connect(autoConnect: false);
  195. print("device: $_mac --> connect $device");
  196. }
  197. Future discoverServices(BluetoothDevice device) async {
  198. characteristicSubscription?.cancel();
  199. var services = await device.discoverServices();
  200. for (var service in services) {
  201. print('${device.name} found! service: $service');
  202. for (var characteristic in service.characteristics) {
  203. print('${device.name} found! characteristic: $characteristic ${characteristic.uuid.toString()}');
  204. if (characteristic.properties.write && characteristic.properties.writeWithoutResponse) {
  205. _writeCharacteristic = characteristic;
  206. }
  207. if (characteristic.properties.notify) {
  208. characteristic.setNotifyValue(true).then((succ) {
  209. print("characteristic notify --> $succ");
  210. if (succ) {
  211. characteristicSubscription = characteristic.value.listen((event) {
  212. print("characteristic change --> $event");
  213. byteNotifier.value = event;
  214. parse(event);
  215. });
  216. }
  217. });
  218. }
  219. }
  220. }
  221. }
  222. void parse(List<int> event) {
  223. if (event?.isEmpty == true) return;
  224. ByteDataReader reader = ByteDataReader();
  225. reader.add(event);
  226. int flagAA = reader.readUint8();
  227. // int flagBB = reader.readUint8();
  228. // int flagCC = reader.readUint8();
  229. int len = reader.readUint8();
  230. int len1 = 0xFF - reader.readUint8();
  231. // int index = reader.readUint8();
  232. int cmd = reader.readUint8();
  233. print(" change --> ${flagAA} ${len} ${~len} ${cmd}");
  234. // print(" change --> ${len} ${~len} ${len1}");
  235. // print(" change --> ${index} ${cmd}");
  236. if (!(flagAA == 0xAA)) return;
  237. if (len != len1) return;
  238. parseCmd(cmd, reader.read(event.length - 5));
  239. }
  240. void parseCmd(int cmd, Uint8List byteArray) {
  241. print(" cmd $cmd data --> $byteArray");
  242. switch (cmd) {
  243. case 0x01:
  244. _parseAction(byteArray);
  245. break;
  246. case 0xA0:
  247. break;
  248. case 0xA1:
  249. _parseQuery(byteArray);
  250. break;
  251. case 0xA5:
  252. _parseStepRealtime(byteArray);
  253. break;
  254. }
  255. }
  256. void _parseAction(Uint8List byteArray) {
  257. ByteDataReader reader = ByteDataReader();
  258. reader.add(byteArray);
  259. int action = reader.readUint8();
  260. int t = reader.readUint16();
  261. print("action: $action ");
  262. actionNotifier.value = action;
  263. }
  264. void _parseQuery(Uint8List byteArray) {
  265. ByteDataReader reader = ByteDataReader();
  266. reader.add(byteArray);
  267. int cmd = reader.readUint8();
  268. switch (cmd) {
  269. case 0x00: // 设备基本信息
  270. Map<String, dynamic> info = {};
  271. List<int> name = [];
  272. for (var i = 0; i < 64; i++) {
  273. name.add(reader.readUint8());
  274. }
  275. info['name'] = String.fromCharCodes(name);
  276. info['softwareVer'] = "${reader.readUint8()}.${reader.readUint8()}";
  277. info['hardwareVer'] = "${reader.readUint8()}.${reader.readUint8()}";
  278. infoNotifier.value = info;
  279. break;
  280. case 0x01: // 设备数据(左鞋,右鞋)
  281. Map<String, dynamic> info = {};
  282. for (var i = 0; i < 2; i++) {
  283. info['${i}_electricity'] = reader.readUint8();
  284. info['${i}_endurance'] = reader.readUint8();
  285. info['${i}_temperature'] = reader.readUint8();
  286. info['${i}_pressure'] = reader.readUint(4);
  287. }
  288. // [170, 187, 204, 23, 232, 0, 161, 1, 50, 0, 33, 0, 0, 156, 41, 51, 0, 34, 0, 0, 150, 19, 232]
  289. dataNotifier.value = info;
  290. electricityNotifier.value = min(info['0_electricity'] ?? 0, info['1_electricity'] ?? 0);
  291. // _testElectricity(info);
  292. break;
  293. case 0x02: // 查询步数
  294. if (reader.remainingLength == 1) {
  295. print("step end ... ");
  296. print(_partInfo?.items);
  297. _partInfo = null;
  298. return;
  299. }
  300. int serial = reader.readUint8();
  301. if (serial == 0) {
  302. int start = reader.readUint64();
  303. int serialCount = reader.readUint8();
  304. print("step start ... $start $serial $serialCount");
  305. if (serialCount > 0) {
  306. if (_partInfo == null) _partInfo = new PartInfo(start ~/ 1000, serialCount);
  307. } else {
  308. _partInfo = null;
  309. }
  310. } else {
  311. if (_partInfo != null) {
  312. if (_partInfo.serial < serial) {
  313. _partInfo.serial = serial;
  314. while (reader.remainingLength > 0) {
  315. int step = reader.readUint16();
  316. int distance = reader.readUint16();
  317. _partInfo.add(step, distance);
  318. print("step read $serial --> ${reader.offsetInBytes} ... ${reader.remainingLength} = info: $step $distance");
  319. }
  320. if (serial > 0 && _partInfo.serialCount == serial) {
  321. print("step end ... ");
  322. _saveInfo(_partInfo);
  323. stepTotalNotifier.value = _partInfo.items.map((e) => e.step).reduce((value, element) => value + element);
  324. _partInfo = null;
  325. }
  326. }
  327. }
  328. ByteDataWriter writer = ByteDataWriter();
  329. writer.writeUint8(0x02);
  330. writer.writeUint8(serial);
  331. write(0xA1, writer.toBytes());
  332. }
  333. break;
  334. case 0x03: // 步数回调
  335. int step = reader.readUint8();
  336. stepNotifier.value = stepNotifier.value + step;
  337. break;
  338. }
  339. }
  340. void _parseStepRealtime(Uint8List byteArray) {
  341. ByteDataReader reader = ByteDataReader();
  342. reader.add(byteArray);
  343. int cmd = reader.readUint8();
  344. if (cmd == 0) {
  345. stepRealtimeSwitchNotifier.value = false;
  346. } else if (cmd == 1) {
  347. stepRealtimeSwitchNotifier.value = true;
  348. } else {
  349. stepRealtimeNotifier.value = reader.readUint16();
  350. }
  351. }
  352. Future _saveInfo(PartInfo partInfo) async {
  353. if (partInfo?.items?.isEmpty == true) return;
  354. List<PartItem> _items = [];
  355. for (var item in partInfo.items) {
  356. if (item.step == 0 && item.distance == 0) continue;
  357. _items.add(item);
  358. }
  359. await StepDB().insertAll(_items);
  360. await uploadInfo();
  361. _queryController.add(true);
  362. }
  363. Future uploadInfo() async {
  364. for (var i = 0; i < 100; i++) {
  365. var list = await StepDB().find();
  366. if (list.isEmpty) break;
  367. List<List<int>> data = List();
  368. int last = 0;
  369. int step = 0;
  370. for (var item in list) {
  371. last = item['time'];
  372. data.add([item['st'], item['di'], item['time']]);
  373. step += item['st'];
  374. }
  375. if (step > 5) {
  376. await api.addDaily(data: json.encode(data));
  377. await StepDB().delete(last);
  378. }
  379. }
  380. // Directory appDocDir = await getApplicationDocumentsDirectory();
  381. // String appDocPath = appDocDir.path;
  382. // Directory saveDir = Directory("$appDocPath/step");
  383. // var files = saveDir.listSync();
  384. // for(var file in files){
  385. // var f= new File(file.path);
  386. // var content = f.readAsStringSync();
  387. // await api.addDaily(data: content);
  388. // file.delete();
  389. // }
  390. }
  391. Future syncDeviceInfo() async {
  392. DateTime d = DateTime.now();
  393. int now = d.millisecondsSinceEpoch;
  394. ByteDataWriter writer = ByteDataWriter();
  395. writer.writeUint8(0x00);
  396. writer.writeUint64(now);
  397. await write(0xA0, writer.toBytes());
  398. }
  399. // 查询 设备基本信息
  400. Future queryDeviceInfo() async {
  401. await write(0xA1, Uint8List.fromList([0x00]));
  402. }
  403. // 查询 设备数据
  404. Future queryDeviceData() async {
  405. await write(0xA1, Uint8List.fromList([0x01]));
  406. }
  407. // 查询 查询步数
  408. Future queryDeviceStep() async {
  409. // await Future.delayed(Duration(seconds: 3));
  410. // _queryController.add(true);
  411. if (_partInfo != null) {
  412. if ((_partInfo.now?.difference(DateTime.now())?.inSeconds ?? 0) < 60) {
  413. return;
  414. }
  415. }
  416. await write(0xA1, createTime(0x02));
  417. }
  418. Future setupGameMode(bool mode) async {
  419. await write(0xA2, mode ? Uint8List.fromList([0x01]) : Uint8List.fromList([0x00]));
  420. }
  421. Future vibrate(int vibrate) async {
  422. ByteDataWriter writer = ByteDataWriter();
  423. writer.writeUint16(vibrate.round());
  424. await write(0xA4, writer.toBytes());
  425. }
  426. Future stepRealTime(bool mode) async {
  427. stepRealtimeNotifier.value = 0;
  428. await write(0xA5, mode ? Uint8List.fromList([0x01]) : Uint8List.fromList([0x00]));
  429. }
  430. Uint8List createTime(int cmd) {
  431. DateTime now = DateTime.now();
  432. DateTime offset = DateTime(now.year, now.month, now.day, now.hour);
  433. print("create now .. $now - $offset");
  434. ByteDataWriter writer = ByteDataWriter();
  435. writer.writeUint8(cmd);
  436. writer.writeUint8(0);
  437. writer.writeUint64(offset.millisecondsSinceEpoch);
  438. writer.writeUint8(max(0, min(60, now.minute - offset.minute)));
  439. return writer.toBytes();
  440. }
  441. Future write(int cmd, Uint8List data) async {
  442. BluetoothDevice _device = device;
  443. if (_device == null) return;
  444. if (_writeCharacteristic == null) {
  445. await discoverServices(_device);
  446. }
  447. if (_writeCharacteristic == null) return;
  448. int length = data.length + 5;
  449. ByteDataWriter writer = ByteDataWriter();
  450. writer.writeUint8(0xAA);
  451. // writer.writeUint8(0xBB);
  452. // writer.writeUint8(0xCC);
  453. writer.writeUint8(length);
  454. writer.writeUint8(0xFF - length);
  455. // writer.writeUint8(0x00);
  456. writer.writeUint8(cmd);
  457. if (data.isNotEmpty) writer.write(data);
  458. int ver = writer.toBytes().reduce((value, element) => value + element);
  459. writer.writeUint8(ver);
  460. Uint8List out = writer.toBytes();
  461. print("out $out");
  462. for(var i =0; i< 10;i++){
  463. try {
  464. await _writeCharacteristic?.write(out, withoutResponse: true);
  465. break;
  466. } catch (e) {
  467. print(e);
  468. await Future.delayed(Duration(milliseconds: 10));
  469. }
  470. }
  471. }
  472. void _testElectricity(Map<String, dynamic> info) async {
  473. var now = DateTime.now();
  474. Directory dir = await getExternalStorageDirectory();
  475. File file = File("${dir.path}/temp/${now.year}_${now.month}_${now.day}.cvs");
  476. print("log file: $file");
  477. if (!file.parent.existsSync()) {
  478. file.parent.createSync();
  479. }
  480. file.writeAsStringSync("${now.hour},${now.minute},${now.second},${info['0_electricity']},${info['1_electricity']}\n", mode: FileMode.append, flush: true);
  481. }
  482. }
  483. class PartItem {
  484. int step;
  485. int distance;
  486. int time;
  487. PartItem(this.step, this.distance, this.time);
  488. Map<String, dynamic> toJson() {
  489. final Map<String, dynamic> data = new Map<String, dynamic>();
  490. data['step'] = this.step;
  491. data['distance'] = this.distance;
  492. data['time'] = this.time;
  493. return data;
  494. }
  495. }
  496. class PartInfo {
  497. int start;
  498. int serial = 0;
  499. int serialCount;
  500. List<PartItem> items = [];
  501. DateTime now;
  502. PartInfo(this.start, this.serialCount) {
  503. now = DateTime.now();
  504. }
  505. void add(int step, int distance) {
  506. items.add(PartItem(step, distance, start + items.length * 60 * 60));
  507. }
  508. }