Archive for December 5th, 2012

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) {

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

// Set things up

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

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

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



  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) {

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


		MillisThen = MillisNow;