Once you've finished setting up your Pico W with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.
# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
# SPDX-License-Identifier: MIT
import os
import ssl
import time
import microcontroller
import board
import wifi
import socketpool
import adafruit_requests
import neopixel
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
from adafruit_io.adafruit_io import IO_HTTP
# latitude
lat = 38.58
# longitude
long = -121.49
# hours in 24 hour time that the pixels should be off
hours_off = (0, 6)
# color of the pixels when rain is expected
PIXELS_COLOR = (0, 0, 255)
# neopixel setup
NUMPIXELS = 30 # number of neopixels
BRIGHTNESS = 0.5 # A number between 0.0 and 1.0, where 0.0 is off, and 1.0 is max.
PIN = board.GP0
pixels = neopixel.NeoPixel(PIN, NUMPIXELS, brightness=BRIGHTNESS, auto_write=False)
# turn on NeoPixels on boot to check wiring
pixels.fill(PIXELS_COLOR)
pixels.show()
# API request to open-meteo
weather_url = "https://api.open-meteo.com/v1/forecast?"
# pass latitude and longitude
# will return sunrise and sunset times
weather_url += "latitude=%d&longitude=%d&timezone=auto&hourly=rain&forecast_days=1" % (lat, long)
# connect to SSID
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
pool = socketpool.SocketPool(wifi.radio)
# adafruit IO info
aio_username = os.getenv('aio_username')
aio_key = os.getenv('aio_key')
# io HTTP for getting the time from the internet
io = IO_HTTP(aio_username, aio_key, requests)
def reset_on_error(delay, error):
print("Error:\n", str(error))
print("Resetting microcontroller in %d seconds" % delay)
time.sleep(delay)
microcontroller.reset()
# function for making http requests with try/except
def get_request(tries, ping):
for i in range(tries):
try:
n = ping
except Exception as error:
print(error)
time.sleep(10)
if i < tries - 1:
continue
raise
break
return n
# pylint: disable=broad-except
# function to make a request to open-meteo & time from IO
def rain_check():
# gets current time
now = get_request(5, io.receive_time())
h = now.tm_hour
time.sleep(1)
# make the API request
weather_call = get_request(5, requests.get(weather_url))
# packs the response into a JSON
response_as_json = weather_call.json()
# gets rain forecast
_chance = response_as_json['hourly']['rain']
return h, _chance
# ticks time tracker
clock = ticks_ms()
# tracker for initial start-up state
first_run = True
# 15 minutes in milliseconds
time_check = 900000
while True:
try:
# every 15 minutes...
if first_run or ticks_diff(ticks_ms(), clock) > time_check:
print("pinging Open-Meteo")
hour, rain_chance = rain_check()
print("Rain expected: %.2f mm" % rain_chance[hour])
if hour in hours_off:
print("sleeping, please don't tell me it's raining")
color = (0, 0, 0)
else:
# if rain is expected. turn pixels blue
if rain_chance[hour] > 0:
print("tut tut, looks like rain")
color = PIXELS_COLOR
# otherwise turn pixels off
else:
print("no rain expected")
color = (0, 0, 0)
if first_run:
first_run = False
pixels.fill(color)
pixels.show()
# reset clock
clock = ticks_add(clock, time_check)
except Exception as e:
reset_on_error(10, e)
Upload the Code and Libraries to the Pico W
After downloading the Project Bundle, plug your Pico W into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the Pico W's CIRCUITPY drive.
- lib folder
- code.py
Your Pico W CIRCUITPY drive should look like this after copying the lib folder and the code.py file:
Add Your settings.toml File
As of CircuitPython 8, there is support for Environment Variables. These Environmental Variables are stored in a settings.toml file. Similar to secrets.py, the settings.toml file separates your sensitive information from your main code.py file. Be sure to add your settings.toml file to your CIRCUITPY drive. You'll need to include your CIRCUITPY_WIFI_SSID, CIRCUITPY_WIFI_PASSWORD, aio_username and aio_key in the file.
CIRCUITPY_WIFI_SSID = "your-wifi-ssid-here" CIRCUITPY_WIFI_PASSWORD = "your-wifi-password-here" aio_username = "your-Adafruit-IO-username-here" aio_key = "your-Adafruit-IO-key-here"
How the CircuitPython Code Works
At the top of the code, you can define some variables for your location's latitude and longitude, the hour range that you want the NeoPixels to be off and the color of the NeoPixels when rain is predicted.
# latitude lat = 40.73 # longitude long = -74.01 # hours in 24 hour time that the pixels should be off hours_off = (0, 6) # color of the pixels when rain is expected PIXELS_COLOR = (0, 0, 255)
TIP: You can find your latitude and longitude in most phone navigation apps, or by entering your address in Google Maps and right-clicking the “pin.” First number is latitude (northern hemisphere locations are positive, southern are negative), second is latitude (negative is west of the Greenwich prime meridian, positive is east).
1–2 decimal place is sufficient for this task, weather being a regional phenomenon.
# neopixel setup NUMPIXELS = 30 # number of neopixels BRIGHTNESS = 0.5 # A number between 0.0 and 1.0, where 0.0 is off, and 1.0 is max. PIN = board.GP0 pixels = neopixel.NeoPixel(PIN, NUMPIXELS, brightness=BRIGHTNESS, auto_write=False) # turn on NeoPixels on boot to check wiring pixels.fill(PIXELS_COLOR) pixels.show()
Weather Request
The weather_url holds the URL for the Open-Meteo HTTP request. The URL passes your latitude and longitude that was defined earlier in the code to get the hourly rain forecast for your location.
# API request to open-meteo weather_url = "https://api.open-meteo.com/v1/forecast?" # pass latitude and longitude # will return sunrise and sunset times weather_url += "latitude=%d&longitude=%d&timezone=auto&hourly=rain&forecast_days=1" % (lat, long)
Connect to WiFi
The Pico W connects to your WiFi network by accessing your SSID and SSID password in the settings.toml file with os.getenv(). Additionally, the Pico W connects to Adafruit IO to get the current time.
# connect to SSID
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
pool = socketpool.SocketPool(wifi.radio)
# adafruit IO info
aio_username = os.getenv('aio_username')
aio_key = os.getenv('aio_key')
location = "America/Los Angeles"
# io HTTP for getting the time from the internet
io = IO_HTTP(aio_username, aio_key, requests)
Error Functions
To handle any errors that come up, two functions are defined. reset_on_error() runs in the event of an error. It prints the error to the REPL and then resets the Pico W to start the code over. get_request() makes an HTTP request and wraps it in a try/except loop so that multiple attempts can be made in the event of an error or timeout.
def reset_on_error(delay, error):
print("Error:\n", str(error))
print("Resetting microcontroller in %d seconds" % delay)
time.sleep(delay)
microcontroller.reset()
# function for making http requests with try/except
def get_request(tries, ping):
for i in range(tries):
try:
n = ping
except Exception as error:
print(error)
time.sleep(10)
if i < tries - 1:
continue
raise
break
return n
What's the Weather?
The rain_check() function uses the get_request() function to get the time from Adafruit IO and make a request to Open-Meteo with the previously defined weather_url. The function returns the current hour and the rain forecast.
def rain_check():
# gets current time
now = get_request(5, io.receive_time())
h = now.tm_hour
time.sleep(1)
# make the API request
weather_call = get_request(5, requests.get(weather_url))
# packs the response into a JSON
response_as_json = weather_call.json()
# gets rain forecast
_chance = response_as_json['hourly']['rain']
return h, _chance
The Loop
In the loop, every 15 minutes the rain_check() function runs to get the rain forecast. If the current time falls in the range of the defined hours_off, then the NeoPixels are turned off. Otherwise, if any rain is expected, the NeoPixels turn on and if no rain is expected they are turned off.
try:
# every 15 minutes...
if first_run or ticks_diff(ticks_ms(), clock) > time_check:
print("pinging Open-Meteo")
hour, rain_chance = rain_check()
print("Rain expected: %.2f mm" % rain_chance[hour])
if hour in hours_off:
print("sleeping, please don't tell me it's raining")
color = (0, 0, 0)
else:
# if rain is expected. turn pixels blue
if rain_chance[hour] > 0:
print("tut tut, looks like rain")
color = PIXELS_COLOR
# otherwise turn pixels off
else:
print("no rain expected")
color = (0, 0, 0)
if first_run:
first_run = False
else:
pixels.fill(color)
pixels.show()
# reset clock
clock = ticks_add(clock, time_check)
except Exception as e:
reset_on_error(10, e)
Page last edited January 21, 2025
Text editor powered by tinymce.