OpenHeartLib: Difference between revisions
BillyBuggy (talk | contribs) (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...) |
BillyBuggy (talk | contribs) No edit summary |
||
Line 13: | Line 13: | ||
* .../hardware/libraries/OpenHeartLib/OpenHeartLib.cpp | * .../hardware/libraries/OpenHeartLib/OpenHeartLib.cpp | ||
=== File OpenHeartLib.h === | ===== File OpenHeartLib.h ===== | ||
<pre> | <pre> | ||
#include <WConstants.h> | #include <WConstants.h> | ||
Line 30: | Line 30: | ||
}; | }; | ||
</pre> | </pre> | ||
=== File OpenHeartLib.cpp === | ===== File OpenHeartLib.cpp ===== | ||
<pre> | <pre> | ||
#include <OpenHeartLib.h> | #include <OpenHeartLib.h> | ||
Line 83: | Line 83: | ||
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 103: | ||
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 127: | ||
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 152: | ||
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 179: | ||
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 203: | ||
</pre> | </pre> | ||
Here is the complete "main" test sketch, | Here is the complete "main" test sketch, where you must select of the eight | ||
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 239: | ||
#elif (NN == 2) | #elif (NN == 2) | ||
//--- Looping over frames w/o delay(). | //--- Looping over frames w/o delay(). | ||
int gDelayTime = 100; | int gDelayTime = 100; | ||
byte gkmax = 27; | byte gkmax = 27; | ||
Line 240: | Line 259: | ||
gk = 0; | gk = 0; | ||
} | } | ||
#elif (NN == 3) | #elif (NN == 3) | ||
//--- DOUBLE Looping with delay() | //--- DOUBLE Looping with delay() | ||
Line 320: | Line 339: | ||
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 411: | ||
//# ++++++++++++++ | //# ++++++++++++++ | ||
//# 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 | ||
//# | | | | ====================== | //# | | | | ====================== |
Revision as of 02:24, 18 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.
While I simply adore the Open Heart Animation Programmer, 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.
The class library consists of only two files (besides your "main" sketch file):
* .../hardware/libraries/OpenHeartLib/OpenHeartLib.h * .../hardware/libraries/OpenHeartLib/OpenHeartLib.cpp
File OpenHeartLib.h
#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
#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 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 ) 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 ]