Saturday, January 31, 2015

Steampunk Light Dress Part Four: Progress Thus Far

A basic video showing the progress of the shift register and 16 LED's.  Have ended up cutting corners and not using the ShiftRegister class in all functions; while being a little bit heavier from a documentation standpoint, I managed to trim about 2000 bytes of pointless variables from the wave and pulse functions.

So  

Monday, January 26, 2015

Shift Registers Have Class

Have been working on adapting my standard pin functions to work with a shift register.  First worked on a class for the shift registers.

First step was declaring the basics; bytes for my clock, latch, and data pins, and an int to declare the total number of pins used on the shift register.  Next big concern was tracking the state of each of the eight pins; as the state can only be off or on, this was done with a simple array of booleans. As I would like to incorporate 24 total LED's into the project, so I added two extra arrays to keep track of the two additional shift registers I will have daisy chained to the first.  Next created a constant, static array of bytes to represent each of the pins values (from B1 to B10000000, or 1 to 255 in decimal.)  We want it static because these values can be shared among all shift registers, and we want it constant because binary math should never change within the scope of this program.

As the array of pins matches up with the array of pin values (pin 0 is 1, pin 2 is 01 etc,) all we have to do to activate a pin is pass an int to the void ActivatePin, which sets the appropriate boolean to true.  To get a value for our ShiftOut function, all we have to do now is add up the values for each pin that is set to true - these are stored in an array of 3 bytes; one to hold the value for each shift register we intend to use.

All that remained was to add a few maintenance functions; ShiftClear() to clear the shift register values and ClearPins() to clear the boolean value of each of the pins.  Also added a RandomPin() function to allow it to output a random pin.
Header and cpp file to follow.




ShiftRegister.h
#ifndef LED_h
#define LED_h
#include <Arduino.h>
#include <WProgram.h>

/*
  ShiftRegister.h - Library for standard and RGB LED's
  Beau Bureau, 2014
  Released into the public domain.
*/

class ShiftRegister{
private:
// Gets the binary equivalent of a given pin
int ReturnPin(int);
// sets the clock and data pins
byte clockPin,latchPin,dataPin;
// sets the number of pins
int noPins;
// Stores the values of our shift registers
byte rArray[3];
// gives all the available numbers
static const byte Pins[8];
// Tells the system which pins have been turned on
bool firstPinsActive[8];
bool secondPinsActive[8];
bool thirdPinsActive[8];
// adds the values of the pins.
void CountPins();


public:
ShiftRegister();
// standard shift register constructor
ShiftRegister(byte,byte,byte, int);
// Public members for red, green and blue.
byte ClockPin() {return clockPin;}
byte LatchPin() {return latchPin;}
byte DataPin() {return dataPin;}
int NoPins() {return noPins;}
byte ShiftNum(int);
void ClearShift();
void ActivatePin(int);
void ClearPins();
byte RandomPin();

};


#endif



ShiftRegister.cpp
#include<ShiftRegister.h>
#include <Arduino.h>
#include <WProgram.h>
const byte ShiftRegister::Pins[]={B00000001,B00000010,B00000100,B00001000,B00010000,B00100000,B01000000,B10000000};


ShiftRegister::ShiftRegister() { 
ClearShift();
ClearPins();
}



ShiftRegister::ShiftRegister(byte c, byte l,byte d, int p) { 
clockPin=c;
latchPin=l;
dataPin=d;
noPins=p;
ClearShift();
ClearPins();
}




void ShiftRegister::CountPins() {
ClearShift();
for (int i=0; i<8;i++)
{
if (firstPinsActive[i]==1)
{
rArray[0]=rArray[0]+Pins[i];
}
if (secondPinsActive[i]==1)
{
rArray[1]=rArray[1]+Pins[i];
}

if (firstPinsActive[i]==2)
{
rArray[2]=rArray[2]+Pins[i];
}
}
}


void ShiftRegister::ClearShift() 
{
for (int i=0;i<3;i++)
{
rArray[i]=0;
}
}
void ShiftRegister::ClearPins() {
for (int i=0; i<8;i++)
{
firstPinsActive[i]=0;
secondPinsActive[i]=0;
thirdPinsActive[i]=0;
}
}

// returns the boolean-equivalent integer of the specified register (can be up to 3)
byte ShiftRegister::ShiftNum(int i)
{
if (i<3)
{
return rArray[i];
}
}

void ShiftRegister::ActivatePin(int input)
{
if (input>24)
{
return;
}
if (input>16)
{
input=input-16;
// this pin stops you from counting the same pin twice
if (thirdPinsActive[input-1]==0)
{
// Activate the pin
thirdPinsActive[input-1]=1;
}
}
if (input>8)
{
input=input-8;
if (secondPinsActive[input-1]==0)
{
secondPinsActive[input-1]=1;
}
}
if (input <=8)
{
if (firstPinsActive[input-1]==0)
{
Serial.print("activating pin ");
Serial.println(input);
     
firstPinsActive[input-1]=1;
}
}
CountPins();

}

