The solution was simple, all I was missing was the return at the end of the command being sent. I was able to operate the SMAC Controller with the following command:

0x20 W 0x012C04 20\r

The full code used is:

const port = new SerialPort(SMAC, {
    baudRate: 115200,
    dataBits: 8,
    stopBits: 1,
    parity: "none",
}, (err) => console.log(err))

if(!port.isOpen) port.open()

port.on('open', () => {
    console.log("Port opened successfully")

    const macro = "0x20 W 0x012C04 20\r"

    // Call Macro 20
    port.write(macro, (err) => {
        if(err) throw err;
        console.log("Write to port successful")
        port.close()
    })
})
Answer from Justin on Stack Overflow
🌐
Serialport
serialport.io › serialport usage
SerialPort Usage | Node SerialPort
November 8, 2024 - const { SerialPort } = require('serialport') const port = new SerialPort({ path: '/dev/tty-usbserial1', baudRate: 57600 }) port.write('main screen turn on', function(err) { if (err) { return console.log('Error on write: ', err.message) } console.log('message written') }) // Open errors will be emitted as an error event port.on('error', function(err) { console.log('Error: ', err.message) })
🌐
Serialport
serialport.io › 📦 stream
📦 stream | Node SerialPort
November 8, 2024 - The write operation is non-blocking. When it returns, data might still not have been written to the serial port. See drain(). Some devices, like the Arduino, reset when you open a connection to them. In such cases, immediately writing to the device will cause transmitted data to be lost as ...
🌐
Stack Overflow
stackoverflow.com › questions › 41026744 › read-serial-port-after-writing-using-node-js
Read Serial Port After Writing using Node.js - Stack Overflow
} }); port.write('#01VER\r', function(err) { if(err) console.log('Write error') else { // HOW TO READ RESPONSE FROM DEVICE? } }); }); ... I think this is a better way to do it. By function calling, first create your function of reading and writing: var serialport = require("serialport"); var SerialPort = serialport.SerialPort; var sp = new SerialPort("/dev/ttyACM0", { baudrate: 9600, parser: serialport.parsers.readline("\n") }); function write() //for writing { sp.on('data', function (data) { sp.write("Write your data here"); }); } function read () // for reading { sp.on('data', function(data) { console.log(data); }); } sp.on('open', function() { // execute your functions write(); read(); });
Top answer
1 of 2
1

I think I got it figured out from the github (https://github.com/voodootikigod/node-serialport page... basically it looks like I was missing the "open" event as shown below:

serialPort.on("open", function () {
  console.log("open");
  serialPort.on("data", function(data) {
    console.log("data received: " + data);
  });  
  serialPort.write("SYST:ADDR?\n", function(err, results) {
    console.log("err: " + err);
    console.log("results: " + results);
  });  
});
2 of 2
1

Here is another approach which works very well and allows for dynamic addressing of a specific serial device. In my case I am only interested in connecting to the Numato device connected to our integrated system which is why I have the conditional logic in the list callback.

 exports.testSerial = function(data) {
    serialPort.list(function(err, ports) {

      var port = {};

      for(var i = 0; i < ports.length; i++) {
        try {
          if(typeof ports[i].manufacturer != 'undefined' && ports[i].manufacturer.includes("Numato")) {
            port = ports[i];
          }
        } catch(err) {
          console.dir(err);
        }
      }

      // the port will be opened via the constructor of this call
      var numato = new serial(port.comName, {baudrate : 19200}, function(err) {
        if(err) {
          return console.dir(err);
        }

        // by having the write call within the callback you can access it directly w/o using .on()
        numato.write('relay ' + data.state + ' ' + data.channel + '\r', function(err) {
          if(err) {
            console.dir('error writing');
            console.dir(err);
          }
          console.dir('serial message written');
          numato.close();
        });
      });

      return true;

    });
 }

Hope this helps someone in the future! For reference this is with library version 4.0.7.

🌐
GitHub
github.com › serialport › node-serialport › issues › 1758
Sequential writing/reading · Issue #1758 · serialport/node-serialport
December 30, 2018 - So // make sure to get it out of there let Package = this.SerialPort._pool.slice( 0, L ); console.log( 'Package: ', Package ); }, ) .catch( err => { console.error( 'Error 1', err ); console.error( 'Closing the connection' ); this.SerialPort.close( null, null ); }, ) ; }, ) .then( () => { this.SerialPort._write( Buffer.from( 'AA000002', 'hex' ) ) .then( async r => { // Read the header, which contains the length of the package await this.SerialPort._read( 2 ); console.log( 'Header: ', this.SerialPort._pool ); let L = this.SerialPort._pool[1] - 1; // Read the package await this.SerialPort._read( L ); // Note: data is stored in "this.SerialPort._pool", but only until you do another read...
Author   serialport
🌐
npm
npmjs.com › package › serialport
serialport - npm
Better yet, program them!. Latest version: 13.0.0, last published: a year ago. Start using serialport in your project by running `npm i serialport`. There are 5260 other projects in the ...
      » npm install serialport
    
Published   Dec 24, 2024
Version   13.0.0
Top answer
1 of 2
4

I figured it out. I put a serial port monitoring application on to see what was happening.

The final answer was

A. When using an array or buffer to pass to the .write() method, it needs to be either a decimal character code (ie. 30 for "0" or 13 for carriage return) or a hex code (ie. 0x30 for "0" or 0x0D for carriage return) for each item in the array. I didn't try unicode or anything else. But see below my thoughts on that.

B. It turns out that in my example code

sp.on("open", function () {
    console.log(comPort + ' is open');
    sp.write(0x80);
    sp.write('123456\r');
});

I needed to be sending the 0x80 as a buffer/array instead. so sp.write([0x80]);, without it sent lots of 00 00 00...s However the LED display vendor's documentation was incorrect as it needed a \r\n at the end and not just a \r.

C. (a bit of a "duh" moment) was simply that Javascript will attempt to convert anything concatenated with a string to a string. So concatenating 0x80 + '123456\r' turns into '128123456\r'.

D. Passing unicode, like sp.write('\u0080') ends up outputing double characters, I'm assuming since unicode is 2 bytes per character, and the string is being treated/expecting single byte encoding. Javascript strings are UTF-16, so I'm guessing the node serialport module doesn't handle it since RS-232 uses 8 bits or less per character which falls into the typical ASCII char set.

2 of 2
0

I tested this on a console, not over a serial port, but I think it should work for you.

There are two options: 1) you can escape into Unicode or 2) you can use the String.fromCharCode function (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode)

Escaping into Unicode is pretty simple, just use \uXXXX where XXXX is the hex representation of your number

sp.write('\u0080123456\r');

Or you can use String.fromCharCode:

sp.write(String.fromCharCode(128) + '123456\r');
Find elsewhere
🌐
GitHub
github.com › serialport › node-serialport
GitHub - serialport/node-serialport: Access serial ports with JavaScript. Linux, OSX and Windows. Welcome your robotic JavaScript overlords. Better yet, program them! · GitHub
Access serial ports with JavaScript. Linux, OSX and Windows. Welcome your robotic JavaScript overlords. Better yet, program them! - serialport/node-serialport
Starred by 6K users
Forked by 1K users
Languages   TypeScript 85.8% | JavaScript 13.2%
🌐
Serialport
serialport.io
Home | Node SerialPort
Nodebots uses SerialPort as the bridge between your javascript and the firmware on thousands of devices from Arduinos to drones.
🌐
GitHub
github.com › DeekyJay › serialport
GitHub - DeekyJay/serialport: Node.js package to access serial ports for reading and writing. Welcome your robotic JavaScript overlords. Better yet, program them! · GitHub
Request a number of bytes from the SerialPort. This function is similar to node's fs.read. Kind: instance method of BaseBinding Returns: Promise - Resolves with the number of bytes read after a read operation. Throws: TypeError When given invalid arguments a TypeError will be thrown. Params: integer offset - is the offset in the buffer to start writing at. ... Accepts a Buffer object. ... Write a number of bytes to the SerialPort This will only be called when there isn't a pending write operation.
Author   DeekyJay
🌐
GitHub
github.com › serialport › node-serialport › issues › 1826
SerialPort write not working · Issue #1826 · serialport/node-serialport
Data rate: ${port.baudRate}`)); port.write('0\n', (err) => { if (err) return console.log('Error on write: ', err.message); console.log('message written') });``` // Code ```void setup() { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(9600); } void loop() { if(Serial.available()){ switch(Serial.read()){ case '0': digitalWrite(LED_BUILTIN, LOW); break; case '1': digitalWrite(LED_BUILTIN, HIGH); break; default: break; } } }``` - SerialPort@ 7.1.4 - Node.js 10.4.2 - Windows - Hardware and chipset Arduino Mega 2560 ·
Author   serialport
🌐
Serialport
serialport.io › docs › 7.x.x › api-serialport
SerialPort | Node SerialPort
const serialport = new SerialPort(path) serialport.write('ROBOT POWER ON')
🌐
Serialport
serialport.io › 📦 serialport
📦 serialport | Node SerialPort
November 8, 2024 - import { SerialPort } from 'serialport' const serialport = new SerialPort({ path: '/dev/example', baudRate: 9600 }) serialport.write('ROBOT POWER ON')
Top answer
1 of 2
2

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);
    })
2 of 2
1

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.

🌐
Medium
medium.com › @pkl9231 › serial-communication-between-node-js-and-arduno-read-and-write-data-2a712e07d337
Serial Communication between node js and Arduino (read and write data) | by Purushotam Kumar | Medium
December 11, 2021 - From node.js there are a number of different approaches, but the one that I liked best because of how simple it is, is to use serialport with parser-readline. It's very convenient to know how/when to split messages, and parser-readline will do just that. Whenever parser-readline finds the delimiter you configure, it will emit the data received as a string.
🌐
Stack Overflow
stackoverflow.com › questions › 63253111 › sending-bytes-with-serialport-nodejs
node.js - Sending bytes with SerialPort Nodejs - Stack Overflow
August 4, 2020 - const SerialPort = require("serialport"); var port = new SerialPort( "COM4", { baudRate: 2400, databits: 8, parity: "none", }, false ); command = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13]; for (var i = 0; i < command.length; i++) { port.write(command[i], function (err) { if (err) { return console.log("Error on write: ", err.message); } console.log(`Sent ${command[i]}`); }); } port.on("data", (line) => console.log(line)); node.js ·
🌐
Node-RED
flows.nodered.org › node › node-red-node-serialport
node-red-node-serialport (node) - Node-RED
In order to control the communication, send a msg.payload to the control node. { "serialport": "/dev/ttyUSB0", "serialbaud": 115200, "databits": 8, "parity": "none", "stopbits": 1, "enabled": true }