実装手順
-
ESP32側で文字列を送信:
- ESP32の
loop()関数内で、pTxCharacteristic->setValue()を使用して文字列を送信します。
- ESP32の
-
Flutter側で文字列を受信:
- Flutterアプリで、
characteristic.value.listen()を使用して通知を受信し、文字列に変換します。
- Flutterアプリで、
ESP32側のコード
以下は、ESP32から文字列を送信する例です。
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLEServer *pServer = NULL;
BLECharacteristic *pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer *pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer *pServer) {
deviceConnected = false;
}
};
void setup() {
Serial.begin(115200);
// Create the BLE Device
BLEDevice::init("AQUA-FAN-BLE");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
if (deviceConnected) {
// 文字列を送信
String message = "Hello from ESP32";
pTxCharacteristic->setValue(message.c_str());
pTxCharacteristic->notify();
delay(1000); // 1秒ごとに送信
}
// 切断処理
if (!deviceConnected && oldDeviceConnected) {
delay(500);
pServer->startAdvertising();
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
}
if (deviceConnected && !oldDeviceConnected) {
oldDeviceConnected = deviceConnected;
}
}
Flutter側のコード
以下は、Flutterアプリで文字列を受信する例です。
import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: BleDeviceSelector(),
);
}
}
class BleDeviceSelector extends StatefulWidget {
_BleDeviceSelectorState createState() => _BleDeviceSelectorState();
}
class _BleDeviceSelectorState extends State<BleDeviceSelector> {
FlutterBluePlus flutterBlue = FlutterBluePlus.instance;
List<BluetoothDevice> devices = [];
BluetoothDevice? connectedDevice;
String status = "Scanning...";
String receivedData = "";
void initState() {
super.initState();
startScan();
}
void startScan() async {
setState(() {
status = "Scanning...";
devices.clear();
receivedData = ""; // 受信データをリセット
});
// 接続中のデバイスを切断
if (connectedDevice != null) {
await connectedDevice!.disconnect();
setState(() {
connectedDevice = null; // 接続中のデバイスをリセット
});
}
// スキャンを停止
await flutterBlue.stopScan();
// キャッシュをクリア
flutterBlue = FlutterBluePlus.instance;
// 少し遅延を追加
await Future.delayed(Duration(seconds: 1));
// スキャンを開始
flutterBlue.startScan(timeout: Duration(seconds: 10));
// スキャン結果をリッスン
flutterBlue.scanResults.listen((results) {
for (ScanResult result in results) {
// 重複チェック
if (!devices.any((device) => device.id == result.device.id)) {
setState(() {
devices.add(result.device);
});
}
}
});
}
void stopScan() async {
await flutterBlue.stopScan();
await Future.delayed(Duration(seconds: 1)); // リソース解放のための遅延
}
void connectToDevice(BluetoothDevice device) async {
setState(() {
status = "Connecting...";
});
// 接続中のデバイスがある場合、切断する
if (connectedDevice != null) {
await connectedDevice!.disconnect();
setState(() {
connectedDevice = null; // 接続中のデバイスをリセット
});
}
// 新しいデバイスに接続
await device.connect();
setState(() {
connectedDevice = device;
status = "Connected to ${device.name ?? device.id.toString()}";
});
discoverServices(device);
}
void discoverServices(BluetoothDevice device) async {
try {
List<BluetoothService> services = await device.discoverServices();
for (BluetoothService service in services) {
for (BluetoothCharacteristic characteristic in service.characteristics) {
// TXキャラクタリスティックに対して通知を有効にする
if (characteristic.uuid.toString().toLowerCase() == "6e400003-b5a3-f393-e0a9-e50e24dcca9e") {
await characteristic.setNotifyValue(true);
characteristic.value.listen((value) {
setState(() {
receivedData = String.fromCharCodes(value); // 受信データを文字列に変換
});
print("Notification received: $value");
});
}
}
}
} catch (e) {
print("Error discovering services: $e");
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BLE Device Selector'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Status: $status"),
if (connectedDevice != null) Text("Connected to: ${connectedDevice!.name ?? connectedDevice!.id.toString()}"),
Text("Received Data: $receivedData"),
Expanded(
child: ListView.builder(
itemCount: devices.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(devices[index].name ?? "Unknown Device (${devices[index].id})"),
subtitle: Text(devices[index].id.toString()),
onTap: () => connectToDevice(devices[index]),
);
},
),
),
ElevatedButton(
onPressed: startScan,
child: Text("Rescan"),
),
],
),
),
);
}
}
動作の流れ
-
ESP32側:
loop()関数内で、1秒ごとに"Hello from ESP32"という文字列を送信します。
-
Flutter側:
characteristic.value.listen()で通知を受信し、receivedDataに文字列を格納します。- 受信した文字列はUIに表示されます。
注意点
- ESP32とFlutterアプリのUUIDが一致しているか確認してください。
- 通知を有効にするキャラクタリスティックのUUIDが正しいか確認してください。
- 受信データのエンコーディングが正しいか確認してください(この例ではUTF-8を使用)。
この実装により、ESP32から送信された文字列をFlutterアプリで受信し、表示することができます。
