diff --git a/package.json b/package.json index 5c90ad8..1e3e4b7 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/config.js b/src/config.js index 931f882..a9dc3b9 100644 --- a/src/config.js +++ b/src/config.js @@ -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: { diff --git a/src/mcp230xxi.js b/src/mcp230xxi.js index fd48d19..3b16e93 100644 --- a/src/mcp230xxi.js +++ b/src/mcp230xxi.js @@ -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' } } } }