flutter_ble_peripheral_slave 2.4.5 copy "flutter_ble_peripheral_slave: ^2.4.5" to clipboard
flutter_ble_peripheral_slave: ^2.4.5 copied to clipboard

Ble peripheral is a Flutter plugin that allows you to use your device as Bluetooth Low Energy (BLE) peripheral

example/lib/main.dart

/// BLE Peripheral Examples Menu
///
/// Choose between:
/// 1. Simple Heartbeat Example - Basic BLE peripheral with heartbeat notifications
/// 2. Advanced Usage Example - Full-featured BLE peripheral with multiple characteristics

import 'dart:async';
import 'dart:io' show Platform;
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter_ble_peripheral_slave/flutter_ble_peripheral_slave.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BLE Peripheral Examples',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const ExampleMenuPage(),
    );
  }
}

/// Main menu page to select which example to run
class ExampleMenuPage extends StatelessWidget {
  const ExampleMenuPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('BLE Peripheral Examples'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(24.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Icon(
                Icons.bluetooth,
                size: 60,
                color: Theme.of(context).colorScheme.primary,
              ),
              const SizedBox(height: 16),
              Text(
                'Choose an Example',
                style: Theme.of(context).textTheme.headlineSmall,
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 24),

              // Simple Example Button
              ElevatedButton.icon(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => const SimpleHeartbeatExamplePage(),
                    ),
                  );
                },
                icon: const Icon(Icons.favorite, size: 28),
                label: const Padding(
                  padding: EdgeInsets.all(12.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Simple Heartbeat',
                        style: TextStyle(
                            fontSize: 18, fontWeight: FontWeight.bold),
                      ),
                      Text(
                        'Basic BLE peripheral with heartbeat',
                        style: TextStyle(fontSize: 13),
                      ),
                    ],
                  ),
                ),
                style: ElevatedButton.styleFrom(
                  alignment: Alignment.centerLeft,
                  padding: const EdgeInsets.all(16),
                ),
              ),

              const SizedBox(height: 12),

              // Advanced Example Button
              ElevatedButton.icon(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => const AdvancedUsageExamplePage(),
                    ),
                  );
                },
                icon: const Icon(Icons.settings_applications, size: 28),
                label: const Padding(
                  padding: EdgeInsets.all(12.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Advanced Usage',
                        style: TextStyle(
                            fontSize: 18, fontWeight: FontWeight.bold),
                      ),
                      Text(
                        'Full-featured with multiple characteristics',
                        style: TextStyle(fontSize: 13),
                      ),
                    ],
                  ),
                ),
                style: ElevatedButton.styleFrom(
                  alignment: Alignment.centerLeft,
                  padding: const EdgeInsets.all(16),
                ),
              ),

              const SizedBox(height: 16),

              // Info card
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    children: [
                      Icon(
                        Icons.info_outline,
                        color: Theme.of(context).colorScheme.primary,
                      ),
                      const SizedBox(height: 8),
                      const Text(
                        'These examples demonstrate how to use your device as a BLE peripheral. Connect using nRF Connect or similar BLE scanner apps.',
                        textAlign: TextAlign.center,
                        style: TextStyle(fontSize: 12),
                      ),
                      if (Platform.isMacOS || Platform.isIOS) ...[
                        const SizedBox(height: 8),
                        Container(
                          padding: const EdgeInsets.all(8.0),
                          decoration: BoxDecoration(
                            color: Colors.orange.shade50,
                            borderRadius: BorderRadius.circular(8),
                            border: Border.all(color: Colors.orange.shade300),
                          ),
                          child: Row(
                            children: [
                              Icon(
                                Icons.warning_amber_rounded,
                                color: Colors.orange.shade700,
                                size: 16,
                              ),
                              const SizedBox(width: 8),
                              Expanded(
                                child: Text(
                                  'Note: iOS devices cannot discover peripherals on macOS/iOS due to Apple privacy filters. Use Android for testing.',
                                  style: TextStyle(
                                    fontSize: 11,
                                    color: Colors.orange.shade900,
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ),
                      ],
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// ============================================================================
// SIMPLE HEARTBEAT EXAMPLE
// ============================================================================

class SimpleHeartbeatExamplePage extends StatefulWidget {
  const SimpleHeartbeatExamplePage({super.key});

  @override
  State<SimpleHeartbeatExamplePage> createState() =>
      _SimpleHeartbeatExamplePageState();
}

class _SimpleHeartbeatExamplePageState
    extends State<SimpleHeartbeatExamplePage> {
  final heartbeatDevice = SimpleHeartbeatDevice();
  bool isInitialized = false;
  bool isAdvertising = false;
  String statusMessage = 'Not initialized';
  List<String> logs = [];

  @override
  void initState() {
    super.initState();
    _initializeBle();
  }

  void _addLog(String message) {
    setState(() {
      logs.insert(
          0, '${DateTime.now().toString().substring(11, 19)}: $message');
      if (logs.length > 20) logs.removeLast();
    });
  }

  Future<void> _initializeBle() async {
    try {
      await heartbeatDevice.initialize();
      setState(() {
        isInitialized = true;
        statusMessage = 'Initialized successfully';
      });
      _addLog('BLE Peripheral initialized');
    } catch (e) {
      setState(() {
        statusMessage = 'Failed to initialize: $e';
      });
      _addLog('Error: $e');
    }
  }

  Future<void> _startAdvertising() async {
    if (!isInitialized) {
      _addLog('Please initialize first');
      return;
    }

    try {
      await heartbeatDevice.startAdvertising();
      setState(() {
        isAdvertising = true;
        statusMessage = 'Advertising started';
      });
      _addLog('Started advertising');
    } catch (e) {
      _addLog('Failed to start advertising: $e');
    }
  }

  Future<void> _stopAdvertising() async {
    try {
      await BlePeripheral.stopAdvertising();
      setState(() {
        isAdvertising = false;
        statusMessage = 'Advertising stopped';
      });
      _addLog('Stopped advertising');
      heartbeatDevice.cleanup();
    } catch (e) {
      _addLog('Failed to stop advertising: $e');
    }
  }

  @override
  void dispose() {
    heartbeatDevice.cleanup();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Simple Heartbeat Example'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Status',
                      style: Theme.of(context).textTheme.titleMedium,
                    ),
                    const SizedBox(height: 8),
                    Text(statusMessage),
                    const SizedBox(height: 8),
                    Text(
                      'Connected Devices: ${heartbeatDevice.connectedDevices.length}',
                      style: const TextStyle(fontWeight: FontWeight.bold),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),
            Row(
              children: [
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: isAdvertising ? null : _startAdvertising,
                    icon: const Icon(Icons.play_arrow),
                    label: const Text('Start Advertising'),
                  ),
                ),
                const SizedBox(width: 8),
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: isAdvertising ? _stopAdvertising : null,
                    icon: const Icon(Icons.stop),
                    label: const Text('Stop Advertising'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.red,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 16),
            Text(
              'Activity Log',
              style: Theme.of(context).textTheme.titleMedium,
            ),
            const SizedBox(height: 8),
            Expanded(
              child: Card(
                child: logs.isEmpty
                    ? const Center(
                        child: Text('No activity yet'),
                      )
                    : ListView.builder(
                        itemCount: logs.length,
                        itemBuilder: (context, index) {
                          return Padding(
                            padding: const EdgeInsets.symmetric(
                              horizontal: 12.0,
                              vertical: 4.0,
                            ),
                            child: Text(
                              logs[index],
                              style: const TextStyle(
                                fontFamily: 'monospace',
                                fontSize: 12,
                              ),
                            ),
                          );
                        },
                      ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

/// Simple heartbeat BLE peripheral device
class SimpleHeartbeatDevice {
  // Service and Characteristic UUIDs
  static const String serviceUuid =
      "0000180D-0000-1000-8000-00805F9B34FB"; // Heart Rate Service
  static const String characteristicUuid =
      "00002A37-0000-1000-8000-00805F9B34FB"; // Heart Rate Measurement

  // Track connected devices and their subscriptions
  final Set<String> connectedDevices = {};
  Timer? heartbeatTimer;
  int heartbeatCounter = 0;

  Future<void> initialize() async {
    await BlePeripheral.initialize();

    // Set up callbacks
    BlePeripheral.setBleStateChangeCallback((isOn) {
      print("BLE State Changed: ${isOn ? 'ON' : 'OFF'}");
    });

    BlePeripheral.setAdvertisingStatusUpdateCallback((advertising, error) {
      if (error != null) {
        print("Advertising Error: $error");
      } else {
        print("Advertising: ${advertising ? 'Started' : 'Stopped'}");
      }
    });

    BlePeripheral.setCharacteristicSubscriptionChangeCallback((
      String deviceId,
      String characteristic,
      bool isSubscribed,
      String? deviceName,
    ) {
      print("Device $deviceId (${deviceName ?? 'Unknown'}) "
          "${isSubscribed ? 'subscribed to' : 'unsubscribed from'} "
          "$characteristic");

      if (isSubscribed) {
        connectedDevices.add(deviceId);
        if (heartbeatTimer == null) {
          _startHeartbeat();
        }
      } else {
        connectedDevices.remove(deviceId);
        if (connectedDevices.isEmpty) {
          _stopHeartbeat();
        }
      }
    });
  }

  Future<void> startAdvertising() async {
    // Add the heart rate service
    await BlePeripheral.addService(
      BleService(
        uuid: serviceUuid,
        primary: true,
        characteristics: [
          BleCharacteristic(
            uuid: characteristicUuid,
            properties: [
              CharacteristicProperties.read.index,
              CharacteristicProperties.notify.index,
            ],
            value: null,
            permissions: [AttributePermissions.readable.index],
          ),
        ],
      ),
    );

    // Start advertising
    if (Platform.isAndroid) {
      await BlePeripheral.startAdvertising(
        services: [serviceUuid],
        localName: "HeartRate Monitor",
      );
    } else {
      await BlePeripheral.startAdvertising(
        services: [serviceUuid],
        localName: "HeartRate Monitor",
        manufacturerData: ManufacturerData(
          manufacturerId: 1234,
          data: Uint8List.fromList([0x01, 0x02]),
        ),
      );
    }
  }

  void _startHeartbeat() {
    print("Starting heartbeat timer");
    heartbeatTimer = Timer.periodic(const Duration(seconds: 1), (timer) async {
      if (connectedDevices.isNotEmpty) {
        heartbeatCounter++;
        // Simulate heart rate between 60-100 bpm
        int heartRate = 60 + (heartbeatCounter % 40);

        // Heart Rate Measurement format: flags byte + heart rate value
        Uint8List heartRateData = Uint8List.fromList([
          0x00, // Flags: Heart Rate Value Format is UINT8
          heartRate, // Heart rate value
        ]);

        try {
          await BlePeripheral.updateCharacteristic(
            characteristicId: characteristicUuid,
            value: heartRateData,
            deviceId: null, // null sends to all connected devices
          );
          print(
              "Sent heartbeat $heartbeatCounter: ${heartRate}bpm to ${connectedDevices.length} device(s)");
        } catch (e) {
          print("Error sending heartbeat: $e");
        }
      }
    });
  }

  void _stopHeartbeat() {
    print("Stopping heartbeat timer");
    heartbeatTimer?.cancel();
    heartbeatTimer = null;
    heartbeatCounter = 0;
  }

  void cleanup() {
    _stopHeartbeat();
    connectedDevices.clear();
  }
}

// ============================================================================
// ADVANCED USAGE EXAMPLE
// ============================================================================

class AdvancedUsageExamplePage extends StatefulWidget {
  const AdvancedUsageExamplePage({super.key});

  @override
  State<AdvancedUsageExamplePage> createState() =>
      _AdvancedUsageExamplePageState();
}

class _AdvancedUsageExamplePageState extends State<AdvancedUsageExamplePage> {
  final ExampleBleDeviceController _controller = ExampleBleDeviceController();
  bool _isInitialized = false;

  @override
  void initState() {
    super.initState();
    _initializeBle();
  }

  Future<void> _initializeBle() async {
    try {
      await _controller.initialize();
      setState(() {
        _isInitialized = true;
      });
    } catch (e) {
      print("Failed to initialize BLE: $e");
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Advanced Usage Example'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text(
              'BLE Status',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            const SizedBox(height: 16),
            _buildStatusCard(),
            const SizedBox(height: 24),
            _buildControlButtons(),
            const SizedBox(height: 24),
            Text(
              'Connected Devices',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            const SizedBox(height: 16),
            _buildDevicesList(),
          ],
        ),
      ),
    );
  }

  Widget _buildStatusCard() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            _buildStatusRow('Initialized', _isInitialized),
            _buildStatusRow('BLE On', _controller.isBleOn),
            _buildStatusRow('Advertising', _controller.isAdvertising),
          ],
        ),
      ),
    );
  }

  Widget _buildStatusRow(String label, bool value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(label),
          Icon(
            value ? Icons.check_circle : Icons.cancel,
            color: value ? Colors.green : Colors.grey,
          ),
        ],
      ),
    );
  }

  Widget _buildControlButtons() {
    return Column(
      children: [
        ElevatedButton(
          onPressed: _isInitialized && !_controller.isAdvertising
              ? () async {
                  await _controller.startAdvertising();
                  setState(() {});
                }
              : null,
          child: const Text('Start Advertising'),
        ),
        const SizedBox(height: 8),
        ElevatedButton(
          onPressed: _isInitialized && _controller.isAdvertising
              ? () async {
                  await _controller.stopAdvertising();
                  setState(() {});
                }
              : null,
          child: const Text('Stop Advertising'),
        ),
        const SizedBox(height: 8),
        ElevatedButton(
          onPressed: _isInitialized && _controller.connectedDevices.isNotEmpty
              ? () async {
                  // Send example custom data
                  await _controller.sendCustomData(
                    ExampleBleDeviceController.notifyCharUuid,
                    [0xFF, 0xAA, 0x55, 0x00],
                  );
                }
              : null,
          child: const Text('Send Custom Data'),
        ),
      ],
    );
  }

  Widget _buildDevicesList() {
    if (_controller.connectedDevices.isEmpty) {
      return const Card(
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: Text(
            'No devices connected',
            textAlign: TextAlign.center,
            style: TextStyle(color: Colors.grey),
          ),
        ),
      );
    }

    return Card(
      child: ListView.builder(
        shrinkWrap: true,
        itemCount: _controller.connectedDevices.length,
        itemBuilder: (context, index) {
          final device = _controller.connectedDevices.elementAt(index);
          return ListTile(
            leading: const Icon(Icons.bluetooth_connected),
            title: Text(device),
          );
        },
      ),
    );
  }
}

