win-duino
Version:
Arduino framework for use node.js on Windows based on duino
370 lines (333 loc) • 10.1 kB
JavaScript
var events = require('events'),
child = require('child_process'),
util = require('util'),
colors = require('colors'),
serial = require('serialport');
/*
* The main Arduino constructor
* Connect to the serial port and bind
*/
var Board = function (options) {
this.logInfo('info', 'initializing');
this.debug = options && options.debug || false;
this.device = options && options.device || 'COM20';
this.baudrate = options && options.baudrate || 9600;
this.writeBuffer = [];
this.isOpen = false;
this.writing = false;
this.attempsWriting = 0;
this.maxAttempsWriting = options && options.maxAttempsWriting || 150;
this.endProcess = false;
var self = this;
this.detect(function (err, serial) {
if (err) {
if(self.listeners('error').length)
self.emit('error', err);
else
throw new Error(err);
}else{
self.serial = serial;
self.emit('connected');
self.logInfo('info', 'binding serial events');
self.serial.on('data', function(data){
self.logInfo('receive', data.toString().red);
self.emit('data', data);
});
// Open Port
//self.open();
//self.delay(100);
//setTimeout(function(){
//console.log('self.open()');
self.open();
}
});
}
//Board.prototype.isOpen = false;
/*
* EventEmitter, I choose you!
*/
util.inherits(Board, events.EventEmitter);
/*
* Detect an Arduino board
* Loop through all USB devices and try to connect
* This should really message the device and wait for a correct response
*/
Board.prototype.detect = function (callback) {
this.logInfo('info', 'attempting to find Arduino board on windows');
var self = this;
var found = false,
err = null,
temp;
try {
temp = new serial.SerialPort(this.device, {
baudrate: this.baudrate,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false,
parser: serial.parsers.readline('\n')
},false);
} catch (e) {
err = e;
}
if (!err) {
found = temp;
self.logSuccess('success','found board at ' + this.device+ " with "+this.baudrate);
} else {
err = new Error('!!!!! Could not find Arduino on '+ this.device);
}
callback(err, found);
}
Board.prototype.exit = function(){
this.endProcess = true;
}
/*
* serialport open
*/
Board.prototype.open = function(){
this.logWarning('port','opening port...');
if(this.isOpen){ this.logWarning('port','port already openned'); return true;}
var self = this;
this.serial.open(function (error) {
if ( error ) {
self.logError('port','error to open: ' + error);
self.isOpen = false;
return false;
} else {
self.logSuccess('port','port openned');
self.isOpen = true;
//console.log(self.isOpen);
//self.processWriteBuffer();
self.ready();
return true;
}
});
}
Board.prototype.ready = function(callback){
self = this;
this.isOpen = true;
self.logInfo('info', 'board not yet ready');
self.sendClearingBytes();
if (self.debug) {
self.logInfo('info', 'sending debug mode toggle on to board');
self.write('99' + self.normalizePin(0) + self.normalizeVal(10),function(){
if (self.writeBuffer.length > 0) {self.processWriteBuffer();}
self.logInfo('info', 'board ready');
self.emit('ready');
});
if (process.platform === "win32") {
var rl = require("readline").createInterface({
input: process.stdin,
output: process.stdout
});
rl.on("SIGINT", function () {
process.emit("SIGINT");
});
}
process.on('SIGINT', function(){
self.logError('ending','Closing');
self.emit('end');
setTimeout(function(){
self.logInfo('info', 'sending debug mode toggle off to board');
self.write('99' + self.normalizePin(0) + self.normalizeVal(0));
self.exit();
}, 100);
});
}else{
self.logInfo('info', 'board ready');
}
return this;
}
/*
* The board will eat the first 4 bytes of the session
* So we give it crap to eat
*/
Board.prototype.sendClearingBytes = function () {
this.serial.write('00000000');
}
/*
* Process the writeBuffer (messages attempted before serial was ready)
*/
Board.prototype.processWriteBuffer = function () {
var t = this.writeBuffer.length;
this.log('info', 'processing buffered messages (' + t + ')');
/*
while (this.writeBuffer.length > 0) {
this.log('info', 'writing buffered message');
this.write(this.writeBuffer.shift());
}
*/
if(t > 0){
this.write(this.writeBuffer.shift());
}else{
if(this.endProcess){
self.logError('end','Closed');
delete self.serial;
process.exit();
}
}
//this.log('info', 'buffered messages processed');
}
Board.prototype.endWriting = function(){
this.writing = false;
}
Board.prototype.varAttempsWriting = function(d){
this.attempsWriting += d;
}
/*
* Low-level serial write
*/
Board.prototype.write = function (m,callback) {
if(this.attempsWriting>this.maxAttempsWriting){ this.logError('error','too many attemps to write'); return false;}
//while(this.writing) { this.logWarning('writing','Writing Waiting'); }
if(!this.writing){
this.writing = true;
var self = this;
if (this.serial) {
this.logWarning('writing', m);
//if(!this.isOpen){ this.open(); }
try{
this.serial.write('!' + m + '.',function(error){
if(error){
self.logError('write','Error Writting');
}else{
self.varAttempsWriting(-1);
self.logSuccess('write', 'Writed ' + m );
self.endWriting();
self.processWriteBuffer();
if(callback) callback();
}
});
} catch (e) {
//this.log('error', 'Error Writing');
this.logError('error', 'Error Writing, buffering message: ' + m.red);
this.writeBuffer.push(m);
}
} else {
this.logWarning('port', 'serial not ready, buffering message: ' + m.red);
this.writeBuffer.push(m);
}
}else{
this.varAttempsWriting(+1);
this.logWarning('write', 'write bussy');
this.logWarning('port', 'serial not ready, buffering message: ' + m.red);
this.writeBuffer.push(m);
}
}
/*
* Add a 0 to the front of a single-digit pin number
*/
Board.prototype.normalizePin = function (pin) {
return this.lpad( 2, '0', pin );
}
Board.prototype.normalizeVal = function(val) {
return this.lpad( 3, '0', val );
}
//
Board.prototype.lpad = function(len, chr, str) {
return (Array(len + 1).join(chr || ' ') + str).substr(-len);
};
Board.prototype.rpad = function(len, chr, str) {
return ( str + Array(len + 1).join(chr || ' ') ).substr(0,len);
};
/*
* Define constants
*/
Board.prototype.HIGH = '255';
Board.prototype.LOW = '000';
/*
* Set a pin's mode
* val == out = 001
* val == in = 000
*/
Board.prototype.pinMode = function (pin, val) {
pin = this.normalizePin(pin);
this.log('pin', 'set pin ' + pin + ' mode to ' + val);
val = (
val == 'out' ?
this.normalizeVal(1) :
this.normalizeVal(0)
);
this.write('00' + pin + val);
}
/*
* Tell the board to write to a digital pin
*/
Board.prototype.digitalWrite = function (pin, val) {
pin = this.normalizePin(pin);
val = this.normalizeVal(val);
this.logInfo('write', 'digitalWrite to pin ' + pin + ': ' + val.green);
this.write('01' + pin + val);
}
/*
* Tell the board to extract data from a pin
*/
Board.prototype.digitalRead = function (pin) {
pin = this.normalizePin(pin);
this.logInfo('read', 'digitalRead from pin ' + pin);
this.write('02' + pin + this.normalizeVal(0));
}
Board.prototype.analogWrite = function (pin, val) {
pin = this.normalizePin(pin);
val = this.normalizeVal(val);
this.logInfo('write', 'analogWrite to pin ' + pin + ': ' + val.green);
this.write('03' + pin + val);
}
Board.prototype.analogRead = function (pin) {
pin = this.normalizePin(pin);
this.logInfo('read', 'analogRead from pin ' + pin);
this.write('04' + pin + this.normalizeVal(0));
}
/*
* Utility function to pause for a given time
*/
Board.prototype.delayArduino = function (ms) {
this.log('delay', ms + ' ms');
this.write('05' + this.lpad(5,'0',ms) );
}
Board.prototype.delay = function (ms) {
ms += +new Date();
while (+new Date() < ms) { }
}
/*
* Logger utility function
*/
Board.prototype.getDateTimeString = function(){
var d = new Date;
var date = '';
var time = '';
//var date = d.getFullYear() + '-' + this.rpad(2,'0',d.getMonth()) + '-' + d.getDate() + ' ' + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds()+ '.'+ d.getMilliseconds();
time = ' ' + this.lpad(2,'0',d.getHours()) + ':' + this.lpad(2,'0',d.getMinutes()) + ':' + this.lpad(2,'0',d.getSeconds()) + '.'+ this.rpad(3,'0',d.getMilliseconds());
return date + time;
}
Board.prototype.log = function (/*level, message*/) {
var args = [].slice.call(arguments);
if (this.debug) {
console.log( this.getDateTimeString().gray + ' win-duino '.blue + this.rpad(10,' ',args.shift()).gray + ' ' + args.join(', '));
}
}
Board.prototype.logSuccess = function(){
var args = [].slice.call(arguments);
if (this.debug) {
console.log( this.getDateTimeString().gray + ' win-duino '.blue + this.rpad(10,' ',args.shift()).green + ' ' + args.join(', '));
}
}
Board.prototype.logError = function(){
var args = [].slice.call(arguments);
if (this.debug) {
console.log( this.getDateTimeString().gray + ' win-duino '.blue + this.rpad(10,' ',args.shift()).red + ' ' + args.join(', '));
}
}
Board.prototype.logWarning = function(){
var args = [].slice.call(arguments);
if (this.debug) {
console.log( this.getDateTimeString().gray + ' win-duino '.blue + this.rpad(10,' ',args.shift()).yellow + ' ' + args.join(', '));
}
}
Board.prototype.logInfo = function(){
var args = [].slice.call(arguments);
if (this.debug) {
console.log( this.getDateTimeString().gray + ' win-duino '.blue + this.rpad(10,' ',args.shift()).magenta + ' ' + args.join(', '));
}
}
module.exports = Board;