Limit switch triggering too slowly / Actuator with max/min adjustment ...

25 Aug.,2025

 

Limit switch triggering too slowly / Actuator with max/min adjustment ...

Introduction:

If you are looking for more details, kindly visit TMAZTZ.

Hi all, I have been reading through endless amounts of related or similar projects to mine. While I did learn a lot, I am still having some issues with coding it seems. I have read through the “How to get the best out of this forum (short version)” and will try to follow it religiously to help avoid generating headaches for anyone. I am a beginner and will be grateful if I was treated as such. I am eager to learn and hope to become a productive and useful member of the forum one day.

Note: I know the guided stated NOT to use “Fritzing diagrams.” However, I think the wiring schematic is simple enough that it should not cause any confusion. Also, please recommend a good software replacement. I have attached the Fritzing diagram along with the LM393 IR speed sensor as the one I can for the Fritzing software only had a 4-pin variant. I am willing to learn, just let me know if anything that I wrote was confusing or can be further clarify to help you guys out.

Choose the right section of the Forum:

I am pretty sure my issue is regarding the code hence I use the “Programing Questions”

Hardware:

  • Arduino Uno (Rev.3) (x1)

  • 30 KG Digital Servo (x1)

  • Dual channel 5v relay module (x1)

  • DC to DC converter (x1)

  • 30 watt DC motor (x1)

  • LM393 IR speed sensor module (3-pin type) (x2)

  • 12V DC power supply (x1)

  • Push Button (2-pins) (x1)

Code problems:

Expected from code:

I am currently trying to build an actuator that can have its maximum and minimum stroke length adjusted by two limit switches located on the outside of the cylinder. The actuator will be powered by a dc motor which will then have its direction controlled by two relays depending on the state of the two limit switches and the push button (cycle button). A servo is also part of the assembly. One full “cycle” should look something like the following.

1: Cycle button pressed

2: Servo moves from 0* to 180* (short delay of 500ms)

3: Motor rotates CW to drive the actuator rod forwards

4: Front Limit switch is triggered

5: Motor STOPS

6: Servo Moves from 180* to 0* (short delay of 500ms)

7: Motor rotates CCW to drive the actuator rod backwards

8: Back Limit switch is triggered

9: Motor STOPS

10: Wait for Cycle button to be pressed

What actually happens:

The program actually works for the most part, however, it seems that at times the front limit sensor would trigger too slowly causing the triggering nub to smash right into the limit sensor. What is confusing to me is that it would work as coded for maybe 8 cycles and then it would suddenly just run past the sensor. The light on the sensor flickers on and off which shows that the sensor senses movement but does nothing or does not do it in time to prevent a collision. I would appreciate any kind of help; I am at my wits end. I am well versed in mechanical design and systems; programing has been quite a difficult process.

The following is the code

#include 
Servo servo;


//Variables

const int relay1 = 2;
const int relay2 = 3;
const int cycleButton = 4;
const int LMF = 6;
const int LMB = 7;
int m;


//PROGRAM FUNCTIONS

void motorCW() // function to make motor turn CW
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, HIGH);
  }

void motorCCW() // function to make motor turn CCW
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, LOW);
  }

void motorSTOP() // function to make motor stop
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
  }


void setup()

{
  pinMode(cycleButton, INPUT_PULLUP); // set pin 4 as INPUT_PULLUP for cycle button
  pinMode(LMF, INPUT); // set pin 6 as INPUT for Front IR Sensor Limit
  pinMode(LMB, INPUT); // set pin 7 as INPUT for Front IR Sensor Limit
  pinMode(relay1, OUTPUT); // set pin 2 as output for relay 1
  pinMode(relay2, OUTPUT); // set pin 3 as output for relay 2
  servo.attach(5); // attach Servo to pin 5
  digitalWrite(relay1, HIGH); // set relay 1 OFF State
  digitalWrite(relay2, HIGH); // set relay 2 OFF State
}


void loop(){  
  if (digitalRead(cycleButton) == LOW) {
    m = 0; 
  }
  if (digitalRead(LMF) == HIGH) {
    m = 1;
  }
  if (digitalRead(LMB) == HIGH) {
    m = 2;
  }
  if (digitalRead(cycleButton) == LOW && digitalRead(LMB) == HIGH) {
    m = 3;
  }
  switch(m) {
    case 0: {
      servo.write(180);
      delay(500);
      motorCW();
      break;
    }

// Ideally for case 1 would be 1: motorSTOP 2: Servo to 0 3: Delay (500) to give time for servo 4: motor CCW; this does not work however. The moment I use motorSTOP it will not start again

    case 1: {
      motorCCW();
      servo.write(0);
      break;
    }
    case 2: {
      motorSTOP();
      break;
    }

// Case 3 is here because the Cycle button wont work once it has run its first cycle as LMB will be triggered HIGH and hence it will trigger function motorSTOP()

    case 3: {
      servo.write(180);
      motorCW();
      break;
    }
  }
}

