Pi-Hole OLED Status Screen


This post shows how I added an OLED status screen to my Raspberry Pi based Pi-Hole system.

Pi-Hole is a network wide advert blocker that you can run on a Raspberry Pi. It runs well on all Pi models and is an ideal project for older hardware. You can read more about it on the official Pi-Hole site.

Having setup Pi-Hole on a Raspberry Pi B+ I wanted a way to add a display so I could quickly check it was working. So I added a cheap 0.96″ OLED display and a momentary switch. The switch allows the display to show additional detail as required. The LED is there to add a nice coloured glow to the box but of course this optional.

Pi-Hole with OLED Screen

OLED Status Screen Hardware Setup

The hardware requirements are fairly basic.

  • Raspberry Pi (any model but ideally you want an Ethernet port)
  • SD card (8GB or larger recommended but 4GB will work)
  • Power supply
  • Network connection
    • Ethernet cable
    • or on-board WiFi
    • or USB WiFi dongle
  • Monitor (optional)
  • Keyboard (optional)
  • Pi case (optional)

The status screen and switch upgrade require :

  • OLED screen with I2C interface [eBay] [AliExpress]
  • Momentary switch [eBay] [AliExpress]
  • Jumper cables to connect screen to the GPIO header
  • LED and resistor (optional)
  • Bezel or mount for screen (optional)

Pi-Hole Installation & Setup

Installing Pi-Hole is fairly straightforward. I don’t really want want to replicate the procedure here. Instead I will provide a summary of what I did with appropriate links to additional guides. Depending on your preference you can either use SSH or a keyboard and monitor to perform the setup.

  • Create a fresh Raspbian SD card with “Raspbian Lite” image and Etcher [guide]
  • Add empty text file named “ssh” to boot partition to enable SSH [guide]
  • Power-up Pi with new SD card
  • Change the default password to something sensible [guide]
  • Run “sudo apt update” followed by “sudo apt upgrade”
  • Get IP address of Pi by either :
    • running ifconfig on the Pi
    • looking in your router settings
    • using a network scanner
  • (optional) configure your router to always assign the same IP address to your Raspberry Pi
  • Follow one of the install procedures described on the Pi-Hole GitHub page. I used the “One-Step Automated Install”

Screen and Switch Wiring

The switch was wired directly to the GPIO header using jumper wires and then stuck onto the lid with two part epoxy glue.

Pi-Hole status screen internal wiring

The screen, switch and LED were wired up as shown below :

The screen was powered from a 3.3V pin and the LED was powered from a GPIO pin.

OLED Screen Setup

OLED screens using the I2C protocol are fairly easy to hook up as you can use jumper cables to connect directly to the GPIO header. The procedure for adding one to your Pi and getting the libraries installed can be found in my How to use an OLED display module tutorial.

Make sure you can run the example Python scripts and your screen is working.

Switch Hardware

The switch can be connected directly to the GPIO header. I connected it across GPIO21 (Pin 40) and Ground (Pin 39). The script defines GPIO21 as “High” and it will be pulled “Low” when the switch is pressed.

gpiozero Installation

The Python script will use gpiozero to control the LED and button. Ensure it is installed by running :

sudo apt install python3-gpiozero

Python Script Installation

The Python script uses the Pi-Hole API. This allows the script to request the current data from Pi-Hole and then Python can format it to display on the screen.

Download the Python script using :

wget https://bitbucket.org/MattHawkinsUK/rpispy-misc/raw/master/pihole/stats.py

and get the required font file :

wget https://bitbucket.org/MattHawkinsUK/rpispy-misc/raw/master/pihole/VCR_OSD_MONO_1.001.ttf

Then get the Pi to run it on startup by editing cron using :

crontab -e

and adding the following line to the end :

@reboot python3 /home/pi/stats.py &

You cron file will look like this :

# For more information see the manual pages of crontab(5) and cron(8)
# m h  dom mon dow   command
@reboot python3 /home/pi/stats.py &

Save and exit using CTRL-X, Y then ENTER.

