diff --git a/src/index.mjs b/src/index.mjs new file mode 100644 index 0000000..8c6513a --- /dev/null +++ b/src/index.mjs @@ -0,0 +1,14 @@ +import MCP230XX from './mcp230xx' +// import Interrupt from './interrupt' +// import Relays from './relays' +// import RelayBanks from './relay-banks' +// import Switches from './switches' +// import SwitchBanks from './switch-banks' + +export { MCP230XX as MCP230XX } +// export { Interrupt as Interrupt } +// export { Relays as Relays } +// export { RelayBanks as RelayBanks } +// export { Switches as Switches } +// export { SwitchBanks as SwitchBanks } +export default MCP230XX diff --git a/src/interrupt.mjs b/src/interrupt.mjs new file mode 100644 index 0000000..3c33f77 --- /dev/null +++ b/src/interrupt.mjs @@ -0,0 +1,50 @@ +import UCIInterrupt from '@uci/interrupt' + +import logger from '@uci/logger' +let log = {} +const LOG_OPTS = (id) => { + return { + repo:'uci-mcp', + npm:'@uci/mcp', + file:'src/interrupt.mjs', + class:'Interrupt', + id:id, + instance_created:new Date().getTime() + }} + +export default class Interrupt extends UCIInterrupt { + constructor(opts) { + super(opts) + log = logger.child(LOG_OPTS(this.id)) + if (!opts.address) log.fatal({opts:opts},'no i2c bus address supplied' ) + this.address = opts.address + this.chip17 = opts.chip17 + this.pin = this.bindFuncs(pin) + this.chip = this.bindFuncs(chip) + this.interrupt = opts.interrupt + } + + async init(){ + await super.init() + } + +} // end of MCP230XX Class + + let times = 0 +async interruptProcess(pin) { + times += 1 + console.log(`Interrupt occured for ${times}th time on pin ${pin}`) + let packet = {cmd:'pin.interrupt.find', pin:pin, times:times} + interrupt.send(packet) // via any attached consumers + } + + interrupt.reply = function (packet) { + console.log(`REPLY: pin ${packet.pin}:${packet.port} on ${packet._header.responder.instanceID} caused the ${packet.times}th interrupt on gpio pin ${packet.inter}`) + } + + await interrupt.init() + process.send('_ready') + +})().catch(err => { + console.error('FATAL: UNABLE TO START SYSTEM!\n',err) +}) diff --git a/src/mcp23008-17.mjs b/src/mcp23008-17.mjs deleted file mode 100644 index a4db9fc..0000000 --- a/src/mcp23008-17.mjs +++ /dev/null @@ -1,304 +0,0 @@ -'use strict' - -const Device = require('@uci/i2c').Device, - portpin = require('./port-pin'), // classes for MCP port and pins - EventEmitter = require('events'), - _ = require('@uci/utils'), - aggregate = require('aggregation/es6') - -const - Port = portpin.Port, - pinConfigs = portpin.configs, // for export so pinConfigs can be accessed at runtime - pause = _.pPause, - debug = _.debug('mcp:23008-17') - -class MCP23008 extends aggregate(Device, EventEmitter) { - constructor(busObj, i2cAddress, opts = {}) { - super(busObj, i2cAddress, opts) - // opts could include options passed on to ports and pin including custom pin config, pin ids...see gpio.js - this.chip_config = opts.chip_config // TODO allow opts.chip_config to be a byte instead of a string pointer - this.ports = {} - opts.portID = 'A' - opts.pids = opts.pids ? opts.pids : opts.pidsA - this.ports.A = new Port(opts) - this.ports.A.state = new _.Byte(opts.stateA) - this.ports.A.interrupt = opts.interruptA ? opts.interruptA : opts.interrupt - } // end constructor - - // helpers - pin(id) { return this.ports.A.pin(id) } // get a reference to a particular pin's object - - pid(address) { return this.ports.A.pid(address) } // return pin id for a given address on a port - - pid(num) { return this.ports.A.pins[num - 1].id } - - portByPin(id) { - if (this.ports.A.pin(id)) { return 'A' } - return false - } - - state(port = 'A') { - return this.ports[port].state.value - } - - // get a handle to the ports interrupt - inter(port = 'A') { - return this.ports[port].interrupt - } - - // Must call after instantiating. - - async init() { - debug.L1(`\n=======\nInitializing ${this.id}`) - await this.writeChipCfg(this.chip_config) // chip settings - await this.writePinsCfg() - // write out any initial state(s) - for (let port in this.ports) { - if (this.state(port)) { - debug.L1(`writing initial port state ${port} ${this.state(port)}`) - await this.writePort(this.state(port), 'force', port) - } - } - } - - // must call after init if using interrupts - async start() { - debug.L1(`starting ${ this.id }`) - for (let port in this.ports) { - // if there are interrupts being used then start them and listeners - if (this.inter(port)) { - this.startInterrupt(port) - } - } - } - - async startInterrupt(port) { - await this.interruptReset(port) - debug.L1(`starting interrupt on port ${ port }`) - await this.inter(port).start() - let chip = this // scope `this` for use in the listener below - // bind handler to the chip so handler can read/write to chip/bank when interrupt is emitted - debug.L3('handler', this.inter(port).handler) - let ihandler = this.inter(port).handler.bind(this) - // inside the listener `this` is the interrupt not the chip/bank - this.inter(port).on('fired', async function () { - debug.L1(`interrupt from ${this.pin_number}`) - let data = await ihandler(port) - debug.L2(`port interrupt event 'fired' data => ${data.bank} on port ${data.port} pin# ${data.pin} pid ${data.pid}`) - chip.emit('fired', data) // emit up the class chain - }) - - } - - async interruptReset(port = 'A') { - await this.read(portReg(0x08, port)) - await pause(100) // give enough time for mcp to reset its interupt - debug.L1(`interrupt reset on ${this.id} port ${port}`) - } - - async interruptPin(port, format) { - if ('AB'.indexOf(port) === -1) { - format = port ? port : null - port = 'A' - } - let result = await this.read(portReg(0x07, port)) - return format ? _.byteFormat(result, { in: 'DEC', out: format }) : result - - } - - async writeChipCfg(cfg = 'default') { - let setting = chip_config[cfg] - let byte = _.byteFormat(setting.val, { in: setting.fmt, out: 'DEC' }) - debug.L1(`writing mcp chip config ${setting.val}`) - await this.write(chip_config.cmd, byte) - } - - // pin configurations should already be set before calling - async writePinsCfg() { - debug.L1('writing mcp pins config') - for (let port in this.ports) { - for (let setting in registers.pin_config) { - let reg = registers.pin_config[setting] - // TODO 0x10 should be based on chip config - let byte = 0 - for (let pin of this.ports[port].pins) { - byte += pin.address * pin.cfg[setting] - debug.L3(`port: ${ port } pin: ${pin.id} setting: ${ setting } reg: ${ reg } byte: ${ byte }`) - } - await this.write(portReg(reg, port), byte) - } - } - debug.L1('done writing mcp pins config') - } // end writePinsCfg - - async readPort(port, opts = {}) { - if ('AB'.indexOf(port) === -1) { - opts = port ? port : {} - port = 'A' - } - let cmd = opts.cmd ? opts.cmd : 'gpio' - let result = await this.read(portReg(registers.pin_cmd[cmd], port)) - debug.L2('read prev ', this.ports[port].state.toFmtPrev('ARY')) - debug.L2('read result', _.byteFormat(result, { in: 'DEC', out: 'ARY' })) - debug.L2('read state ', this.ports[port].state.toFmt('ARY')) - // if port pins changed without a write then update state - if (_.bitChanges(_.byteFormat(result, { in: 'DEC', out: 'ARY' }), this.ports[port].state.toFmt('ARY'))) { - this.ports[port].state.value = result - } - return opts.format ? this.ports[port].state.toFmt(opts.format) : result - } - - async readPin(pin, port, opts = {}) { - if ('AB'.indexOf(port) === -1) { - opts = port ? port : {} - port = 'A' - } - await this.readPort(port, opts) - // TODO return just true or false not the decimal of pin - will need new version on npm - return this.ports[port].state.bwOp(Math.pow(2, pin - 1), 'check') ? true : false - } - - async writePort(byte, op, port = 'A') { - // byte MUST be a decimal - debug.L2(`port:${port}, op:${op}, current pins state\n ${this.ports[port].state.toFmt('PLC') }`) - debug.L2(`byte passed \n [${ _.byteFormat(byte, { in: 'DEC', out: 'PLC'})}]:${byte}`) - if (op !== 'force') { - byte = this.ports[port].state.bwOp(byte, op) - } - debug.L2(`byte to write\n [${ _.byteFormat(byte, { in: 'DEC', out: 'PLC' })}]:${byte}\n=======`) - await this.write(portReg(registers.pin_cmd.gpio, port), byte) - //update to the saved state - this.ports[port].state.value = byte - } - - async pinsOn(pins, port, format) { - if ('AB'.indexOf(port) === -1) { - format = port - port = 'A' - } - if (format) { pins = _.byteFormat(pins, { in: format, out: 'DEC' }) } - await this.writePort(pins, 'on', port) - } - - async allOn(port = 'A') { - let pins = 255 - await this.writePort(pins, 'force', port) - } - - async pinsOff(pins, port, format) { - if ('AB'.indexOf(port) === -1) { - format = port - port = 'A' - } - if (format) { pins = _.byteFormat(pins, { in: format, out: 'DEC' }) } - await this.writePort(pins, 'off', port) - } - - async allOff(port = 'A') { - let pins = 0 - await this.writePort(pins, 'force', port) - } - - async toggle(pins, port, format) { - if ('AB'.indexOf(port) === -1) { - format = port - port = 'A' - } - if (format) { pins = _.byteFormat(pins, { in: format, out: 'DEC' }) } - await this.writePort(pins, 'toggle', port) - } - - async allToggle(port = 'A') { - let pins = 255 - await this.writePort(pins, 'toggle', port) - } - - async force(pins, port, format) { - if ('AB'.indexOf(port) === -1) { - format = port - port = 'A' - } - if (format) { pins = _.byteFormat(pins, { in: format, out: 'DEC' }) } - await this.writePort(pins, 'force', port) - } - -} // end 23008 - -class MCP23017 extends MCP23008 { - constructor(busObj, i2cAddress, opts = {}) { - super(busObj, i2cAddress, opts) - // add a second port - opts.portID = 'B' - opts.pids = opts.pidsB - this.ports.B = new Port(opts) - this.ports.B.state = new _.Byte(opts.stateB) - this.ports.B.interrupt = opts.interruptB ? opts.interruptB : opts.interrupt - } - - pin(id, port) { - if (!port) { - return this.ports.A.pin(id) ? this.ports.A.pin(id) : this.ports.B.pin(id) - } - return this.ports[port].pin(id) - } - - pid(num, port) { - console.log('pin for pid',this.ports[port].pins[num - 1]) - return this.ports[port].pins[num - 1].id - } - -} // end MCP23017 Class - -// EXPORTS -module.exports = { - MCP23008, - MCP23017, - pinConfigs -} - -// Local Functions and Settings============================= - -function portReg(reg, port) { - // TODO check chip configuartion to know what to add for port B. CUrrenly assume IOCON=1 - // index 0,1 ==== 'A','B' - if ((port === 'B') || (port === 1)) { return reg += 0x10 } else { return reg } -} - -/* ww1.microchip.com/downloads/en/DeviceDoc/21952b.pdf - * or see MCP23017.pdf and MCP 23008.pdf in /docs - * see table 1.5 and details in sections 1.6.x following - * !!! for 23017 MUST initialize with bit 7 "BANK" of IOCON = 1 for the addresses below - * then for all registers add 16 (0x10) to each reg address to talk to PortB pins - * this will make reg addresses be equilvant for 23008 and PortA of 23017 - * reg addresses in the config objects are all in Hexidecminal - */ -// Chip Configuration to be used with Register See Page 18 of 23017 doc -let chip_config = { - // byte: ['NULL','INTPOL','ODR','HAEN','DISSLW','SEQOP','MIRROR','BANK'] // see page 18 of 23017 datasheet for 8 setting details - cmd: 0x0A, // IOCON.BANK=0 (msb) at powerup so need to use 0x0A, if set to 1 then use - default: { - val: '10100010', // Split Banks port A + 0x10 = Port B,(ignored by 23008), Sequential operation disabled, active high=pulldown - fmt: 'STR' - }, - oneint: { - val: '11100100', // same as default execpt int pins connected - fmt: 'STR' - } -} - -let registers = { - pin_config: { - dir: 0, - ivrt: 1, - pullup: 6, - intr: 2, - usedef: 4, - defval: 3, - }, - pin_cmd: { - intf: 7, // readonly - intcap: 8, // readonly - gpio: 9, // read/write - olat: 10 // read only - } -} diff --git a/src/mcp230xx-packet.mjs b/src/mcp230xx.mjs similarity index 96% rename from src/mcp230xx-packet.mjs rename to src/mcp230xx.mjs index 818a54d..46039f8 100644 --- a/src/mcp230xx-packet.mjs +++ b/src/mcp230xx.mjs @@ -9,7 +9,7 @@ const LOG_OPTS = (id) => { return { repo:'uci-mcp', npm:'@uci/mcp', - file:'src/mcp230xx-packet.mjs', + file:'src/mcp230xx.mjs', class:'MCP230XX', id:id, instance_created:new Date().getTime()