Skip to main content

I am using servo motors with an arduino nano clone to operate two turnouts.  I cannot get the servos to work.  They go to an initial position, but the buttons will not operate the servos as they should.  Each switch has two LEDs one of which should be lit for position indication.  One of the LEDs stays lit and the other goes on when one button is pressed and off when the other is pressed.  The buttons are on analog inputs used as digital.  I have 10K resistors on these input pins to prevent spurious HIGH signals.  I have not yet determined exactly what  servo positions are needed, just trying to get it working.  Is there a problem with my code, or do I have a hardware/wiring problem?  Code is below - Thanks!

#include <Servo.h>

// constant variables used to set servo angles, in degrees
const int straight1 = 90;
const int divergent1 = 110;
const int straight2 = 90;
const int divergent2 = 110;

// constant variables holding the ids of the pins we are using
const int divergent_led1 = 6;
const int straight_led1 = 7;
const int buttonpin1 = A1;
const int buttonpin1a = A3;
const int servopin1 = 8;
const int divergent_led2 = 9;
const int straight_led2 = 10;
const int buttonpin2 = A2;
const int buttonpin2a = A4;
const int servopin2 = 11;

// create a servo object
Servo Myservo1;  
Servo Myservo2;

void setup()
{
  // set the mode for the digital pins in use
  pinMode(buttonpin1, INPUT);
  pinMode(buttonpin1a, INPUT);
  pinMode(straight_led1, OUTPUT);
  pinMode(divergent_led1, OUTPUT);
 pinMode(buttonpin2, INPUT);
 pinMode(buttonpin2a, INPUT);
  pinMode(straight_led2, OUTPUT);
  pinMode(divergent_led2, OUTPUT);

  // setup the servo
  Myservo1.attach(servopin1);  // attach to the servo on pin 8
  Myservo2.attach(servopin2);  // attach to the servo on pin 11
  Myservo1.write(divergent1); // set the initial servo position
  Myservo2.write(divergent2); // set the initial servo position

  // set initial led states
   digitalWrite(straight_led1, LOW);
   digitalWrite(divergent_led1, HIGH);
   digitalWrite(straight_led2, LOW);
   digitalWrite(divergent_led2, HIGH);
}

void loop()
{
 
    if (digitalRead(buttonpin1) == HIGH){
    
          digitalWrite(straight_led1, LOW);
          digitalWrite(divergent_led1, HIGH);
          Myservo1.write(divergent1);
          }

  if(digitalRead(buttonpin1a) == HIGH){
    
          digitalWrite(straight_led1, HIGH);
          digitalWrite(divergent_led1, LOW);
          Myservo1.write(straight1);
          }
    
  if(digitalRead(buttonpin2) == HIGH){
    
          digitalWrite(straight_led2, LOW);
          digitalWrite(divergent_led2, HIGH);
          Myservo2.write(divergent2);
          }

  if(digitalRead(buttonpin2a) == HIGH){
    
          digitalWrite(straight_led2, HIGH);
          digitalWrite(divergent_led2, LOW);
          Myservo2.write(straight2);
          }       
        
 
 }// end of loop

Original Post

Replies sorted oldest to newest

Just as a general idea, I've had good luck seeing button presses by bringing an input line *low*, rather than high.  So set your input pins using INPUT_PULLUP i.e. "pinMode(buttonpin2, INPUT_PULLUP);"

This places a built-in pullup resistor on the pin, so it will normally read HIGH.  Then simply use a pushbutton to ground that pin and it will read LOW.  No external pullup or pulldown resistors needed.  This is a more conventional way to read button presses.

You could throw a little debug code in there to get a better sense of what code is executing, using Serial.println commands to the serial monitor.  Maybe you'll find that everything is trying to execute, or something else unexpected.  Well, whatever it is, it will be unexpected ;-)

There are a couple problems.

First, switches bounce. Your code does not take this into account. And so you could detect a button to be pressed and make the changes that fall under that if statement; mostly just redundant writes to the LEDs and another write request to the servo to move to the same position as before.

