FIRST commit of socket/packet based i2cbus with separate class bus-packet

It is functional and working with example bus to run (on rpi) and various clients
master
David Kebler 2018-01-31 18:42:11 -08:00
parent c737a29368
commit a2844cc313
16 changed files with 313 additions and 151 deletions

View File

@ -1,11 +1,17 @@
module.exports = { module.exports = {
"ecmaFeatures": {
"modules": true,
"spread" : true,
"restParams" : true
},
"env": { "env": {
"es6": true, "es6": true,
"node": true, "node": true,
"mocha": true "mocha": true
}, },
"parserOptions": { "parserOptions": {
"ecmaVersion": 2017 "ecmaVersion": 2017,
"sourceType": "module"
}, },
"extends": "eslint:recommended", "extends": "eslint:recommended",
"rules": { "rules": {

3
.esmrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"@std/esm": "cjs"
}

3
examples/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.log
*.sock
/node_modules/

20
examples/bus.mjs Normal file
View File

@ -0,0 +1,20 @@
/*
* i2c bus with both unix and tcp socket using defaults. For TCP that is host OS name and port 8080
*
*/
import Bus from '../src/bus-packet'
const delay = time => new Promise(res=>setTimeout(()=>res(),time))
;
(async () => {
// let i2cbus = new Bus({id:'i2c-bus', log:true})
let i2cbus = new Bus({id:'i2c-bus', sockets:'us,ts', log:true})
await i2cbus.init()
})().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
})

52
examples/ipc-relay.mjs Normal file
View File

@ -0,0 +1,52 @@
/*
* A tcp customer/client to talk with the i2c bus and scan the bus for devices
*
*/
const PATH = '/opt/uci/uci-base/src/unix.sock'
import Base from '../../uci-base/src/base'
// import Base from '@uci/base'
const delay = time => new Promise(res=>setTimeout(()=>res(),time))
;
(async () => {
let relays = new Base({sockets:'uc', path:PATH, log:true})
relays.reply = function (packet) {
// console.log(packet.bus)
console.log('response from relays',packet.bus.func, packet.bus.args.cmd, packet.bus.response)
}
await relays.init()
console.log('=============sending============')
let packet = {cmd:'rw', bus:{func:'read', args:{address:39 ,cmd: 0}} }
console.dir(packet.bus)
await relays.send(packet)
packet = {cmd:'rw', bus:{func:'write', args:{address:39,cmd: 0, byte:0}} }
console.dir(packet.bus)
await relays.send(packet)
packet = {cmd:'rw', bus:{func:'read', args:{address:39 ,cmd:0}} }
console.dir(packet.bus)
await relays.send(packet)
packet = {cmd:'rw', bus:{func:'write', args:{address:39,cmd: 9, byte:255}} }
console.dir(packet.bus)
await relays.send(packet)
packet = {cmd:'rw', bus:{func:'read', args:{address:39 ,cmd:9}} }
console.dir(packet.bus)
await relays.send(packet)
await delay(1000)
packet = {cmd:'rw', bus:{func:'write', args:{address:39,cmd: 9, byte:0}} }
console.dir(packet.bus)
await relays.send(packet)
packet = {cmd:'rw', bus:{func:'read', args:{address:39 ,cmd:9}} }
console.dir(packet.bus)
await relays.send(packet)
await delay(1000)
process.kill(process.pid, 'SIGTERM')
})().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
})

30
examples/ipc-scan.mjs Normal file
View File

@ -0,0 +1,30 @@
/*
* i2c bus unix socket and client in one for demo
*
*/
import Bus from '../src/bus-packet'
const delay = time => new Promise(res=>setTimeout(()=>res(),time))
;
(async () => {
let i2cbus = new Bus({id:'i2c-bus', sockets:'us,uc'})
i2cbus.reply = function (packet) {
console.log('==== response from i2cbus =>',packet.bus.response)
}
await i2cbus.init()
console.log('=============sending============')
let packet = {cmd:'rw', bus:{func:'scan'} }
console.dir(packet)
await i2cbus.send(packet)
await delay(1000)
process.kill(process.pid, 'SIGTERM')
})().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
})

