'Clap-on, clap-off' servo not working

Frankly, I suck at coding (only know very basic Python, not even Arduino), and I've been using AI to 'code'. Needless to say, not going great. I've tried to edit the code (two quick claps for servo to move one direction 5 degrees then back after a second, and then another two claps for it to move the other direction 5 degrees then back after a second) and it just hasn't been working. And I know my wiring is correct since the servo IS moving, it's just not moving correctly... at all. The project is due Friday, so I need some help fast lol

Here's the most updated code (still not working):

#include <Servo.h>

Servo myServo;  // Create a servo object

const int soundSensorPin = A0;  
const int servoPin = 9;       

int soundLevel = 0;     
int previousSoundLevel = 0;
int clapThreshold = 50; 
int clapCount = 0;      
int servoPosition = 0;  
bool direction = true; 
unsigned long lastClapTime = 0; 
unsigned long clapInterval = 1000; 
bool servoMoved = false; 

void setup() {
  Serial.begin(9600);  
  myServo.attach(servoPin); 
  myServo.write(servoPosition); 
  delay(1000);  
}

void loop() {
  soundLevel = analogRead(soundSensorPin);  
  Serial.println(soundLevel);


  if (abs(soundLevel - previousSoundLevel) > clapThreshold) {
    unsigned long currentTime = millis(); 
    
    if (currentTime - lastClapTime < clapInterval) {
      clapCount++; 
    }

    previousSoundLevel = soundLevel; 
    lastClapTime = currentTime; 
  }


  if (clapCount >= 2 && !servoMoved) {

    if (direction) {
      myServo.write(servoPosition + 5);  
      delay(500);  
      myServo.write(servoPosition);  
    } else {
      myServo.write(servoPosition - 5); 
      delay(500); 
      myServo.write(servoPosition);  
    }

    direction = !direction;  
    clapCount = 0;  
    servoMoved = true;  
  }


  if (servoMoved && (millis() - lastClapTime) > clapInterval) {
    servoMoved = false;
  }

  delay(50); 
}

My eyes are crossing badly trying to understand your code. Not alarming, no worries. It looks plausible but you are flying blind.

Try placing serial print statements liberally strewn about to print the value of key bribes variables and confirm they are plausible and properly informing the flow through the code.

a7

you might benefit from studying state machines.

Here is a small introduction to the topic: Yet another Finite State Machine introduction

To help out, here is what your machine could look like

1 Like

Here's your sketch with some additions and modifactions.

Try it here

Find my comment

//...

to see where. Mostly I didn't have your sensor, so I made it work from a button.

Two presses of the button make the servo act the way I think you want it to.

// https://wokwi.com/projects/423250666579251201

# include <Servo.h>

const byte clapButton = 7;    // clap! button

Servo myServo;  // Create a servo object

const int soundSensorPin = A0;  
const int servoPin = 9;       

int soundLevel = 0;     
int previousSoundLevel = 0;
int clapThreshold = 50; 
int clapCount = 0;    

//...
int servoPosition = 90;
const int servoDeviation = 30;    //.. something we can see!
bool direction = true; 
unsigned long lastClapTime = 0; 
unsigned long clapInterval = 1000; 
bool servoMoved = false; 

void setup() {
  Serial.begin(9600);  

  pinMode(clapButton, INPUT_PULLUP);

  myServo.attach(servoPin); 
  myServo.write(servoPosition); 
  delay(100);   //... life too short 
}

void loop() {
  soundLevel = analogRead(soundSensorPin);  
//... you can if you need to  Serial.println(soundLevel);

//... detect a clap served in on the button
  static bool lastButton;
  static bool aButtonClap;
  bool gotClapped = false;

  aButtonClap = digitalRead(clapButton) == LOW;

  if (aButtonClap != lastButton) {
    gotClapped = aButtonClap;
    lastButton = aButtonClap;

    if (gotClapped) Serial.println("              CLAP!");
  }



//  if (abs(soundLevel - previousSoundLevel) > clapThreshold) {
  if (gotClapped) {
    gotClapped = false;
    unsigned long currentTime = millis(); 
//... the first clap... doesn't need to check time!
    if (!clapCount) {
      clapCount = 1;
//      Serial.print("clap now "); Serial.println(clapCount);
    }
    else if (currentTime - lastClapTime < clapInterval) {
      clapCount++; 
//      Serial.print("clap now "); Serial.println(clapCount);
    }
//...    else clapCount = 0; ???

    Serial.print("clap now "); Serial.println(clapCount);

    previousSoundLevel = soundLevel; 
    lastClapTime = currentTime; 
  }

  if (clapCount >= 2 && !servoMoved) {

    if (direction) {
      myServo.write(servoPosition + servoDeviation);  
      delay(500);  
      myServo.write(servoPosition);  
    } else {
      myServo.write(servoPosition - servoDeviation); 
      delay(500); 
      myServo.write(servoPosition);  
    }

    direction = !direction;  
    clapCount = 0;  
    servoMoved = true;  
  }

  if (servoMoved && (millis() - lastClapTime) > clapInterval) {
    servoMoved = false;
  }

  delay(50); 
}

