Thingiverse Apps API
As with most Internet of Things (IoT) projects on the PyPortal, we are able to perform our dynamic data and image gathering through the wonderful magic of a REST Application Programming Interface (API) request, that returns a JSON text file.
The Thingiverse Apps API makes it easy for us to request essential info about a Thing, including its name, URL, and image.
In order to use the Thingiverse API, you'll need to sign up for a free developer apps account. This will grant you your own Thingiverse app token needed for running queries.
Here's how to sign up:
- First, log in or sign up for a regular Thingiverse account here https://accounts.thingiverse.com/register
- Once you're logged in, go to the Apps create page here: https://www.thingiverse.com/apps/create
- On this page, click the Web App radio button
- In the Basic Information area, enter an App Name, such as PyPortal Thingiverse Viewer (if you use a name that's already in use, you'll need to adjust it slightly to make it unique). Also enter an App Description
- Click the checkbox to agree to the MakerBot API terms
- Up at the top, right corner of the screen, click on the Create & Get App Key button
This will generate your Client ID, Client Secret, and App Token keys. Do not share these, keep them secret! You'll need to copy and paste them into a secure note somewhere on your computer. Later, we'll enter the App Token into the PyPortal's secrets.py file in order to query the Thingiverse API in read-only mode.
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, along with your Thingiverse App Token like this:
CIRCUITPY_WIFI_SSID = "your_wifi_ssid" CIRCUITPY_WIFI_PASSWORD = "your_wifi_password" AIO_USERNAME = "your_aio_username" AIO_KEY = "your_aio_key" THINGIVERSE_TOKEN = "very_long_thingiverse_app_token"
At this point make sure your WiFi SSID and password are also in settings.toml. You only have to do this once when you set it up. If you move the PyPortal to a different WiFi served location, use a text editor to enter the new values in this file.
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_Thingiverse.
Copy the contents of the PyPortal_Thingiverse 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.
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: 2019 Limor Fried for Adafruit Industries # # SPDX-License-Identifier: MIT import os import time import random import board import adafruit_pyportal # Set up where we'll be fetching data from NUM_THINGS=25 # how many things to select from (we randomize between em) DATA_SOURCE = "https://api.thingiverse.com/users/adafruit/things?per_page="+str(NUM_THINGS) DATA_SOURCE += "&access_token=" + os.getenv("THINGIVERSE_TOKEN") IMAGE_LOCATION = [0, "thumbnail"] TITLE_LOCATION = [0, "name"] URL_LOCATION = [0, "public_url"] # determine the current working directory needed so we know where to find files cwd = ("/"+__file__).rsplit('/', 1)[0] pyportal = adafruit_pyportal.PyPortal(url=DATA_SOURCE, json_path=(TITLE_LOCATION, URL_LOCATION, IMAGE_LOCATION), status_neopixel=board.NEOPIXEL, default_bg=cwd+"/thingiverse_background.bmp", text_font=cwd+"/fonts/Arial-12.bdf", text_position=((5, 10), (5, 230)), text_color=(0x00FF00, 0x00FF00), text_transform=(None, None)) pyportal.preload_font() while True: response = None try: response = pyportal.fetch() print("Response is", response) pyportal.set_background(None) image_url = response[2].replace('_thumb_medium.', '_display_large.') pyportal.wget(pyportal.image_converter_url(image_url,320, 240,color_depth=16), "/cache.bmp", chunk_size=512) pyportal.set_background("/cache.bmp") except (IndexError, RuntimeError, ValueError) as e: print("Some error occured, retrying! -", e) # next thingy should be random! thingy = random.randint(0, NUM_THINGS - 1) URL_LOCATION[0] = TITLE_LOCATION[0] = IMAGE_LOCATION[0] = thingy time.sleep(60 * 3) # cycle every 3 minutes
How It Works
The Thingiverse 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 thingiverse_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 Thingiverse API.
When you make a request of the server, 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://api.thingiverse.com/users/adafruit/things?per_page=1&access_token=
into your browser, where you paste your Thingiverse token in at the end of that URL.
When you enter this in your web browser, you'll see a result returned like this (in the actual code we request 25 things and then randomly pick which one to show, so you may see a different one than this):
[ { "id": 3553481, "name": "Prop-Maker Keyblade", "url": "https://api.thingiverse.com/things/3553481", "public_url": "https://www.thingiverse.com/thing:3553481", "thumbnail": "https://cdn.thingiverse.com/renders/e2/a8/15/5b/c1/41109f706f73d0cbaa74e0f38e597173_thumb_medium.jpg", "creator": { "id": 491, "name": "adafruit", "first_name": "Adafruit", "last_name": "Industries", "url": "https://api.thingiverse.com/users/adafruit", "public_url": "https://www.thingiverse.com/adafruit", "thumbnail": "https://cdn.thingiverse.com/renders/30/1b/8b/a1/70/adafruit-tv-avatar_thumb_medium.jpg" }, "is_private": false, "is_purchased": false, "is_published": true } ]
That result is a JSON (JavaScript Object Notation) array. It is comprised of a single element with a number key:value pairs, or sub-hierarchies of additional key:value pairs. For example, there is one key called name
which has a value of "Prop-Maker Keyblade"
which is expressed this way:
"name":
"Prop-Maker Keyblade"
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!
The thumbnail
key leads to this value: "https://cdn.thingiverse.com/renders/e2/a8/15/5b/c1/41109f706f73d0cbaa74e0f38e597173_thumb_medium.jpg"
Here's the image at that url:
In our code, we'll do a string substitution in order to get the "_display_large" version of that image in place of the "_thumb_medium" version. Here's what that looks like:
We also get the public_url
key, which is the short URL that will be displayed at the bottom of the screen, in this case, "https://www.thingiverse.com/thing:3553481"
You can see how it's done in this part of code.py:
NUM_THINGS=25 # how many things to select from (we randomize between em) DATA_SOURCE = "https://api.thingiverse.com/users/adafruit/things?per_page="+str(NUM_THINGS) DATA_SOURCE += "&access_token=" + secrets['thingiverse_token'] IMAGE_LOCATION = [0, "thumbnail"] TITLE_LOCATION = [0, "name"] URL_LOCATION = [0, "public_url"]
Then, in the pyportal
query we ask for the image name from that URL to get the path to the .jpeg image file, as well as the name and url values.
pyportal = adafruit_pyportal.PyPortal(url=DATA_SOURCE, json_path=(TITLE_LOCATION, URL_LOCATION, IMAGE_LOCATION), status_neopixel=board.NEOPIXEL, default_bg=cwd+"/thingiverse_background.bmp", text_font=cwd+"/fonts/Arial-12.bdf", text_position=((5, 10), (5, 230)), text_color=(0x00FF00, 0x00FF00), text_transform=(None, None))
With all of this prepared, during the main loop of while True:
the code will query the page for the JSON data.
The first of the 25 things will be selected. 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.
Then, the text for both the name and public_url values are drawn on top of the image background, using the specified font, color, and position in the PyPortal constructor.
Note, we're also specifying the text_wrap
amount so that lines of text won't get cut off.
This updates every three minutes, so your Things stay fresh!
Speaking of 3D printing -- if you'd like to make your own 3D printed PyPortal stand, check out the Thingiverse model and instructions here!
If you have AdaBox 011, the laser cut acrylic stand also works very well.
Text editor powered by tinymce.