Second, the servo.write function does not wait for the servo to complete its move before the program continues. And so your loop starts over and sees another button press before the servo can get to where it needs to go. The LEDs should change as soon as the first button press is detected and the servo should eventually get to the new position once the button quits bouncing.

Despite these problems, it should still work anyway. You could try a 5 second delay after the servo.write to observe if anything changes.

I feel a little stupid - I found a wiring problem on both of the switches.  I think they will work when I have the servo positions set correctly.  Thanks for the switch bounce info.  I hold the button until the servo completes it's move, so not problems in this instance, but I see where it could be a problem.  I will get the servo positions tweaked and see if it all works.

If the LEDs aren't changing it's probably an issue reading your pushbuttons.  I'd start by (1) inserting a debug statement (like Serial.println ("Button X pressed")) after your if statements, and (2) wiring the buttons as active-low and using the internal pull-ups, as Randy suggests.

As Leo points out due to switch bounce your code will execute multiple times for each button press, but it should still work since you don't take any action on the switch being released.

Last edited by Professor Chaos

Do you have 4 push button switches, each momentary contact, single pole?

Do you have two push button switches that are on off - single pole, double throw.

Some other push button type/combination?

Need to know as part of analyzing the logic of your code.

BTW - If memory serves me, a mechanical switch will bounce for about 50 milliseconds or so. A code switch debounce can be created, but may not be necessary based upon the type and how many switches you used.

 

I have 4 push button switches, single pole, momentary contact.  The program is working fine now after fixing the wiring problems.  The only problem I have is spurious switch operation (in either direction) occasionally when I operate my other switches that have LGB switch machines.  The arduino is powered a 5v computer power supply.  The LGB switch machines are powered by 12v from the same computer power supply - the ground is common.  Any ideas on how to prevent the spurious operation?  I prefer not to have a dedicated power supply for the arduino if possible.

Is the spurious operation back to "reset" switch positions, or random switch position changes? Would like to know if further analysis is needed.

In either case, adding some capacitors may help. I am a conservative electrical guy, so I may be suggesting more than you need. I would use at least 100uf, 25Volts (also know as WVDC - Working Volts DC). One cap across the +12V, another across the +5. These caps will be polarized, so make sure you connect + of cap to +12 or +5, and - of cap to ground. You seem to be getting enough of a current spike on the +12V that "bleeds" into the +5V within the computer power supply.

BTW - noticed you used HIGH (+5) as the active push button position. Did you consider that if you used LOW (Ground) as the active switch position, you could implement derailing feature using isolated rail segment as derail trigger? If Arduino you use has 4 more inputs, could use these instead to implement derail. Anyways, my email address is in my profile if you want to discuss off the forum.

 

The spurious operation is both states and mimics a button push.  That makes me think a spike causes the inputs to go high.  My bleed resistors are around 10K, a lower value might make a difference, but probably not worth the effort.  Doesn't the computer power supply have large filter capacitors across the outputs?  I have plenty of capacitors around, so I could add some if needed.  Maybe changing my button inputs to low would help in more ways than one - if the spike makes my inputs high, it won't matter if that is the normal level.  Thanks for the idea about the derail - if nothing else works, that will.

It would appear that you could try a software debounce of the switch. Something like (logically speaking)

If Read(pushbutton) is high then

    delay 200milliseconds

    If Read(pushbutton) is high then

        throw the switch

End

This should filter out any spike shorter than 200milliseconds. Key is that you reRead the pushbutton state in each If.

I would create a constant for the delay time value (200milliseconds in above example), so if I need to increase the time, I just need to change the constant's value.

    

I am not familiar with servo motor driver - I assume you have some type of servo motor driver module that the Arduino activates. Can you tell us the servo motor driver module you are using, and the servo motors you are using. When I know what you are using, I will take some time and dig into the specs of the driver module and the servo motor, and then maybe I will be able to make some recommendations.

I assume the servo motor driver module and the servo motors use +5VDC.

