change module name and loading/main
update interrupt pin processing for interrupt module changesmaster
parent
31c15c2230
commit
0de3dfd825
|
@ -0,0 +1,14 @@
|
|||
import MCP230XX from './mcp230xx'
|
||||
// import Interrupt from './interrupt'
|
||||
// import Relays from './relays'
|
||||
// import RelayBanks from './relay-banks'
|
||||
// import Switches from './switches'
|
||||
// import SwitchBanks from './switch-banks'
|
||||
|
||||
export { MCP230XX as MCP230XX }
|
||||
// export { Interrupt as Interrupt }
|
||||
// export { Relays as Relays }
|
||||
// export { RelayBanks as RelayBanks }
|
||||
// export { Switches as Switches }
|
||||
// export { SwitchBanks as SwitchBanks }
|
||||
export default MCP230XX
|
|
@ -0,0 +1,50 @@
|
|||
import UCIInterrupt from '@uci/interrupt'
|
||||
|
||||
import logger from '@uci/logger'
|
||||
let log = {}
|
||||
const LOG_OPTS = (id) => {
|
||||
return {
|
||||
repo:'uci-mcp',
|
||||
npm:'@uci/mcp',
|
||||
file:'src/interrupt.mjs',
|
||||
class:'Interrupt',
|
||||
id:id,
|
||||
instance_created:new Date().getTime()
|
||||
}}
|
||||
|
||||
export default class Interrupt extends UCIInterrupt {
|
||||
constructor(opts) {
|
||||
super(opts)
|
||||
log = logger.child(LOG_OPTS(this.id))
|
||||
if (!opts.address) log.fatal({opts:opts},'no i2c bus address supplied' )
|
||||
this.address = opts.address
|
||||
this.chip17 = opts.chip17
|
||||
this.pin = this.bindFuncs(pin)
|
||||
this.chip = this.bindFuncs(chip)
|
||||
this.interrupt = opts.interrupt
|
||||
}
|
||||
|
||||
async init(){
|
||||
await super.init()
|
||||
}
|
||||
|
||||
} // end of MCP230XX Class
|
||||
|
||||
let times = 0
|
||||
async interruptProcess(pin) {
|
||||
times += 1
|
||||
console.log(`Interrupt occured for ${times}th time on pin ${pin}`)
|
||||
let packet = {cmd:'pin.interrupt.find', pin:pin, times:times}
|
||||
interrupt.send(packet) // via any attached consumers
|
||||
}
|
||||
|
||||
interrupt.reply = function (packet) {
|
||||
console.log(`REPLY: pin ${packet.pin}:${packet.port} on ${packet._header.responder.instanceID} caused the ${packet.times}th interrupt on gpio pin ${packet.inter}`)
|
||||
}
|
||||
|
||||
await interrupt.init()
|
||||
process.send('_ready')
|
||||
|
||||
})().catch(err => {
|
||||
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
|
||||
})
|
|
@ -1,304 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Device = require('@uci/i2c').Device,
|
||||
portpin = require('./port-pin'), // classes for MCP port and pins
|
||||
EventEmitter = require('events'),
|
||||
_ = require('@uci/utils'),
|
||||
aggregate = require('aggregation/es6')
|
||||
|
||||
const
|
||||
Port = portpin.Port,
|
||||
pinConfigs = portpin.configs, // for export so pinConfigs can be accessed at runtime
|
||||
pause = _.pPause,
|
||||
debug = _.debug('mcp:23008-17')
|
||||
|
||||
class MCP23008 extends aggregate(Device, EventEmitter) {
|
||||
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 _.Byte(opts.stateA)
|
||||
this.ports.A.interrupt = opts.interruptA ? opts.interruptA : opts.interrupt
|
||||
} // end constructor
|
||||
|
||||
// helpers
|
||||
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
|
||||
|
||||
pid(num) { return this.ports.A.pins[num - 1].id }
|
||||
|
||||
portByPin(id) {
|
||||
if (this.ports.A.pin(id)) { return 'A' }
|
||||
return false
|
||||
}
|
||||
|
||||
state(port = 'A') {
|
||||
return this.ports[port].state.value
|
||||
}
|
||||
|
||||
// get a handle to the ports interrupt
|
||||
inter(port = 'A') {
|
||||
return this.ports[port].interrupt
|
||||
}
|
||||
|
||||
// Must call after instantiating.
|
||||
|
||||
async init() {
|
||||
debug.L1(`\n=======\nInitializing ${this.id}`)
|
||||
await this.writeChipCfg(this.chip_config) // chip settings
|
||||
await this.writePinsCfg()
|
||||
// write out any initial state(s)
|
||||
for (let port in this.ports) {
|
||||
if (this.state(port)) {
|
||||
debug.L1(`writing initial port state ${port} ${this.state(port)}`)
|
||||
await this.writePort(this.state(port), 'force', port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// must call after init if using interrupts
|
||||
async start() {
|
||||
debug.L1(`starting ${ this.id }`)
|
||||
for (let port in this.ports) {
|
||||
// if there are interrupts being used then start them and listeners
|
||||
if (this.inter(port)) {
|
||||
this.startInterrupt(port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async startInterrupt(port) {
|
||||
await this.interruptReset(port)
|
||||
debug.L1(`starting interrupt on port ${ port }`)
|
||||
await this.inter(port).start()
|
||||
let chip = this // scope `this` for use in the listener below
|
||||
// bind handler to the chip so handler can read/write to chip/bank when interrupt is emitted
|
||||
debug.L3('handler', this.inter(port).handler)
|
||||
let ihandler = this.inter(port).handler.bind(this)
|
||||
// inside the listener `this` is the interrupt not the chip/bank
|
||||
this.inter(port).on('fired', async function () {
|
||||
debug.L1(`interrupt from ${this.pin_number}`)
|
||||
let data = await ihandler(port)
|
||||
debug.L2(`port interrupt event 'fired' data => ${data.bank} on port ${data.port} pin# ${data.pin} pid ${data.pid}`)
|
||||
chip.emit('fired', data) // emit up the class chain
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
async interruptReset(port = 'A') {
|
||||
await this.read(portReg(0x08, port))
|
||||
await pause(100) // give enough time for mcp to reset its interupt
|
||||
debug.L1(`interrupt reset on ${this.id} port ${port}`)
|
||||
}
|
||||
|
||||
async interruptPin(port, format) {
|
||||
if ('AB'.indexOf(port) === -1) {
|
||||
format = port ? port : null
|
||||
port = 'A'
|
||||
}
|
||||
let result = await this.read(portReg(0x07, port))
|
||||
return format ? _.byteFormat(result, { in: 'DEC', out: format }) : result
|
||||
|
||||
}
|
||||
|
||||
async writeChipCfg(cfg = 'default') {
|
||||
let setting = chip_config[cfg]
|
||||
let byte = _.byteFormat(setting.val, { in: setting.fmt, out: 'DEC' })
|
||||
debug.L1(`writing mcp chip config ${setting.val}`)
|
||||
await this.write(chip_config.cmd, byte)
|
||||
}
|
||||
|
||||
// pin configurations should already be set before calling
|
||||
async writePinsCfg() {
|
||||
debug.L1('writing mcp pins config')
|
||||
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
|
||||
for (let pin of this.ports[port].pins) {
|
||||
byte += pin.address * pin.cfg[setting]
|
||||
debug.L3(`port: ${ port } pin: ${pin.id} setting: ${ setting } reg: ${ reg } byte: ${ byte }`)
|
||||
}
|
||||
await this.write(portReg(reg, port), byte)
|
||||
}
|
||||
}
|
||||
debug.L1('done writing mcp pins config')
|
||||
} // end writePinsCfg
|
||||
|
||||
async readPort(port, opts = {}) {
|
||||
if ('AB'.indexOf(port) === -1) {
|
||||
opts = port ? port : {}
|
||||
port = 'A'
|
||||
}
|
||||
let cmd = opts.cmd ? opts.cmd : 'gpio'
|
||||
let result = await this.read(portReg(registers.pin_cmd[cmd], port))
|
||||
debug.L2('read prev ', this.ports[port].state.toFmtPrev('ARY'))
|
||||
debug.L2('read result', _.byteFormat(result, { in: 'DEC', out: 'ARY' }))
|
||||
debug.L2('read state ', this.ports[port].state.toFmt('ARY'))
|
||||
// if port pins changed without a write then update state
|
||||
if (_.bitChanges(_.byteFormat(result, { in: 'DEC', out: 'ARY' }), this.ports[port].state.toFmt('ARY'))) {
|
||||
this.ports[port].state.value = result
|
||||
}
|
||||
return opts.format ? this.ports[port].state.toFmt(opts.format) : result
|
||||
}
|
||||
|
||||
async readPin(pin, port, opts = {}) {
|
||||
if ('AB'.indexOf(port) === -1) {
|
||||
opts = port ? port : {}
|
||||
port = 'A'
|
||||
}
|
||||
await this.readPort(port, opts)
|
||||
// TODO return just true or false not the decimal of pin - will need new version on npm
|
||||
return this.ports[port].state.bwOp(Math.pow(2, pin - 1), 'check') ? true : false
|
||||
}
|
||||
|
||||
async writePort(byte, op, port = 'A') {
|
||||
// byte MUST be a decimal
|
||||
debug.L2(`port:${port}, op:${op}, current pins state\n ${this.ports[port].state.toFmt('PLC') }`)
|
||||
debug.L2(`byte passed \n [${ _.byteFormat(byte, { in: 'DEC', out: 'PLC'})}]:${byte}`)
|
||||
if (op !== 'force') {
|
||||
byte = this.ports[port].state.bwOp(byte, op)
|
||||
}
|
||||
debug.L2(`byte to write\n [${ _.byteFormat(byte, { in: 'DEC', out: 'PLC' })}]:${byte}\n=======`)
|
||||
await this.write(portReg(registers.pin_cmd.gpio, port), byte)
|
||||
//update to the saved state
|
||||
this.ports[port].state.value = byte
|
||||
}
|
||||
|
||||
async pinsOn(pins, port, format) {
|
||||
if ('AB'.indexOf(port) === -1) {
|
||||
format = port
|
||||
port = 'A'
|
||||
}
|
||||
if (format) { pins = _.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 pinsOff(pins, port, format) {
|
||||
if ('AB'.indexOf(port) === -1) {
|
||||
format = port
|
||||
port = 'A'
|
||||
}
|
||||
if (format) { pins = _.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 = _.byteFormat(pins, { in: format, out: 'DEC' }) }
|
||||
await this.writePort(pins, 'toggle', port)
|
||||
}
|
||||
|
||||
async allToggle(port = 'A') {
|
||||
let pins = 255
|
||||
await this.writePort(pins, 'toggle', port)
|
||||
}
|
||||
|
||||
async force(pins, port, format) {
|
||||
if ('AB'.indexOf(port) === -1) {
|
||||
format = port
|
||||
port = 'A'
|
||||
}
|
||||
if (format) { pins = _.byteFormat(pins, { in: format, out: 'DEC' }) }
|
||||
await this.writePort(pins, 'force', port)
|
||||
}
|
||||
|
||||
} // end 23008
|
||||
|
||||
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.state = new _.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)
|
||||
}
|
||||
|
||||
pid(num, port) {
|
||||
console.log('pin for pid',this.ports[port].pins[num - 1])
|
||||
return this.ports[port].pins[num - 1].id
|
||||
}
|
||||
|
||||
} // end MCP23017 Class
|
||||
|
||||
// EXPORTS
|
||||
module.exports = {
|
||||
MCP23008,
|
||||
MCP23017,
|
||||
pinConfigs
|
||||
}
|
||||
|
||||
// Local Functions and Settings=============================
|
||||
|
||||
function portReg(reg, port) {
|
||||
// TODO check chip configuartion to know what to add for port B. CUrrenly assume IOCON=1
|
||||
// index 0,1 ==== 'A','B'
|
||||
if ((port === 'B') || (port === 1)) { 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
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ const LOG_OPTS = (id) => {
|
|||
return {
|
||||
repo:'uci-mcp',
|
||||
npm:'@uci/mcp',
|
||||
file:'src/mcp230xx-packet.mjs',
|
||||
file:'src/mcp230xx.mjs',
|
||||
class:'MCP230XX',
|
||||
id:id,
|
||||
instance_created:new Date().getTime()
|
Loading…
Reference in New Issue