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)
Text editor powered by tinymce.