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 matching your version of CircuitPython. PyPortal requires at least CircuitPython version 4.0.0.

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

  • adafruit_hue
  • simpleio
  • adafruit_bitmap_font
  • adafruit_bus_device
  • adafruit_button
  • adafruit_display_shapes
  • adafruit_display_text
  • adafruit_esp32spi
  • adafruit_touchscreen
  • neopixel

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

Copy the contents of the PyPortal_Philips_Hue_Controller directory to your PyPortal's CIRCUITPY drive. Your CircuitPython code file needs to be named to run on startup.

PyPortal Philips Hue Lighting Controller

Brent Rubell for Adafruit Industries, 2019
import board
import displayio
from adafruit_bitmap_font import bitmap_font
from adafruit_button import Button
import adafruit_touchscreen
from digitalio import DigitalInOut
import busio
import neopixel
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager

# Import Philips Hue Bridge
from adafruit_hue import Bridge

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

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)

# Attempt to load bridge username and IP address from
    username = secrets['hue_username']
    bridge_ip = secrets['bridge_ip']
    my_bridge = Bridge(wifi, bridge_ip, username)
    # Perform first-time bridge setup
    my_bridge = Bridge(wifi)
    print('Finding bridge address...')
    ip = my_bridge.discover_bridge()
    print('Attempting to register username, press the link button on your Hue Bridge now!')
    username = my_bridge.register_username()
                            \n\t"bridge_ip":"{0}", \
                            \n\t"hue_username":"{1}"'.format(ip, username))

# These pins are used as both analog and digital! XL, XR and YU must be analog
# and digital capable. YD just need to be digital
ts = adafruit_touchscreen.Touchscreen(board.TOUCH_XL, board.TOUCH_XR,
                                      board.TOUCH_YD, board.TOUCH_YU,
                                      calibration=((5200, 59000), (5800, 57000)),
                                      size=(320, 240))

# Make the display context
button_group = displayio.Group(max_size=20)
# preload the font
print('loading font...')
font = bitmap_font.load_font("/fonts/Arial-12.bdf")
glyphs = b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,.: '
# button properties
buttons = []

print('loading colors...')
# color conversions (RGB to Philips Hue-compatible HSB)
red = my_bridge.rgb_to_hsb([255, 0, 0])
white = my_bridge.rgb_to_hsb([255, 255, 255])
orange = my_bridge.rgb_to_hsb([255, 165, 0])
yellow = my_bridge.rgb_to_hsb([255, 255, 0])
green = my_bridge.rgb_to_hsb([0, 255, 0])
blue = my_bridge.rgb_to_hsb([0, 0, 255])
purple = my_bridge.rgb_to_hsb([128, 0, 128])
pink = my_bridge.rgb_to_hsb([255, 192, 203])

hue_hsb = {'red': red, 'white': white, 'orange': orange,
           'yellow': yellow, 'green': green, 'blue': blue,
           'purple': purple, 'pink': pink}

print('loading buttons...')
# button fill colors
button_colors = {'red':0xFF0000, 'white':0xFFFFFF,
                 'orange':0xFF9900, 'yellow':0xFFFF00,
                 'green':0x00FF00, 'blue':0x0000FF,
                 'purple':0x9900FF, 'pink': 0xFF00FF}
