091117_2114_TinkeringTu6.png

Tinkering Tuesday – Playmobil Stage – Audio Spectrum Analysis from WAV input with simple logic, FHT and MSGEQ7

Past posts of this series:

Tinkering Tuesday – Playmobil Stage – Einführung

Tinkering Tuesday – Playmobil Stage – Introduction

Tinkering Tuesday – Playmobil Stage – Playmobil parts

Tinkering Tuesday – Playmobil Stage – LED Stage Lights

Tinkering Tuesday – Playmobil Stage – Disco Ball with stepper motor

Tinkering Tuesday – Playmobil Stage – LED Rings
Tinkering Tuesday – Playmobil Stage – Audio Output from microSD with Arduino


This post will be about different possibilities to do sound input analysis (or spectrum analysis) with the Arduino. The source of sound will be my Arduino audio output from the last post for now. But I plan on using a sound sensor board additionally for the final build. The audio output from my last post is a little bit special because it uses PWM. „Normal“ audio is more of a sine wave. To not have any side effects I will do my testing with a second Arduino (nano in my case)

But lets start using the PWM output directly:

Sound input analysis on PWM signals

I made a direct connection from the PWM output of my audio output Arduino to A0 of the input Arduino:

New is only the Arduino nano, connecting A0 and GND.

With this we can use a simple input analysis, based on the code at http://blog.yavilevich.com/2016/08/arduino-sound-level-meter-and-spectrum-analyzer/ (which is by the way a great post for the theory also).

//based on: http://blog.yavilevich.com/2016/08/arduino-sound-level-meter-and-spectrum-analyzer/
#define MicSamples (1024*2)
#define MicPin A0

// measure basic properties of the input signal
// determine if analog or digital, determine range and average.
void MeasureAnalog()
{
long signalAvg = 0, signalMax = 0, signalMin = 1024, t0 = millis();
for (int i = 0; i < MicSamples; i++)
{
int k = analogRead(MicPin);
signalMin = min(signalMin, k);
signalMax = max(signalMax, k);
signalAvg += k;
}
signalAvg /= MicSamples;

// print
Serial.print("Time: " + String(millis() - t0));
Serial.print(" Min: " + String(signalMin));
Serial.print(" Max: " + String(signalMax));
Serial.print(" Avg: " + String(signalAvg));
Serial.print(" Span: " + String(signalMax - signalMin));
Serial.print(", " + String(signalMax - signalAvg));
Serial.print(", " + String(signalAvg - signalMin));
Serial.println("");

}
void setup() {
Serial.begin(115200);
}

void loop() {
MeasureAnalog();
}

As a result I got some readings. First you see the basic noise (without the output Arduino connected), then the plugin noise of the output Arduino and finally the sound playing.

--Basic noise
Time: 229 Min: 0 Max: 8 Avg: 2 Span: 8, 6, 2
Time: 229 Min: 0 Max: 7 Avg: 2 Span: 7, 5, 2
Time: 230 Min: 0 Max: 8 Avg: 2 Span: 8, 6, 2
Time: 229 Min: 0 Max: 8 Avg: 2 Span: 8, 6, 2
--PlugIn
Time: 229 Min: 0 Max: 221 Avg: 89 Span: 221, 132, 89
Time: 229 Min: 0 Max: 180 Avg: 108 Span: 180, 72, 108
Time: 229 Min: 28 Max: 88 Avg: 52 Span: 60, 36, 24
--Playing Sound
Time: 230 Min: 0 Max: 1023 Avg: 24 Span: 1023, 999, 24
Time: 229 Min: 0 Max: 1023 Avg: 327 Span: 1023, 696, 327
Time: 230 Min: 0 Max: 1023 Avg: 332 Span: 1023, 691, 332
Time: 229 Min: 0 Max: 1023 Avg: 327 Span: 1023, 696, 327
Time: 229 Min: 0 Max: 1023 Avg: 331 Span: 1023, 692, 331
Time: 230 Min: 0 Max: 1023 Avg: 323 Span: 1023, 700, 323
Time: 229 Min: 0 Max: 1023 Avg: 327 Span: 1023, 696, 327
Time: 230 Min: 0 Max: 1023 Avg: 326 Span: 1023, 697, 326
Time: 229 Min: 0 Max: 1023 Avg: 325 Span: 1023, 698, 325
Time: 229 Min: 0 Max: 1023 Avg: 325 Span: 1023, 698, 325

Having a closer look at the readings shows what could already be expected. Values ranging from 0 to 1023, the averages varying a bit. It is PWM, so fully on (1023) or fully off (0). That can be verified by a pure analog read:

#define MicPin A0

void setup() {
Serial.begin(115200);

}

