UNPKG

bonescript

Version:

Physical computing library for embedded Linux

825 lines (763 loc) 27.2 kB
// Copyright (C) 2011 - Texas Instruments, Jason Kridner // // var fs = require('fs'); var child_process = require('child_process'); var os = require('os'); var bone = require('./bone'); // Database of pins var functions = require('./functions'); // functions.js defines several math/bit functions that are handy var package_json = require('../package.json'); var g = require('./constants'); var my = require('./my'); var serial = require('./serial'); var iic = require('./iic'); var winston = my.require('winston'); var fibers = my.require('fibers'); var epoll = my.require('epoll'); var autorun = require('./autorun'); var server = require('./server'); var socketHandlers = require('./socket_handlers'); var ffi = require('./ffiimp'); var rc = require('./rc'); var debug = process.env.DEBUG ? true : false; var cbWarn = false; // Detect if we are on a Beagle var hw; if (os.type() == 'Linux' && os.arch() == 'arm') { var osVer = ("" + os.release()).split('.'); if (debug) console.log(osVer.join(":")); if (my.is_new_capemgr() || osVer[0] > 4 || (osVer[0] == 4 && osVer[1] >= 4)) { // Used for 4.4+ kernels using capemgr and universal helpers hw = require('./hw_mainline'); if (debug) console.log('Using Mainline interface'); } else if (my.is_cape_universal()) { // Used for 3.8 kernels using a single universal overlay with pinmux helpers // located in debugfs at /sys/kernel/debug/pinctrl/44e10800.pinmux/pins hw = require('./hw_universal'); if (debug) console.log('Using Universal Cape interface'); } else if (my.is_capemgr()) { // Used for 3.8 kernels using an older out-of-tree version of CapeMgr hw = require('./hw_capemgr'); if (debug) console.log('Using CapeMgr interface'); } else { // Used for 3.2 kernels using /sys/kernel/debug/omap_mux/ hw = require('./hw_oldkernel'); if (debug) console.log('Using 3.2 kernel interface'); } } else { // Incomplete implementation of a set of hardware stubs to run on non BeagleBone targets hw = require('./hw_simulator'); if (debug) winston.debug('Using simulator mode'); } if (debug) { winston.add(winston.transports.File, { filename: hw.logfile, level: 'debug' }); winston.level = 'debug'; } else { winston.setLevels(winston.config.syslog.levels); } if (debug) winston.debug('index.js loaded'); var f = {}; // Keep track of allocated resources var gpio = {}; var gpioInt = {}; var pwm = {}; var ain = false; // returned object has: // mux: index of mux mode // options: array of mode names // slew: 'fast' or 'slow' // rx: 'enabled' or 'disabled' // pullup: 'diabled', 'pullup' or 'pulldown' // pin: key string for pin // name: pin name // pwm: object if pwm enabled, undefind otherwise // freq: frequency of PWM // value: duty cycle of PWM as number between 0 and 1 // gpio: object if GPIO enabled, undefined otherwise // active: GPIO is enabled by the kernel // allocated: boolean for if it is allocated by this application // direction: 'in' or 'out' (allocated might be false) f.getPinMode = function (pin, callback) { pin = hw.getPin(pin); if (debug) winston.debug('getPinMode(' + pin.key + ');'); var mode = { 'pin': pin.key, 'name': pin.name }; if (pin.options) mode.options = pin.options; // Get PWM settings if applicable if ( (typeof pin.pwm != 'undefined') // pin has PWM capabilities && (typeof pwm[pin.pwm.name] != 'undefined') // PWM used for this pin is enabled && (pin.key == pwm[pin.pwm.name].key) // PWM is allocated for this pin ) { mode.pwm = hw.readPWMFreqAndValue(pin, pwm[pin.pwm.name]); } // Get GPIO settings if applicable if ((typeof pin.gpio != 'undefined')) { var n = pin.gpio; mode.gpio = hw.readGPIODirection(n, gpio); if (typeof gpio[n] == 'undefined') { mode.gpio.allocated = false; } else { mode.gpio.allocated = true; } } // Get pinmux settings mode = hw.readPinMux(pin, mode, callback); return (mode); }; f.getPinMode.args = ['pin', 'callback']; f.pinMode = function (pin, direction, mux, pullup, slew, callback) { pin = hw.getPin(pin); if (debug) winston.debug('pinMode(' + [pin.key, direction, mux, pullup, slew] + ');'); if (direction == g.INPUT_PULLUP) pullup = 'pullup'; pullup = pullup || ((direction == g.INPUT) ? 'pulldown' : 'disabled'); slew = slew || 'fast'; mux = (typeof mux != 'undefined') ? mux : 7; // default to GPIO mode var resp = { value: true }; var template = 'bspm'; var n = pin.gpio; if ( direction == g.ANALOG_OUTPUT || mux == g.ANALOG_OUTPUT || (typeof pin.pwm != 'undefined' && mux == pin.pwm.muxmode) ) { if ( (typeof pin.pwm == 'undefined') || // pin does not have PWM capability (typeof pin.pwm.muxmode == 'undefined') // required muxmode is not provided ) { var err = 'pinMode only supports ANALOG_OUTPUT for PWM pins: ' + pin.key; winston.info(err); if (callback) { //support both nodestyle and oldstyle callbacks based on arguments length if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); cbWarn = true; callback({ value: false, err: err }); } else callback(err, false); } return (false); } direction = g.OUTPUT; mux = pin.pwm.muxmode; template = 'bspwm'; pwm[pin.pwm.name] = { 'key': pin.key, 'freq': 0 }; } if (!pin.mux) { winston.info('pinMode: Missing mux name for pin object: ' + JSON.stringify(pin)); } // Handle case where pin is allocated as a gpio-led if (debug) winston.debug('pinMode: pin.led = ' + pin.led); if (pin.led) { if ((direction != g.OUTPUT) || (mux != 7)) { resp.err = 'pinMode only supports GPIO output for LEDs: ' + pin.key; winston.info(resp.err); resp.value = false; if (callback) callback(resp); return (false); } resp = hw.setLEDPinToGPIO(pin, resp); if (typeof resp.err == 'undefined') { gpio[n] = true; } if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); cbWarn = true; callback(resp); } else callback(resp.err, resp.value); } return (resp.value); } // Figure out the desired value var pinData = my.pin_data(slew, direction, pullup, mux); // May be required: mount -t debugfs none /sys/kernel/debug resp = hw.setPinMode(pin, pinData, template, resp); if (typeof resp.err != 'undefined') { if (debug) winston.debug('Unable to configure mux for pin ' + pin + ': ' + resp.err); // It might work if the pin is already muxed to desired mode var currentMode = f.getPinMode(pin); if (currentMode.mux != mux) { resp.value = false; winston.info(resp.err); delete gpio[n]; if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); cbWarn = true; callback(resp); } else callback(resp.err, resp.value); } return (resp.value); } } // Enable GPIO and set direction if (mux == 7) { // Export the GPIO controls resp = hw.exportGPIOControls(pin, direction, resp); if (typeof resp.err != 'undefined') { if (typeof gpio[n] == 'undefined') { delete gpio[n]; } } else { gpio[n] = true; } } else { delete gpio[n]; } if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); cbWarn = true; callback(resp); } else callback(resp.err, resp.value); } return (resp.value); }; f.pinMode.args = ['pin', 'direction', 'mux', 'pullup', 'slew', 'callback']; f.digitalWrite = function (pin, value, callback) { var myCallback = false; if (callback) myCallback = function (resp) { if (!resp || (typeof resp != 'object')) resp = { 'data': resp }; callback(resp); } pin = hw.getPin(pin); if (debug) winston.debug('digitalWrite(' + [pin.key, value] + ');'); value = parseInt(Number(value), 2) ? 1 : 0; //handle case digitalWrite() on Analog_Out if (typeof pin.pwm != 'undefined' && typeof f.getPinMode(pin).mux != 'undefined') { var gpioEnabled = (7 == f.getPinMode(pin).mux); //check whether pin set as gpio if (!gpioEnabled) { winston.debug([pin.key, value] + ' set as ANALOG_OUTPUT modifying duty cycle according to value'); f.analogWrite(pin, value, 2000, myCallback); //write duty cycle as per value return (true); } } hw.writeGPIOValue(pin, value, myCallback); return (true); }; f.digitalWrite.args = ['pin', 'value', 'callback']; f.digitalRead = function (pin, callback) { pin = hw.getPin(pin); if (debug) winston.debug('digitalRead(' + [pin.key] + ');'); var resp = {}; if (typeof pin.ain != 'undefined') { if (callback) { f.analogRead(pin, analogCallback); } else { resp.value = f.analogRead(pin); if (resp.value >= 0.5) { resp.value = g.HIGH; } else { resp.value = g.LOW; } } } else { resp = hw.readGPIOValue(pin, resp, callback); } function analogCallback(x) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); cbWarn = true; callback(analogValue(x)); } else callback(x.err, analogValue(x).value); } function analogValue(x) { if (typeof x.value == 'undefined') return; if (x.value >= 0.5) { x.value = g.HIGH; } else { x.value = g.LOW; } return x; } return (resp.value); }; f.digitalRead.args = ['pin', 'callback']; f.analogRead = function (pin, callback) { pin = hw.getPin(pin); if (debug) winston.debug('analogRead(' + [pin.key] + ');'); var resp = {}; if (!ain) { ain = hw.enableAIN(); } if (typeof pin.ain == 'undefined') { resp.value = f.digitalRead(pin, callback); } else { resp = hw.readAIN(pin, resp, callback); } return (resp.value); }; f.analogRead.args = ['pin', 'callback']; f.shiftOut = function (dataPin, clockPin, bitOrder, val, callback) { dataPin = hw.getPin(dataPin); clockPin = hw.getPin(clockPin); if (debug) winston.debug('shiftOut(' + [dataPin.key, clockPin.key, bitOrder, val] + ');'); var i = 0; var bit; var clock = 0; function next(err) { if (debug) winston.debug('i = ' + i); if (debug) winston.debug('clock = ' + clock); if (err || i == 8) { callback({ 'err': err }); return; } if (bitOrder == g.LSBFIRST) { bit = val & (1 << i); } else { bit = val & (1 << (7 - i)); } if (clock === 0) { clock = 1; if (bit) { f.digitalWrite(dataPin, g.HIGH, next); } else { f.digitalWrite(dataPin, g.LOW, next); } } else if (clock == 1) { clock = 2; f.digitalWrite(clockPin, g.HIGH, next); } else if (clock == 2) { i++; clock = 0; f.digitalWrite(clockPin, g.LOW, next); } } if (callback) { next(); } else { for (i = 0; i < 8; i++) { if (bitOrder == g.LSBFIRST) { bit = val & (1 << i); } else { bit = val & (1 << (7 - i)); } if (bit) { f.digitalWrite(dataPin, g.HIGH); } else { f.digitalWrite(dataPin, g.LOW); } f.digitalWrite(clockPin, g.HIGH); f.digitalWrite(clockPin, g.LOW); } } }; f.shiftOut.args = ['dataPin', 'clockPin', 'bitOrder', 'val', 'callback']; f.attachInterrupt = function (pin, handler, mode, callback) { pin = hw.getPin(pin); if (debug) winston.debug('attachInterrupt(' + [pin.key, handler, mode] + ');'); var n = pin.gpio; var resp = { 'pin': pin, 'attached': false }; // Check if we don't have the required Epoll module if (!epoll.exists) { resp.err = 'attachInterrupt: requires Epoll module'; if (debug) winston.debug(resp.err); if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); cbWarn = true; callback(resp); } else { var err = resp.err; delete resp.err; callback(err, resp); } } return (resp.attached); } // Check if pin isn't already configured as GPIO if (typeof gpio[n] == 'undefined') { resp.err = 'attachInterrupt: pin ' + pin.key + ' not already configured as GPIO'; if (debug) winston.debug(resp.err); resp.attached = false; resp.configured = false; if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); cbWarn = true; callback(resp); } else { var err = resp.err; delete resp.err; callback(err, resp); } } return (resp); } // Check if someone already has a handler configured if (typeof gpioInt[n] != 'undefined') { resp.err = 'attachInterrupt: pin ' + pin.key + ' already has an interrupt handler assigned'; if (debug) winston.debug(resp.err); resp.attached = false; resp.configured = true; if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); cbWarn = true; callback(resp); } else { var err = resp.err; delete resp.err; callback(err, resp); } } return (resp.attached); } handler = (typeof handler === "string") ? my.myeval('(' + handler + ')') : handler; var intHandler = function (err, fd, events) { var m = {}; if (err) { m.err = err; } fs.readSync(gpioInt[n].valuefd, gpioInt[n].value, 0, 1, 0); m.pin = pin; m.value = parseInt(gpioInt[n].value.toString(), 2); if (typeof handler == 'function') m.output = handler(m); else m.output = { handler: handler }; if (m.output && (typeof callback == 'function')) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); cbWarn = true; callback(m); } else { var err = m.err; delete m.err; callback(err, m); } } }; try { gpioInt[n] = hw.writeGPIOEdge(pin, mode); gpioInt[n].epoll = new epoll.Epoll(intHandler); fs.readSync(gpioInt[n].valuefd, gpioInt[n].value, 0, 1, 0); gpioInt[n].epoll.add(gpioInt[n].valuefd, epoll.Epoll.EPOLLPRI); resp.attached = true; } catch (ex) { resp.err = 'attachInterrupt: GPIO input file not opened: ' + ex; if (debug) winston.debug(resp.err); } if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); cbWarn = true; callback(resp); } else { var err = resp.err; delete resp.err; callback(err, resp); } } return (resp.attached); }; f.attachInterrupt.args = ['pin', 'handler', 'mode', 'callback']; f.detachInterrupt = function (pin, callback) { pin = hw.getPin(pin); if (debug) winston.debug('detachInterrupt(' + [pin.key] + ');'); var n = pin.gpio; if (typeof gpio[n] == 'undefined' || typeof gpioInt[n] == 'undefined') { if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); callback({ 'pin': pin, 'detached': false }); } else callback(true, { 'pin': pin, 'detached': false }); } return (false); } gpioInt[n].epoll.remove(gpioInt[n].valuefd); delete gpioInt[n]; if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); callback({ 'pin': pin, 'detached': true }); } else callback(null, { 'pin': pin, 'detached': true }); } return (true); }; f.detachInterrupt.args = ['pin', 'callback']; // See http://processors.wiki.ti.com/index.php/AM335x_PWM_Driver's_Guide // That guide isn't useful for the new pwm_test interface f.analogWrite = function (pin, value, freq, callback) { pin = hw.getPin(pin); if (debug) winston.debug('analogWrite(' + [pin.key, value, freq] + ');'); freq = freq || 2000.0; var resp = {}; // Make sure the pin has a PWM associated if (typeof pin.pwm == 'undefined') { //handle analogWrite() on digital OUTPUT if (typeof pin.gpio != 'undefined') { if (value >= 0.5) { if (callback) f.digitalWrite(pin, g.HIGH, callback); else f.digitalWrite(pin, g.HIGH); return (true); } else { if (callback) f.digitalWrite(pin, g.LOW, callback); else f.digitalWrite(pin, g.LOW); return (true); } } resp.err = 'analogWrite: ' + pin.key + ' does not support analogWrite()'; winston.error(resp.err); if (callback) callback(resp); return (false); } // Make sure there is no one else who has the PWM if ( (typeof pwm[pin.pwm.name] != 'undefined') // PWM needed by this pin is already allocated && (pin.key != pwm[pin.pwm.name].key) // allocation is not by this pin ) { resp.err = 'analogWrite: ' + pin.key + ' requires pwm ' + pin.pwm.name + ' but it is already in use by ' + pwm[pin.pwm.name].key; winston.error(resp.err); if (callback) callback(resp); return (false); } // Enable PWM controls if not already done if (typeof pwm[pin.pwm.name] == 'undefined') { var pinMode = f.getPinMode(pin.key); var slew = pinMode.slew || 'fast'; var pullup = pinMode.pullup || 'disabled'; f.pinMode(pin, g.ANALOG_OUTPUT, pin.pwm.muxmode, pullup, slew); } // Perform update resp = hw.writePWMFreqAndValue(pin, pwm[pin.pwm.name], freq, value, resp); // Save off the freq, value and PWM assignment pwm[pin.pwm.name].freq = freq; pwm[pin.pwm.name].value = value; // All done if (callback) callback(resp); return (true); }; f.analogWrite.args = ['pin', 'value', 'freq', 'callback']; f.getEeproms = function (callback) { var eeproms = {}; eeproms = hw.readEeproms(eeproms); if (eeproms == {}) { if (debug) winston.debug('No valid EEPROM contents found'); } if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); callback(eeproms); } else callback(eeproms == {} ? 'No valid EEPROM contents found' : null, eeproms); } return (eeproms); }; f.getEeproms.args = ['callback']; f.readTextFile = function (filename, callback) { if (typeof callback == 'function') { var cb = function (err, data) { callback({ 'err': err, 'data': data }); }; if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); fs.readFile(filename, 'ascii', cb); } else fs.readFile(filename, 'ascii', callback); } else { return fs.readFileSync(filename, 'ascii'); } }; f.readTextFile.args = ['filename', 'callback']; f.writeTextFile = function (filename, data, callback) { if (typeof callback == 'function') { var cb = function (err) { callback({ 'err': err }); }; fs.writeFile(filename, data, 'ascii', cb); } else { try { return fs.writeFileSync(filename, data, 'ascii'); } catch (ex) { winston.error("writeTextFile error: " + ex); return (false); } } }; f.writeTextFile.args = ['filename', 'data', 'callback']; f.getPlatform = function (callback) { var platform = { 'platform': bone, 'name': "BeagleBone", 'bonescript': package_json.version, 'os': {} }; platform.os.hostname = os.hostname(); platform.os.type = os.type(); platform.os.arch = os.arch(); platform.os.release = os.release(); platform.os.uptime = os.uptime(); platform.os.loadavg = os.loadavg(); platform.os.totalmem = os.totalmem(); platform.os.freemem = os.freemem(); platform.os.networkInterfaces = os.networkInterfaces(); platform = hw.readPlatform(platform); if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); callback(platform); } else callback(null, platform); } return (platform); }; f.getPlatform.args = ['callback']; f.echo = function (data, callback) { winston.info(data); if (callback) { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); callback({ 'data': data }); } else callback(null, data); } return (data); }; f.echo.args = ['data', 'callback']; f.setDate = function (date, callback) { child_process.exec('date -s "' + date + '"', dateResponse); function dateResponse(error, stdout, stderr) { if (typeof callback != 'function') return; else { if (callback.length == 1) { if (!cbWarn) winston.warning("single argument callbacks will be deprecated.please use node-style error-first callbacks: callback(err,response)"); callback({ 'error': error, 'stdout': stdout, 'stderr': stderr }); } else callback({ 'error': error, 'stderr': stderr }, stdout); } } }; f.setDate.args = ['date', 'callback']; f.delay = function (ms) { var fiber = fibers.current; if (typeof fiber == 'undefined') { winston.error('sleep may only be called within the setup or run functions'); return; } setTimeout(function () { fiber.run(); }, ms); fibers.yield(); }; // Exported variables f.bone = bone; // this likely needs to be platform and be detected for (var x in functions) { f[x] = functions[x]; } for (var x in serial) { f[x] = serial[x]; } for (var x in iic) { f[x] = iic[x]; } for (var x in g) { f[x] = g[x]; } for (var x in autorun) { f[x] = autorun[x]; } for (var x in server) { f[x] = server[x]; } for (var x in socketHandlers) { f[x] = socketHandlers[x]; } for (var x in ffi) { f[x] = ffi[x]; } for (var x in rc) { f[x] = rc[x]; } var alreadyRan = false; function run() { if (debug) winston.debug('Calling run()'); if (alreadyRan) return (false); alreadyRan = true; // 'setup' and 'loop' are globals that may or may not be defined if (typeof global.setup == 'function' || typeof global.loop == 'function') { fibers(function () { if (typeof global.setup == 'function') { winston.debug('Running setup()'); global.setup(); } if (typeof global.loop == 'function') { if (debug) winston.debug('Starting loop()'); while (1) { global.loop(); } } }).run(); } } process.nextTick(run); // Global variable assignments // This section is broken out because it will eventually be deprecated f.setGlobals = function () { for (var x in f) { global[x] = f[x]; } global.run = run; }; module.exports = f;