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

Hi, I would like my Inky Frame running on battery power to wake up every hour to run my main.py script. I am using MicroPython v1.21.0, inky_frame v1.21.0 on 2023-10-06; Raspberry Pi Pico W with RP2040.

  • When on USB power: the function inky_frame.sleep_for(60) wakes up the Inky Frame correctly every hour and refreshes the screen.
  • When on Battery power: the function inky_frame.sleep_for(60) wakes up the Inky Frame correctly after one hour, starts the main.py, but when doing graphics.update(), it doesn’t refresh the screen and the power LED stays on indefinitely.

I have also tried with rtc.set_timer() and rtc.set_alarm(), but I don’t find a lot of documentation to use these functions.

Here is my basic script that runs correctly on USB, but not on battery using inky_frame.sleep_for():

from picographics import PicoGraphics, DISPLAY_INKY_FRAME_7 as DISPLAY
graphics = PicoGraphics(DISPLAY)
import inky_helper as ih
import gc

gc.collect()

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

while True:
    graphics.set_pen(ih.inky_frame.WHITE)
    graphics.clear()
    graphics.set_pen(ih.inky_frame.BLACK)
    graphics.set_font('bitmap6')
    graphics.text("Hello Inky", 0, 0, 600, 4)
    graphics.update()
    ih.inky_frame.sleep_for(1)

Did you test your battery? This is so trivial that the answer is probably yes, but just to rule out the obvious. The reason I am asking: from your description it seems that the machinery to restart the Pico works fine. But of course the screen-update needs higher current than an idle (or even booting) Pico. Current draw will result in voltage drop, and if the battery almost empty, this could cause the program to go wild.

Yes, I tested with 2 sets of batteries. When I run it on batteries, the first loop from main.py is running correctly and it refreshes the screen, it’s only after waking from sleep that graphics.update() don’t seem to work anymore.

Does it make any difference if you do:

import inky_frame

inky_frame.sleep_for(1)

In case the nested import in inky_helper.py is causing problems (or crashing invisibly)?

I think you might also be missing an import time in the code snippet above which might explain why it’s crashing?

When you run from usb-power, do you run from an IDE (e.g. Thonny)? This could make a difference.

I would suggest that you update your program to log to UART (using the RX/TX available on the back) or to SD-card. UART has the advantage that you can trace your program even when running on batteries. I usually use a simple USB-UART that connects on one side to my laptop and on the other side to my pico (only connectiong RX/TX/GND). Make sure your USB-UART is 3V3-safe.

SD is in a way simpler (no additional connection), but you have to run your program, take out the SD-card and analyze afterwards. This is tedious if you need multiple iterations. On the other hand, you have everything documented and you could post a log here.

I tried those two things, but without success. The code is working correctly when powered by USB, but with battery, it only refreshes at the first loop.

Yes, I am using Thonny when running from USB power, and that runs smoothly.

I have a more extended script where I log every step to a txt file. When that script runs on battery and when it loops for a second time (after one minute), it skips row graphics.update(). Is it that what you mean? Otherwise, can you point me to how I could use UART?

This link should get you going with uart: Raspberry Pi Pico Serial Communication Example-MicroPython Code (just skip the read part, since you just want to write to the uart and not read anything). The example only transfers a single char, for multiple chars, you would use something like

txData = b'hello world\n\r'
uart.write(txData)

Could you please post your complete extended script? It seems strange that the python-interpreter skips a single row, this never happened to me before.

Alternatively, you could include whatever part of your code you think is causing problems in a try / except loop, and get it to print whatever error it throws to the screen?

i.e.

try:
    # insert suspect code here
except Exception as e:
    display.text(e, 0, 0)
    display.update()

Initially, I thought the problem was resolved because the script completed two loops on battery power. However, after I extended the duration in the sleep_for() function, the issue re-emerged. For clarity, when the device is connected via USB-C and operated with Thonny, it runs the loops without any interruptions.

But, when it’s on battery power, the script consistently halts at the “Updating screen” stage (see script below) after successfully completing the first cycle. It does wake up as intended after a one-minute interval, yet it fails to progress beyond that point (graphics.update()).

Could it be necessary to use different libraries specifically for battery operation? I’ve noticed that if I manually reset the device (using the reset button and pressing button A), it will resume normal operation and refresh the screen. Is it possible to emulate this reset process programmatically within each loop? If so, how might I implement that?

Here is my script (with logging):

