PRESTO and large jpg photos

I’m trying to display a world map on the Presto screen. I’ve resized it to 480x275 pixels in Photoshop. Opened it in Gimp and then exported it with all the tick boxes unticked.
This program is the program I am using:

from presto import Presto
import jpegdec
# Setup for the Presto display
presto = Presto(full_res=True)
#presto = Presto()
display = presto.display

WIDTH, HEIGHT = display.get_bounds()

BLACK = display.create_pen(0, 0, 0)
YELLOW = display.create_pen(255, 255, 0)

def clean(): # Clear the screen to Black
    display.set_pen(BLACK)
    display.clear()
clean()
presto.update()
# Create a new JPEG decoder for our PicoGraphics
j = jpegdec.JPEG(display)

# Open the JPEG file
j.open_file("Map480g.jpg")

# Decode the JPEG
j.decode(0, 0, jpegdec.JPEG_SCALE_FULL, dither=True)

# Display the result
presto.update()

and I keep getting this error:

MicroPython feature/presto-wireless, presto v0.0.6 on 2025-01-08; Presto with RP2350

Type "help()" for more information.

>>> %Run -c $EDITOR_CONTENT

MPY: soft reboot
malloc self
set fb pointers
m_new_class(ST7701...
launch core1
launched core1
core1 returned
Traceback (most recent call last):
  File "<stdin>", line 22, in <module>
OSError: [Errno 2] ENOENT
>>> 

I’m doing something wrong but I cannot see what and do not understand the error message.

Can someone please point me in the right direction?

ENOENT is the code for “file not found”. It references line 22 which is j.open_file("Map480g.jpg"). So maybe you misspelled the filename?!

Thanks - so much time wasted over a single space character!

Working well - What a display!

Somebody may be interested in what I managed to do with my PRESTO, a world map and the internet.

If you would like the code, just ask.

4 Likes

This is fantastic. The addition of the plotter map makes excellent use of the display. I would love the code.

Thanks

My latest version reads the last 500 records and extracted the larger quakes from the list. I shows the largest first. Quite interesting now with the quakes in Santorini. Most of the time the site records loads of very small quakes in CA, USA

Here is the code:

# Test WIFI network DEMO on Pimoroni PRESTO - Vers 2 with Touch Halt
# Uses data from https://earthquake.usgs.gov
#   ==== Tony Goodhew === 8th Feb 2025 ====
# Shows how to download data from a website, process it to
# extract what you need and display results
# on the 480x480 pixel screen with world map
# Video at https://youtu.be/MOoyETQSido

# Touching the screen halts the program

number = 500  # CHANGE number of records downloaded HERE  <++++++++++++++++++++++
size = 4.5    # Minimum magnetude displaye
max = -999.9  # Rogue value for to find max mag quake

import time
from random import randint
from presto import Presto
import network
import rp2
import requests
rp2.country("GB")
from secrets import WIFI_SSID, WIFI_PASSWORD
import jpegdec
import math

# Setup for the Presto display
presto = Presto(full_res=True)
display = presto.display

WIDTH, HEIGHT = display.get_bounds()

touch = presto.touch # Activate touch

# Create a new JPEG decoder for our PicoGraphics
j = jpegdec.JPEG(display)
eq = 212 # Position of equator on the screen

# Create some colours
BLUE = display.create_pen(20,0,255)
WHITE = display.create_pen(255, 255, 255)
RED = display.create_pen(255,0,0)
ORANGE = display.create_pen(245, 165, 4)
GREEN = display.create_pen(0,255,0)
PINK = display.create_pen(250, 125, 180)
CYAN = display.create_pen(0,255,255)
MAGENTA = display.create_pen(255,0,255)
BLACK = display.create_pen(0, 0, 0)
YELLOW = display.create_pen(255, 255, 0)

# A few procedures to be used later
def wait(z): # delay a while
    time.sleep(z)
    
def clean(): # Clear the screen to Black
    display.set_pen(BLACK)
    display.clear()

# Routine to separate and extract data items from a single downloaded line
def splitup(s):
    string = ""
    items = []

    for p in range(len(s)):
        c = s[p]
#        print(type(c))
        if c != chr(124):
            string = string + c
        else:
            items.append(string)
            string = ""
    items.append(string)        

    # Extract date and time from second item
    dt = items[1]
    date = dt[0:10]
    ttime = dt[11:19]
    return(items,date,ttime)

display.set_font("bitmap8") # Change the font
clean()
display.set_pen(RED)
display.text("Large Quake",40,200,460,8)
display.set_pen(BLUE)
display.text("Data from: https://earthquake.usgs.gov",70,400,480,2)
display.text("Tony Goodhew, Leicester UK",120,450,480,2)
presto.update()
time.sleep(1)

for p in range(35):
    display.set_pen(BLACK)
    display.rectangle(0,190,480,100)
    xx = randint(0,16)-8
    yy = randint(0,16)-8
    display.set_pen(ORANGE)
    display.text("Large Quake",40+xx,200+yy,460,8)
    presto.update()
    time.sleep(0.07)
clean()
display.set_pen(RED)
display.text("Large Quake",40,200,460,8)
presto.update()
time.sleep(0.6)

clean()
display.set_pen(RED)

# Activate WiFi 
wlan = network.WLAN(network.STA_IF)
wlan.active(True)

wlan.connect(WIFI_SSID, WIFI_PASSWORD)
max_wait = 30
while max_wait > 0:
    if wlan.status() < 0 or wlan.status() >= 3:
        break
    max_wait -= 1
    print("Waiting for Wi-Fi connection...")
    display.set_pen(RED)
    display.text("Waiting for Wi-Fi connection...",10,200,460,5)
    presto.update()
    time.sleep(1)

if wlan.status() != 3:
    raise RuntimeError("Network connection failed")
else:
    print("Connected to Wi-Fi network.")
    print(wlan.ifconfig())

clean()
display.set_pen(GREEN)
display.text("Connected to WiFi",50,200,460,5)
display.set_pen(BLUE)
display.text("   Processing",70,260,460,5)
presto.update()

# ===== Main Loop ====
running = True # Main loop flag
while running:
    clean()
    display.set_pen(YELLOW)
    display.text("   Updating",70,260,460,5)
    presto.update()
    
    # Access EarthQuake website and get first 15 lines
    addr = "https://earthquake.usgs.gov/fdsnws/event/1/query?format=text&limit=" + str(number)
    response = requests.get(addr)

    # Separate into lines
    lines = []
    for x in response.content.splitlines():
        xs = str(x)
#        print(str(xs))
        lines.append(str(x))
    response.close()

    
    display.set_pen(YELLOW)
    events = []
#    large = []
    for i in range(1,number + 1):    
        k = splitup(lines[i]) # This does the major lifting using the proc above
        events.append(k)
       
    ''' 
    # List of items for information
    names = ['EventID', 'Time', 'Latitude', 'Longitude', 'Depth/km', 'Author', 'Catalog', 'Contributor',
             'ContributorID', 'MagType', 'Magnitude', 'MagAuthor', 'EventLocationName', 'Date', 'Time']
    '''
    # extract the large ones
    large = []
    for i in range(number):
        k,date,ttime = events[i] # Fetch decoded quake data line
        q = float(k[10])
        if q > size:
            large.append(i)
            if q > max:
                max = q
                ii = i
    print(large)
    print(ii)

    # Show recent event high-lights
    pointers = []
    pointers.append(ii)
    
    for i in large:
        pointers.append(i)
    for i in pointers:
        clean()
        
        j.open_file("Phys480g.jpg")  # World map
        # Decode the JPEG
        j.decode(0, 0, jpegdec.JPEG_SCALE_FULL, dither=True)
            
        k,date,ttime = events[i] # Fetch decoded quake data line
        display.set_pen(BLUE)
        if i == pointers[0]:
            display.set_pen(RED)
        display.text("#"+str(i),0,440,100,3)    # sequence number
        display.text(date + "  --  " + ttime,80,440,460,3)  # Date and time
        
        lat = round(float(k[2]),1)
        long = round(float(k[3]),1)
        if long >177: long = 177 # Fudge for mape IDL edge
        xoff = long * 243/180
        display.set_pen(YELLOW)

        display.line(int(xoff + 240),0, int(xoff + 240),308) # Longitude line
        r = -170  # scale factor - depends on map picture
        noff = r * math.log(math.tan((math.pi/4) + math.radians(lat/4)))
        yy = int(212+noff)
        display.line(0,yy,479,yy)  # Latitude line
        place = k[12]
        place = place[:-1]  # remove final quote character
        display.text(place,0,325,460,2)
        
        display.text("Lat/Long: "+str(lat) + "  "+ str(long),150,350,460,2)
        display.text("Magnitude: " + k[10],0,350,460,2)
        mag = abs(float(k[10])) # Once found a negative mag!!!
        magr = round(math.sqrt(mag * 40.0))
        display.set_pen(GREEN)
        if mag > 2.0:
            display.set_pen(YELLOW)
            if mag > 4.0:
                display.set_pen(ORANGE)
                if mag > 6.0:
                    display.set_pen(RED)
                        
        display.circle(400,400,magr) # Area depends on magnitude
        display.set_pen(YELLOW)
        display.text("Type: " + k[9],0,380,460,2)
        deep = round(float(k[4]),3)
        display.text("Depth/km: "+ str(deep),150,380,460,2)
        
        presto.update()

        # Time delay loop - Cannot use time.sleep()
        # Touching the screen halts the program
        deadline = time.ticks_ms() + 3000
        while time.ticks_ms() < deadline:
            touch.poll()
            if touch.state:
                clean()
                presto.update()
                running = False # Reset flag to finish main loop
                i = 10
                break  # Out of while loop
        if running == False:
            break  # Out of counted 'i' loop

print("\nTerminated by screen touch")

You will need my map

It has a slight flaw near the International Date Line, west of NZ where a small vertical slice of Russia is missing. I’ve inserted a fudge while I look for a better map. I’m pretty pleased with the method and how fast the whole thing runs. The Latitude formula is pretty straight forward in line 210. If you use a different map you need to know the line on the screen for your equator and then adjust r until it hits a city’s Lat. correctly. I used London.

I hope you can get it to work.

1 Like

Just one tip: instead of the first part of splitup(s) where you manually iterate of the strings, you could just use parts = s.split('|',maxsplit=3). This should be much faster. Maybe you have to decode the string first, not sure if your response.content is still in bytes.

Thank you. Microython objected to “maxsplit=3” but the rest worked.

# Routine to separate and extract data items from a single downloaded line
def splitup(s):
    s = str(s)       
    items = s.split('|')
    # Extract date and time from second item
    dt = items[1]
    date = dt[0:10]
    ttime = dt[11:19]
    return(items,date,ttime)

Thank you sir. It works great. Keep up the good work!!

I’m glad it works. I was a bit worried that the map might not pass on properly.

Have fun!

Thanks for sharing, Tony.

I was also playing around with your code and found it interesting to see even more events, but all on the map to understand the affected regions better…

That looks great - carefully positioned magnitude blobs.

…I just used your calculation algorithm of the yellow coordinate cross (which I then have commented out, because it were too many lines on the screen) and used it as centre point for the individual blobs…
Also, I increased the threshold in the filter algorithm to just see a bit more…

this is still your code, just slightly adjusted…

Actually, I am interested in the long-lat-calculus, as I want to feed the realtime ISS position into it and draw the flight line onto the map…
thus, it should visualise that the ISS is actually scanning the earth for climate research matters while orbiting the globe every 92 minutes…

This is how the first shot looks like. I think, this is pretty impressive to get a rough positioning of the ISS and it’s route…

I used the open-notify API for the ISS realtime positioning data and just throw the positions into Tony’s long-lat-algorithm to visualise it on screen…

I then also can predict (calculate) by when it will overfly my place, so the kids can run out and wave the astronauts, as they participated in the Astro Pi challenge and their code will be executed on the ISS by the astronauts crew… 😉

1 Like

…have you maybe compared it to a RP2040?

It’s a bit difficult as a basic Pico will not drive the large screen at high res and this program uses loads of memory for data.

…I have improved the API error handling a bit and now there are even more data plots on the screen.
Interesting to see how the ISS is actually orbiting the earth and that it literally overflies quite any part of the world over time…

Every coloured bubble represents a reading of position…

I’m impressed. Are you able to share the method and code?

Hi Tony,

I still need to further improve the API calls, as there are some flaws during the calls and for some reason it breaks up after a few thousand calls pretty much out-of-the-blue.
What you can see in the positioning drawing, there are gaps and hick-ups (just blank green lines lines without the positioning bubbles on it), which I want to get under control first…

What I can share for now is further progress on the API get and interesting flight recordings, which make up for a nice visual.

I already improved the API get a bit by some error handling and am quite happy with the result, but this doesn’t work forever to put somewhere on display (which I want), but breaks at a certain point in time.
I need to track this further down as many hick-ups happen due to the API calls and error handling associated with it (API availability, timeouts, rate limits kicking in, connectivity break-ups on PICO side and other networking issues, retry mechanisms, etc)…
Every flipped bit dropping in will cause the algorithm to not being able to extract the positioning data from the reading and - of course - cause the algorithm to fail and make the program stop working.

After a bit more bug fixing and testing I am happy to share…but for now I feel it’s still a bit buggy…

Sounds like quite a job! Great progress so far. Best wishes.