OpenHeartLib: Difference between revisions

From Noisebridge
Jump to navigation Jump to search
(New page: 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 [http://www.jimmieprodg...)
 
m (Undo revision 45959 by IgAZwmawpK (talk))
 
(5 intermediate revisions by 2 users 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.
 
'''''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 13: 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 30: Line 42:
};
};
</pre>
</pre>
=== File OpenHeartLib.cpp ===
===== File OpenHeartLib.cpp (slightly simplified) =====
<pre>
<pre>
#include <OpenHeartLib.h>
#include <OpenHeartLib.h>
Line 83: Line 95:
     pinMode (allpins[i], INPUT);
     pinMode (allpins[i], INPUT);
}
}
</pre>
Note that the two actual source files contains the Open Heart Animation
Programmer-generated comment lines:
<pre>
//**************************************************************//
//  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 //
//**************************************************************//
</pre>
</pre>


Line 88: Line 115:
of the OpenHearLib class.  It simply flashes the 27 LEDs one at a time:
of the OpenHearLib class.  It simply flashes the 27 LEDs one at a time:


=== File Bill_OpenHeartMain.pde (section NN=1, simplified) ===
(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) =====
<pre>
<pre>


Line 108: 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 133: 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 160: 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 184: Line 215:
</pre>
</pre>


Here is the complete "main" test sketch, with one of eight sections
Here is the complete "main" test sketch, where you must select one of the eight
selected 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 220: Line 251:


#elif (NN == 2)
#elif (NN == 2)
//--- Looping over frames w/o delay(). 'g' is for "global"
//--- Looping over frames w/o delay().
int  gDelayTime = 100;
int  gDelayTime = 100;
byte gkmax      = 27;
byte gkmax      = 27;
Line 240: Line 271:
     gk = 0;
     gk = 0;
}
}
 
#elif (NN == 3)
#elif (NN == 3)
//--- DOUBLE Looping with delay()
//--- DOUBLE Looping with delay()
Line 307: 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();
Line 320: Line 351:
int  gDelayTime = 1000;
int  gDelayTime = 1000;
unsigned long gLastTime = millis();
unsigned long gLastTime = millis();
   int kstart[] = { 11,  0 };          // 16/11 ~ 1.5  
   int kstart[] = { 11,  0 };          // 16/11 ~ 1.5
   int kend  [] = { 27, 11 };
   int kend  [] = { 27, 11 };
void setup() {
void setup() {
Line 392: Line 423:
//# ++++++++++++++
//# ++++++++++++++
//#  From: http://www.instructables.com/id/Charlieplexing-LEDs--The-theory
//#  From: http://www.instructables.com/id/Charlieplexing-LEDs--The-theory
//#  
//#
//#  0v/5v/open A -----[R]-----*-----*-----*-----*    LED ||  A  |  B  |  C
//#  0v/5v/open A -----[R]-----*-----*-----*-----*    LED ||  A  |  B  |  C
//#                            |    |    |    |    ======================
//#                            |    |    |    |    ======================

Latest revision as of 17:29, 31 December 2014

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)[edit]
#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)[edit]
#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)[edit]

#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)[edit]

#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)[edit]
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)[edit]
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)[edit]
//# 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          ]