from picographics import PicoGraphics, DISPLAY_INKY_FRAME_7 as DISPLAY
graphics = PicoGraphics(DISPLAY)
import inky_helper as ih
import time
import gc
import builtins
import inky_frame

gc.collect()

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

# Log file
def get_timestamp():
    now = time.localtime()
    return '{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}'.format(now[0], now[1], now[2], now[3], now[4], now[5])

def log(message):
    timestamp = get_timestamp()
    with open('error_log.txt', 'a') as f:
        f.write(f"{timestamp} - {message}\n")

def print(*args, **kwargs):
    message = " ".join(str(arg) for arg in args)
    log(message)
    builtins.print(*args, **kwargs)

# Loop
while True:
    now = time.localtime()
    formatted_time = "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(now[0], now[1], now[2], now[3], now[4], now[5])
    graphics.set_pen(ih.inky_frame.WHITE)
    print("Clearing screen")
    graphics.clear()
    graphics.set_pen(ih.inky_frame.BLACK)
    graphics.set_font('bitmap6')
    graphics.text("Hello Inky", 0, 0, 600, 4)
    graphics.text(formatted_time, 0, 30, 600, 4)

    try:
        print("Updating screen")
        graphics.update()
    except Exception as e:
        graphics.text(e, 0, 0)
        graphics.update()
    print("Screen updated, going to sleep...")
    inky_frame.sleep_for(1)

Your code (saved as main.py) seems to be working fine for me on battery, it’s waking up and refreshing happily every minute (though it’s always displaying the time as 2021-01-01 00:00:02, as it’s not setting the RTC from anywhere).

Are you seeing your issue with a one minute sleep_for(), or is it only showing up with a longer sleep? Are you positive (ha!) that you’ve got sufficient charge in your battery to be able to manage multiple update cycles?

I’m experiencing this issue even with sleep_for(1). I’ve recharged the batteries and even tried a different battery case. The device refreshes initially, but after a one-minute interval, I notice the LED next to the flag turns on, and then nothing further happens. It appears to get stuck at this point. Is it possible that my device has a fault causing this behavior?

Sounds like that might be a possibility - drop us an email at support@pimoroni.com (with a link to this thread) if you’d like to try swapping it out.

Oh - here’s a thing you could try - running this basic RTC example:

If that throws errors it might point to a fault with the RTC.

Thanks for this suggestion. I ran it with USB plugged in and it works: it says ‘tests complete’. Should I run it on battery as well? If so, does it create a log?

I think the issue is not with the RTC as it correctly turns on the LED after one minute in my previous script. It is more an issue with the graphics.update() which gets stuck when running on battery.

Are you using a 3 battery AA pack? If you are using NiMH rechargeables, might be worth a quick test on fresh, unused alkaline batteries.

It may also be worth leaving one of the inky frame LEDs flashing using low rate PWM to see if Pico is still healthy when graphics.update() misbehaves. Have a look at set_led() in https://github.com/kevinjwalters/micropython-examples/blob/master/pico-w/sdslideshow.py

Hello, I have exactly the same issue with my inky frame 7.3 on v1.21.0. On battery(3 fresh AA), the first graphics update() works then after sleep_for() the second update() hangs. All good if plugged on USB with Thonny.

There is also an issue logged about this on Update of graphics.update() seems to get stuck indefinatelly · Issue #866 · pimoroni/pimoroni-pico · GitHub

Just to add - this has been an issue since release - I got an inky swapped with support suggesting it was defective, but the replacement had the same issue. It’s quite annoying as it stops graphic updates with deep sleep using the battery pack. Everything works fine on usb (I lost a lot of time trying to figure it out, only to eventually see logic and presume the issue was elsewhere and not my code).

Would it be possible to get an acknowledgement of an issue and any thoughts on a possible fix?

Andy

I’ve not been able to replicate this behaviour at all - my Inky Frame dashboard has been running fine on my desk from battery (waking up to update every couple of hours and using sleep_for between updates) for well over a month 🤔

For folks that are having trouble - does hitting reset (and then pressing a front button to trigger a wake up) to start main.py afresh after disconnecting the USB cable help? I wonder if disconnecting the cable whilst the program’s running means it assumes that it has an always on USB connection and so doesn’t sleep properly?

If you’re using wi-fi it might also be worth checking that your connection code is robust. It took me quite a bit of tweaking to find some wi-fi handling code that I was happy with and handled occasional timeout errors etc sensibly without crashing.