World of Electronics and Cyber Consultancy

Detect frequencies or solfege levels using Arduino …

Below is the short video published on YouTube:

Here’s the code:

// ////////////////////////////////////////////////////////////////////////////
// Project: Singing bowls illumination
// Description: It turns on a different colour depending on which Chakra is played
// Engineer: Alberto Lopez
// More: https://misCircuitos.com
// Date: 16 – nov – 2022 Chiang Mai (Thailand)
///////////////////////////////////////////////////////////////////////////////

define DEBUG_MODE //Comment when finish debuging

//—————————————————————————//
int in[128];
byte NoteV[13]={8,23,40,57,76,96,116,138,162,187,213,241,255}; //data for note detection based on frequency
//byte NoteV[13]={8,23,40,57,76,96,116,138,162,187,213,241,255}; //data for note detection based on frequency 440Hz tuning

float f_peaks[5]; // top 5 frequencies peaks in descending order
// pins
const int Mic_pin = A0; // change as per Microphone pin

const int red_pin = 2; //C
const int orange_pin = 3;
const int yellow_pin =4; //E
const int green_pin = 5;
const int blue_pin = 6; //G
const int blueDark_pin = 7;
const int purple_pin = 8; //B

int MINIMUM_SOUND = 383; //minimim sound level threshold to start lighting
//variables
int note;
//—————————————————————————//

void setup() {
Serial.begin(9600);
Serial.println(“Singing Bowls correctly initialized”);
Serial.println(“v1.0 17-nov-2022 Alberto Lopez: misCircuitos.com \n”);

//Declare the LED pins as output
pinMode(red_pin, OUTPUT);
pinMode(orange_pin, OUTPUT);
pinMode(yellow_pin, OUTPUT);
pinMode(green_pin, OUTPUT);
pinMode(blue_pin, OUTPUT);
pinMode(blueDark_pin, OUTPUT);
pinMode(purple_pin, OUTPUT);

pinMode(A0, INPUT);
MINIMUM_SOUND=0;
for(int i=0;i<20;i++)
{
MINIMUM_SOUND+=analogRead(A0);
}
MINIMUM_SOUND=MINIMUM_SOUND/20;
Serial.println(MINIMUM_SOUND);

//testLED(); //Debug LED and light all
}

void loop()
{
//delay(10); //slow down the loop
//testLED();

//debug mic
//int lecturaMic = analogRead(Mic_pin);
//Serial.print(“volumen:”); Serial.println(lecturaMic);

note = ReadNote();

resetLED();

switch(note)
{
case 0: //RED
//Serial.println(“The Bowl number C”);
digitalWrite(red_pin,HIGH);
break;
case 2: //ORANGE
//Serial.println(“The Bowl number D”);
digitalWrite(orange_pin,HIGH);
break;
case 4: //YELLOW
//Serial.println(“The Bowl number E”);
digitalWrite(yellow_pin,HIGH);
break;
case 5: //GREEN
//Serial.println(“The Bowl number F”);
digitalWrite(green_pin,HIGH);
break;
case 7: //LIGHT BLUE
//Serial.println(“The Bowl number G”);
digitalWrite(blue_pin,HIGH);
break;
case 9: //DARK BLUE
//Serial.println(“The Bowl number A”);
digitalWrite(blueDark_pin,HIGH);
break;
case 11: //PURPLE
//Serial.println(“The Bowl number B”);
digitalWrite(purple_pin,HIGH);
break;
default:
resetLED();
}

ifdef DEBUG_MODE

switch(note)
{
case 0:
Serial.println(“The Bowl number C”);
break;
case 2:
Serial.println(“The Bowl number D”);
break;
case 4:
Serial.println(“The Bowl number E”);
break;
case 5:
Serial.println(“The Bowl number F”);
break;
case 7:
Serial.println(“The Bowl number G”);
break;
case 9:
Serial.println(“The Bowl number A”);
break;
case 11:
Serial.println(“The Bowl number B”);
break;
}

endif

}//close loop

void resetLED()
{
digitalWrite(red_pin,LOW);
digitalWrite(orange_pin,LOW);
digitalWrite(yellow_pin,LOW);
digitalWrite(green_pin,LOW);
digitalWrite(blue_pin,LOW);
digitalWrite(blueDark_pin,LOW);
digitalWrite(purple_pin,LOW);
}

