Jump to content
43oh

ELVIS Display Controller


Recommended Posts

I thought I'd better start documenting this, just for motivation. :thumbup:

 

I have a friend who owns a bar with her husband. Her husband is an Elvis impersonator (pretty good) and performs at their bar (among other venues). He has a 4'x8' display using the larger, screw-in type of Christmas lights (about 300 of them) spelling "ELVIS". Currently, the display is strung end-to-end and either on, or off. This display is boring, and blows the inline fuses in the light strings as well.

 

I plan to make them a controller with an electrical box containing five relays and outlets (one for each letter), connected to a control box using CAT-5 cable (RJ45 sockets at each end). The control box will house a LaunchPad (with access to USB port for reprogramming) and six buttons, each triggering an effect ('chase' letters, spell, flash, center-out, on, off).

 

This should be a fairly simple project, as I'm using relay modules from DealExtreme that have transistors and diodes on the boards already. Code should just be interrupts, delays, and switching outputs high/low.

 

The end result should provide a more functional display, but will also be safer since it will be five circuits of the Christmas light wiring, instead of one continuous string.

 

Photo of current display:

post-73-135135556556_thumb.jpg

Link to post
Share on other sites
  • 2 weeks later...

No RGB. I pitched the idea to him and he liked it, but too expensive. We're keeping it in mind, in case he wants a fancier display later.

 

At this point, just five relays, one for each letter. We're keeping the Christmas lights and just rewiring to isolate each letter. This will make it into five short runs vs. the one long one he's using now. This will bring the load(s) down to within tolerances and stop blowing the in-plug fuses. :roll:

Link to post
Share on other sites
FYI, addressable RGB strips are getting really cheap, $4-$5/ft shipped (non-addressable are even cheaper.)

 

I can give you few examples if you want, here's one:

5m, 30 LEDs/m, 12V, waterproof! (I am interested in buying that one myself and doing something for my kids :) )

 

Thanks for the link! Filed for future use! :D

Link to post
Share on other sites
  • 1 month later...

Okay, I got back to this today and I've hit a snag. It's probably obvious to most of you, but I don't get to play with this stuff as much as I'd like.

 

