Setup PyPortal with CircuitPython

We'll need to get our PyPortal board setup so it can run CircuitPython code. Let's walk through these steps to get the latest version of CircuitPython onto your board

The Mu Python Editor

Mu is a simple Python editor that works with Adafruit CircuitPython hardware. It's written in Python and works on Windows, MacOS, Linux and Raspberry Pi. The serial console is built right in, so you get immediate feedback from your board's serial output! While you can use any text editor with your code, Mu makes it super simple.

Installing or upgrading CircuitPython

You should ensure you have CircuitPython 4.0 or greater on your board. Plug your board in with a known good data + power cable (not the cheesy USB cable that comes with USB power packs, they are power only). You should see a new flash drive pop up.

If the drive is CIRCUITPY, then open the boot_out.txt file to ensure the version number is 4.0 or greater. 

You can download everything that you need for the PyPortal code by downloading the following Zip file and copying its contents to your PyPortals CIRCUITPY folder.

Adafruit CircuitPython 5.0.0-beta.0 on 2019-11-19; Adafruit PyPortal with samd51g19

Upload the Code and Files

Click on the Download Project Bundle button below to grab the main code and other files noted (except the library files) directly from GitHub (the repository is here). Drop the files onto the CIRCUITPY main (root) directory (with the font file in a fonts directory). The code will run properly when all of the files have been uploaded including libraries.

Use any text editor or favorite IDE to modify the code. We suggest using Mu as noted above.

# SPDX-FileCopyrightText: 2020 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import board
import displayio
import busio
from digitalio import DigitalInOut
from analogio import AnalogIn
import neopixel
import adafruit_adt7410
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label
from adafruit_button import Button
import adafruit_touchscreen
import adafruit_minimqtt.adafruit_minimqtt as MQTT

# ------------- WiFi ------------- #

# 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

# If you are using a board with pre-defined ESP32 Pins:
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)

# ------- Sensor Setup ------- #
# init. the temperature sensor
i2c_bus = busio.I2C(board.SCL, board.SDA)
adt = adafruit_adt7410.ADT7410(i2c_bus, address=0x48)
adt.high_resolution = True
temperature = "blaa"
# init. the light sensor
light_sensor = AnalogIn(board.LIGHT)

# init. the motion sensor
movement_sensor = DigitalInOut(board.D3)

button1_state = 0
button2_state = 0

# ------------- Screen eliments ------------- #

display = board.DISPLAY

# Backlight function
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))
    try:
        board.DISPLAY.auto_brightness = False
    except AttributeError:
        pass
    board.DISPLAY.brightness = val


# Touchscreen setup
ts = adafruit_touchscreen.Touchscreen(
    board.TOUCH_XL,
    board.TOUCH_XR,
    board.TOUCH_YD,
    board.TOUCH_YU,
    calibration=((5200, 59000), (5800, 57000)),
    size=(320, 240),
)

# ---------- Set the font and preload letters ----------
# Be sure to put your font into a folder named "fonts".
font = bitmap_font.load_font("/fonts/Helvetica-Bold-16.bdf")
# This will preload the text images.
font.load_glyphs(b"abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890- ()")

# ------------- User Inretface Eliments ------------- #

# Make the display context
splash = displayio.Group()
board.DISPLAY.root_group = splash

# Make a background color fill
color_bitmap = displayio.Bitmap(320, 240, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x3D0068
bg_sprite = displayio.TileGrid(color_bitmap, x=0, y=0, pixel_shader=color_palette)
splash.append(bg_sprite)

buttons = []
# Default button styling:
BUTTON_WIDTH = 100
BUTTON_HEIGHT = 100
BUTTON_MARGIN = 10

# Button Objects
button_1 = Button(
    x=BUTTON_MARGIN,
    y=BUTTON_MARGIN,
    width=BUTTON_WIDTH,
    height=BUTTON_HEIGHT,
    label="Button 1",
    label_font=font,
    style=Button.SHADOWROUNDRECT,
    label_color=0x505050,
    fill_color=0x9E9E9E,
    outline_color=0x464646,
)
buttons.append(button_1)

button_2 = Button(
    x=BUTTON_MARGIN,
    y=BUTTON_MARGIN * 2 + BUTTON_HEIGHT,
    width=BUTTON_WIDTH,
    height=BUTTON_HEIGHT,
    label="Button 2",
    label_font=font,
    style=Button.SHADOWROUNDRECT,
    label_color=0x505050,
    fill_color=0x9E9E9E,
    outline_color=0x464646,
)
buttons.append(button_2)

for b in buttons:
    splash.append(b.group)

# Text Label Objects
temperature_label = Label(font, text="temperature", color=0xE300D2)
temperature_label.x = 130
temperature_label.y = 20
splash.append(temperature_label)

light_label = Label(font, text="lux", color=0xE300D2)
light_label.x = 130
light_label.y = 40
splash.append(light_label)

motion_label = Label(font, text="motion", color=0xE300D2)
motion_label.x = 130
motion_label.y = 60
splash.append(motion_label)

feed1_label = Label(font, text="MQTT feed1", color=0xE39300)
feed1_label.x = 130
feed1_label.y = 130
splash.append(feed1_label)

feed2_label = Label(font, text="MQTT feed2", color=0x00DCE3)
feed2_label.x = 130
feed2_label.y = 200
splash.append(feed2_label)

# ------------- MQTT Topic Setup ------------- #

mqtt_topic = "test/topic"
mqtt_temperature = "pyportal/temperature"
mqtt_lux = "pyportal/lux"
mqtt_PIR = "pyportal/pir"
mqtt_button1 = "pyportal/button1"
mqtt_button2 = "pyportal/button2"
mqtt_feed1 = "pyportal/feed1"
mqtt_feed2 = "pyportal/feed2"

# ------------- MQTT Functions ------------- #

# Define callback methods which are called when events occur
# pylint: disable=unused-argument, redefined-outer-name
def connect(client, userdata, flags, rc):
    # This function will be called when the client is connected
    # successfully to the broker.
    print("Connected to MQTT Broker!")
    print("Flags: {0}\n RC: {1}".format(flags, rc))


def disconnected(client, userdata, rc):
    # This method is called when the client is disconnected
    print("Disconnected from MQTT Broker!")


def subscribe(client, userdata, topic, granted_qos):
    # This method is called when the client subscribes to a new feed.
    print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos))