void loop() {
Serial.println(analogRead(MicPin));
}
1023
0
0
1023
0
0
195
1
0
524
0
7
1023
0
988
0
7
1023
0
62
0
574
0
1023
0
0
0
902
0
1023

I don’t know quite the theory why the „in-between-values“ are there but they are. But mostly you can see 0 and 1023.

There is an advanced function to analyse audio on the arduino and it is called the Arduino FHT library. For more information about that please have a look at http://wiki.openmusiclabs.com/wiki/ArduinoFHT. There is also the full theory.

The following sketch is a modified example code and is producing serial output:

/*
fht_adc.pde
guest openmusiclabs.com 9.5.12
example sketch for testing the fht library.
it takes in data on ADC0 (Analog0) and processes them
with the fht. the data is sent out over the serial
port at 115.2kb.  there is a pure data patch for
visualizing the data.
*/

#define LOG_OUT 1 // use the log output function
#define FHT_N 256 // set to 256 point fht

#include <FHT.h> // include the library

void setup() {
Serial.begin(115200); // use the serial port
TIMSK0 = 0; // turn off timer0 for lower jitter
ADCSRA = 0xe5; // set the adc to free running mode
ADMUX = 0x40; // use adc0
DIDR0 = 0x01; // turn off the digital input for adc0
}

void loop() {
while(1) { // reduces jitter
cli();  // UDRE interrupt slows this way down on arduino1.0
long t0 = micros();
for (int i = 0 ; i < FHT_N ; i++) { // save 256 samples
while(!(ADCSRA & 0x10)); // wait for adc to be ready
ADCSRA = 0xf5; // restart adc
byte m = ADCL; // fetch adc data
byte j = ADCH;
int k = (j << 8) | m; // form into an int
k -= 0x0200; // form into a signed int
k <<= 6; // form into a 16b signed int
fht_input[i] = k; // put real data into bins
}
long dt = micros() - t0;
fht_window(); // window the data for better frequency response
fht_reorder(); // reorder the data before doing the fht
fht_run(); // process the data in the fht
fht_mag_log(); // take the output of the fht
//    sei();
//    Serial.write(255); // send a start byte
//    Serial.write(fht_log_out, FHT_N/2); // send out the data
// print as text
for (int i = 0; i < FHT_N / 2; i++)
{
Serial.print(fht_input[i]);
Serial.print(',');
}
long sample_rate = FHT_N * 1000000l / dt;
Serial.print(dt);
Serial.print(',');
Serial.println(sample_rate);
}
}

The result can be seen in the following screenshot:

Puh, that’s a lot of numbers. As a plot in excel this is the result:

Using digital to analog conversion (DAC) to convert PWM back to analog signal

Searching for some solutions I stumbled upon the DAC from http://www.instructables.com/id/Analog-Output-Convert-PWM-to-Voltage/

This is using a capacitor and a resistor to smoothen the PWM signal back into a voltage level. It is also known as a low pass filter (if you exchange the resistor and the capacitor you get a high pass filter as I read in the comments…).

The following schematic shows the enhanced connection:

R1=3,9k, C=100nF (0,1uF)

The output from the basic input readings look much better now:

Time: 230 Min: 155 Max: 547 Avg: 347 Span: 392, 200, 192
Time: 229 Min: 174 Max: 562 Avg: 347 Span: 388, 215, 173
Time: 230 Min: 150 Max: 545 Avg: 347 Span: 395, 198, 197
Time: 229 Min: 175 Max: 528 Avg: 347 Span: 353, 181, 172
Time: 229 Min: 163 Max: 513 Avg: 347 Span: 350, 166, 184
Time: 229 Min: 149 Max: 511 Avg: 347 Span: 362, 164, 198
Time: 230 Min: 180 Max: 539 Avg: 347 Span: 359, 192, 167
Time: 230 Min: 202 Max: 504 Avg: 347 Span: 302, 157, 145
Time: 229 Min: 161 Max: 540 Avg: 347 Span: 379, 193, 186
Time: 230 Min: 147 Max: 582 Avg: 347 Span: 435, 235, 200
Time: 229 Min: 118 Max: 546 Avg: 349 Span: 428, 197, 231
Time: 229 Min: 59 Max: 610 Avg: 347 Span: 551, 263, 288
Time: 230 Min: 187 Max: 553 Avg: 347 Span: 366, 206, 160
Time: 229 Min: 179 Max: 603 Avg: 348 Span: 424, 255, 169
Time: 230 Min: 186 Max: 569 Avg: 347 Span: 383, 222, 161
Time: 229 Min: 175 Max: 502 Avg: 347 Span: 327, 155, 172
Time: 229 Min: 191 Max: 497 Avg: 347 Span: 306, 150, 156
Time: 231 Min: 170 Max: 494 Avg: 347 Span: 324, 147, 177
Time: 229 Min: 203 Max: 517 Avg: 347 Span: 314, 170, 144
Time: 230 Min: 218 Max: 532 Avg: 347 Span: 314, 185, 129
Time: 229 Min: 189 Max: 514 Avg: 347 Span: 325, 167, 158
Time: 230 Min: 201 Max: 484 Avg: 347 Span: 283, 137, 146
Time: 229 Min: 117 Max: 532 Avg: 347 Span: 415, 185, 230