Spell() works fine on startup (in main.c), but just blanks the outputs in the interrupt (I'm using LEDs to test). I think the problem lies in that I'm trying to use the TA0 interrupt in the delay routine while I'm in the P1/P2 interrupt.

 

In the end, I'll want each pin's interrupt to call a different function, but I think I can figure that part out.

 

Can anyone help?

 

//======== Standard MSP430 includes ========
#include "msp430g2211.h"
//======== Grace related includes ========
#include 

//======== Defines ===========
#define E_LED BIT0
#define L_LED BIT1
#define V_LED BIT2
#define I_LED BIT3
#define S_LED BIT4

//========== Declares ========
unsigned long delayCounter = 0;
void spell(void);
void delayHundredths(unsigned long delay);



//  ======== main ========
void main(void)
{
   // Activate Grace-generated configuration
   CSL_init();
   spell();
   while (1)
   {
   }
}

//========== Interrupts =============
// Port 1 interrupt service routine
void port1_interrupt(void)
{
spell();
P1IFG = 0; //clear interrupt flags
}

// Port 2 interrupt service routine
void port2_interrupt(void)
{
spell();
P2IFG = 0; //clear interrupt flags
}

// Timer 0 interrupt service routine
void TAO_10MS_INTERRUPT(void){
 delayCounter++;
}

// ========== Functions ===========

void spell(void)
{
P1OUT &= ~E_LED;  // Set E LED off
P1OUT &= ~L_LED;  // Set L LED off
P1OUT &= ~V_LED;  // Set V LED off
P1OUT &= ~I_LED;  // Set I LED off
P1OUT &= ~S_LED;  // Set S LED off
delayHundredths(50);
P1OUT |= E_LED;  // Set E LED ON
delayHundredths(50);
P1OUT |= L_LED;  // Set L LED ON
delayHundredths(50);
P1OUT |= V_LED;  // Set V LED ON
delayHundredths(50);
P1OUT |= I_LED;  // Set I LED ON
delayHundredths(50);
P1OUT |= S_LED;  // Set S LED ON
}

void delayHundredths(unsigned long delay)
{
delayCounter = 0; // reset the delay counter
while (delayCounter < delay)
{
	// do nothing (hoping this doesn't get optimized away)
}
}

 

Thanks for any help.

Link to post
Share on other sites

The delayHundredth() function is probably using the timer interrupt.

 

Highly recommend that you only call spell() from within the while()

loop in main(). Let the interrupts set a flag that code inside the while()

loop will test for and then call spell(). Calling a function that takes

a "long" time to execute inside an interrupt handler is NOT a good idear.

 

-Rusty-

Link to post
Share on other sites

Perfect! Thanks!

 

=== EDIT ===

Many thanks again, Rusty.

 

Here's what I think is the final version. I'm not worried about low-power modes, since this will run off a wall-wart. Any suggestions from the forum are, of course, quite welcome.

 

// GRACELAND
// 5-Channel controller for ELVIS stage display
// Created using the Grace configuration GUI as an experiment
// One channel per letter, 6 effects possible on 14-pin MSP430
// (one effect is to power cycle the board to turn all channels off)
// C. Dodd - 05 September 2012


//======== Standard MSP430 includes ========
#include "msp430g2211.h"
//======== Grace related includes ========
#include 

//======== Defines ===========
#define E_LED BIT0
#define L_LED BIT1
#define V_LED BIT2
#define I_LED BIT3
#define S_LED BIT4

#define ALL_ON 0x1
#define FLASH  0x2
#define SPELL  0x4
#define CHASE  0x8
#define WIPE   0x10

//========== Declares ========
char effectFlag = 0;
unsigned long delayCounter = 0;

void AllOn(void);
void AllOff(void);
void Flash(void);
void Spell(void);
void Chase(void);
void Wipe(void);

void delayHundredths(unsigned long delay);



//  ======== main ========
void main(void)
{
   // Activate Grace-generated configuration
   CSL_init();

   AllOff();  // Set LEDs off

while (1)
   {
	if(effectFlag == ALL_ON)
	{
		AllOn();
		effectFlag = 0;
	}
	else if(effectFlag == FLASH)
	{
		Flash();
		effectFlag = 0;
	}
	else if(effectFlag == SPELL)
	{
		Spell();
		effectFlag = 0;
	}
	else if(effectFlag == CHASE)
	{
		Chase();
		effectFlag = 0;
	}
	else if(effectFlag == WIPE)
	{
		Wipe();
		effectFlag = 0;
	}
   }
}

//========== Interrupts =============
// Port 1 interrupt service routine
void port1_interrupt(void)
{
if(P1IFG == BIT5)
{
	effectFlag = ALL_ON;
}
else if(P1IFG == BIT6)
{
	effectFlag = FLASH;
}
else if(P1IFG == BIT7)
{
	effectFlag = SPELL;
}
P1IFG = 0; //clear interrupt flags
}

// Port 2 interrupt service routine
void port2_interrupt(void)
{
if(P2IFG == BIT6)
{
	effectFlag = CHASE;
}
else if(P2IFG == BIT7)
{
	effectFlag = WIPE;
}
P2IFG = 0; //clear interrupt flags
}

// Timer 0 interrupt service routine
void TAO_10MS_INTERRUPT(void){
 delayCounter++;
}

// ========== Functions ===========

void AllOn(void)
{
P1OUT |= E_LED;
P1OUT |= L_LED;
P1OUT |= V_LED;
P1OUT |= I_LED;
P1OUT |= S_LED;  // Set LEDs on
}

void AllOff(void)
{
P1OUT &= ~E_LED;  // Set E LED off
P1OUT &= ~L_LED;  // Set L LED off
P1OUT &= ~V_LED;  // Set V LED off
P1OUT &= ~I_LED;  // Set I LED off
P1OUT &= ~S_LED;  // Set S LED off
}

void Flash(void)
{
AllOff();  // Set LEDs off

delayHundredths(20);

AllOn();  // Set LEDs ON

delayHundredths(20);

AllOff();  // Set LEDs off

delayHundredths(20);

AllOn();  // Set LEDs ON

delayHundredths(20);

AllOff();  // Set LEDs off

delayHundredths(20);

AllOn();  // Set LEDs ON
}

void Spell(void)
{
AllOff();  // Set LEDs off

delayHundredths(50);

P1OUT |= E_LED;  // Set E LED ON

delayHundredths(50);

P1OUT |= L_LED;  // Set L LED ON

delayHundredths(50);

P1OUT |= V_LED;  // Set V LED ON

delayHundredths(50);

P1OUT |= I_LED;  // Set I LED ON

delayHundredths(50);

P1OUT |= S_LED;  // Set S LED ON
}

void Chase(void)
{
AllOff();  // Set LEDs off

delayHundredths(20);

P1OUT |= E_LED;  // Set E LED ON

delayHundredths(30);

P1OUT |= L_LED;  // Set L LED ON
P1OUT &= ~E_LED;  // Set E LED off

delayHundredths(30);

P1OUT |= V_LED;  // Set V LED ON
P1OUT &= ~L_LED;  // Set L LED off

delayHundredths(30);

P1OUT |= I_LED;  // Set I LED ON
P1OUT &= ~V_LED;  // Set V LED off

delayHundredths(30);

P1OUT |= S_LED;  // Set S LED ON
P1OUT &= ~I_LED;  // Set I LED off

delayHundredths(30);

AllOn();  // Set LEDs ON
}

void Wipe(void)
{
AllOff();  // Set LEDs off

delayHundredths(20);

P1OUT |= E_LED;  // Set E LED ON
P1OUT |= S_LED;  // Set S LED ON

delayHundredths(30);

P1OUT |= L_LED;  // Set L LED ON
P1OUT |= I_LED;  // Set I LED ON
P1OUT &= ~E_LED;  // Set E LED off
P1OUT &= ~S_LED;  // Set S LED off

delayHundredths(30);

P1OUT |= V_LED;  // Set V LED ON
P1OUT &= ~L_LED;  // Set L LED off
P1OUT &= ~I_LED;  // Set I LED off

delayHundredths(30);

P1OUT |= L_LED;  // Set L LED ON
P1OUT |= I_LED;  // Set I LED ON
P1OUT &= ~V_LED;  // Set V LED off
delayHundredths(30);

P1OUT |= E_LED;  // Set E LED ON
P1OUT |= S_LED;  // Set S LED ON
P1OUT &= ~L_LED;  // Set L LED off
P1OUT &= ~I_LED;  // Set I LED off

delayHundredths(30);

P1OUT |= L_LED;  // Set L LED ON
P1OUT |= I_LED;  // Set I LED ON

delayHundredths(30);

P1OUT |= V_LED;  // Set V LED ON

delayHundredths(30);
}

void delayHundredths(unsigned long delay)
{
delayCounter = 0; // reset the delay counter

while (delayCounter < delay)
{
	// do nothing (hoping this doesn't get optimized away)
}
}

Link to post
Share on other sites

Doc,

The only real problem you might incur is when your 10-ms interrupt goes off

while the delayHundredths() function is in the middle of using the delayCounter variable.

A cheap fix would be to change delayCounter to be an unsigned int of 16-bits.

This would reduce (not eliminate!) the above situation from happening. I mean,

do you really need a 4 billion+ 10-millisecond delay?

 

If you want to make available your 4-billion+ 10-millisecond delays, the "expensive"

fix would be to have a function that grabs the delayCounter value while turning off

that interrupt (or all of them) so the value can't get corrupted. See the file wiring.c

down in the Arduino core library source code.

 

Also, it's best not to zero out the delayCounter in a non-interrupt handler as an

interrupt can occur while zeros are getting written to that 32-bit value. And it'd

be best to just let that counter keep going up and wrapping around (if you leave

ELVIS lit up for a long time).

 

As taken from the Arduino world, we'd do something like this:

void delayHundredths(unsigned long delay)
{
   unsigned long start;

   start = millisx10();      // get the value of your delayCounter

   while (millisx10() - start < delay)
   {
// do nothing (hoping this doesn't get optimized away)
   }
}

 

The way the timing is done here avoids issues when your resulting

target time is on the other size of zero from the current counter value

(the wraparound issue).

 

What you're doing will work... most of the time. However, when that

little interrupt ticks at the wrong time, it's rather hard to say how bad it'll

bite ya. It could make the sign act like it's hung up or you might see

a time delay be very short or be nonexistent. Yer call.

 

Otherwise, you done well this iteration!

 

-Rusty-

Link to post
Share on other sites
  • 3 weeks later...

Got the hardware designed. I decided to leave the whole LaunchPad in the system for two reasons: Ease of reprogramming, and ability to run off of 5VDC (which the relays require).

post-73-135135567715_thumb.png

Six function buttons: All On, All Off, Chase, Spell, Flash (x3), Wipe. I squeezed in the 'All Off' function by just temporarily cutting the ground to the LaunchPad (therefore, the power); the program starts up with all circuits off.

 

The control box will be connected to the relay/outlet box with common CAT-5 cable of (almost) any desired length (not sure what the limit will be from voltage drop). Each box will have a standard RJ45 jack.

 

I've just about finished the control box. Pics when done.

 

Final code:

// GRACELAND
// 5-Channel controller for ELVIS stage display
// Created using the Grace configuration GUI as an experiment
// One channel per letter, 6 effects possible on 14-pin MSP430
// (one effect is to power cycle the board to turn all channels off)
// Carlton Dodd - 05 September 2012


//======== Standard MSP430 includes ========
#include "msp430g2211.h"
//======== Grace related includes ========
#include 

//======== Defines ===========
#define E_LED BIT0
#define L_LED BIT1
#define V_LED BIT2
#define I_LED BIT3
#define S_LED BIT4

#define ALL_ON 0x1
#define FLASH  0x2
#define SPELL  0x4
#define CHASE  0x8
#define WIPE   0x10

//========== Declares ========
char effectFlag = 0;
unsigned long delayCounter = 0;

void AllOn(void);
void AllOff(void);
void Flash(void);
void Spell(void);
void Chase(void);
void Wipe(void);

void delayHundredths(unsigned long delay);



//  ======== main ========
void main(void)
{
   // Activate Grace-generated configuration
   CSL_init();

   AllOff();  // Set LEDs off

while (1)
   {
	if(effectFlag == ALL_ON)
	{
		AllOn();
		effectFlag = 0;
	}
	else if(effectFlag == FLASH)
	{
		Flash();
		effectFlag = 0;
	}
	else if(effectFlag == SPELL)
	{
		Spell();
		effectFlag = 0;
	}
	else if(effectFlag == CHASE)
	{
		Chase();
		effectFlag = 0;
	}
	else if(effectFlag == WIPE)
	{
		Wipe();
		effectFlag = 0;
	}
   }
}

//========== Interrupts =============
// Port 1 interrupt service routine
void port1_interrupt(void)
{
if(P1IFG == BIT5)
{
	effectFlag = ALL_ON;
}
else if(P1IFG == BIT6)
{
	effectFlag = FLASH;
}
else if(P1IFG == BIT7)
{
	effectFlag = SPELL;
}
P1IFG = 0; //clear interrupt flags
}

// Port 2 interrupt service routine
void port2_interrupt(void)
{
if(P2IFG == BIT6)
{
	effectFlag = CHASE;
}
else if(P2IFG == BIT7)
{
	effectFlag = WIPE;
}
P2IFG = 0; //clear interrupt flags
}

// Timer 0 interrupt service routine
void TAO_10MS_INTERRUPT(void){
 delayCounter++;
}

// ========== Functions ===========

void AllOn(void)
{
P1OUT |= E_LED;
P1OUT |= L_LED;
P1OUT |= V_LED;
P1OUT |= I_LED;
P1OUT |= S_LED;  // Set LEDs on
}

void AllOff(void)
{
P1OUT &= ~E_LED;  // Set E LED off
P1OUT &= ~L_LED;  // Set L LED off
P1OUT &= ~V_LED;  // Set V LED off
P1OUT &= ~I_LED;  // Set I LED off
P1OUT &= ~S_LED;  // Set S LED off
}

void Flash(void)
{
AllOff();  // Set LEDs off

delayHundredths(20);

AllOn();  // Set LEDs ON

delayHundredths(20);

AllOff();  // Set LEDs off

delayHundredths(20);

AllOn();  // Set LEDs ON

delayHundredths(20);

AllOff();  // Set LEDs off

delayHundredths(20);

AllOn();  // Set LEDs ON
}

void Spell(void)
{
AllOff();  // Set LEDs off

delayHundredths(50);

P1OUT |= E_LED;  // Set E LED ON

delayHundredths(50);

P1OUT |= L_LED;  // Set L LED ON

delayHundredths(50);

P1OUT |= V_LED;  // Set V LED ON

delayHundredths(50);

P1OUT |= I_LED;  // Set I LED ON

delayHundredths(50);

P1OUT |= S_LED;  // Set S LED ON
}

void Chase(void)
{
AllOff();  // Set LEDs off

delayHundredths(20);

P1OUT |= E_LED;  // Set E LED ON

delayHundredths(30);

P1OUT |= L_LED;  // Set L LED ON
P1OUT &= ~E_LED;  // Set E LED off

delayHundredths(30);

P1OUT |= V_LED;  // Set V LED ON
P1OUT &= ~L_LED;  // Set L LED off

delayHundredths(30);

P1OUT |= I_LED;  // Set I LED ON
P1OUT &= ~V_LED;  // Set V LED off

delayHundredths(30);

P1OUT |= S_LED;  // Set S LED ON
P1OUT &= ~I_LED;  // Set I LED off

delayHundredths(30);

AllOn();  // Set LEDs ON
}

void Wipe(void)
{
AllOff();  // Set LEDs off

delayHundredths(20);

P1OUT |= E_LED;  // Set E LED ON
P1OUT |= S_LED;  // Set S LED ON

delayHundredths(30);

P1OUT |= L_LED;  // Set L LED ON
P1OUT |= I_LED;  // Set I LED ON
P1OUT &= ~E_LED;  // Set E LED off
P1OUT &= ~S_LED;  // Set S LED off

delayHundredths(30);

P1OUT |= V_LED;  // Set V LED ON
P1OUT &= ~L_LED;  // Set L LED off
P1OUT &= ~I_LED;  // Set I LED off

delayHundredths(30);

P1OUT |= L_LED;  // Set L LED ON
P1OUT |= I_LED;  // Set I LED ON
P1OUT &= ~V_LED;  // Set V LED off
delayHundredths(30);

P1OUT |= E_LED;  // Set E LED ON
P1OUT |= S_LED;  // Set S LED ON
P1OUT &= ~L_LED;  // Set L LED off
P1OUT &= ~I_LED;  // Set I LED off

delayHundredths(30);

P1OUT |= L_LED;  // Set L LED ON
P1OUT |= I_LED;  // Set I LED ON

delayHundredths(30);

P1OUT |= V_LED;  // Set V LED ON

delayHundredths(30);
}

void delayHundredths(unsigned long delay)
{
delayCounter = 0; // reset the delay counter

while (delayCounter < delay)
{
	// do nothing (hoping this doesn't get optimized away)
}
}

 

BTW: Rusty: I'm not ignoring your excellent points, I have just chosen to accept those risks in this project. I will definitely keep them in mind for more critical applications. I feel that, if this device misbehaves, the 'All-Off' button is a simple reset. Thanks for educating me. :thumbup:

Link to post
Share on other sites

Works just fine. The test points near the USB jack are perfect for supplying 5V. You just end up using only the voltage regulator on the programming side, but you're still powering the other circuitry on the programming side (drawing more current). This is not a problem for this project, as it's AC-powered and the extra draw is not significant compared to the power required to run the relays.

Link to post
Share on other sites
Yep - I've used it for several projects (including the VFD clock booster). I usually add a single pin female jumper or solder a wire into it. I don't have an LP in front of me or I'd tell you what TP it is.

TP1 is the +5V from USB, TP3 is USB GND. They are plated holes that take a single pin each perfectly.

 

For this project, I soldered wires since it's a permanent installation. (I'm supplying 5v TO the LaunchPad rather than getting it from USB.)

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...