52
examples/tcp-relay.mjs Normal file
View File

@ -0,0 +1,52 @@
/*
* A tcp customer/client to talk with the i2c bus and scan the bus for devices
*
*/
const HOST = 'sbc'
import Base from '../../uci-base/src/base'
// import Base from '@uci/base'
const delay = time => new Promise(res=>setTimeout(()=>res(),time))
;
(async () => {
let relays = new Base({id:'tcp-i2c-client', sockets:'tc', host:HOST, log:true})
relays.reply = function (packet) {
// console.log(packet.bus)
console.log('response from relays',packet.bus.func, packet.bus.args.cmd, packet.bus.response)
}
await relays.init()
console.log('=============sending============')
let packet = {cmd:'rw', bus:{func:'read', args:{address:39 ,cmd: 0}} }
console.dir(packet.bus)
await relays.send(packet)
packet = {cmd:'rw', bus:{func:'write', args:{address:39,cmd: 0, byte:0}} }
console.dir(packet.bus)
await relays.send(packet)
packet = {cmd:'rw', bus:{func:'read', args:{address:39 ,cmd:0}} }
console.dir(packet.bus)
await relays.send(packet)
packet = {cmd:'rw', bus:{func:'write', args:{address:39,cmd: 9, byte:255}} }
console.dir(packet.bus)
await relays.send(packet)
packet = {cmd:'rw', bus:{func:'read', args:{address:39 ,cmd:9}} }
console.dir(packet.bus)
await relays.send(packet)
await delay(1000)
packet = {cmd:'rw', bus:{func:'write', args:{address:39,cmd: 9, byte:0}} }
console.dir(packet.bus)
await relays.send(packet)
packet = {cmd:'rw', bus:{func:'read', args:{address:39 ,cmd:9}} }
console.dir(packet.bus)
await relays.send(packet)
await delay(1000)
process.kill(process.pid, 'SIGTERM')
})().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
})

34
examples/tcp-scan.mjs Normal file
View File

@ -0,0 +1,34 @@
/*
* A tcp customer/client to talk with the i2c bus and scan the bus for devices
*
*/
const HOST = 'sbc'
const delay = time => new Promise(res=>setTimeout(()=>res(),time))
;
(async () => {
let scanner = new Base({id:'tcp-i2c-client', sockets:'tc', host:HOST})
scanner.reply = function (packet) {
let addresses = packet.bus.response.map(device => {
return device.toString(16)})
console.log(packet)
console.log('==== device hex addreses on i2cbus ===\n',addresses)
}
await scanner.init()
console.log('=============sending============')
let packet = {cmd:'rw', bus:{func:'scan'} }
console.dir(packet)
await scanner.send(packet)
await delay(3000)
process.kill(process.pid, 'SIGTERM')
})().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
})

View File