void testLED(){
Serial.println(“LED test debug mode”);
int speed = 2000;
while(1){
resetLED();
digitalWrite(red_pin,HIGH);
Serial.println(“RED”);
delay(speed);
digitalWrite(orange_pin,HIGH);
Serial.println(“ORANGE”);
delay(speed);
digitalWrite(yellow_pin,HIGH);
Serial.println(“YELLOW”);
delay(speed);
digitalWrite(green_pin,HIGH);
Serial.println(“GREEN”);
delay(speed);
digitalWrite(blue_pin,HIGH);
Serial.println(“BLUE”);
delay(speed);
digitalWrite(blueDark_pin,HIGH);
Serial.println(“BLUEDARK”);
delay(speed);
digitalWrite(purple_pin,HIGH);
delay(speed);
}
}

//—————————–Tone Detection Function———————————————-//
// This code won’t work for any board having RAM less than 2kb,
int ReadNote()
{ long unsigned int a1,b,a2;
float a;
float sum1=0,sum2=0;
float sampling;
a1=micros();
for(int i=0;i<128;i++) //DATA ACQUISITION
{
a=analogRead(Mic_pin)-MINIMUM_SOUND; //rough zero shift
//utilising time between two sample for windowing & amplitude calculation
sum1=sum1+a; //to average value
sum2=sum2+aa; // to RMS value a=a(sin(i3.14/128)sin(i3.14/128)); // Hann window in[i]=10a; // scaling for float to int conversion
delayMicroseconds(195); // based on operation frequency range
}
b=micros();

sum1=sum1/128; // Average amplitude
sum2=sqrt(sum2/128); // RMS
sampling= 128000000/(b-a1); // real time sampling frequency

//for very low or no amplitude, this code wont start
//it takes very small aplitude of sound to initiate for value sum2-sum1>3,
//change sum2-sum1 threshold based on requirement
if(sum2-sum1>3){ //THRESHOLD TO START
FFT(128,sampling);
//EasyFFT based optimised FFT code,
//this code updates f_peaks array with 5 most dominent frequency in descending order

for(int i=0;i<12;i++){in[i]=0;} // utilising in[] array for further calculation

int j=0,k=0; //below loop will convert frequency value to note
for(int i=0; i<5;i++) { if(f_peaks[i]>1040){f_peaks[i]=0;}
if(f_peaks[i]>=65.4 && f_peaks[i]<=130.8) {f_peaks[i]=255((f_peaks[i]/65.4)-1);} if(f_peaks[i]>=130.8 && f_peaks[i]<=261.6) {f_peaks[i]=255((f_peaks[i]/130.8)-1);} if(f_peaks[i]>=261.6 && f_peaks[i]<=523.25){f_peaks[i]=255((f_peaks[i]/261.6)-1);} if(f_peaks[i]>=523.25 && f_peaks[i]<=1046) {f_peaks[i]=255((f_peaks[i]/523.25)-1);} if(f_peaks[i]>=1046 && f_peaks[i]<=2093) {f_peaks[i]=255*((f_peaks[i]/1046)-1);} if(f_peaks[i]>255){f_peaks[i]=254;}
j=1;k=0;

     while(j==1)
          {
          if(f_peaks[i]<NoteV[k]){f_peaks[i]=k;j=0;}
          k++;  // a note with max peaks (harmonic) with aplitude priority is selected
          if(k>15){j=0;}
          }

          if(f_peaks[i]==12){f_peaks[i]=0;}
          k=f_peaks[i];
          in[k]=in[k]+(5-i);
        }

k=0;j=0;
for(int i=0;i<12;i++)
{
if(k<in[i]){k=in[i];j=i;} //Max value detection
}
// Note print
// if you need to use note value for some application, use of note number recomendded
// where, 0=c;1=c#,2=D;3=D#;.. 11=B;
//a2=micros(); // time check
k=j;
/*
Serial.print(“note:”);
Serial.print(k);
Serial.println(“,”);
/ /
if(k==0) {Serial.println(‘C’);}
if(k==1) {Serial.print(‘C’);Serial.println(‘#’);}
if(k==2) {Serial.println(‘D’);}
if(k==3) {Serial.print(‘D’);Serial.println(‘#’);}
if(k==4) {Serial.println(‘E’);}
if(k==5) {Serial.println(‘F’);}
if(k==6) {Serial.print(‘F’);Serial.println(‘#’);}
if(k==7) {Serial.println(‘G’);}
if(k==8) {Serial.print(‘G’);Serial.println(‘#’);}
if(k==9) {Serial.println(‘A’);}
if(k==10){Serial.print(‘A’);Serial.println(‘#’);}
if(k==11){Serial.println(‘B’);}
*/
return k;
}
}

