Overview
A long time ago we made a Pi into a WiFi gateway that also blocked ads but the Pi Hole project does a way better job!
This project will make your Pi Zero W act as a DNS (Domain Name Server) The kind of device that tells you that adafruit.com is known as IP address 104.20.38.240.
Except Pi Hole DNS will do a special trick, when it is asked for the IP address of ads.adserver.com (for example) it will return nothing! So you will never even connect to the ad server and get the ad. Your connection will be faster, less data, and no intrusive ads. It works great on computers, tablets, phones, etc. Even if you cannot run an ad-blocker plugin on your phone or tablet, this will work and ad-blocker-detectors can't tell you're running it.
Unlike our WiFi gateway demo, you do not have to set up the Pi as your access point, you will only use it as a DNS ad blocker so it will not act as a bottleneck
We upgraded our Pi Zero Pi Hole with a little display, that makes setting up clients easy and also gives you some nifty stats!
Follow along with this guide to DIY your own
Page last edited February 04, 2026
Text editor powered by tinymce.
Project Parts
This project can be done with any Raspberry Pi, but for the most adorably compact version we're using a Pi Zero W - this has enough power to do what we want, and has built in WiFi too!
Pi Zero W base parts
Its easiest if you pick up a Pi Zero W budget pack as it contains most everything you need
But you can also just DIY with the minimum requirements:
If you are using a Pi Zero W you'll need to add 2x20 headers too
or
Using a Pi 3 Instead of Pi Zero W
Instead of a Pi Zero W you can directly substitute in a Pi 3 or 4 which also has built in WiFi, you won't need the 2x20 header in that case
Page last edited February 04, 2026
Text editor powered by tinymce.
Prepare the Pi
The Pi Zero W is a very minimal computer, so it requires a little work to get it up and running.
We have a guide on how to set up your Pi Zero W 'headless' which is how we recommend you get started. Check out the guide for how to do that!
Here's the quick-start for people with some experience:
- Download the Raspberry Pi Imager to your computer
- Install the 64-bit Lite Raspberry Pi to your micro SD card using the imager. you may need to go to Raspberry Pi OS (other) to find it.
- When it asks you about settings, be sure you have your network settings entered and SSH enabled under Services.
- Re-plug the SD card into your computer (don't use your Pi yet!) and set up your wifi connection by editing supplicant.conf
- Plug the SD card into the Pi Zero W
- If you have an HDMI monitor we recommend connecting it up via the mini HDMI adapter we provide in the budget pack - so you can see that it's booting OK
- Plug in power to the Pi Zero W - you will see the green LED flicker a little. The Pi Zero will reboot while it sets up so wait a good 10 minutes
- If you are running Windows on your computer, install Bonjour support so you can use .local names, you'll need to reboot Windows after installation
- You can then ssh into raspberrypi.local or whichever hostname you entered under settings.
Page last edited February 04, 2026
Text editor powered by tinymce.
Install Pi Hole
Use the following instructions to install Pi Hole:
https://github.com/pi-hole/pi-hole/#one-step-automated-install
Pre-Check
OK once you have set your Pi up and the WiFi is connecting to your home or office network, and you can ssh into it, continue with these easy steps! If you cannot connect via ssh yet, go back and read some of our guides until you are able to log into your Pi.
Change Hostname
I like to do this first so I don't get confused between all the different Pi's in the house.
The hostname can be set in the imager before you get started, but if you forgot or you'd like to change it afterwards, just edit the hostname with sudo nano /etc/hostname and put something else on that first line, like pi-hole
Also change it in the hosts file with sudo nano /etc/hosts to match the same name. It's probably the last line:
Reboot and when you ssh in again, use pi-hole.local
Now's also a good time to change the Pi's password with passwd
Run Pi Hole Installer
There's more information on how installation at https://pi-hole.net/ - as of the writing of this guide, its easier to just run:
curl -sSL https://install.pi-hole.net | bash
It will take quite a while to install, and may seem to 'hang' at points. Just let it do its thing for about 20 minutes!
The installer may complain about needing a requiring IP address, which is recommended. This is because DHCP reservations can expire. However, in practice, if the pi stays in communication with your DHCP server, it usually just keeps renewing the same IP address, which works well enough. if you have an advanced network set up, you can configure a custom IP address
If you have your pi has multiple network interfaces such as WiFi and an ethernet adapter, it will ask you which one you would like to use.
Pick who will be the upstream DNS (for non-ad blocked sites) - Google is fine and will probably be up all the time
The Web Interface is kinda cool, and is password protected. We'll be showing most of the stats on the little OLED but we still need the API to be running so install this.
You'll also need a web interface running, so select this as well unless you already have another one installed.
If you'd like to retain a log of the ads that are blocked, you can enable query logging. If you enable it, you'll also be asked about the amount of content that you'd like logged.
It will keep installing! Just hold tight...
When its done you'll get this final config screen! Copy & paste the password into another window for now
Test Admin Page
On your desktop computer or tablet, visit http://pi.hole/admin or if that doesn't work, try http://[your-ip]/admin (replacing [your-ip] with you IP address) and you should see an administration panel!
On your tablet, phone, computer, etc - Set up your DNS server in the network settings to be the IP address of the Pi
You may need to restart your network or browser to have it kick in, also there may be some cached ads so don't worry if not everything is blocked. Visit your favorite site with ads (not adafruit.com cuz we don't have any! :) and see the difference!
Now that you've got that done, lets continue and install the display!
Page last edited February 04, 2026
Text editor powered by tinymce.
Install PiOLED
Our little PiOLED add on makes a very cute and easy way to display the Pi Hole stats. We were inspired to add this when we saw this tweet!
What a perfect use! Here's how to add it on for some nice stats. It also displays the hostname and IP address so if you forget it you can just look at the display. It will also tick up when its in use so you can tell its working.
Install 2x20 Header
If you are using a Pi Zero you'll need to solder in or somehow attach a 2x20 header so you can plug in the Pi OLED. Use either a plain 2x20 header and solder it in using an iron + some solder...
Or you can use Hammer headers which do not need soldering!
Either way, you'll want to end up with something like this:
Setup Virtual Environment with Blinka
Starting with the Bookworm version of Raspberry Pi OS, you will need to install your python modules in a virtual environment. Even on earlier versions, it's still recommended to install a virtual environment. You can find more information in the Python Virtual Environment Usage on Raspberry Pi guide. To Install and activate the virtual environment, use the following commands:
sudo apt install python3-venv sudo apt-get install -y python3-pip python3 -m venv pihole --system-site-packages source pihole/bin/activate pip3 install --upgrade adafruit-python-shell wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/raspi-blinka.py sudo -E env PATH=$PATH python3 raspi-blinka.py
You will need to activate the virtual environment every time the Pi is rebooted. To activate it:
source pihole/bin/activate
To deactivate, you can use deactivate, but leave it active for now.
This guide assumes that you've gotten your Raspberry Pi up and running, have CircuitPython installed, and have installed CircuitPython libraries for the PiOLED.
source pihole/bin/activate pip3 install adafruit-circuitpython-ssd1306 sudo apt-get install python3-pil
A nice font really helps with this little OLED display, something other than the default PIL font. First thing I did is update the font so its a little clearer. I used Kottke's free Silkscreen font which looks great on small screens.
It's easy to install on your Pi, run
cd ~ wget http://kottke.org/plus/type/silkscreen/download/silkscreen.zip unzip silkscreen.zip
Update stats.py program
Here's the new stats.py code which uses the PiOLED.
Create a new file with nano ~pi/stats.py and paste this code below in! Then save it.
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
# SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries
# SPDX-FileCopyrightText: 2017 James DeVito for Adafruit Industries
# SPDX-FileCopyrightText: 2025 Mikey Sklar for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Copyright (c) 2017 Adafruit Industries
# Author: Ladyada, Tony DiCola & James DeVito
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# This example is for use on (Linux) computers that are using CPython with
# Adafruit Blinka to support CircuitPython libraries. CircuitPython does
# not support PIL/pillow (python imaging library)!
import subprocess
import time
import requests
from board import SCL, SDA
import busio
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont
api_url = "http://localhost/api/stats/summary"
# Create the I2C interface.
i2c = busio.I2C(SCL, SDA)
# Create the SSD1306 OLED class.
disp = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c)
# Leaving the OLED on for a long period of time can damage it
DISPLAY_ON = 10 # on time in seconds
DISPLAY_OFF = 50 # off time in seconds
# Clear display.
disp.fill(0)
disp.show()
# Create blank image for drawing.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))
# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, width, height), outline=0, fill=0)
padding = -2
top = padding
x = 0
# Load nice silkscreen font
font = ImageFont.truetype('/home/pi/slkscr.ttf', 8)
while True:
# Clear the image buffer
draw.rectangle((0, 0, width, height), outline=0, fill=0)
# Shell scripts for system monitoring
cmd = "hostname -I | cut -d' ' -f1 | tr -d '\\n'"
IP = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "hostname | tr -d '\\n'"
HOST = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "top -bn1 | grep load | awk " \
"'{printf \"CPU Load: %.2f\", $(NF-2)}'"
CPU = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "free -m | awk 'NR==2{printf " \
"\"Mem: %s/%sMB %.2f%%\", $3,$2,$3*100/$2 }'"
MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "df -h | awk '$NF==\"/\"{printf " \
"\"Disk: %d/%dGB %s\", $3,$2,$5}'"
Disk = subprocess.check_output(cmd, shell=True).decode("utf-8")
# Pi-Hole data!
try:
r = requests.get(api_url, timeout=2)
r.raise_for_status()
data = r.json()
DNSQUERIES = data["queries"]["total"]
ADSBLOCKED = data["queries"]["blocked"]
CLIENTS = data["clients"]["total"]
except (KeyError, requests.RequestException):
DNSQUERIES = 0
ADSBLOCKED = 0
CLIENTS = 0
draw.text((x, top), "IP: " + IP + " (" + HOST + ")", font=font, fill=255)
draw.text((x, top + 8), "Ads Blocked: " + str(ADSBLOCKED), font=font, fill=255)
draw.text((x, top + 16), "Clients: " + str(CLIENTS), font=font, fill=255)
draw.text((x, top + 24), "DNS Queries: " + str(DNSQUERIES), font=font, fill=255)
# Display image.
disp.image(image)
disp.show()
time.sleep(DISPLAY_ON)
# Blank screen to prevent burn-in
disp.fill(0)
disp.show()
time.sleep(DISPLAY_OFF)
You'll notice its very similar to the original stats.py but we've added PiHole API support. Here's how we did that!
First up, Pi Hole stats are available through the web server, in json format, so we need to add web requests and json parsing to python. Then set the URL for the API access, which is localhost (the same computer) and through the admin page:
import subprocess import json import requests api_url = 'http://localhost/api/stats/summary'
We load up the nice Silkscreen font here, in 8 point type. Note that we have to have the full path of the file.
# Load nice silkscreen font
font = ImageFont.truetype("/home/pi/slkscr.ttf", 8)
This is where we grab the API data. I put it in a try block, so it would retry in case the API access failed for some reason
# Pi Hole data!
try:
r = requests.get(api_url, timeout=2)
r.raise_for_status()
data = r.json()
DNSQUERIES = data["queries"]["total"]
ADSBLOCKED = data["queries"]["blocked"]
CLIENTS = data["clients"]["total"]
except (KeyError, requests.RequestException):
DNSQUERIES = 0
ADSBLOCKED = 0
CLIENTS = 0
If you want to print out different info, run this small script in python to see what is available:
import json import requests api_url = 'http://localhost/api/stats/summary' r = requests.get(api_url) data = json.loads(r.text) print(data)
You can also customize the display printout, but i liked having the IP first, then the pi hole stats below:
draw.text((x, top), "IP: " + str(IP) + "( " + HOST + ")", font=font, fill=255)
draw.text((x, top+8), "Ads Blocked: " + str(ADSBLOCKED), font=font, fill=255)
draw.text((x, top+16), "Clients: " + str(CLIENTS), font=font, fill=255)
draw.text((x, top+24), "DNS Queries: " + str(DNSQUERIES), font=font, fill=255)
This step is critical to being able to access the Pi Hole API. Press enter (leave blank) when prompted for API password.
sudo pihole setpassword
python3 ./stats.py
and look on the OLED to make sure you see your IP address and such!
Lastly we just want to make this run at boot. We'll do that the easy way by editing /etc/rc.local with sudo nano /etc/rc.local and adding sudo ~pi/pihole/bin/python3 ~pi/stats.py & before exit 0
Then save and you can reboot to test it out
Page last edited February 04, 2026
Text editor powered by tinymce.
Install Mini PiTFT
We've updated our popular PiOLED script for use with the Mini PiTFT, a 135x240 Color TFT add-on for your Raspberry Pi. This cute little display has two tactile buttons on GPIO pins that we'll use to make a simple user-interface display for your Pi-Hole.
Install 2x20 Header
If you are using a Pi Zero, you'll need to solder on or somehow attach a 2x20 header so you can plug in the Pi OLED. Use either a plain 2x20 header and solder it in using an iron + some solder...
Or you can use Hammer headers which do not need soldering.
Either way, you'll want to end up with something like this:
Setup Virtual Environment with Blinka
Starting with the Bookworm version of Raspberry Pi OS, you will need to install your python modules in a virtual environment. Even on earlier versions, it's still recommended to install a virtual environment. You can find more information in the Python Virtual Environment Usage on Raspberry Pi guide. To Install and activate the virtual environment, use the following commands:
sudo apt install python3-venv sudo apt-get install -y python3-pip python3 -m venv pihole --system-site-packages source pihole/bin/activate pip3 install --upgrade adafruit-python-shell wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/raspi-blinka.py sudo -E env PATH=$PATH python3 raspi-blinka.py
You will need to activate the virtual environment every time the Pi is rebooted. To activate it:
source pihole/bin/activate
To deactivate, you can use deactivate, but leave it active for now.
Python Setup
This guide assumes that you've gotten your Raspberry Pi up and running, have CircuitPython installed, and have installed CircuitPython libraries for the Mini PiTFT. If not, follow the steps in the guide below and come back to this page when you've completed them.
source pihole/bin/activate pip3 install adafruit-circuitpython-rgb-display sudo apt-get install fonts-dejavu python3-pil python3-numpy
Update the stats.py program
Here's the new stats.py code which uses the Mini PiTFT.
Create a new file using nano ~pi/stats.py and paste the code below in. Then, save the code.
# SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries
# SPDX-FileCopyrightText: 2025 Mikey Sklar for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Copyright (c) 2017 Adafruit Industries
# Author: Brent Rubell, Mikey Sklar
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# This example is for use on (Linux) computers that are using CPython with
# Adafruit Blinka to support CircuitPython libraries. CircuitPython does
# not support PIL/pillow (python imaging library)!
# -*- coding: utf-8 -*-
# Import Python System Libraries
import time
import json
import subprocess
# Import Requests Library
import requests
#Import Blinka
import digitalio
import board
# Import Python Imaging Library
from PIL import Image, ImageDraw, ImageFont
import adafruit_rgb_display.st7789 as st7789
API_URL = "http://localhost/api/stats/summary"
# Configuration for CS and DC pins (these are FeatherWing defaults on M0/M4):
cs_pin = digitalio.DigitalInOut(board.D17)
dc_pin = digitalio.DigitalInOut(board.D25)
reset_pin = None
# Config for display baudrate (default max is 24mhz):
BAUDRATE = 64000000
# Setup SPI bus using hardware SPI:
spi = board.SPI()
# Create the ST7789 display:
disp = st7789.ST7789(
spi,
dc_pin,
cs_pin,
reset_pin,
135,
240,
baudrate=BAUDRATE,
x_offset=53,
y_offset=40,
rotation=90
)
# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
CANVAS_WIDTH = disp.height
CANVAS_HEIGHT = disp.width
image = Image.new('RGB', (CANVAS_WIDTH, CANVAS_HEIGHT))
draw = ImageDraw.Draw(image)
# Load default font (or replace with a TTF if desired)
FONT_PATH = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
font = ImageFont.truetype(FONT_PATH, 24)
buttonA = digitalio.DigitalInOut(board.D23)
buttonA.switch_to_input()
while True:
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), outline=0, fill=(0, 0, 0))
# Shell scripts for system monitoring from here:
# https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load
cmd = "hostname -I | cut -d' ' -f1"
IP = "IP: " + subprocess.check_output(cmd, shell=True).decode("utf-8").strip()
cmd = "hostname | tr -d '\\n'"
HOST = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'"
CPU = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%s MB %.2f%%\", $3,$2,$3*100/$2 }'"
MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%d GB %s\", $3,$2,$5}'"
Disk = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = (
"cat /sys/class/thermal/thermal_zone0/temp | "
"awk '{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}'"
)
Temp = subprocess.check_output(cmd, shell=True).decode("utf-8")
# Pi Hole data!
try:
r = requests.get(API_URL, timeout=5)
r.raise_for_status()
data = r.json()
DNSQUERIES = data["queries"]["total"]
ADSBLOCKED = data["queries"]["blocked"]
CLIENTS = data["clients"]["total"]
except (KeyError, requests.RequestException, json.JSONDecodeError):
DNSQUERIES = None
ADSBLOCKED = None
CLIENTS = None
y = top = 5
x = 5
if not buttonA.value: # just button A pressed
draw.text((x, y), IP, font=font, fill="#FFFF00")
y += font.getbbox(IP)[3]
draw.text((x, y), CPU, font=font, fill="#FFFF00")
y += font.getbbox(CPU)[3]
draw.text((x, y), MemUsage, font=font, fill="#00FF00")
y += font.getbbox(MemUsage)[3]
draw.text((x, y), Disk, font=font, fill="#0000FF")
y += font.getbbox(Disk)[3]
draw.text((x, y), Temp, font=font, fill="#FF00FF")
y += font.getbbox(Temp)[3]
else:
draw.text((x, y), IP, font=font, fill="#FFFF00")
y += font.getbbox(IP)[3]
draw.text((x, y), HOST, font=font, fill="#FFFF00")
y += font.getbbox(HOST)[3]
if ADSBLOCKED is not None:
txt = f"Ads Blocked: {ADSBLOCKED}"
draw.text((x, y), txt, font=font, fill="#00FF00")
y += font.getbbox(txt)[3]
if CLIENTS is not None:
txt = f"Clients: {CLIENTS}"
draw.text((x, y), txt, font=font, fill="#0000FF")
y += font.getbbox(txt)[3]
if DNSQUERIES is not None:
txt = f"DNS Queries: {DNSQUERIES}"
draw.text((x, y), txt, font=font, fill="#FF00FF")
y += font.getbbox(txt)[3]
# Display image.
disp.image(image)
time.sleep(.1)
You'll notice it's very similar to the original stats.py, but we've added PiHole API support. Here's how we did that:
First up, Pi Hole stats are available through the web server, in JSON format, so we need to add web requests and JSON parsing to Python. Then set the URL for the API access, which is localhost (the same computer) and through the admin page:
# Import Python System Libraries import time import json import subprocess # Import Requests Library import requests api_url = 'http://localhost/api/stats/summary'
We load up the nice DejaVuSans font here. Note that we have to have the full path of the file.
# Alternatively load a TTF font. Make sure the .ttf font file is in the # same directory as the python script! # Some other nice fonts to try: http://www.dafont.com/bitmap.php FONT_PATH = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" font = ImageFont.truetype(FONT_PATH, 20)
This is where we grab the API data. I put it in a try block, so it would retry in case the API access failed for some reason
# Pi Hole data!
try:
r = requests.get(api_url)
data = json.loads(r.text)
DNSQUERIES = data['dns_queries_today']
ADSBLOCKED = data['ads_blocked_today']
CLIENTS = data['unique_clients']
except KeyError:
time.sleep(1)
continue
If you want to print out different info, run this small script in python to see what is available:
import json import requests api_url = 'http://localhost/api/stats/summary' r = requests.get(api_url) data = json.loads(r.text) print(data)
Since the MiniTFT has two tactile push-buttons, we modified the script to print out extra information from the Raspberry Pi when you press the top button.
if not buttonA.value: # just button A pressed
draw.text((x, y), IP, font=font, fill="#FFFF00")
y += font.getsize(IP)[1]
draw.text((x, y), CPU, font=font, fill="#FFFF00")
y += font.getsize(CPU)[1]
draw.text((x, y), MemUsage, font=font, fill="#00FF00")
y += font.getsize(MemUsage)[1]
draw.text((x, y), Disk, font=font, fill="#0000FF")
y += font.getsize(Disk)[1]
draw.text((x, y), "DNS Queries: {}".format(DNSQUERIES), font=font, fill="#FF00FF")
else:
draw.text((x, y), IP, font=font, fill="#FFFF00")
y += font.getsize(IP)[1]
draw.text((x, y), HOST, font=font, fill="#FFFF00")
y += font.getsize(HOST)[1]
draw.text((x, y), "Ads Blocked: {}".format(str(ADSBLOCKED)), font=font, fill="#00FF00")
y += font.getsize(str(ADSBLOCKED))[1]
draw.text((x, y), "Clients: {}".format(str(CLIENTS)), font=font, fill="#0000FF")
y += font.getsize(str(CLIENTS))[1]
draw.text((x, y), "DNS Queries: {}".format(str(DNSQUERIES)), font=font, fill="#FF00FF")
y += font.getsize(str(DNSQUERIES))[1]
This step is critical to being able to access the Pi Hole API. Press enter (leave blank) when prompted for API password.
sudo pihole setpassword
Once you have the script saved, you can run it with
python3 stats.py
Look on the Mini PiTFT to make sure you see your IP address along with some statistics from Pi-Hole.
Pushing the top button should display extra statistics about the Pi such as its hostname, CPU load, memory utilization, disk usage, and DNS queries.
Page last edited February 04, 2026
Text editor powered by tinymce.