'use strict' const Device = require('uci-dev').Device, Port = require('uci-gpio').Port, _u = require('uci-utils'), pause = _u.pPause 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' } } // end constructor 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() // return _u.pSeries(jobs) } 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) }) } } } 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}`) } // 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) // } portByInterPin(pin) { return 'A' } 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 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(` config: wrote $ { byte } to register $ { reg } on port $ { port } `)) ) } } 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) } 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 } // end pinRead 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 } // 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' } } 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) } 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 } } // end MCP23017 Class module.exports.MCP23017 = MCP23017 // ============================== function portReg(reg, port) { if (port === 'B') { 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 } }