bluetooth.dart 15 KB

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