OpenHeartLib: Difference between revisions
BillyBuggy (talk | contribs) No edit summary |
BillyBuggy (talk | contribs) No edit summary |
||
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 15: | Line 21: | ||
* .../hardware/libraries/OpenHeartLib/OpenHeartLib.cpp | * .../hardware/libraries/OpenHeartLib/OpenHeartLib.cpp | ||
===== File OpenHeartLib.h ===== | ===== File OpenHeartLib.h (slightly simplified) ===== | ||
<pre> | <pre> | ||
#include <WConstants.h> | #include <WConstants.h> | ||
Line 32: | Line 38: | ||
}; | }; | ||
</pre> | </pre> | ||
===== File OpenHeartLib.cpp ===== | ===== File OpenHeartLib.cpp (slightly simplified) ===== | ||
<pre> | <pre> | ||
#include <OpenHeartLib.h> | #include <OpenHeartLib.h> | ||
Line 109: | Line 115: | ||
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 135: | ||
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 160: | ||
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 187: | ||
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 214: | ||
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 334: | ||
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 11:42, 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
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 ]