CircuitPython Library Installation

First make sure you are running the latest version of Adafruit CircuitPython for your board.

Next you'll need to install the necessary libraries to use the hardware--carefully follow the steps to find and install these libraries from Adafruit's CircuitPython library bundle.  For example the Circuit Playground Express guide has a great page on how to install the library bundle for both express and non-express boards.

Before continuing make sure your board's lib folder has the following files and folders copied over.

  • adafruit_bitmap_font
  • adafruit_bme_280
  • adafruit_bus_device
  • adafruit_display_text
  • adafruit_esp32spi
  • adafruit_io
  • adafruit_sgp30
  • adafruit_veml6075
  • neopixel
  • simpleio

Next connect to the board's serial REPL so you are at the CircuitPython >>> prompt.

Once you have your CircuitPython libraries installed, let's get your PyPortal connected to Adafruit IO and the internet. To this, you'll create a secrets file.

Secrets File Setup

If you have not yet set up a secrets.py file in your CIRCUITPY drive and connected to the internet using it, follow this guide and come back when you've successfully connected to the internet

With a secrets.py file on your CIRCUITPY drive - add your Adafruit IO Username and Adafruit IO Key to the secrets.py file.

Your secrets.py file should look like this:

Download: file
secrets = {
    'ssid' : '_your_wifi_ssid',
    'password': '_your_wifi_password',
    'aio_username' : '_your_adafruit_io_username',
    'aio_key' : '_your_big_huge_super_long_aio_key_'
    }

After you finish editing secrets.py, make sure to save the file (cmd/ctrl+s).

Add CircuitPython Code and Project Assets

In the embedded code element below, click on the Download: Project Zip link, and save the .zip archive file to your computer.

Then, uncompress the .zip file, it will unpack to a folder named pyportal_weatherstation.

Copy the contents of the pyportal_weatherstation directory to your PyPortal's CIRCUITPY drive.

"""
PyPortal Weather Station
==============================================
Turn your PyPortal into a weatherstation with
Adafruit IO

Author: Brent Rubell for Adafruit Industries, 2019
"""
import time
import board
import neopixel
import busio
import analogio
from simpleio import map_range
from digitalio import DigitalInOut

from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager
from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError

# sensor libs
import adafruit_veml6075
import adafruit_sgp30
import adafruit_bme280

# weatherstation graphics helper
import weatherstation_helper

# rate at which to refresh the pyportal and sensors, in seconds
PYPORTAL_REFRESH = 30

# anemometer defaults
anemometer_min_volts = 0.4
anemometer_max_volts = 2.0
min_wind_speed = 0.0
max_wind_speed = 32.4

# Get wifi details and more from a secrets.py file
try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

# PyPortal ESP32 Setup
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)

# Set your Adafruit IO Username and Key in secrets.py
# (visit io.adafruit.com if you need to create an account,
# or if you need your Adafruit IO key.)
ADAFRUIT_IO_USER = secrets['aio_username']
ADAFRUIT_IO_KEY = secrets['aio_key']

# Create an instance of the Adafruit IO HTTP client
io = IO_HTTP(ADAFRUIT_IO_USER, ADAFRUIT_IO_KEY, wifi)

# create an i2c object
i2c = busio.I2C(board.SCL, board.SDA)

# instantiate the sensor objects
veml = adafruit_veml6075.VEML6075(i2c, integration_time=100)
sgp30 = adafruit_sgp30.Adafruit_SGP30(i2c)
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c)
# change this to match the location's pressure (hPa) at sea level
bme280.sea_level_pressure = 1013.25

# init. the graphics helper
gfx = weatherstation_helper.WeatherStation_GFX()

# init. the ADC
adc = analogio.AnalogIn(board.D4)

# Set up Adafruit IO Feeds
print('Getting Group data from Adafruit IO...')
station_group = io.get_group('weatherstation')
feed_list = station_group['feeds']
altitude_feed = feed_list[0]
eco2_feed = feed_list[1]
humidity_feed = feed_list[2]
pressure_feed = feed_list[3]
temperature_feed = feed_list[4]
tvoc_feed = feed_list[5]
uv_index_feed = feed_list[6]
wind_speed_feed = feed_list[7]

