Jump to content
Sign in to follow this  
timotet

ringLight. A WS2812 led based light for my 3d printer.

Recommended Posts

I built a 3d printer last year and needed a small light to illuminate the build area, this is what I came up with.

I call it the ringLight.

post-36-0-48304600-1402168887_thumb.jpg

 

I thought about using an Adafruit Neopixel ring, but due to the finished part being for a specific size I decided to roll

my own. I used a MSP430G2452 in the TSSOP 14 pin package for the micro. I have to thank every one on the forum for

the wealth of info related to the WS2812 leds, and a special thanks to @@oPossum for the led driver code he posted.

The code is pretty straight forward. Once powered on the msp430 loads what ever value from flash that was

used from the last time power was on, then waits for a button press. If the button gets pressed it will scroll through a few

pre programmed colors or animations, per press. Then that value is re-written into flash for the next time.

 

Here is the code:


//  5/10/14
// For controlling the Mandelbots ringLight WS2812 leds
// Thanks to Kevin Timmerman for the ws2811_hs.asm led driver code
// Written by Larry Fogg and Tim Toliver
//

#include "stdint.h"
#include "stdlib.h"
#include <msp430g2452.h>

void write_ws2811_hs(uint8_t *data, unsigned length, uint8_t pinmask); //prototype for ws2811_hs.asm

#define button BIT3
#define ledPin BIT7
#define numColors 1972

static const uint8_t red[12] = { 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00 };
static const uint8_t green[12] = { 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00 };
static const uint8_t blue[12] = { 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF };
static const uint8_t purple[12] = { 0, 128, 128, 0, 128, 128, 0, 128, 128, 0, 128, 128 };
static const uint8_t yellow[12] = { 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00 };
static const uint8_t orange[12] = { 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x00 };
static const uint8_t steelBlue[12] = { 130, 70, 180, 130, 70, 180, 130, 70, 180, 130, 70, 180 };
static const uint8_t pink[12] = { 20, 255, 147, 20, 255, 147, 20, 255, 147, 20, 255, 147 };
static const uint8_t aqua[12] = { 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255 };

static uint8_t z[12]; //GRB values for 4 LEDs
uint8_t pressCount;   //keep track of button presses
uint8_t buttonPressed = 1; //start with true so stored pressCount is executed

//for writing flash
uint8_t * Flash_ptr = (unsigned char *) 0x1040; //location to write in flash info segment C

void millisecDelay(int delay)
{
	int i;

	for (i=0; i<delay; i++)
		_delay_cycles(10000); //roughly 1 msec. calibrate this
}

#pragma vector=PORT1_VECTOR
   __interrupt void PORT1_ISR(void)
    {
	   _disable_interrupts();
       pressCount++;                    //increment button presses
       buttonPressed = 1;				//true
	   millisecDelay(300); 			    //for button debounce
	   P1IFG &= ~button;                //clear interrupt flag
	   _enable_interrupts();
    }

void writeFlash(unsigned char data){
	_disable_interrupts();
	FCTL1 = FWKEY + ERASE;                    //Set Erase bit
	FCTL3 = FWKEY;                            //Clear Lock bit

	*Flash_ptr = 0;                           //Dummy write to erase Flash segment

	FCTL1 = FWKEY + WRT;                      //Set WRT bit for write operation

	*Flash_ptr = data;                        //Write value to flash

	FCTL1 = FWKEY;                            //Clear WRT bit
	FCTL3 = FWKEY + LOCK;                     //Set LOCK bit
	_enable_interrupts();
}

