I want to run these three actions in an ordered manner:
Opening the serial port
Listening to any changes
Sending commands via serial port
The problem is because of the asynchronicity of node js, sometimes data is sent before the port starts listening. Hence, this data is lost. Here is my sample code. How can I solve this problem?
var SerialPort = require("serialport").SerialPort
var serialPort = new SerialPort("COM27", {
baudrate: 57600
}, false); // this is the openImmediately flag [default is true]
//***1***open the serialport
serialPort.open(function (error) {
if ( error ) {
console.log('failed to open: '+error);
}
else {
console.log('open');
}
//***2***port is now listening
serialPort.on('data', function (data) {
console.log('data received: ' + data)
})
//***3***send command using serialport
serialPort.write("AJ+APN?\n", function(err, results) {
console.log('err ' + err);
console.log('results ' + results)
})
});
Create a promise for each of the tasks that should execute in series. Then it should be simple chaining of the promises in the required order.
I don't know about the serialport module you're using, but if it's "standard" in the way it behaves then:
var SerialPort = require("serialport").SerialPort
var serialPort = new SerialPort("COM27", { baudrate: 57600 }, false);
//***1*** listen for events on port
serialPort.on('data', function (data) {
console.log('data received: ' + data);
//***2*** open the serialport
serialPort.open(function (error) {
if ( error ) {
console.log('failed to open: '+error);
} else {
console.log('open');
//***3*** send command using serialport -> after port opened
serialPort.write("AJ+APN?\n", function(err, results) {
console.log('err ' + err);
console.log('results ' + results)
});
}
});
Related
I'm using ssh2 nodejs client (https://github.com/mscdex/ssh2)
I'm trying to do the following:
SSH into box.
Login to docker.
It will re-prompt for password. Enter that password.
I'm failing on 3rd step.
This is my code
var Client = require('ssh2').Client;
var conn = new Client();
conn.on('ready', function() {
console.log('Client :: ready');
conn.exec('sudo docker ps', {pty: true}, function(err, stream) {
if (err) throw err;
stream.on('close', function(code, signal) {
conn.end();
// data comes here
}).on('data', function(data) {
console.log('STDOUT: ' + data);
}).stderr.on('data', function(data) {
console.log('STDERR: ' + data);
});
// stream.end(user.password+'\n');
^^ If i do this, it will work but I won't be able to do anything else afterwards
});
}).connect({
host: 'demo.landingpage.com',
username: 'demo',
password: 'testuser!'
});
How do I enter the password programmatically? (I'm already using {pty: true} while doing conn.exec
Please enlighten!
Assuming your stream is a duplex stream, you have the possibility to write into the stream without ending it by writing
.on('data', (data) => {
stream.write(user.password+'\n');
}
or you can use an cb function
function write(data, cb) {
if (!stream.write(data)) {
stream.once('drain', cb);
} else {
process.nextTick(cb);
}
}
and
.on('data', (data) => {
write(user.password+ '\n', () => {
console.log('done');
}
});
Accordding to my last question SerialPort 'close' event never fire. I was unabled to detected if the COM is disconnected so I have created my own way to detect it.
I have created timestamp, and checked it with interval() every 1 sec to see if it is connected.
when it's detect the COM is unplugged I have try to re-establish the connection or re-instance port with SerialPort like you'll see inside the code below.
When it's try to reconnect I've get Error: Access denied.
There is a way to refresh or clean the cache? , because I think the server still hold the connection when isn't closed propely.
I've also tried port.close() and it's throw me out: Error: Port is not open.
var comPort = '\\\\.\\COM7',
lastDataTime,
lastresult,
count = 0,
lastDataTime,
comStatus,
error;
var port = new SerialPort(comPort, function (err) {
if (err) {
comStatus = false;
return console.log('Error: ', err.message);
}
});
const parser = port.pipe(new Readline());
port.on('open', function () {
console.log('~Port is open.');
parser.on('data', function (data) {
comStatus = true;
lastDataTime = Date.now();
if (++count == 10) {
count = 0;
lastresult = data;
}
});
});
setInterval(function () {
if (Date.now() - lastDataTime > 1000 || !comStatus) {
comStatus = false;
port.close();
port = new SerialPort(comPort, function (err) {
if (err) {
error = 'Error: ' + err.message;
return console.log(error);
}
});
}
}, 1000);
app.get('/', function (req, res) {
res.send((comStatus) ? lastresult : 'Disconnected - ' + error);
console.log(lastresult);
})
Thanks!
As you can see in /node_modules/serialport/lib/serialport.js: close-event may not be emitted (unlike disconnect).
You can add console.log locally like below to simple debug.
P.S. I tested it on Win7x32. Close-event is emitted.
SerialPort.prototype._disconnected = function(err) {
this.paused = true;
this.emit('disconnect', err);
// add: console.log('1', this.closing);
if (this.closing) {
return;
}
// add: console.log('2', this.fd);
if (this.fd === null) {
return;
}
this.closing = true;
if (process.platform !== 'win32') {
this.readable = false;
this.serialPoller.close();
}
// add: console.log('3');
SerialPortBinding.close(this.fd, function(err) {
// add: console.log('4', this._events.close.toString());
this.closing = false;
if (err) {
debug('Disconnect close completed with error: ', err);
}
this.fd = null;
this.emit('close'); // it's your target
}.bind(this));
};
Reconnect example
var SerialPort = require('serialport');
var port = new SerialPort('COM1', {autoOpen: false, baudRate: 9600});
function open () {
port.open(functon (err) {
if (!err)
return;
console.log('Port is not open: ' + err.message);
setTimeout(open, 10000); // next attempt to open after 10s
});
}
port.on('open', function() {
function send() {
if (!port.isOpen()) // v5.x require
return console.log('Port closed. Data is not sent.');
port.write(123, function (err) {
if (err)
console.log('Error on write: ' + err.message)
port.drain(() => console.log('DONE'));
});
}
setInterval(send, 1000);
});
port.on('close', function () {
console.log('CLOSE');
open(); // reopen
});
port.on('data', (data) => console.log('Data: ' + data));
port.on('error', (err) => console.error('Error: ', err.message));
open(); // open manually
According to the serialport.io,
The resume() method causes an explicitly paused, Readable stream to
resume emitting 'data' events, switching the stream into flowing mode.
Simply, when port is closes, serialport library emits a close event
serialport.on('close', function(error){
if(error.disconnected === true){
console.log("disconnected");
}
}
, which will allow us whether port is disconnected or not.
That means the disconnected port is not available to re-establish the connection again, so you have to use serialport.resume() method to re-enable the connection.
serialport.on('close', function(err){
console.log("Port closed.");
if(err.disconnected === true){
console.log("Disconnected!");
serialport.resume(function(e){
reconnectDevice(); // Serial Port Initialization Function. It's your method to declare serial port.
console.log("Error on resuming port:", e);
});
}
});
After that, it will automatically switch COM ports and you won't get error as 'Port Access denied.'.
My node.js script uses the serialport npm package to read and write to COM5 port, which is connected to an RS-232 device. This device only writes to the serial port when it receives a command sent by the PC connected to it.
How can I read what is returned by the RS-232 device after writing to it?
var SerialPort = require('serialport');
var port = new SerialPort('COM5', {
parser: SerialPort.parsers.readline('\r')
}, function() {
port.write('#01RD\r', function(err) {
if(err)
console.log('Write error')
else {
// HOW TO READ RESPONSE FROM DEVICE?
}
});
port.write('#01VER\r', function(err) {
if(err)
console.log('Write error')
else {
// HOW TO READ RESPONSE FROM DEVICE?
}
});
});
I think this is a better way to do it. By function calling,
first create your function of reading and writing:
var serialport = require("serialport");
var SerialPort = serialport.SerialPort;
var sp = new SerialPort("/dev/ttyACM0", {
baudrate: 9600,
parser: serialport.parsers.readline("\n")
});
function write() //for writing
{
sp.on('data', function (data)
{
sp.write("Write your data here");
});
}
function read () // for reading
{
sp.on('data', function(data)
{
console.log(data);
});
}
sp.on('open', function()
{
// execute your functions
write();
read();
});
I have two commands to send to server, first move forward, get the acknowledgment and then send next command move backward. I have written two separate java script files do achieve this. Can it is possible to write in single function. I am trying below code but only move forward command is sent to server.
var net = require('net');
var HOST = '127.0.0.1';
var PORT = 1850;
var client = new net.Socket();
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
client.write('READER_FWD');
//client.end();
});
client.on('data', function(data) {
console.log('DATA: ' + data);
//client.destroy();
//
if (data == 'ACK')
{
console.log('DATA1: ' + data);
client.end();
console.log('DATA2: ' + data);
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
client.write('READER_BWD');
//client.end();
console.log('DATA3: ' + data);
});
}
client.end();
});
client.on('end', function() {
console.log('disconnected from server');
});
client.on('error', function(err) {
console.log(err)
});
I have updated the code, as you rightly pointed out connection is getting close while writing, i have added some delay.
var net = require('net');
var config = {
host: '127.0.0.1',
port: 1850
};
var move = {
forward: 'READER_FWD',
backward: 'READER_BWD'
};
var client = new net.Socket();
client.connect({
host: config.host,
port: config.port
}, function () {
console.log('connected to ' + config.host + ':' + config.port);
client.write(move.forward, function () {
console.log('move forward command sent');
});
});
client.on('data', function (data)
{
var str = data.toString();
if (str === 'ACK')
{
setTimeout(function()
{
console.log('ACK received');
client.write(move.backward, function ()
{
console.log('move backward sent');
client.end();
});
}, 3000);
}
});
client.on('error', function (err) {
console.log('Error : ', err);
});
client.on('close', function () {
console.log('socket closed');
});
You don't have to end your socket and re-open it again in your 'data' listener. You can keep the same socket.
Here is my client.js file which sends the commands:
var net = require('net');
var config = {
host: '127.0.0.1',
port: 1850
};
var move = {
forward: 'READER_FWD',
backward: 'READER_BWD'
};
var client = new net.Socket();
client.connect({
host: config.host,
port: config.port
}, function () {
console.log('connected to ' + config.host + ':' + config.port);
client.write(move.forward, function () {
console.log('move forward command sent');
});
});
client.on('data', function (data) {
var str = data.toString();
if (str === 'ACK') {
console.log('ACK received');
client.write(move.backward, function () {
console.log('move backward sent');
client.end();
});
}
});
client.on('error', function (err) {
console.log('Error : ', err);
});
client.on('close', function () {
console.log('socket closed');
});
The connect() method connects the socket to the server and send the forward command to it. It's exactly the same as yours.
Then, the problem comes from your 'data' listener. Your data listener must do the following things (as you mentionned in your description):
Get data from the server
If it's the ACK message: send the backward command
Then, close the connection (if needed; if not, keep it alive)
Be careful to the following point: the Socket nodejs documentation for the event 'data' says that we are receiving a Buffer. So you need to convert it to a String to compare with another String, using for this the .toString() method of the Buffer.
Thus, as is the Nodejs net.Socket is used with events, I don't think it is possible to send the forward command, listen to the 'data' event and send the backward command.
First, it is not a good idea, because you will put the on 'data' listener after the connection and it is possible that you will miss some data!
Secondly, as it is event based, you should create your architecture that follows the process :)
Below is my code for the server:
var net = require('net');
var port = 1850;
var move = {
forward: 'READER_FWD',
backward: 'READER_BWD'
};
var server = net.createServer(function (client) {
console.log('client connected');
client.on('end', function () {
console.log('client disconnected');
});
client.on('data', function (data) {
var str = data.toString();
if (str === move.forward) {
console.log('move forward command received');
client.write('ACK', function () {
console.log('ACK sent');
});
} else if (str === move.backward) {
console.log('move backward command received: do nothing...');
} else {
console.log('unknown received message: ', str);
}
});
});
server.listen(port, function () { //'listening' listener
console.log('server bound on port: ' + port);
});
Here are also the outputs if needed:
Server:
server bound on port: 1850
client connected
move forward command received
ACK sent
move backward command received: do nothing...
client disconnected
Client:
connected to 127.0.0.1:1850
move forward command sent
ACK received
move backward sent
socket closed
I hope it answers the question. Feel free to ask if there is anything.
Can I send commands to Node.js process running a .js script? Currently I just run node and paste my code into console. It's not very convenient.
You can start a repl server (that has access to the script's scope) like this:
var repl = require('repl'),
net = require('net');
var REPL_PORT = 2323;
var replServer = net.createServer(function(sock) {
repl.start({
prompt: 'myrepl> ',
input: sock,
output: sock,
eval: function(cmd, context, filename, callback) {
var ret, err;
try {
ret = eval(cmd);
} catch (e) {
err = e;
}
if (err)
callback(err);
else
callback(null, ret);
}
}).on('exit', function() {
sock.end();
});
});
replServer.on('error', function(err) {
console.log('REPL error: ' + err);
});
replServer.on('close', function(had_err) {
console.log('REPL shut down ' + (had_err ? 'due to error' : 'gracefully'));
});
replServer.on('listening', function() {
console.log('REPL listening on port ' + REPL_PORT);
});
replServer.listen(REPL_PORT, '127.0.0.1');
Then just telnet to localhost at port 2323 and you'll get a repl prompt that you can type stuff into and poke at variables and such that are defined in your script.