refactored port/pin simplified, refactored mcp config writes to async/await, new read/write mocha test
parent
1a97705ab5
commit
f428abc394
|
@ -1,8 +1,12 @@
|
|||
'use strict'
|
||||
|
||||
const Device = require('@uci/i2c').Device,
|
||||
Port = require('./lib/port-pin').Port, // classes for MCP port and pins
|
||||
pinConfigs = require('./lib/port-pin').configs, // for export so pinConfigs can be accessed at runtime
|
||||
_u = require('@uci/utils'),
|
||||
portpin = require('./port-pin'), // classes for MCP port and pins
|
||||
_u = require('@uci/utils')
|
||||
|
||||
const
|
||||
Port = portpin.Port,
|
||||
pinConfigs = portpin.configs, // for export so pinConfigs can be accessed at runtime
|
||||
pause = _u.pPause
|
||||
|
||||
class MCP23008 extends Device {
|
||||
|
@ -18,19 +22,39 @@ class MCP23008 extends Device {
|
|||
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
|
||||
|
||||
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() {
|
||||
// console.log(`begin initialize ${this.id}`)
|
||||
await this.writeChipCfg(this.chip_config) // chip settings
|
||||
await this.writePinsCfg()
|
||||
for (let port in this.ports) {
|
||||
console.log(`init port ${port} state`)
|
||||
await this.writePort(this.state(port), 'force', port)
|
||||
}
|
||||
}
|
||||
|
||||
// must call after init if using interrupts
|
||||
async start() {
|
||||
// console.log(`starting ${ this.id }`)
|
||||
for (let port in this.ports) {
|
||||
for (let port of this.ports) {
|
||||
// if there are interrupts being used then start them and listeners
|
||||
if (this.inter(port)) {
|
||||
await this.interruptReset(port)
|
||||
|
@ -52,87 +76,54 @@ class MCP23008 extends Device {
|
|||
// console.log(`interrupt reset on ${this.id} port ${port}`)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
state(port = 'A') {
|
||||
return this.ports[port].state.value
|
||||
}
|
||||
|
||||
// get a handle to the ports interrupt
|
||||
inter(port = 'A') {
|
||||
return this.ports[port].interrupt
|
||||
}
|
||||
|
||||
writeChipCfg(cfg = 'default') {
|
||||
async 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 this.write(chip_config.cmd, byte)
|
||||
.then(() => Promise.resolve('mcp chip config: reg $ { chip_config.cmd } byte $ { byte } written '))
|
||||
let byte = _u.byteFormat(setting.val, { in: setting.fmt, out: 'DEC' })
|
||||
await this.write(chip_config.cmd, byte)
|
||||
}
|
||||
|
||||
// pin configurations should already be set before calling
|
||||
writePinsCfg() {
|
||||
async 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]
|
||||
for (let pin of this.ports[port].pins) {
|
||||
byte += pin.address * pin.cfg[setting]
|
||||
// console.log(`port: ${ port } pin: ${pin.id} setting: ${ setting } reg: ${ reg } byte: ${ byte }`)
|
||||
}
|
||||
//console.log(`port $ { port } - setting $ { setting } - reg $ { reg } - byte $ { byte }`)
|
||||
jobs.push(
|
||||
this.write(portReg(reg, port), byte).then(() => Promise.resolve(`
|
||||
config: wrote $ { byte } to register $ { reg } on port $ { port }
|
||||
`))
|
||||
)
|
||||
await this.write(portReg(reg, port), byte)
|
||||
}
|
||||
}
|
||||
return _u.pSeries(jobs)
|
||||
} // end writePinsCfg
|
||||
|
||||
// read(format = 'PLC') {
|
||||
// return u_.byteFormat(this.cur, { in: this.format, out: format })
|
||||
// }
|
||||
|
||||
async readPort(port, cmd = 'gpio') {
|
||||
async readPort(port, opts = {}) {
|
||||
if ('AB'.indexOf(port) === -1) {
|
||||
cmd = port ? port : 'gpio'
|
||||
opts = port ? port : {}
|
||||
port = 'A'
|
||||
}
|
||||
let cmd = opts.cmd ? opts.cmd : 'gpio'
|
||||
let result = await this.read(portReg(registers.pin_cmd[cmd], port))
|
||||
this.ports[port].state.value = result
|
||||
return result
|
||||
return opts.format ? this.ports[port].state.toFmt(opts.format) : result
|
||||
}
|
||||
|
||||
async writePort(byte, op, port = 'A') {
|
||||
// byte MUST be a decimal
|
||||
console.log(`current \n ${this.ports[port].state.toFmt('PLC')}`)
|
||||
console.log(`byte passed ${op} \n ${_u.byteFormat(byte,{in: 'DEC', out:'PLC'})}`)
|
||||
// console.log(`current\n ${this.ports[port].state.toFmt('PLC') }`)
|
||||
// console.log(`byte passed ${op}\n ${ _u.byteFormat(byte, { in: 'DEC', out: 'PLC'})}`)
|
||||
if (op !== 'force') {
|
||||
byte = this.ports[port].state.bwOp(byte, op)
|
||||
}
|
||||
console.log('byte to write decimal \n', byte)
|
||||
console.log(`byte to write \n ${_u.byteFormat(byte, { in: 'DEC', out: 'PLC' })}`)
|
||||
|
||||
// console.log('\nbyte to write decimal \n', byte, op, port)
|
||||
// console.log(`byte to write\n ${ _u.byteFormat(byte, { in: 'DEC', out: 'PLC' }) }`)
|
||||
await this.write(portReg(registers.pin_cmd.gpio, port), byte)
|
||||
//update the saved state
|
||||
this.ports[port].state.value = byte
|
||||
console.log('\n\n')
|
||||
// console.log('\n\n')
|
||||
}
|
||||
|
||||
async on(pins, port, format) {
|
||||
|
@ -206,20 +197,6 @@ class MCP23017 extends MCP23008 {
|
|||
return this.ports[port].pin(id)
|
||||
}
|
||||
|
||||
pid(address, port) {
|
||||
if (!port) {
|
||||
return this.ports.A.pid(address)
|
||||
}
|
||||
return this.ports[port].pid(address)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// EXPORTS
|
||||
|
|
|
@ -4,11 +4,11 @@ const _ = require('@uci/utils')
|
|||
|
||||
class Pin {
|
||||
|
||||
constructor(address, opts = {}) {
|
||||
// at a minimum address is required
|
||||
this.address = new _.Byte(address, opts.fmt)
|
||||
// a unique pin id MUST be supplied in opts or it must be set afterwards!
|
||||
this.id = opts.pid
|
||||
constructor(id, address, opts = {}) {
|
||||
// at a minimum a byte address is required
|
||||
this.address = address
|
||||
// a pin id MUST be supplied in opts 1 to 8
|
||||
this.id = id
|
||||
// a custom pin configuration can be passed or choosen from one of the sets, if neither than the default output config is chosen
|
||||
if (opts.pin_cfg_default) {
|
||||
if (_.keyExists(config_sets, opts.pin_cfg_default)) {
|
||||
|
@ -32,14 +32,6 @@ class Pin {
|
|||
return this.address
|
||||
}
|
||||
|
||||
set adr(add) {
|
||||
if (Number.isInteger(add)) {
|
||||
this.address.reset(add)
|
||||
} else {
|
||||
this.address.reset(add.val, add.fmt)
|
||||
}
|
||||
}
|
||||
|
||||
get pid() { return this.id }
|
||||
|
||||
set pid(pid) { this.id = pid }
|
||||
|
@ -54,9 +46,8 @@ class Pin {
|
|||
}
|
||||
|
||||
show() {
|
||||
console.log(`=====Pin ${this.id} =====\nAddress ${this.address.toFmt('HEX')}`)
|
||||
console.log(`\n=====Pin ${this.id} =====\n Decimal Address ${this.address}`)
|
||||
console.log('Conifguration: ', this.config)
|
||||
|
||||
}
|
||||
|
||||
} // End GPIO Pin
|
||||
|
@ -80,37 +71,22 @@ class Port {
|
|||
this.pins = new Array(8)
|
||||
let pid = ''
|
||||
for (let i = 0; i < 8; i++) {
|
||||
if (opts.pids) {
|
||||
pid = opts.pids[i]
|
||||
} else {
|
||||
pid = this.reverse ? 8 - i : i + 1
|
||||
this.pins[i] = new Pin(i, Math.pow(2, i), opts)
|
||||
}
|
||||
// using default decimal for pin addresses
|
||||
this.pins[i] = new Pin(Math.pow(2, i), _.merge(opts, { pid: pid }))
|
||||
// console.log(`\n\n====PORT ${this.id} =====`)
|
||||
// this.pins[i].show()
|
||||
}
|
||||
}
|
||||
// return a handle to all pins on a port
|
||||
get allPins() {
|
||||
return this.pins
|
||||
}
|
||||
|
||||
// return handle to a port's pin from id
|
||||
pin(id) {
|
||||
for (let i = 0; i < 8; i++) {
|
||||
|
||||
if (this.pins[i].id === id) {
|
||||
return this.pins[i]
|
||||
for (let pins of this.pins) {
|
||||
if (pin.id === id) {
|
||||
return pin
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
// return pin's id on a port from address
|
||||
pid(address) {
|
||||
if (Number.isInteger(address)) { address = new _.Byte(address) }
|
||||
let i = address.toFmt('PLC')[0] - 1
|
||||
return this.pins[i].id
|
||||
return Math.log(address) / Math.log(2)
|
||||
}
|
||||
|
||||
} // end GPIO Port CLass
|
||||
|
|
12
package.json
12
package.json
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@uci/mcp",
|
||||
"main": "lib/mcp23008-17.js",
|
||||
"main": "index.js",
|
||||
"version": "0.0.1",
|
||||
"description": "classes and functions for use the MCP chips",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"testw": "./node_modules/.bin/mocha --reporter list --recursive --watch",
|
||||
"test": "istanbul cover ./node_modules/.bin/_mocha test/ --report lcovonly -- -R spec --recursive && codecov || true"
|
||||
"test": "istanbul cover ./node_modules/.bin/_mocha test/ --report lcovonly -- -R spec --recursive && codecov || true",
|
||||
"rw": "./node_modules/.bin/mocha --reporter list --watch --timeout 30000 test/read-write.test"
|
||||
},
|
||||
"author": "David Kebler",
|
||||
"license": "MIT",
|
||||
|
@ -24,13 +24,13 @@
|
|||
},
|
||||
"homepage": "https://github.com/uCOMmandIt/uci-mcp#readme",
|
||||
"dependencies": {
|
||||
"p-series": "^1.0.0",
|
||||
"@uci/utils": "0.0.1"
|
||||
"@uci/utils": "0.0.1",
|
||||
"p-series": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^3.5.0",
|
||||
"codecov": "^1.0.1",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "^3.2.0"
|
||||
"mocha": "^3.x"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
//to run this test you MUST install @uci/i2c which is not a dependency of @uci/mcp
|
||||
// npm install @uci/i2c
|
||||
const Bus = require('@uci/i2c').Bus,
|
||||
MCP = require('../lib/mcp23008-17'),
|
||||
expect = require('chai').expect,
|
||||
pause = require('@uci/utils').pPause
|
||||
|
||||
let bus = new Bus()
|
||||
|
||||
let ADDR = 0x24
|
||||
let CHIP = 'MCP23017'
|
||||
let MS = 500 // so humans can watch the light otherwise set to zero
|
||||
let PORT = 'B'
|
||||
|
||||
let mcp = new MCP[CHIP](bus, ADDR, {
|
||||
pin_cfg_default: 'output',
|
||||
});
|
||||
|
||||
(async function () {
|
||||
await mcp.init()
|
||||
await mcp.allOff(PORT) // start clean
|
||||
await eachpin()
|
||||
await somepins()
|
||||
})()
|
||||
|
||||
//****************** TEST SUITES **********************
|
||||
async function eachpin() {
|
||||
let result
|
||||
for (let pin of mcp.ports[PORT].pins) {
|
||||
await mcp.on(pin.address, PORT)
|
||||
result = await mcp.readPort(PORT)
|
||||
console.log(pin.address, result)
|
||||
await mcp.off(pin.address, PORT)
|
||||
await pause(MS)
|
||||
}
|
||||
}
|
||||
|
||||
async function somepins() {
|
||||
|
||||
let pins = [1, 7, 8]
|
||||
await mcp.on(pins, PORT, 'PLC')
|
||||
let shouldbe = [1, 7, 8]
|
||||
let result = await mcp.readPort(PORT, { format: 'PLC' })
|
||||
console.log(pins, shouldbe, result)
|
||||
await pause(MS)
|
||||
|
||||
pins = [2, 1, 7]
|
||||
await mcp.on(pins, PORT, 'PLC')
|
||||
shouldbe = [2, 1, 7, 8]
|
||||
result = await mcp.readPort(PORT, { format: 'PLC' })
|
||||
console.log(pins, shouldbe, result)
|
||||
await pause(MS)
|
||||
|
||||
pins = [2, 1, 6]
|
||||
await mcp.toggle(pins, PORT, 'PLC')
|
||||
shouldbe = [6, 7, 8]
|
||||
result = await mcp.readPort(PORT, { format: 'PLC' })
|
||||
console.log(pins, shouldbe, result)
|
||||
await pause(MS)
|
||||
|
||||
pins = [2, 1, 7]
|
||||
await mcp.off(pins, PORT, 'PLC')
|
||||
shouldbe = [6, 8]
|
||||
result = await mcp.readPort(PORT, { format: 'PLC' })
|
||||
console.log(pins, shouldbe, result)
|
||||
await pause(MS)
|
||||
await mcp.allOff(PORT) // clear port after each test
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
//to run this test you MUST install @uci/i2c which is not a dependency of @uci/mcp
|
||||
// npm install @uci/i2c
|
||||
const Bus = require('@uci/i2c').Bus,
|
||||
MCP = require('../lib/mcp23008-17'),
|
||||
expect = require('chai').expect,
|
||||
pause = require('@uci/utils').pPause
|
||||
|
||||
let bus = new Bus()
|
||||
|
||||
let ADDR = 0x21
|
||||
let CHIP = 'MCP23017'
|
||||
let MS = 0 // so humans can watch the light otherwise set to zero
|
||||
let PORT = 'B'
|
||||
|
||||
let mcp = new MCP[CHIP](bus, ADDR, {
|
||||
pin_cfg_default: 'output',
|
||||
});
|
||||
|
||||
describe(
|
||||
`Testing Chip ${CHIP}, Port ${PORT} at I2C bus address of 0x${ADDR.toString(16)}`,
|
||||
function () {
|
||||
init()
|
||||
eachpin()
|
||||
somepins()
|
||||
})
|
||||
|
||||
//****************** TEST SUITES **********************
|
||||
function init() {
|
||||
it('==> Initialize Chip', async function () {
|
||||
|
||||
await mcp.init()
|
||||
await mcp.allOff(PORT) // start clean
|
||||
})
|
||||
}
|
||||
|
||||
function eachpin() {
|
||||
it('==> test each pin', async function () {
|
||||
|
||||
await mcp.init()
|
||||
await mcp.allOff(PORT) // start clean
|
||||
|
||||
for (let pin of mcp.ports[PORT].pins) {
|
||||
await mcp.on(pin.address, PORT)
|
||||
let result = await mcp.readPort(PORT)
|
||||
expect(result, `pin ${pin.id} at address ${pin.address} write/read failed`).to.equal(pin.address)
|
||||
await mcp.off(pin.address, PORT)
|
||||
await pause(MS)
|
||||
}
|
||||
await mcp.allOff() // clear port after each test
|
||||
})
|
||||
}
|
||||
|
||||
function somepins() {
|
||||
it('==> on, off toggle some pins', async function () {
|
||||
|
||||
let pins = [1, 7, 8]
|
||||
await mcp.on(pins, PORT, 'PLC')
|
||||
let shouldbe = [1, 7, 8]
|
||||
let result = await mcp.readPort(PORT, { format: 'PLC' })
|
||||
expect(result.sort(), 'pin write/read failed on').to.deep.equal(shouldbe.sort())
|
||||
await pause(MS)
|
||||
|
||||
pins = [2, 1, 7]
|
||||
await mcp.on(pins, PORT, 'PLC')
|
||||
shouldbe = [2, 1, 7, 8]
|
||||
result = await mcp.readPort(PORT, { format: 'PLC' })
|
||||
expect(result.sort(), 'pin write/read failed on 2').to.deep.equal(shouldbe.sort())
|
||||
await pause(MS)
|
||||
|
||||
pins = [2, 1, 6]
|
||||
await mcp.toggle(pins, PORT, 'PLC')
|
||||
shouldbe = [6, 7, 8]
|
||||
result = await mcp.readPort(PORT, { format: 'PLC' })
|
||||
expect(result.sort(), 'pin write/read failed toggle').to.deep.equal(shouldbe.sort())
|
||||
await pause(MS)
|
||||
|
||||
pins = [2, 1, 6, 7]
|
||||
await mcp.off(pins, PORT, 'PLC')
|
||||
shouldbe = [8]
|
||||
result = await mcp.readPort(PORT, { format: 'PLC' })
|
||||
expect(result.sort(), 'pin write/read failed off').to.deep.equal(shouldbe.sort())
|
||||
await pause(MS)
|
||||
|
||||
await mcp.allOff(PORT) // clear port after each test
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue