Skip to content

Instantly share code, notes, and snippets.

@tjhv
Last active February 17, 2018 05:05
Show Gist options
  • Save tjhv/379e101f229c75c67f033f3074b481e2 to your computer and use it in GitHub Desktop.
Save tjhv/379e101f229c75c67f033f3074b481e2 to your computer and use it in GitHub Desktop.
Stream wrapper for vanilla node.js tty/serial coms
'use strict';
const stream = require('stream');
const fs = require('fs');
const tty = require('tty');
const util = require('util');
//use node.js' child process to spawn and set baudrates
class Serial extends stream.Duplex
{
constructor(opts)
{
super(opts);
this._open = false;
this._port = null;
this._rx = null;
this._tx = null;
this._fd = -1;
}
get port()
{
return this._port;
}
get bytesRead()
{
return this.isOpen ? this._rx.bytesRead : -1;
}
get bytesWritten()
{
return this.isOpen ? this._tx.bytesWritten : -1;
}
get isOpen()
{
return this._open;
}
open(port)
{
return new Promise((resolve, reject) => {
if(this.isOpen)
{
return reject(new Error('Port already open.'));
}
if(!port) {
return reject(new Error('Invalid port specified.'));
}
util.promisify(fs.open)(port, 'r+').then((fd) => {
//check if we're a real tty device..
if(!tty.isatty(fd))
{
//if not, lets get outta here.. close descriptor
util.promisify(fs.close)(fd).then(() => {
return reject(new Error('Invalid tty device.'));
}).catch((err) => {
console.log('Close error:');
return reject(err);
});
}
//if we're here, all SHOULD be well...
this._fd = fd;
this._port = port;
this._tx = new tty.WriteStream(fd);
this._rx = new tty.ReadStream (fd);
//raw mode ftw
this._rx.setRawMode(true);
this._rx.on('data', (data) => {
this.push(data);
});
//TODO: make error handler
this._tx.on('error', (err) => {
console.log('Transmission error:');
console.log(err)
});
this._rx.on('error', (err) => {
console.log('Receive error:');
if(err.code === 'ENXIO')
{
//device has been disconnected
//ENXIO = No such device or address.
console.log('Device has been disconnected.');
}
console.log(err)
});
this._tx.on('close', () => {
console.log('Transmission end closed.');
});
this._rx.on('close', () => {
console.log('Receiving end closed.');
});
this.on('pipe', () => {
console.log('Serial is in sync.');
});
this.on('unpipe', () => {
console.log('Serial is out of sync.');
});
this.on('error', () => {
console.log('Serial has error');
});
this.on('close', () => {
console.log('Serial closed.');
});
this._open = true;
this.emit('open');
return resolve({port, fd});
}).catch((err) => {
return reject(err);
});
});
}
_write(data, enc, callback)
{
return new Promise((resolve, reject) => {
if(!this.isOpen)
{
return reject(new Error('Stream is not open.'));
}
if(!data)
{
return reject(new Error('Cannot write null data to stream.'));
}
//if false, buffer is full. wait for drain
if(!this._tx.write(data))
{
this._tx.once('drain', callback());
}
else {
return resolve(callback());
}
});
}
_read(size)
{
if(!this.isOpen)
{
return new Error('Stream is not open.');
}
}
}
module.exports = Serial;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment