The code for the project is fairly basic, with only three main tasks to complete: fetch the current CPI data, calculate the adjusted value to show on the E-Ink display, and go into deep sleep waiting for a day to do it all over again.
WiFi & Fetch CPI Data
WiFi SSID and password are looked up from the settings.toml file using os.getenv()and a connection is made to the specified network. The code prepares to fetch API data by initializing the connection manager and requests Session.
The get_latest_cpi_value() function is responsible for making the request to the bls.gov API and extracting the relevant bits of data that are used, the current CPI value with the month and year that it came from. The function returns a tuple containing (cpi_value, month, year).
Data inside the bls.gov API is broken into units called time series. Each series has a unique series ID that is used to fetch it from the API. In the most basic usage, a series ID is used at the end of the the API URL like this:https://api.bls.gov/publicAPI/v2/timeseries/data/CUUR0000SA0. More advanced usage allows fetching multiple series at once, or specifying other parameters to filter and find specific data of interest. This project sticks to the basics, see the API docs for more details. CUUR0000SA0 is the series ID for the "universal all items" CPI data that this project uses. If you're interested to go digging deeper into the API, the series ID format documentation page lists other types of available data and how to format the ID for them.
The code sets display.rotation = 0 to put the display into the portrait orientation with the buttons on the right side of the E-Ink display.
The graphics for the project are comprised of the following elements:
- White background - A white rectangle that matches the display size. Everything else is layered on top of it. This makes it so the app can display black text on a white background. A
displayio.Groupthat is set toscale=8is used so theBitmapobject can be smaller than the actual display size allowing it to use less RAM. - 50 Cent Photo - An
OnDiskBitmapis used to show the photo of 50 Cent. The grayscale photo has been dithered and quantized down to 4 total colors: black, white, light grey, and dark grey. These colors match the ones available on the MagTag's E-Ink display. This process gives the illusion of more color depth. The image file is sized appropriately to fit the 128px width of the display in the portrait orientation. - Message Label - This is the first of 3 TextBox elements. It's placed underneath the 50 Cent photo, and it gets the month and year from the CPI data to show a message like "As of September 2025 50 Cent is worth". The alignment setting on the
TextBoxallows all of the text to be shown center-aligned. - Amount Label - The next
TextBoxis placed below the message label. It shows the calculated numerical value of 50 cents from 1994 adjusted for current inflation. ThisTextBoxis initialized withscale=3to make it's characters display 3x larger than the others. - Cent Label - This is the last TextBox, placed at the bottom of the display. It displays the "Cent" label beneath the larger amount value.
Sleep
After fetching and displaying the latest CPI data the script uses deep sleep to put the microcontroller into a low power state for 86400 seconds, or 1 full day. The program will start over from the beginning when it wakes up repeating the whole process.
The code.py file is embedded below. It contains comments that explain the purpose of each line or section of code.
# SPDX-FileCopyrightText: 2025 Tim C, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
50 Cent Consumer Price Index Tracker
This project illustrates how to fetch CPI data from the
US Bureau of Labor Statistics API. CPI data from 1994,
when 50 Cent adopted his moniker, is compared with the latest
data to determine how much 50 Cent from 1994 is worth today.
This project was inspired by: https://50centadjustedforinflation.com/
"""
import json
import os
import time
import alarm
import displayio
from displayio import OnDiskBitmap, TileGrid, Group
import supervisor
from terminalio import FONT
import wifi
import adafruit_connection_manager
from adafruit_display_text.text_box import TextBox
import adafruit_requests
# CPI Data URL
latest_cpi_url = "https://api.bls.gov/publicAPI/v2/timeseries/data/CUUR0000SA0"
# CUUR0000SA0 the series ID for CPI-U (All items)
# Note: bls.gov API is limited to 20 requests per day for unregistered users
# CPI value for June 1994
june_94_cpi = 148.0
# Get WiFi details, ensure these are setup in settings.toml
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
# Initalize Wifi, Socket Pool, Request Session
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
requests = adafruit_requests.Session(pool, ssl_context)
try:
# Connect to the WiFi network
wifi.radio.connect(ssid, password)
except OSError as ose:
print(f"OSError: {ose}")
print("Wifi connected!")
# Get display reference
display = supervisor.runtime.display
# Set to portrait orientation
display.rotation = 0
# Group to hold all visual elements
main_group = Group()
# Full display sized white background
white_bg_group = Group(scale=8)
white_bg_bmp = displayio.Bitmap(display.width // 8, display.height // 8, 1)
white_bg_palette = displayio.Palette(1)
white_bg_palette[0] = 0xFFFFFF
white_bg_tg = TileGrid(bitmap=white_bg_bmp, pixel_shader=white_bg_palette)
white_bg_group.append(white_bg_tg)
main_group.append(white_bg_group)
# 50 Cent photo
photo_bmp = OnDiskBitmap("50_cent.bmp")
photo_tg = TileGrid(bitmap=photo_bmp, pixel_shader=photo_bmp.pixel_shader)
main_group.append(photo_tg)
def get_latest_cpi_value():
"""
Fetch the latest CPI data from BLS API.
:return: tuple containing (latest CPI value, month, year)
"""
try:
response = requests.get(latest_cpi_url)
try:
json_data = response.json()
except json.decoder.JSONDecodeError as json_error:
raise Exception(f"JSON Parse error: {response.text}") from json_error
latest_data = json_data["Results"]["series"][0]["data"][0]
return (
float(latest_data["value"]),
latest_data["periodName"],
latest_data["year"],
)
except Exception as e:
raise Exception(f"Error fetching data from BLS API: {e}") from e
# fetch the latest CPI data
latest_cpi_value, month, year = get_latest_cpi_value()
# Label for the message below the photo
message_lbl = TextBox(
FONT,
128,
TextBox.DYNAMIC_HEIGHT,
align=TextBox.ALIGN_CENTER,
text=f"As of {month} {year}\n50 Cent is worth",
color=0x000000,
scale=1,
)
message_lbl.anchor_point = (0, 0)
message_lbl.anchored_position = (0, photo_tg.tile_height + 6)
main_group.append(message_lbl)
# calculate current value of 50 cents from 1994
current_value = round(50.0 * (latest_cpi_value / june_94_cpi) + 0.5)
# Label for the calculated value
amount_lbl = TextBox(
FONT,
128 // 3,
TextBox.DYNAMIC_HEIGHT,
align=TextBox.ALIGN_CENTER,
text=f"{current_value}",
color=0x000000,
scale=3,
)
amount_lbl.anchor_point = (0, 0)
amount_lbl.anchored_position = (0, photo_tg.tile_height + message_lbl.height + 12)
main_group.append(amount_lbl)
# Cent label at bottom of display
cent_lbl = TextBox(
FONT,
128,
TextBox.DYNAMIC_HEIGHT,
align=TextBox.ALIGN_CENTER,
text="Cent",
color=0x000000,
scale=1,
)
cent_lbl.anchor_point = (0, 1)
cent_lbl.anchored_position = (0, display.height - 2)
main_group.append(cent_lbl)
# set main_group showing on the display
display.root_group = main_group
display.refresh()
# Sleep for 1 day
al = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 86400)
alarm.exit_and_deep_sleep_until_alarms(al)
# Does not return. Exits, and restarts after 1 day
Page last edited November 17, 2025
Text editor powered by tinymce.