Need Help with Self Tuning Guitar Stepper Motor

I am working on a self tuning guitar project (just one string for now), and I need help in how to program the stepper motor to work incrementally depending on my input. I already have the frequency programmed and working. Here is the code I have so far (I’m new here, so sorry if I pasted it wrong). Thank you in advance!

How do you make a stepper motor change incrementally depending on what the frequency is?


#define SAMPLES 128             //Max 128 for Arduino Uno.
#define SAMPLING_FREQUENCY 2048 //Fs = Based on Nyquist, must be 2 times the highest expected frequency.
#define OFFSETSAMPLES 40  //used for calabrating purposes
#define TUNER -3    //Adjust until C3 is 130.50

#include <Stepper.h>

float samplingPeriod;
unsigned long microSeconds;

int X[SAMPLES]; //create vector of size SAMPLES to hold real values
float autoCorr[SAMPLES]; //create vector of size SAMPLES to hold imaginary values
float storedNoteFreq[12] = {130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185, 196, 207.65, 220, 233.08, 246.94};

int sumOffSet = 0;
int offSet[OFFSETSAMPLES]; //create offset vector
int avgOffSet; //create offset vector

int i, k, periodEnd, periodBegin, period, adjuster, noteLocation, octaveRange;
float  maxValue, minValue;
long sum;
int thresh = 0;
int numOfCycles = 0;
float signalFrequency, signalFrequency2, signalFrequency3, signalFrequencyGuess, total;
byte state_machine = 0;
int samplesPerPeriod = 0;


const int stepsPerRevolution = 2048;
Stepper myStepper = Stepper(stepsPerRevolution, 8, 10, 9, 11);


void setup()
{
  myStepper.setSpeed(5);
  Serial.begin(115200); //115200 Baud rate for the Serial Monitor
}