byte ShiftRegister::RandomPin() {

  // return a random pin number
  
  int output=random(0,noPins-1);
return output;

}

Saturday, January 17, 2015

Figuring out Shift Registers

Shift registers are handy and inexpensive.  Using just three pins on your Arduino, you can control hundreds of others.  I've already gone over the rote basics of how the work from a wiring standpoint, but how does it pan out logically?

Here's a (very rough) sketch of a 74HC595 8-bit register.
So, assuming the shift register is properly connected to the Arduino, each of the outputs from 0-7 (8 total) are treated as bits, which, for those of you who don't know, are the protons and electrons of the information world.  Bits are the fundamental building block of all data from the simple byte (8 bits) all the way  up to the highest definition video file.  They're pretty easy to understand as they can only be 1 or 0, which is handy if you want to store something that only has two possible values; a check box on a form for example (which can be either YES or NO,) or in our case whether a pin has power or not (HIGH or LOW.)  Bits have been with us since the dawn of computer logic; since the early Jacquard Looms which took punch cards embedded with rows bits that told the loom what action to perform next.

You'll notice the shift register above is an 8-bit registers, and as almost anyone familiar with computer networking and IP addresses knows, 8 bits make up a byte.  A byte can have 256 possible values from 0 to 255.  Why 256?  Because instead of counting in the decimal we were taught in grade school (which goes in sets from 0 to 10,) you're counting in binary, which goes in sets of 0 or 1.

This is so simple it's deceptively confusing.  From the right, the first value in a line is the 1's place.  It can be either 0 or 1.  The second value is the 2's place.  It can be either 0 or 2.  It basically doubles from here; 4,8,16,32,64,128 etc.  So if you had a value of 10, you'd add 0 in the 1's place to 1 in the 2's place, giving you 2.  this would make 11 3, 110 6, and it goes on and on.

Apply this to our shift register above.  After passing the clock and latchpin and the way we want it to process the data, we give the shiftout function a number representing the pins we want to toggle.  So let's say we wanted to turn output pins 0 and 2 on, but leave 1 off.  

Imagine it like this:

Pin 0: 1 (1=ON)
Pin 1: 0 (0=OFF)
Pin 2: 1

101 in decimal would be 5 (1 in the 4+0 in the 2+1 in the 1.)  Likewise if we wanted to turn 0-2 on we'd have 111, which would be decimal 7 (4+2+1.)   If you're a bit confused as to why we start with pin 0, it's relatively simple; computers start counting at 0 instead of 1 as it is actually the first value in our number system.

So let's say we wanted to hook up another shift register.  Do we need to take up another 3 pins on the Arduino?  Not at all; we just hook the data pin of Register 2 to the overflow pin of register 1, hook the power and ground of register 2 to the proper rails, and connect the clock and latch pins of Register 2 in parallel with Register 1.  It's all diagrammed here.

The wiring seems simple enough, so what about the logic?  Before I read up on it, I confess that I assumed it was as simple as passing it a larger number.  Each register is an 8-bit address space with 256 possible values, so I assumed that if I passed the register the number 511 it would automatically overflow, lighting up all 8 of Register 1's LED's and the first LED of Register 2.

How naive I was.

