The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Day: December 5, 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) {
      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;
    	}
    }