/// Example BLE device controller demonstrating advanced usage patterns
class ExampleBleDeviceController {
  // BLE state tracking
  bool isAdvertising = false;
  bool isBleOn = false;

  // Connected devices
  Set<String> connectedDevices = {};

  // Device configuration
  final String deviceName = "MyDevice";
  final int manufacturerId = 0x012D;

  // Service and Characteristic UUIDs
  static const String serviceUuid = "0000FF00-0000-1000-8000-00805F9B34FB";
  static const String notifyCharUuid = "0000FF01-0000-1000-8000-00805F9B34FB";
  static const String writeCharUuid = "0000FF02-0000-1000-8000-00805F9B34FB";
  static const String heartbeatCharUuid =
      "0000FF03-0000-1000-8000-00805F9B34FB";

  // Timers for periodic operations
  Timer? _heartbeatTimer;
  Timer? _dataTimer;

  // Track subscribers per characteristic
  Set<String> _notifySubscribers = {};
  Set<String> _heartbeatSubscribers = {};

  // Example state data
  int _batteryLevel = 100;

  /// Initialize the BLE peripheral
  Future<void> initialize() async {
    print("Initializing BLE Peripheral...");
    _setupCallbacks();

    try {
      await BlePeripheral.initialize();
      print("✓ BLE Peripheral initialized successfully");
    } catch (e) {
      print("✗ Initialization error: $e");
      rethrow;
    }

    await _waitForBleReady();
    await _addServices();
    print("✓ Setup complete - ready to advertise");
  }

