From 64b57dfcde08138abd1fa9c835d7abb00cbc9760 Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Wed, 28 Aug 2019 11:11:45 +0800 Subject: [PATCH 01/12] Create ads1115 --- jetcard/ads1115 | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 jetcard/ads1115 diff --git a/jetcard/ads1115 b/jetcard/ads1115 new file mode 100644 index 0000000..b4a8434 --- /dev/null +++ b/jetcard/ads1115 @@ -0,0 +1,131 @@ +import smbus +import time + +# Get I2C bus +bus = smbus.SMBus(1) + +# I2C address of the device +ADS1115_IIC_ADDRESS = 0x48 + +# ADS1115 Register Map +ADS1115_REG_CONVERT = 0x00 # Conversion register +ADS1115_REG_CONFIG = 0x01 # Configuration register +ADS1115_REG_LOWTHRESH = 0x02 # Lo_thresh register +ADS1115_REG_HITHRESH = 0x03 # Hi_thresh register + +# ADS1115 Configuration Register +ADS1115_CONFIG_OS_NOEFFECT = 0x00 # No effect +ADS1115_CONFIG_OS_SINGLE = 0x80 # Begin a single conversion +ADS1115_CONFIG_MUX_DIFF_0_1 = 0x00 # Differential P = AIN0, N = AIN1 (default) +ADS1115_CONFIG_MUX_DIFF_0_3 = 0x10 # Differential P = AIN0, N = AIN3 +ADS1115_CONFIG_MUX_DIFF_1_3 = 0x20 # Differential P = AIN1, N = AIN3 +ADS1115_CONFIG_MUX_DIFF_2_3 = 0x30 # Differential P = AIN2, N = AIN3 +ADS1115_CONFIG_MUX_SINGLE_0 = 0x40 # Single-ended P = AIN0, N = GND +ADS1115_CONFIG_MUX_SINGLE_1 = 0x50 # Single-ended P = AIN1, N = GND +ADS1115_CONFIG_MUX_SINGLE_2 = 0x60 # Single-ended P = AIN2, N = GND +ADS1115_CONFIG_MUX_SINGLE_3 = 0x70 # Single-ended P = AIN3, N = GND +ADS1115_CONFIG_PGA_6_144V = 0x00 # +/-6.144V range = Gain 2/3 +ADS1115_CONFIG_PGA_4_096V = 0x02 # +/-4.096V range = Gain 1 +ADS1115_CONFIG_PGA_2_048V = 0x04 # +/-2.048V range = Gain 2 (default) +ADS1115_CONFIG_PGA_1_024V = 0x06 # +/-1.024V range = Gain 4 +ADS1115_CONFIG_PGA_0_512V = 0x08 # +/-0.512V range = Gain 8 +ADS1115_CONFIG_PGA_0_256V = 0x0A # +/-0.256V range = Gain 16 +ADS1115_CONFIG_MODE_CONTIN = 0x00 # Continuous conversion mode +ADS1115_CONFIG_MODE_SINGLE = 0x01 # Power-down single-shot mode (default) +ADS1115_CONFIG_DR_8SPS = 0x00 # 8 samples per second +ADS1115_CONFIG_DR_16SPS = 0x20 # 16 samples per second +ADS1115_CONFIG_DR_32SPS = 0x40 # 32 samples per second +ADS1115_CONFIG_DR_64SPS = 0x60 # 64 samples per second +ADS1115_CONFIG_DR_128SPS = 0x80 # 128 samples per second (default) +ADS1115_CONFIG_DR_250SPS = 0xA0 # 250 samples per second +ADS1115_CONFIG_DR_475SPS = 0xC0 # 475 samples per second +ADS1115_CONFIG_DR_860SPS = 0xE0 # 860 samples per second +ADS1115_CONFIG_CMODE_TRAD = 0x00 # Traditional comparator with hysteresis (default) +ADS1115_CONFIG_CMODE_WINDOW = 0x10 # Window comparator +ADS1115_CONFIG_CPOL_ACTVLOW = 0x00 # ALERT/RDY pin is low when active (default) +ADS1115_CONFIG_CPOL_ACTVHI = 0x08 # ALERT/RDY pin is high when active +ADS1115_CONFIG_CLAT_NONLAT = 0x00 # Non-latching comparator (default) +ADS1115_CONFIG_CLAT_LATCH = 0x04 # Latching comparator +ADS1115_CONFIG_CQUE_1CONV = 0x00 # Assert ALERT/RDY after one conversions +ADS1115_CONFIG_CQUE_2CONV = 0x01 # Assert ALERT/RDY after two conversions +ADS1115_CONFIG_CQUE_4CONV = 0x02 # Assert ALERT/RDY after four conversions +ADS1115_CONFIG_CQUE_NONE = 0x03 # Disable the comparator and put ALERT/RDY in high state (default) + +class ADS1115(object): + + def __init__(self, address=0x48): + self.bus = smbus.SMBus(1); + self.addr = address; + self.channel = 0; + self.gain = ADS1115_CONFIG_PGA_4_096V; + self.coefficient = 0.125; + + def setGain(self,gain): + self.gain = gain; + if gain == ADS1115_CONFIG_PGA_6_144V: + self.coefficient = 0.1875 + elif mygain == ADS1115_CONFIG_PGA_4_096V: + self.coefficient = 0.125 + elif mygain == ADS1115_CONFIG_PGA_2_048V: + self.coefficient = 0.0625 + elif mygain == ADS1115_CONFIG_PGA_1_024V: + self.coefficient = 0.03125 + elif mygain == ADS1115_CONFIG_PGA_0_512V: + self.coefficient = 0.015625 + elif mygain == ADS1115_CONFIG_PGA_0_256V: + self.coefficient = 0.0078125 + else: + self.gain=ADS1115_CONFIG_PGA_4_096V; + self.coefficient = 0.125 + + def setChannel(self,channel): + """Select the Channel user want to use from 0-7 + For Single-ended Output + + 0 : AINP = AIN0 and AINN = AIN1 + 1 : AINP = AIN0 and AINN = AIN3 + 2 : AINP = AIN1 and AINN = AIN3 + 3 : AINP = AIN2 and AINN = AIN3 + 4 : AINP = AIN0 and AINN = GND + 5 : AINP = AIN1 and AINN = GND + 6 : AINP = AIN2 and AINN = GND + 7 : AINP = AIN3 and AINN = GND + """ + self.channel = channel + while self.channel > 7 : + self.channel = 0 + + return self.channel + + def readValue(self): + """Read data back from ADS1115_REG_CONVERT(0x00), 2 bytes + raw_adc MSB, raw_adc LSB""" + + data = bus.read_i2c_block_data(self.addr, ADS1115_REG_CONVERT, 2) + + # Convert the data + raw_adc = data[0] * 256 + data[1] + + if raw_adc > 32767: + raw_adc -= 65535 + raw_adc = int(float(raw_adc)*self.coefficient)*4 + return raw_adc + + def readVoltage(self,channel): + + self.setChannel(channel) + CONFIG_REG = [ADS1115_CONFIG_OS_SINGLE | (self.channel << 4) | ADS1115_CONFIG_PGA_4_096V | ADS1115_CONFIG_MODE_CONTIN, ADS1115_CONFIG_DR_128SPS | ADS1115_CONFIG_CQUE_NONE] + bus.write_i2c_block_data(self.addr, ADS1115_REG_CONFIG, CONFIG_REG) + time.sleep(0.1) + return self.readValue() + + + +if __name__=='__main__': + + # Create an ADS1115 ADC (16-bit) instance. + ads = ADS1115() + while True: + value=ads.readVoltage(4) + print(value) + time.sleep(1) From dc4d859caf1cc55683bb71e28d24137074a98e10 Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Wed, 28 Aug 2019 11:13:30 +0800 Subject: [PATCH 02/12] Update display_server.py --- jetcard/display_server.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jetcard/display_server.py b/jetcard/display_server.py index dd8be72..083a2e5 100644 --- a/jetcard/display_server.py +++ b/jetcard/display_server.py @@ -6,11 +6,12 @@ import PIL.ImageDraw from flask import Flask from .utils import ip_address, power_mode, power_usage, cpu_usage, gpu_usage, memory_usage, disk_usage - +from jetcard import ads1115 class DisplayServer(object): def __init__(self, *args, **kwargs): + self.ads = ads1115.ADS1115() self.display = Adafruit_SSD1306.SSD1306_128_32(rst=None, i2c_bus=1, gpio=1) self.display.begin() self.display.clear() @@ -26,6 +27,8 @@ def __init__(self, *args, **kwargs): def _run_display_stats(self): while self.stats_enabled: + value=self.ads.readVoltage(4)/1000.0 + self.draw.rectangle((0, 0, self.image.width, self.image.height), outline=0, fill=0) # set IP address @@ -39,7 +42,7 @@ def _run_display_stats(self): top = 6 power_mode_str = power_mode() - self.draw.text((4, top), 'MODE: ' + power_mode_str, font=self.font, fill=255) + self.draw.text((4, top), 'MODE: ' + power_mode_str + (" %.1fV")%value, font=self.font, fill=255) # set stats headers top = 14 From 59a4859d839cc4630259644b411b2d9b391ce008 Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Wed, 28 Aug 2019 11:15:28 +0800 Subject: [PATCH 03/12] Update stats.py --- jetcard/stats.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/jetcard/stats.py b/jetcard/stats.py index ff03ac8..4f9dfaa 100644 --- a/jetcard/stats.py +++ b/jetcard/stats.py @@ -26,9 +26,12 @@ from PIL import ImageDraw from PIL import ImageFont from .utils import get_ip_address +from jetcard import ads1115 import subprocess +ads = ads1115.ADS1115() + # 128x32 display with hardware I2C: disp = Adafruit_SSD1306.SSD1306_128_32(rst=None, i2c_bus=1, gpio=1) # setting gpio to 1 is hack to avoid platform detection @@ -64,24 +67,25 @@ while True: - + value=ads.readVoltage(4)/1000.0 + # Draw a black filled box to clear the image. draw.rectangle((0,0,width,height), outline=0, fill=0) # Shell scripts for system monitoring from here : https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load - cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'" + cmd = "top -bn1 | grep load | awk '{printf \"CPU Load:%.2f\", $(NF-2)}'" CPU = subprocess.check_output(cmd, shell = True ) - cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%sMB %.2f%%\", $3,$2,$3*100/$2 }'" + cmd = "free -m | awk 'NR==2{printf \"Mem:%s/%sMB %.1f%%\", $3,$2,$3*100/$2 }'" MemUsage = subprocess.check_output(cmd, shell = True ) - cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%dGB %s\", $3,$2,$5}'" + cmd = "df -h | awk '$NF==\"/\"{printf \"Disk:%d/%dGB %s\", $3,$2,$5}'" Disk = subprocess.check_output(cmd, shell = True ) # Write two lines of text. - draw.text((x, top), "eth0: " + str(get_ip_address('eth0')), font=font, fill=255) - draw.text((x, top+8), "wlan0: " + str(get_ip_address('wlan0')), font=font, fill=255) + draw.text((x, top), "eth0:" + str(get_ip_address('eth0')), font=font, fill=255) + draw.text((x, top+8), "wlan0:" + str(get_ip_address('wlan0')), font=font, fill=255) draw.text((x, top+16), str(MemUsage.decode('utf-8')), font=font, fill=255) - draw.text((x, top+25), str(Disk.decode('utf-8')), font=font, fill=255) + draw.text((x, top+25), str(Disk.decode('utf-8')) + (" %.1f")%value, font=font, fill=255) # Display image. disp.image(image) From 74a013b3c5bac3a5ab14d309f29ffb6c8db3bcc6 Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Wed, 28 Aug 2019 11:36:57 +0800 Subject: [PATCH 04/12] Rename ads1115 to ads1115.py --- jetcard/{ads1115 => ads1115.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename jetcard/{ads1115 => ads1115.py} (100%) diff --git a/jetcard/ads1115 b/jetcard/ads1115.py similarity index 100% rename from jetcard/ads1115 rename to jetcard/ads1115.py From 077e3d2f838b3c873e238d5cf0e1118de9cfa626 Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Wed, 28 Aug 2019 11:37:23 +0800 Subject: [PATCH 05/12] Update stats.py --- jetcard/stats.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jetcard/stats.py b/jetcard/stats.py index 4f9dfaa..6eb444c 100644 --- a/jetcard/stats.py +++ b/jetcard/stats.py @@ -82,8 +82,8 @@ # Write two lines of text. - draw.text((x, top), "eth0:" + str(get_ip_address('eth0')), font=font, fill=255) - draw.text((x, top+8), "wlan0:" + str(get_ip_address('wlan0')), font=font, fill=255) + draw.text((x, top), "eth0:" + str(ip_address('eth0')), font=font, fill=255) + draw.text((x, top+8), "wlan0:" + str(ip_address('wlan0')), font=font, fill=255) draw.text((x, top+16), str(MemUsage.decode('utf-8')), font=font, fill=255) draw.text((x, top+25), str(Disk.decode('utf-8')) + (" %.1f")%value, font=font, fill=255) From 1f08f86d4c6cb472772a0e02bed1305645a7cf11 Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Wed, 28 Aug 2019 11:44:03 +0800 Subject: [PATCH 06/12] Update stats.py --- jetcard/stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetcard/stats.py b/jetcard/stats.py index 6eb444c..5f97988 100644 --- a/jetcard/stats.py +++ b/jetcard/stats.py @@ -25,7 +25,7 @@ from PIL import Image from PIL import ImageDraw from PIL import ImageFont -from .utils import get_ip_address +from .utils import ip_address from jetcard import ads1115 import subprocess From 325f6835ee472bc58359f7d102a9c8246cb681aa Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Sun, 8 Dec 2019 00:48:40 +0800 Subject: [PATCH 07/12] Update display_server.py --- jetcard/display_server.py | 46 ++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/jetcard/display_server.py b/jetcard/display_server.py index 083a2e5..f14880c 100644 --- a/jetcard/display_server.py +++ b/jetcard/display_server.py @@ -7,11 +7,24 @@ from flask import Flask from .utils import ip_address, power_mode, power_usage, cpu_usage, gpu_usage, memory_usage, disk_usage from jetcard import ads1115 +from jetcard import ina219 +import os class DisplayServer(object): def __init__(self, *args, **kwargs): - self.ads = ads1115.ADS1115() + adress = os.popen("i2cdetect -y -r 1 0x48 0x48 | egrep '48' | awk '{print $2}'").read() + if(adress=='48\n'): + self.ads = ads1115.ADS1115() + else: + self.ads = None + + adress = os.popen("i2cdetect -y -r 1 0x41 0x41 | egrep '41' | awk '{print $2}'").read() + if(adress=='41\n'): + self.ina219 = ina219.INA219(addr=0x41) + else: + self.ina219 = None + self.display = Adafruit_SSD1306.SSD1306_128_32(rst=None, i2c_bus=1, gpio=1) self.display.begin() self.display.clear() @@ -26,9 +39,9 @@ def __init__(self, *args, **kwargs): self.enable_stats() def _run_display_stats(self): + Charge = False while self.stats_enabled: - value=self.ads.readVoltage(4)/1000.0 - + self.draw.rectangle((0, 0, self.image.width, self.image.height), outline=0, fill=0) # set IP address @@ -42,8 +55,29 @@ def _run_display_stats(self): top = 6 power_mode_str = power_mode() - self.draw.text((4, top), 'MODE: ' + power_mode_str + (" %.1fV")%value, font=self.font, fill=255) - + + if(self.ina219 != None): + bus_voltage = self.ina219.getBusVoltage_V() # voltage on V- (load side) + current = self.ina219.getCurrent_mA() # current in mA + p = bus_voltage/12.6*100 + if(p > 100):p = 100 + if(current > 30): + Charge = not Charge + else: + Charge = False + + if(Charge == False): + self.draw.text((600, -2), ' ', font=self.font, fill=255) + else: + self.draw.text((120, -2), '*', font=self.font, fill=255) + self.draw.text((4, top), power_mode_str + (" %.1fV")%bus_voltage + (" %.2fA")%(current/1000) + (" %2.0f%%")%p, font=self.font, fill=255) + elif(self.ads != None): + value=self.ads.readVoltage(4)/1000.0 + p = value/12.6*100 + if(p > 100):p = 100 + self.draw.text((4, top), 'MODE: ' + power_mode_str + (" %.1fV")%value + (" %2.0f%%")%p, font=self.font, fill=255) + else: + self.draw.text((4, top), 'MODE: ' + power_mode_str, font=self.font, fill=255) # set stats headers top = 14 offset = 3 * 8 @@ -65,7 +99,7 @@ def _run_display_stats(self): self.display.image(self.image) self.display.display() - + time.sleep(self.stats_interval) def enable_stats(self): From aafa9ec183d946729f7b8998d336b64807308671 Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Sun, 8 Dec 2019 00:50:08 +0800 Subject: [PATCH 08/12] Update stats.py --- jetcard/stats.py | 56 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/jetcard/stats.py b/jetcard/stats.py index 5f97988..387c1af 100644 --- a/jetcard/stats.py +++ b/jetcard/stats.py @@ -27,10 +27,21 @@ from PIL import ImageFont from .utils import ip_address from jetcard import ads1115 - +from jetcard import ina219 +import os import subprocess -ads = ads1115.ADS1115() +adress = os.popen("i2cdetect -y -r 1 0x48 0x48 | egrep '48' | awk '{print $2}'").read() +if(adress=='48\n'): + ads = ads1115.ADS1115() +else: + ads = None + +adress = os.popen("i2cdetect -y -r 1 0x41 0x41 | egrep '41' | awk '{print $2}'").read() +if(adress=='41\n'): + ina = ina219.INA219(addr=0x41) +else: + ina = None # 128x32 display with hardware I2C: disp = Adafruit_SSD1306.SSD1306_128_32(rst=None, i2c_bus=1, gpio=1) # setting gpio to 1 is hack to avoid platform detection @@ -67,7 +78,6 @@ while True: - value=ads.readVoltage(4)/1000.0 # Draw a black filled box to clear the image. draw.rectangle((0,0,width,height), outline=0, fill=0) @@ -81,11 +91,41 @@ Disk = subprocess.check_output(cmd, shell = True ) # Write two lines of text. - - draw.text((x, top), "eth0:" + str(ip_address('eth0')), font=font, fill=255) - draw.text((x, top+8), "wlan0:" + str(ip_address('wlan0')), font=font, fill=255) - draw.text((x, top+16), str(MemUsage.decode('utf-8')), font=font, fill=255) - draw.text((x, top+25), str(Disk.decode('utf-8')) + (" %.1f")%value, font=font, fill=255) + if(ina != None): + if ip_address('eth0') is not None: + draw.text((x, top), 'IP: ' + str(ip_address('eth0')), font=font, fill=255) + elif ip_address('wlan0') is not None: + draw.text((x, top), 'IP: ' + str(ip_address('wlan0')), font=font, fill=255) + else: + draw.text((x, top), 'IP: not available', font=font, fill=255) + + bus_voltage = ina.getBusVoltage_V() # voltage on V- (load side) + current = ina.getCurrent_mA() # current in mA + p = bus_voltage/12.6*100 + if(p > 100):p = 100 + if(current > 30): + Charge = not Charge + else: + Charge = False + + if(Charge == False): + draw.text((120, top), ' ', font=font, fill=255) + else: + draw.text((120, top), '*', font=font, fill=255) + draw.text((x, top+8), ("%.1fV")%bus_voltage + (" %.2fA")%(current/1000) + (" %2.0f%%")%p, font=font, fill=255) + draw.text((x, top+16), str(MemUsage.decode('utf-8')), font=font, fill=255) + draw.text((x, top+25), str(Disk.decode('utf-8')), font=font, fill=255) + elif(ads != None): + value=ads.readVoltage(4)/1000.0 + draw.text((x, top), "eth0: " + str(get_ip_address('eth0')), font=font, fill=255) + draw.text((x, top+8), "wlan0: " + str(get_ip_address('wlan0')), font=font, fill=255) + draw.text((x, top+16), str(MemUsage.decode('utf-8')), font=font, fill=255) + draw.text((x, top+25), str(Disk.decode('utf-8')) + (" %.1f")%value, font=font, fill=255) + else: + draw.text((x, top), "eth0: " + str(get_ip_address('eth0')), font=font, fill=255) + draw.text((x, top+8), "wlan0: " + str(get_ip_address('wlan0')), font=font, fill=255) + draw.text((x, top+16), str(MemUsage.decode('utf-8')), font=font, fill=255) + draw.text((x, top+25), str(Disk.decode('utf-8')), font=font, fill=255) # Display image. disp.image(image) From e49fc130c46a19cda0c6ed83e10c1a7f225a0da8 Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Sat, 7 Dec 2019 08:58:04 -0800 Subject: [PATCH 09/12] update --- jetcard/ina219.py | 200 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 jetcard/ina219.py diff --git a/jetcard/ina219.py b/jetcard/ina219.py new file mode 100644 index 0000000..4594064 --- /dev/null +++ b/jetcard/ina219.py @@ -0,0 +1,200 @@ +import smbus +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=1, addr=0x40): + self.bus = smbus.SMBus(i2c_bus); + 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.bus.read_i2c_block_data(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 + self.bus.write_i2c_block_data(self.addr,address,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): + self.write(_REG_CALIBRATION,self._cal_value) + value = self.read(_REG_SHUNTVOLTAGE) + if value > 32767: + value -= 65535 + return value * 0.01 + + def getBusVoltage_V(self): + self.write(_REG_CALIBRATION,self._cal_value) + 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=0x41) + while True: + bus_voltage = ina219.getBusVoltage_V() # voltage on V- (load side) + shunt_voltage = ina219.getShuntVoltage_mV() / 1000 # voltage between V+ and V- across the shunt + current = ina219.getCurrent_mA() # current in mA + + # INA219 measure bus voltage on the load side. So PSU voltage = bus_voltage + shunt_voltage + print("PSU Voltage: {:6.3f} V".format(bus_voltage + shunt_voltage)) + print("Shunt Voltage: {:9.6f} V".format(shunt_voltage)) + print("Load Voltage: {:6.3f} V".format(bus_voltage)) + print("Current: {:9.6f} A".format(current/1000)) + print("") + + time.sleep(2) \ No newline at end of file From 439054f6944b458b81c3b698df04c5ce323e0625 Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Sun, 8 Dec 2019 19:48:15 +0800 Subject: [PATCH 10/12] Update ads1115.py --- jetcard/ads1115.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/jetcard/ads1115.py b/jetcard/ads1115.py index b4a8434..1e4688a 100644 --- a/jetcard/ads1115.py +++ b/jetcard/ads1115.py @@ -1,9 +1,6 @@ import smbus import time -# Get I2C bus -bus = smbus.SMBus(1) - # I2C address of the device ADS1115_IIC_ADDRESS = 0x48 @@ -101,7 +98,7 @@ def readValue(self): """Read data back from ADS1115_REG_CONVERT(0x00), 2 bytes raw_adc MSB, raw_adc LSB""" - data = bus.read_i2c_block_data(self.addr, ADS1115_REG_CONVERT, 2) + data = self.bus.read_i2c_block_data(self.addr, ADS1115_REG_CONVERT, 2) # Convert the data raw_adc = data[0] * 256 + data[1] @@ -115,7 +112,7 @@ def readVoltage(self,channel): self.setChannel(channel) CONFIG_REG = [ADS1115_CONFIG_OS_SINGLE | (self.channel << 4) | ADS1115_CONFIG_PGA_4_096V | ADS1115_CONFIG_MODE_CONTIN, ADS1115_CONFIG_DR_128SPS | ADS1115_CONFIG_CQUE_NONE] - bus.write_i2c_block_data(self.addr, ADS1115_REG_CONFIG, CONFIG_REG) + self.bus.write_i2c_block_data(self.addr, ADS1115_REG_CONFIG, CONFIG_REG) time.sleep(0.1) return self.readValue() From 64a52e2758d75b21bc0816f14f4c19e7a27a1d5a Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Mon, 17 Feb 2020 16:01:02 +0800 Subject: [PATCH 11/12] Update display_server.py --- jetcard/display_server.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jetcard/display_server.py b/jetcard/display_server.py index f14880c..cf1b911 100644 --- a/jetcard/display_server.py +++ b/jetcard/display_server.py @@ -59,8 +59,10 @@ def _run_display_stats(self): if(self.ina219 != None): bus_voltage = self.ina219.getBusVoltage_V() # voltage on V- (load side) current = self.ina219.getCurrent_mA() # current in mA - p = bus_voltage/12.6*100 + p = (bus_voltage - 9)/3.6*100 if(p > 100):p = 100 + if(p < 0):p = 0 + if(current < 0):current = 0 if(current > 30): Charge = not Charge else: @@ -73,8 +75,9 @@ def _run_display_stats(self): self.draw.text((4, top), power_mode_str + (" %.1fV")%bus_voltage + (" %.2fA")%(current/1000) + (" %2.0f%%")%p, font=self.font, fill=255) elif(self.ads != None): value=self.ads.readVoltage(4)/1000.0 - p = value/12.6*100 + p = (value - 9)/3.6*100 if(p > 100):p = 100 + if(p < 0):p = 0 self.draw.text((4, top), 'MODE: ' + power_mode_str + (" %.1fV")%value + (" %2.0f%%")%p, font=self.font, fill=255) else: self.draw.text((4, top), 'MODE: ' + power_mode_str, font=self.font, fill=255) From 473075828a258e52a60fcb812780fa1e876f2c65 Mon Sep 17 00:00:00 2001 From: waveshare <36142105+waveshare@users.noreply.github.com> Date: Sat, 30 May 2020 16:44:17 +0800 Subject: [PATCH 12/12] Update display_server.py --- jetcard/display_server.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/jetcard/display_server.py b/jetcard/display_server.py index cf1b911..b824261 100644 --- a/jetcard/display_server.py +++ b/jetcard/display_server.py @@ -25,6 +25,12 @@ def __init__(self, *args, **kwargs): else: self.ina219 = None + adress = os.popen("i2cdetect -y -r 1 0x42 0x42 | egrep '42' | awk '{print $2}'").read() + if(adress=='42\n'): + self.ina = ina219.INA219(addr=0x42) + else: + self.ina = None + self.display = Adafruit_SSD1306.SSD1306_128_32(rst=None, i2c_bus=1, gpio=1) self.display.begin() self.display.clear() @@ -55,8 +61,24 @@ def _run_display_stats(self): top = 6 power_mode_str = power_mode() + if(self.ina != None): + bus_voltage = self.ina.getBusVoltage_V() # voltage on V- (load side) + current = self.ina.getCurrent_mA() # current in mA + p = (bus_voltage - 6)/2.4*100 + if(p > 100):p = 100 + if(p < 0):p = 0 + if(current < 0):current = 0 + if(current > 30): + Charge = not Charge + else: + Charge = False - if(self.ina219 != None): + if(Charge == False): + self.draw.text((600, -2), ' ', font=self.font, fill=255) + else: + self.draw.text((120, -2), '*', font=self.font, fill=255) + self.draw.text((4, top), power_mode_str + (" %.1fV")%bus_voltage + (" %.2fA")%(current/1000) + (" %2.0f%%")%p, font=self.font, fill=255) + elif(self.ina219 != None): bus_voltage = self.ina219.getBusVoltage_V() # voltage on V- (load side) current = self.ina219.getCurrent_mA() # current in mA p = (bus_voltage - 9)/3.6*100