Tim
Tim

Reputation: 3803

Nodejs map serial port write to receive data

I am currently using node-serialport module for serial port communication. I will send a command ATEC and it will respond with ECHO.

However, this process of sending and receiving data is async(after i send the data, i will not know when the data will arrive in the data event), the example code is below:

//Register the data event from the serial port
port.on('data', (data) => {
    console.log(data);
});

//Send data using serialport
port.write('ATEC');

Is there anyway I could write it in this way?

//When i send the command, I could receive the data
port.write('ATEC').then((data)=> {
    console.log(data);
});

Is this possible to achieve?

In http communication using request client, we could do something like

request.get('http:\\google.com')
    .on('response', (res) => {
        console.log(res);
    });

I want to replicate the same behaviour using serialport

Upvotes: 2

Views: 4590

Answers (2)

Tim
Tim

Reputation: 3803

I wrap a promise in the serial data receive

function sendSync(port, src) {
    return new Promise((resolve, reject) => {
        port.write(src);
        port.once('data', (data) => {
            resolve(data.toString());
        });

        port.once('error', (err) => {
            reject(err);
        });
    });
}

Please take note, the event is using once instead of on to prevent event from stacking (please check the comments below for more information - thanks @DKebler for spotting it)

Then, I could write the code in sync as below

sendSync(port, 'AThello\n').then((data) => {
    //receive data
});

sendSync(port, 'ATecho\n').then((data) => {
    //receive data
});

or I could use a generator, using co package

 co(function* () {
        const echo = yield sendSync(port, 'echo\n');
        const hello = yield sendSync(port, 'hello 123\n');

        return [echo, hello]
    }).then((result) => {
        console.log(result)
    }).catch((err) => {
        console.error(err);
    })

Upvotes: 2

qDot
qDot

Reputation: 430

We have a similar problem in a project I'm working on. Needed a synchronous send/receive loop for serial, and the serialport package makes that kinda weird.

Our solution is to make some sort of queue of functions/promises/generators/etc (depends on your architecture) that the serial port "data" event services. Every time you write something, put a function/promise/etc into the queue.

Let's assume you're just throwing functions into the queue. When the "data" event is fired, it sends the currently aggregated receive buffer as a parameter into the first element of the queue, which can see if it contains all of the data it needs, and if so, does something with it, and removes itself from the queue somehow.

This allows you to handle multiple different kinds of architecture (callback/promise/coroutine/etc) with the same basic mechanism.

As an added bonus: If you have full control of both sides of the protocol, you can add a "\n" to the end of those strings and then use serialport's "readline" parser, so you'll only get data events on whole strings. Might make things a bit easier than constantly checking input validity if it comes in pieces.

Update:

And now that code has been finished and tested (see the ET312 module in http://github.com/metafetish/buttshock-js), here's how I do it:

function writeAndExpect(data, length) {
  return new Promise((resolve, reject) => {
    const buffer = new Buffer(length);
    this._port.write(data, (error) => {
      if (error) {
        reject(error);
        return;
      }
    });
    let offset = 0;
    let handler = (d) => {
      try {
        Uint8Array.from(d).forEach(byte => buffer.writeUInt8(byte, offset));
        offset += d.length;
      } catch (err) {
        reject(err);
        return;
      }
      if (offset === length) {
        resolve(buffer);
        this._port.removeListener("data", handler);
      };
    };
    this._port.on("data", handler);
  });
}

The above function takes a list of uint8s, and an expected amount of data to get back, returns a promise. We write the data, and then set ourselves up as the "data" event handler. We use that to read until we get the amount of data we expect, then resolve the promise, remove ourselves as a "data" listener (this is important, otherwise you'll stack handlers!), and finish.

This code is very specific to my needs, and won't handle cases other than very strict send/receive pairs with known parameters, but it might give you an idea to start with.

Upvotes: 1

Related Questions