commit 3c030c753036ceac836bf396e0047b3e00580ab9 Author: Kohei Watanabe Date: Fri Oct 18 03:23:29 2019 +0900 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ccbe46 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..eeb5b6d --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# @notweb/i2c + +I2C access with Node.js + +## Usage + +```js +const { requestI2CAccess } = require("@notweb/i2c"); + +const ADT7410_ADDR = 0x48; + +async function main() { + const i2cAccess = await requestI2CAccess(); + const port = gpioAccess.ports.get(1); + const i2cSlave = await port.open(ADT7410_ADDR); + const temperature = + (((await i2cSlave.read8(0x00)) << 8) + (await i2cSlave.read8(0x01))) / 128; + console.log(`Temperature: ${temperature} ℃`); +} + +main(); +``` + +## Document + +[Web I2C API](http://browserobo.github.io/WebI2C) diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..7834829 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,32 @@ +declare type PortNumber = number; +declare type PortName = string; +declare type I2CSlaveAddress = number; +export declare class I2CAccess { + private readonly _ports; + constructor(ports?: I2CPortMap); + readonly ports: I2CPortMap; +} +/** + * Different from Web GPIO API specification. + */ +export declare class I2CPortMap extends Map { + getByName(portName: PortName): I2CPort | undefined; +} +export declare class I2CPort { + private readonly _portNumber; + constructor(portNumber: PortNumber); + readonly portNumber: number; + readonly portName: string; + open(slaveAddress: I2CSlaveAddress): Promise; +} +export interface I2CSlaveDevice { + readonly slaveAddress: I2CSlaveAddress; + read8(registerNumber: number): Promise; + read16(registerNumber: number): Promise; + write8(registerNumber: number, value: number): Promise; + write16(registerNumber: number, value: number): Promise; +} +export declare class OperationError extends Error { +} +export declare function requestI2CAccess(): Promise; +export {}; diff --git a/index.js b/index.js new file mode 100644 index 0000000..65d1152 --- /dev/null +++ b/index.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const i2c_bus_1 = require("i2c-bus"); +const I2CPortMapSizeMax = 32; +const Uint16Max = 65535; +function parseUint16(string) { + const n = Number.parseInt(string, 10); + if (0 <= n && n <= Uint16Max) + return n; + else + throw new RangeError(`Must be between 0 and ${Uint16Max}.`); +} +class I2CAccess { + constructor(ports) { + this._ports = ports == null ? new I2CPortMap() : ports; + } + get ports() { + return this._ports; + } +} +exports.I2CAccess = I2CAccess; +/** + * Different from Web GPIO API specification. + */ +class I2CPortMap extends Map { + getByName(portName) { + const matches = /^i2c-(\d+)$/.exec(portName); + return matches == null ? undefined : this.get(parseUint16(matches[1])); + } +} +exports.I2CPortMap = I2CPortMap; +class I2CPort { + constructor(portNumber) { + this._portNumber = parseUint16(portNumber.toString()); + } + get portNumber() { + return this._portNumber; + } + get portName() { + return `i2c-${this.portNumber}`; + } + async open(slaveAddress) { + const bus = await i2c_bus_1.openPromisified(this.portNumber).catch(error => { + throw new OperationError(error); + }); + return { + slaveAddress, + read8: cmd => bus.readByte(slaveAddress, cmd), + read16: cmd => bus.readWord(slaveAddress, cmd), + write8: (cmd, byte) => bus.writeByte(slaveAddress, cmd, byte), + write16: (cmd, word) => bus.writeWord(slaveAddress, cmd, word) + }; + } +} +exports.I2CPort = I2CPort; +class OperationError extends Error { +} +exports.OperationError = OperationError; +async function requestI2CAccess() { + const ports = new I2CPortMap([...Array(I2CPortMapSizeMax).keys()].map(portNumber => [ + portNumber, + new I2CPort(portNumber) + ])); + return new I2CAccess(ports); +} +exports.requestI2CAccess = requestI2CAccess; diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..4c081d6 --- /dev/null +++ b/index.ts @@ -0,0 +1,90 @@ +import { openPromisified } from "i2c-bus"; + +const I2CPortMapSizeMax = 32; + +const Uint16Max = 65535; + +function parseUint16(string: string) { + const n = Number.parseInt(string, 10); + if (0 <= n && n <= Uint16Max) return n; + else throw new RangeError(`Must be between 0 and ${Uint16Max}.`); +} + +type PortNumber = number; +type PortName = string; + +type I2CSlaveAddress = number; + +export class I2CAccess { + private readonly _ports: I2CPortMap; + + constructor(ports?: I2CPortMap) { + this._ports = ports == null ? new I2CPortMap() : ports; + } + + get ports() { + return this._ports; + } +} + +/** + * Different from Web GPIO API specification. + */ +export class I2CPortMap extends Map { + getByName(portName: PortName) { + const matches = /^i2c-(\d+)$/.exec(portName); + return matches == null ? undefined : this.get(parseUint16(matches[1])); + } +} + +export class I2CPort { + private readonly _portNumber: PortNumber; + + constructor(portNumber: PortNumber) { + this._portNumber = parseUint16(portNumber.toString()); + } + + get portNumber() { + return this._portNumber; + } + + get portName() { + return `i2c-${this.portNumber}`; + } + + async open(slaveAddress: I2CSlaveAddress): Promise { + const bus = await openPromisified(this.portNumber).catch(error => { + throw new OperationError(error); + }); + + return { + slaveAddress, + read8: cmd => bus.readByte(slaveAddress, cmd), + read16: cmd => bus.readWord(slaveAddress, cmd), + write8: (cmd, byte) => bus.writeByte(slaveAddress, cmd, byte), + write16: (cmd, word) => bus.writeWord(slaveAddress, cmd, word) + }; + } +} + +export interface I2CSlaveDevice { + readonly slaveAddress: I2CSlaveAddress; + + read8(registerNumber: number): Promise; + read16(registerNumber: number): Promise; + write8(registerNumber: number, value: number): Promise; + write16(registerNumber: number, value: number): Promise; +} + +export class OperationError extends Error {} + +export async function requestI2CAccess(): Promise { + const ports = new I2CPortMap( + [...Array(I2CPortMapSizeMax).keys()].map(portNumber => [ + portNumber, + new I2CPort(portNumber) + ]) + ); + + return new I2CAccess(ports); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a1609fc --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "@notweb/i2c", + "version": "0.0.1", + "description": "I2C access with Node.js", + "main": "index.js", + "repository": { + "type": "git", + "url": "https://github.com/kou029w/notweb-i2c.git" + }, + "author": "Kohei Watanabe ", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "i2c-bus": "^5.1.0" + }, + "devDependencies": { + "@types/node": "^12.7.12", + "typescript": "^3.6.4" + }, + "scripts": { + "build": "tsc" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..eb1a87d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "noImplicitThis": true, + "alwaysStrict": true, + "strictBindCallApply": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictPropertyInitialization": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..a636acb --- /dev/null +++ b/yarn.lock @@ -0,0 +1,38 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@^12.7.12": + version "12.7.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.12.tgz#7c6c571cc2f3f3ac4a59a5f2bd48f5bdbc8653cc" + integrity sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +i2c-bus@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/i2c-bus/-/i2c-bus-5.1.0.tgz#7d667d225c09017cf044f75fc593d5ea3e0f4ea6" + integrity sha512-u/q1fuZ5xrG77y3uo1rAANycboXsRNjneN+6jXRNMT2yRNpanVkbAP+IArwgsPRCHaY/zKxw7x5G8Y2fSPNseA== + dependencies: + bindings "^1.5.0" + nan "^2.14.0" + +nan@^2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +typescript@^3.6.4: + version "3.6.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d" + integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==