Inky Frame not refreshing screen on battery power using inky_frame.sleep_for()

It would also help if you would print some debugging information to the sd-card or to uart. Instructions are in this thread. This will give an insight on where your programs are exactly hanging. Using print to console does not help, since you are not on USB.

Iā€™m revisiting this from the initial inky launch and using import inky_frame and
inky_frame.sleep_for(however many minutes) works :) I dont think this was available at first, really simple and nice to add. I did need to reset and then press the front to wakeā€¦

This runs the main.py script, so i can do something once, update, sleep and not put anything into loops or timersā€¦

Andy

1 Like

Iā€™ve done more tests, with hitting reset and also writing logs to UART0 to check if any exception is raised while graphics.update() is executed. There is no exception raised, it seems the update() call just hangs the pico before starting refreshing the screen (the previous image is still displayed).

The hangs happens after at least 1 successful update, sometime succeeding for 2 or 3 before to hang.

Is there any way to enable more logging in graphics.update()? Sounds like a race condition somewhereā€¦

I have the very same issues. Everything is running fine on USB, but when switching to battery powered, the update method hangs and will never return.
It seems to be very consistent for me, if i am lucky it updates the screen once after resetting 5-10 times.
Reconnecting to USB instantly fixes the issues (just tried a wall charger instead of computer, also works just fine).
I tried a lot of different things. Starting by checking and switching batteries, moving the code to the main.py, trying to catch Exceptions, logging to SD card, using the updated sleep method ā€œsleep_forā€ which is able to handle 24hours or more, but to no avail.
When the update method is called, it will not update the frame and it will not return and therefor not go to sleep, which of course drains the batteries quickly. There are no Exceptions, the update process just gets stuck.
This somehow destroys the main purpose of this nice toy, so i would be happy if we could get some more support here.
Any ideas are very welcome!

Here is the code i use as a reference. Its mainly the nasa example with some minor enhancements and debugging stuff with the leds. I set the network led to pulsating before calling update. And while update hangs forever, the LED still pulses. So it seems that not the device but the function is hangingā€¦

import gc
import time
import inky_helper as ih
from inky_frame import sleep_for
from picographics import PicoGraphics, DISPLAY_INKY_FRAME_7 as DISPLAY  # 7.3"
import jpegdec
from urllib import urequest
from ujson import load
import sdcard
import os
from machine import Pin, SPI, reset

# A short delay to give USB chance to initialise
time.sleep(0.5)

# Setup for the display.
graphics = PicoGraphics(DISPLAY)
WIDTH, HEIGHT = graphics.get_bounds()
graphics.set_font("bitmap8")

#setup sd card
sd_spi = SPI(0, sck=Pin(18, Pin.OUT), mosi=Pin(19, Pin.OUT), miso=Pin(16, Pin.OUT))
sd = sdcard.SDCard(sd_spi, Pin(22))
os.mount(sd, "/sd")
gc.collect()

# reset()

# Turn any LEDs off that may still be on from last run.
ih.clear_button_leds()
ih.led_warn.off()


try:
    from secrets import WIFI_SSID, WIFI_PASSWORD
    ih.network_connect(WIFI_SSID, WIFI_PASSWORD)
except ImportError:
    print("Create secrets.py with your WiFi credentials")

gc.collect()

FILENAME = "/sd/nasa-apod-daily"

# A Demo Key is used in this example and is IP rate limited. You can get your own API Key from https://api.nasa.gov/
API_URL = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"

# Length of time between updates in minutes.
# Frequent updates will reduce battery life!
UPDATE_INTERVAL = 1439
#UPDATE_INTERVAL = 2

# Variable for storing the NASA APOD Title
apod_title = None


def show_error(text):
    graphics.set_pen(4)
    graphics.rectangle(0, 10, WIDTH, 35)
    graphics.set_pen(1)
    graphics.text(text, 5, 16, 400, 2)


def update():
    global apod_title

    IMG_URL = "https://pimoroni.github.io/feed2image/nasa-apod-800x480-daily.jpg"
    
    now = time.localtime(time.time())
    failedTime = str(now[2]) + "." + str(now[1]) + "." + str(now[0]) + " " + str(now[3]) + ":" + str(now[4]) + ":" + str(now[5])
    apod_title = "Download failed on " + failedTime

    try:
        # Grab the data
        socket = urequest.urlopen(API_URL)
        gc.collect()
        j = load(socket)
        socket.close()
        apod_title = j['title']
        gc.collect()
    except OSError as e:
        print(e)

    try:
        # Grab the image
        socket = urequest.urlopen(IMG_URL)

        gc.collect()

        data = bytearray(1024)
        with open(FILENAME, "wb") as f:
            while True:
                if socket.readinto(data) == 0:
                    break
                f.write(data)
        socket.close()
        del data
        gc.collect()
    except OSError as e:
        print(e)
        show_error("Unable to download image")

