uci-interrupt/lib/interrupt.js

147 lines
4.0 KiB
JavaScript

"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()
])
}
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() {
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
}