Apr 24, 2011

Valentine's Heart with Fading/Blinking LEDs




Catching up on documenting old projects, this one from 2009.

The inspiration for this project was a posting from the good guys at NerdKits.
Humberto Evans gives a great tutorial on a Valentine's Day blinking heart, check it out along with the other great entries on their site.

I decided to try and make my own without the kit offered from the NerdKits folks. After a little research, I found alsmost all of the code I needed was available from Paul Badger of Modern Device, famous for the RBBB Arduino clone and other great products.


The actual base is just a sturdy piece of cardboard cut in the shape of a heart. There are heart templates easily found with a google search. I made an easel stand with another piece of cardboard properly cut at an angle. The code is running on a Modern Device RBBB Arduino, powering the LEDs through a black mini-breadboard that contains current-limiting resistors for the LEDS. The RBBB also prints a message on a 2x16 serial LCD. The 5mm red LEDs are poked through the base, hot-glued to the back. The wires are covered in heat shrink and then fastened in bundles with cable ties to make them neat.

The tricky part is keeping all of the LEDs in the proper order for sequencing the display. You may want to write down on the back of the heart which LED goes to which pin on the RBBB.

Code:
/* PWMallPins.pde
Paul Badger 2007
A program to illustrate one way to implement a PWM loop.
This program fades LED's on Arduino digital pins 2 through 13 in a sinewave pattern.
It could be modified to also modulate pins 0 & 1 and the analog pins.
I didn't modulate pins 0 & 1 just because of the hassle of disconnecting the LEDs on the RX and TX pins (0 & 1).

The PWM loop, as written, operates at about 175 HZ and is flicker-free.
The trick to this of course is not doing too much math in between the PWM loop cycles.
Long delays between the loops are going to show up as flicker. This is true especially of "Serial.print" debug statements
which seem to hog the processor during transmission. Shorter (timewise) statements will just dim the maximum brightness of the LED's.
There are a couple of lines of code (commented out) that implement a potentiometer as a speed control for the dimming.

How it works: The PWM loop turns on all LED's whose values are greater than 0 at the start of the PWM loop.
It then turns off the LED's as the loop variable is equal to the channel's PWM modulation value.
Because the values are limited to 255 (just by the table), all LED's are turned off at the end of the PWM loop.
This has the side effect of making any extra math, sensor reading. etc. will increase the "off" period of the LED's duty cycle. Consequently
the brightest LED value (255), which is supposed to represent a "100%" duty cycle (on all the time), dosesn't really do that.
More math, sensor reading etc will increase the "off time", dimming the LED's max brightness. You can (somewhat) make up for this dimming with
smaller series resistors, since LED's can be overrated if they aren't on all of the time.

The up side of this arrangement is that the LED's stay flicker free.
Note that this program could easily be used to modulate motor speeds with the addition of driver transistors or MOSFET's.
*/

// random fading in and out of
// christmas tree lights

#define NR_OF_LIGHTS 12
int pins[NR_OF_LIGHTS] = {2, 8, 6, 4, 12, 13, 10, 9, 7, 5, 3, 11 };

int values[NR_OF_LIGHTS];
int steps[NR_OF_LIGHTS];

#define NR_OF_FADESTEPS 4
int fadesteps[NR_OF_FADESTEPS] = { 192, 128, 64, 0 };
int fade_delay = 30; // millisec
int fade_cycles = 1000;

int nr_of_blinks = 2;
int blink_delay = 400; //millisec

int effect = 1;

int randomlights[NR_OF_LIGHTS];
bool chosenlights[NR_OF_LIGHTS];

int first_time = 1;


long time; // variable for speed debug
float pwmSpeed[14] = {
0, 0, 1.2, 1.3, 1.4, 1.9, .9, .8, .5, 1.2, 1.37, 1.47, .3, 3.2}; // these constants set the rate of dimming
int pwmVal[14]; // PWM values for 12 channels - 0 & 1 included but not used
float pwmFloats[14];
int i, j, k, l, x, y, z, bufsize, pot; // variables for various counters

unsigned char sinewave[] = //256 values
{
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,

0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,

0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,

0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,

0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,

0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,

0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,

0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,

0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,

0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,

0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,

0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,

0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,

0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,

0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,

0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c

};


void setup(){
pinMode(1, OUTPUT);
Serial.begin(9600);
Serial.print("?vmv4");
Serial.print("?G216");



DDRD=0xFC; // direction variable for port D - make em all outputs except serial pins 0 & 1
DDRB=0xFF; // direction variable for port B - all outputs

}

void fade_routine() {
// time = millis(); // this was to test the loop speed
// for (z=0; z<1000; z++){ // ditto // pot = analogRead(0); // this implemented a potentiometer speed control to control speed of fading for (y=0; y<14; y++){ // calculate one new pwm value every time through the control loop j = (j + 1) % 12; // calculate a new j every time - modulo operator makes it cycle back to 0 after 11 k = j + 2; // add 2 tp tje result - this yields a cycle of 2 to 13 for the channel (pin) select numbers pwmFloats[k] = (pwmFloats[k] + pwmSpeed[k]); // pwmFloats[k] = (pwmFloats[k] + ((pwmSpeed[k] * 15 * (float)pot) / 1023)); // implements potentiometer speed control - see line above if (pwmFloats[k] >= 256){ // wrop around sinewave table index values that are larger than 256
pwmFloats[k] = pwmFloats[k] - 256;
}
else if (pwmFloats[k] < 0){ pwmFloats[k] = pwmFloats[k] + 256; // wrop around sinewave table index values that are less than 0 } pwmVal[k] = sinewave[(int)pwmFloats[k]]; // convert the float value to an integer and get the value out of the sinewave index } PORTD = 0xFC; // all outputs except serial pins 0 & 1 PORTB = 0xFF; // turn on all pins of ports D & B for (z=0; z<3; z++){ // this loop just adds some more repetitions of the loop below to cut down on the time overhead of loop above // increase this until you start to preceive flicker - then back off - decrease for more responsive sensor input reads for (x=0; x<256; x++){ for( i=2; i<14; i++){ // start with 2 to avoid serial pins if (x == pwmVal[i]){ if (i < 8){ // corresponds to PORTD // bitshift a one into the proper bit then reverse the whole byte // equivalent to the line below but around 4 times faster // digitalWrite(i, LOW); PORTD = PORTD & (~(1 << i)); } else{ PORTB = PORTB & (~(1 << (i-8))); // corresponds to PORTB - same as digitalWrite(pin, LOW); - on Port B pins } } } } } } void setAllLights(int value) { for (int i = 0; i < NR_OF_LIGHTS; i++) { digitalWrite(pins[i], value); delay(150); digitalWrite(pins[i],0); } for (int i = NR_OF_LIGHTS; i>0; i--) {
digitalWrite(pins[i], value);
delay(150);
digitalWrite(pins[i],0);
}

