This example uses a PyPortal Titano as a remote control for NeoPixels. It uses HTTP which is pretty slow, but MQTT on PyPortals is a bit unreliable at the moment.
For this project, you need:
If the FeatherWing tripler is out of stock, you can also use a FeatherWing Doubler and put Stacking Headers on either the Feather M4 or the AirLift FeatherWing.
First click Download Project Bundle below. This zip file will contain everything you need for this example. However, the files are also in the zip file you downloaded for the FunHouse, so you can get them from either one, just make sure you take the files from the neopixel_remote directory.
# SPDX-FileCopyrightText: 2021 Eva Herrada for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
NeoPixel remote control using PyPortal Titano.
Colors used are taken from Adafruit_CircuitPython_LED_Animation library
"""
from os import getenv
import time
import math
import board
import busio
from digitalio import DigitalInOut
import displayio
from adafruit_display_shapes.rect import Rect
import adafruit_imageload
import adafruit_touchscreen
# ESP32 SPI
from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager
# Import NeoPixel Library
import neopixel
# Import Adafruit IO HTTP Client
from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
ts = adafruit_touchscreen.Touchscreen(
board.TOUCH_XL,
board.TOUCH_XR,
board.TOUCH_YD,
board.TOUCH_YU,
calibration=((5200, 59000), (5800, 57000)),
size=(480, 320),
)
RED = 0xFF0000
YELLOW = 0xFF9600
ORANGE = 0xFF2800
GREEN = 0x00FF00
TEAL = 0x00FF78
CYAN = 0x00FFFF
BLUE = 0x0000FF
PURPLE = 0xB400FF
MAGENTA = 0xFF0014
WHITE = 0xFFFFFF
BLACK = 0x000000
GOLD = 0xFFDE1E
PINK = 0xF15AFF
AQUA = 0x32FFFF
JADE = 0x00FF28
AMBER = 0xFF6400
colors = [
None,
None,
GREEN,
PURPLE,
GOLD,
AMBER,
None,
None,
ORANGE,
BLUE,
BLACK,
JADE,
None,
None,
YELLOW,
CYAN,
WHITE,
AQUA,
None,
None,
RED,
TEAL,
MAGENTA,
PINK,
]
print(colors)
group = displayio.Group()
# pyportal_setter.xcf has been included so you can edit the colors used in GIMP, just make sure to
# change them here as well
background, palette = adafruit_imageload.load(
"bmps/pyportal_setter.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette
)
tile_grid = displayio.TileGrid(background, pixel_shader=palette)
group.append(tile_grid)
rect = Rect(0, 0, 160, 320, fill=0x000000)
group.append(rect)
print(len(group))
# 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_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# Create an instance of the Adafruit IO HTTP client
io = IO_HTTP(aio_username, aio_key, wifi)
try:
# Get the 'temperature' feed from Adafruit IO
neopixel_feed = io.get_feed("neopixel")
except AdafruitIO_RequestError:
neopixel_feed = io.create_new_feed("neopixel")
board.DISPLAY.root_group = group
print("ready")
last_color = 257
last_index = 0
while True:
p = ts.touch_point
if p:
x = math.floor(p[0] / 80)
y = math.floor(p[1] / 80)
index = 6 * y + x
# Used to prevent the touchscreen sending incorrect results
if last_index == index:
color = colors[index]
if colors[index] is not None:
group[1].fill = color
if last_color != color:
color_str = "#{:06x}".format(color)
print(color_str)
io.send_data(neopixel_feed["key"], color_str)
last_color = color
last_index = index
time.sleep(0.1)
First, you'll need to copy over the following libraries to your PyPortal Titano:
- adafruit_display_shapes/
- adafruit_bus_device/
- adafruit_imageload/
- adafruit_minimqtt/
- adafruit_connection_manager.mpy
- adafruit_io/
- adafruit_esp32spi/
- adafruit_requests.mpy
- adafruit_touchscreen.mpy
- adafruit_ticks.mpy
- adafruit_pixelbuf.mpy
- neopixel.mpy
After you've done that, rename neopixel_remote.py to code.py and put it on the PyPortal.
Next, make a new folder on your PyPortal named bmps and put pyportal_setter.bmp inside it.
Finally, copy over your settings.toml file.
After you've done all that, this is what your CIRCUITPY drive should look like:
When you've done all this, your PyPortal should look like this and you should be able to tap a square and have the top of the display turn that color as well as see the Adafruit IO color indicator turn that color.
# SPDX-FileCopyrightText: 2021 Eva Herrada for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
Sets NeoPixel color based on an Adafruit IO feed.
Uses:
* https://www.adafruit.com/product/3811
* Feather board
* https://www.adafruit.com/product/4264
* https://www.adafruit.com/product/2890
If your Feather board has STEMMA QT, you'll need one of these:
* https://www.adafruit.com/product/4209
But if it doesn't, use stacking headers on either the Feather or the AirLift FeatherWing
"""
from os import getenv
import board
import busio
import adafruit_connection_manager
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import neopixel
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT
from digitalio import DigitalInOut
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
### WiFi ###
# Change the first number to the pin the data line is plugged in to and the second number
# to the number of pixels
pixels = neopixel.NeoPixel(board.D5, 300)
# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.D13)
esp32_ready = DigitalInOut(board.D11)
esp32_reset = DigitalInOut(board.D12)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
"""Use below for Most Boards"""
status_pixel = neopixel.NeoPixel(
board.NEOPIXEL, 1, brightness=0.2
) # Uncomment for Most Boards
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# Define callback functions which will be called when certain events happen.
# pylint: disable=unused-argument
def connected(client):
client.subscribe("neopixel")
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 on_neopixel(client, topic, message):
print(message)
colors = [
int(message.split("#")[1][i : i + 2], 16) for i in range(0, len(message) - 1, 2)
]
print(colors)
pixels.fill(colors)
# Connect to WiFi
print("Connecting to WiFi...")
wifi.connect()
print("Connected!")
pool = adafruit_connection_manager.get_radio_socketpool(esp)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
broker="io.adafruit.com",
username=aio_username,
password=aio_key,
socket_pool=pool,
ssl_context=ssl_context,
)
# Initialize an Adafruit IO MQTT Client
io = IO_MQTT(mqtt_client)
io.add_feed_callback("neopixel", on_neopixel)
# Connect the callback methods defined above to Adafruit IO
io.on_connect = connected
io.on_subscribe = subscribe
# Connect to Adafruit IO
print("Connecting to Adafruit IO...")
io.connect()
io.get("neopixel")
while True:
io.loop()
First, copy over the required libraries:
- adafruit_esp32spi/
- adafruit_io/
- adafruit_minimqtt/
- adafruit_requests.mpy
- neopixel.mpy
The code above should already be downloaded. Rename it to code.py and copy it over to your Feather.
After you've copied all that over, copy your settings.toml over and you should be set as far as the files are concerned.
Assembly
Solder the headers that came with each device. Then put the Feather and the AirLift FeatherWing in the FeatherWing Tripler and attach the data wire from the NeoPixel strip (often green. I used one of these to make it a bit easier) to pin D5 (third pin from the end furthest from the USB port of the header with less pins) on the Tripler and then connect the ground wire (usually white or black) to a GND pin on the Tripler.
Afer you've done all this it should look something like this:
Now, connect the NeoPixel strip to a power source. If you have a lot of pixels, use a power supply but if you only have a few, you can get away with connecting it to the USB pin (note: this only works if you're powering the board through USB. If you're powering it with a LiPo this won't work).
Page last edited March 25, 2025
Text editor powered by tinymce.