Cute Animal APIs
We'll use three sources for our images of cats, dogs, and foxes. These are all websites that have open application programming interfaces (APIs). That means we can query the APIs and we'll be served up some data, including the path to a cute animal image .jpeg file chosen randomly from their collections.
We'll also use Adafruit IO to convert these .jpeg files to .bmp images, which is idea for display on the PyPortal.
Adafruit IO Image Converter Server
In order to use the Adafruit image converter, this project will require you to have an Adafruit IO username and key. Adafruit IO is absolutely free to use, but you'll need to log in with your Adafruit account to use it. If you don't already have an Adafruit login, create one here.
If you haven't used Adafruit IO before, check out this guide for more info.
Once you have logged into your account, there are two pieces of information you'll need to place in your settings.toml file: Adafruit IO username, and Adafruit IO key. Head to io.adafruit.com and simply click the View AIO Key link on the left hand side of the Adafruit IO page to get this information.
Then, add them to the settings.toml file like this:
CIRCUITPY_WIFI_SSID = "your_wifi_ssid" CIRCUITPY_WIFI_PASSWORD = "your_wifi_password" AIO_USERNAME = "your_aio_username" AIO_KEY = "your_aio_key"
Add CircuitPython Code and Assets
In the embedded code element below, click on the Download Project Bundle button, and save the .zip archive file to your computer.
Then, uncompress the .zip file, it will unpack to a folder named PyPortal_cutefuzz.
Copy the contents of the PyPortal_cutefuzz directory to your PyPortal CIRCUITPY drive.
Editing the Code
You can edit the code.py file with any text editor you like. Adafruit suggests installing the free Mu Python editor as it's super handy, recognizes Adafruit boards, and has a built in serial monitor/REPL to interact with the board. Find out more about Mu here.
boot.py
We're using a special file to ensure the .bmp cache writes to the flash properly. This is the unsafe_boot.py file you copied to the drive. Rename it to boot.py now. Then, press the reset button on the PyPortal.
Note that you'll see this scary looking text appear during restart, don't worry, it's supposed to say that!
**************** WARNING ******************
Using the filesystem as a write-able cache!
This is risky behavior, backup your files!
**************** WARNING ******************
This is what the final contents of the CIRCUITPY drive will look like:

# SPDX-FileCopyrightText: 2020 Dan Cogliano for Adafruit Industries # # SPDX-License-Identifier: MIT import time import random import board from adafruit_pyportal import PyPortal from adafruit_display_shapes.circle import Circle WIDTH = board.DISPLAY.width HEIGHT = board.DISPLAY.height #pylint: disable=line-too-long # these lines show the entire collection APIURL = "https://openaccess-api.clevelandart.org/api/artworks?cc0=1&has_image=1&indent=2&limit=1&skip=" IMAGECOUNT = 31954 # uncomment these lines to show just paintings # APIURL = "https://openaccess-api.clevelandart.org/api/artworks?cc0=1&has_image=1&indent=2&limit=1&type=Painting&skip=" # IMAGECOUNT = 3223 BACKGROUND_FILE = "/background.bmp" if WIDTH > 320: BACKGROUND_FILE = "/background_480.bmp" pyportal = PyPortal(default_bg=BACKGROUND_FILE, image_json_path=["data", 0, "images", "web", "url"], image_dim_json_path=(["data", 0, "images", "web", "width"], ["data", 0, "images", "web", "height"]), image_resize=(WIDTH, HEIGHT - 15), image_position=(0, 0), text_font="/fonts/OpenSans-9.bdf", json_path=["data", 0, "title"], text_position=(4, HEIGHT - 9), text_color=0xFFFFFF) circle = Circle(WIDTH - 8, HEIGHT - 7, 5, fill=0) pyportal.splash.append(circle) loopcount = 0 errorcount = 0 while True: response = None try: circle.fill = 0xFF0000 itemid = random.randint(1, IMAGECOUNT) # itemid = 20 # portrait mode example # itemid = 21 # landscape mode example print("retrieving url:", APIURL + str(itemid)) response = pyportal.fetch(APIURL + str(itemid)) circle.fill = 0 print("Response is", response) loopcount = loopcount + 1 except (RuntimeError, KeyError, TypeError) as e: print("An error occured, retrying! -", e) print("loop counter:", loopcount) assert errorcount < 20, "Too many errors, stopping" errorcount = errorcount + 1 time.sleep(60) continue errorcount = 0 stamp = time.monotonic() # wait 5 minutes before getting again while (time.monotonic() - stamp) < (5*60): # or, if they touch the screen, fetch immediately! if pyportal.touchscreen.touch_point: break
The Cutefuzz Image Viewer works like this: first, when it starts up it connects to your Wifi access point as specified (and authenticated) in the secrets.py file.
Background Splash Screen
Next, it displays the cute_background.bmp image file splash screen. This is a 320x240 pixel RGB 16-bit raster graphic in .bmp format.
JSON
In order to retrieve images, we'll be making a query to the different APIs listed in the code.py file for cats, shibes, and foxes.
When you make a request of the servers, you'll get a JSON file returned as the response.
In fact, you can run the same query as the PyPortal does to see the result. Copy and paste this link https://randomfox.ca/floof/
into your browser.
When you enter this in your web browser, you'll see a result returned like this:
{ "image": "http://randomfox.ca/images/91.jpg", "link": "http://randomfox.ca/?i=91" }
That result is a JSON (JavaScript Object Notation) array. It is comprised of a single element with two key:value pairs. For example, there is one key called image which has a value of http://randomfox.ca/images/44.jpg
which is expressed this way:
"image":
"http://randomfox.ca/images/44.jpg"
Since this JSON object array has a consistent way to return the results to us, the code we're running on the PyPortal can easily parse the data and display it!
Here's the image at that url:
You can see how it's done in this part of code.py:
# random fox DATA_SOURCE = "https://randomfox.ca/floof/" IMAGE_LOCATION = ["image"]
Then, in the pyportal
query we ask for the image name from that URL to get the path to the .jpeg image file.
pyportal = PyPortal(url=DATA_SOURCE, status_neopixel=board.NEOPIXEL, default_bg=cwd+"/cute_background.bmp", image_json_path=IMAGE_LOCATION, image_resize=(320, 240), image_position=(0, 0))
With all of this prepared, during the main loop of while True:
the code will query the page for the JSON data.
When it gets the path of the .jpeg file, the pyportal library passes it along to an Adafruit IO image converter server where the file is converted into the format the PyPortal can display, a 320x240 pixel RGB 16-bit .bmp.
This image is then cached onto the PyPortal's storage and displayed on the PyPortal TFT screen.
This updates every five minutes, for frequent novel cute injections!
Cats, Foxes, & Shibes
You can change the floofular animal type by uncommenting/commenting sections of the code as shown here:
# random cat DATA_SOURCE = "https://api.thecatapi.com/v1/images/search" IMAGE_LOCATION = [0, "url"] # random fox #DATA_SOURCE = "https://randomfox.ca/floof/" #IMAGE_LOCATION = ["image"] # random shibe #DATA_SOURCE = "http://shibe.online/api/shibes?count=1" #IMAGE_LOCATION = [0]
That will provide cats, rather than foxes. You can re-comment the cat lines and uncomment the "shibes" lines for floofed out dogs!
Page last edited January 22, 2025
Text editor powered by tinymce.