commit 4c08f12300169686698a5ad78e8ed68662c2460f Author: David Kebler Date: Thu Jan 12 20:05:20 2017 -0800 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e61051f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/node_modules/ +/coverage/ diff --git a/.jshglobals b/.jshglobals new file mode 100644 index 0000000..db4fe74 --- /dev/null +++ b/.jshglobals @@ -0,0 +1,13 @@ +{ +"globals": { + "Debug" : true, + /* MOCHA */ +"describe" : false, +"it" : false, +"xit" : false, +"before" : false, +"beforeEach" : false, +"after" : false, +"afterEach" : false + } +} diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..29bdf7a --- /dev/null +++ b/.jshintrc @@ -0,0 +1,70 @@ +{ + // https://gist.github.com/connor/1597131 + + // Globals - import repo spectific globals + + "extends": "./.jshglobals", + + // Settings + "passfail" : false, // Stop on first error. + "maxerr" : 100, // Maximum error before stopping. + + // Predefined globals whom JSHint will ignore. + "browser" : true, // Standard browser globals e.g. `window`, `document`. + "node" : true, + "rhino" : false, + "couch" : false, + "wsh" : true, // Windows Scripting Host. + + "jquery" : true, + "prototypejs" : false, + "mootools" : false, + "dojo" : false, + + + // Development. + "debug" : false, // Allow debugger statements e.g. browser breakpoints. + "devel" : true, // Allow developments statements e.g. `console.log();`. + + + // ECMAScript + "esversion" : 6, //use this in new version of jshint + "strict" : false, // Require `use strict` pragma in every file. + "globalstrict" : false, // Allow global "use strict" (also enables 'strict'). + + + // The Good Parts. + "asi" : true, // Tolerate Automatic Semicolon Insertion (no semicolons). + "laxbreak" : true, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. + "bitwise" : true, // Allow bitwise operators (&, |, ^, etc.). + "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. + "curly" : true, // Require {} for every new block or scope. + "eqeqeq" : true, // Require triple equals i.e. `===`. + "eqnull" : false, // Tolerate use of `== null`. + "evil" : false, // Tolerate use of `eval`. + "expr" : false, // Tolerate `ExpressionStatement` as Programs. + "forin" : false, // Tolerate `for in` loops without `hasOwnPrototype`. + "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` + "latedef" : false, // Prohipit variable use before definition. + "loopfunc" : false, // Allow functions to be defined within loops. + "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. + "regexp" : true, // Prohibit `.` and `[^...]` in regular expressions. + "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. + "scripturl" : true, // Tolerate script-targeted URLs. + "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. + "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. + "undef" : true, // Require all non-global variables be declared before they are used. + + + // Personal styling preferences. + "newcap" : false, // Require capitalization of all constructor functions e.g. `new F()`. + "noempty" : true, // Prohibit use of empty blocks. + "nonew" : true, // Prohibit use of constructors for side-effects. + "nomen" : true, // Prohibit use of initial or trailing underbars in names. + "onevar" : false, // Allow only one `var` statement per function. + "plusplus" : false, // Prohibit use of `++` & `--`. + "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. + "trailing" : true, // Prohibit trailing whitespaces. + "white" : false, // Check against strict whitespace and indentation rules. + "indent" : 2 // Specify indentation spacing +} diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..f16fc41 --- /dev/null +++ b/.npmignore @@ -0,0 +1,4 @@ +tests/ +test/ +*.test.js +testing/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8ffd227 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: node_js + +node_js: + - '4.0' + - '5.0' + - '6.0' + - 'node' + +sudo: false + +script: npm test + +after_success: + - bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports" diff --git a/index.js b/index.js new file mode 100644 index 0000000..90ae21c --- /dev/null +++ b/index.js @@ -0,0 +1,8 @@ +let opts = { + dirname: __dirname + '/lib', + // http://stackoverflow.com/questions/2078915/a-regular-expression-to-exclude-a-word-string + filter: /^(?!index)([^\.].*)\.js?$/, + recursive: false, + merge: true // remove or comment to have each file in /lib be a prop/key in library...see node-require-all +} +module.exports = require('require-all')(opts); diff --git a/lib/mcp23008-17.js b/lib/mcp23008-17.js new file mode 100644 index 0000000..fc690ac --- /dev/null +++ b/lib/mcp23008-17.js @@ -0,0 +1,146 @@ +'use strict' +const Device = require('./device').Device, + Port = require('./gpio').Port, + _u = require('uci-utils') + +const pSeries = require('p-series'); + +class MCP23008 extends Device { + constructor(busObj, i2cAddress, opts) { + super(busObj, i2cAddress, opts) + // opts could pass in array of custom pin config, or single for all, or anything + // this.registers = registers // load in register database + this.ports = {} + opts.portID = 'A' + this.ports.A = new Port(opts) + // if not specified there RPi is not managing the interrupt + // pin number on RPi that is connected to and services the interrupt. 4/17/27/22 = 7/11/13/15 + this.ports.A.interPin = opts.interPin + } // end constructor + + init() { + console.log('chip ', chip_config.cmd, chipSetByte()) + return this.write(chip_config.cmd, chipSetByte()) // configure chip + .then(this.writePinsCfg()) + + } + + pin(id) { return this.ports.A.pin(id) } // get a reference to a particular pin's object + + // pin configurations should already be set before calling + writePinsCfg() { + let jobs = []; + for (let port in this.ports) { + for (let setting in registers.pin_config) { + let reg = registers.pin_config[setting] + if (port === 'B') { reg = reg + 16 } + 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] + } + // console.log(`port ${port} - setting ${setting} - reg ${reg} - byte ${byte}`) + jobs.push( + () => this.write(reg, byte) // .then((resp)=>console.log(`done writing config ${resp}`)) + ) + } + } + return pSeries(jobs) + } // end writePinsCfg + + pinRead(id, opts) { + let mcpdev = this; + return new Promise(function (resolve, reject) { + let cmd = opts.cmd ? opts.cmd : 'gpio' + let reg = registers.pin_cmd[cmd] + let gpio = mcpdev.pin(id, opts.port) + if (!gpio) { + reject('no pin found for given id') + } + if (gpio.port === 'B') { + reg = reg + 16 + } + // call device class read + console.log('port - reg', gpio.port, reg) + return mcpdev.read(reg).then(resp => { + resp = _u.byteFormat(resp, { in: 'DEC', + out: 'ARY' + }) + let addr = gpio.pin.address.toFmt('ARY') + console.log(addr) + console.log(resp) + resolve(_u.sum(_u.and(addr, resp))) // resolve 1 or 0 + }) + }) + .then(state => console.log(`pin state ${state}`)) + .catch(err => console.log(err)) // end Promise + } // end pinRead + +} // end 23008 +module.exports.MCP23008 = MCP23008 + +class MCP23017 extends MCP23008 { + constructor(busObj, i2cAddress, opts) { + super(busObj, i2cAddress, opts) + // add a second port + + opts.portID = 'B' + this.ports.B = new Port(opts) + this.ports.A.interPin = opts.interPin // if not specified the RPi is not managing the 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) + } + +} // end MCP23017 Class +module.exports.MCP23017 = MCP23017 + +/* 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 cConfiguration to be used with Register +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 + default: { + val: '10000000', // int pins connected, port A + 0x10 = Port B -- ignored by 23008 + fmt: 'STR' + }, + twoints: { + val: '00000001', // int pins separate, port A + 0x10 = Port B -- ignored by 23008 + fmt: 'STR' + } +} + +function chipSetByte(setting = 'default') { + setting = chip_config[setting] + return _u.byteFormat(setting.val, { in: setting.fmt, + out: 'DEC' + }) +} +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 + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..985e544 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "uci-mcp", + "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" + }, + "author": "David Kebler", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/uCOMmandIt/uci-mcp.git" + }, + "keywords": [ + "node.js", + "microchip", + "i2c" + ], + "bugs": { + "url": "https://github.com/uCOMmandIt/uci-mcp/issues" + }, + "homepage": "https://github.com/uCOMmandIt/uci-mcp#readme", + "dependencies": { + "require-all": "git+https://github.com/dkebler/node-require-all.git#merge" + }, + "devDependencies": { + "chai": "^3.5.0", + "codecov": "^1.0.1", + "istanbul": "^0.4.5", + "mocha": "^3.2.0" + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..3d39f2e --- /dev/null +++ b/readme.md @@ -0,0 +1,12 @@ +# uCOMmandIt MCP - Micro Chip Library + +[![Build Status](https://img.shields.io/travis/uCOMmandIt/uci-mcp.svg?branch=master)](https://travis-ci.org/uCOMmandIt/uci-mcp) +[![Inline docs](http://inch-ci.org/github/uCOMmandIt/uci-mcp.svg?branch=master)](http://inch-ci.org/github/uCOMmandIt/uci-mcp) +[![Dependencies](https://img.shields.io/david/uCOMmandIt/uci-mcp.svg)](https://david-dm.org/uCOMmandIt/uci-mcp) +[![devDependencies](https://img.shields.io/david/dev/uCOMmandIt/uci-mcp.svg)](https://david-dm.org/uCOMmandIt/uci-mcp?type=dev) +[![codecov](https://img.shields.io/codecov/c/github/uCOMmandIt/uci-mcp/master.svg)](https://codecov.io/gh/uCOMmandIt/uci-mcp) + +**Classes and Library to Support Microchip corporation chips** + +http://www.microchip.com/wwwproducts/en/mcp23017 +http://www.microchip.com/wwwproducts/en/mcp23008 diff --git a/test/mcp.test.js b/test/mcp.test.js new file mode 100644 index 0000000..ea6022a --- /dev/null +++ b/test/mcp.test.js @@ -0,0 +1,73 @@ + 'use strict' + + const expect = require('chai').expect, + i2c = require('../class'), + _u = require('uci-utils') + + let bus1 = new i2c.Bus(1) + + // console.log(bus1) + // bus1.scan((devs)=>{console.log('devices: ', devs)}) + //bus1.scan().then(results=>console.log(results)) + + describe('I2C Device Classes - ', function () { + + let mcp17 = new i2c.MCP.MCP23017(bus1, 0x20, { + name: 'switches 1-16' + }) + + describe('MCP23017 Class - ', function () { + + it('can set and get a single pin config', function () { + expect(mcp17.pin(8).cfg.dir, "pin address getter failed").to.equal(0) + mcp17.portA.pin(8).cfg.dir = 1 + console.log(mcp17.pin(8).cfg.dir) + + // expect(mcp17.pin(8).config.dir, "pin address setter failed").to.equal(1) + }); + + it('a pin can access device methods when attached to a device', function () { + expect(mcp17.portA.pin(8).device.address.val).to.equal(mcp17.address.val) + }) + + }); + + let mcp8 = new i2c.MCP.MCP23008(bus1, 0x21, { + cfgDefault: 'output', + name: 'relay 1-8' + }) + + describe('MCP23008 Class - ', function () { + + mcp8.pin(8).dir = 1 + it('can set and get a single pin config', function () { + expect(mcp8.port.pin(8).dir, "pin address getter failed").to.equal(1) + mcp8.port.pin(8).dir = 0 + expect(mcp8.port.pin(8).dir, "pin address getter failed").to.equal(0) + expect(mcp8.port.pin(8).itr, "pin address getter failed").to.equal(Boolean(0)) + }); + + it('a pin can access device methods when attached to a device', function () { + expect(mcp17.portA.pin(8).device.address.val).to.equal(mcp17.address.val) + }) + + // console.log(bus1.bus.writeI2cBlock_p) + // mcp8.func() + + const ADDR = 0x21, + DIR = 0x00, + RW = 0x09, + ALLPINS = 0x40, + NOPINS = 0x00 + + mcp8.write(DIR, 0x00).then(() => + mcp8.write(RW, ALLPINS).then(() => + mcp8.read(RW, ALLPINS).then((resp) => { + console.log('relays on', resp) + }) + ) + ) + + }); + + });