Tiny Color Mixer - a Constant-current, 3W RGB LED With Low-battery Indicator and Polymorph Diffuser




About: Film producer and former video game programmer. Electronics enthusiast and amateur maker.

Controlling an RGB LED is a simple and common microcontroller project, often the next project done after blinking and fading an LED.

To make this more interesting, I built it using an ATtiny13, one of the smallest and simplest AVRs, which required "high-voltage programming." I also used a 3W LED because I wanted to learn how to make a constant-current driver. Then I added a low-battery indicator, since that seemed like a useful skill for other projects, and mounted the PCB onto acrylic. (I'd never worked with acrylic and heard it was difficult to drill and cut, but had no issues with either.) Finally, I made a diffuser out of polymorph - I had tried polymorph a few weeks earlier to make the cover for my electronic candle, and I wanted more practice shaping it.

This was made from parts I had lying around. To buy all the components would be about $25, with most of that for the $15 high-power LED. Because this build was to practice with various electronics concepts, I'll use each step to discuss what I learned, hoping that information might be useful for one of your projects.

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

Step 1: Build & Program It

With the exception of the power regulation, this is essentially the same circuit repeated three times, one for red, green, and blue. Three of the ATtiny's pins - PB0, PB1, PB2 - are configured as outputs and are used to rapidly turn on and off a transistor using PWM, which in turn turns on and off the red, green, or blue LED attached to it. (An RGB LED is really just three LEDs mounted in a single package.) The other three pins on the ATtiny13 - PB3, PB4, and PB5 - are configured as inputs and run through the chip's analog-to-digital converter. Each pin is attached to a 10K potentiometer which acts as a voltage divider, providing from 0 to 5 volts depending on whether the knob is turned all the way left or right. The microcontroller converts this voltage to a value from 0-255, which it then uses to determine what percentage of the time the corresponding output pin is on, thus turning the blue knob's position into the blue LED's brightness, etc.

My Eagle schematic and board layout are attached, as well as the print-out of the board's traces which I used to etch the PCB. There are a few jumper wires on top of the board since I've yet to try double-sided etching. The three long wires from the POTs to the ATtiny are also shown on top of the board, although to make it look nicer, I put them on the back of the board using insulated wire.

The RGB LED comes mounted to a heatsink star, but Sparkfun's documentation says that if all three LEDs are fully on, then additional heat sinking is needed. Figuring I'd certainly want to turn all three knobs up and see how bright of a white light this can produce, I decided to attach the LED's star to a larger 1" aluminum square (1/8" thick). The LED isn't really attached as much as held in place by the six power wires, although I did place thermal compound between the two plates, which also keeps it in place.

The LM317's get pretty hot as well -- they are each dissipating around 2-watts -- so I screwed TO-220 heatsinks with thermal grease onto each of those as well.

The C source code is attached. Since the ATtiny13 only has two hardware PWM pins, the PWM here is done "manually." That is, every time the timer counter register overflows, the interrupt routine checks to see whether it's time to turn off the red, blue, or green LED. The chip is running at its default speed of 9.6MHz, which means the overflow interrupt triggers at 37.5KHz (9.6MHz/256), which means the PWM speed is only 146Hz (37500/256). There are ways to update this much faster, but I didn't see any reason to since 146Hz is still well above people's flicker fusion threshold.

Step 2: Disabling RESET to Get One More I/O Pin

The ATtiny13 has just 8 pins, 2 of which are needed for power and 1 of which is normally reserved for reset. With three analog inputs and three PWM outputs, this uses every possible pin, and requires disabling the reset pin.

In one sense, dedicating a microcontroller pin for reset seems wasteful. How often does a device actually need to be reset as opposed to just momentarily cutting the power? With 40 or more pins on most microprocessors, why not have a dedicated reset pin? But with only six, that's giving up more than 15% of your data possibilities. Of course, I could just step up to the next bigger chip - after all, the 14-pin ATtiny24 series is only a few cents more than the 8-pin series, the 20-pin series only a few cents more than that, and the 28-pin ATmegas only slightly more. Then again, using all six pins on an 8-pin chip does seem more elegant, and it seemed like a fun bonus challenge.

The challenge is that In-System Programming (ISP) requires a reset to interrupt the chip and put it into programming mode. But if we disable the reset pin (i.e. turn it into an ordinary I/O pin by clearing bit 0 of the high fuse), then how will we re-program the chip if any changes are needed? One easy option: Don't -- program it right the first time, and if you need to re-program it, get another chip - they're cheaper than a Snickers bar. But the more interesting way is to use high-voltage programming.

That sounds cooler and more exotic than it is. AVR chips run at 1.8 - 5.5 volts, but if you apply 12 volts to the reset pin, rather than being fried, the chip will go into a special programming mode even if the reset pin has been "disabled" into a regular I/O pin. An ISP programmer, like Adafruit's USBtinyISP, is connected to your computer's 5v USB port and can't generate the 12 volts, but ATmel makes several programmers that can.

Or for a fun DIY option, there are several ways to build a high-voltage programmer, or at least a high-voltage fuse resetter that can return an AVR to its default state of having a normal reset pin so that you can then use your ISP. Mnedix has a great instructable for doing this, although I used this Arduino-based one by Paul Willoughby. I had a Sparkfun Arduino-clone that I'd never used, so in just a few minutes I hooked it up to my power-supply and was able to "recover" AVRs that had their fuses locked into non-ISP friendly configurations.

Step 3: Constant Current Driver Using LM317

This project is similar to Project #3 in Dhananjay Gadre's TinyAVR book. Gadre's one uses a regular 5mm RGB LED, so it doesn't require any transistors. I had a 3W RGB LED, so I figured I'd use that and make a much brighter light. (Sadly, in the process of figuring out this driver, I managed to burn out my $15 LED and needed to buy another one.)

The best way to use a high-powered LED is to drive a fixed current through it, which in the case of this LED is up to 350 milliamps for each color. In my first instructable, I used four 3W white LEDs and didn't bother making a constant current driver. I didn't know how and figured it wasn't necessary since these were strobe lights that were only on for a few milliseconds at a time, and they still work fine years later. The problem with using the normal LED scheme of just adding a resistor in series is that you don't know the exact resistance of the resistor (most are plus or minus 5%), you don't know the exact voltage drop of the LED (typically plus or minus .2 volts), and you don't know your starting voltage (which could be plus or minus several volts as the battery goes from fully charged to fully drained). So the current you picked a resistor for might end up being double what you expected, which might burn out a bright LED over time. (This is presumably true for regular 5mm LEDs too, but if you're trying to run 20 milliamps through them and end up running 40, they seem to be able to handle the extra heat. And even if not, they only costs a few cents.)

The easiest way I could find to make a constant-current driver is to use an LM317 adjustable voltage regulator. This chip always tries to pump enough current on its output pin so as to be 1.25 volts higher than the voltage it sees on its adjustment pin. This is primarily used for making a variable voltage regulator by setting up a voltage divider around the fixed 1.25V, but it can also be used to make a constant current driver by inserting a resistor across that 1.25V drop. For example, by using a 3.9 ohm resistor, the LM317 outputs 320 milliamps (1.25V / 3.9 ohms = .320 amps).

Artificial Intelligence has a terrific instructable that shows how to do this for a fixed brightness. To dim the LED (and produce different shades of red, green, and blue), we need to add PWM so we're rapidly turning the LED on and off. Using PWM means we need a transistor between the microcontroller and the LM317. The most common way to do this is to add one on the regulator's output pin, as shown in the first schematic. But that means the transistor needs to be able to handle 320 milliamps, which means it will likely be slightly larger (TO-220) and more expensive than small signal transistors.

In Project #7 of Simon Monk's 30 Arduino Projects book, he shows a clever workaround for achieving the same effect with a small, logic-level transistor. He uses the 2N7000 N-channel MOSFET and attaches it to the adjustment pin, turning off that pin (which has a high impedance and thus very little current on it) rather than the output pin. This way only the LM317 needs to be able to handle high-power, rather than the LM317 plus the PWM transistor. I originally didn't understand how this works, but he graciously explained via e-mail:

"The more conventional schematic you sent needs a high power MOSFET to switch the already regulated current. The trick in my circuit is that I use the LM317 itself as both the current regulator and the high current switch. This means I can use a really low current MOSFET."

As in his schematic, I also added the 1K resistors between the PWM pin and the transistor's gate, even though I thought they were unnecessary with MOSFETs (as opposed to BJTs). Again, he explained:

"While that is true about the gate impedance, and the circuit would probably be just fine without the resistor, it is generally considered good practice to use a gate resistor, for this reason. There is actually quite a significant gate capacitance, so when you first turn the gate on, a momentary high current flows out of the Arduino output to 'fill' the 'gate capacitor'. In theory, this could be enough to damage the Arduino, especially if switching fast with PWM. However, in practice the small resistance of the wires leading to the gate and the general robustness of the ATMega outputs means that you can usually get away without the resistor."

Finally note that the resistor the LM317 uses to set its current also needs to handle a lot of power (i.e. dissipate a lot of heat), which is why those are the larger 1W variety. The 1K resistors on the adjustment pin and the controlling transistor can be much smaller.

Step 4: Low Battery Indicator

As in my first two instructables, I'm once again using a 2-cell LiPo battery, which is nominally 7.4V, but actually starts out at 8.4V. The reason I used a 2S battery is that the blue and green components of the LED require as much as 3.5V, and the LM317 has about a 3V drop when supplying 320 milliamps, so I needed a voltage source that was at least 6.5V.

One downside of multi-cell LiPo batteries is they have no protection circuitry, either against over-current or over-discharging. Allowing a huge amount of current is actually a feature of these batteries - they are designed for RC vehicles, and if your helicopter suddenly needs a lot of amps to stay in the air, it's better to have the battery able to deliver it (even if it ruins the battery) then to have the drone fall out of the sky.

But over-discharging is annoying for every day use. It means if I let the battery run until it dies, there's a very good chance it won't be able to be recharged. I've already had this happen to two of these batteries, and so I end up just plugging them into a charger every so often to recharge, even if they are probably still near full.

Since I might leave this LED for a while, I figured it'd be nice to add an indicator LED that lit up once the battery drops below a threshold so I know it's time to recharge. Since these batteries are 7.4V and my circuit needs about 6.5V to work correctly, I set up the low-battery LED to turn on when the voltage drops to 6.5V.

The simplest low-battery indicator circuit I found is by Swagatam Majumdar on the Homemade Circuits & Schematics blog. It uses just two NPN transistors and a few resistors and consumes only a tiny bit of current (less than 1 milliamp). The first transistor is normally on, which keeps the second transistor turned off and thus the indicator LED off. If the first transistor turns off, the second transistors turns on, which lights the LED. The first transistor will be on as long as it has about 0.6 volts on its base pin. So we set up the resistors controlling the first transistor's base as a voltage divider so that when the battery drops below the target threshold, the base voltage will be lower than 0.62 volts. (I figured out the exact cut-off voltage for my transistor by attaching it to a variable power supply and turning down the voltage until the transistor turned off.)

There are many combinations of resistors that will create the voltage divider, but we want the values to be large enough so that the circuit draws very little current normally, yet small enough that there's still enough current going into the base of the first transistor to turn on (after all, BJTs are current-driven devices). I used the same 33K resistors for the top value as he did in the blog post. Since .62 volts is 9.5% of the 6.5 volt threshold, I needed the bottom resistor to be 9.5% of the total, which meant about 3600 ohms. The standard resistor value of 3.3K is thus too low and 3.9K is too high, so I used a 3.3K resistor in series with a 270 ohm one to create 3570 ohms, which works perfectly when I test it with my power supply. (These resistors are all +/- 5%, although I tested them with a multimeter to get as close as possible.)

Step 5: Polymorph Diffuser

This LED is painful to look directly at. In Gadre's book, he suggests cutting a ping-pong ball in half and putting it over the RGB LED, and that's just for a regular LED running 20 milliamps for each color rather than 320. I didn't have any ping pong balls, but I did have a bag of polymorph that I wanted to practice shaping.

Polymorph is a thermal plastic with a very low melting point. To work with it, you heat the balls to 125-175 degrees Fahrenheit. Some people do this in the microwave, but I found the technique of just adding them to near-boiling water much more effective. The balls clump together so you don't have to worry about them dissolving. As they melt, the form a clear plastic glob that looks and feels like thick clear silly putty.

You can shape it with your fingers as it cools down, gradually turning more opaque as it does. Once it's back to fully white, it's hard enough to saw and drill! Unfortunately, I found it difficult to get precise shapes just by hand - I have the same problem trying to make anything interesting with my daughter's PlayDough.

To actually fit the polymorph over my aluminum square, I decided to create a mold (the aluminum itself would've been a good one, but it was already stuck on the PCB). So I cut a block of wood the same dimensions as the heat sink, heated up the polymorph and then stretched it over the wood. (The first version I did wasn't nearly tall enough since to be a good diffuser, it has to sit a centimeter or more above the LED. So I cut a block out of thicker wood, reheated the polymorph, and did it again.) The polymorph stuck to the wood a little as it hardened, but I was able to pry it off in one piece. The unevenness of my finger prints and stretch marks create a bit more diffusion, which is nice in parts, although I wish I had been more uniform about it. One bonus of using polymorph is I didn't have to glue it to the PCB - I just heated up the bottom edge again by holding it in hot water and then stuck the soft clear edge right onto the PCB, with the hardening plastic acting as a weak glue.

I've attached a video of the final product, although as with most LED projects, the iPhone's camera doesn't do it much justice.

As always, I'm very eager to hear any comments, questions, corrections, tips or suggestions, whether about the electronics, source code, or anything else. Thanks for reading.



    • Indoor Lighting Contest

      Indoor Lighting Contest
    • Make It Fly Challenge

      Make It Fly Challenge
    • Growing Beyond Earth Maker Contest

      Growing Beyond Earth Maker Contest

    12 Discussions


    4 years ago on Step 3

    Hi, I was wondering, are you because of introducing R2 not changing the voltage drop over R4 from 1.25V to less than 0.01V and thus changing the forward current through R4?

    Mark Rehorst

    5 years ago on Introduction

    Nice project, but I think you could do without the LM317s if your code was aware of the duty cycle and prevented exceeding limits. You could switch 5V directly into the LEDs as long as you keep the average current at or below 350mA- most LEDs can tolerate 5X normal current spikes if they are kept short (in this case, a 20% duty cycle will give average current of 350mA and normal brightness). Your circuit would become a uC and 3 MOSFET switches. Efficiency would be higher because the MOSFETs have near zero on resistance and switch very quickly. No heatsinks would be needed. If you want to get really fancy, you put in a small resistance to monitor LED current and the thing could adjust the duty cycle limits on the fly, so as the battery drains and voltage drops the duty cycle could be increased to maintain constant brightness.

    I used a PIC and RFP30N06 to switch 15A current spikes (from a 15V supply) through a 5W LED for a stroboscopic microscope illuminator. By carefully limiting the duty cycle the thing will run for hours on end only getting warm to the touch.

    2 replies
    markmoranMark Rehorst

    Reply 5 years ago on Introduction

    Thanks, Mark. That sounds really intriguing, but I'm not sure I understand how to do it. If I follow correctly, you're saying that besides using the duty-cycle to control the brightness (and thus shade) of each LED, I can also set the overall PWM rate to be fast enough that each LED never sees more than 350 milliamps at a time anyway, or at least not enough so that it burns out? So I could get rid of the LM317s and the resistors and just use the 2N7000 to turn them on and off directly? Would I need a power MOSFET since the 2N7000 is only "rated" for 300ma, or would the same principle also protect the 2N7000?

    I've read elsewhere you can drive a regular LED without a resister using a similar technique, although I don't know how fast the LED has to be turned on and off in order to make sure that it only is exposed to a given current. I assume there's a straight forward way to figure this out? (Sorry if there's an obvious answer, my only electronics knowledge is from reading hobby books over the past couple years.)

    Thanks for the tip and would love to hear more.

    Mark Rehorstmarkmoran

    Reply 5 years ago on Introduction

    If you're going to pulse an LED with current higher than the nominal rating, there are three limits to be concerned with. First, absolute maximum current should generally be limited to 3-5X the nominal current. This prevents destruction of the LED die by electromigration and burning the bond wires like fuses. Second, the maximum on-time of a high current pulse should be limited to prevent excessive heating of the LED die. I'm not sure what a maximum safe value is- keep the on-time short (maybe a few tens of ms max depending on the current) and you're probably OK. Third, duty cycle should be limited to keep maximum average current at or below the nominal rated current.

    Duty cycle can be used to control brightness. If you're using 2X nominal current pulses and you only turn on the LED for 50% of the time, it will look like normal brightness provided you are flashing it fast enough so that the flashing is imperceptible. For example, on 1 ms then off 1 ms then on 1ms, etc. gives 50% duty cycle, and 100% brightness if you are using 2X nominal current because the average current IS the nominal current. It will flash 500 times per second which is imperceptible as flashing unless you're lighting up spinning objects. If you want to dim the LED, just increase the off-time. 1 ms on and 3ms off looks dimmer than 1 ms on and 1 ms off. A uC can easily control the timing of the pulses down to usec level so it is pretty easy to control brightness and therefore color of a 3 color LED..

    Practically speaking, let's say you use a 12V battery for power. and you're driving a LED that drops 3.6V at the rated current of 350 mA. You want to ensure that the current through the LED can never exceed 4X 350mA= 1500mA. The LED will drop a little more voltage at the higher current (current is roughly an exponential function of voltage in an LED), assume 3.9V. 12V-3.9V = 8.1V 8.1V/1.5A = 5.4 Ohms. If you put a 5.4 Ohm resistor in series with the LED you can be sure the current won't exceed 1.5A. Now if you switch the LED on and off with a MOSFET switch you'll be switching 1.5A pulses through the LED. If you drive the LED with 25% duty cycle it will have an average current of 350 mA. The resistor will heat up, so you lose some efficiency by protecting the LED this way.

    I don't know if you could get away with using the small MOSFET. I suggest you give it a try. The RFP30N06 cost $1 each at sparkfun.


    5 years ago on Introduction

    pretty cool, can this project work with normal rgb led ? will be huge difference in circuit and code ? sorry for the noob questions , just faded and blinked the led and want to get the next level :D ,thx for sharing this

    3 replies

    Reply 5 years ago on Introduction

    Thanks, darkarts. Yes, the build is much simpler with a normal RGB LED, and the code is exactly the same. A regular RGB LED needs just 20 milliamps per color, so you can drive it just as you would fade three regular LEDs. You don't need the LM317s to create a constant high-current and you don't need the 2N7000 transistors to turn the LM317s on and off. So the three pins that I have hooked up to the gate of the each 2N7000 would instead just hook directly to the red, green, or blue pin of your RGB LED.

    The fourth pin of your RGB LED would be connected either to ground or your positive voltage, depending on whether it is a common-cathode or common-anode RGB LED. It doesn't really matter since you can drive either one with this circuit, the only difference is whether turning the knob left makes that color brighter or dimmer.

    Between the AVR pins and the LED pins you need a resistor.  My circuit has 1K ohm resistors on PB0, PB1, and PB2, but you'd want much smaller, say 100 ohms each. Assuming your microcontroller is running at 5v, that would allow 30 milliamps to go in the red channel and about 20 milliamps to go into the blue and green, which would work fine. If you want them all to be at 20 ma, you could use 100 ohm resistors for blue and green and 150 ohms for the red.


    Reply 5 years ago on Introduction

    i think i got it, what about injecting the code into the avr ? sorry as i told iam very beginner, i really appreciate your help


    Reply 5 years ago on Introduction

    Here's the schematic from Dhananjay Gadre's TinyAVR book which uses a regular RGB LED. I tried to attach to my last reply, but the server wasn't allowing any file uploads temporarily. This schematic assumes a common-anode LED, but as I said, if you have a common-cathode one, the just assume the connection on the top of the LED in the upper-right that says VCC says GND instead.

    As for programming the AVR, that's a bit longer of an answer. This instructable is partly about how to do high-voltage programming for this particular situation of wanting to disable to reset pin on an 8-pin AVR. But it's much easier to just use a bigger AVR and doesn't really cost any more if you're just making a few of something. If you're just starting out, I strongly suggest building and programming a bunch of circuits with an Arduino. That's how I learned a few years ago and it's a really fun and easy way to get introduced to a whole bunch of connects and fun projects, like driving motors and relays, etc. It also doesn't require any special equipment to program.

    Once you're ready to get a little more under the hood and figure out how stuff is actually working, then I suggest programming AVR chips with an In-Circuit Serial Programmer (ICSP/ISP). The TinyAVR book I mention above assumes you are programming AVR chips directly and has some of this info. But even better is Elliott Williams' terrific new book, Make: AVR Programming http://amzn.com/1449355781, which goes step by step from the beginnings to some pretty advanced projects.


    I found it on Sparkfun's website and thought it looked interesting. I then didn't do anything with it for two years and finally got around to trying it a few weeks ago. It's actually useful and does seem fun to be able to cheaply make your own hard plastic. I've read you can color it by adding food coloring or another dye. I haven't tried sugru yet but I imagine it has its own pros and cons. I think the final product is softer and more like rubber with sugru, rather than hard, stiff plastic with polymorph.