Novice questions re servo sketch

I'm using the sketch below to experiment with the 'SG90 micro servo' that came with my starter kit.

Q1: With the values shown I would expect to get a 180 degree sweep in both directions, but I estimate it as about 150. Trying other values, it always falls short by20-30 degrees. Is that normal?

Q2: Is there a sketch available anywhere that uses a function, with parameters (such as start and finish angle, and duration)? That might be neater and easier to use for testing.

Q3: For the project I have in mind I will probably need a servo with greater torque but still compact in size. Any recommendations before I start googling please?

Q4: On a general point, why are comments inside /* always in a faint, hard-to-read font? Can that be changed?

Any other practical advice would be appreciated please.

 /* Sweep
 by BARRAGAN <http://barraganstudio.com>
 This example code is in the public domain.

 modified 8 Nov 2013
 by Scott Fitzgerald
 http://www.arduino.cc/en/Tutorial/Sweep
*/

#include <Servo.h>

Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards

int pos = 0;    // variable to store the servo position

void setup() {
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}

void loop() {
  for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
    // in steps of 1 degree
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }
  for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }
}

Many servos are physically incapable of moving through a full 180° and trying to force them past their natural end-points risks causing damage to them. You need to experiment to find the limits of your particular servo.

If you use Servo.writeMicroseconds() you will get a much finer degree of control. The normal range is from about 500 to 2400 microsecs - but again you need to figure out the limits for your servo.

I don't have answers to the other questions.

...R

Thanks Robin.

Here's a simple test sketch:

/*
 Try this test sketch with the Servo library to see how your
 servo responds to different settings, type a position
 (0 to 180) or if you type a number greater than 180 it will be
 interpreted as microseconds(544 to 2400), in the top of serial
 monitor and hit [ENTER], start at 90 (or 1472) and work your
 way toward zero (544) 5 degrees (or 50 micros) at a time, then
 toward 180 (2400). 
*/
#include <Servo.h>
Servo servo;

void setup() {
  // initialize serial:
  Serial.begin(9600); //set serial monitor baud rate to match
  servo.write(90);
  servo.attach(9);
  prntIt();
}

void loop() {
  // if there's any serial available, read it:
  while (Serial.available() > 0) {

    // look for the next valid integer in the incoming serial stream:
    int pos = Serial.parseInt();
    pos = constrain(pos, 0, 2400);
    servo.write(pos);
    prntIt();
  }
}
void prntIt()
{
  Serial.print("  degrees = "); 
  Serial.print(servo.read());
  Serial.print("\t");
  Serial.print("microseconds =  ");
  Serial.println(servo.readMicroseconds());
}

Thanks @JCA34F, that looks very helpful; will try it this morning.

"...or if you type a number greater than 180 it will be interpreted as microseconds(544 to 2400)..."

That alone will explain some of the puzzling behaviour I've been getting! Where can I find that documented? There may be other gems like it.


Anyone re my Q4? Is that how the IDE is designed, or is it a setting I've missed?

Where can I find that documented?

In Servo.cpp

void Servo::write(int value)
{
  if(value < MIN_PULSE_WIDTH)
  {  // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
    if(value < 0) value = 0;
    if(value > 180) value = 180;
    value = map(value, 0, 180, SERVO_MIN(),  SERVO_MAX());
  }
  this->writeMicroseconds(value);
}

I do not know of any setting that allows you to change the font of block comments, unlike the font of the IDE editor as a whole and the behaviour of the editor when using Auto Format, for example

Thanks UKHeliBob.

I'm puzzled why code blocks get that distinctive faint (less important?) font! I'll just break up lines with '//' then.

BTW, did you imply that Auto Format normalises it? If so, not here.

did you imply that Auto Format normalises it?

No. I was trying to explain that the font size and type of the editor as a whole can be changed and that some of the actions of Auto Format can be changed

Terrypin:
I'm using the sketch below to experiment with the 'SG90 micro servo' that came with my starter kit.

Q1: With the values shown I would expect to get a 180 degree sweep in both directions, but I estimate it as about 150. Trying other values, it always falls short by20-30 degrees. Is that normal?

Metal geared servos, costing more, are a bit better with their range. Still, I test out the range limit of individual servos to get an idea of the number to use. I use 20Kg metal geared servos.

Terrypin:
Q2: Is there a sketch available anywhere that uses a function, with parameters (such as start and finish angle, and duration)? That might be neater and easier to use for testing.

After initialization of the servo, I send them to their start positions and go from there.

Terrypin:
Q3: For the project I have in mind I will probably need a servo with greater torque but still compact in size. Any recommendations before I start googling please?

Metal geared servo.

I have found that the plastic geared servo lasts about 1 week under continuous operation. The gears wear down and the torquing becomes loose.

Terrypin:
Any other practical advice would be appreciated please.