Turns out you have to pass the shiftout to the register twice (there's no sense showing the code here, as this blog, again, demonstrates it quite well.)  The first shiftout will pass the value to register 1 and the second will overflow to register 2.  Each shiftout must contain a value between 0 and 256, indicating which bits you want to turn on or off.  I edited the above blog's code (as I only had room for two additional LED's on my breadboard,) and eureka; it worked perfectly.

Hopefully this will provide some basic help understanding both the shiftout function and the broader topic of shift registers in general.

Sunday, January 11, 2015

Steampunk Light Dress Part Five: Overloading Sparkle()

By dividing my  arrays of LED's into groups, or arrays, I can fire off a wider array of effects.  Take the example below.  I have three arrays of LED arrays:
  • 12X2 single-colored LED's (1-12.) 
  • An array of 2X12 RGB LED's  (A and B.)
  • Another array of 12X2 single-color LED's (13-24)
Using arrays OF arrays of LED's, I can:
  • Chase sequentially from 1-23, ignoring the RGB LED's.
  • Do a ping-pong effect (chasing back and forth across the panel) on 1-12 and 13-23 simultaneously.
  • Fade the RGB LED's, leaving 1-23 off.
  • Fire off every other or every third single-color LED.
Only thing that remains is to overload the functions to take either arrays of LED's or arrays OF arrays of LED's.  Why?  Because I'd like to allow for applications that use fewer LED's (like the parasol,) to still use the functions without getting overly complicated.

(As of yet untested) example code follows:

void sparkle(Led ledList[], int arraySize, long seconds)
{
  // the intial setting of the potentiometer.
  int initVal=analogRead(potPin);
  
Serial.println(seconds);
// Convert the milliseconds to seconds
Serial.println("Going to sparkle for ");
Serial.println(seconds);
Serial.println("MS!");
// Set the count variable (this will track the MS invested in the run and tell the program when to stop.)
 long count=0;
 do
 {
 // Select a random bulb
 int bulb=random(0,arraySize);  
 // Turn it on (if it's RGB, randomize the color)
 fireLed(ledList[bulb], true);
 // The on sensitivity obtained from the static sense pin
 byte onSensitivity=map(analogRead(staticSensePin),0,1023,1,12); 
 // Select a random pause time
 int inPause=random(20*onSensitivity,50*onSensitivity);
 // Add this to the total count time
 count+=inPause;
 // Pause
 delay(inPause);z
 // Turn off the selected bulb
 turnOffLed(ledListarrayNum[bulb]);
 // break if we've gone beyond the time
 if (analogRead(potPin)>high||analogRead(potPin)<low)
 {
   return;
 }
 // Adds a value calculated from the sensitivity pot
 byte sensitivity=map(analogRead(sensePin),0,1023,1,12); 
 // Select a random pause time
 int outPause=random(50*sensitivity,500*sensitivity);
 // Add this to the total count time.
 count+=outPause;
 // Pause
 delay(outPause);
Serial.println(count);
 }
 while ((seconds)>count);
 // Stop once the alloted number of minutes are done.
}


void sparkle(Led ledList[][], int arraySize, byte noOfArrays, long seconds)
{
  // the intial setting of the potentiometer.
  int initVal=analogRead(potPin);
  
Serial.println(seconds);
// Convert the milliseconds to seconds
Serial.println("Going to sparkle for ");
Serial.println(seconds);
Serial.println("MS!");
// Set the count variable (this will track the MS invested in the run and tell the program when to stop.)
 long count=0;
 do
 {
 // Select a random bulb
 int arrayNum=random(0,noOfArrays);
 int bulb=random(0,arraySize);  
 // Turn it on (if it's RGB, randomize the color)
 fireLed(ledList[arrayNum][bulb], true);
 // The on sensitivity obtained from the static sense pin
 byte onSensitivity=map(analogRead(staticSensePin),0,1023,1,12); 
 // Select a random pause time
 int inPause=random(20*onSensitivity,50*onSensitivity);
 // Add this to the total count time
 count+=inPause;
 // Pause
 delay(inPause);z
 // Turn off the selected bulb
 turnOffLed(ledListarrayNum[bulb]);
 // break if we've gone beyond the time
 if (analogRead(potPin)>high||analogRead(potPin)<low)
 {
   return;
 }
 // Adds a value calculated from the sensitivity pot
 byte sensitivity=map(analogRead(sensePin),0,1023,1,12); 
 // Select a random pause time
 int outPause=random(50*sensitivity,500*sensitivity);
 // Add this to the total count time.
 count+=outPause;
 // Pause
 delay(outPause);
Serial.println(count);
 }
 while ((seconds)>count);
 // Stop once the alloted number of minutes are done.
}


Wednesday, January 7, 2015

Steampunk Light Dress Part Four: Using Map() and Constrain() to Calibrate a Sound Sensor



Setting up the  Arduino Compatible Mini Sound Sensor for my Victorian bustle project was a bit of a challenge.  This is not a complaint regarding the device.  The sensor is simplicity itself; hook vcc to your 5V, gnd to ground, and OUT to one of your Arduino's analog inputs. Interpreting those values (which in theory could be as high as 1024, but I've never actually had peak above 400,) in a meaningful way is more of a challenge.

The goal was to get the lights in the bustle gown to pulse to music.  Fans, low level chatter, and other ambient noises are not a part of the music, and as such should be filtered as much as possible.  Determining the ambient noise level is, again, simplicity incarnate; just have it collect a steady sum of incoming values and divide that against the number of samples collected to get a running average.  Collecting a peak level is just as simple; have a global peak variable (initialized to 0.)  If the collected sample is above this peak level, it becomes the new peak level.

So you have your average and your peak values.  To determine how brightly your LED's should fire, all you have to do is determine where your collected value falls between these two values, and map that to a number that's meaningful to your RGB (which accepts numbers between 0 and 254.)  A function called 'map' does just that.  Map takes five arguments: an input value (val below,) the range this value should fall into (two values; valAvg (my average) and globalPeak (my ceiling,) and the range you want to put it into (two values: 0 and 254).

int soundNum=map(val,valAvg,globalPeak,0,254); 

So you've got your calibrated sound value.  Probably.  Problems could occur above if val somehow eluded my range.  Let's say a scandal occurs, the room falls silent for >30ms (an eternity in computer time.)   The sensor pulls a 0.  My average is 5.  As this falls outside the expected input range, ti will also be mapped outside the output range.  The result could be -1, a number my LED's (expecting a range of 0-254) won't care for.

The solution is constrain.  Constrain takes three arguments, your initial value (soundNum below,) a floor (0,) and a ceiling (254.)  

soundNum=constrain(soundNum,0,254);

If, in the above example, soundNum was initially -15, it would be set to 0.  If it was 1024, it would be set to 254.

Beautiful.  We now have something that SHOULD accurately represent a calibrated sound level within the room.  We don't HAVE to trust math, of course.  With a simple adjustment of a steampunk dial, my lovely wife can have some level of manual control over the interpreted input value.  Still, proper calibration makes the whole process easier.

There are still bugs to be worked out.  Feeding the global sum variable (even a long) 1-1024 every ms will eventually break it, resulting in highly questionable results (granted, this would take 9,223,372,036,854,775,807 minutes even with a steady input of peak values.)  I'll likely tell it to stop likely stop pulling the average after 10 minutes or so.

Saturday, January 3, 2015

Fading Like a Flower

There comes a time in each man's life when he questions whether or not RGB LED's absolutely need to be incorporated in all the functions he's programming into his wife's Victorian-era bustle dress.  The answer, in my case at least, is "no, no they don't."

It wasn't an easy decision, of course.  On the one hand, the RGB LED's separate red green and blue lamps can be set independently to mock up just about any color, and you can shift and fade through the spectrum at will.  On the other, each of those lamps consume just as many resources as any standard lamp; eg 30ma and a precious pwm pin (the Uno only has six.)  

Unless you want to add an analog mux to the system (am opting to keep things light and simple at this point,) this means you only get two RGB's per strand.  That is unless I run them in parallel 1, A, 2, B, 3, C, 4 (etc...) fashion (where "A" represents the first RGB, "B" represents the second, and the numbers represent the static LED's in line.) This would give me a potential 9 RGB's per strand+9 standard LED's for a total of 18 per side, or 36 total per strand (there will be 2 strands.)  The only function both would interact in would be 'solid,' where the RGB's could be set to a standard red, green, or blue, maxing out only 1 lamp.

Friday, January 2, 2015

Tesla Coil Umbrella?

After seeing it in a Christmas display, seriously considering a tesla coil design for the lights on the umbrella.  Would be relatively simple... 3-4 rows of LED's, each fired off by one pin.

Mucking About with Shift Registers


May be seriously reconsidering how to run this dog&pony show after playing around a bit with a  shift register.  A shift register, to my layman's understanding, holds much in common with the switchboards you see in old  movies or the original Lassie television series; your Arduino dials into the register (via 3 pins) and tells it which of its eight output pins it would like to talk to.  Effectively, three of your pins become eight, so you end up with more than double the number of pins you started with.

Shift registers can be daisy-chained, and while they are digital by nature, there is an analog equivalent called an analog multiplexer/demultiplexer, allowing you to do effectively the same thing.

Steampunk Light Dress Part Three: The Easy Part

Hammered and QA'd the code for keeping the lights steady.  Simple, really; just a for loop that iterates over the all the passed lights (which I established in their own custom class and are passed in the function via an array) and sets them all to on.

Once one of these two things happen:

  •  Given number of seconds (passed to the function as an argument of type long) have passed.
  • The user changes the setting on the main potentiometer (effectively changing the setting.)
The "turnOffAll" function turns off all the lamps and return routes back to the function that called it.

Code follows.  A few values pulled from outside the scope of the function:
  • potPin: the pin of the main potentiometer.  This passes a value from 0 to 1024, and determines which setting the Arduino kicks off.
  • High and Low: the floor and ceiling value range for the current function.  Believe this one runs when the potentiometer is set in the 400-500 range... so if the user sets it below 399 or above 501, they would like the lights to do something different.
 To do:
  • Rename a few of the functions to something more logical
  • More comments

void solid(Led ledList[],int arraySize, long seconds)
{
  // The count increments each time the lights fire.  Once the count exceeds the number of seconds passed, it returns to the main/random loop. 
 long count=0;
// Stores the initial value of the Main Pot.
  int initVal=analogRead(potPin);
// This is our loop.  It runs while both the 
  do {
    for (int thisPin = 0; thisPin < arraySize; thisPin++)  {
        if (ledList[thisPin].IsRGB())
        {
        fireLed(ledList[thisPin],true);
        }
        else {
        digitalWrite(ledList[thisPin].RPin(), HIGH); 
        }
        // Reads the Main Pot.  If the user has set the potentiometer outside the range of the current function (represented by high and low,) set the count to seconds, breaking to do/while loop.
       if (analogRead(potPin)<low||analogRead(potPin)>high)
       {
         // Set the count to seconds.  Once count is iterated, the do/while loop stops. 
         count=seconds;
       }
       count=count+50;
 }
delay(50);
while (count<seconds);
  turnOffAll(ledList,arraySize);
  return;
}

void turnOffAll(Led ledList[], int arraySize)
{
    for (int i=0; i<arraySize;i++)
  {
    turnOffLed(ledList[i]);
  }

}

// Note: the random parameter determines whether or not the LED's color is randomized (false leaves it set at the current color.)
void fireLed(Led led, bool rand)
{
  // Randomize the color
  if (rand&&(led.IsRGB()))
  {
    led.SetRandomColor();
    led.SetRed(255);
    Serial.println("setting random color");


  }

      // Fire red, green, then blue
      analogWrite(led.RPin(),led.Red());
      analogWrite(led.GPin(),led.Green());
      analogWrite(led.BPin(),led.Blue());
      }
      else
      {
      digitalWrite(led.RPin(),HIGH);
      }
}

void turnOffLed(Led led)
{
      if (led.IsRGB())
      {
      // If it's an RGB, turn off red, green, then blue
      analogWrite(led.RPin(),0);
      analogWrite(led.GPin(),0);
      analogWrite(led.BPin(),0);
      }
      else
      {
        digitalWrite(led.RPin(),LOW);
      }

}

Thursday, January 1, 2015

Steampunk Light Dress Part Two: Let there be Sparkles

Hammered out (and tested) a function to make the lights sparkle.  The general idea is:

  • Accept an array of the custom Led class, the size of the array, and the number of seconds the function is to be executed.
  • Select a random bulb
  • If it's an LED bulb, set it to a random color.
  • Light the bulb for a random period of time modified by the static sensitivity pot, which is mapped from x1 to x12.
  •  Verify that the wearer has not changed the settings potentiometer.  If they have, return to the main loop to run another function.
  • Turn off the bulb for a random period of time modified per above on the sensitivity pot.
  • Track the total number of seconds paused.  If more than the number of seconds passed, return to the main loop.
The idea is that the "bursts" should be brief in comparison to the pauses, and both should randomize between long and short, giving it a very random, "sparkling" appearance.

The code follows.  Note that it relies upon the following global variables set outside the function scope:
  •  potPin, Sensepin, Staticsensepin: constants referring to the pin #'s of the setting potentiometer(the dial that tells the Arduino what function to run,) the sensitivity potentiometer (the dial that, in our music application, tells it how much gain to add to the sound sensor's analog signal,) and the static sensitivity potentiometer (the dial that tells the music program how sensitive the static (non RGB) LED's should be.)
  • fireLed, turnOffLed: functions to fire and turn off the LED.
  • High, Low: The floor and ceiling values for the setting potentiometer.  If the potentiometer changes beyond these bounds it means that the user wants to go to another function.


  void sparkle(Led ledList[], int arraySize, long seconds)
{
  // the intial setting of the Settings potentiometer.
  int initVal=analogRead(potPin);
  
// Set the count variable (this will track the MS invested in the run and tell the program when to stop.)
 long count=0;
 do
 {
 // Select a random bulb
 int bulb=random(arraySize);  
 // Turn it on (if it's RGB, randomize the color)
 fireLed(ledList[bulb], true);
 // The on sensitivity obtained from the static sense pin
 byte onSensitivity=map(analogRead(staticSensePin),0,1023,1,12); 
 // Select a random pause time
 int inPause=random(20*onSensitivity,50*onSensitivity);
 // Add this to the total count time
 count+=inPause;
 // Pause
 delay(inPause);
 // Turn off the selected bulb
 turnOffLed(ledList[bulb]);
 // break if we've gone beyond the time
 if (analogRead(potPin)>high||analogRead(potPin)<low)
 {
   return;
 }
 // Adds a value calculated from the sensitivity pot
 byte sensitivity=map(analogRead(sensePin),0,1023,1,12); 
 // Select a random pause time

 int outPause=random(50*sensitivity,500*sensitivity);
 // Add this to the total count time.
 count+=outPause;
 // Pause
 delay(outPause);
Serial.println(count);
 }
 while ((seconds)>count);
 // Stop once the alloted number of minutes are done.
}

How (and Why) to Create a Library in Arduino

So here's the thing I discovered after many hours of frustration... while the Arduino will allow you to create your own custom classes, the IDE under version 1.0.6 compiles everything at once.

This means (to my somewhat rough understanding) that your function is created at the same time the class (which you are trying to pass to it) is.

Error, Will Robinson.  

Does not compute.  

The object you are declaring does not exist.

The good news: there's a way to get around this.  After digging around in the forums, I found you needed to create a library.  For those of you who don't know C libraries, they are containers for classes and other goodies, and an easy way to keep your code modular and clean.  They consist, in c++ of a head file containing the initial class declaration, and a .cpp file containing methods and other such goodies.  All you need to do is add a reference in your file.

While I found the linked article very informative, I had some difficulty figuring out how to create the files themselves.  I must admit that in my professional life I've been pampered by Visual Studio and its ability to handle libraries and other components for you.   How does one create and manage libraries manually?

After doing some digging, I found this blog that pointed me in the correct direction.  Here's how I did it:

  1. Turn off the Arduino IDE (not 100% this is necessary, but it won't recognize your class until it restarts.)
  2. Went into file explorer
  3. Navigated to the program file for Arduino (in my case C:\Program Files (x86)\Arduino)
  4. Open the libraries folder
  5. In the libraries folder, create a new folder (name it for your class (in my case LED))
  6. Open your class folder and create two new notepad files.
  7. Name one [YourClassName].h and the other [YourClassName].cpp (where [YourClassName] is the  same name as you folder.)
  8. Open the .h  file.  Add the lines:
    #ifndef [YourClassName]_h  //If this header has not been defined in this scope...
    #define [YourClassName]_h // Define it
    #include <Arduino.h>  // Reference the arduino program core libraries (not 100% this has to be here, but hey)
    #include <WProgram.h>
  9. Paste in your class declaration.  This will be everything EXCEPT the external methods and members outside the class declaration brackets.
  10. Cap it off with an #endif
  11. Save and close the .h file.
  12. Open the .cpp file.  Add the following lines:
    #include<[YourClassName].h>  // References your header files
    #include <Arduino.h>  // Reference the arduino core libraries
    #include <WProgram.h>
  13. Paste in the remainder of your code.
  14. Reopen the IDE and open your sketch.
  15. Remove the class declaration and any methods/members.
  16. Go to Sketch >> Import Library >> [Your Class Name]
  17. Done!

NOTES/TROUBLESHOOTING:
  • Remember to replace any instances of [YourClassName] with the name of your class.  This needs to be the same between your folder name, cpp and h file.  I would recommend keeping everything down to the capitalization the same!
  • I'm not saying that this is the optimal, expert-prescribed, or only, way to go about this.  This is just what worked for me.
  • If your text editor adds .txt to everything, trim it off the end.  If you have Hide Extensions for known File Types checked under your view options, you may not see the .txt suffix, but it will appear as a notepad icon and NOT process as a library.


My code follows.

Led.h:

#ifndef LED_h
#define LED_h
#include <Arduino.h>
#include <WProgram.h>

/*
  LED.h - Library for standard and RGB LED's
  Beau Bureau, 2014
  Released into the public domain.
*/

class Led {
private:
int red, green, blue, rPin, gPin, bPin;
bool isValidRGB(int);
public:
Led() {};
Led(int);
Led(int,int,int);
int Blue() {return blue;}
int Green() {return green;}
int Red() {return red;}
//RPin is also the power pin.
int RPin() {return rPin;}
int GPin() {return gPin;}
int BPin() {return bPin;}
int Power() {return rPin;}
// checks whether the bPin has been set to -1.  If it is set to -1 it is not an RGB LED.
bool IsRGB() {return bPin!=-1;}
void SetRed(int);
void SetGreen(int);
void SetBlue(int);
void SetRandomColor();
};


#endif


//


Led.cpp:

#include<LED.h>
#include <Arduino.h>
#include <WProgram.h>
Led::Led(int p) {
  //Initiates a standard LED.  the rPin is power.
  rPin=p;
  // Either g or b can bee tested against to see if this is an RGB.  They will be equal to -1 if a standard RGB.
  gPin=-1;
  bPin=-1;
  red=0;
  green=0;
  blue=0;
}

Led::Led(int r,int g,int b) {
// Initiates an RGB LED, sets color to white. 
rPin=r;
gPin=g;
bPin=b;
red=255;
green=255;
blue=255;
}



bool Led::isValidRGB(int val)
{
 if (val>-1&&val<256)
 {
 return true;
 }
 else {
 return false;
 }
}

void Led::SetRandomColor() {

  // Randomize, choose a color of 0-6. 0=red, 1=green, 2=blue, 3=purple, 4=yellow, 5=orange
  
  int color=random(0,5);

switch (color) {
   case 0: 
   red=255;
   green=0;
   blue=0;
   break;
   case 1:
   green=255;
   red=0;
   blue=0;
   break;
   case 2: 
   blue=255;
   red=0;
   blue=0;
   break;
   case 3:
   red=255;
   blue=255;
   green=0;
   break;
   case 4:
   red=255;
   green=255;
   blue=0;
   break;
   default:
   red=255;
   green=128;
   blue=0;
   break;
 }

}

void Led::SetRed(int val) {

if (isValidRGB(val))
{
  red=val;
}
}

void Led::SetBlue(int val) {

if (isValidRGB(val))
{
  blue=val;
}
}

void Led::SetGreen(int val) {

if (isValidRGB(val))
{
  green=val;
}
}





There we go!  Hope this saves you some hours of hair-pulling frustration.

Some Crude C++ Code

Despite my pidgin-level recollection of c++, have hammered out some base code to get things running in a crude approximation of the way I want.

The Basics:


Attached to my Arduino are:
  • A mini sound sensor (5v, outputs an analog signal from 0-1024 dependent on the amplitude of the sound sensed.)
    • Gnd and Power to their respective rails
    • Out to A0 on the Duino
  • A standard LED
    • GND to rail (via 110k ohm resistor)
    • Anode to pin 8 on the Duino
  • An RGB LED
    • GND to rail
    • Red to pin 3 on the Duino (via 110k ohm resistor)
    • Green to pin 5 on the Duino (via 110k ohm resistor)
    • Red to pin 7 on the Duino (via 110k ohm resistor)
Please see the somewhat dark and fuzzy picture below.


Did an initial test by doing a Serial.print(analogRead(0)) and firing it off.  Sure enough, I saw a stream of values from 1 to around 300 flying in on the serial reader.  Great!  Fired off another test, using the map function to convert the analog signal range of 0-1024 to the RGB's range of 0-255 and fired the mapped value off to the RGB LED's red pin.  Uploaded the code, fired up some Lindsey Stirling on iTunes and let there be light!

One minor problem, though... while the peak signal range of the sound sensor was supposedly 1024, I couldn't get it to read anything above 400, even by tapping on the mic.  As such, I would only get a dim red.  Decided to:
  • Track the average sound level in the room (this would help it only pick up beats, and compensate for low-level conversation etc.)
  • Only fire off IF the sound detected was above the average level AND multiply the value by how many times louder than the average detected sound value (trimming it if it went above the peak level (in this case 255))
Code for the calculation function follows:

int getRelativeValue(int val, int avg, int peak)
{
  // Map the sensitivity level (controlled by the pin
  int sensitivity=map(analogRead(sensePin),0,1023,0,128);     
  int output=val;
   if (val>avg)
   {
     double mult=2;

     if (avg>0)
     {
       // Set the multiplier by how many times greater the value is.
       mult=val/avg;
     }
       // multiply the value by the weighed multiplier
       output=val*mult+sensitivity;
       if (output>peak)
       {
       output=peak;
       }
   }
   return output;
    
}

Significant improvement.  After a few seconds of acclimating to the University of Texas' Danserye, the red pin showed a much wider spectrum, going bright on the drum beats and trumpet blasts and soft on the bells and flutes.

One thing I didn't like, though.  This was an RGB LED, and I was only firing on red.  There were two other lamps I wasn't even touching.  The idea of firing a random color appealed to me.  The idea of periodically changing that color (based on both a randomly set number of ms and the strength of the sound wave,) rocked my world.  Created a simple case switch routine to first randomly determine the dominant color, then the strengths of the other two (I created a 5-way switch that stepped, by .25, from 0 to 1.)   Code follows:

double calcRandomPerc(int i)
{
  double output=0;
  switch(i)
  {
  case 1:
  break;
  case 2:
  output=0.25;
  break;
  case 3:
  output=0.5;
  break;
  case 4:
  output=0.75;
  break;
  default:
  output=1;
  break;
  }
  return output;
}

This double would be multiplied against the value of the detected sound... so if the detected sound were 255,  Red was 1, Green was 0 and Blue was .5, Red would be 255, Green would be 0 and  Blue would be 128... a faint purple.  Threw in a counter, which I incremented by the delay value plus the strength of the beat; once this counter exceeded a randomized ceiling between 3,000 and 5,000 the color would be reset. This, again, worked pretty well.

Added the static (in this case yellow) LED to the mix next, but after many tries couldn't get it to light.  After about half an hour of troubleshooting, discovered that this was because of the way I was passing my LED's as an array of a custom LED class to my music pulsing function.  I'm so used to manipulating enumerations of objects using C#'s List, I forgot that if you want to pass arrays as arguments, you have to pass the number of items contained within the arrays as a constant, and ended up never changing this constant from what I had initially set it to: 1. As such, it was only looping over the first item in the array (the RGB LED.)  Once this rookie mistake was resolved, the yellow LED flashed with the RGB.

After watching it for a while, I discovered that this wasn't precisely what I wanted it to do.  The digital yellow  almost always blazed over the more undestated analog RGB.  Experimented around with values until I ended up only having the LED fire if the mapped sound range was over 128.  Voila, the yellow would only flash on the stronger beats.

The code:
          if (soundNum>128)
         {
           fireLed(ledList[thisPin], false);
         }

After a night's rest, I decided to add two potentiometers to the sketch, allowing my wife to fine-tune the sensitivity to the environment  (this could also potentially be employed to change sparkle/chase speed etc.)   One potentiometer would create a modifier that would be weight the sound value, the other could be adjusted to make the static LED's more sensitive.  Drew up (and compiled) the code for this, but haven't tested it yet.

The code as it is follows.  Please see this entry that covers my declaration of a custom class and why libraries are your friend when dealing with Duinos if curious about exactly what LED.h does.

#include <LED.h>
Led ledList[]={Led(3,5,6), Led(8)};
const int soundPin=0;
void setup()
{
  Serial.begin(9600);
}


void loop()
{
  musicPulse(ledList,2,1000);
}
int getRelativeValue(int val, int avg, int peak)
{
       
  int output=val;
   if (val>avg)
   {
     double mult=2;

     if (avg>0)
     {
       // Set the multiplier by how many times greater the value is.
       mult=val/avg;
     }
     do {
       // multiply the value by the weighed multiplier
       output=val*mult;
       //If too high, step multiplier down by 10%
       mult=mult*.9;
     } while (output>peak);
   }
   return output;
    
}
double calcRandomPerc(int i)
{
  double output=0;
  switch(i)
  {
  case 1:
  break;
  case 2:
  output=0.25;
  break;
  case 3:
  output=0.5;
  break;
  case 4:
  output=0.75;
  break;
  default:
  output=1;
  break;
  }
  return output;
}

void musicPulse(Led ledList[],int arraySize,long seconds)
{
  int valCount=0;
  long valTot=0;
  int newPeak=0;
  int countPeak=30000;
  // initialize divisors
  double redDiv=1;
  double blueDiv=1;
  double greenDiv=1;
  int count=0;
  // read from the sound sensor
  do {
  int val=analogRead(soundPin);
  // add this to the total (to calculate avg)
  valTot=valTot+val;
  valCount=valCount+1;
  // If the reading is above 100, proceed.  Else wait 
    if (val>0)
    {
      
      if (count==0)
      {
        // Determine the color ratio
      redDiv=calcRandomPerc(random(1,5));
      blueDiv=calcRandomPerc(random(1,5));
      greenDiv=calcRandomPerc(random(1,5));
      // set the focal color (the one set to 1.)
          switch (random(1,3))
          {
            case 1:
            redDiv=1;
            break;
            case 2:
            blueDiv=1;
            break;
            case 3:
            greenDiv=1;
            break;
          }
         
          // randomly set the next count time
          countPeak=random(5000,60000);
      }
      if (count%100==0)
      {
        newPeak=newPeak/2;
      }
      
      int soundNum=map(val,0,1023,0,255);
      if (val>newPeak)
      {
        newPeak=val;
      }
      // Calculate the average.
      double valAvg=(valTot/valCount);
      
      soundNum=getRelativeValue(soundNum,valAvg,254);
      for (int thisPin = 0; thisPin < arraySize; thisPin++)  {
         int i=ledList[thisPin].RPin();
         
        if(ledList[thisPin].IsRGB())
         {
          
        // Set the color divisors to create a random color.
      // The color will last 30 seconds.  If the count is zero, set the colors.
      
           // Pass the value to the effected pins
           ledList[thisPin].SetRed(soundNum*redDiv);
           ledList[thisPin].SetBlue(soundNum*blueDiv);
           ledList[thisPin].SetGreen(soundNum*greenDiv);
           // Light the bulb
           fireLed(ledList[thisPin], false);
         }
         // If it's a standard LED...
         else {
           
         // if the latest reading is higher than average, light the LED.
          if (val>valAvg+(newPeak/2)&&(soundNum>128))
         {
           Serial.println("should've fired.");

           fireLed(ledList[thisPin], false);
         }
         else {
         Serial.println("didnt fired.");
         }
      }
      // Step up the count by the delay value (30 ms)
      count=count+30+soundNum*2;
      // if the count has exceeded the peak count time, set the count to 0 (the color will randomize on the next loop.)
      if (count>countPeak)
      {
        count=0;
      }  
      }
      delay(30);
    // Turn off the pin
      for (int thisPin = 0; thisPin < arraySize; thisPin++)  {  
          turnOffLed(ledList[thisPin]);
        }

        // if the potentiometer is set below 800, switch.f
    } 
    
    
  } while(true); // Puts us in a loop until the potentiometer is changed.
}

void fireLed(Led led, bool rand)
{

  // Randomize the color
  if (rand&&(led.IsRGB()))
  {
    led.SetRandomColor();
    led.SetRed(255);


  }

  if (led.IsRGB())
      {

      // Fire red, green, then blue
      analogWrite(led.RPin(),led.Red());
      analogWrite(led.GPin(),led.Green());
      analogWrite(led.BPin(),led.Blue());
      }
      else
      {
      digitalWrite(led.RPin(),HIGH);
      }
}

void turnOffLed(Led led)
{
      if (led.IsRGB())
      {
      // If it's an RGB, turn off red, green, then blue
      analogWrite(led.RPin(),0);
      analogWrite(led.GPin(),0);
      analogWrite(led.BPin(),0);
      }
      else
      {
        digitalWrite(led.RPin(),LOW);
      }

}