Notifications
Clear all

Arduino and servo control  

Page 1 / 2

Spyder
(@spyder)
Prominent Member
Joined: 2 years ago
Posts: 884
Topic starter  

 This should have been easy (I thought)

The goal is to get 2 servos (smraza S52, which is the equivalent of MG90S I think) mounted back to back, running almost full sweep, in opposite directions, at the same time at the same speed, (which would have the end result of them moving in the same directions since they're mounted back to back) triggered by a pbno, controlled by an Arduino Nano

I started with the sweep code from Bill's Arduino and servo control lesson then added a second pin for the second servo, and quickly realized that was stupid cuz the code only moved one servo at a time, so I figured, why not just reverse the polarity on one of them, and tie both signal wires to the same output pin, which is when things got confusing, and I had barely even started

With the signal wires from both servos tied to the same pin on the breadboard, the servos still moved independently, first one, then the other. So I commented out the second pin in the code, and re-uploaded it, but still, they only move one at a time

(The servoeaser is supposed to stop them from slamming at the end of the sweep. I added it but didn't get round to implementing it yet, so it's currently commented out and irrelevant)

This should have been the easier part of the project than actually mounting the things and getting the hinges set correctly

Can anybody help me out, 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>
// #include "ServoEaser.h" Servo servo1;  // create servo object to control a servo // Servo servo2;  // create servo object to control a servo // two servo objects and a push button will be used int pos = 0;    // variable to store the servo position void setup() {  servo1.attach(9);  // attaches the servo on pin 9 to the servo object //  servo2.attach(10);  // attaches the servo on pin 10 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    servo1.write(pos);              // tell servo to go to position in variable 'pos' //    servo2.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    servo1.write(pos);              // tell servo to go to position in variable 'pos' //    servo2.write(pos);              // tell servo to go to position in variable 'pos'    delay(15);                       // waits 15ms for the servo to reach the position  } }

 


Quote
DroneBot Workshop
(@dronebot-workshop)
Workshop Guru Admin
Joined: 2 years ago
Posts: 911
 
Posted by: @spyder

why not just reverse the polarity on one of them

I certainly hope you don't mean reverse positive and negative in them, that would likely kill the servo controller embedded in the motor!

Your code sounds like it is doing exactly what you asked it to do, as you use two for-loops. It has to run through the first one before it can run through the second one. So the servos move independently.

What you need to do is realize the relationship between the two servos. When servo1 is at 180 degrees, servo2 needs to be at 0 degrees. When servo1 is at 179 degrees then servo2 needs to be at 1 degree. And so on and so on.

The relationship, mathematically, seems to be servo2 = (servo1 - 180) * -1, which does give a "negative zero" at one extreme but otherwise seems valid. 

Add another variable in the for-loop that is the result of that calculation. Then drive the second servo using that variable in the same for-loop, you only would need one.  The "negative zero" condition could be fixed with an if statement to detect it and produce a zero.

And, of course, you would only need one for-loop.

Hope that makes some sense!

😎

Bill

 

 

This post was modified 2 months ago by DroneBot Workshop

"Never trust a computer you can’t throw out a window." — Steve Wozniak


Aswin and Spyder liked
ReplyQuote
Spyder
(@spyder)
Prominent Member
Joined: 2 years ago
Posts: 884
Topic starter  
Posted by: @dronebot-workshop

I certainly hope you don't mean reverse positive and negative in them, that would likely kill the servo controller embedded in the motor!

Thanks Bill,

Yea, that was gonna be what I would have done, had things worked better

I understand the intent behind your math, but I was only using the sweep script as a test, see, I don't really want them to sweep in the way the script is written, I want them to sorta "snap" from one almost full position (which I will have to determine after I mount them) to the opposite almost full position. My estimate is that they'll probably sweep about 140, maybe from 20 to 160 (and reverse) or thereabouts, probably less actually

It's for this thing

I won't know until I get them mounted exactly how much they need to swing, and I don't want them going slow, more like a snapish kind of action, and I'll need to mount the servos before I even figure out how long to make the arms, and, of course, where to mount them so the faceplate clears and exactly how much they need to swing

Like most of my other projects, I'm designing it as I go with a clear starting point and ending point, with the middle being kinda vague


ReplyQuote
frogandtoad
(@frogandtoad)
Honorable Member
Joined: 2 years ago
Posts: 744
 

@spyder

Posted by: @spyder

I won't know until I get them mounted exactly how much they need to swing, and I don't want them going slow, more like a snapish kind of action, and I'll need to mount the servos before I even figure out how long to make the arms, and, of course, where to mount them so the faceplate clears and exactly how much they need to swing

I could be wrong, but I'm not sure if that servoeaser will actually work as you might expect, but like I said... I could be wrong, having never used it 🙂

If you're looking for a snapping action, I'n not sure why you're even looking at sweeping values, why not just go there immediately?

How about something like this, where you can fine tune the angles for a softer stop - Maybe even glue a piece of foam/rubber in there to absorb any hard hit's and or noise?

-- NOT TESTED --

#include <Servo.h>

Servo leftEyeLid;
Servo rightEyeLid;
 
void setup() {
  leftEyeLid.attach(9);
  rightEyeLid.attach(10);
 }

void openEyeLids(int angle1, int angle2) {
  leftEyeLid.write(angle1);
  rightEyeLid.write(angle2);
 }

void closeEyeLids(int angle1, int angle2) {
  leftEyeLid.write(angle1);
  rightEyeLid.write(angle2);
 }

void loop()
 {
  openEyeLids(20, 140);
  delay(1000);

  closeEyeLids(140, 20);
  delay(1000);
 }

Cheers.


Spyder liked
ReplyQuote
Spyder
(@spyder)
Prominent Member
Joined: 2 years ago
Posts: 884
Topic starter  

@frogandtoad

That's perfect !

Thank you !

I was getting frustrated with it and was about to switch over to a raspi0W and pca9685 (that's what I used in a bunch of my robots, so I have at least a passing familiarity with it, as opposed to my almost non-existent experience with arduino), but that combination of boards, while I could probably make it work, would be really expensive in the real estate area inside the helmet, then you add the batteries, and it becomes a huge mess

So this code worked perfectly on the first try, then I tried to add the button, and it started stuttering

I'm thinking I need to trigger it, then detatch the servos so they quit stuttering, but I got confused on the loop. What this script was supposed to be trying to do is press the button, and whichever position the servos are in, go to the other position.

They're sorta doing that. They move from one position to the other when I press the button, but they stutter whenever I let go of the button

Failing that, I could just use 2 nanos, one for open and one for close, cuz a second nano takes very little real estate

I'm probably overcomplicating this

 

 

#include <Servo.h>

Servo leftEyeLid;
Servo rightEyeLid;
int button = 2;

boolean oldButtonState = LOW;
boolean newButtonState = LOW;
 
void setup() {
  leftEyeLid.attach(9);
  rightEyeLid.attach(10);
  pinMode(button, INPUT);
 }

void openEyeLids(int angle1, int angle2) {
  leftEyeLid.write(angle1);
  rightEyeLid.write(angle2);
 }

void closeEyeLids(int angle1, int angle2) {
  leftEyeLid.write(angle1);
  rightEyeLid.write(angle2);
 }

void loop()
 {
newButtonState = digitalRead(button);
if ( newButtonState != oldButtonState ) 
leftEyeLid.attach(9);
rightEyeLid.attach(10);
{
   // has the monkey pressed the button?
   if ( newButtonState == HIGH )
   {
       if (leftEyeLid.read() == 20) { leftEyeLid.write(140); rightEyeLid.write(20); } // if opened, then close
       else                    { leftEyeLid.write(20); rightEyeLid.write(140);} if closed, then open
   }
   oldButtonState = newButtonState;
// leftEyeLid.detach(); rightEyeLid.detach();
}   
}
  
//  openEyeLids(20, 140);
//  delay(1000);

//  closeEyeLids(140, 20);
//  delay(1000);
// }
// }


ReplyQuote
robotBuilder
(@robotbuilder)
Honorable Member
Joined: 2 years ago
Posts: 655
 

@spyder

Do the servos have their own power supply?
I don't think you would need to keep attaching the servo to a pin in the main loop?
With a button remember in a loop it will keep reading it until you release it.
Is the button HIGH or LOW when pressed?
You can use Serial.print() to debug code.

 


ReplyQuote
Spyder
(@spyder)
Prominent Member
Joined: 2 years ago
Posts: 884
Topic starter  
Posted by: @robotbuilder

Do the servos have their own power supply?

Yes. I've got 2 servos so I needed external power. I wanted to make sure they had enough to lift the mask. Besides, running servos off the same power as the nano didn't seem a great idea

Posted by: @robotbuilder

I don't think you would need to keep attaching the servo to a pin in the main loop?

I didn't need that line til I started trying to detatch them after they move, but detatching didn't work the way I expected

Posted by: @robotbuilder

With a button remember in a loop it will keep reading it until you release it.

Yea, odd thing about that. I press the button, they swing, I release the button, they stutter, then I press the button again, and they swing the other way like I wanted them to, but they stutter again when I release it, so pressing the button does swap positions, but it stutters when I let go. Seems like I'm almost there

Posted by: @robotbuilder

Is the button HIGH or LOW when pressed?

High when pressed, low when released... Or not now that I think about it. The button goes to ground, not to positive. Suddenly I'm not so sure

Posted by: @robotbuilder

You can use Serial.print() to debug code.

Would there be anything to print with no errors ?


ReplyQuote
robotBuilder
(@robotbuilder)
Honorable Member
Joined: 2 years ago
Posts: 655
 

What happens if you change,

if ( newButtonState == HIGH )

to

if ( newButtonState == LOW )



ReplyQuote
Spyder
(@spyder)
Prominent Member
Joined: 2 years ago
Posts: 884
Topic starter  

@robotbuilder

Almost the same results with the stuttering, with the exception that the button has far less effectiveness

Holding it down does nothing, and momentary press only changes the position about 10% of the time

I also changed

boolean oldButtonState = LOW;
boolean newButtonState = LOW;
 
to
 
boolean oldButtonState = HIGH;
boolean newButtonState = HIGH;
Which seemed logical in accordance with your proposed edit, due to leaving it in the original configuration had very close to 0% effect
 
The only time in the original configuration that the button had effect was when the stutter brought the horns to a stop at one end of the swing or the other, which didn't happen often, then it just went back to stuttering. Holding it down did nothing
 
I suppose that would have more effect if I tied the button (D2) back to positive, but I'm not sure of the consequences of introducing power directly back into D2
 
 
 
 

 


ReplyQuote
robotBuilder
(@robotbuilder)
Honorable Member
Joined: 2 years ago
Posts: 655
 

@spyder

Tomorrow I will wire up a couple of servo motors and a button and see if I can figure it out unless of course you or someone else beats me to it 🙂

 


ReplyQuote
frogandtoad
(@frogandtoad)
Honorable Member
Joined: 2 years ago
Posts: 744
 

@spyder

Posted by: @spyder

That's perfect !

Thank you !

I was getting frustrated with it and was about to switch over to a raspi0W and pca9685 (that's what I used in a bunch of my robots, so I have at least a passing familiarity with it, as opposed to my almost non-existent experience with arduino), but that combination of boards, while I could probably make it work, would be really expensive in the real estate area inside the helmet, then you add the batteries, and it becomes a huge mess

So this code worked perfectly on the first try, then I tried to add the button, and it started stuttering

I'm thinking I need to trigger it, then detatch the servos so they quit stuttering, but I got confused on the loop. What this script was supposed to be trying to do is press the button, and whichever position the servos are in, go to the other position.

They're sorta doing that. They move from one position to the other when I press the button, but they stutter whenever I let go of the button

Failing that, I could just use 2 nanos, one for open and one for close, cuz a second nano takes very little real estate

I'm probably overcomplicating this

 [snip code]

You're welcome.

I'm almost certain that the jitter you're experiencing is because the the write calls are being constantly made, even if the eyelids are in position, every iteration of the loop.  You're probably also experiencing some button noise, so it's also a good idea to include a button debounce feature.

Here is some code I wrote a while back to do just that (one toggle at a time), so that unnecessary calls are not made:

#include <Servo.h>

#define button 8

Servo leftEyeLid;
Servo rightEyeLid;

int oldBtnState;
int newBtnState;
int toggleState = HIGH;

bool changeOccurred = false;

unsigned long prevMillis = 0;

void openEyeLids(int angle1, int angle2) {
  Serial.println("Opened");

  leftEyeLid.write(angle1);
  rightEyeLid.write(angle2);
 }

void closeEyeLids(int angle1, int angle2) {
  Serial.println("Closed");
  
  leftEyeLid.write(angle1);
  rightEyeLid.write(angle2);
 }

bool debounceButton(int presetDelay) {
  return millis() - prevMillis > presetDelay? true : false;
 }

void setup() {
  Serial.begin(9600);
  pinMode(button, INPUT_PULLUP);
 }

void loop()
 {
  newBtnState = digitalRead(button);

  if((newBtnState == HIGH && oldBtnState == LOW) && debounceButton(250)) {
     changeOccurred = true;
     toggleState = !toggleState;

     prevMillis = millis();
    } 
  else {
     changeOccurred = false;
    }

  // We only want to call our functionss if the state changed, otherwise
  // we will continue to call the functions even if the eyeLids are already
  // in the correct position... doing that can cause the jitter that we're
  // trying to avoid.
  if(changeOccurred == true) {
     if(toggleState == HIGH)
       openEyeLids(20, 140);
     else
       closeEyeLids(140, 20);
    }

  oldBtnState = newBtnState;
 }

Hopefully this should do the trick - All you have to worry about is fine tuning the angles to suit.

Cheers.


ReplyQuote
frogandtoad
(@frogandtoad)
Honorable Member
Joined: 2 years ago
Posts: 744
 

@robotbuilder

Posted by: @robotbuilder

@spyder

Tomorrow I will wire up a couple of servo motors and a button and see if I can figure it out unless of course you or someone else beats me to it 🙂

 

 Sorry, I already had an example, just needed to tweak it 😉


ReplyQuote
Spyder
(@spyder)
Prominent Member
Joined: 2 years ago
Posts: 884
Topic starter  

@frogandtoad

Okay, this one isn't causing any stuttering, or moving, but, the serial does show it switching from "open" to "closed" on button press, or, more accurately, when I release the button, the actual press did nothing. Also, one of the LEDs on the nano briefly flashes on release as well, an indicator I suppose

I originally tried it the way you wrote it, but the button is on D2 not D8, which I expected not to work, but I tried it anyway just in case arduino has pin naming schemes anything like the pi, but it seems that 8 means 8 and 2 means 2

Then I considered how "HIGH" was written in the code, and figured, if I burn out the nano, I've got a few left and tied the button to positive instead of negative, which did nothing. This must be why the serial only prints on release of the button, pressing makes LOW, and it goes HIGH on release, so HIGH must be the normal state

This must be very close tho, cuz it's doing enough to know open and closed are related to the button. I'm trying to add a serial print to the if/then to see if it's getting that far, but it doesn't like my formatting

I'm keeping a couple of the previous sketches open along with this one, because it seems like somewhere between them should be the answer. One makes them (sorta) move with the button, one prints correctly, and one swings perfectly


ReplyQuote
frogandtoad
(@frogandtoad)
Honorable Member
Joined: 2 years ago
Posts: 744
 

@spyder

Posted by: @spyder

Okay, this one isn't causing any stuttering, or moving, but, the serial does show it switching from "open" to "closed" on button press, or, more accurately, when I release the button, the actual press did nothing. Also, one of the LEDs on the nano briefly flashes on release as well, an indicator I suppose

I originally tried it the way you wrote it, but the button is on D2 not D8, which I expected not to work, but I tried it anyway just in case arduino has pin naming schemes anything like the pi, but it seems that 8 means 8 and 2 means 2

Then I considered how "HIGH" was written in the code, and figured, if I burn out the nano, I've got a few left and tied the button to positive instead of negative, which did nothing. This must be why the serial only prints on release of the button, pressing makes LOW, and it goes HIGH on release, so HIGH must be the normal state

Ok, it sounds like you have a floating pin.  Even though I used INPUT_PULLUP in pin mode, I also incorporated a 10k PULL_DOWN resistor on my tactile button.

Posted by: @spyder

This must be very close tho, cuz it's doing enough to know open and closed are related to the button. I'm trying to add a serial print to the if/then to see if it's getting that far, but it doesn't like my formatting

Yes, I think it must be the PULL_DOWN resistor, because I get no jitter at all, and works perfectly every time.

Try my full code again with the PULL_DOWN resistor in place, but first comment out the servo write statements in the functions, so just the print statements are working, so you can fine tune the debounce value first.

Cheers.


ReplyQuote
Spyder
(@spyder)
Prominent Member
Joined: 2 years ago
Posts: 884
Topic starter  

@frogandtoad

Ah, ok. I suppose I should've included the precise schematic in the first post. You couldn't have known I had made a very basic error in wiring.

So I just watched this video on floating pins

I've seen Bill use the "pullup" resistors, but I never understood why. Now I get it. The resistor gives it a reference point. Makes sense now that I've seen it explained

Using the 3rd code, with the 10k resistor tied to +5v, it still prints (opened/closed) PERFECTLY, but the servos don't move at all, not even a stutter. 

Using the 2nd code, with the resistor tied to +5v, the servos react MUCH better, but they still have a bit of a stutter when I press the button, but only when I press the button. When I let go, they end up at one end point or the other, but sometimes I have to press the button a few times, and they occasionally get slightly out of sync

I added the debounce to the 2nd code, and changing the time anywhere from 50 - 950 doesn't seem to make any difference

The 3rd code is printing perfectly, so I tried to add the servo movement to the print statement, but it didn't want to compile no matter how I worded it

The addition of the resistor was an absolute plus tho !

 This is the code that's working the best so far. As you can see, I had to change the original state of the button to HIGH due to the resistor being tied to +5v and then newButtonState to LOW, and that's where I added the debounce, which seemed logical, but could be completely wrong as it doesn't appear to change anything

 

#include <Servo.h>

Servo leftEyeLid;
Servo rightEyeLid;
int button = 2;

unsigned long prevMillis = 0;

boolean oldButtonState = HIGH;
boolean newButtonState = HIGH;

bool debounceButton(int presetDelay) {
  return millis() - prevMillis > presetDelay? true : false;
 }
 
void setup() {
  leftEyeLid.attach(9);
  rightEyeLid.attach(10);
  pinMode(button, INPUT);
 }

void openEyeLids(int angle1, int angle2) {
  leftEyeLid.write(angle1);
  rightEyeLid.write(angle2);
 }

void closeEyeLids(int angle1, int angle2) {
  leftEyeLid.write(angle1);
  rightEyeLid.write(angle2);
 }

void loop()
 {
newButtonState = digitalRead(button);
if ( newButtonState != oldButtonState ) 
leftEyeLid.attach(9);
rightEyeLid.attach(10);
{
   // has the monkey pressed the button ?
   if (( newButtonState == LOW ) && debounceButton(950))
   {
       if (leftEyeLid.read() == 20) { leftEyeLid.write(140); rightEyeLid.write(20); } // if opened, then close
       else                    { leftEyeLid.write(20); rightEyeLid.write(140);} // if closed, then open
   }
   oldButtonState = newButtonState;
// leftEyeLid.detach(); rightEyeLid.detach();
}   
}
  
//  openEyeLids(20, 140);
//  delay(1000);

//  closeEyeLids(140, 20);
//  delay(1000);
// }
// }

 


ReplyQuote
Page 1 / 2