ESP32からテキスト文を送信しflutterで受信する方法

実装手順

  1. ESP32側で文字列を送信:

    • ESP32の loop() 関数内で、pTxCharacteristic->setValue() を使用して文字列を送信します。
  2. Flutter側で文字列を受信:

    • Flutterアプリで、characteristic.value.listen() を使用して通知を受信し、文字列に変換します。

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"),
            ),
          ],
        ),
      ),
    );
  }
}


動作の流れ

  1. ESP32側:

    • loop() 関数内で、1秒ごとに "Hello from ESP32" という文字列を送信します。
  2. Flutter側:

    • characteristic.value.listen() で通知を受信し、receivedData に文字列を格納します。
    • 受信した文字列はUIに表示されます。

注意点

  • ESP32とFlutterアプリのUUIDが一致しているか確認してください。
  • 通知を有効にするキャラクタリスティックのUUIDが正しいか確認してください。
  • 受信データのエンコーディングが正しいか確認してください(この例ではUTF-8を使用)。

この実装により、ESP32から送信された文字列をFlutterアプリで受信し、表示することができます。

タイトルとURLをコピーしました