0.1.38
commands.js added pin.cfgs for multiple pin configurations refactored pin.status, pin._state to better handle multiple pins and 08 vs 17 mpc230xx.js changed to not having default sockets but socket can be added by passing lport or lpath now supports new new uci-utils/ready module and adds an observer for when chip and pins are configured mcp230xxi.js now supports new new uci-utils/ready module by adds an observer for when the interrupt process connects and also resets refactored the interrupt methods and commands to compliment changes in the interrupt modulemaster
parent
9650698683
commit
9a7fdd9539
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
* i2c bus unix socket and client in one for demo
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Base from '../../uci-base/src/base'
|
|
||||||
import { spawn } from 'child_process'
|
|
||||||
|
|
||||||
const PATH = '/opt/sockets/mcp.sock'
|
|
||||||
|
|
||||||
const delay = time => new Promise(res=>setTimeout(()=>res(),time))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const i2cbus = spawn('node',['-r', '@std/esm', './examples/bus'])
|
|
||||||
i2cbus.stdout.on('data', function(buf) {
|
|
||||||
console.log('[I2C BUS]', String(buf))
|
|
||||||
})
|
|
||||||
|
|
||||||
;
|
|
||||||
(async () => {
|
|
||||||
|
|
||||||
let mcpclient = new Base({id:'mcpclient', sockets:'uc#c>n', uc:{path:PATH}})
|
|
||||||
|
|
||||||
mcpclient.reply = function (packet) {
|
|
||||||
console.log('for request ',packet._header.request)
|
|
||||||
console.log('mcp status is ',packet.status)
|
|
||||||
}
|
|
||||||
|
|
||||||
await mcpclient.init()
|
|
||||||
console.log('=============sending============')
|
|
||||||
// const pins='2,3,4'
|
|
||||||
// const pins=3
|
|
||||||
// const pins='all'
|
|
||||||
// const pins=[1,3,5,7]
|
|
||||||
|
|
||||||
let packet = {cmd:'pin.cfg', pins:'all'}
|
|
||||||
await mcpclient.send(packet)
|
|
||||||
packet = {cmd:'pin.cfg', pins:'all', port:'B'}
|
|
||||||
await mcpclient.send(packet)
|
|
||||||
packet = {cmd:'pin.state.off', pins:'all' }
|
|
||||||
await mcpclient.send(packet)
|
|
||||||
packet = {cmd:'pin.state.off', pins:'all', port:'B' }
|
|
||||||
await mcpclient.send(packet)
|
|
||||||
packet = {cmd:'pin.state.on', pins:'2,7', port:'B' }
|
|
||||||
await mcpclient.send(packet)
|
|
||||||
packet = {cmd:'pin.state.on', pins:'3,6'}
|
|
||||||
await mcpclient.send(packet)
|
|
||||||
packet = {cmd:'pin.status', pins:'all'}
|
|
||||||
await mcpclient.send(packet)
|
|
||||||
packet = {cmd:'pin.status', pins:'all', port:'B'}
|
|
||||||
await mcpclient.send(packet)
|
|
||||||
|
|
||||||
process.kill(process.pid, 'SIGTERM')
|
|
||||||
|
|
||||||
|
|
||||||
})().catch(err => {
|
|
||||||
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
|
|
||||||
})
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
import { MCP230XXi } from '../src'
|
|
||||||
import { MCP230XX } from '../src'
|
|
||||||
import Base from '@uci/base'
|
|
||||||
|
|
||||||
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 announce = new Base({id:'switch:announce'})
|
|
||||||
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// just something to do
|
|
||||||
async function local_action (details) {
|
|
||||||
if (details.id === 'sw17') {
|
|
||||||
await ry8.pin.state.toggle({pins:'2,4,6,8'})
|
|
||||||
}
|
|
||||||
if (details.id === 'sw8') {
|
|
||||||
if (details.pin===1) {
|
|
||||||
if (details.state==='off') await ry8.pin.state.off({pins:'all'})
|
|
||||||
else ry8.pin.state.on({pins:'all'})
|
|
||||||
}
|
|
||||||
if (details.pin===8) {
|
|
||||||
if (details.state==='off') await ry8.pin.state.off({pins:'1,3,5,7'})
|
|
||||||
else await ry8.pin.state.on({pins:'1,3,5,7'})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
|
|
||||||
await sw17.init()
|
|
||||||
sw17.interruptProcessor(iprocess)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
})
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* i2c bus with both unix and tcp socket using defaults. For TCP that is host OS name and port 8080
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
import MCP230XX from '../src/mcp230xx-packet'
|
|
||||||
// const PATH = ''
|
|
||||||
|
|
||||||
;
|
|
||||||
(async () => {
|
|
||||||
|
|
||||||
// let chip8 = new MCP230XX({id:'mcp8-27', address:0x27, nmcp: { path: '/opt/sockets/mcp2.sock' }})
|
|
||||||
let chip17 = new MCP230XX({id:'mcp17-26', chip17:true, address:0x26})
|
|
||||||
|
|
||||||
await chip17.init()
|
|
||||||
// await chip8.init()
|
|
||||||
console.log(await chip17.pin.cfg({pins:'all'}))
|
|
||||||
// console.log(await chip17.pin.cfg({pins:'all', cfg:'toggle_switch'}))
|
|
||||||
// console.log(await chip17.pin.cfg({pins:'all', port:'B', cfg:'toggle_switch'}))
|
|
||||||
// console.log(await chip8.pin.cfg({pins:'all', cfg:'toggle_switch'}))
|
|
||||||
|
|
||||||
})().catch(err => {
|
|
||||||
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
|
|
||||||
})
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import { MCP230XX } from '../src'
|
||||||
|
|
||||||
|
// const TRANSPORT = process.env.TRANSPORT || 'tcp'
|
||||||
|
const HOST = process.env.BUS_HOST || 'sbc'
|
||||||
|
const PORT = process.env.BUS_PORT || 1776
|
||||||
|
const ADDRESS = process.env.DEVICE_ADDR || 39
|
||||||
|
const CHIP17 = !!process.env.CHIP17 || false
|
||||||
|
|
||||||
|
;
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
let outputs = new MCP230XX({id:'mcp', chip17:CHIP17, address:ADDRESS, bus:{host:HOST, port:PORT}})
|
||||||
|
|
||||||
|
console.log(await outputs.socketsInit())
|
||||||
|
|
||||||
|
await new Promise((resolve) => setTimeout(resolve,50))
|
||||||
|
|
||||||
|
outputs.on('ready:mcp', async ev => {
|
||||||
|
console.log('mcpready', ev)
|
||||||
|
// all pins are outputs by default
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
console.log('pass', i)
|
||||||
|
await outputs.commands.pin.state.on({pins:'all'})
|
||||||
|
await outputs.commands.pin.state.on({port:'B', pins:'all'}) // will be ingnored on mcp23008
|
||||||
|
await new Promise((resolve) => setTimeout(resolve,50))
|
||||||
|
await outputs.commands.pin.state.off({pins:'all'})
|
||||||
|
await outputs.commands.pin.state.off({port:'B', pins:'all'}) // will be ingnored on mcp23008
|
||||||
|
}
|
||||||
|
|
||||||
|
process.kill(process.pid, 'SIGTERM')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})().catch(err => {
|
||||||
|
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
|
||||||
|
})
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import { MCP230XXi } from '../src'
|
||||||
|
|
||||||
|
const HOST = process.env.BUS_HOST
|
||||||
|
const PATH = process.env.BUS_PATH
|
||||||
|
const PORT = process.env.BUS_PORT || 1776
|
||||||
|
const ADDRESS = process.env.DEVICE_ADDR || 0x26
|
||||||
|
const CHIP17 = !!process.env.CHIP17 || true
|
||||||
|
const LISTEN_PORT = process.env.PORT || 9001
|
||||||
|
|
||||||
|
let switches = new MCP230XXi([9,10],{id:'switches', chip17:CHIP17, path:PATH, host:HOST, port:PORT, address:ADDRESS, iport:LISTEN_PORT})
|
||||||
|
|
||||||
|
// switches.registerSocket('interrupt','c','t',{host:'switchesd.local', port:1777})
|
||||||
|
|
||||||
|
;
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
if (process.env.VERBOSE==='true') {
|
||||||
|
|
||||||
|
switches.on('log', async log => {
|
||||||
|
if (log.level!=='trace') {
|
||||||
|
console.log(
|
||||||
|
'LOG:',log.level,':',
|
||||||
|
log.msg,
|
||||||
|
'socket:',log.socketName,
|
||||||
|
)
|
||||||
|
if (log.packet) console.dir(log.packet)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
switches.on('connection:socket', async ev => {
|
||||||
|
console.log('connection event: outbound > ', ev.state,'to socket',ev.socketName)// if (ev.state ==='connected') switches.duplex = true
|
||||||
|
})
|
||||||
|
|
||||||
|
switches.on('connection:consumer', async ev => {
|
||||||
|
// console.log(ev)
|
||||||
|
console.log('connection event: inbound >', ev.state,'from consumer',ev.name)// if (ev.state ==='connected') switches.duplex = true
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('ready observers for switches', switches.ready.observerNames)
|
||||||
|
|
||||||
|
switches.ready.addObserverDetails('bus',{msg:'this is some details related to bus observer'})
|
||||||
|
|
||||||
|
|
||||||
|
switches.ready.subscribe(x=>{
|
||||||
|
console.log('=====================switches ready================?',x ? 'YES':'NO')
|
||||||
|
console.log('what has failed: ',switches.ready.failure,' details:', switches.ready.details.get(switches.ready.failure)||'none')
|
||||||
|
})
|
||||||
|
|
||||||
|
// this.ready.subscribe('interrupt:connected',(res)=>console.log('interrupt connected............',res))
|
||||||
|
// this.ready.subscribe('mcp',(res)=>console.log('mcp............',res))
|
||||||
|
// this.ready.subscribe('interrupt:reset',(res)=>console.log('interrupt reset............',res))
|
||||||
|
|
||||||
|
let res = await switches.socketsInit()
|
||||||
|
if (res.errors) console.log(res)
|
||||||
|
|
||||||
|
})().catch(err => {
|
||||||
|
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
|
||||||
|
})
|
18
package.json
18
package.json
|
@ -1,10 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "@uci/mcp",
|
"name": "@uci/mcp",
|
||||||
"main": "src",
|
"main": "src",
|
||||||
"version": "0.1.37",
|
"version": "0.1.38",
|
||||||
"description": "Classes and Helper Functions for using the MCP chip on I2C Bus",
|
"description": "Classes and Helper Functions for using the MCP chip on I2C Bus",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"relays": "node -r esm examples/relays",
|
"outputs": "./node_modules/.bin/nodemon -r esm --preserve-symlinks examples/outputs",
|
||||||
|
"switches": "./node_modules/.bin/nodemon -r esm --preserve-symlinks examples/switches",
|
||||||
"swrl": "UCI_ENV=dev node -r esm examples/mcp-switch-relay",
|
"swrl": "UCI_ENV=dev node -r esm examples/mcp-switch-relay",
|
||||||
"swr": "node -r esm examples/mcp-switch-relay",
|
"swr": "node -r esm examples/mcp-switch-relay",
|
||||||
"test": "./node_modules/.bin/mocha --reporter list --timeout 30000",
|
"test": "./node_modules/.bin/mocha --reporter list --timeout 30000",
|
||||||
|
@ -28,9 +29,16 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/uCOMmandIt/uci-mcp#readme",
|
"homepage": "https://github.com/uCOMmandIt/uci-mcp#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uci/i2c-device": "^0.1.21",
|
"@uci-utils/bind-funcs": "^0.2.4",
|
||||||
"@uci-utils/logger": "^0.0.15",
|
"@uci-utils/byte": "^0.2.3",
|
||||||
"@uci-utils/byte": "^0.2.3"
|
"@uci-utils/logger": "0.0.15",
|
||||||
|
"@uci-utils/ready": "^0.1.2",
|
||||||
|
"@uci/base": "^0.1.36",
|
||||||
|
"@uci/i2c-device": "^0.1.24",
|
||||||
|
"@uci/logger": "0.0.6",
|
||||||
|
"@uci/socket": "^0.2.28",
|
||||||
|
"is-plain-object": "^3.0.0",
|
||||||
|
"merge-anything": "^2.4.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
|
|
|
@ -4,7 +4,6 @@ import logger from '@uci-utils/logger'
|
||||||
|
|
||||||
let log = logger({file:'/src/commands.js', package:'@uci/mcp'})
|
let log = logger({file:'/src/commands.js', package:'@uci/mcp'})
|
||||||
|
|
||||||
|
|
||||||
// TODO add uci debug logging
|
// TODO add uci debug logging
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -37,7 +36,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Individual Pin Configurations
|
// Pin(s) Configurations
|
||||||
// set is for cfg: 'custom' assume a setting is zero unless given
|
// 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} }
|
// 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
|
// first get the current byte (pin) state for that setting
|
||||||
|
@ -45,9 +44,9 @@ export default {
|
||||||
cfg: async function(packet){
|
cfg: async function(packet){
|
||||||
let cfg = {}
|
let cfg = {}
|
||||||
let reply = { cmd:'reply', status:{} }
|
let reply = { cmd:'reply', status:{} }
|
||||||
packet.cfg = packet.cfg || 'output'
|
packet.config = packet.config || packet.cfg || 'output'
|
||||||
if (packet.cfg==='custom') cfg = packet.set
|
if (packet.config==='custom') cfg = packet.set
|
||||||
else cfg = PIN.cfgset[packet.cfg]
|
else cfg = PIN.cfgset[packet.config]
|
||||||
for(let name of Object.keys(PIN.setting)) {
|
for(let name of Object.keys(PIN.setting)) {
|
||||||
let op = cfg[name] ? 'on' : 'off'
|
let op = cfg[name] ? 'on' : 'off'
|
||||||
log.debug({cmd:'pin.cfg', line:28, msg:'setting pin register', operation:op, registerName:name, resgisterNum:PIN.setting[name]})
|
log.debug({cmd:'pin.cfg', line:28, msg:'setting pin register', operation:op, registerName:name, resgisterNum:PIN.setting[name]})
|
||||||
|
@ -58,31 +57,40 @@ export default {
|
||||||
return reply
|
return reply
|
||||||
},
|
},
|
||||||
|
|
||||||
// state is equivalent to read
|
cfgs: async function({configs}){
|
||||||
|
let res = await Promise.all(configs.map(config => this.commands.pin.cfg(config)))
|
||||||
|
let error = res.reduce((err,cfg) => {return (err || !!cfg.error)},false)
|
||||||
|
return ({error:error, res:res})
|
||||||
|
},
|
||||||
|
|
||||||
|
// status is equivalent to read
|
||||||
status: async function (packet) {
|
status: async function (packet) {
|
||||||
|
if(!this.chip17 && packet.port==='B') return {error:'sent request for port B. No such port on MCP2008'}
|
||||||
let reg = packet.reg ? PIN.cmd[packet.reg] : PIN.cmd.gpio
|
let reg = packet.reg ? PIN.cmd[packet.reg] : PIN.cmd.gpio
|
||||||
if (!reg) return {error:`unknown register ${packet.reg}`, packet:packet }
|
if (!reg) return {error:`unknown register ${packet.reg}`, packet:packet }
|
||||||
let reply = { cmd:'reply'}
|
let pins = parsePins(packet.pins || packet.pin)
|
||||||
let pins = parsePins(packet.pins)
|
|
||||||
let state = new _.Byte()
|
let state = new _.Byte()
|
||||||
let bus = await this.bus.read(sreg(reg, packet.port))
|
let bus = await this.bus.read(sreg(reg, packet.port))
|
||||||
if (bus.error) return bus
|
if (bus.error) return bus
|
||||||
state.value = bus.response
|
state.value = bus.response
|
||||||
reply.status =
|
let reply = {
|
||||||
{ port:state.toFmt('ARY'),
|
port:state.toFmt('ARY'),
|
||||||
pins: pins.value.map(pin => {
|
pins: pins.value.map(pin => {
|
||||||
if (state.toFmt('PLC').indexOf(pin) !==-1) return [pin, 'on']
|
if (state.toFmt('PLC').indexOf(pin) !==-1) return [pin, 'on']
|
||||||
else return [pin,'off']
|
else return [pin,'off']
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (packet.pin) reply.state = reply.pins[0][1]
|
||||||
return reply
|
return reply
|
||||||
},
|
},
|
||||||
_state: async function(packet,op,reg){
|
_state: async function(packet,op,reg){
|
||||||
|
if(!this.chip17 && packet.port==='B') return {error:'sent request for port B. No such port on MCP2008'}
|
||||||
reg = (reg!==undefined)? reg : PIN.cmd.gpio
|
reg = (reg!==undefined)? reg : PIN.cmd.gpio
|
||||||
log.debug({cmd:'pin._state', line:82, msg:'_state change request', operation:op, register:reg, packet:packet})
|
log.debug({cmd:'pin._state', line:82, msg:'_state change request', operation:op, register:reg, packet:packet})
|
||||||
let reply = { cmd:'reply'}
|
let reply = { cmd:'reply'}
|
||||||
let pins = parsePins(packet.pins)
|
let pins = parsePins(packet.pins || packet.pin)
|
||||||
let state = new _.Byte()
|
let state = new _.Byte()
|
||||||
|
// TODO support setting state on both ports if non given
|
||||||
let bus = await this.bus.read(sreg(reg,packet.port))
|
let bus = await this.bus.read(sreg(reg,packet.port))
|
||||||
if (bus.error) return bus
|
if (bus.error) return bus
|
||||||
state.value = bus.response
|
state.value = bus.response
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import Device from '@uci/i2c-device'
|
import { I2CDevice as Device, map, changed, isPlainObject, to, merge} from '@uci/i2c-device'
|
||||||
// import Device from '../../uci-i2c-device/src/device-packet'
|
|
||||||
import commands from './commands'
|
import commands from './commands'
|
||||||
|
|
||||||
import logger from '@uci-utils/logger'
|
import logger from '@uci-utils/logger'
|
||||||
let log = {}
|
let log = {}
|
||||||
|
|
||||||
|
@ -16,32 +14,30 @@ class MCP230XX extends Device {
|
||||||
})
|
})
|
||||||
this.chip17 = opts.chip17
|
this.chip17 = opts.chip17
|
||||||
this.chipCfg = opts.chipCfg || 'default'
|
this.chipCfg = opts.chipCfg || 'default'
|
||||||
// this._configured = false
|
this.pinsCfg = opts.pinsCfg || this.chip17 ? [{port:'A', pins:'all'},{port:'B', pins:'all'}] : [{pins:'all'}]
|
||||||
|
|
||||||
this.commands = this.bindFuncs(commands)
|
this.commands = this.bindFuncs(commands)
|
||||||
this.addNamespace('commands', 's') // allow access to commands via socket/server
|
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
|
|
||||||
this.once('ready:i2c', () =>{
|
|
||||||
this.configure()
|
|
||||||
if (this._ready) this._ready.call(this)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async registerReadyFunc(func) {
|
if (opts.lport) this.registerSocket('mcp-t','s','t',{port:opts.lport})
|
||||||
this._ready = func
|
if (opts.lpath) this.registerSocket('mcp-n','s','n',{path: opts.lpath})
|
||||||
}
|
|
||||||
|
|
||||||
async configure() {
|
this.ready.addObserver('mcp',this.ready.getObserver('i2c').pipe(
|
||||||
|
map(async ready => {
|
||||||
|
if (ready) {
|
||||||
let res = await this.commands.chip.cfg({cfg:this.chipCfg})
|
let res = await this.commands.chip.cfg({cfg:this.chipCfg})
|
||||||
if (res.error) {
|
if (!res.error) {
|
||||||
let err={level:'fatal', msg:'unable to configure mcp chip', error:res.error, cfg:this.chipCfg, address:this.address}
|
let res = await this.commands.pin.cfgs({configs:this.pinsCfg})
|
||||||
log.fatal(err)
|
return res.error? false :true
|
||||||
this.emit('status', err)
|
} return false
|
||||||
} else {
|
|
||||||
this.emit('status', {level:'info', msg:'mcp chip was properly configured', cfg:this.chipCfg})
|
|
||||||
this.emit('ready:mcp')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
))
|
||||||
|
|
||||||
|
} // end contructor
|
||||||
|
|
||||||
} // end of MCP230XX Class
|
} // end of MCP230XX Class
|
||||||
|
|
||||||
export default MCP230XX
|
export default MCP230XX
|
||||||
|
export {MCP230XX, map, changed, isPlainObject, to, merge}
|
||||||
|
|
164
src/mcp230xxi.js
164
src/mcp230xxi.js
|
@ -1,5 +1,6 @@
|
||||||
import MCP230XX from './mcp230xx'
|
import {MCP230XX, map, changed, isPlainObject, to, merge} from './mcp230xx'
|
||||||
import { byteFormat } from '@uci-utils/byte'
|
import { byteFormat } from '@uci-utils/byte'
|
||||||
|
// import Ready from '@uci-utils/ready'
|
||||||
|
|
||||||
import logger from '@uci-utils/logger'
|
import logger from '@uci-utils/logger'
|
||||||
let log = {}
|
let log = {}
|
||||||
|
@ -17,11 +18,11 @@ class MCP230XXi extends MCP230XX {
|
||||||
if (!Array.isArray(pins)) { options = pins; pins = options.interrupt.pins} // if pins sent via .interrupt.pins
|
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
|
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
|
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({method:'constructor', line:21, msg:'passed options before setting',options:opts})
|
log.debug({method:'constructor', line:21, msg:'passed options before setting',options:opts})
|
||||||
|
opts.lport = opts.lport || (opts.interrupt || {}).port || opts.iport
|
||||||
|
if (!opts.lport) opts.lpath = opts.lpath || (opts.interrupt || {}).ipath || opts.ipath || 'interrupt' // must has a socket litener for interrupt process
|
||||||
super(opts)
|
super(opts)
|
||||||
this.opts = opts
|
this.pinsCfg = opts.pinsCfg || this.chip17 ? [{port:'A', pins:'all', cfg:'input_interrupt'},{port:'B', pins:'all', cfg:'input_interrupt'}] : [{pins:'all', cfg:'input_interrupt'}]
|
||||||
|
|
||||||
log = logger({
|
log = logger({
|
||||||
file: 'src/mcp230xxi.js',
|
file: 'src/mcp230xxi.js',
|
||||||
class: 'MCP230XXi',
|
class: 'MCP230XXi',
|
||||||
|
@ -29,127 +30,116 @@ class MCP230XXi extends MCP230XX {
|
||||||
id: this.id
|
id: this.id
|
||||||
})
|
})
|
||||||
|
|
||||||
log.debug({ method:'constructor', line:32, opts: opts, msg:'mcp interrupt options after calling super() on base'})
|
pins.forEach((pin,index) => {
|
||||||
|
|
||||||
pins.forEach(pin => {
|
|
||||||
this[pin] = opts['i' + pin] || {}
|
this[pin] = opts['i' + pin] || {}
|
||||||
|
this[pin].mport = this[pin].mport || index ? 'B' : 'A'
|
||||||
})
|
})
|
||||||
this.pins = pins
|
this.pins = pins
|
||||||
this.commands.interrupt = this.bindFuncs(icommands) // add interrupt pin commands to base set in "command.js"
|
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.bind(this) // default processor
|
||||||
this._interruptProcess = process
|
|
||||||
this.ready = false
|
|
||||||
log.debug({ opts: opts, pins: pins }, 'options for mcp interrupt processor')
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
if (this.ipath) await this.addSocket('inter-n','s','n',{path:this.ipath})
|
const conditionHandler = async ev => {
|
||||||
if (this.iport) await this.addSocket('inter-t','s','t',{port:this.iport})
|
if ((ev||{}).state ==='connected'){
|
||||||
await super.init()
|
let data = (ev.data ||{})
|
||||||
// this will set default type to internal pullup, only need to to change indivial pins to external if desired
|
if (data.type === 'interrupt' || [ev.name, data.name, data.id].some(name => (name||'').includes('interrupt')) ) {
|
||||||
await this.commands.pin.cfg({port:'A',pins:'all',cfg:'input_interrupt'})
|
|
||||||
let status = await this._resetInterrupt('A')
|
|
||||||
log.debug({method:'init', line:52, msg:'default configure all port A pins as interrupts. Resetting port A interrupt', status:status})
|
|
||||||
if (this.chip17) {
|
|
||||||
await this.commands.pin.cfg({port:'B',pins:'all',cfg:'input_interrupt'})
|
|
||||||
let status = await this._resetInterrupt('B')
|
|
||||||
log.debug({method:'init', line:52, msg:'default configure all port B pins as interrupts. Resetting port B interrupt', status: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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
async _resetInterrupt(port) {
|
|
||||||
await this.bus.read(port !== 'B' ? 0x08 : 0x18) // 0x08 is intcap interrupt capture register
|
|
||||||
log.debug({method:'_resetInterrupt', line:72, msg: `reset interrupt for port ${port || 'A'},${this.id} arg ${port !== 'B' ? 0x08 : 0x18}`})
|
|
||||||
return(await this._readyInterrupt(port))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getPortByPin(pin) {
|
this.ready.addObserver('interrupt:connected',this.getSocket(`mcp-${opts.lport? 't':'n'}`),{event:'connection:consumer',condition:conditionHandler})
|
||||||
let port = this[pin] ? this[pin].mport : null
|
|
||||||
let index = this.pins.map(pin => pin.toString()).indexOf(pin.toString())
|
this.ready.addObserver('interrupt:reset',this.ready.combineObservers(['mcp','interrupt:connected']).pipe(
|
||||||
port = port || index <= 0 ? 'A' : 'B'
|
map(async ready=>{
|
||||||
return port
|
if (ready) return await this.resetInterrupt()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
))
|
||||||
|
} // end constructor
|
||||||
|
|
||||||
|
async interruptState(pin) {
|
||||||
|
let istate = await this.bus.read(this.getPort(pin) !== 'B' ? 0x07 : 0x17)
|
||||||
|
let pullup = (this.chipCfg||'').includes('Pullup') ? true : false
|
||||||
|
let state = istate.response ? true && pullup : false || !pullup
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
interruptProcessor(func) {
|
async resetInterrupt(pins) {
|
||||||
this._interruptProcess = func
|
if (!Array.isArray(pins)) pins = pins ? [pins] : this.pins
|
||||||
|
this._ireset = (await Promise.all(pins.map(async pin => {
|
||||||
|
await this.bus.read(this.getPort(pin) !== 'B' ? 0x08 : 0x18) // 0x08 is intcap interrupt capture register
|
||||||
|
return(await this.interruptState(pin))
|
||||||
|
})
|
||||||
|
)).reduce((res,val) => res && val)
|
||||||
|
return this._ireset
|
||||||
|
}
|
||||||
|
|
||||||
|
getPort(pin) {
|
||||||
|
if (pin==='A' || pin==='B') return pin
|
||||||
|
return this[pin].mport
|
||||||
|
}
|
||||||
|
|
||||||
|
registerInterruptProcessor(func) {
|
||||||
|
this._interruptProcess = func.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end of MCP230XXi Class
|
} // end of MCP230XXi Class
|
||||||
|
|
||||||
export default MCP230XXi
|
export default MCP230XXi
|
||||||
|
export { MCP230XXi, map, changed, isPlainObject, to, merge}
|
||||||
|
|
||||||
|
|
||||||
// default processor
|
// default processor
|
||||||
function process(details) {
|
function process(details) {
|
||||||
details.id = this.id
|
details.id = this.id
|
||||||
console.log('----default interrupt processor for mcp instance----')
|
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('here is where you could either locally take some action or send on a message to another process')
|
||||||
console.log('create your own function and register it with .interruptProcesser(function)')
|
console.log('create your own function and register it with .interruptProcesser(function)')
|
||||||
console.log(this.id)
|
console.log(this.id)
|
||||||
console.dir(details)
|
console.dir(details)
|
||||||
|
console.log('------------------------------------------')
|
||||||
}
|
}
|
||||||
|
|
||||||
// commands to be added to pin packet command functions
|
// commands to be added to pin packet command functions
|
||||||
const icommands = {
|
const icommands = {
|
||||||
|
|
||||||
status: async function(packet) {
|
status: async function(packet) {
|
||||||
// pin is interrupt pin on sbc for port
|
let pin = packet.ipin || packet.pin
|
||||||
let port = packet.port || this._getPortByPin(packet.pin)
|
let state = await this.interruptState(pin)
|
||||||
let res = await this._readyInterrupt(port)
|
return {cmd:'reply', request:'interrupt.status', port:this.getPort(pin), ipin:pin, ready:state}
|
||||||
return {cmd:'reply', request:'interrupt.status', port:port, ipin:packet.pin, ready:res}
|
|
||||||
},
|
},
|
||||||
reset: async function(packet) {
|
reset: async function(packet) {
|
||||||
// pin is interrupt pin on sbc for port
|
let pin = packet.ipin || packet.pin
|
||||||
let port = packet.port || this._getPortByPin(packet.pin)
|
let state = await this.resetInterrupt(pin)
|
||||||
log.error({cmd:'interrupt.reset', line:114, packet: packet, port:port, msg:`forced remote interrupt reset for port ${port}`})
|
let res = {level:state ? 'debug':'error', msg:`remote reset request from ${packet._header.sender.instanceID} for pin ${pin}: ${state?'ready': 'error'}` }
|
||||||
await this._resetInterrupt(port)
|
this.emit('log',res)
|
||||||
let res = await this._readyInterrupt(port)
|
return {state:state}
|
||||||
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
|
// finds which mcp pin caused the interrupt
|
||||||
find: async function(inter) {
|
find: async function(inter) {
|
||||||
if (this.ready) {
|
let ipin = inter.ipin || inter.pin
|
||||||
// protects tripped interrupt before it's fully initialized, or interrupt requests arriving before porcessing is complete
|
if (!await this.interruptState(ipin)) { // if it's already reset then this is false trip
|
||||||
this.ready = false
|
let res = await this.commands.pin.status({ pins: 'all', reg: 'intf', port:this.getPort(ipin) }) // read port interrupt status
|
||||||
log.debug({cmd:'interrupt.find', line:124, msg:'raw packet from interrupt, finding pin that caused interrupt', inter:inter})
|
let status = await this.resetInterrupt(ipin) // now reset
|
||||||
let packet = { pins: 'all', reg: 'intf' }
|
let pin = byteFormat(res.port||[], { in: 'ARY', out: 'PLC' })[0]
|
||||||
packet.port = inter.port || this._getPortByPin(inter.pin)
|
if (!pin) {
|
||||||
let res = await this.commands.pin.status(packet) // read port interrupt status
|
|
||||||
let status = await this._resetInterrupt(packet.port)
|
|
||||||
this.ready = true
|
|
||||||
log.debug({cmd:'interrupt.find', line:130, interrupt:res, reset:status, msg:'interrupt read and reset'})
|
|
||||||
if (!res.status) {
|
|
||||||
log.warn({cmd:'interrupt.find', line:132, inter:inter, msg:'no pin associated with interrupt'})
|
log.warn({cmd:'interrupt.find', line:132, inter:inter, msg:'no pin associated with interrupt'})
|
||||||
return { error: 'no pin associated with interrupt' }
|
return { error: 'no pin associated with interrupt' }
|
||||||
}
|
}
|
||||||
let pin = byteFormat(res.status.port, { in: 'ARY', out: 'PLC' })
|
else { // avoid bad interrupt (i.e. no pin caused interrupt)
|
||||||
res.pin = pin[0]
|
delete inter.cmd; delete inter._header
|
||||||
packet.pins = pin[0]
|
inter.ipin = ipin
|
||||||
packet.reg = null
|
inter.pin = pin
|
||||||
if (packet.pins) { // avoid bad interrupt (i.e. no pin caused interrupt)
|
inter.port = this[ipin].mport
|
||||||
res.ipin = inter.pin
|
inter.interrupt_ready = status
|
||||||
res.port = packet.port
|
inter.state = (await this.commands.pin.status({pin:pin, port:this.getPort(ipin)})).state
|
||||||
delete inter.cmd; delete inter._header; delete inter.pin
|
this._interruptProcess(inter)
|
||||||
Object.assign(res,inter)
|
// replying with reset command which is also a check
|
||||||
delete res.status
|
return {cmd:'reset', pin:ipin}
|
||||||
res.interrupt_ready = status || false
|
|
||||||
res.state = (await this.commands.pin.status(packet)).status.pins[0][1]
|
|
||||||
this._interruptProcess(res)
|
|
||||||
return res
|
|
||||||
} else {
|
|
||||||
log.warn({cmd:'interrupt.find', line:151, inter:inter, msg:'no pin associated with interrupt'})
|
|
||||||
return { error: 'no pin associated with interrupt' }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
} // end commands
|
||||||
|
|
Loading…
Reference in New Issue