This can also be observed in the pure readings:

245
253
310
368
424
398
382
370
390
405
402
386
400
399
387
345
308
292
320
338
337
313
317
335
353
371
348
290
280
279
263
232
221
247
319

I have to admit in the FHT output I don’t see much difference:

Connecting some LEDs I modified the sketch to light the LEDs up based on some thresholds.

//based on: http://blog.yavilevich.com/2016/08/arduino-sound-level-meter-and-spectrum-analyzer/
#define MicSamples (1024*2)
#define MicPin A0
#define LEDRedPin 11
#define LEDGreenPin 9
#define LEDBluePin 10

// measure basic properties of the input signal
// determine if analog or digital, determine range and average.
void MeasureAnalog()
{
long signalAvg = 0, signalMax = 0, signalMin = 1024, t0 = millis();
for (int i = 0; i < MicSamples; i++) { int k = analogRead(MicPin); signalMin = min(signalMin, k); signalMax = max(signalMax, k); signalAvg += k; } signalAvg /= MicSamples; // print Serial.print("Time: " + String(millis() - t0)); Serial.print(" Min: " + String(signalMin)); Serial.print(" Max: " + String(signalMax)); Serial.print(" Avg: " + String(signalAvg)); Serial.print(" Span: " + String(signalMax - signalMin)); Serial.print(", " + String(signalMax - signalAvg)); Serial.print(", " + String(signalAvg - signalMin)); Serial.println(""); if(signalMax - signalMin > 450){
analogWrite(LEDRedPin, 255);
} else {
analogWrite(LEDRedPin, 0);
}
if(signalMax - signalMin > 400){
analogWrite(LEDGreenPin, 255);
} else {
analogWrite(LEDGreenPin, 0);
}
if(signalMax - signalMin > 500){
analogWrite(LEDBluePin, 255);
} else {
analogWrite(LEDBluePin, 0);
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(LEDRedPin, OUTPUT);
pinMode(LEDBluePin, OUTPUT);
pinMode(LEDGreenPin, OUTPUT);
}

void loop() {
MeasureAnalog();
}

It is currently based on the intro, so it is not really an optimal solution for the whole song and for other songs. I will have to see how to configure the threshholds best. Maybe they have to adopt to the levels of the prior readings.

MSGEQ7

Then I connected the MSGEQ7 according to the schematic shown at http://nuewire.com/info-archive/msgeq7-by-j-skoba/. Using the very same Arduino sketch from there I got a lot of zeros for my input with some readings in different frequencies in between. What I also recognized was that the direct PWM output from the other Arduino was giving at least some results (not for the intro of the test song but from the chorus onwards. I have no clue why this is…). When I used the DAC / low pass filter shown above, I got no readings at all. I had no 200ohm resistor and used a 220ohm but should this result in such a difference? I don’t think so. I also tried connecting an MP3 player directly to the MSGEQ7 but also got no results.

Abb: Wiring with DAC – R2=200k, C2=33pF, C3=100nF, C4=10nF, C5=100nF

Abb: Wiring without DAC – R2=200k, C2=33pF, C3=100nF, C4=10nF, C5=100nF

I found a MSGEQ7 Demo app at https://forum.arduino.cc/index.php?topic=425799.0. Adopted to my setup:

/*
MSGEQ7 Demo app
Look for the breakout board on www.whizoo.com

This code runs on an Arduino Duemilanove, but will run on other Arduino models.

Connections:
- GND to GND on MSGEQ7 breakout board, and LED's
- 5V to VDD on MSGEQ7 breakout board
- A0 to OUT on MSGEQ7 breakout board
- D2 to STROBE on MSGEQ7 breakout board
- D3 to RESET on MSGEQ7 breakout board
- D9 to LED 0 (indicator for frequency band 0)
- D10 to LED 1 (indicator for frequency band 1)
- D11 to LED 2 (indicator for frequency band 2)
- no pin to LED 3 (indicator for frequency band 3)
- no pin to LED 4 (indicator for frequency band 4)
- no pin to LED 5 (indicator for frequency band 5)

*/

// Hardware-specific defines
#define MSGEQ7_STROBE_PIN      2
#define MSGEQ7_RESET_PIN       3
#define MSGEQ7_ANALOG_PIN      A0

#define NUM_FREQUENCY_BANDS    3

