├── Grand9K-Pixel.ttf ├── README.md ├── RaspberryPi.py ├── images ├── iPhatNameBadge.png ├── inky-cal.jpg ├── inky-display.jpg ├── inky-final.JPG ├── inky-pHAT.JPG ├── micro-usb-case.JPG ├── micro-usb-vbusGnd.JPG ├── nametag.jpg ├── ras-soldering.JPG └── simple-data.jpg └── ras.sh /Grand9K-Pixel.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/Grand9K-Pixel.ttf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi Inky pHAT Peripheral Display - Quote, Currently Playing Spotify Song and Local Weather 2 | 3 | ## Overview 4 | A project that uses a Raspberry Pi (Zero WH) to display information on to a Pimoroni Inky 5 | pHAT screen display. The information displayed is data retrieved from API endpoints: a quote stored 6 | in a Firebase Realtime database, the song the user is currently listening to on Spotify, and 7 | the weather in degrees celcius for the user's city. The Inky pHAT display will update periodically with 8 | new information. 9 | 10 | ## Motivation 11 | I started this project as a means to try out using Raspberry Pi for the first time. 12 | I was curious to see how the Pi's work, as well as how it functions with the Inky pHAT display 13 | attached. To find out, I ended up creating this project that periodically makes a few HTTP GET requests to 14 | various API's and displays the retrieved information out on the display, and as a result functions 15 | as a peripheral display for a desk. 16 | 17 | ## Setup 18 | 1. Install Raspbian to Raspberry Pi and complete Pi setup. 19 | 2. Shut Pi down and attach Inky pHAT display. Power on Pi. 20 | 3. Install `inky` dependencies 21 | 1. ```console 22 | curl https://get.pimoroni.com/inky | bash 23 | ``` 24 | 4. Try `name-badge.py` example to make sure every thing is working as intended. 25 | 1. ```python 26 | python3 name-badge.py --type "auto" --colour "yellow" --name "Ben Carroll" 27 | ``` 28 | 5. Clone this repo 29 | 1. ```console 30 | git clone https://github.com/bencarroll1/raspberryPiE-InkDisplay.git 31 | ``` 32 | 6. Enter the following to `RaspberryPi.py`: 33 | 1. WeatherAPI ([Request an API key](https://www.weatherapi.com/docs/)): 34 | 1. API key 35 | 2. Spotify API ([Create an app on Spotify for Developers](https://developer.spotify.com/documentation/)): 36 | 1. client_id 37 | 2. client_secret 38 | 3. redirect_uri 39 | 3. Firebase ([Docs here](https://firebase.google.com/docs/reference/rest/database)): 40 | 1. database name 41 | 2. database region 42 | 7. In the file `ras.sh`, change the pathing to `raspberryPiE-InkDisplay` as appropriate. 43 | 1. My own are listed below, but yours may be different: 44 | ```shell 45 | #!/usr/bin/bash 46 | cd /home/pi/Desktop/inkyTesting/raspberryPiE-InkDisplay 47 | python3 ./RaspberryPi.py 48 | ``` 49 | 2. Save and place on the Desktop 50 | 8. Open a terminal and enter the following: 51 | ```shell 52 | crontab -e 53 | ``` 54 | 1. If its your first time using crontab, you will be prompted to select an editor. Choose Nano, the recommended one. 55 | 2. Scroll to the bottom of the file, and under the last commented line enter: 56 | ```shell 57 | */3 9-23 * * * /home/pi/Desktop/ras.sh >> /home/pi/Desktop/cron.log 2>&1 58 | ``` 59 | 3. Press `ctrl` + `o` to save the changes you have made 60 | 4. Press `Enter` 61 | 5. Press `ctrl` + `x` to exit Nano 62 | 63 | This will run the bash script every 3 minutes between 9 a.m. and 11:59 p.m. 64 | 65 | Some information on [burn in](https://forums.pimoroni.com/t/my-inky-phat-clock-refresh-speed-question/6955) from the Pimoroni Forums. 66 | 67 | ## 3D Printed Case & Soldering Process 68 | In order to have this project neatly displayed on my desk as a peripheral display, I needed to have a neat way 69 | to display the Inky pHAT display. To achieve this, I ordered a 3D-printed case from a local printer using 70 | [this](https://github.com/balenalabs/inkyshot/tree/master/assets/case-design1-rear-usb "Inkyshot 3D printed case") .stl design. 71 | 72 | After I received the case, I got to work to attach a [new female micro USB adapter](https://dl.wish.com/b7LSt "Female micro USB adapter from Wish") to the Raspberry 73 | Pi for the power source connection as the current micro USB port was underneath the Pi and Inky pHAT, 74 | making it difficult to both power and display the device neatly. Relocating the micro USB port would allow me to have 75 | the power source wire come out the back of the case instead. 76 | 77 | Using a soldering iron, I attached a short length of the red part of the [insulated wire](https://dl.wish.com/WrnQs "Insulated copper wire from Wish") 78 | to the VBUS hole on the new female micro USB port and the black part of the wire to the ground (GND) 79 | hole. 80 | 81 | ![Female micro USB adapter soldering connections to insulated wire](images/micro-usb-vbusGnd.JPG) 82 | 83 | I then attached the other end of the red/VBUS wire to the circuit board connection furthest from the edge on the back 84 | of where the original power source adapter is on the Pi (See picture below for reference), and the black/GND wire 85 | to the circuit board connection nearest the edge. 86 | 87 | ![Raspberry Pi Zero soldering to attach new micro usb port](images/ras-soldering.JPG) 88 | 89 | With that, the soldering was complete and Pi was ready to be fitted into the case. There are two holes 90 | on the back of the case that the new power source can sit in, and the Pi and Inky pHAt just fit into 91 | the front part of the case. 92 | 93 | ![Micro USB port attaching to 3D printed case](images/micro-usb-case.JPG) 94 | 95 | The finished Result: 96 | 97 | ![The finished product in the 3D printed case](images/inky-final.JPG) 98 | 99 | 100 | ## Results 101 | To begin with, I set up the Rasberry Pi Zero with Raspbian, as this was the recommended OS. 102 | Raspbian is probably not needed for this project, as the final product doesn't require a GUI, etc., 103 | but it was my first time using a Raspberry Pi, so I went with it. 104 | 105 | Once the Pi was set up, I shut it down and attached the Inky pHAT and rebooted it. 106 | I then installed the necessary dependencies and and tried displaying some of the example Python scripts 107 | available for the Inky pHAT: 108 | 109 | ![Inky pHAT calendar example](images/inky-cal.jpg) 110 | 111 | ![Inkt pHAT nametag terminal example](images/iPhatNameBadge.png) 112 | 113 | ![Inky pHAT nametag example](images/nametag.jpg) 114 | 115 | I then got to work on retrieving data from the specified APIs. This was straightforward as 116 | I've retrieved data from APIs with Python before. 117 | 118 | Once I had that working, I began working on displaying the retrieved 119 | data on to the Inky pHAT. This was a bit trickier as I had to figure out how to 120 | display the data within the dimensions of the screen, and how to format it correctly. 121 | 122 | ![Inky pHAT simple display of retrieved API data ](images/simple-data.jpg) 123 | 124 | Aftersome trial and error, I got the data displaying correctly within 125 | the dimensions of the Inky pHAT. I also removed any brackets or unneeded characters from 126 | the outputted display. 127 | 128 | ![Inky pHAT with Spotify, WeatherAPI and Firebase Realtime database data retrieved and formatted. ](images/inky-display.jpg) 129 | 130 | I then wrote a bash script to run the Python script and used Crontab to make run the bash script 131 | every 3 minutes between 9 a.m. and 11:59 p.m. This isn't real time data, especially for Spotify but 3 132 | minutes is roughly the length of a song, and I avoid burn in refreshing this infrequently. 133 | 134 | ![Inky pHAT final](images/inky-pHAT.JPG) 135 | 136 | The project in a 3D printed case: 137 | 138 | ![The finished product in the 3D printed case](images/inky-final.JPG) 139 | 140 | Here is a short video showcasing the Pi Zero with the Inky pHAT screen in the 3D printed case in action: 141 | 142 | [https://photos.app.goo.gl/h4ub3YzUvfz5MzDr8](https://photos.app.goo.gl/h4ub3YzUvfz5MzDr8) 143 | -------------------------------------------------------------------------------- /RaspberryPi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import argparse 4 | import inkyphat 5 | from PIL import Image, ImageFont, ImageDraw 6 | from inky.auto import auto 7 | import random 8 | import requests 9 | import json 10 | import spotipy 11 | from spotipy.oauth2 import SpotifyOAuth 12 | 13 | try: 14 | inky_display = auto(ask_user=True, verbose=True) 15 | except TypeError: 16 | raise TypeError("You need to update the Inky library to >= v1.1.0") 17 | 18 | 19 | try: 20 | inky_display.set_border(inky_display.YELLOW) 21 | except NotImplementedError: 22 | pass 23 | 24 | # Figure out scaling for display size 25 | scale_size = 1.0 26 | padding = 0 27 | 28 | if inky_display.resolution == (400, 300): 29 | scale_size = 2.20 30 | padding = 15 31 | 32 | if inky_display.resolution == (600, 448): 33 | scale_size = 2.20 34 | padding = 30 35 | 36 | # Create a new canvas to draw on 37 | img = Image.new("P", inky_display.resolution) 38 | draw = ImageDraw.Draw(img) 39 | 40 | # Load the fonts 41 | Grand9K_Pixel = ImageFont.truetype("/home/pi/Desktop/inkyTesting/raspberryPiE-InkDisplay/Grand9K-Pixel.ttf", int(16 * scale_size)) 42 | 43 | Grand9K_Pixel_14 = ImageFont.truetype("/home/pi/Desktop/inkyTesting/raspberryPiE-InkDisplay/Grand9K-Pixel.ttf", int(14 * scale_size)) 44 | 45 | """ 46 | Firebase 47 | 48 | Code to retrieve data from Firebase database API 49 | """ 50 | 51 | # Reference: code written by Adam Bowie, as seen here: https://www.adambowie.com/blog/2019/09/news-twitter-feeds-and-inky-what-e-ink-display/ 52 | 53 | 54 | def reflow_quote(quote, width, font): 55 | words = quote.split(" ") 56 | reflowed = ' ' 57 | line_length = 0 58 | 59 | for i in range(len(words)): 60 | word = words[i] + " " 61 | word_length = font.getsize(word)[0] 62 | line_length += word_length 63 | 64 | if line_length < width: 65 | reflowed += word 66 | else: 67 | line_length = word_length 68 | reflowed = reflowed[:-1] + "\n " + word 69 | 70 | return reflowed 71 | 72 | 73 | # api-endpoint 74 | urlPrefix = 'https://DATABASENAME-default-rtdb.REGION.firebasedatabase.app/quotes/' 75 | 76 | # get random int between first quote id to last for endpoint 77 | quoteStr = str(random.randint(1, 30)) 78 | print(quoteStr) 79 | urlQuoteSuffix = '/quote.json' 80 | urlAuthorSuffix = '/author.json' 81 | urlCitySuffix = '/city.json' 82 | 83 | quoteUrl = urlPrefix + quoteStr + urlQuoteSuffix 84 | authorUrl = urlPrefix + quoteStr + urlAuthorSuffix 85 | cityUrl = urlPrefix + quoteStr + urlCitySuffix 86 | 87 | # get requests 88 | quote = requests.get(url=quoteUrl) 89 | author = requests.get(url=authorUrl) 90 | city = requests.get(url=cityUrl) 91 | 92 | quoteData = str(quote.json()) 93 | authorData = str(author.json()) 94 | cityData = str(city.json()) 95 | 96 | quote = '"' + quoteData + '"' 97 | author = '~' + authorData 98 | 99 | if '"None"' in quote: 100 | quote = '"' + "In all sincerity, I couldn't retrieve a quote okayyy." + '"' 101 | author = '~' + 'Ramona Mazur Singer (' + quoteStr + ')' 102 | 103 | # print quote 104 | print('"' + quoteData + '"' + ' ~' + authorData + ' (' + cityData +')') 105 | 106 | quoter = reflow_quote(quote, inky_display.WIDTH, font=Grand9K_Pixel) 107 | 108 | """ 109 | Spotipy 110 | 111 | Spotipy code to retrieve currently playing song. 112 | """ 113 | # spotipy instance with auth info 114 | sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id="CLIENT_ID", 115 | client_secret="CLIENT_SECRET", 116 | redirect_uri="RDR_URI", 117 | scope="user-read-currently-playing")) 118 | 119 | # spotipy currently playing function 120 | current_song = sp.currently_playing() 121 | 122 | # getting currently playing song on user account 123 | try: 124 | body = json.loads(json.dumps(current_song)) 125 | artists = "" 126 | for a in body["item"]["artists"]: 127 | if artists != "": 128 | artists += ", " 129 | artists += a["name"] 130 | song, artist = str(body["item"]["name"]), str(artists) 131 | 132 | nowPlayingSong = song 133 | nowPlayingArtist = artist 134 | 135 | print(nowPlayingSong) 136 | print(nowPlayingArtist) 137 | 138 | except: 139 | print("Nothing is being played at the minute") 140 | nowPlayingSong = 'Nothing Playing' 141 | nowPlayingArtist = '' 142 | 143 | """ 144 | WeatherAPI 145 | 146 | Code to retrieve weather info for city 147 | """ 148 | 149 | # API endpoint 150 | weatherAPIEndpoint = 'http://api.weatherapi.com/v1/current.json?key=API_KEY&q=CITY' 151 | 152 | # sending get request and saving the response as response object 153 | dublinCityWeather = requests.get(url=weatherAPIEndpoint) 154 | 155 | # extracting data in json format 156 | data = json.loads(json.dumps(dublinCityWeather.json())) 157 | 158 | city = data['location']['name'] 159 | country = data['location']['country'] 160 | currentWeatherDegrees = str(data['current']['temp_c']) 161 | currentWeatherInWords = data['current']['condition']['text'] 162 | 163 | print(currentWeatherDegrees + 164 | 'C - ' + currentWeatherInWords + ' - ' + city + ', ' + country) 165 | 166 | weather = currentWeatherDegrees + 'C - ' + city + ', ' + country 167 | 168 | # Top and bottom y-coordinates for the white strip 169 | y_top = int(inky_display.height * (5.0 / 10.0)) 170 | y_bottom = y_top + int(inky_display.height * (5.0 / 10.0)) 171 | 172 | # Calculate the positioning and draw the quote text 173 | quote_w, quote_h = Grand9K_Pixel.getsize(str(quoter)) 174 | quote_x = int((inky_display.width - quote_w) / 2) 175 | quote_y = 0 + padding - 6 176 | 177 | draw.text((0, quote_y), quoter, 178 | inky_display.BLACK, font=Grand9K_Pixel) 179 | 180 | # Calculate the positioning and draw the author text 181 | author_w, author_h = Grand9K_Pixel.getsize(author) 182 | author_x = int((inky_display.width - author_w) / 2) 183 | author_y = quote_h + padding + 18 184 | 185 | draw.text((author_x, author_y), author, 186 | inky_display.YELLOW, font=Grand9K_Pixel) 187 | 188 | # Calculate the positioning and draw the spotify song and artist text 189 | spotifySong_w, spotifySong_h = Grand9K_Pixel.getsize(str(nowPlayingSong)) 190 | spotifySong_x = int((inky_display.width - spotifySong_w) / 2) 191 | spotifySong_y = author_h + padding + 42 192 | 193 | draw.text((spotifySong_x, spotifySong_y), str(nowPlayingSong), 194 | inky_display.BLACK, font=Grand9K_Pixel) 195 | 196 | # Calculate the positioning and draw the spotify song and artist text 197 | spotifyArtist_w, spotifyArtist_h = Grand9K_Pixel.getsize(str(nowPlayingArtist)) 198 | spotifyArtist_x = int((inky_display.width - spotifyArtist_w) / 2) 199 | spotifyArtist_y = spotifySong_h + padding + 61 200 | 201 | draw.text((spotifyArtist_x, spotifyArtist_y), str(nowPlayingArtist), 202 | inky_display.YELLOW, font=Grand9K_Pixel_14) 203 | 204 | # Calculate the positioning and draw the weather text 205 | weather_w, weather_h = Grand9K_Pixel.getsize(str(weather)) 206 | weather_x = int((inky_display.width - weather_w) / 2) 207 | weather_y = int(y_top + ((y_bottom - y_top - weather_h) / 2) + 22) 208 | 209 | draw.text((weather_x, weather_y), weather, 210 | inky_display.BLACK, font=Grand9K_Pixel_14) 211 | 212 | # Display the completed name badge 213 | inky_display.set_image(img) 214 | inky_display.show() 215 | -------------------------------------------------------------------------------- /images/iPhatNameBadge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/images/iPhatNameBadge.png -------------------------------------------------------------------------------- /images/inky-cal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/images/inky-cal.jpg -------------------------------------------------------------------------------- /images/inky-display.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/images/inky-display.jpg -------------------------------------------------------------------------------- /images/inky-final.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/images/inky-final.JPG -------------------------------------------------------------------------------- /images/inky-pHAT.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/images/inky-pHAT.JPG -------------------------------------------------------------------------------- /images/micro-usb-case.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/images/micro-usb-case.JPG -------------------------------------------------------------------------------- /images/micro-usb-vbusGnd.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/images/micro-usb-vbusGnd.JPG -------------------------------------------------------------------------------- /images/nametag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/images/nametag.jpg -------------------------------------------------------------------------------- /images/ras-soldering.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/images/ras-soldering.JPG -------------------------------------------------------------------------------- /images/simple-data.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencarroll1/raspberryPiE-InkDisplay/41135b0aa6f8a16c26e088873d0b192464acaf22/images/simple-data.jpg -------------------------------------------------------------------------------- /ras.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | cd /home/pi/Desktop/inkyTesting/raspberryPiE-InkDisplay 3 | python3 ./RaspberryPi.py --------------------------------------------------------------------------------