1

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

  • What error do you get? – Edgar Bonet Apr 20 '17 at 15:39
  • Basicly it outputs "TestTestTest" with random length – Berrigan Apr 20 '17 at 15:40
  • What's more if I use Arduino code to send the data like Serial.println("Test"), and use readline('\n') I get correct results – Berrigan Apr 20 '17 at 15:41
  • Weird. For me, Arduino's Serial.println() plays well with serialport's readline("\r\n"). – Edgar Bonet Apr 20 '17 at 15:43
  • Is this your code, or the above? – Berrigan Apr 20 '17 at 17:01
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 Apr 20 '17 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 statement pos = 0;. – Edgar Bonet Apr 20 '17 at 20:02
0

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());
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.