Breaking void loop into 3 separate sections...

For a current project, I am using a servo to strike individual piano keys at 3 different velocities. I have implemented a piezo transducer to detect the vibrations after every key strike in an attempt to trigger each individual velocity only after the previous key strike sound has fully decayed to silence.

So the order of my code should look something like the attached file, but the servo seems to be going through all 3 velocities at once after it detects that the first vibration has stopped. I am trying to get it to cease the loop after each trigger and then move on to the next command as a new task.

The code so far is thus…

#include <Servo.h> 

Servo myservo;

int knockSensor = 0;               

byte val = 0;

int statePin = LOW;

int THRESHOLD = 80;



void setup() {
 

   myservo.attach(9);
  myservo.write(90);

 Serial.begin(9600);

}


void loop() {

  val = analogRead(knockSensor);     

  if (val <= THRESHOLD) {

    myservo.write(0);
    delay (120);

    myservo.write(180);
    delay (120);

    myservo.write(90);
    delay (5000);
    
  }

  delay(1000);

  if (val <= THRESHOLD) {

    myservo.write(70);
    delay (130);

    myservo.write(110);
    delay (130);

    myservo.write(90);
    delay (5000);

  }

    if (val <= THRESHOLD) {

    myservo.write(85);
    delay (175);

    myservo.write(95);
    delay (260);

    myservo.write(90);
    delay (5000);
    } 

 delay (100);
}

Arduino UNO
Adafruit Motor Shield
Piezo tranducer
Continuous servo

You're using the same reading for all phases. Is that intentional?

AWOL: You're using the same reading for all phases. Is that intentional?

The same threshold reading? yes this is intentional

But it's the same value - why bother testing it each time?

AWOL: But it's the same value - why bother testing it each time?

Because I am using the device to record the keys of a piano at 3 different velocities. The reason for this is that I will be mapping each recorded key to the keys of a MIDI piano to recreate the human feel of a real piano when the MIDI piano is played. As the player hits the MIDI key at a certain velocity, the corresponding recorded velocity will sound.

So why not test once, and just start the sequence? Maybe I'm missing something.

AWOL: So why not test once, and just start the sequence? Maybe I'm missing something.

Incase trigger 2 is activated before the sound of trigger 1 has fully decayed. The same goes for the triggering velocity 3 before trigger 2 has fully decayed

You have 3 IF statements based on the same value so they will always ALL be either true or false. Hence you only need the first one.

You are not checking the knock sensor after every single servo movement. You only test it after every 9 servo movements. Is that what you want?

From your description it seems to me you want this sort of logic

if (sensor is silent)
   strike a key
repeat

and if you want a succession of different strikes, maybe this would be the logic

if (sensor is silent)
   strike a key with hardness
   increase hardness
     if hardness is too high
        set hardness back to zero
repeat

with either of these stratagems there should be no need for a delay()

,,,R

Robin2: You have 3 IF statements based on the same value so they will always ALL be either true or false. Hence you only need the first one.

You are not checking the knock sensor after every single servo movement. You only test it after every 9 servo movements. Is that what you want?

I want to test the sensor after every 3 movements actually. Is this possible? This is what I though the if commands were doing.

Then perhaps you need something like this

if (sensor is silent)
   strike a key with hardness
   increase hardness
   wait
   strike a key with hardness
   increase hardness
   wait
   strike a key with hardness
   set hardness back to 0
repeat

Note that the only line in your program that checks the sensor is

val = analogRead(knockSensor);

You can use delay() to create the waits but if your program becomes more complex that will probably make it very unresponsive. Have a look at how millis() is used to manage timing without blocking in several things at a time

...R

Robin2: Note that the only line in your program that checks the sensor is

val = analogRead(knockSensor);

Thank you for your help. This was the main issue with my code! I can consider this solved :)

I spoke to soon. My adapted program seems to repeat the first trigger after the vibration has stopped being detected instead of moving on to the next trigger.

I thought that inserting "val = analogRead(knockSensor); " would split the sections by simply reading the program chronologically.

    val = analogRead(knockSensor); 

    if (val <= THRESHOLD) {

    delay(1000); 
  
    myservo.write(85);
    delay (175);

    myservo.write(95);
    delay (260);

    myservo.write(90);
    delay (1000);

    }

    val = analogRead(knockSensor); 

    if (val <= THRESHOLD) {

    delay(1000); 
  
    myservo.write(80);
    delay (140);

    myservo.write(100);
    delay (140);

    myservo.write(90);
    delay (1000); 

    }

    val = analogRead(knockSensor); 

    if (val <= THRESHOLD) {

    delay(1000); 
  
    myservo.write(30);
    delay (120);

    myservo.write(130);
    delay (120);

    myservo.write(90);
    delay (1000); 

    }

  }

    buttonState = digitalRead(buttonPin);
    delay (1000); 

}

