Joe
Joe

Reputation: 909

Node.js | How to send and receive one byte to a device through serial port?

I am trying to write Node.js script that uses the serialport npm package to read and write data to COM5 serial port, which is connected to a device using RS-232 cable. This device is automatically transmitting the data it has.

To retrieve the data stored inside the device, I need to send, first, one byte 0x80 (uInt8) to the device and expect to receive 81 as a hex number from the device. Second, I need to send another byte 0x81 (uInt8) and then expect to receive all stored data (around 130000 bytes) as lines.

There is no way how to retrieve the whole stored data only by two bytes, one after another. Communication with the device can only be done by bytes.

I tried the following code:

import SerialPort from 'serialport';

const sp = new SerialPort.SerialPort({path: "COM5", baudRate: 115200});

const buff = Buffer.allocUnsafe(1);
buff.writeUInt8(0x80, 0);

sp.write(buff, function (err){
    if (err){
        return console.log("Error on write: ", err.message);
    }
    console.log("Port.write: ", buff);
});

sp.on('readable', function () {
    var arr = new Uint8Array(sp._pool);
    console.log('Data:', arr);
});

When running my script (using node myScript.mjs), I see the following:

PortWrite:  <Buffer 80>
Data: Uint8Array(65536) [
    0,  30, 167, 106, 127,   2,  0,  0,  32,  31, 170, 106,
  127,   2,   0,   0,   0, 128,  0,  0,   0,   0,   0,   0,
   16,  84,  18,  76, 196,   0,  0,  0, 100,  84,  18,  76,
  196,   0,   0,   0, 184,  84, 18, 76, 196,   0,   0,   0,
  160,  84,  18,  76, 196,   0,  0,  0, 244,  84,  18,  76,
  196,   0,   0,   0,  72,  85, 18, 76, 196,   0,   0,   0,
  240, 120,  18,  76, 196,   0,  0,  0,  68, 121,  18,  76,
  196,   0,   0,   0, 152, 121, 18, 76, 196,   0,   0,   0,
  240, 120,  18,  76,
  ... 65436 more items
]

The program doesn't exit until I hit Ctrl+c. I am not sure what ps._pool does but it seems the output is not correct but looks like giving something at random.

I am new to Node.js. I am also not sure how to add an argument TimeOut of 10 seconds in the sp object.

I tried to use Matlab, for testing, and I could retrieve the data successfully.

I tried Python too but didn't work for me - PySerial package is reading serial data before writing?.

Upvotes: 5

Views: 2130

Answers (1)

ug_
ug_

Reputation: 11440

I believe you are listening for your data incorrectly. SerialPort is a form of a stream called a duplex which are both readable and writable. To listen for new data on a stream it is common practice to listen for the data event.

Also worth noting in Javascript when variables start with an underscore, such as _pool it usually means its a private variable which isn't generally suppose to use. See this SO question for more info

Simply rewriting sp.on('readable', function () { part to be

sp.on('data', function(buffer) {
    console.log(buffer);
});

Should make you start seeing the data your expecting.


To test this I wrote up the scenario you outlined but only wrote 10,000 bytes after 0x81 to test.

Please don't take this as great way to work with streams in JS. This was my attempt at doing it "lightweight" and there's probably better ways to write this

const {SerialPort} = require('serialport');

const sp = new SerialPort({path: "COM2", baudRate: 115200});

function Deferred(data) {
    this.data = data;
    this.resolve = null;
    this.reject = null;
    this.reset = () => {
        this.promise = new Promise(function(resolve, reject) {
            this.resolve = resolve;
            this.reject = reject;
        }.bind(this));
    }

    this.reset();
}

async function run() {
    let readBuffer = Buffer.alloc(0);
    let nextReadDeferred = new Deferred();

    sp.on('data', function(newData) {
        readBuffer = Buffer.concat([readBuffer, newData], newData.length + readBuffer.length)
        console.log('Got new data: ' + newData.length + ' buffer length is ' + readBuffer.length);
        nextReadDeferred.resolve(readBuffer);
        nextReadDeferred.reset();
    });

    console.log('Start 0x80')
    sp.write(Buffer.from([0x80]), e => { if(e) throw e; });
    await nextReadDeferred.promise;
    if(0x81 === readBuffer[0]) {
        // clear the buffer in prep of getting our data
        readBuffer = Buffer.alloc(0);
        console.log('Send 0x81 to start data flow')
        sp.write(Buffer.from([0x81]), e => { if(e) throw e; });

        console.log('Starting to receive data. Not sure how to know when were done?');
        // Dont know what to do here, I dont think the serial port closes. Maybe we are supposed to just expect a duration
        // of no data transfer? I dont know a whole lot about COM communication
    } else {
        console.warn('Expected 0x81 but got', readBuffer)
    }
}

run();

Output:

$ node index.js
Start 0x80
Got new data: 1 buffer length is 1
Send 0x81 to start data flow
Starting to receive data. Not sure how to know when were done?
Got new data: 1024 buffer length is 1024
Got new data: 4096 buffer length is 5120
Got new data: 1024 buffer length is 6144
Got new data: 3856 buffer length is 10000

To test this I used eterlogic for a virtual com port backed with a TCP server and the following Node code to be my "device"

const s = require('net').Socket();
s.connect(5555, '127.0.0.1');

s.on('data', function(d){
    console.log('Got', d);
    if(d.equals(Buffer.from([0x80]))) {
        s.write(Buffer.from([0x81]));
    } else if(d.equals(Buffer.from([0x81]))) {
        s.write(Buffer.alloc(10000, 1));
    }
});

Upvotes: 1

Related Questions