def adc_to_wind_speed(val):
    """Returns anemometer wind speed, in m/s.
    :param int val: ADC value
    """
    voltage_val = val / 65535 * 3.3
    return map_range(voltage_val, 0.4, 2, 0, 32.4)

def send_to_io():
    # handle sending sensor data to Adafruit IO
    io.send_data(uv_index_feed['key'], uv_index)
    io.send_data(wind_speed_feed['key'], wind_speed)
    io.send_data(temperature_feed['key'], bme280_data[0])
    io.send_data(humidity_feed['key'], bme280_data[1])
    io.send_data(pressure_feed['key'], bme280_data[2])
    io.send_data(altitude_feed['key'], bme280_data[3])
    io.send_data(eco2_feed['key'], sgp_data[0])
    io.send_data(tvoc_feed['key'], sgp_data[1])

while True:
    print('obtaining sensor data...')
    # Get uv index from veml6075
    uv_index = veml.uv_index
    # Get eco2, tvoc from sgp30
    eCO2, TVOC = sgp30.iaq_measure()
    sgp_data = [eCO2, TVOC]
    # Store bme280 data as a list
    bme280_data = [bme280.temperature, bme280.humidity,
                   bme280.pressure, bme280.altitude]
    # Get wind speed
    wind_speed = adc_to_wind_speed(adc.value)
    # Display sensor data on PyPortal using the gfx helper
    print('displaying sensor data...')
    gfx.display_data(uv_index, bme280_data,
                     sgp_data, wind_speed)
    print('sensor data displayed!')
    try:
        try:
            print('Sending data to Adafruit IO...')
            gfx.display_io_status('Sending data to IO...')
            send_to_io()
            gfx.display_io_status('Data Sent!')
            print('Data sent!')
        except AdafruitIO_RequestError as e:
            raise AdafruitIO_RequestError('IO Error: ', e)
    except (ValueError, RuntimeError) as e:
        print("Failed to get data, retrying\n", e)
        wifi.reset()
        continue
    time.sleep(PYPORTAL_REFRESH)

Before running the code, verify CIRCUITPY looks like the following:

From the Mu Editor, click the Serial button to open the REPL. The output should look like the following:

Download: file
code.py output:
Set icon to  /icons/pyportal_splash.bmp
loading fonts...
setting up textareas...
Getting Group data from Adafruit IO...
obtaining sensor data...
displaying sensor data...
UV Index:  0.0110945
Temperature: 24.7 C
Humidity: 10.2%
Wind Speed: 0.090100 m/s
Altitude: -98.900 meters, Pressure: 1025.18 hPa
eCO2 = 400 ppm   TVOC = 0 ppb

Code Usage

The code will first attempt to initialize all of the sensors, libraries, and get the Adafruit IO weatherstation group.

If everything worked properly, you should see the PyPortal and Adafruit IO+ Logo appear on your PyPortal.

Shortly after the splash screen disappears, you should see the sensor text fields appear. 

The code will first attempt to initialize all of the sensors, libraries, and get the Adafruit IO weatherstation group.

If everything worked properly, you should see the PyPortal and Adafruit IO+ Logo appear on your PyPortal.

Shortly after the splash screen disappears, you should see the sensor text fields appear. 

Every 30 seconds, the PyPortal will obtain new data from the sensors and send it to Adafruit IO.

  • To change the rate at which the PyPortal samples and sends data (in seconds), modify the PYPORTAL_REFRESH variable.

When the data is sent from the weather station to Adafruit IO, the status text area will change to Data Sent!

But how do we know the data was actually sent to Adafruit IO?

Adafruit IO Usage

While your PyPortal displays Data Sent!, how do you know the data reached the Adafruit IO service?

The monitor page on Adafruit IO displays a live view off incoming data (and errors!). 

Navigate to the Adafruit IO Monitor page. Only data received while the tab is open will be displayed on the page.

Wait for the PyPortal to send data to Adafruit IO. You should see incoming data appear underneath the Live Data heading.

This guide was first published on Apr 02, 2019. It was last updated on Apr 02, 2019.

This page (CircuitPython Code) was last updated on Nov 06, 2020.