Acting as a modbus slave through RTU - communication questions - node.js

I am using the following code to simulate a modbus slave device:
const modbus = require('jsmodbus')
const SerialPort = require('serialport')
const options = {
baudRate: 115200
}
const socket = new SerialPort("COM4", options)
const server = new modbus.server.RTU(socket)
server.on('connect', function (client) {
console.log("in connect")
console.log(client);
});
server.on('connection', function (client) {
console.log("in connection")
console.log(client);
});
server.on('readHoldingRegisters', function (adr, len) {
console.log("in readHoldingRegisters")
console.log("adr: " + adr);
console.log("len: " + len);
});
The code above does actually simulate a device. The master I have set up can see a slave device when I run this code. The problem is that I can't seem to get the server functions to reach their console.log sections.
I have two theories.
First, my slave device uses the jsmodbus library to simulate a server and my master device uses modbus-serial to communicate. Could this cause a problem?
My second theory is that the code I have above is running all at once and doesn't look around or stay open to see future communications. How would I solve this?
I am open to new theories as well. My goal is eventually to pass modbus data back to the master through the server.on commands.
Edit
I know for sure there is data coming in if I read the data on the serial port directly:
socket.on('readable', function () {
console.log('Data:', socket.read()) //prints incoming buffer data
});
I still am not getting data through the server commands.

Related

Node stream pipeline stops prematurely

Think of this piece of code as a relay between a NAT-ted service (input) and an external service (output) that wants to communicate with the input.
This relay is on a public server and opens two ports in order to relay:
port 4040 where input connects and forwards the TCP traffic from target service
port 4041 where some external client connects to the relay
The relay should pipe what it receives from input on port 4040 to the external client on port 4041.
I can see both services connecting to the relay but the data flow just stops after, what I suspect the output socket closing. In the following example I used a stream.pipeline but I also tried with simple .pipe directly on the sockets with same results
import net from "net"
import stream from "stream";
export default () => {
const inputServer = net.createServer();
const outputServer = net.createServer();
inputServer.listen(4040, "0.0.0.0", () => {
console.log('TCP Server is running on port ' + 4040 + '.');
});
outputServer.listen(4041, "0.0.0.0", () => {
console.log('TCP Server is running on port ' + 4041 + '.');
});
let inSocket = null;
inputServer.on('connection', (sock) => {
inSocket = sock;
});
outputServer.on('connection', (sock) => {
if (inSocket) {
stream.pipeline(inSocket, sock, (err) => {
if (err) {
console.error('Pipeline failed.', err);
} else {
console.log('Pipeline succeeded.');
}
})
stream.pipeline(sock, inSocket, (err) => {
if (err) {
console.error('Pipeline failed.', err);
} else {
console.log('Pipeline succeeded.');
}
})
}
});
}
My goal is to keep an open socket to the input service and relay with any output will connect.
data flow just stops after, what I suspect the output socket closing
pipeline or .pipe() will automatically close the output stream when the input stream ends so it won't keep your input stream open for successive input stream connections.
Using .pipe(), you can override that behavior by passing an option:
inSocket.pipe(sock, {end: false});
and
sock.pipe(inSocket, {end: false});
You will then need some separate error handling for each stream as .pipe() doesn't do as complete error handling as pipeline() does.
In this way, you let each stream close itself when the client chooses rather than having the server close it when a given streaming operation is complete.
I don't see a similar option for pipeline().
I'm also curious how you plan to use this. Do you intend for there to be one long connected inSocket and many separate connections to the outputServer? Do you intend or need more than one outputServer connection at once? Or just one at at time. Since you're not auto destroying, I wonder if you need to do some manual cleanup (unpiping, for example) when any socket disconnects? .pipe() is also famous for not unwinding all its event listeners which can sometimes lead to GC issues if you don't manually clean up things properly.

How to prevent repeated responses from Node.js server

