How to Build a Raspberry Pi Frost Alert Using a Weather API

A Raspberry Pi, three LEDs, and a weather forecast are all you need to build a simple frost warning system.

In this tutorial, we’ll use the Visual Crossing Weather API to retrieve forecast minimum temperatures for a location. A Python script running on the Raspberry Pi will analyze the forecast and illuminate one of three LEDs:

  • Red: freezing temperatures are forecast
  • Yellow: cold temperatures are forecast, but not freezing
  • Green: no cold temperatures are forecast

The project provides a simple example of combining live weather data with Raspberry Pi GPIO hardware.

We’ll use the Visual Crossing Timeline Weather API, Python 3, and the GPIO Zero library recommended by Raspberry Pi for controlling GPIO devices.

What you’ll need

For this project you’ll need:

  • A network-connected Raspberry Pi
  • Raspberry Pi OS
  • Three LEDs: red, yellow, and green
  • Three suitable current-limiting resistors, such as 220–330 Ω
  • Breadboard and jumper wires
  • Python 3
  • A Visual Crossing Weather API key

If you’re new to the Visual Crossing Weather API, see:https://www.visualcrossing.com/resources/documentation/weather-api/how-do-i-get-started-with-the-weather-api/

The complete Timeline Weather API documentation is available at:https://www.visualcrossing.com/resources/documentation/weather-api/timeline-weather-api/

How the frost alert works

Our Raspberry Pi will periodically retrieve the weather forecast for a specified location.

We’ll examine the minimum temperature for the next three forecast days.

The alert status will be:

Red     Minimum forecast temperature <= 32°F
Yellow  Minimum forecast temperature <= 40°F
Green   Minimum forecast temperature > 40°F

For metric units, the equivalent defaults are:

Red     Minimum forecast temperature <= 0°C
Yellow  Minimum forecast temperature <= 4°C
Green   Minimum forecast temperature > 4°C

The thresholds and number of forecast days are configuration values, so you can easily adjust them for your application.

Using a defined three-day alert period is more useful than checking the entire available forecast. If freezing weather is forecast far into the future, you may not want the warning light to remain red continuously for many days.

Step 1 – Wire the LEDs to the Raspberry Pi

We’ll use three GPIO pins:

GPIO 23 – red LED
GPIO 24 – yellow LED
GPIO 25 – green LED

Each LED should be connected through a current-limiting resistor.

For example:

GPIO pin → resistor → LED → ground

Do not connect an LED directly to a GPIO output without a resistor.

You can view the GPIO layout for your particular Raspberry Pi by running:

pinout

Raspberry Pi provides additional GPIO documentation here:

https://www.raspberrypi.com/documentation/computers/raspberry-pi.html

Step 2 – Install the Python libraries

This project uses:

  • gpiozero to control the LEDs
  • requests to retrieve weather data

On Raspberry Pi OS, install them using:

sudo apt update
sudo apt install -y python3-gpiozero python3-requests

GPIO Zero is designed specifically to simplify physical computing projects on Raspberry Pi.

The Raspberry Pi GPIO documentation is available at:

https://www.raspberrypi.com/documentation/usage/iot

Step 3 – Create a Visual Crossing Weather API key

Create or sign in to your Visual Crossing Weather account and obtain your Weather API key.

You can learn more about the available developer services at:https://www.visualcrossing.com/weather-api/

You can also interactively explore historical and forecast datasets using Visual Crossing Weather Data.

Visual Crossing plans include usage and request limits. Your application should be designed to operate within the limits of your account plan and avoid unnecessarily repeating identical requests.

Step 4 – Store the API key securely

Rather than placing the API key directly in the Python source file, store it in an environment variable.

From the Raspberry Pi terminal:

export VISUAL_CROSSING_API_KEY="YOUR_API_KEY"

Our Python script can then read it using:

import os

API_KEY = os.environ["VISUAL_CROSSING_API_KEY"]

This helps prevent the API key from accidentally being committed to a public source-code repository.

If you configure the script to start automatically, make sure the environment variable is also available to the service or scheduled process that runs it.

Step 5 – Test the weather forecast request

The Timeline Weather API uses a simple URL structure.

For example, this request retrieves forecast data for Chicago:

https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/Chicago%2CIL?unitGroup=us&include=days&key=YOUR_API_KEY&contentType=json

For our frost alert, we don’t need every available weather field.

We only need:

datetime
tempmin
conditions

We can therefore add:

elements=datetime,tempmin,conditions

The complete request becomes:

https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/Chicago%2CIL?unitGroup=us&include=days&elements=datetime,tempmin,conditions&key=YOUR_API_KEY&contentType=json

Daily forecast records are returned in the Timeline API days array.

For example:

weather["days"][0]["tempmin"]

returns the forecast minimum temperature for the first day.

For more information about the available weather fields, see the Visual Crossing Weather Data documentation.

Step 6 – Retrieve the forecast with Python

Create a file named:

frost_alert.py

Start with the following code:

import os
import requests

API_KEY = os.environ["VISUAL_CROSSING_API_KEY"]

LOCATION = "Chicago, IL"
UNIT_GROUP = "us"

FORECAST_DAYS = 3

FREEZING_TEMP = 32
COLD_TEMP = 40


def get_weather_forecast():
    url = (
        "https://weather.visualcrossing.com/"
        "VisualCrossingWebServices/rest/services/timeline/"
        + requests.utils.quote(LOCATION, safe="")
    )

    params = {
        "key": API_KEY,
        "unitGroup": UNIT_GROUP,
        "include": "days",
        "elements": "datetime,tempmin,conditions",
        "contentType": "json"
    }

    response = requests.get(
        url,
        params=params,
        timeout=15
    )

    response.raise_for_status()

    return response.json()

requests.get() retrieves the forecast from the Weather API.

The params dictionary contains the query parameters that will be added to the request.

We also specify:

timeout=15

so that a temporary network problem does not cause the Raspberry Pi to wait indefinitely.

Finally:

response.raise_for_status()

causes HTTP errors to be reported rather than attempting to process an unsuccessful response as weather data.

Step 7 – Determine the frost warning level

Next, we’ll examine the first few forecast days and find the lowest predicted temperature.

Add:

def get_alert_status(weather):
    days = weather["days"][:FORECAST_DAYS]

    if not days:
        raise ValueError("Weather API returned no forecast days")

    coldest_day = min(
        days,
        key=lambda day: day["tempmin"]
    )

    lowest_temp = coldest_day["tempmin"]

    if lowest_temp <= FREEZING_TEMP:
        status = "freezing"
    elif lowest_temp <= COLD_TEMP:
        status = "cold"
    else:
        status = "normal"

    return status, coldest_day

The expression:

weather["days"][:FORECAST_DAYS]

limits our frost analysis to the configured alert period.

Then:

min(days, key=lambda day: day["tempmin"])

finds the day with the lowest forecast minimum temperature.

This gives us both the warning status and the day responsible for the alert.

Step 8 – Control the LEDs with GPIO Zero

Now add GPIO Zero:

from gpiozero import LED

Create the three LED objects:

red_led = LED(23)
yellow_led = LED(24)
green_led = LED(25)

Then create a function that displays the current alert:

def display_alert(status):
    red_led.off()
    yellow_led.off()
    green_led.off()

    if status == "freezing":
        red_led.on()
    elif status == "cold":
        yellow_led.on()
    else:
        green_led.on()

Only one status LED will be illuminated at a time.

Step 9 – Periodically update the frost forecast

Weather forecasts don’t need to be downloaded every few seconds.

For this example, we’ll refresh the forecast every 30 minutes:

UPDATE_INTERVAL = 30 * 60

You could choose a different interval depending on your application, but repeatedly downloading the same forecast every few seconds provides little benefit and unnecessarily increases API usage.

Our main loop becomes:

import time


def main():
    while True:
        try:
            print("Retrieving weather forecast...")

            weather = get_weather_forecast()

            status, coldest_day = get_alert_status(
                weather
            )

            display_alert(status)

            print(
                f"Status: {status}, "
                f"date: {coldest_day['datetime']}, "
                f"minimum temperature: "
                f"{coldest_day['tempmin']}"
            )

        except requests.RequestException as error:
            print(
                "Unable to retrieve weather forecast:",
                error
            )

        except Exception as error:
            print(
                "Unable to process weather forecast:",
                error
            )

        time.sleep(UPDATE_INTERVAL)

If a Weather API or network error occurs, the program reports the error and tries again at the next scheduled update rather than terminating immediately.

Step 10 – Complete Raspberry Pi frost alert script

