Files
miniproject_osl_nodejs/node_modules/gpio/lib/gpio.js
T
2016-04-05 11:42:41 +00:00

247 lines
6.2 KiB
JavaScript

var fs = require('fs');
var util = require('util');
var path = require('path');
var EventEmitter = require('events').EventEmitter;
var exists = fs.exists || path.exists;
var gpiopath = '/sys/class/gpio/';
var logError = function(e) { if(e) console.log(e.code, e.action, e.path); };
var logMessage = function() { if (exports.logging) console.log.apply(console, arguments); };
var _write = function(str, file, fn, override) {
if(typeof fn !== "function") fn = logError;
fs.writeFile(file, str, function(err) {
if(err && !override) {
err.path = file;
err.action = 'write';
logError(err);
} else {
if(typeof fn === "function") fn();
}
});
};
var _read = function(file, fn) {
fs.readFile(file, "utf-8", function(err, data) {
if(err) {
err.path = file;
err.action = 'read';
logError(err);
} else {
if(typeof fn === "function") fn(data);
else logMessage("value: ", data);
}
});
};
var _unexport = function(number, fn) {
_write(number, gpiopath + 'unexport', function(err) {
if(err) return logError(err);
if(typeof fn === 'function') fn();
}, 1);
};
var _export = function(n, fn) {
if(exists(gpiopath + 'gpio'+n)) {
// already exported, unexport and export again
logMessage('Header already exported');
_unexport(n, function() { _export(n, fn); });
} else {
logMessage('Exporting gpio' + n);
_write(n, gpiopath + 'export', function(err) {
// if there's an error when exporting, unexport and repeat
if(err) _unexport(n, function() { _export(n, fn); });
else if(typeof fn === 'function') fn();
}, 1);
}
};
var _testwrite = function(file, fn) {
fs.open(file, 'w', function(err, fd) {
if (err) {
fn(false, err);
return;
}
fs.close(fd, function(err){
fn(true, null);
});
});
};
// fs.watch doesn't get fired because the file never
// gets 'accessed' when setting header via hardware
// manually watching value changes
var FileWatcher = function(path, interval, fn) {
if(typeof fn === 'undefined') {
fn = interval;
interval = 100;
}
if(typeof interval !== 'number') return false;
if(typeof fn !== 'function') return false;
var value;
var readTimer = setInterval(function() {
_read(path, function(val) {
if(value !== val) {
if(typeof value !== 'undefined') fn(val);
value = val;
}
});
}, interval);
this.stop = function() { clearInterval(readTimer); };
};
var GPIO = function(headerNum, opts) {
opts = opts || {};
var self = this;
var dir = opts.direction;
var interval = opts.interval;
if(typeof interval !== 'number') interval = 100;
this.interval = interval;
this.headerNum = headerNum;
this.value = 0;
this.PATH = {};
this.PATH.PIN = gpiopath + 'gpio' + headerNum + '/';
this.PATH.VALUE = this.PATH.PIN + 'value';
this.PATH.DIRECTION = this.PATH.PIN + 'direction';
this.export(function() {
var onSuccess = function() {
self.setDirection(dir, function () {
if(typeof opts.ready === 'function') opts.ready.call(self);
});
};
var attempts = 0;
var makeAttempt = function() {
attempts += 1;
_testwrite(self.PATH.DIRECTION, function(success, err){
if (success) {
onSuccess();
} else {
logMessage('Could not write to pin: ' + err.code);
if (attempts <= 5) {
logMessage('Trying again in 100ms');
setTimeout(makeAttempt, 100);
} else {
logMessage('Failed to access pin after 5 attempts. Giving up.');
}
}
});
};
makeAttempt();
});
};
util.inherits(GPIO, EventEmitter);
/**
* Export and unexport gpio#, takes callback which fires when operation is completed
*/
GPIO.prototype.export = function(fn) { _export(this.headerNum, fn); };
GPIO.prototype.unexport = function(fn) {
if(this.valueWatcher) this.valueWatcher.stop();
_unexport(this.headerNum, fn);
};
/**
* Sets direction, default is "out"
*/
GPIO.prototype.setDirection = function(dir, fn) {
var self = this, path = this.PATH.DIRECTION;
if(typeof dir !== "string" || dir !== "in") dir = "out";
this.direction = dir;
logMessage('Setting direction "' + dir + '" on gpio' + this.headerNum);
function watch () {
if(dir === 'in') {
if (!self.valueWatcher) {
// watch for value changes only for direction "in"
// since we manually trigger event for "out" direction when setting value
self.valueWatcher = new FileWatcher(self.PATH.VALUE, self.interval, function(val) {
val = parseInt(val, 10);
self.value = val;
self.emit("valueChange", val);
self.emit("change", val);
});
}
} else {
// if direction is "out", try to clear the valueWatcher
if(self.valueWatcher) {
self.valueWatcher.stop();
self.valueWatcher = null;
}
}
}
_read(path, function(currDir) {
var changedDirection = false;
if(currDir.indexOf(dir) !== -1) {
logMessage('Current direction is already ' + dir);
logMessage('Attempting to set direction anyway.');
} else {
changedDirection = true;
}
_write(dir, path, function() {
watch();
if(typeof fn === 'function') fn();
if (changedDirection) {
self.emit('directionChange', dir);
}
}, 1);
});
};
/**
* Internal getter, stores value
*/
GPIO.prototype._get = function(fn) {
var self = this, currVal = this.value;
if(this.direction === 'out') return currVal;
_read(this.PATH.VALUE, function(val) {
val = parseInt(val, 10);
if(val !== currVal) {
self.value = val;
if(typeof fn === "function") fn.call(this, self.value);
}
});
};
/**
* Sets the value. If v is specified as 0 or '0', reset will be called
*/
GPIO.prototype.set = function(v, fn) {
var self = this;
var callback = typeof v === 'function' ? v : fn;
if(typeof v !== "number" || v !== 0) v = 1;
// if direction is out, just emit change event since we can reliably predict
// if the value has changed; we don't have to rely on watching a file
if(this.direction === 'out') {
if(this.value !== v) {
_write(v, this.PATH.VALUE, function() {
self.value = v;
self.emit('valueChange', v);
self.emit('change', v);
if(typeof callback === 'function') callback(self.value, true);
});
} else {
if(typeof callback === 'function') callback(this.value, false);
}
}
};
GPIO.prototype.reset = function(fn) { this.set(0, fn); };
exports.logging = false;
exports.export = function(headerNum, direction) { return new GPIO(headerNum, direction); };
exports.unexport = _unexport;