Search code examples
flutterandroid-studiodartserial-communicationmobile-development

Flutter blluetooth serial communication


I have a microcontroller connected with HC-06 bluetooth module. I want to build a flutter app that can comunicate with the microcontroller to send an integer value via bluetooth and then receive integer values as well from the microcontroller. The code on the other end is written by C programming. on my side I started building the app with flutter. I am using flutter_bluetooth_serial package. For the bluetooth connection, I used some of the code from the example on the official package github. https://github.com/edufolly/flutter_bluetooth_serial. (I copied the file named: BluetoothDeviceListEntry to my project).

Next, I created the main page of the application and the bluetooth connection coding. This is my main.dart file:

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

}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage() ,
      debugShowCheckedModeBanner: false,
    );
  }
}


class HomePage extends StatefulWidget{
  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with WidgetsBindingObserver {

  BluetoothState _bluetoothState = BluetoothState.UNKNOWN;

  List<BluetoothDevice> devices =  <BluetoothDevice>[];


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

    _getBTState();
    _stateChangeListener();
    _listBondedDevices();
    _stateChangeListener();
  }

  @override
  void dispose(){
    WidgetsBinding.instance!.removeObserver(this);
    super.dispose();

    @override
    void disChangeAppLifeCycleState(AppLifecycleState state){
      if(state.index == 0){
        //resume
        if(_bluetoothState.isEnabled){
          _listBondedDevices();
        }
      }
    }

  }
  _getBTState(){
    FlutterBluetoothSerial.instance.state.then((state){
      _bluetoothState = state;
      if(_bluetoothState.isEnabled){
        _listBondedDevices();
      }
      setState(() {});

    });
  }
  _stateChangeListener(){
    FlutterBluetoothSerial.instance.onStateChanged().listen((BluetoothState state) {
      _bluetoothState = state;
      if(_bluetoothState.isEnabled){
        _listBondedDevices();
      }else{
        devices.clear();
      }
      print("State isEnabled: ${state.isEnabled}");
      setState(() {});
    });

  }

  _listBondedDevices(){
    FlutterBluetoothSerial.instance.getBondedDevices().then((List<BluetoothDevice> bondedDevices){
      devices = bondedDevices;
      setState(() {});
    });


  }



  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Bluetooth Scanner"),),
      body: Container(
        child: Column(children: <Widget>[
          SwitchListTile(
            title: Text('Enable Bluetooth'),
            value: _bluetoothState.isEnabled,
            onChanged: (bool value){
              future() async{
                if(value){
                  await FlutterBluetoothSerial.instance.requestEnable();
                }else{
                  await FlutterBluetoothSerial.instance.requestDisable();
                }
                future().then((_){
                  setState(() {});
                });
              }
            },
          ),
          ListTile(
            title: Text("Bluetooth Status"),
            subtitle: Text(_bluetoothState.toString()),
            trailing: RaisedButton(child: Text("Settings"), onPressed: (){
              FlutterBluetoothSerial.instance.openSettings();
            },
           ),
          ),

          // adaptor code


          Expanded(
            child: ListView(
            children: devices
                .map((_device)=> BluetoothDeviceListEntry(
                    device: _device,
                    enabled: true,
                    onTap: (){
                      print("item");
                      _startDataCollection(context, _device);
                    },
                ))
                .toList(),
          ),
          ),

         // discovery code

            ],
          ),
      ),
      );
    }
    void _startDataCollection(BuildContext context, BluetoothDevice server){
    Navigator.of(context).push(MaterialPageRoute(builder: (context){
      return DetailPage(server: server);
    }));
    }
  }

When I press on the paired device the function _startDataCollection is triggered and I go to the other file (detailpage.dart) and here is where I started suffering...

here is the coding in detail page:


class DetailPage extends StatefulWidget {

  final BluetoothDevice server;

  DetailPage({required this.server});


  @override
  State<DetailPage> createState() => _DetailPageState();
}

class _DetailPageState extends State<DetailPage> {
  late BluetoothConnection connection;
  bool isConnecting = true;
  bool get isConnected => connection.isConnected;
  bool isDisconnecting = false;

  int _selectedvalue =0;

  int chunks = 0;
  int rcvdData = 0;
  int contentLength =0;
  late Uint8List _bytes;



  @override
  void initState() {
    super.initState();
    _getBTConnection();

    }

    @override
    void dispose() {
    if(isConnected){
      isDisconnecting = true;
      connection.dispose();
    }
    super.dispose();

    }

  _getBTConnection(){
    BluetoothConnection.toAddress(widget.server.address).then((_connection){
      connection = _connection;
      isConnecting = false;
      isDisconnecting = false;
      setState(() {});

      connection.input?.listen(_onDataReceived).onDone(() {
        if(isDisconnecting){
          print("Disconnecting locally");


        }else{
          print("Disconnecting remotely");

        }
        if(mounted){
          setState(() {});
        }
        Navigator.of(context).pop();

      });

    }).catchError((error){
      Navigator.of(context).pop();

    });
  }
 

  _onDataReceived(int data){
    if(data != null && data.bitLength > 0){
      chunks.add(data);

    

    }

  }


  Future<void> _sendMessageString() async {
    connection.output.add(ascii.encode('Hello!'));
    await connection.output.allSent;
    print('ok send message');
    }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey,
      appBar: AppBar(title: (isConnecting ? Text('Connecting to ${widget.server.name} ...')
          : isConnected ? Text ('Connected with ${widget.server.name}') : Text(
          'Disconnected with ${widget.server.name}')),),
      body: SafeArea(child: isConnected

          ? Column(
        children: <Widget>[
         submitButton()
        ],
            )
          : const Center(
        child: Text(
          "Connecting ...",
          style: TextStyle(
              fontSize: 24,
              fontWeight: FontWeight.bold,
              color: Colors.white),

        ),
      ),

        ));
  }
  Widget submitButton(){
    return Container(
      padding: const EdgeInsets.all(16),
      child: ElevatedButton(
        
      onPressed: () {
        print (chunks);
        },
        child: const Padding(
          padding: EdgeInsets.all(8),
          child: Text("show value", style: TextStyle(fontSize: 24),
          ),

        ),
      ),
    );
  }
  
}

What i am trying to do is that i want to print the received data in the compiler when i press the button (show value) but I am getting 2 errors. The first one is on _getBTConnection function:

"connection.input?.listen(_onDataReceived).onDone(()"

The argument type 'dynamic Function(int)' can't be assigned to the parameter type 'void Function(Uint8List)?'.

And the other one is on (_onDataReceived) function:

The method 'add' isn't defined for the type 'int'.

I tried to make some functions to send integers as well but non of them seems to work as well. Please if anyone can help me with this as im new to flutter

thank you so much


Solution

  • Look at the example in the link you provided. The type of incoming data is Uint8List not int.

    try {
        BluetoothConnection connection = await BluetoothConnection.toAddress(address);
        print('Connected to the device');
    
        connection.input.listen((Uint8List data) {
            print('Data incoming: ${ascii.decode(data)}');
            ...
        }).onDone(() {
            print('Disconnected by remote request');
        });
    }
    catch (exception) {
        print('Cannot connect, exception occured');
    }
    

    Use ByteData to read values from data.

    final byteData = ByteData.sublistView(data);
    final value = byteData.getFloat32(0);