Projects Research & Design Hub

Tunable Spectrum Light Fixture

Written by Emmett Milliken

Intelligent Illumination

Getting the right lighting is tricky, especially when you’re trying to shoot a video. Any given room may have multiple inside light sources combined with sunlight from a window. But what if you could replicate that ambient light by mimicking the spectral power of any light source? In this project article, learn how this Cornell student built a system to do just that, using a PIC32 MCU, LEDs and the DMX-512A smart lighting protocol.

  • How to build a system that replicates ambient light by mimicking the spectral power of any light source
  • How to implement the DMX-512A smart lighting protocol
  • How to control LEDs using a PIC32 MCU
  • How to craft a user interface using a three-digit, eight-segment display
  • How to develop multi-threaded software using the stackless threader Protothreads

  • Microchip PIC32 MCU
  • Maxim Integrated MAX481CPA+-ND RS-485 transceiver
  • Mean Well PS-255 5V power supply
  • Saber Z4 LEDs from Luxeon Star LEDs
  • BCD to seven segment decoder
  • Mathwork’s MATLAB

Inspiration for this project came from the idea of a light fixture that could duplicate the ambient light in a room. The motivation for this was for situations such as making videos indoors, where there might be multiple light sources, including sunlight and overhead fluorescent lights, yet still not enough for recording. In working toward that concept, I developed a light fixture that can be tuned to mimic the spectral power distribution (SPD) of other light sources (Figure 1). The SPD of a light source determines both the overall perceived color of the light and how clearly the colors of an object are illuminated by that light.

FIGURE 1 – My tunable spectrum light fixture

LEDs serve well as a tunable light source, because they have a narrow enough spectral bandwidth to allow for fairly distinct regions of control. At the same time, their bandwidth is wide enough to cover the visible spectrum with a reasonable number of individual types and not end up being incredibly choppy with big gaps. Figure 2 shows typical spectral power distributions of common light sources.

FIGURE 2 – Spectral power distributions of common light sources


Advertise Here

While the device may be able to sense spectra and tune itself one day, I wanted both a simpler starting point and the versatility of having manual control. I accomplished this by using DMX-512A (hereafter referred to as DMX), the control protocol used most widely in the intelligent lighting industry.

Aside from the Microchip Technology PIC32 microcontroller (MCU), the three main elements of the system are the DMX transceiver, the user interface for setting the DMX base address and the LEDs with their drivers. A block diagram of the hardware of the fixture is shown in Figure 3. The DMX transceiver converts the received signal to a signal compatible with the PIC32. The user interface subsystem consists of three buttons and a display to show the current base address. The two blue buttons increase and decrease the base address, and the red button was added to change between operation modes. The LED subsystem controls the output of the LEDs, using pulse width modulation (PWM) through shift register and a low-side switch for each LED. See Figure 4 for a full schematic of the fixture.

FIGURE 3 – Block diagram of light fixture. Red lines represent power, and blue lines represent data.

FIGURE 4 – Complete schematic for the tunable spectrum light fixture.

DMX control is split into “universes”—each containing 512 “channels.” A channel is an 8-bit value that typically controls a single parameter of a fixture, such as intensity, pan or tilt. The more things a fixture can do, the more channels it will need to control. All fixtures in the same universe will be sent the same packet, which will have as many as 512 channels. Each fixture is assigned a base address for the starting point of its channel data in the packet.

In more technical terms, DMX is a serial communication protocol that uses an RS-485 electrical interface and 5-pin XLR connectors. Each DMX packet starts with a long break, followed by 513 bytes of data. The first byte is a start code, indicating the type of information in the packet, and the following 512 bytes are data. The RS-485 signal has to be converted to a single-sided signal that the PIC can understand. To do this, I used a Maxim Integrated MAX481 IC, which is an RS- 485 transceiver. The connections are shown in the full schematic (Figure 4).

The user interface is composed of a three-digit, eight-segment display, the hardware to drive the display and three buttons. Figure 5 shows the user interface from the outside of the fixture. Each button is connected to high on one side, and to an input pin of the PIC on the other side. A 330Ω resistor is used to protect the pin, as good practice. An internal 10kΩ pull-down resistor completes the path to ground, forming a usually LOW-HIGH-ON button press input. Two of the buttons are used to increase or decrease the DMX base address, and the third button is intended for future use. The three-digit display is a 12 lead package, with a single pin for each of the three digits, a single pin for each of the eight segments and one unconnected pin

FIGURE 5 – Front panel outside


Advertise Here

