Users Online
With the PyPortal running CircuitPython code, we can have the PyPortal display the current number of users online on the Adafruit Discord server.
The PyPortal will do the following:
- Display a custom background .bmp image
- Determine the current number of users online on the Discord server
- Display the number on top of the background image
Install 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_Discord.
Copy the contents of the PyPortal_Discord directory to your PyPortal's CIRCUITPY drive.
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 """ This example will access shields.io API, grab the SVG graphic and then use regular expression search to locate the number of online discord users, then display it on a screen. If you can find something that spits out text, we can display it! """ import time import board from adafruit_pyportal import PyPortal from adafruit_portalbase.network import CONTENT_TEXT # Set up where we'll be fetching data from DATA_SOURCE = "https://img.shields.io/discord/327254708534116352.svg" # a regular expression for finding the data within the SVG xml text! DATA_LOCATION = [r">([0-9]+ online)<"] cwd = ("/"+__file__).rsplit('/', 1)[0] pyportal = PyPortal(url=DATA_SOURCE, regexp_path=DATA_LOCATION, status_neopixel=board.NEOPIXEL, default_bg=cwd+"/discord_background.bmp", text_font=cwd+"/fonts/Collegiate-50.bdf", text_position=(70, 216), text_color=0x000000) while True: try: value = pyportal.fetch(force_content_type=CONTENT_TEXT) print("Response is", value) except RuntimeError as e: print("Some error occured, retrying! -", e) time.sleep(60)
How it Works
The PyPortal Discord display does the following things:
Background
First, it displays a bitmap graphic named discord_background.bmp as the screen's background. This is a 320 x 240 pixel RGB 16-bit raster graphic in .bmp format.
Font
To display information on the screen, the PyPortal code will use a bitmapped font overlayed on top of the background. The font used here is are bitmap fonts made from the Collegiate typeface. You can learn more about converting type in this guide.
In order to speed up the display of text, the pyportal.preload_font()
command is used to place the needed glyphs into memory.
Shield.io, .SVGs, and RegEx
Here's the really snazzy part: we want the PyPortal to grab the number of current users online on the Adafruit Discord server (or whichever server you specify in the code). Well, where do we get this number? Most PyPortal projects use a REST API call to grab a JSON file, but that isn't available for Discord servers. Shields.io comes to the rescue!
Shields.io is a service that can provide the data we need in convenient little graphics. The shields are often used on websites to indicate status and data on services such as GitHub repos. Here, we'll use it to get the user count we need!
If we enter this url in a web browser, we'll get a shield for the online user count for the Discord server: https://img.shields.io/discord/327254708534116352.svg
If we download the .svg file itself, this is what it looks like:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="108" height="20"> <linearGradient id="b" x2="0" y2="100%"> <stop offset="0" stop-color="#bbb" stop-opacity=".1"></stop> <stop offset="1" stop-opacity=".1"></stop> </linearGradient> <clipPath id="a"> <rect width="108" height="20" rx="3" fill="#fff"></rect> </clipPath> <g clip-path="url(#a)"> <path fill="#555" d="M0 0h33v20H0z"></path> <path fill="#4c1" d="M33 0h75v20H33z"></path> <path fill="url(#b)" d="M0 0h108v20H0z"></path> </g> <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="175" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="230">chat</text> <text x="175" y="140" transform="scale(.1)" textLength="230">chat</text> <text x="695" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="650">1163 online</text> <text x="695" y="140" transform="scale(.1)" textLength="650">1169 online</text> </g> </svg>
Much of this code is used to draw the graphic we saw above, however, it also contains this magical tidbit:
< text x="695" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="650">1169 online</text >
Notice at the end there is says 1169 online
, which is just the data we're looking for.
In order to grab that string for display on the PyPortal, our code needs to be able to find it. Unlike a JSON file, there isn't a key:value pair in the .svg file, so instead we can hunt for it with a regular expression.
A regular expression is a special sequence of characters that is used to search for a particular pattern in a string. In this case, r">([0-9]+ online)<"
is the expression we'll use in CircuitPython to find what we need.
What we're searching for is an occurrence of a number and the world "online" to appear together. The [0-9]
section of the expression says "look for any number" while the + online
section says "and that number must be next to a space and the word 'online'".
Thus, it is the expression "1169 online" that will be displayed on the screen.
Data Source and Location
Our CircuitPython code uses two variables to specify the url for the .svg file and regular expression to run in order to get the data we want.
DATA_SOURCE = "https://img.shields.io/discord/327254708534116352.svg"
DATA_LOCATION = [r">([0-9]+ online)<"]
PyPortal Constructor
When we set up the pyportal
constructor, we are providing it with these things:
-
url
to query -
regex_path
the regular expression to run in order to locate the data -
default_bg
path and name to display the background bitmap -
text_font
path and name to the font used for displaying the follower count value -
text_position
on the screen's x/y coordinate system -
text_color
sets, well, the text color!
Fetch
With the PyPortal set up, we can then use pyportal.fetch()
to do the query and parsing of the data and then display it on screen on top of the background image.
This repeats once every minute to stay current!
Customization
You can customize this project to make it your own and point to different Discord server, as well as adjust the graphics and text.
To find the channel ID number of a given Discord server, you'll need to turn on Developer mode in the advanced appearance settings of your user settings. Then, right-click on any server and click Copy ID.
Text Position
Depending on the design of your background bitmap and the length of the text you're displaying, you may want to reposition the text and caption. You can do this with the text_position
and caption_position
options.
The PyPortal's display is 320 pixels wide and 240 pixels high. In order to refer to those positions on the screen, we use an x/y coordinate system, where x is horizontal and y is vertical.
The origin of this coordinate system is the upper left corner. This means that a pixel placed at the upper left corner would be (0,0) and the lower right corner would be (320, 240).
Text Color
Another way to customize your display is to adjust the color of the text. The line text_color=0x000000
in the constructor shows how. You will need to use the hexadecimal value for any color you want to display.
You can use something like https://htmlcolorcodes.com/ to pick your color and then copy the hex value, in this example it would be 0x0ED9EE
Background Image
If you would like to create your own background, awesome! You'll want to save the file with these specifications:
- 320 x 240 pixels
- 16-bit RGB color (8-bits per channel)
- Save file as .bmp format
You can then copy the .bmp file to the root level of the CIRCUITPY drive. Make sure you refer to this new filename in the pyportal constructor line:
default_bg=cwd+"/discord_background.bmp"
Change that line to use the new filename name, such as:
default_bg=cwd+"/my_new_background.bmp"
Page last edited January 20, 2025
Text editor powered by tinymce.