In torquing servos using the Arduino servo library Servo - Arduino Reference, which works pretty good, start off with using writeMicroseconds().

Using a single degree is less granularity then using a uS values which can be a partial degree. There is, basically, a uS range of 500 to 2500, 0 degrees to 180, or 200uS per 180 degrees. That's 11.11uS per degree, giving you the ability to torque partial degrees.

Pay attention to the options of attach(). If the servo is limited to 90 degrees, you still get 2000uS of torque range but now the granularity is 22.22uS per degree.

If you use an ESP32 there is an ESP32 servo library or you can use, directly, a ESP PWM library. I use the PWM library esp32-hal-ledc.h, which gives a granularity of ~1/3 of a uSec, way more then what a hobby servo can respond.

Power the servo's with their own regulator. Servo's don't work well through breadboards. I have found that I can get away with a single 7805 for 3 servos in continuous operation.

You should write calibration routines.

One routine to find the individual servo's min max sweep range. Driving servos beyond their physical range can damage the servos.

One routine to find level. Such as I use my servos from a 90 degree position, 1500uS. The servos (X/Y) move +/-90 degrees in response to platform movement. The round shoe horn attaches to the servo at either, up to, +15 or -25 uSeconds off from 90 degrees. Thus 90 degrees may actually be const int iLIDAR_Posit90 = 1475;.

Thanks Idahowalker, much appreciate that detailed advice. Lot to get my head around though. For example the penny hasn't yet quite dropped about the relationships between microsecs, degrees and torque. Will study and experiment.

Right now I'm trying the test code from @JCA34F up-thread. But I still haven't grasped the exact steps to achieve what I would have thought were obvious objectives for any servo.

  1. Make it swing through a specific number of degrees, D.

  2. Over a user-settable time period T (effectively its 'speed'.)

  3. Ensure that the rotor arm start position (when the sketch is first loaded), is at a given location. (e.g. the arm must be at right angles to the base, or whatever.)

In all the above, I assume that the maximum possible torque for that particular servo would always be required, whether or not it's necessary for a particular application. Unless of course it has to be compromised to achieve D and T.

By way of background: I'm considering a servo (with appropriate improvised mechanics) as an alternative to a solenoid for pressing a rather firm button. So D will be very small, maybe 10 degs, and T say 250 mS. In contrast, the only solenoid I have that can reliably press the button, is a large and heavy 24V type and delivers its press alarmingly fast, maybe 50 ms.

Terrypin:
Right now I'm trying the test code from @JCA34F up-thread. But I still haven't grasped the exact steps to achieve what I would have thought were obvious objectives for any servo.

  1. Make it swing through a specific number of degrees, D.

  2. Over a user-settable time period T (effectively its 'speed'.)

  3. Ensure that the rotor arm start position (when the sketch is first loaded), is at a given location. (e.g. the arm must be at right angles to the base, or whatever.)

Unfortunately that's not how a normal hobby-type servo works at all. It only has a simple positional feedback mechanism. And that's the type of servo we're dealing with round here at least 99% of the time

A command pulse (like writeMicroseconds(1200)) sends it to a specific position. How many degrees it travels depends on where it starts from. There is no direct way to tell it to move a specific number of degrees from wherever it just happens to be.

There is also no speed control. The servo always moves at full speed. To get it to appear to move slower you simply move it in increments of travel with delays between moves (see the Sweep IDE example).

The initial position you can arrange by sending a write() or writeMicroseconds() command before you attach the servo. But obviously if the servo is not physically positioned in the correct place then it will/must jump round to where you want it to be.

Steve

you can just do a little math to get the percentage you are between the start and finish times and apply it to the angle. here is a simple function that works.

#include <Servo.h>



Servo myservo;
unsigned long tick,stamp;
long tot,dif;
int start,finish;

void setup() {
Serial.begin(9600);
 myservo.attach(9);

// start angle , finish angle , time in miliseconds to get there
MoveTo(myservo.read(),90,5000);

}


void loop() {

  if(millis()-tick>10){ tick=millis();
  dif = tick-stamp;
  if(dif>0&&dif<tot){
  float percent = float(dif)/float(tot);
 
  dif = finish-start;
  dif = int(dif*percent)+start;
  Serial.println(dif);
  myservo.write(dif);
  
  }else{ 
    //reached finish
    myservo.write(finish);
    Serial.println(finish); }
  }
}

   void MoveTo(int from, int to,unsigned long tim ){
    tot = tim;
    start = from;
    finish = to;
    stamp = millis();
 

}

I just got a 6221MG servo last week. it was around 20 bucks. Its great. its around four times as strong as the little cheap ones and about twice as big. I am very happy with it.

Thanks both, I'll experiment along those lines.


It's straying OT, but given the relative complexity of servo control (to me anyway) I'm beginning to wish I could use my solenoid instead, if only I could slow it down!

