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 Product

4) 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 

## 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 below
python3 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 sample
https://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.py
You 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




Similar project in E-ink








Comments

VitouXY said…
Hi. You can share the complete code to display the 'System Information'. I have problems running the published code ... apparently it is incomplete ...
please!?
Unknown said…
After sudo python3 -m pip install --upgrade luma.oled I got an error message saying /usr/bin/python3: No module named pip...Please help!

Popular Posts