Communicating with ESP32 via serial with serialports.js (node.js) - node.js

I have an esp32 board loaded with this software which is logging values about the number of detected Wi-Fi and Bluetooth devices detected via LoraWAN. In senddata.cpp it seems to be logging out the values that I need (though I'm not quite sure I understand how or where it is sending them via serial):
ESP_LOGD(TAG, "Sending count results: pax=%d / wifi=%d / ble=%d", count.pax, count.wifi_count, count.ble_count);
I set up a node.js app with the SerialPort.io library to be able to read data coming over serial. I've successfully identified the COM port on my PC that is receiving data, and I can log out the data buffer as follows:
const SerialPort = require("serialport").SerialPort;
const serialPort = new SerialPort({
path: "COM4",
baudRate: 9600,
autoOpen: false,
});
serialPort.open(function (error) {
if (error) {
console.log("failed to open: " + error);
} else {
console.log("serial port opened");
serialPort.on("data", function (data) {
// get buffered data and parse it to an utf-8 string
console.log(data);
data = data.toString("utf-8");
console.log(data);
});
serialPort.on("error", function (data) {
console.log("Error: " + data);
});
}
});
Which yields output in node.js as a buffer, e.g. <Buffer bc 08 AD>, but after the toString("utf-8") it is a bunch of gibberish. Clearly I am not encoding or decoding the serial output properly, but I'm not sure where to make adjustments. Does anyone know how I can get this serial output into the proper format to use in node.js?
--- Update Re: Questions ---
The board is a ttgo / lilygo lora32 - the library I used seems to say it supports both this board and communication over SPI. I am able to get readable data via the debug console with the platform.io extension for VSCode on Windows / Mac. I believe the baud is 9600, which was the only thing I seemed to need to specify on the serialports.io side.
I did receive this advice from the library author:
You need
a messagebuffer, to store the payload
a queue, as buffer for the serial data
a protocol, suitable for your application
1+2: see spislave.cpp (change the SPI transmit calls by serial port calls)
3: consider overhead and checksum, e.g. transfer the payload as byte array or UTF8 string, e.g. comma separated string with checksum, as used in NMEA.
Unfortunately I'm a bit out of my depth to make sense of that (though I'm working on it).
Also - the JavaScript code that has successfully worked via the things network uses to decode the payload from the board is here.

Does anyone know how I can get this serial output into the proper format to use in node.js?
It looks like many of the smaller/older boards this software interfaces with use baud 9600, but line 98 of main.cpp specifies a baud rate of 115200 for the debug messages:
// setup debug output or silence device
#if (VERBOSE)
Serial.begin(115200);
esp_log_level_set("*", ESP_LOG_VERBOSE);
I suspect switching to a baud rate of 115200 will help:
const serialPort = new SerialPort({
path: "COM4",
baudRate: 115200,
autoOpen: false,
});
If that doesn't do the trick, you can make sure the other serial parameters match those set in main.cpp, starting at line 394. UART0 is the one that emits the log messages on the ESP:
static void ble_spp_uart_init(void)
{
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_RTS,
.rx_flow_ctrl_thresh = 122,
.source_clk = UART_SCLK_DEFAULT,
};
...
Looking at the API, you could specify the other parameters like so:
const serialPort = new SerialPort({
PATH: "COM4"
baudRate: 115200,
databits: 8,
parity: false,
stopbits: 1,
});
though I'm not quite sure I understand how or where it is sending them via serial
The ESP_LOG functions are really just special wrappers around vprintf. vprintf normally writes to stdout by default but the ESP redirects that to a dedicated UART (serial port). Check out the source:
static vprintf_like_t s_log_print_func = &vprintf;
void esp_log_writev(esp_log_level_t level,
const char *tag,
const char *format,
va_list args)
{
if (!esp_log_impl_lock_timeout()) {
return;
}
esp_log_level_t level_for_tag = s_log_level_get_and_unlock(tag);
if (!should_output(level, level_for_tag)) {
return;
}
(*s_log_print_func)(format, args);
}
That UART handles the encoding and buffers required to transmit over the serial port.

You need to check following things :
Check if baud rates of both the esp32 and node.js are same - most of the time garbage data received on COM if COM port configuration is not done properly.
Need to check, what data you are expecting and what is the format -is it in text (utf8) format or some binary format ? Accordingly you need to parse the out put.
It would be clear once you post a sample data the esp32 is sending and the buffer you are receiving on Node.js code.