  /// Setup all BLE callbacks
  void _setupCallbacks() {
    BlePeripheral.setBleStateChangeCallback((bool isOn) {
      isBleOn = isOn;
      print("BLE State Changed: ${isOn ? 'ON' : 'OFF'}");
    });

    BlePeripheral.setAdvertisingStatusUpdateCallback(
        (bool advertising, String? error) {
      isAdvertising = advertising;
      if (error != null) {
        print("Advertising error: $error");
      } else {
        print("Advertising: ${advertising ? 'Started' : 'Stopped'}");
      }
    });

    BlePeripheral.setCharacteristicSubscriptionChangeCallback((
      String deviceId,
      String characteristicId,
      bool isSubscribed,
      String? deviceName,
    ) {
      print(
          "Subscription Change: Device=$deviceId, Char=$characteristicId, Subscribed=$isSubscribed");

      final displayName =
          deviceName?.isNotEmpty == true ? deviceName! : deviceId;

      if (isSubscribed) {
        connectedDevices.add(displayName);

        if (characteristicId.toLowerCase() == notifyCharUuid.toLowerCase()) {
          _notifySubscribers.add(deviceId);
          if (_dataTimer == null) {
            _startDataTransmission();
          }
        }

        if (characteristicId.toLowerCase() == heartbeatCharUuid.toLowerCase()) {
          _heartbeatSubscribers.add(deviceId);
          if (_heartbeatTimer == null) {
            _startHeartbeat();
          }
        }
      } else {
        if (characteristicId.toLowerCase() == notifyCharUuid.toLowerCase()) {
          _notifySubscribers.remove(deviceId);
          if (_notifySubscribers.isEmpty) {
            _dataTimer?.cancel();
            _dataTimer = null;
          }
        }

        if (characteristicId.toLowerCase() == heartbeatCharUuid.toLowerCase()) {
          _heartbeatSubscribers.remove(deviceId);
          if (_heartbeatSubscribers.isEmpty) {
            _heartbeatTimer?.cancel();
            _heartbeatTimer = null;
          }
        }

        connectedDevices.remove(displayName);
      }
    });
  }

