It's always funny to examine techniques at the edge of one’s knowledge. And for all MCU nerds, it’s a challenge to see how much you can make the MCU do. This project involves a lot of elements.
1. Streaming audio from SD-Card via PWM (no wave-shield)
2. MSGEQ7 Equalizer for making a mood-lamp. Colors based on music genre.
3. Selection of music genre by selection knop.
4. A mechanical counter that show the track number of the music.
5. A lever for advancing and rewinding the mechanical counter
6. Potentiometers for sound level and maybe for speed.
I have accomplished the first two goals. The third is not a technical challenge. What I could use was some suggestions for genres to select among. Remember that the mood-lamp have to show colors according to the genre. Romantic could be red. Melancholic music should show blue lights. Etc.
The mechanical counter could be done by steppers or servos. The lever could be mechanical attached to the counter, but I think I’m just going to use a potentiometer, and then advance the number wheels by a motor. Any other suggestions will be welcome.
I’m using an Arduino Uno. So I have to be careful counting the pins. I’m going to use shift registers for digital outputs and analog input for reading buttons.
The picture above is only a draft. I want to get everything working before making any carpentry or decision about actual looks.
This is current state:
It's playing music and the lamp at the lower right corner is blinking according to the music.
I use SimpleSDAudio for playing audio. Good information about this can be found on http://hackerspace-ffm.de/wiki/index.php?title=SimpleSDAudio. Could be handy for those who want their robot to speak or make sounds. SimpleSDAudio includes an Arduino library, and as a part of this, a tool for preparing sound files called SOX. So you don’t have to find and install the tool as it’s in a sub library to the Arduino library. When preparing sound files, use the tool as shown here:
sox inputfile.wav --norm=-1 -e unsigned-integer -b 8 -r 31250 -c 1 -t raw outputfile.raw
Ordinary wav-files, that is not prepared this way, has a very poor sound quality.
For the light effects, a page about Arduino/MSGEQ7 Audio Spectrum LED display can be found at: http://www.n00bsys0p.co.uk/blog/2012/07/09/arduinomsgeq7-audio-spectrum-led-display. This is a very simple IC to use. It's a 7-band Graphic Equaliser Display Filter. Two digital pins, one analog pin and six line of code are enough to make this work.
There will of course be updates.
-- Update 10. January
When Dickel posted about TLC5940 I was thinking: So ein Ding muss ich auch haben. (https://www.robotshop.com/letsmakerobots/node/35677).
My challenge was that timer interrupts and SPI were already in use by SimpleSDAudio. TLC5940 libraries often use two timer interrupts. One for GSCLK and another for initiating the update sequence. First I made a 555 circuit to make an external pwm signal for GSCLK. It worked when running TCL5940 alone, but not in combination with SimpelSDAudio. To stop using the other timer, I use a 12 bit counter in combination with my 555 circuit, to trigger interrupt on a pin instead of a timer interrupt. This also worked. But again. not together with SimpleSDAudio. Using SPI also stopped SimpleAudio. Finaly I bit banged at TCL5940 at every time I had read MSGEQ7 and calculated values for the LED’s. Apparently this was frequent enough to make the LED’s work. No need for the external counter. VPRG is sourced at 5v. With the external 555 circuit for GSCLK I only use 4 pins for running TLC5940.
My coding was based on Kevin Darrah’s code. http://www.kevindarrah.com/download/arduino_code/TimersCountersV6.ino
But this code is without interrupts and without using SPI:
byte ch=0, chbit=0, spibit=0, spibyte=0;// variables used by tlc sub routine
int SINData;//variable used to shift data to the TLC
byte transferbyte[24];// bytes (1612=192 bits) that are sent out to the tlc5940 via shiftOut
int i, j, k, l,m,n; //misc variables
int pBlank = 6;
int pXLAT = 2;
int pSin = 4;
int pSCLK = 8;
void setup(){
pinMode(pXLAT, OUTPUT);
pinMode(pSin, OUTPUT);
pinMode(pBlank, OUTPUT);
pinMode(pSCLK, OUTPUT);
init5940();
}
void init5940() {
for(i=0; i<24; i++) transferbyte[i]=0; //clear out Gray Scale Data
noInterrupts();// set up the counters, so don’t go into interrupts
//attachInterrupt(1, stateChange, RISING );
interrupts();// kick off the timers!
for(i=0; i<16; i++) tlc(i, 0);//wipe out the data in tlc
pinMode(pBlank, OUTPUT);//BLANK We set this pin up here, so it remains in a high impedance
}
void loop(){
lamp_test();
}
void stateChange(){
PORTD |= 1<<pBlank;// write blank HIGH to reset the 4096 counter in the TLC
PORTD |= 1<<pXLAT;// write XLAT HIGH to latch in data from the last data stream
PORTD &= 0<<pXLAT; //XLAT can go low now
PORTD &= 0<<pBlank;//Blank goes LOW to start the next cycle
digitalWrite(pSCLK,HIGH);
digitalWrite(pSCLK,LOW);
for(SINData=24; SINData>=0; SINData–) shiftOut(pSin, pSCLK, MSBFIRST,(transferbyte[SINData]));
}
void lamp_test(){
for(l=0; l<16; l++){
for(k=0; k<150; k++){
tlc(l, k);
stateChange();
delay(5);
}
for(k=25; k<698; k++) {
tlc(l, k6);
stateChange();
}
tlc(l,0);
stateChange();
}
}
void tlc(int channel, int value){
if(value>4095) value=4095;
if(value<0) value=0;
spibit=0;
if(bitRead(channel, 0)) spibit=4;
spibyte = int(channel*3/2);//this assignes which byte the 12 bit value starts in
for(chbit=0; chbit<12; chbit++, spibit++){// start right at where the update will go
if(spibit==8){//during the 12 bit cycle, the limit of byte will be reached
spibyte++;//roll into the next byte
spibit=0;//reset the bit count in the byte
}
if(bitRead(value, chbit)) bitSet(transferbyte[spibyte], spibit);
else bitClear(transferbyte[spibyte], spibit);
}
}