Terrypin:
Anyone re my Q4? Is that how the IDE is designed, or is it a setting I've missed?

About Q4:
Information about visual appearance is stored in file "theme.txt".
You can find theme.txt here (depending on you operating system):
Windows: <arduino_program_folder>\lib\theme\theme.txt
Linux: <arduino_program_folder>/lib/theme/theme.txt
Mac: /Applications/Arduino.app/Contents/Java/lib/theme/theme.txt

To change comments appearance there are two lines that can be changed almost at the end of theme.txt

# TEXT - COMMENTS
editor.comment1.style = #434F54,plain
editor.comment2.style = #95a5a6,plain

comment1.style is for comments starting with //
comment2.style is for comments between /* */
The numbers after # are values for red green and blue (in hex format): #rrggbb
You may change the colour to your needs.
Example: If you change these lines like this ...

# TEXT - COMMENTS
editor.comment1.style = #008800,plain
editor.comment2.style = #ff0000,plain

... comments will look like this:
change_editor_colors_comments.png

change_editor_colors_comments.png

@ uxomm :
TNX for that, K++

@uxomm:

Many thanks, that was really bugging me! Now have consistent appearance for both comment types.

Thank you for feedack.
Good to hear, that the information did help.

I'm making progress thanks to help here and in allaboutcircuits.com but still struggling to achieve my objective. I want the servo to move a small angle and stop for a specified time.

I've tried many variations of values in Sweep.ino that comes with the Arduino library but still not close to success. All attempts result in the arm moving through an angle (which I can change) at a certain speed (which I can change), but then returning almost immediately.

I plan to use the servo to control a firmish button. The first step in a sequence of presses is to hold it down for 3s.

Sweep is designed to continually sweep back and forth. If you just want it to move one way remove the second for loop e.g.

void loop() {
  for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
    // in steps of 1 degree
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }
delay(2000)   // 
}

Steve

Thanks @slipstick. That 'duh...' moment came as I dozed off last night!

I'd be very grateful if you and/or others could check the code I duly wrote this morning. Particularly my comments explaining to myself what I'm doing. It's doing what I want but I'm keen to understand the why and how.

/* This is my edit of the original Sweep sketch.
  To achieve:
  1. A small rotation of about 10 deg,
  2. A delay of 3 s before reversing.
  
  In the planned project this will press a rather firm button for 3 s before releasing it, the first step of my project.

  Comments from the original author(s) have added quotation marks to distinguish them from mine.
*/

#include <Servo.h> // Uses code from the Arduino library

Servo myservo;  // "Create servo object to control a servo"
// (Apparently defines my particular servo as an 'object'.)

int pos = 0;    /* Define variable for the servo position and initially arbitrarily set it to zero. */

const int triggerPin = 7; /*Define variable for pin that will be used as the low-going trigger. Ultimately that will come from simple external electronics*/

void setup()
{
  Serial.begin(9600); /* Maybe I'll need this for testing and learning about the Serial Monitor. */

  myservo.attach(9);  // "attaches the servo on pin 9 to the servo object"
  pinMode(triggerPin, INPUT_PULLUP); /* Avoids need for a resistor.  It's therefore normally High. When    it's taken low it allows the code within the loop to run.*/

  myservo.write(pos); // "servo rotates to pos"
}

// Servo arm is now at right angles to the front edge of my desk.

void loop()
{

  if (digitalRead(triggerPin) == LOW)
  {
    // A momentary LOW triggers the following:
    // Power up by pressing CUBE button for 3 s, say 3100 ms
    // IOW moving the servo a small angle and holding it there for 3s.

    for (pos =  0; pos <= 10; pos += 1)
    { // "goes from 0 degrees to pos degrees in steps of 1 degree"
      myservo.write(pos); // "servo rotates to pos"

      delay(20); // "time for servo to reach the position"
 /*That comment (also repeated later) confused me for
        quite a while. From further study I understand that the
        delay of 40 ms is the 'pulse width' time. So it's one of
        THREE factors determining the TOTAL duration of the movement.
        (Or the speed of that movement.) Another is obviously the
        angle. And a third must be the increment within the FOR
        function. I suspect they are probably linearly proprtional, so
        Duration =
        (Pulse width) x [(End position)-(Start position)]/Increment
      */
 
    }

    // Wait for 3 s before reversing
    delay(3000);

    for (pos = 10; pos >= 0; pos -= 1)
    { // returns from pos degrees to 0 degrees
      myservo.write(pos);           // "servo returns to 0"
      delay(20);                        // "time for servo to reach the position"
    }

  }

}

/* At this point in the eventual project the button will therefore have been pressed for 3s and the servo arm will be back at its original position.

  As the trigger was only LOW very briefly, it is HIGH again by this time. So further looping is now disabled until later code allows it. 
  (BTW, I see that with a '// Comment' the word 'time' gets formatted red. */