If you had capacitors on hand, you could try putting a cap in as close to the servo motor driver module as possible. You can try TVS diodes if you have them on hand, but I don't know if the power disturbance the LGB switch machines are of sufficient amplitude for the TVS diode to dampen them.

I am not using a servo driver, just a pin out from the arduino.  The servos are the typical inexpensive ones (usually with blue plastic cases) sold on Ebay.  The servos are 5v.  The strange thing is that the servos are affected even when the arduino is unplugged from the socket, leaving the servo connected only to the 5v power supply.  Maybe a pulse is induced in the signal line going to the servo.  Tomorrow I will try putting a filter capacitor in near each servo. 

A few things:

1. Those servos, while inexpensive, also come in with a rather high defective rate.  I keep a sketch handy that I know works, where I can quickly wire up the servos to ensure they are working before using them in a new prototype circuit.  I wasted about 2 hours of my life one night because I didnt validate the servo was good before putting it in a newly designed circuit. 

2. It does sound like you are doing this, but ensure you are using the power supply's 5v output, and not the +5 volt pin on the arduino.  Although it will work, the on board 5 volt regulator of the arduino really cant be counted on to deliver the power needed for a servo. 

3. You will get completely unexpected results on the servo if there is no PWM signal coming into it.  

4. As mentioned by another poster, use a digital pin for your switch, and set it for INPUT_PULLUP.  Attach the switch to this pin and to ground.  INPUT_PULLUP will attach the pin internally to 5volts thru a 15K resistor, and you will be detecting a switch being pressed when the pin == LOW. This method is bulletproof and doesnt require outboard discreet components, which is really nice.

5. For debouncing, the delay can work, but can still be dirty due to the nature of the switches contacts. And the microcontroller cant do anything else during that time.  What I do is when I detect the switch being pressed (pin goes low) to set the state as "pressed", which performs the function that needs to occur when the pin state goes from HIGH to LOW. ( ie, move the servo to the new position, set the led lights, etc) and then capture the current running timer value into a variable using millis().  On each subsequent cycle, if the button is detected as being pressed, I refresh the millis() value into that variable.   If the button is released (pin goes HIGH) then subtract the current millis() from the saved millis() value from the variable to see if it is greater than 250. ( a quarter second). If its not, it does nothing and simply cycles through again.  Once the pin is high, and the amount of time between the current millis() and saved millis() is greater than 250, call the function to perform the steps needed for  the switch release. 

 

Did some homework and I think there is a basic flaw in your program that may have some effect. Still think there is some noise issue from the LGB switch machines, but the change in your program may overcome this.

The servo motor must me constantly sent a pulse to keep it held in your desired position. A pulse must be sent in every 20 millisecond window. The longest pulse for your servo motor is 2 milliseconds, and since you have 2 servo motors, that's 4 milliseconds. Give your code some overhead time, say 2 milliseconds, and an engineering insurance to make 20 millisecond window of 1 millisecond, and we end up with a delay we need of 20 - 4 - 2 - 1 = 13 milliseconds.

You need two new variables, SW1_Pos and SW2_Pos (for switch position) and initialize these to your program's "reset" switch positionso when program starts looping, servos will be written with these reset positions until a PB is pushed to change a switch, as you will see below.

So the logic of your code has to be - every loop through program servos get updated - with prior position if no PB is pushed, or a new position is a PB is pushed. Give this a shot and let me know.

If PB1 is high, SW1_Pos = divergent1, your LED control code

If PB1A is high, SW1_Pos = straight1, your LED control code

If PB2 is high, SW2_Pos = divergent2, your LED control code

If PB2A is high, SW2_Pos = straight2, your LED control code

Myservo1.write(SW1-Pos)

Myservo2.write(SW2-Pos)

Delay 13 milliseconds

Last edited by MED

Add Reply

Post

OGR Publishing, Inc., 1310 Eastside Centre Ct, Ste 6, Mountain Home, AR 72653
800-980-OGRR (6477)
www.ogaugerr.com

×
×
×
×
Link copied to your clipboard.
×
×