refactored port/pin simplified, refactored mcp config writes to async/await, new read/write mocha test

master
David Kebler 2017-05-26 21:00:04 -07:00
parent 1a97705ab5
commit f428abc394
5 changed files with 218 additions and 111 deletions

View File

@ -1,8 +1,12 @@
'use strict' 'use strict'
const Device = require('@uci/i2c').Device, const Device = require('@uci/i2c').Device,
Port = require('./lib/port-pin').Port, // classes for MCP port and pins portpin = require('./port-pin'), // 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')
_u = require('@uci/utils'),
const
Port = portpin.Port,
pinConfigs = portpin.configs, // for export so pinConfigs can be accessed at runtime
pause = _u.pPause pause = _u.pPause
class MCP23008 extends Device { class MCP23008 extends Device {
@ -18,19 +22,39 @@ class MCP23008 extends Device {
this.ports.A.interrupt = opts.interruptA ? opts.interruptA : opts.interrupt this.ports.A.interrupt = opts.interruptA ? opts.interruptA : opts.interrupt
} // end constructor } // 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() { async init() {
// console.log(`begin initialize ${this.id}`)
await this.writeChipCfg(this.chip_config) // chip settings await this.writeChipCfg(this.chip_config) // chip settings
await this.writePinsCfg() await this.writePinsCfg()
for (let port in this.ports) { for (let port in this.ports) {
console.log(`init port ${port} state`)
await this.writePort(this.state(port), 'force', port) await this.writePort(this.state(port), 'force', port)
} }
} }
// must call after init if using interrupts
async start() { async start() {
// console.log(`starting ${ this.id }`) // 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 there are interrupts being used then start them and listeners
if (this.inter(port)) { if (this.inter(port)) {
await this.interruptReset(port) await this.interruptReset(port)
@ -52,87 +76,54 @@ class MCP23008 extends Device {
// console.log(`interrupt reset on ${this.id} port ${port}`) // 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 async writeChipCfg(cfg = 'default') {
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') {
// console.log(`writing mcp chip config ${this.id}`) // console.log(`writing mcp chip config ${this.id}`)
let setting = chip_config[cfg] let setting = chip_config[cfg]
let byte = _u.byteFormat(setting.val, { in: setting.fmt, let byte = _u.byteFormat(setting.val, { in: setting.fmt, out: 'DEC' })
out: 'DEC' await this.write(chip_config.cmd, byte)
})
return this.write(chip_config.cmd, byte)
.then(() => Promise.resolve('mcp chip config: reg $ { chip_config.cmd } byte $ { byte } written '))
} }
// pin configurations should already be set before calling // pin configurations should already be set before calling
writePinsCfg() { async writePinsCfg() {
// console.log(`writing mcp pins config ${this.id}`) // console.log(`writing mcp pins config ${this.id}`)
let jobs = []
for (let port in this.ports) { for (let port in this.ports) {
for (let setting in registers.pin_config) { for (let setting in registers.pin_config) {
let reg = registers.pin_config[setting] let reg = registers.pin_config[setting]
// TODO 0x10 should be based on chip config // TODO 0x10 should be based on chip config
let byte = 0 let byte = 0
let pins = this.ports[port].allPins for (let pin of this.ports[port].pins) {
for (let i = 0; i < 8; i++) { byte += pin.address * pin.cfg[setting]
let pin = pins[i] // console.log(`port: ${ port } pin: ${pin.id} setting: ${ setting } reg: ${ reg } byte: ${ byte }`)
byte += pin.address.toFmt('DEC') * pin.cfg[setting]
} }
//console.log(`port $ { port } - setting $ { setting } - reg $ { reg } - byte $ { byte }`) await this.write(portReg(reg, port), byte)
jobs.push(
this.write(portReg(reg, port), byte).then(() => Promise.resolve(`
config: wrote $ { byte } to register $ { reg } on port $ { port }
`))
)
} }
} }
return _u.pSeries(jobs)
} // end writePinsCfg } // end writePinsCfg
// read(format = 'PLC') { async readPort(port, opts = {}) {
// return u_.byteFormat(this.cur, { in: this.format, out: format })
// }
async readPort(port, cmd = 'gpio') {
if ('AB'.indexOf(port) === -1) { if ('AB'.indexOf(port) === -1) {
cmd = port ? port : 'gpio' opts = port ? port : {}
port = 'A' port = 'A'
} }
let cmd = opts.cmd ? opts.cmd : 'gpio'
let result = await this.read(portReg(registers.pin_cmd[cmd], port)) let result = await this.read(portReg(registers.pin_cmd[cmd], port))
this.ports[port].state.value = result 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') { async writePort(byte, op, port = 'A') {
// byte MUST be a decimal // byte MUST be a decimal
console.log(`current \n ${this.ports[port].state.toFmt('PLC')}`) // 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(`byte passed ${op}\n ${ _u.byteFormat(byte, { in: 'DEC', out: 'PLC'})}`)
if (op !== 'force') { if (op !== 'force') {
byte = this.ports[port].state.bwOp(byte, op) byte = this.ports[port].state.bwOp(byte, op)
} }
console.log('byte to write decimal \n', byte) // console.log('\nbyte to write decimal \n', byte, op, port)
console.log(`byte to write \n ${_u.byteFormat(byte, { in: 'DEC', out: 'PLC' })}`) // console.log(`byte to write\n ${ _u.byteFormat(byte, { in: 'DEC', out: 'PLC' }) }`)
await this.write(portReg(registers.pin_cmd.gpio, port), byte) await this.write(portReg(registers.pin_cmd.gpio, port), byte)
//update the saved state //update the saved state
this.ports[port].state.value = byte this.ports[port].state.value = byte
console.log('\n\n') // console.log('\n\n')
} }
async on(pins, port, format) { async on(pins, port, format) {
@ -206,20 +197,6 @@ class MCP23017 extends MCP23008 {
return this.ports[port].pin(id) 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 } // end MCP23017 Class
// EXPORTS // EXPORTS

View File

@ -4,11 +4,11 @@ const _ = require('@uci/utils')
class Pin { class Pin {
constructor(address, opts = {}) { constructor(id, address, opts = {}) {
// at a minimum address is required // at a minimum a byte address is required
this.address = new _.Byte(address, opts.fmt) this.address = address
// a unique pin id MUST be supplied in opts or it must be set afterwards! // a pin id MUST be supplied in opts 1 to 8
this.id = opts.pid 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 // 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 (opts.pin_cfg_default) {
if (_.keyExists(config_sets, opts.pin_cfg_default)) { if (_.keyExists(config_sets, opts.pin_cfg_default)) {
@ -32,14 +32,6 @@ class Pin {
return this.address 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 } get pid() { return this.id }
set pid(pid) { this.id = pid } set pid(pid) { this.id = pid }
@ -54,9 +46,8 @@ class Pin {
} }
show() { 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) console.log('Conifguration: ', this.config)
} }
} // End GPIO Pin } // End GPIO Pin
@ -80,37 +71,22 @@ class Port {
this.pins = new Array(8) this.pins = new Array(8)
let pid = '' let pid = ''
for (let i = 0; i < 8; i++) { for (let i = 0; i < 8; i++) {
if (opts.pids) { this.pins[i] = new Pin(i, Math.pow(2, i), opts)
pid = opts.pids[i]
} else {
pid = this.reverse ? 8 - i : i + 1
} }
// 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 // return handle to a port's pin from id
pin(id) { pin(id) {
for (let i = 0; i < 8; i++) { for (let pins of this.pins) {
if (pin.id === id) {
if (this.pins[i].id === id) { return pin
return this.pins[i]
} }
} }
return false return false
} }
// return pin's id on a port from address // return pin's id on a port from address
pid(address) { pid(address) {
if (Number.isInteger(address)) { address = new _.Byte(address) } return Math.log(address) / Math.log(2)
let i = address.toFmt('PLC')[0] - 1
return this.pins[i].id
} }
} // end GPIO Port CLass } // end GPIO Port CLass

View File

@ -1,12 +1,12 @@
{ {
"name": "@uci/mcp", "name": "@uci/mcp",
"main": "lib/mcp23008-17.js", "main": "index.js",
"version": "0.0.1", "version": "0.0.1",
"description": "classes and functions for use the MCP chips", "description": "classes and functions for use the MCP chips",
"main": "index.js",
"scripts": { "scripts": {
"testw": "./node_modules/.bin/mocha --reporter list --recursive --watch", "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", "author": "David Kebler",
"license": "MIT", "license": "MIT",
@ -24,13 +24,13 @@
}, },
"homepage": "https://github.com/uCOMmandIt/uci-mcp#readme", "homepage": "https://github.com/uCOMmandIt/uci-mcp#readme",
"dependencies": { "dependencies": {
"p-series": "^1.0.0", "@uci/utils": "0.0.1",
"@uci/utils": "0.0.1" "p-series": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"chai": "^3.5.0", "chai": "^3.5.0",
"codecov": "^1.0.1", "codecov": "^1.0.1",
"istanbul": "^0.4.5", "istanbul": "^0.4.5",
"mocha": "^3.2.0" "mocha": "^3.x"
} }
} }

68
test/read-write.js Normal file
View File

@ -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
}

86
test/read-write.test.js Normal file
View File

@ -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
})
}