Reboot the Pi and the script should display Pi-Hole data on the screen after 30 seconds. This delay gives the Pi time to connect to the network before it tries asking Pi-Hole for data. Pressing the button will scroll an additional set of data onto the screen.

OLED Screen Bezel

The black screen bezel was 3D printed to enclose the OLED screen and it’s PCB. Due to variations in OLED module the bezel allows a bit of adjustment. The screen had to be positioned by hand and then glued into position. The bezel was attached to the case using double sided tape.

The STL file is available from my Thingiverse channel.

Here is a basic animation of the 3D printed bezel :

Pi-Hole Status Screen in Action

The three available status screens can be seen below :

Pi-Hole status screen examples

Every 30 seconds the main screen scrolls and refreshes the data. This gives a good indication that the script is still running without needing to check anything else.

  • Screen 1 (Main)
    • Total blocked percentage
    • Total blocked count
  • Screen 2
    • IP address
    • Blocked percentage
    • Total blocked count
    • Total number of queries
  • Screen 3
    • IP address
    • CPU utilisation
    • Memory in use/Total memory
    • Disk space used/Total disk space

The case I’m using is a “DesignSpark black” case from RS Components.

Thoughts on Ad-blocking

I decided to try Pi-Hole because as a parent I needed to be able to demonstrate I was in complete control of my network and my household internet rules were the law. This was generally aimed at ensuring the safety of children using my WiFi rather than spying on the adults.

This was especially true when my son’s friends were bringing internet enabled devices into my house. I didn’t want to deny them the ability to use those devices but I had no control over how they were configured. Parental controls or fancy apps may help your own kids but you can’t impose them on visitors.

Ad-blocking is a controversial topic especially amongst those that rely on adverts to generate an income stream. This is true for this site where advertising helps offset the hosting costs. I’ve always been in two minds as to whether I should encourage others to run a system that will strip the ads from this site and the sites of other content generators. I understand why people want to do it but I also feel on this site adverts are a tiny price to pay for content that is generally helpful to people.

But the choice is ultimately yours.



  1. Is it hard to add a sleep mode to this?
    Where an additional button could be used to wake the screen for a predetermined time, and the first button will scroll as designed.

    Love the great tutorials you put out.

  2. Hi there. Is it possible to do this stuff with an older Raspi? I tried it with your tutorial but the display stays black.


    • Pi-Hole runs fine on older models. I use a B+. I recently updated the tutorial so may be worth retrying.

  3. Does not work for me. Did a clean install of “Lite” on my Pi 2. I followed your guides to I2C displays – All ok. I can see animations, images … with the python3 commands. PiHole installed and working, webinterface and all. The thing is, after rebooting the OLED does not reset. I still have the last image presented from the testscripts and it just sits there like the display is not properly initialized or reset. Your script says (rst=None) in the display section? Changing it to rst=RST did not help. My OLED is at 0x3C, and as I said all else works just as it should. Any ideas?

    • I’ve just updated the instructions to include the installation of gpiozero which isn’t included by default in Raspbian Lite.
      sudo apt install python3-gpiozero

  4. The gpiozero module is not default installed on a Raspbian LITE image. I ran the script manually and it returned a ModuleNotFound error. Running sudo apt install python3-gpiozero solved the problem. Nice work. Have mine running on a Pi 2 that was collecting dust otherwise.

  5. When it says 15.2% of the queries have been blocked does that mean the remaining 84.8% of queries have resulted in ads coming through? I assume that’s based on your black/whitelists?
    If that makes sense?

    • I think it means the other 84.8% weren’t considered ads so were allowed to pass. So normal requests to allowed domains for all the other stuff being requested on your network webpages, images, kitten videos, Facebook posts etc.

  6. Thanks for putting this together, one thing that I needed to do was after downloading the font copy it to /usr/share/fonts/truetype
    and run fc font-cache

    If I have the font in the same directory as the stats.py script it does not seem to matter – but running this from a different directory or using the crontab seemed to need to have the font installed.

Leave A Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.