# list of color buttons and their properties
color_btn = [
    {'name':'red', 'pos':(15, 80), 'color':button_colors['red']},
    {'name':'white', 'pos':(85, 80), 'color':button_colors['white']},
    {'name':'orange', 'pos':(155, 80), 'color':button_colors['orange']},
    {'name':'yellow', 'pos':(225, 80), 'color':button_colors['yellow']},
    {'name':'pink', 'pos':(15, 155), 'color':button_colors['pink']},
    {'name':'green', 'pos':(85, 155), 'color':button_colors['green']},
    {'name':'blue', 'pos':(155, 155), 'color':button_colors['blue']},
    {'name':'purple', 'pos':(225, 155), 'color':button_colors['purple']}

# generate color buttons from color_btn list
for i in color_btn:
    button = Button(x=i['pos'][0], y=i['pos'][1],
                    width=BUTTON_WIDTH, height=BUTTON_HEIGHT, name=i['name'],
                    fill_color=i['color'], style=Button.ROUNDRECT)

# light property buttons and their properties
prop_btn = [
    {'name':'onoff', 'pos':(15, 15), 'label':'on/off'},
    {'name':'up', 'pos':(75, 15), 'label':'+'},
    {'name':'down', 'pos':(135, 15), 'label':'-'},
    {'name':'room', 'pos':(195, 15), 'label':'room'},
    {'name':'lamp', 'pos':(255, 15), 'label':'lamp'}

# generate property buttons from prop_btn list
for i in prop_btn:
    button = Button(name=i['name'], x=i['pos'][0], y=i['pos'][1],
                    width=55, height=40, label=i['label'],
                    label_font=font, style=Button.SHADOWROUNDRECT)

# add buttons to the group
for b in buttons:

# Hue Light/Group Identifiers
hue_lights={'lamp': 1, 'livingroom': 2}
hue_selector = hue_lights['lamp']

# Default to 25% brightness
current_brightness = 25

while True:
    touch = ts.touch_point
    if touch:
        for i, button in enumerate(buttons):
            if button.contains(touch):
                button.selected = True
                if == 'room':
                    hue_selector = hue_lights['livingroom']
                    print('Switching to ', hue_selector)
                elif == 'lamp':
                    hue_selector = hue_lights['lamp']
                    print('Switching to ', hue_selector)
                elif == 'onoff':
                    print('Toggling {0}...'.format(hue_selector))
                elif == 'up':
                    current_brightness += 25
                    print('Setting {0} brightness to {1}'.format(hue_selector, current_brightness))
                    my_bridge.set_light(int(hue_selector), bri=current_brightness)
                elif == 'down':
                    current_brightness -= 25
                    print('Setting {0} brightness to {1}'.format(hue_selector, current_brightness))
                    my_bridge.set_light(int(hue_selector), bri=current_brightness)
                    print('Setting {0} color to {1}'.format(hue_selector,
                button.selected = False
                button.selected = False

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

Bridge Setup

The Hue system is built around the Hue Bridge. To communicate with the bridge, you'll need to register a unique username (stored on the bridge) for the PyPortal. you'll also need the Bridge's IP address to communicate with it.

We've built the CircuitPython Hue library to automate the process of discovering and registering a username with the bridge - but, you'll still need to add these values to the file. This is a one-time setup and does not need to be repeated.

This guide requires you to edit and interact with CircuitPython code. Mu is a simple code editor that works with the Adafruit CircuitPython boards. 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!

Before proceeding, click the button below to install the Mu Editor. There are versions for PC, mac, and Linux.

Secrets File Setup

First, use Mu to open up a file on your CIRCUITPY drive.

Edit the file to enter your local WiFi credentials. For help getting connected to the Internet using it, follow this guide and come back to this page when you've successfully connected to the Internet

You can then close, saving the updated file onto the PyPortal.

Next, open the Mu editor and open the file

For this next step, you will need to connect to the serial console using Mu or a different terminal program.

Once connected to the PyPortal, the REPL will display that the code is searching for the bridge address using the WiFi credentials entered previously into

Download: file output:
Finding bridge address...

If the bridge address was found, the code will attempt to register a random, unique username with the bridge:

Download: file
Attempting to register username, press the link button on your Hue Bridge now!

When it prints this, you have exactly 120 seconds to press the link button on top of the Hue Bridge.

  • We've found that you may need to press this button more than once during this period to ensure a username is successfully generated.

Once the button on top of the bridge is pressed, the code will print the bridge IP address and username to the REPL:

Download: file
ADD THESE VALUES TO SECRETS.PY:                             

Modify the file again by adding bridge_ip and hue_username from the REPL output and save the file. Your file should look similar to the sample below.

Download: file
secrets = {
    'ssid' : 'home ssid',
    'password' : 'my password',
    'timezone' : "America/New_York", #
    'bridge_ip' : '',
    'hue_username': 'UpsdCULBXvOtWXaqgeTpdAp26Np9hu43x5XjkaLX'


hue_username displaying as 'None'?

If the code is returning None for the hue_username:




Make sure that you're pressing all the way down on the button. Try tapping it multiple times during the username generation process to ensure it returns a username back to the PyPortal.

bridge_ip displaying as 'None'?

If the code is returning None for the bridge_ip:




Ensure the bridge is connected to the same wireless network as the PyPortal.


Setting up the PyPortal with the bridge is finished! You do not need to repeat this process again unless you misplace the randomly generated username string.

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

This page (Hue Bridge Setup) was last updated on Mar 16, 2021.

Text editor powered by tinymce.