@ -1,8 +0,0 @@
let opts = {
dirname: __dirname + '/src',
// 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('@uci/require-all')(opts)

View File

@ -1,11 +1,12 @@
{ {
"name": "@uci/i2c", "name": "@uci/i2c-bus",
"version": "0.1.1", "version": "0.1.0",
"description": "Bus and Device Classes for I2C Interfacing", "description": "I2c Bus Classes for Communication to I2C bus via socket or direct call",
"main": "index.js", "main": "src/bus",
"scripts": { "scripts": {
"testw": "./node_modules/.bin/mocha --reporter list --recursive --watch", "test": "mocha -r @std/esm test/*.test.mjs",
"test": "istanbul cover ./node_modules/.bin/_mocha test/ --report lcovonly -- -R spec --recursive && codecov || true" "testw": "mocha -r @std/esm test/*.test.mjs --watch --recurse --watch-extensions mjs",
"testci": "istanbul cover ./node_modules/.bin/_mocha test/ --report lcovonly -- -R spec --recursive && codecov || true"
}, },
"author": "David Kebler", "author": "David Kebler",
"license": "MIT", "license": "MIT",
@ -23,11 +24,14 @@
"url": "https://github.com/uCOMmandIt/i2c/issues" "url": "https://github.com/uCOMmandIt/i2c/issues"
}, },
"homepage": "https://github.com/uCOMmandIt/i2c#readme", "homepage": "https://github.com/uCOMmandIt/i2c#readme",
"@std/esm": "cjs",
"dependencies": { "dependencies": {
"@uci/require-all": "^2.x", "@uci/base": "^0.1.0",
"i2c-bus": "^1.x" "i2c-bus": "^1.x",
"pify": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@std/esm": "^0.18.0",
"chai": "^3.5.0", "chai": "^3.5.0",
"chai-as-promised": "^6.0.0", "chai-as-promised": "^6.0.0",
"codecov": "^1.0.1", "codecov": "^1.0.1",

76
src/bus-packet.mjs Normal file
View File

@ -0,0 +1,76 @@
import i2c from 'i2c-bus'
import pify from 'pify'
// import Base from '@uci/base'
import Base from '../../uci-base/src/base'
export default class Bus extends Base {
constructor(opts) {
opts.sockets = opts.sockets || 'us'
super(opts)
this.busnum = opts.busnum || 1
this.i2cbus = i2c.open(this.busnum, () => {})
this.funcs = bus_funcs
// this.init = this.init.bind(this)
}
async init(){
// console.log('init', this.rw)
await super.init()
}
async rw (packet){
// console.dir('=>',packet.bus.fusncs)
if (!packet.bus.func) return {error: 'no i2c bus function in packet', packet: packet.bus }
if (this.funcs[packet.bus.func]) {
packet.bus.response = await this.funcs[packet.bus.func].bind(this)(packet.bus.args)
packet.cmd = 'reply'
return packet
}
return {error: 'no i2c bus function available for packet function', packet: packet.bus }
}
} // end of Bus Packet Class
const bus_funcs = {
scan: function () { return pify(this.i2cbus.scan).bind(this.i2cbus)() },
close: function () { return pify(this.i2cbus.close).bind(this.i2cbus)() },
readRaw: function (arg) {
return pify(this.i2cbus.i2cRead).bind(this.i2cbus)(arg.address, arg.length, arg.buffer)
},
writeRaw: function (arg) {
return pify(this.i2cbus.i2cWrite).bind(this.i2cbus)(arg.address, arg.length, arg.buffer)
},
read: function (arg) {
// console.log('read: address, cmd', address, cmd)
return pify(this.i2cbus.readByte).bind(this.i2cbus)(arg.address, arg.cmd)
},
write: function (arg) {
// console.log('write: address, cmd, byte', arg.address, arg.cmd, arg.byte)
return pify(this.i2cbus.writeByte.bind(this.i2cbus))(arg.address, arg.cmd, arg.byte)
},
read2: function (arg) {
return pify(this.i2cbus.readWord.bind(this.i2cbus))(arg.address, arg.cmd)
},
write2: function (arg) {
return pify(this.i2cbus.writeWord.bind(this.i2cbus))(arg.address, arg.cmd, arg.bytes)
},
receive: function (arg) {
// console.log('receivebyte', address)
return pify(this.i2cbus.receiveByte.bind(this.i2cbus))(arg.address)
},
send: function (arg) {
// console.log('sendbyte', address,byte)
return pify(this.i2cbus.sendByte.bind(this.i2cbus))(arg.address, arg.byte)
}
}

View File

@ -1,12 +1,8 @@
// Solely a promise wrapper module for the basic async functions of the fivdi/i2c-bus javascript bindings C API
// https://github.com/fivdi/i2c-bus
'use strict' import i2c from 'i2c-bus'
import pify from 'pify'
const i2c = require('i2c-bus'), export default class Bus {
pify = require('pify')
class Bus {
constructor(busnum=1) { constructor(busnum=1) {
this.busnum = busnum this.busnum = busnum
this.bus = i2c.open(this.busnum, () => {}) this.bus = i2c.open(this.busnum, () => {})
@ -55,7 +51,3 @@ class Bus {
} // end of Bus Class } // end of Bus Class
module.exports = {
Bus
}

View File

@ -1,76 +0,0 @@
'use strict'
// **********************************
class Device {
// bus is i2c-bus bus object
constructor(bus, address, opts) {
this.bus = bus
this.address = address
if (opts) {
this.id = opts.id // must be unique within a bus
this.desc = opts.desc
this.channel = opts.channel // if using TAC9546A channel number on which device is attached
}
}
async _setChannel() {
// console.log('before set',this.address,this.id,this.channel, this.bus.getState)
if (this.channel) {
if (!this.bus.address) { return Promise.reject('Channel set but no mux on bus')}
return this.bus.set(this.channel)
}
return Promise.resolve() // no channel for device either no mux or device is attached to mux bypass
}
// for devices that need just a simple send of a byte without a register command
async receive() {
await this._setChannel()
return this.bus.receive(this.address)
}
async send(cmd, byte) {
await this._setChannel()
return this.bus.send(this.address, cmd, byte)
}
// for devices needing a buffer/stream
async readRaw(length, buffer) {
await this._setChannel()
return this.bus.readRaw(this.address, length, buffer)
}
async writeRaw(length, buffer) {
await this._setChannel()
return this.bus.writeRaw(this.address, length, buffer)
}
// both cmd and byte should be a single byte as a decimal or hex
async read(cmd) {
await this._setChannel()
// console.log('after set before read',this.address,this.id,this.channel,this.bus.getState)
return this.bus.read(this.address, cmd)
}
async write(cmd, byte) {
await this._setChannel()
// console.log('after set, before write',this.address,this.id,this.channel,this.bus.getState)
return this.bus.write(this.address, cmd, byte)
}
// for I2C devices that use a word length packackage
async read2(cmd) {
await this._setChannel()
return this.bus.read2(this.address, cmd)
}
async write2(cmd, bytes) {
await this._setChannel()
return this.bus.write2(this.address, cmd, bytes)
}
}
module.exports = {
Device
}

View File

@ -1,23 +0,0 @@
'use strict'
const chai = require('chai'),
chaiAsPromised = require("chai-as-promised"),
Bus = require('../lib/bus').Bus
chai.use(chaiAsPromised)
const expect = chai.expect
let bus = new Bus()
// const addresses = [0x20, 0x26, 0x27]
const addresses = []
describe('Bus Class - ', function () {
it('Can scan the bus for devices', function () {
return expect(bus.scan().catch(err => console.log("an error", err))).to.eventually.deep.equal(addresses)
})
})

21
test/bus.test.mjs Normal file
View File

@ -0,0 +1,21 @@
import Bus from '../src/bus'
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
chai.use(chaiAsPromised)
const expect = chai.expect
let bus = new Bus()
const addresses = [0x25, 0x26, 0x27]
// const addresses = []
describe('Bus Class - ', function () {
it('Can scan the bus for devices', function () {
return expect(bus.scan().catch(err => console.log('an error', err))).to.eventually.deep.equal(addresses)
})
})

View File

@ -1,24 +0,0 @@
'use strict'
const chai = require('chai'),
chaiAsPromised = require('chai-as-promised'),
Bus = require('@uci/i2c').Bus,
Device = require('../lib/device').Device
chai.use(chaiAsPromised);
const expect = chai.expect
let bus = new Bus()
let device = new Device(bus, 0x20)
describe('Device Class - ', function () {
let SET = 0xff
it('Can write and read to actual device', function () {
device.write(0x09, SET).then(expect(device.read(0x0A)).to.eventually.equal(SET))
.then(setTimeout(() => device.write(0x09, 0), 3000))
.catch(err => console.log('an error', err))
})
})