void loadLED (int ledNum, uint8_t R, uint8_t G, uint8_t  //load z[] for one LED
{
	z[ledNum*3]=G;
	z[ledNum*3+1]=R;
	z[ledNum*3+2]=B;
}

void writeLEDs ()
{
	_disable_interrupts();
	write_ws2811_hs(z, sizeof(z), ledPin);
	_enable_interrupts();
}

void getRGB(int color, uint8_t *R, uint8_t *G, uint8_t * //this generates RGB values for 1972 rainbow colors from red to violet and back to red
{
	float brightness = 0.3; //scale factor to dim LEDs. if they are too bright, color washes out

	if (color>=(numColors/2)) //adjust color for return from violet to red
		color=numColors-color;
	if (color < 199)
	{
		*R = 255;
		*G = 56 + color;
		*B = 57;
	}
	else if (color < 396)
	{
		*R = 254 - (color - 199);
		*G = 255;
		*B = 57;
	}
	else if (color < 592)
	{
		*R = 57;
		*G = 255;
		*B = 58 + (color - 396);
	}
	else if (color < 789)
	{
		*R = 57;
		*G = 254 - (color - 592);
		*B = 255;
	}
	else
	{
		*R = 58 + (color - 789);
		*G = 57;
		*B = 255;
	}
	*R*=brightness; //apply brightness modification
	*G*=brightness;
	*B*=brightness;
}

void allWhite(uint8_t intensity) //all LEDs stay white
{
	int i;

	for (i=0; i<12;i++)
		z[i] = intensity;
	writeLEDs();
}

void allBlack() //all LEDs off (black)
{
	int i;

	for (i=0; i<12;i++)
		z[i] = 0x00;
	writeLEDs();
}

void lightning ()
{
	uint8_t R, G, B;
	int color; //numeric index into color palette
	int flashDuration;

	while (!buttonPressed)
	{
		allBlack(); //clear LEDs
		color = rand()%numColors;
		if (color%7==0) //every so often, throw in a bright white flash
		{
			R=G=B=0xff;
			flashDuration = 10; //quicker for white flash
		}
		else
		{
			getRGB (color, &R, &G, &; //get random color
			flashDuration = 50; //slower for color flash. makes color more apparent
		}
		loadLED (rand()%4, R, G, ; //load color into random LED
		writeLEDs(); //flash the LED
		millisecDelay(flashDuration);
		allBlack(); //clear LEDs
		millisecDelay(rand()%500); //wait for next flash
	}
}

void drawSnake (int headLoc) //draw snake starting at headLoc
{
	int shoulderLoc, torsoLoc, tailLoc;
	uint8_t R, G, B; //color of head

	getRGB (rand()%numColors, &R, &G, &;
	shoulderLoc = (headLoc+1)%4; //find correct array locations for body parts. keep within 0-3 range
	torsoLoc = (headLoc+2)%4;
	tailLoc = (headLoc+3)%4;
	loadLED (headLoc, R, G, ; //random color head
	loadLED (shoulderLoc, 0, 0, 0);
	loadLED (torsoLoc, 0, 0, 0);
	loadLED (tailLoc, 0, 0, 0); //black tail
	writeLEDs(); //draw the snake
}

void chaseSnakeTail () //white head, black tail. Snake goes in circles
{
	int headLocation=0; //which LED is the head (white)
	int crawlSpeed; //delay between snake moves
	int slowSpeed=150;
	int fastSpeed= 10;
	int CWdirection = 1; //start with clockwise direction = true


	while (!buttonPressed)
	{
		for (crawlSpeed=slowSpeed; crawlSpeed>fastSpeed; crawlSpeed-=2) //speed up tail chasing
		{
			drawSnake (headLocation);
			millisecDelay(crawlSpeed);
			if (CWdirection)
				headLocation +=3;
			else
				headLocation++;
			headLocation%=4; //keep LED in range (0-3)
			if (buttonPressed)
				break;
		}

		CWdirection = !CWdirection; //reverse direction for next cycle

		for (crawlSpeed=fastSpeed; crawlSpeed<slowSpeed; crawlSpeed+=2) //slow down tail chasing
		{
			drawSnake (headLocation);
			millisecDelay(crawlSpeed);
			if (CWdirection)
				headLocation +=3;
			else
				headLocation++;
			headLocation%=4; //keep LED in range (0-3)
			if (buttonPressed)
				break;
		}
	}
}

void allColors() //display rainbow color progression, each LED out of phase with the others
{
	uint8_t R, G, B;
	int color;
	int ledNum; //0-3

	while (!buttonPressed)
		for (color=0; color<numColors; color++) //progress from red to violet and back to red
		{
			for (ledNum=0; ledNum<4; ledNum++)
			{
				getRGB ((color+ledNum*200)%numColors, &R, &G, &; //offset LED colors by 200 from neighbors
				loadLED (ledNum, R, G, ;
			}
			writeLEDs();
			millisecDelay(4);
			if (buttonPressed) //allow exit from function
				break;
		}
}

void writeLeds(const uint8_t color[12]) {

	unsigned j;
	for (j = 0; j < 12; j++) {
		z[j] = color[j];
		write_ws2811_hs(z, sizeof(z), ledPin);
	}

}
void main(void)
{
 	WDTCTL = WDTPW + WDTHOLD;            // No watchdog reset

	DCOCTL = 0;
	BCSCTL1 = CALBC1_12MHZ; // Run at 12 MHz
	DCOCTL  = CALDCO_12MHZ;

	//P1SEL &= ~ledPin | button;
	P1DIR |= ledPin;   // ledPin is an output
	P1OUT = 0;         // set port 1 low


	P1DIR &= ~button;  // button is an input
	P1REN |= button;   // pull down on button
	P1OUT &= ~button;  // set pull down
	P1IES &= ~button;  // Interrupt Edge Select - 0: trigger on rising edge, 1: trigger on falling edge
    P1IFG &= ~button;  // interrupt flag for p1.3 is off
    P1IE |= button;    // enable interrupt

    FCTL2 = FWKEY + FSSEL_1 + FN3;             // MCLK/32 for Flash Timing Generator 12mhz/32 = 375hz  // these both work has to be between 257-476 hz
    //FCTL2 = FWKEY + FSSEL_1 + 0x1A;             // MCLK/27 for Flash Timing Generator 12mhz/27 = 444hz

    pressCount = *Flash_ptr; // load value written in flash

    _enable_interrupts();

	while (1)
	{
		if (buttonPressed) {
			buttonPressed = 0; //reset

			switch (pressCount) {
			case 0: //bright white
				allWhite(0x80);
				break;
			case 1: //dim white
				allWhite(0x40);
				break;
			case 2:
				lightning();
				break;
			case 3:
				allColors();
				break;
			case 4:
				chaseSnakeTail();
				break;
			case 5:
				writeLeds(red);
				break;
			case 6:
				writeLeds(blue);
				break;
			case 7:
				writeLeds(green);
				break;
			case 8:
				writeLeds(purple);
				break;
			case 9:
				writeLeds(yellow);
				break;
			case 10:
				writeLeds(orange);
				break;
			case 11:
				writeLeds(steelBlue);
				break;
			case 12:
				writeLeds(pink);
				break;
			case 13:
				writeLeds(aqua);
				break;
			default:
				pressCount = 0; //default to 0
				buttonPressed = 1; //force execution of case 0
				break;
			}
			writeFlash(pressCount);  // write to flash for poweroff
		}
	}
}



I didn't use any kind of level shifting for the leds. It seems alot of folks on here had no trouble getting the leds to

work with out it, and I didn't read about the level shifting until after I sent off for the boards. So I got lucky on that one.

 

For powering the the board I wanted to use the existing 12volt supply for the printer, so I put a couple of LDO's on

board for that. I of course used TI parts that I sampled from TI. (as a matter of fact I sampled the msp430 too thanks TI!!!)

For the 3.6 volt side I used the TPS70936 and for the 5 volt side I used the LP2989. Both of the LDO's are able according to

the data sheets to handle a 12 volt input.The 3.6 volt side worked out as expected but I must have done something

wrong with the LP2989. The data sheet says the part will provide up to 500mA continuously, the WS2812 at full brightness

pulls 60mA. I put 4 leds on the board so thats 240mA if they are all full blast. When I have all the leds on full the LDO gets

HOT! As far as the layout goes I copied the shematic from the data sheet, and used the recommended parts.

 

This is from the datasheet:

post-36-0-84289600-1402170350_thumb.png

This is from my schematic:

post-36-0-70639000-1402170363_thumb.png

 

To get around a full redesign I don't push them that hard, and it works. It does bug me though, so if anyone has any

insight or suggestions let me know. I have one more pcb I can hack on and see if I can make it work better.

 

Besides that I am happy with the design and it fits the machine perfectly. (Just like it supposed to!)

It's nice to see what I am printing.

Here are a couple more photos , and some video.

 

post-36-0-00821600-1402172992_thumb.jpg

post-36-0-02745400-1402173039_thumb.jpg

 

 

Share this post


Link to post
Share on other sites

For powering the the board I wanted to use the existing 12volt supply for the printer, so I put a couple of LDO's on

board for that. I of course used TI parts that I sampled from TI. (as a matter of fact I sampled the msp430 too thanks TI!!!)

For the 3.6 volt side I used the TPS70936 and for the 5 volt side I used the LP2989. Both of the LDO's are able according to

the data sheets to handle a 12 volt input.The 3.6 volt side worked out as expected but I must have done something

wrong with the LP2989. The data sheet says the part will provide up to 500mA continuously, the WS2812 at full brightness

pulls 60mA. I put 4 leds on the board so thats 240mA if they are all full blast. When I have all the leds on full the LDO gets

HOT! As far as the layout goes I copied the shematic from the data sheet, and used the recommended parts.

12V - 5V = 7V

 

7V * 0.5A = 3.5W

 

So you need a heatsink that will get rid of that 3.5 watts with acceptable temperature rise. It's in a SOIC package, so that's not really possible.

 

So... use a TO-220 package regulator with a heatsink or a switching regulator.

Share this post


Link to post
Share on other sites

12V - 5V = 7V

 

7V * 0.5A = 3.5W

 

So you need a heatsink that will get rid of that 3.5 watts with acceptable temperature rise. It's in a SOIC package, so that's not really possible.

Cool project, these LEDs are so versatile :)

 

Another option, is to place a high wattage resistor in series before your LDO.

This could be done now, the resistor could be mounted on a small heatsink somewhere in your printer, and the then a cable can run from that to the LEDs.

 

R = V/I = 5/0.5 = 10R

P = 2.5W

 

Then the LDO's power dissipation,

V_drop = 7 - 5 = 2.

P = 2 * 0.5 = 1 W.

 

That is still high, but now a 3x improvement.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×