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 resetmaster
parent
d28d05b2aa
commit
ea9a04f6dc
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@uci/mcp",
|
"name": "@uci/mcp",
|
||||||
"main": "src",
|
"main": "src",
|
||||||
"version": "0.1.26",
|
"version": "0.1.27",
|
||||||
"description": "Classes and Helper Functions for using the MCP chip on I2C Bus",
|
"description": "Classes and Helper Functions for using the MCP chip on I2C Bus",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"relays": "node -r esm examples/relays",
|
"relays": "node -r esm examples/relays",
|
||||||
|
|
|
@ -25,12 +25,12 @@ export const CHIP = {
|
||||||
|
|
||||||
export const PIN = {
|
export const PIN = {
|
||||||
setting: { // these are the mcp registers = command for each setting
|
setting: { // these are the mcp registers = command for each setting
|
||||||
dir: 0,
|
dir: 0, // IODIR 0=output 1=input
|
||||||
ivrt: 1,
|
ivrt: 1, // IPOL will report the opposite state if set
|
||||||
pullup: 6,
|
intr: 2, //GPITEN 1= pin will throw interrupt (following registers are for interrupt)
|
||||||
intr: 2,
|
pullup: 6, // GPPU 1=internal pullup 0=none
|
||||||
usedef: 4,
|
usedef: 4, // INTCON - 0= interrupt on any change, 1= interrupt on change of value from defval
|
||||||
defval: 3,
|
defval: 3, // DEFVAL default value for interrupt comparison - usedef must be set
|
||||||
},
|
},
|
||||||
cfgset:{
|
cfgset:{
|
||||||
output: {
|
output: {
|
||||||
|
|
225
src/mcp230xxi.js
225
src/mcp230xxi.js
|
@ -14,44 +14,48 @@ log = logger({
|
||||||
|
|
||||||
class MCP230XXi extends MCP230XX {
|
class MCP230XXi extends MCP230XX {
|
||||||
constructor(pins, options={}) {
|
constructor(pins, options={}) {
|
||||||
|
|
||||||
if (!Array.isArray(pins)) { options = pins; pins = options.interrupt.pins} // if pins sent via .interrupt.pins
|
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
|
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
|
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
|
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})
|
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 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.ipath) {
|
||||||
if (typeof opts.iport === 'number') {
|
// if (typeof opts.iport === 'number') {
|
||||||
opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'inter#c>t'
|
// opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'inter#c>t'
|
||||||
opts.inter = { port: opts.iport }
|
// opts.inter = { port: opts.iport }
|
||||||
opts.inter.host = opts.ihost || opts.host
|
// opts.inter.host = opts.ihost || opts.host
|
||||||
}
|
// }
|
||||||
if (opts.ipath) {
|
// if (opts.ipath) {
|
||||||
opts.inter = { path: opts.ipath || 'interrupt' }
|
// opts.inter = { path: opts.ipath || 'interrupt' }
|
||||||
opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'inter#c>n'
|
// opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'inter#c>n'
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
// otherwise each pin will have its own socket so make a client for each
|
// // otherwise each pin will have its own socket so make a client for each
|
||||||
pins.forEach((pin, index) => {
|
// pins.forEach((pin, index) => {
|
||||||
let ipin = 'i' + pin
|
// let ipin = 'i' + pin
|
||||||
opts[ipin] = opts[pin] || Object.assign({},opts.interrupt) || {}
|
// opts[ipin] = opts[pin] || Object.assign({},opts.interrupt) || {}
|
||||||
if (index === 1) opts[ipin].mport = opts[ipin].mport || 'B'
|
// if (index === 1) opts[ipin].mport = opts[ipin].mport || 'B'
|
||||||
delete opts[pin]
|
// delete opts[pin]
|
||||||
if (opts[ipin].host) {
|
// if (opts[ipin].host) {
|
||||||
opts[ipin].port = opts[ipin].port ? opts[ipin].port + +pin : 9000 + parseInt(pin)
|
// opts[ipin].port = opts[ipin].port ? opts[ipin].port + +pin : 9000 + parseInt(pin)
|
||||||
opts[ipin].host = opts[ipin].host
|
// opts[ipin].host = opts[ipin].host
|
||||||
opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + ipin + '#c>t'
|
// opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + ipin + '#c>t'
|
||||||
}
|
// }
|
||||||
// no host will make a pipe by default
|
// // no host will make a pipe by default
|
||||||
else {
|
// else {
|
||||||
opts[ipin].path = (opts[ipin].path || 'interrupt') + ':'+ pin
|
// opts[ipin].path = (opts[ipin].path || 'interrupt') + ':'+ pin
|
||||||
opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + ipin + '#c>n'
|
// opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + ipin + '#c>n'
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
super(opts)
|
super(opts)
|
||||||
|
this.opts = opts
|
||||||
|
|
||||||
log = logger({
|
log = logger({
|
||||||
file: 'src/mcp230xxi.js',
|
file: 'src/mcp230xxi.js',
|
||||||
|
@ -66,94 +70,121 @@ class MCP230XXi extends MCP230XX {
|
||||||
this[pin] = opts['i' + pin] || {}
|
this[pin] = opts['i' + pin] || {}
|
||||||
})
|
})
|
||||||
this.pins = pins
|
this.pins = pins
|
||||||
Object.assign(this.commands.pin, this.bindFuncs(ipincommands)) // add interrupt pin commands to base set in "command.js"
|
this.commands.interrupt = this.bindFuncs(icommands) // add interrupt pin commands to base set in "command.js"
|
||||||
this.addNamespace('commands', 'c')
|
this.addNamespace('commands', 'c') // add access via push to same commands
|
||||||
this._interruptProcess = null
|
this._interruptProcess = process
|
||||||
this.ready = false
|
this.ready = false
|
||||||
log.info({ opts: opts, pins: pins }, 'options for mcp interrupt processor')
|
log.info({ opts: opts, pins: pins }, 'options for mcp interrupt processor')
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
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()
|
await super.init()
|
||||||
for (let pin of this.pins) {
|
// this will set default type to internal pullup, only need to to change indivial pins to external if desired
|
||||||
let cfg = {
|
await this.pin.cfg({port:'A',pins:'all',cfg:'input_interrupt'})
|
||||||
port: this[pin].mport || 'A',
|
let status = await this._resetInterrupt('A')
|
||||||
pins: this[pin].pins || 'all',
|
log.debug('configure all port A pins as interrupts. Reset port A interrupt', status)
|
||||||
cfg: this[pin].type || 'input_interrupt'
|
if (this.chip17) {
|
||||||
}
|
await this.pin.cfg({port:'B',pins:'all',cfg:'input_interrupt'})
|
||||||
// this will set default type to internal pullup, only need to to change indivial pins to external if desired
|
let status = await this._resetInterrupt('B')
|
||||||
await this.pin.cfg(cfg)
|
log.debug('configure all port B pins as interrupts. Reset port B interrupt', status)
|
||||||
log.info('initial resetting of mcp interrupt port for corresponding sbc gpio pin')
|
}
|
||||||
await this._reset(this[pin].mport)
|
}
|
||||||
this.ready = true
|
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) {
|
async _resetInterrupt(port) {
|
||||||
// local non-packet hidden command
|
|
||||||
log.debug(`resetting interrupt for port ${port || 'A'},${this.id} arg ${port !== 'B' ? 0x08 : 0x18}`)
|
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) {
|
interruptProcessor(func) {
|
||||||
this._interruptProcess = func
|
this._interruptProcess = func
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end of MCP230XXi Class
|
} // end of MCP230XXi Class
|
||||||
|
|
||||||
export default MCP230XXi
|
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
|
// commands to be added to pin packet command functions
|
||||||
const ipincommands = {
|
const icommands = {
|
||||||
interrupt: {
|
status: async function(packet) {
|
||||||
reset: async function(packet) {
|
// pin is interrupt pin on sbc for port
|
||||||
log.info(
|
let port = packet.port || this._getPortByPin(packet.pin)
|
||||||
{ packet: packet },
|
let res = await this._readyInterrupt(port)
|
||||||
`'socket has push requested a reset of mcp interrupt port for gpio pin ${
|
return {cmd:'reply', request:'interrupt.status', port:port, ipin:packet.pin, ready:res}
|
||||||
packet.pin
|
},
|
||||||
}`
|
reset: async function(packet) {
|
||||||
)
|
// pin is interrupt pin on sbc for port
|
||||||
let port = this[packet.pin].mport || 'A'
|
let port = packet.port || this._getPortByPin(packet.pin)
|
||||||
await this._reset(port)
|
log.info({ packet: packet, port:port, msg:`remote interrupt reset for port ${port}`})
|
||||||
},
|
await this._resetInterrupt(port)
|
||||||
// given a gpio interrupt then push a packet with cmd: 'pin.interrupt.find' and pin: the gpio pin number
|
let res = await this._readyInterrupt(port)
|
||||||
find: async function(inter) {
|
return {cmd:'reply', request:'interrupt.reset', port:port, ipin:packet.pin, ready:res}
|
||||||
if (this.ready) {
|
},
|
||||||
// protects tripped interrupt before it's fully initialized, or interrupt requests arriving before porcessing is complete
|
// given a gpio interrupt then push a packet with cmd: 'pin.interrupt.find' and pin: the gpio pin number
|
||||||
this.ready = false
|
find: async function(inter) {
|
||||||
log.debug({msg:'raw packet from interrupt, will now find the pin that caused interrupt', inter:inter})
|
if (this.ready) {
|
||||||
let packet = { pins: 'all', reg: 'intf' }
|
// protects tripped interrupt before it's fully initialized, or interrupt requests arriving before porcessing is complete
|
||||||
packet.port = inter.port || this[inter.pin].mport || 'A'
|
this.ready = false
|
||||||
let res = await this.pin.status(packet)
|
log.debug({msg:'raw packet from interrupt, finding pin that caused interrupt', inter:inter})
|
||||||
if (!res.status) return { error: 'no pin associated with interrupt' }
|
let packet = { pins: 'all', reg: 'intf' }
|
||||||
let pin = byteFormat(res.status.port, { in: 'ARY', out: 'PLC' })
|
packet.port = inter.port || this._getPortByPin(inter.pin)
|
||||||
res.pin = pin[0]
|
let res = await this.pin.status(packet) // read port interrupt status
|
||||||
packet.pins = pin[0]
|
let status = await this._resetInterrupt(packet.port)
|
||||||
packet.reg = null
|
log.debug({interrupt:res, reset:status, msg:'interrupt read and reset'})
|
||||||
if (packet.pins) {
|
this.ready = true
|
||||||
// avoid bad interrupt (i.e. no pin caused interrupt)
|
if (!res.status) {
|
||||||
res.ipin = inter.pin
|
log.warn({inter:inter, msg:'no pin associated with interrupt'})
|
||||||
res.port = packet.port
|
return { error: 'no pin associated with interrupt' }
|
||||||
delete inter.cmd; delete inter._header; delete inter.pin
|
}
|
||||||
Object.assign(res,inter)
|
let pin = byteFormat(res.status.port, { in: 'ARY', out: 'PLC' })
|
||||||
delete res.status
|
res.pin = pin[0]
|
||||||
res.state = (await this.pin.status(packet)).status.pins[0][1]
|
packet.pins = pin[0]
|
||||||
console.log('emitted interrupt packet', res)
|
packet.reg = null
|
||||||
this.emit('interrupt', res)
|
if (packet.pins) { // avoid bad interrupt (i.e. no pin caused interrupt)
|
||||||
log.debug({msg:'found pin now resetting mcp port interrupt', response:res})
|
res.ipin = inter.pin
|
||||||
}
|
res.port = packet.port
|
||||||
await this._reset(packet.port)
|
delete inter.cmd; delete inter._header; delete inter.pin
|
||||||
this.ready = true
|
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
|
return res
|
||||||
|
} else {
|
||||||
|
log.warn({inter:inter, msg:'no pin associated with interrupt'})
|
||||||
|
return { error: 'no pin associated with interrupt' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue