<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Photons, Electrons, and Dirt &#187; Uncategorized</title>
	<atom:link href="https://bikerglen.com/blog/category/uncategorized/feed/" rel="self" type="application/rss+xml" />
	<link>https://bikerglen.com/blog</link>
	<description>A blog by Glen Akins</description>
	<lastBuildDate>Mon, 16 Feb 2026 00:47:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.2.38</generator>
	<item>
		<title>RP2040 MIDI Soundboard Player for Live Streaming</title>
		<link>https://bikerglen.com/blog/rp2040-midi-soundboard-player-for-live-streaming/</link>
		<comments>https://bikerglen.com/blog/rp2040-midi-soundboard-player-for-live-streaming/#comments</comments>
		<pubDate>Sat, 14 Feb 2026 00:39:20 +0000</pubDate>
		<dc:creator><![CDATA[Glen]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://bikerglen.com/blog/?p=5925</guid>
		<description><![CDATA[In this project, I use an off-the-shelf MIDI controller to trigger an RP2040 to play out sound effects from a microSD card through an audio DAC and optional amplifier and speaker. Read through to see how to combine the RP2040&#8217;s USB &#8230; <a href="https://bikerglen.com/blog/rp2040-midi-soundboard-player-for-live-streaming/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<div id="attachment_6070" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/PXL_20260214_000349328_2048px.jpg"><img class="wp-image-6070 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/PXL_20260214_000349328_2048px-1024x683.jpg" alt="PXL_20260214_000349328-edit" width="640" height="427" /></a><p class="wp-caption-text">The little gray box plays back sound effects triggered by the Launchpad Mini. It has a line-level output for input to mixer to use during live streams. There&#8217;s also a version with a built-in speaker for live in-person sound effects too!</p></div>
<p>In this project, I use an off-the-shelf MIDI controller to trigger an RP2040 to play out sound effects from a microSD card through an audio DAC and optional amplifier and speaker. Read through to see how to combine the RP2040&#8217;s USB host, FatFS, and I2S audio capabilities into a fun audio project and for all the build details!</p>
<p><span id="more-5925"></span></p>
<h2>Motivation</h2>
<div id="attachment_6068" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0498_2048px.jpg"><img class="wp-image-6068 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0498_2048px-1024x682.jpg" alt="DSC_0498" width="640" height="426" /></a><p class="wp-caption-text">Three USB MIDI controllers and the two versions of this project. The yellow and red has a built-in speaker for use in person. The gray one with the RED LEDs has a line output for a mixer to use during live streams.</p></div>
<p>This project is a follow on to my <a href="https://bikerglen.com/blog/lizard-3d-printed-single-purpose-soundboard/">Lizard single-purpose soundboard project</a> but supporting many more buttons and audio samples. That project already had the hardware to support tons of audio samples but the number of buttons was limited by the number of available microcontroller pins. After some research, I decided the most practical way to add lots of robust buttons to the project was to use an off-the-shelf MIDI controller like the Midi Fighter Spectra or Novation Launchpad Mini MK3.</p>
<div id="attachment_6000" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/image.png"><img class="size-large wp-image-6000" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/image-1024x597.png" alt="Sound Show screen from their online documentation" width="640" height="373" /></a><p class="wp-caption-text">Sound Show screen from their online documentation.</p></div>
<p>These MIDI controllers normally connect to a PC or Mac using USB and trigger instrument sounds or sound effects in software like Ableton Live or <a href="https://soundshow.app/">Sound Show</a>. I didn&#8217;t want to use a PC to play back sounds because I wanted something small and reliable that booted quickly and had a minimal risk of systems sounds making it into the live audio.</p>
<p>Fortunately, for someone who would like to use these controllers with their own hardware and software, the USB MIDI protocol is standardized, both these devices have robust documentation, and TinyUSB, the RP2040&#8217;s USB stack, already has USB MIDI host features.</p>
<div id="attachment_6066" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/PXL_20260213_232739969.jpg"><img class="wp-image-6066 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/PXL_20260213_232739969-1024x576.jpg" alt="PXL_20260213_232739969" width="640" height="360" /></a><p class="wp-caption-text">The line out version is small enough to tuck out of the way somewhere. It&#8217;s sitting on the top right corner of my mixer.</p></div>
<p>In addition to more audio samples and buttons, I wanted a version that could feed a line-level signal into an audio mixer for use on calls and when live streaming so this project comes in two versions: the first version contains an amplifier and speaker for use in person and the second has a line-level output for connecting to a mixer. The final change was to develop the software using the Rapsberry PI RP2350/RP2040 C SDK rather than in CircuitPython.</p>
<h2>Hardware Prototype</h2>
<div id="attachment_5942" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/pcio-dev-hardware.png"><img class="size-large wp-image-5942" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/pcio-dev-hardware-1024x600.png" alt="Fritzing schematic of all the development hardware." width="640" height="375" /></a><p class="wp-caption-text">Fritzing diagram of all the development hardware.</p></div>
<p>Before ordering boards, I prototyped the hardware and software using a Raspberry Pi RP2040 Pico development board. The fritzing sketch for the prototype development is shown in the figure above. The components in the sketch are the following:</p>
<ul>
<li>Raspberry Pi RP2040 Pico</li>
<li>Raspberry Pi Debug Probe</li>
<li>Adafruit micro SD SPI / SDIO card breakout board</li>
<li>Adafruit I2S amplifier BFF add-on for QT Py and Xiao</li>
<li>Generic 40mm diameter, 4 Ohm 5 Watt speaker</li>
<li>Generic MicroUSB to USB A USB OTG cabkle.</li>
<li>Custom qwiic three LED board (more on this later).</li>
</ul>
<p>For a development system, I used a Raspberry Pi 5. For a MIDI controller, I used a Midi Fighter Spectra. I later acquired a Novation Launchpad Mini Mk3 which works too. To make connections to the Pico easier, I used a <a href="https://amzn.to/3O1qWkJ">Pico breakout board</a> (affiliate link) that breaks out the Pico connections to screw terminals and a row of headers. It also helps to anchor everything in place on the workbench.</p>
<div id="attachment_5955" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/PXL_20260208_175413631_annotated.jpg"><img class="wp-image-5955 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/PXL_20260208_175413631_annotated-1024x576.jpg" alt="PXL_20260208_175413631" width="640" height="360" /></a><p class="wp-caption-text">The prototype hardware I used to develop the software and finalize the hardware design.</p></div>
<p>The photo above shows all the hardware connected together on the workbench. The stepper motor drivers are left over from another Pico project and are not used in this project.</p>
<p>In the upper left, is the Raspberry Pi 5 with the RP2040 C SDK installed on it. It&#8217;s connected to my network and the Pico debug probe, highlighted in yellow. The Pico debug probe is in turn connected to the Pico&#8217;s SWD connector and UART0. This setup lets me remote into the Pi, debug the Pico, and view the Pico&#8217;s serial output from my main desktop rather than trying to use a keyboard and mouse on the workbench.</p>
<p>On the right is the Midi Fighter Spectra connected via the red cable to the USB OTG cable which is connected to the Pico&#8217;s micro USB port. To supply power to the project, the yellow and green wires at the top-right corner of the Pico, highlighted in green, are connected to a bench supply set for 5 volts and 500 mA. The rest of the hardware is</p>
<ul>
<li>the I2S amp board, highlighted in blue</li>
<li>the speaker, highlighted in red</li>
<li>the microSD break out board, highlighted in orange</li>
<li>the custom qwiic LED board, highlighted in purple</li>
</ul>
<p>I did not put all the hardware together immediately. I started with just enough hardware to supply power to the Pico, program and debug the Pico, and connect to the MIDI controller. Once the MIDI controller software was working, I added the microSD card hardware. Once that was working, I added the audio hardware. Finally, I added the LEDs.</p>
<h2>Software Development Environment</h2>
<p>For a software development environment, I used the Raspberry Pi RP2350/RP2400 C/C++ SDK. The SDK is installed using the <a href="https://www.raspberrypi.com/documentation/microcontrollers/c_sdk.html">official instructions</a> on the Raspberry Pi website. The SDK includes the TinyUSB library and the I2S audio libraries but not a useful microSD card or FatFS library.</p>
<p>Unfortunately though, the TinyUSB version currently included with the C/C++ SDK is 0.18 and major revisions were made to the MIDI host stack in 0.19 so the TinyUSB library needs to be updated before it can be used. I deleted the original tinyusb directory in the pico-sdk/lib directory then ran git clone https://github.com/hathach/tinyusb to get the latest version in its place.</p>
<p>If you want to use a specific release, use at least version 0.20.0 which can be downloaded with https://github.com/hathach/tinyusb &#8211;branch 0.20.0. There&#8217;s probably a way to do this with git without needing to remove the original tinyusb directory but it was pretty late at night and I was in a hurry so I went with the expedient over the judicious.</p>
<p>To access the microSD card, I used the 3rd party <a href="https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico">no-OS-FatFS-SD-SDIO-SPI-RPi-Pico</a> library. To install the microSD library, clone its GitHub repo into the $PICO_SDK_PATH/lib directory. Once cloned into the lib directory, projects can use it by adding the following line to their CMakeLists.txt:</p>
<pre>add_subdirectory(${PICO_SDK_PATH}/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/src build-fat)</pre>
<p>where the first argument is the path to the cloned library&#8217;s src directory and the second argument, build-fat, is the name of a directory to build the library in. Cmake will automatically create this directory inside its main build directory. Build-fat could be named anything so long as it doesn&#8217;t conflict with any other libraries added to the build.</p>
<h2>MIDI Software Test</h2>
<p>With the hardware gathered together and the SDK installed, it was time to try to listen to commands from some MIDI controllers. I decided to use the <a href="https://github.com/rppicomidi/usb_midi_host/tree/main/examples/C-code/usb_midi_host_example">USB MIDI host example</a> as a starting point. Beware that while the examples in this library are still valid, the rest of the code in that repo has been incorporated into the mainline TinyUSB release and is no longer maintained.</p>
<h3>Clone, Build, and Download the Example</h3>
<p>The following commands should clone the USB MIDI host repository, build the example program, and download it to the Raspberry Pi Pico:</p>
<pre>cd
git clone https://github.com/rppicomidi/usb_midi_host.git temp
cd temp/examples/C-code/usb_midi_host_example/
mkdir build
cd build
cmake ..
openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program usb_midi_host_example.elf verify reset exit"</pre>
<p>Once that&#8217;s completed, open a serial terminal to view the outputs of the Pico&#8217;s serial port through the Pico debug probe and connect a MIDI controller to the Pico&#8217;s USB port using a micro USB OTG cable.</p>
<h3>Midi Fighter Spectra Results</h3>
<div id="attachment_5971" style="width: 937px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-10-073729.png"><img class="wp-image-5971 size-full" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-10-073729.png" alt="Screenshot 2026-02-10 073729" width="927" height="796" /></a><p class="wp-caption-text">The Midi Fighter Spectra&#8217;s configuration is mostly limited to choosing LED ring colors and configuring the device&#8217;s side buttons.</p></div>
<p>I configured my Midi Fighter Spectra as shown above. The settings are mostly limited to choosing the buttons&#8217; LED ring colors and configuring the function of the device&#8217;s side buttons. More more information, see the Fighter&#8217;s <a href="https://djtechtools.com/midi-fighter-setup/">setup page</a> or <a href="https://s3.amazonaws.com/MF_Support_Docs/Midi+Fighter+Spectra+User+Guide.pdf">user guide</a> (PDF).</p>
<p>With the Midi Fighter Spectra connected, these are the results of pushing the top row of buttons in order from left to right:</p>
<pre>Pico MIDI Host Example
MIDI Device Index = 0, MIDI device address = 1, 1 IN cables, 1 OUT cables
MIDI Device Index = 0 is unmounted
MIDI Device Index = 0, MIDI device address = 1, 1 IN cables, 1 OUT cables
MIDI RX Cable #0:92 30 7f 
MIDI RX Cable #0:82 30 7f 
MIDI RX Cable #0:92 31 7f 
MIDI RX Cable #0:82 31 7f 
MIDI RX Cable #0:92 32 7f 
MIDI RX Cable #0:82 32 7f 
MIDI RX Cable #0:92 33 7f 
MIDI RX Cable #0:82 33 7f</pre>
<p>The 0x92 and 0x82 are note on and note off commands on MIDI channel 3. Pressing a button generates a note on command and releasing a button generates a note off command. The 0x2 in the values of 0x92 and 0x82 is MIDI channel 3 which was set for the device in the Midi Fighter configuration utility. The second numbers, 0x30 &#8230; 0x33, are the notes corresponding to each button pressed. Finally, the last value, 0x7f, is the note velocity. For more information on what these commands and numbers mean, see <a href="https://learn.sparkfun.com/tutorials/midi-tutorial/all">SparkFun&#8217;s excellent MIDI tutorial</a>.</p>
<p>When it comes time to putting all the software pieces together, I&#8217;m going to be interested in the note on commands on channel 3, 0x92, and the notes corresponding to the buttons on the keypad. If you keep pressing buttons, you&#8217;ll see the note numbers are not in top-to-bottom, left-to-right order. The note numbers are documented in the <a href="https://s3.amazonaws.com/MF_Support_Docs/Midi+Fighter+Spectra+User+Guide.pdf">user guide</a> (PDF) and we&#8217;ll sort them all out later.</p>
<h3>Novation Launchpad Mini Mk3 Results</h3>
<div id="attachment_5988" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-10-175638.png"><img class="wp-image-5988 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-10-175638-1024x622.png" alt="Screenshot 2026-02-10 175638" width="640" height="389" /></a><p class="wp-caption-text">The Novation Launchpad Mini Mk3 configuration software. It has a lot more to configure then the MIdi Fighter Spectra.</p></div>
<p>The right hand column in the screenshot above shows the configuration of the top left button on my Launchpad. As you can see, the Launchpad is considerably more configurable than the Spectra. I configured all the buttons on the user custom page of the Launchpad to generate MIDI control change (CC) messages on button presses and releases. I also numbered the buttons from 0 to 63 from the top left to the bottom right. For more information on programming the Launchpad, see the <a href="https://downloads.novationmusic.com/novation/launchpad-mk3/launchpad-mini-mk3-0">Launchpad Mini Mk3 user guide</a>.</p>
<p>Here&#8217;s the result of pressing the top left four buttons in order from left to right on the Launchpad:</p>
<pre>Pico MIDI Host Example
MIDI Device Index = 0, MIDI device address = 1, 2 IN cables, 2 OUT cables
MIDI RX Cable #1:b1 00 7f 
MIDI RX Cable #1:b1 00 00 
MIDI RX Cable #1:b1 01 7f 
MIDI RX Cable #1:b1 01 00 
MIDI RX Cable #1:b1 02 7f 
MIDI RX Cable #1:b1 02 00 
MIDI RX Cable #1:b1 03 7f 
MIDI RX Cable #1:b1 03 00</pre>
<p>The 0xb1 is a control change (CC) message on MIDI channel 2. The second numbers, from 0x00 through 0x03, correspond to the button numbers configured previously. Finally, 0x7f is for a button press and 0x00 is for a button release. For more information on what these commands and numbers mean, see <a href="https://learn.sparkfun.com/tutorials/midi-tutorial/all">SparkFun&#8217;s excellent MIDI tutorial</a>.</p>
<p>When it comes time to putting all the software pieces together, I&#8217;m going to be interested in the CC commands on channel 2, the button numbers, and when buttons are pressed as indicated by 0x7f.</p>
<p>At this point, we no longer need the temp directory and its contents. It is useful, however, if you get a new MIDI controller or change the configuration of a MIDI controller and want to see what MIDI commands are emitted when a knob is twisted, a slider is moved, or a button is pressed.</p>
<h2>microSD Card Software Test</h2>
<p>After verifying I could get the USB MIDI host stack to run, I verified I could perform reads and writes to the microSD card using the  no-OS-FatFS-SD-SDIO-SPI-RPi-Pico library. I decided to run the simple example included in the repo. This example did take two small modifications to run successfully.</p>
<p><span style="color: #993300;">Warning: this example completely erases the inserted microSD card and creates a new file with a hello world in it. You will need a microSD card you don&#8217;t mind erasing to run this example.</span></p>
<p>Create a copy of the simple example in a temp directory:</p>
<pre>cd
cp -r pico/pico-sdk/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/examples/simple ./temp
cd temp</pre>
<p>Next, edit CMakeLists.txt to pull the SD card library sources from where they were installed. For me, this was in the lib directory of the Pico SDK installation:</p>
<pre>add_subdirectory(${PICO_SDK_PATH}/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/src build-fat)</pre>
<p>Lastly, edit hw_config.c to change the chip select to GPIO5 to match how the hardware prototype is wired:</p>
<pre>    .ss_gpio = 5  // The SPI slave select GPIO for this SD card</pre>
<p>Now, build the project:</p>
<pre>mkdir build
cd build
cmake ..
make</pre>
<p><span style="color: #993300;">Warning: This example will completely erase the microSD card!!! Once the demo runs, the only file on the microSD card will be a file called &#8220;filename.txt&#8221; containing the string &#8220;Hello, world!&#8221;</span></p>
<p>Once it is built, insert a microSD card in the microSD card breakout board and download and ran the generated .elf file:</p>
<pre>openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program simple_example.elf verify reset exit"</pre>
<p>After running the project, power down the target and use a PC or Mac to verify the card was formatted and the file was created and contained the correct contents.</p>
<h2>I2S Audio Software Test</h2>
<p>So far, so good! Both the MIDI and the microSD card were working, but could I create audio? The pico-playground includes a program to generate a sine wave on a connected I2S DAC using the pico-extras i2s library, both of which are installed as part of the Pico C/C++ SDK. This sounded like a good starting point for making some noise. It was also the most difficult example to get to compile because I had to make my own CMakeLists.txt.</p>
<pre>cd
mkdir temp
cd temp
cp $PICO_SDK_PATH/../pico-playground/audio/sine_wave/sine_wave.c .
cp $PICO_SDK_PATH/external/pico_sdk_import.cmake .
cp $PICO_SDK_PATH/../pico-extras/external/pico_extras_import.cmake .</pre>
<p>In the temp directory, create a new CMakeLists.txt with the following contents:</p>
<pre>cmake_minimum_required(VERSION 3.13)

set(BOARD pico_sdk)
include(pico_sdk_import.cmake)
include(pico_extras_import.cmake)

project(pico_audio_i2s C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()

add_executable(sine_wave_i2s
        sine_wave.c
        )

target_link_libraries(sine_wave_i2s PRIVATE
        pico_stdlib
        pico_audio_i2s
        )

target_compile_definitions(sine_wave_i2s PRIVATE
        # compile time configuration of I2S
        PICO_AUDIO_I2S_MONO_INPUT=1
        #define for our example code
        USE_AUDIO_I2S=1
        ) 

pico_enable_stdio_usb(sine_wave_i2s 0)
pico_enable_stdio_uart(sine_wave_i2s 1)

# create map/bin/hex file etc.
pico_add_extra_outputs(sine_wave_i2s)</pre>
<p>Now create the build directory, build the project, and download the binary to the Pico:</p>
<pre>mkdir build
cd build
cmake ..
make
openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program sine_wave_i2s.elf verify reset exit"</pre>
<p>If you have a terminal connected to the Pico, you can use different keys, as documented in sine_wave.c, to change the volume and pitch of the tone. I used this example to test the Pico I2S capabilities with both the MAX98357-based I2S Amplifier BFF board and a PCM5100 DAC breakout board. This ensured that my hardware and software could provide either a speaker level or line out level depending on the connected DAC.</p>
<h2>Pulling All the Software Blocks Together</h2>
<p>The next step was to put all these building blocks together into a program to detect button presses on the MIDI controller and then, in response to these button presses, read audio samples from the microSD card and play them out through the attached I2S DAC.</p>
<p>The first step in <a href="https://github.com/bikerglen/rp2040-midi-player/blob/main/src/main.c">main.c</a> is to initialize all the hardware, initialize a queue for moving button presses from the USB MIDI receive callback to the main loop, and initialize a periodic timer that&#8217;s used to blink the LED on the Pico dev board.</p>
<p>Once everything is initialized, the main loop is entered. This loop performs the following functions:</p>
<ol>
<li>Calls the TinyUSB main loop function, tuh_task.</li>
<li>Attempts to mount the microSD card if one is not already mounted.</li>
<li>Attempts to copy audio samples from a file to a free audio buffer if a file is open and a free audio buffer is available. If an audio buffer is available and a file is not opened, zero samples are copied to the available audio buffer.</li>
<li>If a new button is pressed, closes any playing audio file and opens the next audio file to play. The samples will be copied from the newly opened audio file to an audio buffer the next time a free audio buffer is available.</li>
<li>If support for the I2C-based status LEDs is compiled into the project, updates the LEDs to reflect the current state of the hardware.</li>
</ol>
<p>The audio buffers are sized so that one audio buffer is equal to one block of audio samples on the microSD card: 128 stereo 16 bit samples are 512 bytes which is the size of block on the filesystem.</p>
<h3>Build Instructions</h3>
<p>To build the audio player, first clone the repo, cd into the source directory, then make and enter the build directory:</p>
<pre>git clone https://github.com/bikerglen/rp2040-midi-player.git
cd rp2040-midi-player/src
mkdir build
cd build</pre>
<p>Next, run a variation of cmake based on the hardware type. For the Pico-based protoype hardware:</p>
<pre>cmake ..</pre>
<p>For the Adafruit QT PY RP2040-based hardware used by the boards described later in this blog post:</p>
<pre>cmake .. -DPICO_BOARD=adafruit_qtpy_rp2040</pre>
<p>If you decide to add the I2C-based LED indicators to the project, LED support can be compiled into the project by modifying the cmake command line to include either -DSTATUS_LED_CONFIG=single or -DSTATUS_LED_CONFIG=triple. The function of these is described later.</p>
<p>For example, to build using the real hardware with a single status LED, the cmake line would be:</p>
<pre>cmake .. -DPICO_BOARD=adafruit_qtpy_rp2040 -DSTATUS_LED_CONFIG=single</pre>
<p>After running cmake to generate the required makefiles, run make:</p>
<pre>make</pre>
<p>This will generate both a .elf file for use with the Pico debug probe and a .uf2 file to use with the RP2040&#8217;s UF2 bootloader. If you decide to change the build configuration and need to rerun cmake, you can either delete the CMakeCache.txt file or remove the entire build directory.</p>
<h3>Formatting and Naming Audio Files for the Player</h3>
<div id="attachment_6030" style="width: 631px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-13-080655.png"><img class="size-full wp-image-6030" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-13-080655.png" alt="Export settings to format audio samples for the audio player." width="621" height="575" /></a><p class="wp-caption-text">Export settings to format audio samples for the audio player.</p></div>
<p>The audio player uses headerless (raw) stereo audio files sampled at 48 kHz. A stereo pair of stereo samples is ordered left then right. Each sample is 16 bits and in little endian byte order. Files in this format can be exported from Audacity using the settings shown in the Export Audio dialog box shown above. Alternatively, files can be converted using FFMPEG:</p>
<pre>ffmpeg -i in_file.mp3 -f s16le -acodec pcm_s16le -ar 48000 out_file.raw</pre>
<p>Files should be named in the format effectXX.raw where XX is a two digit hex number corresponding to the button that triggers the audio sample, e.g. effect00.raw, effect01.raw, &#8230;, effectff.raw. I will accept pull requests from anyone who wants to add WAVE input or a mapping file to map buttons to audio files.</p>
<h3>Fixing Stuttering Audio</h3>
<div id="attachment_6007" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/scope_0.png"><img class="wp-image-6007 size-full" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/scope_0.png" alt="scope_0" width="800" height="632" /></a><p class="wp-caption-text">Scope traces showing the microSD card block read command occasionally taking an excessive amount of time to complete.</p></div>
<p>After getting the software to mostly work, I had a problem with stuttering audio. I suspected some task inside my main loop wasn&#8217;t completing in time to service and fill the next audio buffer. To narrow the problem down, I decided to profile key portions of the code using an oscilloscope and a few of the unused GPIO&#8217;s on the Pico. By surrounding each key task in the main loop with a gpio_put call to turn on a GPIO before calling the task and another gpio_put call to turn off a GPIO after calling the task, I could see how long each task was taking to complete.</p>
<p>The yellow trace in the oscilloscope screen capture above shows the amount of time spent in the tuh_task call. The green trace shows the amount of time spent reading audio samples from the microSD card then filling the audio buffers with those samples. By looking at the two traces, I quickly narrowed the problem down to the latter of the two tasks. The tuh_task USB tasks always completed quickly. Refilling the audio buffers normally completed in a few milliseconds, but occasionally it&#8217;d take over 20 milliseconds to fill one buffer!</p>
<p>I kept moving the gpio_put calls for the green trace deeper and deeper into the FatFS and SD card driver stack until they were around the call to send the read block command to the microSD card and await the microSD card response. The microSD card I was using would periodically take 22 ms to complete the read block command—and it&#8217;d do this for six read block commands in a row! The solution turned out to be an easy fix: I changed to a different brand of microSD cards and the problem disappeared.</p>
<h3>Remounting the microSD Card after an Error or Removal</h3>
<p>The second issue I had was that once the microSD card code hit an error, it&#8217;d never recover. The solution to this was a bit more difficult to find. After tons of searching, I found a forum post where someone else was having a similar issue. The solution turned out to be adding these lines of code to unmount the microSD card and tell the underlying hardware driver that the card has been removed:</p>
<pre>      f_unmount ("");
      sd_card_t *sd_card_p = sd_get_by_num(0);
      sd_card_p-&gt;state.m_Status |= STA_NOINIT | STA_NODISK;</pre>
<p>These would normally be called in response to the card detect pin detecting the card being removed. I don&#8217;t have a card detect pin so the best I can do is call these after an error and then try to remount the card until successful.</p>
<h2>Boards</h2>
<p>With both the hardware and software working, it was time to create some circuit boards and make the project a bit more permanent and reliable. Since this project comes in two versions, there&#8217;s two different circuit board designs. The first design is for the amplifier and speaker version of the project. The second design is for the DAC-only, line out version of the project.</p>
<h3>Where&#8217;s the Power Connector?!</h3>
<p>While working on the board designs, I discovered that there are USB C on-the-go cables for adding a keyboard or mouse to a phone and, importantly, powering both the phone and keyboard or mouse from a USB wall adapter simultaneously. These are typically sold as USB C OTG adapters with power.</p>
<p>I ordered a few and verified they could power my target system, power the MIDI controller, and pass data from the MIDI controller to the target system. This eliminated the need to add a separate DC jack or USB C connector to power the project. This allowed the board designs to be simpler and the finished projects to be quite a bit smaller. There&#8217;s a section at the end of this blog post on how to use these cables to connect everything together.</p>
<h3>Speaker Version</h3>
<div id="attachment_6051" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0481_2048px.jpg"><img class="wp-image-6051 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0481_2048px-1024x682.jpg" alt="DSC_0481" width="640" height="426" /></a><p class="wp-caption-text">The speaker version circuit board with the Adafruit QT PY RP2040 and Audio BFF boards plugged into it.</p></div>
<p>The completed speaker version board is shown above. The boards for this project are incredibly simple as they serve only to connect pins on the daughter development boards together and mechanically hold the boards in place.</p>
<h4>Schematic</h4>
<div id="attachment_5945" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/speaker-version-schematic.png"><img class="wp-image-5945 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/speaker-version-schematic-1024x764.png" alt="speaker-version-schematic" width="640" height="478" /></a><p class="wp-caption-text">Speaker version schematic.</p></div>
<p>The schematic is shown above. Like I said, it just connects pins on the dev boards together. There&#8217;s also a 3-pin header that&#8217;s connected to the QT PY&#8217;s UART for debugging if needed.</p>
<h4>Board Layout</h4>
<div id="attachment_5947" style="width: 718px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/speaker-version-board1.png"><img class="wp-image-5947 size-full" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/speaker-version-board1.png" alt="speaker-version-board" width="708" height="360" /></a><p class="wp-caption-text">Speaker version PCB layout.</p></div>
<p>The board layout is shown above. The QT PY RP2040 board goes on the left and the Audio BFF board goes on the right. The board mounts to the bottom of the enclosure and against its rear wall. This makes the USB C port and microSD card slot accessible from the rear of the enclosure. I used low-profile headers and header sockets so the between board spacing is theoretically 7.62 mm. It works out in practice to about 7.8 mm.</p>
<h4>Bill of Materials</h4>
<p>The bill of materials for the speaker version is shown in the table below.</p>
<table width="628">
<tbody>
<tr>
<th>Quantity</th>
<th>Description</th>
<th>Manufacturer</th>
<th>Part Number</th>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>QT PY RP2040</td>
<td>Adafruit</td>
<td><a href="https://www.adafruit.com/product/4900">4900</a></td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>Audio BFF for QT PY or Xiao</td>
<td>Adafruit</td>
<td><a href="https://www.adafruit.com/product/5769">5769</a></td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>3-pin standard header</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;">4</td>
<td>7-pin low profile headers, cut from larger strip</td>
<td>Adafruit</td>
<td><a href="https://www.adafruit.com/product/3009">3009</a></td>
</tr>
<tr>
<td style="text-align: right;">4</td>
<td>7-pin low profile header sockets, cut from a 50-pin strip</td>
<td>Samtec</td>
<td><a href="https://www.digikey.com/en/products/detail/samtec-inc/CES-150-01-T-S/1104124">CES-150-01-T-S</a></td>
</tr>
<tr>
<td style="text-align: right;">4</td>
<td>0.315″ Diameter Rubber Feet</td>
<td>3M</td>
<td><a href="https://www.digikey.com/en/products/detail/3m/SJ5076/570288">SJ5076</a></td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td><a href="https://amzn.to/4rNqEwj">2&#8243;, 4 ohm, 10 W black speaker</a></td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td><a href="https://amzn.to/4aEJVcC">USB C OTG Adapter with Power</a></td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;"> 1</td>
<td>Molex PicoBlade 2-pin Cable &#8211; 200mm</td>
<td>Adafruit</td>
<td><a href="https://www.adafruit.com/product/3922">3922</a></td>
</tr>
<tr>
<td style="text-align: right;"> 4</td>
<td>M3 x 4 mm</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;"> 8</td>
<td>M3 x 8 mm</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;"> 4</td>
<td>M3 washers</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;"> 4</td>
<td>M3 hex nuts</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;"> 2</td>
<td>M2 x 6 mm</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
</tbody>
</table>
<h3>Line Out Version</h3>
<div id="attachment_6041" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0476_2048px.jpg"><img class="wp-image-6041 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0476_2048px-1024x682.jpg" alt="DSC_0476" width="640" height="426" /></a><p class="wp-caption-text">The line out version circuit board with the PCM5100 break out board, Adafruit QT PY RP2040 dev board, and microSD breakout board plugged into it.</p></div>
<p>The completed line out version of the board is shown above. The boards for this project are incredibly simple as they serve only to connect pins on the daughter development boards together and mechanically hold the boards in place.</p>
<h4>Schematic</h4>
<div id="attachment_5949" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/dac-version-schematic.png"><img class="wp-image-5949 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/dac-version-schematic-1024x764.png" alt="dac-version-schematic" width="640" height="478" /></a><p class="wp-caption-text">DAC version schematic.</p></div>
<p>The schematic is shown above. Like I said, it just connects pins on the dev boards together. There&#8217;s also a 3-pin header that&#8217;s connected to the QT PY&#8217;s UART for debugging if needed.</p>
<h4>Board Layout</h4>
<div id="attachment_5948" style="width: 952px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/dac-version-board.png"><img class="wp-image-5948 size-full" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/dac-version-board.png" alt="dac-version-board" width="942" height="559" /></a><p class="wp-caption-text">DAC version PCB layout.</p></div>
<p>The board layout is shown above. The PCM5100 breakout board is on the left, the QT PY RP2040 board is in the middle, and the microSD breakout board is on the right. The board mounts to the bottom of the enclosure and against its rear wall. This makes the 3.5 mm line out jack, the USB C port and the microSD card slot accessible from the rear of the enclosure. I used low-profile headers and header sockets so the between board spacing is theoretically 7.62 mm. It works out in practice to about 7.8 mm. The daughter boards are held in place using six 3D-printed standoffs.</p>
<h4>Bill of Materials</h4>
<p>The bill of materials for the line out version is shown in the table below.</p>
<table width="628">
<tbody>
<tr>
<th>Quantity</th>
<th>Description</th>
<th>Manufacturer</th>
<th>Part Number</th>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>PCM5100 breakout board</td>
<td>Adafruit</td>
<td><a href="https://www.adafruit.com/product/6251">6251</a></td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>QT PY RP2040</td>
<td>Adafruit</td>
<td><a href="https://www.adafruit.com/product/4900">4900</a></td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>microSD SPI/SDIO breakout board</td>
<td>Adafruit</td>
<td><a href="https://www.adafruit.com/product/4682">4682</a></td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>3-pin standard header</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;">2</td>
<td>7-pin low profile headers, cut from larger strip</td>
<td>Adafruit</td>
<td><a href="https://www.adafruit.com/product/3009">3009</a></td>
</tr>
<tr>
<td style="text-align: right;">2</td>
<td>7-pin low profile header sockets, cut from a 50-pin strip</td>
<td>Samtec</td>
<td><a href="https://www.digikey.com/en/products/detail/samtec-inc/CES-150-01-T-S/1104124">CES-150-01-T-S</a></td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>8-pin low profile headers, cut from larger strip</td>
<td>Adafruit</td>
<td><a href="https://www.adafruit.com/product/3009">3009</a></td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>8-pin low profile header sockets, cut from a 50-pin strip</td>
<td>Samtec</td>
<td><a href="https://www.digikey.com/en/products/detail/samtec-inc/CES-150-01-T-S/1104124">CES-150-01-T-S</a></td>
</tr>
<tr>
<td style="text-align: right;">1</td>
<td>9-pin low profile headers, cut from larger strip</td>
<td>Adafruit</td>
<td><a href="https://www.adafruit.com/product/3009">3009</a></td>
</tr>
<tr>
<td style="text-align: right;"> 1</td>
<td>9-pin low profile header sockets, cut from a 50-pin strip</td>
<td>Samtec</td>
<td><a href="https://www.digikey.com/en/products/detail/samtec-inc/CES-150-01-T-S/1104124">CES-150-01-T-S</a></td>
</tr>
<tr>
<td style="text-align: right;">4</td>
<td>0.315″ Diameter Rubber Feet</td>
<td>3M</td>
<td><a href="https://www.digikey.com/en/products/detail/3m/SJ5076/570288">SJ5076</a></td>
</tr>
<tr>
<td style="text-align: right;"> 1</td>
<td><a href="https://amzn.to/4aEJVcC">USB C OTG Adapter with Power</a></td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;"> 2</td>
<td>M3 x 4 mm screws</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;"> 14</td>
<td>M2 x 4 mm screws</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: right;"> 6</td>
<td><a href="https://github.com/bikerglen/rp2040-midi-player/blob/main/case/dac-version/standoff%20M2%20x%205%20x%207.8%20mm.3mf">3d printed</a> M2 x 5 mm x 7.8 mm standoffs</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
</tbody>
</table>
<h2>Adding LEDs using I2C</h2>
<div id="attachment_6042" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0466_2048px.jpg"><img class="wp-image-6042 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0466_2048px-1024x682.jpg" alt="DSC_0466" width="640" height="426" /></a><p class="wp-caption-text">The LED board mounted to the underside of the line out version&#8217;s lid.</p></div>
<p>After prototyping the hardware and software and experimenting with a few different case designs, I decided I wanted some LEDs to indicate that a MIDI controller was connected, the microSD card was mounted, and the unit was ready to play samples. I really like the looks of the <a href="https://www.printables.com/model/52673-piscsi-mini-case">PiSCSI Mini Case</a> and the implementation of the LED and light guide on that case so I decided to design a similar light guide and add some LEDs to the project!</p>
<h3>Hardware</h3>
<div id="attachment_6043" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0468_2048px.jpg"><img class="wp-image-6043 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0468_2048px-1024x682.jpg" alt="DSC_0468" width="640" height="426" /></a><p class="wp-caption-text">The top side of the LED board showing the JST connector, I2C IO expander, and three LEDs. Behind the board is a light guide.</p></div>
<p>I liked the design of the PiSCSI LED and light guide but I didn&#8217;t like the idea of soldering and heat shrinking a bunch of resistors and wires together, especially since I wanted up to three LEDs. I decided to build a small board that connected to the QT PY RP2040&#8217;s qwiic connector via a short cable and could drive up to three LEDs using a TI TCA9536 I2C IO expander.</p>
<h4>Schematic</h4>
<div id="attachment_5950" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/qwiic-leds-schematic.png"><img class="wp-image-5950 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/qwiic-leds-schematic-1024x764.png" alt="qwiic-leds-schematic" width="640" height="478" /></a><p class="wp-caption-text">Schematic of the I2C IO expander LED board.</p></div>
<p>The schematic for the LED board is shown above. There&#8217;s a 1 mm pitch JST SH connector for connecting to the I2C qwiic host. That connector connects power, ground, SDA, and SCL to the TCA9536 I2C IO expander. The IO expander then drives up to three LEDs through current limiting resistors. There&#8217;s an option to add pullup resistors to the SDA and SCL signals if the I2C host doesn&#8217;t already have them.</p>
<h4>Board</h4>
<div id="attachment_5952" style="width: 531px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/qwiic-leds-board1.png"><img class="wp-image-5952 size-full" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/qwiic-leds-board1.png" alt="qwiic-leds-board" width="521" height="184" /></a><p class="wp-caption-text">I2C IO expander LED board layout.</p></div>
<p>After drawing the schematic, the requirements for the board were that the LEDs would be spaced wide enough apart to accommodate the light guides with 2 mm of space between each guide. It would also have 2.2 mm holes for M2 screws to connect it to either the base or the lid of a 3D-printed enclosure. The completed board layout is shown above.</p>
<p>The outlines of the light guides and the spaces between the light guides are shown with gray lines. The space between the light guides is enough room for a 3D printed separator to prevent light from bleeding between the guides. The screw heads overlap a bit into this space but not enough to affect the construction or operation of a separator.</p>
<h4>Bill of Materials</h4>
<p>The bill of materials for the LED board is shown in the table below. I selected LEDs with a viewing angle between 30° and 40°. Narrower viewing angles don&#8217;t throw enough light sideways to light up the light guide very well.</p>
<table width="628">
<tbody>
<tr>
<th>Quantity</th>
<th>Description</th>
<th>Manufacturer</th>
<th>Part Number</th>
</tr>
<tr>
<td>1</td>
<td>JST SH 1 mm pitch, 4 position, surface mount socket</td>
<td>Adafruit</td>
<td><a href="https://www.digikey.com/en/products/detail/adafruit-industries-llc/4208/10230005">4208</a></td>
</tr>
<tr>
<td>1</td>
<td>4 port I2C IO expander</td>
<td>Texas Instruments</td>
<td><a href="https://www.digikey.com/en/products/detail/texas-instruments/TCA9536DGKR/15856810">TCA9536DGKR</a></td>
</tr>
<tr>
<td>1</td>
<td>0.1 µF 0603 capacitor</td>
<td>generic</td>
<td>&#8212;</td>
</tr>
<tr>
<td>2</td>
<td>4.7 kΩ 0603 resistor</td>
<td>generic</td>
<td>&#8212;</td>
</tr>
<tr>
<td>3</td>
<td>100 Ω &#8211; 1 kΩ 0603 resistors depending on LED forward voltage and desired current / brightness</td>
<td>generic</td>
<td>&#8212;</td>
</tr>
<tr>
<td>3</td>
<td>T-1 3 mm LEDs</td>
<td>generic</td>
<td>&#8212;</td>
</tr>
<tr>
<td>1</td>
<td>50 mm JST SH 1 mm pitch, 4 position cable assembly</td>
<td>Adafruit</td>
<td><a href="https://www.digikey.com/en/products/detail/adafruit-industries-llc/4399/10824268">4399</a></td>
</tr>
</tbody>
</table>
<h3>Light Guides</h3>
<div id="attachment_5991" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-10-181954.png"><img class="wp-image-5991 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-10-181954-1024x724.png" alt="Screenshot 2026-02-10 181954" width="640" height="453" /></a><p class="wp-caption-text">Dimensioned drawing of the light guide.</p></div>
<p>The diagram above shows a sketch of the light guide including its dimensions. It&#8217;s 8 mm wide and 6 mm tall. The visible window is 8 mm x 2 mm. A hole in the center is sized to accommodate most T-1 3 mm LEDs.</p>
<div id="attachment_5993" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-10-182550.png"><img class="wp-image-5993 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-10-182550-1024x630.png" alt="Screenshot 2026-02-10 182550" width="640" height="394" /></a><p class="wp-caption-text">Getting ready to print the light guides.</p></div>
<p>I printed the light guides five at a time out of Bambu Lab clear transparent PETG filament. I used the default 0.20 mm standard settings but placed the seam on the back and upped the number of wall loops to 4. Increasing the wall loops ensured the entire light guide was filled in solid.</p>
<div id="attachment_6050" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0482_2048px.jpg"><img class="wp-image-6050 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0482_2048px-1024x682.jpg" alt="DSC_0482" width="640" height="426" /></a><p class="wp-caption-text">The printed light guides.</p></div>
<p>The photo above shows a handful of the printed light guides. Since they&#8217;re not completely transparent, they will evenly diffuse the light from the LED across the entire visible surface of the light guide when it&#8217;s in the enclosure.</p>
<h3>Assembly</h3>
<div id="attachment_6045" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0474_2048px.jpg"><img class="wp-image-6045 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0474_2048px-1024x682.jpg" alt="DSC_0474" width="640" height="426" /></a><p class="wp-caption-text">The LEDs mostly hold the light guides in place. The entire assembly mounts into a cavity with cutouts for each light guide then screws hold it in place.</p></div>
<p>The photo above shows the light guides pressed onto the LEDs on the circuit board. For the DAC / line out version of the project, I populated all three LEDs and light guides and it will mount into the underside of the lid of the enclosure. For the speaker version of this project, I only populated the right-most LED and light guide then printed another small piece to secure the assembly to the bottom of the inside of the enclosure base. Details to follow.</p>
<h3>Software</h3>
<p>With the boards assembled, it was time to write some software to test the board. This example is available in the examples/i2c_expander_example directory of the <a href="https://github.com/bikerglen/rp2040-midi-player">player&#8217;s GitHub repository</a>. Key sections of the code are highlighted below.</p>
<p>First some defines for the I2C instance and I2C pins on the Pico:</p>
<pre>#define I2C_NUMBER  0
#define I2C_SDA_PIN 16
#define I2C_SCL_PIN 17

#define TCA9536_I2C_ADDRESS 0x41</pre>
<p>Next configure the Pico&#8217;s I2C interface:</p>
<pre>  // configure rp2040 i2c hardware
  i2c_init (I2C_INSTANCE(I2C_NUMBER), 100 * 1000);
  gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
  gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
  gpio_pull_up(I2C_SDA_PIN);
  gpio_pull_up(I2C_SCL_PIN);</pre>
<p>This code initializes the I2C I/O expander and turns all the LEDs off:</p>
<pre>  // set all four tca9536 pins to outputs by writing config reg 0x03 to 0x00
  data[0] = 0x03;
  data[1] = 0x00;
  i2c_write_blocking (I2C_INSTANCE(I2C_NUMBER), TCA9536_I2C_ADDRESS, data, 2, false);

  // set all four tca9536 outputs high by writing output port reg 0x01 to 0x0F (all leds off)
  data[0] = 0x01;
  data[1] = 0x0F;
  i2c_write_blocking (I2C_INSTANCE(I2C_NUMBER), TCA9536_I2C_ADDRESS, data, 2, false);</pre>
<p>Finally, individual LEDs can be controlled by setting or clearing bits in the I/O expander&#8217;s output register. Setting a bit turns the LED off and clearing a bit turns the LED on. The left led is controlled by bit 2:</p>
<pre>    // left led on
    data[0] = 0x01;
    data[1] = 0x0B;
    i2c_write_blocking (I2C_INSTANCE(I2C_NUMBER), TCA9536_I2C_ADDRESS, data, 2, false);</pre>
<p>The middle led is controlled by bit 1:</p>
<pre>    // middle led on
    data[0] = 0x01;
    data[1] = 0x0d;
    i2c_write_blocking (I2C_INSTANCE(I2C_NUMBER), TCA9536_I2C_ADDRESS, data, 2, false);</pre>
<p>The right led is controlled by bit 0:</p>
<pre>    // right led on
    data[0] = 0x01;
    data[1] = 0x0e;
    i2c_write_blocking (I2C_INSTANCE(I2C_NUMBER), TCA9536_I2C_ADDRESS, data, 2, false);</pre>
<p>The i2c_expander_example initializes everything then runs a loop that blinks the LEDs one at a time from left to right. The example can be built and run by changing into the i2c_expander_example directory and executing the following commands:</p>
<pre>mkdir build
cd build
cmake ..
make 
openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program i2c_example.elf verify reset exit"</pre>
<p>The main audio player uses these building blocks to control the LEDs in response to the state of the system. The number of LEDs used by the main audio player is configured by supplying additional command line options to cmake when building the player. Adding -DSTATUS_LED_CONFIG=single configures the player to use a single LED and -DSTATUS_LED_CONFIG=triple configures the player to use three LEDs.</p>
<p>When three LEDs are configured, the LEDs indicate left to right: the MIDI controller connection state, the microSD card error state, and whether a sample is playing or not. When only one LED is configured, the LED indicates a MIDI controller is connected and the microSD card is mounted without errors. When the LED is on, the system is ready to play samples. The LED does not change state when a sample is playing.</p>
<h2>Enclosures</h2>
<div id="attachment_6074" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0494_with_screws_2048px.jpg"><img class="size-large wp-image-6074" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0494_with_screws_2048px-1024x677.jpg" alt="    On the left is the completed line out version. On the right is the speaker version." width="640" height="423" /></a><p class="wp-caption-text">On the left is the completed line out version. On the right is the speaker version.</p></div>
<p>Since this project comes into two variations, I needed two different enclosures. The first enclosure is for the speaker version of the project that includes a small amplifier and speaker. The second enclosure is for the line out version of the project that includes a DAC with line out on a stereo 3.5 mm jack.</p>
<h3>Speaker Version</h3>
<div id="attachment_6057" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0484_2048px1.jpg"><img class="wp-image-6057 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0484_2048px1-1024x682.jpg" alt="DSC_0484" width="640" height="426" /></a><p class="wp-caption-text">Front view of the speaker version.</p></div>
<div id="attachment_6056" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0485_2048px.jpg"><img class="wp-image-6056 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0485_2048px-1024x682.jpg" alt="DSC_0485" width="640" height="426" /></a><p class="wp-caption-text">Rear view of the speaker version.</p></div>
<p>The speaker version of the completed project is shown in the photos above. This enclosure was modeled after the <a href="https://bikerglen.com/blog/lizard-3d-printed-single-purpose-soundboard/">Lizard project&#8217;s enclosure</a>. It&#8217;s been made square and slightly taller. It includes the same raised hex dome speaker grill printed out of a separate color from the rest of the lid.</p>
<div id="attachment_6024" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-12-1425061.png"><img class="wp-image-6024 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-12-1425061-1024x722.png" alt="Screenshot 2026-02-12 142506" width="640" height="451" /></a><p class="wp-caption-text">Dimensioned diagram of the speaker version.</p></div>
<p>A diagram of the enclosure is shown above. The enclosure measures 71 x 71 x 52 mm excluding the grill. On the front is a single LED to indicate the player is ready to play samples. On the rear of the enclosure is a microSD card slot and a USB C connector for power and the MIDI controller.</p>
<p>All three files in the <a href="https://github.com/bikerglen/rp2040-midi-player/tree/main/case/spkr-version">speaker version enclosure</a> directory need to be 3D printed as well as a <a href="https://github.com/bikerglen/rp2040-midi-player/blob/main/case/led-light-guide/lightguides%202.2mm.3mf">single light guide</a>. The .3mf files are configured for the Bambu Studio slicer for printing on a Bambu printer.</p>
<div id="attachment_6048" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0478_2048px.jpg"><img class="wp-image-6048 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0478_2048px-1024x682.jpg" alt="DSC_0478" width="640" height="426" /></a><p class="wp-caption-text">Inside view of the speaker version with all the electronics.</p></div>
<p>Once the files are printed, the main board is held in place using 4 M3 x 4 mm screws. The LED board and light guides are held in place using the led cover and 2 M2 x 6 mm screws. The speaker is held in place on the lid using 4 sets of M3 x 8 mm screws, M3 washers, and M3 hex nuts. Finally the lid is held in place using 4 M3 x 8 mm screws.</p>
<h3>Line Out Version</h3>
<div id="attachment_6059" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0492_2048px.jpg"><img class="wp-image-6059 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0492_2048px-1024x682.jpg" alt="DSC_0492" width="640" height="426" /></a><p class="wp-caption-text">Front view of the line out version.</p></div>
<div id="attachment_6060" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0493_2048px.jpg"><img class="wp-image-6060 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0493_2048px-1024x682.jpg" alt="DSC_0493" width="640" height="426" /></a><p class="wp-caption-text">Rear view of the line out version.</p></div>
<p>The line out version&#8217;s enclosure is shown in the photos above. This enclosure design was inspired by the <a href="https://www.printables.com/model/52673-piscsi-mini-case">PiSCSI Mini Case</a>. It&#8217;s smaller and its features have been made slimmer to better match its smaller size.</p>
<div id="attachment_6025" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-12-143648.png"><img class="wp-image-6025 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/Screenshot-2026-02-12-143648-1024x723.png" alt="Screenshot 2026-02-12 143648" width="640" height="452" /></a><p class="wp-caption-text">Dimensioned diagram of the line out version.</p></div>
<p>A diagram of the enclosure is shown above. The enclosure measures 80 x 50 x 27 mm. On the front are three status LEDs to indicate a MIDI controller is connected, the microSD card is mounted, and whether audio is playing or not. If the code is compiled for a single status LED, the left two LEDs will remain off and the right-most LED will simply indicate the player is ready to play samples. On the rear of the enclosure is a microSD card slot, a USB C connector for power and the MIDI controller, and a 3.5 mm stereo audio jack.</p>
<p>All three files in the <a href="https://github.com/bikerglen/rp2040-midi-player/tree/main/case/dac-version">line-out version enclosure</a> directory need to be 3D printed as well as three <a href="https://github.com/bikerglen/rp2040-midi-player/blob/main/case/led-light-guide/lightguides%202.2mm.3mf">light guides</a>. The .3mf files are configured for the Bambu Studio slicer for printing on a Bambu printer.</p>
<div id="attachment_6046" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0475_2048px.jpg"><img class="wp-image-6046 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0475_2048px-1024x682.jpg" alt="DSC_0475" width="640" height="426" /></a><p class="wp-caption-text">Inside view of the line out version.</p></div>
<p>Once the files are printed, the main board is held in place using the slots and guides inside the enclosure and 2 M3 x 4 mm screws. The LED board and light guides are held in place using 2 M2 x 4 mm screws. The lid is a simple friction fit. The 7.8 mm standoffs go between the daughter boards and the main board and are held in place using 12 M2 x 4 mm screws.</p>
<h2>Connecting Power and the MIDI Controller</h2>
<div id="attachment_6053" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/PXL_20260213_223004577_2048px.jpg"><img class="wp-image-6053 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/PXL_20260213_223004577_2048px-1024x572.jpg" alt="PXL_20260213_223004577_2048px" width="640" height="358" /></a><p class="wp-caption-text">How to connect and power the projects. Yellow cable is power from a USB wall adapter, red cable goes to the MIDI controller, OTG adapter plugs directly into the project.</p></div>
<p>Power and the MIDI controller are connected to the project using a USB C OTG with power adapter cable:</p>
<ul>
<li>The male USB C connector on the OTG adapter plugs into the back of the project.</li>
<li>The male USB A connector from the MIDI controller plugs into the female USB A connector on the OTG adapter. This is the red cable in the photo above.</li>
<li>The male USB C connector from the power brick plugs into the female USB C connector on the OTG adapter. This is the yellow cable in the photo above.</li>
</ul>
<p>The photo above shows these connections. Using an OTG adapter saves having to have a separate USB C or DC barrel jack on the back of the project which greatly simplified the board and enclosure design.</p>
<h2>Bonus: 3D Printed Stands for the MIDI Controllers!</h2>
<div id="attachment_6064" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0496_2048px.jpg"><img class="wp-image-6064 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/02/DSC_0496_2048px-1024x681.jpg" alt="DSC_0496_2048px" width="640" height="426" /></a><p class="wp-caption-text">A Midi Fighter Twister on a 3D printed stand, a 3D printed stand, and a Midi Fighter Spectra lying flat on the bench.</p></div>
<p>If you need a stand for the Launchpad Mini Mk3, <a href="https://www.thingiverse.com/thing:6525455">this 3D printable stand</a> for it on Thingiverse is pretty nice. If you need a stand for the Midi Fighter Twister or Midi Fighter Spectra, I designed a similar 3d printable stand for them. The files for my stand are in the <a href="https://github.com/bikerglen/rp2040-midi-player/tree/main/case/midi-fighter-stand">case/midi-fighter-stand</a> directory of this project&#8217;s <a href="https://github.com/bikerglen/rp2040-midi-player">GitHub repository</a>. The same stand works for either Midi Fighter controller.</p>
<h2>Design Files</h2>
<p>The source code, PCB design files, and 3D print files for this project can be downloaded at <a href="https://github.com/bikerglen/rp2040-midi-player">https://github.com/bikerglen/rp2040-midi-player</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://bikerglen.com/blog/rp2040-midi-soundboard-player-for-live-streaming/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building a 3D-Printed Traffic Light with Miniature Round Displays</title>
		<link>https://bikerglen.com/blog/3d-printed-traffic-light-with-mini-round-displays/</link>
		<comments>https://bikerglen.com/blog/3d-printed-traffic-light-with-mini-round-displays/#comments</comments>
		<pubDate>Mon, 12 Jan 2026 17:53:59 +0000</pubDate>
		<dc:creator><![CDATA[Glen]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://bikerglen.com/blog/?p=5438</guid>
		<description><![CDATA[In this project, I build a miniature 3D-printed traffic signal and matching Wi-Fi-enabled controller cabinet. The big twist is that each light on the signal is really a small round LCD permitting traditional traffic signals like bicycles, globes, and arrows &#8230; <a href="https://bikerglen.com/blog/3d-printed-traffic-light-with-mini-round-displays/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<div id="attachment_5807" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0353_2k.jpg"><img class="wp-image-5807 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0353_2k-1024x679.jpg" alt="DSC_0353" width="640" height="424" /></a><p class="wp-caption-text">The completed LCD traffic signal including body, mast, controller cabinet with Wi-Fi antenna, concrete pedestal for the controller, and platform.</p></div>
<p>In this project, I build a miniature 3D-printed traffic signal and matching Wi-Fi-enabled controller cabinet. The big twist is that each light on the signal is really a small round LCD permitting traditional traffic signals like bicycles, globes, and arrows as well as random images to be displayed—without disassembling or taking apart the signal.</p>
<p><span id="more-5438"></span></p>
<h2>Inspiration</h2>
<div id="attachment_5824" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0390_2k.jpg"><img class="wp-image-5824 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0390_2k-1024x682.jpg" alt="DSC_0390" width="640" height="426" /></a><p class="wp-caption-text">On the left, a 3D-printed USB-controlled neopixel traffic light. The electronics are in the base. On the right is a circular LCD also in a 3D-printed traffic light housing. Front center is a Waveshare 0.71&#8243; LCD and its acrylic cabochon lens.</p></div>
<p>Two of the first projects I built with my 3D printer are shown in the photo above. On the left is a traditional traffic signal built with three W2812b Neopixel RGB LEDs. Each aspect of the signal face can be set to an arbitrary color from a terminal window. They use a PIC16F1454 emulating a USB communications device class device for a controller.</p>
<p>On the right is a 2.1&#8243; round LCD driven by an Adafruit Qualia ESP32-S3 board running CircuitPython. The housing is styled after a <a href="https://archive.org/details/crousehindselect00crou/page/19/mode/1up">vintage Crouse-Hinds Type RM traffic signal unit</a> from the early 1900s. It&#8217;s neat being able to display arbitrary images on the round LCD.</p>
<p>In the foreground is a small 0.71&#8243; Waveshare LCD module. After ordering a bunch of these small displays, I wondered if I could build a three aspect traffic signal like the one on the left in the photo above except using an LCD display instead of just a simple color changing LED.</p>
<div id="attachment_5826" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0393_2k.jpg"><img class="wp-image-5826 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0393_2k-1024x682.jpg" alt="DSC_0393" width="640" height="426" /></a><p class="wp-caption-text">The Waveshare 0.71&#8243; round LCD is indeed small at only 18 mm in diameter. Each LCD includes a small round lens that affixes to the display using pressure sensitive adhesive.</p></div>
<p>The Waveshare displays are small, measure only 18 mm in diameter, and are mounted on a board that measures roughly 20 x 22 mm. The 16-bit color displays are 160 x 160 pixels and use a GC9D01 controller with a SPI interface. They connect to a microcontroller using an 8-pin JST SH 1 mm pitch connector. Each display includes a small acrylic cabochon lens.</p>
<h2>About the Design</h2>
<div id="attachment_5910" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0359_2k-3.jpg"><img class="wp-image-5910 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0359_2k-3-1024x682.jpg" alt="DSC_0359_2k-3" width="640" height="426" /></a><p class="wp-caption-text">Angled view of the completed LCD traffic signal including body, mast, controller cabinet with Wi-Fi antenna, concrete pedestal for the controller, and platform.</p></div>
<p>After a few prototypes and wondering what to do about all the wires and required microcontroller, I settled on the final design shown in the photo above. The main signal body is exactly the same size as the Neopixel RGB LED controller but houses three full-color 160 x 160 pixel LCD modules! The electronics have been moved from the round base to a controller cabinet that looks just like the street-side cabinet at a real intersection. The entire project is just under 180 mm tall.</p>
<div id="attachment_5809" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0363_2k.jpg"><img class="wp-image-5809 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0363_2k-1024x682.jpg" alt="DSC_0363" width="640" height="426" /></a><p class="wp-caption-text">Each signal aspect is a 160 x 160 LCD module capable of displaying full color images.</p></div>
<p>Each signal aspect is a little miniature display. This one is displaying the profile picture I use on many social media sites (and at my day job).</p>
<div id="attachment_5810" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0365_2k.jpg"><img class="wp-image-5810 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0365_2k-1024x682.jpg" alt="DSC_0365" width="640" height="426" /></a><p class="wp-caption-text">Each signal aspect includes a black hood.</p></div>
<p>Each aspect has a black sun hood to shield the display from ambient light like a real traffic signal. The housing and the hoods are printed as a single piece using Bambu Lab&#8217;s AMS but they could be printed separately and glued together if necessary.</p>
<div id="attachment_5811" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0359-2_2k.jpg"><img class="wp-image-5811 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0359-2_2k-1024x682.jpg" alt="DSC_0359-2_2k" width="640" height="426" /></a><p class="wp-caption-text">A close up of the signal mast and controller cabinet.</p></div>
<p>To the right of the signal is a controller cabinet. The controller cabinet rests on a concrete pedestal. On top of the controller cabinet is my favorite 2.4 GHz Wi-Fi antenna that doesn&#8217;t require a separate ground plane. If you build lots of these, you can use Wi-Fi and MQTT to synchronize their operation!</p>
<div id="attachment_5812" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0379_2k.jpg"><img class="wp-image-5812 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0379_2k-1024x682.jpg" alt="DSC_0379" width="640" height="426" /></a><p class="wp-caption-text">The controller cabinet houses the electronics that control the LCDs.</p></div>
<p>Inside the controller cabinet are the electronics that control the traffic signal. Conceivably the electronics are small enough to fit in a round base under the signal but the cabinet is cute and more like a real traffic signal.</p>
<div id="attachment_5817" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0382_2k.jpg"><img class="wp-image-5817 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0382_2k-1024x682.jpg" alt="DSC_0382" width="640" height="426" /></a><p class="wp-caption-text">On the back of the controller cabinet is USB C port for powering and programming the project. On the back of the signal mast is a slot for routing the pre-terminated cable assemblies from the LCD to the controller.</p></div>
<p>On the back of the controller cabinet is a USB 2.0 Type C connector for powering and programming the traffic signal. I used pre-terminated JST SH cable assemblies to connect the displays to the controller. These assemblies required me to add a slot down the back of the mast and rectangular holes in the platform, stand, and cabinet to route the cables and connectors without disassembling the cables.</p>
<div id="attachment_5815" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0381_2k.jpg"><img class="wp-image-5815 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0381_2k-1024x682.jpg" alt="DSC_0381" width="640" height="426" /></a><p class="wp-caption-text">There&#8217;s 24 separate wires inside the signal mast!</p></div>
<p>Inside the slot are 24 wires—eight for each display! It&#8217;s amazing that they fit but they do! The wires are held in place using the two hooks sculpted into the mast.</p>
<h2>Bill of Materials</h2>
<p>This project requires some electronics, some mechanical hardware, some 3D printed plastic pieces, and a custom controller printed circuit board. The parts required are listed in this section. Building the controller and printing the plastic pieces is detailed in following sections.</p>
<h3>Electronics</h3>
<div id="attachment_5834" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0399_2k.jpg"><img class="wp-image-5834 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0399_2k-1024x682.jpg" alt="DSC_0399" width="640" height="426" /></a><p class="wp-caption-text">It&#8217;s a bit of a mess but it&#8217;s mostly cabling, connectors, and passives.</p></div>
<p>The electronics for this project consists of three small LCD displays, cables to connect the displays to a controller card, the parts for the controller card, a Wi-Fi antenna, and a cable to connect the Wi-Fi antenna to the controller card. A lower cost antenna and antenna cable can be substituted for the specified parts or the patch antenna supplied with the XIAO board can be stuck to the controller cabinet&#8217;s right interior wall.</p>
<table width="628">
<tbody>
<tr>
<th>Quantity</th>
<th>Description</th>
<th>Manufacturer</th>
<th>Part Number</th>
</tr>
<tr>
<td>3</td>
<td>0.71&#8243; round 160&#215;160 LCD</td>
<td>Waveshare</td>
<td>29380</td>
</tr>
<tr>
<td>3</td>
<td>JST SR 1.00mm 8 position 12&#8243; cable assembly</td>
<td>JST</td>
<td>A08SR08SR30K305A</td>
</tr>
<tr>
<td>3</td>
<td>JST SR 1.00mm 8 position SMT vertical header</td>
<td>JST</td>
<td>BM08B-SRSS-TB(LF)(SN)</td>
</tr>
<tr>
<td>1</td>
<td>XIAO ESP32S3 module, no headers</td>
<td>Seeed Studio</td>
<td><span id="spnManufacturerPartNumber">113991114 </span></td>
</tr>
<tr>
<td>1</td>
<td>Octal non-inverting level translator / buffer IC</td>
<td>Texas Instruments</td>
<td>SN74LVC245APWR</td>
</tr>
<tr>
<td>1</td>
<td>10k 5% 0605 resistor</td>
<td>Generic</td>
<td>&#8212;</td>
</tr>
<tr>
<td>6</td>
<td>100 5% 0605 resistor</td>
<td>Generic</td>
<td>&#8212;</td>
</tr>
<tr>
<td>1</td>
<td>0.1 uF 0605 capacitor</td>
<td>Generic</td>
<td>&#8212;</td>
</tr>
<tr>
<td>1</td>
<td>Miniature 2.4GHz RP-SMA antenna</td>
<td>TE Connectivity Linx</td>
<td>ANT-2.4-CW-RH-RPS</td>
</tr>
<tr>
<td>1</td>
<td>100mm U.FL to RP-SMA coax cable assembly</td>
<td>Amphenol RF</td>
<td>336314-13-0100</td>
</tr>
</tbody>
</table>
<h3>Hardware</h3>
<div id="attachment_5867" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0432_2k.jpg"><img class="size-large wp-image-5867" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0432_2k-1024x682.jpg" alt="Nuts and bolts." width="640" height="426" /></a><p class="wp-caption-text">Nuts and bolts.</p></div>
<p>Hardware for this project consists of screws to mount the displays to their mounting frame and to attach the stand and controller cabinet to the base platform.</p>
<table width="628">
<tbody>
<tr>
<th>Quantity</th>
<th>Size</th>
<th>Purpose</th>
</tr>
<tr>
<td>6</td>
<td>M2 x 4mm pan head hex screws</td>
<td>Mount displays to display mounting plate</td>
</tr>
<tr>
<td>3</td>
<td>2-56 x 7/16&#8243; pan head hex screws</td>
<td>Mount controller cabinet to base platform</td>
</tr>
<tr>
<td>4</td>
<td>2-56 x 5/16&#8243; black oxide flat head hex screws</td>
<td>Mount stand to base platform</td>
</tr>
<tr>
<td>7</td>
<td>2-56 hex nuts</td>
<td>&#8212;</td>
</tr>
<tr>
<td>4</td>
<td>0.315″ diameter rubber feet, e.g. <a href="https://www.digikey.com/en/products/detail/3m/SJ5076/570288">DigiKey #SJ5076</a></td>
<td>&#8212;</td>
</tr>
</tbody>
</table>
<h3>Filament</h3>
<div id="attachment_5831" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0398_2k.jpg"><img class="wp-image-5831 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0398_2k-1024x682.jpg" alt="DSC_0398" width="640" height="426" /></a><p class="wp-caption-text">The Bambu Lab PLA filaments used for the project.</p></div>
<p>I used Bambu&#8217;s PLA filament for this project. I&#8217;ve had success using filament from Polyterra and Polar Filament as well. The links below take you to the specific Bambu filaments I used for this project:</p>
<ul>
<li><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=43045596233864">Sunflower Yellow Basic PLA</a></li>
<li><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40988815556744">Black Basic PLA</a></li>
<li><a href="https://us.store.bambulab.com/products/pla-matte?id=43292383936648">Bone White Matte PLA</a></li>
<li><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40475106803848">Silver Basic PLA</a> (or <a href="https://us.store.bambulab.com/products/pla-metal?id=41150855610504">Iron Gray Metallic PLA</a>)</li>
</ul>
<h2>Building the Controller</h2>
<div id="attachment_5836" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0402_2k.jpg"><img class="wp-image-5836 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0402_2k-1024x682.jpg" alt="DSC_0402_2k" width="640" height="426" /></a><p class="wp-caption-text">The XIAO ESP32-S3 module that forms the heart of the signal controller plus the circuit board, connectors, buffer IC, and passives.</p></div>
<p>The photo above shows the parts required to build the controller for the signal. They&#8217;re all surface mount.</p>
<h3>Schematic</h3>
<div id="attachment_5789" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/rect-smol-lcd-signal-controller-schematic-v1.png"><img class="wp-image-5789 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/rect-smol-lcd-signal-controller-schematic-v1-1024x404.png" alt="rect smol lcd signal controller schematic v1" width="640" height="253" /></a><p class="wp-caption-text">Completed schematic for the controller board.</p></div>
<p>The schematic for the controller is shown above. At the heart of the controller is a Seeed Studio XIAO ESP32-S3 module. It can run CircuitPython which makes displaying images on the displays much easier than using the Arduino development environment.</p>
<p>The low-speed, per-display signals like control/data selects and chip selects run directly from the XIAO module to the displays. The common high-speed clock and data signals are buffered, duplicated, and ran through some 100 Ω resistors to lessen ringing and emissions and ran to the displays. Then it&#8217;s just 1 mm pitch JST SH headers.</p>
<h3>Layout</h3>
<div id="attachment_5790" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/rect-smol-lcd-signal-controller-board-v1.png"><img class="wp-image-5790 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/rect-smol-lcd-signal-controller-board-v1-1024x449.png" alt="rect smol lcd signal controller board v1" width="640" height="281" /></a><p class="wp-caption-text">Completed layout for the controller board.</p></div>
<p>The board layout is nothing spectacular. There is, however, a large keepout on the top layer under the XIAO module where no traces or vias can be run to keep from shorting them to the XIAO module.</p>
<div id="attachment_5791" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/boards-top.png"><img class="wp-image-5791 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/boards-top-1024x419.png" alt="boards top" width="640" height="262" /></a><p class="wp-caption-text">OSH Park preview of the board top.</p></div>
<p>I fabbed the boards at OSH Park. The preview for the board top is shown above.</p>
<div id="attachment_5792" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/boards-bottom.png"><img class="wp-image-5792 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/boards-bottom-1024x419.png" alt="boards bottom" width="640" height="262" /></a><p class="wp-caption-text">OSH Park preview of the board bottom.</p></div>
<p>And the preview for the board bottom is shown above.</p>
<h3>Boards</h3>
<div id="attachment_5820" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0383_2k.jpg"><img class="wp-image-5820 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0383_2k-1024x678.jpg" alt="DSC_0383" width="640" height="424" /></a><p class="wp-caption-text">Board fabbed at OSH Park complete with trademark purple color and mouse bites.</p></div>
<p>The fabbed board is shown above. The mouse bites must be removed for the board to fit in the controller cabinet. I used a rotary tool, safety glasses, and an N95 respirator to grind off the mouse bites.</p>
<h3>Assembly</h3>
<div id="attachment_5838" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0404_2k.jpg"><img class="wp-image-5838 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0404_2k-1024x682.jpg" alt="DSC_0404" width="640" height="426" /></a><p class="wp-caption-text">The assembled controller board. The mouse bites have been ground off to allow the board to fit in the controller cabinet.</p></div>
<p>The assembled control board is shown in the photo above. After grinding off the mouse bites, I started assembly by tacking down two opposite corners of the XIAO module then test fitting the board and USB C connector in the controller cabinet. Since everything was aligned and fit, I finished soldering the module then moved on to the rest of the components.</p>
<div id="attachment_5839" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0405_2k.jpg"><img class="wp-image-5839 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0405_2k-1024x696.jpg" alt="DSC_0405" width="640" height="435" /></a><p class="wp-caption-text">The completed controller board seated in the controller cabinet.</p></div>
<p>The completed controller seats comfortably in its slots inside the controller cabinet in the photo above. A USB C cable should fully plug into the XIAO module from the rear of the cabinet.</p>
<h2>Installing Software on Controller</h2>
<p>The controller runs CircuitPython. The biggest catch is that the <a href="https://circuitpython.org/board/seeed_xiao_esp32s3/">default build for the XIAO ESP32S3 module</a> supports a maximum of one display. This necessitates changing a constant in the CircuitPython sources and building them for the module.</p>
<p>Fortunately, Tod Kurt over at todbot.com has <a href="https://todbot.com/blog/2022/05/19/multiple-displays-in-circuitpython-compiling-custom-circuitpython/">complete instructions</a> on how to do this. There&#8217;s also an Adafruit Learn <a href="https://learn.adafruit.com/building-circuitpython/build-circuitpython" target="_blank">Guide to Building CircuitPython</a>.  I&#8217;ve replicated and updated the instructions below.</p>
<h3>Build CircuitPython with Support for Three Displays</h3>
<p>First, make a Python venv, check out the code, fetch the submodules, and install the required Python dependencies:</p>
<pre class="wp-block-code"><code>python3 -m venv build
source build/bin/activate
git clone https://github.com/adafruit/circuitpython circuitpython-glen
cd circuitpython-glen
make fetch-all-submodules
git checkout main
pip3 install --upgrade -r requirements-dev.txt
pip3 install --upgrade -r requirements-doc.txt
make -C mpy-cross</code></pre>
<p>Now install the Espressif ESP-IDF and build CircuitPython for the Seeed Studio XIAO ESP32-S3 Sense module (which is close enough to the non-sense version of the module):</p>
<pre class="wp-block-code"><code>cd ports/espressif
make fetch-port-submodules
deactivate
./esp-idf/install.sh
. ./esp-idf/export.sh 
pip3 install minify.html
pip3 install jsmin
sudo apt install cmake (if not already installed)
sudo apt install ninja-build (if not already installed)
make -j10 BOARD=seeed_xiao_esp32_s3_sense
ls -l build-seeed_xiao_esp32_s3_sense
</code></pre>
<p>There should be a firmware.uf2 file in the directory:</p>
<pre class="wp-block-code"><code>-rw-r--r-- 1 glen glen 3672576 Jan 8 21:06 firmware.uf2</code></pre>
<p>Now increase the number of supported displays to 3 by editing (or adding if not present) the following line and setting the number of displays to 3 in boards/seeed_xiao_esp32_s3_sense/mpconfigboard.h:</p>
<pre class="wp-block-code"><code>#define CIRCUITPY_DISPLAY_LIMIT (3)  </code></pre>
<p>Rebuild the firmware.uf2 file with the changed display count:</p>
<pre class="wp-block-code"><code>make -j10 BOARD=seeed_xiao_esp32_s3_sense
ls -l build-seeed_xiao_esp32_s3_sense</code></pre>
<p>And the result:</p>
<pre class="wp-block-code"><code>-rw-r--r--  1 glen glen  3673600 Jan  8 21:11 firmware.uf2</code></pre>
<p>If you have trouble building the firmware.uf2 file, you can use <a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/firmware.uf2">this one</a>. These steps worked for building CircuitPython 10.0.3.</p>
<h3>Install the UF2 Bootloader on the XIAO Module</h3>
<p>The next step is to install the UF2 bootloader on the XIAO module. This can be done from <a href="https://circuitpython.org/board/seeed_xiao_esp32s3/">this webpage</a> using a Chromium-based browser such as Microsoft Edge or Google Chrome. Click &#8220;Open Installer&#8221; then select &#8220;Install Bootloader Only.&#8221; Follow the on-screen prompts. Do not skip the full erase. Reset or power cycle the board and the volume &#8220;XIAOS3BOOT&#8221; should mount on your PC.</p>
<h3>Install the Modified CircuitPython Build on the XIAO Module</h3>
<p>Drag the newly created firmware.uf2 file to the XIAOS3BOOT volume to install CircuitPython on the module. A few seconds after the file is finished copying, a new volume named &#8220;CIRCUITPY&#8221; should mount on your PC.</p>
<h3>Install Example CircuitPython Code on the XIAO Module</h3>
<p>Download <a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/triple-lcd-signal-cpy10.zip">this zip file</a> and extract the files. Drag the extracted files, not the folder with the extracted files, into the CIRCUITPY drive. When prompted to replace the existing code.py, click replace. This code was tested against CircuitPython 10.0.3.</p>
<h3>Test Displays and Wiring</h3>
<p>Power down the controller board and connect the displays to the controller. Power up the controller board, and after about 10 seconds, the displays should display red, yellow, and green then start to pop the chicken from one display to the next. A bit of static on the displays after power up is normal.</p>
<h2>Printing the Design</h2>
<div id="attachment_5829" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0397_2k1.jpg"><img class="wp-image-5829 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0397_2k1-1024x682.jpg" alt="DSC_0397" width="640" height="426" /></a><p class="wp-caption-text">The finished 3D printed parts for this project.</p></div>
<p>I designed the 3D printed parts in Fusion then printed them on a Bambu Lab X1C. The .3mf files below should be ready to print and slice on Bambu and similar printers.</p>
<p>The signal housing requires supports and is printed using two colors of filaments. If you don&#8217;t have access to a multicolor printer, a manual filament change between the body and hood layers can be done or the entire piece printed out of just black or just yellow filament.</p>
<p>The signal mast is also printed using two colors of filament and a manual filament change is possible between the black and silver portions if needed. The cabinet door prints at a 45 degree angle and requires supports. It could also be printed flat on its back with supports.</p>
<table width="628">
<tbody>
<tr>
<th colspan="1">Part</th>
<th colspan="1">Description</th>
<th colspan="1">Download File</th>
<th colspan="1">Filament</th>
<th colspan="1">Print Time</th>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/signal_hoods_and_body.3mf"><img class="aligncenter wp-image-5761" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/signal-hoods-and-body-icon-150x150.png" alt="signal-hoods-and-body-icon" width="48" height="48" /></a></td>
<td>Signal Hoods and Body</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/signal_hoods_and_body.3mf">signal_hoods_and_body.3mf</a></td>
<td>Sunflower Yellow, Black</td>
<td style="text-align: right;">1h50m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/lcd_mounting_plate.3mf"><img class="alignnone wp-image-5765" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/lcd_mounting_plate_icon.png" alt="lcd_mounting_plate_icon" width="48" height="48" /></a></td>
<td>LCD Mounting Plate</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/lcd_mounting_plate.3mf">lcd_mounting_plate.3mf</a></td>
<td>Black</td>
<td style="text-align: right;"> 28m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/signal_body_rear_lid.3mf"><img class="alignnone wp-image-5767" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/signal_body_rear_lid_icon.png" alt="signal_body_rear_lid_icon" width="48" height="48" /></a></td>
<td>Signal Body Rear Lid</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/signal_body_rear_lid.3mf">signal_body_rear_lid.3mf</a></td>
<td>Sunflower Yellow</td>
<td style="text-align: right;"> 24m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/stand.3mf"><img class="alignnone wp-image-5773" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/stand_icon.png" alt="stand_icon" width="48" height="48" /></a></td>
<td>Stand or Mast</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/stand.3mf">stand.3mf</a></td>
<td>Black, Silver</td>
<td style="text-align: right;"> 1h02m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/platform.3mf"><img class="alignnone wp-image-5778" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/platform_icon1.png" alt="platform_icon" width="48" height="48" /></a></td>
<td>Signal Platform</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/platform.3mf">platform.3mf</a></td>
<td>Black</td>
<td style="text-align: right;"> 35m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/pedestal.3mf"><img class="alignnone wp-image-5780" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/pedestal_icon.png" alt="pedestal_icon" width="48" height="48" /></a></td>
<td>Concrete Pedestal</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/pedestal.3mf">pedestal.3mf</a></td>
<td>Bone White</td>
<td style="text-align: right;"> 20m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/cabinet.3mf"><img class="alignnone wp-image-5782" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/cabinet_icon.png" alt="cabinet_icon" width="48" height="48" /></a></td>
<td>Controller Cabinet</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/cabinet.3mf">cabinet.3mf</a></td>
<td>Silver</td>
<td style="text-align: right;">1h00m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/cabinet_door.3mf"><img class="alignnone wp-image-5783" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/cabinet_door_icon.png" alt="cabinet_door_icon" width="48" height="48" /></a></td>
<td>Cabinet Door</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/cabinet_door.3mf">cabinet_door.3mf</a></td>
<td>Silver</td>
<td style="text-align: right;">49m</td>
</tr>
</tbody>
</table>
<h2>Putting Everything Together</h2>
<h3>1) Install lenses on LCDs.</h3>
<div id="attachment_5843" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0406_2k.jpg"><img class="wp-image-5843 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0406_2k-1024x682.jpg" alt="DSC_0406_2k" width="640" height="426" /></a><p class="wp-caption-text">LCDs and acrylic lenses. One LCD has an acrylic lens affixed to it.</p></div>
<p>Use a dry, lint-free cloth to clean any debris on the backside of the lenses. Remove the protective film from the LCD and the adhesive ring on the lens. Gently press the lens onto the LCD. Repeat for all three LCDs.</p>
<h3>2) Connect cables to displays and route cables through frame.</h3>
<div id="attachment_5844" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0412_2k.jpg"><img class="wp-image-5844 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0412_2k-1024x682.jpg" alt="DSC_0412_2k" width="640" height="426" /></a><p class="wp-caption-text">Cable assemblies connected to displays and routed through mounting plate.</p></div>
<p>Connect the cables to the displays then route the free end through the display mounting plate as shown above.</p>
<h3>3) Mount the displays to the frame using the six M2 x 4 mm screws.</h3>
<div id="attachment_5845" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0414_2k.jpg"><img class="wp-image-5845 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0414_2k-1024x682.jpg" alt="DSC_0414_2k" width="640" height="426" /></a><p class="wp-caption-text">The displays are mounted to the backing plate and the cables are routed through the plate and behind the displays.</p></div>
<p>Mount the displays to the frame using the M2 x 4mm screws. The bottom of the frame has a notch cut out of it. The top two displays are mounted right-side up with their connectors pointed toward the bottom of the frame. The bottom display is mounted upside down with its connector pointed toward the top of the frame. The connectors at the opposite ends of the cables can be labeled with the positions of the display if wanted.</p>
<h3>4) Place the frame into the signal body with the notch to the bottom.</h3>
<div id="attachment_5846" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0416_2k.jpg"><img class="wp-image-5846 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0416_2k-1024x682.jpg" alt="DSC_0416_2k" width="640" height="426" /></a><p class="wp-caption-text">Mounting plate and displays placed in the signal body housing.</p></div>
<p>Place the mounting plate and displays into the signal housing and route the cables toward the bottom of the housing and out through the slot on the bottom of the housing.</p>
<h3>5) Carefully snap the back on to the signal body while avoiding pinching any wires.</h3>
<div id="attachment_5847" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0417_2k.jpg"><img class="wp-image-5847 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0417_2k-1024x682.jpg" alt="DSC_0417_2k" width="640" height="426" /></a><p class="wp-caption-text">Rear lid placed on signal body with display cable assemblies running out the bottom.</p></div>
<p>While holding the cables out of the way, snap the signal back onto the rear of the signal housing.</p>
<h3>6) Working one cable bundle at a time, route the wires from the signal body through the signal mast.</h3>
<div id="attachment_5848" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0419_2k.jpg"><img class="wp-image-5848 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0419_2k-1024x682.jpg" alt="DSC_0419_2k" width="640" height="426" /></a><p class="wp-caption-text">First display cable assembly routed through the signal mast.</p></div>
<p>Route a connector from the top of the stand through to the bottom of the stand then gently ease the cable&#8217;s conductors into the mast. This should be done without using any tools to avoid breaking or nicking any of the conductors.</p>
<div id="attachment_5849" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0420_2k.jpg"><img class="wp-image-5849 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0420_2k-1024x682.jpg" alt="DSC_0420_2k" width="640" height="426" /></a><p class="wp-caption-text">Second display cable assembly routed through the signal mast.</p></div>
<p>Repeat the process for the second cable assembly.</p>
<div id="attachment_5850" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0422_2k.jpg"><img class="wp-image-5850 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0422_2k-1024x682.jpg" alt="DSC_0422_2k" width="640" height="426" /></a><p class="wp-caption-text">Third and final display cable assembly routed through the signal mast.</p></div>
<p>And finally, the third cable assembly.</p>
<h3>7) Gently slide the signal mast up the wires until the signal body is firmly seated on the mast.</h3>
<div id="attachment_5851" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0426_2k.jpg"><img class="wp-image-5851 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0426_2k-1024x682.jpg" alt="DSC_0426_2k" width="640" height="426" /></a><p class="wp-caption-text">Signal body firmly seated on signal mast with cables routed out the bottom of the mast.</p></div>
<p>Gently slide the signal mast up along the wires until it is seated firmly in the base of the signal housing. You may need to gently pull on the wires from the bottom of the stand as you do this.</p>
<h3>8) Place the four rubber feet on the bottom of the signal platform.</h3>
<div id="attachment_5854" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0428_2k.jpg"><img class="wp-image-5854 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0428_2k-1024x682.jpg" alt="DSC_0428_2k" width="640" height="426" /></a><p class="wp-caption-text">Bottom of the platform with rubber feet placed on the raised area in each corner.</p></div>
<p>Stick a rubber foot on the raised square in each corner of the underside of the signal platform.</p>
<h3>9) Install antenna on controller cabinet.</h3>
<div id="attachment_5859" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0430_2k.jpg"><img class="wp-image-5859 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0430_2k-1024x682.jpg" alt="DSC_0430_2k" width="640" height="426" /></a><p class="wp-caption-text">The controller cabinet with the coax cable assembly and antenna mounted.</p></div>
<p>Remove the nut and washer from the coax cable assembly. Push the RP-SMA connector from the inside through the top of the controller cabinet and secure using the previously removed nut and washer. Finger tight is good enough. Screw the antenna on to the RP-SMA connector. Again, finger tight is good enough.</p>
<h3>10) Install the controller cabinet and concrete pedestal on the signal platform.</h3>
<div id="attachment_5869" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0439_2k.jpg"><img class="wp-image-5869 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0439_2k-1024x682.jpg" alt="DSC_0439_2k" width="640" height="426" /></a><p class="wp-caption-text">Attaching the front right corner.</p></div>
<p>Use the three 2-56 x 7/16&#8243; pan head screws to secure the signal cabinet and pedestal to the platform. The screws feed up from the bottom of the platform and their is a recess inside the cabinet for each of the three nuts. The photo above shows the orientation of the platform, pedestal, and cabinet.</p>
<div id="attachment_5870" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0440_2k.jpg"><img class="size-large wp-image-5870" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0440_2k-1024x682.jpg" alt="All three corners secured." width="640" height="426" /></a><p class="wp-caption-text">All three corners secured.</p></div>
<p>The photo above shows the cabinet and pedestal secured to the platform.</p>
<h3>11) Route the cables and connectors from the signal body and mast through the slot in the signal platform.</h3>
<div id="attachment_5872" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0442_2k.jpg"><img class="wp-image-5872 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0442_2k-1024x682.jpg" alt="DSC_0442_2k" width="640" height="426" /></a><p class="wp-caption-text">Cables routed through the left side of the platform.</p></div>
<p>Route the cables from the signal stand through the rectangular slot on the platform as shown above.</p>
<h3>12) Install the signal body and mast on the signal platform.</h3>
<div id="attachment_5873" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0443_2k.jpg"><img class="size-large wp-image-5873" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0443_2k-1024x682.jpg" alt="Mast attached to platform." width="640" height="426" /></a><p class="wp-caption-text">Mast attached to platform.</p></div>
<p>Use the 2-56 x 5/16&#8243; flat head screws and 2-56 nuts to mount the signal, body, and mast to the platform.</p>
<h3>13) Route the cables and connectors underneath the signal platform, through the slots, and into the controller cabinet.</h3>
<div id="attachment_5874" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0445_2k.jpg"><img class="size-large wp-image-5874" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0445_2k-1024x682.jpg" alt="Pull the display cables through the platform and pedestal and into the controller cabinet." width="640" height="426" /></a><p class="wp-caption-text">Pull the display cables through the platform and pedestal and into the controller cabinet.</p></div>
<p>Route the cables back up through the platform and into the controller cabinet. Pull them out toward the front of the cabinet to ease connecting them to the controller board in the next step.</p>
<h3>14) Connect coax to controller board.</h3>
<div id="attachment_5876" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0451_2k.jpg"><img class="size-large wp-image-5876" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0451_2k-1024x682.jpg" alt="Antenna connected to controller board." width="640" height="426" /></a><p class="wp-caption-text">Antenna connected to controller board.</p></div>
<p>I hate U.FL connectors but we&#8217;re stuck with them. Carefully connect the U.FL connector on the cable assembly to the U.FL connector on the XIAO moduule. It just presses in place but can be tricky to align.</p>
<h3>15) Connect signal cables to controller board and test everything.</h3>
<div id="attachment_5881" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0453_2k.jpg"><img class="wp-image-5881 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0453_2k-1024x682.jpg" alt="DSC_0453_2k" width="640" height="426" /></a><p class="wp-caption-text">Display cables connected to the controller.</p></div>
<p>Connect the signal cables to the controller board. Plug the controller board into a USB port or power adapter. Verify the displays are working and in the right order. My CircuitPython script in the code section above displays red on the top display, yellow on the middle, and green on the bottom. If any displays are out of order, power down the board, swap cables as needed, and test again.</p>
<h3>16) Slide the controller board into the controller cabinet and tuck any excess cable inside the cabinet.</h3>
<div id="attachment_5879" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0454_2k.jpg"><img class="wp-image-5879 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0454_2k-1024x682.jpg" alt="DSC_0454_2k" width="640" height="426" /></a><p class="wp-caption-text">Controller cables and board neatly placed in cabinet.</p></div>
<p>I twisted the board around once to get the wires to coil in the back of the cabinet. Also, if any wires under the platform prove unruly, they can be tacked in place with a bit of tape applied across the wires and stuck to the bottom of the platform.</p>
<h3>17) Snap the lid on to the controller cabinet.</h3>
<div id="attachment_5878" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0455_2k.jpg"><img class="wp-image-5878 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0455_2k-1024x682.jpg" alt="DSC_0455_2k" width="640" height="426" /></a><p class="wp-caption-text">The door snaps on to the cabinet.</p></div>
<p>The last step is to press the cabinet door into place on the controller cabinet. Congratulations!</p>
<h2>Wrap Up</h2>
<div id="attachment_5882" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0456_2k.jpg"><img class="wp-image-5882 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2026/01/DSC_0456_2k-1024x682.jpg" alt="DSC_0456" width="640" height="426" /></a><p class="wp-caption-text">And then there were two! Original prototype in the background on the left and the final model in the foreground on the right.</p></div>
<p>That&#8217;s! Thanks for reading along or at least looking at all the pictures. If you chose to build your own LCD traffic signal, I hope it was a fun and rewarding project!</p>
<h2>Downloads</h2>
<p>All the files for the project can be downloaded using the links below.</p>
<h3>3D Print Files</h3>
<ul>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/signal_hoods_and_body.3mf">signal_hoods_and_body.3mf</a></li>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/lcd_mounting_plate.3mf">lcd_mounting_plate.3mf</a></li>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/signal_body_rear_lid.3mf">signal_body_rear_lid.3mf</a></li>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/stand.3mf">stand.3mf</a></li>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/platform.3mf">platform.3mf</a></li>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/pedestal.3mf">pedestal.3mf</a></li>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/cabinet.3mf">cabinet.3mf</a></li>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/cabinet_door.3mf">cabinet_door.3mf</a></li>
</ul>
<h3>Eagle PCB Files:</h3>
<ul>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/rect smol lcd signal controller v1.sch">Schematic</a></li>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/rect smol lcd signal controller v1.brd">Board Layout</a></li>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/rect smol lcd signal controller v1.zip">Gerbers (zip file)</a></li>
</ul>
<h3>Seeed XIAO ESP32-S3 CircuitPython 10 Firmware</h3>
<ul>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/firmware.uf2">Firmware UF2 File</a></li>
</ul>
<h3>Python Example Display Code</h3>
<ul>
<li><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/smol-lcd-signal/triple-lcd-signal-cpy10.zip">Python Code and Graphics (zip file)</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://bikerglen.com/blog/3d-printed-traffic-light-with-mini-round-displays/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>My First Neopixel Party Light</title>
		<link>https://bikerglen.com/blog/my-first-neopixel-party-light/</link>
		<comments>https://bikerglen.com/blog/my-first-neopixel-party-light/#comments</comments>
		<pubDate>Thu, 25 Dec 2025 18:22:02 +0000</pubDate>
		<dc:creator><![CDATA[Glen]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://bikerglen.com/blog/?p=5602</guid>
		<description><![CDATA[Inspired by various colorful and chonky kids toys and some old Color Kinetics architectural fixtures, this colorful Neopixel spotlight packs an Adafruit Sparkle Motion Mini into its base and can be controlled over Wi-Fi using your choice of development environments. &#8230; <a href="https://bikerglen.com/blog/my-first-neopixel-party-light/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<div id="attachment_5672" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/11/DSC_0289_2k.jpg"><img class="wp-image-5672 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/11/DSC_0289_2k-1024x682.jpg" alt="DSC_0289" width="640" height="426" /></a><p class="wp-caption-text">A white <a href="https://bikerglen.com/blog/color-kinetics-colorburst-6-teardown/">Color Kinetics ColorBlast 4</a>, a black Color Kinetics C200, and two super-colorful My First Neopixel dance party lights.</p></div>
<p>Inspired by various colorful and chonky kids toys and some old Color Kinetics architectural fixtures, this colorful Neopixel spotlight packs an Adafruit Sparkle Motion Mini into its base and can be controlled over Wi-Fi using your choice of development environments. Read on to learn how to 3D print and build your own to bring a bit of colorful light into your work or living space!</p>
<p><span id="more-5602"></span></p>
<h2>Motivation</h2>
<div id="attachment_5673" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/11/DSC_0291_2k.jpg"><img class="wp-image-5673 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/11/DSC_0291_2k-1024x682.jpg" alt="DSC_0291" width="640" height="426" /></a><p class="wp-caption-text">In the background is the original floodlight with the real 4.5&#8243; Fresnel lens and a white plastic diffuser. In the foreground are two of the My First Neopixel Party Lights.</p></div>
<p>I found this old 4.5&#8243; clear Fresnel lens on eBay and decided I wanted to incorporate it into a spotlight. I found a <a href="https://makerworld.com/en/models/823222-airfield-searchlight-industrial-lamp-kit-001#profileId-766551">model</a> I liked online, but it was the wrong size, did not have adjustable pivots, had a restrictive license agreement, and was only available as an STL mesh. It also only had room for a simple 60mm diameter puck light like those used for undercabinet lighting. I wanted to use a large Neopixel ring so it could change colors and I wanted room for the electronics inside the light.</p>
<p>I decided to make my own parameterized model instead. The result is shown in the photo above. It works but the model is pretty boring overall. Why was I always printing so much stuff in black filament when I had an entire shelf of colorful filament? And just how well did I parameterize things? How small could I make the model? What would break as I adjust the size downward? Could I make something without requiring an obscure and expensive Fresnel lens?</p>
<p>Then I remembered all those super colorful Little Tikes and Fisher Price My First toys, and more importantly, the parodies of those toys like the <a href="https://blog.littletikes.com/work-hard-play-harder-with-my-first-cubicle/">Little Tikes My First Cubicle Playset</a>, the <a href="https://www.snopes.com/fact-check/fisher-price-seance-playset/">Fisher Price Seance Playset</a>, and the <a href="https://www.reddit.com/r/funny/comments/87x13l/a_new_game_from_fisherprice/">Soul Crushing Meeting Playset</a>. And the project idea was born: could I make my Neopixel spotlight smaller, eliminate the need for an obscure lens, and more importantly, colorful and fun looking?</p>
<h2>About the Design</h2>
<div id="attachment_5680" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0292_2k.jpg"><img class="wp-image-5680 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0292_2k-1024x676.jpg" alt="DSC_0292" width="640" height="423" /></a><p class="wp-caption-text">A close up of the finished fixture.</p></div>
<p>The photograph above shows the final version of the My First Neopixel Party Light. It&#8217;s printed from four colors and black and white filament. Everything is 3D printed except the metal hardware. It consists of a red retaining ring that holds a white plastic light diffuser on to a yellow body. The yellow body is connected to a green yoke that&#8217;s connected to a blue base. It has faux heatsink fins all around resembling the C200 wash light&#8217;s heat sink and a round base for the electronics resembling the round electrical box attached to the bottom of the ColorBurst 4 light.</p>
<div id="attachment_5681" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0304_2k.jpg"><img class="size-large wp-image-5681" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0304_2k-1024x682.jpg" alt="    Inside the main body of the fixture is room for an Adafruit Neopixel Jewel 7. There's also a black shield to prevent light from the LEDs spilling through the. yellow plastic of the main body." width="640" height="426" /></a><p class="wp-caption-text">Inside the main body of the fixture is room for an Adafruit Neopixel Jewel 7. There&#8217;s also a black shield to prevent light from the LEDs spilling through the. yellow plastic of the main body.</p></div>
<p>Inside the yellow body are a black plastic light shield and an Adafruit Neopixel Jewel 7 with seven RGBW Neopixel LEDs. The light shield keeps the light from the LEDs from shining through the yellow body. The Neopixel Jewel is held in place using two small screws.</p>
<div id="attachment_5675" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/11/DSC_0296_2k.jpg"><img class="wp-image-5675 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/11/DSC_0296_2k-1024x678.jpg" alt="DSC_0296" width="640" height="424" /></a><p class="wp-caption-text">Inside the base of the light is room for an Adafruit Sparkle Motion Mini.</p></div>
<p>On the bottom of the base are four rubber sticky feet. Inside the base is a space to mount an Adafruit Sparkle Motion Mini. The Sparkle Motion Mini is held in place using four small screws.</p>
<div id="attachment_5676" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/11/DSC_0312_2k.jpg"><img class="wp-image-5676 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/11/DSC_0312_2k-1024x682.jpg" alt="DSC_0312" width="640" height="426" /></a><p class="wp-caption-text">The Adafruit Sparkle Motion Mini&#8217;s USB C port pokes through an oval hole on the back of the fixture.</p></div>
<p>The Sparkle Motion Mini&#8217;s USB 2.0 Type C connector is accessible through a cutout on the rear of the base.</p>
<h3>CAD Model</h3>
<div id="attachment_5654" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/my-first-neopixel-lamp-assembly-v6.jpg"><img class="wp-image-5654 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/my-first-neopixel-lamp-assembly-v6-1024x683.jpg" alt="my first neopixel lamp assembly v6" width="640" height="427" /></a><p class="wp-caption-text">The completed CAD model. The Fusion base opaque plastic appearance was duplicated,, set to the official Bambu Lab RGB colors, and applied to each body in the model.</p></div>
<p>The image above shows a render of the <a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/my_first_neopixel_lamp_assembly_v7.f3z">completed Autodesk Fusion 3D model</a>. The completed model is an assembly of smaller 3D models contained in separate files. To see the parameters associated with each part of the design, you&#8217;ll need to dive into each file.</p>
<p>If you change.a parameter, you&#8217;ll need to hop back to the completed assembly and update the linked model to see the changes to the overall model. A brief description of the parameters appears at the end of this post. All the files include the full design history as well so what isn&#8217;t parameterized can still be edited.</p>
<div class="wp-caption alignnone" style="width: 650px;">
<p style="margin-bottom: 0px; margin-top: 0px;"><video autoplay="autoplay" loop="loop" controls="controls" width="630" height="355"><source src="https://bikerglen.com/wp/wp-content/uploads/2025/12/yoink_480p60.mp4" type="video/mp4" />Your browser does not support the video tag.</video></p>
<p class="wp-caption-text">The completed My First Neopixel Party Light.</p>
</div>
<p>I used the Fusion appearance tool to give each part of the model an opaque plastic appearance. The color of each unique appearance in the model was set to the Bambu Lab official filament RGB values for that color. The video above shows how close a match the render colors are to the 3D printed parts&#8217; colors.</p>
<h3>Electronics</h3>
<div id="attachment_5683" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0313_2k.jpg"><img class="wp-image-5683 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0313_2k-1024x682.jpg" alt="DSC_0313" width="640" height="426" /></a><p class="wp-caption-text">The electronics for this project consist of an Adafruit Sparkle Motion Mini, an Adafruti Neopixel Jewel 7, and three strands of wire.</p></div>
<p>The electronics for this project are minimal: a board with some Neopixels, a board to control the Neopixels, and some wire. Soldering the wires onto the Neopixel Jewel is the hardest part of the project because there just isn&#8217;t really a lot of room for error when trying to reach the solder pads with the soldering iron while avoiding melting the LED housings. Pin outs and connections are shown in the Assembling the Design section of this post.</p>
<h2>Bill of Materials</h2>
<p>The complete bill of materials for the project is listed in the table below. The exact filaments I used are listed in the next section. If you have a local Do It Best or ACE Hardware, almost all the hardware for this project can be purchased by the piece there and for far less money than ordering online.</p>
<table width="628">
<tbody>
<tr>
<th colspan="1">Qty</th>
<th colspan="1">Description</th>
<th colspan="1">Supplier 1</th>
<th colspan="1">Supplier 2</th>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td>Adafruit Sparkle Motion Mini</td>
<td><a href="https://www.adafruit.com/product/6314">Adafruit</a></td>
<td><a href="https://www.digikey.com/en/products/detail/adafruit-industries-llc/6314/27525827">DigiKey</a></td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td>Adafruit Neopixel Jewel 7 x 5050 RGBW</td>
<td><a href="https://www.adafruit.com/product/2858">Adafruit</a></td>
<td><a href="https://www.digikey.com/en/products/detail/adafruit-industries-llc/2858/5878437">DigiKey</a></td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td>M4 x 12 mm External Hex Head Bolt</td>
<td><a href="https://www.mcmaster.com/91287a113/">McMaster</a></td>
<td><a href="https://amzn.to/4nWP3Oo">Amazon</a></td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td>M4 Knurled Thumb Nut</td>
<td><a href="https://www.mcmaster.com/92815A201/">McMaster</a></td>
<td><a href="https://amzn.to/3WbzPco">Amazon</a></td>
</tr>
<tr>
<td style="text-align: center;">2</td>
<td>M4 Hex Nuts</td>
<td><a href="https://www.mcmaster.com/91828A231/">McMaster</a></td>
<td><a href="https://amzn.to/47uBWye">Amazon</a></td>
</tr>
<tr>
<td style="text-align: center;">2</td>
<td>M4 x 10 mm Knurled Thumb Screws</td>
<td><a href="https://www.mcmaster.com/99607a276/">McMaster</a></td>
<td><a href="https://amzn.to/4qlIGWM">Amazon</a></td>
</tr>
<tr>
<td style="text-align: center;">6</td>
<td>M2 x 4 mm Screws</td>
<td><a href="https://www.mcmaster.com/92095A451/">McMaster</a></td>
<td><a href="https://amzn.to/4qdhDwJ">Amazon</a></td>
</tr>
<tr>
<td style="text-align: center;">6</td>
<td>M2 x 6 mm Screws (Black Oxide Steel)</td>
<td><a href="https://www.mcmaster.com/91239A704/">McMaster</a></td>
<td><a href="https://amzn.to/3JmsF1X">Amazon</a></td>
</tr>
<tr>
<td style="text-align: center;">4</td>
<td>0.315&#8243; Diameter Rubber Feet</td>
<td> &#8212;</td>
<td><a href="https://www.digikey.com/en/products/detail/3m/SJ5076/570288">DigiKey</a></td>
</tr>
<tr>
<td style="text-align: center;"> &#8212;</td>
<td>Stranded Hookup Wire</td>
<td><a href="https://www.adafruit.com/product/3111">Adafruit</a></td>
<td><a href="https://www.digikey.com/en/products/detail/adafruit-industries-llc/3111/6198256">DigiKey</a></td>
</tr>
<tr>
<td style="text-align: center;"> &#8212;</td>
<td>Soldering Iron and Solder</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
<tr>
<td style="text-align: center;"> &#8212;</td>
<td>3D Printer and Filament</td>
<td>&#8212;</td>
<td>&#8212;</td>
</tr>
</tbody>
</table>
<p><em>Disclaimer: The Amazon links above are affiliate links and Glen may earn compensation for sales from these links through an affiliate program</em>.<em> The other links are not part of any affiliate program.</em></p>
<h2>Printing the Design</h2>
<div id="attachment_5678" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0309_2k.jpg"><img class="wp-image-5678 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0309_2k-1024x682.jpg" alt="DSC_0309_2k" width="640" height="426" /></a><p class="wp-caption-text">All the 3D printed parts for the project plus some of the hardware required for assembly.</p></div>
<p>The ready-to-print Bambu Studio 3MF files for this design can be downloaded by clicking the links in the table below. The table also lists the filaments I used and the print times on a Bambu X1C. The base and yoke require support. The rest of the design can be printed without support. I did not use any specialized support or support interface material for this design. The Autodesk Fusion archive files can be download in the <em>Modifying the Design</em> section at the end of this post.</p>
<table width="628">
<tbody>
<tr>
<th colspan="1">Part</th>
<th colspan="1">Description</th>
<th colspan="1">Download File</th>
<th colspan="1">Filament</th>
<th colspan="1">Print Time</th>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/blue_base_for_sparkle_motion_mini.3mf"><img class="alignnone size-large wp-image-5606" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/base.png" alt="base" width="98" height="98" /></a></td>
<td>Base</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/blue_base_for_sparkle_motion_mini.3mf">blue_base_for_sparkle_motion_mini.3mf</a></td>
<td><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=43045599215752">Cobalt Blue</a></td>
<td style="text-align: right;">1h11m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/green_yoke.3mf"><img class="alignnone size-full wp-image-5605" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/yoke.png" alt="yoke" width="98" height="98" /></a></td>
<td>Yoke</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/green_yoke.3mf">green_yoke.3mf</a></td>
<td><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=41884190146696">Bambu Green</a></td>
<td style="text-align: right;">39m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/yellow_housing_for_neopixel_jewel.3mf"><img class="alignnone size-large wp-image-5608" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/housing.png" alt="housing" width="98" height="98" /></a></td>
<td>Housing</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/yellow_housing_for_neopixel_jewel.3mf">yellow_housing_for_neopixel_jewel.3mf</a></td>
<td><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=43045596233864">Sunflower Yellow</a></td>
<td style="text-align: right;">1h04m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/red_retaining_ring.3mf"><img class="alignnone size-large wp-image-5610" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/ring.png" alt="ring" width="98" height="98" /></a></td>
<td>Retaining Ring</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/red_retaining_ring.3mf">red_retaining_ring.3mf</a></td>
<td><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40475107000456">Red</a></td>
<td style="text-align: right;">24m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/black_liner.3mf"><img class="alignnone size-large wp-image-5609" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/liner.png" alt="liner" width="98" height="98" /></a></td>
<td>Liner</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/black_liner.3mf">black_liner.3mf</a></td>
<td><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40988815556744">Black</a></td>
<td style="text-align: right;">22m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/white_diffuser_with_black_ring.3mf"><img class="alignnone size-large wp-image-5607" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/diffuser.png" alt="diffuser" width="98" height="98" /></a></td>
<td>Lens</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/white_diffuser_with_black_ring.3mf">white_diffuser_with_black_ring.3mf</a></td>
<td><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40475106640008">Jade White</a> and <a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40988815556744">Black</a></td>
<td style="text-align: right;">17m</td>
</tr>
</tbody>
</table>
<h2>Assembling the Design</h2>
<h3>1) Solder power, ground, and data wires to the Neopixel Jewel</h3>
<div id="attachment_5688" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0330_2k.jpg"><img class="wp-image-5688 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0330_2k-1024x682.jpg" alt="DSC_0330" width="640" height="426" /></a><p class="wp-caption-text">The Neopixel Jewel with red, black, and yellow wires soldered respectively to a VDD pad, a GND pad, and the data IN pad.</p></div>
<p>This is the toughest part of the project in my opinion. It&#8217;s tough fitting the soldering iron between the LEDs to reach the Jewel&#8217;s solder pads. It can be done but requires patience. Solder a red wire to one of the VDD pads, a black wire to one of the GND pads, and a yellow or third color wire to the IN pad. Solder the wires such that they leave perpendicular to a line through the mounting holes and all in the same direction. Leave about 8&#8243; of wire extending from each pad.</p>
<h3>2) Place the two M4 nuts in their cavities on the inside of the upper housing.</h3>
<div id="attachment_5689" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0332_2k.jpg"><img class="wp-image-5689 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0332_2k-1024x682.jpg" alt="DSC_0332" width="640" height="426" /></a><p class="wp-caption-text">One of two M4 nuts placed inside the yellow main body of the fixture.</p></div>
<p>Place the nuts in their cavities on the inside of the yellow body. It&#8217;s a tight fit. If you have bigger hands, it might be helpful to thread one of the M4 thumbscrews into them then pull them into position from the outside of the body.</p>
<h3>3) Place black light shield into the upper housing.</h3>
<div id="attachment_5690" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0333_2k.jpg"><img class="wp-image-5690 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0333_2k-1024x682.jpg" alt="DSC_0333" width="640" height="426" /></a><p class="wp-caption-text">Light shield placed inside the yellow body.</p></div>
<p>Place the black light shield inside the yellow housing. The black tabs align with the mounting studs for the Neopixel Jewel. Without the shield, the light from the Neopixels will shine through the yellow body of the fixture. The Jewel will eventually hold the retaining ring in place.</p>
<h3>4) Temporarily thread the M4 thumb screws into the M4 nuts.</h3>
<div id="attachment_5691" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0334_2k.jpg"><img class="wp-image-5691 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0334_2k-1024x688.jpg" alt="DSC_0334" width="640" height="430" /></a><p class="wp-caption-text">Thread the thumbscrews into the nuts.</p></div>
<p>There&#8217;s still a bit of a gap between the M4 nuts and the light shield so threading the thumbscrews loosely into the nuts will prevent them from falling out of place during the rest of the assembly steps.</p>
<h3>5) Thread Neopixel Jewel wires through the slot on the bottom of the upper housing.</h3>
<div id="attachment_5692" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0335_2k.jpg"><img class="wp-image-5692 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0335_2k-1024x682.jpg" alt="DSC_0335" width="640" height="426" /></a><p class="wp-caption-text">Thread the wires down and through the slot in the bottom of the body.</p></div>
<p>Thread the wires through the slot in the bottom of the body. The ordering doesn&#8217;t matter as long as they lay kinda flat in the body.</p>
<h3>6) Secure Neopixel Jewel to the bottom of the upper housing using 2 of the M2 x 4 mm screws.</h3>
<div id="attachment_5693" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0337_2k.jpg"><img class="wp-image-5693 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0337_2k-1024x682.jpg" alt="DSC_0337" width="640" height="426" /></a><p class="wp-caption-text">Using a small hex driver to install the 2nd of two M2 x 4 mm screws to hold the Neopixel Jewel in place.</p></div>
<p>Use a small hex driver or hex key and two M2 x 4 mm screws to install the Neopixel Jewel in the bottom of the body. Do not overtighten the screws. This step can be kinda tricky since the body is deep and the Neopixel Jewel is mounted at the bottom of the body.</p>
<h3>7) Place diffuser into the upper housing with the white disc toward the outside.</h3>
<div id="attachment_5694" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0339_2k.jpg"><img class="wp-image-5694 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0339_2k-1024x682.jpg" alt="DSC_0339" width="640" height="426" /></a><p class="wp-caption-text">The diffuser installed in the body.</p></div>
<p>Place the diffuser with the black ring down / the white &#8220;lens&#8221; up into the body. It&#8217;s a loose fit.</p>
<h3>8) Secure red retaining ring over the diffuser using the six M2 x 6 mm screws.</h3>
<div id="attachment_5695" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0342_2k.jpg"><img class="wp-image-5695 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0342_2k-1024x682.jpg" alt="DSC_0342" width="640" height="426" /></a><p class="wp-caption-text">Red retaining ring secured over lens on body.</p></div>
<p>Use the six M2 x 6 mm screws to secure the red retaining ring over the lens and body. Again, be careful not to overtighten.</p>
<h3>9) Place the M4 x 12 mm hex bolt in its cavity in the bottom housing.</h3>
<div id="attachment_5697" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0343_2k.jpg"><img class="wp-image-5697 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0343_2k-1024x682.jpg" alt="DSC_0343" width="640" height="426" /></a><p class="wp-caption-text">M4 x 12 mm bolt loosely placed in base.</p></div>
<p>Place the M4 x 12 mm hex bolt in its cavity in the bottom housing.</p>
<h3>10) Place a small piece of tape over the hex bolt to hold it in place.</h3>
<div id="attachment_5698" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0344_2k.jpg"><img class="wp-image-5698 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0344_2k-1024x682.jpg" alt="DSC_0344_2k" width="640" height="426" /></a><p class="wp-caption-text">Some white gaffer&#8217;s tape over the head of the bolt in the housing.</p></div>
<p>Place a small piece of tape over the head of the bolt in the base of the housing. This will keep the bolt from falling out until the light is completely assembled. It&#8217;ll also prevent the bolt from shorting into the Sparkle Motion Mini if it is inadvertently loosened later.</p>
<h3>11) Thread wires through the slot on the bottom housing and connect them to the Adafruit Sparkle Motion Mini.</h3>
<div id="attachment_5700" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0345_2k.jpg"><img class="wp-image-5700 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0345_2k-1024x682.jpg" alt="DSC_0345" width="640" height="426" /></a><p class="wp-caption-text">Wires routed from body through base and connected to the Sparkle Motion Mini.</p></div>
<p>Route the wires from the body through the base and to he Sparkle Motion Mini. Strip a few mm from each wire and connect them to the screw terminals on the Sparkle Motion Mini. Connect the red wire to the +5V terminal, the black wire to the GND terminal, and the data wire to the data terminal closest to the VDD terminal.</p>
<h3>12) Arrange wires and secure the Sparkle Motion Mini in place using the last four of the M2 x 4mm screws.</h3>
<div id="attachment_5701" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0347_2k.jpg"><img class="wp-image-5701 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0347_2k-1024x682.jpg" alt="DSC_0347" width="640" height="426" /></a><p class="wp-caption-text">Sparkle Motion Mini secured in the base of the fixture.</p></div>
<p>Clear the inside of the base of any support material then place the Sparkle Motion Mini into the base. Be sure the Sparkle Motion Mini&#8217;s USB C port is accessible through the rear of the base. Secure the development board in place using the last four M2 x 4 mm screws.</p>
<h3>13) Place four rubber sticky feet on the bottom of the base.</h3>
<div id="attachment_5703" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0349_2k.jpg"><img class="wp-image-5703 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0349_2k-1024x682.jpg" alt="DSC_0349" width="640" height="426" /></a><p class="wp-caption-text">Rubber feet stuck to the base.</p></div>
<p>On the bottom of the base are four slight indentations for rubber feet. Make sure they&#8217;re clear of any support material then place a sticky rubber foot in each indentation.</p>
<h3>14) Place the yoke on the lower housing and secure it using the M4 thumb nut.</h3>
<div id="attachment_5704" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0350_2k.jpg"><img class="wp-image-5704 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0350_2k-1024x682.jpg" alt="DSC_0350" width="640" height="426" /></a><p class="wp-caption-text">Yoke placed over hex bolt on base and secured with a thumb nut.</p></div>
<p>Place the yoke on the base over the protruding M4 bolt then secure the yoke in place using the M4 thumb nut.</p>
<h3>15) Remove the M4 thumb screws, place the upper housing in the yoke, and secure it in place using the thumb screws.</h3>
<div id="attachment_5705" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0351_2k.jpg"><img class="size-large wp-image-5705" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0351_2k-1024x682.jpg" alt="Congratulations! The fixture build is complete. Time to move on to the software." width="640" height="426" /></a><p class="wp-caption-text">Congratulations! The fixture build is complete. Time to move on to the software.</p></div>
<p>Remove the M4 thumb screws from the yellow body, place the yellow body in the green yoke, and replace the M4 thumbscrews. This completes the mechanical assembly of the design.</p>
<h2>Programming the Design</h2>
<p>The My First Neopixel Party Light can be controlled using the popular WLED LED controller software or custom software can be developed to control it using CircuitPython or the Arduino development environments. I&#8217;d suggest reading through the following sections and reading the links to the official Adafruit documentation for the Sparkle Motion Mini and only then deciding how you want to control your light.</p>
<h3>WLED</h3>
<p>WLED doesn&#8217;t require any programming but the configuration and usage options can be overwhelming. If you want to jump right in without writing any software, this is the best option. The WLED setup guide is available at <a href="https://learn.adafruit.com/adafruit-sparkle-motion-mini/wled-setup">Adafruit Sparkle Motion Mini WLED Setup Guide</a>. The LEDs are connected to GPIO 32.</p>
<h3>CircuitPython</h3>
<p>CircuitPython is usually easier than Arduino except that the Sparkle Motion Mini doesn&#8217;t have a native USB port which complicates matters. If you know Python or have used CircuitPython before, this may still be the simplest route. The CircuitPython setup guide is available at <a href="https://learn.adafruit.com/adafruit-sparkle-motion-mini/install-circuitpython">Adafruit Sparkle Motion Mini CircuitPython Setup Guide</a>. The LEDs are connected to GPIO 32.</p>
<h3>Arduino</h3>
<p>If you&#8217;re familiar with C or Arduino, sticking with the Arduino development is likely the easiest path forward. The Arduino setup guide is available at <a href="https://learn.adafruit.com/adafruit-sparkle-motion-mini/arduino-ide-setup">Adafruit Sparkle Motion Mini Arduino Setup Guide</a>. The LEDs are connected to GPIO 32.</p>
<h2>Alternative Lens Design</h2>
<div id="attachment_5685" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0314_2k.jpg"><img class="wp-image-5685 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0314_2k-1024x682.jpg" alt="DSC_0314" width="640" height="426" /></a><p class="wp-caption-text">On the left is the light designed for direct viewing. It has a white translucent lens that diffuses the colors from the LEDs. On the right, is a light designed for washing small surfaces with different colors of light. It has an almost clear lens with a faux Fresnel lens pattern on it.</p></div>
<p>The standard lens, shown on the fixture on the left in the photo above, diffuses the light and is ideal for directly viewing the light from the LEDs. If you&#8217;d like to use the fixture for illumination like splashing a color on a wall, a translucent lens, as shown on the fixture on the right, will allow for more light to pass. The ready-to-print files for the translucent lens can be downloaded from the links in the table below. They consist of a spacer ring printed out of black PLA and a translucent lens printed out of clear translucent PETG.</p>
<table width="628">
<tbody>
<tr>
<th colspan="1">Part</th>
<th colspan="1">Description</th>
<th colspan="1">Download File</th>
<th colspan="1">Filament</th>
<th colspan="1">Print Time</th>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/clear_lens.3mf"><img class="alignnone size-full wp-image-5661" src="https://bikerglen.com/wp/wp-content/uploads/2025/11/lens.png" alt="lens" width="98" height="98" /></a></td>
<td>Translucent Lens</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/clear_lens.3mf">clear_lens.3mf</a></td>
<td><a href="https://us.store.bambulab.com/products/petg-translucent?id=42479468281992">Clear Translucent PETG</a></td>
<td style="text-align: right;">17m</td>
</tr>
<tr>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/clear_lens_spacer.3mf"><img class="alignnone size-full wp-image-5662" src="https://bikerglen.com/wp/wp-content/uploads/2025/11/spacer.png" alt="spacer" width="98" height="98" /></a></td>
<td>Spacer for Translucent Lens</td>
<td><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/clear_lens_spacer.3mf">clear_lens_spacer.3mf</a></td>
<td><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40988815556744">Black</a></td>
<td style="text-align: right;">9m</td>
</tr>
</tbody>
</table>
<div id="attachment_5686" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0317_2k.jpg"><img class="wp-image-5686 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/DSC_0317_2k-1024x682.jpg" alt="DSC_0317" width="640" height="426" /></a><p class="wp-caption-text">clockwise from upper left: a PLA ring to hold the clear PETG lenses in place, the standard two-color diffuser, a clear PETG lens printed with concentric infill and fixed seam position, a clear PETG lens.printed with Archimedes spiral infill, and a clear PETG lens with concentric infill and random seam positions.</p></div>
<p>The spacer ring uses the stock 0.2 mm slice print settings. The lens, however, is printed with 100% concentric ring infill and random seam alignment. This results in a lens with a circular pattern inside it and no large visible seam across it. The Archimedes infill pattern results in a spiral pattern inside it and is also aesthetically pleasing. Using anything other than a random seam alignment will likely result in a visible seam across the lens. The lens is 7 layers thick but could be printed thinner if desired which would allow even more light to pass.</p>
<h2>Modifying the Design</h2>
<div id="attachment_5745" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/12/temp-v2.png"><img class="size-large wp-image-5745" src="https://bikerglen.com/wp/wp-content/uploads/2025/12/temp-v2-1024x819.png" alt="Computed lens retaining ring dimensions." width="640" height="512" /></a><p class="wp-caption-text">Lens retaining ring dimensions.</p></div>
<p>The design parameterization is focused on getting the lens retaining ring and main body housing dimensions correct. From there, the design history can be used to adjust the number of screws, the number and size of heat sink fins, and other design features.</p>
<p>The image above shows the main retaining ring dimensions and their values as included in the design file archive (linked below). Everything starts with the diameter of the lens opening (50 mm), the diameter of the lens (54 mm), and the size of the screws (M2 or 2 mm) and the diameter of the heads of the screws (a bit less than 4.5 mm). From those dimensions, the rest of the dimensions are calculated.</p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>lens diameter</td>
<td>54</td>
</tr>
<tr>
<td>lens opening diameter</td>
<td>50</td>
</tr>
<tr>
<td>screw hole diameter</td>
<td>2</td>
</tr>
<tr>
<td>screw fit clearance</td>
<td>0.2</td>
</tr>
<tr>
<td>screw head opening diameter</td>
<td>4.5</td>
</tr>
<tr>
<td>screw lens clearance</td>
<td>1.5</td>
</tr>
<tr>
<td>screw shroud thickness</td>
<td>1.5</td>
</tr>
<tr>
<td>screw ring diameter</td>
<td>59.2</td>
</tr>
<tr>
<td>screw shroud diameter</td>
<td>7.5</td>
</tr>
<tr>
<td>outside diameter</td>
<td>66.7</td>
</tr>
<tr>
<td>upper ring diameter</td>
<td>57.2</td>
</tr>
</tbody>
</table>
<p>To assist in setting the parameters, a spreadsheet is linked below. The values from the spreadsheet are shown in the table above. Everything below and including screw ring diameter is computed from the values above screw ring diameter.</p>
<p>Download the spreadsheet, adjust these values, then copy and paste the spreadsheet values into the design parameters and you should be able to adjust the dimensions of the lens ring and main housing to fit any sized lens or retaining ring screws.</p>
<p>This mostly works but you&#8217;ll likely still need to use the sketches and design history to adjust the location of the M4 nut cavities and cosmetic features like the heatsink fins. If you get lots of errors in the design history, backing up the design history to the error then editing the feature, even if it&#8217;s just clicking edit and immediately clicking ok, can sometimes fix the errors</p>
<p>The spreadsheet:</p>
<p><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/spotlight_parameters.xlsx">Excel Workbook for Calculating Housing Sizes</a></p>
<p>The complete Autodesk Fusion archive:</p>
<p><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/tiny-spotlight/my_first_neopixel_lamp_assembly_v7.f3z">Autodesk Fusion Archive</a></p>
<p>Related:</p>
<p><a href="https://bikerglen.com/blog/color-kinetics-colorburst-6-teardown/">Color Kinetics ColorBurst 6 Teardown</a></p>
]]></content:encoded>
			<wfw:commentRss>https://bikerglen.com/blog/my-first-neopixel-party-light/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lizard: a 3D-Printed Single-Purpose Soundboard</title>
		<link>https://bikerglen.com/blog/lizard-3d-printed-single-purpose-soundboard/</link>
		<comments>https://bikerglen.com/blog/lizard-3d-printed-single-purpose-soundboard/#comments</comments>
		<pubDate>Thu, 16 Oct 2025 03:24:50 +0000</pubDate>
		<dc:creator><![CDATA[Glen]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://bikerglen.com/blog/?p=5447</guid>
		<description><![CDATA[A brief demo video of the lizard soundboard! A friend forwarded a video of the lizard slapping the lizard emoji on their phone and I instantly knew I needed to do the same! My phone doesn&#8217;t sound out emojis though &#8230; <a href="https://bikerglen.com/blog/lizard-3d-printed-single-purpose-soundboard/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<div class="wp-caption alignnone" style="width: 650px;">
<p style="margin-bottom: 0px; margin-top: 0px;"><iframe title="YouTube video player" src="https://www.youtube.com/embed/46y_BPe_aAM" width="630" height="355" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>
<p class="wp-caption-text">A brief demo video of the lizard soundboard!</p>
</div>
<p>A friend forwarded a video of the lizard slapping the lizard emoji on their phone and I instantly knew I needed to do the same! My phone doesn&#8217;t sound out emojis though so I built a single-purpose sound board that does one thing and one thing only: it says Lizard every time you press the button. Follow along to build your own using off-the-shelf electronics and some 3D printing!</p>
<p><span id="more-5447"></span></p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs</em>.</p>
<h2>The Electronics</h2>
<div id="attachment_5457" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0237.jpg"><img class="wp-image-5457 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0237-1024x682.jpg" alt="crop_DSC_0237" width="640" height="426" /></a><p class="wp-caption-text">The electronics for the soundboard project.</p></div>
<p>The electronics for this project are minimal: a button, two dev boards stacked together, a speaker, and an optional microSD card. Basic soldering skills are needed to solder headers and header sockets on the dev boards and wires onto the speaker.</p>
<h3>Selecting an Audio Player</h3>
<div id="attachment_5458" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0240.jpg"><img class="size-large wp-image-5458" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0240-1024x681.jpg" alt="Adafruit QT PY RP2040 and Audio BFF." width="640" height="426" /></a><p class="wp-caption-text">Adafruit QT PY RP2040 and Audio BFF.</p></div>
<p>For an audio player I chose the <a href="https://www.adafruit.com/product/4900">Adafruit QT Py RP2040</a> with the <a href="https://www.adafruit.com/product/5769">Adafruit Audio BFF Add-on for QT Py and Xiao</a>. This combination offers a micro capable of running CircuitPython for easy code development, a microSD card slot for storing audio samples, an I2S audio DAC, and a built-in 3W mono audio amplifier. This is everything that&#8217;s needed to store and play back audio on a small speaker!</p>
<p>If the audio samples will fit entirely inside the QT Py RP2040&#8217;s built-in flash storage (8MB minus room for the software), an alternate audio board is the <a href="https://www.adafruit.com/product/5770">Adafruit I2S Amplifer BFF Add-On for QT Py and Xiao</a>. This board is basically the Audio BFF minus the microSD card slot. If you&#8217;re up for a programming challenge and would like to trigger sounds over Wi-Fi and MQTT, the <a href="https://www.adafruit.com/product/5426">Adafruit QT Py ESP32-S3</a> board has Wi-Fi and is a drop-in replacement for the RP2040-based board.</p>
<h3>Selecting a Speaker</h3>
<div id="attachment_5459" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0241.jpg"><img class="wp-image-5459 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0241-1024x682.jpg" alt="Three candidates for the project's speaker." width="640" height="426" /></a><p class="wp-caption-text">Three candidates for project speaker.</p></div>
<p>I tried three different speakers before settling on the <a href="https://amzn.to/4mJZBPJ">generic 2&#8243; 4Ω 5W speaker</a> with the red surround and cone. The <a href="https://www.adafruit.com/product/3968">smaller round one</a> and <a href="https://www.adafruit.com/product/3351">enclosed oval one</a> are available from Adafruit. I went with the larger speaker because it was the loudest and the mounting holes would make it easy to mount in the enclosure.</p>
<h3>Selecting a Button</h3>
<div id="attachment_5460" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0242.jpg"><img class="size-large wp-image-5460" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0242-1024x682.jpg" alt="I have a ton of these buttons around the house. Only decision was what color." width="640" height="426" /></a><p class="wp-caption-text">I have a ton of these buttons around the house. Only decision was what color.</p></div>
<p>The generic buttons shown in the image above are my go-to for cheap push buttons that can easily take modicum of abuse. The screw terminals are convenient too. They&#8217;re available under various <a href="https://amzn.to/48o0hH5">alphabet soup</a> <a href="https://amzn.to/4mHK56F">brand names</a> on Amazon.</p>
<h3>Miscellaneous Cables</h3>
<div id="attachment_5461" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0243.jpg"><img class="wp-image-5461 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0243-1024x681.jpg" alt="crop_DSC_0243" width="640" height="426" /></a><p class="wp-caption-text">A panel-mount USB C extension, a two pin picoblade connector and pre-terminated leads, and a Qwiic jumper cable.</p></div>
<p>The last part of the electronics are some cables to connect everything together. You&#8217;ll need a <a href="https://www.adafruit.com/product/3922">2-pin PicoBlade cable</a> to connect the speaker and a <a href="https://www.adafruit.com/product/4210">4-pin Qwiic cable</a> to connect the button. Lastly, a <a href="https://www.mycablemart.com/store/cart.php?m=product_detail&amp;p=11346">panel-mount USB C extension</a> makes for a cleaner final project than having a USB cable permanently dangling out of the rear of the project.</p>
<h2>The Enclosure</h2>
<div id="attachment_5480" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0027.jpg"><img class="wp-image-5480 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0027-1024x683.jpg" alt="The evolution of the design for the enclosure arranged left to right from the earliest version to the final version." width="640" height="427" /></a><p class="wp-caption-text">The evolution of the design for the enclosure.</p></div>
<p>I went through four iterations when designing the enclosure. These iterations are shown in the photo above. The final enclosure design is on the far right. All the versions were designed in Autodesk Fusion and printed out of PLA plastic on a Bambu X1C 3D printer.</p>
<h3>Version 1</h3>
<div id="attachment_5468" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0253.jpg"><img class="wp-image-5468 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0253-1024x682.jpg" alt="crop_DSC_0253" width="640" height="426" /></a><p class="wp-caption-text">The first version of the enclosure for the project.</p></div>
<p>The first version of the enclosure measured 100 x 66 x 42 mm. It was designed as a snap-together unit by following the instructions in this <a href="https://www.youtube.com/watch?v=VVmOtM60VWw">great video tutorial</a> from Adafruit on designing snap-fit cases.</p>
<p>Since I saved the parameterized case design from this video in my Fusion library years ago, I only had to open the file and adjust some parameters to the needed dimensions to get a basic case design for the project. That left me free to focus my energy on designing the aspects of the case that were unique to this project.</p>
<div id="attachment_5462" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0244.jpg"><img class="wp-image-5462 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0244-1024x682.jpg" alt="crop_DSC_0244" width="640" height="426" /></a><p class="wp-caption-text">The pieces of the 3D printed enclosure include the box, a snap-fit lid, and a piece that slides into a notch in the back of the enclosure to accommodate a permanently attached USB cable.</p></div>
<p>The enclosure presented three design challenges: mounting the speaker, allowing the USB cable to escape the enclosure, and holding the electronics in place.</p>
<h4>Mounting the Speaker</h4>
<div id="attachment_5503" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/lid.png"><img class="size-large wp-image-5503" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/lid-1024x819.png" alt="The lid with a 51 mm x 1.5 mm deep circular cutout for the lip around the speaker's rubber surround." width="640" height="512" /></a><p class="wp-caption-text">The lid with a 51 mm x 1.5 mm deep circular cutout for the lip around the speaker&#8217;s rubber surround.</p></div>
<p>The speaker has a lip around the rubber surround. It&#8217;s about 50 mm in diameter and 1 mm tall. To accommodate the lip, I added a 1.5 mm raised square area with a 51 circular cutout for the speaker and its lip to the underside of the lid. This makes the lid 3.5 mm thick in this area, 2 mm thick where the grill is, and 2 mm thick elsewhere. The four holes in the corner are 42 mm apart and 3.3 mm in diameter for a loose fit with M3 screws.</p>
<div id="attachment_5505" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/hex-initial.png"><img class="size-large wp-image-5505" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/hex-initial-1024x819.png" alt="The centers of the two hexagons are located on the hypotenuse of a 30-60-90 triangle with a length of 6 mm." width="640" height="512" /></a><p class="wp-caption-text">The centers of the two hexagons are located on the hypotenuse of a 30-60-90 triangle with a length of 6 mm.</p></div>
<p>The hexagon grill was created by drawing two hexagons then using the rectangular pattern tool to repeat the two hexagons in both the vertical and horizontal directions. The hexagons are 5 mm across with 1 mm between their sides. This is a spacing of 6 mm on center.</p>
<p>The first hexagon was located in the center of the grill. The center of the second hexagon needed to be 6 mm from the center of the first hexagon but on a diagonal. This diagonal is the hypotenuse of a 30-60-90 triangle. If the hypotenuse is 6 mm, the short side of the triangle is 6/2 = 3 mm and the long side of the triangle is 6/2*sqrt(3) = 5.196 mm.</p>
<p>I used the construction line type to draw a 3 mm x 5.196 mm two point rectangle from the center of the first hexagon down and to the left then placed the center of the second hexagon on the second corner of the rectangle as shown in the image above.</p>
<div id="attachment_5504" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/hex-grid.png"><img class="size-large wp-image-5504" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/hex-grid-1024x819.png" alt="The hexagon sketch used to create the speaker grill." width="640" height="512" /></a><p class="wp-caption-text">The hexagon sketch used to create the speaker grill.</p></div>
<p>After the two hexagons were in place, I used the rectangular pattern tool to duplicate the two hexagons across the entire speaker grill. The x spacing was 6 mm and the y spacing was 6*sqrt(3) = 10.392 mm. These are just double the values used to sketch the rectangle used to space the first two hexagons. I was then able to use the extrusion tool to cut the hexagons through the 51 mm diameter circular grill area on the lid.</p>
<h4>The USB Cable Slot</h4>
<div id="attachment_5463" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0245.jpg"><img class="wp-image-5463 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0245-1024x682.jpg" alt="crop_DSC_0245" width="640" height="426" /></a><p class="wp-caption-text">This piece slides into the notch of the enclosure to hold the USB cable in place.</p></div>
<p>When I first built this enclosure, I did not have the panel mount USB C extension cable and clearly a USB C connector is not going to pass through a hole just large enough for the actual USB cable.</p>
<p>To run the USB cable out of the enclosure, I created a small slot in the enclosure. I slipped the cable through this slot. Once the cable was through, I printed a filler piece that slipped into the slot and held the cable in place.</p>
<h4>Room for the Electronics</h4>
<div id="attachment_5507" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/electronics.png"><img class="size-large wp-image-5507" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/electronics-1024x819.png" alt="Some posts and a small cover keep the electronics in place and from shorting to the speaker or button." width="640" height="512" /></a><p class="wp-caption-text">Some posts and a small cover keep the electronics in place and from shorting to the speaker or button.</p></div>
<p>To hold the electronics in place, I added a few posts to the bottom left of the enclosure and printed a little cover for them. The electronics slip in sideways with the USB cable up top and running to the left in the picture above. The cover is held in place using a pair of M2.5 screws. This keeps the electronics from moving around and shorting to the speaker or button.</p>
<h4>The Result</h4>
<div id="attachment_5464" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0246.jpg"><img class="wp-image-5464 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0246-1024x682.jpg" alt="crop_DSC_0246" width="640" height="426" /></a><p class="wp-caption-text">USB cable installed. Version 1 of the enclosure on the left. Version 2 of the enclosure on the right.</p></div>
<p>This version of the case worked out great except for one fatal flaw: the snap together lid fit somewhat loose into the case. This caused the lid to rattle every time the sounds played back! Instead of getting nice clean audio, I&#8217;d get audio plus a lot of rattling noises. I decided the next version would use screws to hold everything together tightly to stop the rattling.</p>
<h3>Version 2</h3>
<div id="attachment_5469" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0254.jpg"><img class="wp-image-5469 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0254-1024x682.jpg" alt="crop_DSC_0254" width="640" height="426" /></a><p class="wp-caption-text">The second version of the project. It&#8217;s a bit wider and the cover screws to the base now.</p></div>
<p>Version 2 of the enclosure is more or less identical to version 1 of the enclosure except there are now four M3 screws in the corners to hold the case together. The case grew from 66 mm wide to 75 mm wide to accommodate the screws. This version of the case is 100 x 75 x 42 mm.</p>
<div id="attachment_5511" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/m3-screw-together.png"><img class="size-large wp-image-5511" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/m3-screw-together-1024x819.png" alt="The 3 mm holes in the corners fit M3 screws." width="640" height="512" /></a><p class="wp-caption-text">The 3 mm holes in the corners fit M3 screws.</p></div>
<p>The holes in the lid are 3.3 mm in diameter for a loose fit with M3 screws. The holes in the base are 3 mm diameter so that the M3 screws will bite and thread into them—just don&#8217;t over tighten them. The walls of the screws holes are 1.5 mm at their thinnest.</p>
<h4>The Result</h4>
<p>This version worked well and the case no longer rattled. With the basics out of the way, it was time to get creative and try out multi-color prints and different print support materials.</p>
<h3>Version 3</h3>
<div id="attachment_5470" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0255.jpg"><img class="wp-image-5470 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0255-1024x681.jpg" alt="crop_DSC_0255" width="640" height="426" /></a><p class="wp-caption-text">The third version of the enclosure. This version is 4 mm narrower than version 3 and sports a raised silver speaker grill.</p></div>
<p>For the third version of the enclosure, I realized I could make it 4 mm narrower and still have room for the screws in the corners. I also wanted to try to make a domed silver speaker grill like you might see on an old outdoor intercom panel or on industrial equipment. This version of the enclosure measures 100 x 71 x 42 mm.</p>
<h4>Designing the Grill</h4>
<div id="attachment_5514" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/unembossed.png"><img class="wp-image-5514 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/unembossed-1024x819.png" alt="unembossed" width="640" height="512" /></a><p class="wp-caption-text">51 mm diameter speaker grill before embossing. The radius of the top surface is 110 mm and the bottom surface is flat. It&#8217;s 2 mm thick at the outside edge.</p></div>
<p>The grill started as a 51 mm disc with a flat bottom and gently curved top surface. It&#8217;s 2 mm thick at the edges and a few millimeters thicker in the middle. It started as a sketch of the right half of the disc&#8217;s cross section then I used the revolve tool to spin the cross section into the solid disc.</p>
<div id="attachment_5515" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/embossing-grill.png"><img class="wp-image-5515 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/embossing-grill-1024x585.png" alt="embossing-grill" width="640" height="366" /></a><p class="wp-caption-text">The Fusion emboss tool in action. The plane with the hexagons is offset 20 mm from the outside top edge of the disc. The emboss tool worked best with only a single curved surface so the top surface is curved and the bottom surface is flat.</p></div>
<p>I stuck with the same hexagon pattern as for the flat speaker grill. I positioned it 20 mm above the disc then used the emboss tool to cut the hexagons into the disc. The emboss tool cuts the hexagons with their sides angled inward toward the center of the sphere forming the top surface of the disc. The emboss tool results in a grill that looks like it was formed from pressing a metal screen between two spherical dies while the extrude tool would have resulted in a grill that looks like it was punched into a sheet of metal.</p>
<div id="attachment_5516" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/domed-grill.png"><img class="wp-image-5516 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/domed-grill-1024x819.png" alt="domed-grill" width="640" height="512" /></a><p class="wp-caption-text">The finished grill as a separate component. It&#8217;s 51 mm in diameter and 2 mm thick throughout. The flat bottom surface has had some curvature cut into it.</p></div>
<p>The last step was to scoop out the bottom of the disc to make it a near-uniform 2 mm thick across its entire surface. I added the bottom curve to the disc&#8217;s cross section sketch then used the revolve tool a second time to cut the bottom curve out. The final result is shown above. This was created as a separate component in a separate file so I could paste it where needed.</p>
<h4>Printing the Grill</h4>
<div id="attachment_5483" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0269.jpg"><img class="wp-image-5483 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0269-1024x681.jpg" alt="crop_DSC_0269" width="640" height="426" /></a><p class="wp-caption-text">Two attempts at prototyping the speaker grill. The one on the right printed with a PETG support interface cleaned up nicer than the 100% PLA print on the left.</p></div>
<p>I made two test prints of the grill. The first test print was out of white PLA using white PLA for the support material. This print is on the left in the photo above. It turned out well except there were lots of strands of PLA leftover where the support could not be cleanly separated from the design.</p>
<p>The second test print was out of white PLA with white PLA support but with black PETG for the support interface. The support and the design separated very easily from each other leaving very little residual support material behind. The print&#8217;s purge settings probably needed to be adjusted a bit because the white PLA was a tad darker from the PETG mixing into it.</p>
<h4>Exporting Multicolored Prints from Fusion to the Bambu Slicer</h4>
<div id="attachment_5538" style="width: 752px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/fusion-export.png"><img class="size-full wp-image-5538" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/fusion-export.png" alt="Example of exporting a component consisting of multiple bodies from Fusion for multicolored printing in the Bambu slicer." width="742" height="552" /></a><p class="wp-caption-text">Example of exporting a component consisting of multiple bodies from Fusion for multicolored printing in the Bambu slicer.</p></div>
<p>Next up was to figure out how to print the main part of the lid in one color and the speaker grill in a second color. The trick to exporting prints from Fusion to the Bambu slicer for multicolored printing is to export a single component containing separate sub-components or bodies for each different color in the print.</p>
<p>In the case of the lid, I have a body and a sub-component inside the main lid component. The body is most of the lid. The sub-component is just the speaker grill and the grill is held in place in the lid with a rigid joint.</p>
<p>Inside Fusion, I select 3D print then click in the design browser on the main component containing the bodies and/or subcomponents. This gets exported as a single .3mf file (above) that can be imported into the Bambu slicer (below).</p>
<div id="attachment_5537" style="width: 940px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/Screenshot-2025-09-28-203359.png"><img class="wp-image-5537 size-full" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/Screenshot-2025-09-28-203359.png" alt="Screenshot 2025-09-28 203359" width="930" height="541" /></a><p class="wp-caption-text">Answer yes to this question.</p></div>
<p>When the file is imported into the slicer, the slicer will show the dialog box in the screen capture above. Click yes. The Bambu slicer will now treat the imported design as a single design consisting of multiple objects.</p>
<div id="attachment_5539" style="width: 527px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/Screenshot-2025-09-28-205353.png"><img class="size-full wp-image-5539" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/Screenshot-2025-09-28-205353.png" alt="Click the Objects button next to the Process label to display a list of objects in the design and assign a filament slot to each." width="517" height="372" /></a><p class="wp-caption-text">Click the Objects button next to the Process label to display a list of objects in the design and assign a filament slot to each.</p></div>
<p>Clicking the Process from Global to Objects will display a list of objects in the design. You can now click on an object in the objects list then type a number key corresponding to a project&#8217;s filament slot and that object will be printed out of the filament assigned to that slot in the project.</p>
<h4>The Result</h4>
<div id="attachment_5478" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0261.jpg"><img class="wp-image-5478 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0261-1024x682.jpg" alt="crop_DSC_0261" width="640" height="426" /></a><p class="wp-caption-text">A close up of the raised silver hexagon speaker grill on the third version of the enclosure.</p></div>
<p>Convinced I had the grill and support and multicolored printing figured out, I printed out version 3 of the enclosure. This is the first version of the enclosure to be printed with the lid right side up. The first two versions where printed with the lid facing down to eliminate the need for support material. Obviously with a domed speaker grill, support material would be needed regardless of which direction the lid faced.</p>
<p>This time I used green for the enclosure—it&#8217;s what was in the printer—and silver for the grill. I just printed everything in PLA without worrying about the support interface. The final print is shown in the image above. It turned out pretty well even using PLA for the support material! Peeling away the support material from the bottom of the lid was a bit of a pain though.</p>
<h3>Final Version</h3>
<div id="attachment_5471" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0256.jpg"><img class="wp-image-5471 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0256-1024x682.jpg" alt="crop_DSC_0256" width="640" height="426" /></a><p class="wp-caption-text">The final version of the enclosure. This one is 2 mm taller but loses the USB C pigtail in favor of a rear-mounted USB C connector.</p></div>
<p>After assembling version 3 of the enclosure, I started searching for some USB C panel mount connectors so I could eliminate the dangling USB cable from the project. I found one but it required increasing the height of the enclosure by 2 mm to fit underneath the speaker. This version of the enclosure increased to 100 x 71 x 44 mm in size.</p>
<h4>Adding a USB C Connector</h4>
<div id="attachment_5522" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/FE-USB2CC-PM_drawing-1.png"><img class="size-large wp-image-5522" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/FE-USB2CC-PM_drawing-1-1024x791.png" alt="The datasheet with the dimensions of the panel-mount USB C extension." width="640" height="494" /></a><p class="wp-caption-text">The datasheet with the dimensions of the panel-mount USB C extension.</p></div>
<p>After much digging around, I found a <a href="https://www.mycablemart.com/store/cart.php?m=product_detail&amp;p=11346">panel-mount USB 2.0 Type C extension cable</a>. The retailer even had a dimensioned drawing (shown above).</p>
<div id="attachment_5525" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/usb-dimensions.png"><img class="wp-image-5525 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/usb-dimensions-1024x819.png" alt="usb dimensions" width="640" height="512" /></a><p class="wp-caption-text">USB Type C panel cutout dimensions.</p></div>
<p>I used the drawing and some other resources to put together a panel cutout for the rear of the enclosure. I then placed the cutout on the rear of the enclosure and used the sketch to cut holes in the enclosure.</p>
<div id="attachment_5526" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/usb-c.png"><img class="size-large wp-image-5526" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/usb-c-1024x819.png" alt="The rear of the case with the cutout for the panel-mount USB C connector." width="640" height="512" /></a><p class="wp-caption-text">The rear of the case with the cutout for the panel-mount USB C connector.</p></div>
<h4>Printing the Lid</h4>
<p>This time I printed the lid in Bambu&#8217;s <a href="https://us.store.bambulab.com/products/pla-basic-filament?id=43045599215752">cobalt blue</a> and <a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40475106803848">silver</a> basic PLA using their new <a href="https://us.store.bambulab.com/products/support-for-pla-new">support for PLA</a> material for the support interface. Using this material for the support interface made it easy to separate the support and design from each other.</p>
<h4>The Result</h4>
<div id="attachment_5476" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0264.jpg"><img class="wp-image-5476 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/crop_DSC_0264-1024x682.jpg" alt="crop_DSC_0264" width="640" height="426" /></a><p class="wp-caption-text">The rear of the final version of the enclosure with the panel-mount USB C connector installed.</p></div>
<p>The photo above shows the finished and final version of the enclosure. Both the USB connector and domed silver speaker grill came out well!</p>
<h2>Printing the Enclosure</h2>
<h3>The Base</h3>
<div id="attachment_5530" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/bambu-studio-base.png"><img class="wp-image-5530 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/bambu-studio-base-1024x657.png" alt="bambu-studio-base" width="640" height="411" /></a><p class="wp-caption-text">The Bambu slicer open with the base of the enclosure loaded and ready to print.</p></div>
<p>I printed the base on my Bambu X1C using the &#8220;0.20 standard&#8221; slicer settings. I set the seam alignment to back and enabled supports on the oval cutout for the USB connector. This is a pretty basic print. Total print time was 1 hour and 11 minutes with no filament changes.</p>
<h3>The Lid</h3>
<div id="attachment_5532" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/bambu-studio-lid.png"><img class="wp-image-5532 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/bambu-studio-lid-1024x657.png" alt="bambu-studio-lid" width="640" height="411" /></a><p class="wp-caption-text">The Bambu slicer open with the enclosure lid loaded and ready to print.</p></div>
<p>The lid is a significantly more complicated print than the base. I started with the &#8220;0.16 high quality&#8221; settings then enabled support generation. I used Bambu&#8217;s <a href="https://us.store.bambulab.com/products/pla-basic-filament?id=43045599215752">cobalt blue</a> and <a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40475106803848">silver</a> basic PLA for the lid and speaker grill respectively. For the support interface, I used Bambu&#8217;s new <a href="https://us.store.bambulab.com/products/support-for-pla-new">support for PLA</a> material.</p>
<div id="attachment_5490" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/vlcsnap-2025-09-26-21h19m15s606.png"><img class="wp-image-5490 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/vlcsnap-2025-09-26-21h19m15s606-1024x631.png" alt="vlcsnap-2025-09-26-21h19m15s606" width="640" height="394" /></a><p class="wp-caption-text">Screen capture from the time lapse video of the Bambu X1C 3D printer printing the enclosure lid.</p></div>
<p>This print took 3 hours and 26 minutes with 46 filament changes! The photo above shows the printer putting down a support interface layer of the Bambu support for PLA material.</p>
<h3>The Electronics Cover</h3>
<div id="attachment_5533" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/09/bambu-studio-cover.png"><img class="size-large wp-image-5533" src="https://bikerglen.com/wp/wp-content/uploads/2025/09/bambu-studio-cover-1024x657.png" alt="The cover for the electronics." width="640" height="411" /></a><p class="wp-caption-text">The cover for the electronics.</p></div>
<p>The final print is the cover for the electronics. This one is pretty trivial and it really doesn&#8217;t matter what material or color it is printed out of. I used black PLA. Total print time was ten and a half minutes. It&#8217;s even upside down in the screenshot above though it doesn&#8217;t matter.</p>
<h2>Assembly</h2>
<p>With all the materials gathered together and all the parts printed out, it&#8217;s time to assemble the project! (The design files and the complete bill of materials to use as a handy checklist as you procure the required parts and materials are available at the end of this post.)</p>
<h3>1) Connect the button to the qwiic cable</h3>
<div id="attachment_5557" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/1.jpg"><img class="wp-image-5557 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/1-1024x683.jpg" alt="1" width="640" height="427" /></a><p class="wp-caption-text">I used a sewing needle to remove the unneeded red and yellow wires from the JST connector.</p></div>
<p>Connect the button to the black (GND) and blue (SDA) wires on the qwiic cable. I removed the red and yellow wire from the JST connector using a sewing needle and gently prying up on the plastic tabs then backing the unneeded wires out of the connector.</p>
<h3>2) Solder the picoblade cable to the speaker</h3>
<div id="attachment_5558" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/2.jpg"><img class="wp-image-5558 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/2-1024x683.jpg" alt="2" width="640" height="427" /></a><p class="wp-caption-text">Polarity doesn&#8217;t matter on the speaker connections.</p></div>
<p>Solder the wires on the picoblade cable to the speaker terminals. Polarity doesn&#8217;t matter since this is mono so phasing doesn&#8217;t matter and we don&#8217;t care if a positive voltage makes a positive or negative displacement of the diaphragm.</p>
<h3>3) Solder the header pins on the QT Py</h3>
<div id="attachment_5559" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/3.jpg"><img class="wp-image-5559 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/3-1024x683.jpg" alt="3" width="640" height="427" /></a><p class="wp-caption-text">If you have an old breadboard, soldering the headers becomes vastly easier.</p></div>
<p>Solder the header pins on to the QT Py board. You can use an old breadboard to hold the pins straight and upright while you solder if you have one. This is the Qt PY ESP32-S3 board but it&#8217;s the same pinout and process if you&#8217;re using the Qt PY RP2040 board.</p>
<h3>4) Solder the header sockets on the Audio BFF</h3>
<div id="attachment_5560" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/4.jpg"><img class="wp-image-5560 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/4-1024x683.jpg" alt="4" width="640" height="427" /></a><p class="wp-caption-text">Stick some unused header pins into the breadboard then set the header sockets on the pins for easier soldering.</p></div>
<p>Solder the header sockets on to the Audio BFF board. You can use a breadboard and some spare header pins to hold everything in place while soldering.</p>
<h3>5) Connect the electronics together</h3>
<div id="attachment_5562" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/5.jpg"><img class="size-large wp-image-5562" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/5-1024x683.jpg" alt="When connected correctly, the speaker and button cables will be on opposite ends of the stack of boards." width="640" height="427" /></a><p class="wp-caption-text">When connected correctly, the speaker and button cables will be on opposite ends of the stack of boards.</p></div>
<p>Plug the boards into each other and connect the button and speaker to the boards. Pay close attention that the two boards are aligned so that the power and data pins are aligned and they are not rotated 180° or shifted by a pin.</p>
<h3>6) Install the microSD card</h3>
<div id="attachment_5564" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/6.jpg"><img class="size-large wp-image-5564" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/6-1024x683.jpg" alt="The smallest reputable microSD card I could find and easily acquire in 2025." width="640" height="427" /></a><p class="wp-caption-text">The smallest reputable microSD card I could find and easily acquire in 2025.</p></div>
<p>Format and copy a mono 22,050 Hz, 16-bit, little-endian wave file to the microSD card and install the microSD card in the audio BFF board&#8217;s microSD slot. Free software such as Audacity or FFMPEG can be used to convert the files if you only have an MP3. If you&#8217;re using the I2S Amplifier BFF board, you&#8217;ll copy the sound file to the QT Py&#8217;s on-board flash as part of installing the software later.</p>
<h3>7) Test everything</h3>
<div id="attachment_5566" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/7.jpg"><img class="size-large wp-image-5566" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/7-1024x683.jpg" alt="Hopefully the speaker made some sort of noise." width="640" height="427" /></a><p class="wp-caption-text">Hopefully the speaker made some sort of noise.</p></div>
<p>At this point, it&#8217;s a good idea to test everything. Follow the instructions under the software heading below to install CircuitPython and the soundboard software. Have some fun. Once everything is tested, unplug the speaker and pushbutton from the electronics and continue assembly.</p>
<h3>8) Mount the pushbutton to the lid</h3>
<div id="attachment_5568" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/8.jpg"><img class="wp-image-5568 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/8-1024x683.jpg" alt="8" width="640" height="427" /></a><p class="wp-caption-text">The pushbutton mounted to the lid.</p></div>
<p>Thread the button through the lid and nut then tighten the nut on the back of the button. Finger tight is good enough.</p>
<h3>9) Mount the speaker to the lid</h3>
<div id="attachment_5569" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/9.jpg"><img class="wp-image-5569 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/9-1024x683.jpg" alt="9" width="640" height="427" /></a><p class="wp-caption-text">The speaker and button mounted to the lid.</p></div>
<p>Use four of the M3 x 8 mm screws, washers, and nuts to mount the speaker to the lid. Mount the speaker with the terminals oriented down toward the pushbutton.</p>
<h3>10) Mount the USB connector to the base and route the cable</h3>
<div id="attachment_5570" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/10.jpg"><img class="wp-image-5570 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/10-1024x683.jpg" alt="10" width="640" height="427" /></a><p class="wp-caption-text">USB C jack mounted and cable routed.</p></div>
<p>Use two more of the M3 x 8 mm screws to mount the USB C extension to the enclosure. Route the cable as shown in the photo above.</p>
<h3>11) Connect all the cables and place the electronics inside their spot in the box</h3>
<div id="attachment_5571" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/11.jpg"><img class="wp-image-5571 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/11-1024x683.jpg" alt="11" width="640" height="427" /></a><p class="wp-caption-text">Electronics in place.</p></div>
<p>Reconnect the pushbutton and speaker to the electronics, place the electronics in the box, and connect the USB C extension to the QT Py board.</p>
<h3>12) Place the cover on the electronics</h3>
<div id="attachment_5573" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/12.jpg"><img class="wp-image-5573 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/12-1024x683.jpg" alt="12" width="640" height="427" /></a><p class="wp-caption-text">The cover holds the electronics in place and keep things from shorting out.</p></div>
<p>Place the cover on the electronics and secure it using the two M2.5 x 8 mm screws. PLA is not the best for holding threads so be careful not to over tighten!</p>
<h3>13) Mount the lid to the box</h3>
<div id="attachment_5575" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/13.jpg"><img class="wp-image-5575 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/13-1024x683.jpg" alt="13" width="640" height="427" /></a><p class="wp-caption-text">Just four more screws to go.</p></div>
<p>Place the lid on the box and use the last four M3 x 8 mm screws to mount the lid to the box. PLA is not the best for holding threads so be careful not to over tighten!</p>
<h3>14) Test and Enjoy</h3>
<div id="attachment_5579" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2025/10/14b.jpg"><img class="wp-image-5579 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2025/10/14b-1024x683.jpg" alt="merged" width="640" height="427" /></a><p class="wp-caption-text">The completed project. Enjoy infinite lizards at the push of a button!</p></div>
<p>The completed project. Enjoy infinite lizards at the push of a button!</p>
<h2>The Software</h2>
<p>This project uses a short CircuitPython script for the software. The script waits for a button push then plays a random wave file from either the dev board&#8217;s built-in flash or from a microSD card inserted in the Audio BFF board. If the button is pressed while a file is playing, the playing file is interrupted and a new random wave file is started.</p>
<h3>Install CircuitPython</h3>
<p>Follow the steps to install CircuitPython from the CircuitPython download pages for the selected dev board:</p>
<ul>
<li><a href="https://circuitpython.org/board/adafruit_qtpy_rp2040/">Adafruit QT Py RP2040</a></li>
<li><a href="https://circuitpython.org/board/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/">Adafruit QT Py ESP32-S3 4MB Flash / 2MB PSRAM</a></li>
<li><a href="https://circuitpython.org/board/adafruit_qtpy_esp32s3_nopsram/">Adafruit QT Py ESP32-S3 8MB Flash / No PSRAM</a></li>
</ul>
<h3>Install the Adafruit Audio BFF Example and Add Wave Files</h3>
<p>Follow the steps in the <a href="https://learn.adafruit.com/adafruit-audio-bff/circuitpython">Adafruit Audio BFF Learning Guide</a> to install the sd card folder, wave files, libraries, and CircuitPython script.</p>
<p>Once the script works using the dev board&#8217;s built-in button, change the button line to use the SDA1 GPIO instead of the built-in button GPIO:</p>
<pre>button = digitalio.DigitalInOut(board.<strong>SDA1</strong>)</pre>
<p>Now pressing the big red button should trigger the playback of sounds!</p>
<h3>Change When Sounds Can Start (Optional)</h3>
<p>I wanted pressing the button a 2nd time while a sound is already playing to interrupt the currently sound and begin a new one. This was a relatively easy change to the code. Delete the else: and shift the lines of code below the else four spaces to the left:</p>
<pre>    if not button.value:
        if mixer and mixer.voice[0].playing:
            print("stopping")
            mixer.voice[0].stop()
            if wavefile:
                wavefile.close()

        wavefile, wave = open_audio()
        mixer = audiomixer.Mixer(voice_count=1,
                                 sample_rate=wave.sample_rate,
                                 channel_count=wave.channel_count,
                                 bits_per_sample=wave.bits_per_sample,
                                 samples_signed=True)
        mixer.voice[0].level = 0.1
        audio.play(mixer)
        mixer.voice[0].play(wave)</pre>
<h3>Add Code to Debounce the Pushbutton (Optional)</h3>
<p>After changing how quickly sounds could start and stop, I noticed that sometimes a sound would start twice after just one button press. This is caused by the button contacts bouncing and the CircuitPython script sampling the button input multiple times while the contacts were still bouncing.</p>
<p>To solve this problem, I added a 10 ms delay to the main loop and then added a state machine to debounce the pushbutton. The state machine must see the button down twice in 10 ms to count as a new button press and the state machine must then see the button up twice in 10 ms to count as a button release. This solved the button bounce problem.</p>
<p>To add debouncing to the script, add the following variable and function definition between the open_audio function and the start of the while loop:</p>
<pre>button_state = 0

def debounce (state, button_value):

  new_press = False

  if state == 0:
    if button_value == False:
      state = 1
  elif state == 1:
    if button_value == False:
      state = 2
      new_press = True
    else:
      state = 0
  elif state == 2:
    if button_value == True:
      state = 3
  elif state == 3:
    if button_value == True:
      state = 0
    else:
      state = 2

  return state, new_press</pre>
<p>Then change:</p>
<pre>    if not button.value:</pre>
<p>To:</p>
<pre>    time.sleep (0.01)
    button_state, button_pressed = debounce (button_state, button.value)

    if button_pressed:</pre>
<p>Finally, from the end of the script, delete:</p>
<pre>        while not button.value:
            pass</pre>
<h3>Code Listing</h3>
<p>The final version of the example code with my changes is listed below:</p>
<pre># SPDX-FileCopyrightText: 2023 ladyada for Adafruit Industries
#
# SPDX-License-Identifier: MIT

# Demo audio player that plays random wav files from internal storage or
# SD card. Default pinout matches the Audio BFF for QT Py S2, S3 and RP2040

import os
import random
import audiocore
import board
import audiobusio
import audiomixer
import adafruit_sdcard
import storage
import digitalio
import time

card_cs = digitalio.DigitalInOut(board.A0)
card_cs.direction = digitalio.Direction.INPUT
card_cs.pull = digitalio.Pull.UP
sdcard = None

DATA = board.A1
LRCLK = board.A2
BCLK = board.A3
audio = audiobusio.I2SOut(BCLK, LRCLK, DATA)
mixer = None

button = digitalio.DigitalInOut(board.SDA1)
button.switch_to_input(pull=digitalio.Pull.UP)

button_state = 0
wavefile = 0

wave_files = []
for filename in sorted(os.listdir("/")):
    filename = filename.lower()
    if filename.endswith(".wav") and not filename.startswith("."):
        wave_files.append(filename)


def open_audio():
    n = random.choice(wave_files)
    print("playing", n)
    f = open(n, "rb")
    w = audiocore.WaveFile(f)
    return f, w


def debounce (state, button_value):

  new_press = False

  if state == 0:
    if button_value == False:
      state = 1
  elif state == 1:
    if button_value == False:
      state = 2
      new_press = True
    else:
      state = 0
  elif state == 2:
    if button_value == True:
      state = 3
  elif state == 3:
    if button_value == True:
      state = 0
    else:
      state = 2

  return state, new_press


while True:
    if not sdcard:
        try:
            sdcard = adafruit_sdcard.SDCard(board.SPI(), card_cs)
            vfs = storage.VfsFat(sdcard)
            storage.mount(vfs, "/sd")
            print("Mounted SD card")
            wave_files = ["/"+file for file in os.listdir('/') if file.endswith('.wav')]
            wave_files += ["/sd/"+file for file in os.listdir('/sd') if file.endswith('.wav')]
            print(wave_files)
        except OSError:
            pass

    time.sleep (0.01)
    button_state, button_pressed = debounce (button_state, button.value)

    if button_pressed:
        if mixer and mixer.voice[0].playing:
            print("stopping")
            mixer.voice[0].stop()
            if wavefile:
                wavefile.close()

        wavefile, wave = open_audio()
        mixer = audiomixer.Mixer(voice_count=1,
                                 sample_rate=wave.sample_rate,
                                 channel_count=wave.channel_count,
                                 bits_per_sample=wave.bits_per_sample,
                                 samples_signed=True)
        mixer.voice[0].level = 1.0
        audio.play(mixer)
        mixer.voice[0].play(wave)</pre>
<h2>Conclusion</h2>
<p>This was a fun project, I learned a bit more Autodesk Fusion, and got some more Python experience while building it.</p>
<p>Two notes on board selection:</p>
<ol>
<li>I found that the QT Py ESP32-S3 audio would pop if ending a sound effect early while the QT Py RP2040 would not. For this reason, I would recommend using the QT Py RP2040 for this project unless Wi-Fi is absolutely needed.</li>
<li>I also found that audio is quicker to start after a button press, at least for the QT Py ESP32-S3 board, if using the microSD card rather than the dev board&#8217;s on-board flash for the audio files. For this reason, I&#8217;d recommend using the Audio BFF with a microSD card over the I2S Amplifier BFF. I haven&#8217;t tested using the QT Py RP2040&#8217;s on-board flash to see if it faster than</li>
</ol>
<p>A complete list of the bill of materials and design files follows.</p>
<h2>Bill of Materials</h2>
<h3>Electrical Parts</h3>
<ul>
<li><a href="https://amzn.to/48o0hH5">button</a></li>
<li><a href="https://www.adafruit.com/product/4210">qwicc cable</a></li>
<li><a href="https://www.adafruit.com/product/4900">qt py rp2040</a></li>
<li><a href="https://www.adafruit.com/product/5769">audio bff for qt py</a></li>
<li><a href="https://www.bhphotovideo.com/c/product/1661920-REG/kingston_sdcit2_8gb_8gb_microsdhc_industrial_c10.html">microsd card</a></li>
<li><a href="https://www.adafruit.com/product/3922">picoblade cable</a></li>
<li><a href="https://amzn.to/4mJZBPJ">speaker</a></li>
<li><a href="https://www.mycablemart.com/store/cart.php?m=product_detail&amp;p=11346">usb c panel mount cable</a></li>
<li>headers and <a href="https://www.adafruit.com/product/3008">header sockets</a> to connect boards together</li>
</ul>
<h3>Mechanical Parts</h3>
<ul>
<li>2 M2.5 x 8mm pan head black oxide screw</li>
<li>10 M3 x 8mm pan head black oxide screw</li>
<li>4 M3 black oxide hex nut</li>
<li>4 M3 black oxide washer</li>
<li><a href="https://www.digikey.com/en/products/detail/3m/SJ5076/570288">4 rubber feet</a></li>
</ul>
<h3>Filament</h3>
<ul>
<li><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40988815556744">black</a></li>
<li><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=43045599215752">blue</a></li>
<li><a href="https://us.store.bambulab.com/products/pla-basic-filament?id=40475106803848">silver</a></li>
<li><a href="https://us.store.bambulab.com/products/support-for-pla-new?id=578872019709923335">support</a></li>
</ul>
<h3>3D Printed Parts</h3>
<ul>
<li><a href="http://bikerglen.com/wp/wp-content/uploads/2025/10/lizard/Box.3mf">enclosure base</a></li>
<li><a href="http://bikerglen.com/wp/wp-content/uploads/2025/10/lizard/Lid.3mf">enclosure lid</a></li>
<li><a href="http://bikerglen.com/wp/wp-content/uploads/2025/10/lizard/Cover.3mf">electronics cover</a></li>
</ul>
<h3>Miscellaneous</h3>
<ul>
<li><a href="https://amzn.to/43dHXN7">speaker foam</a> to stop any rattling</li>
<li>soldering iron and solder</li>
<li>flexible usb cables that won&#8217;t pull your dev board off your desk
<ul>
<li><a href="https://amzn.to/4mTyamt">super flexible a to c</a></li>
<li><a href="https://amzn.to/3KHBL9U">super flexible c to c</a></li>
</ul>
</li>
</ul>
<h2>Design Files</h2>
<ul>
<li><a href="http://bikerglen.com/wp/wp-content/uploads/2025/10/lizard/code.py">code.py</a></li>
<li><a href="http://bikerglen.com/wp/wp-content/uploads/2025/10/lizard/lizard_22050.wav">lizard_22050.wav</a></li>
<li><a href="http://bikerglen.com/wp/wp-content/uploads/2025/10/lizard/Box.3mf">box.3mf</a></li>
<li><a href="http://bikerglen.com/wp/wp-content/uploads/2025/10/lizard/Lid.3mf">lid.3mf</a></li>
<li><a href="http://bikerglen.com/wp/wp-content/uploads/2025/10/lizard/Cover.3mf">cover.3mf</a></li>
</ul>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs</em>.</p>
<h3></h3>
]]></content:encoded>
			<wfw:commentRss>https://bikerglen.com/blog/lizard-3d-printed-single-purpose-soundboard/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://bikerglen.com/wp/wp-content/uploads/2025/10/lizard/lizard_22050.wav" length="0" type="audio/wav" />
		</item>
		<item>
		<title>Building a Battery-Powered Zigbee Gate Sensor with the nRF52840</title>
		<link>https://bikerglen.com/blog/building-a-battery-powered-zigbee-gate-sensor/</link>
		<comments>https://bikerglen.com/blog/building-a-battery-powered-zigbee-gate-sensor/#comments</comments>
		<pubDate>Sun, 02 Jun 2024 18:25:07 +0000</pubDate>
		<dc:creator><![CDATA[Glen]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://bikerglen.com/blog/?p=5213</guid>
		<description><![CDATA[Tired of taking the trash out in the dark? Forgot to turn on the outside lights on the way to the trash cans? Read on to learn how I built a waterproof, battery-powered Zigbee hall-effect sensor to automatically turn on &#8230; <a href="https://bikerglen.com/blog/building-a-battery-powered-zigbee-gate-sensor/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/DSC_0138_crop_2048px.jpg"><img class="alignnone size-large wp-image-5368" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/DSC_0138_crop_2048px-1024x682.jpg" alt="DSC_0138_crop_2048px" width="640" height="426" /></a></p>
<p>Tired of taking the trash out in the dark? Forgot to turn on the outside lights on the way to the trash cans? Read on to learn how I built a waterproof, battery-powered Zigbee hall-effect sensor to automatically turn on my backyard landscape lights when I open the gate to go into the backyard at night.</p>
<p><span id="more-5213"></span></p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs.</em></p>
<h2>Motivation</h2>
<div id="attachment_5289" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/button-slap.jpg"><img class="wp-image-5289 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/button-slap-1024x576.jpg" alt="button-slap" width="640" height="360" /></a><p class="wp-caption-text">Me coming home after a night mountain bike ride and having to push some buttons to get some light in the backyard.</p></div>
<p>In my previous Zigbee project, <a href="https://bikerglen.com/blog/building-battery-powered-zigbee-buttons/">I built some buttons</a> to control some outdoor lighting. This is great except smart houses shouldn&#8217;t require taking extra actions like pressing buttons, opening a phone app, or yelling &#8220;Hey, Alexa!&#8221; for something to happen. Actions, like turning on a light, should just happen as a part of something you&#8217;re already doing.</p>
<p>I wanted my backyard landscape lights to turn on whenever I wheeled the bike into the backyard or took out the trash without me doing anything I wasn&#8217;t already going to do. And then I wanted them to turn off when I left the backyard.</p>
<p>To accomplish this goal, I built a battery-powered Zigbee hall effect sensor to turn on my backyard landscape lights whenever the side gate is opened and then turn them off again after the gate is closed.</p>
<h2>Why I Couldn&#8217;t Reuse My Zigbee Button Board</h2>
<div id="attachment_5394" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/vlcsnap-2024-06-02-21h24m19s187.jpg"><img class="wp-image-5394 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/vlcsnap-2024-06-02-21h24m19s187-1024x576.jpg" alt="vlcsnap-2024-06-02-21h24m19s187" width="640" height="360" /></a><p class="wp-caption-text">Me connecting a magnetic reed switch to the Zigbee button board from the previous post.</p></div>
<p>I originally wanted to reuse my Zigbee button board from the previous post. I could pretty easily disconnect the buttons and connect a magnetic reed switch in their place. If I used a normally open sensor, the contacts would be open when the gate is closed and the current consumption of the nRF52840 would be in the microamps.</p>
<p>When the gate is opened, however, the contacts would be closed and the nRF52840&#8217;s 13 kΩ weak internal pullup would be connected to ground through the contacts. With a nominally 3 V battery and a 13 k resistor connected to ground, the current consumption would jump to 230 µA or over 50 times higher than when the gate is closed. This would quickly kill the battery life if the gate were left open for any significant period of time. I needed a better solution.</p>
<h2>Existing Off-the-Shelf Solutions</h2>
<div id="attachment_5386" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240603_025334823_crop.jpg"><img class="wp-image-5386 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240603_025334823_crop-1024x683.jpg" alt="PXL_20240603_025334823_crop" width="640" height="427" /></a><p class="wp-caption-text">Two off-the-shelf Zigbee door sensors.</p></div>
<p>I purchased two inexpensive, off-the-shelf Zigbee door sensors to see how these worked and see if I could learn anything from their design.</p>
<h3>Sonoff SNZB-04</h3>
<div id="attachment_5387" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240603_025449189_crop.jpg"><img class="wp-image-5387 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240603_025449189_crop-1024x683.jpg" alt="PXL_20240603_025449189_crop" width="640" height="427" /></a><p class="wp-caption-text">A Sonoff SNZB-04 wireless Zigbee door sensor.</p></div>
<p>The first sensor was a <a href="https://amzn.to/4jJ3Fyt">Sonoff SNZB-04</a>. It&#8217;s powered by a CR2032 coin cell. I took it apart and discovered that it too used a magnetic reed switch. If I remember correctly, it used a 100 kΩ pullup resistor on the board. That&#8217;s better than the nRF52840&#8217;s 13 kΩ but it would still lead to the gate drawing 5 times as much current in one position as the other. And it&#8217;s not waterproof.</p>
<h3>Third Reality 3RDS17BZ</h3>
<div id="attachment_5388" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240603_025835512_crop.jpg"><img class="wp-image-5388 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240603_025835512_crop-1024x683.jpg" alt="PXL_20240603_025835512_crop" width="640" height="427" /></a><p class="wp-caption-text">A Third Reality 3RDS17BZ wireless Zigbee door sensor.</p></div>
<p>The second sensor was a <a href="https://amzn.to/3YAnLCU">Third Reality 3RDS17BZ</a> wireless door sensor. It&#8217;s powered by a common AAA battery. I disassembled it and quickly noticed there was not a large magnetic reed switch on the board.</p>
<div id="attachment_5389" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/DSC_3720_crop.jpg"><img class="wp-image-5389 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/DSC_3720_crop-1024x683.jpg" alt="DSC_3720_crop" width="640" height="427" /></a><p class="wp-caption-text">Close up of the Third Reality&#8217;s hall effect sensor on the edge of its circuit board.</p></div>
<p>I looked a little closer at the board edge near where the magnet goes. On the edge was a small device in a 3-pin SOT-23 package. What could it possibly be but a Hall effect sensor! It outputs one logic state when the magnet is close to it and the other logic state when the magnet is not near it.</p>
<p>At this point, I needed to research Hall effect sensors to see what their typical current consumption was. If the Third Reality door sensor was water proof, I likely could have used it as is.</p>
<h2>Hall Effect Sensor Test Board</h2>
<div id="attachment_5363" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/05/PXL_20240602_175840133_crop.jpg"><img class="wp-image-5363 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/05/PXL_20240602_175840133_crop-1024x682.jpg" alt="PXL_20240602_175840133_crop" width="640" height="426" /></a><p class="wp-caption-text">My Hall effect sensor test board with a CR2450 coin cell and two Hall effect sensors from TI. One is in SOT-23-3 SMD package and the other is in a TO-92-3 through-hole package.</p></div>
<p>After a few hours of research, I found a promising series of Hall effect sensors from TI, the DRV5032FB series. These sensors are available in surface mount and through hole packages, have a current consumption of about 0.5 µA, and have push-pull outputs so I would not need to use the nRF52840&#8217;s internal pullups. I quickly designed and built a test board, shown above, to try them out and gauge their suitability for my application.</p>
<h3>Schematic</h3>
<div id="attachment_5365" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/drv5032-eval.png"><img class="wp-image-5365 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/drv5032-eval-1024x764.png" alt="drv5032-eval" width="640" height="478" /></a><p class="wp-caption-text">Hall effect sensor test board schematic.</p></div>
<p>The schematic of the test board is shown above. It&#8217;s just a small battery, two sensors with each in a different package, and two low-current LEDs to indicate when a magnetic field has been detected by each of the sensors.</p>
<h3>Test Results</h3>
<div id="attachment_5364" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/05/finding_the_field.jpg"><img class="wp-image-5364 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/05/finding_the_field-1024x902.jpg" alt="finding_the_field" width="640" height="564" /></a><p class="wp-caption-text">Testing the Hall effect sensors with a magnet mounted to the gate.</p></div>
<p>The results with the test board were promising. Both sensors worked well and detected the presence or absence of the magnet at a sufficient distance to work with the gap between the fence&#8217;s gate and the fence&#8217;s post.</p>
<p>One weird thing is that my magnet&#8217;s magnetic field had a big gap in the middle and was strongest at the top and bottom of the magnet rather than in the middle. In the photo above, you can see the magnetic field is not detected when the sensor is centered on the magnet (left) but is detected when it is placed even with the lower screw (right).</p>
<h2>Zigbee Gate Sensor Board</h2>
<div id="attachment_5366" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/merged-cropped-2048px.jpg"><img class="wp-image-5366 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/merged-cropped-2048px-1024x683.jpg" alt="merged-cropped" width="640" height="427" /></a><p class="wp-caption-text">Photograph of the completed Zigbee gate sensor board.</p></div>
<p>With the hall effect sensor selected and tested, it was time to design the gate sensor board. The completed board is shown above.</p>
<h3>Schematic</h3>
<div id="attachment_5371" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/nrf52840-contact-v03-sch.png"><img class="wp-image-5371 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/nrf52840-contact-v03-sch-1024x764.png" alt="nrf52840-contact-v03-sch" width="640" height="478" /></a><p class="wp-caption-text">Schematic of the ZIgbee gate sensor board.</p></div>
<p>The schematic for the gate sensor is shown above. It&#8217;s almost identical to the Zigbee button board except the screw terminals for the buttons have been replaced with the option to use either the surface mount or through hole version of the DRV5032FB Hall effect sensors.</p>
<p>The very high integration of the Minew Semiconductor MS88SF2 RF module means there’s not much to the hardware. It’s the RF module plus a few buttons, LEDs, passives, and the sensors. Note that the MS88FS2 is available in three different power configurations. For my battery-powered four-input Zigbee transmitter, I’m using “Configuration 3: 1.8V-3.6V to VDD.”</p>
<h3>The Enclosure</h3>
<div id="attachment_5421" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20250419_232042511.jpg"><img class="size-large wp-image-5421" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20250419_232042511-1024x647.jpg" alt="An unstuffed gate sensor board and the waterproof enclosure for it." width="640" height="404" /></a><p class="wp-caption-text">An unstuffed gate sensor board and the waterproof enclosure for it.</p></div>
<p>The board is a funky shape because I wanted it to fit in the waterproof Hammond enclosure shown above. This is Hammond part number 1554B2GYCL.</p>
<h3>Software Development and Current Measurement</h3>
<div id="attachment_5398" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240611_041536271_2000px_crop.jpg"><img class="wp-image-5398 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240611_041536271_2000px_crop-1024x682.jpg" alt="PXL_20240611_041536271_2000px_crop" width="640" height="426" /></a><p class="wp-caption-text">Measuring the boards power consumption using the Nordic Power Profiler Kit II.</p></div>
<p>The software for this board is almost identical to the software for the four-button Zigbee board. The only material changes were reducing the number of supported buttons from four down to one, disabling the internal pullups on the input connected to the Hall effect sensor, and enabling callbacks on both edges of the input signal.</p>
<p>Once the software was ready, I used the Nordic Power Profiler II to measure the average current consumption of the board over a few 10 minute intervals. The measurement results were:</p>
<p>4.26 μA with no transmissions in a 10 minute period with gate open.</p>
<p>4.00 μA with no transmissions in a 10 minute period with gate closed.</p>
<p>4.27 μA with 2 transmissions in a 10 minute period.</p>
<p>These measurements are inline with my expectations given the current consumption of the previous board and the current consumption spec from the Hall effect sensor&#8217;s datasheet.</p>
<h3>Software Changes vs My Zigbee Switch Board</h3>
<p>The biggest software change was initializing the nRF52840&#8217;s input pin that is connected to the Hall effect sensor to generate a callback on both rising and falling inputs. The four button Zigbee sensor listened only for falling edges. A falling edge would wake the CPU and then the CPU would stay active and wait for the rising edge at the input before going back to sleep.</p>
<p>This would waste battery while the gate was closed and the Hall effect sensor&#8217;s output was low. To prevent excess current draw when the gate is in the closed position, I reconfigured the input to listen for both edges then sleep as soon as the Zigbee status messages were transmitted.</p>
<p>Now, when the gate is opened, the Hall effect sensor&#8217;s output goes high and a rising edge is detected by the nRF52840. This wakes the CPU, the CPU sends a gate opened message, then goes back to sleep. Then, when the gate is closed, the Hall effect sensor&#8217;s output goes low and a falling edge is detected by the nRF52840. This wakes the CPU, the CPU sends a gate closed message, then goes back to sleep.</p>
<h2>Installation</h2>
<div id="attachment_5368" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/DSC_0138_crop_2048px.jpg"><img class="wp-image-5368 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/DSC_0138_crop_2048px-1024x682.jpg" alt="DSC_0138_crop_2048px" width="640" height="426" /></a><p class="wp-caption-text">The completed Zigbee Hall effect gate sensor mounted to the fence post.</p></div>
<p>I mounted the sensor to the fence post opposite the post with the gate&#8217;s hinges.</p>
<div id="attachment_5369" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240602_174630157_crop.jpg"><img class="wp-image-5369 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240602_174630157_crop-1024x682.jpg" alt="PXL_20240602_174630157_crop" width="640" height="426" /></a><p class="wp-caption-text">The magnet mounted to the gate and the sensor mounted to the fence post. Note the strange position of the magnet relative to the sensor. The need for the offset was discussed in a previous section of this blog post.</p></div>
<p>Then I mounted the magnet to the opening edge of the gate. I swung the gate open and closed a few times and verified the sensor transmitted messages to the Zigbee coordinator successfully.</p>
<h2>The Lights, Transformer, and Z-Wave Outlet</h2>
<div id="attachment_5427" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240722_024127548.jpg"><img class="size-large wp-image-5427" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20240722_024127548-1024x488.jpg" alt="My landscape lights. These are in the front yard." width="640" height="305" /></a><p class="wp-caption-text">My landscape lights. These are in the front yard.</p></div>
<p>My landscape lights are <a href="https://amzn.to/3YzDUID">simple warm white bollards</a> that run off 12 VAC or 12 VDC. A photo of some of them in the front yard is shown above. I really like that the bulb is at the top of the fixture and projects downward toward the ground rather than outward. They provide sufficient light to see what you&#8217;re doing without being blinding.</p>
<div id="attachment_5426" style="width: 597px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20250419_231159546.jpg"><img class="size-full wp-image-5426" src="https://bikerglen.com/wp/wp-content/uploads/2024/06/PXL_20250419_231159546.jpg" alt="The landscape light transformer and z-wave switched outlet." width="587" height="1000" /></a><p class="wp-caption-text">The landscape light transformer and z-wave switched outlet.</p></div>
<p>They&#8217;re connected using <a href="https://amzn.to/4cJvDb6">16 AWG wire</a> and <a href="https://amzn.to/4izQZcs">insulation-displacement connectors</a> to a low-voltage landscape lighting transformer from <a href="https://amzn.to/4cJvDb6">Dewenwils</a>. The transformer is set to its &#8220;constant&#8221; mode so that the lights are always on when the transformer is powered.</p>
<p>To switch power to the transformer and turn the lights on and off when the gate is opened and closed, I use a <a href="https://amzn.to/4cJvDb6">Zooz ZEN14 outdoor double plug</a>. The Zooz plug is a Z-Wave device so it required installing and running Z-Wave JS UI on the same Raspberry Pi that runs Zigbee2MQTT for the gate sensor.</p>
<h2>Tying Everything Together with Python</h2>
<p>I could have used Home Assistant to tie everything together but my needs are simple enough that a Python script and an MQTT broker were sufficient to link the Zigbee gate sensor to the to the Z-Wave outlet that controls the lights.</p>
<p>When the gate is opened or closed, my sensor sends a Zigbee message to Zigbee2MQTT and Zigbee2MQTT publishes a <em>gated opened</em> or <em>gated closed</em> message on the <em>rear gate</em> topic to the MQTT broker.</p>
<p>My Python script is subscribed to the <em>rear gate</em> topic on the MQTT broker. When it receives the <em>gate opened</em> message on the <em>rear gate</em> topic it:</p>
<ol>
<li>Sends a short gate opened message to my cell phone using pushover.net.</li>
<li>Takes a photo using a camera above the gate and sends that photo to my cell phone using pushover.net</li>
<li>Determines if it is between dusk and dawn. If it&#8217;s dark outside, it sends an <em>outlet on</em> message on the <em>rear gate outlet</em> topic to the MQTT broker.</li>
</ol>
<p>When the Python script receives the <em>gate closed</em> message on the <em>rear gate</em> topic, it sends an <em>outlet off</em> message on the <em>rear gate outlet</em> topic to the MQTT broker.</p>
<p>Z-Wave JS UI is subscribed to the <em>rear gate outlet</em> topic. When it receives an <em>outlet on</em> or <em>outlet off</em> message, it will send a Z-Wave command to turn the Zooz outlet for the lights on or off.</p>
<p>The end effect is that if it is dark out, opening the gate creates a Zigbee <em>gate opened</em> message that gets translated by a bunch of software into a Z-Wave <em>outlet on</em> message thus turning on the lights. Then closing the gate creates a Zigbee <em>gate closed</em> message that gets translated into a Z-Wave <em>outlet off</em> message thus turning off the lights.</p>
<h2>Conclusion</h2>
<p>This was interesting project. The most interesting part, however, is seeing how long the battery lasts. So far, the battery has lasted through a complete year in the Colorado outdoors. This includes a summer with peak temperatures in the 100s and a winter with overnight lows well below 0<span id="degree-symbol" class="font3">°</span> Fahrenheit. The sensor and battery have both, in fact, outlasted the gate and fence they were mounted on. The gate and fence will be replaced next month and the sensor will be moved to the new fence to begin year two.</p>
<h2>Design files</h2>
<p>The design files and nRF52840 software for this project are available on Github in my <a href="https://github.com/bikerglen/homebrew_zigbee_devices/tree/main/nrf52840-contact" target="_blank">Zigbee homebrew contact sensor repository</a>.</p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs</em>.</p>
]]></content:encoded>
			<wfw:commentRss>https://bikerglen.com/blog/building-a-battery-powered-zigbee-gate-sensor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building Battery-Powered Zigbee Buttons with the nRF52840</title>
		<link>https://bikerglen.com/blog/building-battery-powered-zigbee-buttons/</link>
		<comments>https://bikerglen.com/blog/building-battery-powered-zigbee-buttons/#comments</comments>
		<pubDate>Tue, 23 Apr 2024 00:57:16 +0000</pubDate>
		<dc:creator><![CDATA[Glen]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://bikerglen.com/blog/?p=5201</guid>
		<description><![CDATA[Ever want to control some outdoor lighting without using your phone or going back in the house? Read on to see how I installed a set of landscape lights then built a set of outdoor, waterproof Zigbee buttons to control &#8230; <a href="https://bikerglen.com/blog/building-battery-powered-zigbee-buttons/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<div id="attachment_5203" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/rinse.jpg"><img class="wp-image-5203 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/rinse-1024x651.jpg" alt="rinse" width="640" height="407" /></a><p class="wp-caption-text">Rinsing the bike off after a muddy night ride can be needed. Being able to see what you&#8217;re doing without getting your phone out or going inside the house is great.</p></div>
<p>Ever want to control some outdoor lighting without using your phone or going back in the house? Read on to see how I installed a set of landscape lights then built a set of outdoor, waterproof Zigbee buttons to control them using an nRF52840 module, Zigbee2MQTT, Python, and TinyTuya.</p>
<p><span id="more-5201"></span></p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs.</em></p>
<h2>Motivation</h2>
<div id="attachment_5238" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/muddy-butt.jpg"><img class="size-large wp-image-5238" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/muddy-butt-1024x576.jpg" alt="Behold my muddy butt!" width="640" height="360" /></a><p class="wp-caption-text">Behold my muddy butt!</p></div>
<p>We had a much wetter than normal summer here last year. My bike (and myself) were often too muddy to bring in the house after a ride. I&#8217;d either get done with a night ride or get home after dark after a long drive home and couldn&#8217;t see to rinse the bike off in the <a href="https://www.youtube.com/watch?v=CnTKS-d_h5c" target="_blank">backyard bike rack I built many years ago</a>. I decided I needed some lights for the bike rack and I wanted to control them outdoors with real buttons. I did not want to need to dig my phone out of my pocket or take my gloves off or walk in the house to control the lights.</p>
<h2>The Lights</h2>
<div id="attachment_5257" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/lights-in-box.jpg"><img class="size-large wp-image-5257" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/lights-in-box-1024x576.jpg" alt="Thank you, XMCOSY+, for the free set of lights!" width="640" height="360" /></a><p class="wp-caption-text">Thank you, XMCOSY+, for the free set of lights!</p></div>
<p>After <a href="https://bikerglen.com/blog/xmcosy-string-lights-with-wled-and-tinytuya/">writing</a> about the <a href="https://amzn.to/47vNZYU" target="_blank">XMCOSY+ string lights</a> I hung in my backyard, XMCOSY+ offered to send me a set of their <a href="https://amzn.to/44bbRRA" target="_blank">Low-Voltage Double-Head RGBW Pathway Lights</a> for <span id="productTitle" class="a-size-large product-title-word-break">free. I took them up on the offer. </span></p>
<p><span id="productTitle" class="a-size-large product-title-word-break">The lights feature warm white and red, green, and blue LEDs. They can be set to warm white or any color using the Tuya Smart Life app for iPhone or Android. Each light has two heads that can be swiveled up and down independently to direct light where it&#8217;s needed. These lights are usually aimed downward to light up a path or walkway but they can be pointed upward to light up plants or wash walls too.<br />
</span></p>
<div id="attachment_5241" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/muddy-bike.jpg"><img class="size-large wp-image-5241" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/muddy-bike-1024x670.jpg" alt="Behold my muddy bike!" width="640" height="419" /></a><p class="wp-caption-text">Behold my muddy bike!</p></div>
<p><span id="productTitle" class="a-size-large product-title-word-break">They were likely expecting a more traditional unboxing and review video but I put the lights to use lighting up the bike rack in my backyard. They really do make it easier to see to rinse off the bike. You can see one of the lights behind my snow and mud covered bike in the photo above.<br />
</span></p>
<h3>The Light Set</h3>
<div id="attachment_5262" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/light-set.jpg"><img class="size-large wp-image-5262" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/light-set-1024x573.jpg" alt="The complete XMCOSY+ dual-headed pathway light set." width="640" height="358" /></a><p class="wp-caption-text">The complete XMCOSY+ dual-headed pathway light set.</p></div>
<p>The <a href="https://amzn.to/4b1cCig" target="_blank">light set</a> includes a power supply, four 8-foot splitter cables, four ground stakes, four double-head pathway lights, and an extra 13-foot extension cable that can be used if the distance between the power supply and the first light or any two lights is greater than eight feet. A <a href="https://amzn.to/3xDf1Bh" target="_blank">beefier 100 W power supply</a> and <a href="https://amzn.to/3UqaT0x" target="_blank">extra extension cables</a> are available separately as well.</p>
<h3>Power Supply Details</h3>
<div id="attachment_5264" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/power-supply.jpg"><img class="wp-image-5264 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/power-supply-1024x682.jpg" alt="power-supply" width="640" height="426" /></a><p class="wp-caption-text">The 36 W power supply for the lights. The power supply includes both a 12 V, 3 A switching power supply and a Tuya cloud-based Wi-Fi PWM LED controller.</p></div>
<p>The power supply is shown in the photo above. The AC line cord is on the left and the 5-pin female connector for the lights is on the right. The power supply is rated for 12 V at 3 A or 36 W and has both FCC and ETL certifications.</p>
<p>The power supply contains an embedded Tuya IoT Wi-Fi LED controller. The power supply&#8217;s connector has a +12 V common output and four ground returns for the four different colors of LEDs embedded in the light heads. Since all the lights are connected in parallel to the same output, the lights cannot be set to different colors at the same time.</p>
<h3>Power Cable Details</h3>
<div id="attachment_5265" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/splitter-cable.jpg"><img class="wp-image-5265 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/splitter-cable-1024x682.jpg" alt="splitter-cable" width="640" height="426" /></a><p class="wp-caption-text">Eight foot, five pin splitter cable with one male plug and two female receptacles.</p></div>
<p>The lights include four 8-foot splitter cables and one 13-foot extension cable. One of the four splitter cables is shown above. The splitter cable has one male plug and two female receptacles. The male plug connects to the power supply or a previous splitter cable. The two female connectors connect to a light head and the next splitter cable. This is the normal cable that is strung between the power supply and the first light and then between each subsequent pair of lights.</p>
<div id="attachment_5266" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/extension-cable.jpg"><img class="wp-image-5266 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/extension-cable-1024x682.jpg" alt="extension-cable" width="640" height="426" /></a><p class="wp-caption-text">The 13-foot extension cable.</p></div>
<p>The light set also includes a single 13-foot extension cable. This cable can be put between the power supply and the first light or between any pair of lights if the distance between them is longer than that allowed by the 8-foot splitter cable. <a href="https://amzn.to/3UqaT0x" target="_blank">Extra extension cables</a> can be purchased separately if needed. All the cables and connectors have five conductors.</p>
<h3>Fixture Details</h3>
<div id="attachment_5267" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/lamp-head.jpg"><img class="wp-image-5267 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/lamp-head-1024x682.jpg" alt="lamp-head" width="640" height="426" /></a><p class="wp-caption-text">The pathway light and its metal ground stake.</p></div>
<p>Each light has two rotatable heads and a metal ground stake. To set up a light, use a rubber mallet to set the stake in the ground, press the lamp into the cup on the stake, and connect the light&#8217;s power connector to one of the female sockets on the splitter or extension cables.<span style="color: #ff0000;"><br />
</span></p>
<div id="attachment_5314" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/angles.jpg"><img class="size-large wp-image-5314" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/angles-1024x326.jpg" alt="Each light head can be rotated independently about 330 degrees." width="640" height="204" /></a><p class="wp-caption-text">Each light head rotates independently through about 330 degrees.</p></div>
<p>Once the lamp is in place, the heads on either side of the post can be rotated independently to direct light where it&#8217;s needed. The total rotation is about 330 degrees.<span style="color: #ff0000;"><br />
</span></p>
<div id="attachment_5270" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/twisty.jpg"><img class="wp-image-5270 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/twisty-1024x683.jpg" alt="twisty" width="640" height="427" /></a><p class="wp-caption-text">The two heads on each light can be rotated individually through an angle of about 330 degrees.</p></div>
<p>Most people will place the lights along a walk way and aim the lights down toward the walk way or will place them on the edge of a flower bed and rotate them out or down toward the plants. I placed one on either side of my bike rack and rotated the lights up toward the bikes.</p>
<h3>Electrical Details</h3>
<div id="attachment_5268" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/connectors.jpg"><img class="wp-image-5268 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/connectors-1024x682.jpg" alt="connectors" width="640" height="426" /></a><p class="wp-caption-text">The female power supply connector and a male light head connector.</p></div>
<p>The power supply and lights use the proprietary five-pin connectors shown in the photo above. The connectors are about an inch in diameter.</p>
<div id="attachment_5276" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/5-pin-pinout-12v.drawio1.png"><img class="wp-image-5276 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/5-pin-pinout-12v.drawio1-1024x707.png" alt="5 pin pinout 12v.drawio" width="640" height="442" /></a><p class="wp-caption-text">Pin out of the five pin connector looking into one of the male connector on one of the lights. The current values are for a single dual-headed light fixture.</p></div>
<p>This is the first light set I&#8217;ve examined that uses PWM dimming directly from the power supply / controller versus all the other sets that used some form of the WS2811 protocol.</p>
<h3>Color Temperature</h3>
<div id="attachment_5227" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/color-temp.jpg"><img class="wp-image-5227 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/color-temp-907x1024.jpg" alt="color-temp" width="640" height="723" /></a><p class="wp-caption-text">Top: one of the double-headed pathway lights next to my Aperture MC Pro video light. Bottom: holding a diffusion gel sheet in front of the lights.</p></div>
<p>I don&#8217;t have a spectrometer to measure the color temperature of the lights but I estimated the color temperature to be about 2700K by matching the color of an adjustable color temperature video light to the color of the pathway lights. Matching the color temperature was made easier by viewing both lights through a small scrap of diffusion gel. This matches with the lights claimed color temperature.</p>
<h2>Controlling the Lights</h2>
<h3>Tuya Smart Life App</h3>
<div id="attachment_5323" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/tuya-app.jpg"><img class="size-large wp-image-5323" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/tuya-app-1024x732.jpg" alt="Three screens from the Tuya Smart Life App used to control the pathway lights." width="640" height="458" /></a><p class="wp-caption-text">Three screens from the Tuya Smart Life App used to control the pathway lights.</p></div>
<p>These string lights are based on a Tuya IoT processor and as such can be controlled via the Tuya Smart Life app. Three screens from the app are shown in the image above. The leftmost screen shows the slider used to adjust the brightness of the lights when they are set to white. The middle screen shows the color wheel and slider used to adjust the color and brightness of the lights when they&#8217;re set to color. The rightmost screen shows some of the color changing effects these lights can do. Note that all lights are always the same color.</p>
<p>All in all, I think the app is adequate for most people’s needs. I, however, intend to control these lights using the TinyTuya library from a Python script in conjunction with the Zigbee buttons I&#8217;m building.</p>
<h3>TinyTuya</h3>
<p>Describing how to get started with TinyTuya is beyond the scope of this blog post. Fortunately, the <a href="https://github.com/jasonacox/tinytuya/tree/master" target="_blank">documentation and getting started guide</a> for the library is quite good and the <a href="https://github.com/jasonacox/tinytuya/tree/master/examples">examples</a> and <a href="https://github.com/jasonacox/tinytuya/issues" target="_blank">issues</a> pages cover most of the missing details.</p>
<p>The Python script below shows the basics of turning these lights on, setting them to white at full brightness, and turning them off again.</p>
<pre>import tinytuya
import time

ttdevs={}
ttdevs['bike_stand_floods'] = ("&lt;DEVICE ID&gt;", "&lt;DEVICE IP&gt;", "LOCAL KEY")

#----------------------------------------------------------------------------------------------
# turn tuya floods on or off
#

def tuya_floods (name, on):
  print ("sending " + ("on" if (on) else "off") + " request to " + name + "...")

  d = tinytuya.BulbDevice(ttdevs[name][0], ttdevs[name][1], ttdevs[name][2])
  d.set_version(3.3)
  d.set_socketPersistent(False)

  if on:
    d.turn_on()
    d.set_white()
  else:
    d.turn_off()

  print (("on" if (on) else "off") + " request to " + name + " done.")

#----------------------------------------------------------------------------------------------
# main
#

if __name__ == "__main__":

  while True:
    tuya_floods ('bike_stand_floods', True)
    time.sleep (1)
    tuya_floods ('bike_stand_floods', False)
    time.sleep (1)</pre>
<h3>Cloud-Free Alternatives</h3>
<p>In addition to using the Tuya Smart Life app or the Tuya local API via TinyTuya, the lights can be controlled using common off-the-shelf PWM RGBW LED strip controllers such as those made by Gledopto, Zooz, and Shelly. A brief survey of some available 3rd party controls follows, but for now, I&#8217;m personally sticking with TinyTuya and the stock controller.</p>
<p>The easiest way to connect the lights to a 3rd party controller is to cut the male connector off the 13-foot extension, strip the wires, and connect the wires to the new controller. The female end of the extension may then be connected to a splitter cable or a pathway light as normal. The black wire in the extension cable is the common +12 V. The remaining wires are white, red, green, and blue and are the grounds for their respective colors of LEDs.</p>
<h4>Gledopto 5-in-1 Zigbee Controller</h4>
<div id="attachment_5287" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/gledopto-5in12.jpg"><img class="wp-image-5287 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/gledopto-5in12-1024x682.jpg" alt="gledopto-5in1" width="640" height="426" /></a><p class="wp-caption-text">The Gledopto Zigbee Pro+ 5 in 1 Smart LED Controller.</p></div>
<p>If you have an existing Zigbee network, my favorite Zigbee LED light controller is the <a href="https://www.gledopto.com/h-col-434.html" target="_blank">Gledopto Zigbee Pro+ 5 in 1 LED controller #GL-C-201P</a>. It can control single color, CCT white, RGB, RGBW, and RGBCCT LED strips or lights. The lever terminal strips are convenient and it&#8217;s compatible with Zigbee2MQTT out of the box.</p>
<p>To use this controller with the XMCOSY+ path lights, press the &#8220;Opt&#8221; button until the indicator LED is yellow to place the device in RGBW mode. Once in RGBW mode, add it to Zigbee2MQTT and it&#8217;ll show up as a GL-C-007P Pro RGBW controller. You can then set the <a href="https://www.zigbee2mqtt.io/devices/GL-C-007P.html">device&#8217;s attributes</a> to control the attached lights.</p>
<p>I purchased mine from <a href="https://www.aliexpress.us/item/3256806030901743.html" target="_blank">AliExpress</a>. Be sure to click &#8220;5in1 Pro Plus&#8221; before adding the device to the cart if you go this route. There are also similar looking devices on Amazon like the <span id="productTitle" class="a-size-large product-title-word-break"><a href="https://amzn.to/449d0sQ" target="_blank">GIDEALED Smart ZigBee 3.0 LED Controller 5 in 1</a> but I haven&#8217;t personally tried these.</span></p>
<h4>Zooz RGBW Z-Wave Controller</h4>
<div id="attachment_5280" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/zooz-rgbw.jpg"><img class="wp-image-5280 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/zooz-rgbw-1024x682.jpg" alt="zooz-rgbw" width="640" height="426" /></a><p class="wp-caption-text">The Zooz ZEN31 RGBW Dimmer. The cable on the left is connected to a +12 V power supply. The cable on the right is the extension cable that came with the lights that I cut up and connected to the dimmer. If this were for anything other than testing, I&#8217;d do a neater job stripping and inserting the wires in the terminal strip.</p></div>
<p>If you have a Z-Wave network or are looking for a UL-certified LED controller, the <a href="https://www.getzooz.com/zooz-zen31-rgbw-dimmer/" target="_blank">Zooz ZEN31 RGBW dimmer</a> is a great alternative to the Zigbee devices mentioned above. It can be monitored and controlled using an MQTT broker, the <a href="https://github.com/zwave-js/zwave-js-ui" target="_blank">Z-Wave JS UI software</a>, and a Z-Wave dongle like the <a href="https://www.getzooz.com/zooz-zst39-z-wave-long-range-usb-stick/" target="_blank">Zooz Z-Wave Long Range 800 Stick</a>. In the photo above, I have the Z-Wave RGBW dimmer connected to a power supply (cable from the left) and the cut extension cable that came in my set of lights (cable going down).</p>
<h4>Shelly RGBW Wi-Fi Controller</h4>
<div id="attachment_5281" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/shelly-rgbw.jpg"><img class="wp-image-5281 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/shelly-rgbw-1024x682.jpg" alt="shelly-rgbw" width="640" height="426" /></a><p class="wp-caption-text">The Shelly RGBW2 Wi-Fi LED controller. It&#8217;s the only 3rd party controller that sits directly on a Wi-Fi network and thus doesn&#8217;t need a Zigbee or Z-Wave hub.</p></div>
<p>If you don&#8217;t have an existing Zigbee or Z-Wave network or want a simple Wi-Fi RGBW LED controller, the <a href="https://amzn.to/3xT7AWI" target="_blank">Shelly RGBW2 Wi-Fi LED controller</a> is a great product. It can talk directly to an MQTT broker over your existing Wi-Fi network without the need for Zigbee or Z-Wave hub.</p>
<h2>Outdoor Rated Controls</h2>
<p>My goal for this project is to be able to control the lights without having to dig my phone out or take my gloves off. To do this, I want some physical buttons! And I want them to be waterproof so they can be located outdoors!</p>
<h3>The Buttons</h3>
<div id="attachment_5229" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/buttons.jpg"><img class="wp-image-5229 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/buttons-1024x699.jpg" alt="buttons" width="640" height="437" /></a><p class="wp-caption-text">Generic 22 mm momentary mushroom head push button switches.</p></div>
<p>After digging through my boxes(!) of industrial controls and browsing Amazon and AliExpress, I selected some generic 22mm momentary mushroom head push buttons in bright colors. They&#8217;re listed as IP65 but I&#8217;m a bit skeptical. Time will tell. The buttons are available from <a href="https://amzn.to/3UrI042" target="_blank">Amazon under the Apiele brand name</a>. I selected a green button to turn the lights on and a red button to turn the lights off.</p>
<h3>The Enclosure</h3>
<div id="attachment_5231" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/the-enclosure.jpg"><img class="wp-image-5231 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/the-enclosure-1024x699.jpg" alt="the-enclosure" width="640" height="437" /></a><p class="wp-caption-text">Automation Direct&#8217;s one-hole and two-hole waterproof button enclosures.</p></div>
<p>For an enclosure for the buttons, I used Automation Direct&#8217;s <a href="https://www.automationdirect.com/adc/shopping/catalog/enclosures_-a-_racks/pushbutton_enclosures/sa100sl" target="_blank">SA100SL single-hole</a> and <a href="https://www.automationdirect.com/adc/shopping/catalog/enclosures_-a-_racks/pushbutton_enclosures/sa105-40sl" target="_blank">SA105-40SL double-hole</a> waterproof pushbutton enclosures. These enclosures have pre-drilled 22 mm holes, are IP65 rated, and are relatively inexpensive. These enclosures also have plenty of room for a small circuit board and battery.</p>
<h2>Existing Solutions</h2>
<h3>Indoor-Rated Zigbee and Wi-Fi Smart Buttons / Scene Controllers</h3>
<div id="attachment_5282" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/indoor-scene-controllers.jpg"><img class="wp-image-5282 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/indoor-scene-controllers-1024x682.jpg" alt="indoor-scene-controllers" width="640" height="426" /></a><p class="wp-caption-text">Upper left: Sonoff SNZB-01P Zigbee wireless switch. Lower left: Shelly Button 1 in black. Right: MOES Wireless Smart Scene Switch.</p></div>
<p>If you&#8217;re good with locating the buttons to control the lights indoors or in the garage, there are lots of Zigbee options and at least one Wi-Fi option. My favorite Zigbee buttons are the single-button <a href="https://amzn.to/4b2qWXH" target="_blank">Sonoff SNZB-01P Smart Button</a> and the four-button <a href="https://amzn.to/4b2qWXH" target="_blank">MOES Wireless Smart Scene Switch</a>. These directly integrate with Zigbee2MQTT and a Python script subscribed to their MQTT messages can be used to control other Zigbee, Z-Wave, and Wi-Fi devices on the network.</p>
<p>The only battery-powered Wi-Fi smart button I could find is the <a href="https://amzn.to/4b2qWXH" target="_blank">Shelly Button 1</a>. Like other Wi-Fi devices, it does not require a Zigbee or Z-Wave hub to connect to the network. Like other Shelly devices, it can publish and subscribe to an MQTT broker directly.</p>
<p>The only downside to a battery-powered Wi-Fi button is it takes a few seconds to connect to the Wi-Fi network from its low-power sleep state before it can send the on or off command to the MQTT broker to send to any scripts or lights. This results in a very noticeable delay between pushing the button and the lights changing state.</p>
<p>The obvious solution at this point would be to rip the guts out of the Sonoff button, solder some wires across the button terminals, then connect the wires to the 22 mm pushbuttons and slap everything into the two-button enclosure. Oh wait, there&#8217;s only one button to solder wires to on the Sonoff device and the MOES device with multiple buttons is too big to fit in the waterproof enclosure.</p>
<h3>Hue Light Switch Module</h3>
<div id="attachment_5225" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/hue-switch.jpg"><img class="wp-image-5225 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/hue-switch-1024x649.jpg" alt="hue-switch" width="640" height="406" /></a><p class="wp-caption-text">The Hue Wall Switch Module connected to a single button inside a single-hole waterproof enclosure.</p></div>
<p>After doing more searching, I stumbled on the <a href="https://amzn.to/3WbgYPS" target="_blank"><span id="productTitle" class="a-size-large product-title-word-break">Philips Hue Wall Switch Module</span></a>. This device is almost perfect. It&#8217;s Zigbee and supports two buttons or two toggle switches. If you&#8217;re 100% in on the Hue ecosystem, this is the device for you. Connect it to some buttons or switches, add it to the Hue hub, create some automations to turn some Hue lights on and off and you&#8217;re done.</p>
<p>I&#8217;ve been trying to lessen my dependence on Hue devices, however, since they started requiring an account to use their app. Technically, the Hue Wall Switch Module is supposed to work with Zigbee2MQTT but I was experiencing high battery drain issues caused by the module sending attribute reports way too frequently. I did not experience these issues when using it with the Hue Hub.</p>
<p>If you want to get crafty, you can connect to the Hue Hub&#8217;s streaming API from a Python script, listen for the button pushes, and use those to trigger other devices outside the Hue ecosystem:</p>
<pre>import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import json

HUE_HUB_URL_BASE    = 'https://&lt;YOUR_HUB_IP&gt;:443'
HUE_APPLICATION_KEY = '&lt;YOUR_HUB_APP_KEY&gt;'

#----------------------------------------------------------------------------------------------
# main
#

if __name__ == "__main__":

  # probably a bad idea
  requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

  print ("opening http stream on hue hub")

  # open an http stream an keep listening for pushed updates
  stream_url = HUE_HUB_URL_BASE + '/eventstream/clip/v2'
  stream_headers = {
    "Accept": "text/event-stream", 
    'hue-application-key': HUE_APPLICATION_KEY
  }

  while True:
    try:
      r = requests.get (stream_url, headers=stream_headers, verify=False,
        timeout=20000, stream=True)

      for line in r.iter_lines():
        if line[0:4] == b'data':
          payload = line[6:]
          parsed = json.loads (payload)
          for item in parsed:
            if 'data' in item:
              if 'button' in item['data'][0]:
                event = item['data'][0]['button']['button_report']['event']
                devid = item['data'][0]['id']
                if event == 'initial_press':
    
                  if devid == '&lt;YOUR_FIRST_BUTTON_DEVICE_GUID&gt;':
                    print ('green button pressed')
                    # do something cool here
    
                  elif devid == '&lt;YOUR_SECOND_BUTTON_DEVICE_GUID&gt;':
                    print ('red button pressed')
                    # do something cool here

    # seems to die about once a day, restart  
    except requests.exceptions.ConnectionError:
      print ("requests.exceptions.ConnectionError raised, retrying.")</pre>
<h3>Thread Sensor Tag Module</h3>
<p>At this point, I had narrowed my selection of microcontrollers down to an nRF52840 Zigbee controller from Nordic Semiconductor. A lot of this was based on already having several nRF52840 dev kits on hand from some prior Bluetooth work.</p>
<p>While trying to find this microcontroller in an easy-to-solder and pre-certified RF module, I stumbled upon the <a href="https://www.homesmartmesh.com/microcontrollers/nrf52/thread-sensortag/">Thread Sensor Tag</a>. The Thread Sensor Tag is also on <a href="https://hackaday.io/project/180630-openthread-sensor-tag">hackaday.io</a>.</p>
<p>The Thread Sensor Tag uses an nRF52840 RF module made by Minew Semiconductor. This RF module has all of its pins on castellated edges on the outer periphery of the module and is easy to hand solder without the use of special equipment. The RF modules are available directly from Minew Semiconductor for about $7 each on AliExpress.</p>
<p>The RF module is based on the Nordic Semiconductor nRF52840 microcontroller. The nRF52840 SDK includes the Zypher RTOS, a complete Zigbee stack, and several Zigbee examples. In addition, the nRF52840 uses the popular Visual Studio Code for its development environment. The nRF52840 features extremely low power consumption making it perfect for battery-powered devices. I found my microcontroller and RF module!</p>
<h2>Design Requirements</h2>
<p>The following is a list of requirements for my hardware design:</p>
<ul>
<li>Use a Minew Semiconductor MS88SF2 nRF52840 RF module.</li>
<li>Connections for up to four external push button switches.</li>
<li>Reset button.</li>
<li>User button.</li>
<li>Two LEDs.</li>
<li>Standard ARM 10-pin SWD programmer / debugger connection.</li>
<li>Powered by a large 620 mAh CR2450 coin cell battery.</li>
<li>Small board able to fit in either the single-button or dual-button outdoor enclosures.</li>
</ul>
<h2>Hardware Design</h2>
<div id="attachment_5215" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/four-input-minew-sch.png"><img class="wp-image-5215 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/four-input-minew-sch-1024x764.png" alt="four-input-minew-sch" width="640" height="478" /></a><p class="wp-caption-text">Zigbee button transmitter schematic.</p></div>
<p>The very high integration of the Minew Semiconductor MS88SF2 RF module means there&#8217;s not much to the hardware. It&#8217;s the RF module plus a few buttons, LEDs, passives, and connectors. The push button inputs are held in the high state using the nRF52840&#8217;s internal pullups. The complete schematic is shown above.</p>
<p>Note that the MS88FS2 is available in three different power configurations. For my battery-powered four-input Zigbee transmitter, I&#8217;m using &#8220;Configuration 3: 1.8V-3.6V to VDD.&#8221;</p>
<div id="attachment_5216" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/four-input-minew-pcb.png"><img class="wp-image-5216 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/four-input-minew-pcb-1024x800.png" alt="four-input-minew-pcb" width="640" height="500" /></a><p class="wp-caption-text">Zigbee button transmitter board layout.</p></div>
<p>The board layout is shown above. For best RF performance, the antenna should be hanging in free space and the ground plane kept clear of it. The battery holder solders to the bottom of the board.</p>
<h2>Hardware Bring Up</h2>
<div id="attachment_5217" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/DSC_0100_crop_2000px.jpg"><img class="wp-image-5217 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/DSC_0100_crop_2000px-1024x682.jpg" alt="DSC_0100" width="640" height="426" /></a><p class="wp-caption-text">The front of the assembled Zigbee button transmitter board.</p></div>
<p>The assembled board is shown in the photograph above. The only through-hole component is the five-pin terminal strip to connect the buttons.</p>
<div id="attachment_5218" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/DSC_0102_crop_2000px.jpg"><img class="wp-image-5218 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/DSC_0102_crop_2000px-1024x681.jpg" alt="DSC_0102" width="640" height="426" /></a><p class="wp-caption-text">The back of the assembled Zigbee button transmitter board. Note that the wireless module&#8217;s antenna is hanging in free space and clear of the PCB, ground plane, and battery.</p></div>
<p>The photo above shows the back side of the board. The antenna is free and clear of the board, ground plane, and battery. On the next revision, I&#8217;m going to include the chamfered corner that indicates the (+) terminal of the battery holder on the silkscreen layer.<span style="color: #ff0000;"><br />
</span></p>
<div id="attachment_5329" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/debugging.jpg"><img class="size-large wp-image-5329" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/debugging-1024x682.jpg" alt="For programming and debugging, I used a J-Link Edu Mini." width="640" height="426" /></a><p class="wp-caption-text">For programming and debugging, I used a J-Link Edu Mini.</p></div>
<p>For programming and debugging the nRF52840 from Visual Studio Code, I used nRF Connect Visual Studio Code plugins, version 2.5.1 of the nRF Connect SDK, and a Segger J-Link EDU Mini SWD programmer and debugger.</p>
<p>Programming and debugging can be done while the board is powered from the CR2450 coin cell. For board bring-up and most of the development work though, I used a current limited bench supply and some alligator clips to clip on to the battery holder&#8217;s (+) and (-) terminals.</p>
<h2>Embedded Software Development</h2>
<p>The hardest part of this project was the software development. The learning curve is tremendous and the nRF Zigbee library is not documented very well. I was able to leverage the examples included in the SDK to get started. From there, <span class="p-name vcard-fullname d-block overflow-hidden">Otávio Ribeiro&#8217;s <a href="https://github.com/otaviojr/zigbee_light_sensor" target="_blank">Zigbee Light Sensor</a> code and Jan Gnip&#8217;s <a href="https://github.com/nobodyguy/zigbee_air_quality_monitor_firmware" target="_blank">Zigbee Air Quality Monitor</a> code were also useful, particularly for figuring out how to declare and use the extended identity Zigbee cluster library that would let Zigbee2MQTT identify and interview my module.<br />
</span></p>
<p>Since my Zigbee module is going outdoors, I wanted direct attribute reports of the battery voltage. Unfortunately, in the Zigbee cluster library, battery voltage is not a reportable attribute. It has to be polled. But if the device is asleep for 8 hours a day, good luck polling it. I could have relied on the battery percentage attribute reports only but the raw battery voltage is much more useful.</p>
<p>I eventually was able to kluge together a hack to wake the module from sleep every eight hours, use the ADC to measure VDD, and send an attribute report with the battery level and the battery voltage back to my Zigbee coordinator and Zigbee2MQTT. It&#8217;ll definitely fail any Zigbee compliance testing, but I can at least monitor the battery voltage during the dead of winter.</p>
<p>There&#8217;s a few gotchas around calling things at interrupt time vs on the main Zigbee thread and waking the scheduler from sleep. These are documented pretty well in the comments in my code.</p>
<p>The code uses the ZCL genOnOff cluster to report button presses and releases. Button release reports can be turned off using a #ifdef in main.c. The code uses the three defined commands (OFF, ON, TOGGLE) and five reserved values to report all eight possible button state changes. These are detailed in the table below:</p>
<table width="330">
<tbody>
<tr>
<td width="64">Button</td>
<td width="64">Pin</td>
<td width="101">Press</td>
<td width="101">Release</td>
</tr>
<tr>
<td>0</td>
<td>P0.04</td>
<td>0x00 (Off)</td>
<td>0x04 (Reserved)</td>
</tr>
<tr>
<td>1</td>
<td>P0.06</td>
<td>0x01 (On)</td>
<td>0x05 (Reserved)</td>
</tr>
<tr>
<td>2</td>
<td>P0.08</td>
<td>0x02 (Toggle)</td>
<td>0x06 (Reserved)</td>
</tr>
<tr>
<td>3</td>
<td>P0.12</td>
<td>0x03 (Reserved)</td>
<td>0x07 (Reserved)</td>
</tr>
</tbody>
</table>
<p>Again, it&#8217;s not going to win any rewards for Zigbee compliance but it works!</p>
<h2>Power Consumption</h2>
<div id="attachment_5331" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/measuring-power.jpg"><img class="size-large wp-image-5331" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/measuring-power-1024x682.jpg" alt="Measuring device power using a Nordic Semiconductor PPK2 power profiler kit." width="640" height="426" /></a><p class="wp-caption-text">Measuring device power using a Nordic Semiconductor PPK2 power profiler kit.</p></div>
<p>If you&#8217;re serious about battery-powered design, get the Nordic Semiconductor PPK2 Power Profiler Kit! Just do it. The PPK2 supplies power and measures the connected device&#8217;s current consumption. The instantaneous current measurements are accumulated over time to derive an average current. If you let the PPK2 accumulate data over night, you can get an accurate average current value that encompasses all your device&#8217;s different modes of operation.</p>
<p>With the PPK2, you can also make changes to the software and see the impacts of those changes on current consumption. For example, enabling the UART for serial printf-style debugging increased the current consumption from a few μA to 600 μA. You can see that waking the microcontroller from sleep to poll for button presses consumes almost an entire mA versus a few μA sleeping and using the wake on interrupt feature. You can also see that Zigbee transmissions consume about 15 to 20 mA during the brief time while the radio is transmitting.</p>
<div id="attachment_5333" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/power-no-macs.png"><img class="size-large wp-image-5333" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/power-no-macs-1024x759.png" alt="Idle current consumption is pretty good!" width="640" height="474" /></a><p class="wp-caption-text">Idle current consumption is pretty good!</p></div>
<p>The screenshot above shows the average current consumption over a two minute period. 1.77 μA is pretty good! This, of course, does not show any Zigbee transmissions from periodic attribute reports or pressing buttons. If you run the PPK2 overnight, the average current consumption with a few button presses and attribute reports is about 3 μA.</p>
<p>A brand new CR2450 coin cell has 620 mAh of energy. 620 mAh divided by 3 μA is about 200,000 hours of operation. This is, in theory, about 23.5 years of operation from a single battery. In reality, it&#8217;ll never last that long but I&#8217;m really curious to see how long the battery does last!</p>
<p>You can also do this calculation using Coulombs. 620 mAh is 2232 C of charge. 3 μA is 3 μC/s. 2232/3e-6 is 744e6 seconds of operation. Divide that by 3600 seconds in an hour, 24 hours in a day, and 365 days in a year for 23.5 years!</p>
<h2>Installation</h2>
<div id="attachment_5220" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/vlcsnap-2024-04-02-08h06m39s192.jpg"><img class="wp-image-5220 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/vlcsnap-2024-04-02-08h06m39s192-1024x576.jpg" alt="vlcsnap-2024-04-02-08h06m39s192" width="640" height="360" /></a><p class="wp-caption-text">Getting ready to install the hardware.</p></div>
<p>The photo above shows one of the completed four-input modules, programmed and powered and ready to go.</p>
<div id="attachment_5221" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/vlcsnap-2024-04-02-08h06m49s599.jpg"><img class="wp-image-5221 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/vlcsnap-2024-04-02-08h06m49s599-1024x576.jpg" alt="vlcsnap-2024-04-02-08h06m49s599" width="640" height="360" /></a><p class="wp-caption-text">Buttons mounted to enclosure, board secured in place with a bit of tape, and the buttons connected to the board.</p></div>
<p>I secured the module to the back of the box using a short piece of 3M Command adhesive, installed the switches in the box, and wired everything up.</p>
<div id="attachment_5222" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/vlcsnap-2024-04-02-08h07m07s284.jpg"><img class="wp-image-5222 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/vlcsnap-2024-04-02-08h07m07s284-1024x576.jpg" alt="vlcsnap-2024-04-02-08h07m07s284" width="640" height="360" /></a><p class="wp-caption-text">Front view of the buttons and enclosure.</p></div>
<p>The completed buttons and transmitter in their box. Since the screws for mounting the box are under the cover, hold off on tightening the cover until the box is mounted outside.</p>
<div id="attachment_5223" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/vlcsnap-2024-04-02-08h08m45s442.jpg"><img class="wp-image-5223 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/vlcsnap-2024-04-02-08h08m45s442-1024x576.jpg" alt="vlcsnap-2024-04-02-08h08m45s442" width="640" height="360" /></a><p class="wp-caption-text">Finishing up the installation of the buttons.</p></div>
<p>I mounted the box to a fence post just inside my back gate and secured the cover.<span style="color: #ff0000;"><br />
</span></p>
<h2>Linking Everything Together with Python, Zigbee2MQTT, and TinyTuya</h2>
<div id="attachment_5289" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/04/button-slap.jpg"><img class="wp-image-5289 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/04/button-slap-1024x576.jpg" alt="button-slap" width="640" height="360" /></a><p class="wp-caption-text">Getting ready to mash that big green power button!</p></div>
<p>Now to make the buttons do something when they&#8217;re pressed! Integrating the buttons into Zigbee2MQTT required the creation of a custom converter. The documentation for creating custom converters is extremely lacking but the code below seems to work.</p>
<pre>const {batteryPercentage,identify} = require('zigbee-herdsman-converters/lib/modernExtend');
const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const extend = require('zigbee-herdsman-converters/lib/extend');
const ota = require('zigbee-herdsman-converters/lib/ota');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const {} = require('zigbee-herdsman-converters/lib/tuya');
const utils = require('zigbee-herdsman-converters/lib/utils');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const e = exposes.presets;
const ea = exposes.access;

const fromZigbee_CustomActions = {
    cluster: 'genOnOff',
    type: 'raw',
    convert: (model, msg, publish, options, meta) =&gt; {
        if ((0, utils.hasAlreadyProcessedMessage)(msg, model, msg.data[1]))
            return;

        return { action: `cmd_${msg.data[2]}`};
    },
};

const definition = {
    zigbeeModel: ['four-input'],
    model: 'four-input',
    vendor: 'bikerglen.com',
    description: 'four contact closure input device',
    extend: [batteryPercentage(),identify()],
    fromZigbee: [fz.command_off, fz.command_on, fz.command_toggle, fromZigbee_CustomActions, fz.battery],
    toZigbee: [tz.battery_voltage], // permits reading voltage attribute over mqtt
    exposes: [e.battery_voltage()],
};

module.exports = definition;
</pre>
<p>I called this file four-input.js and placed it in /top/zigbee2mqtt/data. The configuration.yaml file in the same directory needs these two lines appended to the end of the file to recognize the custom converter:</p>
<pre>external_converters:
  - four-input.js</pre>
<p>After changing the configuration.yaml file, restart zigbee2mqtt and it should import the custom converter. At this point, the device can be added to Zigbee2MQTT by clicking join all and pressing the reset button. Once joined, the red LED on the transmitter will go out and Zigbee2MQTT should successfully interview the device. Sometimes it takes a few tries for everything to proceed smoothly.</p>
<p>If you connect MQTT Explorer to the same MQTT broker as Zigbee2MQTT and press the buttons, you should now the actions shown in the table below for each button press or button release (if releases were compiled into the binary):</p>
<table width="348">
<tbody>
<tr>
<td width="64">Button</td>
<td width="64">Pin</td>
<td width="110">Press</td>
<td width="110">Release</td>
</tr>
<tr>
<td>0</td>
<td>P0.04</td>
<td>&#8220;action&#8221;:&#8221;off&#8221;</td>
<td>&#8220;action&#8221;:&#8221;cmd_4&#8243;</td>
</tr>
<tr>
<td>1</td>
<td>P0.06</td>
<td>&#8220;action&#8221;:&#8221;on&#8221;</td>
<td>&#8220;action&#8221;:&#8221;cmd_5&#8243;</td>
</tr>
<tr>
<td>2</td>
<td>P0.08</td>
<td>&#8220;action&#8221;:&#8221;toggle&#8221;</td>
<td>&#8220;action&#8221;:&#8221;cmd_6&#8243;</td>
</tr>
<tr>
<td>3</td>
<td>P0.12</td>
<td>&#8220;action&#8221;:&#8221;cmd_3&#8243;</td>
<td>&#8220;action&#8221;:&#8221;cmd_7&#8243;</td>
</tr>
</tbody>
</table>
<p>Finally, the Python code below can be used to connect to the MQTT broker, subscribe to the button press/release messages, and toggle the pathway lights on or off using TinyTuya. The following items in the code need to be set for your specific network and device:</p>
<ul>
<li>The MQTT broker&#8217;s IP address (192.168.X.X below).</li>
<li>The pathway light&#8217;s ID, IP address, and local API key from the TinyTuya poll of the device on your network (&lt;TT_LOCAL_ID&gt;, 192.68.X.X, and &lt;TT_LOCAL_KEY&gt; below).</li>
<li>The MAC address of the Zigbee module from Zigbee2MQTT (0x&lt;ZIGBEE_DEVICE_MAC&gt; in two places below).<span style="color: #ff0000;"><br />
</span></li>
</ul>
<pre>import concurrent.futures
import paho.mqtt.client as mqtt
import json
import tinytuya

MQTT_HOST           = '192.168.X.X'
MQTT_PORT           = 1883

ttdevs={}
ttdevs['bike_stand_floods'] = ("&lt;TT_LOCAL_ID&gt;", "192.168.X.X", "&lt;TT_LOCAL_KEY&gt;")

thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=32)


#----------------------------------------------------------------------------------------------
# mqtt on_connect
#

def on_connect (self, userdata, flags, rc, trash):
  print ("Connected with result code " + str(rc))
  # Minew nRF52840 module
  result = self.subscribe ("zigbee2mqtt/0x&lt;ZIGBEE_DEVICE_MAC&gt;", 0)


#----------------------------------------------------------------------------------------------
# mqtt on_message
#

def on_message (self, userdata, msg):
  print (msg.topic + " " + str (msg.payload))

  # Minew nRF52840 module
  if msg.topic == "zigbee2mqtt/0x&lt;ZIGBEE_DEVICE_MAC&gt;":
    if msg.payload:
      parsed = json.loads (msg.payload);

      if 'action' in parsed:

        if (parsed['action'] == 'on'):
          thread_pool.submit (tuya_floods, 'bike_stand_floods', True)
        elif (parsed['action'] == 'off'):
          thread_pool.submit (tuya_floods, 'bike_stand_floods', False)


#----------------------------------------------------------------------------------------------
# turn tuya floods on or off
#

def tuya_floods (name, on):
  print ("sending " + ("on" if (on) else "off") + " request to " + name + "...")

  d = tinytuya.BulbDevice(ttdevs[name][0], ttdevs[name][1], ttdevs[name][2]) 
  d.set_version(3.3)
  d.set_socketPersistent(False)

  if on:
    d.turn_on()
    d.set_white()
  else:
    d.turn_off()

  print (("on" if (on) else "off") + " request to " + name + " done.")

  
#----------------------------------------------------------------------------------------------
# main
#

if __name__ == "__main__":

  client = mqtt.Client (client_id="", userdata=None, protocol=mqtt.MQTTv5)
  client.on_connect = on_connect
  client.on_message = on_message
  client.connect (host=MQTT_HOST, port=MQTT_PORT)

  client.loop_forever ()</pre>
<h2>The Final Result</h2>
<div class="wp-caption alignnone" style="width: 650px;">
<p style="margin-bottom: 0px; margin-top: 0px;"><iframe title="YouTube video player" src="https://www.youtube.com/embed/HsqLvxWZbr4" width="630" height="355" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>
<p class="wp-caption-text">Here&#8217;s a video of of this project. It&#8217;s a minute and a half long. If you want to skip to the Zigbee stuff, fast forward to about 40 seconds.</p>
</div>
<p>The video embedded above covers the entire project from motivation, through the use and installation of the lights and Zigbee buttons, and the final result. If you want to skip to the Zigbee stuff, fast forward to about 40 seconds.</p>
<h2>Design Files</h2>
<p>The design files are available on Github in my <a href="https://github.com/bikerglen/homebrew_zigbee_devices/tree/main/nrf52840-four-input" target="_blank">Zigbee homebrew four-input repository</a>.</p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs.</em></p>
]]></content:encoded>
			<wfw:commentRss>https://bikerglen.com/blog/building-battery-powered-zigbee-buttons/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Controlling XMCOSY+ String Lights with WLED and TinyTuya</title>
		<link>https://bikerglen.com/blog/xmcosy-string-lights-with-wled-and-tinytuya/</link>
		<comments>https://bikerglen.com/blog/xmcosy-string-lights-with-wled-and-tinytuya/#comments</comments>
		<pubDate>Tue, 16 Jan 2024 04:28:44 +0000</pubDate>
		<dc:creator><![CDATA[Glen]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://bikerglen.com/blog/?p=5105</guid>
		<description><![CDATA[Two years ago, I hung a set of generic G50 RGB globe lights in the backyard. They weren&#8217;t exactly waterproof and the individual lights became water logged and started acting erratically. This year, I replaced them with two sets of &#8230; <a href="https://bikerglen.com/blog/xmcosy-string-lights-with-wled-and-tinytuya/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<div id="attachment_5189" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240117_004200321_2k.jpg"><img class="size-large wp-image-5189" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240117_004200321_2k-1024x549.jpg" alt="Two XMCOSY+ RGBW light strings hanging in my backyard over my snow-covered lawn." width="640" height="343" /></a><p class="wp-caption-text">Two XMCOSY+ RGBW light strings hanging in my backyard over my snow-covered lawn.</p></div>
<p>Two years ago, I hung a set of generic G50 RGB globe lights in the backyard. They weren&#8217;t exactly waterproof and the individual lights became water logged and started acting erratically. This year, I replaced them with two sets of <a href="https://amzn.to/47vNZYU" target="_blank">XMCOSY+ RGBW Patio String Lights</a>. The new XMCOSY lights look great and I like that there&#8217;s real white LEDs in them so they can provide both decoration and functional illumination for my backyard. Read on to find out more about the installation, the lights, controlling them with WLED, and, new to me, controlling them locally with TinyTuya.</p>
<p><span id="more-5105"></span></p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs.</em></p>
<h2>Mechanics of the Install</h2>
<div id="attachment_5112" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240115_214635422_2k.jpg"><img class="size-large wp-image-5112" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240115_214635422_2k-1024x576.jpg" alt="The two strings of light radiate outward from a corner of my house to the fence line." width="640" height="360" /></a><p class="wp-caption-text">The two strings of light radiate outward from a corner of my house to the fence line.</p></div>
<p>The original lights were hung using a <span id="productTitle" class="a-size-large product-title-word-break"><a href="https://amzn.to/48wtlJx" target="_blank">Newleray String Light Hanging Kit</a></span>. This kit contains everything needed to suspend a set of string lights between two points including brackets, carabiner clips, clamps, turnbuckles, screws, and nylon-coated cable. The diagram below shows a typical use of the kit.</p>
<div id="attachment_5135" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/hanging-diagram.jpg"><img class="size-large wp-image-5135" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/hanging-diagram-1024x518.jpg" alt="Diagram demonstrating how to use the contents of the string light hanging kit from the manufacturer's description." width="640" height="324" /></a><p class="wp-caption-text">Diagram demonstrating how to use the contents of the string light hanging kit from the manufacturer&#8217;s description.</p></div>
<p>Two years ago, I hung two runs of the nylon-coated cable from a corner of the house to the fence at a 60<span id="degree-symbol" class="font3">°</span> angle and suspended the lights from the cable using zip ties. This arrangement takes the weight of the lights off their power conductors. It also made it fairly trivial to remove the old lights by cutting their zip ties and to hang the new lights in their place with new zip ties.</p>
<div id="attachment_5114" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240115_221225235_2k.jpg"><img class="size-large wp-image-5114" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240115_221225235_2k-1024x576.jpg" alt="Clamp and carabiner on the fence side." width="640" height="360" /></a><p class="wp-caption-text">Clamp and carabiner on the fence side with the new string lights attached.</p></div>
<p>On the fence side, I screwed a small board into the side of a fence post and attached the fixed end of the cable about 8 feet off the ground using a clamp, carabiner clip, and bracket as shown in the photo above.</p>
<div id="attachment_5115" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240115_221242994_2k.jpg"><img class="size-large wp-image-5115" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240115_221242994_2k-1024x576.jpg" alt="Clamp and turnbuckle on the house side." width="640" height="360" /></a><p class="wp-caption-text">Clamp and turnbuckle on the house side with the new string lights attached. The clamp is hidden behind the bulb.</p></div>
<p>On the house side, I attached the other end of the cable to the house using a cable clamp, turnbuckle, and bracket as shown in the photo above. I then repeated this setup from the house to another segment of the fence. Each bulb was hung with a zip tie from the nylon-coated wire.</p>
<div id="attachment_5117" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240115_221314399_2k.jpg"><img class="size-large wp-image-5117" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240115_221314399_2k-1024x576.jpg" alt="hanging around" width="640" height="360" /></a><p class="wp-caption-text">The lights hang from the nylon-coated wire using zip ties. I coiled up the excess cable between each light and zip tied those in place too.</p></div>
<p>The strings I purchased are advertised as 49 feet long with 15 RGBW S14 bulbs. The 49 feet includes the power supply leads, lead in to the first lamp, lead out from the last lamp, and the actual distance between the lamps. The spacing between each bulb is about 80 cm or 2.62 feet so there&#8217;s about 11.2 m or 36.7 feet of cable between the first bulb and the last bulb.</p>
<p>Since my runs are about 30 feet each and I wanted to use all 15 bulbs on each run, I had some excess wire between each lamp. I made neat coils between each lamp with the excess wire and zip tied them in place to the nylon-coated hanging wire. It&#8217;s been pretty cold outside so the wire isn&#8217;t very flexible. I&#8217;ll revisit the installation in the late spring or summer when the wire is more flexible and straighten everything out a better.</p>
<h2>Detailed Description</h2>
<div id="attachment_5122" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/bulb2.jpg"><img class="size-large wp-image-5122" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/bulb2-1024x682.jpg" alt="1 of the 15 bulbs on each strand." width="640" height="426" /></a><p class="wp-caption-text">1 of the 15 bulbs on each strand.</p></div>
<p>Each bulb is suspended by a short length of wire from a T in the main power cable. The T has a small loop at the top to hang it from a hook or zip tie. The bulbs themselves are clear plastic S14-style bulbs. Each bulb is about 1.75&#8243; in diameter and about 3.25&#8243; long including the black plastic base.</p>
<div id="attachment_5124" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240115_224457139_2k.jpg"><img class="size-large wp-image-5124" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240115_224457139_2k-1024x682.jpg" alt="Clear outer plastic shell and inner acrylic diffuser." width="640" height="426" /></a><p class="wp-caption-text">Clear outer plastic shell and inner acrylic diffuser.</p></div>
<p>The clear plastic outer shell can be unscrewed to examine the inner acrylic diffuser and circuit board. The LEDs shine from above on to the cone at the bottom of the diffuser and the cone reflects the light outward. This makes the light glow similar to the filament in an incandescent bulb. The ability to remove the outer shell will also likely be useful for draining any water ingress during spring monsoon season. We shall see.</p>
<div id="attachment_5126" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/DSC_3665_2k.jpg"><img class="wp-image-5126 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/DSC_3665_2k-1024x682.jpg" alt="Each lamp contains 3 RGB LEDs and 1 warm white LED." width="640" height="426" /></a><p class="wp-caption-text">Each lamp contains 3 RGB LEDs and 1 warm white LED. The legs on the diffuser fit into the cutouts on the sides of the circuit board.</p></div>
<p>The circuit board has three small RGB LEDs and one large warm white LED. On the board are soldered connections for power, ground, data in, and data out. Since the boards are soldered in place to their wires, a repair / swap of a circuit board between lamps is likely very difficult. I can&#8217;t see a controller IC but I suspect there&#8217;s a WS2814 or similar on the back side of the circuit board since each light can be set to a different color independent of the others.</p>
<div id="attachment_5174" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/power-data-supply-wb-corrected.jpg"><img class="size-large wp-image-5174" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/power-data-supply-wb-corrected-1024x684.jpg" alt="The power supply and wireless controller are a single integrated unit. There are two touch sensitive buttons on top of the supply." width="640" height="428" /></a><p class="wp-caption-text">The power supply and wireless controller are a single integrated unit. There are two touch sensitive buttons on top of the supply.</p></div>
<p>The power data supply is shown in the photo above. The AC line cord is on the left and the power/data cable to the lights is on the right. The power/data cable connects to the first light in the string using a waterproof 3-pin connector. The power supply is rated for 12 V at 3 A or 36 W.</p>
<div id="attachment_5177" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240116_033256550.jpg"><img class="size-large wp-image-5177" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240116_033256550-1024x576.jpg" alt="fff" width="640" height="360" /></a><p class="wp-caption-text">The female connector on the cable from the power supply.</p></div>
<p>The power data supply connects to the lights via a 3 pin round connector. The connector has both a knotch and a flat for polarity.</p>
<h2>Tuya Smart Life App</h2>
<div id="attachment_5179" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/smart-life-app-screenshots.jpg"><img class="size-large wp-image-5179" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/smart-life-app-screenshots-1024x740.jpg" alt="Three different screens from the Tuya Smart Life app." width="640" height="463" /></a><p class="wp-caption-text">Three different screens from the Tuya Smart Life app.</p></div>
<p>These string lights are based on a Tuya IoT processor and as such can be controlled via the Tuya Smart Life app. Three screens from the app are shown in the image above. The leftmost screen shows the main screen of the app. From this screen, the lights can be set to various preprogrammed scenes. Some scenes have static colors while others have dynamic effects.</p>
<p>The middle screen is the DIY section of the app where you can construct new scenes and program all the lamps to be different colors. In this screen, I&#8217;ve set the first bulb to red, the second to green, the third to blue, etc., and repeated this pattern to the end of the string. When I hit the preview button, the lights will change to the set colors. From there, the set pattern can be added to a scene.</p>
<p>The last screen is the settings screen where you set the number of bulbs in the light string. It defaults to 30 even though each string only has 15 bulbs. If you connect two of the strings end-to-end and some of the bulbs don&#8217;t change colors, make sure this is set to 30 on this screen.</p>
<p>All in all, I think the app is adequate for most people&#8217;s needs. I intend to use the Smart Life app most of the time with these lights but was interested in being able to control them from crontab and a Python script. This can be done either officially through the Tuya IoT cloud developer API&#8217;s, or unofficially, locally using the TinyTuya Python library. More on that near the end of this post.</p>
<h2>FCC Info</h2>
<div id="attachment_5175" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/power-data-supply-label.jpg"><img class="size-large wp-image-5175" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/power-data-supply-label-1024x684.jpg" alt="Manufacturer's label on the back of the power supply / controller." width="640" height="428" /></a><p class="wp-caption-text">Manufacturer&#8217;s label on the back of the power supply / controller.</p></div>
<p>The FCC ID of the product is 2AI5T-DMD-045-W3 and the light is manufactured by the Shenzhen Bling Lighting Technologies Co., Ltd.</p>
<div id="attachment_5137" style="width: 1029px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/fcc-external-photos.jpg"><img class="size-full wp-image-5137" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/fcc-external-photos.jpg" alt="External photo of the light string from the FCC documentation." width="1019" height="741" /></a><p class="wp-caption-text">External photo of the light string from the manufacturer&#8217;s FCC test data.</p></div>
<p>I headed over to <a href="https://fccid.io/2AI5T-DMD-045-W3" target="_blank">fccid.io and looked up the FCC ID</a>. Yep, that&#8217;s an external photo of the light string and its power supply / controller.</p>
<div id="attachment_5138" style="width: 1024px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/fcc-internal-photos.jpg"><img class="wp-image-5138 size-full" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/fcc-internal-photos.jpg" alt="internal photo" width="1014" height="758" /></a><p class="wp-caption-text">internal photo of the light string&#8217;s power supply and controller from the manufacturer&#8217;s FCC test data.</p></div>
<p>Next I opened up the PDF with the internal photos. On the other side of the board is a typical switching power supply. On this side of the board, you can see the wireless module, the two touch sensitive buttons, and, most impressively, the very nice isolation between the high voltage and low voltage ends of the PCB! I suspect the wireless module is a C-Chip CC8000 but can&#8217;t make out enough detail to confirm.</p>
<h2>Protocol Information</h2>
<div id="attachment_5140" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/3-pin-pinout-12v.drawio.png"><img class="size-large wp-image-5140" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/3-pin-pinout-12v.drawio-1024x544.png" alt="Pinouts drawn looking into the connectors." width="640" height="340" /></a><p class="wp-caption-text">Pinouts drawn looking into the connectors.</p></div>
<p>The pinout of the connectors is shown above. On the left is the pinout viewed looking into the female connector on the controller cable. On the right is the pinout viewed looking into the male connector on the light cable.</p>
<div id="attachment_5145" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/scope_5.png"><img class="size-full wp-image-5145" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/scope_5.png" alt="5" width="800" height="632" /></a><p class="wp-caption-text">In every pair of transmitted data bits, the first bit is slightly shorter than the second bit.</p></div>
<p>I hooked the ground and data pins on the power data supply cable to an oscilloscope. The waveform was the WS2811 protocol at 800 kHz and using 5 V logic levels. Each light had 32 bits of control information. The transmission order was 8 bits of red, 8 bits of green, 8 bits of blue, and 8 bits of warm white.</p>
<p>I did notice that the first bit in every pair of transmitted data bits was slightly shorter than the second bit. You can see this between the X1 and X2 cursors in the scope capture above. The first bit of every pair is about 1.2 μs and the second bit is 1.3 μs for an average bit time of 1.25 μs.</p>
<div id="attachment_5146" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/scope_1.png"><img class="size-full wp-image-5146" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/scope_1.png" alt="1" width="800" height="632" /></a><p class="wp-caption-text">Transmission of a zero bit.</p></div>
<p>The scope screenshot above shows the transmission of a zero bit. The high time is 266 ns which is within the WS2814 datasheet&#8217;s spec of 220 ns to 380 ns. The low time is 934 ns which is also within the WS2814 datasheet&#8217;s spec of 580 ns to 1000 ns.</p>
<div id="attachment_5147" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/scope_2.png"><img class="size-full wp-image-5147" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/scope_2.png" alt="2" width="800" height="632" /></a><p class="wp-caption-text">Transmission of a one bit. That high time is a bit sus.</p></div>
<p>The scope screenshot above shows the transmission of a one bit. The high time is 572 ns which is just shy of the WS2814 datasheet&#8217;s spec of 580 ns to 1000 ns. The low time is 628 ns which is within the WS2814 datasheet&#8217;s spec of 580 ns to 1000 ns. Despite the high bit time being a bit short, it still seems to work fine. It could be a different chip than a WS2814 too.</p>
<div id="attachment_5148" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/scope_4.png"><img class="size-full wp-image-5148" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/scope_4.png" alt="4" width="800" height="632" /></a><p class="wp-caption-text">The last few bits.</p></div>
<p>The total time to send a frame of pixel data is 1.24368 ms with 30 lights enabled in the app. This corresponds to 31 lights of 4 channels each with 8 bits per channel. That&#8217;s one extra &#8220;dummy&#8221; light that&#8217;s transmitted in every frame.</p>
<div id="attachment_5149" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/warm-white.png"><img class="size-full wp-image-5149" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/warm-white.png" alt="warm white" width="800" height="632" /></a><p class="wp-caption-text">Warm white as selected from the main scene screen in the app.</p></div>
<p>While I had the scope out, I was curious what RGBW levels were used for warm white and cool white. I selected each of these colors of white light using the main scene screen in the app, captured the transmitted RGBW levels on the scope, and annotated the captures. Warm white is shown in the capture above. This is an RGBW value of (0x7F, 0x32, 0x00, 0xB2). That&#8217;s mid red, a bit of green, no blue, and a lot of white.</p>
<div id="attachment_5150" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/cool-white.png"><img class="wp-image-5150 size-full" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/cool-white.png" alt="cool white" width="800" height="632" /></a><p class="wp-caption-text">Cool white as selected from the main scene screen in the app.</p></div>
<p>And cool white is shown in the capture above. This is an RGBW value of (0x64, 0x64, 0x64, 0x32). That&#8217;s equal parts red, green, and blue and half white. To make warm white, the controller is mixing the warm white LED with some red and green. To make cool white, the controller is mixing a bit of warm white into a brighter composite white made from the red, green, and blue LEDs.</p>
<h2>Controlling with WLED / FastLED</h2>
<div id="attachment_5194" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240121_181302934_2k.jpg"><img class="size-large wp-image-5194" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/PXL_20240121_181302934_2k-1024x576.jpg" alt="I used a 12 V, 3 A desktop power supply and an Athom Tech WLED controller to test the light strings with WLED." width="640" height="360" /></a><p class="wp-caption-text">Preparing to test the light string with a 12 V, 3 A desktop power supply and an Athom Tech WLED controller.</p></div>
<p><del>I haven&#8217;t tried it because it&#8217;s been -11<span id="degree-symbol" class="font3">°</span>F outside all weekend, but these lights should be 100% usable through WLED by connecting them to your favorite 12 V-capable WLED controller hardware. Both the bulit-in effects and E1.31/Art-Net/DDP streaming should work.</del></p>
<p><strong>Update:</strong> The temperature is almost 60<span id="degree-symbol" class="font3">°</span>F warmer this weekend than last weekend and I was able to go outside and test one of the light strings with <a href="https://github.com/Aircoookie/WLED" target="_blank">WLED</a> running on an <a href="https://www.athom.tech/blank-1/wled-esp32-music-addressable-led-strip-controller" target="_blank">Athom Tech WLED controller</a>. The Athom controller uses an ESP32 and comes preloaded with a <a href="https://github.com/athom-tech/Sound-Reactive-WLED" target="_blank">custom version of WLED</a> that works with their IR and RF remotes. I did not try an OTA update but the firmware can be updated or replaced by snapping the back cover off and connecting a 3.3V USB-to-serial cable to the serial port pads inside.</p>
<div id="attachment_5196" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2024/01/Screenshot_20240121-114249.png"><img class="size-large wp-image-5196" src="https://bikerglen.com/wp/wp-content/uploads/2024/01/Screenshot_20240121-114249-1024x916.png" alt="WLED LED configuration for use with one string of 15 lights." width="640" height="573" /></a><p class="wp-caption-text">WLED LED configuration for use with one string of 15 lights.</p></div>
<p>The LED settings I used with a single string of 15 lights are shown in the snippet above from WLED&#8217;s LED Preferences screen. I set the LED type to SK6812 and the color order to RGB. With a string of 15 lights, setting the number of LEDs to 15 worked. No extra dummy LED was needed after the 15 real LEDs. To use WLED and the light string with E1.31, Art-Net, or DDP streaming, set the data type to Multi RGBW in the Sync Interfaces screen. All the WLED effects work 100% as expected with these light strings.</p>
<h2>Controlling with TinyTuya</h2>
<p>Describing how to get started with TinyTuya is beyond the scope of this blog post. Fortunately, the <a href="https://github.com/jasonacox/tinytuya/tree/master" target="_blank">documentation and getting started guide</a> for the library is quite good and the <a href="https://github.com/jasonacox/tinytuya/tree/master/examples">examples</a> and <a href="https://github.com/jasonacox/tinytuya/issues" target="_blank">issues</a> pages cover most of the missing details. What I did find missing though was how to control a strip or string of multiple bulbs that all can be set to different colors independently. This information is likely specific to the XMCOSY string lights but I hope that it&#8217;s applicable and useful to people with other sets of strip or string lights too.</p>
<p>I went through the basic setup process for TinyTuya which required the following steps:</p>
<ol>
<li>Install tinytuya using pip.</li>
<li>Create a Smart Life app account using the Smart Life app.</li>
<li>Add the string lights to the Smart Life app and verify the app controls the lights.</li>
<li>Create a Tuya IoT account.</li>
<li>Link the Smart Life app account to the Tuya IoT account by scanning a QR code from the IoT account in the Smart Life app.</li>
<li>Import my devices from the Smart Life app into the Tuya IoT account.</li>
<li>Run tinytuya scan to get the local IP addresses of the string lights.</li>
<li>Run tinytuya wizard with the IoT account keys to produce a json file with the device IDs, local device keys, and device versions that I&#8217;d need to issue local commands to my devices.</li>
</ol>
<p>After performing all those steps, I could run the provided examples and had basic control over the string lights. It sounds like a complicated process but it&#8217;s <em>much</em> easier than setting up a Microsoft Azure or Amazon AWS account and doesn&#8217;t require a credit card. It took about an hour from start to having working Python scripts controlling the lights in the backyard.</p>
<p>The basics worked well enough but what I really wanted to do was set the colors of all the bulbs separately. Googling revealed <a href="https://github.com/jasonacox/tinytuya/issues/61">one issue</a> that pertained to controlling a strip of LEDs but the issue&#8217;s discussion thread ended without really reaching a resolution. I think. The thread did contain details on monitoring the traffic between the phone app and the string lights using the monitor.py script in the examples directory. I ran that script then used the diy mode in the app to set all the lamps red then green then blue. I received three messages in the monitor.py example. When I set the all the lamps to red, I received the message:</p>
<pre>Received Payload: {'dps': {'102': 'AP8AAACAAQABAABkZAAAAQAAZGQAAAEAAGRkAAABAABkZAAAAQAAZGQAAAEAA
GRkAAABAABkZAAAAQAAZGQAAAEAAGRkAAABAABkZAAAAQAAZGQAAAEAAGRkAAABAABkZAAAAQAAZGQAAAEAAGRkAAABAABkZ
AAAAQAAZGQAAAEAAGRkAAABAABkZAAAAQAAZGQAAAEAAGRkAAABAABkZAAAAQAAZGQAAAEAAGRkAAABAABkZAAAAQAAZGQAA
AEAAGRkAAABAABkZAAAAQAAZGQAAAEAAGRkAAA='}, 't': 1705261231}</pre>
<p>When I set the all the lamps to green, I received the message:</p>
<pre>Received Payload: {'dps': {'102': 'AP8AAACAAQABAHhkZAAAAQB4ZGQAAAEAeGRkAAABAHhkZAAAAQB4ZGQAAAEAe
GRkAAABAHhkZAAAAQB4ZGQAAAEAeGRkAAABAHhkZAAAAQB4ZGQAAAEAeGRkAAABAHhkZAAAAQB4ZGQAAAEAeGRkAAABAHhkZ
AAAAQB4ZGQAAAEAeGRkAAABAHhkZAAAAQB4ZGQAAAEAeGRkAAABAHhkZAAAAQB4ZGQAAAEAeGRkAAABAHhkZAAAAQB4ZGQAA
AEAeGRkAAABAHhkZAAAAQB4ZGQAAAEAeGRkAAA='}, 't': 1705261260}</pre>
<p>When I set the all the lamps to blue, I received the message:</p>
<pre>Received Payload: {'dps': {'102': 'AP8AAACAAQABAPBkZAAAAQDwZGQAAAEA8GRkAAABAPBkZAAAAQDwZGQAAAEA8
GRkAAABAPBkZAAAAQDwZGQAAAEA8GRkAAABAPBkZAAAAQDwZGQAAAEA8GRkAAABAPBkZAAAAQDwZGQAAAEA8GRkAAABAPBkZ
AAAAQDwZGQAAAEA8GRkAAABAPBkZAAAAQDwZGQAAAEA8GRkAAABAPBkZAAAAQDwZGQAAAEA8GRkAAABAPBkZAAAAQDwZGQAA
AEA8GRkAAABAPBkZAAAAQDwZGQAAAEA8GRkAAA='}, 't': 1705261278}</pre>
<p>The equals at the end made me think these were base 64 encoded strings so I ran them through a base64 decoder and dumped them as hex strings (one decoded string per line, truncated):</p>
<pre>00 ff 00 00 00 80 01 00 01 00 00 64 64 00 00 01 00 00 64 64 00 00 01 00 00 64 64 00 00 ...
00 ff 00 00 00 80 01 00 01 00 78 64 64 00 00 01 00 78 64 64 00 00 01 00 78 64 64 00 00 ...
00 ff 00 00 00 80 01 00 01 00 f0 64 64 00 00 01 00 f0 64 64 00 00 01 00 f0 64 64 00 00 ...</pre>
<p>I noticed a repeating pattern of 7 bytes after the first 8 bytes. And there were 30 of this repeating pattern of 7 bytes! And the app was set to 30 lights! Reformatting the red, green, and blue decoded strings yielded:</p>
<pre>00 ff 00 00 00 80 01 00  01 00 00 64 64 00 00 
                         01 00 00 64 64 00 00 
                         01 00 00 64 64 00 00 ...

00 ff 00 00 00 80 01 00  01 00 78 64 64 00 00 
                         01 00 78 64 64 00 00 
                         01 00 78 64 64 00 00 ...

00 ff 00 00 00 80 01 00  01 00 f0 64 64 00 00 
                         01 00 f0 64 64 00 00 
                         01 00 f0 64 64 00 00 ...</pre>
<p>That&#8217;s 8 bytes of header followed by 30 sets of 7 bytes. The third column was the only thing that changed in the messages from red to green to blue. 0x00 is 0 decimal, 0x78 is 120 decimal, and 0xf0 is 240 decimal. That&#8217;s the hue! And 0x64 is 100 decimal. That sounds like percentage for saturation and intensity. I repeated this process through different hue, saturation, and intensity settings in the app. I learned that the 7 bytes corresponded to:</p>
<table>
<tbody>
<tr>
<th>Byte</th>
<th>Description</th>
</tr>
<tr>
<td>0</td>
<td>Always 0x01</td>
</tr>
<tr>
<td>1</td>
<td>Hue (0 to 360) MSB</td>
</tr>
<tr>
<td>2</td>
<td>Hue (0 to 360) LSB</td>
</tr>
<tr>
<td>3</td>
<td>Saturation (0 to 100)</td>
</tr>
<tr>
<td>4</td>
<td>Intensity (0 to 100)</td>
</tr>
<tr>
<td>5</td>
<td>Unknown</td>
</tr>
<tr>
<td>6</td>
<td>Unknown</td>
</tr>
</tbody>
</table>
<p>I then ran some captures while adjusting the color temperature of the light strings and was able to update the table:</p>
<table>
<tbody>
<tr>
<th>Byte</th>
<th>Description</th>
</tr>
<tr>
<td>0</td>
<td>Flag: 0x00 for CCT, 0x01 for HSI</td>
</tr>
<tr>
<td>1</td>
<td>HSI Hue (0 to 360) MSB</td>
</tr>
<tr>
<td>2</td>
<td>HSI Hue (0 to 360) LSB</td>
</tr>
<tr>
<td>3</td>
<td>HSI Saturation (0 to 100)</td>
</tr>
<tr>
<td>4</td>
<td>HSI Intensity (0 to 100)</td>
</tr>
<tr>
<td>5</td>
<td>CCT Color Temperature (0 (warmest) to 100 (coolest))</td>
</tr>
<tr>
<td>6</td>
<td>CCT Intensity (0 to 100)</td>
</tr>
</tbody>
</table>
<p>Note that bytes 1, 2, 3, and 4 are always zero when byte 0 is 0 and bytes 5 and 6 are always zero when byte 0 is 1.</p>
<p>The Python script below will set the lights on the light string to the colors specified in the <em>colors</em> dictionary declared in __main__ at the end of the listing. Each color is specified with a tuple containing the six parameters described in the comments at the beginning of the listing. The colors will be repeated if there&#8217;s more lights on the string than colors specified in the dictionary. Don&#8217;t forget to replace the device IP, device ID, device key, and device version with the values for your light string.</p>
<pre># Format of the color tuple in main is 
#
# ( HSI Flag, Hue, Sat, Int, CCT Temp, CCT Int )
#
# HSI Flag = 0 for CCT mixing, 1 for HSI mixing
#
# If HSI Flag is 1:
#   Hue is 0 to 359, 0 is red, 120 is green, 240 is blue
#   Sat is 0 to 100
#   Int is 0 to 100
#   CCT Temp is 0
#   CCT Int is 0
#
# If HSI Flag is 0:
#   Hue is 0
#   Sat is 0
#   Int is 0
#   CCT Temp is 0 to 100, 0 = warmest and 100 = coolest
#   CCT Int is 0 to 100
#
# When using the smart life app's diy feature to set WW, NW, or CW at 100%:
#
#   WW is   0, 100
#   NW is  50, 100
#   CW is 100, 100
#
# Hue is 2 bytes, MSB first. The rest are 1 byte each.
#

import tinytuya
import time
import base64

# replace the x's with the data for your light string, IP is the local IP, not the cloud IP
DEVICE_IP = "x.x.x.x"
DEVICE_ID = "xxxxxxxxxxxxxxxxxxxxxx"
DEVICE_KEY = "xxxxxxxxxxxxxxxx"
DEVICE_VER = 3.3

def xmcosy_string_lights_encode_colors (lights, colors, offset):

  # header is 8 bytes and always the same
  header = b'\x00\xff\x00\x00\x00\x80\x01\x00'

  # replicate the specified colors across the specified number of lights as many times as possible
  light = 0
  index = offset
  levels = []
  for light in range (lights):
    levels.append (colors[index])
    index += 1
    if index &gt;= len(colors):
      index = 0

  # form the data byte string by combining the header and all the encoded light level tuples
  data = header
  for light in range (lights):
    encoded_level = levels[light][0].to_bytes (1, 'big')  # hsi/white flag
    encoded_level += levels[light][1].to_bytes (2, 'big') # hue, 2 bytes, MSB first
    encoded_level += levels[light][2].to_bytes (1, 'big') # saturation
    encoded_level += levels[light][3].to_bytes (1, 'big') # intensity
    encoded_level += levels[light][4].to_bytes (1, 'big') # cct color temperature
    encoded_level += levels[light][5].to_bytes (1, 'big') # cct brigtness
    data += encoded_level

  # base 64 encode the data string and convert to ascii
  b64 = base64.b64encode (data).decode ('ascii')

  return b64

if __name__ == '__main__':

  # 30 lights
  lights = 30

  # these 6 colors will be replicated 5 times across the 30 lights
  colors = [
    ( 1,   0, 100, 100,   0,   0 ), # RED
    ( 1,  60, 100, 100,   0,   0 ), # YELLOW
    ( 1, 120, 100, 100,   0,   0 ), # GREEN
    ( 1, 180, 100, 100,   0,   0 ), # CYAN
    ( 1, 240, 100, 100,   0,   0 ), # BLUE
    ( 1, 300, 100, 100,   0,   0 ), # MAGENTA
  ]

  """
  # these 3 color temps will be replicated 10 times across the 30 lights
  colors = [
    ( 0,   0,   0,   0,   0, 100 ), # WW
    ( 0,   0,   0,   0,  50, 100 ), # NW
    ( 0,   0,   0,   0, 100, 100 ), # CW
  ]
  """

  # make the colors chase down the string
  d = tinytuya.BulbDevice(DEVICE_ID, DEVICE_IP, DEVICE_KEY, version=DEVICE_VER, persist=False)
  while True:
    for i in range (len(colors)):
      d102 = xmcosy_string_lights_encode_colors (lights, colors, len(colors)-1-i)
      d.set_value (102, d102)
      time.sleep(1)</pre>
<h2>Verdict</h2>
<p>These lights are great! I highly recommend both the <span id="productTitle" class="a-size-large product-title-word-break"><a href="https://amzn.to/48wtlJx" target="_blank">Newleray String Light Hanging Kit</a></span> and the <a href="https://amzn.to/47vNZYU" target="_blank">XMCOSY+ RGBW Patio String Lights</a>. The hanging kit will keep the stress off the string light&#8217;s wiring and make any needed maintenance a breeze to complete.</p>
<p>The lights look great at night regardless of whether running RGB effects or being used for ambient or work lighting. I do wish, however, that these light were available with a 50 cm bulb spacing instead of the 80 cm bulb spacing because more bulbs spaced closer together almost always look better when displaying RGB effects.</p>
<p>The light construction is high quality and it&#8217;s nice to see an ETL-rated power supply as well. The fact that these lights can be controlled via their native app, WLED, through the Tuya IoT cloud, and locally through the TinyTuya library gives them enough flexibility to fit into anybody&#8217;s smart home control scenario.</p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs.</em></p>
]]></content:encoded>
			<wfw:commentRss>https://bikerglen.com/blog/xmcosy-string-lights-with-wled-and-tinytuya/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Controlling 5 out of 6 Ustellar GemBand Smart Flood Lights with WLED</title>
		<link>https://bikerglen.com/blog/controlling-5-out-of-6-ustellar-smart-flood-lights-with-wled/</link>
		<comments>https://bikerglen.com/blog/controlling-5-out-of-6-ustellar-smart-flood-lights-with-wled/#comments</comments>
		<pubDate>Sat, 02 Dec 2023 15:54:39 +0000</pubDate>
		<dc:creator><![CDATA[Glen]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://bikerglen.com/blog/?p=5011</guid>
		<description><![CDATA[In my my last post, I looked at four different sets of inexpensive LED flood lights to see which could be controlled using WLED. While they all could be controlled with WLED to some extent, only some could take advantage &#8230; <a href="https://bikerglen.com/blog/controlling-5-out-of-6-ustellar-smart-flood-lights-with-wled/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<div id="attachment_5014" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/ustellar-box-and-light.jpg"><img class="wp-image-5014 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/ustellar-box-and-light-1024x576.jpg" alt="ustellar - box and light" width="640" height="360" /></a><p class="wp-caption-text">The Ustellar 100 W light set box and one of the lights.</p></div>
<p>In my my last post, I looked at <a href="https://bikerglen.com/blog/controlling-inexpensive-led-lights-with-wled/" target="_blank">four different sets of inexpensive LED flood lights</a> to see which could be controlled using WLED. While they all could be controlled with WLED to some extent, only some could take advantage of WLED&#8217;s extensive library of built-in effects. In this post, I&#8217;m going to take a detailed look at the <a href="https://amzn.to/3uCp3Bd" target="_blank">100W version of Ustellar&#8217;s GemBand set of six RGBW smart flood lights</a>. It&#8217;s a little different than the previous sets but can still be controlled with WLED with one pretty big caveat.</p>
<p><span id="more-5011"></span></p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs.</em></p>
<h2>Detailed Description of the Lights</h2>
<div id="attachment_5042" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/GemBandSmartFloodLightsRGBW6-Pack-3_1024x10241.jpg"><img class="size-large wp-image-5042" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/GemBandSmartFloodLightsRGBW6-Pack-3_1024x10241-1024x818.jpg" alt="D" width="640" height="511" /></a><p class="wp-caption-text">Diagram from Ustellar&#8217;s website showing the major components of the six flood light set.</p></div>
<p>The set of lights includes a power adapter and six flood lights. There&#8217;s a connector between the power adapter and the first light. The rest of the lights are permanently attached to each other with 3 m of cable from one light to the next. The diagram from the Ustellar website shown above is accurate.</p>
<p>At the end of <a href="https://bikerglen.com/blog/controlling-inexpensive-led-lights-with-wled/" target="_blank">my previous post</a>, I outlined a list of five rules for finding lights that potentially use the WS2811 protocol and could be compatible with WLED and other pixel controllers. This set potentially violates two of those rules. First, there&#8217;s not a separate controller between the power supply and the lights. Second, from the diagram, photos, and videos, I couldn&#8217;t tell if the connector between the power supply and the first light had two, three, or more pins. I went ahead, took a risk, and bought the set.</p>
<p>I purchased my set of lights direct from Ustellar&#8217;s website for $123 but they&#8217;re available on <a href="https://amzn.to/49XD6S4" target="_blank">Amazon for as low as $159.99 minus a $50 coupon</a>.</p>
<div id="attachment_5020" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01598-crop.jpg"><img class="size-large wp-image-5020" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01598-crop-1024x682.jpg" alt="B" width="640" height="426" /></a><p class="wp-caption-text">One of the aluminum and glass flood lights and its plastic ground stake.</p></div>
<p>Each light is housed in a sturdy one-piece aluminum enclosure with a frosted glass front. The lights measure about 150 mm x 100 mm and contain 6 rows of LEDs distributed as 4 rows of warm white LEDs and 2 rows of RGB LEDs. The is exactly double the number of rows and LEDs as the 40 W version. The glass is sealed to the aluminum enclosure with adhesive making non-destructive disassembly highly unlikely. The included ground stakes are plastic so use an old screwdriver or similar to start the hole before whacking at the stakes mercilessly with a rubber mallet.</p>
<div id="attachment_5021" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01601-crop.jpg"><img class="size-large wp-image-5021" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01601-crop-1024x682.jpg" alt="F" width="640" height="426" /></a><p class="wp-caption-text">The included 48 V 2.08 A / 100 W power supply.</p></div>
<p>The 48 V at 2.08 A / 100 W power supply is a sealed waterproof brick. The manufacturer and model number are listed on the supply and there&#8217;s a public, real datasheet available on the manufacturer&#8217;s website. This power supply does appear to be customized somewhat for Ustellar though.</p>
<div id="attachment_5023" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01604-crop.jpg"><img class="size-large wp-image-5023" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01604-crop-1024x682.jpg" alt="G" width="640" height="426" /></a><p class="wp-caption-text">The two-pin connector on the cable from the power supply.</p></div>
<p>The input side of the power supply uses a standard two-prong North American AC line cord. The output side of the power supply uses the two-prong connector shown in the photo above. This connects to the mating connector on the power cord from the first light in the string. Both cords are permanently attached to the power supply.</p>
<p>Looking back at the markings on the edges of the top of power supply, the line cord has line and neutral and the output cord has DC (+) and DC (-). This appears to be a simple power supply rather than a power supply with a built-in wireless receiver and controller for the lights.</p>
<p>If the controller is not in the power supply, that leaves open two possibilities:</p>
<ol>
<li>There&#8217;s a wireless receiver in the first light and the remaining lights follow commands from the it.</li>
<li>There&#8217;s a wireless receiver in every light.</li>
</ol>
<p>If it&#8217;s the first possibility, maybe I can still use 5 of the 6 lights. If it&#8217;s the second, well, I&#8217;m going to need to visit a local glass shop to get some new glass cut to make use of any of the lights. Time to do some research.</p>
<h2>Research</h2>
<p>I couldn&#8217;t find much information on the 100 W version of the light set. I did find lots of information on the 40 W version of the light set and my hope was the 100 W set would function similarly.</p>
<h3>Setup Video</h3>
<div id="attachment_5016" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/Screenshot-2023-11-11-083637.png"><img class="wp-image-5016 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/Screenshot-2023-11-11-083637-1024x576.png" alt="Screenshot 2023-11-11 083637" width="640" height="360" /></a><p class="wp-caption-text">A frame from the instructional video for the 40 W set of flood lights.</p></div>
<p>First up, I found a video on how to set up the lights. The most interesting part of the video is shown in the screen capture above. This image strongly implies there&#8217;s only a single wireless receiver / controller and it&#8217;s located in the first light in the string.</p>
<h3>FCC Filings</h3>
<p>Next up, I headed over to fccid.io to search for the FCC filings for the light. The FCC ID on the tag on the power supply is 2AWONUT88875. Despite this power supply only being for the 100 W light set, the indicated FCC ID was for the <a href="https://fccid.io/2AWONUT88875" target="_blank">filings for the 40 W version of the set</a>. Better than nothing I guess.</p>
<div id="attachment_5038" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/Internal-Photos-5935817-unlocked-page-1a-2.jpg"><img class="size-large wp-image-5038" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/Internal-Photos-5935817-unlocked-page-1a-2-1024x768.jpg" alt="F" width="640" height="480" /></a><p class="wp-caption-text">An internal view of two of the flood lights from the 40 W set. The light on the right is the first light in the chain. The light on the left is representative of the remaining flood lights.</p></div>
<p>One of the internal photos from the FCC filing is shown above. I added the green and blue boxes on to the photo. The light on the right is the first light in the chain. The light on the left is representative of the remaining flood lights.</p>
<p>If you look inside the green boxes, there&#8217;s only a wireless module in the first light. The remaining lights do not have a wireless module. If you look inside the blue boxes, there&#8217;s only two wires connected to the input of the first light but three wires connected to the input of the second light.</p>
<div id="attachment_5040" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/Internal-Photos-5935817-unlocked-connections1.jpg"><img class="size-large wp-image-5040" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/Internal-Photos-5935817-unlocked-connections1-1024x582.jpg" alt="C" width="640" height="364" /></a><p class="wp-caption-text">Circuit board from one of the lights with the power and data inputs highlighted in orange and the power and data outputs highlighted in yellow.</p></div>
<p>The power and data input and output terminals are shown inside the orange and yellow boxes I added to the 2nd internal photo shown above. On the first light, the black and white wires from the power supply are connected to the 24V and GND inputs and there&#8217;s no connection to the DIN terminal. There is, however, a connection to the DOUT terminal. On the second and subsequent lights, the black, white, and red wires from the output of the previous light are connected to the 24V, GND, and DIN terminals of the next light.</p>
<div id="attachment_5036" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/Internal-Photos-5935817-unlocked-ws2814.jpg"><img class="size-large wp-image-5036" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/Internal-Photos-5935817-unlocked-ws2814-1024x771.jpg" alt="B" width="640" height="482" /></a><p class="wp-caption-text">Circuit board from one of the lights with a WS2814 four-channel LED driver highlighted in magenta.</p></div>
<p>At this point, I felt safe to assume that only the first light contains a wireless receiver and controller and the remaining lights are controlled by the first light. I still did not know what the protocol was until looking at the third internal photo shown above. Yep, inside the magenta box is a WS2814 four-channel LED driver.</p>
<p>By the way, the wireless module is a BK7231N but it would be difficult to flash custom firmware on it since you must either break the glass to get to the programming terminals or figure out how to do an unauthorized over-the-air update.</p>
<h2>Digging into My 100 Watt Set</h2>
<div id="attachment_5024" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01605-with-highlights-crop.jpg"><img class="size-large wp-image-5024" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01605-with-highlights-crop-1024x682.jpg" alt="G" width="640" height="426" /></a><p class="wp-caption-text">One of my lights from the 100 W set with the power connector highlighted in magenta and my surgery to discover how many conductors in the cable to the next light highlighted in yellow.</p></div>
<p>I felt pretty confident at this point that at least five of the six lights used the WS281x protocol and could be controlled via WLED. Of course, all the information I had was for the 40 W set and the big question was if the 100 W kit worked the same as the 40 W kit. If the wire between the first and second light was three conductors, I was golden. If it was only two conductors, this 100 W set functioned differently than the 40 W set and I&#8217;d need a new plan.</p>
<h3>Counting the Conductors</h3>
<p>I noticed the two conductor wire from the first light to the power supply was more flexible and had a tighter bend radius than the wire from the first to the second light. This was a good sign there was at least one more conductor in the wire between the lights. I used a knife to make a quarter inch slit in the outer insulation jacket and peeked inside. Sure enough, three wires!</p>
<h3>Discovering the Protocol</h3>
<div id="attachment_5071" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231130_042008186-crop.jpg"><img class="wp-image-5071 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231130_042008186-crop-1024x576.jpg" alt="A" width="640" height="360" /></a><p class="wp-caption-text">Probing the data output from the first light in the set. The left black wire is the power input from the power supply. The right black wire is the power and data output to the second light in the set. I&#8217;ve replaced the second light with an oscilloscope probe. I also slipped the backshell for a waterproof connector over the wire so I don&#8217;t forget later.</p></div>
<p>At this point, I cut the power supply and first light from the rest of the set. I removed the outer jacket from the cable to the second light and stripped the wires. I used a DMM to verify the black wire was 48 VDC and the white wire was ground as shown in the FCC filings.</p>
<p>Then, before I forgot, I slipped a waterproof connector&#8217;s backshell over the power and data output cable since I&#8217;m going to put a set of connectors between the first and second lights. This will let me easily swap between using the controller built into the first light and my WLED controller later.</p>
<p>To decode the protocol, I connected an oscilloscope probe to the red data wire and white ground wire as shown in the photo above. I then observed the protocol&#8217;s waveforms while using the set&#8217;s native app to set the lights to various colors.</p>
<div id="attachment_5068" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_7.png"><img class="size-full wp-image-5068" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_7.png" alt="A" width="800" height="632" /></a><p class="wp-caption-text">The first 17 bits of the protocol.</p></div>
<p>The waveform was the WS2811 protocol at 950 kHz and using 5 V logic levels. Each light had 4 channels of color information and each channel had 8 bits. The transmission order was { 8 bits of warm white, 8 bits of red, 8 bits of green, and 8 bits of blue }.</p>
<div id="attachment_5069" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_8.png"><img class="size-full wp-image-5069" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_8.png" alt="B" width="800" height="632" /></a><p class="wp-caption-text">B</p></div>
<p>The total length of the data sent to the lights was ~167.2 μs. At 950 kHz, this is 160 bits which corresponds to 5 lights of 4 channels of 8 bits.</p>
<h2>New 48 V WLED Controller</h2>
<div id="attachment_5073" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231130_053321664-crop.jpg"><img class="wp-image-5073 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231130_053321664-crop-1024x576.jpg" alt="PXL_20231130_053321664 - crop" width="640" height="360" /></a><p class="wp-caption-text">Left to right: the Olimex ESP32 Gateway board that will run WLED, my first interface board with a maximum supported pixel voltage of 24 V, and my new interface board with a maximum supported pixel voltage of 48 V.</p></div>
<p>With the pinout and protocol known, it was time to look at controlling the lights with WLED, an Olimex ESP32 Gateway, and my interface board. The interface board I built previously has a 5 V DC/DC converter to power the Olimex ESP32 Gateway board from the LED power supply. The DC/DC converter is rated up to an input voltage of 36 V.</p>
<p>Since these lights are 48 V, I had to redesign the interface board with a DC/DC converter rated up to an input voltage of 72 V and upgrade the input capacitors to some with a 100 V rating. The Olimex ESP32 Gateway board, the 24 V version of the interface board, and the 48 V version of the interface board are shown in the photo above.</p>
<div id="attachment_5074" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231130_052906525-crop.jpg"><img class="wp-image-5074 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231130_052906525-crop-1024x576.jpg" alt="PXL_20231130_052906525 - crop" width="640" height="360" /></a><p class="wp-caption-text">The WLED controller connected to the second light in the string. I placed a 3-pin male waterproof connector on the light&#8217;s power/data input cable. This is connected to a 3-pin female waterproof connector with a short pigtail to the WLED controller.</p></div>
<p>I soldered a waterproof 3-pin male connector on to the power and data cable to the first light in the string of now five lights. I used a short pigtail with a mating female connector to connect the string of lights to the WLED controller and interface board. I also soldered a female connector on the power and data output of the light with the wireless module inside it. The connectors allow me to swap easily between using 6 lights with the factory controller or 5 lights with the WLED controller.</p>
<div id="attachment_5075" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231130_052841101-crop.jpg"><img class="wp-image-5075 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231130_052841101-crop-1024x576.jpg" alt="PXL_20231130_052841101 - crop" width="640" height="360" /></a><p class="wp-caption-text">Power from my bench supply, an Ethernet cable, and the last five lights of the string are connected to the WLED controller. I didn&#8217;t feel like dealing with the 15 meters of cables so I did my initial testing at lower output levels in the box. There&#8217;s a fire extinguisher under my desk too.</p></div>
<p>With everything connected, it was time to try out WLED with the lights. I threw everything in the box then threw the box on my workbench and powered up the controller and lights. The photo above shows the five lights, the connectors, and the WLED controller with the 48 V interface board.</p>
<div id="attachment_5076" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231130_052916024-crop.jpg"><img class="wp-image-5076 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231130_052916024-crop-1024x576.jpg" alt="PXL_20231130_052916024 - crop" width="640" height="360" /></a><p class="wp-caption-text">More colors. Also still in the box.</p></div>
<p>While WLED supports RGBW pixels, permits reordering the color components, and swapping one color channel and with the white channel, it does not permit arbitrary reordering of the channel components. With this limitation, none of the WLED built-in effects work as expected. The best I could do with WLED is to select SK6812 RGBW pixels in the LED settings panel and multi RGBW in the sync interfaces panel. With these settings selected, WLED functioned as a transparent E1.31/ArtNet/DDP interface to the pixels.</p>
<h2>Conclusions</h2>
<div id="attachment_5092" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/12/PXL_20231203_012744769-crop.jpg"><img class="size-large wp-image-5092" src="https://bikerglen.com/wp/wp-content/uploads/2023/12/PXL_20231203_012744769-crop-1024x576.jpg" alt="Hey! That's 6 lights! Yep, I didn't want to drag my bench supply outside and I don't have the connectors I want for the 48 V power input yet. " width="640" height="360" /></a><p class="wp-caption-text">Hey! That&#8217;s 6 lights! Yep, I didn&#8217;t want to drag my bench supply outside and I don&#8217;t have the connectors I want for the 48 V power input yet so this photo is taken using six lights and the stock controller built into the first light.</p></div>
<p>These floodlights are available in both a 40 W set and at 100 W set. Both sets function similarly with a power supply, a wireless controller built into the first light, and then five more daisy chained lights that are controlled by the first light. The 40 W set uses a 24 V power supply and the 100 W set uses as 48 V power supply.</p>
<p>As long as you&#8217;re willing to use only five of the six lights from the set, have a controller supporting the set&#8217;s supply voltage, and can live without using most of the built-in effects, either of these light sets can be used with WLED. Since WLED does not support arbitrary channel reordering, the built-in effects are nearly useless but the lights can still be controlled over the network.</p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs.</em></p>
]]></content:encoded>
			<wfw:commentRss>https://bikerglen.com/blog/controlling-5-out-of-6-ustellar-smart-flood-lights-with-wled/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Controlling Inexpensive LED Lights with WLED</title>
		<link>https://bikerglen.com/blog/controlling-inexpensive-led-lights-with-wled/</link>
		<comments>https://bikerglen.com/blog/controlling-inexpensive-led-lights-with-wled/#comments</comments>
		<pubDate>Tue, 21 Nov 2023 23:36:12 +0000</pubDate>
		<dc:creator><![CDATA[Glen]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://bikerglen.com/blog/?p=4853</guid>
		<description><![CDATA[I&#8217;m always looking for new LED lights for Halloween, Christmas, and just general tinkering. My preference is for lights that can be modified to be controlled locally by WLED. Controlling the lights locally lets me integrate the lights into larger &#8230; <a href="https://bikerglen.com/blog/controlling-inexpensive-led-lights-with-wled/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<div id="attachment_4854" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_014607379.jpg"><img class="size-large wp-image-4854" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_014607379-1024x596.jpg" alt="The Contenders" width="640" height="373" /></a><p class="wp-caption-text">A Few of the Contenders</p></div>
<p>I&#8217;m always looking for new LED lights for Halloween, Christmas, and just general tinkering. My preference is for lights that can be modified to be controlled locally by WLED. Controlling the lights locally lets me integrate the lights into larger displays and coordinate the colors of all the connected lights using software like xLights. I recently purchased a few sets of random LED lights from 3rd party sellers on Amazon. Read on to find out which ones can be rewired to work with WLED and, by extension, xLights.</p>
<p><span id="more-4853"></span></p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs.<br />
</em></p>
<h2>My WLED Controller</h2>
<div id="attachment_4922" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01481-crop.jpg"><img class="size-large wp-image-4922" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01481-crop-1024x682.jpg" alt="The Olimex ESP32-Gateway board and my interface board." width="640" height="426" /></a><p class="wp-caption-text">The Olimex ESP32-Gateway board and my interface board.</p></div>
<p>WLED runs on numerous platforms. I&#8217;m running WLED on an Olimex ESP32-Gateway board. I built a small interface board that connects the gateway board to the lights. This board connects to and mounts under the ESP32-Gateway using a 20 pin 0.1&#8243; header. The small board has a two-pin connector for power from a power supply and a three-pin connector to supply power and data to the lights. Even though I&#8217;m using my own board, these lights could be controlled by any off-the-shelf WLED controller that supports the lights&#8217; supply voltages.</p>
<div id="attachment_4912" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/esp32-gateway-driver-schematic.png"><img class="size-large wp-image-4912" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/esp32-gateway-driver-schematic-1024x792.png" alt="Schematic of my pixel driver board. It's a few connectors, a power supply for the Olimex ESP32 Gateway board, and a 3.3 V to 5 V level shifter with series resistor and transient protector to drive the pixels." width="640" height="495" /></a><p class="wp-caption-text">Schematic of my pixel driver board. It&#8217;s a few connectors, a 5 V  power supply for the Olimex ESP32 Gateway board, and a 3.3 V to 5 V level shifter with series resistor and transient protector to drive the data line of the connected pixels from the ESP32.</p></div>
<p>As shown in the schematic above, the interface board contains a +5 V power supply for the gateway board and a +3.3 V to +5 V level translator for driving the pixels&#8217; data line at +5 V from one of the gateway board&#8217;s +3.3 V output pins. The CUI DC/DC converter on the interface board supports a wide range of input voltages which allows the interface board to support both 12 V and 24 V pixels. There&#8217;s a series resistor and transient protector on the +5 V pixel data output to prevent problems.</p>
<h2>Set #1: <span id="productTitle" class="a-size-large product-title-word-break">APPECK Smart WiFi Garden Lawn Lights, Set of 15<br />
</span></h2>
<div id="attachment_4870" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_213749526.jpg"><img class="size-large wp-image-4870" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_213749526-1024x576.jpg" alt="Light box setting next to light in my front yard." width="640" height="360" /></a><p class="wp-caption-text">The empty box sitting next to one of the installed lights in my front yard.</p></div>
<p>First up are the 15 pack of <a href="https://amzn.to/3GdMHq4" target="_blank">APPECK Smart RGBICW Pathway Lights</a>. I purchased two boxes of 15 lights at $36.99 each minus a $3.70 coupon for a total of $70.28 + tax.</p>
<h3>Detailed Description</h3>
<div id="attachment_4871" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_211823632.jpg"><img class="size-large wp-image-4871" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_211823632-1024x576.jpg" alt="RGBW pixel with stake attached." width="640" height="360" /></a><p class="wp-caption-text">RGBW pixel with stake attached.</p></div>
<p>These are small RGB + WW dots that can be used to line the edge of a flower bed or garden. Each light is about 2&#8243; in diameter and comes with a small plastic stake that screws to the back of the light and is used to secure the light in the ground. The distance between each light is about two feet.</p>
<div id="attachment_4888" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231110_001435792.jpg"><img class="size-large wp-image-4888" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231110_001435792-1024x576.jpg" alt="The lights placed in the grass on each side of the driveway." width="640" height="360" /></a><p class="wp-caption-text">The lights placed in the grass on each side of the driveway.</p></div>
<p>I placed my two sets in the grass on each side of the driveway for the holidays.</p>
<div id="attachment_4875" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_211756276.jpg"><img class="size-large wp-image-4875" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_211756276-1024x576.jpg" alt="24V 1A power supply." width="640" height="360" /></a><p class="wp-caption-text">24 V 1 A 24 W power supply.</p></div>
<p>Each set consists of a power supply, a controller, and the 15 lights. The power supply plugs directly into the wall (or an extension cord as shown in the photo above) and is rated for 24 V at 1 A or 24 W.</p>
<div id="attachment_4876" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_212057592.jpg"><img class="size-large wp-image-4876" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_212057592-1024x576.jpg" alt="The power and light cables are permanently attached to the controller but can be unscrewed from the power supply and lights." width="640" height="360" /></a><p class="wp-caption-text">The power and light cables are permanently attached to the controller but can be unscrewed from the power supply and lights.</p></div>
<p>The controller is a small black plastic puck with a single button. The button is used to turn the light string on and off, change the light pattern, and enter pairing mode.</p>
<div id="attachment_4877" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_212022591.jpg"><img class="size-large wp-image-4877" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_212022591-1024x576.jpg" alt="Controller cable connector on the left, power supply connector on the right." width="640" height="360" /></a><p class="wp-caption-text">Controller cable connector on the left, power supply connector on the right.</p></div>
<p>The controller connects to the power supply via a 2 pin round connector.</p>
<div id="attachment_4878" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_213414887.jpg"><img class="size-large wp-image-4878" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_213414887-1024x576.jpg" alt="Light string connector on the left, controller connector on the right." width="640" height="360" /></a><p class="wp-caption-text">Male light string connector on the left, female controller connector on the right.</p></div>
<p>The controller connects to the lights via a 3 pin round connector. These connectors are the same size as HolidayCoro / xConnect connectors but the pins and sockets are spaced slightly different so they are not compatible with each other.</p>
<div id="attachment_4880" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_220354250.jpg"><img class="size-large wp-image-4880" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_220354250-1024x576.jpg" alt="The included RF remote control." width="640" height="360" /></a><p class="wp-caption-text">The included RF remote control.</p></div>
<p>The lights&#8217; basic functionality can be controlled via the included RF remote control or, for more advanced functionality, via the Smart Life &#8211; Smart Living app by Volcano Technology Limited. The app does require fine location permission since it uses Bluetooth to discover and pair the lights to your Wi-Fi network.</p>
<h3>FCC Info</h3>
<div id="attachment_4881" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_215717858.jpg"><img class="size-large wp-image-4881" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231111_215717858-1024x576.jpg" alt="Tag on one of the controller cables with the FCC ID." width="640" height="360" /></a><p class="wp-caption-text">Tag on one of the controller cables with the FCC ID.</p></div>
<p>The FCC ID of 2A7VP-C6601A on the tag reveals the light is manufactured by the Shenzhen Jindian Zhiguang Lighting Co., Ltd.</p>
<div id="attachment_4883" style="width: 915px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/Internal-Photos-6118046.jpg"><img class="size-full wp-image-4883" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/Internal-Photos-6118046.jpg" alt="Internal photo of the controller for the lights." width="905" height="679" /></a><p class="wp-caption-text">Internal photo of the controller for the lights.</p></div>
<p>If you head over to <a href="https://fccid.io/2A7VP-C6601A">fccid.io</a>, you can download PDFs with internal and external photos. The controller is based on a BK7231NQN32 wireless module. From looking at the internal photo, you can see there&#8217;s a two wire power input at the bottom of the photos and a three wire power / data output at the top of the photo. The good news here is three wire power / data usually means there&#8217;s some sort of WS2811-like control possibility!</p>
<h3>Protocol Information</h3>
<div id="attachment_4887" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/3-pin-pinout.drawio.png"><img class="size-large wp-image-4887" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/3-pin-pinout.drawio-1024x544.png" alt="Pin outs drawn looking into the connectors." width="640" height="340" /></a><p class="wp-caption-text">Pinouts drawn looking into the connectors.</p></div>
<p>The pinout of the connectors is shown above. On the left is the pinout viewed looking into the female connector on the controller cable. On the right is the pinout viewed looking into the male connector on the light cable.</p>
<div id="attachment_4902" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_0.png"><img class="size-full wp-image-4902" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_0.png" alt="840 kHz bit rate with 5 V logic levels." width="800" height="632" /></a><p class="wp-caption-text">840 kHz bit rate with 5 V logic levels. This is the first light lit up red at 100%.</p></div>
<p>I hooked the ground and data out pins on the controller cable to an oscilloscope. The waveform was the WS2811 protocol at 840 kHz. Each light had 32 bits of control information. The transmission order was 8 bits of red, 8 bits of green, 8 bits of blue, and 8 bits of warm white. This was repeated 15 times, once for each light on the string.</p>
<h3>Controlling with WLED</h3>
<h4>Hardware Connections</h4>
<div id="attachment_4935" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01497-crop.jpg"><img class="size-large wp-image-4935" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01497-crop-1024x682.jpg" alt="The power supply, light string, and network connected to my WLED controller." width="640" height="426" /></a><p class="wp-caption-text">The power supply, light string, and network connected to my WLED controller.</p></div>
<p>The hardware connections are pretty simple thanks to the interface board. The power supply connects to the 2-pin connector on the left and the lights connect to the 3-pin connector on the right.</p>
<h4>WLED Configuration</h4>
<div id="attachment_4906" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/wled-configuration-c6601a.png"><img class="size-large wp-image-4906" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/wled-configuration-c6601a-1024x841.png" alt="Key parameters in the LED Preferences configuration pane on the left and key parameters in the Sync Interface configuration pane on the right." width="640" height="526" /></a><p class="wp-caption-text">Key parameters in the LED Preferences configuration pane on the left and key parameters in the Sync Interface configuration pane on the right.</p></div>
<p>To use these lights with WLED, the closest matching pixel type is SK6812 RGBW with RGB color ordering. Selecting this pixel type and color ordering in the LED Preferences panel will permit the pathway lights to use all of WLED&#8217;s built-in effects. To control the pathway lights over the network, be sure to select Multi RGBW from the Sync Interfaces panel to match the RGBW pixel type.</p>
<h3>Verdict</h3>
<p>All WLED features including the large selection of built-in effects are available and the pixels are controllable through both the web interface and over the network using either sACN E1.31 or DDP. I liked these lights and used them in my 2023 holiday light setup.</p>
<h2>Set #2: <span id="productTitle" class="a-size-large product-title-word-break">APPECK Low Voltage Landscape Lights, Set of 6<br />
</span></h2>
<div id="attachment_4933" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01494-crop.jpg"><img class="size-large wp-image-4933" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01494-crop-1024x682.jpg" alt="The spotlight box and one of the spotlights." width="640" height="426" /></a><p class="wp-caption-text">The spotlight box and one of the spotlights.</p></div>
<p>Next up are the 6 pack of <a href="https://amzn.to/3sHO3q2" target="_blank">APPECK Smart RGBICW Color Changing Landscape Lights</a>. I purchased one box at $199.99 minus a $99 coupon for a total of $100.99 + tax.</p>
<h3>Detailed Description</h3>
<div id="attachment_4937" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01500-crop.jpg"><img class="size-large wp-image-4937" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01500-crop-1024x682.jpg" alt="Both the spotlight and stake are constructed of metal and powder coated." width="640" height="426" /></a><p class="wp-caption-text">Both the spotlight and stake are constructed of metal and powder coated.</p></div>
<p>These are small RGB + CCT white landscape lights. CCT means the light includes both warm white and cool white LEDs so the color temperature of the white light can be smoothly adjusted to anywhere from a warm yellowish light to a cool blueish light. They look like spot lights but the beam angle is pretty wide and they function more like flood lights than spot lights. Each light is about 2.5&#8243; in diameter and 4&#8243; long. The attached bracket can be screwed into a wall, ceiling, or deck or the included spike can be attached for fixing the light on the ground. The distance between each light is about 10 feet.</p>
<div id="attachment_4923" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01482-crop.jpg"><img class="size-large wp-image-4923" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01482-crop-1024x682.jpg" alt="The 24 V 2 A / 48 W power supply included with the spotlights." width="640" height="426" /></a><p class="wp-caption-text">The 24 V 1.5 A / 36 W power supply included with the spotlights.</p></div>
<p>Each set consists of a power supply, a controller, and six lights. The power supply and controller are very similar to those included with the APPECK pathway lights above. The power supply plugs directly into the wall and is rated for 24 V at 1.5 A or 36 W.</p>
<div id="attachment_4924" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01483-crop.jpg"><img class="size-large wp-image-4924" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01483-crop-1024x682.jpg" alt="The controller for the spotlights. It looks identical to the controller for the pathway lights. It's not. " width="640" height="426" /></a><p class="wp-caption-text">The controller for the spotlights. It looks identical to the controller for the pathway lights. It&#8217;s not.</p></div>
<p>The controller looks identical to the controller included with the APPECK pathway lights above. Again, it&#8217;s a black plastic puck with a single button to turn the lights on and off, change light patterns, and enter pairing mode. Even though the controller looks similar, it appears as a different model in the Smart Life app and uses a different protocol than the pathway lights.</p>
<div id="attachment_4925" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01489-crop.jpg"><img class="size-large wp-image-4925" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01489-crop-1024x682.jpg" alt="Controller cable connector on the left, power supply connector on the right." width="640" height="426" /></a><p class="wp-caption-text">Controller power cable connector on the left, power supply and power supply connector on the right.</p></div>
<p>The controller connects to the power supply via a 2 pin round connector.</p>
<div id="attachment_4927" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01490-crop.jpg"><img class="wp-image-4927 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01490-crop-1024x683.jpg" alt="the" width="640" height="427" /></a><p class="wp-caption-text">Female controller connector on the left, male light string connector on the right.</p></div>
<p>The controller connects to the lights via a 3 pin round connector. These connectors are the same size as HolidayCoro / xConnect connectors but the pins and sockets are spaced slightly different so they are not compatible with each other.</p>
<div id="attachment_4928" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01495-crop.jpg"><img class="size-large wp-image-4928" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01495-crop-1024x683.jpg" alt="The RF remote control for the spotlights." width="640" height="427" /></a><p class="wp-caption-text">The RF remote control for the spotlights.</p></div>
<p>The lights’ basic functionality can be controlled via the included RF remote control or, for more advanced functionality, via the Smart Life – Smart Living app by Volcano Technology Limited. The remote is slightly different than the one included with the pathway lights. The remotes from the flood lights and the pathway lights do not interfere with each other. The app does require fine location permission since it uses Bluetooth to discover and pair the lights to your Wi-Fi network.</p>
<h3>FCC Info</h3>
<div id="attachment_4929" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01491-crop.jpg"><img class="size-large wp-image-4929" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01491-crop-1024x682.jpg" alt="Label on the string of spotlights with the FCC ID." width="640" height="426" /></a><p class="wp-caption-text">Label on the string of spotlights with the FCC ID.</p></div>
<p>The FCC ID on the tag is 2A7VP-C6605D.</p>
<div id="attachment_4954" style="width: 988px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/nearly-identical.png"><img class="size-full wp-image-4954" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/nearly-identical.png" alt="Searching for the FCC ID reveals a model difference letter. The interesting FCC ID for these lights is 2A7VP-C6607C." width="978" height="675" /></a><p class="wp-caption-text">Searching for the FCC ID reveals a model difference letter. The interesting FCC ID for these lights is 2A7VP-C6607C.</p></div>
<p>Searching for the 2A7VP-C6605D FCC ID in the FCC database yields a Model Difference letter asserting that the C6605D model is identical in all but form factor and model number to the C6607C model. Searching for the 2A7VP-C6607C FCC ID reveals the meaty FCC entry for the lights. These lights are manufactured by the Shenzhen Jindian Zhiguang Lighting Co., Ltd. Unfortunately, the Internal Photos PDF is being held back until April 30, 2024, under a temporary confidentiality request.</p>
<h3>Protocol</h3>
<div id="attachment_4887" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/3-pin-pinout.drawio.png"><img class="size-large wp-image-4887" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/3-pin-pinout.drawio-1024x544.png" alt="Pin outs drawn looking into the connectors." width="640" height="340" /></a><p class="wp-caption-text">Pinouts drawn looking into the connectors.</p></div>
<p>The pinout of the connectors is shown above. On the left is the pinout viewed looking into the female connector on the controller cable. On the right is the pinout viewed looking into the male connector on the light cable. This is the same pinout as for the APPECK pathway lights described above.</p>
<div id="attachment_4904" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_1.png"><img class="size-full wp-image-4904" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_1.png" alt="750 kHz bit rate with 5 V logic levels. 16 bits per color." width="800" height="632" /></a><p class="wp-caption-text">750 kHz bit rate with 5 V logic levels. 16 bits per color. This is the first light lit up red at 100%.</p></div>
<p>I hooked the ground and data out pins on the controller cable to an oscilloscope. The waveform was the WS2811 protocol at 750 kHz. Each light had 80 bits of control information. The transmission order was 16 bits of red, 16 bits of green, 16 bits of blue, 16 bits of cool white, and 16 bits of warm white. This was repeated 12 times despite there only being 6 lights in the set.</p>
<p>If all these bits are real, the lights can be set to billions of different colors and the high resolution will be useful for gamma correction at low brightness levels. Also, I can&#8217;t wait for the FCC to publish the internal photos because I&#8217;m really curious what driver chip supports 5 channel of 16 bits each.</p>
<h3>Controlling with WLED</h3>
<h4>Hardware Connections</h4>
<div id="attachment_4930" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01499-crop.jpg"><img class="size-large wp-image-4930" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01499-crop-1024x682.jpg" alt="The power supply, light string, and network connected to my WLED controller." width="640" height="426" /></a><p class="wp-caption-text">The power supply, light string, and network connected to my WLED controller.</p></div>
<p>The hardware connections are pretty simple thanks to my interface board. The power supply connects to the 2-pin connector on the left and the lights connect to the 3-pin connector on the right.</p>
<h4>WLED Configuration</h4>
<div id="attachment_4956" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/c6605-wled-config.png"><img class="size-large wp-image-4956" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/c6605-wled-config-1024x514.png" alt="WLED LED and sync interface config panels for these lights." width="640" height="321" /></a><p class="wp-caption-text">WLED LED and sync interface configuration panels for these lights.</p></div>
<p>WLED does not support pixels with five 16-bit channels. The best we can do is to configure WLED to transparently pass 60 DMX channels received via sACN E1.31 or DDP to the spotlights then use other software to control the spotlights over the network.</p>
<p>To do this, I configured 20 pixels of WS281x with RGB color ordering in the LED configuration panel, and in the interfaces panels, I selected Multi RGB for the DMX mode and made sure both the Force max brightness and Disable realtime gamma correction boxes were checked.</p>
<h3>Verdict</h3>
<div id="attachment_4931" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231119_165738318.jpg"><img class="size-large wp-image-4931" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231119_165738318-1024x576.jpg" alt="The six spotlights connected to my WLED controller. The light data is being sent over the network from some software that supports 5 channel, 16-bit per channel fixtures." width="640" height="360" /></a><p class="wp-caption-text">The six spotlights connected to my WLED controller. The light data is being sent over the network from some software that supports 5 channel, 16-bit per channel fixtures.</p></div>
<p>WLED does not support pixels with five 16-bit channels. As a result, the spotlights are not really controllable through the WLED web interface and few, if any, of WLED&#8217;s built-in effects will work with the spotlights. The likely only useful built-in effect is creating an all-black preset to turn off the spotlights in the absence of DMX data.</p>
<p>The spotlights can be controlled, however, via sACN e1.31 and DDP by defining the 6 spotlights as 20 8-bit RGB pixels in WLED and using the Multi RGB DMX mode. In this case, the software sending the e1.31 or DDP packets to the WLED controller must support five channel fixtures with 16 bits per channel.</p>
<p>I will likely use these lights to illuminate larger props at Halloween.</p>
<h2>Set #3: Lumary <span id="productTitle" class="a-size-large product-title-word-break">Smart Landscape Lights, Set of 6</span></h2>
<div id="attachment_4939" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231119_174026680-crop.jpg"><img class="size-large wp-image-4939" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231119_174026680-crop-1024x576.jpg" alt="photo of box and light" width="640" height="360" /></a><p class="wp-caption-text">The spotlight box and one of the miniature spotlights.</p></div>
<p>Next up is a 6 pack of <a href="https://amzn.to/3sHmJrZ" target="_blank">Lumary Smart Outdoor Spotlights</a>. I purchased one box for $69.99 + tax.</p>
<h3>Detailed Description</h3>
<div id="attachment_4940" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231119_174228256-crop.jpg"><img class="wp-image-4940 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231119_174228256-crop-1024x576.jpg" alt="photo of light and stake" width="640" height="360" /></a><p class="wp-caption-text">Both the spotlight and stake are constructed of metal and powder coated.</p></div>
<p>These are the smallest of the lights I examined. They&#8217;re only 1.5&#8243; in diameter and about 3&#8243; long. They&#8217;re also the lowest powered and least bright of the lights I examined. The entire set is powered by a 12 V at 1 A or 12 W adapter. The other light sets have power adapters with at least twice the wattage.</p>
<div id="attachment_4957" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/lumary-schematic.jpg"><img class="size-large wp-image-4957" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/lumary-schematic-1024x420.jpg" alt="System components and the distances between them." width="640" height="263" /></a><p class="wp-caption-text">System components and the distances between them.</p></div>
<p>Like the previous two sets, this set consists of a plugin AC power supply, a controller, and a string of lights. The seller&#8217;s photo above shows a schematic of the set and the distances between each component of the system.</p>
<div id="attachment_4941" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231119_174720837-crop.jpg"><img class="wp-image-4941 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231119_174720837-crop-1024x576.jpg" alt="" width="640" height="360" /></a><p class="wp-caption-text">The 12 V 1 A / 12 W AC adapter and controller.</p></div>
<p>The power adapter and controller are shown in the photo above. The power and light cables are permanently attached to the controller. The controller&#8217;s power cable connects directly to the power supply. The controller&#8217;s light cable connects to a long lead wire from the first fixture in the string. Note that unlike the previous two sets, the power supply is only 12 V at 1 A / 12 W.</p>
<div id="attachment_4944" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01501-crop.jpg"><img class="wp-image-4944 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01501-crop-1024x683.jpg" alt="" width="640" height="427" /></a><p class="wp-caption-text">Power supply and power supply connector on the left, controller power cable connector on the right.</p></div>
<p>The controller connects to the power supply via a 2 pin round connector.</p>
<div id="attachment_4945" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01502-crop.jpg"><img class="wp-image-4945 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01502-crop-1024x682.jpg" alt="" width="640" height="426" /></a><p class="wp-caption-text">Female controller connector on the left, male light string connector on the right.</p></div>
<p>The controller connects to the lights via a 3 pin round connector. These connectors are slightly different than the connectors used on the previous two sets.</p>
<div id="attachment_4946" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01503-crop.jpg"><img class="wp-image-4946 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01503-crop-1024x682.jpg" alt="photo of remote control" width="640" height="426" /></a><p class="wp-caption-text">The remote control included with the set. I never could get it to work. It looks like it&#8217;s an IR remote rather than an RF remote.</p></div>
<p>This set included the fanciest remote of the lot. It appears to be an IR remote control powered by 2 AAA batteries. I could never get it to work.</p>
<h3>Ignore the Bundled App</h3>
<p>The QR code in the instruction manual links to the Lumary app. Unfortunately, the Lumary app requires an account with a verified email address to use the app and lights. Instead of using the bundled app, use the Tuya Smart Life app instead. The Tuya app has no problem pairing with these lights and controlling them. It also has the advantage that it&#8217;s the same app for all the lights I&#8217;ve examined in this post.</p>
<h3>FCC Info</h3>
<p><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231119_174414061-crop.jpg"><img class="size-large wp-image-4942" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231119_174414061-crop-1024x576.jpg" alt="" width="640" height="360" /></a></p>
<p>The FCC ID according to the label on the back of the controller is 2AVB5-SSL.</p>
<div id="attachment_4959" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/string-lights.png"><img class="size-large wp-image-4959" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/string-lights-1024x866.png" alt="The real product associated with this FCC ID?" width="640" height="541" /></a><p class="wp-caption-text">The real product associated with this FCC ID?</p></div>
<p>Searching fccid.io for the FCC ID on the controller <a href="https://fccid.io/2AVB5-SSL">links</a> to a database entry for a patio light string made by the Shenzhen Andysom Lighting Co., Ltd. The power adapter and controller have different form factors as well. I guess it&#8217;s possible the guts are the same between products and that the FCC database is just missing a Model Difference letter. Still seems a bit sus.</p>
<h3>Protocol</h3>
<div id="attachment_4961" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/3-pin-12v-pinout.drawio.png"><img class="size-large wp-image-4961" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/3-pin-12v-pinout.drawio-1024x559.png" alt="Controller and light cable connector pinouts." width="640" height="349" /></a><p class="wp-caption-text">Controller and light cable connector pinouts.</p></div>
<p>The pinout of the connectors is shown above. On the left is the pinout viewed looking into the female connector on the controller cable. On the right is the pinout viewed looking into the male connector on the light cable.</p>
<div id="attachment_4963" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_3.png"><img class="size-full wp-image-4963" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_3.png" alt="Scope trace with the first light set to 100% red. 685 kHz, 5 V logic levels and 8 bits per channel. " width="800" height="632" /></a><p class="wp-caption-text">Scope trace with the first light set to 100% red. 685 kHz, 5 V logic levels and 8 bits per channel.</p></div>
<p>I hooked the ground and data out pins on the controller cable to an oscilloscope. The waveform was the WS2811 protocol at 685 kHz. Each light had 32 bits of control information. The transmission order was 8 bits of red, 8 bits of green, 8 bits of blue, and 8 bits of warm white. Even though there are only six lights on the string, the controller sent data for 100 lights.</p>
<h3>Controlling with WLED</h3>
<h4>Hardware Connections</h4>
<div id="attachment_4979" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231120_004104901-crop.jpg"><img class="size-large wp-image-4979" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231120_004104901-crop-1024x576.jpg" alt="Oh no! White, green, and black wires instead of the usual red, white, and black wires." width="640" height="360" /></a><p class="wp-caption-text">Oh no! White, green, and black wires instead of the usual red, white, and black wires.</p></div>
<p>I cut the light cable off the controller and pulled back the outer jacket to reveal white, green, and black wires instead of the usual red, white, and black wires. I used a DMM and scope to find the power, data, and ground wires. For this set of lights, the white wire was +12 V, the green wire was data, and the black wire was ground.</p>
<h4>WLED Configuration</h4>
<div id="attachment_4985" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/Screenshot-2023-11-19-180613.png"><img class="size-large wp-image-4985" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/Screenshot-2023-11-19-180613-1024x739.png" alt="WLED LED and sync interface configuration panels for these lights." width="640" height="462" /></a><p class="wp-caption-text">WLED LED and sync interface configuration panels for these lights.</p></div>
<p>To use these lights with WLED, the closest matching pixel type is SK6812 RGBW with RGB color ordering. Selecting this pixel type and color ordering in the LED Preferences panel will permit the lights to use all of WLED&#8217;s built-in effects.</p>
<p>If the exact number of lights in the string (6) is entered for the number of pixels, the lights will flicker horribly, especially on the white channel. The stock controller sends 100 lights of data. I recommend configuring WLED for 100 pixels even though there&#8217;s only six lights on the string.</p>
<p>To control these lights over the network, be sure to select Multi RGBW from the Sync Interfaces panel to match the RGBW pixel type.</p>
<h3>Verdict</h3>
<div id="attachment_4982" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231120_010021130-crop.jpg"><img class="size-large wp-image-4982" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231120_010021130-crop-1024x594.jpg" alt="The six miniature spotlights placed around the WLED controller." width="640" height="371" /></a><p class="wp-caption-text">The six miniature spotlights placed around the WLED controller.</p></div>
<p>With these lights, all WLED features including the large selection of built-in effects are available and the pixels are controllable through both the web interface and over the network. Just be sure to set the number of pixels to 100 in the WLED LED configuration panel to prevent flickering. I will likely use these lights to illuminate smaller props like zombie ground breakers at Halloween.</p>
<h2>Set #4: <span id="productTitle" class="a-size-large product-title-word-break">ALFELE RGBCW Outdoor Flood Lights, Set of 6</span></h2>
<div id="attachment_4996" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01508-crop.jpg"><img class="size-large wp-image-4996" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01508-crop-1024x682.jpg" alt="No branding on the box for these lights." width="640" height="426" /></a><p class="wp-caption-text">No branding on the box for these lights.</p></div>
<p>Finally, the last set to examine is the set of 6 <a href="https://amzn.to/3MTQQDD" target="_blank">ALFELE </a><span id="productTitle" class="a-size-large product-title-word-break"><a href="https://amzn.to/3MTQQDD" target="_blank">RGBCW Outdoor Flood Lights</a>. I purchased one set for $99 minus a $15 lightning deal and a $30 coupon for a final price of $54 plus tax.<br />
</span></p>
<h3>Detailed Description</h3>
<div id="attachment_4998" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01510-crop.jpg"><img class="size-large wp-image-4998" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01510-crop-1024x682.jpg" alt="The set includes six lights, six plastic stakes, a power data supply, and an RF remote control." width="640" height="426" /></a><p class="wp-caption-text">The set includes six lights, six plastic stakes, the nuts and bolts to connect the spikes to the lights, a power data supply, and an RF remote control.</p></div>
<p>These lights are the first floodlights that actually look like floodlights! These lights consist of heavy aluminum enclosures with transparent glass fronts. Each light measures about 5.25&#8243; x 3.75&#8243; x 1.25&#8243; and there is 3 meters of cable between each light. Behind the glass are 2 rows of seven warm white LEDs with a row of seven combination RGB LEDs between them.</p>
<p>The lights can be mounted to a wall, ceiling, or deck using the included mounting brackets or anchored to the ground using the included plastic spikes that attach to the mounting brackets. Unlike the other lights, these lights incorporate the AC power supply and wireless control electronics into a single monolithic brick that I&#8217;m calling a power data supply.</p>
<div id="attachment_4999" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01511-crop.jpg"><img class="size-large wp-image-4999" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01511-crop-1024x682.jpg" alt="The power supply for these lights also houses the Wi-Fi/BLE/RF controller." width="640" height="426" /></a><p class="wp-caption-text">The power supply for these lights also houses the Wi-Fi/BLE/RF controller.</p></div>
<p>The power data supply is shown in the photo above. The AC line cord is on the left and the power/data cable to the lights is on the right. The power/data cable connects to the first light in the string using a waterproof 3-pin connector. The power supply is rated for 24 V at 1.75 A or 42 W.</p>
<div id="attachment_5000" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01516-crop.jpg"><img class="size-large wp-image-5000" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01516-crop-1024x683.jpg" alt="Female controller connector on the left, male light string connector on the right." width="640" height="427" /></a><p class="wp-caption-text">Female controller connector on the left, male light string connector on the right.</p></div>
<p>The power data supply connects to the lights via a 3 pin round connector. These connectors are the same size as all the rest of the lights connectors but, yet again, are just different enough to not be compatible.</p>
<div id="attachment_5001" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01514-crop.jpg"><img class="size-large wp-image-5001" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/DSC01514-crop-1024x682.jpg" alt="The set includes a remote control that can control some basic light settings." width="640" height="426" /></a><p class="wp-caption-text">The set includes a remote control that can control some basic light settings.</p></div>
<p>The lights’ basic functionality can be controlled via the included RF remote control or, for more advanced functionality, via the Smart Life – Smart Living app by Volcano Technology Limited.</p>
<h3>FCC Info</h3>
<div id="attachment_4987" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/External-Photos-6211322-unlocked.jpg"><img class="size-large wp-image-4987" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/External-Photos-6211322-unlocked-1024x837.jpg" alt="External photos of the model LSE-042 flood light string with an FCC ID of 2AI5T-LSE-042. This is more or less my set except it has two more lights than mine." width="640" height="523" /></a><p class="wp-caption-text">External photos of the model LSE-042 flood light string with an FCC ID of 2AI5T-LSE-042. This is more or less my set except it has two more lights than mine.</p></div>
<p>The FCC ID for this intentional radiator is not on the box, on the power data supply, or on any of the lights. Fortunately, the box indicates the lights are manufactured by the Shenzhen Bling Lighting Technologies CO.,LTD. Looking them up, they have a grantee code of 2AI5T. The full FCC ID for these lights should then be the grantee code and model number or 2AI5T-LSE-048-W3.</p>
<p>Unfortunately, that FCC ID does not exist. The closest matching ID is 2AI5T-LSE-042. The external photos for the power data supply and lights are a match other than the number of lights. The database entry for the larger -042 model includes a Model Difference letter covering any model with model number formatted as LSE-XXX-YY. The 2AI5T-LSE-042 certification therefore should cover these lights.</p>
<div id="attachment_4989" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/Internal-Photos-6211326-unlocked.jpg"><img class="size-large wp-image-4989" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/Internal-Photos-6211326-unlocked-1024x816.jpg" alt="The insides of the lights' power data supply." width="640" height="510" /></a><p class="wp-caption-text">The insides of the power data supply.</p></div>
<p>From the internal photos, the power data supply includes a switching power supply and a wireless module. The wireless module is a C-Chip CC8000. It also looks like we have another white/green/black wire combination rather than the usual red/white/black wire combination.</p>
<h3>Protocol</h3>
<p><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/3-pin-pinout-24v-2.drawio.png"><img class="alignnone size-large wp-image-4966" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/3-pin-pinout-24v-2.drawio-1024x544.png" alt="3 pin pinout 24v 2.drawio" width="640" height="340" /></a></p>
<p>The pinout of the connectors is shown above. On the left is the pinout viewed looking into the female connector on the controller cable. On the right is the pinout viewed looking into the male connector on the light cable.</p>
<p><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_4.png"><img class="alignnone size-full wp-image-4967" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_4.png" alt="scope_4" width="800" height="632" /></a></p>
<p>I hooked the ground and data pins on the power data supply cable to an oscilloscope. The waveform was the WS2811 protocol at 850 kHz and using 5 V logic levels. Each light had 32 bits of control information. The transmission order was 8 bits of red, 8 bits of green, 8 bits of blue, and 8 bits of warm white. This was repeated once for each light on the string.</p>
<h3>Controlling with WLED</h3>
<h4>Hardware Connections</h4>
<div id="attachment_5006" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231121_232806522-crop.jpg"><img class="size-large wp-image-5006" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231121_232806522-crop-1024x576.jpg" alt="Black, green, and white wires again this time but in a different order!" width="640" height="360" /></a><p class="wp-caption-text">Black, green, and white wires again this time but in a different order!</p></div>
<p>I cut the light cable off the controller and pulled back the outer jacket to reveal white, green, and black wires instead of the usual red, white, and black wires. I used a DMM and scope to find the power, data, and ground wires. For this set of lights, the black wire was +24 V, the green wire was data, and the white wire was ground.</p>
<h4>WLED Configuration</h4>
<div id="attachment_5008" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/Screenshot-2023-11-21-1633421.png"><img class="size-large wp-image-5008" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/Screenshot-2023-11-21-1633421-1024x606.png" alt="WLED LED and sync interface configuration panels for these lights." width="640" height="379" /></a><p class="wp-caption-text">WLED LED and sync interface configuration panels for these lights.</p></div>
<p>To use these lights with WLED, the closest matching pixel type is SK6812 RGBW with RGB color ordering. Selecting this pixel type and color ordering in the LED Preferences panel will permit the lights to use all of WLED&#8217;s built-in effects. To control these lights over the network, be sure to select Multi RGBW from the Sync Interfaces panel to match the RGBW pixel type.</p>
<h3>Verdict</h3>
<div id="attachment_5004" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231121_213606750-crop.jpg"><img class="size-large wp-image-5004" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231121_213606750-crop-1024x576.jpg" alt="Enjoy this photo of the lights set up on the basement floor. " width="640" height="360" /></a><p class="wp-caption-text">Enjoy this photo of the lights set up on the basement floor.</p></div>
<p>With these lights, all WLED features including the large selection of built-in effects are available and the pixels are controllable through both the web interface and over the network. Of all the sets examined, these are likely the best lights for lighting up large surfaces like a fence or the sides of a house. If I were to use them to light up a facade, I&#8217;d place them about every 4 to 6 feet.</p>
<h2>Quick Update: Govee LED Flood Lights and Spot Lights</h2>
<div id="attachment_5099" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231128_004110855.jpg"><img class="size-large wp-image-5099" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231128_004110855-1024x576.jpg" alt="A set of 2 Govee outdoor LED flood lights controlled by WLED over the network using E1.31." width="640" height="360" /></a><p class="wp-caption-text">A set of 2 Govee outdoor LED flood lights controlled by WLED over the network using E1.31.</p></div>
<div class="status__content" tabindex="0">
<div class="status__content__text status__content__text--visible translate" lang="en">
<p>Update on another two sets of inexpensive LED lights: Both the set of 2 <a href="https://amzn.to/4a4SGLD" target="_blank">Govee outdoor flood lights</a> and the set of 2 <a href="https://amzn.to/4a6BPrO" target="_blank">Govee outdoor spot lights</a> use the WS2811 protocol.</p>
<div id="attachment_5100" style="width: 810px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_5-govee-floods.png"><img class="size-full wp-image-5100" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/scope_5-govee-floods.png" alt="scope" width="800" height="632" /></a><p class="wp-caption-text">scope</p></div>
<p>In the case of both the floods and the spots, it&#8217;s 750 kHz, 5 V logic, and 2 lights of 6 channels each. Each channel is 8 bits and the channel ordering is { warm white, cool white, empty, green, red, blue }. The larger sets should work as well but the number of lights will need to be increased to the number of lights in the set.</p>
<div id="attachment_5102" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231203_172856393-crop.jpg"><img class="size-large wp-image-5102" src="https://bikerglen.com/wp/wp-content/uploads/2023/11/PXL_20231203_172856393-crop-1024x576.jpg" alt="A set of 2 Govee outdoor LED spot lights controlled by WLED over the network using E1.31." width="640" height="360" /></a><p class="wp-caption-text">A set of 2 Govee outdoor LED spot lights controlled by WLED over the network using E1.31.</p></div>
<p>Since these are 6 channel fixtures, they won&#8217;t work well with the WLED built-in effects but will work with e1.31/ArtNet/DDP if WLED is configured for multi RGB and 4 WS2811 pixels. They come with a 24 V, 1 A, 24 W AC adapter.</p>
</div>
</div>
<h2>Summary of WLED Settings</h2>
<p>The table below summarizes the different light sets examined, their properties, and the settings required to use them with WLED.</p>
<table width="628">
<tbody>
<tr>
<th colspan="1"></th>
<th colspan="4">Fixture Properties</th>
<th colspan="4">WLED Configuration</th>
</tr>
<tr>
<th>Set</th>
<th># Lights</th>
<th>Volts</th>
<th>Amps</th>
<th>Watts</th>
<th>Pixel Count</th>
<th>Pixel Type</th>
<th>Color Order</th>
<th>DMX Mode</th>
</tr>
<tr>
<td><span id="productTitle" class="a-size-large product-title-word-break">APPECK Smart WiFi Garden Lawn Lights</span></td>
<td>15 lights</td>
<td>24 V</td>
<td>1.0 A</td>
<td>24 W</td>
<td>15</td>
<td>SK6812 RGBW</td>
<td>RGB</td>
<td>Multi RGBW</td>
</tr>
<tr>
<td><span id="productTitle" class="a-size-large product-title-word-break">APPECK Low Voltage Landscape Lights (See note 1.)<br />
</span></td>
<td>6 lights</td>
<td>24 V</td>
<td>1.5 A</td>
<td>36 W</td>
<td>20</td>
<td>WS281x</td>
<td>RGB</td>
<td>Multi RGB</td>
</tr>
<tr>
<td>Lumary <span id="productTitle" class="a-size-large product-title-word-break">Smart Landscape Lights (See note 2.)<br />
</span></td>
<td>6 lights</td>
<td>12 V</td>
<td>1.0 A</td>
<td>12 W</td>
<td> 100</td>
<td>SK6812 RGBW</td>
<td> RGB</td>
<td>Multi RGBW</td>
</tr>
<tr>
<td><span id="productTitle" class="a-size-large product-title-word-break">ALFELE RGBCW Outdoor Flood Lights</span></td>
<td>6 lights</td>
<td>24 V</td>
<td>1.75 A</td>
<td>42 W</td>
<td> 6</td>
<td>SK6812 RGBW</td>
<td> RGB</td>
<td>Multi RGBW</td>
</tr>
<tr>
<td><span id="productTitle" class="a-size-large product-title-word-break">Govee Outdoor Flood Lights and Spot Lights (See note 3.)<br />
</span></td>
<td>2 lights</td>
<td>24 V</td>
<td>1 A</td>
<td>24 W</td>
<td>4</td>
<td>WS281x</td>
<td> RGB</td>
<td>Multi RGB</td>
</tr>
</tbody>
</table>
<p>Notes:</p>
<ol>
<li>The APPECK low-voltage landscape lights use 6 lights of 5 channels of 16 bits. WLED&#8217;s built-in effects cannot be used with these lights but they may be used with WLED and some 3rd party lighting control software.</li>
<li>The Lumary smart landscape lights will flicker especially on the warm white channel if configured for only 6 pixels. For best results, configure WLED to send 100 pixels even though there are only 6 lights in the set.</li>
<li>The set of 2 Govee LED flood lights and the set of 2 Govee LED spot lights both use 2 lights of 6 channels of 8 bits. WLED&#8217;s built-in effects cannot be used with these lights but they may be used with WLED and some 3rd party lighting control software.</li>
</ol>
<h2>What to Look for When Buying Random Cheap Lights off the Internet</h2>
<p>Based on my experience with these four light sets, if a light set meets the following guidelines, it likely uses the WS281x control protocol and could be hacked to work with WLED:</p>
<ol>
<li>Look for low-voltage lights only; no line-powered lights.</li>
<li>Look for an AC adapter followed by a separate controller followed by a string of lights.</li>
<li>Look for lights where there&#8217;s a 3 pin connector between the AC adapter or controller and the first light on the string.</li>
<li>Look for light strings where each light can be set to a different color simultaneously.</li>
<li>Look for lights with an app.</li>
</ol>
<p>Some light sets, like those from XMCOSY+, use PWM to control the brightness of their LEDs. They fit #1, #2, and #5 above but have a 5-pin connector between the controller and the light string (violates #3) and all lights must be set to the same color (violates #4). If you have a PWM-controlled light set, these are not directly controllable using WLED. They could still be hacked to be controlled using DMX and commonly available DMX to PWM decoders.</p>
<p>In my next blog post, we&#8217;ll take a look at a set of Ustellar flood lights that violate guidelines #2 and #3 above. Will they still be controllable using WLED hardware? Find out next time.</p>
<p><em>Disclaimer: Glen may earn compensation for sales from links on this post through affiliate programs.</em></p>
]]></content:encoded>
			<wfw:commentRss>https://bikerglen.com/blog/controlling-inexpensive-led-lights-with-wled/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cracking Open and Controlling a 747 Fuel Gauge</title>
		<link>https://bikerglen.com/blog/fuel-gauge-ac-motor-pid-loop/</link>
		<comments>https://bikerglen.com/blog/fuel-gauge-ac-motor-pid-loop/#comments</comments>
		<pubDate>Thu, 08 Jun 2023 04:05:09 +0000</pubDate>
		<dc:creator><![CDATA[Glen]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://bikerglen.com/blog/?p=4209</guid>
		<description><![CDATA[A video of my hardware and software controlling the pointer and counter on a 747 fuel quantity indicator. In this post, I disassemble a 747 fuel quantity indicator and reverse engineer the electromechanical parts of the indicator. I then apply &#8230; <a href="https://bikerglen.com/blog/fuel-gauge-ac-motor-pid-loop/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<div class="wp-caption alignnone" style="width: 650px;">
<p style="margin-bottom: 0px; margin-top: 0px;"><iframe title="YouTube video player" src="https://www.youtube.com/embed/ZTkho-Eqw9c" width="630" height="355" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>
<p class="wp-caption-text">A video of my hardware and software controlling the pointer and counter on a 747 fuel quantity indicator.</p>
</div>
<p>In this post, I disassemble a 747 fuel quantity indicator and reverse engineer the electromechanical parts of the indicator. I then apply hardware and software techniques used on previous projects to build a PID controller for a control loop consisting of the AC servo motor and feedback potentiometer in the indicator. This control loop is used to position the dial and counter on the face of the instrument to values entered into a serial terminal.</p>
<p><span id="more-4209"></span></p>
<h2>Outward Appearances</h2>
<div id="attachment_4778" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3594-3x2-crop.jpg"><img class="size-large wp-image-4778" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3594-3x2-crop-1024x683.jpg" alt="The front of several 747 fuel quantity indicators I have. This one maxes out at 95,000 lbs of fuel." width="640" height="427" /></a><p class="wp-caption-text">The front of several 747 fuel quantity indicators I have. This one maxes out at 95,000 lbs of fuel.</p></div>
<p>Pictured above is a 747 fuel quantity indicator. It has a dial and pointer that indicate the fuel quantity from 0 to 95,000 pounds. In addition, there&#8217;s a mechanical drum counter that indicates the fuel quantity with a resolution of 100 pounds from 0.0 to 95.0 thousands of pounds.</p>
<p>This gauge is most likely from a 747-200 where a third crewmember, the flight engineer, managed the fuel consumption of the aircraft. In later aircraft, like the 747-400, fuel management was computerized and the flight engineer&#8217;s position was eliminated in favor of a two crewmember flight deck. On these aircraft, the fuel quantity indicators are integrated into the glass cockpit displays of the pilot and first officer.</p>
<div id="attachment_4772" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/747-Fuel-Tanks.png"><img class="size-large wp-image-4772" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/747-Fuel-Tanks-1024x718.png" alt="747 fuel tanks from The 747 Fuel System by E.D. Ayson, R.R. Dhanani, and G.A. Parker of The Boeing Company for the Society of Automotive Engineers." width="640" height="449" /></a><p class="wp-caption-text">747 fuel tanks from <em>The 747 Fuel System</em> by E.D. Ayson, R.R. Dhanani, and G.A. Parker of The Boeing Company for the Society of Automotive Engineers, April 1970.</p></div>
<p>The illustration above is from an article on the <a href="https://www.fire.tc.faa.gov/pdf/fsr-0178.pdf">747 fuel system (PDF)</a> from 1970. The 747-100 pictured has seven fuel tanks. Later versions of the aircraft added an optional 8th tank in the tail. Since jet fuel weighs about 6.7 to 6.8 pounds per gallon depending on formulation and temperature, this gauge is likely from the center tank (12,890 gallons or 87,000 pounds) or one of the two inboard wing tanks (12,240 gallons or 82,620 pounds). The gauge I later modify and control is likely from one of the two outboard wing tanks since it only goes to 35,000 pounds.</p>
<p>Barney Britton has some excellent flight deck photos from the first Boeing 747 aircraft on Tumbler. The last photo in <a href="https://barneybrittonphoto.tumblr.com/post/97337596614/ra001-worlds-first-boeing-747-jumbo-jet">this set</a> shows the flight engineer&#8217;s console. The fuel management system is in the bottom center of the console with yellow knobs on either side of it. In that photo, you can see that the inboard and center tanks have gauges that go to 95,000 pounds, the outboard tank gauges only go to 35,000, and the reserve tanks only go to 3,500 pounds.</p>
<div id="attachment_4777" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3582-3x2-crop.jpg"><img class="size-large wp-image-4777" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3582-3x2-crop-1024x683.jpg" alt="rear" width="640" height="427" /></a><p class="wp-caption-text">The rear view of a 747 fuel quantity indicator. The center contact is a shielded coaxial connection to the fuel level sensor. Behind the F/E panel are small potentiometers for calibrating the indicator.</p></div>
<p>The rear of the fuel quantity indicator is shown in the photo above. It has a cover marked with an F and an E and a large connector. Behind the cover are a few small potentiometers for calibrating the fuel quantity indicator. The connector has a center coaxial contact surrounded by 10 pin contacts. The center coaxial contact connects to the fuel level sensor which is called a tank unit as described in <em>The 747 Fuel System</em>:</p>
<blockquote><p>Tank units, which sense fuel quantity, and compensator units in each tank provide input signals to the corresponding fuel quantity indicators. &#8230; The tank units are variable capacitors, their capacitance varying with the level of fuel in the tank. The compensator units &#8230; provide a correction for variation in dieletric constant with the fuel density.</p></blockquote>
<p>A large metal band around the very end of the fuel quantity indicator seals the end plate and gauge internals inside the main body. The metal band is likely welded in place then covered with gobs of gray paint.</p>
<h2>Internal Photos</h2>
<div id="attachment_4781" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3574-16x9-crop.jpg"><img class="size-large wp-image-4781" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3574-16x9-crop-1024x576.jpg" alt="side 1" width="640" height="360" /></a><p class="wp-caption-text">Internal photo of a 747 fuel quantity indicator. The large black cylinder is the feedback potentiometer for the control loop. The small cylinder below it is the AC servo motor that moves the pointer and counter.</p></div>
<p>One of the indicators I have came with the end seal already removed. These internal photos are from that indicator. In the photo above, 1 of 2 electronics boards is visible at the left end of the gauge. To the right of that is a large black cylinder. The black cylinder is the feedback potentiometer for the control loop that manages the pointer position and counter value.</p>
<p>Below that is a smaller, silver cylinder. The silver cylinder is the AC servo motor used to rotate the pointer and counter. To the right of those are a bunch of gears that couple the servo motor, feedback potentiometer, pointer, and counter together in various ratios. Finally 1 of 2 large yellow capacitors for the motor is visible.</p>
<div id="attachment_4782" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3576-16x9-crop.jpg"><img class="size-large wp-image-4782" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3576-16x9-crop-1024x576.jpg" alt="side 2" width="640" height="360" /></a><p class="wp-caption-text">The other side of the insides of the 747 fuel quantity indicator. A small transformer and the bottom of the mechanical drum counter are visible from this angle.</p></div>
<p>Flipping the indicator over as shown above reveals the second of the two circuit boards in the indicator. The power supply transformer and bottom of the mechanical drum counter are visible from this angle.</p>
<div id="attachment_4788" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3577-3x2-crop.jpg"><img class="wp-image-4788 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3577-3x2-crop-1024x683.jpg" alt="3577" width="640" height="427" /></a><p class="wp-caption-text">Closeup of the highly linear feedback potentiometer. 1 kΩ ±3% with a linearity of ±0.06%.</p></div>
<p>The photo above is a closeup of the feedback potentiometer. The resistance is 1 kΩ ±3%. The linearity spec is ±0.06% which likely places this part at the stratospheric end of expensive.</p>
<div id="attachment_4789" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3581-3x2-crop.jpg"><img class="size-large wp-image-4789" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3581-3x2-crop-1024x683.jpg" alt="3581" width="640" height="427" /></a><p class="wp-caption-text">A closeup of the servo motor and 1 of 2 large capacitors in the indicator. The motor is rated for 26 VAC on both phases.</p></div>
<p>The photo above is a closeup the AC servo motor and some of the gears it turns. The AC servo motor has two windings. Both windings are rated for 26 VAC 400 Hz.</p>
<h2>Mechanical Operation</h2>
<div id="attachment_4798" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/electro-mechanical-diagram.drawio1.png"><img class="size-large wp-image-4798" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/electro-mechanical-diagram.drawio1-1024x665.png" alt="mechanical" width="640" height="416" /></a><p class="wp-caption-text">A reverse engineered block diagram of the electromechanical portions of the fuel quantity indicator.</p></div>
<p>After inspecting the indicator and identifying the various components, it was time to reverse engineer parts of the gauge to see if I could get it working again. The gauge is likely powered from 115 VAC 400 Hz and would require a variable capacitance to control it. Neither of which was something I wanted to try to implement. With some reservation, I decided the stock electronics would have to go and I focused my reverse engineering on the electromechanical parts of the indicator.</p>
<p>The diagram above documents my reverse engineering efforts. Looking at the diagram, a two-phase AC servo motor turns some gears. The gears turn the dial pointer, the drum counter, and the feedback potentiometer. The motor wires and potentiometer wires connect to the power supply transformer and circuit boards. The 90° phase shift required between the motor windings is provided by one of the two large yellow capacitors in the indicator. My plan is to disconnect the seven wires shown on the diagram from the stock electronics and connect them to my own electronics instead.</p>
<h2>Hacking the Gauge</h2>
<div id="attachment_4787" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3584-3x2-crop.jpg"><img class="size-large wp-image-4787" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3584-3x2-crop-1024x683.jpg" alt="hack saw" width="640" height="427" /></a><p class="wp-caption-text">The first step in the hack was to hack open the fuel quantity indicator with a hack saw.</p></div>
<p>So far, we&#8217;ve been looking at a pair of indicators that go from 0 to 95,000 pounds. Both of these indicators were in too good of condition to hack up. I have a third indicator, however, that was in rougher condition. It is from one of the outboard tanks so it only goes from 0 to 35,000 pounds. I decided to make this indicator the subject of my hacks.</p>
<p>The first step in the hack was to get the indicator open. My weapon of choice was a hacksaw. The indicator body is aluminum so a hack saw cut it open fairly easily and quickly. I was careful not to nick the circuit boards or internal wiring. If I ever had to do this again, I&#8217;d probably attack the band at the end of the unit with a rotary tool and pair of pliers.</p>
<div id="attachment_4824" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3595-16x9-crop.jpg"><img class="size-large wp-image-4824" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3595-16x9-crop-1024x576.jpg" alt="The cracked open and separated indicator. I added the green connector the assembly to make connecting to my electronics easier." width="640" height="360" /></a><p class="wp-caption-text">The cracked open and separated indicator. I added the green connector to the assembly to make connecting to my electronics easier.</p></div>
<p>I removed the indicator body from its shell, unscrewed the screws holding the circuit boards in place, and carefully disconnected the seven wires connected to the motor and potentiometer as well as the lamp power and chassis ground wire. These wires were all ridiculously short so I soldered them onto a connector then soldered longer wires onto a mating connector to give the wires more reach.</p>
<div id="attachment_4790" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3589-3x2-crop.jpg"><img class="wp-image-4790 size-large" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3589-3x2-crop-1024x683.jpg" alt="DSC_3589 3x2 crop" width="640" height="427" /></a><p class="wp-caption-text">After carefully separating the electromechanical parts from the electronic parts, I soldered a connector to the motor, lighting, and feedback potentiometer wires.</p></div>
<p>The photo above is a closeup of the connector I added to the indicator wiring and the mating connector that runs to my hardware.</p>
<table>
<tbody>
<tr>
<th>My Pin</th>
<th>Original Color</th>
<th>My Function</th>
</tr>
<tr>
<td>A</td>
<td>BLU</td>
<td>constant motor winding (+)</td>
</tr>
<tr>
<td>B</td>
<td>VIO</td>
<td>constant motor winding (-)</td>
</tr>
<tr>
<td>C</td>
<td>YLW-ORN</td>
<td>variable motor winding (+)</td>
</tr>
<tr>
<td>D</td>
<td>WHT-ORN</td>
<td>variable motor winding (-)</td>
</tr>
<tr>
<td>E</td>
<td>GRN-ORN</td>
<td>chassis / lighting ground</td>
</tr>
<tr>
<td>F</td>
<td>YLW-BRN</td>
<td>lighting (+)</td>
</tr>
<tr>
<td>H</td>
<td>BRN-BLU</td>
<td>POT WIPER</td>
</tr>
<tr>
<td>J</td>
<td>WHT-GRY</td>
<td>POT CCW (GND)</td>
</tr>
<tr>
<td>K</td>
<td>BRN</td>
<td>POT CW (+3.3V)</td>
</tr>
</tbody>
</table>
<p>The table above documents the wiring of the connector I added and each pin&#8217;s function.</p>
<h2>The Controller</h2>
<div id="attachment_4806" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/system-diagram.drawio2.png"><img class="size-large wp-image-4806" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/system-diagram.drawio2-1024x426.png" alt="The block diagram of my replacement controller for the 747 fuel quantity indicator is shown above." width="640" height="266" /></a><p class="wp-caption-text">The block diagram of my replacement controller for the 747 fuel quantity indicator is shown above.</p></div>
<p>The block diagram for my controller for the indicator is shown above. This controller combines a two channel version of the AC power supply I used for my <a href="https://bikerglen.com/blog/the-kollsman-electric-tachometer-indicator/">tachometer project</a> and a <a href="https://en.wikipedia.org/wiki/PID_controller">PID controller</a>. In this case, the AC power supply frequency is fixed at 400 Hz.</p>
<p>The first channel of the AC power supply has a fixed magnitude and drives the blue / violet winding of the AC servo motor. A large series capacitor on the blue / violet winding ensures the two motor windings are driven 90° out of phase as required by an AC induction motor.</p>
<p>The second channel of the AC power supply has its sign and magnitude scaled by the output of the PID controller and drives the yellow / white winding of the AC servo motor. This winding controls the speed and direction of the AC servo motor in response to the output of the PID controller. (For more details on AC servo motor operation, see the AC servo motor section of my <a href="https://bikerglen.com/blog/relive-your-rides-on-an-altimeter/">altitude indicator project</a>.)</p>
<p>The motor then drives the dial pointer, drum counter, and feedback potentiometer through a series of gears. The voltage across the potentiometer represents the current value of the dial and counter. The voltage is digitized using an analog-to-digital converter and used to compute the error between the desired pointer / counter value and the current pointer / counter value. The error is input to the PID controller to close the control loop.</p>
<h2>The Hardware</h2>
<div id="attachment_4784" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3588-3x2-crop1.jpg"><img class="size-large wp-image-4784" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3588-3x2-crop1-1024x683.jpg" alt="hardware" width="640" height="427" /></a><p class="wp-caption-text">The hacked fuel quantity indicator and my controller hardware.</p></div>
<p>The hardware for this project is exactly the same as the hardware for the <a href="https://bikerglen.com/blog/the-kollsman-electric-tachometer-indicator/">tachometer project</a> except this project makes use of one of the Raspberry Pi Pico&#8217;s analog-to-digital converter channels and only requires 2 of the 3 DAC and amplifier channels to be stuffed. The feedback potentiometer is connected to the ADC channel using the 3-pin header on the Pico adapter board. The blue / violet motor winding is connected to the first DAC channel and the yellow / white motor winding is connected to the second DAC channel.</p>
<div id="attachment_4785" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3590-16x9-crop1.jpg"><img class="size-large wp-image-4785" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/DSC_3590-16x9-crop1-1024x576.jpg" alt="hardware" width="640" height="360" /></a><p class="wp-caption-text">Another view of the hacked fuel quantity indicator and my controller hardware.</p></div>
<p>The photo above is another view of the hardware. This photo shows the face of the outboard wing tank fuel quantity indicator for the first time.</p>
<h2>The Software</h2>
<p>The software for this project consists of three main tasks:</p>
<ol>
<li>The first task is to prompt the user for the number of thousands of pounds to indicate on the dial and counter. This value is transformed into a 12-bit set point. This task runs as needed to receive an input from the user using a simple command line interface.</li>
<li>The second task is to read the RP2040&#8217;s A/D converter to obtain the current position of the pointer and counter, calculate the error between the set point and the current position, and run the PID controller to calculate the next gain value that&#8217;s used to adjust the magnitude of the AC servo motor&#8217;s control winding voltage. This task runs at 100 Hz.</li>
<li>The third and final task is to generate the two channels of sine waves to control the motor. This task runs at 40 kHz to output a sine wave that has been sampled at 100 discrete points. This results in 400 Hz sine wave outputs.</li>
</ol>
<h3>User Input Task</h3>
<p>The code for the user input task is shown below:</p>
<pre>// run get command state machine to get a line of input (non-blocking)
GetCommand ();

// once a line of input is received, process it
if (cmd_state == 2) {
    int index = 0;
    char *buffptr = strtok (cmd_buffer, ",");
    while (buffptr != NULL) {
        switch (index++) {
            case 0:
                target = pwl_interp (atof (buffptr));
                target = (target &gt; 4095) ? 4095 : target;
                target = (target &lt;    0) ?    0 : target;
                printf ("target = %d\n", target);
                break;
        }
        buffptr = strtok (NULL, ",");
    }
    cmd_state = 0;
}</pre>
<p>This code sits in the main loop and runs the command interpreter state machine as often as it can. Once a return (0x0d) is encountered in the input stream, the command line is processed.</p>
<p>The first value entered on the command line is converted to a floating point number representing the number of tens of thousand of pounds to indicate on the indicator. For example, entering 10.3 would be interpreted as needing to position the pointer just clockwise of the 10 mark while moving the counter to indicate 10.3.</p>
<p>This value is converted to the expected analog-to-digital reading for this location on the dial and counter using a piecewise linear interpolation scheme and a sparsely populated table of known pounds to A/D readings. In the case of 10,300 pounds, the expected A/D reading would be 1338. This value, 1338, becomes the set point for the PID controller.</p>
<h3>PID Controller Task</h3>
<p>The PID controller task runs at 100 Hz. The code is shown below:</p>
<pre>//----------------------------------------
// run pid loop
//----------------------------------------

// get lots of adc readings
sum = 0;
for (i = 0; i &lt; 512; i++) {
    sum += adc_read (); 
}
position = round (sum / 512.0);

// calculate error
error = target - position;

// calculate P term
pTerm = KP * error;

// calculate I term
sumError += error * Ts; 
if (sumError &gt; Imax*Ts) sumError = Imax*Ts;
if (sumError &lt;= -Imax*Ts) sumError = -Imax*Ts;
iTerm = KI * sumError;

// calculate D term
deltaError = error - lastError;
lastError = error;
currentFilterEstimate = (alpha*previousFilterEstimate) + (1-alpha)*deltaError;
previousFilterEstimate = currentFilterEstimate;
dTerm = KD * currentFilterEstimate / Ts; 

// add terms together
float newScale = pTerm + iTerm + dTerm;

// saturate result
if (newScale &gt; 1.0) newScale = 1.0;
if (newScale &lt; -1.0) newScale = -1.0;

// update speed and direction for core 1 ISR
critical_section_enter_blocking (&amp;scale_critsec);
scale = newScale;
critical_section_exit (&amp;scale_critsec);</pre>
<p>Every 10 ms, this code gets an A/D reading, calculates the error between the set point and the A/D reading, calculates and sums the P, I, and D terms of the controller, saturates the result, then safely writes the saturated result to the sine wave generator task using a critical section.</p>
<p>The A/D reading actually consists of 512 A/D readings taken in rapid succession and averaged together. This removes noise in the potentiometer and A/D converter that can wreak havoc with the derivative term of the PID controller. These effects will be discussed in the next section of this post.</p>
<p>The error, <em>error</em>, is simply the difference between the set point, <em>target</em>, and the actual position, <em>position</em>. The next few lines calculate the P, I, and D terms of the PID controller. These calculations rely heavily on a bunch of constants defined earlier in the file. The values of these constants are the result of tuning the PID controller and will vary from system to system. Finally, the sum of the P, I, and D terms is saturated to a value between -1.0 and +1.0 and passed to the interrupt service routine that generates the sine waves.</p>
<h3>Sine Wave Generator Task</h3>
<p>The sine wave generator tasks runs at 40 kHz and generates a pair of 400 Hz sine waves using a table of 100 samples of a single complete sine wave:</p>
<pre>bool repeating_timer_callback_40kHz (struct repeating_timer *t)
{
    uint16_t a;

    a = 0xB000 | ((uint16_t)dac0B &lt;&lt; 4);
    gpio_put (SPI_CS0n_PIN, 0);
    spi_write16_blocking (SPI_IF, &amp;a, 1);
    gpio_put (SPI_CS0n_PIN, 1);

    a = 0xB000 | ((uint16_t)dac1B &lt;&lt; 4);
    gpio_put (SPI_CS1n_PIN, 0);
    spi_write16_blocking (SPI_IF, &amp;a, 1);
    gpio_put (SPI_CS1n_PIN, 1);

    if (++sin_phase &gt;= 100) {
        sin_phase = 0;
    }

    critical_section_enter_blocking (&amp;scale_critsec);
    dac0B = 128+sine[sin_phase];
    dac1B = 128+scale*sine[sin_phase];
    critical_section_exit (&amp;scale_critsec);

    return true;
}</pre>
<p>This code outputs the current value of the sine waves to the DACs using one of the Pico&#8217;s two SPI peripherals. It then updates the sine waves&#8217; phase modulo 100 since the sine wave consists of 100 points. Once the phase is updated, it calculates the next value of the fixed-magnitude sine wave used to power the AC servo motor and the next value of variable-magnitude sine wave used to control the AC servo motor. The calculations are done inside a critical section so that the PID controller cannot write a new value of <em>scale</em> while the interrupt service routine is using it.</p>
<h2>Putting it All Together</h2>
<div id="attachment_4847" style="width: 650px" class="wp-caption alignnone"><a href="https://bikerglen.com/wp/wp-content/uploads/2023/06/PXL_20230606_223939021-crop.jpg"><img class="size-large wp-image-4847" src="https://bikerglen.com/wp/wp-content/uploads/2023/06/PXL_20230606_223939021-crop-1024x683.jpg" alt="The complete test setup." width="640" height="427" /></a><p class="wp-caption-text">The complete test setup ready to go on the bench.</p></div>
<p>In the photo above, the Raspberry Pi Pico, adapter board, analog op amp &amp; buffer board, and fuel quantity indicator are connected together. The red, yellow, and black wires go to a bench supply set for <span class="ILfuVd" lang="en"><span class="hgKElc">±</span></span>12 V with the current limit set at 150 mA. The yellow and orange wires connect the Pico&#8217;s serial port to a Raspberry Pi 400&#8217;s serial port. The purple, blue, and black wires connect the Pico&#8217;s SWD connector to the same Rasppberry Pi for programming and debugging. A micro USB cable supplies +5V power to the Pico.</p>
<h2>Results</h2>
<div class="wp-caption alignnone" style="width: 650px;">
<p style="margin-bottom: 0px; margin-top: 0px;"><iframe title="YouTube video player" src="https://www.youtube.com/embed/ZTkho-Eqw9c" width="630" height="355" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>
<p class="wp-caption-text">A video of my hardware and software controlling the pointer and counter on a 747 fuel quantity indicator.</p>
</div>
<p>With some fine tuning, the project worked! Two issues were encountered while building the project. These issues are documented below.</p>
<h3>Derivative Issues</h3>
<p>The derivative term of a PID controller is extremely sensitive to noise in the A/D converter readings. When I used only a single A/D reading per 100 Hz PID update cycle, the derivative term would cause the control value calculated in the PID loop to rapidly jump back and forth between -1.0 and 1.0 even though the pointer was very near or at the set point.</p>
<p>This resulted in high current consumption and the AC servo motor rapidly moving back and forth and changing direction without really moving the pointer or counter much. Even though the pointer and counter looked like they weren&#8217;t moving, this was causing wear and tear on the AC servo motor and drive gears which were moving.</p>
<p>Averaging 512 samples together as shown in the code above eliminated the noise and the issues with the derivative term of the PID controller. The AC servo motor now all but stops moving when the set point is reached.</p>
<h3>Issues Around 22,500 Pounds</h3>
<p>The second issue could not be solved. The potentiometer has a noisy or dead spot around 22,500 pounds. This results in the pointer and counter being a bit off from the set point when the set point is in the neighborhood of 22,500 pounds. The problem is worse when approaching this region from one direction than the other. Although I believe the aircraft this indicator came from was dismantled for parts, it&#8217;s possible this indicator was removed for this issue before the final decommissioning of the aircraft.</p>
<h2>Conclusion</h2>
<p>I would have preferred to operate the gauge intact and without permanent modifications but given the high AC supply voltage, the variable capacitor sensor, and the scarcity of the gauge&#8217;s mating mixed-contact connector, I did not deem this feasible with the parts and equipment I had accessible. Instead, I hacked the gauge to replace the normal electronics with my electronics. In a future revision of this project, I&#8217;d like to miniaturize my electronics so that they&#8217;d fit in the existing gauge enclosure.</p>
<h2>Downloads</h2>
<p>The board design files and software for this project are available in the <a href="https://github.com/bikerglen/avionics/tree/main/747-fuel-gauge">747 Fuel Gauge</a> directory of my <a href="https://github.com/bikerglen/avionics/">avionics repository</a> on Github.</p>
<h2>References</h2>
<p><a href="https://www.boeing.com/commercial/aeromagazine/aero_09/fuel_story.html">Boeing Aircraft Fuel Management</a></p>
<p><a href="https://www.fire.tc.faa.gov/pdf/fsr-0178.pdf">The 747 Fuel System (PDF)</a></p>
<p><a href="https://barneybrittonphoto.tumblr.com/post/97337596614/ra001-worlds-first-boeing-747-jumbo-jet">Boeing 747 RA001 Flight Deck Photos</a></p>
<p><a href="https://controlstation.com/blog/derivative-affect-pid-controller-performance/">How Does the Derivative Term Affect PID Controller Performance?</a></p>
<p><a href="https://controlstation.com/blog/the-d-in-pid-stands-for-do-not-use-sometimes/">The “D” in PID Stands for: Do Not Use (Sometimes)</a></p>
]]></content:encoded>
			<wfw:commentRss>https://bikerglen.com/blog/fuel-gauge-ac-motor-pid-loop/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