Hi Perry, it is an honor to have you reply personally and so quickly I apologize for the use of fritzing for the diagram. It is just that I am super new to this and fritzing seemed to be the simplest software to quickly get a visual reference to what was written. I will try to learn KiCAD right after this project has completed.

**"*In switch case 0 you have half a second where nothing happens, plenty of time for the motor to drive past the limit." From my understanding is that once the button is pressed the servo will require some time to actually move to its requested position, so while this movement is happening I added the delay. Once the servo has fully stopped at the requested position in this case 180, the motor should then move. I tried it without the delay also; same result, the nub still sometimes run passes the front limit switch. Also, it typically takes at least a few seconds for the nub to reach the Front Limit when driven from the Back Limit. So I do not see how this can be an issue.

I am not sure if my understanding is correct. Though I think that once the "cycle button" is pressed no other cases are true, hence the arduino must be consistently reading the "if statements" looking for a trigger to happen for it to then refer to a case #. If I am not mistaken then while the motor is in motion there should not be anything stopping the arduino for being prepared for a trigger, either from the front or back limits.

"There's plenty on this website about timing without delay, both in tutorials and replies to questions, including by me in the past few days. Do some searching." - You are referring to the use of the millis() function correct? I have honest to god did many searches (I have been at this for a little over one month); and am still left a little unsure of how to proceed forward from the point I am currently at. I have actually watched a lot of tutorials online also. However, I must say that the examples are rather vague or very specific to their example that it makes it hard to apply the code else where. Please also send me a link in regards to where I should start. The concepts are fairly simple, but the implementation is quite difficult to be honest, but I am trying.

"Otherwise nicely presented post."
Thanks I want the post to be useful for others who could potentially be interested in making a similar actuator in the future; and to learn from a very detailed post to answer any potential lingering questions they might have.

I eagerly wait for a reply, thanks for giving me hope Perry, much appreciated.

Hi gcjr

"once you enable the motor, in either direction, the code needs to constantly monitor the limit switches." - I read this somewhere and implemented it in a way that I understood (perhaps wrongly). This is what I understand from my own code. Once a specific Case # is met, the action under the Case # is preformed, and then after all the requested actions are preformed the arduino then "leaves" the Case # to go constantly read the "IF statements" above to until another Case # can be triggered. Therefore in my head I do not see how the trigger can be missed. as it will already be constantly reading the "IF statements" above.

"when you reposition the servo and give it time to move, the motor should probably be stopped unless it is guaranteed not to hit the limit switch during that delay." - Yes this is correct. Notice Case 0 where I have written a specific order of events, so in my mind it should work something like this. Servo is told to move from 0* - 180*. While this is happening the next command is to wait for half a second which from visual inspection of the movement is enough for the Servo to position itself. Then the function "motorCW()" is called for to move the motor forward. Once the motor is in motion then the arduino breaks out of Case 1. Then it should theoretically go on the monitor the "IF statements" above as neither LMF (Front Limit) or LMB (Back Limit) is HIGH, "Cycle button" would have already been let go of so it would read HIGH (due to INPUT_PULLUP). So Cases 0, 1, 2 will not activate. For Case 3 to happen the nub would have to have already completed the cycle and stop at LMB and have "Cycle button" be pressed to work. I added this due to the fact that initially once the cycle completed it would just not move again due to Case 2 being constantly True.

"in case 1, when it hits the LMF, the motor should be stopped, the servo can be commanded to a new position, there can be a delay and finally the motor enabled for CCW." - I tried implementing this exactly like Case 0. However, Once I call for the function "motorSTOP()" the arduino stops the motor and does nothing else. It does not follow any command after than point. Which is very confusing as from my understanding it should not simply stop and do nothing. For example.

Case 1: {
motorSTOP();
servo.write(0);
delay(500);
motorCCW();
break;
}

This would be ideal, but somehow it does not work. It just stops and that's it. So I had to simply make due with the servo trying to move to its requested position as the motor suddenly changes its direction.

"shouldn't cases 1 and 3 be the same? presumably the need for case 0 is to start the device when the motor in between limit switches." - This is correct. Case 0 is to start the device between the limits. However, a strange anomaly where the moment I power the arduino it always run Case 0 right away. A fix would be nice lol.

