chrisb2 / pi_ina219

This Python library supports the INA219 voltage, current and power monitor from Texas Instruments with a Raspberry Pi using the I2C bus. The intent of the library is to make it easy to use the quite complex functionality of this sensor.
MIT License
114 stars 34 forks source link

Problem with this on a Pi Pico #33

Open BritishTechGuru opened 2 years ago

BritishTechGuru commented 2 years ago

I tried this on a Pi Pico and this is what came up:

%Run -c $EDITOR_CONTENT Traceback (most recent call last): File "", line 1, in File "/lib/ina219.py", line 5, in ImportError: no module named 'logging'

chrisb2 commented 2 years ago

I have never tried my library on the pico, can you give me some more details on how you installed it?

Regards, Chris

chrisb2 commented 2 years ago

I presume you have installed the Micropython port for the Pico https://github.com/micropython/micropython/tree/master/ports/rp2 from https://micropython.org/download/?port=rp2?

In any case as this a microcontroller you probably need this version of that library https://github.com/chrisb2/pyb_ina219. The readme includes instructions for installing the logging library. I have never tried this library on the Pico, but I do have a Pico so could have a go at the weekend if you have any problems.

regards, Chris

BritishTechGuru commented 2 years ago

OK. Got muddled. Thought I was responding to a response to my ticket on the I2C GPS unit. Back on track now. I just tried the code on the Pi Pico and got some very strange results.

With no battery connected to the voltage sensor I get: Voltage: 0.856 V Current: 0.007 A Percent: 0.0 %

Voltage: 0.856 V Current: 0.007 A Percent: 0.0 %

Voltage: 0.856 V Current: 0.006 A Percent: 0.0 %

With a brand new 1.5v battery connected I get... Voltage: 0.700 V Current: 14.768 A Percent: 0.0 %

Voltage: 0.696 V Current: 15.279 A Percent: 0.0 %

Voltage: 0.680 V Current: 16.019 A Percent: 0.0 %

With a brand new 9v battery I get Voltage: 0.800 V Current: 8.878 A Percent: 0.0 %

Voltage: 0.796 V Current: 8.481 A Percent: 0.0 %

Voltage: 0.804 V Current: 8.009 A Percent: 0.0 %

Voltage: 0.804 V Current: 7.563 A Percent: 0.0 %

It just seems to be giving me very random figures and I just can't make sense of them.

With a worn out 9v battery I get: Voltage: 0.708 V Current: 7.740 A Percent: 0.0 %

Voltage: 0.732 V Current: 7.022 A Percent: 0.0 %

Voltage: 0.708 V Current: 6.487 A Percent: 0.0 %

Voltage: 0.704 V Current: 5.864 A Percent: 0.0 %

BritishTechGuru commented 2 years ago

The code I'm using is... I think I took the logfile out.

rom machine import I2C
import time

# Config Register (R/W)
_REG_CONFIG                 = 0x00
# SHUNT VOLTAGE REGISTER (R)
_REG_SHUNTVOLTAGE           = 0x01

# BUS VOLTAGE REGISTER (R)
_REG_BUSVOLTAGE             = 0x02

# POWER REGISTER (R)
_REG_POWER                  = 0x03

# CURRENT REGISTER (R)
_REG_CURRENT                = 0x04

# CALIBRATION REGISTER (R/W)
_REG_CALIBRATION            = 0x05

class BusVoltageRange:
    """Constants for ``bus_voltage_range``"""
    RANGE_16V               = 0x00      # set bus voltage range to 16V
    RANGE_32V               = 0x01      # set bus voltage range to 32V (default)

class Gain:
    """Constants for ``gain``"""
    DIV_1_40MV              = 0x00      # shunt prog. gain set to  1, 40 mV range
    DIV_2_80MV              = 0x01      # shunt prog. gain set to /2, 80 mV range
    DIV_4_160MV             = 0x02      # shunt prog. gain set to /4, 160 mV range
    DIV_8_320MV             = 0x03      # shunt prog. gain set to /8, 320 mV range

