Code PyPortal with CircuitPython

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

Adafruit IO username, and Adafruit IO key. Head to io.adafruit.com and simply click the View AIO Key link on the left hand side of the Adafruit IO page to get this information.

Then, add them to the secrets.py file:

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_'
    }

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_Smart_Thermometer.

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

Make sure to save the fonts (Ninito-Black-17.bdf and Ninito-Light-75.bdf) into the fonts folder on the CIRCUITPY volume and save pyportal_splash.bmp into the icons folder.

Rename the thermometer.py file to code.py so it will automatically run when the PyPortal restarts.

"""
PyPortal Smart Thermometer
==============================================
Turn your PyPortal into an internet-connected
thermometer with Adafruit IO

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

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

# thermometer graphics helper
import thermometer_helper

# rate at which to refresh the pyportal screen, in seconds
PYPORTAL_REFRESH = 2

# 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.)
try:
    ADAFRUIT_IO_USER = secrets['aio_username']
    ADAFRUIT_IO_KEY = secrets['aio_key']
except KeyError:
    raise KeyError('To use this code, you need to include your Adafruit IO username \
and password in a secrets.py file on the CIRCUITPY drive.')

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

# Get the temperature feed from Adafruit IO
temperature_feed = io.get_feed('temperature')

# init. graphics helper
gfx = thermometer_helper.Thermometer_GFX(celsius=False)

# init. adt7410
i2c_bus = busio.I2C(board.SCL, board.SDA)
adt = adafruit_adt7410.ADT7410(i2c_bus, address=0x48)
adt.high_resolution = True

# init. the light sensor
light_sensor = AnalogIn(board.LIGHT)

def set_backlight(val):
    """Adjust the TFT backlight.
    :param val: The backlight brightness. Use a value between ``0`` and ``1``, where ``0`` is
                off, and ``1`` is 100% brightness.
    """
    val = max(0, min(1.0, val))
    board.DISPLAY.auto_brightness = False
    board.DISPLAY.brightness = val

while True:
    # read the light sensor
    light_value = light_sensor.value
    print('Light Value: ', light_value)
    # read the temperature sensor
    temperature = adt.temperature
    try: # WiFi Connection
        if light_value < 1000: # turn on the backlight
            set_backlight(1)
            print('displaying temperature...')
            gfx.display_temp(temperature)
            # Get and display date and time form Adafruit IO
            print('Getting time from Adafruit IO...')
            datetime = io.receive_time()
            print('displaying time...')
            gfx.display_date_time(datetime)
        else: # turn off the backlight
            set_backlight(0)
        try: # send temperature data to IO
            gfx.display_io_status('Sending data...')
            print('Sending data to Adafruit IO...')
            io.send_data(temperature_feed['key'], temperature)
            print('Data sent!')
            gfx.display_io_status('Data sent!')
        except AdafruitIO_RequestError as e:
            raise AdafruitIO_RequestError('IO Error: ', e)
    except (ValueError, RuntimeError) as e: # WiFi Connection Failure
        print("Failed to get data, retrying\n", e)
        wifi.reset()
        continue
    time.sleep(PYPORTAL_REFRESH)

This is what the final contents of the CIRCUITPY drive will look like:

If you run into any errors, such as "ImportError: no module named `adafruit_display_text.label`" be sure to update your libraries to the latest release bundle!

Code Usage

Your PyPortal will boot up to a splash screen displaying the PyPortal logo along with the Analog Devices and Adafruit IO logos.

While the PyPortal seems like it's just displaying a splash screen - it is doing a lot of work behind the scenes! The PyPortal is loads in the two fonts this project requires and sets up labels for displaying text.

Wave your hand in front of the PyPortal's light sensor to turn on the display's backlight!

Your PyPortal Thermometer will display the current temperature reading, pull in the date and time from Adafruit IO (based off of your IP address), and send the data to Adafruit IO.

When it finishes sending data, it'll turn the display back off but continue to send data to Adafruit IO.

Why is the backlight off by default?

When the backlight is turned on, it produces heat. This interferes with the ADT7410's ambient temperature readings. 

Adafruit IO Usage

While the PyPortal thermostat can display its temperature along with the current date/time on its screen - what if you're physically away from the thermostat?

How do we know that the temperature data is being sent from the thermostat to Adafruit IO?

Open the Adafruit IO Dashboard you created earlier. Notice that the fill and values of the gauge changes as values are sent from your PyPortal to Adafruit IO.

Then, leave the PyPortal running for a while and come back later to see new data appear on the line graph.

PyPortal Customization

Displaying temperature in Fahrenheit 

Live in a region where Fahrenheit is the standard? The thermometer_helper can handle displaying the temperature as either Fahrenheit or Celsius.

To display the temperature in Fahrenheit, modify the following line in code.py from:

gfx = thermometer_helper.Thermometer_GFX()

to

gfx = thermometer_helper.Thermometer_GFX(celsius=False)

Changing fonts

Want to use a different font? The fonts for this project are referenced at the top of the thermometer_helper.py file as info_font and temperature_font

The PyPortal reads .BDF (bitmap distribution format) fonts, so you'll need to convert a font into this format, and then modify the code to use the new font.

For more information about converting fonts, read the learning guide here...

Custom Wall Mount

The wall mount used in this project PyPortal was created by the Ruiz Brothers. For detailed instructions about how to print your own, check out the learning system guide here.

This guide was first published on Mar 22, 2019. It was last updated on Mar 22, 2019. This page (Code PyPortal with CircuitPython) was last updated on Sep 21, 2019.