I am developing a node.js application for my Raspberry Pi which receives data from its serial port, but I don't directly develop the application on it, I use my main computer instead. So I have this code in my app.js :
var serialport = require("serialport");
var SerialPort = serialport.SerialPort;
var sp = new SerialPort("/dev/ttyACM0", {
parser: serialport.parsers.readline("\n")
});
sp.on("data", function (rawData) {
...
This works well on the Rasperry Pi but I want to be able to run the application on my development computer first without having to comment every block of code about the serial port.
What is the best way to achieve this ? Is there a way to mock the serial port ?
AFAIK, there aren't any libraries that do this natively right now. What I've done in the past is to use the node-serialport library's own test code as an example, eg: https://github.com/Manetos/node-serialport/blob/157e6f9df7989abd72d509c9827d13b2b10258de/test_mocks/linux-hardware.js
If you take a look at that file, they're mocking the serial port behavior for their own tests, you can simply copy what they're doing there and use it in your stuff, and you should be good to go.
Hope that helps!
I needed the same thing and wasn't able to find details of exactly how to do it, but I kept hitting this question in my searches. After a bit of researching and finding a few vague references in different areas, I was able to put together the following. Hopefully this will be helpful to others who might land here.
You can extend the SerialPort - MockBindings class with your own and then simply implement a custom write function that will receive the data, form a proper response, then emit it back to the caller.
const MockSerialBinding = require('#serialport/binding-mock');
class EmulatedDeviceSerialBinding extends MockSerialBinding {
constructor(opt = {}) {
super(opt);
}
// THIS IS THE METHOD THAT GETS TRIGGERED WHEN THE CODE TO TEST WRITES TO A DEVICE
async write(buffer) {
// Use this method to detect the supported commands and emulate a response
const cmd = Buffer.from(buffer).toString();
let response = 'Unknown Command!'; // Default response
// Custom logic here to determine the proper response
super.emitData(response);
}
}
module.exports = EmulatedDeviceSerialBinding;
For your test, prepare a fake serial device that can be targeted that uses the class above:
const SerialPort = require('serialport');
SerialPort.Binding = EmulatedDeviceSerialBinding;
// Setup a new mock serial device that can be the target
EmulatedDeviceSerialBinding.createPort('ttyUSB_TestTarget', {
echo: true,
readyData: '\r\nhostname#user:~$ ' // This will append a 'prompt' to the end of each response (like a linux terminal would)
});
Now your logic would be the same except you'd connect to the emulated device port instead of a real port. Here is a snippet from an Express Route Middleware:
const SerialPort = require('serialport');
const SerialRegexParser = require('#serialport/parser-regex');
const serialParser = new SerialRegexParser({regex: /(?<Prompt>.*[$#]\s*$)/m});
const serialPortOptions = {
baudRate: 115200
};
// The req.params.devicePort value be the name of the emulated port
const port = new SerialPort(req.params.devicePort, serialPortOptions, (err) => {
if (err) {
return res.status(400).send({error: err.message});
}
});
port.pipe(serialParser);
// THIS WILL BE TRIGGERED WHEN THE EmulatedDeviceSerialBinding does the emitData call
serialParser.once('data', (data) => {
if (res.headersSent) return; // We've already responded to the client, we can't send more
const dataString = Buffer.from(data).toString();
// Remove the command executed from the beginning if it was echoed
respDoc.data = dataString.replace(cmdToExecute, '').trimEnd();
return res.send(respDoc);
});
// Send the command to the device
// THIS WILL TRIGGER THE EmulatedDeviceSerialBinding.write() FUNCTION
port.write(cmdToExecute, (err) => {
if (err) {
return res.status(400).send({error: err.message});
}
});
The program flow is:
Express Middleware port.write(cmdToExecute)
-> EmulatedDeviceSerialBinding.write()
-> Express Middleware serialParser.once('data') callback
I was able to achieve this using the com0com modem emulator available at https://sourceforge.net/projects/com0com/
It will create two virtual COM ports (eg. COM5 and COM6) which map to each other. You can connect your application to one of the COM ports and your emulator code to the other COM port. The emulator can then be configured to read the input and write back out accordingly.
Related
Probably this is already answered question but somehow the solutions that I found would not work. It is better to describe.
I have an embedded linux device which has a C based application running and nodejs is running as a web server. I use TCP socket for inter-process communication.
Main problem is that C application is designed to run in a syncronous way but not nodejs.
Web page sends requests to nodejs and nodejs asks to C application and wait for application result to send response to web page.
Usually this is not a problem since everything runs very fast. But in the embed side, now there are slow processes thus it makes nodejs wait.
var counterVal = 0;
app.post('/ajax.html', jsonParser, async function (req, res) {
var q = url.parse(req.url, true);
var formData = req.body;
var cMessage = { counter : counterVal };
var message = { ...q.query, ...formData, ...cMessage };
await remoteSocket.write(JSON.stringify(message), function ()
{
remoteSocket.on('data', function (e) {
try
{
remoteSocket._events.data = function (data) { };
var ret = JSON.parse(e);
if (message.counter == ret.counter)
{
res.send(e);
}
else
{
logError("received error : "+ message.counter + ": " + ret.counter);
}
}
catch (err) {
logError(err);
}
});
});
});
I use this code to handle web requests. Counter value is used as handshake. Some of the requests gets into logError because of mismatch.
But when i look into wireshark records packet by packet, there is no mismatch. ever request has correct response.
app.post is async so data listener is registered twice. Then response for a packet triggers two listeners but one listener is for next packet. Therefore is gives a mismatch.
What is a way to make app.post to run in sync mode?
I am currently working on a NodeJS project using the serialport module in conjunction with RxJS Observables. The intended 'flow'/use case is as follows:
name of a serial port portName is sent via the serial port
since RxD and TxD are linked to each other, the data is 'echoed' on the hardware side
data is read through the serial port
incoming data is handled by the readline-parser and passed to an RxJS Observable
if read data is equal to the previously sent portName, observable is not longer needed and will be completed by observer.complete()
I was able to implement the above mentioned flow, but need to do some further implementations like
timeout if no data is received within a given period of time
retries to send command again in case of timeout or other errors
I am working on the timeout implementation and tried both NodeJS' setTimeout() and RxJS' own timeout function. When applying any kind of timeout function, the data does not seem to be read/retrieved by the serial port which in turn raises a timeout error.
Assuming that there is no data, this seems to be quite fine at first glance, since the timeout does do what it should do. However, I was able to double-check that the desired data is sent to the port by using not only an emulated serial port in software, but also by using two CP2102 USB-to-Serial-converters (see comments in code for further details):
'use strict';
const Rx = require('rxjs');
const { interval } = require('rxjs');
const { timeout } = require('rxjs/operators');
const SerialPort = require('serialport');
const Readline = require('#serialport/parser-readline');
// `tries` is needed for later implementation of communcation retries
const myObservable = (portName, tries) => {
const port = new SerialPort(portName);
const parser = port.pipe( new Readline() );
port.write(`${portName}\n`);
return Rx.Observable
.create( (observer) => {
parser.on('data', (data) => {
observer.next(data);
console.log(`Detection will be: ${data == portName} (${data} vs. ${portName})`);
if (data == portName)
{
port.close( (err) => {
if (err)
{
console.log(`Error on closing serial port: ${err}`);
observer.error(err);
}
});
observer.complete();
}
})
})
// `timeout` is needed for later implementation of communication timeout, see comment at end of code
// .pipe(timeout(10000))
}
const myObserver = {
next: x => console.log(`got data from observable: ${x}`),
error: err => console.error(`something wrong occurred: ${err}`),
complete: () => console.log('done'),
};
console.log('before subscribe');
const sub = myObservable('/dev/tty.usbserial-FTG7L3FX', null).subscribe(myObserver);
// double-checked that data is sent by using software (created an emulated pair of virtual serial ports with `socat -d -d pty,raw,echo=0 pty,raw,echo=0`)
// --> data is sent, but not read/retrieved when either using `setTimeout` or RxJS' own `timeout()`
// const sub = myObservable('/dev/ttys003', null).subscribe(myObserver);
// double-checked that data is sent by using hardware interfaces (used two CP2102 modules with pairwise-crossed RxD and TxD)
// --> data is sent, but not read/retrieved when either using `setTimeout` or RxJS' own `timeout()`
// const sub = myObservable('/dev/tty.SLAB_USBtoUART', null).subscribe(myObserver);
console.log('after subscribe');
// when commenting the following `setTimeout()` data is retrieved, but does not work with `setTimeout()`
// so tried to use RxJS' `timeout()` operator --> not working either
// setTimeout(() => {
// sub.unsubscribe();
// console.log('unsubscribed');
// }, 10000);
What am I missing here? Why is data sent when not using any kind of timeout but is not when applying a timeout function?
Updates due to further investigation:
When timeout() is applied, the data is sent after the timeout duration which means that the timeout fires both sending data and exiting the observable since it is timed out. So the .timeout() does not seem to be applied to the returned Rx.Observable, but to the whole function myObservable.
I work with node-red and develop a custom node at the moment that uses websockets to connect to a device and request data from it.
function query(node, msg, callback) {
var uri = 'ws://' + node.config.host + ':' + node.config.port;
var protocol = 'Lux_WS';
node.ws = new WebSocket(uri, protocol);
var login = "LOGIN;" + node.config.password;
node.ws.on('open', function open() {
node.status({fill:"green",shape:"dot",text:"connected"});
node.ws.send(login);
node.ws.send("REFRESH");
});
node.ws.on('message', function (data, flags) {
processResponse(data, node);
});
node.ws.on('close', function(code, reason) {
node.status({fill:"grey",shape:"dot",text:"disconnected"});
});
node.ws.on('error', function(error) {
node.status({fill:"red",shape:"dot",text:"Error " + error});
});
}
In the processResponse function I need process the first response. It gives me an XML with several ids that I need to request further data.
I plan to set up a structure that holds all the data from the first request, and populate it further with the data that results from the id requests.
And that's where my problem starts, whenever I send a query from within the processResponse function, I trigger an event that results in the same function getting called again, but then my structure is empty.
I know that this is due to the async nature of nodejs and the event system, but I simply don't see how to circumvent this behavior or do my code in the right way.
If anybody can recommend examples on how to deal with situations like this or even better could give an example, that would be great!
I want nodejs to send and receive messages with xbee. I know that the xbee setup works because I tested it on x-ctu. I tried the following but can't receive the message. It says it's open.
var util = require('util');
var SerialPort = require('serialport').SerialPort;
var xbee_api = require('xbee-api');
var C = xbee_api.constants;
var xbeeAPI = new xbee_api.XBeeAPI({
api_mode: 1
});
var serialport = new SerialPort("COM7", {
baudrate: 9600,
parser: xbeeAPI.parseRaw(1000)
});
serialport.on("open", function() {
console.log("open");
});
// All frames parsed by the XBee will be emitted here
//I think this is the problem
xbeeAPI.on("frame_object", function(frame) {
console.log(">>", frame);
});
I figured it out a few day ago. I relized I could just use serial port library.
You need to listen to the serial port first and then parse the data with xbee-api
serialport.on('data', function (data) {
try {
xbeeAPI.parseRaw(data);
} catch (e) {
console.error(e);
}
xbeeAPI.on("frame_object", function (frame) {
console.log(frame);
// do what do you want with the frame
}
}
You need to process the frame switch his frame.type, is case of ZIGBEE_RECEIVE_PACKET you need to convert data to string frame.data.toString(), i don't know why using API1 mode but please try to use 57600 baud-rate or higher to avoid the checksum mismatch problems Good luck.
I am following this tutorial on making HTML5 games. I wanted to try and mix node in to make it multiplayer. I am using node.js(v0.10.4) on server and crafty.js on front end.
I am using socket.io to send and receive messages. For now it's just me(not multiple clients). The weird thing that happens is that the message that comes from the server seems to be sent multiple times. I turned on debug mode in socket.io but it only seems to be sending the data once, yet on the front end the data seems to be coming in, in multiples. I set an incrementor on the data and it seems as if the incrementor is not incrementing multiple times but instead I am getting multiple copies of the same data.
here's node code:
var http = require('http').createServer(handler),
static = require('node-static'),
io = require('socket.io').listen(http);
io.set('log level', 3);
http.listen(80);
//attach the socket to our server
var file = new static.Server(); //Create a file object so we can server the files in the correct folder
function handler(req, res) {
req.addListener('end', function() {
file.serve(req, res);
}).resume();
}
io.sockets.on('connection', function (socket) { //listen for any sockets that will come from the client
socket.on('collected', function(data) {
/**** here's where the data is being sent back to the client *****/
socket.emit('messageFromServer', { data: data.number });
});
});
and here's front end code:
//messenger entity
Crafty.c('SendRecieveMessages',{
count: 0,
sendMessageToServer : function() {
console.log('got a village');
/**** Here's where we send the message to the server ****/
socket.emit('collected', { village : "The message went to the server and back. it's collected", number : this.count });
this.count++;
},
recieveMessageFromServer : function() {
socket.on('messageFromServer', function(data) {
/*** This data seems to be coming back or logging multiple times? ***/
console.log(data);
});
}
});
Lastly here's a screenshot of the debug in process. As you can see number is not always incrementing, it almost looks like the data is getting stored. Thanks!
http://cl.ly/image/0i3H0q2P1X0S
It looks like every time you call Crafty.c, recieveMessageFromServer() is getting called too. Every time recieveMessageFromServer is invoked, it attaches an additional event listener on the socket. That's why the first time data comes back you get one copy, then the second time you get two, the third time you get three, and so on.
You either need to prevent recieveMessageFromServer from being called multiple times, or use removeListener or removeAllListeners to remove the previously attached listeners.
Thanks to #Bret Copeland for helping me figure this one out. As he pointed out, every time socket.on() is called, it seems to add another listener. To prevent this...
I declared a global variable:
I declared a variable as a property in my Game object(in craftyjs, so use whatever you want in your setup)
Game = {
//lots of other code here...
//need this to use later for socket.io
send_message : true
}
then edited my recieveMessageFromServer() function to check whether its ok to send the message or not:
recieveMessageFromServer : function() {
console.log('does this show up multiple times?');
/* Check whether the send_message is true before sending */
if (Game.send_message) {
socket.on('messageFromServer', function(data) {
console.log(data);
Game.send_message = false;
});
}
}