We're running into a problem where we're getting multiple responses sent from our Node server to a web client which are connected by a socket server (socket.io). By listening with Docklight, I can see that we're really only getting a single response from the serial device, but for some reason the Node server is sending multiples, and they accumulate, so the first time you send a serial command (and it doesn't matter what commands) might only get a couple, next time a couple more, next time a couple more and so on. So if you run several serial commands, you'll get back lots of multiple responses.
Our environment is Windows 7 64 bit, Node V 4.5.0, serialport V 4.0.1. However, this needs to run on Windows, Mac & Linux when we're done. The dev team (me & one other guy) are both fairly new to Node, but otherwise capable developers.
I think what's happening is I'm not using the .flush() & .drain() functions properly and the serialport buffer still contains serial data. Our proprietary devices return either S>, or <Executed/> prompts when a command has completed, so I store the serial response in a buffer until I see one or the other, then process the data (in this example just providing a boolean response whether the device is responding with one or the other or not). For example, if I send a <CR><LF> to one of our devices, it should respond with S> (or <Executed/> depending).
The client calls into the server with this:
socket.on('getDeviceConnected', readDeviceResponse);
function readDeviceResponse(isDeviceResponding) {
console.log('getDeviceConnected');
console.log(isDeviceResponding);
}
function getDeviceConnected() {
console.log("Sending carriage return / line feed.");
socket.emit('getDeviceConnected', '\r\n');
}
And on the server, here's what I'm trying:
socket.on('getDeviceConnected', function (connectionData) {
//write over serial buffer before the write occurs to prevent command accumulation in the buffer.
serialBuffer = '';
sbeSerialPort.write(connectionData, function (err, results) {
//since there's no way to tell if the serial device hasn't responded, set a time out to return a false after allowing one second to elapse
setTimeout(function () {
console.log('Inside getDeviceConnected setTimeout');
console.log('Is serial device responding:', isSerialDeviceResponding);
if (!isSerialDeviceResponding) {
console.log('Serial device timed out.');
socket.emit('getDeviceConnected', false);
}
}, 1000);
if (err) {
console.log('Serial port error level:', err);
}
if (results) {
if (results === 2) {
console.log('Serial port is responding');
}
}
});
sbeSerialPort.on('data', function (serialData) {
isSerialDeviceResponding = true;
console.log('Does S> prompt exist?', serialData.lastIndexOf('S>'));
while(!serialData.lastIndexOf('S>') > -1 || !serialData.lastIndexOf('<Executed/>') > -1){
serialBuffer += serialData;
break;
}
if (isSerialDeviceResponding) {
socket.emit('getDeviceConnected', true);
isSerialDeviceResponding = true;
}
sbeSerialPort.flush(function (err, results) {
if (err) {
console.log(err);
return;
}
if(results){
console.log('Serial port flush return code:', results);
}
});
});
I'm not very sure about the .flush() implementation here, and I've omitted the .drain() part because neither of them seems to do much of anything (assuming they were correctly implemented).
How do I insure that there is no data left in the serialport buffer when the .write() command is complete? Or do you see other problems with how I'm handling the serial data?
Edit, Source code up on pastebin.com:
Server.js
Client.js
HTML

Unable to send OSC messages with node's osc package. Port closed error, even though the port on the machine is open

I'm using the code below to try send OSC messages to a computer on the network. I'm using a package called osc.
I'm unable to send messages to the machine running the OSC server and receive the error below when attempting to send OSC messages:
Error: Uncaught, unspecified "error" event. (Can't send packets on a closed osc.Port object. Please open (or reopen) this Port by calling open().)
Code
let osc = require('osc');
let oscUDP = new osc.UDPPort({
remoteAddress: "192.168.1.5",
remotePort: 8004
});
oscUDP.send({
address: "/carrier/frequency",
args: 440
});
oscUDP.open();
If I put oscUDP.open() before the send call I get a different error:
Error: send EINVAL 192.168.1.5:8004
at Object.exports._errnoException (util.js:1007:11)
at exports._exceptionWithHostPort (util.js:1030:20)
at SendWrap.afterSend [as oncomplete] (dgram.js:402:11)
I am running OSCulator on OSX as the server. The code above lives on a different machine. When I run nmap on the IP address the port is open:
nmap 192.168.1.5 -p 8004
Starting Nmap 6.40 ( http://nmap.org ) at 2016-08-30 08:22 BST
Nmap scan report for 192.168.1.5
Host is up (0.13s latency).
PORT STATE SERVICE
8004/tcp open unknown
If I use osc-cli the messages are received on the machine running the OSC server:
osc --host 192.168.1.5:8004 /test 1 2 3
So it would seem the problem isn't with closed ports at all as the messages are sent and received when using osc-cli.
Any ideas?
I know I'm coming to this quite late, and it looks like you found a different library that works for you, but I thought a response might be helpful for others who are facing this issue. I'm the developer of osc.js, the original library you were trying to use.
First off, as background information, osc.js is factored into two different layers:
The low-level API that provides functions for reading and writing OSC messages and bundles to/from Typed Arrays.
The higher-level, event-based Port API, which provides a collection of platform-specific transport objects, which offer an easy way to do bidirectional communication over protocols like UDP, Web Sockets, etc.
In the case of your example code, you were trying to send an OSC message on your UDPPort object prior to it being ready. When you open() a Port, it may need to perform asynchronous operations such as opening up a socket, etc. As a result, it fires an event (aptly called ready) when the Port is all set to be used. Until ready fires, you won't be able to send or receive OSC packets.
So in the case of your original code, it looks like you were assuming that this line was synchronous and that you could call send() immediately afterwards:
oscUDP.open();
Instead, you just needed to listen for the ready event prior to attempting to send a message on the Port. Like this:
oscUDP.on("ready", function () {
oscUDP.send({
address: "/carrier/frequency",
args: 440
});
});
The osc.js Node.js example illustrates this pattern. But when I saw your question, I realized that the sample code in the osc.js README was a bit ambiguous in this regard. I have improved the event documentation and the inline README sample code to be more clear in this regard. Sorry for the confusion.
There are cases, perhaps such as yours, where the higher-level API isn't quite what you need. osc.js also provides functions for easily encoding an OSC packet as a Uint8Array, which can be converted into a Node.js buffers. So you could have done something similar to your solution just by using osc.js' osc.writeMessage() function. It has always been quite well documented, fortunately. Here's your example, modified to use osc.js' low-level API:
const dgram = require('dgram');
const client = dgram.createSocket('udp4');
const osc = require('osc');
const HOST = '192.168.1.5';
const PORT = 8004;
process.on('SIGINT', function() {
client.close();
});
let oscNoteMessage = function(note, value) {
var message = osc.writeMessage({
address: '/note/' + note,
args: [
{
type: 'i',
value: value
}
]
});
return Buffer.from(message);
}
let noteOn = function(note) {
return oscNoteMessage(note, 1);
}
let noteOff = function(note) {
return oscNoteMessage(note, 0);
}
let send = function(message) {
client.send(message, PORT, HOST, function(err, bytes) {
if(err) throw new Error(err);
})
}
send(noteOn('c'));
setTimeout(function() {
send(noteOff('c'));
}, 1000);
Anyway, I'm glad you were able to come up with a solution that works for your project, and I hope this response helps other users who may encounter similar issues. And of course, feel free to ask questions or file issues on the osc.js issue tracker.
Best regards, and apologies for the trouble you experienced using the library!
I figured it's actually pretty easy to send OSC data over UDP without the need for any packages except a2r-osc which is used for encoding OSC data.
I'm posting the solution incase anyone else in interested:
const dgram = require('dgram');
const client = dgram.createSocket('udp4');
const osc = require('a2r-osc');
const HOST = '192.168.1.5';
const PORT = 8004;
process.on('SIGINT', function() {
client.close();
});
let noteOn = function(note) {
return new osc.Message('/note/' + note, 'i', 1).toBuffer();
}
let noteOff = function(note) {
return new osc.Message('/note/' + note, 'i', 0).toBuffer();
}
let send = function(message) {
client.send(message, PORT, HOST, function(err, bytes) {
if(err) throw new Error(err);
})
}
send(noteOn('c'));
setTimeout(function() {
send(noteOff('c'));
}, 1000);

node.js and bluetooth barcode-scanner

I am struggling quite a while now to get a solid, long-term connection to a bluetooth barcode scanner from Inateck using node.js. The process is running in the background (linux, no input-focus) that's why I configured the scanner as a SPP device.
The connection is basically working as long as the scanner doesn't automatically switch off to save power, which is after about 5 minutes.
My first approach was to use the bluetooth-serial-port package. It discovers the scanner, reads the barcodes but when the scanner switches off, I don't know how to re-connect. I added an interval timer to check the connection and try to connect again if isOpen() returns false (which works once). When I press the button on the scanner it switches back on and I can re-connect but after a view seconds isOpen() returns false even if the connection is established, and I don't get any further readings. Here is my code:
var btSerial = new (require('bluetooth-serial-port')).BluetoothSerialPort();
var btInterval = null;
btSerial.on('found', function (address, name) {
btSerial.findSerialPortChannel(address, function (channel) {
if (address === '00:06:11:68:15:81') {
btSerial.connect(address, channel, function () {
console.log('connected to ' + address);
btInterval = setInterval(function () {
if (!btSerial.isOpen()) {
btSerial.close();
clearInterval(btInterval);
console.log('lost connection - try to reconnect');
btSerial.inquire();
}
}, 5000);
}, function () {
console.log('cannot connect to ' + address);
});
}
}, function () {
console.log('found nothing');
});
});
btSerial.on('finished', function () {
console.log('finished');
});
btSerial.on('data', function (buffer) {
console.log(buffer.toString('utf-8'));
});
btSerial.inquire();
The output looks like this:
finished
connected to 00:06:11:68:15:81
found nothing
... scanning works ...
lost connection - try to reconnect
finished
connected to 00:06:11:68:15:81
... scanning works ...
lost connection - try to reconnect
finished
... that's it - no more scans ...
^C
An other idea was to use nodes fs() an read directly from '/dev/rfcomm0'.
scanner = fs.createReadStream('/dev/rfcomm0', {bufferSize: 1});
scanner.on('open', function () {
logger.info('Scanner connected');
});
scanner.on('end', function () {
logger.info('End of data stream');
});
scanner.on('close', function () {
logger.info('Scanner disconnected');
});
scanner.on('error', function (error) {
logger.error('Scanner error');
});
scanner.on('data', function (chunk) {
logger.info(chunk.toString('ascii', 0, 13));
}
});
});
Connecting is done by the OS automatically when reading from the device and I do receive the codes via on('data',..). But I do have the same problem when the scanner switches off after a while. I do receive the on('close',..) event but reconnecting using fs.createReadStream() again doesn't work any more.
Maybe someone of you already had to deal which such a problem and can give me a hint how to handle this. I appreciate every suggestion.
Thanks,
Max
That's not the way I wanted to go for but a bash script to launch my node app when the scanner is available, does the job:
#!/bin/bash
echo "Press CTRL+C to stop..."
while :
do
if hcitool scan | grep -q "00:06:11:68:15:81"; then
# BT scanner found
node .
fi
sleep 1
done

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