Spectrum Analyzer - circuit noise

I am trying to build a Christmas light controller using this setup:
https://github.com/dimecoin/XmasFHT

Everything is going well, but I am getting lots of noise on the circuit - nothing connected to the audio input, but getting readings on the serial monitor. Even with the "A0" wire disconnected from the Arduino, I still have data on the serial port.

How can I reduce the noise on the circuit?

Here's my serial monitor output:
image

This is the code I'm presently using:

/* 
* Christmas Light Controller & Real Time Frequency Analyzer based on FHT code by Open Music Labs at http://openmusiclabs.com
* 
* Then modified by PK from : https://dqydj.com/build-your-own-real-time-frequency-analyzer-and-christmas-light-controller/
*
* Modified by dimecoin: https://github.com/dimecoin/XmasFHT
*
* Requires FHT library, from here: 
* 	http://wiki.openmusiclabs.com/wiki/ArduinoFHT
*/

/////////////////////////////////////////////////////////////////////
// Easy Customizations
/////////////////////////////////////////////////////////////////////

// Adjust the Treshold - what volume should make it light up?
#define THRESHOLD 35

// Old way if you want to statically set this.
// Attempt to 'zero out' noise when line in is 'quiet'.  You can change this to make some segments more sensitive.
// defaults: 
// { 100, 81, 54, 47, 56, 58, 60, 67 };
//int oct_bias[] = { 136, 107, 44, 47, 56, 58, 60, 77 };

// New Auto calibration.
uint8_t oct_bias[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
uint16_t cal_bias[] = { 0, 0, 0, 0, 0, 0, 0, 0 };

/* Number of times to sample the "natural noise" on wires to get average.
 * This average is used to cancel out noise while running.
 * Don't call to many times or will be slow to startup.  
 * Dont' call over 16777215 or so times or it might overflow (plus would take forever to startup).
	ie. 256 (max reading) * CAL_TIME needs to be <= (2^32)-1 (size in bits of unint16_t)
*/

#define CAL_TIME 100

// Divide Threshold by 2 for top octave? 1 - yes 2 - no.  Makes highest frequency blink more.
#define TOP_OCTAVE_DIVIDE false

// This is for ACTIVE HIGH relays (works with LEDS for testing), switch values if you have ACTIVE LOW relays.
#define ACTIVE_ON LOW
#define ACTIVE_OFF HIGH

// enable for serial mode output, comment out to speed up lights
#define DEBUG

// Timer/delay for self test on startup.
#define SELFTESTTIME 100

/////////////////////////////////////////////////////////////////////
// Hard Customizations - know what you are doing, please.
/////////////////////////////////////////////////////////////////////
// FHT defaults - don't change without reading the Open Music Labs documentation at openmusiclabs.com
#define LOG_OUT 1		// use the log output function
#define FHT_N 256		// set to 256 point fht
#define OCTAVE 1
#define OCT_NORM 0

// include the library, must be done after some of the aboves are defined.. (required by FHT, won't work if included in wrong order)
#include <FHT.h>

// Delay - defines how many cycles before the lights will update.  OML's algorithm at 256 samples (needed for our 8 octaves) takes
// 3.18 ms per cycle, so we essentially throw out 14 cycles (I used mechanical relays, you can lower this for solid state relays).
// 15 cycles = 47.7 ms update rate.  Be careful here and don't change it too quickly!  I warned you!
// Default is 15
#define DELAY 15

// Don't change NUM_PINS.  FHT outputs 8 octs.
#define NUM_PINS 8
// Pin configuration, there is only 8 channels here.  Add duplicate entries if you don't have 8 lights, must be 8!
int relayPins[] = { 2, 3, 4, 5, 6, 7, 8, 9 };

uint8_t x[NUM_PINS];


void frequencyGraph(uint8_t x[], int size);

void setup() {

	// pin setup
	for (int i = 0; i < NUM_PINS; i++) {
		pinMode(relayPins[i], OUTPUT);
		digitalWrite(relayPins[i], ACTIVE_OFF);
	}

	// quick self test
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < NUM_PINS; j++) {
			digitalWrite(relayPins[j], ACTIVE_ON);
			delay(SELFTESTTIME);
			digitalWrite(relayPins[j], ACTIVE_OFF);
		}
	}

#ifdef DEBUG
	Serial.begin(115200);
	while (!Serial) {
	};
#endif

	TIMSK0 = 0;		// turn off timer0 for lower jitter
	ADCSRA = 0xe5;		// set the adc to free running mode

	// This is setting up A0 - dime
	ADMUX = 0x40;		// use adc0
	DIDR0 = 0x01;		// turn off the digital input for adc0

}

/**********************************************************************************
  Loop - includes initialization function and the full loop
  
**********************************************************************************/

void loop() {

	// True full loop
	int q = 0;
	int cal = 0;

	while (1) {		// reduces jitter

		cli();		// UDRE interrupt slows this way down on arduino1.0

		for (int i = 0; i < FHT_N; i++) {	// save 256 samples
			while (!(ADCSRA & 0x10)) ;	// wait for adc to be ready
			ADCSRA = 0xf5;	// restart adc

			// This is his way of reading Analog 0 (A0).  It pulls in L[ow] and H[igh] bit. - dimecoin
			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
		}

		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_octave();	// take the output of the fht

		sei();

		// We are in calibration mode.
		if (cal < CAL_TIME) {

			for (int i = 0; i < NUM_PINS; ++i) {
				cal_bias[i] += fht_oct_out[i];
			}

#ifdef DEBUG
			Serial.print(F("Calibrating "));
			Serial.print(cal);
			Serial.print(F("/"));
			Serial.println(CAL_TIME);
#endif

			cal++;
			continue;
		}
		// Calibration mode has just ended, crunch data collected.
		if (cal == CAL_TIME) {

			for (int i = 0; i < NUM_PINS; ++i) {
				oct_bias[i] = (uint8_t) (cal_bias[i] / CAL_TIME);
			}

#ifdef DEBUG
			Serial.println(F("--------------------------------------"));
			Serial.println(F("Done with Cal"));

			for (int i = 0; i < NUM_PINS; ++i) {
				Serial.print(oct_bias[i]);
				Serial.print(" ");
			}

			Serial.println(F(""));
			Serial.println(F("--------------------------------------"));

			for (int i = 0; i < NUM_PINS; ++i) {
				Serial.print(fht_oct_out[i] - oct_bias[i]);
				Serial.print(F(" "));
			}
			Serial.println(F(""));
			Serial.println(F("--------------------------------------"));

			Serial.flush();

#endif

			// Ready signal.
			for (int i = 0; i < NUM_PINS; i++) {
				digitalWrite(relayPins[i], ACTIVE_ON);
			}
			for (int i = 0; i < NUM_PINS; i++) {
				digitalWrite(relayPins[i], ACTIVE_OFF);
			}

			cal++;
			continue;
		}
		// Normal play mode

		if (q % DELAY == 0) {

			for (int i = 0; i < NUM_PINS; i++) {
				x[i] = fht_oct_out[i] - oct_bias[i];
			}

			frequencyGraph(x, NUM_PINS);

#ifdef DEBUG
			for (int i = 0; i < NUM_PINS; ++i) {
				Serial.print(x[i]);
				Serial.print(F(" "));
			}
			Serial.println(F(""));
#endif

		}

		++q;
	}

}

void frequencyGraph(uint8_t x[], int size) {

	int top_threshold = THRESHOLD;

	for (int i = 0; i < size - 1; i++) {
		x[i] = max(x[i], 0);

		// Special logic for last pin
		if (TOP_OCTAVE_DIVIDE && i == (size - 1)) {
			top_threshold /= 2;
		}

		if (x[i] >= top_threshold) {
			digitalWrite(relayPins[i], ACTIVE_ON);
		} else if (x[i] < top_threshold) {
			// && digitalRead(relayPins[i]) == ACTIVE_ON ) {
			digitalWrite(relayPins[i], ACTIVE_OFF);
		}

	}

}

It is absolutely normal for floating inputs.

You have to find the source of the noise, first. As pointed out by b707, you must connect A0 to something, such as the Arduino ground or to the+5 volt pin.
Your noise may be related to the length of wires in your project, or signal wires next to mains power or perhaps the lighting in your area.
Shielded wires for all signal wires might help. Ground only one end of the shield otherwise it becomes a noise conductor.
Do you have all circuit grounds connected at a single point?

Oh, yeah, for sure, but as high as 255? That's the max for that channel. Seems suspicious to me.

OK, tried connecting to ground to pin A0 and I got all zeros ( 0 0 0 0 0 0 0 0) in the serial monitor - but then I also tied in the audio output to pin A0 too, but it wouldn't respond to to any audio input. So I disconnected and tried the +5 volt to pin A0 and got 0 0 0 0 0 1 0 0 consistently. Again tried tying in the audio output to pin A0 too, but it wouldn't respond to to any audio input.

I think I do. They all terminate on the same ground bar, if that's what you mean?

Here are some pics of my setup.



Indeed....
Do not connect A0 to GND directly, connect it with resistor 10-20k. May be even 100-300K, it needs to be determined locally.

oh... ok.
Well, I've disconnected the relays - thinking this could be an issue too.
Then I disconnected the audio input from A0 and proceeded to ground to pin A0 with a 100k resistor inline and I got the following.
image

There's really noting connected to the Arduino, now.

  •  +5 volt to the divider circuit
    
  •  Ground to ground bar
    
  •  Ground w/ 100k resistor to pin A0
    

Does this tell us anything? I'm not sure what to do next.

What code this output from?

Sorry, it's been a long day. What do you mean by "What code this output from?"
I included the code in my first post.

If so, digits in this output has nothing to do with analog reading from pin AO
No, of course they are generally related to the input signal, but through a lot of intermediate calculations. To test your input you need to run the code for reading the values of pin a0 only instead of whole code of spectrum analyzer.

Bwahahaha... you should see the stunned look I have on my face right now. Whatever you said went right over my head.

Do you mean start new sketch and pull data from A0?

i guess it can be useful to find the cause of the issue

Are you SURE you want a free running or only do a measurement when you ask?

Yes, and use a serial plotter and show it to us, run this program

//const int sensorIn = PB0;
const int sensorIn = A0;
double Voltage = 0;

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

void loop() {

  Voltage = getVPP();
  Serial.print( Voltage);
  Serial.println(" Amps RMS");
}

float getVPP()
{
  float result;
  int readValue;             //value read from the sensor
  int maxValue = 0;          // store max value here
  int minValue = 1024;          // store min value here, 4095.0
  //int minValue = 4095.0;

  uint32_t start_time = millis();
  while ((millis() - start_time) < 10)
    //while((millis()-start_time) < 100)  //sample for 1 Sec
  {
   // readValue = analogRead(sensorIn);
    readValue = analogRead(A0);
 //readValue = analogRead(PB0);

    if (readValue > maxValue)
    {
      maxValue = readValue;
    }
    if (readValue < minValue)
    {
      minValue = readValue;
    }
  }
  result = (maxValue - minValue);

  return result;
}

With the circuit shown on GitHub the analog input isn't floating. And turning-down the volume/sensitivity pot will effectively ground the input and that should kill most of the noise.

As a "sanity check" check the raw readings (Skip all of that FFT code and run the Analog Read Serial Example but without the delay).

It's got a bias circuit so with silence the raw readings should about half of the ADC range (about 512 on a "regular" Arduino with a 10-bit DAC.)

With an audio signal the reading will deviate above and below the bias. The readings will "look random" because you are sampling a waveform, but louder sounds you should see some bigger deviations (maybe down to zero and up to 1023 if it's loud enough) and with quiet signals it should stay near 512 (again assuming a 10-bit ADC).

ok... I like the sound of the "sanity check". I set up a breadboard with the same pro mini and 10k pot, uploaded the sketch per the tutorial and checked the Serial Monitor.

Dialed the pot to the left and got:
image

Then dialed the pot to the right and got this:
image

So I'm under the impression that my parts are OK.

I then put the Pro Mini and 10k pot back on my protype and checked pin A0.

Dialed the pot to the left and got:
image

Then dialed the pot to the right and got this:
image

This wasn't the result I expected... leaving me to suspect that something is up with my circuit. Do you agree? Anything else I should check next?

Something is wrong.

I just noticed that C1 is backwards. The - end should go to the pot and the + end to the two resistors (where we are expecting +2.5VDC). But I'm not sure that's the problem.

Do you have a multimeter.

Try starting with just R2 & R3. The two equal value resistors should make a 50/50 voltage divider for 2.5V and a reading of about 512.

When you're getting a reading of about 512 add the C1 and the pot.

Capacitor C4 should isolate the DC bias from the pot and the audio so the pot should have no effect when there is no audio. (It may have a very-temporary effect as the capacitor charges/discharges for a fraction of a second while the capacitor charges/discharges and then the reading should return to 512.)

C2 will act as a low-pass filter and a noise filter. I'd leave it for last. It's going to reduce the high frequencies so you might want to leave it out or use a lower-value capacitor.

Hi, @jalbes
Can you reverse engineer your project and please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, power supplies, component names and pin labels.

Thanks.. Tom.. :grinning: :+1: :coffee: :australia:

OK... so I took a voltage reading off:
R3 (positive end) and got 5.2V
R2 (negative end) and got 0.1V

I played with the pot but it had no effect on the voltage for either R2 nor R3.

Umm... there is no C4 on this circuit. Did you mean C1?

Sorry, C1. :frowning:

And what do you measure at the junction of R2 & R3?

at the junction, where my Lead connects to pin A0, I get 2.5V