Guitar Rhythm Taper

shakamuni01:
Well this code here version 7 seems more responsive but a long way to go. I gotta figure out how to reset the tap count when it goes from playback mode into record mode each time. I also changed the parameters from true and false to Low and high. Somehow it seemed to respond better to that. Don’t know why.

I looked briefly at the code you posted and you use a goto (usually not a good idea) to go to a place in code. From there I don’t see a way back so that’s a pretty good reason why it doesn’t work. I conditioned transfer between record and playback modes based on the state of a switch. I would suggest you learn why the code I posted worked before you start taking it apart. The difference between for and while loops is critical in that code. As is the use of Tapcount to know when taps have been recorded or not.

Sorry about that.

I started taking apart the one you posted with the playback mode because it wasn’t working. It would not really record well at all, and when it did it would play the rhythm back once and then stop.

I tried to research this usage of modes but could find anything about it on the ardiuno reference page. Hence went to goto reality.

Is the mode functions you are using synonymous with case functions?

… followup. Realized why really no version was working well.

I was using analog pin but the program was based on digital read. Now I will go back and try your code again.

Saying that the one I tinkered with since seems to work well too. Obviously will take some refining.

#include <Servo.h>

Servo servo1;                    // create first servo object to control first servo

// declare the constants
const int FSR = 0;                         // FSR on pin 0
const int programSwitch = 2;               // Record playback switch on pin 2
const int servoPin = 6;                    // servo is on pin 6
const int LEDpin = 13;                     // use onboard LED on pin 13
const int MaxNumIntervals = 20;            // the max number of intervals to be recorded
const int Tapthresh = 5;                  // threshold for FSR tap detection
const int strikePos = 170;                 // servo command for strike position
const int retractPos = 0;                  // servo command for retract position
const unsigned long strikeTime = 250;      // time, in msec, btw the servo striking and retracting
const unsigned long minTapInterval = 100;  // min time between taps in msecs

// declare the variables
int TapCount = 0;                           // Incrementer for the array
int TapCntCopy = 0;                         // Copy of tap count
int mode = 0;                               // mode is record (1) or playback (0)
unsigned long TapTime=millis();             // Reference for when this tap started.
unsigned long tapReadings[MaxNumIntervals]; // set aside memory for interval times

void setup()
{
  pinMode(programSwitch, INPUT); // define record playback pin as an input
  pinMode(LEDpin, OUTPUT);       // define LED pin as an output
  digitalWrite(LEDpin, LOW);     // set the LED off
  servo1.attach(servoPin);       // attaches the first servo
  Serial.begin(9600);            // init the serial port to 9600 baud, 8N1
}


void loop()

{
  while(analogRead(programSwitch)==0)  // initial check of record playback mode switch
  {
   for (int i=0; i<=(TapCount-1); i++)  //  It is at tap count-1 becuase we are playing the intervals between the tapcount.
      {
        servo1.write(strikePos);  // command servo to strike
        delay(strikeTime);        // wait just long enough for servo to get there
        servo1.write(retractPos); // command servo back to zero position
        delay(tapReadings[i]);    // pauses between beats
        
        
        Serial.print("the fucking thing worked");
        Serial.print("loop FSR is ");       //survey code next 10 lines
        Serial.println(analogRead(FSR));
        Serial.print("loop programSwitch is ");
        Serial.println(analogRead(programSwitch));
        Serial.print("loop TapCount is ");
        Serial.println(TapCount);
        Serial.print("loop i is ");
        Serial.println(i);
        delay (0);
        
        if (i>=TapCount)          // this allows the array to play itself through again and again when it get to the end.
              {i=0;}
        if(analogRead(programSwitch)>0)    //This function will pull it out of the rythm playing loop
              {    
              TapCount=0;                         //Tapcount gets reset to 0 as it enters record mode.
              goto Recordmode;                    // jump out of 'for loop' to Recordmode  
              }       
            
        }
   }

    
    // now run the tap detection routine over and over and oever
    
    Recordmode:
    
    
    while((analogRead(programSwitch)>0))                    // In theory nothing should happen to this whole section if the program button is not pressed.   
    {      
    Serial.println("Start tapping");
    Serial.print("RecordMode 1 FSR is ");                     //survey code next six lines 
    Serial.println(analogRead(FSR));
    Serial.print("RecordMode 1 programSwitch is ");
    Serial.println(analogRead(programSwitch));
    Serial.print("RecordMode 1 tapcount is ");
    Serial.println(TapCount);
    delay (0);
      
      
      if(analogRead(programSwitch)==0) //read switch to see if user commands playback mode
        {    
        Serial.println("Finished recording tap data");
        }      
      
      if(TapCount <= MaxNumIntervals)
        {
        if(analogRead(FSR) > Tapthresh)                // a tap has been detected !
            {
             digitalWrite(LEDpin, LOW);                // blink LED off to show tap detected
             if(TapCount == 0)                         // 1st tap detected
               {
                TapTime = millis();
                Serial.println("Detected 1'st tap");            
                for (int i=0; i<MaxNumIntervals; i++) 
                {tapReadings[i]=0;}                    //Now reset the entire possible listening array
               }
          else                                         // this 'else' refers to is TapCount is more than 0 which would be anything after the first tap.  
                                                       //This is so the program doesn't count the interval time before and taps have started.
               {
                tapReadings[TapCount-1] = int(millis() - TapTime);     // record interval btw this tap and prior tap and the active recording starts here.
                TapTime = millis();
                Serial.print("Recorded interval #   ");                //send interval count to PC
                Serial.println(TapCount);
               }
             TapCount++;                            // tap count = 21 after 20 intervals.  Due to an interval being the space between two points of tap.
             delay(minTapInterval);                 // now wait for tap to be released, FSR to go back to normal
             digitalWrite(LEDpin, HIGH);            // turn LED back on
            }
         }
      else                                          //This 'else' refers to it being beyond the max intervals.
      {
        digitalWrite(LEDpin, LOW);                  //tell user all done recording
        Serial.println("Recorded max number of taps");
      }
    }
  }

