OpenHeartLib: Difference between revisions

From Noisebridge
Jump to navigation Jump to search
No edit summary
No edit summary
(One intermediate revision by the same user not shown)
Line 1: Line 1:
At the [[Noisebridge]]  soldering party on Valentine's Day, February 14 2009,
At the [[Noisebridge]]  soldering party on Valentine's Day, February 14 2009,
thanks to the gracious assistance of Mitch, Christie, Andy, and many others,
thanks to the gracious assistance of Mitch, Christie, Andy, and many others,
I built a [http://www.jimmieprodgers.com/openheart Jimmie Rodgers' Open Heart].
I built a [http://www.jimmieprodgers.com/openheart Jimmie Rodgers' Open Heart]
Arduino LED thingy.  This kit is a passive LED I/O device and needs a separate
Arduino board to operate.


<youtube>Nad5AJaIcBI</youtube>
'''''I posted a video demonstrating the eight sections of my main sketch'''''
'''''(listed below) at http://www.youtube.com/watch?v=WIUIz8DRLxg .'''''


While I simply adore the
While I simply adore the
[http://www.jimmieprodgers.com/OpenHeartProgrammer.html Open Heart Animation Programmer],
[http://www.jimmieprodgers.com/OpenHeartProgrammer.html Open Heart Animation Programmer] Flash .swf application,
I decided to take a look at the code it generates
I decided to take a look at the code it generates
and write a small C++ class for the Arduino Development Environment,
and write a small C++ class for the Arduino Development Environment,
that lets you control the Open Heart LED's by calling simple functions.
that lets you control the Open Heart LED's by calling simple functions.
(Note, I am relatively new to Arduino coding and would appreciate
comments from others; you cannot hurt my feelings! :-) )


The class library consists of only two files (besides your "main" sketch file):
The class library consists of only two files (besides your "main" sketch file):
Line 15: Line 21:
   * .../hardware/libraries/OpenHeartLib/OpenHeartLib.cpp
   * .../hardware/libraries/OpenHeartLib/OpenHeartLib.cpp


===== File OpenHeartLib.h =====
(The wiki will not let me upload a .zip so send me email at nye2@email.com
and I will email you file OpenHeartLib.zip containing these two files and
my eight-section main sketch.)
 
===== File OpenHeartLib.h (slightly simplified) =====
<pre>
<pre>
#include <WConstants.h>
#include <WConstants.h>
Line 32: Line 42:
};
};
</pre>
</pre>
===== File OpenHeartLib.cpp =====
===== File OpenHeartLib.cpp (slightly simplified) =====
<pre>
<pre>
#include <OpenHeartLib.h>
#include <OpenHeartLib.h>
Line 109: Line 119:
in after clicking on "Generate" in the Open Heart Animation Programmer.)
in after clicking on "Generate" in the Open Heart Animation Programmer.)


===== File Bill_OpenHeartMain.pde (section NN=1, simplified) =====
===== File Bill_OpenHeartMain.pde (section NN=1, simplified) =====
<pre>
<pre>


Line 129: Line 139:
throughout your code, as shown in the above code rewritten:
throughout your code, as shown in the above code rewritten:


===== File Bill_OpenHeartMain.pde (section NN=1) =====
===== File Bill_OpenHeartMain.pde (section NN=1) =====
<pre>
<pre>


Line 154: Line 164:
the ''for'' loop and use something like:
the ''for'' loop and use something like:


===== File Bill_OpenHeartMain.pde (section NN=2) =====
===== File Bill_OpenHeartMain.pde (section NN=2) =====
<pre>
<pre>
int  gDelayTime = 100;
int  gDelayTime = 100;
Line 181: Line 191:
on at the same time, you have to multiplex at high speed:
on at the same time, you have to multiplex at high speed:


===== File Bill_OpenHeartMain.pde (section NN=5) =====
===== File Bill_OpenHeartMain.pde (section NN=5) =====
<pre>
<pre>
int  gDelayTime = 300;
int  gDelayTime = 300;
Line 208: Line 218:
sections by setting the '''#define NN''' value:
sections by setting the '''#define NN''' value:


===== File Bill_OpenHeartMain.pde (complete) =====
===== File Bill_OpenHeartMain.pde (complete) =====
<pre>
<pre>
//# NN=1 -- With delay().
//# NN=1 -- With delay().
Line 328: Line 338:
       for ( byte k=kstart[j] ; k<kend[j] ; k++ ) {
       for ( byte k=kstart[j] ; k<kend[j] ; k++ ) {
         ht.turnOnLed( k );
         ht.turnOnLed( k );
         if ( j == 1 )
         if ( j == 1 )     // Twinkle the middle LED group (ie, flash slower).
           delay( 5 );
           delay( 5 );
         ht.alloff();
         ht.alloff();

Revision as of 13:14, 20 February 2009

At the Noisebridge soldering party on Valentine's Day, February 14 2009, thanks to the gracious assistance of Mitch, Christie, Andy, and many others, I built a Jimmie Rodgers' Open Heart Arduino LED thingy. This kit is a passive LED I/O device and needs a separate Arduino board to operate.

I posted a video demonstrating the eight sections of my main sketch (listed below) at http://www.youtube.com/watch?v=WIUIz8DRLxg .

While I simply adore the Open Heart Animation Programmer Flash .swf application, I decided to take a look at the code it generates and write a small C++ class for the Arduino Development Environment, that lets you control the Open Heart LED's by calling simple functions.

(Note, I am relatively new to Arduino coding and would appreciate comments from others; you cannot hurt my feelings! :-) )

The class library consists of only two files (besides your "main" sketch file):

 * .../hardware/libraries/OpenHeartLib/OpenHeartLib.h
 * .../hardware/libraries/OpenHeartLib/OpenHeartLib.cpp

(The wiki will not let me upload a .zip so send me email at nye2@email.com and I will email you file OpenHeartLib.zip containing these two files and my eight-section main sketch.)

File OpenHeartLib.h (slightly simplified)
#include <WConstants.h>

class OpenHeart {
public:
  OpenHeart();
  void setPinMap( byte pin1, byte pin2, byte pin3,
                  byte pin4, byte pin5, byte pin6 );
  void turnOnLed( byte led );
  void alloff();
private:
  static byte pin1, pin2, pin3, pin4, pin5, pin6;
  static byte heartpins[27][2];
  static byte allpins[];
};
File OpenHeartLib.cpp (slightly simplified)
#include <OpenHeartLib.h>

byte OpenHeart::pin1=1, OpenHeart::pin2=2, OpenHeart::pin3=3,
     OpenHeart::pin4=4, OpenHeart::pin5=5, OpenHeart::pin6=6;

byte OpenHeart::heartpins[27][2] = {
  {pin3, pin1}, {pin1, pin3}, {pin2, pin1}, {pin1, pin2}, {pin3, pin4},
  {pin4, pin1}, {pin1, pin4}, {pin1, pin5}, {pin6, pin1}, {pin1, pin6},
  {pin6, pin2}, {pin4, pin3}, {pin3, pin5}, {pin5, pin3}, {pin5, pin1},
  {pin2, pin5}, {pin5, pin2}, {pin2, pin6}, {pin4, pin5}, {pin5, pin4},
  {pin3, pin2}, {pin6, pin5}, {pin5, pin6}, {pin4, pin6}, {pin2, pin3},
  {pin6, pin4}, {pin4, pin2}
};

byte OpenHeart::allpins[7];   // Gets indexed 1~6, 0 is unused.

OpenHeart::OpenHeart() {};

void OpenHeart::setPinMap( byte pin1, byte pin2, byte pin3,
                           byte pin4, byte pin5, byte pin6 ) {
  allpins[1] = pin1;
  allpins[2] = pin2;
  allpins[3] = pin3;
  allpins[4] = pin4;
  allpins[5] = pin5;
  allpins[6] = pin6;
  for ( byte k=0 ; k<27 ; k++ ) {
    heartpins[k][0] = allpins[ heartpins[k][0] ];
    heartpins[k][1] = allpins[ heartpins[k][1] ];
  }
  OpenHeart::pin1 = pin1;
  OpenHeart::pin2 = pin2;
  OpenHeart::pin3 = pin3;
  OpenHeart::pin4 = pin4;
  OpenHeart::pin5 = pin5;
  OpenHeart::pin6 = pin6;
}

void OpenHeart::turnOnLed (byte led) {
  byte pospin = heartpins[led][0];
  byte negpin = heartpins[led][1];
  pinMode      (pospin, OUTPUT);
  pinMode      (negpin, OUTPUT);
  digitalWrite (pospin, HIGH  );
  digitalWrite (negpin, LOW   );
}

void OpenHeart::alloff() {
  for( byte i=1 ; i<7 ; i++ )
    pinMode (allpins[i], INPUT);
}

Note that the two actual source files contains the Open Heart Animation Programmer-generated comment lines:

//**************************************************************//
//  Name    : Charlieplexed Heart control                       //
//  Author  : Jimmie P Rodgers   www.jimmieprodgers.com         //
//  Date    : 08 Feb, 2008  Last update on 02/13/08             //
//  Version : 1.3                                               //
//  Notes   : Uses Charlieplexing techniques to light up        //
//          : a matrix of 27 LEDs in the shape of a heart       //
//          : project website: www.jimmieprodgers.com/openheart //
//**************************************************************//

The following minimal "main" sketch shows a simple usage of the OpenHearLib class. It simply flashes the 27 LEDs one at a time:

(In all of the sample sketches here, you likely have to modify the pin arguments to setPinMap(). These are the same pin mappings that you fill in after clicking on "Generate" in the Open Heart Animation Programmer.)

File Bill_OpenHeartMain.pde (section NN=1, simplified)

#include <OpenHeartLib.h>
OpenHeart ht = OpenHeart();
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  for ( byte k=0 ; k<27 ; k++ ) {
    ht.turnOnLed (k);
    delay (100);     // (msec)
    ht.alloff ();
  }
}

