# CircuitPython Elgato WiFi Light Controller

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/128/911/medium800/lcds___displays_edited_P1440538.jpg?1711390093)

You can use CircuitPython to control your wireless Elgato lights. The [Elgato series of lights](https://www.elgato.com/us/en/p/key-light) have become really popular and use an app on your desktop or phone to control them. The [API is fairly simple though](https://github.com/adamesch/elgato-key-light-api/) and [uses HTTP requests](https://apihandyman.io/hacking-elgato-key-light-with-postman/) to send and receive JSON data. This makes for an excellent CircuitPython project with the Feather ESP32-S3 Reverse TFT. The board connects to your WiFi network and sends HTTP requests to your Elgato light when you press one of its three onboard buttons.

https://youtube.com/shorts/VFuk4eITt7I

The rotary encoder lets you dial in the brightness and color temperature. You can send the new values to the light by pressing the D0 or D1 buttons.

![lcds___displays_updateTemp.gif](https://cdn-learn.adafruit.com/assets/assets/000/128/917/medium640thumb/lcds___displays_updateTemp.jpg?1711392087)

The onboard TFT lets you see the current settings for your light at a glance. You can press the D2 button to read data from the light.

![lcds___displays_d2_read.gif](https://cdn-learn.adafruit.com/assets/assets/000/128/918/medium640thumb/lcds___displays_d2_read.jpg?1711392233)

## Inspiration

This project was inspired by the [Three Elgato Key Light Remote by Brian Dorey](https://www.briandorey.com/post/esp32-three-elgato-key-light-remote). That project used an ESP32 board with a custom three button PCB running Arduino code to control the their lighting setup.

## Parts
### Adafruit ESP32-S3 Reverse TFT Feather

[Adafruit ESP32-S3 Reverse TFT Feather](https://www.adafruit.com/product/5691)
Like Missy Elliot, we like to ["put our [Feather] down, flip it and reverse it"](https://www.youtube.com/watch?v=cjIvu7e6Wq8)&nbsp;and that's exactly what we've done with this new development board. It's basically our **<a...></a...>**

Out of Stock
[Buy Now](https://www.adafruit.com/product/5691)
[Related Guides to the Product](https://learn.adafruit.com/products/5691/guides)
![Video of a rectangular microcontroller with a TFT display. A pink manicured finger presses each of the tactile buttons, which are recognized on the TFT display.](https://cdn-shop.adafruit.com/product-videos/640x480/5691-05.jpg)

### Adafruit I2C Stemma QT Rotary Encoder Breakout with Encoder

[Adafruit I2C Stemma QT Rotary Encoder Breakout with Encoder](https://www.adafruit.com/product/5880)
Rotary encoders are soooo much fun! Twist em this way, then twist them that way. Unlike potentiometers, they go all the way around and often have little detents for tactile feedback. But, if you've ever tried to add encoders to your project you know that they're a real challenge to...

In Stock
[Buy Now](https://www.adafruit.com/product/5880)
[Related Guides to the Product](https://learn.adafruit.com/products/5880/guides)
![Angled Shot of the I2C Stemma QT Rotary Encoder Breakout with Encoder.](https://cdn-shop.adafruit.com/640x480/5880-00.jpg)

### Cream Micro Potentiometer Knob - 4 pack

[Cream Micro Potentiometer Knob - 4 pack](https://www.adafruit.com/product/5539)
Have you found yourself in a maze of twisty-turny knobs, all alike? These micro potentiometer&nbsp;(or rotary encoder) knobs offer a rainbow burst of color to your synth or control panel. They are made of a hard plastic, with a grippy star shape. The shape is sometimes referred to as...

In Stock
[Buy Now](https://www.adafruit.com/product/5539)
[Related Guides to the Product](https://learn.adafruit.com/products/5539/guides)
![Angled shot of four cream micro knobs.](https://cdn-shop.adafruit.com/640x480/5539-00.jpg)

### STEMMA QT / Qwiic JST SH 4-pin Cable - 100mm Long

[STEMMA QT / Qwiic JST SH 4-pin Cable - 100mm Long](https://www.adafruit.com/product/4210)
This 4-wire cable is a little over 100mm / 4" long and fitted with JST-SH female 4-pin connectors on both ends. Compared with the chunkier JST-PH these are 1mm pitch instead of 2mm, but still have a nice latching feel, while being easy to insert and remove.

<a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/4210)
[Related Guides to the Product](https://learn.adafruit.com/products/4210/guides)
![Angled shot of STEMMA QT / Qwiic JST SH 4-pin Cable.](https://cdn-shop.adafruit.com/640x480/4210-00.jpg)

### Lithium Ion Polymer Battery Ideal For Feathers - 3.7V 400mAh

[Lithium Ion Polymer Battery Ideal For Feathers - 3.7V 400mAh](https://www.adafruit.com/product/3898)
Lithium-ion polymer (also known as 'lipo' or 'lipoly') batteries are thin, light, and powerful. The output ranges from 4.2V when completely charged to 3.7V. This battery has a capacity of **400mAh** for a total of about 1.9 Wh. If you need a larger (or smaller!)...

In Stock
[Buy Now](https://www.adafruit.com/product/3898)
[Related Guides to the Product](https://learn.adafruit.com/products/3898/guides)
![Slim Lithium Ion Polymer Battery 3.7v 400mAh with JST 2-PH connector and short cable](https://cdn-shop.adafruit.com/640x480/3898-05.jpg)

### JST-PH Battery Extension Cable - 500mm

[JST-PH Battery Extension Cable - 500mm](https://www.adafruit.com/product/1131)
By popular demand, we now have a handy extension cord for all of our JST PH-terminated battery packs (such as our LiIon/LiPoly and 3xAAA holders). One end has a JST-PH compatible socket, and the other end has a matching plug. Between the two, 500mm of color coded wire. Handy for wearable...

In Stock
[Buy Now](https://www.adafruit.com/product/1131)
[Related Guides to the Product](https://learn.adafruit.com/products/1131/guides)
![Front shot of JST-PH Battery Extension Cable.](https://cdn-shop.adafruit.com/640x480/1131-00.jpg)

### Part: USB A to USB C Cable
quantity: 1
Pink and Purple Woven USB A to USB C Cable - 1 meter long
[USB A to USB C Cable](https://www.adafruit.com/product/5153)

### Part: M2.5 Screws and Stand-offs
quantity: 1
Black Nylon Machine Screw and Stand-off Set – M2.5 Thread
[M2.5 Screws and Stand-offs](https://www.adafruit.com/product/3299)

### Part: M2 Screws and Stand-offs
quantity: 1
M2 x 4mm/6mm/8mm/10mm/12mm/16mm/20mm
[M2 Screws and Stand-offs](https://www.amazon.com/gp/product/B07W5HBRMP/)

## Elgato Key Light Options
### Part: Elgato Key Light
quantity: 1
Elgato Key Light
[Elgato Key Light](https://www.amazon.com/Elgato-Key-Light-Professional-app-adjustable/dp/B07L755X9G/)

or

### Part: Elgato Key Light Mini
quantity: 1
Elgato Key Light Mini
[Elgato Key Light Mini](https://www.amazon.com/Elgato-Key-Light-Professional-app-adjustable/dp/B09PRNHLM7/?th=1)

# CircuitPython Elgato WiFi Light Controller

## 3D Printing

![](https://cdn-learn.adafruit.com/assets/assets/000/128/891/medium800/lcds___displays_edited_P1440458.jpg?1711371510)

The controller may be assembled with 3D printed parts, described below. The enclosure has three parts: a case, a lid and a small NeoPixel diffuser.

The STL files can be downloaded directly here or from Printables.

[Elgato_WiFi_Light_Controller_STL_Files.zip](https://cdn-learn.adafruit.com/assets/assets/000/128/919/original/Elgato_WiFi_Light_Controller_STL_Files.zip?1711392889)
[Printables Download](https://www.printables.com/model/819833-elgato-wifi-light-controller)
The case and lid snap fit together. The lid has cutouts and mounting holes for the Feather. The case has mounting holes for the rotary encoder and has space for a battery if you want to use the project without USB power.

![lcds___displays_edited_P1440462.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/892/medium640/lcds___displays_edited_P1440462.jpg?1711371646)

A tiny NeoPixel diffuser can be printed in clear PLA. It pops into the cutout in the case and lets you see the NeoPixel on the rotary encoder.

![lcds___displays_edited_P1440451.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/893/medium640/lcds___displays_edited_P1440451.jpg?1711371882)

# CircuitPython Elgato WiFi Light Controller

## Install CircuitPython

[CircuitPython](https://github.com/adafruit/circuitpython) is a derivative of [MicroPython](https://micropython.org) designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the **CIRCUITPY** drive to iterate.

## CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython running on your board.

[Download the latest version of CircuitPython for this board via circuitpython.org](https://circuitpython.org/board/adafruit_feather_esp32s3_reverse_tft/)
 **Click the link above to download the latest CircuitPython UF2 file.**

Save it wherever is convenient for you.

![install_circuitpython_on_most_boards_CircuitPython_downloaded.jpg](https://cdn-learn.adafruit.com/assets/assets/000/102/129/medium640/install_circuitpython_on_most_boards_CircuitPython_downloaded.jpg?1620922559)

![](https://cdn-learn.adafruit.com/assets/assets/000/119/387/medium800/adafruit_products_resetWithNeo.jpg?1678478978)

Plug your board into your computer, using a known-good data-sync cable, directly, or via an adapter if needed.

Double-click the **reset** button (highlighted in red above), and you will see the **RGB status LED(s)** turn green (highlighted in green above). If you see red, try another port, or if you're using an adapter or hub, try without the hub, or different adapter or hub.

For this board, tap reset and wait for the LED to turn purple, and as soon as it turns purple, tap reset again. The second tap needs to happen while the LED is still purple.

If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!

A lot of people end up using charge-only USB cables and it is very frustrating! **Make sure you have a USB cable you know is good for data sync.**

You will see a new disk drive appear called **FTHRS3BOOT**.

&nbsp;

Drag the **adafruit\_circuitpython\_etc.uf2** file to **FTHRS3BOOT**.

![adafruit_products_s3Boot.jpg](https://cdn-learn.adafruit.com/assets/assets/000/118/996/medium640/adafruit_products_s3Boot.jpg?1677603548)

![adafruit_products_copyTos3.jpg](https://cdn-learn.adafruit.com/assets/assets/000/118/997/medium640/adafruit_products_copyTos3.jpg?1677603564)

The **BOOT** drive will disappear and a new disk drive called **CIRCUITPY** will appear.

That's it!

![install_circuitpython_on_most_boards_CIRCUITPY.jpg](https://cdn-learn.adafruit.com/assets/assets/000/102/130/medium640/install_circuitpython_on_most_boards_CIRCUITPY.jpg?1620923145)

# CircuitPython Elgato WiFi Light Controller

## Create Your settings.toml File

CircuitPython works with WiFi-capable boards to enable you to make projects that have network connectivity. This means working with various passwords and API keys. As of [CircuitPython 8](https://circuitpython.org/downloads), there is support for a **settings.toml** file. This is a file that is stored on your **CIRCUITPY** drive, that contains all of your secret network information, such as your SSID, SSID password and any API keys for IoT services. It is designed to separate your sensitive information from your **code.py** file so you are able to share your code without sharing your credentials.

CircuitPython previously used a **secrets.py** file for this purpose. The **settings.toml** file is quite similar.

Warning: Your **settings.toml** file should be stored in the main directory of your **CIRCUITPY** drive. It should not be in a folder.

## CircuitPython **settings.toml** File

This section will provide a couple of examples of what your **settings.toml** file should look like, specifically for CircuitPython WiFi projects in general.

The most minimal **settings.toml** file must contain your WiFi SSID and password, as that is the minimum required to connect to WiFi. Copy this example, paste it into your **settings.toml** , and update:

- `your_wifi_ssid`
- `your_wifi_password`

```auto
CIRCUITPY_WIFI_SSID = "your_wifi_ssid"
CIRCUITPY_WIFI_PASSWORD = "your_wifi_password"
```

Many CircuitPython network-connected projects on the Adafruit Learn System involve using Adafruit IO. For these projects, you must _also_ include your Adafruit IO username and key. Copy the following example, paste it into your settings.toml file, and update:

- `your_wifi_ssid`
- `your_wifi_password`
- `your_aio_username`
- `your_aio_key`

```auto
CIRCUITPY_WIFI_SSID = "your_wifi_ssid"
CIRCUITPY_WIFI_PASSWORD = "your_wifi_password"
ADAFRUIT_AIO_USERNAME = "your_aio_username"
ADAFRUIT_AIO_KEY = "your_aio_key"
```

Some projects use different variable names for the entries in the **settings.toml** file. For example, a project might use `ADAFRUIT_AIO_ID` in the place of `ADAFRUIT_AIO_USERNAME`. **If you run into connectivity issues, one of the first things to check is that the names in the settings.toml file match the names in the code.**

Warning: Not every project uses the same variable name for each entry in the **settings.toml** file! Always verify it matches the code.

## **settings.toml** File Tips
Here is an example **settings.toml** file.

```auto
# Comments are supported
CIRCUITPY_WIFI_SSID = "guest wifi"
CIRCUITPY_WIFI_PASSWORD = "guessable"
CIRCUITPY_WEB_API_PORT = 80
CIRCUITPY_WEB_API_PASSWORD = "passw0rd"
test_variable = "this is a test"
thumbs_up = "\U0001f44d"
```

In a **settings.toml** file, it's important to keep these factors in mind:

- Strings are wrapped in double quotes; ex: `"your-string-here"`
- Integers are _ **not** _ quoted and may be written in decimal with optional sign (`+1`, `-1`, `1000`) or hexadecimal (`0xabcd`).
  - Floats (decimal numbers), octal (`0o567`) and binary (`0b11011`) are not supported.

- Use `\u` escapes for weird characters, `\x` and `\ooo` escapes are not available in **.toml** files
  - Example: `\U0001f44d` for 👍 (thumbs up emoji) and `\u20ac` for € (EUR sign)

- Unicode emoji, and non-ASCII characters, stand for themselves as long as you're careful to save in "UTF-8 without BOM" format

&nbsp;

&nbsp;

When your&nbsp; **settings.toml&nbsp;** file is ready, you can save it in your text editor with the **.toml** &nbsp;extension.

![adafruit_products_dotToml.jpg](https://cdn-learn.adafruit.com/assets/assets/000/117/071/medium640/adafruit_products_dotToml.jpg?1671034293)

## Accessing Your **settings.toml** Information in **code.py**
In your **code.py** file, you'll need to `import` the `os` library to access the **settings.toml** file. Your settings are accessed with the `os.getenv()` function. You'll pass your settings entry to the function to import it into the **code.py** file.

```python
import os

print(os.getenv("test_variable"))
```

![](https://cdn-learn.adafruit.com/assets/assets/000/117/072/medium800/adafruit_products_tomlOutput.jpg?1671034496)

In the upcoming CircuitPython WiFi examples, you'll see how the **settings.toml&nbsp;** file is used for connecting to your SSID and accessing your API keys.

# CircuitPython Elgato WiFi Light Controller

## Code the Controller

Once you've finished setting up your Feather ESP32-S3 Reverse TFT with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.

To do this, click on the **Download Project Bundle** button in the window below. It will download to your computer as a zipped folder.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/Elgato_WiFi_Light_Controller/code.py

## Upload the Code and Libraries to the Feather ESP32-S3 Reverse TFT

After downloading the Project Bundle, plug your Feather ESP32-S3 Reverse TFT into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called **CIRCUITPY**. Unzip the folder and copy the following items to the Feather's **CIRCUITPY** drive:

- **lib** folder
- **code.py**
- **roundedHeavy-26.bdf**
- **roundedHeavy-46.bdf**

Your Feather ESP32-S3 Reverse TFT **CIRCUITPY** drive should look like this after copying the **lib** folder, **.bdf** font files and the **code.py** file.

![CIRCUITPY](https://adafruit.github.io/Adafruit_Learning_System_Guides/Elgato_WiFi_Light_Controller.png )

## Add Your **settings.toml** File

As of CircuitPython 8.0.0, there is support for [Environment Variables](https://docs.circuitpython.org/en/latest/docs/environment.html). Environment variables are stored in a **settings.toml** file. Similar to **secrets.py** , the **settings.toml** file separates your sensitive information from your main **code.py** file. Add your **settings.toml** file as described in the [Create Your settings.toml File page](https://learn.adafruit.com/mars-clock/create-your-settings-toml-file) earlier in this guide. You'll need to include your light's IP address as `ELGATO_LIGHT`. You'll also need to include your WiFi network SSID and password as `CIRCUITPY_WIFI_SSID` and `CIRCUITPY_WIFI_PASSWORD`.

```python
CIRCUITPY_WIFI_SSID = "your-ssid-here"
CIRCUITPY_WIFI_PASSWORD = "your-ssid-password-here"
ELGATO_LIGHT = "your-Elgato-light-IP-address-here"
```

## How the CircuitPython Code Works

At the top of the CircuitPython code are user definable variables. `num_lights` is the number of lights you are using. The `light` variable holds your Elgato light IP address that is loaded in from your **settings.toml** file. The `clock_timer` is a non-blocking delay for use with `ticks` for updating text on the display after requests are sent to the light.

```python
num_lights = 1
light = os.getenv('ELGATO_LIGHT')
clock_clock = ticks_ms()
clock_timer = 3 * 1000
```

## seesaw Setup

Next, the rotary encoder and its switch are instantiated over I2C.

```python
i2c = board.I2C()  # uses board.SCL and board.SDA
seesaw = seesaw.Seesaw(i2c, addr=0x36)
encoder = rotaryio.IncrementalEncoder(seesaw)
seesaw.pin_mode(24, seesaw.INPUT_PULLUP)
switch = seesaw_digitalio.DigitalIO(seesaw, 24)
switch_state = False
pixel = neopixel.NeoPixel(seesaw, 6, 1)
pixel.brightness = 0.2
pixel.fill((255, 0, 0))
```

## WiFi and Buttons

The Feather connects to your WiFi SSID. Then, the three onboard buttons are set up as digital inputs.

```python
#  connect to your SSID
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
print("Connected to WiFi")
pixel.fill((0, 0, 255))

pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())

# buttons
button0 = digitalio.DigitalInOut(board.D0)
button0.direction = digitalio.Direction.INPUT
button0.pull = digitalio.Pull.UP
button0_state = False
button1 = digitalio.DigitalInOut(board.D1)
button1.direction = digitalio.Direction.INPUT
button1.pull = digitalio.Pull.DOWN
button1_state = False
button2 = digitalio.DigitalInOut(board.D2)
button2.direction = digitalio.Direction.INPUT
button2.pull = digitalio.Pull.DOWN
button2_state = False
```

## Display

Next up is all of the display code. Two different sized font files are used for displaying text on the TFT. The light's IP address and connection status is shown on the TFT in the smaller font. The brightness percentage and LED temperature are shown in the larger font. A small circle is used to show whether the light is on or off.

```python
group = displayio.Group()
board.DISPLAY.root_group = group

# font for graphics
sm_file = "/roundedHeavy-26.bdf"
sm_font = bitmap_font.load_font(sm_file)
# font for text only
lg_file = "/roundedHeavy-46.bdf"
lg_font = bitmap_font.load_font(lg_file)
http_text = bitmap_label.Label(sm_font, text=" ")
http_text.anchor_point = (1.0, 0.0)
http_text.anchored_position = (board.DISPLAY.width, 0)
group.append(http_text)
status_text = bitmap_label.Label(sm_font, text=" ")
status_text.anchor_point = (0.0, 0.5)
status_text.anchored_position = (0, board.DISPLAY.height / 2)
group.append(status_text)
temp_text = bitmap_label.Label(lg_font, text=" K")
temp_text.anchor_point = (1.0, 0.5)
temp_text.anchored_position = (board.DISPLAY.width, board.DISPLAY.height / 2)
group.append(temp_text)
bright_text = bitmap_label.Label(lg_font, text=" %", x=board.DISPLAY.width//2, y=90)
bright_text.anchor_point = (1.0, 1.0)
bright_text.anchored_position = (board.DISPLAY.width, board.DISPLAY.height - 15)
group.append(bright_text)
onOff_circ = Circle(12, 12, 10, fill=None, stroke = 2, outline = 0xcccc00)
group.append(onOff_circ)
```

## Functions

### Converting Kelvin to Elgato and Back

A few functions are used throughout the loop. The first are `kelvin_to_elgato()` and `elgato_to_kelvin()`. This converts degrees Kelvin to the values used by the Elgato API to set the LED temperature and vice versa. The light can range in temperature from 2900K to 7000K and the Elgato API looks from values that range from `143` to `344`. This means that the degrees Kelvin value is multiplied by `0.05` to convert to the API value and the API value is divided by `0.05` to show a more familiar value in degrees Kelvin.

```python
def kelvin_to_elgato(value):
    t = value * 0.05
    t = max(min(344, int(t)), 143)
    return t

def elgato_to_kelvin(value):
    t = value / 0.05
    return t
```

### Control the Light

The `ctrl_light()` function sends HTTP requests to the Elgato light. The light can be turned on or off and its brightness and temperature can be set. There is an extensive `try`/`except` loop in the function to try and handle any errors that can pop-up when communicating with the light. Once the request is successfully sent, the status text on the TFT is updated to read "`sent!`".

```python
def ctrl_light(b, t, onOff):
    url = f"http://{light}:9123/elgato/lights"
    json = {"numberOfLights":num_lights,"lights":[{"on":onOff,"brightness":b,"temperature":t}]}
    print(f"PUTting data to {url}: {json}")
    status_text.text = "sending.."
    for i in range(5):
        try:
            pixel.fill((0, 255, 0))
            r = requests.request(method="PUT", url=url, data=None, json=json,
                                 headers = {'Content-Type': 'application/json'}, timeout=10)
            print("-" * 40)
            print(r.status_code)
            # if PUT fails, try again
            if r.status_code != 200:
                status_text.text = "..sending.."
                pixel.fill((255, 255, 0))
                time.sleep(2)
                r = requests.request(method="PUT", url=url, data=None, json=json,
                                     headers = {'Content-Type': 'application/json'}, timeout=10)
            if r.status_code != 200:
                pixel.fill((255, 0, 0))
        except Exception:
            pixel.fill((255, 0, 0))
            time.sleep(2)
            if i < 5 - 1:
                continue
            raise
        break
    status_text.text = "sent!"
    light_indicator(onOff)
    pixel.fill((255, 0, 255))
```

### Read the Light

The `read_light()` function makes an HTTP request to the light to read the current status of the light. It returns whether the light is on or off and the current brightness and temperature. This is handy if you are controlling the light with this project and the app so that they can stay in sync. The TFT updates to reflect any changed values.

```python
def read_light():
    status_text.text = "reading.."
    for i in range(5):
        try:
            pixel.fill((0, 255, 0))
            r = requests.get(f"http://{light}:9123/elgato/lights")
            j = r.json()
            if r.status_code != 200:
                status_text.text = "..reading.."
                pixel.fill((255, 255, 0))
                time.sleep(2)
                r = requests.get(f"http://{light}:9123/elgato/lights")
                j = r.json()
            if r.status_code != 200:
                pixel.fill((255, 0, 0))
        except Exception:
            pixel.fill((255, 0, 0))
            time.sleep(2)
            if i < 5 - 1:
                continue
            raise
        break
    status_text.text = "read!"
    pixel.fill((255, 0, 255))
    onOff = j['lights'][0]['on']
    light_indicator(onOff)
    b = round(j['lights'][0]['brightness'] / 10) * 10
    bright_text.text = f"{b}%"
    t = j['lights'][0]['temperature']
    color_t = round(elgato_to_kelvin(t) / 100) * 100
    temp_text.text = f"{color_t}K"
    return b, color_t, t, onOff
```

### Toggle the Circle

The last function is `light_indicator()`. This function toggles the `fill` of the `Circle `object on the TFT depending on whether the light is on or off. If the light is off, the `fill` of the `Circle` is set to `None`. Otherwise, it is set to yellow (`0xCCCC00`).

```python
def light_indicator(onOff):
    if onOff:
        onOff_circ.fill = 0xcccc00
    else:
        onOff_circ.fill = None
```

## Flight Check Request

Right before the loop, an initial call of the `read_light()` function occurs. This allows the CircuitPython code to begin in sync with your light. The other purpose of this is to make sure your light is on and connected to your network. If the request fails, then an error message prints to the serial monitor prompting you to check your light and its IP address.

```python
# get on/off state of light on start-up
try:
    brightness, color_temp, temp, light_on = read_light()
except Exception:
    print()
    print("Could not find your Elgato light on the network..")
    print("Make sure it is powered on and that its IP address is correct in settings.toml.")
    print()
    raise
```

## The Loop

Finally, the loop. The rotary encoder controls setting the values for brightness and temperature. You can toggle between setting the two by pressing the switch in the encoder. The value of `adjust_temp` is tracked for this purpose.

```python
if position != last_position:
	if position > last_position:
		if adjust_temp:
			color_temp = color_temp + 100
			color_temp = max(min(7000, color_temp), 2900)
			temp_text.text = f"{color_temp}K"
		else:
			brightness = brightness + 10
			brightness = max(min(100, brightness), 10)
			bright_text.text = f"{brightness}%"
	else:
		if adjust_temp:
			color_temp = color_temp - 100
			color_temp = max(min(7000, color_temp), 2900)
			temp_text.text = f"{color_temp}K"
		else:
			brightness = brightness - 10
			brightness = max(min(100, brightness), 10)
			bright_text.text = f"{brightness}%"
	temp = kelvin_to_elgato(color_temp)
	last_position = position
```

## Control the Light

The rotary encoder does not send this information to the light though, only the buttons control sending and receiving requests from the light. This avoids overwhelming the API. The first and second buttons send the `ctrl_light()` function and send the brightness and temperature values. Only the first button toggles the light on or off though. If you want to keep the light in its current power state but update the brightness and temperature, you can press the second button.

```python
# toggle light on/off
if not button0.value and not button0_state:
	button0_state = True
	light_on = not light_on
	ctrl_light(brightness, temp, light_on)
	clock_clock = ticks_add(clock_clock, clock_timer)
# update brightness/temp
if button1.value and not button1_state:
	button1_state = True
	light_on = True
	ctrl_light(brightness, temp, light_on)
	clock_clock = ticks_add(clock_clock, clock_timer)
```

## Read the Light

The third button uses the `read_light()` function to fetch the current state of the light and its values. `ticks` is used to update the status text on the TFT. After `read_light()` and `ctrl_light()` successfully communicate with the light, the text updates to "`sent!`" or "`read!`". After three seconds, it reverts back to "`Connected`".

```python
# check values
if button2.value and not button2_state:
	button2_state = True
	brightness, color_temp, temp, light_on = read_light()
	clock_clock = ticks_add(clock_clock, clock_timer)
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer:
	status_text.text = "Connected"
	clock_clock = ticks_add(clock_clock, clock_timer)
```

# CircuitPython Elgato WiFi Light Controller

## Assembly

![](https://cdn-learn.adafruit.com/assets/assets/000/128/894/medium800/lcds___displays_edited_P1440448.jpg?1711380971)

Plug the rotary encoder into the Feather with a STEMMA QT cable.

![lcds___displays_edited_P1440467.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/895/medium640/lcds___displays_edited_P1440467.jpg?1711380978)

If you want to use the project with a battery, plug in a JST 2-PH extension cable to the Feather JST PH port. This will make plugging and unplugging a battery easier in the enclosure.

![lcds___displays_edited_P1440474.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/899/medium640/lcds___displays_edited_P1440474.jpg?1711381857)

Mount the Feather to the lid of the enclosure using M2.5 screws and nuts for the mounting holes on either side of the USB port and M2 screws and nuts for the mounting holes on either side of the ESP32-S3 module.

![lcds___displays_edited_P1440476.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/900/medium640/lcds___displays_edited_P1440476.jpg?1711381901)

Secure four M2.5 stand-offs to the mounting holes on the rotary encoder with M2.5 nuts.&nbsp;

![lcds___displays_edited_P1440504.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/901/medium640/lcds___displays_edited_P1440504.jpg?1711381955)

Attach the rotary encoder to the case via the stand-offs with 4 M2.5 screws. The NeoPixel should be lined up with the cutout in the case.

![lcds___displays_edited_P1440512.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/902/medium640/lcds___displays_edited_P1440512.jpg?1711382002)

Attach a knob to the rotary encoder and insert the NeoPixel diffuser into the case cutout.

![lcds___displays_edited_P1440517.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/903/medium640/lcds___displays_edited_P1440517.jpg?1711382059)

That completes the assembly!

![lcds___displays_edited_P1440523.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/904/medium640/lcds___displays_edited_P1440523.jpg?1711382095)

# CircuitPython Elgato WiFi Light Controller

## Use

![](https://cdn-learn.adafruit.com/assets/assets/000/128/912/medium800/lcds___displays_edited_P1440526.jpg?1711390105)

You can power the project with either a USB-C cable or a lipoly battery. Before powering it up, check that your Elgato light is switched on and connected to your network.

You'll need to make sure that the light's IP address is correct in your **settings.toml** file. You can find the IP address in the Elgato app by clicking on the **Accessory settings icon**.

![lcds___displays_appIP.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/910/medium640/lcds___displays_appIP.jpg?1711383248)

Once the project is powered up, it will perform an initial fetch of the light's current settings and display them on the TFT LCD.

You can press the D0 button to turn the light on or off. The current brightness and temperature settings will be sent as well.

![lcds___displays_onOff.gif](https://cdn-learn.adafruit.com/assets/assets/000/128/916/medium640thumb/lcds___displays_onOff.jpg?1711391640)

You can press D1 to send new brightness and temperature values to the light without toggling it on or off.

![lcds___displays_d1_send.gif](https://cdn-learn.adafruit.com/assets/assets/000/128/915/medium640thumb/lcds___displays_d1_send.jpg?1711391605)

The brightness and temperature values are controlled by the rotary encoder. You can toggle between controlling each by pressing the encoder switch.

![lcds___displays_rotaryDemo.gif](https://cdn-learn.adafruit.com/assets/assets/000/128/913/medium640thumb/lcds___displays_rotaryDemo.jpg?1711390600)

You can press D2 to read the light's values back and update the CircuitPython values to keep the light and controller in sync. This is handy if you are switching between controlling the light via the app and this project.

![lcds___displays_d2_read.gif](https://cdn-learn.adafruit.com/assets/assets/000/128/914/medium640thumb/lcds___displays_d2_read.jpg?1711390772)


## Featured Products

### Adafruit ESP32-S3 Reverse TFT Feather

[Adafruit ESP32-S3 Reverse TFT Feather](https://www.adafruit.com/product/5691)
Like Missy Elliot, we like to ["put our [Feather] down, flip it and reverse it"](https://www.youtube.com/watch?v=cjIvu7e6Wq8)&nbsp;and that's exactly what we've done with this new development board. It's basically our **<a...></a...>**

Out of Stock
[Buy Now](https://www.adafruit.com/product/5691)
[Related Guides to the Product](https://learn.adafruit.com/products/5691/guides)
### Adafruit I2C Stemma QT Rotary Encoder Breakout with Encoder

[Adafruit I2C Stemma QT Rotary Encoder Breakout with Encoder](https://www.adafruit.com/product/5880)
Rotary encoders are soooo much fun! Twist em this way, then twist them that way. Unlike potentiometers, they go all the way around and often have little detents for tactile feedback. But, if you've ever tried to add encoders to your project you know that they're a real challenge to...

In Stock
[Buy Now](https://www.adafruit.com/product/5880)
[Related Guides to the Product](https://learn.adafruit.com/products/5880/guides)
### Cream Micro Potentiometer Knob - 4 pack

[Cream Micro Potentiometer Knob - 4 pack](https://www.adafruit.com/product/5539)
Have you found yourself in a maze of twisty-turny knobs, all alike? These micro potentiometer&nbsp;(or rotary encoder) knobs offer a rainbow burst of color to your synth or control panel. They are made of a hard plastic, with a grippy star shape. The shape is sometimes referred to as...

In Stock
[Buy Now](https://www.adafruit.com/product/5539)
[Related Guides to the Product](https://learn.adafruit.com/products/5539/guides)
### STEMMA QT / Qwiic JST SH 4-pin Cable - 100mm Long

[STEMMA QT / Qwiic JST SH 4-pin Cable - 100mm Long](https://www.adafruit.com/product/4210)
This 4-wire cable is a little over 100mm / 4" long and fitted with JST-SH female 4-pin connectors on both ends. Compared with the chunkier JST-PH these are 1mm pitch instead of 2mm, but still have a nice latching feel, while being easy to insert and remove.

<a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/4210)
[Related Guides to the Product](https://learn.adafruit.com/products/4210/guides)
### JST-PH Battery Extension Cable - 500mm

[JST-PH Battery Extension Cable - 500mm](https://www.adafruit.com/product/1131)
By popular demand, we now have a handy extension cord for all of our JST PH-terminated battery packs (such as our LiIon/LiPoly and 3xAAA holders). One end has a JST-PH compatible socket, and the other end has a matching plug. Between the two, 500mm of color coded wire. Handy for wearable...

In Stock
[Buy Now](https://www.adafruit.com/product/1131)
[Related Guides to the Product](https://learn.adafruit.com/products/1131/guides)
### Lithium Ion Polymer Battery Ideal For Feathers - 3.7V 400mAh

[Lithium Ion Polymer Battery Ideal For Feathers - 3.7V 400mAh](https://www.adafruit.com/product/3898)
Lithium-ion polymer (also known as 'lipo' or 'lipoly') batteries are thin, light, and powerful. The output ranges from 4.2V when completely charged to 3.7V. This battery has a capacity of **400mAh** for a total of about 1.9 Wh. If you need a larger (or smaller!)...

In Stock
[Buy Now](https://www.adafruit.com/product/3898)
[Related Guides to the Product](https://learn.adafruit.com/products/3898/guides)
### Pink and Purple Woven USB A to USB C Cable - 1 meter long

[Pink and Purple Woven USB A to USB C Cable - 1 meter long](https://www.adafruit.com/product/5153)
This cable is not only super-fashionable, with a woven pink and purple Blinka-like pattern, it's also made for USB C for our modernized breakout boards, Feathers, and more.&nbsp;&nbsp;[If you want something just like it but for Micro B, we...](https://www.adafruit.com/product/4111)

Out of Stock
[Buy Now](https://www.adafruit.com/product/5153)
[Related Guides to the Product](https://learn.adafruit.com/products/5153/guides)
### Black Nylon Machine Screw and Stand-off Set – M2.5 Thread

[Black Nylon Machine Screw and Stand-off Set – M2.5 Thread](https://www.adafruit.com/product/3299)
Totaling 380 pieces, this **M2.5 Screw Set** &nbsp;is a must-have for your workstation.&nbsp;You'll have enough screws, nuts, and hex standoffs to fuel your maker tendencies&nbsp;for days on end! M2.5 size screws fit almost all of the Adafruit breakout/dev board mounting holes...

In Stock
[Buy Now](https://www.adafruit.com/product/3299)
[Related Guides to the Product](https://learn.adafruit.com/products/3299/guides)

## Related Guides

- [Adafruit I2C QT Rotary Encoder](https://learn.adafruit.com/adafruit-i2c-qt-rotary-encoder.md)
- [Adafruit ESP32-S3 Reverse TFT Feather](https://learn.adafruit.com/esp32-s3-reverse-tft-feather.md)
- [Generating Text with ChatGPT, Pico W & CircuitPython](https://learn.adafruit.com/generating-text-with-chatgpt-pico-w-circuitpython.md)
- [LED Matrix Wall Arcade for Pico-8](https://learn.adafruit.com/led-matrix-wall-arcade.md)
- [Ninja Timer: Giant 7-Segment Display](https://learn.adafruit.com/ninja-timer-giant-7-segment-display.md)
- [Raspberry Pi Zero Stand](https://learn.adafruit.com/raspberry-pi-zero-stand.md)
- [Fruit Jam Video Music](https://learn.adafruit.com/fruit-jam-video-music.md)
- [MP3 Playback in CircuitPython with Lars the Sloth Puppet](https://learn.adafruit.com/mp3-circuitpython-lars.md)
- [CYBERDECK Expansion Plate](https://learn.adafruit.com/cyberdeck-plate.md)
- [Numpad 4000 Mechanical Keyswitch Data Entry Device](https://learn.adafruit.com/numpad-4000-mechanical-keyswitch-data-entry-device.md)
- [PicoDVI Adafruit IO Feed Dashboard](https://learn.adafruit.com/dvi-io.md)
- [Bricktunes: LEGO Synthesizer Glove](https://learn.adafruit.com/bricktunes-lego-glove-synthesizer.md)
- [3D Printed Frame for Adafruit IS31FL3741 LED Glasses](https://learn.adafruit.com/3d-printed-frame-for-led-glasses-is31fl3741.md)
- [Remote Effects Trigger Box](https://learn.adafruit.com/remote-effects-trigger.md)
- [Raspberry Pi HQ Camera Case](https://learn.adafruit.com/raspberry-pi-hq-camera-case.md)
- [PyPortal Air Quality Display](https://learn.adafruit.com/pyportal-air-quality-display.md)
- [Faderwave Synthesizer](https://learn.adafruit.com/faderwave-synthesizer.md)
