esp_flutter_sdk 1.0.0
esp_flutter_sdk: ^1.0.0 copied to clipboard
ESP Flutter SDK for provisioning ESP devices via BLE and WiFi. Provides APIs for device discovery, connection, and network configuration with support for Security 0/1/2.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:esp_flutter_sdk/esp_flutter_sdk.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ESP配网示例',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final espManager = ESPProvisioningManager.getInstance();
final List<BleDevice> bleDevices = [];
final List<WiFiAccessPoint> wifiNetworks = [];
ESPDevice? currentDevice;
bool isScanning = false;
String statusMessage = '准备就绪';
@override
void initState() {
super.initState();
setupCallbacks();
}
void setupCallbacks() {
// BLE扫描回调
espManager.onBleDeviceFound((device) {
setState(() {
// 避免重复添加
if (!bleDevices.any((d) => d.address == device.address)) {
bleDevices.add(device);
}
});
});
espManager.onBleScanCompleted(() {
setState(() {
isScanning = false;
statusMessage = 'BLE扫描完成';
});
});
espManager.onBleScanFailed((error) {
setState(() {
isScanning = false;
statusMessage = 'BLE扫描失败: $error';
});
showErrorDialog('BLE扫描失败', error);
});
// 设备连接回调
espManager.onDeviceConnected(() {
setState(() {
statusMessage = '设备已连接';
});
});
espManager.onDeviceConnectionFailed((error) {
setState(() {
statusMessage = '设备连接失败: $error';
});
showErrorDialog('连接失败', error);
});
// 配网回调
espManager.onWifiConfigSent(() {
setState(() {
statusMessage = 'WiFi配置已发送';
});
});
espManager.onWifiConfigApplied(() {
setState(() {
statusMessage = 'WiFi配置已应用';
});
});
espManager.onProvisioningSuccess(() {
setState(() {
statusMessage = '配网成功!';
});
showSuccessDialog('配网成功', '设备已成功连接到WiFi网络');
});
espManager.onProvisioningFailed((error, reason) {
String reasonText = '';
if (reason != null) {
switch (reason) {
case ProvisionFailureReason.authFailed:
reasonText = '认证失败';
break;
case ProvisionFailureReason.networkNotFound:
reasonText = '网络未找到';
break;
case ProvisionFailureReason.deviceDisconnected:
reasonText = '设备断开连接';
break;
case ProvisionFailureReason.unknown:
reasonText = '未知错误';
break;
}
}
setState(() {
statusMessage = '配网失败: $error ${reasonText.isNotEmpty ? "($reasonText)" : ""}';
});
showErrorDialog('配网失败', '$error ${reasonText.isNotEmpty ? "\n原因: $reasonText" : ""}');
});
}
Future<void> startBleScan() async {
try {
setState(() {
isScanning = true;
bleDevices.clear();
statusMessage = '正在扫描BLE设备...';
});
await espManager.searchBleEspDevices(prefix: 'PROV_');
} catch (e) {
setState(() {
isScanning = false;
statusMessage = '启动扫描失败: $e';
});
showErrorDialog('扫描失败', e.toString());
}
}
Future<void> stopBleScan() async {
try {
await espManager.stopBleScan();
setState(() {
isScanning = false;
statusMessage = 'BLE扫描已停止';
});
} catch (e) {
showErrorDialog('停止扫描失败', e.toString());
}
}
Future<void> connectToBleDevice(BleDevice bleDevice) async {
try {
setState(() {
statusMessage = '正在连接到 ${bleDevice.name}...';
});
// 创建设备
currentDevice = await espManager.createESPDevice(
transportType: TransportType.ble,
securityType: SecurityType.security2,
);
// 设置POP(如果需要)
currentDevice!.setProofOfPossession('abcd1234');
// 连接设备
await currentDevice!.connectBLEDevice(
bleDevice.address,
bleDevice.primaryServiceUuid ?? '',
);
// 扫描WiFi网络
await scanWifiNetworks();
} catch (e) {
setState(() {
statusMessage = '连接失败: $e';
});
showErrorDialog('连接失败', e.toString());
}
}
Future<void> scanWifiNetworks() async {
try {
setState(() {
statusMessage = '正在扫描WiFi网络...';
});
if (currentDevice == null) {
showErrorDialog('错误', '设备未连接');
return;
}
final networks = await currentDevice!.scanNetworks();
setState(() {
wifiNetworks.clear();
wifiNetworks.addAll(networks);
statusMessage = '找到 ${networks.length} 个WiFi网络';
});
// 显示WiFi列表对话框
if (mounted) {
showWiFiListDialog();
}
} catch (e) {
setState(() {
statusMessage = '扫描WiFi网络失败: $e';
});
showErrorDialog('WiFi扫描失败', e.toString());
}
}
Future<void> provisionDevice(String ssid, String password) async {
try {
if (currentDevice == null) {
showErrorDialog('错误', '设备未连接');
return;
}
setState(() {
statusMessage = '正在配网...';
});
await currentDevice!.provision(ssid, password);
} catch (e) {
setState(() {
statusMessage = '配网失败: $e';
});
showErrorDialog('配网失败', e.toString());
}
}
void showWiFiListDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('选择WiFi网络'),
content: SizedBox(
width: double.maxFinite,
child: ListView.builder(
shrinkWrap: true,
itemCount: wifiNetworks.length,
itemBuilder: (context, index) {
final network = wifiNetworks[index];
return ListTile(
leading: Icon(
Icons.wifi,
color: network.rssi > -60 ? Colors.green : Colors.orange,
),
title: Text(network.wifiName),
subtitle: Text('RSSI: ${network.rssi} dBm'),
trailing: Icon(
network.security != 0 ? Icons.lock : Icons.lock_open,
),
onTap: () {
Navigator.pop(context);
showPasswordDialog(network.wifiName);
},
);
},
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
],
),
);
}
void showPasswordDialog(String ssid) {
final passwordController = TextEditingController();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('连接到 $ssid'),
content: TextField(
controller: passwordController,
decoration: const InputDecoration(
labelText: 'WiFi密码',
border: OutlineInputBorder(),
),
obscureText: true,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
provisionDevice(ssid, passwordController.text);
},
child: const Text('连接'),
),
],
),
);
}
void showErrorDialog(String title, String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(title),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('确定'),
),
],
),
);
}
void showSuccessDialog(String title, String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Row(
children: [
const Icon(Icons.check_circle, color: Colors.green),
const SizedBox(width: 8),
Text(title),
],
),
content: Text(message),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
// 重置状态
setState(() {
currentDevice = null;
bleDevices.clear();
wifiNetworks.clear();
statusMessage = '准备就绪';
});
},
child: const Text('确定'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('ESP设备配网'),
),
body: Column(
children: [
// 状态栏
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
color: Colors.blue.shade50,
child: Text(
statusMessage,
style: const TextStyle(fontSize: 16),
textAlign: TextAlign.center,
),
),
// BLE设备列表
Expanded(
child: bleDevices.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.bluetooth_searching,
size: 64,
color: Colors.grey.shade400,
),
const SizedBox(height: 16),
Text(
isScanning ? '正在扫描...' : '点击下方按钮开始扫描BLE设备',
style: TextStyle(
fontSize: 16,
color: Colors.grey.shade600,
),
),
],
),
)
: ListView.builder(
itemCount: bleDevices.length,
itemBuilder: (context, index) {
final device = bleDevices[index];
return Card(
margin: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
child: ListTile(
leading: Icon(
Icons.bluetooth,
color: device.rssi > -60
? Colors.blue
: Colors.orange,
size: 32,
),
title: Text(
device.name,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('地址: ${device.address}'),
Text('RSSI: ${device.rssi} dBm'),
],
),
trailing: ElevatedButton(
onPressed: () => connectToBleDevice(device),
child: const Text('连接'),
),
),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: isScanning ? stopBleScan : startBleScan,
icon: Icon(isScanning ? Icons.stop : Icons.search),
label: Text(isScanning ? '停止扫描' : '开始扫描'),
),
);
}
@override
void dispose() {
espManager.dispose();
super.dispose();
}
}