0.1.27 reworked mcp interrupt class to be more robust resetting the interrupt

added a reset command to allow other processes to trigger an mcp port interrupt reset
master
David Kebler 2019-04-11 21:15:26 -07:00
parent d28d05b2aa
commit ea9a04f6dc
3 changed files with 135 additions and 104 deletions

View File

@ -1,7 +1,7 @@
{
"name": "@uci/mcp",
"main": "src",
"version": "0.1.26",
"version": "0.1.27",
"description": "Classes and Helper Functions for using the MCP chip on I2C Bus",
"scripts": {
"relays": "node -r esm examples/relays",

View File

@ -25,12 +25,12 @@ export const CHIP = {
export const PIN = {
setting: { // these are the mcp registers = command for each setting
dir: 0,
ivrt: 1,
pullup: 6,
intr: 2,
usedef: 4,
defval: 3,
dir: 0, // IODIR 0=output 1=input
ivrt: 1, // IPOL will report the opposite state if set
intr: 2, //GPITEN 1= pin will throw interrupt (following registers are for interrupt)
pullup: 6, // GPPU 1=internal pullup 0=none
usedef: 4, // INTCON - 0= interrupt on any change, 1= interrupt on change of value from defval
defval: 3, // DEFVAL default value for interrupt comparison - usedef must be set
},
cfgset:{
output: {

View File

@ -14,44 +14,48 @@ log = logger({
class MCP230XXi extends MCP230XX {
constructor(pins, options={}) {
if (!Array.isArray(pins)) { options = pins; pins = options.interrupt.pins} // if pins sent via .interrupt.pins
let opts = Object.assign({},options) // don't allow passed options to mutate
if (opts.interrupt) delete opts.interrupt.pins // if .interrupt was passed then .pins must be removed
delete opts.sockets // .sockets is used by uci base so clear it if was used by enduser and sent by mistake
log.debug({msg:'passed options before setting',options:opts})
// if options doesn't contain a pin number key with mport and other interrupt pin related information
// then it is assume that pins[0] is port A and pins[1] is port B
// if iport or ipath is set then all interrupts have a single consolidating socket
if (typeof opts.iport === 'number' || opts.ipath) {
if (typeof opts.iport === 'number') {
opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'inter#c>t'
opts.inter = { port: opts.iport }
opts.inter.host = opts.ihost || opts.host
}
if (opts.ipath) {
opts.inter = { path: opts.ipath || 'interrupt' }
opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'inter#c>n'
}
} else {
// otherwise each pin will have its own socket so make a client for each
pins.forEach((pin, index) => {
let ipin = 'i' + pin
opts[ipin] = opts[pin] || Object.assign({},opts.interrupt) || {}
if (index === 1) opts[ipin].mport = opts[ipin].mport || 'B'
delete opts[pin]
if (opts[ipin].host) {
opts[ipin].port = opts[ipin].port ? opts[ipin].port + +pin : 9000 + parseInt(pin)
opts[ipin].host = opts[ipin].host
opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + ipin + '#c>t'
}
// no host will make a pipe by default
else {
opts[ipin].path = (opts[ipin].path || 'interrupt') + ':'+ pin
opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + ipin + '#c>n'
}
})
}
// if (typeof opts.iport === 'number' || opts.ipath) {
// if (typeof opts.iport === 'number') {
// opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'inter#c>t'
// opts.inter = { port: opts.iport }
// opts.inter.host = opts.ihost || opts.host
// }
// if (opts.ipath) {
// opts.inter = { path: opts.ipath || 'interrupt' }
// opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'inter#c>n'
// }
// } else {
// // otherwise each pin will have its own socket so make a client for each
// pins.forEach((pin, index) => {
// let ipin = 'i' + pin
// opts[ipin] = opts[pin] || Object.assign({},opts.interrupt) || {}
// if (index === 1) opts[ipin].mport = opts[ipin].mport || 'B'
// delete opts[pin]
// if (opts[ipin].host) {
// opts[ipin].port = opts[ipin].port ? opts[ipin].port + +pin : 9000 + parseInt(pin)
// opts[ipin].host = opts[ipin].host
// opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + ipin + '#c>t'
// }
// // no host will make a pipe by default
// else {
// opts[ipin].path = (opts[ipin].path || 'interrupt') + ':'+ pin
// opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + ipin + '#c>n'
// }
// })
// }
super(opts)
this.opts = opts
log = logger({
file: 'src/mcp230xxi.js',
@ -66,94 +70,121 @@ class MCP230XXi extends MCP230XX {
this[pin] = opts['i' + pin] || {}
})
this.pins = pins
Object.assign(this.commands.pin, this.bindFuncs(ipincommands)) // add interrupt pin commands to base set in "command.js"
this.addNamespace('commands', 'c')
this._interruptProcess = null
this.commands.interrupt = this.bindFuncs(icommands) // add interrupt pin commands to base set in "command.js"
this.addNamespace('commands', 'c') // add access via push to same commands
this._interruptProcess = process
this.ready = false
log.info({ opts: opts, pins: pins }, 'options for mcp interrupt processor')
}
async init() {
if (this.ipath) await this.addSocket('inter-n','s','n',{path:this.ipath})
if (this.iport) await this.addSocket('inter-t','s','t',{port:this.iport})
await super.init()
for (let pin of this.pins) {
let cfg = {
port: this[pin].mport || 'A',
pins: this[pin].pins || 'all',
cfg: this[pin].type || 'input_interrupt'
}
// this will set default type to internal pullup, only need to to change indivial pins to external if desired
await this.pin.cfg(cfg)
log.info('initial resetting of mcp interrupt port for corresponding sbc gpio pin')
await this._reset(this[pin].mport)
this.ready = true
// this will set default type to internal pullup, only need to to change indivial pins to external if desired
await this.pin.cfg({port:'A',pins:'all',cfg:'input_interrupt'})
let status = await this._resetInterrupt('A')
log.debug('configure all port A pins as interrupts. Reset port A interrupt', status)
if (this.chip17) {
await this.pin.cfg({port:'B',pins:'all',cfg:'input_interrupt'})
let status = await this._resetInterrupt('B')
log.debug('configure all port B pins as interrupts. Reset port B interrupt', status)
}
}
async _readyInterrupt(port) {
let istate = await this.bus.read(port !== 'B' ? 0x07 : 0x17)
if (istate.response) {
this.ready = false
return false
}
else {
this.ready = true
return true
}
this.on('interrupt', function(details) {
details.id = this.id
if (!this._interruptProcess) {
console.log('----default interrupt processor for mcp instance----')
console.log('create your own function and register it with .interruptProcess(function)')
console.log(this.id)
console.dir(details)
} else this._interruptProcess(details)
})
}
async _reset(port) {
// local non-packet hidden command
async _resetInterrupt(port) {
log.debug(`resetting interrupt for port ${port || 'A'},${this.id} arg ${port !== 'B' ? 0x08 : 0x18}`)
return await this.bus.read(port !== 'B' ? 0x08 : 0x18) // 0x08 is intcap interrupt capture register
await this.bus.read(port !== 'B' ? 0x08 : 0x18) // 0x08 is intcap interrupt capture register
return(await this._readyInterrupt(port))
}
_getPortByPin(pin) {
let port = this[pin] ? this[pin].mport : null
let index = this.pins.map(pin => pin.toString()).indexOf(pin.toString())
port = port || index <= 0 ? 'A' : 'B'
return port
}
interruptProcessor(func) {
this._interruptProcess = func
}
} // end of MCP230XXi Class
export default MCP230XXi
// default processor
function process(details) {
details.id = this.id
console.log('----default interrupt processor for mcp instance----')
console.log('here is where you could either locally take some action for send/push on a message to another process')
console.log('create your own function and register it with .interruptProcesser(function)')
console.log(this.id)
console.dir(details)
}
// commands to be added to pin packet command functions
const ipincommands = {
interrupt: {
reset: async function(packet) {
log.info(
{ packet: packet },
`'socket has push requested a reset of mcp interrupt port for gpio pin ${
packet.pin
}`
)
let port = this[packet.pin].mport || 'A'
await this._reset(port)
},
// given a gpio interrupt then push a packet with cmd: 'pin.interrupt.find' and pin: the gpio pin number
find: async function(inter) {
if (this.ready) {
// protects tripped interrupt before it's fully initialized, or interrupt requests arriving before porcessing is complete
this.ready = false
log.debug({msg:'raw packet from interrupt, will now find the pin that caused interrupt', inter:inter})
let packet = { pins: 'all', reg: 'intf' }
packet.port = inter.port || this[inter.pin].mport || 'A'
let res = await this.pin.status(packet)
if (!res.status) return { error: 'no pin associated with interrupt' }
let pin = byteFormat(res.status.port, { in: 'ARY', out: 'PLC' })
res.pin = pin[0]
packet.pins = pin[0]
packet.reg = null
if (packet.pins) {
// avoid bad interrupt (i.e. no pin caused interrupt)
res.ipin = inter.pin
res.port = packet.port
delete inter.cmd; delete inter._header; delete inter.pin
Object.assign(res,inter)
delete res.status
res.state = (await this.pin.status(packet)).status.pins[0][1]
console.log('emitted interrupt packet', res)
this.emit('interrupt', res)
log.debug({msg:'found pin now resetting mcp port interrupt', response:res})
}
await this._reset(packet.port)
this.ready = true
const icommands = {
status: async function(packet) {
// pin is interrupt pin on sbc for port
let port = packet.port || this._getPortByPin(packet.pin)
let res = await this._readyInterrupt(port)
return {cmd:'reply', request:'interrupt.status', port:port, ipin:packet.pin, ready:res}
},
reset: async function(packet) {
// pin is interrupt pin on sbc for port
let port = packet.port || this._getPortByPin(packet.pin)
log.info({ packet: packet, port:port, msg:`remote interrupt reset for port ${port}`})
await this._resetInterrupt(port)
let res = await this._readyInterrupt(port)
return {cmd:'reply', request:'interrupt.reset', port:port, ipin:packet.pin, ready:res}
},
// given a gpio interrupt then push a packet with cmd: 'pin.interrupt.find' and pin: the gpio pin number
find: async function(inter) {
if (this.ready) {
// protects tripped interrupt before it's fully initialized, or interrupt requests arriving before porcessing is complete
this.ready = false
log.debug({msg:'raw packet from interrupt, finding pin that caused interrupt', inter:inter})
let packet = { pins: 'all', reg: 'intf' }
packet.port = inter.port || this._getPortByPin(inter.pin)
let res = await this.pin.status(packet) // read port interrupt status
let status = await this._resetInterrupt(packet.port)
log.debug({interrupt:res, reset:status, msg:'interrupt read and reset'})
this.ready = true
if (!res.status) {
log.warn({inter:inter, msg:'no pin associated with interrupt'})
return { error: 'no pin associated with interrupt' }
}
let pin = byteFormat(res.status.port, { in: 'ARY', out: 'PLC' })
res.pin = pin[0]
packet.pins = pin[0]
packet.reg = null
if (packet.pins) { // avoid bad interrupt (i.e. no pin caused interrupt)
res.ipin = inter.pin
res.port = packet.port
delete inter.cmd; delete inter._header; delete inter.pin
Object.assign(res,inter)
delete res.status
res.state = (await this.pin.status(packet)).status.pins[0][1]
log.debug('emit/call interrupt details processing', res)
this.emit('mcpInterrupt',Object.assign({},res)) // emit for end user purposes, don't allow mutation
this._interruptProcess(res)
return res
} else {
log.warn({inter:inter, msg:'no pin associated with interrupt'})
return { error: 'no pin associated with interrupt' }
}
}
}