Arduino Snippets: RGB LED Strip With MOSFET Drivers

For obvious reasons, I need some Arduino-based sensors and displays…

First up, an RGB LED strip with MOSFET drivers:

RGB Strip with MOSFET Drivers

RGB Strip with MOSFET Drivers

The MOSFETs must have logic-level gates for this to work, of course. The tiny ZVNL110A MOSFETs have a channel resistance of about 3 Ω that limits their maximum current to around 300 mA, so the LED strip can have maybe a dozen segments, tops. If you use logic-level power MOSFETs, then the sky’s the limit. This also works with a single RGB LED on the +5 V supply with dropping resistors; you probably shouldn’t drive it directly from the port pins, though, particularly with an Arduino Pro Mini’s tiny regulator, because 60-ish mA will toast the regulator.

You may want gate pulldown resistors, 10 kΩ or so, to prevent the gates from drifting high when the outputs aren’t initialized. No harm will come with the single LED segment I’m using, but if the MOSFET gates float half-on with a dozen segments, then the transistor dissipation will get out of hand.

The usual LED test code seems pretty boring, so I conjured up a mood light that drives the PWM outputs with three raised sinusoids having mutually prime periods: 9, 11, and 13 seconds. That makes the pattern repeat every 21 minutes, although, being male, I admit most of the colors look the same to me. The PdBase constant scales milliseconds of elapsed time to radians for the trig functions:

const double PdBase = 1.00 * 2.0 * M_PI / 1000.0;

For an even more mellow mood light, change the leading 1.00 to, say, 0.01: the basic period becomes 100 seconds and the repeat period covers 1.5 days. You (well, I) can't see the color change at that rate, but it's never the same when you look at it twice.

All the pins / periods / intensity limits live in matching arrays, so a simple loop can do the right thing for all three colors. You could put the data into structures or classes or whatever, then pass pointers around, but I think it's obvious enough what's going on.

Rather than bothering with scaling integer arithmetic and doping out CORDIC trig functions again, I just used floating point. Arduinos are seductive that way.

The main loop runs continuously and updates the LEDs every 10 ms. There's no good reason for that pace, but it should fit better with the other hardware I'm conjuring up.

You can see the individual intensity steps at low duty cycles, because the 8 bit PWM step size is 0.4%. So it goes.

Despite my loathing of solderless breadboards, it works OK:

RGB LED Strip Driver - breadboard

RGB LED Strip Driver - breadboard

The MOSFETs stand almost invisibly between the drain wires to the LEDs and the gate wires to the Arduino.

The Arduino source code:

// RGB LED strip mood lighting
// Ed Nisley - KE4ANU - November 2012

//#include <stdio.h>
//#include <math.h>

//----------
// Pin assignments

const byte PIN_RED = 9;				// PWM - LED driver outputs +active
const byte PIN_GREEN = 10;
const byte PIN_BLUE = 11;

const byte PIN_HEARTBEAT = 13;		// DO - Arduino LED

//----------
// Constants

const int UPDATEMS = 10;						// update LEDs only this many ms apart

const double PdBase = 1.00 * 2.0 * M_PI / 1000.0;		// scale time in ms to radians

const byte Pins[] = {PIN_RED,PIN_GREEN,PIN_BLUE};
const float Period[] = {(1.0/9.0) * PdBase,(1.0/11.0) * PdBase,(1.0/13.0) * PdBase};
const float Intensity[] = {255.0,255.0,255.0};

#define TCCRxB 0x02						// Timer prescaler

//----------
// Globals

unsigned long MillisNow;
unsigned long MillisThen;

//-- Helper routine for printf()

int s_putc(char c, FILE *t) {
  Serial.write(c);
}

byte MakePWM(float MaxValue,double SineWave) {
	return trunc((SineWave + 1.0) * MaxValue/2.0);
}

//------------------
// Set things up