  Future<void> _waitForBleReady() async {
    final timeout = Duration(seconds: 5);
    final startTime = DateTime.now();

    while (!isBleOn) {
      if (DateTime.now().difference(startTime) > timeout) {
        print("Warning: BLE state timeout - proceeding anyway");
        break;
      }
      await Future.delayed(Duration(milliseconds: 100));
    }

    if (isBleOn) {
      print("✓ BLE is powered on and ready");
    }
  }

  Future<void> _addServices() async {
    try {
      await BlePeripheral.addService(
        BleService(
          uuid: serviceUuid,
          primary: true,
          characteristics: [
            BleCharacteristic(
              uuid: notifyCharUuid,
              properties: [
                CharacteristicProperties.read.index,
                CharacteristicProperties.notify.index,
              ],
              descriptors: [],
              value: null,
              permissions: [AttributePermissions.readable.index],
            ),
            BleCharacteristic(
              uuid: writeCharUuid,
              properties: [
                CharacteristicProperties.write.index,
                CharacteristicProperties.writeWithoutResponse.index,
              ],
              descriptors: [],
              value: null,
              permissions: [AttributePermissions.writeable.index],
            ),
            BleCharacteristic(
              uuid: heartbeatCharUuid,
              properties: [
                CharacteristicProperties.read.index,
                CharacteristicProperties.notify.index,
              ],
              descriptors: [],
              value: null,
              permissions: [AttributePermissions.readable.index],
            ),
          ],
        ),
      );
      print("✓ BLE service added successfully");
    } catch (e) {
      print("✗ Error adding service: $e");
      rethrow;
    }
  }

