Saturday, June 28, 2025

Raspberry Pi Weather Clock

 I have a 2 line by 16 character LCD display with a I2C interface. I wanted to use it as a clock and date display which also could display the current weather.


Date and Time display
Date and Time Display


Weather Display
Weather Display
I am using a raspberry pi 5 to run a python script to drive the display using the GPIO pins

Rapsberry Pi 5 clock/weather


You will need an API key to open weather openweatherapi. Put your api key in an .env file as openweathermapapikey=yourkey. The python script uses the dotenv library to read this and use your API key. This is a free service with usage limitations. You will need to change the parameters to the open weather API to use you city and units.

        params = {
            "q": "Etobicoke,Ontario,CA",
            "units": "metric",  # For Celsius
            "appid": OPENWEATHERMAPAPIKEY  # API key
        }

#!/usr/bin/env python3
"""
lcd_weatherclock.py - A program that displays a clock on an LCD screen
and shows weather information for 10 seconds when a button is pressed.
"""

from LCD import LCD
import datetime
import time
import requests
import os
from dotenv import load_dotenv
from gpiozero import Button
import threading

# Load environment variables from .env file
load_dotenv()

# Initialize LCD
lcd = LCD(2, 0x27, True)
lcd.clear()

# Initialize button with debounce
button = Button(21, pull_up=True, bounce_time=0.2)  # 200ms debounce time

# Global variables
display_mode = "clock"  # Can be "clock" or "weather"
weather_timer = None

def get_etobicoke_weather():
    """
    Get current weather for Etobicoke, Ontario, Canada using OpenWeatherMap API
    """
    try:
        # OpenWeatherMap API endpoint for current weather
        url = "https://api.openweathermap.org/data/2.5/weather"

        # Get APP ID for weather api
        OPENWEATHERMAPAPIKEY = os.getenv("openweathermapapikey")
        # Parameters for Etobicoke, Ontario, Canada
        params = {
            "q": "Etobicoke,Ontario,CA",
            "units": "metric",  # For Celsius
            "appid": OPENWEATHERMAPAPIKEY  # API key
        }

        # Make the API request
        response = requests.get(url, params=params)

        # Check if the request was successful
        if response.status_code == 200:
            # Parse the JSON response
            weather_data = response.json()

            # Extract relevant information
            temperature = weather_data["main"]["temp"]
            condition = weather_data["weather"][0]["main"]
            humidity = weather_data["main"]["humidity"]

            return {
                "temperature": temperature,
                "condition": condition,
                "humidity": humidity
            }
        else:
            return {"error": f"API Error: {response.status_code}"}

    except Exception as e:
        return {"error": f"Error: {str(e)}"}

def display_weather(lcd):
    """
    Display weather data on the LCD
    """
    global display_mode
    
    # Set display mode to weather
    display_mode = "weather"
    
    # Show loading message
    lcd.clear()
    lcd.message("Weather...")
    
    # Get weather data
    weather_data = get_etobicoke_weather()
    
    lcd.clear()
    if "error" in weather_data:
        lcd.message("Weather Error")
        lcd.message(weather_data["error"][:16], 2)  # Truncate to fit LCD width
    else:
        # Format and display weather information
        temp_str = f"Temp: {weather_data['temperature']:.1f}C"
        cond_str = f"{weather_data['condition']} {weather_data['humidity']}%"

        lcd.message(temp_str)
        lcd.message(cond_str, 2)
    
    # Set a timer to switch back to clock after 10 seconds
    global weather_timer
    if weather_timer:
        weather_timer.cancel()
    weather_timer = threading.Timer(10.0, switch_to_clock)
    weather_timer.start()

def switch_to_clock():
    """
    Switch display back to clock mode
    """
    global display_mode
    display_mode = "clock"
    # The main loop will update the display on the next iteration

def display_clock(lcd, force_update=False):
    """
    Display clock on the LCD
    """
    now = datetime.datetime.now()
    currenttime = now.strftime("%H:%M:%S")
    currentdate = now.strftime("%d-%m-%Y")
    
    # Only update if time/date changed or force update is requested
    if force_update or currenttime != display_clock.lasttime or currentdate != display_clock.lastdate:
        lcd.clear()
        lcd.message('%02d:%02d:%02d' % (now.hour, now.minute, now.second), 1)
        lcd.message('%02d-%02d-%04d' % (now.day, now.month, now.year), 2)
        display_clock.lasttime = currenttime
        display_clock.lastdate = currentdate

# Initialize static variables for display_clock function
display_clock.lasttime = ''
display_clock.lastdate = ''

def button_pressed():
    """Function called when button is pressed"""
    global display_mode
    if display_mode == "clock":
        display_weather(lcd)

# Assign callback function to button press event
button.when_pressed = button_pressed

# Display welcome message
lcd.message("Weather Clock")
lcd.message("Starting...", 2)
time.sleep(2)
lcd.clear()

# Main loop
try:
    while True:
        if display_mode == "clock":
            display_clock(lcd)
        time.sleep(0.1)
except KeyboardInterrupt:
    # Clean up on exit
    if weather_timer:
        weather_timer.cancel()
    lcd.clear()
    print("\nExiting program")