// Duemilanove only has 6 PWM outputs, so the last LED won't respond properly.  Your
// board may have more PWM outputs.  Typically you only want to monitor the lowest
// frequency bands because that is where the beat is.
int led[NUM_FREQUENCY_BANDS] = {9, 10, 11};

// There is a concept of "persistence of vision" with LED's.  The LED has to be on long enough
// for the eye to recognise that it is on.  When a high volume is received on a frequency band,
// The LED is turned on (at a high PWM value) and then gradually faded until the next beat in
// that frequency.
int ledPWMValue[NUM_FREQUENCY_BANDS] = {0, 0, 0};

void setup() {
// Set the LED pins as outputs
for (int i=0; i<NUM_FREQUENCY_BANDS; i++)
pinMode(led[i], OUTPUT);

// Set up the MSGEQ7 IC
pinMode(MSGEQ7_ANALOG_PIN, INPUT);
pinMode(MSGEQ7_STROBE_PIN, OUTPUT);
pinMode(MSGEQ7_RESET_PIN, OUTPUT);
digitalWrite(MSGEQ7_RESET_PIN, LOW);
digitalWrite(MSGEQ7_STROBE_PIN, HIGH);
Serial.begin(9600);
}

// This loop executes around 100 times per second
void loop() {
int frequencyBandVolume;

// Toggle the RESET pin of the MSGEQ7 to start reading from the lowest frequency band
digitalWrite(MSGEQ7_RESET_PIN, HIGH);
digitalWrite(MSGEQ7_RESET_PIN, LOW);

// Read the volume in every frequency band from the MSGEQ7
for (int i=0; i<NUM_FREQUENCY_BANDS; i++) { digitalWrite(MSGEQ7_STROBE_PIN, LOW); // delayMicroseconds(20); // Allow the output to settle frequencyBandVolume = analogRead(MSGEQ7_ANALOG_PIN); digitalWrite(MSGEQ7_STROBE_PIN, HIGH); Serial.print(frequencyBandVolume); Serial.print(" "); if (i == NUM_FREQUENCY_BANDS-1) { Serial.println(""); } // The read value is 10-bit (0 to 1024). PWM needs a value from 0 to 255, so divide by 4 frequencyBandVolume = frequencyBandVolume >> 2;

// Fade the current LED value for this band
ledPWMValue[i] = ledPWMValue[i] > 7? ledPWMValue[i] - 7 : 0;

// Don't show the lower values
if ((frequencyBandVolume > 70) && (frequencyBandVolume < 100)){ // If the new volume is greater than that currently being showed then show the higher volume if (frequencyBandVolume > ledPWMValue[i])
ledPWMValue[i] = frequencyBandVolume;
}

if (frequencyBandVolume > 220) {
// If the new volume is greater than that currently being showed then show the higher volume
if (frequencyBandVolume > ledPWMValue[i])
ledPWMValue[i] = frequencyBandVolume;
}

// Set the LED PWM value to the frequency band's volume
analogWrite(led[i],  ledPWMValue[i]);
}

// Wait before executing this loop again
//delay(2);
}

With some LEDs connected (there will be another post regarding the LEDs) you can see the result for the chorus in the video below:

I don’t have so much time to tinker more with the MSGEQ7 right now, so I will just go with a simple input solution I guess. At least I will take the fading of the LEDs from the MSGEQ7 Demo app. But one more note: http://www.eetimes.com/author.asp?doc_id=1323030 recommends to also use 100nF instead of the 10nF capacitor. Perhaps that might be something worth to try. The site has also the wiring for stereo input.

Doing it all on one Arduino

For my tests I used two separate Arduinos. But of course I would really like to reduce this down to one Arduino. So I connected the output of the DAC directly to the A0 pin of the Arduino UNO and combined the two sketches:

// ---------------------------------------------------------------------------------
// DO NOT USE CLASS-10 CARDS on this project - they're too fast to operate using SPI
// ---------------------------------------------------------------------------------

#include <SD.h>
#include <TMRpcm.h>
TMRpcm tmrpcm;

File root;
File entry;
#define MicSamples (1024^2)
#define MicPin A0
#define LEDRedPin 11
#define LEDGreenPin 9
#define LEDBluePin 10

// ---------------------------------------------------------------------------------
// set chipSelect to '10' if using the $2 SD card module or '4' if using the
// Ethernet shield's microSD card instead.
const int chipSelect = 10;
// ---------------------------------------------------------------------------------

const int oldCard = SPI_HALF_SPEED;
const int newCard = SPI_QUARTER_SPEED;

// ---------------------------------------------------------------------------------
// set cardType to 'oldCard' if using an old SD card (more than a few years old) or
// to 'newCard' if using a newly-purchase Class-4 card.
int cardType = newCard;
// ---------------------------------------------------------------------------------

