simple_son
Posts: 1
Joined: Tue Jul 20, 2021 10:12 pm

Script Stopping When Display Turns Off

Tue Jul 20, 2021 10:28 pm

RPi 4B 8gb on Raspbian
Noob

I am working on a small project to add an OLED display to my Pi's case to display system information and UPS battery info from the following guide: https://www.the-diy-life.com/mini-raspb ... ment-26030

I have the display working using the provided script from the tutorial without any edits. However, I noticed that the display will freeze after a while and stop updating any of the stats. I suspect this is happening when the Pi puts the display to sleep, but not 100% sure. I ran the stats.py script in Thonny and got the following output once it stopped.

Python 3.7.3 (/usr/bin/python3)
>>> %Run stats.py
Traceback (most recent call last):
File "/home/pi/stats.py", line 90, in <module>
IP = subprocess.check_output(cmd, shell = True )
File "/usr/lib/python3.7/subprocess.py", line 395, in check_output
**kwargs).stdout
File "/usr/lib/python3.7/subprocess.py", line 472, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/lib/python3.7/subprocess.py", line 775, in __init__
restore_signals, start_new_session)
File "/usr/lib/python3.7/subprocess.py", line 1412, in _execute_child
errpipe_read, errpipe_write = os.pipe()
OSError: [Errno 24] Too many open files
>>>

Stats.py script below:

Code: Select all

import time
import smbus2
import logging
from ina219 import INA219,DeviceRangeError

import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

import subprocess

DEVICE_BUS = 1
DEVICE_ADDR = 0x17
PROTECT_VOLT = 3700
SAMPLE_TIME = 2

# Raspberry Pi pin configuration:
RST = None     # on the PiOLED this pin isnt used

# 128x64 display with hardware I2C:
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)

# Initialize library.
disp.begin()

# Clear display.
disp.clear()
disp.display()

# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))

# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)

# Draw a black filled box to clear the image.
draw.rectangle((0,0,width,height), outline=0, fill=0)

# Draw some shapes.
# First define some constants to allow easy resizing of shapes.
padding = -2
top = padding
bottom = height-padding
# Move left to right keeping track of the current x position for drawing shapes.
x = 0

# Display counter
dispC = 0

# Load default font.
# font = ImageFont.load_default()

# Alternatively load a TTF font.  Make sure the .ttf font file is in the same directory as the python script!
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
font = ImageFont.truetype('PixelOperator.ttf', 16)

while True:

    # 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 = "hostname -I | cut -d\' \' -f1"
    IP = subprocess.check_output(cmd, shell = True )
    cmd = "top -bn1 | grep load | awk '{printf \"CPU: %.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 }'"
    MemUsage = subprocess.check_output(cmd, shell = True )
    cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%dGB %s\", $3,$2,$5}'"
    Disk = subprocess.check_output(cmd, shell = True )
    cmd = "vcgencmd measure_temp |cut -f 2 -d '='"
    temp = subprocess.check_output(cmd, shell = True )
    
    # Scripts for UPS monitoring
    ina = INA219(0.00725,address=0x40)
    ina.configure()
    piVolts = round(ina.voltage(),2)
    piCurrent = round (ina.current())
    
    ina = INA219(0.005,address=0x45) 
    ina.configure()
    battVolts = round(ina.voltage(),2)
    
    try:
        battCur = round(ina.current())
        battPow = round(ina.power()/1000,1)
    except DeviceRangeError:
        battCur = 0
        battPow = 0
    
    bus = smbus2.SMBus(DEVICE_BUS)

    aReceiveBuf = []
    aReceiveBuf.append(0x00)   # Placeholder

    for i in range(1,255):
        aReceiveBuf.append(bus.read_byte_data(DEVICE_ADDR, i))
    
    if (aReceiveBuf[8] << 8 | aReceiveBuf[7]) > 4000:
        chargeStat = 'Charging USB C'
    elif (aReceiveBuf[10] << 8 | aReceiveBuf[9]) > 4000:
        chargeStat = 'Charging Micro USB.'
    else:
        chargeStat = 'Not Charging'
    
    battTemp = (aReceiveBuf[12] << 8 | aReceiveBuf[11])
    
    battCap = (aReceiveBuf[20] << 8 | aReceiveBuf[19])


    if (dispC <= 15):
        # Pi Stats Display
        draw.text((x, top+2), "IP: " + str(IP,'utf-8'), font=font, fill=255)
        draw.text((x, top+18), str(CPU,'utf-8') + "%", font=font, fill=255)
        draw.text((x+80, top+18), str(temp,'utf-8') , font=font, fill=255)
        draw.text((x, top+34), str(MemUsage,'utf-8'), font=font, fill=255)
        draw.text((x, top+50), str(Disk,'utf-8'), font=font, fill=255)
        dispC+=1
        
    else:
    
        # UPS Stats Display
        draw.text((x, top+2), "Pi: " + str(piVolts) + "V  " + str(piCurrent) + "mA", font=font, fill=255)
        draw.text((x, top+18), "Batt: " + str(battVolts) + "V  " + str(battCap) + "%", font=font, fill=255)
        if (battCur > 0):
            draw.text((x, top+34), "Chrg: " + str(battCur) + "mA " + str(battPow) + "W", font=font, fill=255)
        else:
            draw.text((x, top+34), "Dchrg: " + str(0-battCur) + "mA " + str(battPow) + "W", font=font, fill=255)
        draw.text((x+15, top+50), chargeStat, font=font, fill=255)
        dispC+=1
        if (dispC == 30):
            dispC = 0

    # Display image.
    disp.image(image)
    disp.display()
    time.sleep(.1)

