title: Flutter_blue and Adafruit Bluefruit BLE characteristic error: could not locate CCCD descriptor for characteristic
body: <p>I am currently running into an issue with my ble project for flutter. I am using the flutter blue package by Paul DeMarco and the additional page for the app is based on &quot;ThatProject&quot; dust sensor from youtube.  I have an Adafruit Feather 32u4 board, and I am attempting to notify the client (my flutter app) that It has a series of numbers to send, but I am not getting any output. I am able to connect to the device, and seem to properly send the service UUID and characteristic UUID, but im not sure if it is coming with proper properties.
  I am using the adafruit BLE code to program the board, and I can get the values if I use adafruit's app. I am just trying to get the values on my own flutter app.</p>
  <p>I am running into an error as follows:</p>
  <pre><code>E/flutter (32139): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: PlatformException(set_notification_error, could not locate CCCD descriptor for characteristic: 6e400002-b5a3-f393-e0a9-e50e24dcca9e, null, null)
  </code></pre>
  <p>Here is my code. I believe the missing CCCD is coming from this part:</p>
  <pre><code>import 'dart:async';
  import 'dart:convert' show utf8;
  
  import 'package:flutter/material.dart';
  import 'package:flutter_blue/flutter_blue.dart';
  import 'package:oscilloscope/oscilloscope.dart';
  
  class SensorPage extends StatefulWidget {
    const SensorPage({Key key, this.device}) : super(key: key);
    final BluetoothDevice device;
  
    @override
    _SensorPageState createState() =&gt; _SensorPageState();
  }
  
  class _SensorPageState extends State&lt;SensorPage&gt; {
    final String SERVICE_UUID = &quot;6e400001-b5a3-f393-e0a9-e50e24dcca9e&quot;;
    final String CHARACTERISTIC_UUID = &quot;6e400002-b5a3-f393-e0a9-e50e24dcca9e&quot;;
    bool isReady;
    Stream&lt;List&lt;int&gt;&gt; stream;
    List&lt;double&gt; traceDust = List();
  
    @override
    void initState() {
      super.initState();
      isReady = false;
      connectToDevice();
    }
  
    connectToDevice() async {
      // if (widget.device == null) {
      //   // _Pop();
      //   return;
      // }
  
      //timeout timer, watchdog timer if you will
      new Timer(const Duration(seconds: 15), () {
        if (!isReady) {
          disconnectFromDevice();
          //   _Pop();
        }
      });
  
      await widget.device.connect();
      discoverServices();
    }
  
    disconnectFromDevice() {
      if (widget.device == null) {
        //_Pop();
        return;
      }
  
      widget.device.disconnect();
    }
  
    discoverServices() async {
      // if (widget.device == null) {
      //   //  _Pop();
      //   return;
      // }
  
      BluetoothCharacteristic ss;
      List&lt;BluetoothService&gt; services = await widget.device.discoverServices();
      services.forEach((service) {
        debugPrint(&quot;This Service UUID is!${service.uuid.toString()}&quot;);
        if (service.uuid.toString() == SERVICE_UUID) {
          service.characteristics.forEach((characteristic) {
            debugPrint(&quot;This char UUID is!${characteristic.uuid.toString()}&quot;);
            if (characteristic.uuid.toString() == CHARACTERISTIC_UUID) {
              debugPrint(&quot;Here is !isNotifying: ${!characteristic.isNotifying}&quot;);
              debugPrint(&quot;Here is characteristic.value: ${characteristic.value}&quot;);
              ss = characteristic;
              stream = ss.value;
              setState(() {
                isReady = true;
              });
            } //this one
          });
        } //this one
      });
      await ss.setNotifyValue(true);
      stream = ss.value;
      if (!isReady) {
        // _Pop();
      }
    }
  
    Future&lt;bool&gt; _onWillPop() {
      return showDialog(
          context: context,
          builder: (context) =&gt;
              new AlertDialog(
                title: Text('Are you sure?'),
                content: Text('Do you want to disconnect device and go back?'),
                actions: &lt;Widget&gt;[
                  new FlatButton(
                      onPressed: () =&gt; Navigator.of(context).pop(false),
                      child: new Text('No')),
                  new FlatButton(
                      onPressed: () {
                        disconnectFromDevice();
                        Navigator.of(context).pop(true);
                      },
                      child: new Text('Yes')),
                ],
              ) ??
              false);
    }
  
    // _Pop() {
    //   Navigator.of(context).pop(true);
    // }
  
    String _dataParser(List&lt;int&gt; dataFromDevice) {
      debugPrint(&quot;current value is-&gt; ${utf8.decode(dataFromDevice)}&quot;);
      return utf8.decode(dataFromDevice);
    }
  
    @override
    Widget build(BuildContext context) {
      Oscilloscope oscilloscope = Oscilloscope(
        showYAxis: true,
        padding: 0.0,
        backgroundColor: Colors.black,
        traceColor: Colors.white,
        yAxisMax: 3000.0,
        yAxisMin: 0.0,
        dataSet: traceDust,
      );
  
      return WillPopScope(
        onWillPop: _onWillPop,
        child: Scaffold(
          appBar: AppBar(
            title: Text('Optical Dust Sensor'),
          ),
          body: Container(
              child: !isReady
                  ? Center(
                      child: Text(
                        &quot;Waiting...&quot;,
                        style: TextStyle(fontSize: 24, color: Colors.red),
                      ),
                    )
                  : Container(
                      child: StreamBuilder&lt;List&lt;int&gt;&gt;(
                        stream: stream,
                        builder: (BuildContext context,
                            AsyncSnapshot&lt;List&lt;int&gt;&gt; snapshot) {
                          if (snapshot.hasError)
                            return Text('Error: ${snapshot.error}');
  
                          if (snapshot.connectionState ==
                              ConnectionState.active) {
                            debugPrint(&quot;snapshot.error: ${snapshot.error}.&quot;);
                            debugPrint(&quot;snapshot.data: ${snapshot.error}.&quot;);
                            debugPrint(
                                &quot;snapshot.connectionState: ${snapshot.connectionState}.&quot;);
                            debugPrint(&quot;snapshot.hasdata?: ${snapshot.hasData}.&quot;);
  
                            var currentValue = _dataParser(snapshot.data);
                            traceDust.add(double.tryParse(currentValue) ?? 0);
  
                            return Center(
                                child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: &lt;Widget&gt;[
                                Expanded(
                                  flex: 1,
                                  child: Column(
                                      mainAxisAlignment: MainAxisAlignment.center,
                                      children: &lt;Widget&gt;[
                                        Text('Current value from Sensor',
                                            style: TextStyle(fontSize: 14)),
                                        Text('$currentValue ug/m3',
                                            style: TextStyle(
                                                fontWeight: FontWeight.bold,
                                                fontSize: 24))
                                      ]),
                                ),
                                Expanded(
                                  flex: 1,
                                  child: oscilloscope,
                                )
                              ],
                            ));
                          } else {
                            return Text('Check the stream');
                          }
                        },
                      ),
                    )),
        ),
      );
    }
  }
  
  </code></pre>
  <pre><code>// Copyright 2017, Paul DeMarco.
  // All rights reserved. Use of this source code is governed by a
  // BSD-style license that can be found in the LICENSE file.
  
  import 'dart:async';
  
  import 'package:flutter/material.dart';
  import 'package:K9Harness/Pages/Sensor_page.dart';
  import 'package:K9Harness/Bluetooth/widgets.dart';
  import 'package:flutter_blue/flutter_blue.dart';
  
  class MyBluetoothPage extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        color: Colors.lightBlue,
        home: StreamBuilder&lt;BluetoothState&gt;(
            stream: FlutterBlue.instance.state,
            initialData: BluetoothState.unknown,
            builder: (c, snapshot) {
              final state = snapshot.data;
              if (state == BluetoothState.on) {
                return FindDevicesScreen();
              }
              return BluetoothOffScreen(state: state);
            }),
      );
    }
  }
  
  class BluetoothOffScreen extends StatelessWidget {
    const BluetoothOffScreen({Key key, this.state}) : super(key: key);
  
    final BluetoothState state;
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        backgroundColor: Colors.lightBlue,
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: &lt;Widget&gt;[
              Icon(
                Icons.bluetooth_disabled,
                size: 200.0,
                color: Colors.white54,
              ),
              Text(
                'Bluetooth Adapter is ${state.toString().substring(15)}.',
                style: Theme.of(context)
                    .primaryTextTheme
                    .subhead
                    .copyWith(color: Colors.white),
              ),
            ],
          ),
        ),
      );
    }
  }
  
  class FindDevicesScreen extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Find Devices'),
        ),
        body: RefreshIndicator(
          onRefresh: () =&gt; FlutterBlue.instance
              .startScan(
                  scanMode: ScanMode.balanced,
                  withServices: [
                    Guid(&quot;6E400001-B5A3-F393-E0A9-E50E24DCCA9E&quot;)
                  ], //FIXME check the other ways where &quot;.startScan&quot; is implemented
                  timeout: Duration(seconds: 4))
              .catchError((error) {
            print(&quot;error starting scan $error&quot;);
          }),
          child: SingleChildScrollView(
            child: Column(
              children: &lt;Widget&gt;[
                StreamBuilder&lt;List&lt;BluetoothDevice&gt;&gt;(
                  stream: Stream.periodic(Duration(seconds: 2))
                      .asyncMap((_) =&gt; FlutterBlue.instance.connectedDevices),
                  initialData: [],
                  builder: (c, snapshot) =&gt; Column(
                    children: snapshot.data
                        .map((d) =&gt; ListTile(
                              title: Text(d.name),
                              subtitle: Text(d.id.toString()),
                              trailing: StreamBuilder&lt;BluetoothDeviceState&gt;(
                                stream: d.state,
                                initialData: BluetoothDeviceState.disconnected,
                                builder: (c, snapshot) {
                                  if (snapshot.data ==
                                      BluetoothDeviceState.connected) {
                                    return RaisedButton(
                                      child: Text('OPEN'),
                                      onPressed: () =&gt; Navigator.of(context).push(
                                          MaterialPageRoute(
                                              builder: (context) =&gt;
                                                  DeviceScreen(device: d))),
                                    );
                                  }
                                  return Text(snapshot.data.toString());
                                },
                              ),
                            ))
                        .toList(),
                  ),
                ),
                StreamBuilder&lt;List&lt;ScanResult&gt;&gt;(
                  stream: FlutterBlue.instance.scanResults,
                  initialData: [],
                  builder: (c, snapshot) =&gt; Column(
                    children: snapshot.data
                        .map(
                          (r) =&gt; ScanResultTile(
                            result: r,
                            onTap: () =&gt; Navigator.of(context)
                                .push(MaterialPageRoute(builder: (context) {
                              r.device.connect();
                              return SensorPage(device: r.device);
                            })),
                          ),
                        )
                        .toList(),
                  ),
                ),
              ],
            ),
          ),
        ),
        floatingActionButton: StreamBuilder&lt;bool&gt;(
          stream: FlutterBlue.instance.isScanning,
          initialData: false,
          builder: (c, snapshot) {
            if (snapshot.data) {
              return FloatingActionButton(
                child: Icon(Icons.stop),
                onPressed: () =&gt; FlutterBlue.instance.stopScan(),
                backgroundColor: Colors.red,
              );
            } else {
              return FloatingActionButton(
                  child: Icon(Icons.search),
                  onPressed: () =&gt; FlutterBlue.instance
                          .startScan(
                              scanMode: ScanMode.balanced,
                              withServices: [
                                Guid(&quot;6E400001-B5A3-F393-E0A9-E50E24DCCA9E&quot;)
                              ], //FIXME check the other ways where &quot;.startScan&quot; is implemented
                              timeout: Duration(seconds: 4))
                          .catchError((error) {
                        print(&quot;error starting scan $error&quot;);
                      }));
            }
          },
        ),
      );
    }
  }
  
  class DeviceScreen extends StatelessWidget {
    const DeviceScreen({Key key, this.device}) : super(key: key);
  
    final BluetoothDevice device;
  
    List&lt;Widget&gt; _buildServiceTiles(List&lt;BluetoothService&gt; services) {
      return services
          .map(
            (s) =&gt; ServiceTile(
              service: s,
              characteristicTiles: s.characteristics
                  .map(
                    (c) =&gt; CharacteristicTile(
                      characteristic: c,
                      onReadPressed: () =&gt; c.read(),
                      onWritePressed: () =&gt; c.write([13, 24]),
                      onNotificationPressed: () =&gt;
                          c.setNotifyValue(!c.isNotifying),
                      descriptorTiles: c.descriptors
                          .map(
                            (d) =&gt; DescriptorTile(
                              descriptor: d,
                              onReadPressed: () =&gt; d.read(),
                              onWritePressed: () =&gt; d.write([11, 12]),
                            ),
                          )
                          .toList(),
                    ),
                  )
                  .toList(),
            ),
          )
          .toList();
    }
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text(device.name),
          actions: &lt;Widget&gt;[
            StreamBuilder&lt;BluetoothDeviceState&gt;(
              stream: device.state,
              initialData: BluetoothDeviceState.connecting,
              builder: (c, snapshot) {
                VoidCallback onPressed;
                String text;
                switch (snapshot.data) {
                  case BluetoothDeviceState.connected:
                    onPressed = () =&gt; device.disconnect();
                    text = 'DISCONNECT';
                    break;
                  case BluetoothDeviceState.disconnected:
                    onPressed = () =&gt; device.connect();
                    text = 'CONNECT';
                    break;
                  default:
                    onPressed = null;
                    text = snapshot.data.toString().substring(21).toUpperCase();
                    break;
                }
                return FlatButton(
                    onPressed: onPressed,
                    child: Text(
                      text,
                      style: Theme.of(context)
                          .primaryTextTheme
                          .button
                          .copyWith(color: Colors.white),
                    ));
              },
            )
          ],
        ),
        body: SingleChildScrollView(
          child: Column(
            children: &lt;Widget&gt;[
              StreamBuilder&lt;BluetoothDeviceState&gt;(
                stream: device.state,
                initialData: BluetoothDeviceState.connecting,
                builder: (c, snapshot) =&gt; ListTile(
                  leading: (snapshot.data == BluetoothDeviceState.connected)
                      ? Icon(Icons.bluetooth_connected)
                      : Icon(Icons.bluetooth_disabled),
                  title: Text(
                      'Device is ${snapshot.data.toString().split('.')[1]}.'),
                  subtitle: Text('${device.id}'),
                  trailing: StreamBuilder&lt;bool&gt;(
                    stream: device.isDiscoveringServices,
                    initialData: false,
                    builder: (c, snapshot) =&gt; IndexedStack(
                      index: snapshot.data ? 1 : 0,
                      children: &lt;Widget&gt;[
                        IconButton(
                          icon: Icon(Icons.refresh),
                          onPressed: () =&gt; device.discoverServices(),
                        ),
                        IconButton(
                          icon: SizedBox(
                            child: CircularProgressIndicator(
                              valueColor: AlwaysStoppedAnimation(Colors.grey),
                            ),
                            width: 18.0,
                            height: 18.0,
                          ),
                          onPressed: null,
                        )
                      ],
                    ),
                  ),
                ),
              ),
              StreamBuilder&lt;int&gt;(
                stream: device.mtu,
                initialData: 0,
                builder: (c, snapshot) =&gt; ListTile(
                  title: Text('MTU Size'),
                  subtitle: Text('${snapshot.data} bytes'),
                  trailing: IconButton(
                    icon: Icon(Icons.edit),
                    onPressed: () =&gt; device.requestMtu(223),
                  ),
                ),
              ),
              StreamBuilder&lt;List&lt;BluetoothService&gt;&gt;(
                stream: device.services,
                initialData: [],
                builder: (c, snapshot) {
                  return Column(
                    children: _buildServiceTiles(snapshot.data),
                  );
                },
              ),
            ],
          ),
        ),
      );
    }
  }
  
  </code></pre>