"each iteration thru loop reinvokes the switch machine so after invoking a switch case, it will be repeated. while the servo should not do anything different, the delay will prevent checking the limit switch" - This I understand, hence I have tried only to implement the short delay while the motor has stopped to give the servo time to rotate. So therefore the delay time would have already been up before the motor nub was able to trigger LMB anyways.

"you've implemented a state machine the repeats some action in each case. a more convention state machine would be to look for an event, in you case, look for a cycle button or limit switch to become active. when they do, perform some action (i.e. move servo, enable motor) and then change state to look for a new event. in your case, the code expects a specific event but could look for multiple events." - I understand what you are saying, though I still do not understand why sometimes the sensor triggers too slowly even without any delay functions at all I have tried it. As mentioned in my previous reply to Perry, the code works; however, sometimes it runs pass the limit and smashes into the other side and does not switch to another Case #. For example, imagine the motor already in motion going forward and the servo has already positioned itself. Now as no "IF statements" are true the arduino should constantly cycle through all the "IFs," therefore it should already be constantly checking the LMF (Front Limit) while actually checking all the other limits also. Therefore LMF should trigger HIGH and start Case 1 right away. Which it does to an extend and then it fails at random almost as if the nub went by too quickly for sensor to catch it. Would reading 1 specific limit help speed things up? Though I doubt that its the issue as the arduino should be quick enough to read all "IF statements" in time. Maybe the forum would benefit from a video reference lol.

Want more information on What Triggers a Limit Switch?? Feel free to contact us.

Thanks for your reply gcjr.

Reply to gcjr,

IT WORKS!!! (sorry for shouting ), I don't know how to thank you enough gcjr. You have really saved me a another few weeks of headache. I will go over your code until everything clicks in my brain. The way you restructured the code also made it so much clearer; I will try to replicate your format for my next project(s). On that note, I have another question; well three actually.

Question #1