void setup() {
  pinMode(PIN_HEARTBEAT,OUTPUT);
  digitalWrite(PIN_HEARTBEAT,LOW);	// show we arrived

  TCCR1B = TCCRxB;					// set frequency for PWM 9 & 10
  TCCR2B = TCCRxB;					// set frequency for PWM 3 & 11

  pinMode(PIN_RED,OUTPUT);
  analogWrite(PIN_RED,0);			// force gate voltage = 0

  pinMode(PIN_GREEN,OUTPUT);
  analogWrite(PIN_GREEN,0);

  pinMode(PIN_BLUE,OUTPUT);
  analogWrite(PIN_BLUE,0);

  Serial.begin(9600);
  fdevopen(&s_putc,0);				// set up serial output for printf()

  printf("RGB LED Mood Lighting\r\nEd Nisley - KE4ZNU - November 2012\r\n");
}

//------------------
// Run the test loop

void loop() {

	MillisNow = millis();

	if ((MillisNow - MillisThen) > UPDATEMS) {
		digitalWrite(PIN_HEARTBEAT,HIGH);

		for (byte i = 0; i < 3; i++)
			analogWrite(Pins[i],MakePWM(Intensity[i],sin(MillisNow * Period[i])));

		digitalWrite(PIN_HEARTBEAT,LOW);

		MillisThen = MillisNow;
	}
}

About these ads

  1. #1 by ewf on 5-December-2012 - 10:38

    Did you mean 7, 11, and 13 for the mutually prime ?

    • #2 by Ed on 5-December-2012 - 13:18

      The Period array has them in the bottom of those hideous fractions: 9, 11, and 13.

      Not, I suppose, that it really matters… [grin]

      [Update: The fact that 9 isn't prime doesn't matter as long as all three periods are mutually prime.]

  2. #3 by Tom Wheless on 5-December-2012 - 11:37

    Nice circuit, Ed! Where did you purchase your MOSFETs from? (Or, since you’re not supposed to end sentences with prepositions, I should say: “From where did you purchase your MOSFETs?”) Digikey has them at $0.68 each for qty 10 or $0.60 for qty 25. Is this the best price you’ve found?

    BTW, thanks for all the interesting stuff you put into your website. I check it almost every day.

    Best regards,
    Tom

    • #4 by Ed on 5-December-2012 - 13:26

      “From where did you purchase your MOSFETs?”

      Or shall we say “From whence did you … ” [grin]

      Being a bottom feeder, I got ‘em surplus from Electronic Goldmine. They’re now out of stock, of course, and the in-stock ZVNL120 MOSFETs are not at all similar (and have a gibberish description), so DigiKey seems as good as any.

      Thanks for the good words!

      • #5 by Tom Wheless on 7-December-2012 - 13:14

        Hi Ed,
        Rather than use a MOSFET, I decided to use some cheap 2N3904 NPN bipolar transistors I had lying around and they worked great! (For any electronics newbies reading this, I put a 4.7K resistor in series with the base to limit the base current to about 1mA, which is still enough to ensure the transistor is saturated when it’s on.) I used some discrete 5mm LEDs I had lying around and soldered them to a 2″ x 2″ piece of copper-clad PCB material using the “Manhattan prototyping” method (a habit from my days of working on hi-freq stuff). I drove the LEDs from a Sparkfun Arduino Pro (5V, 16MHz) board I have and it works great! The blue LEDs I used are not very bright (they’re some samples that are about 10 years old, and I think they’ve made a lot of improvements to blue LEDs in the last 5 years), so I think I’ll double the number of blue LEDs I’m using and then mount it on my workbench and let it run continuously, to amuse me while I’m working.

        Thanks again Ed for a neat little project!

        Best regards,
        Tom

        • #6 by Ed on 7-December-2012 - 15:18

          a lot of improvements to blue LEDs in the last 5 years

          Even the red ones from long ago (marked “high efficiency”) look like fireflies compared to anything recent.

          A chunk of white packing foam over the LEDs makes a good diffuser: the strip looks like a glowing brick and doesn’t cast shadows on the walls.

          Glad you liked it!

  1. Arduino Snippets: LED Bargraph Display « The Smell of Molten Projects in the Morning
  2. Arduino Snippets: Hall Effect Sensor « The Smell of Molten Projects in the Morning
  3. Arduino Snippets: Analog Button Input « The Smell of Molten Projects in the Morning
  4. Arduino Snippets: LED Stroboscopic Tachometer « The Smell of Molten Projects in the Morning
  5. 6C21 Triode | The Smell of Molten Projects in the Morning