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 code.py 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 secrets.py file
try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

# 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 = secrets['hue_username']
    bridge_ip = secrets['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('ADD THESE VALUES TO SECRETS.PY: \
                            \n\t"bridge_ip":"{0}", \
                            \n\t"hue_username":"{1}"'.format(ip, 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(max_size=20)
board.DISPLAY.show(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:

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

Secrets File Setup

First, use Mu to open up a secrets.py 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 secrets.py, saving the updated file onto the PyPortal.

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.

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 secrets.py:

Download: file
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:

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:                             
        "bridge_ip":"192.160.0.00",                             
        "hue_username":"UpsdCULBXvOtWXaqgeTpdAp26Np9hu43x5XjkaLX"

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

Download: file
secrets = {
    'ssid' : 'home ssid',
    'password' : 'my password',
    'timezone' : "America/New_York", # http://worldtimeapi.org/timezones
    'bridge_ip' : '192.160.0.00',
    'hue_username': 'UpsdCULBXvOtWXaqgeTpdAp26Np9hu43x5XjkaLX'
    }

Issues?

hue_username displaying as 'None'?

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.

bridge_ip displaying as 'None'?

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.

Done

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.