pezi / dart_periphery

dart_periphery is a Dart port of the native c-periphery library
BSD 3-Clause "New" or "Revised" License
36 stars 9 forks source link

Receive data continuously / listen for incoming data for Serial #14

Open riorizki opened 1 year ago

riorizki commented 1 year ago

Hi I'm trying to use this library to receive data from serial (raspberry) how can i listen to incoming data? does it use poll data inside a timer like this?

timer = Timer.periodic(const Duration(milliseconds: 100), (timer) {
      var poll = s.poll(0);
      if (poll) {
        var event = s.read(256, 100);
        print('payload = $event');
        payload.add(event.toString());
        _controller.animateTo(
          _controller.position.maxScrollExtent,
          duration: const Duration(milliseconds: 100),
          curve: Curves.fastOutSlowIn,
        );
        setState(() {});
});

is using a timer like this good ? or is there another more efficient way?

Thank you for your help

eximius313 commented 1 year ago

A great example of listening to serial events is implemented in libserialport library:

import 'package:libserialport/libserialport.dart';

final name = SerialPort.availablePorts.first;
final port = SerialPort(name);
if (!port.openReadWrite()) {
  print(SerialPort.lastError);
  exit(-1);
}

port.write(/* ... */);

final reader = SerialPortReader(port);
reader.stream.listen((data) {
  print('received: $data');
});

It's a very nice soluton, so I hope that something simmilar could be implemented in dart_periphery

Same solution could be also developed for GPIO events. Honestly - I have no idea how to listen to events based on this example, but there is a fantastic library (flutter_gpiod) that provides Stream<SignalEvent> stream = line.onEvent; method in GpioLine class and this approach could be very handy in dart_periphery as well

eximius313 commented 1 year ago

@pezi , do you consider possibility to improve Serial and GPIO handling via Stream?

pezi commented 1 year ago

Yes - I will write an example using an dart isolate process which passes the serial data via stream. The one thread model of dart/flutter seems to conflicts with polling hardware code - e.g. read serial data with a timeout. I tried at the beginning of this project to encapsulate polling code as a future - didn't worked.

eximius313 commented 1 year ago

The one thread model of dart/flutter seems to conflicts with polling hardware code - e.g. read serial data with a timeout. I tried at the beginning of this project to encapsulate polling code as a future - didn't worked.

How about using the same mechanisms as libserialport and flutter_gpiod do for Serial and GPIO? I've tested them quite intensively and they're very reliable

pezi commented 1 year ago

Yes I will take a look on these libs on a second step - but as a first step, I want to write/test general code for streaming sensor (Serial/I2C,etc) data with this lib. I wrote a a little flutter-pi app for the the bme680 sensor, air measurement. The demo works, but the code still retrieves data by polling - the i2c bus/sensor is fast enough - there are no visual glichtes in the UI - but I want to change the sensor reading to a stream model.

eximius313 commented 1 year ago

Awesome! I'm looking forward to see the results!

pezi commented 1 year ago

I started with the first tests - I adapted an isolate example for a long running task: gist code snippet

But this code has the problem, the possibility to stop the polling thread in a clean manner. An isolate can be killed per API, but the hardware ressources are still not closed. The ReceivePort/SendPort isolate communication works like a ping pong - send - wait for a message - reponse. But every waiting code for a termination signal stalls the running polling loop.

Next week I am in poland - familiy business , having more time, I will rethink this problem, and I am honest, I am not a expert for the dart concurrency model.

eximius313 commented 1 year ago

libserialport uses this approach:

void _startRead() {
    _receiver = ReceivePort();
    _receiver!.listen((data) {
      if (data is SerialPortError) {
        _controller.addError(data);
      } else if (data is Uint8List) {
        _controller.add(data);
      }
    });
    final args = _SerialPortReaderArgs(
      address: _port.address,
      timeout: _timeout,
      sendPort: _receiver!.sendPort,
    );
    Isolate.spawn(
      _waitRead,
      args,
      debugName: toString(),
    ).then((value) => _isolate = value);
  }

https://github.com/jpnurmi/libserialport.dart/blob/main/lib/src/reader.dart

pezi commented 1 year ago

I played arround with reflection and annotation to pass generic code into a the scope of an isolate. The isolate class calls annotated static methods to pass data to an event stream.

isolate_helper

pezi commented 1 year ago

Real world example Cozir CO2 Sensor

Next step - flutter_pi test application.