Serial monitor printing zero

THIS POST IS IN TWO PARTS

PART 1

I've made a frequency detector that stores the frequency of the sound input in a variable called 'frequency'. Previously I had the code printing this variable to the serial monitor with no problem. The following is the code that did that.

//clipping indicator variables
boolean clipping = 0;

//data storage variables
byte newData = 0;
byte prevData = 0;
unsigned int time = 0;//keeps time and sends vales to store in timer[] occasionally
int timer[10];//sstorage for timing of events
int slope[10];//storage for slope of events
unsigned int totalTimer;//used to calculate period
unsigned int period;//storage for period of wave
byte index = 0;//current storage index
float frequency;//storage for frequency calculations
int maxSlope = 0;//used to calculate max slope as trigger point
int newSlope;//storage for incoming slope data

//variables for decided whether you have a match
byte noMatch = 0;//counts how many non-matches you've received to reset variables if it's been too long
byte slopeTol = 3;//slope tolerance- adjust this if you need
int timerTol = 10;//timer tolerance- adjust this if you need

//variables for amp detection
unsigned int ampTimer = 0;
byte maxAmp = 0;
byte checkMaxAmp;
byte ampThreshold = 30;//raise if you have a very noisy signal

void setup(){
  
  Serial.begin(9600);
  
  pinMode(13,OUTPUT);//led indicator pin
  pinMode(12,OUTPUT);//output pin
  
  cli();//diable interrupts
  
  //set up continuous sampling of analog pin 0 at 38.5kHz
 
  //clear ADCSRA and ADCSRB registers
  ADCSRA = 0;
  ADCSRB = 0;
  
  ADMUX |= (1 << REFS0); //set reference voltage
  ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
  
  ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
  ADCSRA |= (1 << ADATE); //enabble auto trigger
  ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN); //enable ADC
  ADCSRA |= (1 << ADSC); //start ADC measurements
  
  sei();//enable interrupts
}

ISR(ADC_vect) {//when new ADC value ready
  
  PORTB &= B11101111;//set pin 12 low
  prevData = newData;//store previous value
  newData = ADCH;//get value from A0
  if (prevData < 127 && newData >=127){//if increasing and crossing midpoint
    newSlope = newData - prevData;//calculate slope
    if (abs(newSlope-maxSlope)<slopeTol){//if slopes are ==
      //record new data and reset time
      slope[index] = newSlope;
      timer[index] = time;
      time = 0;
      if (index == 0){//new max slope just reset
        PORTB |= B00010000;//set pin 12 high
        noMatch = 0;
        index++;//increment index
      }
      else if (abs(timer[0]-timer[index])<timerTol && abs(slope[0]-newSlope)<slopeTol){//if timer duration and slopes match
        //sum timer values
        totalTimer = 0;
        for (byte i=0;i<index;i++){
          totalTimer+=timer[i];
        }
        period = totalTimer;//set period
        //reset new zero index values to compare with
        timer[0] = timer[index];
        slope[0] = slope[index];
        index = 1;//set index to 1
        PORTB |= B00010000;//set pin 12 high
        noMatch = 0;
      }
      else{//crossing midpoint but not match
        index++;//increment index
        if (index > 9){
          reset();
        }
      }
    }
    else if (newSlope>maxSlope){//if new slope is much larger than max slope
      maxSlope = newSlope;
      time = 0;//reset clock
      noMatch = 0;
      index = 0;//reset index
    }
    else{//slope not steep enough
      noMatch++;//increment no match counter
      if (noMatch>9){
        reset();
      }
    }
  }
    
  if (newData == 0 || newData == 1023){//if clipping
    PORTB |= B00100000;//set pin 13 high- turn on clipping indicator led
    clipping = 1;//currently clipping
  }
  
  time++;//increment timer at rate of 38.5kHz
  
  ampTimer++;//increment amplitude timer
  if (abs(127-ADCH)>maxAmp){
    maxAmp = abs(127-ADCH);
  }
  if (ampTimer==1000){
    ampTimer = 0;
    checkMaxAmp = maxAmp;
    maxAmp = 0;
  }
  
}

void reset(){//clea out some variables
  index = 0;//reset index
  noMatch = 0;//reset match couner
  maxSlope = 0;//reset slope
}


void checkClipping(){//manage clipping indicator LED
  if (clipping){//if currently clipping
    PORTB &= B11011111;//turn off clipping indicator led
    clipping = 0;
  }
}


void loop(){
  
  checkClipping();
  
  if (checkMaxAmp>ampThreshold){
    frequency = 38462/float(period);//calculate frequency timer rate/period
  
    //print results
    Serial.print(frequency);
    Serial.println(" hz");
  }
  
  delay(100);//delete this if you want
  
  //do other stuff here
}

I then changed the code to compare three consecutive readings against each other and only print the reading if they are in a range of 4hz of each other. I did this to eliminate incorrect readings. This worked, however there were still a few incorrect readings. I only changed the void loop since the last code, so that's all I've included below.