def publish(client, userdata, topic, pid):
    # This method is called when the client publishes data to a feed.
    print("Published to {0} with PID {1}".format(topic, pid))


def message(client, topic, message):
    """Method callled when a client's subscribed feed has a new
    value.
    :param str topic: The topic of the feed with a new value.
    :param str message: The new value
    """
    print("New message on topic {0}: {1}".format(topic, message))
    if topic == "pyportal/feed1":
        feed1_label.text = "Next Bus: {}".format(message)
    if topic == "pyportal/feed2":
        feed2_label.text = "Weather: \n    {}".format(message)
    if topic == "pyportal/button1":
        if message == "1":
            buttons[0].label = "ON"
            buttons[0].selected = False
            print("Button 1 ON")
        else:
            buttons[0].label = "OFF"
            buttons[0].selected = True
            print("Button 1 OFF")


# ------------- Network Connection ------------- #

# Connect to WiFi
print("Connecting to WiFi...")
wifi.connect()
print("Connected to WiFi!")

# Initialize MQTT interface with the esp interface
MQTT.set_socket(socket, esp)

# Set up a MiniMQTT Client
client = MQTT.MQTT(
    broker=secrets["broker"],
    port=1883,
    username=secrets["user"],
    password=secrets["pass"],
)

# Connect callback handlers to client
client.on_connect = connect
client.on_disconnect = disconnected
client.on_subscribe = subscribe
client.on_publish = publish
client.on_message = message

print("Attempting to connect to %s" % client.broker)
client.connect()

print(
    "Subscribing to %s, %s, %s, and %s"
    % (mqtt_feed1, mqtt_feed2, mqtt_button1, mqtt_button2)
)
client.subscribe(mqtt_feed1)
client.subscribe(mqtt_feed2)
client.subscribe(mqtt_button1)
client.subscribe(mqtt_button2)

# ------------- Code Loop ------------- #
while True:
    # Poll the message queue
    client.loop()

    # Read sensor data and format
    light_value = lux = light_sensor.value
    light_label.text = "Light Sensor: {}".format(light_value)
    temperature = round(adt.temperature)
    temperature_label.text = "Temp Sensor: {}".format(temperature)
    movement_value = movement_sensor.value
    motion_label.text = "PIR Sensor: {}".format(movement_value)

    # Read display button press
    touch = ts.touch_point
    if touch:
        for i, b in enumerate(buttons):
            if b.contains(touch):
                print("Sending button%d pressed" % i)
                if i == 0:
                    # Toggle switch button type
                    if button1_state == 0:
                        button1_state = 1
                        b.label = "ON"
                        b.selected = False
                        print("Button 1 ON")
                    else:
                        button1_state = 0
                        b.label = "OFF"
                        b.selected = True
                        print("Button 1 OFF")
                    print("Sending button 1 state: ")
                    client.publish(mqtt_button1, button1_state)
                    # for debounce
                    while ts.touch_point:
                        print("Button 1 Pressed")
                if i == 1:
                    # Momentary button type
                    b.selected = True
                    print("Sending button 2 state: ")
                    client.publish(mqtt_button2, 1)
                    # for debounce
                    while ts.touch_point:
                        print("Button 2 Pressed")
                    print("Button 2 reliced")
                    print("Sending button 2 state: ")
                    client.publish(mqtt_button2, 0)
                    b.selected = False

    # Publish sensor data to MQTT
    print("Sending light sensor value: %d" % light_value)
    client.publish(mqtt_lux, light_value)

    print("Sending temperature value: %d" % temperature)
    client.publish(mqtt_temperature, temperature)

    print("Sending motion sensor value: %d" % movement_value)
    client.publish(mqtt_PIR, "{}".format(movement_value))

The Font File

You will also need to create a folder named fonts on the PyPortal CIRCUITPY drive with the following bitmap font in it, available for download in the project zip or the GitHub repo:

Secrets.py file

You will also need a file named secrets.py that will contain your WiFi credentials as well as your MQTT server credentials.

secrets = {
    'ssid' : '_your_wifi_ssid_',
    'password' : '_your_wifi_password_',
    'broker' : '_your_mqtt_broker_url_or_ip',
    'user' : '_your_mqtt_broker_username_',
    'pass' : '_your_mqtt_broker_password_'
    }

When you are finished the CIRCUITPY drive should look something like this:

CIRCUITPY

Required Libraries

You will need the following Libraries installed for this project to run:

  • adafruit_adt7410.mpy
  • adafruit_button.mpy
  • adafruit_logging.mpy
  • adafruit_minimqtt.mpy
  • adafruit_pyportal.mpy
  • adafruit_requests.mpy
  • adafruit_touchscreen.mpy
  • neopixel.mpy
  • adafruit_bitmap_font
  • adafruit_bus_device
  • adafruit_display_shapes
  • adafruit_display_text
  • adafruit_esp32spi
  • adafruit_io
  • adafruit_register

This guide was first published on Jan 08, 2020. It was last updated on Dec 08, 2023.

This page (CircuitPython Code) was last updated on Dec 08, 2023.

Text editor powered by tinymce.