diff --git a/examples/mcp-switch-relay.js b/examples/mcp-switch-relay.js index 3784560..627a801 100644 --- a/examples/mcp-switch-relay.js +++ b/examples/mcp-switch-relay.js @@ -4,31 +4,35 @@ */ import { MCP230XXi } from '../src' import { MCP230XX } from '../src' +import Base from '@uci/base' -const BUS_HOST = 'sbc' +const BUS_HOST = 'sbc' // if ihost not given will be the same as host: const BUS_PORT = 1776 // 1776 is default port for i2c-bus module const SW_ADDRESS = 0x25 // 1776 is default port for i2c-bus module const RY_ADDRESS = 0x27 // 1776 is default port for i2c-bus module -// let chip8 = new MCP230XX({id:'mcp8-27', address:0x27, nmcp: { path: '/opt/sockets/mcp2.sock' }}) -// let sw17 = new MCP230XXi([9,10],{id:'sw17', chip17:true, host:BUS_HOST, address:SW_ADDRESS, iport:9000, 10:{mport:'B'}}) +let announce = new Base({id:'switch:announce'}) -let sw17 = new MCP230XXi([9,10],{id:'sw17', chip17:true, host:BUS_HOST, address:SW_ADDRESS+1, iport:true, 10:{mport:'B'}}) -let sw8 = new MCP230XXi([24],{id:'sw8', host:BUS_HOST, address:SW_ADDRESS, iport:true}) -let ry8 = new MCP230XX({id:'ry8', host:BUS_HOST, address:RY_ADDRESS}) -sw17.reply = () => {} -ry8.reply = () => {} -sw8.reply = () => {} +let sw17 = new MCP230XXi([9,10],{id:'sw17', chip17:true, host:BUS_HOST, address:SW_ADDRESS+1}) +// let sw8 = new MCP230XXi([24],{id:'sw8', host:BUS_HOST, address:SW_ADDRESS}) +// let ry8 = new MCP230XX({id:'ry8', host:BUS_HOST, address:RY_ADDRESS}) +const reply = { reply: () => {} } +// ry8.c = reply; +sw17.c=reply +// sw8.c= reply function iprocess(details) { console.log('--common interrupt processor--') console.log(details) - action(details) + announce.push() + // action function can do whatever but best would be to push the details to any attached client (mqtt or some database that then pushes + // local_action(details) } -async function action (details) { +// just something to do +async function local_action (details) { if (details.id === 'sw17') { - await ry8.pin.state.toggle({pins:details.pin}) + await ry8.pin.state.toggle({pins:'2,4,6,8'}) } if (details.id === 'sw8') { if (details.pin===1) { @@ -46,19 +50,15 @@ async function action (details) { (async () => { await sw17.init() - let cfg = {port:'B',pins:'1', cfg:'toggle_switch_pulldown'} - console.log(await sw17.pin.cfg(cfg)) sw17.interruptProcessor(iprocess) - - - await sw8.init() - sw8.interruptProcessor(iprocess) - - - await ry8.init() - let packet = {pins:'all', cfg:'output'} - await ry8.pin.cfg(packet) + // await sw8.init() + // sw8.interruptProcessor(iprocess) + // + // + // await ry8.init() + // let packet = {pins:'all', cfg:'output'} + // await ry8.pin.cfg(packet) })().catch(err => { console.error('FATAL: UNABLE TO START SYSTEM!\n',err) diff --git a/package.json b/package.json index e4aedb5..a10745f 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "@uci/mcp", "main": "src", - "version": "0.1.4", + "version": "0.1.8", "description": "Classes and Helper Functions for using the MCP chip on I2C Bus", "scripts": { "relays": "node -r esm examples/relays", + "swrl": "UCI_LOG=true node -r esm examples/mcp-switch-relay | pino-colada", "swr": "node -r esm examples/mcp-switch-relay", "test": "./node_modules/.bin/mocha --reporter list --timeout 30000", "testw": "./node_modules/.bin/mocha --reporter list -- watch --timeout 30000", diff --git a/src/commands.js b/src/commands.js index 1ba7286..955ba09 100644 --- a/src/commands.js +++ b/src/commands.js @@ -3,102 +3,105 @@ import _ from '@uci/utils/src/byte' import { CHIP, PIN } from './config' -export const chip = { +export default { + chip:{ // for custom chip configuration set packet.cfg='custom' then packet.setting should be a // configuration byte with given format ('STR' by defaul). - cfg: async function(packet){ + cfg: async function(packet){ // first make sure chip is in set to BANK=0 if not already - let bus = await this.bus.write(0x05,0) - if (bus.error) return bus - let setting = {} - let cfg = packet.cfg || 'default' - if (cfg === 'custom') setting = {val:packet.setting, fmt:packet.fmt || 'STR'} - else { - if (CHIP[cfg]) setting = CHIP[cfg] - else return {error:`no chip settings for ${cfg}`} + let bus = await this.bus.write(0x05,0) + if (bus.error) return bus + let setting = {} + let cfg = packet.cfg || 'default' + if (cfg === 'custom') setting = {val:packet.setting, fmt:packet.fmt || 'STR'} + else { + if (CHIP[cfg]) setting = CHIP[cfg] + else return {error:`no chip settings for ${cfg}`} + } + let byte = _.byteFormat(setting.val, { in: setting.fmt, out: 'DEC' }) + if (byte < 128) byte += 128 // make sure BANK=1 remains on + let reg = this.chip17 ? 0x0A : 0x05 + // console.log(this.chip17, reg, this.id, this.address) + bus = await this.bus.write(reg,byte) + if (bus.error) return bus + bus = await this.bus.read(0x05) + if (bus.error) return bus + return { cmd:'reply', response:_.byteFormat(bus.response,{in:'DEC',out:'STR'}) } } - let byte = _.byteFormat(setting.val, { in: setting.fmt, out: 'DEC' }) - if (byte < 128) byte += 128 // make sure BANK=1 remains on - let reg = this.chip17 ? 0x0A : 0x05 - // console.log(this.chip17, reg, this.id, this.address) - bus = await this.bus.write(reg,byte) - if (bus.error) return bus - bus = await this.bus.read(0x05) - if (bus.error) return bus - return { cmd:'reply', response:_.byteFormat(bus.response,{in:'DEC',out:'STR'}) } - } -} - -// Individual Pin Configurations -// set is for cfg: 'custom' assume a setting is zero unless given -// packet = { pin:, port:, cfg: , set:{dir: 0, ivrt: 0, pullup: 0, intr: 0, usedef: 0,defval: 0} } -// first get the current byte (pin) state for that setting -export const pin = { - cfg: async function(packet){ - let cfg = {} - let reply = { cmd:'reply', status:{} } - packet.cfg = packet.cfg || 'output' - if (packet.cfg==='custom') cfg = packet.set - else cfg = PIN.cfgset[packet.cfg] - for(let name of Object.keys(PIN.setting)) { - let op = cfg[name] ? 'on' : 'off' - // console.log(name, op) - let busreply = await this.pin._state(packet,op,PIN.setting[name]) - if (busreply.error) return busreply - reply.status[name] = busreply.status - } - return reply }, - // state is equivalent to read - status: async function (packet) { - let reg = packet.reg ? PIN.cmd[packet.reg] : PIN.cmd.gpio - if (!reg) return {error:`unknown register ${packet.reg}`, packet:packet } - let reply = { cmd:'reply'} - let pins = parsePins(packet.pins) - let state = new _.Byte() - let bus = await this.bus.read(sreg(reg, packet.port)) - if (bus.error) return bus - state.value = bus.response - reply.status = + // Individual Pin Configurations + // set is for cfg: 'custom' assume a setting is zero unless given + // packet = { pin:, port:, cfg: , set:{dir: 0, ivrt: 0, pullup: 0, intr: 0, usedef: 0,defval: 0} } + // first get the current byte (pin) state for that setting + pin:{ + cfg: async function(packet){ + let cfg = {} + let reply = { cmd:'reply', status:{} } + packet.cfg = packet.cfg || 'output' + if (packet.cfg==='custom') cfg = packet.set + else cfg = PIN.cfgset[packet.cfg] + for(let name of Object.keys(PIN.setting)) { + let op = cfg[name] ? 'on' : 'off' + // console.log(name, op) + let busreply = await this.pin._state(packet,op,PIN.setting[name]) + if (busreply.error) return busreply + reply.status[name] = busreply.status + } + return reply + }, + + // state is equivalent to read + status: async function (packet) { + let reg = packet.reg ? PIN.cmd[packet.reg] : PIN.cmd.gpio + if (!reg) return {error:`unknown register ${packet.reg}`, packet:packet } + let reply = { cmd:'reply'} + let pins = parsePins(packet.pins) + let state = new _.Byte() + let bus = await this.bus.read(sreg(reg, packet.port)) + if (bus.error) return bus + state.value = bus.response + reply.status = { port:state.toFmt('ARY'), pins: pins.value.map(pin => { if (state.toFmt('PLC').indexOf(pin) !==-1) return [pin, 'on'] else return [pin,'off'] }) } - return reply - }, - _state: async function(packet,op,reg){ - reg = (reg!==undefined)? reg : PIN.cmd.gpio - // console.log(op, reg, packet) - let reply = { cmd:'reply'} - let pins = parsePins(packet.pins) - let state = new _.Byte() - let bus = await this.bus.read(sreg(reg,packet.port)) - if (bus.error) return bus - state.value = bus.response - bus = await this.bus.write(sreg(reg,packet.port),state.bwOp(pins.value,op,{in:'PLC', out:'DEC'})) - if (bus.error) return bus - bus = await this.bus.read(sreg(reg,packet.port)) - if (bus.error) return bus - state.value = bus.response - reply.status = state.bwOp(pins.value,'check',{in:'PLC', out:'PLC'}) - return reply - }, - // threse three only for output pins - state : { - on: async function (packet) { - return this.pin._state(packet,'on') + return reply }, - off: async function (packet) { - return this.pin._state(packet,'off') + _state: async function(packet,op,reg){ + reg = (reg!==undefined)? reg : PIN.cmd.gpio + // console.log(op, reg, packet) + let reply = { cmd:'reply'} + let pins = parsePins(packet.pins) + let state = new _.Byte() + let bus = await this.bus.read(sreg(reg,packet.port)) + if (bus.error) return bus + state.value = bus.response + bus = await this.bus.write(sreg(reg,packet.port),state.bwOp(pins.value,op,{in:'PLC', out:'DEC'})) + if (bus.error) return bus + bus = await this.bus.read(sreg(reg,packet.port)) + if (bus.error) return bus + state.value = bus.response + reply.status = state.bwOp(pins.value,'check',{in:'PLC', out:'PLC'}) + return reply }, - toggle: async function (packet) { - return this.pin._state(packet,'toggle') + // threse three only for output pins + state : { + on: async function (packet) { + return this.pin._state(packet,'on') + }, + off: async function (packet) { + return this.pin._state(packet,'off') + }, + toggle: async function (packet) { + return this.pin._state(packet,'toggle') + } } - } -} // end pin. + } // end pin. + +} const parsePins = function(pins) { if (typeof pins==='number') pins = [pins] diff --git a/src/mcp230xx.js b/src/mcp230xx.js index c3641e2..32ac7ee 100644 --- a/src/mcp230xx.js +++ b/src/mcp230xx.js @@ -1,6 +1,6 @@ import Device from '@uci/i2c-device' // import Device from '../../uci-i2c-device/src/device-packet' -import { pin, chip } from './commands' +import commands from './commands' import logger from '@uci/logger' let log = {} @@ -10,13 +10,15 @@ export default class MCP230XX extends Device { super(opts) log = logger({file:'src/mcp230xx.js',class:'MCP230XX',name:'mcp',id:this.id}) this.chip17 = opts.chip17 - this.pin = this.bindFuncs(pin) - this.chip = this.bindFuncs(chip) + this.commands = this.bindFuncs(commands) + this.addNamespace('commands','s') // allow access to commands via socket/server + this.pin = this.commands.pin // add a simplier reference for local access + this.chipcfg = this.commands.chip.cfg // add a simplier reference for local access } async init(){ await super.init() - let res = await this.chip.cfg({}) + let res = await this.chipcfg({}) let cfg = this.chip17 ?'10100010':'00100010' if (res.response !==cfg ) throw `could not configure mcp chip at ${this.address}=0x${this.address.toString(16)}` } diff --git a/src/mcp230xxi.js b/src/mcp230xxi.js index 3d86c71..7cd468e 100644 --- a/src/mcp230xxi.js +++ b/src/mcp230xxi.js @@ -4,56 +4,64 @@ import { byteFormat } from '@uci/utils/src/byte' import logger from '@uci/logger' let log = {} -// sockets:'inter#s>t', inter:{port:INTERRUPT_PORT} +// if opts.iport not set then will be generated based on pin number export default class MCP230XXi extends MCP230XX { constructor(pins,opts) { if (typeof opts.iport ==='number' || opts.ipath) { if (typeof opts.iport ==='number') { - opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + 'inter#s>t' + 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#s>n' + opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + 'inter#c>n' } } else { - pins.forEach( pin => { + pins.forEach( (pin,index) => { let ipin = 'i'+pin opts[ipin] = opts[pin] || {} + if (index===1) opts[ipin].mport = opts[ipin].mort || 'B' + delete(opts[pin]) if (opts[ipin].port || opts.iport !=='number') { opts[ipin].port = opts[ipin].port || opts.iport if (typeof opts[ipin].port !=='number') opts[ipin].port = 9000 + pin - opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + ipin +'#s>t' + opts[ipin].host = opts[ipin].host || opts.ihost || opts.host + opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + ipin +'#c>t' } // either on the same host as bus and interrupt or not - no need for both else { opts[pin].path = opts[pin].path || opts.ipath if(!opts[pin].path) Object.assign(opts[pin],{path:'interrupt:'+pin}) - opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + pin+'#s>n' + opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + pin+'#c>n' } }) } super(opts) + log = logger({file:'src/mcp230xxi.js',class:'MCP230XXi',name:'mcp',id:this.id}) + log.info({opts:opts},'mcp interrupt options after calling super() on base') pins.forEach(pin => { - this[pin] = opts[pin] || {} + this[pin] = opts['i'+pin] || {} }) this.pins = pins - log = logger({file:'src/mcp230xxi.js',class:'MCP230XXi',name:'mcp',id:this.id}) - Object.assign(this.pin, this.bindFuncs(pin)) // add interrupt pin commands to base set in "command.js" + 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.ready = false + log.info({opts:opts, pins:pins}, 'options for mcp interrupt processor') } async init(){ await super.init() this.pins.forEach(async pin => { - // console.log(this.id,pin,this[pin]) - // let cfg = {port:this[pin].mport||'A',pins:this[pin].pins||'all', cfg:this[pin].type ||'toggle_switch'} - // console.log(cfg) - await this.pin.cfg({port:this[pin].mport||'A',pins:this[pin].pins||'all', cfg:this[pin].type ||'toggle_switch'}) - this.pin.interrupt.reset(this[pin].mport) + let cfg = {port:this[pin].mport||'A',pins:this[pin].pins||'all', cfg:this[pin].type ||'toggle_switch'} + await this.pin.cfg(cfg) + log.info('resetting mcp port for corresponding gpio pin') + await this.pin.interrupt.reset(this[pin].mport) + this.ready=true }) this.on('interrupt', function (details) { @@ -71,32 +79,34 @@ export default class MCP230XXi extends MCP230XX { } // end of MCP230XX Class -const pin = { interrupt: { +// commands to be added to pin command functions +const ipincommands= { interrupt: { reset: async function (port) { - console.log('resetting interrupt for port',port || 'A',this.id) + log.info(`resetting interrupt for port ${port || 'A'},${this.id}`) return await this.bus.read(port!=='B' ? 0x08 : 0x18) // 0x08 is intcap interrupt capture register }, - find: async function (inter) { - // console.dir(inter) - let packet = {pins:'all',reg:'intf'} - packet.port = inter.port || this[inter.pin].mport || 'A' - // console.log(`${inter.count}th interrupt fired, pin:${inter.pin}`) - // console.log('packet to read port\n',packet.port) - let res = await this.pin.status(packet) - this.pin.interrupt.reset(packet.port) - if (!res.status) return {error:'no pin associated with interrupt'} - let pin = byteFormat(res.status.port, { in: 'ARY', out: 'PLC' }) - // console.log('pin and port that caused the interrupt', pin, packet.port) - res.pin = pin[0] - packet.pins = pin[0] - packet.reg = null - res.state = (await this.pin.status(packet)).status.pins[0][1] - res.port = packet.port - res.count = inter.count - res.inter = inter.pin - delete res.status - this.emit('interrupt',res) - return res + // given a gpio interrupt then push a packet with cmd: 'pin.interrupt.find' and pin: the gpio pin number + find: async function (inter) { // inter is a hash packet + if(this.ready){ // protects tripped interrupt before it's fully initialized and reset + log.info({packet:inter},'finding mcp pin which caused interrupt') + let packet = {pins:'all',reg:'intf'} + packet.port = inter.port || this[inter.pin].mport || 'A' + let res = await this.pin.status(packet) + log.info('found pin now resetting mcp port interrupt') + await this.pin.interrupt.reset(packet.port) + 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 + res.state = (await this.pin.status(packet)).status.pins[0][1] + res.port = packet.port + res.count = inter.count + res.inter = inter.pin + delete res.status + this.emit('interrupt',res) + return res + } } } }