Building your own Raspberry Pi Zero with tiny OLED Display
Final Project Result
The project makes of Pi Zero W, OLED display, Python 3 and few JSON call to build your own Digital Display Device. You can extend our Sample Base class to customise your own Screen for different purpose.
System Information
Current Market Gold Price
This is all you need to buy before start
1) Raspberry Pi Zero with Pin and at least 8 G Mirco SD Card
2) OLED Display 1.3 inch with keys
3) A very cool and nice Cable (Optional)
The way that it connects to my Pi Zero looks great and relatively easy to place on my Desk
Similar product in Amazon but not exactly the same
Link to Product4) A plastic Case (Optional)
After that Install the screen and become the following
Setup required Library
1) Install your Own Raspbian OS into your Mirco SD Card
2) Login your Raspbian and upgrade every once if necessary
sudo apt-get dist-upgrade
or
sudo apt-get update
3) Install Library for your OLED
sudo apt-get install python-dev python-pip libfreetype6-dev libjpeg-dev
sudo -H pip install --upgrade pip
sudo apt-get purge python-pip
sudo -H pip install --upgrade luma.oled
sudo apt-get purge python-pip
sudo -H pip install --upgrade luma.oled
## Since we will be using python3, we also need to run the following
sudo python3 -m pip install --upgrade luma.oled
## I am using mscorefonts to display in the OLED, so you also need to install
sudo apt-get install ttf-mscorefonts-installer
4) Now install Adsense library to login your account and get the summary
sudo pip install --upgrade google-api-python-client
sudo pip install --upgrade oauth2client
sudo python -m pip install pytz
###Useful Link to setup Adsense account
###sample code to get account detail and tutorial to generate your Access Key
Try to spend time to verify your access and setup the Key
Create your Main Python class and Connect to OLED
##Create main.py and declare import library
import RPi.GPIO as GPIO
import _thread
# init GPIO
GPIO.setmode(GPIO.BCM)
KEY1_PIN = 21
KEY2_PIN = 20
KEY3_PIN = 16
KEY_UP_PIN = 6
KEY_DOWN_PIN = 19
KEY_LEFT_PIN = 5
KEY_RIGHT_PIN = 26
KEY_PRESS_PIN = 13
GPIO.setup(KEY1_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up
GPIO.setup(KEY2_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up
GPIO.setup(KEY3_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up
GPIO.setup(KEY_UP_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up
GPIO.setup(KEY_DOWN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up
GPIO.setup(KEY_LEFT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up
GPIO.setup(KEY_RIGHT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up
GPIO.setup(KEY_PRESS_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up
## Define Reboot / Shutdown Call
def rebootSystem():
command = "/usr/bin/sudo /sbin/shutdown -r now"
import subprocess
process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
output = process.communicate()[0]
print(output)
def shutdownSystem():
command = "/usr/bin/sudo /sbin/shutdown now"
import subprocess
process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
output = process.communicate()[0]
print(output)
##Define Key Event
def run_job_key(threadName, delay):
pressed = 0
print(threadName + " will be started")
while True:
## define key 3 as reboot/shutdown key
if GPIO.input(KEY3_PIN): # button is released
pressed = pressed + 1
else: # button is pressed:
print("Key3 pressed")
for i in range(30):
if GPIO.input(KEY3_PIN) == False:
sleep(0.1)
if (i >= 25):
shutdownSystem()
break
else:
rebootSystem()
break
break
##Define Main and register Key
def main():
cmd = "date '+%Y-%m-%d %H:%M:%S'"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
date_time = ps.communicate()[0].decode('utf-8').strip()
print("Calling main " + str(date_time))
try:
_thread.start_new_thread(run_job_key, ("run_job_key", 0,))
except:
print("Error: unable to start thread")
## Create Device object for display
width = 128
height = 64
image = Image.new('1', (width, height))
# 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
line = 8
RST = 25
CS = 8
DC = 24
USER_I2C = 0
if USER_I2C == 1:
GPIO.setmode(GPIO.BCM)
GPIO.setup(RST, GPIO.OUT)
GPIO.output(RST, GPIO.HIGH)
serial = i2c(port=1, address=0x3c)
else:
serial = spi(device=0, port=0, bus_speed_hz=8000000, transfer_size=4096, gpio_DC=24, gpio_RST=25)
device = sh1106(serial, rotate=2) # sh1106
## Load Prepared Screen class
loadingScreen = TextScreen("Loading...")
screenList = [SystemScreen(),
loadingScreen, TimeScreen()
]
## Loop all screen to display result
try:
##Each screen may init a single thread to download required data or calling a external library
for i in range(len(screenList)):
screenList[i].startThread()
while True:
device.clear()
for i in range(len(screenList)):
for count in range(screenList[i].getSleepCount()):
with canvas(device) as draw:
screenList[i].draw(x, top, width, height, line, draw, device, 255)
sleep(screenList[i].getSleepTime())
screenList[i].next()
except:
print("except")
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
print("*** print_tb:")
traceback.print_tb(exceptionTraceback, limit=1, file=sys.stdout)
print("*** print_exception:")
traceback.print_exception(exceptionType, exceptionValue, exceptionTraceback,
limit=2, file=sys.stdout)
print("*** print_exc:")
traceback.print_exc()
print("*** format_exc, first and last line:")
formatted_lines = traceback.format_exc().splitlines()
print(formatted_lines[0])
print(formatted_lines[-1])
print("*** format_exception:")
print(repr(traceback.format_exception(exceptionType, exceptionValue,
exceptionTraceback)))
print("*** extract_tb:")
print(repr(traceback.extract_tb(exceptionTraceback)))
print("*** format_tb:")
print(repr(traceback.format_tb(exceptionTraceback)))
# print("*** tb_lineno:", traceback.tb_lineno(exceptionTraceback))
GPIO.cleanup()
###End Main
if __name__ == "__main__":
main()
Create Screen Base Class
from PIL import ImageFont
class ScreenBase:
def __init__(self):
self.x = 0
self.top = 0
self.font = ImageFont.load_default()
self.fontbig = ImageFont.truetype("/usr/share/fonts/truetype/msttcorefonts/arial.ttf", 18)
self.fontsuperbig = ImageFont.truetype("/usr/share/fonts/truetype/msttcorefonts/arial.ttf", 25)
self.temp = ""
self.temp_new = ""
def draw(self, x, top, width, height, line, panel, device, fill):
raise NotImplementedError("Should have implemented this")
def getSleepTime(self):
raise NotImplementedError("Should have implemented this")
def getSleepCount(self):
raise NotImplementedError("Should have implemented this")
def startThread(self):
pass
def next(self):
pass
Create System Screen
from ScreenBase import *
import subprocess
class SystemScreen(ScreenBase):
def draw(self, x, top, width, height, line, panel, device, fill):
##panel.rectangle(device.bounding_box, outline="white", fill="black")
cmd = "hostname -I"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
IP = ps.communicate()[0].decode('utf-8').strip()
cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
CPU = ps.communicate()[0].decode('utf-8').strip()
cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%sMB %.2f%%\", $3,$2,$3*100/$2 }'"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
MemUsage = ps.communicate()[0].decode('utf-8').strip()
cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%dGB %s\", $3,$2,$5}'"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
Disk = ps.communicate()[0].decode('utf-8').strip()
cmd = """/sbin/ifconfig wlan0 | grep "RX packets" | awk '{print $6" " $7}' | sed 's/[()]//g'"""
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
network_rx = ps.communicate()[0].decode('utf-8').strip()
# print("network_rx "+network_rx )
cmd = """/sbin/ifconfig wlan0 | grep "TX packets" | awk '{print $6" " $7}' | sed 's/[()]//g'"""
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
network_tx = ps.communicate()[0].decode('utf-8').strip()
# print("network_tx " + network_tx)
cmd = "date '+%Y-%m-%d %H:%M:%S'"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
date_time = ps.communicate()[0].decode('utf-8').strip()
cmd = "vcgencmd measure_temp | sed 's/temp=//g'"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
temp = ps.communicate()[0].decode('utf-8').strip()
cmd = "vcgencmd measure_volts | sed 's/volt=//g'"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
volts = ps.communicate()[0].decode('utf-8').strip()
panel.text((x, top), str(IP), font=self.font, fill=fill)
panel.text((x, top + line), str(CPU), font=self.font, fill=fill)
panel.text((x, top + (line * 2)), str(MemUsage), font=self.font, fill=fill)
panel.text((x, top + (line * 3)), str(Disk), font=self.font, fill=fill)
panel.text((x, top + (line * 4)), str(network_rx) + "/" + str(network_tx), font=self.font, fill=fill)
panel.text((x, top + (line * 5)), str(temp) + "/" + str(volts), font=self.font, fill=fill)
panel.text((x, top + (line * 6)), str(date_time), font=self.font, fill=fill)
def getSleepTime(self):
return 1
def getSleepCount(self):
return 5
def startThread(self):
print("Not implemented")
Create Current Time Screen
from ScreenBase import *
import subprocess
class TimeScreen(ScreenBase):
def draw(self, x, top, width, height, line, panel, device, fill):
cmd = "date '+%Y-%m-%d %H:%M:%S'"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
date_time = ps.communicate()[0].decode('utf-8').strip()
datesize = panel.textsize(str(date_time).split(" ")[0]);
# draw.font = fontsuperbig
timesize = panel.textsize(str(date_time).split(" ")[1]);
panel.text((x + (width / 2) - timesize[0], top + (height / 2) - timesize[1]),
str(date_time).split(" ")[1], font=self.fontsuperbig, fill=fill)
panel.text((x + (width / 2) - timesize[0] + 2, top + (height / 2) + timesize[1] + 5),
str(date_time).split(" ")[0], font=self.fontbig, fill=fill)
def getSleepTime(self):
return 1
def getSleepCount(self):
return 5
def startThread(self):
print("Not implemented")
Test your main now
Remote login to your Pi Zero now and run the command belowpython3 main.py
Your should see the following screen which indicate your python call is connected to OLED display successfully
How to show your Admob Summary in the OLED Display?
First of all download the sample project and test your setting in the samplehttps://github.com/googleads/googleads-adsense-examples/tree/master/python/v1.4
Once you have tested and generated your own Account ID, try to access your Admob Account
Create your own python class to access the summary of today
##Create file and name it as admob_today.pyYou may need to change the timezone from Hong Kong to other/system default
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | import datetime from pytz import timezone import argparse import sys from adsense_util import get_account_id from adsense_util_data_collator import DataCollator from apiclient import sample_tools from oauth2client import client # Declare command-line flags. argparser = argparse.ArgumentParser(add_help=False) argparser.add_argument( '--report_id', help='The ID of the saved report to generate') def main(argv): # Authenticate and construct service. service, flags = sample_tools.init( argv, 'adsense', 'v1.4', __doc__, __file__, parents=[argparser], scope='https://www.googleapis.com/auth/adsense.readonly') # Process flags and read their values. saved_report_id = flags.report_id now_pacific = datetime.datetime.now(timezone('Asia/Hong_Kong')) + datetime.timedelta(days=1) today_pacific = now_pacific.strftime('%Y-%m-%d') yesterday_pacific = datetime.datetime.now(timezone('Asia/Hong_Kong')) yesterday_pacific = yesterday_pacific.strftime('%Y-%m-%d') ##print (" today_pacific " , today_pacific) ##print ("yesterday_pacific " , yesterday_pacific) try: # Let the user pick account if more than one. account_id = get_account_id(service) # Retrieve report if saved_report_id: result = service.accounts().reports().saved().generate( accountId=account_id, savedReportId=saved_report_id).execute() else: result = service.accounts().reports().generate( accountId=account_id, startDate=yesterday_pacific, endDate=today_pacific, metric=['PAGE_VIEWS', 'CLICKS', 'COST_PER_CLICK', 'EARNINGS'], dimension=['MONTH'], sort=['+MONTH'], useTimezoneReporting=True).execute() result = DataCollator([result]).collate_data() # Display headers. for header in result['headers']: print ('%25s' % header['name']), ##print print # Display results. for row in result['rows']: for column in row: print ('%25s' % column), print # Display date range. ##print 'Report from %s to %s.' % (result['startDate'], result['endDate']) ##print except client.AccessTokenRefreshError: print ('The credentials have been revoked or expired, please re-run the ' 'application to re-authorize') if __name__ == '__main__': main(sys.argv) |
##Test it by python admob_today.py
Create a new Screen to display your summary
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | from ScreenBase import * import subprocess class AdmobTodayScreen(ScreenBase): def __init__(self): self.count = 0 super(AdmobTodayScreen, self).__init__() def draw(self, x, top, width, height, line, panel, device, fill): try: cmd = "python admob_today.py | grep -v MONTH | awk '{ print $1\"=\"$2\"=\"$3\"=\"$4\"=\"$5}'" ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) result = ps.communicate()[0].decode('utf-8').strip() if(len(result) <= 0 or result == ""): result = "0=0=0=0=0" if(self.count % 3 == 0): text = "$"+str(result).split("=")[4] panel.text((x, top), "Today Earn", font=self.fontsuperbig, fill=fill) panel.text((width - self.fontsuperbig.getsize(text)[0] - 2, top + 26), text, font=self.fontsuperbig, fill=fill) if(self.count % 3 == 1): text = str(result).split("=")[2] + " / $" + str(result).split("=")[3] panel.text((x, top), "T Clicks", font=self.fontsuperbig, fill=fill) panel.text((width - self.fontsuperbig.getsize(text)[0] - 2, top + 26), text, font=self.fontsuperbig, fill=fill) if(self.count % 3 == 2): text = str(result).split("=")[1] panel.text((x, top), "T Views", font=self.fontsuperbig, fill=fill) panel.text((width - self.fontsuperbig.getsize(text)[0] - 2, top + 26), text, font=self.fontsuperbig, fill=fill) except: print("Error when AdmobTodayScreen") def getSleepTime(self): return 3 def getSleepCount(self): return 3 * 5 def startThread(self): print("Not implemented") def next(self): self.count = self.count + 1 |
Include the new Screen into main.py
from AdmobTodayScreen import *
screenList = [SystemScreen(),
loadingScreen, TimeScreen(),
loadingScreen, AdmobTodayScreen() ]
##Run the python3 main.py again, eventually your will see the following result
Comments
please!?