import { openPromisified } from 'i2c-bus'; /** * I2C Port Map Max サイズ */ const I2CPortMapSizeMax = 32; /** * Uint16 Max サイズ */ const Uint16Max = 65535; /** * * Uint16型変換処理 * @param parseString 変換文字列 * @return Uint16型変換値 */ function parseUint16(parseString: string) { const n = Number.parseInt(parseString, 10); if (0 <= n && n <= Uint16Max) return n; else throw new RangeError(`Must be between 0 and ${Uint16Max}.`); } /** ポート番号 */ type PortNumber = number; /** ポート名 */ type PortName = string; /** I2C Slave アドレス */ type I2CSlaveAddress = number; /** * I2CAccess クラス */ export class I2CAccess { private readonly _ports: I2CPortMap; /** * Creates an instance of GPIOAccess. * @param ports ポート番号 */ constructor(ports?: I2CPortMap) { this._ports = ports == null ? new I2CPortMap() : ports; } /** * ポート情報取得処理 * @return 現在のポート情報 */ get ports(): I2CPortMap { return this._ports; } } /** Different from Web I2C API specification. */ export class I2CPortMap extends Map { getByName(portName: PortName): I2CPort | undefined { const matches = /^i2c-(\d+)$/.exec(portName); return matches == null ? undefined : this.get(parseUint16(matches[1])); } } /** * I2CPort クラス */ export class I2CPort { private readonly _portNumber: PortNumber; /** * Creates an instance of GPIOPort. * @param portNumber ポート番号 */ constructor(portNumber: PortNumber) { this._portNumber = parseUint16(portNumber.toString()); } /** * ポート番号取得処理 * @return 現在のポート番号 */ get portNumber(): PortNumber { return this._portNumber; } /** * ポート名取得処理 * @return 現在のポート名 */ get portName(): string { return `i2c-${this.portNumber}`; } /** * I2CSlave 接続デバイスオープン処理 * @param slaveAddress 接続デバイス情報のアドレス * @return I2CSlaveDevice インスタンスの生成の完了 */ async open(slaveAddress: I2CSlaveAddress): Promise { const bus = await openPromisified(this.portNumber).catch((error) => { throw new OperationError(error); }); return { slaveAddress, /** * @function * I2C 読み取り処理 * @param registerNumber 読み取りアドレス */ read8: (registerNumber) => bus.readByte(slaveAddress, registerNumber).catch((error) => { throw new OperationError(error); }), /** * @function * I2C 読み取り処理 * @param registerNumber 読み取りアドレス */ read16: (registerNumber) => bus.readWord(slaveAddress, registerNumber).catch((error) => { throw new OperationError(error); }), /** * @function * I2c s/I2c/I2C 書き込み処理 * @param registerNumber 書き込みアドレス * @param byte 書き込みの値(バイト) */ write8: async (registerNumber, byte) => { try { await bus.writeByte(slaveAddress, registerNumber, byte); return byte; } catch (error: any) { throw new OperationError(error); } }, /** * @function * I2c bytes 書き込み処理 * @param registerNumber 書き込みアドレス * @param word 書き込みの値(ワード) */ write16: async (registerNumber, word) => { try { await bus.writeWord(slaveAddress, registerNumber, word); return word; } catch (error: any) { throw new OperationError(error); } }, /** * @function * I2c bytes 読み取りバイト処理 * Different from Web I2C API specification. */ readByte: async () => { try { const byte = await bus.receiveByte(slaveAddress); return byte; } catch (error: any) { throw new OperationError(error); } }, /** * @function * I2c bytes 読み取りバイト処理 * Different from Web I2C API specification. * @param length 読み取る配列の長さ */ readBytes: async (length) => { try { const { bytesRead, buffer } = await bus.i2cRead( slaveAddress, length, Buffer.allocUnsafe(length) ); return new Uint8Array(buffer.slice(0, bytesRead)); } catch (error: any) { throw new OperationError(error); } }, /** * @function * I2c bytes 書き込みバイト処理 * Different from Web I2C API specification. * @param byte 書き込みの値 */ writeByte: async (byte) => { try { await bus.sendByte(slaveAddress, byte); return byte; } catch (error: any) { throw new OperationError(error); } }, /** * @function * I2c bytes 書き込み処理 * Different from Web I2C API specification. * @param bytes 書き込みの値の配列 */ writeBytes: async (bytes) => { try { const { bytesWritten, buffer } = await bus.i2cWrite( slaveAddress, bytes.length, Buffer.from(bytes) ); return new Uint8Array(buffer.slice(0, bytesWritten)); } catch (error: any) { throw new OperationError(error); } }, }; } } /** * I2CSlaveDevice クラス */ export interface I2CSlaveDevice { /** I2C Slave アドレス */ readonly slaveAddress: I2CSlaveAddress; /** * @function * I2C 読み取り処理 * @param registerNumber 読み取りアドレス */ read8(registerNumber: number): Promise; /** * @function * I2C 読み取り処理 * @param registerNumber 読み取りアドレス */ read16(registerNumber: number): Promise; /** * @function * I2c s/I2c/I2C 書き込み処理 * @param registerNumber 書き込みアドレス * @param value 書き込みの値(バイト) */ write8(registerNumber: number, value: number): Promise; /** * @function * I2c bytes 書き込み処理 * @param registerNumber 書き込みアドレス * @param value 書き込みの値(ワード) */ write16(registerNumber: number, value: number): Promise; /** * @function * I2c bytes 読み取りバイト処理 * Different from Web I2C API specification. */ readByte(): Promise; /** * @function * I2c bytes 読み取りバイト処理 * Different from Web I2C API specification. * @param length 読み取る配列の長さ */ readBytes(length: number): Promise; /** * @function * I2c bytes 書き込みバイト処理 * Different from Web I2C API specification. * @param byte 書き込みの値 */ writeByte(byte: number): Promise; /** * @function * I2c bytes 書き込みバイト配列処理 * Different from Web I2C API specification. * @param bytes 書き込みの値の配列 */ writeBytes(bytes: Array): Promise; } /** * 操作エラー */ export class OperationError extends Error { /** * Creates an instance of OperationError. * @param message エラーメッセージ */ constructor(message: string) { super(message); this.name = this.constructor.name; } } // Web I2Cの仕様に基づく意図的なasync関数の使用なので、ルールを無効化 // eslint-disable-next-line export async function requestI2CAccess(): Promise { const ports = new I2CPortMap( [...Array(I2CPortMapSizeMax).keys()].map((portNumber) => [ portNumber, new I2CPort(portNumber), ]) ); return new I2CAccess(ports); }