for (int i = 6, j=7; i>0; i--, j++) {
digitalWrite(pins[i], value);
digitalWrite(pins[j], value);
delay(150);
digitalWrite(pins[i],0);
digitalWrite(pins[j],0);
}
for (int i = 0, j = 12; i < 7; i++, j--) { digitalWrite(pins[i], value); digitalWrite(pins[j], value); delay(150); digitalWrite(pins[i],0); digitalWrite(pins[j],0); } } /** * */ void blinkAll() { for (int i = 0; i < nr_of_blinks; i++) { setAllLights(1); delay(blink_delay); // setAllLights(0); // delay(blink_delay); } } void runningLight(int startvalue) { setAllLights(startvalue); for (int j = 0; j < 2; j++) { randomize(); for (int i = 0; i < NR_OF_LIGHTS; i++) { analogWrite(pins[randomlights[i]], 255 - startvalue); delay(200); analogWrite(pins[randomlights[i]], startvalue); } } } void randomize() { for (int i = 0; i < NR_OF_LIGHTS; i++) { chosenlights[i] = false; } //first one always ok int r = (int)random(NR_OF_LIGHTS); randomlights[0] = r; chosenlights[r] = true; //next 4 for (int i = 1; i < 5; i++) { while (true) { r = (int)random(NR_OF_LIGHTS); if (chosenlights[r] == false) { break; } } randomlights[i] = r; chosenlights[r] = true; } //last one for (int i = 0; i < NR_OF_LIGHTS; i++) { if (chosenlights[i] == false) { randomlights[5] = i; break; } } } void loop(){ DDRD=0xFC; // direction variable for port D - make em all outputs except serial pins 0 & 1 DDRB=0xFF; // direction variable for port B - all outputs for (int j = 0; j < fade_cycles; j++) { fade_routine(); } if (first_time) { Serial.print("?f"); //clear screen delay(1000); Serial.print("Happy Valentines"); Serial.print(" Love Bob "); first_time = 0; } if (effect == 1) { blinkAll(); effect = 1; } /* else if (effect == 2) { runningLight(0); effect = 3; } else if (effect == 3) { runningLight(255); effect = 1; } */ }

Valentine's Heart with Fading/Blinking LEDs


Catching up on documenting old projects, this one from 2009.
The inspiration for this project was a posting from the good guys at NerdKits.
Humberto Evans gives a great tutorial on a Valentine's Day blinking heart, check it out along with the other great entries on their site.
I decided to try and make my own without the kit offered from the NerdKits folks. After a little research, I found alsmost all of the code I needed was available from Paul Badger of Modern Device, famous for the RBBB Arduino clone and other great products.

Img_1706

The actual base is just a sturdy piece of cardboard cut in the shape of a heart. There are heart templates easily found with a google search. I made an easel stand with another piece of cardboard properly cut at an angle. The code is running on a Modern Device RBBB Arduino, powering the LEDs through a black mini-breadboard that contains current-limiting resistors for the LEDS. The RBBB also prints a message on a 2x16 serial LCD. The 5mm red LEDs are poked through the base, hot-glued to the back. The wires are covered in heat shrink and then fastened in bundles with cable ties to make them neat.
The tricky part is keeping all of the LEDs in the proper order for sequencing the display. You may want to write down on the back of the heart which LED goes to which pin on the RBBB.

Here's the code, hope posterous doesn't mess it up!
/*
 PWMallPins.pde
 Paul Badger 2007
 A program to illustrate one way to implement a PWM loop.
 This program fades LED's on Arduino digital pins 2 through 13 in a sinewave pattern.
 It could be modified to also modulate pins 0 & 1 and the analog pins.
 I didn't modulate pins 0 & 1 just because of the hassle of disconnecting the LEDs on the RX and TX pins (0 & 1).
 The PWM loop, as written, operates at about 175 HZ and is flicker-free.
 The trick to this of course is not doing too much math in between the PWM loop cycles.
 Long delays between the loops are going to show up as flicker. This is true especially of "Serial.print" debug statements
 which seem to hog the processor during transmission. Shorter (timewise) statements will just dim the maximum brightness of the LED's.
 There are a couple of lines of code (commented out) that implement a potentiometer as a speed control for the dimming.
 How it works: The PWM loop turns on all LED's whose values are greater than 0 at the start of the PWM loop.
 It then turns off the LED's as the loop variable is equal to the channel's PWM modulation value.
 Because the values are limited to 255 (just by the table), all LED's are turned off at the end of the PWM loop.
 This has the side effect of making any extra math, sensor reading. etc. will increase the "off" period of the LED's duty cycle. Consequently
 the brightest LED value (255), which is supposed to represent a "100%" duty cycle (on all the time), dosesn't really do that.
 More math, sensor reading etc will increase the "off time", dimming the LED's max brightness. You can (somewhat) make up for this dimming with
 smaller series resistors, since LED's can be overrated if they aren't on all of the time.
 The up side of this arrangement is that the LED's stay flicker free.
 Note that this program could easily be used to modulate motor speeds with the addition of driver transistors or MOSFET's.
 */
// random fading in and out of
// christmas tree lights
#define NR_OF_LIGHTS 12
int pins[NR_OF_LIGHTS] = {2, 8, 6, 4, 12, 13, 10, 9, 7, 5, 3, 11 };
int values[NR_OF_LIGHTS];
int steps[NR_OF_LIGHTS];
#define NR_OF_FADESTEPS 4
int fadesteps[NR_OF_FADESTEPS] = { 192, 128, 64, 0 };
int fade_delay = 30; // millisec
int fade_cycles = 1000;
int nr_of_blinks = 2;
int blink_delay = 400; //millisec
int effect = 1;
int randomlights[NR_OF_LIGHTS];
bool chosenlights[NR_OF_LIGHTS];
int first_time = 1;

long time;                                                                    // variable for speed debug
float pwmSpeed[14] = {
 0, 0, 1.2, 1.3, 1.4, 1.9, .9, .8, .5, 1.2, 1.37, 1.47, .3, 3.2};             // these constants set the rate of dimming
int pwmVal[14];                                                               // PWM values for 12 channels - 0 & 1 included but not used
float pwmFloats[14];
int i, j, k, l, x, y, z, bufsize, pot;                                        // variables for various counters
unsigned char sinewave[] =        //256 values
{
  0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
  0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
  0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
  0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
  0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
  0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
  0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
  0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
  0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
  0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
  0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
  0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
  0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
  0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
};

void setup(){
  pinMode(1, OUTPUT);
  Serial.begin(9600);
  Serial.print("?vmv4");
  Serial.print("?G216");


  DDRD=0xFC;      // direction variable for port D - make em all outputs except serial pins 0 & 1
  DDRB=0xFF;      // direction variable for port B - all outputs
}
void fade_routine() {
    // time = millis();               // this was to test the loop speed
  // for (z=0; z<1000; z++){        // ditto
  //  pot = analogRead(0);          // this implemented a potentiometer speed control to control speed of fading
  for (y=0; y<14; y++){             // calculate one new pwm value every time through the control loop
    j = (j + 1) % 12;              // calculate a new j every time - modulo operator makes it cycle back to 0 after 11
    k = j + 2;                      // add 2 tp tje result - this yields a cycle of 2 to 13 for the channel (pin) select numbers
    pwmFloats[k] =  (pwmFloats[k] + pwmSpeed[k]);
    // pwmFloats[k] =  (pwmFloats[k] + ((pwmSpeed[k]  * 15 * (float)pot) / 1023));    // implements potentiometer speed control - see line above
      if (pwmFloats[k] >= 256){                  // wrop around sinewave table index values that are larger than 256
      pwmFloats[k] = pwmFloats[k] - 256;
    }
    else if  (pwmFloats[k] < 0){
      pwmFloats[k] = pwmFloats[k] + 256;        // wrop around sinewave table index values that are less than 0
    }
    pwmVal[k] = sinewave[(int)pwmFloats[k]];                   // convert the float value to an integer and get the value out of the sinewave index
  }
  PORTD = 0xFC;              // all outputs except serial pins 0 & 1
  PORTB = 0xFF;              // turn on all pins of ports D & B
for (z=0; z<3; z++){         // this loop just adds some more repetitions of the loop below to cut down on the time overhead of loop above
                             // increase this until you start to preceive flicker - then back off - decrease for more responsive sensor input reads
  for (x=0; x<256; x++){
    for( i=2; i<14; i++){    // start with 2 to avoid serial pins
      if (x == pwmVal[i]){
        if (i < 8){    // corresponds to PORTD
          // bitshift a one into the proper bit then reverse the whole byte
          // equivalent to the line below but around 4 times faster
          // digitalWrite(i, LOW);
          PORTD = PORTD & (~(1 << i));
        }  
        else{  
          PORTB = PORTB & (~(1 << (i-8)));         // corresponds to PORTB - same as digitalWrite(pin, LOW); - on Port B pins
        }
      }
    }
  }
}
  }
void setAllLights(int value) {
  for (int i = 0; i < NR_OF_LIGHTS; i++) {
    digitalWrite(pins[i], value);
    delay(150);
    digitalWrite(pins[i],0);
  }
  for (int i = NR_OF_LIGHTS; i>0; i--) {
    digitalWrite(pins[i], value);
    delay(150);
    digitalWrite(pins[i],0);
  }
 
  for (int i = 6, j=7; i>0; i--, j++) {
    digitalWrite(pins[i], value);
    digitalWrite(pins[j], value);
    delay(150);
    digitalWrite(pins[i],0);
    digitalWrite(pins[j],0);
  }
 for (int i = 0, j = 12; i < 7; i++, j--) {
    digitalWrite(pins[i], value);
    digitalWrite(pins[j], value);
    delay(150);
    digitalWrite(pins[i],0);
     digitalWrite(pins[j],0);
  }
}
/**
 *
 */
void blinkAll() {
  for (int i = 0; i < nr_of_blinks; i++) {
    setAllLights(1);
    delay(blink_delay);
   // setAllLights(0);
   // delay(blink_delay);
  }
}

void runningLight(int startvalue) {
  setAllLights(startvalue);
  for (int j = 0; j < 2; j++) {
    randomize();
    for (int i = 0; i < NR_OF_LIGHTS; i++) {
    analogWrite(pins[randomlights[i]], 255 - startvalue);
    delay(200);
    analogWrite(pins[randomlights[i]], startvalue);
    }
  }
}
void randomize() {
  for (int i = 0; i < NR_OF_LIGHTS; i++) {
    chosenlights[i] = false;
  }
  //first one always ok
  int r = (int)random(NR_OF_LIGHTS);
  randomlights[0] = r;
  chosenlights[r] = true;
  //next 4
  for (int i = 1; i < 5; i++) {
    while (true) {
    r = (int)random(NR_OF_LIGHTS);
    if (chosenlights[r] == false) {
      break;
    }
    }
    randomlights[i] = r;
    chosenlights[r] = true;
  }
  //last one
  for (int i = 0; i < NR_OF_LIGHTS; i++) {
    if (chosenlights[i] == false) {
    randomlights[5] = i;
    break;
    }
  }
}

void loop(){
 
 
   DDRD=0xFC;      // direction variable for port D - make em all outputs except serial pins 0 & 1
  DDRB=0xFF;      // direction variable for port B - all outputs
   for (int j = 0; j < fade_cycles; j++) {
    fade_routine();
    }
  if (first_time) {
     Serial.print("?f");  //clear screen
    delay(1000);
    Serial.print("Happy Valentines");
    Serial.print("       Love Bob     ");
    first_time = 0;
  }
 
     
 
 
 
 
 if (effect == 1) {
    blinkAll();
    effect = 1;
  }
  /*
  else if (effect == 2) {
    runningLight(0);
    effect = 3;
  }
  else if (effect == 3) {
    runningLight(255);
    effect = 1;
  }
 */
 
}