class ADCResolution:
    """Constants for ``bus_adc_resolution`` or ``shunt_adc_resolution``"""
    ADCRES_9BIT_1S          = 0x00      #  9bit,   1 sample,     84us
    ADCRES_10BIT_1S         = 0x01      # 10bit,   1 sample,    148us
    ADCRES_11BIT_1S         = 0x02      # 11 bit,  1 sample,    276us
    ADCRES_12BIT_1S         = 0x03      # 12 bit,  1 sample,    532us
    ADCRES_12BIT_2S         = 0x09      # 12 bit,  2 samples,  1.06ms
    ADCRES_12BIT_4S         = 0x0A      # 12 bit,  4 samples,  2.13ms
    ADCRES_12BIT_8S         = 0x0B      # 12bit,   8 samples,  4.26ms
    ADCRES_12BIT_16S        = 0x0C      # 12bit,  16 samples,  8.51ms
    ADCRES_12BIT_32S        = 0x0D      # 12bit,  32 samples, 17.02ms
    ADCRES_12BIT_64S        = 0x0E      # 12bit,  64 samples, 34.05ms
    ADCRES_12BIT_128S       = 0x0F      # 12bit, 128 samples, 68.10ms

class Mode:
    """Constants for ``mode``"""
    POWERDOW                = 0x00      # power down
    SVOLT_TRIGGERED         = 0x01      # shunt voltage triggered
    BVOLT_TRIGGERED         = 0x02      # bus voltage triggered
    SANDBVOLT_TRIGGERED     = 0x03      # shunt and bus voltage triggered
    ADCOFF                  = 0x04      # ADC off
    SVOLT_CONTINUOUS        = 0x05      # shunt voltage continuous
    BVOLT_CONTINUOUS        = 0x06      # bus voltage continuous
    SANDBVOLT_CONTINUOUS    = 0x07      # shunt and bus voltage continuous

class INA219:
    def __init__(self, i2c_bus=0, addr=0x40, sda=machine.Pin(0),scl=machine.Pin(1)):
        self.i2c_bus = i2c_bus
        self.sda = sda
        self.scl = scl
        self.i2c = I2C(i2c_bus,scl=self.scl,sda=self.sda);
        self.addr = addr

        # Set chip to known config values to start
        self._cal_value = 0
        self._current_lsb = 0
        self._power_lsb = 0
        self.set_calibration_32V_2A()

    def read(self,address):
        data = self.i2c.readfrom_mem(self.addr, address, 2)
        return ((data[0] * 256 ) + data[1])

    def write(self,address,data):
        temp = [0,0]
        temp[1] = data & 0xFF
        temp[0] =(data & 0xFF00) >> 8
        time.sleep_ms(100)
        self.i2c.writeto_mem(self.addr,address,bytes(temp))

    def set_calibration_32V_2A(self):
        """Configures to INA219 to be able to measure up to 32V and 2A of current. Counter
           overflow occurs at 3.2A.
           ..note :: These calculations assume a 0.1 shunt ohm resistor is present
        """
        # By default we use a pretty huge range for the input voltage,
        # which probably isn't the most appropriate choice for system
        # that don't use a lot of power.  But all of the calculations
        # are shown below if you want to change the settings.  You will
        # also need to change any relevant register settings, such as
        # setting the VBUS_MAX to 16V instead of 32V, etc.

        # VBUS_MAX = 32V             (Assumes 32V, can also be set to 16V)
        # VSHUNT_MAX = 0.32          (Assumes Gain 8, 320mV, can also be 0.16, 0.08, 0.04)
        # RSHUNT = 0.1               (Resistor value in ohms)

        # 1. Determine max possible current
        # MaxPossible_I = VSHUNT_MAX / RSHUNT
        # MaxPossible_I = 3.2A

        # 2. Determine max expected current
        # MaxExpected_I = 2.0A

        # 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit)
        # MinimumLSB = MaxExpected_I/32767
        # MinimumLSB = 0.000061              (61uA per bit)
        # MaximumLSB = MaxExpected_I/4096
        # MaximumLSB = 0,000488              (488uA per bit)

        # 4. Choose an LSB between the min and max values
        #    (Preferrably a roundish number close to MinLSB)
        # CurrentLSB = 0.0001 (100uA per bit)
        self._current_lsb = 1  # Current LSB = 100uA per bit

        # 5. Compute the calibration register
        # Cal = trunc (0.04096 / (Current_LSB * RSHUNT))
        # Cal = 4096 (0x1000)

        self._cal_value = 4096

        # 6. Calculate the power LSB
        # PowerLSB = 20 * CurrentLSB
        # PowerLSB = 0.002 (2mW per bit)
        self._power_lsb = .002  # Power LSB = 2mW per bit

        # 7. Compute the maximum current and shunt voltage values before overflow
        #
        # Max_Current = Current_LSB * 32767
        # Max_Current = 3.2767A before overflow
        #
        # If Max_Current > Max_Possible_I then
        #    Max_Current_Before_Overflow = MaxPossible_I
        # Else
        #    Max_Current_Before_Overflow = Max_Current
        # End If
        #
        # Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT
        # Max_ShuntVoltage = 0.32V
        #
        # If Max_ShuntVoltage >= VSHUNT_MAX
        #    Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX
        # Else
        #    Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage
        # End If

        # 8. Compute the Maximum Power
        # MaximumPower = Max_Current_Before_Overflow * VBUS_MAX
        # MaximumPower = 3.2 * 32V
        # MaximumPower = 102.4W

        # Set Calibration register to 'Cal' calculated above
        self.write(_REG_CALIBRATION,self._cal_value)
        # Set Config register to take into account the settings above
        self.bus_voltage_range = BusVoltageRange.RANGE_32V
        self.gain = Gain.DIV_8_320MV
        self.bus_adc_resolution = ADCResolution.ADCRES_12BIT_32S
        self.shunt_adc_resolution = ADCResolution.ADCRES_12BIT_32S
        self.mode = Mode.SANDBVOLT_CONTINUOUS
        self.config = self.bus_voltage_range << 13 | \
                      self.gain << 11 | \
                      self.bus_adc_resolution << 7 | \
                      self.shunt_adc_resolution << 3 | \
                      self.mode
        self.write(_REG_CONFIG,self.config)

    def getShuntVoltage_mV(self):
        value = self.read(_REG_SHUNTVOLTAGE)
        if value > 32767:
            value -= 65535
        return value * 0.01

    def getBusVoltage_V(self):  
        self.read(_REG_BUSVOLTAGE)
        return (self.read(_REG_BUSVOLTAGE) >> 3) * 0.004

    def getCurrent_mA(self):
        value = self.read(_REG_CURRENT)
        if value > 32767:
            value -= 65535
        return value * self._current_lsb

