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 |