Clock and Temperature display
Using an ILI9341 SPI display with a DS18B20 Temperature sensor connected to a Raspberry PI Zero.
I did a search on Youtube and found a starting point. The script I found had me running in just a few minutes. I made some modifications and posted that below. Hopefully this helps someone.
I'm using a SunFounder DS18B20 to get the temperature in the room. I only want an accurate time source with the temperature. Using a Pi Zero with NTP and the temperature sensor is all I need. To display the data, I found a 2.2 inch ILI9341. I hope to get this into a case more suitable for a desktop.
To get the DS18B20 connected, I followed the instructions from the vendor: https://www.sunfounder.com/learn/sensor-kit-v2-0-for-raspberry-pi-b-plus/lesson-26-ds18b20-temperature-sensor-sensor-kit-v2-0-for-b-plus.html
I'm using a SunFounder DS18B20 to get the temperature in the room. I only want an accurate time source with the temperature. Using a Pi Zero with NTP and the temperature sensor is all I need. To display the data, I found a 2.2 inch ILI9341. I hope to get this into a case more suitable for a desktop.
To get the DS18B20 connected, I followed the instructions from the vendor: https://www.sunfounder.com/learn/sensor-kit-v2-0-for-raspberry-pi-b-plus/lesson-26-ds18b20-temperature-sensor-sensor-kit-v2-0-for-b-plus.html
To get the display connected and setup the modules, I did a couple of searches on the web and used this one: https://pi0cket.com/ili9341-raspberry-pi-guide/
My abbreviated config for the display is at the bottom of this post. I was running Stretch Lite when I originally wrote this article. For Buster Lite on a Pi display, I have those steps abbreviated at the bottom of this post.
<---- ILI9341 Pin to Raspberry PI Zero Pin ---->
My abbreviated config for the display is at the bottom of this post. I was running Stretch Lite when I originally wrote this article. For Buster Lite on a Pi display, I have those steps abbreviated at the bottom of this post.
Clock based on first script below |
The three connections on the left are the temperature sensor. From the grey wire to the right are the 9 connectors for the display |
The other side of the Pi Zero to show those connections |
The ILI9341 connections |
SunFounder DS18B20 |
Raspberry PI Pinout |
<---- ILI9341 Pin to Raspberry PI Zero Pin ---->
SDO/MISO ---- 21 (GPIO9) LED ---- 12 (GPIO18) SCK ---- 23 (GPIO11) SDI/MOSI ---- 19 (GPIO10) DC/RS ---- 18 (GPIO24) RESET ---- 22 (GPIO25) CS ---- 24 (GPIO8) GND ---- 20 (GND) VCC ---- 17 (3v3)
<---- Sunfounder DS18B20 Sensor Pin to Raspberry PI Zero Pin ---->
SIG (1) ---- 7 (GPIO4) VCC (2) ---- 2 (5v) GND (3) ---- 6 (GND)
<---- Begin Code Section for myclock.py ---->
#!/usr/bin/python #---------------------------------------------------------------- # Note: # ds18b20's data pin must be connected to pin7. # replace the 28-XXXXXXXXX as yours. #---------------------------------------------------------------- import pygame, sys, os, time, datetime, signal from pygame.locals import * os.environ["SDL_FBDEV"] = "/dev/fb1" ## Globals pygame.init() ## Set up the screen display_width = 320 display_height = 240 #display_width = 800 #display_height = 480 DISPLAYSURF = pygame.display.set_mode((display_width, display_height), 0, 16) pygame.mouse.set_visible(0) pygame.display.set_caption('Room Temp') # set up the colors BLACK = ( 0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) GREEN = ( 0, 255, 0) COBALTGREEN = ( 61, 145, 64) BLUE = ( 0, 0, 255) CYAN = ( 0, 255, 255) YELLOW = (255, 255, 0) BANANA = (227,207,87) GOLD1 = (255,215,0) EMERALDGREEN = (0, 201, 87) ALICEBLUE = (240,248,255) ds18b20 = '' def setup(): global ds18b20 for i in os.listdir('/sys/bus/w1/devices'): if i != 'w1_bus_master1': ds18b20 = i def readTemp(): # location = '/sys/bus/w1/devices/28-00000a423922/w1_slave' location = '/sys/bus/w1/devices/' + ds18b20 + '/w1_slave' tfile = open(location) text = tfile.read() tfile.close() secondline = text.split("\n")[1] temperaturedata = secondline.split(" ")[9] temperature = float(temperaturedata[2:]) temperature = temperature / 1000 return temperature def signal_handler (signal, frame): pygame.quit() sys.exit(0) def DrawLine(color, startX,startY,stopX,stopY): pygame.draw.line(DISPLAYSURF, color, [startX,startY], [stopX,stopY], 1) ## Start signal.signal(signal.SIGINT, signal_handler) setup() ## Main loop while True: currenttime = datetime.datetime.now() if readTemp() != None: currenttemp = readTemp() temp_c = str("{0:.1f}".format(currenttemp)) temp_f = str("{0:.1f}".format((currenttemp*9/5)+32)) ## Draw the title black_square_that_is_the_size_of_the_screen = pygame.Surface(DISPLAYSURF.get_size()) black_square_that_is_the_size_of_the_screen.fill((0, 0, 0)) DISPLAYSURF.blit(black_square_that_is_the_size_of_the_screen, (0, 0)) font = pygame.font.Font(None, 30) text = font.render("Room Temp", 1, EMERALDGREEN) textpos = text.get_rect(center=(display_width*.5,int(round(.08*display_height)))) DISPLAYSURF.blit(text, textpos) ## Draw temperatures font = pygame.font.Font(None, 70) text = font.render(temp_f, 1, GOLD1) textpos = text.get_rect(center=(display_width*.25,int(round(.30*display_height)))) DISPLAYSURF.blit(text, textpos) font = pygame.font.Font(None, 20) textF = font.render(u'\u00b0' + "F", 1, GOLD1) textposF = textpos[0] + textpos[2], textpos[1] + 10 DISPLAYSURF.blit(textF, textposF) font = pygame.font.Font(None, 70) text = font.render(temp_c, 1, GOLD1) textpos = text.get_rect(center=(display_width*.75,int(round(.30*display_height)))) DISPLAYSURF.blit(text, textpos) font = pygame.font.Font(None, 20) textF = font.render(u'\u00b0' + "C", 1, GOLD1) textposF = textpos[0] + textpos[2], textpos[1] + 10 DISPLAYSURF.blit(textF, textposF) ## Draw date font = pygame.font.Font(None, 40) text = font.render(currenttime.strftime("%A, %b %d"), 1, ALICEBLUE) textpos = text.get_rect(center=(display_width/2,int(round(.58*display_height)))) DISPLAYSURF.blit(text, textpos) ## Draw time font = pygame.font.Font(None, 85) text = font.render(currenttime.strftime("%I:%M %p"), 1, ALICEBLUE) textpos = text.get_rect(center=(display_width/2,int(round(.79*display_height)))) DISPLAYSURF.blit(text, textpos) ## Draw Lines DrawLine(EMERALDGREEN, 5, int(round(.16*display_height)), display_width-5, int(round(.16*display_height))) DrawLine(EMERALDGREEN, 5, int(round(.45*display_height)), display_width-5, int(round(.45*display_height))) DrawLine(EMERALDGREEN, display_width*.5, int(round(.16*display_height)), display_width*.5, int(round(.45*display_height))) ## Update the LCD pygame.display.update() ## Sleep time! time.sleep(15)
<---- End Code Section ---->
Abbreviated display configuration:
Assuming you have updated your install (apt update and apt upgrade) and have the DS18B20 connected and working, here are the steps I used based on the Pi0cket blog mentioned above. These steps will get the display running your script automatically on a reboot:
sudo rm /usr/bin/pythonsudo ln -s /usr/bin/python3 /usr/bin/pythonsudo apt install python3-pipsudo pip3 install pygame==1.9.6
2. SDL 1.2 gets installed using the following:
sudo apt-get install libsdl1.2-devsudo apt-get install libsdl-image1.2-dev
sudo apt-get install libsdl-ttf2.0-dev
one line to get all 3:sudo apt-get install -y libsdl1.2-dev libsdl-image1.2-dev libsdl-ttf2.0-dev
-Enable SPI (in raspi-config -> Interfacing Options -> P4 SPI -> Yes)-Disable Overscan (in raspi-config -> Advanced Options -> A2 Overscan -> No)-Exit raspi-config
4. Execute: sudo nano /etc/modules
at the bottom of file add:
spi-bcm2835fbtft_device
my file looks like this:
5. Execute: sudo nano /etc/modprobe.d/fbtft.conf
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
spi-bcm2835
fbtft_device
5. Execute: sudo nano /etc/modprobe.d/fbtft.conf
options fbtft_device name=fb_ili9341 gpios=reset:25,dc:24,led:18 speed=16000000 bgr=1 rotate=90 custom=1
my file has only one line and looks like this:
options fbtft_device name=fb_ili9341 gpios=reset:25,dc:24,led:18 speed=16000000 bgr=1 rotate=90 custom=1
6. Make the ILI9341 display show your content:
con2fbmap 1 1
7. If the display shows up, reboot.
sudo reboot
Execute: sudo nano /etc/rc.localadd: /home/pi/bin/myclock.ph &
my rc.local looks like this:
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
printf "My IP address is %s\n" "$_IP"
fi
sudo python /home/pi/myclock.py &
exit 0
9. If you're feeling comfortable, reboot your pi.
sudo reboot
You should see the results of your python script after the reboot is completed.
Utilize OpenWeatherMap data
I cleaned up some of my earlier script above. I also added an API call to get some additional weather information using openweathermap.org data. You'll need to register with openweathermap. The basic weather information is free to access. I wrote a separate blog that discusses openweathermap a little more. If you only need that information, it's here. Below is the python script with the additional data.Shown with the added weather data |
#!/usr/bin/python #---------------------------------------------------------------- # Note: # ds18b20's data pin must be connected to pin7. # replace the 28-XXXXXXXXX as yours. #---------------------------------------------------------------- import pygame, sys, os, time, datetime, signal, requests from pygame.locals import * os.environ["SDL_FBDEV"] = "/dev/fb1" ## Globals pygame.init() ## Set up the screen display_width = 320 display_height = 240 display_center = int(display_width*.5) display_rcenter = int(display_width*.75) display_lcenter = int(display_width*.25) DISPLAYSURF = pygame.display.set_mode((display_width, display_height), 0, 16) pygame.mouse.set_visible(0) pygame.display.set_caption('Room Temp') # set up the colors BLACK = ( 0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) GREEN = ( 0, 255, 0) COBALTGREEN = ( 61, 145, 64) BLUE = ( 0, 0, 255) CYAN = ( 0, 255, 255) YELLOW = (255, 255, 0) BANANA = (227,207,87) GOLD1 = (255,215,0) EMERALDGREEN = (0, 201, 87) ALICEBLUE = (240,248,255) ROSYBROWN1 = (255,193,193) ds18b20 = '' def setup(): global ds18b20 for i in os.listdir('/sys/bus/w1/devices'): if i != 'w1_bus_master1': ds18b20 = i def readTemp(): # location = '/sys/bus/w1/devices/28-00000a423922/w1_slave' location = '/sys/bus/w1/devices/' + ds18b20 + '/w1_slave' tfile = open(location) text = tfile.read() tfile.close() secondline = text.split("\n")[1] temperaturedata = secondline.split(" ")[9] temperature = float(temperaturedata[2:]) temperature = temperature / 1000 return temperature def signal_handler (signal, frame): pygame.quit() sys.exit(0) def DrawLine(color, startX,startY,stopX,stopY): pygame.draw.line(DISPLAYSURF, color, [startX,startY], [stopX,stopY], 1) def openweather(): api_key = "get your api key at openweathermap.org/api" base_url = "https://api.openweathermap.org/data/2.5/weather?" complete_url = base_url + 'id=5391959&appid=' + api_key response = requests.get(complete_url) if response.status_code != 200: exit return response.json() ## Start signal.signal(signal.SIGINT, signal_handler) setup() ## Main loop while True: currenttemp = readTemp() temp_c = str("{0:.1f}".format(currenttemp)) temp_f = str("{0:.1f}".format((currenttemp*9/5)+32)) response = openweather() outtemp = float((response['main']['temp']-273.15)*9/5+32) feeltemp = float((response['main']['feels_like']-273.15)*9/5+32) outtemp = str("{0:.1f}".format(outtemp)) feeltemp = str("{0:.1f}".format(feeltemp)) weather_desc = response['weather'][0]['description'] ## Draw the title blank_screen = pygame.Surface(DISPLAYSURF.get_size()) blank_screen.fill((0, 0, 0)) DISPLAYSURF.blit(blank_screen, (0, 0)) font = pygame.font.Font(None, 25) text = font.render("Room", 1, EMERALDGREEN) textpos = text.get_rect(center=(display_lcenter,16)) DISPLAYSURF.blit(text, textpos) font = pygame.font.Font(None, 25) text = font.render("Outside/Feel", 1, EMERALDGREEN) textpos = text.get_rect(center=(display_rcenter,16)) DISPLAYSURF.blit(text, textpos) ## Draw Lines DrawLine(COBALTGREEN, 5, 28, display_width-5, 28) DrawLine(COBALTGREEN, 5, 114, display_width-5, 114) DrawLine(COBALTGREEN, display_center, 28, display_center, 114) ## Draw temperatures font = pygame.font.Font(None, 70) text = font.render(temp_f, 1, GOLD1) textpos = text.get_rect(center=(display_lcenter,56)) DISPLAYSURF.blit(text, textpos) font = pygame.font.Font(None, 20) textF = font.render(u'\u00b0' + "F", 1, GOLD1) textposF = textpos[0] + textpos[2], textpos[1] + 6 DISPLAYSURF.blit(textF, textposF) font = pygame.font.Font(None, 50) text = font.render(temp_c, 1, GOLD1) textpos = text.get_rect(center=(display_lcenter,96)) DISPLAYSURF.blit(text, textpos) font = pygame.font.Font(None, 20) textF = font.render(u'\u00b0' + "C", 1, GOLD1) textposF = textpos[0] + textpos[2], textpos[1] + 5 DISPLAYSURF.blit(textF, textposF) font = pygame.font.Font(None, 70) text = font.render(outtemp, 1, GOLD1) textpos = text.get_rect(center=(display_rcenter,56)) DISPLAYSURF.blit(text, textpos) font = pygame.font.Font(None, 20) textF = font.render(u'\u00b0' + "F", 1, GOLD1) textposF = textpos[0] + textpos[2], textpos[1] + 6 DISPLAYSURF.blit(textF, textposF) font = pygame.font.Font(None, 50) text = font.render(feeltemp, 1, GOLD1) textpos = text.get_rect(center=(display_rcenter,96)) DISPLAYSURF.blit(text, textpos) font = pygame.font.Font(None, 20) textF = font.render(u'\u00b0' + "F", 1, GOLD1) textposF = textpos[0] + textpos[2], textpos[1] + 5 DISPLAYSURF.blit(textF, textposF) font = pygame.font.Font(None, 20) text = font.render(weather_desc, 1, ROSYBROWN1) textpos = text.get_rect(center=(display_rcenter,126)) DISPLAYSURF.blit(text, textpos) ## Draw time currenttime = datetime.datetime.now() font = pygame.font.Font(None, 85) text = font.render(currenttime.strftime("%I:%M %p"), 1, WHITE) textpos = text.get_rect(center=(display_center,180)) DISPLAYSURF.blit(text, textpos) ## Draw date font = pygame.font.Font(None, 40) text = font.render(currenttime.strftime("%A, %b %d"), 1, ALICEBLUE) textpos = text.get_rect(center=(display_center,222)) DISPLAYSURF.blit(text, textpos) ## Update the LCD pygame.display.update() ## Sleep time! time.sleep(15)
Abbreviated Buster Steps:
buster lite with pi0 and ili9341
1. make sure you perform rpi-update to update to the latest firmware:
sudo rpi-update
2. sudo vi /boot/config.txt
- add to the end of the file:
dtoverlay=fbtft,ili9341,dc_pin=24,reset_pin=25
dtparam=speed=16000000
dtparam=custom=1
dtparam=bgr=1
dtparam=led_pin=18
dtparam=rotate=90
3. I use python3 so make sure that is ready:
sudo rm /usr/bin/python
sudo ln -s /usr/bin/python3 /usr/bin/python
sudo apt install python3-pip
sudo pip3 install pygame==1.9.6
4. SDL 1.2 gets installed using the following:
sudo apt-get install libsdl1.2-dev
sudo apt-get install libsdl-image1.2-dev
sudo apt-get install libsdl-ttf2.0-dev
If you read the earlier instructions for the Stretch version, you don't need the module setup that we did. You should be able to reboot the PI and see your display.
buster-lite with pi3b+ and 7 inch raspberry pi display
sudo vi /boot/config.txt
- add to the end of the file:
lcd_rotate=2
reboot
sudo raspi-config
Under System Options, set the following for your needs:
S1 Wireless LAN
S4 Hostname
S5 Boot / Auto Login
Under Display Options
D2 Underscan -> Yes to overscan
Interface Options
P2 SSH enable Yes
P7 1-Wire enable Yes
Configure Localization Options for your environment
L1 Locale
L2 Timezone
L3 Keyboard
Finish and reboot
Change the pi user password
passwd
Update and Install additional components:
sudo apt update
sudo apt -y upgrade
sudo rm /usr/bin/python
sudo ln -s /usr/bin/python3 /usr/bin/python
sudo apt install -y python3-pip
sudo apt install -y vim
sudo pip3 install pygame
sudo apt install -y libsdl2-2.0
sudo apt install -y python3-sdl2
start the clock