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...

Break-away 0.1" 2x20-pin Strip Dual Male Header

PRODUCT ID: 2822
If we could eat headers, we'd have them for breakfast, lunch, and dinner.  But we can't :( So we're making the best of it and selling them! This 2x20-pin...
$0.95
IN STOCK

Or you can use Hammer headers which do not need soldering!

GPIO Hammer Headers - Solderless Raspberry Pi Connectors

PRODUCT ID: 3413
If your soldering isn't quite up to scratch, or you just don't own a soldering iron yet, then these nifty hammer headers  from Pimoroni might be just what you need. They come...
$6.50
IN STOCK

Either way, you'll want to end up with something like this:

Add Pi OLED support

While still logged into the Pi Hole Pi Zero W, and in the terminal shell, you can install support for the PiOLED. Follow our step by step guide to add I2C support and install the Python library

After you get to the part where you run sudo python stats.py and get this screen, come back here!

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

Here's the new stats.py code.

Create a new file with nano ~pi/stats.py and paste this in! Then save it

# 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.
import time

import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

import subprocess
import json
import requests

api_url = 'http://localhost/admin/api.php'

# Raspberry Pi pin configuration:
RST = None     # on the PiOLED this pin isnt used

# 128x32 display with hardware I2C:
disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST)

# Initialize library.
disp.begin()

# Clear display.
disp.clear()
disp.display()

# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
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)

# Draw some shapes.
# First define some constants to allow easy resizing of shapes.
padding = -2
top = padding
bottom = height-padding
# Move left to right keeping track of the current x position for drawing shapes.
x = 0

# Load nice silkscreen font
font = ImageFont.truetype("/home/pi/slkscr.ttf", 8)

while True:
    # Draw a black filled box to clear the image.
    draw.rectangle((0,0,width,height), outline=0, fill=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 = subprocess.check_output(cmd, shell = True )
    cmd = "hostname"
    HOST = subprocess.check_output(cmd, shell = True )
    cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'"
    CPU = subprocess.check_output(cmd, shell = True )
    cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%sMB %.2f%%\", $3,$2,$3*100/$2 }'"
    MemUsage = subprocess.check_output(cmd, shell = True )
    cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%dGB %s\", $3,$2,$5}'"
    Disk = subprocess.check_output(cmd, shell = True )

    # 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:
      time.sleep(1)
      continue

    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)

    # skip over original stats
    #draw.text((x, top+8),     str(CPU), font=font, fill=255)
    #draw.text((x, top+16),    str(MemUsage),  font=font, fill=255)
    #draw.text((x, top+25),    str(Disk),  font=font, fill=255)

    # Display image.
    disp.image(image)
    disp.display()
    time.sleep(.1)

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/admin/api.php'

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)
      data = json.loads(r.text)
      DNSQUERIES = data['dns_queries_today']
      ADSBLOCKED = data['ads_blocked_today']
      CLIENTS = data['unique_clients']
    except:
      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/admin/api.php'
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)

Test & Stats at Startup

Once you have the script saved, you can run it with sudo python ~pi/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 python ~pi/stats.py & before exit 0

Then save and you can reboot to test it out

Last updated on 2017-08-15 at 11.48.35 PM Published on 2017-08-15 at 11.48.38 PM