int wasPlaying = 0;
int inSwitch = 7;
int finished = 0;
int start = 0;
int pauseOn = 0;
unsigned long timeDiff = 0;
unsigned long timePress = 0;
int analogMeasureCounter = 0;
int analogMeasureValue = 0;
long signalAvg = 0, signalMax = 0, signalMin = 1024, t0 = millis();

void setup() {
Serial.begin(9600);
Serial.print("\nInitializing SD card...");
pinMode(chipSelect, OUTPUT);
if (!SD.begin(chipSelect,cardType)) {
Serial.println("failed!");
return;
}
Serial.println("done.");

tmrpcm.speakerPin = 9;

pinMode(inSwitch,INPUT_PULLUP);
digitalWrite(inSwitch,HIGH);

root = SD.open("/");
}

void loop(void) {
if(!tmrpcm.isPlaying() && wasPlaying == 1) {
tmrpcm.stopPlayback();
playNext();
}
MeasureAnalog();
if (millis() - timeDiff > 50) { // check switch every 100ms
timeDiff = millis(); // get current millisecond count

if(digitalRead(inSwitch) == LOW) {

if(start==0) {
start=1;
playNext();
delay(200);

} else {

timePress = millis();
while(digitalRead(inSwitch)==LOW) {
delay(50);
}
if (millis() - timePress < 1000 && start == 1) { tmrpcm.pause(); if (pauseOn == 0) { pauseOn = 1; } else { pauseOn = 0; } } if (millis() - timePress > 1000 && start == 1) {
if (pauseOn == 1) {pauseOn = 0; tmrpcm.pause();}
tmrpcm.stopPlayback();
timePress = millis();
if (finished == 0) {
playNext();
} else {
finished = 0;
Serial.println("Restarting.");
root.rewindDirectory();
playNext();
}
}
}
}
}
}

void playNext() {
entry = root.openNextFile();
if (entry) {
entry.close();
tmrpcm.play(entry.name());
Serial.print("Playing ");
Serial.print(entry.name());
wasPlaying = 1;
} else {
if (wasPlaying == 1) {
Serial.println("Completed playback.");
wasPlaying = 0;
finished = 1;
start = 0;
root.rewindDirectory();
}
}
}
void MeasureAnalog()
{
if (analogMeasureCounter == MicSamples) {
signalAvg /= MicSamples;

// print
Serial.print("Time: " + String(millis() - t0));
Serial.print(" Min: " + String(signalMin));
Serial.print(" Max: " + String(signalMax));
Serial.print(" Avg: " + String(signalAvg));
Serial.print(" Span: " + String(signalMax - signalMin));
Serial.print(", " + String(signalMax - signalAvg));
Serial.print(", " + String(signalAvg - signalMin));
Serial.println("");

//    if(signalMax - signalMin > 450){
//      analogWrite(LEDRedPin, 255);
//    } else {
//      analogWrite(LEDRedPin, 0);
//    }
//    if(signalMax - signalMin > 400){
//      analogWrite(LEDGreenPin, 255);
//    } else {
//      analogWrite(LEDGreenPin, 0);
//    }
//    if(signalMax - signalMin > 500){
//      analogWrite(LEDBluePin, 255);
//    } else {
//      analogWrite(LEDBluePin, 0);
//    }
t0 = millis();
analogMeasureCounter = 0;
} else {
//Serial.println( analogRead(MicPin));
analogMeasureValue = analogRead(MicPin);
//signalMin = min(signalMin, analogMeasureValue);
//signalMax = max(signalMax, analogMeasureValue);
signalAvg += analogMeasureValue;
analogMeasureCounter++;
}

}

That resulted in very slow readings. Instead of every 230 milliseconds I got a reading every 2-4 seconds. Also reducing the number of MicSamples down to 512 resulted in the following output:

Time: 1197 Min: 1024 Max: 0 Avg: 234 Span: -1024, -234, -790
Time: 1274 Min: 1024 Max: 0 Avg: 236 Span: -1024, -236, -788
Time: 1235 Min: 1024 Max: 0 Avg: 233 Span: -1024, -233, -791
Time: 1275 Min: 1024 Max: 0 Avg: 236 Span: -1024, -236, -788
Time: 1196 Min: 1024 Max: 0 Avg: 231 Span: -1024, -231, -793
Time: 1258 Min: 1024 Max: 0 Avg: 231 Span: -1024, -231, -793
Time: 1274 Min: 1024 Max: 0 Avg: 238 Span: -1024, -238, -786

So still over a second. That is not usable for sound reactive lights. It would lag very much behind.

I also got to remove the calculation of min and max values because the dynamic memory was giving a warning and the playback did not start anymore.

TMRpcm versions

I used the version of TMRpcm that came with the Auduino. It doesn’t include volume adjustment yet and so I wanted to upgrade to the most recent version of the library from https://github.com/TMRh20/TMRpcm/wiki. Unfortunately I was not able to get that one running. I tried the basic examples and also my above used codes but it was refusing to playback anything. I have no clue why.

In the end I will just continue with the current setup of two Arduinos (perhaps I might exchange the one that is doing the audio playback with an Arduino nano and try the recent version again. But for now I don’t want to reconnect all the cables. I will go for some PCB design soon and solder everything together. That will make rewiring everything easier.

More to read

Fading RGB FHT: http://shin-ajaran.blogspot.de/2014/11/arduino-spectrum-analyzer-for-music-to.html

PWM to Voltage: http://www.instructables.com/id/Arduino-RC-Circuit-PWM-to-analog-DC/

Wiring of the MSGEQ7 http://nuewire.com/info-archive/msgeq7-by-j-skoba/

Matching Instructable: http://www.instructables.com/id/Extreme-Nightlight-Color-Organ/

MSGEQ7 forum post with TestCode https://forum.arduino.cc/index.php?topic=425799.0

MSGEQ7 Datasheet: https://www.sparkfun.com/datasheets/Components/General/MSGEQ7.pdf

MSGEQ7 Tutorial http://tronixstuff.com/2013/01/31/tutorial-arduino-and-the-msgeq7-spectrum-analyzer/

MSGEQ7 Tutorial https://www.baldengineer.com/msgeq7-simple-spectrum-analyzer.html

Mic Amp: http://www.redcircuits.com/Page38.htm

AnalogIn Basic analysis, theory: http://blog.yavilevich.com/2016/08/arduino-sound-level-meter-and-spectrum-analyzer/

FFT for Arduino: http://wiki.openmusiclabs.com/wiki/ArduinoFFT

FHT for Arduino: http://wiki.openmusiclabs.com/wiki/ArduinoFHT

Arduino, FHT, RGB Led, sound to lighting: https://gist.github.com/teos0009/6e54aff7e3d236d17ac4

091117_2112_TinkeringTu6.png

Tinkering Tuesday – Playmobil Stage – Audio Output from microSD with Arduino

Past Posts of this series:

Tinkering Tuesday – Playmobil Stage – Einführung
Tinkering Tuesday – Playmobil Stage – Introduction
Tinkering Tuesday – Playmobil Stage – Playmobil parts
Tinkering Tuesday – Playmobil Stage – LED Stage Lights
Tinkering Tuesday – Playmobil Stage – Disco Ball with stepper motor
Tinkering Tuesday – Playmobil Stage – LED Rings


 

The most important thing a stage needs is sound! As I want to keep costs down I want to use one of the several cheap Arduino copies I have lying around. That will be not the best quality sound but it will work. In the following post I will describe my way to Arduino sound.

microSD card to Arduino

I bought some cheap microSD card modules from eBay (1 EUR for 2 pcs) to save the audio files on.

MicroSD cards work with 3.3V serial communication, the Arduino with 5V. As I had a spare LLC (Logic Level Converter) from my HUEify project lying around I decided to use that one. I couldn’t find much about using one with a microSD adapter on the web (here is a german site: http://physudo.blogspot.de/2013/09/sd-karten-mit-dem-arduino-beschreiben.html) but in the end it is straight forward. The only I issue I had (which made me nearly giving up) was that connecting everything through the LLC always resulted in the Arduino not being able to initialize the card. Directly connecting the MISO (Master In Slave Out) pin from the SD to the Arduino finally solved it. There is no issue in voltage level as this pin only sends data from the SD card to the Arduino (Master=Arduino in, Slave=SD card Out) so it communicates with 3.3V which is enough for the Arduino.

This is the schematic I came up with (the fancy colors are a result of my jumper wires on the breadboard, see picture below):

If you even want to save the microSD adapter, you can build your own adapter for normal SD cards. In this picture shown by Nathan Chantrell on flickr, you can also see the connections that have to be made.

Using the sample code from https://www.arduino.cc/en/Tutorial/listfiles you can test whether your connections are working. My first attempts always showed „initialization failed“ until I connected MISO directly to the Arduino and not via the LLC. You also need to format the card in FAT16 or FAT32. I had a 2GB card lying around and my bigger cards did not work. So just try different ones if it is not working.

Making the audio connection

I had no Amplifier or something (but I ordered it) at first, so I searched for a way to hook up the Arduino directly to a speaker. I had a small battery powered Bluetooth speaker which also had a 3.5 input jack. So I used that one. It is pretty much crap but for testing it is sufficient. Mainly I followed the instructions at apcmag, building the Auduino. Only with some adjustments to match what I have on hand.

The connection to audio is also shown very simply in the following screenshot:

Source: Arduino Music and Audio Projects – Mike Cook (on Google books)

The setup can be seen in the following image:

For coding I used the Auduino code from apcmag. Try setting the cardType variable to newCard if working with newer cards (they provide a modified SD card library). You also need to convert your audio to compatible WAV files. Just follow the instructions on apcmag.

What should I say: There is sound!

But as you can hear in the video the output has much noise. The speaker has no volume control so this might be the problem.

Using LM386 amplifier module with passive speaker

To attach a normal speaker, I ordered some cheap LM386 modules from ebay (5pcs 3.79 EUR). Specifications can be found on www.petervis.com. It is rated for 5V-12V power so can be powered directly from the Arduino.

I had an old Logitech speaker set lying around which I don’t need anymore so I cut the passive speaker off the other part to serve as a speaker.

The connection is also very easy:

LM386 Arduino
VCC/VDD 5V
IN Pin 9
GND GND

The second ground pin is for use with external power. You need then to connect the IN and GND from the sound source and the VCC and second GND pin from your power source.

The result can be seen in the following picture and the sound quality is already much better than the crappy bluetooth speaker:

The only thing that is not working is the volume control. On the left of the small potentiometer there is a small space where it gets a little more quiet and finally silent but everywhere else it is nearly the same volume.

Using PAM8403 amplifier module with passive speaker

Just out of curiosity I ordered another cheap amplifying module based on the PAM8403. This one is stereo and has a bigger potentiometer. It is 1 EUR on ebay for one piece. It is also rated for 5V power (2.5V-5V) and has a maximum output of 2x3W (5V, 4 Ohm)

The pinout is as easy as from the LM386 but for stereo. I soldered the pin headers. You need 3 for input, 2 for power and 4 for the output to the speakers. I used only mono output for the test:

From left to right:

  • rout-
  • rout+
  • lout+
  • lout-
  • power-
  • power+
  • input L
  • input GND
  • input R

Input GND and power- are connected so you only need one if powering directly from the Arduino.

The setup of the module looked like this:

The sound is slightly better and also louder. By using the volume control the sound goes to mute smoothly but is also clipping at about half the way.

Summary

The PAM I felt was slightly better at sound quality but only at the right volume. The advantage would be an adjustable volume but as half of the potentiometer causes distortion I can’t expose it to the user. So in the end I think I will stick with the LM386 for the build because the sound quality was good and the volume was also acceptable so it doesn’t have to be adjusted.

Further reading

Here are some more links on the topics:

Auduino build: http://apcmag.com/arduino-project-5-digital-audio-player.htm/

Audio on an Arduino nano: http://apcmag.com/play-wav-filesn-arduino-nano.htm/

Extensive post using SimpleSDaudio library, building SD card adapter, connecting speakers http://www.hackerspace-ffm.de/wiki/index.php?title=SimpleSDAudio

Builiding an adapter for microSD cards (and some other useful breadboarding tips): https://protostack.com.au/2011/09/8-breadboard-hacks/

SD card connection to arduino: https://www.flickr.com/photos/nathanchantrell/6323290363/in/photostream/

Connecting SD card to Arduino with LLC: http://physudo.blogspot.de/2013/09/sd-karten-mit-dem-arduino-beschreiben.html

SD/MMC From the ground up: http://forum.arduino.cc/index.php?topic=8314.0

Another great instruction from sparkfun – MicroSD Shield and SD Breakout Hookup Guide: https://learn.sparkfun.com/tutorials/microsd-shield-and-sd-breakout-hookup-guide

091117_2101_TinkeringTu3.jpg

Tinkering Tuesday – Playmobil Stage – LED Rings

Past posts of this series:

Tinkering Tuesday – Playmobil Stage – Einführung

Tinkering Tuesday – Playmobil Stage – Introduction

Tinkering Tuesday – Playmobil Stage – Playmobil parts

Tinkering Tuesday – Playmobil Stage – LED Stage Lights

Tinkering Tuesday – Playmobil Stage – Disco Ball with stepper motor


This one will be a rather short post to describe the LED rings that I used for the background effect.

They are very cheap WS2812B based LED rings from ebay. I bought 3 different ones, 7 LEDs (1,10 EUR), 16 LEDs (2,29 EUR) and 24 LEDs (3,69 EUR). The wiring is very easy, the WS2812B is addressable and requires only 5V, GND and one control pin from the Arduino. They are even chainable so I connected the control pins of all three rings to only require one singel Arduino pin.

In the following picture you can see the wiring. Yellow is the control pin chained from the biggest to the smallest ring. The black ground wire is also chained. I decided to provide the 5V to every ring on its own to avoid a loss of brightness (but I guess with only 47 LEDs that was not really necessary). I even made a professional looking schematic, see the small orange paper in the pic?!

Soldered and chained rings

In the next picture you see the solder pads in a closeup.. Here is the smallest ring only connecting the input side (black=GND, red=5V, yellow=control pin). As you saw above I secured everything with hot glue.

Solder pad closeup

Here it is already glued to the back of the stage:

Glued to the background

As a beginning I searched for an easy to use library and found FastLED. They are also compatible with Adafruits NeoPixel. So go have a look there. They have great guides.

The most simple program using it is:

#include "FastLED.h"
CRGB leds[1];
void setup() { FastLED.addLeds&lt;NEOPIXEL, 6&gt;(leds, 1); }
void loop() {
leds[0] = CRGB::White; FastLED.show(); delay(30);
leds[0] = CRGB::Black; FastLED.show(); delay(30);
}

This only blinks the first LED. There are some more great examples in the download from Github and the basics are very easy.

Here is a small test video switching all LEDs one after another from red to yellow to blue:

I wanted to use something with a little more effects than just blinking like with the LED spots so I chose one of the bigger examples, the NoisePlusPallette which can be found at: https://github.com/FastLED/FastLED/blob/master/examples/NoisePlusPalette/NoisePlusPalette.ino

This is using readymade color palettes and combines that with a random noise generator. You can influence the parameters (e.g. with the sound input) and so make this you own. I didn’t have so much time so I just fed in some parameters from my sound input and adapted it to the number of LEDs. The result will later be described in my Software wrapup.

I used acrylic glass that I sanded so it is not clear but milky to make the effect more outstanding. That will be seen later in the woodworking part. In my demonstration video you can see the rings in action (here directly starting at 0:11):

090317_0848_TinkeringTu5.jpg

Tinkering Tuesday – Playmobil Stage – Disco Ball with stepper motor

Past posts of this series:

Tinkering Tuesday – Playmobil Stage – Einführung
Tinkering Tuesday – Playmobil Stage – Introduction
Tinkering Tuesday – Playmobil Stage – Playmobil parts
Tinkering Tuesday – Playmobil Stage – LED Stage Lights


 

As already written on my last post I bought a disco mirror ball (5cm diameter) from Conrad:

I had some cheap stepper motors lying around so I decided to use one of those to drive the ball. Those are 28BYJ-48 motors with a ULN2003APG based driver board:

The motor was mounted using two simple screws:

View from above the stage
View from inside the stage

With a small drill bit I was able to make a small hole into the shaft and with some „Tüdeldraht“ and hot glue the ball was mounted onto it.

Final assembly

For the software part the following website http://www.eprojectszone.com/2016/11/19/arduino-and-uln2003apg/ was very helpful in understanding the ULN2003APG and using it together with an Arduino.

//http://www.eprojectszone.com/2016/11/19/arduino-and-uln2003apg/

int motorpin1 = 10;

int motorpin2 = 9;

int motorpin3 = 8;

int motorpin4 = 5;

int t =2;

void setup() {

pinMode(motorpin1, OUTPUT);

pinMode(motorpin2, OUTPUT);

pinMode(motorpin3, OUTPUT);

pinMode(motorpin4, OUTPUT);

}

void loop() {

digitalWrite(motorpin1, HIGH);

digitalWrite(motorpin2, LOW);

digitalWrite(motorpin3, LOW);

digitalWrite(motorpin4, LOW);

delay(t);

digitalWrite(motorpin1, LOW);

digitalWrite(motorpin2, HIGH);

digitalWrite(motorpin3, LOW);

digitalWrite(motorpin4, LOW);

delay(t);

digitalWrite(motorpin1, LOW);

digitalWrite(motorpin2, LOW);

digitalWrite(motorpin3, HIGH);

digitalWrite(motorpin4, LOW);

delay(t);

digitalWrite(motorpin1, LOW);

digitalWrite(motorpin2, LOW);

digitalWrite(motorpin3, LOW);

digitalWrite(motorpin4, HIGH);

delay(t);

}

This is not the most beautiful way to use a stepper motor in your code, so I just switched over to the stepper library (https://www.arduino.cc/en/Reference/Stepper). This reduces the code mostly to the following:

#include <Stepper.h>

#define motorpin1 7

#define motorpin2 8

#define motorpin3 12

#define motorpin4 13

#define STEPS 4096

Stepper stepper(STEPS, motorpin1, motorpin2, motorpin3, motorpin4);

void setup() {

stepper.setSpeed(2);

}

void loop() {

stepper.step(1);

}

This on its own worked great. But I had to learn that this is working just as the first code and is blocking the Arduino / slowing it down so much that everything else is not working as I was expecting it to be (especially the Audio analysis). Also together with the LED rings it was not really working out. It also produces interferences in the power lines. I used one single power source (5V 3A). But this led to so much interferences in using the stepper motor in the Audio output that I had to come up with another solution. More on that in a later part 😉

In the video there is the disco ball in action (starting at 0:48):