a7

+1. I would say any time even looking into it would be well spent.

There may be some benefit to seeing the original sketch work, but there is no doubt that anything even a bit more complicate would be easier to do with the state machine approach.

a7

Hi, @arduinofella
Welcome the forum.

What are you using as your sound sensor?

Can you please post a copy of your circuit, a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, power supplies, component names and pin labels.

Tom.... :smiley: :+1: :coffee: :australia:

Here is an example with the state machine following the approach from my tutorial and the above state machine description.

I replaced the sound sensor with a short potentiometer, so move it to the right to go above the threshold and back to the left and do that twice to get the "double clap" effect.

I think this illustrates how more readable a code can be using the right coding structure.

click to see the code

https://europe1.discourse-cdn.com/arduino/original/4X/0/5/9/05969069926a464e23c946bc115a75e89a83adfa.png


  /* ============================================
  code is placed under the MIT license
  Copyright (c) 2025 J-M-L
  For the Arduino Forum : https://forum.arduino.cc/u/j-m-l

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  ===============================================
*/

#include <Servo.h>

Servo myServo;  // Create a servo object

const byte soundSensorPin = A0;
const byte servoPin = 9;

const int clapThreshold = 900;
const unsigned long clapInterval = 1000;
const unsigned long servoInterval = 500;
const int servoPosition = 90;

int deltaPos = 50; // should be 5° but 50° is more visible
unsigned long t0;
enum {IDLE, FIRST_CLAP, ACTION} state = IDLE;

bool clapDetected() {
  static bool previousClapDetected = false;
  bool clapDetected =  analogRead(soundSensorPin) >= clapThreshold;
  if (not previousClapDetected && clapDetected) {
    previousClapDetected = true;
    return true;
  }
  previousClapDetected = clapDetected;
  return false;
}

void setup() {
  Serial.begin(115200);
  myServo.attach(servoPin);
  myServo.write(servoPosition);
}

void loop() {
  switch (state) {
    case IDLE:
      if (clapDetected()) {
        t0 = millis();
        state = FIRST_CLAP;
      }
      break;

    case FIRST_CLAP:
      if (clapDetected()) {
        t0 = millis();
        myServo.write(servoPosition + deltaPos);
        state = ACTION;
      } else if (millis() - t0 >= clapInterval) state = IDLE;
      break;

    case ACTION:
      if (millis() - t0 >= servoInterval) {
        myServo.write(servoPosition);
        deltaPos = -deltaPos;
        state = IDLE;
      }
      break;
  }
}

PS/ @arduinofella May be I left a bug intentionally, so not something to copy and give to the teacher... She/He will find out pretty quickly you cheated and copied it from here.



Here are the photos of the wiring.

Continuous rotation servo?

Yeah - FT90R is continuous rotation - you won’t get a degree position

Really? Boy do I feel stupid lol

Wait… theoretically I could just have the servo rotate for a certain amount of time at a certain speed in one direction, achieving the same result (albeit less precise), right? Problem is, no idea where to even start on coding THAT.

Correct.
Write servo >90 for one direction and <90 for another. Write 90 to stop servo.
91 moves slowly, 180 at full speed (or 89 / 0).

Srsly? You already allow time for the servo to move, so allow the same time for it to move in the way you want to try.

It will not work well, but you should go ahead anyway. Minor variations will acccumulate and end up being unsatisfactory.

To go CW, you need to specify an angle greater than 90 degrees.

To go CCW, an angle of less than 90.

To stop, an angle of 90. Oops, I hope it is close to stopped. 91? 89? Switch to specifying the angle as a time in microseconds… 1500? 1507?

Anyway, here you waggle the servo, a real servo:

      myServo.write(servoPosition - 5); 
      delay(500); 
      myServo.write(servoPosition);  

and this would make the attempt to wrangle a continuous rotation servo into being what it is not:

      myServo.write(120); // moving CW at a clip. Or is that CCW
      delay(318); 
//      myServo.write(90);  // stop

      myServo.write(60); // moving CCW at a clip.
      delay(318); 
      myServo.write(90);  // stop

where I have 318 as the time and 30 as the speed. 90 +/- 30. Adjust to taste.

I don't have one, so it will be interesting to see how it works.

Or… buy another servo, a non-continuous rotation servo. :expressionless:

HTH

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.