User avatar
B.Goode
Posts: 13954
Joined: Mon Sep 01, 2014 4:03 pm
Location: UK

Re: Script Stopping When Display Turns Off

Wed Jul 21, 2021 2:42 pm

simple_son wrote:
Tue Jul 20, 2021 10:28 pm
RPi 4B 8gb on Raspbian
Noob

I am working on a small project to add an OLED display to my Pi's case to display system information and UPS battery info from the following guide: https://www.the-diy-life.com/mini-raspb ... ment-26030

I have the display working using the provided script from the tutorial without any edits. However, I noticed that the display will freeze after a while and stop updating any of the stats. I suspect this is happening when the Pi puts the display to sleep, but not 100% sure. I ran the stats.py script in Thonny and got the following output once it stopped.

Python 3.7.3 (/usr/bin/python3)
>>> %Run stats.py
Traceback (most recent call last):
File "/home/pi/stats.py", line 90, in <module>
IP = subprocess.check_output(cmd, shell = True )
File "/usr/lib/python3.7/subprocess.py", line 395, in check_output
**kwargs).stdout
File "/usr/lib/python3.7/subprocess.py", line 472, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/lib/python3.7/subprocess.py", line 775, in __init__
restore_signals, start_new_session)
File "/usr/lib/python3.7/subprocess.py", line 1412, in _execute_child
errpipe_read, errpipe_write = os.pipe()
OSError: [Errno 24] Too many open files
>>>

Stats.py script below:

Code: Select all

import time
import smbus2
import logging
from ina219 import INA219,DeviceRangeError

import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

import subprocess

DEVICE_BUS = 1
DEVICE_ADDR = 0x17
PROTECT_VOLT = 3700
SAMPLE_TIME = 2

# Raspberry Pi pin configuration:
RST = None     # on the PiOLED this pin isnt used

# 128x64 display with hardware I2C:
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)

# Initialize library.
disp.begin()

# Clear display.
disp.clear()
disp.display()

# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))

# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)

# Draw a black filled box to clear the image.
draw.rectangle((0,0,width,height), outline=0, fill=0)

# Draw some shapes.
# First define some constants to allow easy resizing of shapes.
padding = -2
top = padding
bottom = height-padding
# Move left to right keeping track of the current x position for drawing shapes.
x = 0

# Display counter
dispC = 0

# Load default font.
# font = ImageFont.load_default()

# Alternatively load a TTF font.  Make sure the .ttf font file is in the same directory as the python script!
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
font = ImageFont.truetype('PixelOperator.ttf', 16)