//—————————–FFT Function———————————————-//
// EasyFFT code optimised for 128 sample size to reduce mamory consumtion

float FFT(byte N,float Frequency)
{
byte data[8]={1,2,4,8,16,32,64,128};
int a,c1,f,o,x;
a=N;

  for(int i=0;i<8;i++)                 //calculating the levels
     { if(data[i]<=a){o=i;} }
  o=7;

byte in_ps[data[o]]={}; //input for sequencing
float out_r[data[o]]={}; //real part of transform
float out_im[data[o]]={}; //imaginory part of transform

x=0;
for(int b=0;b<o;b++) // bit reversal
{
c1=data[b];
f=data[o]/(c1+c1);
for(int j=0;j<c1;j++)
{
x=x+1;
in_ps[x]=in_ps[j]+f;
}
}

  for(int i=0;i<data[o];i++)            // update input array as per bit reverse order
     {
      if(in_ps[i]<a)
      {out_r[i]=in[in_ps[i]];}
      if(in_ps[i]>a)
      {out_r[i]=in[in_ps[i]-a];}      
     }

int i10,i11,n1;
float e,c,s,tr,ti;

for(int i=0;i<o;i++)                                    //fft
{
 i10=data[i];              // overall values of sine cosine  
 i11=data[o]/data[i+1];    // loop with similar sine cosine
 e=6.283/data[i+1];
 e=0-e;
 n1=0;

      for(int j=0;j<i10;j++)
      {
      c=cos(e*j); 
      s=sin(e*j); 
      n1=j;

            for(int k=0;k<i11;k++)
             {
             tr=c*out_r[i10+n1]-s*out_im[i10+n1];
             ti=s*out_r[i10+n1]+c*out_im[i10+n1];

             out_r[n1+i10]=out_r[n1]-tr;
             out_r[n1]=out_r[n1]+tr;

             out_im[n1+i10]=out_im[n1]-ti;
             out_im[n1]=out_im[n1]+ti;          

             n1=n1+i10+i10;
              }       
         }
 }

//—> here onward out_r contains amplitude and our_in conntains frequency (Hz)
for(int i=0;i<data[o-1];i++) // getting amplitude from compex number
{
out_r[i]=sqrt((out_r[i]out_r[i])+(out_im[i]out_im[i])); // to increase the speed delete sqrt
out_im[i]=(iFrequency)/data[o]; /
Serial.print(out_im[i],2); Serial.print(“Hz”);
Serial.print(“\t”); // uncomment to print freuency bin
Serial.println(out_r[i]);
*/
}

x=0; // peak detection
for(int i=1;iout_r[i-1] && out_r[i]>out_r[i+1])
{in_ps[x]=i; //in_ps array used for storage of peak number
x=x+1;}
}

s=0;
c=0;
for(int i=0;i<x;i++) // re arraange as per magnitude
{
for(int j=c;j<x;j++)
{
if(out_r[in_ps[i]]<out_r[in_ps[j]])
{s=in_ps[i];
in_ps[i]=in_ps[j];
in_ps[j]=s;}
}
c=c+1;
}

for(int i=0;i<5;i++)     // updating f_peak array (global variable)with descending order
 {
 f_peaks[i]=(out_im[in_ps[i]-1]*out_r[in_ps[i]-1]+out_im[in_ps[i]]*out_r[in_ps[i]]+out_im[in_ps[i]+1]*out_r[in_ps[i]+1])
 /(out_r[in_ps[i]-1]+out_r[in_ps[i]]+out_r[in_ps[i]+1]);
 }

}
//————————————————————————————//

Download the Code