Related

when using serialport in Node.js I need to parse every byte without using a delimiter

I have a legacy device from which I need to receive serial data and it does not send a delimiter. Therefore I need to send every byte I receive to a function. My code is written in Javascript and I use node.js with the npm serialport package. I have tried the following code, but it still does not send the data to my serialHandler function unless a return (\r) is received.
I was expecting it to send each byte received to the serialHandler function without the need of a delimiter, but in my troubleshooting, it did not send anything to the function until it received the \r delimiter. It then parsed it by each byte as expected.
How can I send each byte received to the serialHandler function without using a delimiter?
node version 16.17.0
serialport version 10.5.0
Here is the code.
const {SerialPort} = require('serialport');
const { ByteLengthParser } = require('#serialport/parser-byte-length');
const port = new SerialPort({ //set serial port pins and baudrate
path: '/dev/ttyAMA1',
baudRate: 115200
});
const serialParser = port.pipe(new ByteLengthParser({ length: 1 }));
serialParser.on('data', serialHandler);
function serialHandler(serialChar){
//does stuff
}
I've figured out that it was actually the sending software I was testing with(Putty) that was causing my issues. It had local line editing forced on, causing it only to send data when you pressed Enter.

Nodejs serial port to tcp

Is there a way to establish a host to stream a serial connection over tcp using nodejs - I want to stream the sensor data from the iot device on my computer to a connected computer to a web server. streaming of the raw data is fine - the remote computer will process it. I was looking into net and serialport npm packages - but im unsure of how to marry the two...
Thanks!
Preparation
Pretty much each vendor or device has its own serial communication protocol. Usually these devices also use packets with headers, checksums, but each device does this in a different way.
The first question is really, to what extend you want to forward the packet headers and checksum information. You may want to translate incoming packets to events or perhaps already to some kind of JSON message.
Assuming that you just want to forward the data in raw format without any pre-processing, it is still valuable to determine where a packet starts and ends. When you flush data over TCP/IP it's best not to do so halfway one of those serial packets.
For instance, it could be that your device is a barcode scanner. Most barcode scanners send a CR (carriage return) at the end of each scan. It would make sense to actively read incoming bytes looking for a CR character. Then each time a CR character is noticed you flush your buffer of bytes.
But well, it isn't always a CR. Some devices package their data between STX (0x02) and ETX (0x03) characters. And there are some that send fixed-length packages (e.g. 12 bytes per message).
Just for clarity, you could end up sending your data every 100 bytes while a message is actually 12 bytes. That would break some of the packet. Once in a while your TCP receiver would receive an incomplete packet. Having said all that. You could also add all this logic on the TCP receiving side. When an incomplete packet is received, you could keep it in a buffer in the assumption that the next incoming packet will contain the missing bytes.
Consider if it's worth it
Note that there are commercial RS232-to-ethernet devices that you can buy of the shelf and configure (~100EUR) that do exactly what you want. And often in the setup of that kind device you would have the option to configure a flush-character. (e.g. that CR). MOXA is probably the best you can get. ADAM also makes decent devices. These vendors have been making this kind of devices for ~30 years.
To get you started
But for the sake of exercise, here we go.
First of all, you would need something to communicate with your serial device.
I used this one:
npm install serialport#^9.1.0
You can pretty much blindly copy the following code. But obviously you need to set your own RS232 or USB port settings. Look in the manual of your device to determine the baudrate, databits, stopbits, parity and optionally RTS/DTR
import SerialPort from "serialport";
export class RS232Port {
private port: SerialPort;
constructor(private listener: (buffer: Buffer) => any, private protocol) {
this.port = new SerialPort("/dev/ttyS0", {
baudRate: 38400,
dataBits: 8,
stopBits: 1,
parity: "none",
});
// check your RTS/DTR settings.
// this.port.on('open', () => {
// this.port.set({rts: true, dtr: false}, () => {
// });
//});
const parser = this.port.pipe(this.protocol);
parser.on('data', (data) => {
console.log(`received packet:[${toHexString(data)}]`);
if (this.listener) {
this.listener(data);
}
});
}
sendBytes(buffer: Buffer) {
console.log(`write packet:[${toHexString(buffer)}]`);
this.port.write(buffer);
}
}
The code above continuously reads data from a serial device, and uses a "protocol" to determine where messages start/end. And it has a "listener", which is a callback. It can also send bytes with its sendBytes function.
That brings us to the protocol, which as explained earlier is something that should read until a separator is found.
Because I have no clue what your separator is. I will present you with an alternative, which just waits for a silence. It assumes that when there is no incoming data for a certain time, that the message will be complete.
export class TimeoutProtocol extends Transform {
maxBufferSize: number;
currentPacket: [];
interval: number;
intervalID: any;
constructor(options: { interval: number, maxBufferSize: number }) {
super()
const _options = { maxBufferSize: 65536, ...options }
if (!_options.interval) {
throw new TypeError('"interval" is required')
}
if (typeof _options.interval !== 'number' || Number.isNaN(_options.interval)) {
throw new TypeError('"interval" is not a number')
}
if (_options.interval < 1) {
throw new TypeError('"interval" is not greater than 0')
}
if (typeof _options.maxBufferSize !== 'number' || Number.isNaN(_options.maxBufferSize)) {
throw new TypeError('"maxBufferSize" is not a number')
}
if (_options.maxBufferSize < 1) {
throw new TypeError('"maxBufferSize" is not greater than 0')
}
this.maxBufferSize = _options.maxBufferSize
this.currentPacket = []
this.interval = _options.interval
this.intervalID = -1
}
_transform(chunk: [], encoding, cb) {
clearTimeout(this.intervalID)
for (let offset = 0; offset < chunk.length; offset++) {
this.currentPacket.push(chunk[offset])
if (this.currentPacket.length >= this.maxBufferSize) {
this.emitPacket()
}
}
this.intervalID = setTimeout(this.emitPacket.bind(this), this.interval)
cb()
}
emitPacket() {
clearTimeout(this.intervalID)
if (this.currentPacket.length > 0) {
this.push(Buffer.from(this.currentPacket))
}
this.currentPacket = []
}
_flush(cb) {
this.emitPacket()
cb()
}
}
Then finally the last piece of the puzzle is a TCP/IP connection. Here you have to determine which end is the client and which end is the server. I skipped that for now, because there are plenty of tutorials and code samples that show you how to set up a TCP/IP client-server connection.
In some of the code above I use a function toHexString(Buffer) to convert the content of a buffer to a hex format which makes it easier to print it to the console log.
export function toHexString(byteArray: Buffer) {
let s = '0x';
byteArray.forEach(function (byte) {
s += ('0' + (byte & 0xFF).toString(16)).slice(-2);
});
return s;
}

