Forgot an appointment? No morewith your very own stylish E-Paper calendar. Follow this full size tutorial to setup your Raspberry Pi, connect it to your Waveshare E-paper and display the events of your calendar using the Google Calendar API.
Goals of this instructable
- Get to know about SSH, FTP and VNC so your able to wireless update your code and Pi
- Program your Pi to work with the Waveshare E-paper screen
- Guide you trough the setup of the Google Calendar API
- Learn about Cronjobs on the Pi
Supplies
- Raspberry Pi with headers (RPi 3 of 4 is most easy because it has integrated wifi*)
- I used a RPi 2B with a WN725N Nano usb wifi dongle
- MicroSD card (and a USB-reader of SD adapter to flash it on a PC/laptop)
- Waveshare 7.5 inch black/white e-ink screen V2 800×480 with HAT for Raspberry Pi
- Ikea Ribba Photo frame with passe partout of 10×15 cm
*If you can’t get the wifi up and running, maybe you need a keyboard/mouse/monitor and ethernet/hdmi-cable once to setup wifi.
Step 1: Setup Raspberry Pi With Preconfigured Settings
First step is to configure your Raspberry Pi in such way that you can connect to it from your pc or laptop, without hooking it up to a monitor, keyboard and mouse. (This is possible with a Raspbery that has onboard wifi (3 Or 4)or using usb wifi dongles that work out of the box)
Flash microSD card with Raspberry Pi Os using preconfigured settings
Download the ‘Raspberry Pi Imager’ from raspberrypi.com/software. Choose Raspberry Pi OS and choose your storage (microSD card). Click on the settings wheel to preconfigure your operating system.
- Enable SSH (Use password authentication)
- Choose Username and Password
- Setup Wifi credentials and wifi country
- Set you locale settings including timezome
Flash the image to your microSD card, insert it into your Pi and boot it up!
Step 2: Connect to Your Pi Through SSH
Your Pi should now be started and connected to WiFi. Next step is to be able to connect to it from your PC, therefore, we are going to connect using SSH.
Check if Raspberry Pi is online
On your PC, start a command line program (eg Windows Command Line or Windows Powershell) and type
ping raspberripi.local or ping raspberrypi
you should now see the MAC address and mostly, also the local IP-address.
No Raspberry Pi found? Probably your Pi is not connected. Check out this guide to get your WiFi running. You probably need to connect the Pi to a monitor / keyboard / mouse to use the user interface of Raspberry Pi OS and get your wifi up and running.
Setup SSH
With SSH you can control everything on your Pi. We already enabled this in your OS in the first step. Now we are going to use it! Standard, the preconfigured username is pi and the address of the Pi raspberrypi.local. To connect trough SSH, open up a commandline on your PC and type:
SSH pi@raspberrypi
You will be prompted for the password (sometimes you need to give a onetime ‘yes’ to really connect), insert your pass and hit Enter. Congratulations: you’re connected!
Update your Pi
Now your connected through SSH, first update your Pi. Type
sudo apt-get update
this will install any new packages that can be added to the Raspberry Pi’s library of applications. When finished, reboot:
sudo reboot
Now update all installed packages:
sudo apt-get upgrade
and reboot again with sudo reboot
Step 3: Enable VNC
Our goal is to work from our PC. But sometimes you just want to use the user interface of Raspberry Pi OS. This is easy with VNC, which establishes a ‘remote monitor’.
Setup VNC
Install VNC viewer on your PC from RealVNC. Next, open your command line en login to your Pi through SSH as explained before. Type the following command:
sudo raspi-config
Go to 3. Interface Options > VNC > Enable. VNC is now enabled!
Open VNC viewer on your PC, go to File > New connection. Fill in the IP-address (‘raspberrypi’) and give it a name. Connect, fill in your password and tada! You can now see and use the user interface of the Pi without using a monitor / keyboard / mouse!
Troubles to get it running: Check the VNC-guide of Raspberry Pi.
Step 4: Setup FTP With Notepad++
Because we are working from our PC, we also want to edit (python)-files remotely. Therefore, we need a FTP-server and Notepad++.
FTP: To transfer files to your Pi we’re gonna use FTP.
Notepad++: A code-text editor that has a FTP plugin, so you can easily program directly on your Pi
FTP
There is no FTP-server pre-installed, so we have to do this manually. We’re using vsFTPd, this utility is lightweight, secure, and easy to use. Type the following command:
sudo apt install vsftpd
After installation, configure vsFTPd by editing the settings file:
sudo nano /etc/vsftpd.conf
Nano is a text editor that will run inside your command line program. Remove the # from the following lines:
write_enable=YES local_umask=022
Next, find the line:
anonymous_enable=YES
and change it to:
anonymous_enable=NO
Go to the end of the document and add the following lines:
user_sub_token=$USER local_root=/home/$USER/
Now save hitting CTRL + X confirm with Y(es) + Enter. Restart the server:
sudo service vsftpd restart
Done!
Troubles: Check this tutorialon which my steps are based.
Setup Notepad++
Install Notepad++ on your PC: https://notepad-plus-plus.org/downloads/. Open Notepad++ and go to Plugins > Plugins admin and search and install NppFTP. After installation go to Plugins > NppFTP > Show NppFTP window. To connect to the FTP-server of your Pi:
Right-click in the NppFTP window > Create a new profile
Hostname: raspberrypi
Username: pi
Password: [your password of the Pi]
Click Close and give your profile a name. Right-click on it and connect!
All set for the real work!
Step 5: Setup Raspberry Pi to Work With E-ink Screen and Run Sample Code
We are gonna work with Python to control the Waveshare E-ink screen. Therefore, we need to install some library’s on the Pi. We simply follow the instructions from the Waveshare Wiki for our 7.5″ V2 black/white E-ink screen. Start our command line, login through SSH and follow these steps.
1) Enable SPI interface
sudo raspi-config
Go to 3 Interface options > SPI > Enable SPI. Select Finish to return to the terminal.
Reboot with sudo reboot
2) Optional: Install Python library’s (you can skip these step, since they have to be present in Raspberry Pi OS)
sudo apt-get install python3-pip sudo apt-get install python3-pil sudo apt-get install python3-numpy sudo pip3 install RPi.GPIO sudo pip3 install spidev
3) Download the demo from GitHub
git clone https://github.com/waveshare/e-Paper.git cd e-Paper/RaspberryPi_JetsonNano/
4) Connect the HAT of the E-ink screen to the Raspberry Pi headers. Make sure the switches on the Hat are set to B (Display config) and to 0 (Interface config).
5) Run the sample and watch your screen come to live!
cd python/examples/ python epd_7in5_V2_test.py
Step 6: Create Your First E-paper ‘Hello World’
Now are software and hardware are setup, it’s time to start coding. Open Notepad++ and connect to the Pi using the FTP plugin (or use Filezilla).
Create folders
Inside the Pi directory create these folders (case sensitive), see screenshot also
calendar
– – prog
– – lib
– – – – waveshare_epd
Next copy the EPD library from the sample code to the ‘waveshare_epd’ folder.
From e-Paper/RaspberryPi_JetsonNano/python/lib/waveshare_epd/
copy epd7in5_V2.py and epdconfig.py
to
calendar/lib/waveshare_epd/
From e-Paper/RaspberryPi_JetsonNano/python/pic
copy Font.ttc
to
calendar/prog/
Create our Hello World example
In Notepad++ create a new file, save it as calendar.py (Python file) to a PC folder.
Copy the code below. This is based on the example code from Waveshare. I really recommend the waveshare wiki to learn about all the ‘epd.xxx’ commands and how to print text, lines, figures and display monochrome bmp’s.
import sys import os libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib') if os.path.exists(libdir): sys.path.append(libdir) from waveshare_epd import epd7in5_V2 import logging import time from PIL import Image,ImageDraw,ImageFont import traceback logging.basicConfig(level=logging.DEBUG) #Enable log-information for debbuging try: epd = epd7in5_V2.EPD() epd.init() epd.Clear() font48 = ImageFont.truetype("Font.ttc", 48) #Draw something on the e-ink screen Himage = Image.new('1', (epd.width, epd.height), 255) #Clear the frame (255 = white) draw = ImageDraw.Draw(Himage) #Create new image buffer draw.text((40, 40), 'Hello world', font = font48, fill = 0) #Write text on position 40,40 (x,y) in black epd.display(epd.getbuffer(Himage)) #Display the buffer on the screen time.sleep(4) epd.init() epd.Clear() epd.sleep() except IOError as e: logging.info(e)
Save the file (calendar.py). Next, right click in NppFTP on your ‘prog’ folder and choose ‘Upload current file here’. Calendar.py is now uploaded to your Pi. Run the sample, by opening op the command line on your PC and connect through SSH, then type:
cd calendar/prog #open folder python calendar.py #run the python code
The screen will now show ‘Hello World’ for 4 seconds, erase it and go to sleep. See enclosed demo.
Step 7: Get the Google Calendar API Working
We now know how to write information to the e-ink screen. Now it is time to get the information from our Google calendar.
First step is simple: Create a project in ‘Google cloud’. Next is to setup the calendar API, this can be a bit more challenging, fortunately, Google has a quickstart to get you going, so follow these steps:
https://developers.google.com/calendar/api/quickstart/python
Some tips about the Calendar API:
- Enable the API: this is easy
- Credentials: Save the credentials.json file in the same folder as your main python file (the ‘prog’ folder) using NppFTP inside Notepad++ by rightclicking the folder and click ‘Upload other file here’ and selecte the credentials.json.
- Install the Google python library: easy but don’t forget to ‘sudo reboot’ afterwards
- Copy the quickstart.py code inside a new document inside Notepad++ and save it to the ‘prog’ folder.
- Important: Temporary delete the calendar.py file (or move it to another folder). Somehow you get an error when you run the quickstart.py if the calendar.py is in the same folder (see comments below).
- Run quickstart.py once using VNC viewer (not from your PC!), because you need to login your Google account to create the ‘token.json’ file. Therefore launch the ‘Terminal’ in Raspberry Pi OS (see screenshot):
- In the terminal type ‘cd calendar/prog’ and then ‘python quickstart.py’
- Chrome webbrowser will automatically open, to login your google account en give access to your project
- A token.json file will automatically created inside the ‘prog’ folder
- When done, your upcoming events will list inside the terminal!
- Since the token.json file is created, you can now run the quickstart.py from your commandline on your PC. to get the same results
Step 8: Display Google Calendar Information on the Screen
Let’s put it all together! We know how to display information on our e-ink screen AND we know how to get the Google Calender events. To keep it simple: we’re gonna incorporate parts of the quickstart.py code into our ‘Hello world’ code to display a simple list of our upcoming events.
###### Waveshare part ####### import sys import os libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib') if os.path.exists(libdir): sys.path.append(libdir) from waveshare_epd import epd7in5_V2 import logging import time from PIL import Image,ImageDraw,ImageFont import traceback logging.basicConfig(level=logging.DEBUG) ###### Calendar API part ##### import datetime from datetime import date, timedelta import os.path from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build from googleapiclient.errors import HttpError SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] try: #Calendar API part creds = None if os.path.exists('token.json'): creds = Credentials.from_authorized_user_file('token.json', SCOPES) if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( 'credentials.json', SCOPES) creds = flow.run_local_server(port=0) with open('token.json', 'w') as token: token.write(creds.to_json()) service = build('calendar', 'v3', credentials=creds) now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time print('Getting the upcoming 10 events') events_result = service.events().list(calendarId='primary', timeMin=now, maxResults=10, singleEvents=True, orderBy='startTime').execute() events = events_result.get('items', []) if not events: print('No upcoming events found.') #E-paper part epd = epd7in5_V2.EPD() epd.init() epd.Clear() font18 = ImageFont.truetype("Font.ttc", 18) Himage = Image.new('1', (epd.width, epd.height), 255) draw = ImageDraw.Draw(Himage) draw.text((10, 20), 'UPCOMING EVENTS', font = font18, fill = 0) #Loop trough calendar events and draw them to buffer y = 60 for event in events: start = event['start'].get('dateTime', event['start'].get('date')) draw.text((10, y), start + ' ' + event['summary'], font = font18, fill = 0) y = y + 30 #Display buffer on screen for 25 seconds epd.display(epd.getbuffer(Himage)) time.sleep(25) epd.init() epd.Clear() epd.sleep() except IOError as e: logging.info(e)
Run the code by SSH-ing into your Pi and execute the python code.
cd calendar/prog #open folder python calendar.py #run the python code
Super! We got our (rudimentary) calendar! In the next steps we’re gonna make some nice lay-out!
Step 9: Optimize Your Code to Get a Nice Lay-out
In this step we’re gonna make a nice lay-out. To get a good overview I have three imaginary colums: today, events later this week and events next week. To spread the information about this columns, just be creative with the x,y position of the draw text statement, and a lot of if statements. I enclosed the file to get you going.
Some explanation of the code
Current date (top left)
This creates the current date at the top left of the screen. It simply viewing the current date in different formats, using strftime. See this site for an overview of all the date formats.
#TODAY draw.text((20, 20), date.today().strftime("%d"), font = font56, fill = 0) draw.text((90, 24), date.today().strftime("%B"), font = font24, fill = 0) draw.text((90, 50), date.today().strftime("%A"), font = font24, fill = 0) draw.text((20, 120), 'TODAY', font = font18, fill = 0)
Calendar events
This is a bit harder, mostly because Google returns both date events (whole day events) and datetime events (specific start time). What it does:
- Get the variables of the first event (startdate, starttime, enddate)
- Set startdate to current date, if an event started before today
- Check week of the event (if event is not today), if this is not the week of the previous event, then
- Current week? Display ‘later this week’
- Week after that? Display ‘next week’
- Week after that? To far away: stop loop
- Check day of event (if event is not today), is this not the day of the previous event, then
- Display ‘DAY Month – date’
- If all the above is done, display the actual calendar event
#Loop trough calendar events and draw them to buffer x = 20 #start position on x-axis of events on e-ink screen y = 140 #start position on y-axis of events on e-ink screen curweek = '' #variable for week of event curday = '' #variable for day of event for event in events: start = event['start'].get('dateTime', event['start'].get('date')) end = event['end'].get('dateTime', event['end'].get('date')) summ = event['summary'] if len(start) == 10: #events that are 'whole day'-events startdate = datetime.datetime.strptime(start,"%Y-%m-%d") enddate = datetime.datetime.strptime(end,"%Y-%m-%d") time = '' if len(start) == 25: #events that start at specific time startdate = datetime.datetime.strptime(start,"%Y-%m-%dT%H:%M:%S%z") enddate = datetime.datetime.strptime(end,"%Y-%m-%dT%H:%M:%S%z") time = startdate.strftime(" (%H:%M)") startdate_date = startdate.date() if startdate_date < date.today(): #multi day events, set start date at current date startdate = date.today() startdate_date = date.today() day = startdate.strftime("%a %m-%d") week = startdate.strftime("%V") if curweek != week and startdate_date != date.today(): if (week == date.today().strftime("%V")): x = 280 y = 25 draw.text((x, y), 'LATER THIS WEEK', font = font18, fill = 0) elif (int(week) == int(date.today().strftime("%V"))+1): x = 530 y = 25 draw.text((x, y), 'NEXT WEEK', font = font18, fill = 0) else: break curweek = week y = y + 25 if curday != day and startdate_date != date.today(): draw.text((x, y+4), day.upper(), font = font18, fill = 0) curday = day y = y + 20 draw.text((x, y),' ' + event['summary'] + time, font = font15, fill = 0) y = y + 20
Refresh time
To see if there has been an update, you can display the date and time of the refresh very small in the lower right corner.
Read more: E-paper Calendar: Raspberry Pi With E-ink Screen and Google Calendar API (Full Tutorial)