Arduino and Node.js via Serial port

Reading from the serial port in arduino is one of the most common things to do, and yet it’s not always easy to find good/simple documentation and/or articles about it. In this case, I’ll focus on one simple scenario, communicating with a node.js process via the serial port.

One of the reasons why you might want to do this, is either to communicate to the arduino from your local computer, or perhaps to communicate from a raspberry pi with a USB cable. While Arduino has proven to be an amazing board with very powerful capabilities, there are some complex tasks that for DIY’ers can be daunting and unnecessary, for example anything ML related, image processing, complex communications (www, websockets, SSL, etc…), etc…

So, if you find yourself wondering how to “talk” to your arduino in the most simple way, USB cable and Serial might as well be what you need. (If you are looking to connec to your Arduino via serial to do complex stuff, I urge you to check firmata.js).

Writing to serial in Arduino, reading from node.js

Writing to the serial port in Arduino is actually quite trivial. If you ever saw any arduino samples, this will look awfully familiar:

void setup() {
Serial.begin(9600); // Starts the serial communication
}
void loop() {
Serial.println("hello from arduino!");
delay(1000);
}

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. The code for this is super simple as well:

const SerialPort = require('serialport');
const Readline = require('@serialport/parser-readline');
const port = new SerialPort('/dev/ttyACM0', { baudRate: 9600 });
const parser = port.pipe(new Readline({ delimiter: '\n' }));
// Read the port data
port.on("open", () => {
console.log('serial port open');
});
parser.on('data', data =>{
console.log('got word from arduino:', data);
});

A few things to notice from this snipet:

  • In order to use serialport you have to npm install serialport it.
  • You will have to find the name of the serial port in your computer. An easy way to do this is from Arduino IDE, tools > port menu.
  • Make sure to use the same baud rate as the arduino (defined in setup function).
  • Since we used Serial.println (print line), we used the \n delimiter in the Readline parser.
  • Notice how we subscribe to the data event on the parser, and not the port directly.

Reading from the serial in arduino

Reading from the arduino is where things become slightly more fuzzy. In our case, since we control the other end of the serial port, we control how the data will be sent.

In order to simplify the process considerably, I strongly recommend you:

  1. define a max length for your messages.
  2. define a 1 character long message separator. ie: ‘\n’

Sending a message from node.js as you might expect is very simple:

  port.write('hello from node\n', (err) => {
if (err) {
return console.log('Error on write: ', err.message);
}
console.log('message written');
});

Due to the fact that we defined a max length for messages, we can define a variable “data” to reserve the space in memory.

const byte DATA_MAX_SIZE = 32;
char data[DATA_MAX_SIZE]; // an array to store the received data

The function for reading from the serial port becomes pretty simple:

void receiveData() {
static char endMarker = '\n'; // message separator
char receivedChar; // read char from serial port
int ndx = 0; // current index of data buffer
  // clean data buffer
memset(data, 32, sizeof(data));
  // read while we have data available and we are
// still receiving the same message.
while(Serial.available() > 0) {
    receivedChar = Serial.read();
    if (receivedChar == endMarker) {
data[ndx] = '\0'; // end current message
return;
}
    // looks like a valid message char, so append it and
// increment our index
data[ndx] = receivedChar;
ndx++;
    // if the message is larger than our max size then
// stop receiving and clear the data buffer. this will
// most likely cause the next part of the message
// to be truncated as well, but hopefully when you
// parse the message, you'll be able to tell that it's
// not a valid message.
if (ndx >= DATA_MAX_SIZE) {
break;
}
}
  // no more available bytes to read from serial and we
// did not receive the separato. it's an incomplete message!
Serial.println("error: incomplete message");
Serial.println(data);
memset(data, 32, sizeof(data));
}

For as long as you don’t send messages larger than DATA_MAX_SIZE, you should be fine. Like the comment in the code suggests, make sure that you parse your incoming messages in a robust way.

Notice the use of memset to "clean" the data buffer.

Notice that anything you print using Serial.println will be received by the node.js process so, make sure to parse your messages accordingly in the node.js code as well!