"use strict"; const fs = require('mz/fs'), exec = require('mz/child_process').exec, EventEmitter = require('events'), Epoll = require('epoll').Epoll, _ = require('uci-utils') const pWaitFor = require('p-wait-for') const pathExists = require('path-exists'); const debounce = require('debounce'); const GPIO_ROOT_PATH = '/sys/class/gpio/' class Interrupt extends EventEmitter { // bus is i2c-bus bus object constructor(pin_number, opts = {}) { super() this.num = pin_number; this.hook = opts.hook // will be passed back with the emit this.path = GPIO_ROOT_PATH + 'gpio' + this.num + '/' this.edge = opts.edge // default 'both' this.delay = opts.delay // default = 50 this.pull = opts.pull // default = pu this.puller = opts.puller ? opts.puller : (num, state) => { return `raspi-gpio set ${num} ${state}` } // default rpi using raspi-gpio // this.puller = (num, state) => { return `raspi-gpio set ${num} ${state}` } this.dbt = opts.debounce ? opts.debounce : 200 this.poller = new Epoll( debounce((err, fd, events) => { if (err) { this.emit('error', err) } else { // this.clear().then(this.emit('fired', this.hook)) this.emit('fired', this.hook) } }, this.dbt, true) // true = leading ) } init() { process.on('SIGINT', () => { this.exit().then((resp) => console.log("\n", resp)) // unexport on cntrl-c .catch(err => console.log("error:", err)) }) return _.pSeries([ this.exit(), this._pull(this.num, this.pull), this._export(this.num), this._direction('in', this.delay), () => this._edge(this.edge), // TODO determine why these need additional function () => this._fd() ]) } // manual firing for testing fire(name = 'fired') { this.emit(name, this.hook) } clear() { let buffer = Buffer.from([0x00]) return fs.read(this.valuefd, buffer, 0, 1, 0).then(() => { return Promise.resolve('interrupt cleared') }) } exit() { return new Promise((resolve, reject) => { pathExists(this.path) .then((exists) => { if (exists) { fs.writeFile(GPIO_ROOT_PATH + 'unexport', this.num) .then(() => { if (this.valuefd) { this.stop() fs.close(this.valuefd) } this._pull(this.num, 'pn') }) .then(resolve('unexported and exited')) } else { resolve('already unexported') } }) .catch(err => resolve(err)) }) } start() { // this.inter(port).on('fired', () => { // console.log(`interrupt port ${port} hook me \n ${relays}`) // // hook.bind(this)(port) // // }) return this.clear().then(() => this.poller.add(this.valuefd, Epoll.EPOLLPRI | Epoll.EPOLLONESHOT)); } reset() { return this.clear().then(() => this.poller.modify(this.valuefd, Epoll.EPOLLPRI | Epoll.EPOLLONESHOT)); } stop() { this.poller.remove(this.valuefd).close(); } /*******private methods*********/ _pull(num, state = 'pu') { if (this.puller === 'external') { return Promise.resolve('pull must be set externally') } else { return exec(this.puller(num, state)) .then(() => { return Promise.resolve(`pull set ${state}`) }) } } _fd() { return fs.open(this.path + 'value', 'r+') .then(fd => { this.valuefd = fd return Promise.resolve(`value fd set ${fd}`) }) } _export(num) { return new Promise(function (resolve, reject) { fs.writeFile(GPIO_ROOT_PATH + 'export', num) .then(() => { return resolve('exported') }) .catch((err) => { return reject(err) }) }) } _direction(dir = 'in', delay = 50) { return pWaitFor(() => pathExists(this.path + 'direction')) .then(_.pDelay(delay)) .then(() => { return fs.writeFile(dir + 'direction', dir) .then(() => { return Promise.resolve(`direction set ${dir}`) }) }) } _edge(edge = 'both') { return fs.writeFile(this.path + 'edge', edge) .then(() => { return Promise.resolve(`edge set ${edge}`) }) } } module.exports = { Interrupt }