if __name__=='__main__':

    # Create an ADS1115 ADC (16-bit) instance.
    ina219 = INA219(addr=0x40)
    while True:
        bus_voltage = ina219.getBusVoltage_V()             # voltage on V- (load side)
        current = ina219.getCurrent_mA()                   # current in mA
        P = (bus_voltage -3)/1.2*100
        if(P<0):P=0
        elif(P>100):P=100

        # INA219 measure bus voltage on the load side. So PSU voltage = bus_voltage + shunt_voltage
        print("Voltage:  {:6.3f} V".format(bus_voltage))
        print("Current:  {:6.3f} A".format(current/1000))
        print("Percent:  {:6.1f} %".format(P))
        print("")

        time.sleep(2)`
chrisb2 commented 2 years ago

The weird results sound like you may not have a common ground. A good explanation of required circuit can be found in https://www.rototron.info/raspberry-pi-ina219-tutorial/

Chris

BritishTechGuru commented 2 years ago

I had an interesting experience with a couple of other addons that I bought for the Pi Pico. One was an LCD display I was having issues with. It transpired that when I squeezed the display, the text appeared. Clearly a faulty display. That had me thinking about the INA219 and wondering if that too was faulty since we never want to believe something we have just purchased is already broken. Here's a photo of the offending INA219. https://photos.app.goo.gl/xAzYiBvqbUcD2HJ86 Looking at the INA219 I see a large shunt resistor labelled R100. I have zero idea what that resistor value is. That might be the problem. It could be that the guy on eBay wasn't entirely informed about the voltages it's supposed to measure.

Given that I've just had a pile of Buck converters that don't work, a broken LCD and now this - all from different suppliers on eBay - it sounds like I'm running into the same kind of situation I've had before. eBay has periods when nothing I buy from there ever works or is ever as described. I quit eBay for a few months and then go back and the stuff I order from then on is never faulty until the next period. I can't explain that as it defies logic what with it always being different suppliers and different products.

So, I'm wondering about the INA219. I have a second INA219 but given that so much from eBay just hasn't been in working condition when it arrived, I might just toss it in the trash untested and go to somewhere like DigiKey or Mouser to buy another.

chrisb2 commented 2 years ago

R100 is 0.1 Ohms, this is the std shunt resistor value. Your INA219 breakout looks pretty much identical to mine. Maybe make a sketch of your circuit and attach a photo of it and I will see if anything comes to mind.

Chris