Sometimes it is better to use variables instead of sprinkling constants throughout your code, as shown in the above code rewritten:

File Bill_OpenHeartMain.pde (section NN=1)

#include <OpenHeartLib.h>
OpenHeart ht = OpenHeart();
int  gDelayTime = 100;  // (msec) 'g' stands for "global".
byte gkmax      = 27;   // Number of LEDs
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  for ( byte k=0 ; k<gkmax ; k++ ) {
    ht.turnOnLed (k);
    delay (gDelayTime);
    ht.alloff ();
  }
}

If all you wanted to do with your Arduino was flash the LEDs, the above code is fine. But if you need to do some other computations, such as at the end of your function loop(), you would not want to put your cpu in massive "wait" states with those delay() calls. Instead, you can "unroll" the for loop and use something like:

File Bill_OpenHeartMain.pde (section NN=2)
int  gDelayTime = 100;
byte gkmax      = 27;
byte gk         = 0;
unsigned long gLastTime = millis();
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  if ( gk < gkmax ) {
    if  ( millis() < gLastTime + gDelayTime )
      ht.turnOnLed (gk);
    else {
      ht.alloff();
      gk++;
      gLastTime = millis();
    }
  } else
    gk = 0;
}

Because of how charlieplexing works, you can only turn on one LED at a time with method turnOnLed(). To make it appear that more than one LED is on at the same time, you have to multiplex at high speed:

File Bill_OpenHeartMain.pde (section NN=5)
int  gDelayTime = 300;
unsigned long gLastTime = millis();
int kstart[] = { 23,  4,  0 };   // Three groups of LED's
int kend  [] = { 27, 23,  4 };
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  for ( byte j=0 ; j<3 ; j++ ) {
    while ( millis() < gLastTime + gDelayTime ) {
      for ( byte k=kstart[j] ; k<kend[j] ; k++ ) {
        ht.turnOnLed( k );
        if ( j == 1 )     // Twinkle the middle LED group (ie, flash slower).
          delay( 5 );
        ht.alloff();
      }
    }
    gLastTime = millis();
  }
}

Here is the complete "main" test sketch, where you must select one of the eight sections by setting the #define NN value:

File Bill_OpenHeartMain.pde (complete)
//# NN=1 -- With delay().
//# NN=2 -- Looping over frames w/o delay().
//# NN=3 -- DOUBLE Looping with delay().
//# NN=4 -- Program DOUBLE looping over frames w/o delay().
//# NN=5 -- Simultaneous with tiny delay().
//# NN=6 -- Two flickering groups.
//# NN=7 -- Purely random, one LED at a time.
//# NN=8 -- Sequential concentric ring groups -- BEATING HEART!

#define NN 8    /* Pick which section below to compile and execute. */

#include <OpenHeartLib.h>

OpenHeart ht = OpenHeart();   // OpenHeart is a class.

#if (NN == 1)
//--- With delay(). 'g' is for "global"
int  gDelayTime = 100;
byte gkmax      = 27;   // Number of Open Heart LED's
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  for ( byte k=0 ; k<gkmax ; k++ ) {
    ht.turnOnLed (k);
    delay        (gDelayTime);
    ht.alloff    ();
  }
}

#elif (NN == 2)
//--- Looping over frames w/o delay().
int  gDelayTime = 100;
byte gkmax      = 27;
byte gk         = 0;
unsigned long gLastTime = millis();
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  if ( gk < gkmax ) {
    if  ( millis() < gLastTime + gDelayTime )
      ht.turnOnLed (gk);
    else {
      ht.alloff();
      gk++;
      gLastTime = millis();
    }
  } else
    gk = 0;
}

#elif (NN == 3)
//--- DOUBLE Looping with delay()
int  gDelayTime = 100;
byte gjmax      = 3;
byte gkmax      = 9;
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  for   ( byte j=0 ; j<gjmax ; j++ ) {
    for ( byte k=0 ; k<gkmax ; k++ ) {
      ht.turnOnLed( j*gkmax + k );
      delay       ( gDelayTime );
      ht.alloff   ();
    }
  }
}