while True:

    # 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 = "hostname -I | cut -d\' \' -f1"
    IP = subprocess.check_output(cmd, shell = True )
    cmd = "top -bn1 | grep load | awk '{printf \"CPU: %.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 }'"
    MemUsage = subprocess.check_output(cmd, shell = True )
    cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%dGB %s\", $3,$2,$5}'"
    Disk = subprocess.check_output(cmd, shell = True )
    cmd = "vcgencmd measure_temp |cut -f 2 -d '='"
    temp = subprocess.check_output(cmd, shell = True )
    
    # Scripts for UPS monitoring
    ina = INA219(0.00725,address=0x40)
    ina.configure()
    piVolts = round(ina.voltage(),2)
    piCurrent = round (ina.current())
    
    ina = INA219(0.005,address=0x45) 
    ina.configure()
    battVolts = round(ina.voltage(),2)
    
    try:
        battCur = round(ina.current())
        battPow = round(ina.power()/1000,1)
    except DeviceRangeError:
        battCur = 0
        battPow = 0
    
    bus = smbus2.SMBus(DEVICE_BUS)

    aReceiveBuf = []
    aReceiveBuf.append(0x00)   # Placeholder

    for i in range(1,255):
        aReceiveBuf.append(bus.read_byte_data(DEVICE_ADDR, i))
    
    if (aReceiveBuf[8] << 8 | aReceiveBuf[7]) > 4000:
        chargeStat = 'Charging USB C'
    elif (aReceiveBuf[10] << 8 | aReceiveBuf[9]) > 4000:
        chargeStat = 'Charging Micro USB.'
    else:
        chargeStat = 'Not Charging'
    
    battTemp = (aReceiveBuf[12] << 8 | aReceiveBuf[11])
    
    battCap = (aReceiveBuf[20] << 8 | aReceiveBuf[19])


    if (dispC <= 15):
        # Pi Stats Display
        draw.text((x, top+2), "IP: " + str(IP,'utf-8'), font=font, fill=255)
        draw.text((x, top+18), str(CPU,'utf-8') + "%", font=font, fill=255)
        draw.text((x+80, top+18), str(temp,'utf-8') , font=font, fill=255)
        draw.text((x, top+34), str(MemUsage,'utf-8'), font=font, fill=255)
        draw.text((x, top+50), str(Disk,'utf-8'), font=font, fill=255)
        dispC+=1
        
    else:
    
        # UPS Stats Display
        draw.text((x, top+2), "Pi: " + str(piVolts) + "V  " + str(piCurrent) + "mA", font=font, fill=255)
        draw.text((x, top+18), "Batt: " + str(battVolts) + "V  " + str(battCap) + "%", font=font, fill=255)
        if (battCur > 0):
            draw.text((x, top+34), "Chrg: " + str(battCur) + "mA " + str(battPow) + "W", font=font, fill=255)
        else:
            draw.text((x, top+34), "Dchrg: " + str(0-battCur) + "mA " + str(battPow) + "W", font=font, fill=255)
        draw.text((x+15, top+50), chargeStat, font=font, fill=255)
        dispC+=1
        if (dispC == 30):
            dispC = 0

    # Display image.
    disp.image(image)
    disp.display()
    time.sleep(.1)


" However, I noticed that the display will freeze after a while and stop updating any of the stats. I suspect this is happening when the Pi puts the display to sleep, but not 100% sure. "

That might be to confuse cause and effect. I think it more likely that the problem is the one the Python Interpreter reports - "OSError: [Errno 24] Too many open files" Once the script has failed the screen display will not be maintained.


There seems to be a lot going on 10 times a second in your main loop that is redundant? How likely is it that the IP address will change that frequently? Why do you need a new SMBUS object at every pas through the loop?

cleverca22
Posts: 6042
Joined: Sat Aug 18, 2012 2:33 pm

Re: Script Stopping When Display Turns Off

Wed Jul 21, 2021 2:51 pm

Code: Select all

    ina = INA219(0.005,address=0x45) 
    bus = smbus2.SMBus(DEVICE_BUS)
i also notice your initializing 2 different things in the for loop
that would likely be opening 2 file handles on every look, until it runs out of available handles!

Return to “Troubleshooting”