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",
|
||||
"main": "src",
|
||||
"version": "0.1.37",
|
||||
"version": "0.1.38",
|
||||
"description": "Classes and Helper Functions for using the MCP chip on I2C Bus",
|
||||
"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",
|
||||
"swr": "node -r esm examples/mcp-switch-relay",
|
||||
"test": "./node_modules/.bin/mocha --reporter list --timeout 30000",
|
||||
|
@ -28,9 +29,16 @@
|
|||
},
|
||||
"homepage": "https://github.com/uCOMmandIt/uci-mcp#readme",
|
||||
"dependencies": {
|
||||
"@uci/i2c-device": "^0.1.21",
|
||||
"@uci-utils/logger": "^0.0.15",
|
||||
"@uci-utils/byte": "^0.2.3"
|
||||
"@uci-utils/bind-funcs": "^0.2.4",
|
||||
"@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": {
|
||||
"chai": "^4.2.0",
|
||||
|
|
|
@ -4,7 +4,6 @@ import logger from '@uci-utils/logger'
|
|||
|
||||
let log = logger({file:'/src/commands.js', package:'@uci/mcp'})
|
||||
|
||||
|
||||
// TODO add uci debug logging
|
||||
|
||||
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
|
||||
// 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
|
||||
|
@ -45,9 +44,9 @@ export default {
|
|||
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]
|
||||
packet.config = packet.config || packet.cfg || 'output'
|
||||
if (packet.config==='custom') cfg = packet.set
|
||||
else cfg = PIN.cfgset[packet.config]
|
||||
for(let name of Object.keys(PIN.setting)) {
|
||||
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]})
|
||||
|
@ -58,31 +57,40 @@ export default {
|
|||
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) {
|
||||
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
|
||||
if (!reg) return {error:`unknown register ${packet.reg}`, packet:packet }
|
||||
let reply = { cmd:'reply'}
|
||||
let pins = parsePins(packet.pins)
|
||||
let pins = parsePins(packet.pins || packet.pin)
|
||||
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']
|
||||
})
|
||||
}
|
||||
let reply = {
|
||||
port:state.toFmt('ARY'),
|
||||
pins: pins.value.map(pin => {
|
||||
if (state.toFmt('PLC').indexOf(pin) !==-1) return [pin, 'on']
|
||||
else return [pin,'off']
|
||||
})
|
||||
}
|
||||
if (packet.pin) reply.state = reply.pins[0][1]
|
||||
return reply
|
||||
},
|
||||
_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
|
||||
log.debug({cmd:'pin._state', line:82, msg:'_state change request', operation:op, register:reg, packet:packet})
|
||||
let reply = { cmd:'reply'}
|
||||
let pins = parsePins(packet.pins)
|
||||
let pins = parsePins(packet.pins || packet.pin)
|
||||
let state = new _.Byte()
|
||||
// TODO support setting state on both ports if non given
|
||||
let bus = await this.bus.read(sreg(reg,packet.port))
|
||||
if (bus.error) return bus
|
||||
state.value = bus.response
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import Device from '@uci/i2c-device'
|
||||
// import Device from '../../uci-i2c-device/src/device-packet'
|
||||
import { I2CDevice as Device, map, changed, isPlainObject, to, merge} from '@uci/i2c-device'
|
||||
import commands from './commands'
|
||||
|
||||
import logger from '@uci-utils/logger'
|
||||
let log = {}
|
||||
|
||||
|
@ -16,32 +14,30 @@ class MCP230XX extends Device {
|
|||
})
|
||||
this.chip17 = opts.chip17
|
||||
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.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) {
|
||||
this._ready = func
|
||||
}
|
||||
if (opts.lport) this.registerSocket('mcp-t','s','t',{port:opts.lport})
|
||||
if (opts.lpath) this.registerSocket('mcp-n','s','n',{path: opts.lpath})
|
||||
|
||||
this.ready.addObserver('mcp',this.ready.getObserver('i2c').pipe(
|
||||
map(async ready => {
|
||||
if (ready) {
|
||||
let res = await this.commands.chip.cfg({cfg:this.chipCfg})
|
||||
if (!res.error) {
|
||||
let res = await this.commands.pin.cfgs({configs:this.pinsCfg})
|
||||
return res.error? false :true
|
||||
} return false
|
||||
}
|
||||
return false
|
||||
})
|
||||
))
|
||||
|
||||
} // end contructor
|
||||
|
||||
async configure() {
|
||||
let res = await this.commands.chip.cfg({cfg:this.chipCfg})
|
||||
if (res.error) {
|
||||
let err={level:'fatal', msg:'unable to configure mcp chip', error:res.error, cfg:this.chipCfg, address:this.address}
|
||||
log.fatal(err)
|
||||
this.emit('status', err)
|
||||
} else {
|
||||
this.emit('status', {level:'info', msg:'mcp chip was properly configured', cfg:this.chipCfg})
|
||||
this.emit('ready:mcp')
|
||||
}
|
||||
}
|
||||
} // end of MCP230XX Class
|
||||
|
||||
export default MCP230XX
|
||||
export {MCP230XX, map, changed, isPlainObject, to, merge}
|
||||
|
|
162
src/mcp230xxi.js
162
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 Ready from '@uci-utils/ready'
|
||||
|
||||
import logger from '@uci-utils/logger'
|
||||
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
|
||||
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
|
||||
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})
|
||||
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)
|
||||
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({
|
||||
file: 'src/mcp230xxi.js',
|
||||
class: 'MCP230XXi',
|
||||
|
@ -29,127 +30,116 @@ class MCP230XXi extends MCP230XX {
|
|||
id: this.id
|
||||
})
|
||||
|
||||
log.debug({ method:'constructor', line:32, opts: opts, msg:'mcp interrupt options after calling super() on base'})
|
||||
|
||||
pins.forEach(pin => {
|
||||
pins.forEach((pin,index) => {
|
||||
this[pin] = opts['i' + pin] || {}
|
||||
this[pin].mport = this[pin].mport || index ? 'B' : 'A'
|
||||
})
|
||||
this.pins = pins
|
||||
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
|
||||
this.ready = false
|
||||
log.debug({ opts: opts, pins: pins }, 'options for mcp interrupt processor')
|
||||
}
|
||||
this._interruptProcess = process.bind(this) // default processor
|
||||
|
||||
async init() {
|
||||
if (this.ipath) await this.addSocket('inter-n','s','n',{path:this.ipath})
|
||||
if (this.iport) await this.addSocket('inter-t','s','t',{port:this.iport})
|
||||
await super.init()
|
||||
// this will set default type to internal pullup, only need to to change indivial pins to external if desired
|
||||
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
|
||||
|
||||
const conditionHandler = async ev => {
|
||||
if ((ev||{}).state ==='connected'){
|
||||
let data = (ev.data ||{})
|
||||
if (data.type === 'interrupt' || [ev.name, data.name, data.id].some(name => (name||'').includes('interrupt')) ) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
else {
|
||||
this.ready = true
|
||||
return true
|
||||
}
|
||||
|
||||
this.ready.addObserver('interrupt:connected',this.getSocket(`mcp-${opts.lport? 't':'n'}`),{event:'connection:consumer',condition:conditionHandler})
|
||||
|
||||
this.ready.addObserver('interrupt:reset',this.ready.combineObservers(['mcp','interrupt:connected']).pipe(
|
||||
map(async ready=>{
|
||||
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
|
||||
}
|
||||
|
||||
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))
|
||||
async resetInterrupt(pins) {
|
||||
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
|
||||
}
|
||||
|
||||
_getPortByPin(pin) {
|
||||
let port = this[pin] ? this[pin].mport : null
|
||||
let index = this.pins.map(pin => pin.toString()).indexOf(pin.toString())
|
||||
port = port || index <= 0 ? 'A' : 'B'
|
||||
return port
|
||||
getPort(pin) {
|
||||
if (pin==='A' || pin==='B') return pin
|
||||
return this[pin].mport
|
||||
}
|
||||
|
||||
interruptProcessor(func) {
|
||||
this._interruptProcess = func
|
||||
registerInterruptProcessor(func) {
|
||||
this._interruptProcess = func.bind(this)
|
||||
}
|
||||
|
||||
} // end of MCP230XXi Class
|
||||
|
||||
export default MCP230XXi
|
||||
export { MCP230XXi, map, changed, isPlainObject, to, merge}
|
||||
|
||||
|
||||
// default processor
|
||||
function process(details) {
|
||||
details.id = this.id
|
||||
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(this.id)
|
||||
console.dir(details)
|
||||
console.log('------------------------------------------')
|
||||
}
|
||||
|
||||
// commands to be added to pin packet command functions
|
||||
const icommands = {
|
||||
|
||||
status: async function(packet) {
|
||||
// pin is interrupt pin on sbc for port
|
||||
let port = packet.port || this._getPortByPin(packet.pin)
|
||||
let res = await this._readyInterrupt(port)
|
||||
return {cmd:'reply', request:'interrupt.status', port:port, ipin:packet.pin, ready:res}
|
||||
let pin = packet.ipin || packet.pin
|
||||
let state = await this.interruptState(pin)
|
||||
return {cmd:'reply', request:'interrupt.status', port:this.getPort(pin), ipin:pin, ready:state}
|
||||
},
|
||||
reset: async function(packet) {
|
||||
// pin is interrupt pin on sbc for port
|
||||
let port = packet.port || this._getPortByPin(packet.pin)
|
||||
log.error({cmd:'interrupt.reset', line:114, packet: packet, port:port, msg:`forced remote interrupt reset for port ${port}`})
|
||||
await this._resetInterrupt(port)
|
||||
let res = await this._readyInterrupt(port)
|
||||
return {cmd:'reply', request:'interrupt.reset', port:port, ipin:packet.pin, ready:res}
|
||||
let pin = packet.ipin || packet.pin
|
||||
let state = await this.resetInterrupt(pin)
|
||||
let res = {level:state ? 'debug':'error', msg:`remote reset request from ${packet._header.sender.instanceID} for pin ${pin}: ${state?'ready': 'error'}` }
|
||||
this.emit('log',res)
|
||||
return {state:state}
|
||||
},
|
||||
// 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) {
|
||||
if (this.ready) {
|
||||
// protects tripped interrupt before it's fully initialized, or interrupt requests arriving before porcessing is complete
|
||||
this.ready = false
|
||||
log.debug({cmd:'interrupt.find', line:124, msg:'raw packet from interrupt, finding pin that caused interrupt', inter:inter})
|
||||
let packet = { pins: 'all', reg: 'intf' }
|
||||
packet.port = inter.port || this._getPortByPin(inter.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) {
|
||||
let ipin = inter.ipin || inter.pin
|
||||
if (!await this.interruptState(ipin)) { // if it's already reset then this is false trip
|
||||
let res = await this.commands.pin.status({ pins: 'all', reg: 'intf', port:this.getPort(ipin) }) // read port interrupt status
|
||||
let status = await this.resetInterrupt(ipin) // now reset
|
||||
let pin = byteFormat(res.port||[], { in: 'ARY', out: 'PLC' })[0]
|
||||
if (!pin) {
|
||||
log.warn({cmd:'interrupt.find', line:132, inter:inter, msg:'no pin associated with interrupt'})
|
||||
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
|
||||
if (packet.pins) { // avoid bad interrupt (i.e. no pin caused interrupt)
|
||||
res.ipin = inter.pin
|
||||
res.port = packet.port
|
||||
delete inter.cmd; delete inter._header; delete inter.pin
|
||||
Object.assign(res,inter)
|
||||
delete res.status
|
||||
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' }
|
||||
else { // avoid bad interrupt (i.e. no pin caused interrupt)
|
||||
delete inter.cmd; delete inter._header
|
||||
inter.ipin = ipin
|
||||
inter.pin = pin
|
||||
inter.port = this[ipin].mport
|
||||
inter.interrupt_ready = status
|
||||
inter.state = (await this.commands.pin.status({pin:pin, port:this.getPort(ipin)})).state
|
||||
this._interruptProcess(inter)
|
||||
// replying with reset command which is also a check
|
||||
return {cmd:'reset', pin:ipin}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end commands
|
||||
|
|
Loading…
Reference in New Issue