From f428abc3946d6bd9e167a6ff696dcfb0105e59d2 Mon Sep 17 00:00:00 2001 From: David Kebler Date: Fri, 26 May 2017 21:00:04 -0700 Subject: [PATCH] refactored port/pin simplified, refactored mcp config writes to async/await, new read/write mocha test --- lib/mcp23008-17.js | 117 ++++++++++++++++------------------------ lib/port-pin.js | 46 ++++------------ package.json | 12 ++--- test/read-write.js | 68 +++++++++++++++++++++++ test/read-write.test.js | 86 +++++++++++++++++++++++++++++ 5 files changed, 218 insertions(+), 111 deletions(-) create mode 100644 test/read-write.js create mode 100644 test/read-write.test.js diff --git a/lib/mcp23008-17.js b/lib/mcp23008-17.js index 4efcbb3..4317c51 100644 --- a/lib/mcp23008-17.js +++ b/lib/mcp23008-17.js @@ -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 diff --git a/lib/port-pin.js b/lib/port-pin.js index b3f9f57..06a5af2 100644 --- a/lib/port-pin.js +++ b/lib/port-pin.js @@ -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 - } - // 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() + this.pins[i] = new Pin(i, Math.pow(2, i), opts) } } - // 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 diff --git a/package.json b/package.json index 7ba10d5..3ad2ff0 100644 --- a/package.json +++ b/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" } } diff --git a/test/read-write.js b/test/read-write.js new file mode 100644 index 0000000..5c765fb --- /dev/null +++ b/test/read-write.js @@ -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 +} diff --git a/test/read-write.test.js b/test/read-write.test.js new file mode 100644 index 0000000..63a53fd --- /dev/null +++ b/test/read-write.test.js @@ -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 + }) +}