Please post the complete program.

...R

I thought that inserting "val = analogRead(knockSensor); " would split the sections by simply reading the program chronologically

How can that be? An analogRead takes around 100us. Any one which satisfies the threshold criterion will trigger that part of sequence. Any that fails will call the next analogRead again at some arbitrary point in the cycle.

Here is the full program…

#include <Servo.h> 

Servo myservo;

int knockSensor = 0;               

byte val = 0;

int statePin = LOW;

int THRESHOLD = 100;

int buttonState = 0; 

int buttonPin = 2;


void setup() {

  myservo.attach(9);
  myservo.write(90);
  pinMode(buttonPin, INPUT_PULLUP);
 

}


void loop() {

    buttonState = digitalRead(buttonPin);

    delay (1000);

    if (buttonState == HIGH) {

    val = analogRead(knockSensor); 

    if (val <= THRESHOLD) {

    delay(1000); 
  
    myservo.write(85);
    delay (175);

    myservo.write(95);
    delay (260);

    myservo.write(90);
    delay (1000);

    }

    val = analogRead(knockSensor); 

    if (val <= THRESHOLD) {

    delay(1000); 
  
    myservo.write(80);
    delay (140);

    myservo.write(100);
    delay (140);

    myservo.write(90);
    delay (1000); 

    }

    val = analogRead(knockSensor); 

    if (val <= THRESHOLD) {

    delay(1000); 
  
    myservo.write(30);
    delay (120);

    myservo.write(130);
    delay (120);

    myservo.write(90);
    delay (1000); 

    }

  }

    buttonState = digitalRead(buttonPin);
    delay (1000); 

}

The only way I can think to explain what I am trying to do here is to put it like this…

If (button is triggered, start sequence)

and then

If (first note fades to silence, trigger next note)

and then

If (second not fades to silence, trigger third note)

etc.

Avocado232:
The only way I can think to explain what I am trying to do here is to put it like this…

If (button is triggered, start sequence)

and then

If (first note fades to silence, trigger next note)

and then

If (second not fades to silence, trigger third note)

etc.

Before I look at your code I need to clarify something from your earlier Replies.

How is a single note triggered? In your Reply #8 you lead me to believe that a single note requires three successive movements of a single servo. Is that correct?

In other words, in programming terms, the servo will move 3 times followed by a period waiting for silence?

…R
PS I cannot play any musical instrument and I know nothing about music. I like Cream and Donna Summer and others.

If (first note fades to silence, trigger next note)

In your first alanlogRead "if" conditional block, you have nearly 2 and a half seconds of delay - how long can a note last?

Maybe you need to research using a state machine.

Robin2: Before I look at your code I need to clarify something from your earlier Replies.

How is a single note triggered? In your Reply #8 you lead me to believe that a single note requires three successive movements of a single servo. Is that correct?

In other words, in programming terms, the servo will move 3 times followed by a period waiting for silence?

...R PS I cannot play any musical instrument and I know nothing about music. I like Cream and Donna Summer and others.

Yes 3 movements of the servo would equate to one trigger. First it would rotate clockwise to hit the note, then it would travel back to it's initial position, and finally it would wait at it's initial position.

And yes the silence follows each of the three movements. The Piezo is put in place to detect whether the note is still ringing out, and only when the pickup detects silence shall the the next set of three movements take place, and so on

Avocado232: Yes 3 movements of the servo would equate to one trigger. First it would rotate clockwise to hit the note, then it would travel back to it's initial position, and finally it would wait at it's initial position.

And yes the silence follows each of the three movements.

The suggestion I posted in Reply #9 deals with that - except that I assumed the second 2 movements would be hitting keys. Just use whatever servo angles are needed - for example

if (sensor is silent)
   move servo to  strike key
   wait (if needed)
   move servo away from key
   wait (if needed)
   move servo to 3rd position
repeat

...R

Robin2: The suggestion I posted in Reply #9 deals with that - except that I assumed the second 2 movements would be hitting keys. Just use whatever servo angles are needed - for example

if (sensor is silent)
   move servo to  strike key
   wait (if needed)
   move servo away from key
   wait (if needed)
   move servo to 3rd position
repeat

...R

But the key will be struck at 3 different velocities. So instead of repeating the above command, I need it to move on to the next command to activate the second trigger. So more like:

1st trigger at set hardness:

if (sensor is silent) move servo to strike key move servo away from key hold position once completed...

2nd trigger at set hardness:

if (sensor is silent) move servo to strike key move servo away from key hold position once completed...

3rd trigger at set hardness:

if (sensor is silent) move servo to strike key move servo away from key hold position once completed...