def draw():
    ih.stop_network_led()
    ih.inky_frame.button_a.led_on()
    
    jpeg = jpegdec.JPEG(graphics)
    gc.collect()

    graphics.set_pen(1)
    graphics.clear()
    
    try:
        ih.clear_button_leds()
        ih.inky_frame.button_b.led_on()
        jpeg.open_file(FILENAME)
        jpeg.decode()
    except OSError:
        ih.clear_button_leds()
        ih.inky_frame.button_c.led_on()
        graphics.set_pen(4)
        graphics.rectangle(0, (HEIGHT // 2) - 20, WIDTH, 40)
        graphics.set_pen(1)
        graphics.text("Unable to display image!", 5, (HEIGHT // 2) - 15, WIDTH, 2)
        graphics.text("Check your network settings in secrets.py", 5, (HEIGHT // 2) + 2, WIDTH, 2)

    graphics.set_pen(0)
    graphics.rectangle(0, HEIGHT - 25, WIDTH, 25)
    graphics.set_pen(1)
    graphics.text(apod_title, 5, HEIGHT - 20, WIDTH, 2)

    ih.clear_button_leds()
    ih.inky_frame.button_d.led_on()
    gc.collect()
    time.sleep(1)
    try:
        ih.pulse_network_led()
        graphics.update()
    except Exception as e:
        ih.stop_network_led()
        print("Exception occured!", e)
        ih.inky_frame.button_d.led_off()
        time.sleep(0.2)
        ih.inky_frame.button_d.led_on()
        time.sleep(0.2)
        ih.inky_frame.button_d.led_off()
        time.sleep(0.2)
        ih.inky_frame.button_d.led_on()
        time.sleep(0.2)
        ih.inky_frame.button_d.led_off()
        time.sleep(0.2)
        ih.inky_frame.button_d.led_on()
        time.sleep(0.2)
        with open("/sd/exception.txt", "w") as file:
            file.write("Exception occured:\r\n")
            file.write(e)
        
    ih.clear_button_leds()
    ih.inky_frame.button_e.led_on()
    time.sleep(1)
    ih.clear_button_leds()
    ih.stop_network_led()


while True:
    update()
    draw()
    ih.led_warn.off()
    ih.stop_network_led()
    ih.clear_button_leds()
    ##ih.sleep(ih.app.UPDATE_INTERVAL)
    sleep_for(UPDATE_INTERVAL)
    reset()


Got myself a 7.3" in today as well, exact same issue although it sometimes also happens when connected to Thonny (showing me the debug data Iā€™m printing). A reset and button press on battery usually works but as soon as it wakes up and restarts it usually hangs again. I narrowed it down to graphics.update() as well, it hangs on that part without errors or timeouts, it just stays there and drains the battery. I built in steps lighting up the LEDs at the various steps (before and after network connection, downloading, etc) so I can see while on battery where it is in the code while it infrequently hangs on network it almost always is the graphics.update() code where nothing happens. Not having this work properly pretty much defeats the purpose of the device for me unfortunately. Hopefully itā€™s something that can be fixed.

Good news! Seems i have found and fixed the issue. As we identified some kind of blocking behaviour i had a deeper dive into the code. I modified the busy_wait function to return after a given timeout. This way, a block will be removed after a timeout of currently 30 seconds and the code continues. I am not really sure why the block has not been removed before, but anyways, this seems to fix thei issue. I have now ~ 20 refreshes on battery with a 100% success rate.
If you want to try the fix, you can download the firmware here:
https://github.com/w3stbam/pimoroni-pico/releases/download/1.21.1/firmware.uf2

If it works for you, give some feedback here then i can make a pull request against the main repo

1 Like

This works, but it might not be the best approach. The problem is that the Inky class and the driver class both do the busy-wait. During init, the Inky class tells the driver class that it will take care of busy-waiting, but the driver does not respect this setting everywhere. So it would probably be better to fix the root-cause than to bail out after a given time. Note that the display needs up to 40 seconds for updates and keeps the busy pin active during that time, so just ignoring it could run you into trouble.

Yes i know its not the best approach, but i do not have enough insight to fix the root cause. This at least seems to circumvent the hang and successfully updates the screen. I raised the default timeout value to 45 seconds to be on the safer side. Another relase for that can be found here: Releases Ā· w3stbam/pimoroni-pico Ā· GitHub (1.21.2)

2 Likes

Thanks for putting in the work, hack or not, this firmware resolved the issue for me, I can now reliably sleep and refresh every hour :)

I was having the same graphics.update() hang, but on USB power. (Using very custom main.py Python code - not the standard launcher).

@Kapetzel , your release (with the 45 second fix) fixed the issue for me. Thankyou!