Add CircuitPython Code and Project Assets
In the embedded code element below, click on the Download Project Bundle button, 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 CIRCUITPY drive. Your CircuitPython code file needs to be named code.py to run on startup.
# SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries # # SPDX-License-Identifier: MIT """ PyPortal Philips Hue Lighting Controller Brent Rubell for Adafruit Industries, 2019 """ import os 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 secrets = dict() secrets["ssid"] = os.getenv("CIRCUITPY_WIFI_SSID") secrets["password"] = os.getenv("CIRCUITPY_WIFI_PASSWORD") # ESP32 SPI 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 secrets.py try: username = os.getenv("HUE_USERNAME") bridge_ip = os.getenv("BRIDGE_IP") my_bridge = Bridge(wifi, bridge_ip, username) except: # 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() print(f'ADD THESE VALUES TO settings.toml: \ \nBRIDGE_IP = "{ip}" \ \nHUE_USERNAME = "{username}"') raise # 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() board.DISPLAY.root_group = button_group # preload the font print('loading font...') font = bitmap_font.load_font("/fonts/Arial-12.bdf") glyphs = b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,.: ' font.load_glyphs(glyphs) # button properties BUTTON_WIDTH = 60 BUTTON_HEIGHT = 60 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) buttons.append(button) # 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) buttons.append(button) # add buttons to the group for b in buttons: button_group.append(b.group) # 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 button.name == 'room': hue_selector = hue_lights['livingroom'] print('Switching to ', hue_selector) elif button.name == 'lamp': hue_selector = hue_lights['lamp'] print('Switching to ', hue_selector) elif button.name == 'onoff': print('Toggling {0}...'.format(hue_selector)) my_bridge.toggle_light(int(hue_selector)) elif button.name == '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 button.name == '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) else: print('Setting {0} color to {1}'.format(hue_selector, button.name)) my_bridge.set_light(light_id=int(hue_selector), hue=int(hue_hsb[button.name][0]), sat=int(hue_hsb[button.name][1]), bri=int(hue_hsb[button.name][2])) button.selected = False else: button.selected = False
This is what the final contents of the CIRCUITPY drive will look like:

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
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 secrets.py 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.
First, if you have not already done so, set up a settings.toml file with your local WiFi information, as described in this guide here.
Next, open the Mu editor and open the file code.py.
For this next step, you will need to connect to the serial console using Mu or a different terminal program.
- Not sure how to connect to your PyPortal's REPL? Read this page here and come back to the guide once you're connected.
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 settings.toml:
code.py output: Finding bridge address...
If the bridge address was found, the code will attempt to register a random, unique username with the bridge:
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.
Modify the settings.toml file by adding BRIDGE_IP
and HUE_USERNAME
and save the file. Your settings.toml file should look similar to the sample below:
CIRCUITPY_WIFI_SSID = "your_wifi_ssid" CIRCUITPY_WIFI_PASSWORD = "your_wifi_password" BRIDGE_IP = "192.160.0.00" HUE_USERNAME = "UpsdCULBXvOtWXaqgeTpdAp26Np9hu43x5XjkaLX"
If the code is returning None
for the hue_username
:
ADD THESE VALUES TO SECRETS.PY
"bridge_ip":"192.160.0.00",
"hue_username":"None"
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.
If the code is returning None
for the bridge_ip
:
ADD THESE VALUES TO SECRETS.PY
"bridge_ip":"None",
"hue_username":"UpsdCULBXvOtWXaqgeTpdAp26Np9hu43x5XjkaLX"
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.
Page last edited January 21, 2025
Text editor powered by tinymce.