4

I am working on a nodejs project that utilizes a serial port for communicating with a microcontroller. I can get everything to work using serialport but I'm struggling to determine the best method for handling received data.

Here's the basic program I'm testing with:

const serialPort = require('serialport');
const delimiter = require('@serialport/parser-delimiter')

const readline = require('readline');
const EventEmitter = require('events');

const rl = readline.createInterface({
  input: process.stdin, 
  output: process.stdout
});

rl.setPrompt('> ');
rl.prompt();

const uart = new serialPort('path-to-port', {
  baudRate: 9600
});

const parser = uart.pipe(new delimiter({ delimiter: '\n'}));

const event = new EventEmitter();

parser.on('data', (data) => {
  event.emit('dataReady', data);
});

rl.on('line', (input) => {
  if(input === 'close'){
    console.log('Bye');
    process.exit();
  }

  uart.write(input + '\n', (err) => {
    if (err) {
      console.log('uart write error');
    }
  });

  console.log('Waiting on data...');
  readAsync().then((data) => {
    console.log(' ->', data.toString());
    rl.prompt();
  });


});

const readAsync = () => {
  const promise = new Promise((resolve, reject) => {
    event.once('dataReady', (data) => {
      resolve(data);
    });
  });

  return promise;
}

I would like to return a promise that will be fulfilled once data has been received. Currently I'm emitting an event and using a listener to fulfill the promise. Is this the best method or is there a better option?

UPDATE

I plan to export a function for writing to the serial port that fulfills a promise once data is received.

Example:

module.exports.write = (cmd) => {
  uart.write(input + '\n', (err) => {
    if (err) {
      console.log('uart write error');
    }
  });
  return readAsync();
}
Dillon Wiggins
  • 193
  • 1
  • 9

1 Answers1

1

For something that streams an arbitrary amount of data (i.e., the parser object may have many data events), you probably don't want to use a promise. Once a promise is resolved, it is resolved for good (i.e., you can only put data into it once -- which it looks like you know by the way you have set up readAsync). You might want to instead try something called an Observable. You can connect the observable to the stream in question and it will queue them up for later transformations or use (you can think of it as a promise that can accept multiple data items). Here is a good SO answer explaining how streams (what you are currently with) and observables can interoperate for more programmatic flexibility.

Unfortunately, Node.js does not have an Observable utility built-in, but there is a canonical and battle-tested solution, RxJS.

Jclangst
  • 1,274
  • 7
  • 11
  • Interesting, I did not know about Observables. No data should be received without data being sent first. Essentially I send a command and receive a response. Since received data is at a known time should I just stick with the single event? See my update for my intended use case. – Dillon Wiggins Sep 05 '18 at 15:03
  • If you are just ping-ponging back and forth, you might be able to just stick with a Promise, but I just want to confirm that it is NOT possible that you could receive multiple `data` events from the uart after a single `write` event. If this could happen, you'd want to buffer all of those data events together, right? And then only resolve the promise once the data has all been read? – Jclangst Sep 05 '18 at 15:11
  • On second look, even if the ping-pong would work, based on everything that I am seeing in your code, I'd wager you still want to use Observables to take advantage of the "reactive" paradigm. You "react" to every newline character received by doing some action (printing to the console, executing an action, etc.). – Jclangst Sep 05 '18 at 15:14
  • Yes, I should not receive data at an unexpected time. I format the data coming from the MCU so I may add a flag for broadcasted vs response data then handle it seperately in the parser.on data. Thanks! – Dillon Wiggins Sep 05 '18 at 15:14
  • I will work on implementing Observables and see if there is a difference in performance and reliability. Always nice to learn something new. Thanks for your input. – Dillon Wiggins Sep 05 '18 at 15:22