  Future<void> startAdvertising() async {
    print("Starting advertising...");

    if (Platform.isIOS) {
      await BlePeripheral.startAdvertising(
        services: [serviceUuid],
        localName: deviceName,
        addManufacturerDataInScanResponse: false,
      );
    } else {
      await BlePeripheral.startAdvertising(
        services: [serviceUuid],
        localName: deviceName,
        manufacturerData: ManufacturerData(
          manufacturerId: manufacturerId,
          data: Uint8List.fromList([0x01, 0x02, 0x03]),
        ),
        addManufacturerDataInScanResponse: true,
      );
    }
  }

  Future<void> stopAdvertising() async {
    print("Stopping advertising...");
    await BlePeripheral.stopAdvertising();
  }

  void _startHeartbeat() {
    _heartbeatTimer?.cancel();
    _batteryLevel = 100;
    print("Starting heartbeat transmission...");

    _heartbeatTimer = Timer.periodic(const Duration(seconds: 5), (timer) async {
      if (_heartbeatSubscribers.isEmpty) return;

      _batteryLevel -= 1;
      if (_batteryLevel < 0) _batteryLevel = 100;

      final heartbeat = Uint8List.fromList([
        0xC2,
        _batteryLevel,
        0x00,
        0x01,
        0x01,
      ]);

      for (final deviceId in _heartbeatSubscribers) {
        try {
          await BlePeripheral.updateCharacteristic(
            characteristicId: heartbeatCharUuid,
            value: heartbeat,
            deviceId: deviceId,
          );
          print("Heartbeat sent to $deviceId: Battery=$_batteryLevel%");
        } catch (e) {
          print("Error sending heartbeat to $deviceId: $e");
        }
      }
    });
  }

