diff --git a/lib/mcp23008-17.js b/lib/mcp23008-17.js index 12b1e6b..594d810 100644 --- a/lib/mcp23008-17.js +++ b/lib/mcp23008-17.js @@ -1,256 +1,226 @@ 'use strict' const Device = require('uci-dev').Device, - Port = require('uci-gpio').Port, - _u = require('uci-utils'), - pause = _u.pPause + Port = require('uci-gpio').Port, + _u = require('uci-utils'), + pause = _u.pPause, + aseq = _u.aSeq class MCP23008 extends Device { - constructor(busObj, i2cAddress, opts = {}) { - super(busObj, i2cAddress, opts) - // opts could pass in array of custom pin config, or single for all, or anything - // this.registers = registers // load in register database - this.ports = {} - opts.portID = 'A' - opts.pids = opts.pids ? opts.pids : opts.pidsA - this.ports.A = new Port(opts) - this.chip_config = opts.chip_config // TODO allow opts.chip_config to be a byte instead of a string pointer - this.ports.A.interrupt = opts.interruptA ? opts.interruptA : opts.interrupt - // if (this.ports.A.interrupt) { this.ports.A.interrupt.port = 'A' } + 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 _u.Byte(opts.stateA) + this.ports.A.interrupt = opts.interruptA ? opts.interruptA : opts.interrupt + } // end constructor - } // end constructor + async init() { + // console.log(`begin initialize ${this.id}`) + await this.writeChipCfg(this.chip_config) // chip settings + await this.writePinsCfg() + for (let port in this.ports) { + console.log(`init port ${port} state`) + await this.writePort(this.state(port), 'force', port) + } + } - async init() { - // console.log('chip configuration', chip_config.cmd, chipSetByte()) - // console.log(`begin initialize ${this.id}`) - await this.writeChipCfg(this.chip_config) - // let jobs = [this.writeChipCfg(this.chip_config)] // configure chip - // for (let port in this.ports) { - // if (this.inter(port)) { - // console.log(`initialize interrupt port ${port}`) - // jobs.push( - // this.inter(port).init() - // .then(sbci => { - // this.inter(port).sbci = sbci - // console.log(`sbci ${this.inter(port).sbci}`) - // }) - // ) - // // sbci = single board computer interrrupt to distinguish it from interrupt on MCP - // } - // } - // jobs.push(this.writePinsCfg()) - await this.writePinsCfg() + async start() { + // console.log(`starting ${ this.id }`) + for (let port in this.ports) { + // if there are interrupts being used then start them and listeners + if (this.inter(port)) { + await this.interruptReset(port) + await this.inter(port).start() + // bind handler to the chip so handler can read/write to chip/bank when interrupt is emitted + let ihandler = this.inter(port).handler.bind(this) + // inside the listener `this` is the interrupt not the chip/bank + this.inter(port).on('fired', function () { + console.log(`interrupt from ${this.pin_number}`) + ihandler(port) + }) + } + } + } - // return _u.pSeries(jobs) - } + async interruptReset(port = 'A') { + await this.read(portReg(0x08, port)) + await pause(100) // give enough time for mcp to reset its interupt + // console.log(`interrupt reset on ${this.id} port ${port}`) + } - async start() { - // console.log(`starting ${ this.id }`) - for (let port in this.ports) { - // if there are interrupts being used then start them and listeners - if (this.inter(port)) { - await this.interruptReset(port) - await this.inter(port).start() - // bind handler to the chip to handler can read/write to chip/bank when interrupt is emitted - let ihandler = this.inter(port).handler.bind(this) - // inside the listener `this` is the interrupt not the chip/bank - this.inter(port).on('fired', function () { - console.log(`interrupt from ${this.pin_number}`) - ihandler(port) - }) - } - } - } + pin(id) { return this.ports.A.pin(id) } // get a reference to a particular pin's object - async interruptReset(port = 'A') { - await this.read(portReg(0x08, port)) - await pause(300) // give enough time for mcp to reset its interupt - console.log(`interrupt reset on ${this.id} port ${port}`) - } + pid(address) { return this.ports.A.pid(address) } // return pin id for a given address on a port - // start() { - // let starts = [] - // for (let port in this.ports) { - // if (this.inter(port)) { - // starts.push( - // this.read(portReg(0x08, port)) - // .then(_u.pDelay(100)) // give enough time for mcp to reset its interupt - // .then(() => { - // return this.inter(port).start() - // .then(resp => { - // console.log(resp) - // // this.inter(port).on('fired', () => { - // // console.log(` - //interrupt port $ { port } hook me\ n $ { relays } - // `) - // // // hook.bind(this)(port) - // return Promise.resolve() - // }) - // return Promise.resolve(` - //interrupt port $ { port } started `) - // }) - // .catch(err => console.log("error:", err)) - // ) - // } - // } - // return Promise.all(starts) - // } + portByPin(id) { + if (this.ports.A.pin(id)) { return 'A' } + return false + } - portByInterPin(pin) { - return 'A' - } + state(port = 'A') { + return this.ports[port].state.value + } - pin(id) { return this.ports.A.pin(id) } // get a reference to a particular pin's object + // get a handle to the ports interrupt + inter(port = 'A') { + return this.ports[port].interrupt + } - pid(address) { return this.ports.A.pid(address) } // return pin id for a given address on a port + writeChipCfg(cfg = 'default') { + // console.log(`writing mcp chip config ${this.id}`) + let setting = chip_config[cfg] + let byte = _u.byteFormat(setting.val, { in: setting.fmt, + out: 'DEC' + }) + return this.write(chip_config.cmd, byte) + .then(() => Promise.resolve(`mcp chip config: reg $ { chip_config.cmd } byte $ { byte } written `)) + } - portByPin(id) { - if (this.ports.A.pin(id)) { return 'A' } - return false - } - - // get a handle to the ports interrupt - inter(port = 'A') { - return this.ports[port].interrupt - } - - writeChipCfg(cfg = 'default') { - // console.log(`writing mcp chip config ${this.id}`) - let setting = chip_config[cfg] - let byte = _u.byteFormat(setting.val, { in: setting.fmt, - out: 'DEC' - }) - return super.write(chip_config.cmd, byte) - .then(() => Promise.resolve(`mcp chip config: reg $ { chip_config.cmd } byte $ { byte } written `)) - } - - // pin configurations should already be set before calling - writePinsCfg() { - // console.log(`writing mcp pins config ${this.id}`) - let jobs = []; - 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; - let pins = this.ports[port].allPins - for (let i = 0; i < 8; i++) { - let pin = pins[i] - byte += pin.address.toFmt('DEC') * pin.cfg[setting] - } - //console.log(`port $ { port } - setting $ { setting } - reg $ { reg } - byte $ { byte }`) - jobs.push( - super.write(portReg(reg, port), byte).then(() => Promise.resolve(` + // pin configurations should already be set before calling + writePinsCfg() { + // console.log(`writing mcp pins config ${this.id}`) + let jobs = []; + 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; + let pins = this.ports[port].allPins + for (let i = 0; i < 8; i++) { + let pin = pins[i] + byte += pin.address.toFmt('DEC') * pin.cfg[setting] + } + //console.log(`port $ { port } - setting $ { setting } - reg $ { reg } - byte $ { byte }`) + jobs.push( + this.write(portReg(reg, port), byte).then(() => Promise.resolve(` config: wrote $ { byte } to register $ { reg } on port $ { port } `)) - ) - } - } - return _u.pSeries(jobs) - } // end writePinsCfg + ) + } + } + return _u.pSeries(jobs) + } // end writePinsCfg - // reads all pins in all ports - readPins(cmd = 'gpio', fmt) { - let jobs = [] - let reg = registers.pin_cmd[cmd] - for (let port in this.ports) { - jobs.push(() => super.read(portReg(reg, port))) - } - return _u.pSeries(jobs) - } + // read(format = 'PLC') { + // return u_.byteFormat(this.cur, { in: this.format, out: format }) + // } - pinRead(id, opts) { - let mcpdev = this; - return new Promise(function (resolve, reject) { - let cmd = opts.cmd ? opts.cmd : 'gpio' - let fmt = opts.fmt ? opts.fmt : { in: 'PLC', out: 'PLC' } - let reg = registers.pin_cmd[cmd] - let gpio = mcpdev.pin(id) - if (!gpio) { - reject('no pin found for given id') - } - console.log('id of fired pin', id) - console.log('port of fired pin', mcpdev.portByPin(id)) - return mcpdev.read(portReg(reg, mcpdev.portByPin(id))).then(resp => { - resp = _u.byteFormat(resp, { in: 'DEC', - out: 'ARY' - }) - let addr = gpio.adr.toFmt('ARY') - // console.log(addr) - // console.log(resp) - resolve(_u.sum(_u.and(addr, resp))) // resolve 1 or 0 - }).catch(err => reject(err)) // end Promise - }) // end promise + async readPort(port, cmd = "gpio") { + if ('AB'.indexOf(port) === -1) { + cmd = port ? port : "gpio" + port = 'A' + } + let result = await this.read(portReg(registers.pin_cmd[cmd], port)) + this.ports[port].state.value = result + return result + } - } // end pinRead + async writePort(byte, op, port = 'A') { + // byte MUST be a decimal + console.log(`current \n ${this.ports[port].state.toFmt('PLC')}`) + console.log(`byte passed ${op} \n ${_u.byteFormat(byte,{in: 'DEC', out:'PLC'})}`) + if (op !== 'force') { + byte = this.ports[port].state.bwOp(byte, op) + } + console.log("byte to write decimal \n", byte) + console.log(`byte to write \n ${_u.byteFormat(byte, { in: 'DEC', out: 'PLC' })}`) - pinWrite(id, opts) { - let mcpdev = this; - return new Promise(function (resolve, reject) { - let cmd = opts.cmd ? opts.cmd : 'gpio' - let reg = registers.pin_cmd[cmd] - let gpio = mcpdev.pin(id, opts.port) - if (!gpio) { - reject('no pin found for given id') - } - if (gpio.port === 'B') { - reg = reg + 16 - } - // call device class read - // console.log('port - reg', gpio.port, reg) - return mcpdev.read(reg).then(resp => { - resp = _u.byteFormat(resp, { in: 'DEC', - out: 'ARY' - }) - let addr = gpio.pin.address.toFmt('ARY') - // // console.log(addr) - // console.log(resp) - resolve(_u.sum(_u.and(addr, resp))) // resolve 1 or 0 - }) - }) - // .then(state => console.log(`pin state $ { state }`)) - .catch(err => console.log(err)) // end Promise - } // end pinRead + await this.write(portReg(registers.pin_cmd.gpio, port), byte) + //update the saved state + this.ports[port].state.value = byte + console.log("\n\n") + } + + async on(pins, port, format) { + if ('AB'.indexOf(port) === -1) { + format = port + port = 'A' + } + if (format) { pins = _u.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 off(pins, port, format) { + if ('AB'.indexOf(port) === -1) { + format = port + port = 'A' + } + if (format) { pins = _u.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 = _u.byteFormat(pins, { in: format, out: 'DEC' }) } + await this.writePort(pins, 'toggle', port) + } + + async allToggle(port = 'A') { + let pins = 255 + await this.writePort('toggle', port) + } + + async force(pins, port, format) { + if ('AB'.indexOf(port) === -1) { + format = port + port = 'A' + } + if (format) { pins = _u.byteFormat(pins, { in: format, out: 'DEC' }) } + await this.writePort(pins, 'force', port) + } } // end 23008 + module.exports.MCP23008 = MCP23008 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.interrupt = opts.interruptB ? opts.interruptB : opts.interrupt - // if (this.ports.B.interrupt) { this.ports.B.interrupt.hook = 'B' } - } + 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 _u.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) - } + 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(address, port) { - if (!port) { - return this.ports.A.pid(address) - } - return this.ports[port].pid(address) - } + pid(address, port) { + if (!port) { + return this.ports.A.pid(address) + } + return this.ports[port].pid(address) + } - portByInterPin(pin) { - if (this.ports.A.interrupt.pin_number === pin) { return 'A' } - if (this.ports.B.interrupt.pin_number === pin) { return 'B' } - return false - } - - portByPin(id) { - console.log('pin id in portbypin', id) - if (this.ports.A.pin(id)) { return 'A' } - if (this.ports.B.pin(id)) { return 'B' } - return false - } + portByPin(id) { + console.log('pin id in portbypin', id) + if (this.ports.A.pin(id)) { return 'A' } + if (this.ports.B.pin(id)) { return 'B' } + return false + } } // end MCP23017 Class module.exports.MCP23017 = MCP23017 @@ -258,7 +228,9 @@ module.exports.MCP23017 = MCP23017 // ============================== function portReg(reg, port) { - if (port === 'B') { return reg += 0x10 } else { return reg } + // TODO what to return for port B will depend on chip configuration + // if not letter assume index 0,1. + if ((port === 'B') || (port === 1)) { return reg += 0x10 } else { return reg } } /* ww1.microchip.com/downloads/en/DeviceDoc/21952b.pdf @@ -271,31 +243,31 @@ function portReg(reg, port) { */ // 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' - } + // 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 - } + 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 + } }