From 3c030c753036ceac836bf396e0047b3e00580ab9 Mon Sep 17 00:00:00 2001 From: Kohei Watanabe Date: Fri, 18 Oct 2019 03:23:29 +0900 Subject: [PATCH] initial commit --- .gitignore | 1 + README.md | 26 +++++++++++++++ index.d.ts | 32 ++++++++++++++++++ index.js | 66 +++++++++++++++++++++++++++++++++++++ index.ts | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 25 ++++++++++++++ tsconfig.json | 17 ++++++++++ yarn.lock | 38 ++++++++++++++++++++++ 8 files changed, 295 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 index.d.ts create mode 100644 index.js create mode 100644 index.ts create mode 100644 package.json create mode 100644 tsconfig.json create mode 100644 yarn.lock 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==