我在 Flutter 应用程序中使用 ESP32 作为 BLE 设备时遇到了问题(我使用 Flutter_Blue_Plus 包)。我希望能够读取 ESP 发送的信息并同时写入另一个特征。 但是当我读完之后开始写作时,我会遇到很大的延迟,这是我想避免的。
我的问题如下:
我有一个 ESP32,配置为充当 BLE 设备。 我的 Flutter 应用程序通过 BLE 连接到该 ESP32,并尝试从特征中读取数据。 我开始通过应用程序将 JSON 文件写入 EPS32 上的另一个特征。 此时在 ESP32 接收数据之前我有一个很大的延迟,该延迟对应于读取的持续时间。 (如果没有开始读取,我就没有这个延迟)
(抱歉我的代码没有优化)
这是我的主页(其中包含已读内容):
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key,}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int? _rssi;
int? _mtuSize;
BluetoothConnectionState _connectionState = BluetoothConnectionState.disconnected;
bool _isConnectingOrDisconnecting = false;
StreamSubscription<BluetoothConnectionState>? _connectionStateSubscription; // pb avec ça j'ai rajouter ? et enlever late mais erreur : _HomeScreenState.dispose failed to call super.dispose. psq pas init
StreamSubscription<bool>? _isConnectingOrDisconnectingSubscription;
StreamSubscription<int>? _mtuSubscription;
@override
void initState() {
super.initState();
if (BLEdevice?.remoteId.toString() != '00:00:00:00:00:00') {
_connectionStateSubscription = BLEdevice!.connectionState.listen((state) async {
_connectionState = state;
if (state == BluetoothConnectionState.connected) {
services = [];
}
if (state == BluetoothConnectionState.connected && _rssi == null) {
_rssi = await BLEdevice!.readRssi();
}
setState(() {});
});
_mtuSubscription = BLEdevice!.mtu.listen((value) {
_mtuSize = value;
setState(() {});
});
_isConnectingOrDisconnectingSubscription = BLEdevice!.isConnectingOrDisconnecting.listen((value) {
_isConnectingOrDisconnecting = value;
setState(() {});
});
getServices();
}
}
@override
void dispose() {
//print(BLEdevice?.remoteId.toString());
if (BLEdevice?.remoteId.toString() != '00:00:00:00:00:00') {
_connectionStateSubscription?.cancel();
_mtuSubscription?.cancel();
_isConnectingOrDisconnectingSubscription?.cancel();
super.dispose();
}
else
{
super.dispose();
}
}
bool get isConnected {
return _connectionState == BluetoothConnectionState.connected;
}
Future onConnectPressed() async {
try {
await BLEdevice!.connectAndUpdateStream();
} catch (e) {
print("Connect Error");
}
}
Future onDisconnectPressed() async {
try {
await BLEdevice!.disconnectAndUpdateStream();
BLEdevice = BluetoothDevice(remoteId: const DeviceIdentifier('00:00:00:00:00:00'));
MaterialPageRoute route = MaterialPageRoute(
builder: (context) => HomeScreen(), settings: const RouteSettings(name: '/ScanScreen'));
Navigator.of(context).push(route);
} catch (e) {
print("Disconnect Error");
}
}
Future getServices() async {
try {
services = await BLEdevice!.discoverServices();
} catch (e) {
print('Error: service discovery failed');
}
}
Widget buildCustomCard(BuildContext context, Icon icon, String text, double valueBLE, [Widget? destinationScreen]) // build custom card avec data
{
Size size = MediaQuery.of(context).size;
return FutureBuilder<void>(
future: getServices(),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.hasError)
{
return Text('Error line 166 home: ${snapshot.error}');
}
else
{
BluetoothCharacteristic? firstCharacteristic;
if (services.isNotEmpty && services[0].characteristics.isNotEmpty) {
firstCharacteristic = services[0].characteristics[0];
BluetoothUtil.readCharacteristic(firstCharacteristic,() => mounted? {setState(() {})}:null);
}
return firstCharacteristic != null
? CustomCard(
size: size,
title: text,
icon: icon,
valueBLE: valueBLE,
destinationScreen:destinationScreen
)
: CustomCard(
size: size,
title: text,
icon: icon,
valueBLE: valueBLE,
);
}
}
);
}
Widget buildCustomCardSelect(BuildContext context,Icon icon, String title, Color color, bool state, Function() onToggle) //build customCard avec bouton
{
Size size = MediaQuery.of(context).size;
return FutureBuilder<void>(
future: getServices(),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
{
return CustomCardSelect(
size: size,
title: title,
icon: icon,
color: color,
state: state,
onToggle: onToggle,
);
}
else if (snapshot.hasError)
{
return Text('Error: ${snapshot.error}');
}
else
{
BluetoothCharacteristic? writeCharacteristic;
if (services.isNotEmpty && services[0].characteristics.isNotEmpty) {
writeCharacteristic = services[0].characteristics[1];
}
return writeCharacteristic != null
? CustomCardSelect(
size: size,
title: title,
icon: icon,
color: color,
state: state,
onToggle: onToggle,
characteristic: writeCharacteristic
):CustomCardSelect(
size: size,
title: title,
icon: icon,
color: color,
state: state,
onToggle: onToggle,
);
}
}
);
}
@override
Widget build(BuildContext context, ) {
Size size = MediaQuery.of(context).size; //Size(400, 400);// !!!! ???
return Scaffold(
backgroundColor: WhiteColor,
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: size.width * 0.01),
child: Column(
children: [
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: size.width * 0.01),
child: Text(
"Data ",
style: TextStyle(fontSize: 23, fontWeight: FontWeight.bold, color: Color.fromARGB(255, 0, 0, 0)),
),
),
SizedBox(height: size.height * 0.02),
Container(
padding: EdgeInsets.only(left: size.width * 0.02),
height: size.height * 0.20,
child: ListView(
clipBehavior: Clip.none,
scrollDirection: Axis.horizontal,
children:[
buildCustomCard(context, Icon(Icons.thermostat, size: 99 ,color: Colors.grey.shade400), "Température",currentTemp ,temperatureScreen()),
SizedBox(width: size.width * 0.05),
buildCustomCard(context, Icon(Icons.bubble_chart_outlined, size :55,color: Colors.grey.shade400), "TDS",currentTDS.toDouble()),
SizedBox(width: size.width * 0.05),
buildCustomCard(context, Icon(Icons.science_outlined, size: 99 ,color: Colors.grey.shade400), "PH",currentPH),
SizedBox(width: size.width * 0.05),
]
),
),
Padding(padding: EdgeInsets.only(bottom: size.width * 0.05)),
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: size.width * 0.01),
child: const Text(
"Action ",
style: TextStyle(fontSize: 23, fontWeight: FontWeight.bold, color: Color.fromARGB(255, 0, 0, 0),),
),
),
SizedBox(height: size.height * 0.02),
// buildCustomCardSelect(context, const Icon(Icons.water_drop_outlined) ,"Pompe à air", Colors.blue.shade400),
Padding(padding: EdgeInsets.only(bottom: size.width * 0.05)),
buildCustomCardSelect(context, const Icon(Icons.light_mode) ,"Lumière", Colors.yellow.shade400, light, () => setState(() {light = !light;})),
Padding(padding: EdgeInsets.only(bottom: size.width * 0.07 )),
]
),
)
)
),
);
}
}
这是我的读写功能:
import 'dart:convert';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:fishbotica/constants/data.dart';
class BluetoothUtil {
static Future<void> writeCharacteristic(
//< BluetoothCharacteristic? characteristic,
) async {
try {
await services[0].characteristics[1].write(
jsonToByte(),
withoutResponse: services[0].characteristics[1].properties.writeWithoutResponse,
);
} catch (e) {
print('error write ble : $e');
}
}
static List<int> jsonToByte() {
String jsonData =
json.encode(
{
"regulationTemp" :1// regulationTemp,
// "forceTemp" : forceTemp ? 1 : 0,
// "light" : setLight ? 1: 0,
}
);
List<int> dataByte = utf8.encode(jsonData);
return dataByte;
}
static Future<void> readCharacteristic(BluetoothCharacteristic characteristic, Function() onToggle) async {
String characteristicValue = "999";
double doubleValue = 999;
try {
final List<int> value = await characteristic.read();
print(value.toString());
// Convertir les données de la caractéristique en chaîne
String jsonString = String.fromCharCodes(value );
Map<String, dynamic> jsonData = json.decode(jsonString);
currentTemp = double.parse(jsonData["temp"]);
currentPH = jsonData["pH"];
currentTDS = jsonData["TDS"];
onToggle();
} catch (e) {
print('Erreur lors de la lecture de la caractéristique : $e');
}
}
}
这是我管理写入并在主页上启动的小部件:
class CustomCardSelect extends StatefulWidget {
Size size;
String title;
Icon icon;
Color color;
bool state;
Function() onToggle;
BluetoothCharacteristic? characteristic;
CustomCardSelect(
{super.key,
required this.size,
required this.title,
required this.icon,
required this.color,
required this.state,
required this.onToggle,
this.characteristic,
});
@override
State<CustomCardSelect> createState() => _CustomCardSelectState();
}
class _CustomCardSelectState extends State<CustomCardSelect>
with SingleTickerProviderStateMixin {
// BLE
late bool isChecked;
//bool temp = false;
BluetoothCharacteristic? get c => widget.characteristic;
@override
void initState() {
super.initState();
isChecked = widget.state;
}
@override
void dispose() {
super.dispose();
}
void setIconColor() {
setState(() {
if (!isChecked) {
widget.icon = Icon(widget.icon.icon, size: 35, color: Colors.grey.shade400);
} else {
widget.icon = Icon(widget.icon.icon, size: 35, color: widget.color);
}
});
}
@override
Widget build(BuildContext context) {
return
Container(
height: widget.size.height * 0.14,
width: widget.size.width * 0.9,
decoration: BoxDecoration(
color: KCustomCard,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.grey.shade400),
boxShadow: const [
BoxShadow(
color: Colors.black12,
offset: Offset(3, 3),
blurRadius: 8,
),
BoxShadow(
color: Colors.white,
offset: Offset(-3, -3),
blurRadius: 8,
),
],
),
child: Padding(
padding: EdgeInsets.all(15),
child:Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children:
[
Icon(widget.icon.icon, color: Colors.black, size: 35),
Padding(padding: EdgeInsets.only(left: 15)),
Text(widget.title, style: const TextStyle(fontSize: 19, fontWeight: FontWeight.bold, color: kBlueColor)) ,
]
),
Transform.scale(
scale: 1.1,
child: CupertinoSwitch(
value: isChecked,
onChanged: (isChecked) async {
this.isChecked = isChecked;
setState(() {
if (isChecked) {
setIconColor();
} else {
setIconColor();
}
});
await BluetoothUtil.writeCharacteristic();
widget.onToggle();
}
)
),
]
),
)
);
}
}
我还没有找到任何人谈论这个问题。 预先感谢您的回复;)
我想建议,为了避免等待蓝色被读取,您可以将蓝色读取条目保存在列表中,并在写入 json 文件之前监听此列表中的更改。