Real life Super Mario coin block

Hello,

I started building this instructable http://www.instructables.com/id/Coin-block/ using Arduino Mega and a MP3 shield. So far everything is working. I’ve got it so that when I press a button the sound plays and the (not modified) continuous servo rotates 360 degrees then stops. The problem I’m having, is that the servo will not stop at the same place every time. Each time it rotates it will either stop ahead of the 12 o’clock point by a few centimeters or behind it by centimeters. With each iteration (button press), it moves further away from the 12 o’clock point until it makes it all the way back to the 12 0’clock point and then starts all over again.

I don’t know if this qualifies as servo creep… It seems like there is no way for a continuous servo to know positions since it doesn’t have a potentiometer. However, I’m a little confused because the servo instructions (http://learn.parallax.com/KickStart/900-00008) for my servo, seem to indicate that the stop position can be set by setting the trim during calibration; which I’ve done.

I can successfully stop rotation with either myServo.writeMicroseconds(1500) or by using myServo.detach() but neither fix the problem above. I’ve also played with setting the delay to many different values - which does help but it only reduces the distance. If I actually have it come it contact with something like my finger, then whatever little improvements I get from adjusting the delay doesn’t matter. I’m wondering if I need to get a different servo or if there is something I’m missing.

#include <Servo.h>                //Include servo library to easily work with servo.

unsigned char cmd_buf[10];       //Array to hold command values
unsigned char i;                 // variable to temporarily hold each command value in for loop
int inputPin = 52;               // choose the input pin (for a pushbutton)
int val = 0;                    // variable for reading the pin status (Checks status of pushbutton)
int bounceCheck = 0;            // variable for debouncing (http://www.roguescience.org/wordpress/building-a-midi-out-controller/part-3-add-a-switch/exercise-6/)
int pos = 0;
Servo myServo;                  //Create servo object using servo library.
                  

/** Function to iterate though list of command values and store them 
in a array then send the command through the Arduino serial connection to the Elechouse MP3 shield. (elechouse.com) */
void ArduinoMP3Shield_SendCMD(unsigned char *cmd_buf, unsigned len) 
{
    unsigned i;
    for(i=0; i<len; i++){
        Serial.write(cmd_buf[i]);
    }
}
/** Function to specify the list of commands to send to the MP3 shield 
and then call the "ArduinoMP3Shield_SendCMD" to collect the commands then send them */
void MP3ShieldCMD_Setup()
{
 /** wait until arduino mp3 shield is ready */
    delay(1000);
    
 Serial.begin(9600);
    
    /** Refer to manual for the details on the commands below (elechouse.com)*/
    
    /** set volume */
    cmd_buf[0] = 0x7E;          // START
    cmd_buf[1] = 0x03;          // Length
    cmd_buf[2] = 0xA7;          // Command
    cmd_buf[3] = 0x1F;          // new volume (0x1F is the highest volume)
    cmd_buf[4] = 0x7E;          // END
    ArduinoMP3Shield_SendCMD(cmd_buf, 5);
    
    /** set play mode play single */
    cmd_buf[0] = 0x7E;          // START
    cmd_buf[1] = 0x03;          // Length
    cmd_buf[2] = 0xA9;          // Command SET MODE
    cmd_buf[3] = 0x00;          // set mode (0x00 means to only play once)
    cmd_buf[4] = 0x7E;          // END
    ArduinoMP3Shield_SendCMD(cmd_buf, 5);
    
    /** select SPI Flash and play first mp3 file */
    cmd_buf[0] = 0x7E;          // START
    cmd_buf[1] = 0x04;          // Length
    cmd_buf[2] = 0xA1;          // Command (0xA1 accesses the 0000-moneda.mp3 on the SPI flash)
    cmd_buf[3] = 0x00;          // file number high byte
    cmd_buf[4] = 0x01;          // file number low byte
    cmd_buf[5] = 0x7E;          // END
    ArduinoMP3Shield_SendCMD(cmd_buf, 6); 
  }  
  
void sweep_360()
{
  myServo.attach(9);  // Servo is connected to digital pin 9
  
  myServo.writeMicroseconds(2000);  // Counter clockwise
  delay(1175);                      // Wait for servo to fully rotate
  myServo.writeMicroseconds(1500);
  //myServo.detach();  // Stop
}

void setup()
{
   pinMode(inputPin, INPUT);     // declare pushbutton as input
}

void loop()
{
  val = digitalRead(inputPin);            //read input value
  delay(10);                              //wait 10ms
  bounceCheck = digitalRead(inputPin);   //check again
  if(val == bounceCheck){                //if val is the same then not a bounce
    if (val == LOW) {                  //check if the input is LOW call the two functions
        sweep_360();                  //Call sweep function first to allow for the ~1200ms delay, otherwise it won't make a full rotation
        MP3ShieldCMD_Setup();        //Call function "MP3ShieldCMD_Setup" to play sound.

     }   
   }
}

I attached my wiring diagram in case there is something wrong there. Thank you advance for your help.

Mp3_Button_Diagram_Without_Shield.fzz (7.75 KB)

writeMicroseconds() says:

Writes a value in microseconds (uS) to the servo, controlling the shaft accordingly. On a standard servo, this will set the angle of the shaft. On standard servos a parameter value of 1000 is fully counter-clockwise, 2000 is fully clockwise, and 1500 is in the middle.

Note that some manufactures do not follow this standard very closely so that servos often respond to values between 700 and 2300.

Emphasis mine. You indicate you have played with the DELAY. Have you played with the values in writeMicrosends()?

I have, but not outside the values Parallax says. 1500 does stop it just not in the same place every time. I’ve tried 1725 1750 and 1300 etc… Is there such a thing as a 360 position feedback servo? Or do you have other suggestions?

If you can’t figure out how to get your continous servo to stop at the right place consistently with code, my best suggestion would be to look at doing it with hardware.

Some sort of sensor- an IR emitter and detector come to mind- set up so the arm controlled by the servo passes between them. The trigger action runs the servo until there is a detected IR break and then shuts it down maybe.

Maybe it is a simple as just using a different servo?

Ahh I was afraid it may require something like that....

I was looking for another servo on amazon and found one with the code for ardiuino below:

int servopin=7;//Define servo PWM control as digital 7 int myangle;//define variable angle int pulse width;//define variable pulse width int val; void servopulse(int servopin,int myangle)//define a pulse function { pulsewidth=(myangle*11)+500;//translate angle to a pulse width value between 500-2480

digitalWrite(servopin,HIGH);//pull the interface signal level to high delayMicroseconds(pulsewidth);//delay in microseconds digitalWrite(servopin,LOW);//pull the interface signal level to low

I'm using the servo library, but this seems more direct. I wonder if that would help?