I used an 74LS7447 chip to convert from binary coded decimal (BCD) to a driver for a seven-segment display, which excludes the dot point. The 74LS7447 driver can only output values for one digit at a time. The display also allows for only one number to be written to any of the digits at one time. That’s because the segment logic must be the same for all the digits, since they share a single pin. To display three digits, I had to flash between the three numbers—only powering one digit at a time. This created a less bright display, but was good enough for the purposes of this project.

Representing a number between zero and nine requires 4 bits, which the 74LS7447 takes in parallel. To control the three digits separately, I also needed to be able to turn them on and off. This would have taken seven output pins. Similarly, for the LED control, I used an 8-bit shift register. The PIC has a 32-bit SPI mode, which supports up to four, 8-bit shift registers in a row. This enables the device to have only one line that sends data to all 12 LEDs and the front panel display.

I combined the display and the buttons on a board for chassis mounting. To keep the rest of this subsystem together, I created a second board that could be sandwiched on the back. Figure 6 and Figure 7 show the separated front panel and display driver circuit boards, and the combined board assembly, respectively. By using this design, I was able to have fewer wires going between physical components.

FIGURE 6 – Separated front panel circuit

FIGURE 7 – Assembled front panel circuit

The transceiver and user interface set levels for the LEDs. For this project, I wanted high-power illumination LEDs in a range of colors. Luxeon Star LEDs sells custom color mixing boards with high-power color LEDs, at a price point low enough to fit my budget. (A table with a breakdown of my component costs is available in RESOURCES at the end of this article.) I ordered the 12 unique LEDs, pre-soldered onto three aluminum PCBs, from Luxeon’s Saber Z4 line. Due to the small size and tight packing, I was able to use three lenses for all 12 of the LEDs, which improved the color blending. Additionally, the aluminum PCBs allowed for direct bonding of the PCBs to a heatsink, to dissipate the heat generated by the LEDs.


Advertise Here

For the LED driver circuit, I used a low-side switch controlled by a TIP31 transistor. An LED is connected in series with a resistor and then transistor, with the LED anode connected to 5V, and the NPN transistor emitter connected to ground. The transistor allows current to flow through the LED only when the voltage applied to the base of the transistor exceeds the turn-on voltage. There are 12 copies of this circuit, one for each LED.

The bases of the transistors are connected to the output pins of shift registers. When the shift register is outputting low, the LED will be off, and when the shift register is high, the LED will be on. Using serial-in, parallel-out shift registers allows control of all 12 LEDs using only three pins for SPI data, clock, and enable. To simplify the wiring, I connected the LED driver circuits to whichever shift register bit was closest. I made up for the resulting jumbled order of colors by reordering the data in software.

I used a 5V, 5A power supply to power all the LEDs and everything else. For safety and convenience, I also included an IEC C14 socket—which is commonly used for desktop computers and monitors—to allow the fixture to be disconnected without unplugging it from the wall, and a rocker switch interrupting the hot wire of the 120V AC connection. The 5V power supply can power all components of the device, from the microcontroller, to the logic, to the LEDs. Using a single power supply greatly simplified wiring.

The software has several tasks that are split into threads. One thread receives DMX packets over UART and stores the most recent valid packet. Another manages the DMX base address, allowing the user to change the address from the front panel. The 12, single-byte DMX channel values must then be interpreted, so that the correct value can be set for the intensity of each LED. Finally, a separate thread generates the PWM signals to control the LEDs.

The design of the software relies heavily on outside code. I used Adam Dunkel’s light-weight, stackless threader, Protothreads [1]. I also used two header files from Bruce Land [2] [3] as the basis of my project, because they include Protothreads and a serial receive thread. I made only small changes to serial receive, to receive DMX packets. DMX packets start with a break, which is a period where the signal stays low longer than a standard character. This generates a UART error, which must be cleared to receive the rest of the packet. If the error is not cleared, the UART will stop and not receive anything else. This is a feature of PIC32, where a UART overrun error locks up the receive FIFO until the overrun error bit is cleared. There are no stop characters in DMX, so I removed that as a possible reason to stop receiving. I also added some variables to help track if the most recent packet was a good, valid packet.

Every time the serial thread is executed, it checks to see if the most recent packet received by the UART reached the full 513 character. If it has, the dmx_rx_buffer is filled with those values, discarding the break and start characters. Then the color levels, or duty cycle out of 255, for each color is set to the corresponding DMX value, determined by the color and DMX base address. Then a thread is spawned to prepare to receive the next UART packet.