void loop()
{ 
  //*****************************************************************
  //Calabration Section
  //*****************************************************************
  Serial.println("Calabrating. Please do not play any notes during calabration.");
  for (i = 0; i < OFFSETSAMPLES; i++)
  {
    offSet[i] = analogRead(0); //Reads the value from analog pin 0 (A0), quantize it and save it as a real term.
    //Serial.println(offSet[i]); //use this to adjust the sound detection module to approximately half or 512 when no sound is played.
    sumOffSet = sumOffSet + offSet[i];
  }
  samplesPerPeriod = 0;
  maxValue = 0;
  
  //*****************************************************************
  //Prepare to accept input from A0
  //*****************************************************************
  avgOffSet = round(sumOffSet / OFFSETSAMPLES);
  Serial.println("Counting down.");
  delay(1000);  //pause for 1 seconds
  Serial.println("3");
  delay(1000);  //pause for 1 seconds
  Serial.println("2");
  delay(1000);  //pause for 1 
  Serial.println("1");
  delay(1000);  //pause for 1 seconds
  Serial.println("Play your note!");
  delay(250);  //pause for 1/4 second for reaction time

  //*****************************************************************
  //Collect SAMPLES samples from A0 with sample period of samplingPeriod
  //*****************************************************************
  samplingPeriod = 1.0 / SAMPLING_FREQUENCY; //Period in microseconds
  for (i = 0; i < SAMPLES; i++)
  {
    microSeconds = micros();    //Returns the number of microseconds since the Arduino board began running the current script.
    X[i] = analogRead(0); //Reads the value from analog pin 0 (A0), quantize it and save it as a real term.
    
    /*remaining wait time between samples if necessary in seconds */
    while (micros() < (microSeconds + (samplingPeriod * 1000000)))
    {
      //do nothing just wait
    }
  }

  //*****************************************************************
  //Autocorrelation Function
  //*****************************************************************

  for (i = 0; i < SAMPLES; i++) //i=delay
  {
    sum = 0;
    for (k = 0; k < SAMPLES - i; k++) //Match signal with delayed signal
    {
      sum = sum + (((X[k]) - avgOffSet) * ((X[k + i]) - avgOffSet)); //X[k] is the signal and X[k+i] is the delayed version
    }
    autoCorr[i] = sum / SAMPLES;

    // First Peak Detect State Machine
    if (state_machine==0 && i == 0)
    {
      thresh = autoCorr[i] * 0.5;
      state_machine = 1;
    }
    else if (state_machine == 1 && i>0 && thresh < autoCorr[i] && (autoCorr[i]-autoCorr[i-1])>0) //state_machine=1, find 1 period for using first cycle
    {
      maxValue = autoCorr[i];
      
    }
    else if (state_machine == 1&& i>0 && thresh < autoCorr[i-1] && maxValue == autoCorr[i-1] && (autoCorr[i]-autoCorr[i-1])<=0)
    {
      periodBegin = i-1;
      state_machine = 2;
      numOfCycles = 1;
      samplesPerPeriod = (periodBegin - 0);
      period = samplesPerPeriod;
      adjuster = TUNER+(50.04 * exp(-0.102 * samplesPerPeriod)); 
      signalFrequency = ((SAMPLING_FREQUENCY) / (samplesPerPeriod))-adjuster; // f = fs/N
    }
    else if (state_machine == 2 && i>0 && thresh < autoCorr[i] && (autoCorr[i]-autoCorr[i-1])>0) //state_machine=2, find 2 periods for 1st and 2nd cycle
    {
      maxValue = autoCorr[i];
    }
    else if (state_machine == 2&& i>0 && thresh < autoCorr[i-1] && maxValue == autoCorr[i-1] && (autoCorr[i]-autoCorr[i-1])<=0)
    {
      periodEnd = i-1;
      state_machine = 3;
      numOfCycles = 2;
      samplesPerPeriod = (periodEnd - 0);
      signalFrequency2 = ((numOfCycles*SAMPLING_FREQUENCY) / (samplesPerPeriod))-adjuster; // f = (2*fs)/(2*N)
      maxValue = 0;
    }
    else if (state_machine == 3 && i>0 && thresh < autoCorr[i] && (autoCorr[i]-autoCorr[i-1])>0) //state_machine=3, find 3 periods for 1st, 2nd and 3rd cycle
    {
      maxValue = autoCorr[i]; 
    }
    else if (state_machine == 3&& i>0 && thresh < autoCorr[i-1] && maxValue == autoCorr[i-1] && (autoCorr[i]-autoCorr[i-1])<=0)
    {
      periodEnd = i-1;
      state_machine = 4;
      numOfCycles = 3;
      samplesPerPeriod = (periodEnd - 0);
      signalFrequency3 = ((numOfCycles*SAMPLING_FREQUENCY) / (samplesPerPeriod))-adjuster; // f = (3*fs)/(3*N)
    }
  }

  //*****************************************************************
  //Result Analysis
  //*****************************************************************
  if (samplesPerPeriod == 0)
  {
    Serial.println("Hmm..... I am not sure. Are you trying to trick me?");
  }
  else
  { 
    //prepare the weighting function
    total = 0;
    if (signalFrequency !=0)
    {
      total = 1;
    }
    if(signalFrequency2 !=0)
    {
      total = total + 2;
    }
    if (signalFrequency3 !=0)
    {
      total = total + 3;
    }

    //calculate the frequency using the weighting function
    signalFrequencyGuess = ((1/total) * signalFrequency) + ((2/total) * signalFrequency2) + ((3/total) * signalFrequency3); //find a weighted frequency
    Serial.print("The note you played is approximately ");
    Serial.print(signalFrequencyGuess);     //Print the frequency guess.
    Serial.println(" Hz.");

    //find octave range based on the guess
    octaveRange=3;
    while (!(signalFrequencyGuess >= storedNoteFreq[0]-7 && signalFrequencyGuess <= storedNoteFreq[11]+7 ))
    {
      for(i = 0; i < 12; i++)
      {
        storedNoteFreq[i] = 2 * storedNoteFreq[i];
      }
      octaveRange++;
    }
    
    //Find the closest note
    minValue = 10000000;
    noteLocation = 0;
    for (i = 0; i < 12; i++)
    {
      if(minValue> abs(signalFrequencyGuess-storedNoteFreq[i]))
      {
        minValue = abs(signalFrequencyGuess-storedNoteFreq[i]);
        noteLocation = i;
      }
    }
    
    //Print the note
    Serial.print("I think you played ");
    if(noteLocation==0)
    { 
      Serial.print("C");
    }  
    else if(noteLocation==1)
    {
      Serial.print("C#");
    }
    else if(noteLocation==2)
    {
      Serial.print("D");
    }
    else if(noteLocation==3)
    {
      Serial.print("D#");
    }
    else if(noteLocation==4)
    {
      Serial.print("E");
    }
    else if(noteLocation==5)
    {
      Serial.print("F");
    }
    else if(noteLocation==6)
    {
      Serial.print("F#");
    }
    else if(noteLocation==7)
    {
      Serial.print("G");
    }
    else if(noteLocation==8)
    {
      Serial.print("G#");
    }
    else if(noteLocation==9)
    {
      Serial.print("A");
    }
    else if(noteLocation==10)
    {
      Serial.print("A#");
    }
    else if(noteLocation==11)
    {
      Serial.print("B");
    }
    

    
    Serial.println(octaveRange);
    
  }
  //*****************************************************************
  //Stop here. Hit reset button on Arduino to restart
  //*****************************************************************
  while (1);
}```

Please edit your post to include the code in code tags ("</>" editor button).

What is the question?

Sorry about that, it should be good now.

You might consider using a PID algorithm, which in its simplest (P) form is to make a tuning change of size proportional to the error (difference) between desired frequency and measured frequency. The proportionality constant Kp can be determined by various well documented means, including trial and error.

However, since the mechanical output is discrete steps, that complicates the situation. You need to measure the frequency change per motor step in order to have a better idea how to implement it.