@name-industry/ni-ina219
Version:
Module for using the WaveShare UPS Raspberry Pi Hat that has an embedded Texas Instruments INA219 sensor.
249 lines (220 loc) • 7.58 kB
JavaScript
/**
* @class I2cBus
*
* @summary
* Temporary included I2c-Bus wrapper
*
* @description
* Notes:
* <br /><br />
* In node we can allocate a buffer with a size and type and default values.
* I am not sure what the underlying wrappers to the main SMBus returns.
* <br /><br />
* Raspberry Pi -> lscpu | grep -i endian<br />
* returns Little Endian also double check<br />
* echo -en \\001\\002 | od -An -tx2<br />
* 0201 # little-endian <- returns<br />
* 0102 # big-endian<br />
*/
import i2c from 'i2c-bus';
class I2CBus {
constructor() {
/** @type {Number} */
this.i2cAddress = 66; // UPS Hat address
/** @type {Object | Undefined} */
this.wire = undefined;
/** @type {Number} */
this.busNumber = 1; // default on PI "/dev/i2c-1"
/** @type {String} */
this.i2cAddressAsHex = "0x42"; // UPS Hat address
}
/**
* @method I2cBus#initialize
*
* @description
* Please note i2c-bus lib allows you to connect to any
* bus number as long as its an INT. So the returned
* bus object does not fail/error on "open" of an arbitrary
* hardware bus line.
*
* @param {Number} i2cAddress Address in hex of the sensor ie: 0x42 || 66
* @param {Number} busNumber The Bus address as an integer ie: 1 ( for PI )
* @returns {Promise<(ResultObject|ErrorResultObject)>} returns dto
*/
initialize = async function (
i2cAddress = 0x42, // note: JS passes around and displays Decimal
busNumber = 1,
options = {}
) {
let wire;
try {
wire = await i2c.openPromisified(busNumber, options).then(wire => wire);
} catch (error) {
return {
success: false,
msg: "[I2c] - Error on open I2CBus",
data: {
errorMessage: error.stack.split("\n")[0],
errorStack: error
}
}
}
// I2C_Bus docs do not say anything about scan being Promises
// but wire.scan returns a promise.
// TODO: set timer prior to calling this.
// on occasion the bus hangs the underlying request
// can lock up the program ( PI )
let addressFound = await wire.scan(i2cAddress);
// delay
// Sometimes a sensor is in sleep mode ( not the UPS Hat )
// so we can pause and re-scan again ( a scan can trigger a wake up
// making the sensor visible on I2c )
addressFound = await wire.scan(i2cAddress);
if (addressFound.length === 0 || addressFound[0] !== i2cAddress) {
return {
success: false,
msg: "[I2c] - No device found",
data: {
wire: wire,
i2cAddressRequested: "0x" + i2cAddress.toString(16)
}
}
} else {
this.updateState(i2cAddress, wire, busNumber, "0x" + i2cAddress.toString(16));
return {
success: true,
msg: "[I2c] - Ready",
data: {
wire: wire,
busNumber: busNumber,
i2cAddressAsHex: "0x" + i2cAddress.toString(16)
}
}
}
}
/**
* @method I2cBus#updateState
*
* @summary
* Update class instance variables
*
* @param {Number} i2cAddress
* @param {Object} wire
* @param {Number} busNumber
* @param {String} i2cAddressAsHex
*/
updateState = function (i2cAddress, wire, busNumber, i2cAddressAsHex) {
this.i2cAddress = i2cAddress;
this.wire = wire;
this.busNumber = busNumber;
this.i2cAddressAsHex = i2cAddressAsHex;
}
/**
* @method I2cBus#getConnectedDevices
*
* @summary
* Expose bus scan in the odd case its needed
*
* @returns {Promise<(ResultObject|ErrorResultObject)>} returns dto
*/
getConnectedDevices = async function () {
// active + asleep
let allAddressesFound = await this.wire.scan();
// delay
// active ( waking up asleep )
allAddressesFound = await this.wire.scan();
if (allAddressesFound.length === 0) {
return {
success: false,
msg: "[I2c] - No devices found",
data: {}
}
} else {
return {
success: true,
msg: "[I2c] - All devices on bus",
data: {
allAddresses: allAddressesFound.map((v, i) => { return "0x" + v.toString(16) })
}
}
}
}
/**
* @method I2cBus#readRegister
*
* @summary
* Returns bytesRead and the hydrated buffer on success
*
* @description
* Registers are 16 bits. 2 bytes.
* Requires the I2c register address in Hex to read from
*
* @param {Number} register address in hex of the register to read from ie: 0x01
* @returns {Promise<(ResultObject|ErrorResultObject)>} returns value object
*/
readRegister = async function (register) {
let resultBuffer = Buffer.alloc(2, 0, "utf-8");
let data;
try {
data = await this.wire.readI2cBlock(this.i2cAddress, register, 2, resultBuffer);
} catch (error) {
return {
success: false,
msg: "[I2c Bus] - Read Error",
data: {
errorName: error.name,
errorMessage: error.message
}
}
}
return {
success: true,
msg: "[I2c Bus] - Bytes read",
data: {
bytesRead: data.bytesRead,
buffer: data.buffer,
payload: data.buffer.readInt16BE(0, 2)
}
}
}
/**
* @method I2cBus#writeRegister
*
* @summary
* Write wrapper for INA-219 register
*
* @description
* Use I2C lib to write to the INA-219 register - writeI2cBlock method
*
* @param {Number} register address in hex of the register to write to ie: 0x04
* @param {Number} value the new value to be written to the register
* @returns {Promise<(ResultObject|ErrorResultObject)>} returns value object
*/
writeRegister = async function (register, value) {
let bytes = Buffer.alloc(2, 0, "utf-8"); // why is this not "binary" 22
bytes[0] = (value >> 8) & 0xFF;
bytes[1] = value & 0xFF;
let data;
try {
data = await this.wire.writeI2cBlock(this.i2cAddress, register, 2, bytes);
} catch (error) {
return {
success: false,
msg: "[I2c Bus] - Write Error",
data: {
errorName: error.name,
errorMessage: error.message
}
}
}
return {
success: true,
msg: "[I2c Bus] - Bytes written",
data: {
register: register,
value: value
}
}
}
}
export default new I2CBus();