I noticed that you included all sequence into one single function (why didn't I think of doing this ). This is practically done to help make the code cleaner to read and to make it run only once correct? Another thing I just learned is that you could call for a function inside of a function (function-ception lol)

void motorCW () // function to make motor turn CW
{
    motorSTOP ();
    servo.write (180);
    delay (500);
    digitalWrite (relay1, LOW);
    digitalWrite (relay2, HIGH);
}

void motorCCW () // function to make motor turn CCW
{
    motorSTOP ();
    servo.write (0);
    delay (500);
    digitalWrite (relay1, HIGH);
    digitalWrite (relay2, LOW);
}

Question #2

I actually saw a lot of "enum" while I was on my learning quest. I know I should not post links but (Arduino Playground - Enum Resource) I read this and was really confused. what exactly does this "enum" do? I noticed that after the "#else" statement the "enum" logic is inverted (side note: why is there a # in front of else?). Also I noticed that you reassigned the pins for example LMF and LMB to A2 and A3; are these not analog pins on the arduino? what is the purpose of this? I see LM_ON being used but not LM_OFF. I am so very sorry if I come off as rambling non-sense, but I really have no idea what I am reading. To me it seems like you have assigned 2 sets of peripheral for the arduino. for example.

const int relay1 = 10; //relay1 set A

const int relay2 = 11; //relay2 set A

and
const in relay1 = 2; // relay1 set B

const int relay2 = 3; //relay2 set B

const int relay1 = 10;
const int relay2 = 11;
const int cycleButton = A1;
const int LMF = A2;
const int LMB = A3;

enum { LM_ON = LOW, LM_OFF = HIGH };

// -------------------------------------
#else
#include 
//Variables
const int relay1 = 2;
const int relay2 = 3;
const int cycleButton = 4;
const int LMF = 6;
const int LMB = 7;

enum { LM_ON = HIGH, LM_OFF = LOW };
#endif

Servo servo;

I would really appreciate an in-depth line by line explanation if that's not too much to ask :o . I am eager to learn, I just need the right hole to run down. I believe that this post will be ideal for anyone looking to truly understand some state machine programing.

From my understanding of your "void loop" is that you first integrated the "IF statements" directly into the Case # itself and the second difference is that after each case you have m = to the next state that the arduino should look for. While my original "void loop" basically said to constantly read all three "IF Statements" which sometimes would make the loop overshoot a critical triggering moment (lol triggering moment) of the limit switch. So to clarify. Once the "cycle button" is already pressed and the motor is already going forward; there is really no point in checking the "cycle button" or "LMB" as these will be impossible for the nub to trigger. Therefore by setting the state of m = 1 will have the loop constantly checking case #1 correct? Please let me know if my understanding is correct.

Question #3

If there were two LMF as oppose to one, and there is an equal chance for LMF1 and LMF2 to trigger and I want to implement something for this; would the following work?

    case 1: {
        if (digitalRead (LMF) == LM_ON || (digitalRead (LMF2) == LM_ON)) {
          motorCCW ();
          m = 2;
          Serial.println (" case 2");
        }
        break;
      }

Reply to wildbill
Hi wildbill, it is all starting to make sense to me now. Thank you so much. I think I understood enough of what was happening to arrive to your explanation also. Would you mind giving my questions a quick glance and confirm if my understanding is correct?

"Millis can come later - one mind bending problem at a time." - Would you have implemented millis() as oppose to a state machine? If so why? In which case(s) would one be a more powerful than the other? State machine logic just seems so much more conceptually natural. I guess millis() would also if I understood it .

"Please change the name of your global m variable. Single character names are so sixties " - LOL, to be honest, I was not even around yet during the sixties :D. However, I will keep this in mind as logically it makes sense to give global variables are more meaningful name.

To both wildbill and gcjr, both of you guys are super awesome, I learned so much in just a few post. Thanks again guys.

redneckcrake:
I will go over your code until everything clicks in my brain.

you should understand the code you're working with

redneckcrake:
what exactly does this "enum" do? I noticed that after the "#else" statement the "enum" logic is inverted (side note: why is there a # in front of else?). Also I noticed that you reassigned the pins for example LMF and LMB to A2 and A3; are these not analog pins on the arduino? what is the purpose of this? I see LM_ON being used but not LM_OFF.

The #if/#else/#endif are preprocessor commands the effectively comment out code.

i post code i've tested. i can do lot with a multifunction board having a few LEDs and buttons. the #if/#else allow me to redefine I/O pin and polarities (enum) for my hardware while maintaining your definitions. I tested the code with #if 1.

the enum is another way of defining constants that are closely related to one another and often sequential. i could have defined LM_ON using constant byte or #define

redneckcrake:
I am so very sorry if I come off as rambling non-sense, but I really have no idea what I am reading.

i learned a lot from Elements of Programming Style that explained the flaws in programs and fixed them

redneckcrake:
From my understanding of your "void loop" is that you first integrated the "IF statements" directly into the Case # itself and the second difference is that after each case you have m = to the next state that the arduino should look for.

your code is an awkward state machine. a state machine has a number of states and moves between states based on events and performs some action some events may cause it to return to the same state. in other cases, no event causes it to remain in the state.

there are 2 types of state machines: one where the action is determined by the state and the other, where the action is associated with the event

the code i posted implements a sequencer, a simple state machine because each state handles a specific event and only transitions to one other state.

redneckcrake:
So to clarify. Once the "cycle button" is already pressed and the motor is already going forward; there is really no point in checking the "cycle button" or "LMB" as these will be impossible for the nub to trigger.

true

redneckcrake:
Therefore by setting the state of m = 1 will have the loop constantly checking case #1 correct?

no, your code will constantly perform the action for that state which is defined by case 1:

redneckcrake:
If there were two LMF as oppose to one, and there is an equal chance for LMF1 and LMF2 to trigger and I want to implement something for this; would the following work?

    case 1: {

if (digitalRead (LMF) == LM_ON || (digitalRead (LMF2) == LM_ON)) {
         motorCCW ();
         m = 2;
         Serial.println (" case 2");
       }
       break;
     }

sounds right

this is an example of where two different events are handled, although they both transition to the same next state

Reply gcjr,

Hi so you are suggesting that it has something to do with all the wires going everywhere? For example, I should not have the servo wires running over the arduino (or does this not matter)? I am not quite sure where to trouble shoot.

"even the best of systems may encounter unexpected behaviors and has code to deal with them"
So typically a program will generally have a "fail safe" code to run when some erratic behavior is found to prevent catastrophic failure correct? I would imagine that such a program would be necessary for complex machinery not something as simple as an actuator consisting of a few simple parts, relays and a servo though.

What would you suggest I check physically gcjr? As for the code, I would not even know what to add to what has already been written. As it all makes sense to me, I read more about State Machines and learned quite a bit regarding the concepts and how a program should function. A good example I came across was one using a state machine concept for the ghosts in Pac-Man (which depending on the current state of something the ghost would perform a specific task until another state is triggered). All this makes sense, its just that I have no idea why my machine is not functioning properly.

The more I read into FSM the more I am reminded of Aristotle's quote, "The more you know, the more you realize you don't know."

The company is the world’s best limit swith supplier. We are your one-stop shop for all needs. Our staff are highly-specialized and will help you find the product you need.