shakamuni01:
Sorry about that.

I started taking apart the one you posted with the playback mode because it wasn’t working. It would not really record well at all, and when it did it would play the rhythm back once and then stop.

Odd, it worked just fine when I tested it. Now I didn't have a servo to tap anything, all I went by was an LED flashing to the recorded taps. And I didn't have an FSR to record the taps, rather I had a momentary button. But as I said those were things that needed to be worked out on your setup. Timing and mechanical placement of the servo arm is something to be "tuned" as is the threshold on the FSR for a reliable tap detection. The rest of the logic worked just fine in terms of switching btw playback and record and playing back what had been recorded, at least for me.

shakamuni01:
I tried to research this usage of modes but could find anything about it on the ardiuno reference page. Hence went to goto reality. Is the mode functions you are using synonymous with case functions?

The concept of "modes" is an old one, in your case there being only 2 modes (record and playback) meant a simple *if ... else* statement could be used. If mode = playback do X, Y and Z tasks. Else the mode must be record and so do W, U and V tasks. But you could also use *switch ... case* statements and I commonly do when there's more than 2 or 3 "modes". Perhaps you can Google finite state machine to learn about that. Or read the link below. In this usage "mode" and "state" are the same thing.

http://playground.arduino.cc/Code/FiniteStateMachine

In an FSM the code in each state does a certain set of functions you define, that are somehow sufficiently different from those functions done in another state. In your case record and playback would be the 2 states. In each state the code does what it does and then looks for certain conditions to change to another state. In your case the reading of the non-momentary playback record switch was 1 input into the decision to change state. In going from playback to record the switch had to be settled in the record position AND the playback had to have completed the last tap.

There were other niceties that had to be considered, such as preserving the prior tap recording if the user changed his/her mind and taking care of switch bouncing and lighting the onboard LED to show what was going on in the code … and the majority of the code was to handle those niceties. Switch bounce can ruin otherwise good running code.

I see.

Thanks for you patient responses Mee n Mac

I am guessing now your code will work fine. I can’t remember off hand the original because I must have made 30 versions since, but again it is probably due to my error of using an analog pin when it said digital. One things I am planning to do is to use two servos instead of one because one cannot generate the taps fast enough. So I will probably add in this section something like:

Here is the original part:

for (int i=0; i<=(TapCount-1); i++) // It is at tap count-1 becuase we are playing the intervals between the tapcount.

{

servo1.write(strikePos); // command servo to strike

delay(strikeTime); // wait just long enough for servo to get there

servo1.write(retractPos); // command servo back to zero position

delay(tapReadings*); // pauses between beats*
(and will add)
i=i++ // this will push the array ahead one step
servo2.write(strikePos); // command servo to strike
delay(strikeTime); // wait just long enough for servo to get there
servo2.write(retractPos); // command servo back to zero position
delay(tapReadings); // pauses between beats
Who knows, if I get confident and cocky I may even try and make another array that could fill up that can record the magnitude of the tap. So that then the strike position will vary for each array step.:slight_smile:

I have no idea of what you’re using for hardware at the moment. Trying to debug code on an iffy hardware platform is likely going to lead you to pull your hair out. So get the switches and tap sensing working and make sure the code is aligned with whatever you’re using, accounting for it’s peculiarities.

As for the dual servos, you’re probably correct. I recall an article (I’ve searched and searched and can’t find it … thought it was over at HaD ??) wherein the person had taken a windup toy monkey that played a drum and Arduino’ed it. He used 2 servos as the toy had 2 arms that drummed originally. There was a bit of tuning to get the servos to strike the drum head just hard enough but not for too long a time (too far a motion). Getting the arm’s to retract to just the right position was another tuned thing.

I don’t quite “see it” in the snippet above but I get the idea that one servo plays the odd taps (#1, 3, 5 etc) while the other does the even taps (#2, 4, 6 etc), thus giving a longer time for each to get back and then go forth again.

“I don’t quite “see it” in the snippet above but I get the idea that one servo plays the odd taps (#1, 3, 5 etc) while the other does the even taps (#2, 4, 6 etc), thus giving a longer time for each to get back and then go forth again.”

Correct.

I am planning to use a spring loaded fulcrum so hopefully the servo only has to go about 50 and not 170. Who knows if it is effective enough one servo may do.