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.
Download the Project Bundle
Your project will use a specific set of CircuitPython libraries, and the code.py file. To get everything you need, click on the Download Project Bundle link below, and uncompress the .zip file.
Connect your computer to the board via a known good USB power+data cable. A new flash drive should show up as CIRCUITPY.
Drag the contents of the uncompressed bundle directory onto your board CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.
# SPDX-FileCopyrightText: Copyright (c) 2023 john park for Adafruit Industries # # SPDX-License-Identifier: MIT import asyncio from random import randint, uniform import busio import board import adafruit_aw9523 # pin descriptions are based on physical LED placement # 0 bakery window, 1 lamp1 A, 2 lamp1 B, 3 bakery sconce, 4 lamp 2, 5 music sconce a, # 6 music sconce b, 7 music candle, 8 bakery red a, 9 bakery red b, 10 bakery green a, # 11 bakery green b i2c = busio.I2C(board.SCL1, board.SDA1) leddriver = adafruit_aw9523.AW9523(i2c) # Set all pins to outputs and LED (const current) mode leddriver.LED_modes = 0xFFFF leddriver.directions = 0xFFFF window_set = [8, 10, 9, 11] # red/green string always_on_set = [0, 1, 2, 4] # window and lanterns always_on_set_maxes = [100, 30, 30, 40] # maximum brightness per light # lights that are always on for n in range(len(always_on_set)): leddriver.set_constant_current(always_on_set[n], always_on_set_maxes[n]) async def flicker(pin, min_curr, max_curr, interval): while True: rand_max_curr = randint(min_curr, max_curr) for i in range(min_curr, rand_max_curr): leddriver.set_constant_current(pin, i) # aw9523 pin, current out of 255 await asyncio.sleep(0.07) await asyncio.sleep(uniform(0.0, interval)) async def string_lights(interval, max_curr): while True: for i in range(len(window_set)): # fade up for j in range(max_curr): leddriver.set_constant_current(window_set[i], j) print(j) await asyncio.sleep(interval) for i in range(len(window_set)): # fade down for j in range(max_curr): leddriver.set_constant_current(window_set[i], max_curr-j) print(j) await asyncio.sleep(interval) async def main(): led0_task = asyncio.create_task(flicker(3, 3, 10, 0.7)) # music candle led1_task = asyncio.create_task(flicker(5, 6, 12, 0.7)) # music sconce a led2_task = asyncio.create_task(flicker(6, 6, 12, 0.7)) # music sconce b led3_task = asyncio.create_task(flicker(7, 3, 10, 0.7)) # music candle led4_task = asyncio.create_task(string_lights(0.03, 30)) await asyncio.gather(led0_task, led1_task, led2_task, led3_task, led4_task) asyncio.run(main())
How it Works
Imports
First the code imports the necessary library modules:
-
asyncio
: This module provides infrastructure for writing single-threaded concurrent code using coroutines -
randint
,uniform
: Functions for generating random numbers -
busio
,board
: for setting up the I2C communication bus -
adafruit_aw9523
: library for the AW9523 LED driver board
import asyncio from random import randint, uniform import busio import board import adafruit_aw9523
I2C and Driver Setup
-
busio.I2C
initializes one I2C bus on the STEMMA QT port pins -
adafruit_aw9523.AW9523(i2c)
initializes the AW9523 driver using the I2C bus with the nameleddriver
- Configure all of the
leddriver
pins as LED constant current mode with their direction set to output
i2c = busio.I2C(board.SCL1, board.SDA1) leddriver = adafruit_aw9523.AW9523(i2c) leddriver.LED_modes = 0xFFFF leddriver.directions = 0xFFFF
Light Sets
Four of the lights are part of the red/green string over the upper floor bakery window. These pins are added to a list named window_set[]
with the proper order for running a left-right chase pattern.
Four other lights (lanterns, bakery shop display window) will always be on, so they are handled very simply just once. The always_on_set[]
is a list of these four pins.
The always_on_set_maxes[]
define the brightness level for the "always on" set of lights.
window_set = [8, 10, 9, 11] # red/green string always_on_set = [0, 1, 2, 4] # window and lanterns always_on_set_maxes = [100, 30, 30, 40] # maximum brightness per light
Always-On Set Activation
Next we'll turn on the LEDs that are always on at a constant current value.
for n in range(len(always_on_set)): leddriver.set_constant_current(always_on_set[n], always_on_set_maxes[n])
async flicker() Function
We define a function that flickers lights with inputs for pin, minimum current, maximum current, and the timing interval.
Similar to a regular CircuitPython function, but these are asyncio
functions, which means they can operate in a non-blocking way so their timing is independent of other async functions.
This is really helpful for creating multiple different lighting effects that happen simultaneously! In fact, this function will run concurrently on four different LEDs, lending them an organic look, since they operate independently of each other with their own randomized brightness levels.
async def flicker(pin, min_curr, max_curr, interval): while True: rand_max_curr = randint(min_curr, max_curr) for i in range(min_curr, rand_max_curr): leddriver.set_constant_current(pin, i) # aw9523 pin, current out of 255 await asyncio.sleep(0.07) await asyncio.sleep(uniform(0.0, interval))
async string_lights() Function
This function is a typical holiday string light effect where each light fades up in turn and then loops around to fade down in order.
async def string_lights(interval, max_curr): while True: for i in range(len(window_set)): # fade up for j in range(max_curr): leddriver.set_constant_current(window_set[i], j) print(j) await asyncio.sleep(interval) for i in range(len(window_set)): # fade down for j in range(max_curr): leddriver.set_constant_current(window_set[i], max_curr-j) print(j) await asyncio.sleep(interval)
In the main()
async function, create each task that is to be executed concurrently. These tasks call the previously defined async functions flicker()
and string_lights()
. In the case of flicker()
, different pins, minimum and maximum current values and timing intervals per task can be specified.
The asyncio.gather()
function runs the multiple co-routines concurrently, and awaits their completion before starting them again, respectively.
async def main(): led0_task = asyncio.create_task(flicker(3, 3, 10, 0.7)) # music candle led1_task = asyncio.create_task(flicker(5, 6, 12, 0.7)) # music sconce a led2_task = asyncio.create_task(flicker(6, 6, 12, 0.7)) # music sconce b led3_task = asyncio.create_task(flicker(7, 3, 10, 0.7)) # music candle led4_task = asyncio.create_task(string_lights(0.03, 30)) await asyncio.gather(led0_task, led1_task, led2_task, led3_task, led4_task)
asyncio.run(main())
Page last edited January 22, 2025
Text editor powered by tinymce.