#elif (NN == 4)
//--- Program DOUBLE looping over frames w/o delay()
int  gDelayTime = 100;
byte gjmax      = 3;
byte gkmax      = 9;
byte gj         = 0;
byte gk         = 0;
unsigned long gLastTime = millis();
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
  gLastTime = millis();  // Not needed unless above executes too early.
}
void loop() {
  if   ( gj < gjmax ) {
    if ( gk < gkmax ) {
      if ( millis() < gLastTime + gDelayTime )
        ht.turnOnLed(gj*9 + gk);
      else {
        ht.alloff();
        gk++;
        gLastTime = millis();
      }
    } else {
      gk = 0;  // Restart inner.
      gj++;    // Increment outer.
    }
  } else
    gj = 0;    // Restart outer.
}

#elif (NN == 5)
//--- Simultaneous with tiny delay()
int  gDelayTime = 300;
unsigned long gLastTime = millis();
//int kstart[] = {  0, 11, 23 };   // Top to bottom.
//int kend  [] = { 11, 23, 27 };
//int kstart[] = { 23, 11,  0 };   // Bottom to top.
//int kend  [] = { 27, 23, 11 };
  int kstart[] = { 23,  4,  0 };   // Bot-to-top many lights in top group
  int kend  [] = { 27, 23,  4 };
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  for ( byte j=0 ; j<3 ; j++ ) {
    while ( millis() < gLastTime + gDelayTime ) {
      for ( byte k=kstart[j] ; k<kend[j] ; k++ ) {
        ht.turnOnLed( k );
        if ( j == 1 )     // Twinkle the middle LED group (ie, flash slower).
          delay( 5 );
        ht.alloff();
      }
    }
    gLastTime = millis();
  }
}

#elif (NN == 6)
//--- Two flickering groups.
int  gDelayTime = 1000;
unsigned long gLastTime = millis();
  int kstart[] = { 11,  0 };          // 16/11 ~ 1.5
  int kend  [] = { 27, 11 };
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  for ( byte j=0 ; j<2 ; j++ ) {
    while ( millis() < gLastTime + gDelayTime ) {
      for ( byte k=kstart[j] ; k<kend[j] ; k++ ) {
        ht.turnOnLed( k );
        delay( j==0 ? 4 : 6 );
        ht.alloff();
      }
    }
    gLastTime = millis();
  }
}

#elif (NN == 7)
//--- Purely random, one LED at a time.
int  gDelayTime = 1;
void setup() {
  Serial.begin(9600);
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  int k = random(27);
  ht.turnOnLed( k );
  delay( gDelayTime );
  ht.alloff();
  delay( gDelayTime );
}

#elif (NN == 8)
//--- Sequential concentric ring groups -- BEATING HEART!
int gDelayTime = 250;
byte rings[][13] = { { 0,  1,  2,  3,  4, 10, 11, 17, 18, 22, 23, 25, 26},
                     { 5,  6,  7,  8,  9, 12, 16, 19, 21, 24, 99, 99, 99},
                     {13, 14, 15, 20, 99, 99, 99, 99, 99, 99, 99, 99, 99},
                     { 5,  6,  7,  8,  9, 12, 16, 19, 21, 24, 99, 99, 99}
                   };
byte ringsN[ ] = { 13, 10, 4, 10 };  // Number of non-99 in each row above.
byte Nrings = 4;
byte ringNo = 0;
unsigned long gLastTime = millis();
void setup() {
  ht.setPinMap( 4, 5, 3, 2, 7, 6 );
}
void loop() {
  byte k;
  if ( ringNo < Nrings ) {
    k = 0;
    while ( millis() < gLastTime + gDelayTime ) {
      byte led = rings [ringNo][k];
      if ( led != 99 ) {
        ht.turnOnLed( led );
        delay( 10 * (ringsN[0]+0.0)/ringsN[ringNo] );
        ht.alloff();
      }
      k = (k+1) % ringsN[ringNo];
    }
    ringNo++;
    gLastTime = millis();
  } else
    ringNo = 0;
}

#endif

//# Charlieplexing
//# ++++++++++++++
//#   From: http://www.instructables.com/id/Charlieplexing-LEDs--The-theory
//#
//#   0v/5v/open A -----[R]-----*-----*-----*-----*    LED ||  A  |  B  |  C
//#                             |     |     |     |    ======================
//#                           1 V   2 ^     |     |     1      1     0    XXX
//#                             |     |     |     |     2      0     1    XXX
//#   0v/5v/open B -----[R]-----*-----*   5 V   6 ^     3     XXX    1     0
//#                             |     |     |     |     4     XXX    0     1
//#                           3 V   4 ^     |     |     5      1    XXX    0
//#                             |     |     |     |     6      0    XXX    1
//#   0v/5v/open C -----[R]-----*-----*-----*-----*

//# Open Heart LED Numbers
//#   [     0  1     2  3    ]
//#   [  4  5  6  7  8  9 10 ]
//#   [ 11 12 13 14 15 16 17 ]
//#   [    18 19 20 21 22    ]
//#   [       23 24 25       ]
//#   [          26          ]