node-web-i2c/index.ts

308 lines
7.8 KiB
TypeScript
Raw Normal View History

2022-01-11 18:06:49 +09:00
import { openPromisified } from 'i2c-bus';
2019-10-18 03:23:29 +09:00
/**
* I2C Port Map Max
*/
2019-10-18 03:23:29 +09:00
const I2CPortMapSizeMax = 32;
/**
* Uint16 Max
*/
2019-10-18 03:23:29 +09:00
const Uint16Max = 65535;
/**
*
* Uint16型変換処理
* @param parseString
* @return Uint16型変換値
*/
2022-01-11 18:06:49 +09:00
function parseUint16(parseString: string) {
const n = Number.parseInt(parseString, 10);
2019-10-18 03:23:29 +09:00
if (0 <= n && n <= Uint16Max) return n;
else throw new RangeError(`Must be between 0 and ${Uint16Max}.`);
}
/** ポート番号 */
2019-10-18 03:23:29 +09:00
type PortNumber = number;
/** ポート名 */
2019-10-18 03:23:29 +09:00
type PortName = string;
/** I2C Slave アドレス */
2019-10-18 03:23:29 +09:00
type I2CSlaveAddress = number;
/**
* I2CAccess
*/
2019-10-18 03:23:29 +09:00
export class I2CAccess {
private readonly _ports: I2CPortMap;
/**
* Creates an instance of GPIOAccess.
* @param ports
*/
2019-10-18 03:23:29 +09:00
constructor(ports?: I2CPortMap) {
this._ports = ports == null ? new I2CPortMap() : ports;
}
/**
*
* @return
*/
get ports(): I2CPortMap {
2019-10-18 03:23:29 +09:00
return this._ports;
}
}
2019-12-19 14:04:48 +09:00
/** Different from Web I2C API specification. */
2019-10-18 03:23:29 +09:00
export class I2CPortMap extends Map<PortNumber, I2CPort> {
getByName(portName: PortName): I2CPort | undefined {
2019-10-18 03:23:29 +09:00
const matches = /^i2c-(\d+)$/.exec(portName);
return matches == null ? undefined : this.get(parseUint16(matches[1]));
}
}
/**
* I2CPort
*/
2019-10-18 03:23:29 +09:00
export class I2CPort {
private readonly _portNumber: PortNumber;
/**
* Creates an instance of GPIOPort.
* @param portNumber
*/
2019-10-18 03:23:29 +09:00
constructor(portNumber: PortNumber) {
this._portNumber = parseUint16(portNumber.toString());
}
/**
*
* @return
*/
get portNumber(): PortNumber {
2019-10-18 03:23:29 +09:00
return this._portNumber;
}
/**
*
* @return
*/
get portName(): string {
2019-10-18 03:23:29 +09:00
return `i2c-${this.portNumber}`;
}
/**
* I2CSlave
* @param slaveAddress
* @return I2CSlaveDevice
*/
2019-10-18 03:23:29 +09:00
async open(slaveAddress: I2CSlaveAddress): Promise<I2CSlaveDevice> {
2020-06-25 11:45:22 +09:00
const bus = await openPromisified(this.portNumber).catch((error) => {
2019-10-18 03:23:29 +09:00
throw new OperationError(error);
});
return {
slaveAddress,
/**
* @function
* I2C
* @param registerNumber
*/
read8: (registerNumber) =>
bus.readByte(slaveAddress, registerNumber).catch((error) => {
2019-10-18 21:47:52 +09:00
throw new OperationError(error);
}),
/**
* @function
* I2C
* @param registerNumber
*/
read16: (registerNumber) =>
bus.readWord(slaveAddress, registerNumber).catch((error) => {
2019-10-18 21:47:52 +09:00
throw new OperationError(error);
}),
/**
* @function
* I2c s/I2c/I2C
* @param registerNumber
* @param byte
*/
write8: async (registerNumber, byte) => {
2020-02-12 02:20:53 +09:00
try {
await bus.writeByte(slaveAddress, registerNumber, byte);
2020-02-12 02:20:53 +09:00
return byte;
} catch (error: any) {
2019-10-18 21:47:52 +09:00
throw new OperationError(error);
2020-02-12 02:20:53 +09:00
}
},
/**
* @function
* I2c bytes
* @param registerNumber
* @param word
*/
write16: async (registerNumber, word) => {
2020-02-12 02:20:53 +09:00
try {
await bus.writeWord(slaveAddress, registerNumber, word);
2020-02-12 02:20:53 +09:00
return word;
} catch (error: any) {
2019-10-18 21:47:52 +09:00
throw new OperationError(error);
2020-02-12 02:20:53 +09:00
}
},
/**
* @function
* I2c bytes
* Different from Web I2C API specification.
*/
2019-12-19 14:04:48 +09:00
readByte: async () => {
try {
const byte = await bus.receiveByte(slaveAddress);
return byte;
} catch (error: any) {
2019-12-19 14:04:48 +09:00
throw new OperationError(error);
}
},
/**
* @function
* I2c bytes
* Different from Web I2C API specification.
* @param length
*/
2020-06-25 11:45:22 +09:00
readBytes: async (length) => {
2019-12-19 14:04:48 +09:00
try {
const { bytesRead, buffer } = await bus.i2cRead(
slaveAddress,
length,
Buffer.allocUnsafe(length)
);
return new Uint8Array(buffer.slice(0, bytesRead));
} catch (error: any) {
2019-12-19 14:04:48 +09:00
throw new OperationError(error);
}
},
/**
* @function
* I2c bytes
* Different from Web I2C API specification.
* @param byte
*/
2020-06-25 11:45:22 +09:00
writeByte: async (byte) => {
2019-12-19 14:04:48 +09:00
try {
await bus.sendByte(slaveAddress, byte);
return byte;
} catch (error: any) {
2019-12-19 14:04:48 +09:00
throw new OperationError(error);
}
},
/**
* @function
* I2c bytes
* Different from Web I2C API specification.
* @param bytes
*/
2020-06-25 11:45:22 +09:00
writeBytes: async (bytes) => {
2019-12-19 14:04:48 +09:00
try {
const { bytesWritten, buffer } = await bus.i2cWrite(
slaveAddress,
2020-02-06 11:18:33 +09:00
bytes.length,
2019-12-19 14:04:48 +09:00
Buffer.from(bytes)
);
return new Uint8Array(buffer.slice(0, bytesWritten));
} catch (error: any) {
2019-12-19 14:04:48 +09:00
throw new OperationError(error);
}
2020-06-25 11:45:22 +09:00
},
2019-10-18 03:23:29 +09:00
};
}
}
/**
* I2CSlaveDevice
*/
2019-10-18 03:23:29 +09:00
export interface I2CSlaveDevice {
/** I2C Slave アドレス */
2019-10-18 03:23:29 +09:00
readonly slaveAddress: I2CSlaveAddress;
/**
* @function
* I2C
* @param registerNumber
*/
2019-10-18 03:23:29 +09:00
read8(registerNumber: number): Promise<number>;
/**
* @function
* I2C
* @param registerNumber
*/
2019-10-18 03:23:29 +09:00
read16(registerNumber: number): Promise<number>;
/**
* @function
* I2c s/I2c/I2C
* @param registerNumber
* @param value
*/
2019-10-18 03:23:29 +09:00
write8(registerNumber: number, value: number): Promise<number>;
/**
* @function
* I2c bytes
* @param registerNumber
* @param value
*/
2019-10-18 03:23:29 +09:00
write16(registerNumber: number, value: number): Promise<number>;
2019-12-19 14:04:48 +09:00
/**
* @function
* I2c bytes
* Different from Web I2C API specification.
*/
2019-12-19 14:04:48 +09:00
readByte(): Promise<number>;
/**
* @function
* I2c bytes
* Different from Web I2C API specification.
* @param length
*/
2019-12-19 14:04:48 +09:00
readBytes(length: number): Promise<Uint8Array>;
/**
* @function
* I2c bytes
* Different from Web I2C API specification.
* @param byte
*/
2019-12-19 14:04:48 +09:00
writeByte(byte: number): Promise<number>;
/**
* @function
* I2c bytes
* Different from Web I2C API specification.
* @param bytes
*/
2019-12-19 14:04:48 +09:00
writeBytes(bytes: Array<number>): Promise<Uint8Array>;
2019-10-18 03:23:29 +09:00
}
/**
*
*/
2019-10-18 21:45:56 +09:00
export class OperationError extends Error {
/**
* Creates an instance of OperationError.
* @param message
*/
2019-10-18 21:45:56 +09:00
constructor(message: string) {
super(message);
this.name = this.constructor.name;
}
}
2019-10-18 03:23:29 +09:00
// Web I2Cの仕様に基づく意図的なasync関数の使用なので、ルールを無効化
// eslint-disable-next-line
2019-10-18 03:23:29 +09:00
export async function requestI2CAccess(): Promise<I2CAccess> {
const ports = new I2CPortMap(
2020-06-25 11:45:22 +09:00
[...Array(I2CPortMapSizeMax).keys()].map((portNumber) => [
2019-10-18 03:23:29 +09:00
portNumber,
2020-06-25 11:45:22 +09:00
new I2CPort(portNumber),
2019-10-18 03:23:29 +09:00
])
);
return new I2CAccess(ports);
}