Cannot use analogRead() while using FHT.h library

Hey everybody!

This is my first time writing here so I wanna be as descriptive about my problem as possible.

Basically I'm working on a project involving sound signal (through Jack 3,5 mm) visualization using the FHT.h library and displaying a frequency spectrum on an LCD display (based on the work of Hazi Tech: DIY Audio Spectrum Analyzer LCD Display | 6 Patterns | Arduino & 1602 LCD - YouTube). With that I also have 3 potentiometers, the first used as high frequency EQ, the second as low frequency EQ and the third as volume adjuster.

This is my code so far:

#define AUTO_GAIN 1       // auto adjust by volume
#define VOL_THR 10        // silence threshold (below it there will be no display on the matrix)
#define LOW_PASS 40       // lower threshold of noise sensitivity (no jumps in the absence of sound)
#define DEF_GAIN 120      // default maximum threshold 
#define FHT_N 256         // spectrum width x2
#define LOG_OUT 1

#include <FHT.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

unsigned long cas_pred = 0;
int HF_pred = 0;
int LF_pred = 0;
int VOL_pred = 0;
int stav = 0;

byte posOffset[16] = {2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
byte maxValue, maxValue_f, set = 1, gain_sp = DEF_GAIN;
float k = 0.1;
int i, j, pattern;
unsigned long gainTimer;

void setup()
{
  Serial.begin(9600);
  ADMUX  = 0b01100000;
  ADCSRA = 0b11010100;
  lcd.init();
  lcd.init();
  lcd.backlight();
}

void loop()
{
  unsigned long cas = millis();

/*
  int pot_HF = analogRead(A1);
  int pot_LF = analogRead(A2);
  int pot_VOL = analogRead(A3);

  int HF = map(pot_HF, 0, 950, 1, 10);
  int LF = map(pot_LF, 0, 950, 1, 10);
  int VOL = map(pot_VOL, 0, 950, 1, 10);
*/

  byte v1[8] = {0, 0, 0, 0, 0, 0, 0, 31};
  byte v2[8] = {0, 0, 0, 0, 0, 0, 31, 31};
  byte v3[8] = {0, 0, 0, 0, 0, 0, 31, 31};
  byte v4[8] = {0, 0, 0, 0, 31, 0, 31, 31};
  byte v5[8] = {0, 0, 0, 31, 31, 0, 31, 31};
  byte v6[8] = {0, 0, 0, 31, 31, 0, 31, 31};
  byte v7[8] = {0, 31, 0, 31, 31, 0, 31, 31};
  byte v8[8] = {31, 31, 0, 31, 31, 0, 31, 31};

  lcd.createChar(0, v1);
  lcd.createChar(1, v2);
  lcd.createChar(2, v3);
  lcd.createChar(3, v4);
  lcd.createChar(4, v5);
  lcd.createChar(5, v6);
  lcd.createChar(6, v7);
  lcd.createChar(7, v8);



  analyzeAudio();   // FHT function, clogs the fht_log_out [] array with values ​​along the spectrum



  for (int pos = 0; pos < 16; pos++)
  {
    if (fht_log_out[posOffset[pos]] > maxValue)
    {
      maxValue = fht_log_out[posOffset[pos]];
    }

    lcd.setCursor(pos, 0);
    int posLevel = map(fht_log_out[posOffset[pos]], LOW_PASS, gain_sp, 0, 15);
    posLevel = constrain(posLevel, 0, 15);

    while (j < 2)
    {
      j++;


      if (posLevel > 7)
      {
        lcd.write((uint8_t)posLevel - 8);
        lcd.setCursor(pos, 1);
        lcd.write((uint8_t)7);
      }

      else
      {
        lcd.print(" ");
        lcd.setCursor(pos, 1);
        lcd.write((uint8_t)posLevel);
      }
    }
    j = 0;
  }



  if (AUTO_GAIN)
  {
    maxValue_f = maxValue * k + maxValue_f * (1 - k);
    if (millis() - gainTimer > 1500) {
      if (maxValue_f > VOL_THR) gain_sp = maxValue_f;
      else gain_sp = 150; gainTimer = millis();
    }
    else {
      gain_sp = DEF_GAIN;
    }
  }



  switch (stav)
  {
    case 1:

      if (cas - cas_pred >= 3000)
      {
        lcd.clear();
        stav = 0;
      }

      break;

    case 2:

      if (cas - cas_pred >= 3000)
      {
        lcd.clear();
        stav = 0;
      }

      break;

    case 3:

      if (cas - cas_pred >= 3000)
      {
        lcd.clear();
        stav = 0;
      }

      break;
  }


  /*
  if (abs(pot_HF - HF_pred) > 100)
  {
    stav = 1;

    HF_pred = pot_HF;

    Serial.print("HF = ");
    Serial.println(HF);
    Serial.println(pot_HF);

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("HF");
    lcd.setCursor(0, 1);
    lcd.print(HF);

    cas_pred = cas;
  }


  
  if (abs(pot_LF - LF_pred) > 100)
  {
    stav = 2;

    LF_pred = pot_LF;

    Serial.print("LF = ");
    Serial.println(LF);
    Serial.println(pot_LF);

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("LF");
    lcd.setCursor(0, 1);
    lcd.print(LF);

    cas_pred = cas;
  }



  if (abs(pot_VOL - VOL_pred) > 100)
  {
    stav = 3;

    VOL_pred = pot_VOL;

    Serial.print("VOL = ");
    Serial.println(VOL);
    Serial.println(pot_VOL);

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("VOL");
    lcd.setCursor(0, 1);
    lcd.print(VOL);

    cas_pred = cas;
  }
  */

}



void analyzeAudio()
{
  while (i < FHT_N) {
    i++;
    do {
      ADCSRA |= (1 << ADSC);
    }
    while ((ADCSRA & (1 << ADIF)) == 0); fht_input[i] = (ADCL | ADCH << 8);
  } i = 0;
  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
}

For now I want to be able to display the frequency spectrum and with the change of value of any of the three potentiometers I want to display the value of that potentiometer (scaled). The problem is that whenever I adress the analogRead() function to a variable, instead of nice jumping frequency spectrum, I just get the bottom lines of the frequencies displayed and a periodical wave that activates and deactiavtes all of the pixels on the LCD. If I remove the variable (or change it to a comment) the visualization functions again perfectly.

In the code there are commented out the declarations of the variables used to monitor the potentiometers and the section for displaying the values on the LCD. Like that the visualization will work from the start. If the declarations of the variables used for storing the potentiometer data are used then the wave pattern of pixels starts to appear and visualization doesn't work.

Please don't mind the other parts of the code as I'm sure that in this state even with the analogRead() function working the visualization would probably collide with the cases in switch used for displaying potentiometer values on the LCD. I'm just trying to understand here why the code doesn't work while trying to use the analogRead() function.

So, the final question is, can I use the analogRead() function here or not?

Thank you everybody for replying, help would be much appreciated.

For a meaningful spectrum display, the audio sampling must be continuous and the sample rate must be constant. On a typical Arduino, that means the Arduino can't really do much else.

The best you can do is collect a complete array of audio data, transform it, display the result, sample the pots, then start over collecting audio data.

and don't do that in the loop...

The audio input is using direct register access to the ADC. When you use the 'analogRead()' function from the Arduino core it is going to assume that the ADC is still set up for analogRead(). The two uses conflict.

I think the easiest solution would be to learn how to read the potentiometer inputs using direct register access so you can set the ADC back to the state the audio input expects when the reading of the pots is over.

The ADMUX register is used to select the analog input.

I haven't worked through the register bits, but I think the issue is the opposite, that is, the direct register access in "analyzeAudio()" assumes the "setup" configuration of ADC registers that are subsequently changed by analogRead.

Namely, ADMUX and ADCSRA are configured in "setup", but are modified in analogRead(). The solution might be to move those register assignments from "setup" to the beginning of analyzeAudio().

1 Like

That may be the case. Couldn't hurt to give it a try.

Sounds promising. Will definately give it a try and keep you guys updated.

Thanks for your repsones!

In the old forum (2013):
http://forum.arduino.cc/index.php/topic,38153.0.html

A library by:

  Written by:  Tom Roberts  11/8/89
  Made portable:  Malcolm Slaney 12/15/94 malcolm@interval.com
  Enhanced:  Dimitrios P. Bouras  14 Jun 2006 dbouras@ieee.org
  Modified for 8bit values David Keller  10.10.2010

Is now hosted here:
fix_fft.c · GitHub

Anyway, the AVR analog was utilized for L/R channels:

My notes indicated it worked:
image

FFT_test4.zip (5.8 KB)

So, as @MrMark pointed out, I moved the register assignments down to the analyzeAudio() declaration and now it works wonders!

While in visualization state I can freely change the EQ and volume and the values are being displayed on the LCD and after 3 seconds it jumps back to visualization.

Thank you all so much for your help!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.