Here is the complete program:

import os
import time
import requests

from gpiozero import LED


API_KEY = os.environ["VISUAL_CROSSING_API_KEY"]

LOCATION = "Chicago, IL"
UNIT_GROUP = "us"

FORECAST_DAYS = 3

FREEZING_TEMP = 32
COLD_TEMP = 40

UPDATE_INTERVAL = 30 * 60


red_led = LED(23)
yellow_led = LED(24)
green_led = LED(25)


def get_weather_forecast():
    url = (
        "https://weather.visualcrossing.com/"
        "VisualCrossingWebServices/rest/services/timeline/"
        + requests.utils.quote(LOCATION, safe="")
    )

    params = {
        "key": API_KEY,
        "unitGroup": UNIT_GROUP,
        "include": "days",
        "elements": "datetime,tempmin,conditions",
        "contentType": "json"
    }

    response = requests.get(
        url,
        params=params,
        timeout=15
    )

    response.raise_for_status()

    return response.json()


def get_alert_status(weather):
    days = weather["days"][:FORECAST_DAYS]

    if not days:
        raise ValueError(
            "Weather API returned no forecast days"
        )

    coldest_day = min(
        days,
        key=lambda day: day["tempmin"]
    )

    lowest_temp = coldest_day["tempmin"]

    if lowest_temp <= FREEZING_TEMP:
        status = "freezing"
    elif lowest_temp <= COLD_TEMP:
        status = "cold"
    else:
        status = "normal"

    return status, coldest_day


def display_alert(status):
    red_led.off()
    yellow_led.off()
    green_led.off()

    if status == "freezing":
        red_led.on()
    elif status == "cold":
        yellow_led.on()
    else:
        green_led.on()


def main():
    while True:
        try:
            print("Retrieving weather forecast...")

            weather = get_weather_forecast()

            status, coldest_day = get_alert_status(
                weather
            )

            display_alert(status)

            print(
                f"Status: {status}, "
                f"date: {coldest_day['datetime']}, "
                f"minimum temperature: "
                f"{coldest_day['tempmin']}"
            )

        except requests.RequestException as error:
            print(
                "Unable to retrieve weather forecast:",
                error
            )

        except Exception as error:
            print(
                "Unable to process weather forecast:",
                error
            )

        time.sleep(UPDATE_INTERVAL)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\nFrost alert stopped.")

Run the script with:

python3 frost_alert.py

The appropriate LED should illuminate after the first forecast has been retrieved.

Using metric temperatures

The example above uses US units:

UNIT_GROUP = "us"

FREEZING_TEMP = 32
COLD_TEMP = 40

For metric temperatures, change these values to:

UNIT_GROUP = "metric"

FREEZING_TEMP = 0
COLD_TEMP = 4

You can also customize these thresholds to match the plants, equipment, or other conditions that you are trying to protect.

Improving the frost alert

This project intentionally uses simple daily minimum temperatures, but there are many ways to extend it.

For example, you could:

  • Examine hourly temperature forecasts to estimate when freezing conditions will begin.
  • Flash the red LED when freezing conditions are expected within the next few hours.
  • Add a buzzer or display.
  • Send an email, SMS, or push notification.
  • Monitor multiple locations.
  • Record forecast temperatures to a local database.
  • Compare the forecast against historical weather.
  • Include wind, precipitation, snow, humidity, or other weather variables.
  • Use different temperature thresholds for different plants or equipment.

Hourly forecasts are available in each Timeline Weather API day’s hours array when hourly data is requested.

The same Visual Crossing Weather API can provide historical weather, current conditions, hourly forecasts, daily forecasts, weather alerts, and additional environmental data.

If you want to explore those datasets interactively rather than through code, visit Visual Crossing Weather Data.

API usage

Visual Crossing plans include usage and request limits that should be followed when building automated applications.

A frost alert does not normally need to retrieve a completely new forecast every few seconds. Choose an update interval appropriate for your application and avoid repeatedly requesting identical information when it can be reused.

See the Visual Crossing Weather API page for current API options and account plans.

Next steps

This simple frost warning system demonstrates how easily physical devices can respond to forecast weather information.

From here you can explore:

The same approach can be adapted to create weather-driven irrigation systems, freeze protection monitors, outdoor equipment alerts, greenhouse controllers, and many other Raspberry Pi weather projects.