Text Editor

Adafruit recommends using the Mu editor for editing your CircuitPython code. You can get more info in this guide.

Alternatively, you can use any text editor that saves simple text files.

Code

Copy the code shown below and paste it into Mu, then save it to your CIRCUITPY drive with the name code.py

# SPDX-FileCopyrightText: 2021 Tod Kurt @todbot and John Park for Adafruit Industries
# SPDX-License-Identifier: MIT
# QT Py encoder based on https://github.com/todbot/qtpy-knob
# Retroreflective chromakey light ring
#                 Mount a rotary encoder directly to an Adafruit QT Py,
#                 add some neopixels to get a color/brightness controller
#
import time
import board
from digitalio import DigitalInOut, Direction, Pull
import neopixel
import rotaryio


dim_val = 0.2

NUM_PIX = 24

PIX_TYPE = "RGB"  # RGB or RGBW

if PIX_TYPE == "RGB":
    ORDER = (1, 0, 2)
    GREEN = (0, 255, 0)
    BLUE = (0, 0, 255)
    WHITE = (255, 255, 255)
    BLACK = (0, 0, 0)
else:
    ORDER = (1, 0, 2, 3)
    GREEN = (0, 255, 0, 0)
    BLUE = (0, 0, 255, 0)
    WHITE = (0, 0, 0, 255)
    BLACK = (0, 0, 0, 0)


colors = [GREEN, BLUE, WHITE, BLACK]
current_color = 0


ring = neopixel.NeoPixel(
    board.MISO, NUM_PIX, brightness=0.2, auto_write=False, pixel_order=ORDER
)
ring.fill(colors[current_color])
ring.show()

# button of rotary encoder
button = DigitalInOut(board.MOSI)
button.pull = Pull.UP

# Use pin A2 as a fake ground for the rotary encoder
fakegnd = DigitalInOut(board.A2)
fakegnd.direction = Direction.OUTPUT
fakegnd.value = False

encoder = rotaryio.IncrementalEncoder(board.A3, board.A1)

print("---Chromakey Light Ring---")

last_encoder_val = encoder.position
ring_pos = 0
rainbow_pos = 0
last_time = time.monotonic()
ring_on = True

while True:
    encoder_diff = last_encoder_val - encoder.position  # encoder clicks since last read
    last_encoder_val = encoder.position

    if button.value is False:  # button pressed
        current_color = (current_color + 1) % len(colors)
        ring.fill(colors[current_color])
        ring.show()
        time.sleep(0.5)  # debounce

    else:
        if encoder_diff > 0:
            if dim_val >= 0.01:
                dim_val = (dim_val - 0.01) % 1.0
                ring.brightness = dim_val
                ring.show()
        elif encoder_diff < 0:
            if dim_val <= 0.99:
                dim_val = (dim_val + 0.01) % 1.0
                ring.brightness = dim_val
                ring.show()

        time.sleep(0.01)

How It Works

Libraries

First, the libraries are imported -- time for debouncing pauses, board for pin definitions, digitalio to set pins to inputs or outputs and to use the encoder knob button, neopixel for RGB LEDs, and rotaryio for reading the encoder.

NeoPixel Setup

Next, the dim_val variable is created and set for the starting brightness value of 20%.

Then NUM_PIX variable is set to the number of NeoPixels in the ring you're using, in this case 24.

Depending on the type of NeoPixel ring you use, you'll set it to either "RGB" or "RGBW".

PIX_TYPE = "RGB"  # RGB or RGBW

if PIX_TYPE == "RGB":
    ORDER = (1, 0, 2)
    GREEN = (0, 255, 0)
    BLUE = (0, 0, 255)
    WHITE = (255, 255, 255)
    BLACK = (0, 0, 0)
else:
    ORDER = (1, 0, 2, 3)
    GREEN = (0, 255, 0, 0)
    BLUE = (0, 0, 255, 0)
    WHITE = (0, 0, 0, 255)
    BLACK = (0, 0, 0, 0)
You can create any color you like for your chromakey -- for example, red or "magic pink" are used when shooting props or puppets that have green and blue in them.

A list for colors is created so you can cycle between them using the push encoder button, then the ring is set up and turned on to the first color in the list.

colors = [GREEN, BLUE, WHITE, BLACK]
current_color = 0


ring = neopixel.NeoPixel(
    board.MISO, NUM_PIX, brightness=0.2, auto_write=False, pixel_order=ORDER
)
ring.fill(colors[current_color])
ring.show()

Pin Setup

You'll set up the encoder pins next. Based on the physical layout of the encoder legs, you'll need to set pin A2 as a "fake" ground by setting it to be an output pin. Thanks again to Tod Kurt for this setup.

The encoder pins are on A3 and A2.

# button of rotary encoder
button = DigitalInOut(board.MOSI)
button.pull = Pull.UP

# Use pin A2 as a fake ground for the rotary encoder
fakegnd = DigitalInOut(board.A2)
fakegnd.direction = Direction.OUTPUT
fakegnd.value = False

encoder = rotaryio.IncrementalEncoder(board.A3, board.A1)

The state of the encoder position is stored in the last_encoder_val variable to compare it to the currently read value, thus detecting if the knob is being turned and if so in which direction.

Main Loop

The main loop of the program does the following things:

Compares the encoder value to the last encoder value.

If the encoder value is increasing (knob is turned clockwise), the brightness is increased. Conversely, if the encoder value is decreasing, the brightness is decreased.

When the push encoder button is pressed, the color is switched to the next one in the colors list.

while True:
    encoder_diff = last_encoder_val - encoder.position  # encoder clicks since last read
    last_encoder_val = encoder.position

    if button.value is False:  # button pressed
        current_color = (current_color + 1) % len(colors)
        ring.fill(colors[current_color])
        ring.show()
        time.sleep(0.5)  # debounce

    else:
        if encoder_diff > 0:
            if dim_val >= 0.01:
                dim_val = (dim_val - 0.01) % 1.0
                ring.brightness = dim_val
                ring.show()
        elif encoder_diff < 0:
            if dim_val <= 0.99:
                dim_val = (dim_val + 0.01) % 1.0
                ring.brightness = dim_val
                ring.show()

        time.sleep(0.01)

This guide was first published on Mar 10, 2021. It was last updated on Mar 10, 2021.

This page (Code the Chromakey Light Ring) was last updated on Jun 08, 2023.

Text editor powered by tinymce.