The main function sets up the timer that produces the PWM signals. Then it sets up the SPI channel that sends the signal to the shift registers. It also sets up the buttons by setting those pins to inputs with pull-down resistors, turns on SPI, sets up all the threads, turns on system-wide interrupts needed for Protothreads, initiates the threads and then schedules them.

The timer2 interrupt generates PWM signals by writing new values out SPI to a line of shift registers. The timer interrupt is running at 100kHz, so with 8 bits of resolution on the PWM, the LED refresh rate is around 391Hz, which is plenty fast to look constant to the human eye. The PWM is generated by comparing a temporary counter to the duty cycle of each color, and setting that bit high or low. The light output bits are ORed together with a rotating set of three values, corresponding to enabling one digit of the display and the value that it should display in that digit.

I measured the spectral output of each LED at the highest setting on the fixture, using a small, integrating sphere. The results are shown in Figure 8. The amplitude of the curves at any given point is based on the number of counts detected at the wavelength. These data allowed me to calculate the expected total spectral output of the fixture, given the DMX intensity input values for each LED.

Figure 8 – Measured spectral power distribution of LEDs in the fixture.

As a proof of concept, I created a Mathwork’s MATLAB script that calculates the DMX input values, based on a “least squares best fit” scheme to reference spectra shown in Figure 2. Plots of the resulting simulated spectral power distributions are shown in Figure 9. The fixture successfully duplicates the overall shape of the SPD. While some specific wavelengths cannot be reproduced using this particular set of LEDs, the spectral content of the light is still much closer to the reference spectra than that produced by a fixture with only RGB LEDs. These approximations are determined only from the best least squares fit, which isn’t necessarily the best fit in terms of perceived color.

FIGURE 9 – Simulated output spectra based on a “least squares best fit” scheme to calculate DMX input values

Given that most people have only three types of color receptors in their eyes—and there are 12 different LEDs—it is possible to produce multiple light outputs which, when viewed directly, are perceived as the same color. However, if light of the same perceived color but with different spectral content is reflected off of an object, the object may not look the same. Because of this, the next two steps are to find which DMX input values best recreate the perceived color of the light, and then to determine which of these have the most similar spectral content. Preliminary tests showed that the high-power LEDs in this project provided strong illumination with effective color balance and control.

All in all, this project accomplished what I set out to do: Create a DMX-controlled lighting fixture, which, with outside control, could duplicate a given spectral power distribution. Special thanks to Clifford Pollock for the use of his lab for spectral power measurements, and to Bruce Land for his guidance throughout the process of this project. 


[1] Protothreads by Adam Dunkels:
[2] Bruce Land’s modified Protothreads configuration header file, originally authored by Syed Tahmid Mahbub:
[3] Bruce Land’s Protothreads header file for PIC32:

Bruce Land’s SPI:

Bruce Land’s UART:

ProtoThreads, headers, example code:

DMX-512A Specification:  (Login Required)


Saber Z-4 Datasheet:

Luxeon Z Datasheet:

Luxeon Z Color Line Datasheet:

Luxeon UV U Line Datasheet:

Kingbright Three Digit Numeric Display Datasheet:

Mean Well Power Supply Datasheet:


Name Part Number Cost Quantity Total Cost Vendor
PIC32MX250F128B   $5.00 1 $5.00  
5V power supply PS-255 $7.00 1 $7.00 All Electronics
RS-485 transceiver ic MAX481CPA+-ND $2.65 1 $2.65 Digi-Key
BCD to seven segment decoder SN7447AN $3.15 1 $3.15 Digi-Key
Saber Z4 (red, UV, pc amber, cyan) SZ-04-H6U7A6H2 $21.70 1 $21.70 Luxeon Star LEDs
Saber Z4 (red-orange, white, lime, blue) SZ-04-H7K1H9H3 $16.71 1 $16.71 Luxeon Star LEDs
Saber Z4 (deep red, royal blue, amber, green) SZ-04-H5H4H8H1 $15.44 1 $15.44 Luxeon Star LEDs
Frosted 12° 20mm optic (plus optic holder) 10194 (10432) $2.60 3 $6.30 Luxeon Star LEDs

Kingbright |
Luxeon Star LEDs |
Mathworks |
Maxim Integrated |
Mean Well |
Microchip Technology |


Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.

Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article

Emmett Milliken is a student in Electrical and Computer Engineering at Cornell University. His academic interests include embedded systems, solid-state lighting and robotics.

Supporting Companies

Upcoming Events

Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

Tunable Spectrum Light Fixture

by Emmett Milliken time to read: 11 min