I tried to make a 2 way communication between my Arduino and a PC(running linux mint) using NodeJS:
My Arduino code:
void setup() {
Serial.begin(9600);
}
void loop() {
if(Serial.available()) Serial.println(Serial.readString()+"\r");
}
My NodeJS code:
var SerialPort = require('serialport');
var port = new SerialPort('/dev/ttyUSB0',{
baudRate: 9600,
parity: 'none',
stopBits: 1,
flowControl: false,
parser: SerialPort.parsers.readline('\r\n')
});
port.on('open',function(){
setInterval(() => port.write('Test'),1000);
});
port.on('data',(data) => console.log(data.toString()));
There is possible encoding error, but I cannot seem to find and fix this. Thanks in advance
2 Answers 2
You don't need to revert to sending single bytes. Line oriented protocols work well with both Arduino and Node.js. The only difficulty is that you have to decide what line terminators you want to use, and then consistently use them.
I tend to use CR for the PC → Arduino stream and CRLF for Arduino
→ PC. Using different line terminators is kind of odd, but this
convention plays well with terminal emulators: the emulator usually
sends CR when you hit <Enter>
, and expects CRLF in return. Also,
Arduino's Print::println()
sends CRLF.
Here is a simple test demonstrating this convention. Node.js side:
var SerialPort = require('serialport'),
port = new SerialPort('/dev/ttyACM0', {
baudRate: 9600,
parser: SerialPort.parsers.readline('\r\n')
});
port.on('open', () => setInterval(() => port.write('Test\r'), 1000));
port.on('data', console.log);
Notice the different line terminators in readline()
and write()
.
The Arduino side:
void setup() {
Serial.begin(9600);
}
void loop() {
static char buffer[128];
static size_t pos;
if (Serial.available()) {
char c = Serial.read();
if (c == '\r') { // line terminator
buffer[pos] = '0円'; // terminate the string
Serial.print(F("Arduino received: "));
Serial.println(buffer); // echo received message
pos = 0; // reset buffer position
} else if (pos < sizeof buffer - 1) { // regular character
buffer[pos++] = c; // buffer it
}
}
}
Here you may notice that Arduino has no equivalent of
parsers.readline()
, so I had to implement it (comparing the incoming
character to '\r'
). Note that explicit buffering of the incoming
message is a standard way of handling line-oriented protocols on
Arduino. Stream::readString()
does not work well for this purpose, as
it waits for incoming bytes until it times out.
Stream::readStringUntil()
may work better, but I prefer to avoid it
just because it returns a String
, which relies on heap allocation.
-
Well, this code works great, but you forget that the content of the "previous" buffer stays in the array, which results in results like: "Arduino received: Testest"Berrigan– Berrigan2017年04月20日 19:51:13 +00:00Commented Apr 20, 2017 at 19:51
-
@Berrigan: Oops! I forgot to properly terminate the string. Fixed. Now it works. The previous content of the buffer is now irrelevant, as only the contents before the first
'0円'
is considered to be part of the "string". After a complete string is received, the buffer is reset by the statementpos = 0;
.Edgar Bonet– Edgar Bonet2017年04月20日 20:02:05 +00:00Commented Apr 20, 2017 at 20:02
Ok, I solved the issue by using pure bytes, and then casting the response to the string in nodeJS, I post the codes in case anyone needs them:
var SerialPort = require('serialport');
var port = new SerialPort('/dev/ttyUSB0',{
baudRate: 9600,
parity: 'none',
stopBits: 1,
flowControl: false,
// parser: SerialPort.parsers.readline('\r\n')
});
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
port.on('open',() => {
setInterval(() => port.write('Test'),500);
});
port.on('data',(data) => {
console.log(ab2str(data));
});
And the Arduino code:
void setup() {
Serial.begin(9600);
}
void loop() {
if(Serial.available()) Serial.write(Serial.read());
}
Serial.println()
plays well with serialport'sreadline("\r\n")
.