Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Non-blocking PicoGraphics update() for Inky Frame #936

Open
LionsPhil opened this issue Apr 26, 2024 · 4 comments
Open
Labels
[- inky frame -] https://shop.pimoroni.com/products/inky-frame-5-7 enhancement New feature or request

Comments

@LionsPhil
Copy link
Contributor

LionsPhil commented Apr 26, 2024

More-or-less what it says on the tin, and especially for the Inky Frame where the work being waited on is a ~40-second operation happening on the display controller. Either something like

display.update(blocking = False)

or

display.set_blocking(False)
display.update()

Exposing a matching is_busy() and/or busy_wait() would help use this safely.

Reasoning: I have a thin-client which does two things that are interacting unfortunately here:

That means the socket has to be held open, effectively buffering the image in the network, until that first update() has finished the e-ink dance---and makes having reasonable timeouts tougher. Ideally, the sequence would be:

  • Clear the framebuffer, and set the eink controller update()ing, non-blocking
    • The blocking wait is already controllable in the inky73 driver, just not exposed up through PicoGraphics. (It also has the desired is_busy()/busy_wait().)
    • (As far as I can tell, the earlier wait shouldn't be conditional, since if execution proceeds past that with the controller still busy, commands will just get ignored---or worse, some of them ignored if it becomes ready midway.)
  • At this point the image data has already been sent to the controller, so start overwriting the PicoGraphics framebuffer with the streamed decode from the network, and close and finish that off.
  • Block for the first update() to have completed, then start a second to send and display the new framebuffer contents.

(Yes, the network contents could be buffered to system RAM, flash, or an SD card, but only the latter reliably has enough space, and the framebuffer is right there to save the writes and further power overhead if PicoGraphics wasn't busy-waiting. :) )

@riffnshred
Copy link

this would be really helpful. I'm dealing with this issue at the moment and I'm not sure what to do.

@Gadgetoid Gadgetoid added enhancement New feature or request [- inky frame -] https://shop.pimoroni.com/products/inky-frame-5-7 labels Oct 30, 2024
@Gadgetoid
Copy link
Member

Broadly agreed here, assuming I don't run into any roadblocks I'll try to make this change.

@Gadgetoid
Copy link
Member

Gadgetoid commented Oct 31, 2024

Have done some preliminary testing and it seems to work as I'd expect. Beta build here: https://github.com/pimoroni/pimoroni-pico/releases/tag/v1.24.0-beta1

I'm sure I'm preaching to the choir here, but in case anyone drops by and wants to try this, I modified the PlaceKitten example to grab the next image while updating:

import gc
import uos
import time
import random
import machine
import jpegdec
import uasyncio
import sdcard
import WIFI_CONFIG
from urllib import urequest
from network_manager import NetworkManager
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY

# Length of time between updates in Seconds.
# Frequent updates will reduce battery life!
UPDATE_INTERVAL = 20 * 1

"""
random placekitten (from a very small set)
You *must* insert an SD card into Inky Frame!
We need somewhere to save the jpg for display.
"""

gc.collect()  # We're really gonna need that RAM!


def status_handler(mode, status, ip):
    print(mode, status, ip)


network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler)
uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK))


graphics = PicoGraphics(DISPLAY)

WIDTH, HEIGHT = graphics.get_bounds()
FILENAME = "/sd/placekitten.jpg"
ENDPOINT = "http://placecats.com/{0}/{1}"


sd_spi = machine.SPI(0, sck=machine.Pin(18, machine.Pin.OUT), mosi=machine.Pin(19, machine.Pin.OUT), miso=machine.Pin(16, machine.Pin.OUT))
sd = sdcard.SDCard(sd_spi, machine.Pin(22))
uos.mount(sd, "/sd")
gc.collect()  # Claw back some RAM!

jpeg = jpegdec.JPEG(graphics)
gc.collect()  # For good measure...

graphics.set_blocking(False)

while True:
    url = ENDPOINT.format(WIDTH, HEIGHT + random.randint(0, 10))
    print(f"Fetching {url}")

    socket = urequest.urlopen(url)

    # Stream the image data from the socket onto disk in 1024 byte chunks
    # the 600x448-ish jpeg will be roughly ~24k, we really don't have the RAM!
    print("Writing...")
    data = bytearray(1024)
    with open(FILENAME, "wb") as f:
        while True:
            if socket.readinto(data) == 0:
                break
            f.write(data)
    socket.close()
    gc.collect()  # We really are tight on RAM!

    graphics.set_pen(1)
    graphics.clear()

    jpeg.open_file(FILENAME)
    jpeg.decode()
    
    if graphics.is_busy():
        print("Busy waiting...")

    while graphics.is_busy():
        time.sleep(0.5)

    print("Displaying...")
    graphics.update()
    print("Done!...")

    time.sleep(UPDATE_INTERVAL)

@riffnshred
Copy link

Siiiiiick. Thanks dude. I'll test it out over the weekend. Thanks for the great work as always !!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[- inky frame -] https://shop.pimoroni.com/products/inky-frame-5-7 enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants