Last active
February 17, 2018 05:05
-
-
Save tjhv/379e101f229c75c67f033f3074b481e2 to your computer and use it in GitHub Desktop.
Stream wrapper for vanilla node.js tty/serial coms
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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