void loop(){
  
  checkClipping();
  
  if (checkMaxAmp>ampThreshold){
    frequency = 38462/float(period);//calculate frequency timer rate/period

    //define all variables
  int count = 0;
  int threshold = 4;
  int prevPrevValue = 0;
  int prevValue = 0;
  int currentValue = 0;
  int trueFrequency = 0;

if (count == 0)
{
   delay(100);        
   prevPrevValue = 0; //reset values 
   prevValue = 0;
   currentValue = 0;
   count = 0;
   count ++;
}

if (count == 1)
{
   prevPrevValue = frequency;
   if (prevPrevValue > 0);
   {
      count ++;
   }
}

if (count == 2)
{
   prevValue = frequency;
   if (prevValue > 0)
   {
      count ++;
   }
}

if (count == 3)
{
   currentValue = frequency;
   if (currentValue > 0)
   {
      count ++;
   }
}

if (count == 4)
{
   if (abs(prevPrevValue - prevValue < threshold))
   {
      trueFrequency = prevPrevValue;
      Serial.print (trueFrequency);
      Serial.println (" hz");
      count ++;
   }

   else if (abs(prevValue - currentValue < threshold))
   {
      trueFrequency = currentValue;
      Serial.print (trueFrequency);
      Serial.println (" hz");
      count ++;
   }

   else
   {
      count ++;
   }
}

if (count = 5)
{
  count = 0;
}
  }
  

}

PART 2

I then tried to make it even more accurate by comparing three consectutive readings of three consectutive readings (essentially creating another layer of what I did before). However, when I uploaded the following code, the serial monitor printed '0 hz' every time there was a sound input, whereas the previous code printed the real frequency every time there was a sound input. (once again I only changed void loop, so that's all I've included. This should be used on top of the rest of the previous code)

void loop(){
  
  checkClipping();
  
  if (checkMaxAmp>ampThreshold){
    frequency = 38462/float(period);//calculate frequency timer rate/period

    //define all variables
  int count = 0;
  int threshold = 4;
  int prevPrevValue = 0;
  int prevValue = 0;
  int currentValue = 0;
  int trueCount = 0;
  int trueFrequency = 0;
  int firstTrueFrequency = 0;
  int secondTrueFrequency = 0;
  int thirdTrueFrequency = 0;
  int finalFrequency = 0;

if (count == 0)
{
   delay(100);        
   prevPrevValue = 0; //reset values 
   prevValue = 0;
   currentValue = 0;
   firstTrueFrequency = 0;
   secondTrueFrequency = 0;
   thirdTrueFrequency = 0;
   count = 0;
   trueCount ++; // make trueCount = 1
   count ++; // make count = 1
}

if (count == 1)
{
   prevPrevValue = frequency;
   if (prevPrevValue > 0);
   {
      count ++;
   }
}

if (count == 2)
{
   prevValue = frequency;
   if (prevValue > 0)
   {
      count ++;
   }
}

if (count == 3)
{
   currentValue = frequency;
   if (currentValue > 0)
   {
      count ++;
   }
}

if (count == 4)
{
   if (abs(prevPrevValue - prevValue < threshold))
   {
      trueFrequency = prevPrevValue;
      trueCount ++;
      count ++;
   }

   else if (abs(prevValue - currentValue < threshold))
   {
      trueFrequency = currentValue;
      trueCount ++;
      count ++;
   }

   else
   {
      count ++;
      trueCount++;
   }
}
// ALL OF ABOVE IS CORRECT

if (count == 5 && trueCount == 1)
{
  firstTrueFrequency = trueFrequency;
  if (firstTrueFrequency > 0);
  {
    count = 0; 
  }
 
}

if (count == 5 && trueCount == 2)
{
  secondTrueFrequency = trueFrequency;
  if (secondTrueFrequency > 0);
  {
    count = 0; 
  }
}

if (count == 5 && trueCount == 3)
{
  thirdTrueFrequency = trueFrequency;
  if (thirdTrueFrequency > 0);
  {
    count = 0; 
  }
}
//ALL OF ABOVE IS CORRECT

if (trueCount = 4);
{
  if (abs(firstTrueFrequency - secondTrueFrequency < threshold))
   {
      finalFrequency = firstTrueFrequency;
      Serial.print (finalFrequency);
      Serial.println (" hz");
      trueCount ++;
      count ++;
   }

   else if (abs(secondTrueFrequency - thirdTrueFrequency < threshold))
   {
      finalFrequency = thirdTrueFrequency;
      Serial.print (finalFrequency);
      Serial.println (" hz");
      trueCount ++;
      count ++;
   }

   else
   {
      count ++;
      trueCount++;
   }
}

if (count == 6 && trueCount == 5)
{
  trueCount = 0;  
  count = 0;
}
  }
  

}

Why is it printing '0 hz' and what do I do to the code to fix it?

 if (trueCount = 4);

Bong !

one way to avoid those types of errors is to put the constant on the left side

if (0 = trueCount)

which would generate a compiler error

which would generate a compiler error

True, but to me it reads unnaturally. I find it easier to write the test properly rather than to backwards write it

what's properly?

gcjr:
what's properly?

It was sloppy of me to use that word. "Properly" was just a short way of saying "in a way that can be read as a natural sentence" but bear in mind I an a native English speaker and what is natural to you may not be natural to me

An example in a different context. In many languages the noun precedes the adjective that describes it but in English it is the other way round, hence "car blue" sounds wrong to me whereas "blue car" sounds natural

Hi UKHeliBob,

Your first correction, though it was a mistake and a silly one at that, did not solve the problem of the serial moniter printing '0 hz'.

ARDUINO_GEEK:
Hi UKHeliBob,

Your first correction, though it was a mistake and a silly one at that, did not solve the problem of the serial moniter printing '0 hz'.

There were two mistakes.

If you make a change, post your code

  if (abs(firstTrueFrequency - secondTrueFrequency < threshold))

Here you are taking the absolute value of a boolean expression.

You probably meant:

  if (abs(firstTrueFrequency - secondTrueFrequency) < threshold)

gcjr:
one way to avoid those types of errors is to put the constant on the left side

if (0 = trueCount)

which would generate a compiler error

Oh! Never thought of that. Brilliant!

(Even gave you a karma kick for that one.)

-jim lee