USB-to-RS485 using Nodejs

I am trying to receive and send data from a vacuum gauge (previous Model of https://www.pfeiffer-vacuum.com/en/products/measurement/digiline/gauges/?detailPdoId=13238&request_locale=en_US) with a computer (Linux 16.04) via an USB-to-RS485-Interface (the half-duplex USB485-STISO from http://www.hjelmslund.dk/). When I send a request to the gauge using a specific protocol it is supposed to answer to the request and I should be able to receive it with the interface. I managed to send data but whenever I send data, it seems that nothing comes back. I'm trying to do this with Node.js. The Code that I used so far is:
function pack(address, action, parameter, data) {
var length = String('00' + data.length.toString()).slice(-2);
var bufferAsString = address + action + parameter + length + data;
var check = 0;
for (var i = 0; i < bufferAsString.length; ++i) {
check += bufferAsString.charCodeAt(i)
}
var checkSum = String('000' + String(check % 256)).slice(-3);
var buffer = Buffer.from(bufferAsString + checkSum),
carriageReturn = Buffer.from('\r');
return Buffer.concat([buffer, carriageReturn]);
}
var serialPort = require('serialport');
var SerialPort = serialPort.SerialPort;
var port = new SerialPort('/dev/ttyUSB0', {
baudrate: 9600,
dataBits: 8,
stopBits: 1,
parity: 'none'
}, false);
port.open(function(err) {
if (err) {
return console.log('Error opening port: ', err.message);
}
console.log(port.isOpen());
port.on('data', function (data) {
console.log('Data: ' + data);
});
port.on('close', function() {
console.log('port closed')
});
var sendBuffer = pack('001', '00', '740', '=?');
setInterval(function() {
port.write(sendBuffer, function(err, bytes) {
console.log('send' + bytes)
});
port.drain();
}, 1000)
});
That is supposed to send a request every second to the gauge to measure the pressure. I know that the request is being send since the TxD-Led blinks shortly every second. But I receive no answer to that request.
I also tried other methods of sending data (mostly via python and the terminal) but with similar success. The green lamp for sending always flashes up but then nothing happens and no answer is received.
I am at a loss as to what to try next and would really appreciate any help that you could give me.
UPDATE:
Ok so I seem to have found one possible error in the whole thing. I was working with an oszilloscope to capture the signal that is going out of the interface when I send something. I started with single ascii-characters to see if the most basic signals are cominng out right. For ascii '0' the signal that is being sent is 10000011001, for ascii '1' it is 10100011001. So those are almost what I would expect, except that there seem to be 2 startbits. Normally I would expect there to be only 1 startbit. Is there a way to change the amount of startbits sent?
Here are the outputs of the Oszilloscope:
this is a communication problem:
1 check the protocol-based communications parameters like baud rate, parity, start-/stop-bits they have to be consistent
(if you use UART protocol on RS-485 other protocols like MODBUS, Profibus,... are also possible, this is a difference to normal RS-232)
If the gauge uses 9600 baud for communication you can not use 115200 baud in your command. In the nodejs code you do not set any parameter (i assume you use the UART protocol because of your nodejs). If the gauge uses any other protocol the nodejs code will also not work, despite that there are not set any parameters like baud rate, parity,... in the code
https://en.wikipedia.org/wiki/RS-485
for other protocols node js serial module can not be used
http://libmodbus.org/
http://www.pbmaster.org/
2 check the proprietary commands you send to the gauge. When i want to read out the data of my multimeter i have to send an ASCII 'D' = 0100 0100 (bin) to get an answer (endianness ?) If i send any other value the multimeter stays silent.
http://electronicdesign.com/what-s-difference-between/what-s-difference-between-rs-232-and-rs-485-serial-interfaces
Unless you have DE pulled high and R︦E︦ tied to ground, your conversation will be rather one-sided.
And if you do wire as above, you need to be able to deal with your own echo in the received data.

Serial port communication between Nodejs and Arduino

I am trying to communication between Arduino and Nodejs. But problem is when I communicate between I got correct reading as well as some garbage reading in between correct reading. Still cant understand how to resolve this problem ?
Here is the Nodejs part which is used to read data from COM port
var SerialPort = require("serialport").SerialPort;
var serialport = new SerialPort("COM23",{baudrate:9600});
serialport.on('open', function(){
serialport.on('data', function(data){
console.log(data[0]);
});
});
Here is my simple Arduino code
int led = 13;
void setup() {
Serial.begin(9600);
pinMode(led, OUTPUT);
}
void loop() {
digitalWrite(led, HIGH);
Serial.write(1);
delay(1000);
digitalWrite(led, LOW);
Serial.write(0);
delay(1000);
}
Limit the size of the data transferred, also define a parser for the serial transmission.
take a look to the serial port documentation https://github.com/voodootikigod/node-serialport#parsers
You have two options "raw" and "readline".
Out of the box, node-serialport provides two parsers one that simply emits the raw buffer as a data event and the other which provides familiar "readline" style parsing. To use the readline parser, you must provide a delimiter as such '\n'
here is an example for node.js.
var sp = new SerialPort('/dev/tty.usbmodem14111', {
//sp.parsers.readline('\r')
//serialport.parsers.raw
baudrate: 9600,
parser: serialport.parsers.readline('\n')
});
in the Arduino side, use the Serial function println();
Serial.println("your message to the node server");
I hope this helps.

Arduino wireless and Node.js

I'm working on an Arduino project to control motors and read sensors. I decided to use the web view using Node.js as a medium channel to read/write from the serial port to the browser using either libraries (SerialPort and SerialPort2).
Both are working fine when I connect the Arduino directly to the USB device using a wire, but Node.js can't seem to read anything when I connect the Arduino to the USB device through my wireless adapter** (APC220) even though I can read everything received on it using the Arduino serial monitor.
I checked every possible reason behind that; I checked the baud-rate I'm using for the Arduino communication with the wirelss serial and the APC220 and the bridge connector (USB-to-serial converter). They all have the same settings: 9600 baud-rate, no parity /flowcontrol, data bits: 8 , stop bits: 1.
The behavior is as follows. It connects to the COM port without trouble, and then I tried printing the errors, but it seems there are none identified by either SerialPort libraries. Then no reading comes to the event (data), which means it (Node.js) is not interacting with the serialport even though it is open.
Note:
I know I can use another Arduino as a medium between the USB port and the wireless adapter, but I want to understand this problem and solve it cleanly without such work around.
What could the problem be?
server [node.js]:
var SerialPort = require('serialport2').SerialPort;
var portName = 'COM15';
var io = require('socket.io').listen(8000); // Server listens for socket.io communication at port 8000
io.set('log level', 1); // Disables debugging. This is optional. You may remove it if desired.
var sp = new SerialPort(); // Instantiate the serial port.
sp.open(portName, { // portName is instatiated to be COM3, replace as necessary
baudRate: 9600, // This is synchronised to what was set for the Arduino code
dataBits: 8, // This is the default for Arduino serial communication
parity: 'none', // This is the default for Arduino serial communication
stopBits: 1, // This is the default for Arduino serial communication
flowControl: false // This is the default for Arduino serial communication
});
io.sockets.on('connection', function (socket) {
// If socket.io receives message from the client browser then
// this call back will be executed.
socket.on('message', function (msg) {
console.log(msg);
});
// If a web browser disconnects from Socket.IO then this callback is called.
socket.on('disconnect', function () {
console.log('disconnected');
});
});
var cleanData = ''; // This stores the clean data
var readData = ''; // This stores the buffer
sp.on('data', function (data) { // Call back when data is received
readData = data.toString(); // Append data to buffer.
// If the letters '[' and ']' are found on the buffer then isolate what's in the middle
// as clean data. Then clear the buffer.
console.log(readData); // **Here you should be able to print the data if you receive any**
if (readData.indexOf(']') >= 0 && readData.indexOf('[') >= 0) {
cleanData = readData.substring(readData.indexOf('[') + 1, readData.indexOf(']'));
readData = '';
console.log("-- "+cleanData);
io.sockets.emit('message', cleanData);
}else if(readData.indexOf('[') >= 0){
cleanData = readData.substring(readData.indexOf('[') + 1, readData.length);
readData = '';
}else if(readData.indexOf(']') >= 0){
cleanData += readData.substring(0, readData.indexOf(']'));
readData = '';
console.log("-- "+cleanData);
io.sockets.emit('message', cleanData);
}else{
cleanData += readData;
readData = '';
}
//console.log(readData);
//io.sockets.emit('message', readData);
});
While the monitor is running no other program can read the serial port.
In case you do not open both at the same time then things are more tricky. My suggestion would be to spy on the wire. That is: install Wireshark and have a look at the data on the serial connection / USB bus.
You might also want to check how the serial port of the APC220 and the Arduino differ with regard to their serial/USB converters. Another idea would be to analyze this issue under Linux since may allow more insights into the low-level differences of the chip sets / USB activity. Of course if you do not have Linux experience this is hard to do, but maybe you know some Linux enthusiasts.
Well, both your codes look all right, so I'm pretty sure your problem is something obvious (like the nose in the middle of your face) that you don't see because you're too focused on details. So here's a checklist I'd do first:
Are you sure your serial interface is the COM15, and never changes?
Are you sure both APC devices have correct baudrate configured?
Did you try making your Arduino send a simple code that sends the same thing over the channel?
Like:
void loop() {
...println("TEST");
delay(1000);
}
And on your host:
sp.on('data', function (data) {
console.log(data.toString());
});
When you get something buggy in your system, try to build the most simple use case of that buggy part, so you're sure that's nothing else in your codes that interferes with that. You don't need to make your Arduino work on the GPS stuff, as well as your Node.js stuff work on the web stuff.
Just make it the most simple as you can. (And don't forget to add a delay in your Arduino loop, or you may have difficulties reflashing the chip).
You may also want to add to your code the error catching part of serialport2:
port.on('error', function(err) {
console.log("ERROR receiving serial data: ", err);
});
As well as for your open() statement:
sp.open(portName, portConfig, function (err) {
console.log("ERROR opening serial port: ", err);
});
As you may be missing error reporting on the host side!

Resources