123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- import 'dart:async';
- import 'dart:io';
- import 'dart:typed_data';
- import 'dart:ui';
- import 'package:buffer/buffer.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
- import 'package:nordic_dfu/nordic_dfu.dart';
- import 'package:nrf/app_subscription_state.dart';
- import 'package:nrf/scanner.dart';
- import 'find.dart';
- class DFUList extends StatefulWidget {
- final Set<DiscoveredDevice> selectedResults;
- final File file;
- const DFUList({Key? key, required this.selectedResults, required this.file}) : super(key: key);
- @override
- State<StatefulWidget> createState() => _State();
- }
- class Device {
- final DiscoveredDevice device;
- String? msg;
- DiscoveredDevice? updateDevice;
- Device(this.device);
- }
- class DfuEvent {
- final String id;
- final String msg;
- DfuEvent(this.id, this.msg);
- }
- class _State extends State<DFUList> with SubscriptionState {
- late List<Device> items;
- String? _finishMsg;
- StreamSubscription? scanForDevices;
- final flutterReactiveBle = FlutterReactiveBle();
- @override
- void initState() {
- super.initState();
- items = List.from(widget.selectedResults.map((e) => Device(e)));
- _startDfu().then((value) {
- ScaffoldMessenger.of(context).showSnackBar(SnackBar(
- content: Text('任务完成,总数:${widget.selectedResults.length},完成数:$value'),
- ));
- setState(() {
- _finishMsg = '任务完成,总数:${widget.selectedResults.length},完成数:$value';
- });
- // _search();
- });
- }
- @override
- void dispose() {
- scanForDevices?.cancel();
- NordicDfu().abortDfu();
- super.dispose();
- }
- Future _connect(DiscoveredDevice device, Uint8List data) async {
- int tryCount = 3;
- bool _connecting = true;
- Timer timer = Timer(const Duration(seconds: 30), () {
- _connecting = false;
- });
- await Future.delayed(const Duration(seconds: 2));
- while (tryCount > 0) {
- if (!_connecting) return;
- try {
- final stream = flutterReactiveBle.connectToDevice(id: device.id, connectionTimeout: const Duration(seconds: 10));
- await for (var state in stream) {
- print("device state: $state");
- if (state.connectionState == DeviceConnectionState.connected) {
- var services = await flutterReactiveBle.discoverServices(device.id);
- for (DiscoveredService service in services) {
- for (var characteristic in service.characteristics) {
- if (characteristic.isWritableWithoutResponse) {
- QualifiedCharacteristic characteristicWrite = QualifiedCharacteristic(serviceId: service.serviceId, characteristicId: characteristic.characteristicId, deviceId: device.id);
- print("device characteristicWrite: ${device.id} ${service.serviceId} ${characteristic.characteristicId}");
- for (var i = 0; i < 4; i++) {
- print("device write light");
- _write(characteristicWrite, Uint8List.fromList([0xB0, 0x01]));
- _write(characteristicWrite, data);
- await Future.delayed(const Duration(milliseconds: 500));
- }
- _connecting = false;
- }
- }
- }
- print("device disconnect");
- break;
- } else if (state.connectionState == DeviceConnectionState.disconnected) {
- tryCount--;
- break;
- }
- }
- if (!_connecting) {
- break;
- }
- } catch (e) {
- print("bluetooth -- connect error: $e");
- }
- await Future.delayed(const Duration(seconds: 3));
- }
- timer.cancel();
- }
- _write(QualifiedCharacteristic characteristicWrite, Uint8List data) {
- try {
- int length = data.length + 4;
- ByteDataWriter writer = ByteDataWriter();
- writer.writeUint8(0xAA);
- writer.writeUint8(length);
- writer.writeUint8(0xFF - length);
- if (data.isNotEmpty) writer.write(data);
- int ver = writer.toBytes().reduce((value, element) => value + element);
- writer.writeUint8(ver);
- Uint8List out = writer.toBytes();
- print("write ${characteristicWrite.deviceId} ${characteristicWrite.characteristicId} ${data}");
- flutterReactiveBle.writeCharacteristicWithoutResponse(characteristicWrite, value: out);
- } catch (e) {
- print("write error");
- print(e);
- }
- }
- _search() {
- scanForDevices?.cancel();
- final flutterReactiveBle = FlutterReactiveBle();
- scanForDevices = flutterReactiveBle.scanForDevices(withServices: [Uuid.parse(SH_UUID)]).listen((e) {
- if (e.name.isEmpty) return;
- final knownDeviceIndex = items.indexWhere((d) => d.device.id == e.id);
- if (knownDeviceIndex >= 0) {
- items[knownDeviceIndex].updateDevice = e;
- }
- setState(() {});
- })
- ..onError((e, s) {
- setState(() {
- scanForDevices = null;
- });
- })
- ..onDone(() {
- setState(() {
- scanForDevices = null;
- });
- });
- }
- Future<int> _startDfu() async {
- String file = widget.file.path;
- int num = 0;
- for (var e in items) {
- if (mounted) {
- try {
- print("start dfu ${e.device.id}");
- await doDfu(e.device.id, widget.file.path);
- print("start dfu ${e.device.id} done!");
- num += 1;
- // ByteDataWriter writer = ByteDataWriter();
- // writer.writeUint8(0xB2);
- // await _connect(e.device, writer.toBytes());
- } catch (e) {
- print(e);
- }
- await Future.delayed(const Duration(seconds: 3));
- }
- }
- return num;
- }
- _addLog(String deviceId, String msg) {
- setState(() {
- for (var e in items) {
- if (e.device.id == deviceId || deviceId.contains(e.device.id.substring(0, 12))) {
- e.msg = msg;
- }
- }
- });
- }
- Future<String?> doDfu(String deviceId, String filePath) async {
- DateTime start = DateTime.now();
- _addLog(deviceId, "DFU wait start!");
- return await NordicDfu().startDfu(
- deviceId,
- filePath,
- fileInAsset: false,
- onEnablingDfuMode: (deviceAddress) {
- _addLog(deviceAddress, "EnablingDfuMode");
- },
- onDfuProcessStarted: (deviceAddress) {
- _addLog(deviceAddress, "DfuProcessStarted");
- },
- onDfuProcessStarting: (deviceAddress) {
- _addLog(deviceAddress, "DfuProcessStarting");
- },
- onDeviceConnecting: (deviceAddress) {
- _addLog(deviceAddress, "DeviceConnecting");
- },
- onDeviceConnected: (deviceAddress) {
- _addLog(deviceAddress, "DeviceConnected");
- },
- onDfuCompleted: (deviceAddress) {
- _addLog(deviceAddress, "DfuCompleted use:${DateTime.now().difference(start).inSeconds}s");
- },
- onFirmwareValidating: (deviceAddress) {
- _addLog(deviceAddress, "FirmwareValidating");
- },
- onProgressChanged: (
- deviceAddress,
- percent,
- speed,
- avgSpeed,
- currentPart,
- partsTotal,
- ) {
- _addLog(deviceAddress, "$percent% speed:${avgSpeed.toStringAsFixed(2)}kb/s");
- },
- onError: (
- String? deviceAddress,
- int? error,
- int? errorType,
- String? message,
- ) {
- _addLog(deviceAddress ?? "", "Error $error $errorType $message");
- },
- );
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- actions: [
- if (_finishMsg?.isNotEmpty == true)
- ElevatedButton(
- onPressed: () async {
- _search();
- },
- child: Text("检测升级后版本"),
- ),
- if (scanForDevices != null)
- ElevatedButton(
- onPressed: () async {
- setState(() {
- scanForDevices?.cancel();
- scanForDevices = null;
- });
- },
- child: Text("搜索中..."),
- ),
- if (_finishMsg?.isNotEmpty != true)
- IconButton(
- icon: const CircularProgressIndicator(
- backgroundColor: Colors.white,
- ),
- onPressed: () {},
- ),
- ],
- ),
- body: Column(
- children: [
- Text("升级文件:${widget.file}"),
- if (_finishMsg?.isNotEmpty == true)
- Center(
- child: Text("$_finishMsg"),
- ),
- Expanded(
- child: ListView.builder(
- itemBuilder: (context, index) {
- return Padding(
- padding: const EdgeInsets.all(8.0),
- child: Row(
- children: [
- Column(
- children: [
- ConstrainedBox(
- constraints: const BoxConstraints(maxWidth: 140.0),
- child: Text(
- items[index].device.name,
- style: const TextStyle(fontSize: 14, color: Color(0xff333333)),
- softWrap: true,
- ),
- ),
- ConstrainedBox(
- constraints: const BoxConstraints(maxWidth: 140.0),
- child: Text(
- items[index].device.id,
- style: const TextStyle(fontSize: 12, color: Color(0xff999999)),
- ),
- ),
- Text(
- "${items[index].device.manufacturerData}",
- style: const TextStyle(fontSize: 12, color: Color(0xff999999)),
- ),
- if (items[index].updateDevice != null && items[index].updateDevice!.manufacturerData.length > 1)
- Text(
- " --> ${items[index].updateDevice!.manufacturerData}",
- style: TextStyle(
- fontSize: 12,
- color: items[index].device.manufacturerData.isEmpty
- ? Colors.green
- : (items[index].updateDevice!.manufacturerData[0] > items[index].device.manufacturerData[0])
- ? Colors.green
- : Colors.redAccent),
- ),
- ],
- crossAxisAlignment: CrossAxisAlignment.start,
- ),
- const SizedBox(
- width: 12,
- ),
- Expanded(
- child: Text("${items[index].msg}"),
- ),
- ElevatedButton(
- onPressed: () async {
- await showDialog(
- context: context,
- builder: (context) => Find(device: items[index].device),
- );
- },
- child: const Text("找鞋"),
- ),
- ],
- ),
- );
- },
- itemCount: items.length,
- ),
- ),
- ],
- ),
- );
- }
- }
|