15. Interfacing


PyPortal Titano - Programming The Touchscreen Interface, and Connecting Input Buttons

This week, I plan on utilizing the Adafruit PyPortal Titano Which contains 2 microprocessors (ESP32E and ATSAMD51J20), a touchscreen and other peripherals, an integrated I2C port and wifi. I plan on using the I2C connector to interface with the rest of my project. You can view how I implemented I2C into my components here. The purpose of the pyportal in my project is to act as the screen in which the user can interact with sound settings, configuring inputs/outputs, and to host a visual representation of aspects of my project such as sound waves or which device it is connected to. It can be programmed using CircuitPython(a lightweight subset of MicroPython), so I will let this handle the more visual, such as the GUI, and less-time sensitive tasks, such as sound processing. For the sound portion, I will be programming a separate chip using C++ for optimal efficiency - since python tends to have slower run-times due to its nature as an interpreted language. Nonetheless, python is a wonderful and simple language that will still allow me to complex tasks while simultaneously speeding up the development process due to its easier syntax and powerful built-in functionality.

Design File Links
item link

PyPortal Basic Setup

Luckily, the setup for it is pretty simple. You just take the latest stable release of CircuitPython, and throw it onto the PyPortal after pressing the reset button twice. It will then automatically read and configure the files and start running. Interestingly, if there is an error in your code, it will show you the error message and traceback info right on the screen. I was receiving this error message on the screen:

It states that I have an incompatible ".mpy" file, so I followed the instructions on the website provided by the error code. On that website I found this excerpt:

I tried to update my libraries according to the link, but it just ended up working after resetting it. I think it may be wise to eventually remove all unused libraries to save program space. On the right is the base code that comes with the micropython image. I don't have any interest in using the wifi on it, but this is a great way to test if the wifi on your PyPortal works by changing the ssid and password in the file called "secrets.py"

Link to Code
import time
import board
from adafruit_pyportal import PyPortal

# Set up where we'll be fetching data from
DATA_SOURCE = "https://www.adafruit.com/api/quotes.php"
QUOTE_LOCATION = [0, 'text']
AUTHOR_LOCATION = [0, 'author']

# the current working directory (where this file is)
cwd = ("/"+__file__).rsplit('/', 1)[0]
pyportal = PyPortal(url=DATA_SOURCE,
                    json_path=(QUOTE_LOCATION, AUTHOR_LOCATION),
                    status_neopixel=board.NEOPIXEL,
                    default_bg=cwd+"/quote_background.bmp",
                    text_font=cwd+"/fonts/Arial-ItalicMT-23.bdf",
                    text_position=((20, 160),  # quote location
                                   (5, 280)), # author location
                    text_color=(0xFFFFFF,  # quote text color
                                0x8080FF), # author text color
                    text_wrap=(40, # characters to wrap for quote
                               0), # no wrap for author
                    text_maxlen=(180, 30), # max text size for quote & author
                   )

# speed up projects with lots of text by preloading the font!
pyportal.preload_font()

while True:
    try:
        value = pyportal.fetch()
        print("Response is", value)
    except (ValueError, RuntimeError) as e:
        print("Some error occured, retrying! -", e)
    time.sleep(60)
                        
Here's what it shows when it fails to connect to the ssid. Here's an example of what it shows when it's connected.
It is basically just connected to the internet and pulls random quotes from adafruit's website.



Programming the PyPortal

Luckily, programming the PyPortal is pretty straightforward -> code some python -> save the file onto the portal -> portal automatically resets and runs new code. This makes it relatively nice to work with, as I don't have to worry about programming through Thonny or Arduino, and can just use my regular VSCode setup. To get more information on the PyPortal object - assuming it is important ot use, I went through the github for micropython and found the original __init__.py file for the PyPortal class. In particular, this section stood out to me as it talks about the different parameters for the constructor and what they are for. This will let me weed out which functions of the PyPortal I need and don't need.
I decided to use the following parameters for my constructor call:
  • default_bg: to set the default background image source
  • status_neopixel: for debugging with the onboard LED
  • text_font: to set my favorite font- Helvetica (actually coolvetica since it's a royalty free comparable font)
  • text_scale: to set the default size of text
Some notable mentions that I will not be using at this point in time:
  • external_spi
After messing around with these settings, and browsing the different libraries available for circuitpython/the pyportal, I was a little lost due to the amount of classes and their similarities to eachother. I then found this guide on Adafruit on "Making a PyPortal User Interface DisplayIO". It conveniently walks through all the necessary and basic use cases for different classes set up for the PyPortal, and also provides handy links to the documentation for each class.
class PyPortal(PortalBase):
"""Class representing the Adafruit PyPortal.
:param url: The URL of your data source. Defaults to ``None``.
:param headers: The headers for authentication, typically used by Azure API's.
:param json_path: The list of json traversal to get data out of. Can be list of lists for
                  multiple data points. Defaults to ``None`` to not use json.
:param regexp_path: The list of regexp strings to get data out (use a single regexp group). Can
                    be list of regexps for multiple data points. Defaults to ``None`` to not
                    use regexp.
:param convert_image: Determine whether or not to use the AdafruitIO image converter service.
                      Set as False if your image is already resized. Defaults to True.
:param default_bg: The path to your default background image file or a hex color.
                   Defaults to 0x000000.
:param status_neopixel: The pin for the status NeoPixel. Use ``board.NEOPIXEL`` for the on-board
                        NeoPixel. Defaults to ``None``, not the status LED
:param str text_font: The path to your font file for your data text display.
:param text_position: The position of your extracted text on the display in an (x, y) tuple.
                      Can be a list of tuples for when there's a list of json_paths, for example
:param text_color: The color of the text, in 0xRRGGBB format. Can be a list of colors for when
                   there's multiple texts. Defaults to ``None``.
:param text_wrap: Whether or not to wrap text (for long text data chunks). Defaults to
                  ``False``, no wrapping.
:param text_maxlen: The max length of the text for text wrapping. Defaults to 0.
:param text_transform: A function that will be called on the text before display
:param int text_scale: The factor to scale the default size of the text by
:param json_transform: A function or a list of functions to call with the parsed JSON.
                       Changes and additions are permitted for the ``dict`` object.
:param image_json_path: The JSON traversal path for a background image to display. Defaults to
                        ``None``.
:param image_resize: What size to resize the image we got from the json_path, make this a tuple
                     of the width and height you want. Defaults to ``None``.
:param image_position: The position of the image on the display as an (x, y) tuple. Defaults to
                       ``None``.
:param image_dim_json_path: The JSON traversal path for the original dimensions of image tuple.
                            Used with fetch(). Defaults to ``None``.
:param success_callback: A function we'll call if you like, when we fetch data successfully.
                         Defaults to ``None``.
:param str caption_text: The text of your caption, a fixed text not changed by the data we get.
                         Defaults to ``None``.
:param str caption_font: The path to the font file for your caption. Defaults to ``None``.
:param caption_position: The position of your caption on the display as an (x, y) tuple.
                         Defaults to ``None``.
:param caption_color: The color of your caption. Must be a hex value, e.g. ``0x808000``.
:param image_url_path: The HTTP traversal path for a background image to display.
                         Defaults to ``None``.
:param esp: A passed ESP32 object, Can be used in cases where the ESP32 chip needs to be used
                         before calling the pyportal class. Defaults to ``None``.
:param busio.SPI external_spi: A previously declared spi object. Defaults to ``None``.
:param debug: Turn on debug print outs. Defaults to False.
"""