bcffd916 |
#!/usr/bin/python3
|
8623d74e |
#
# Module: ina260.py
#
# Description: This module acts as an interface between the INA260 sensor
# and downstream applications that use the data. Class methods get
# current, voltage, and power data from the INA260 sensor. It acts as a
# library module that can be imported into and called from other Python
# programs.
#
|
bcffd916 |
# Copyright 2022 Jeff Owrey
|
8623d74e |
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/license.
#
# Revision History
# * v10 released 01 June 2021 by J L Owrey; first release
|
bcffd916 |
# * v20 released 16 March 2022 by J L Owrey; major revision to add
# fuctionality allowing i2c serial devices to use i2c serial bus
# multiplexer. Also upgraded to python 3.
|
8623d74e |
#
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
# Import the I2C interface library
|
bcffd916 |
import i2cmux
|
8623d74e |
# Define Device Registers
CONFIG_REG = 0x0
ID_REG = 0xFE
CUR_REG = 0x1
VOLT_REG = 0x2
PWR_REG = 0x3
|
b98e8b95 |
# Define default sm bus address.
|
bcffd916 |
DEFAULT_MUX_CHANNEL = 0
|
b98e8b95 |
DEFAULT_BUS_ADDRESS = 0x40
DEFAULT_BUS_NUMBER = 1
# Define the default sensor configuration. See the INA260 data sheet
# for meaning of each bit. The following bytes are written to the
# configuration register
# byte 1: 11100000
# byte 2: 00100111
DEFAULT_CONFIG = 0xE027
|
8623d74e |
class ina260:
# Initialize the INA260 sensor at the supplied address (default
# address is 0x40), and supplied bus (default is 1). Creates
# a new SMBus object for each instance of this class. Writes
# configuration data (two bytes) to the INA260 configuration
# register.
|
bcffd916 |
def __init__(self, objMux, sAddr=DEFAULT_BUS_ADDRESS,
chan=DEFAULT_MUX_CHANNEL,
|
3e66b3fa |
config=DEFAULT_CONFIG,
debug=False):
|
b98e8b95 |
# Instantiate a smbus object.
|
bcffd916 |
self.mux = objMux
|
8623d74e |
self.sensorAddr = sAddr
|
bcffd916 |
self.channel = chan
|
3e66b3fa |
self.debugMode = debug
|
b98e8b95 |
# Initialize INA260 sensor.
initData = [(config >> 8), (config & 0x00FF)]
|
bcffd916 |
self.mux.write_i2c_block_data(self.channel, self.sensorAddr, \
CONFIG_REG, initData)
|
3e66b3fa |
if self.debugMode:
data = self.getInfo()
|
cd727c58 |
print(self)
|
3e66b3fa |
print("manufacturer ID: %s %s\n"\
|
cd727c58 |
"INA260 configuration register: %s %s\n" % data)
|
8623d74e |
## end def
|
3e66b3fa |
def getInfo(self):
|
b98e8b95 |
# Read manufacture identification data.
|
bcffd916 |
mfcid = self.mux.read_i2c_block_data(self.channel, \
self.sensorAddr, ID_REG, 2)
|
8623d74e |
mfcidB1 = format(mfcid[0], "08b")
mfcidB2 = format(mfcid[1], "08b")
|
b98e8b95 |
# Read configuration data.
|
bcffd916 |
config = self.mux.read_i2c_block_data(self.channel, \
self.sensorAddr, CONFIG_REG, 2)
|
8623d74e |
configB1 = format(config[0], "08b")
configB2 = format(config[1], "08b")
return (mfcidB1, mfcidB2, configB1, configB2)
## end def
|
d65ec281 |
def getCurrentReg(self):
# Read current register and return raw binary data for test and
# debug.
|
bcffd916 |
data = self.mux.read_i2c_block_data(self.channel, \
self.sensorAddr, CUR_REG, 2)
|
d65ec281 |
dataB1 = format(data[0], "08b")
dataB2 = format(data[1], "08b")
return (dataB1, dataB2)
## end def
|
8623d74e |
def getCurrent(self):
# Get the current data from the sensor.
# INA260 returns the data in two bytes formatted as follows
# -------------------------------------------------
|
3e66b3fa |
# bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
8623d74e |
# -------------------------------------------------
# byte 1 | d15 | d14 | d13 | d12 | d11 | d10 | d9 | d8 |
# -------------------------------------------------
# byte 2 | d7 | d6 | d5 | d4 | d3 | d2 | d1 | d0 |
# -------------------------------------------------
# The current is returned in d15-d0, a two's complement,
# 16 bit number. This means that d15 is the sign bit.
|
bcffd916 |
data=self.mux.read_i2c_block_data(self.channel, \
self.sensorAddr, CUR_REG, 2)
|
3e66b3fa |
if self.debugMode:
dataB1 = format(data[0], "08b")
dataB2 = format(data[1], "08b")
print("current register: %s %s" % (dataB1, dataB2))
|
8623d74e |
# Format into a 16 bit word.
bdata = data[0] << 8 | data[1]
# Convert from two's complement to integer.
# If d15 is 1, the the number is a negative two's complement
|
d65ec281 |
# number. The absolute value is 2^16 - 1 minus the value
|
b98e8b95 |
# of d15-d0 taken as a positive number.
|
8623d74e |
if bdata > 0x7FFF:
|
b98e8b95 |
bdata = -(0xFFFF - bdata) # 0xFFFF equals 2^16 - 1
|
8623d74e |
# Convert integer data to mAmps.
mAmps = bdata * 1.25 # LSB is 1.25 mA
return mAmps
## end def
|
d65ec281 |
def getVoltageReg(self):
# Read voltage register and return raw binary data for test
# and debug.
|
bcffd916 |
data = self.mux.read_i2c_block_data(self.sensorAddr, VOLT_REG, 2)
|
d65ec281 |
dataB1 = format(data[0], "08b")
dataB2 = format(data[1], "08b")
return (dataB1, dataB2)
## end def
|
8623d74e |
def getVoltage(self):
# Get the voltage data from the sensor.
# INA260 returns the data in two bytes formatted as follows
# -------------------------------------------------
|
3e66b3fa |
# bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
8623d74e |
# -------------------------------------------------
# byte 1 | d15 | d14 | d13 | d12 | d11 | d10 | d9 | d8 |
# -------------------------------------------------
# byte 2 | d7 | d6 | d5 | d4 | d3 | d2 | d1 | d0 |
# -------------------------------------------------
# The voltage is returned in d15-d0 as an unsigned integer.
|
bcffd916 |
data=self.mux.read_i2c_block_data(self.channel, \
self.sensorAddr, VOLT_REG, 2)
|
3e66b3fa |
if self.debugMode:
dataB1 = format(data[0], "08b")
dataB2 = format(data[1], "08b")
print("voltage register: %s %s" % (dataB1, dataB2))
|
8623d74e |
# Convert data to volts.
volts = (data[0] << 8 | data[1]) * 0.00125 # LSB is 1.25 mV
return volts
## end def
def getPower(self):
# Get the wattage data from the sensor.
# INA260 returns the data in two bytes formatted as follows
# -------------------------------------------------
|
3e66b3fa |
# bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
8623d74e |
# -------------------------------------------------
# byte 1 | d15 | d14 | d13 | d12 | d11 | d10 | d9 | d8 |
# -------------------------------------------------
# byte 2 | d7 | d6 | d5 | d4 | d3 | d2 | d1 | d0 |
# -------------------------------------------------
# The wattage is returned in d15-d0 as an unsigned integer.
|
bcffd916 |
data=self.mux.read_i2c_block_data(self.channel, \
self.sensorAddr, PWR_REG, 2)
|
3e66b3fa |
if self.debugMode:
dataB1 = format(data[0], "08b")
dataB2 = format(data[1], "08b")
print("power register: %s %s" % (dataB1, dataB2))
|
8623d74e |
# Convert data to milliWatts.
mW = (data[0] << 8 | data[1]) * 10.0 # LSB is 10.0 mW
return mW
## end def
## end class
def test():
|
bcffd916 |
import time
|
8623d74e |
# Initialize the smbus and INA260 sensor.
|
bcffd916 |
mux = i2cmux.i2cmux()
pwr1 = ina260(mux, sAddr=0x40, chan=0, debug=True)
|
8623d74e |
# Print out sensor values.
while True:
|
b98e8b95 |
print("%6.2f mA" % pwr1.getCurrent())
print("%6.2f V" % pwr1.getVoltage())
print("%6.2f mW\n" % pwr1.getPower())
|
8623d74e |
time.sleep(2)
## end def
if __name__ == '__main__':
test()
|