2e887e4a6d8dec2103baf916bc3d0c2c48b94edc
\353\260\260\355\204\260\353\246\254 \354\240\204\354\225\225 \354\240\204\353\245\230 \354\235\275\353\212\224 \354\275\224\353\223\234.md
| ... | ... | @@ -0,0 +1,334 @@ |
| 1 | +ina226.py |
|
| 2 | + |
|
| 3 | +``` |
|
| 4 | +#!/usr/bin/env python3 |
|
| 5 | +# -*- coding: utf-8 -*- |
|
| 6 | +import time |
|
| 7 | +import math |
|
| 8 | +import ctypes |
|
| 9 | +import sys |
|
| 10 | + |
|
| 11 | +PYTHON_SMBUS_LIB_PRESENT = True |
|
| 12 | +PYTHON_AARDVARK_LIB_PRESENT = False |
|
| 13 | + |
|
| 14 | +try: |
|
| 15 | + import smbus |
|
| 16 | +except ImportError as e: |
|
| 17 | + PYTHON_SMBUS_LIB_PRESENT = False |
|
| 18 | + |
|
| 19 | +try: |
|
| 20 | + import pyaardvark |
|
| 21 | +except ImportError as e: |
|
| 22 | + PYTHON_AARDVARK_LIB_PRESENT = False |
|
| 23 | + |
|
| 24 | + |
|
| 25 | + |
|
| 26 | +INA226_ADDRESS =(0x40) |
|
| 27 | + |
|
| 28 | +INA226_REG_CONFIG =(0x00) |
|
| 29 | +INA226_REG_SHUNTVOLTAGE =(0x01) |
|
| 30 | +INA226_REG_BUSVOLTAGE =(0x02) |
|
| 31 | +INA226_REG_POWER =(0x03) |
|
| 32 | +INA226_REG_CURRENT =(0x04) |
|
| 33 | +INA226_REG_CALIBRATION =(0x05) |
|
| 34 | +INA226_REG_MASKENABLE =(0x06) |
|
| 35 | +INA226_REG_ALERTLIMIT =(0x07) |
|
| 36 | + |
|
| 37 | +INA226_BIT_SOL =(0x8000) |
|
| 38 | +INA226_BIT_SUL =(0x4000) |
|
| 39 | +INA226_BIT_BOL =(0x2000) |
|
| 40 | +INA226_BIT_BUL =(0x1000) |
|
| 41 | +INA226_BIT_POL =(0x0800) |
|
| 42 | +INA226_BIT_CNVR =(0x0400) |
|
| 43 | +INA226_BIT_AFF =(0x0010) |
|
| 44 | +INA226_BIT_CVRF =(0x0008) |
|
| 45 | +INA226_BIT_OVF =(0x0004) |
|
| 46 | +INA226_BIT_APOL =(0x0002) |
|
| 47 | +INA226_BIT_LEN =(0x0001) |
|
| 48 | + |
|
| 49 | +#enum replacement, but not truly |
|
| 50 | +#now replaced class by dict because it can give me back keys |
|
| 51 | +ina226_averages_t = dict( |
|
| 52 | + INA226_AVERAGES_1 = 0b000, |
|
| 53 | + INA226_AVERAGES_4 = 0b001, |
|
| 54 | + INA226_AVERAGES_16 = 0b010, |
|
| 55 | + INA226_AVERAGES_64 = 0b011, |
|
| 56 | + INA226_AVERAGES_128 = 0b100, |
|
| 57 | + INA226_AVERAGES_256 = 0b101, |
|
| 58 | + INA226_AVERAGES_512 = 0b110, |
|
| 59 | + INA226_AVERAGES_1024 = 0b111) |
|
| 60 | + |
|
| 61 | +ina226_busConvTime_t = dict( |
|
| 62 | + INA226_BUS_CONV_TIME_140US = 0b000, |
|
| 63 | + INA226_BUS_CONV_TIME_204US = 0b001, |
|
| 64 | + INA226_BUS_CONV_TIME_332US = 0b010, |
|
| 65 | + INA226_BUS_CONV_TIME_588US = 0b011, |
|
| 66 | + INA226_BUS_CONV_TIME_1100US = 0b100, |
|
| 67 | + INA226_BUS_CONV_TIME_2116US = 0b101, |
|
| 68 | + INA226_BUS_CONV_TIME_4156US = 0b110, |
|
| 69 | + INA226_BUS_CONV_TIME_8244US = 0b111) |
|
| 70 | + |
|
| 71 | +ina226_shuntConvTime_t = dict( |
|
| 72 | + INA226_SHUNT_CONV_TIME_140US = 0b000, |
|
| 73 | + INA226_SHUNT_CONV_TIME_204US = 0b001, |
|
| 74 | + INA226_SHUNT_CONV_TIME_332US = 0b010, |
|
| 75 | + INA226_SHUNT_CONV_TIME_588US = 0b011, |
|
| 76 | + INA226_SHUNT_CONV_TIME_1100US = 0b100, |
|
| 77 | + INA226_SHUNT_CONV_TIME_2116US = 0b101, |
|
| 78 | + INA226_SHUNT_CONV_TIME_4156US = 0b110, |
|
| 79 | + INA226_SHUNT_CONV_TIME_8244US = 0b111) |
|
| 80 | + |
|
| 81 | +ina226_mode_t = dict( |
|
| 82 | + INA226_MODE_POWER_DOWN = 0b000, |
|
| 83 | + INA226_MODE_SHUNT_TRIG = 0b001, |
|
| 84 | + INA226_MODE_BUS_TRIG = 0b010, |
|
| 85 | + INA226_MODE_SHUNT_BUS_TRIG = 0b011, |
|
| 86 | + INA226_MODE_ADC_OFF = 0b100, |
|
| 87 | + INA226_MODE_SHUNT_CONT = 0b101, |
|
| 88 | + INA226_MODE_BUS_CONT = 0b110, |
|
| 89 | + INA226_MODE_SHUNT_BUS_CONT = 0b111) |
|
| 90 | + |
|
| 91 | +# available options are 'AARDVARK','SBC_LINUX_SMBUS' |
|
| 92 | +I2C_DRIVER = 'AARDVARK' |
|
| 93 | +# other I2C options |
|
| 94 | +I2C_DEFAULT_CLK_KHZ = 100 |
|
| 95 | +I2C_DEFAULT_BUS_NUMBER = 0 |
|
| 96 | + |
|
| 97 | +class ina226: |
|
| 98 | + def __init__(self,ina226_addr = INA226_ADDRESS, i2c_bus_number=I2C_DEFAULT_BUS_NUMBER, i2c_clk_Khz=I2C_DEFAULT_CLK_KHZ, i2c_driver_type = I2C_DRIVER): |
|
| 99 | + |
|
| 100 | + self.i2c_bus = smbus.SMBus(i2c_bus_number) |
|
| 101 | + self.readRegister16 = self.readRegister16_SMBUS |
|
| 102 | + self.writeRegister16 = self.writeRegister16_SMBUS |
|
| 103 | + if i2c_clk_Khz != I2C_DEFAULT_CLK_KHZ: |
|
| 104 | + print 'Python SMBUS linux driver doesn\'t provide I2C CLK Freq Manipulation support yet,' |
|
| 105 | + print 'So Ignoring i2c_clk_khz param and using default.' |
|
| 106 | + |
|
| 107 | + self.ina226_address = ina226_addr |
|
| 108 | + self.vBusMax = 36 |
|
| 109 | + self.vShuntMax = 0.08192 |
|
| 110 | + self.rShunt = 0.1 |
|
| 111 | + self.currentLSB = 0 |
|
| 112 | + self.powerLSB = 0 |
|
| 113 | + self.iMaxPossible = 0 |
|
| 114 | + |
|
| 115 | + #not using with statement related code yet |
|
| 116 | + |
|
| 117 | + #this causes some issue may be because when exception occurs I am manually calling |
|
| 118 | + #self.close() and even this function tries to call the same. Need to check. |
|
| 119 | + #def __del__(self): |
|
| 120 | + # self.close() |
|
| 121 | + |
|
| 122 | + def close(self): |
|
| 123 | + self.i2c_bus.close() |
|
| 124 | + |
|
| 125 | + def readRegister16_SMBUS(self,register): |
|
| 126 | + #higher_byte = self.i2c_bus.read_byte_data(self.ina226_address,register) |
|
| 127 | + #lower_byte = self.i2c_bus.read_byte_data(self.ina226_address,register+1) |
|
| 128 | + data = self.i2c_bus.read_i2c_block_data(self.ina226_address,register,2) |
|
| 129 | + higher_byte = data[0] |
|
| 130 | + lower_byte = data[1] |
|
| 131 | + #there is still some issue in read which we need to fix, we are not able to print negative current--done--fixed using ctypes int16 return |
|
| 132 | + word_data = higher_byte << 8 | lower_byte |
|
| 133 | + #return word_data |
|
| 134 | + return ctypes.c_int16(word_data).value |
|
| 135 | + |
|
| 136 | + def writeRegister16_SMBUS(self,register,dataWord): |
|
| 137 | + higher_byte = (dataWord >> 8) & 0xff |
|
| 138 | + lower_byte = dataWord & 0xff #truncating the dataword to byte |
|
| 139 | + self.i2c_bus.write_i2c_block_data(self.ina226_address,register,[higher_byte,lower_byte]) |
|
| 140 | + |
|
| 141 | + def readRegister16_AARDVARK(self,register): |
|
| 142 | + #higher_byte = self.i2c_bus.read_byte_data(self.ina226_address,register) |
|
| 143 | + #lower_byte = self.i2c_bus.read_byte_data(self.ina226_address,register+1) |
|
| 144 | + #data = self.i2c_bus.read_i2c_block_data(self.ina226_address,register,2) |
|
| 145 | + |
|
| 146 | + register_addr_str = chr(register) |
|
| 147 | + |
|
| 148 | + byte_char_data = self.i2c_bus.i2c_master_write_read(self.ina226_address,register_addr_str,2) |
|
| 149 | + |
|
| 150 | + data = [ord(b) for b in byte_char_data] |
|
| 151 | + |
|
| 152 | + higher_byte = data[0] |
|
| 153 | + lower_byte = data[1] |
|
| 154 | + #there is still some issue in read which we need to fix, we are not able to print negative current--done--fixed using ctypes int16 return |
|
| 155 | + word_data = higher_byte << 8 | lower_byte |
|
| 156 | + #return word_data |
|
| 157 | + return ctypes.c_int16(word_data).value |
|
| 158 | + #if this does not work as expected than we should try to read bytes and they convert into word as in Arduino |
|
| 159 | + #return self.i2c_bus.read_word_data(self.ina226_address,register) |
|
| 160 | + |
|
| 161 | + def writeRegister16_AARDVARK(self,register,dataWord): |
|
| 162 | + higher_byte = (dataWord >> 8) & 0xff |
|
| 163 | + lower_byte = dataWord & 0xff #truncating the dataword to byte |
|
| 164 | + |
|
| 165 | + data = (register, higher_byte, lower_byte) |
|
| 166 | + data = ''.join(chr(c) for c in data) |
|
| 167 | + |
|
| 168 | + self.i2c_bus.i2c_master_write(self.ina226_address,data) |
|
| 169 | + |
|
| 170 | + #self.i2c_bus.write_i2c_block_data(self.ina226_address,register,[higher_byte,lower_byte]) |
|
| 171 | + |
|
| 172 | + #doesn't work |
|
| 173 | + #self.i2c_bus.write_byte_data(self.ina226_address,register,higher_byte) |
|
| 174 | + #self.i2c_bus.write_byte_data(self.ina226_address,register+1,lower_byte) |
|
| 175 | + #if this does not work as expected than we should try to read bytes and they convert into word as in Arduino |
|
| 176 | + #self.i2c_bus.write_word_data(self.ina226_address,register,dataWord) |
|
| 177 | + |
|
| 178 | + def configure(self,avg = ina226_averages_t['INA226_AVERAGES_1'], busConvTime = ina226_busConvTime_t['INA226_BUS_CONV_TIME_1100US'], shuntConvTime = ina226_shuntConvTime_t['INA226_SHUNT_CONV_TIME_1100US'], mode = ina226_mode_t['INA226_MODE_SHUNT_BUS_CONT']): |
|
| 179 | + config = 0 |
|
| 180 | + config |= (avg << 9 | busConvTime << 6 | shuntConvTime << 3 | mode) |
|
| 181 | + self.writeRegister16(INA226_REG_CONFIG, config) |
|
| 182 | + return True |
|
| 183 | + |
|
| 184 | + def calibrate(self,rShuntValue = 0.1, iMaxExcepted = 2): |
|
| 185 | + self.rShunt = rShuntValue |
|
| 186 | + |
|
| 187 | + self.iMaxPossible = self.vShuntMax / self.rShunt |
|
| 188 | + |
|
| 189 | + minimumLSB = float(iMaxExcepted) / 32767 |
|
| 190 | + |
|
| 191 | + #print "minimumLSB:"+str(minimumLSB) |
|
| 192 | + |
|
| 193 | + self.currentLSB = int((minimumLSB * 100000000)) |
|
| 194 | + #print "currentLSB:"+str(self.currentLSB) |
|
| 195 | + self.currentLSB /= 100000000.0 |
|
| 196 | + self.currentLSB /= 0.0001 |
|
| 197 | + self.currentLSB = math.ceil(self.currentLSB) |
|
| 198 | + self.currentLSB *= 0.0001 |
|
| 199 | + |
|
| 200 | + self.powerLSB = self.currentLSB * 25; |
|
| 201 | + |
|
| 202 | + |
|
| 203 | + #print "powerLSB:"+str(self.powerLSB) |
|
| 204 | + #print "rshunt:"+str(self.rShunt) |
|
| 205 | + |
|
| 206 | + calibrationValue = int(((0.00512) / (self.currentLSB * self.rShunt))) #if we get error need to convert this to unsigned int 16 bit instead |
|
| 207 | + |
|
| 208 | + self.writeRegister16(INA226_REG_CALIBRATION, calibrationValue) |
|
| 209 | + |
|
| 210 | + return True |
|
| 211 | + |
|
| 212 | + def getAverages(self): |
|
| 213 | + value = self.readRegister16(INA226_REG_CONFIG) |
|
| 214 | + value &= 0b0000111000000000 |
|
| 215 | + value >>= 9 |
|
| 216 | + return value |
|
| 217 | + |
|
| 218 | + def getMaxPossibleCurrent(self): |
|
| 219 | + return (self.vShuntMax / self.rShunt) |
|
| 220 | + |
|
| 221 | + def getMaxCurrent(self): |
|
| 222 | + maxCurrent = (self.currentLSB * 32767) |
|
| 223 | + maxPossible = self.getMaxPossibleCurrent() |
|
| 224 | + |
|
| 225 | + if maxCurrent > maxPossible: |
|
| 226 | + return maxPossible |
|
| 227 | + else: |
|
| 228 | + return maxCurrent |
|
| 229 | + |
|
| 230 | + def getMaxShuntVoltage(self): |
|
| 231 | + maxVoltage = self.getMaxCurrent() * self.rShunt |
|
| 232 | + if maxVoltage >= self.vShuntMax: |
|
| 233 | + return self.vShuntMax |
|
| 234 | + else: |
|
| 235 | + return maxVoltage |
|
| 236 | + |
|
| 237 | + def getMaxPower(self): |
|
| 238 | + return (self.getMaxCurrent() * self.vBusMax) |
|
| 239 | + |
|
| 240 | + def readBusPower(self): |
|
| 241 | + return (self.readRegister16(INA226_REG_POWER) * self.powerLSB) |
|
| 242 | + |
|
| 243 | + def readShuntCurrent(self): |
|
| 244 | + return (self.readRegister16(INA226_REG_CURRENT) * self.currentLSB) |
|
| 245 | + |
|
| 246 | + def readShuntVoltage(self): |
|
| 247 | + voltage = self.readRegister16(INA226_REG_SHUNTVOLTAGE) |
|
| 248 | + return (voltage * 0.0000025) |
|
| 249 | + |
|
| 250 | + def readBusVoltage(self): |
|
| 251 | + voltage = self.readRegister16(INA226_REG_BUSVOLTAGE) |
|
| 252 | + return (voltage * 0.00125) |
|
| 253 | + |
|
| 254 | + def getBusConversionTime(self): |
|
| 255 | + value = self.readRegister16(INA226_REG_CONFIG) |
|
| 256 | + value &= 0b0000000111000000 |
|
| 257 | + value >>= 6 |
|
| 258 | + return value |
|
| 259 | + |
|
| 260 | + def getShuntConversionTime(self): |
|
| 261 | + value = self.readRegister16(INA226_REG_CONFIG) |
|
| 262 | + value &= 0b0000000000111000 |
|
| 263 | + value >>= 3 |
|
| 264 | + return value |
|
| 265 | + |
|
| 266 | + def getMode(self): |
|
| 267 | + value = self.readRegister16(INA226_REG_CONFIG) |
|
| 268 | + value &= 0b0000000000000111 |
|
| 269 | + return value |
|
| 270 | + |
|
| 271 | + def setMaskEnable(self, mask): |
|
| 272 | + self.writeRegister16(INA226_REG_MASKENABLE, mask) |
|
| 273 | + |
|
| 274 | + def getMaskEnable(self): |
|
| 275 | + return self.readRegister16(INA226_REG_MASKENABLE) |
|
| 276 | + |
|
| 277 | + def enableShuntOverLimitAlert(self): |
|
| 278 | + self.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_SOL) |
|
| 279 | + |
|
| 280 | + def enableBusOverLimitAlert(self): |
|
| 281 | + self.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_BOL) |
|
| 282 | + |
|
| 283 | + def enableBusUnderLimitAlert(self): |
|
| 284 | + self.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_BUL) |
|
| 285 | + |
|
| 286 | + def enableOverPowerLimitAlert(self): |
|
| 287 | + self.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_POL) |
|
| 288 | + |
|
| 289 | + def enableConversionReadyAlert(self): |
|
| 290 | + self.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_CNVR) |
|
| 291 | + |
|
| 292 | + def setBusVoltageLimit(self, voltage): |
|
| 293 | + value = voltage / 0.00125 |
|
| 294 | + self.writeRegister16(INA226_REG_ALERTLIMIT, value) |
|
| 295 | + |
|
| 296 | + def setShuntVoltageLimit(self, voltage): |
|
| 297 | + value = voltage * 25000 |
|
| 298 | + self.writeRegister16(INA226_REG_ALERTLIMIT, value) |
|
| 299 | + |
|
| 300 | + def setPowerLimit(self, watts): |
|
| 301 | + value = watts / self.powerLSB |
|
| 302 | + self.writeRegister16(INA226_REG_ALERTLIMIT, value) |
|
| 303 | + |
|
| 304 | + def setAlertInvertedPolarity(self, inverted): |
|
| 305 | + temp = self.getMaskEnable() |
|
| 306 | + |
|
| 307 | + if (inverted): |
|
| 308 | + temp |= INA226_BIT_APOL; |
|
| 309 | + else: |
|
| 310 | + temp &= ~INA226_BIT_APOL; |
|
| 311 | + self.setMaskEnable(temp) |
|
| 312 | + |
|
| 313 | + def setAlertLatch(self, latch): |
|
| 314 | + temp = self.getMaskEnable() |
|
| 315 | + if (latch): |
|
| 316 | + temp |= INA226_BIT_LEN |
|
| 317 | + else: |
|
| 318 | + temp &= ~INA226_BIT_LEN |
|
| 319 | + self.setMaskEnable(temp) |
|
| 320 | + |
|
| 321 | + def isMathOverflow(self): |
|
| 322 | + return ((self.getMaskEnable() & INA226_BIT_OVF) == INA226_BIT_OVF) |
|
| 323 | + |
|
| 324 | + def isAlert(self): |
|
| 325 | + return ((self.getMaskEnable() & INA226_BIT_AFF) == INA226_BIT_AFF) |
|
| 326 | + |
|
| 327 | +import sys |
|
| 328 | +iSensor = ina226(INA226_ADDRESS,1) |
|
| 329 | +iSensor.configure(avg = ina226_averages_t['INA226_AVERAGES_4'],) |
|
| 330 | +iSensor.calibrate(rShuntValue = 0.02, iMaxExcepted = 2) |
|
| 331 | +time.sleep(1) |
|
| 332 | +batteryPercentage = ((round(iSensor.readBusVoltage(),3)-10.5) / (14.3-10.5)) * 100.0 |
|
| 333 | +sys.stdout.write(str(round(iSensor.readShuntCurrent(),3)) +' '+ str(round(iSensor.readBusVoltage(),1)) +' '+ str(round(iSensor.readBusPower(),1))+' ' + str(round(batteryPercentage,1)) +'\n') |
|
| 334 | +``` |
|
| ... | ... | \ No newline at end of file |