//////////////////////// // flutter main.dart//// // flutter pub dd get // flutter pub add universal_ble // works with linux laptop with ble // works with linux desktop with ble adapter // works with pixel 7a // works with samsung galaxy 10 // connects to picow with id "28:CD:C1:08:28:9E" // // this app contains two files // 1. main.dart // 2. permission_handler.dart (see below after main.dart // // pubspec.yaml // plugin needed // 1. device_info_plus // 2. get // 3. permission_handler // 4. universal_ble // import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:universal_ble/universal_ble.dart'; import './permission_handler.dart'; import 'dart:async'; import 'dart:io'; import 'dart:convert'; void main() { runApp( MaterialApp( title: 'Universal BLE', debugShowCheckedModeBanner: false, darkTheme: ThemeData.dark(), themeMode: ThemeMode.system, home: const MyApp()));} class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State createState() => _MyAppState();} class _MyAppState extends State { QueueType _queueType = QueueType.global; late BleDevice device; late BleService service; late List discoveredServices=[]; late BleCharacteristic charTx; ScanFilter? scanFilter; var status='scan and connect'.obs; void write_test(val) async { Uint8List data=utf8.encode(val); try{await UniversalBle.writeValue( device.deviceId,service.uuid,charTx.uuid,data,BleOutputProperty.withResponse); } catch(e) {print(e);}} void handleScan(result) async{ if (result.deviceId == "28:CD:C1:08:28:9E"){ device=result; status.value = 'connecting...'; try{await UniversalBle.connect(device.deviceId);} catch(e){print(e.runtimeType);print(e);} try{var services=await(UniversalBle.discoverServices(device.deviceId)); discoveredServices=services;} catch(e){print(e);} for(var s in discoveredServices){ if(s.uuid.toString() == "6e400001-b5a3-f393-e0a9-e50e24dcca9e"){ status.value = 'discovering services...'; print("service found"); service=s; var chars=service.characteristics; for(var c in chars){ status.value = 'finding characteristic...'; if(c.uuid.toString() == "6e400002-b5a3-f393-e0a9-e50e24dcca9e"){ status.value="app ready"; charTx=c;}}}}}} void handleConnect(String deviceId,bool isConnected,String? error){ status.value = isConnected ? "Connected" : "Scanning...";} void handleQueue(String id, int len){ print("queue: $id len: $len");} Future startScan()async{ print("startscan function"); try{await UniversalBle.stopScan();}catch(e){print(e);} try{await UniversalBle.startScan();}catch(e){print(e);}} @override void initState() { super.initState(); UniversalBle.queueType = QueueType.global;//works for desktop UniversalBle.timeout = const Duration(seconds:30);//30 works for android //UniversalBle.timeout = const Duration(seconds:10);//works for desktop UniversalBle.onQueueUpdate = handleQueue; UniversalBle.onScanResult = handleScan; UniversalBle.onConnectionChange = handleConnect;} @override Widget build(BuildContext bc){ return Scaffold(appBar:AppBar(title:Text('universal ble demo')), body:Column(spacing:20,children:[ ElevatedButton(child:Text("check permissions"),onPressed:()async{ bool hasPermissions=await PermissionHandler.arePermissionsGranted(); if (hasPermissions){print("permission granted");}}), ElevatedButton(onPressed:(){startScan();}, child:Obx(()=>Text('${status}'))), ElevatedButton(onPressed:(){write_test('t\n');},child:Text('on')), ElevatedButton(onPressed:(){write_test('u\n');},child:Text('off')), Text(''' troubleshooting: plug/unplug usb on the picow micropython wait for the status to say app ready before proceeding, if you are unable to get app ready status then something is wrong and not connecting with the ble. if you already connected once on the picow then you need to plug/unplug the picow because the picow is already in the system devices. '''), ]));}} /////////////////////////// //permission_handler.dart /////////////////////////// // ignore_for_file: avoid_print import 'dart:async'; import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:permission_handler/permission_handler.dart'; /* Required Permissions : <-----------> IOS : - Bluetooth <-----------> Android : if AndroidVersions < 12 - Location - Bluetooth else - Bluetooth Scan - Bluetooth Connect <-----------> Macos : <-----------> Windows : None <-----------> Linux : None <-----------> Web : Check if Browser Supports Bluetooth */ class PermissionHandler { static Future arePermissionsGranted() async { if (!isMobilePlatform) return true; var status = await _permissionStatus; bool blePermissionGranted = status[0]; bool locationPermissionGranted = status[1]; if (locationPermissionGranted && blePermissionGranted) return true; if (!blePermissionGranted) { PermissionStatus blePermissionCheck = await Permission.bluetooth.request(); if (blePermissionCheck.isPermanentlyDenied) { print("Bluetooth Permission Permanently Denied"); openAppSettings(); } return false; } if (!locationPermissionGranted) { PermissionStatus locationPermissionCheck = await Permission.location.request(); if (locationPermissionCheck.isPermanentlyDenied) { print("Location Permission Permanently Denied"); openAppSettings(); } return false; } return false; } static Future> get _permissionStatus async { bool blePermissionGranted = false; bool locationPermissionGranted = false; if (await requiresExplicitAndroidBluetoothPermissions) { bool bleConnectPermission = (await Permission.bluetoothConnect.request()).isGranted; bool bleScanPermission = (await Permission.bluetoothScan.request()).isGranted; blePermissionGranted = bleConnectPermission && bleScanPermission; locationPermissionGranted = true; } else { PermissionStatus permissionStatus = await Permission.bluetooth.request(); blePermissionGranted = permissionStatus.isGranted; locationPermissionGranted = await requiresLocationPermission ? (await Permission.locationWhenInUse.request()).isGranted : true; } return [locationPermissionGranted, blePermissionGranted]; } static bool get isMobilePlatform => !kIsWeb && (Platform.isAndroid || Platform.isIOS); static Future get requiresLocationPermission async => !kIsWeb && Platform.isAndroid && (!await requiresExplicitAndroidBluetoothPermissions); static Future get requiresExplicitAndroidBluetoothPermissions async { if (kIsWeb || !Platform.isAndroid) return false; AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo; return androidInfo.version.sdkInt >= 31; } }