  void _startDataTransmission() {
    _dataTimer?.cancel();
    print("Starting data transmission...");

    _dataTimer = Timer.periodic(const Duration(seconds: 1), (timer) async {
      if (_notifySubscribers.isEmpty) return;

      final timestamp = DateTime.now().millisecondsSinceEpoch;
      final data = Uint8List(8);
      final byteData = ByteData.view(data.buffer);
      byteData.setInt64(0, timestamp, Endian.little);

      for (final deviceId in _notifySubscribers) {
        try {
          await BlePeripheral.updateCharacteristic(
            characteristicId: notifyCharUuid,
            value: data,
            deviceId: deviceId,
          );
        } catch (e) {
          print("Error sending data to $deviceId: $e");
        }
      }
    });
  }

  Future<void> sendCustomData(String characteristicUuid, List<int> data) async {
    final subscribers =
        characteristicUuid.toLowerCase() == notifyCharUuid.toLowerCase()
            ? _notifySubscribers
            : _heartbeatSubscribers;

    if (subscribers.isEmpty) {
      print("No subscribers for characteristic $characteristicUuid");
      return;
    }

    final uint8Data = Uint8List.fromList(data);

    for (final deviceId in subscribers) {
      try {
        await BlePeripheral.updateCharacteristic(
          characteristicId: characteristicUuid,
          value: uint8Data,
          deviceId: deviceId,
        );
        print("Custom data sent to $deviceId");
      } catch (e) {
        print("Error sending custom data to $deviceId: $e");
      }
    }
  }

  void dispose() {
    print("Disposing BLE controller...");
    _heartbeatTimer?.cancel();
    _heartbeatTimer = null;
    _dataTimer?.cancel();
    _dataTimer = null;
    stopAdvertising();
    _notifySubscribers.clear();
    _heartbeatSubscribers.clear();
    connectedDevices.clear();
    print("✓ Cleanup complete");
  }
}
3
likes
150
points
21
downloads

Publisher

unverified uploader

Weekly Downloads

Ble peripheral is a Flutter plugin that allows you to use your device as Bluetooth Low Energy (BLE) peripheral

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_ble_peripheral_slave

Packages that implement flutter_ble_peripheral_slave