Trip servo motion after no signal for 2 seconds

To start with I'm a mechanical engineer, and know enough to be dangerous with programing and such.

I'm looking to control a servo to move one way when it gets a 5v signal and the other way when there is no signal after 2 seconds. I have the first part done, but don't know where to start on the second condition. I'm thinking this would be a if/else statement but not sure how to set it up to wait for "no signal"

Welcome to the forum

When the start condition is met, ie the input becomes LOW (NOTE : not is LOW), save the value of millis() as startTime and set a boolean to true. Let's name the boolean isWaiting

Now, one of 2 things will happen

  1. The input becomes HIGH
    If that happens set isWaiting to false
    or
  2. Two seconds, timed by subtracting startTime from the current value of millis(), elapses
    If that happens and isWaiting is true then two seconds has passed with the input remaining LOW so take the required action

Maybe.

So I say while the signal is HIGH (5 volts), keep setting a timer to the current time. Keep telling the servo to go to position one.

When it is LOW, check the timer - if the timer reaches two seconds, move the other way.

Try this (I can't believe I found it)

Any activity keeps the LED illuminated. No activity for a period extinguishes the LED.

@robwaddell74 say how what you want is different.

a7

oh man, so close. It works until I don't hit the button within that 3.5 seconds, then it constantly does the second if statement. I need it to wait the 3.5 seconds or whatever then do the second if statement and wait again until it times out or gets the 5v signal. Here is what I'm working with right now:

#include <Servo.h>
Servo myservo1;  //servo1

const byte sensorPin = 2;
const byte outPin = 13; // also flashes the built-in LED
const int ServoPin = 3;

//declare H-Bridge Motor Controller
int In1 = 7; //this variable designates to digital pin #7 on Arduino to motor controler's IN1
int In2 = 8; //#8 on Arduino to motor controller's IN2
int ENA = 5; //#5 on Aruino to motor controller's ENA pin
int SPEED = 250; // speed of motor

const unsigned long actionDelay = 3500; // action 3.5 seconds for testing - life too short

void setup() 
{
  pinMode(13, OUTPUT);

   myservo1.attach(ServoPin); //activating pin 3 for servo1

   // motor controller setup
   pinMode(In1,OUTPUT); //tells arduino that In1 pin is set to give the output
   pinMode(In2,OUTPUT); //tells arduino that In2 pin is set to give the output
   pinMode(ENA,OUTPUT); //tells arduino that ENA pin is set to give the output
   
   // to decide the polarity we need to set one pin to "High" and one pin to "low"
   digitalWrite(In1,LOW);
   digitalWrite(In2,HIGH);

   //Speed section - between 0 and 255 (highest being 255)
   analogWrite(ENA,SPEED);

   //delay code
   pinMode (sensorPin, INPUT_PULLUP);
   pinMode (outPin, OUTPUT);
   digitalWrite(outPin, LOW);
}

void loop() {
  static unsigned long startTime; // time at action

  unsigned long now = millis();   // fancier 'currentMillis' ooh!
  int buttonState = !digitalRead(sensorPin);  // button pulled up!

  if (digitalRead(sensorPin)) {
    digitalWrite(outPin, HIGH);
    startTime = now;
    delay(500);
    digitalWrite(outPin, LOW);

    myservo1.write(40); //setting servo1 accept tilt (set to -40)
    delay(700); //wait
    myservo1.write(90); //setting servo1
  }
  if (now - startTime >=actionDelay){
    digitalWrite(outPin,LOW);
    delay (500);
    digitalWrite(outPin, LOW);

    myservo1.write(140); //setting servo1 accept tilt (set to -40)
    delay(700); //wait
    myservo1.write(90); //setting servo1
  }
}

I'm using a push button to provide my 5v signal for testing

I am not clear which of the two suggested solutions you are trying to use. The one suggested by @alto777 is simpler than mine to program so I suggest that you follow that one

A question for you

    int buttonState = !digitalRead(sensorPin);  // button pulled up!

Why are you doing this, especially as you never subsequently use the value returned

I'm using some of the code that @alto777 had in their project, so I'll keep going with that method.

I'm using a servo and electric eye to sort parts. If the part is good, it gets kicked one way, if the part is bad it gets kicked the other way.

So right now I'm working on that part of the code. The electric eye only outputs only one signal, either good or bad. I've set it up so that it outputs a 5v signal if the part is good, so if I get a good signal then the part needs to be kicked one way, if there is no signal (bad part) I need to kick the other way. Then return the servo and wait for either a signal or no signal to kick the part the correct way.

I hope that helps explain what I'm working towards.

First, I see

      digitalWrite(outPin,LOW);
      delay (500);
      digitalWrite(outPin, LOW);

probably those digitalWrite()s shoukd be different.

Next, a minor detail, but in my simulation, the input is read once, and then that variable informs any subsequent logic based on the input:

  int buttonState = digitalRead(sensorPin);  // button pulled down!

  if (buttonState) {

This may not matter here; trust me when I tell you this is headed to a place where it will very much make a difference.

Also, I removed the '!' logical not operator. The assignment to buttonState is your one place to adjust for the sense (active high or active low) of the input signal. My pushbutton was pulled up, so LOW means pressed, thus to get buttonState to be true when the button is pressed, I needed to invert the reading.

Clarify what each a digital reading of LOW or HIGH means.

Now here

I am unsure what you want - sounds like a reject kick every 3.5 seconds if there is never a "good" reading.

Or a kick one way or the other every 3.5 seconds.

Does something synchronize the servo kick (good or bad) to the appearance of the device under test? You cannot rely on the Arduino keeping good time over any reasonable stretch. I see these coming along at some rate, somehow.

Doing something just once is easy, just condition the doing on a flag (here a variable that holds only two values, usually true and false) and set that flag so it won't do it again.

The harder part is placing resetting that flag, so the once only thing can happen once only again. You have to identify the condition that should reset the mechanism.

I'm guessing, always a risk of wasting brain power, which BTW I have precious little of, that there needs to be some additional input, and perhaps another time period.

I suggest you stop worrying about the code for the moment, and describe again this with a timing diagram or informally with code-like language.

Or regular words, but don't make a word salad. It seems like a simple enough process. Doing things once, or every N milliseconds, is easy.

You may enjoy working in the wokwi - you can modify my project and it will be yours to save and work on. Look forward to adding a simulated servo when the time comes.

a7

Hello robwaddell74

What is the task of the sketch in real life?

Because I have no life had some time waiting here for she who must not be kept waiting, I went ahead with a more elaborate simulation, still all coded literally. No libraries, that is.

As it runs, pressing the button starts a test. If the button stays down for 3.5 seconds, the test is considered to be a success. If the button is released before the time period passed, the test is a failure.

The button logic deglitches the input signal, a step beyond simple debouncing that ignores brief noise, excursions to the opposite condition that don't last long enough to be considered. That does mean some signals that you'd rather reject can appear to be stable. All that is very context dependent, so another thing we might like to know is the exact nature of the signal / no signal.

Since @paulpaulson has shown up, I made the code follow the IPO model. Srsly, I would have anyway.

Read about IPO in its larger meanings here:

The IPO Model

The sketch is a bit more complicated than it might need be, but if you can see how it works it may be close enough to what really needs be done so you can either modify or or ask good questions about how to do.

I left all the printing in there to verify what the sketch is doing. It can be removed. Additional printing can be added - some code I can read, sometimes I have to have help from the printing. You'll notice when you are on your hands and knees with this some I already commented out.

// https://forum.arduino.cc/t/how-to-delay-using-millis/1142938
// https://wokwi.com/projects/395060119030612993

// version 5. Sue me.

const byte buttonPin = 2;
const byte ledPin = 3;

const unsigned long actionDelay = 3500; // action 3.5 seconds for testing - life too short

void setup() {
  Serial.begin(115200);
  Serial.println("\nWake up!\n");

  pinMode (buttonPin, INPUT_PULLUP);
  pinMode (ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
}

# define RATE 20    // milliseconds per loop (50 Hz)
# define PRESST LOW

void loop() {
  static unsigned long lastLoop;

  static byte previousButton;
  static byte theState;

  static unsigned long startTime; // time at action
  static bool inTestPhase;        // are we testing?

  bool gotPressed = false;        // set each loop - use it or lose it
  bool gotReleased = false;       // also

  unsigned long now = millis();   // fancier 'currentMillis' ooh!

// just run the rest of this at 1000 / RATE Hz

  if (now - lastLoop < RATE) return;
  lastLoop = now;


// INPUT
// read and deglitch the input
  int button = digitalRead(buttonPin) == PRESST;

  if (button == previousButton) {   // stable input
    if (button != theState) {       // edge detected

      if (button) {
        // Serial.println("I see that go pressed");
        gotPressed = true;
      }
      else {
        // Serial.println("I see that go released");
        gotReleased = true;
      }

      theState = button;
    }
  }
  previousButton = button;

// PROCESS
// now we have theState, and gotPressed and gotReleased to work with and we develop

  bool passed = false;
  bool failed = false;

// when the button is pressed, reset the timer, turn ON the LED

  if (gotPressed) {
    startTime = now;
    digitalWrite(ledPin, HIGH);
    Serial.println("                 new world. LED ON");
    inTestPhase = true;
  }

  if (!theState && inTestPhase) {
    digitalWrite(ledPin, LOW);
    Serial.println("            device failed..."); 
    failed = true;  
    inTestPhase = false; 
  }

// when (if!) the timer expires, see if we made it or not
  if (now - startTime >= actionDelay) {

// but we do the thing just once
    if (inTestPhase) {
      digitalWrite(ledPin, LOW);
      Serial.println("                            timer!"); 

      if (theState) {
        Serial.println("You made it! I hope you feel a m a z i n g!");
        passed = true;
      }
      else {
        Serial.println("this should never print.");
      }
    
      inTestPhase = false;
    }
  }

// OUTPUT
  if (passed) Serial.println("\nPASS");
  if (failed) Serial.println("\nFAIL");
}

Try it here:

Wokwi_badge pass/fail time test


At the very least I hope this allows you to refine your statements about how this should work.

I'm still waiting for my beach buddy to text, so. :expressionless:

a7

#include "Servo.h"

Servo servo;

const byte PinServo = 10;
const byte PinSig   = A1;
const byte PinLed   = LED_BUILTIN;

const byte Off = HIGH;
const byte On  = LOW;

unsigned long MsecPeriod = 2000;
unsigned long msec0;

bool  active;

void
loop (void)
{
    unsigned long msec = millis ();
    if (active && msec - msec0 >= MsecPeriod) {
        active = false;
        digitalWrite (PinLed, Off);
        servo.write (0);
    }

    byte sig = digitalRead (PinSig);
    if (HIGH == sig)  {
        msec0  = msec; 
        active = true;
        digitalWrite (PinLed, On);
        servo.write (90);
    }
}

void
setup (void)
{
    Serial.begin (9600);
    pinMode (PinSig, INPUT_PULLUP);
    pinMode (PinLed, OUTPUT);
    digitalWrite (PinLed, Off);

    servo.attach (PinServo);
    servo.write (0);
}

Okay so I think I know what I'm missing now... it is the delay on the good kick.

Main goal, have the servo sit at the 90 position until it either gets a signal the part is bad and it moves to the 40 position then back to 90, or gets no signal for 2 seconds and it moves to the 140 position then back to 90. Then wait for the next signal or no-signal time period.

so with this code:

#include <Servo.h>
Servo myservo1;  //servo1

const byte sensorPin = 2;
const byte outPin = 13; // also flashes the built-in LED
const int ServoPin = 3;

//declare H-Bridge Motor Controller
int In1 = 7; //this variable designates to digital pin #7 on Arduino to motor controler's IN1
int In2 = 8; //#8 on Arduino to motor controller's IN2
int ENA = 5; //#5 on Aruino to motor controller's ENA pin
int SPEED = 250; // speed of motor

const unsigned long actionDelay = 3500; // action 3.5 seconds for testing - life too short

void setup() 
{
  pinMode(13, OUTPUT);

   myservo1.attach(ServoPin); //activating pin 3 for servo1

   // motor controller setup
   pinMode(In1,OUTPUT); //tells arduino that In1 pin is set to give the output
   pinMode(In2,OUTPUT); //tells arduino that In2 pin is set to give the output
   pinMode(ENA,OUTPUT); //tells arduino that ENA pin is set to give the output
   
   // to decide the polarity we need to set one pin to "High" and one pin to "low"
   digitalWrite(In1,LOW);
   digitalWrite(In2,HIGH);

   //Speed section - between 0 and 255 (highest being 255)
   analogWrite(ENA,SPEED);

   //delay code
   pinMode (sensorPin, INPUT_PULLUP);
   pinMode (outPin, OUTPUT);
   digitalWrite(outPin, LOW);

   //trigger code
   pinMode(7, OUTPUT);//define arduino pin D7 as the trigger output pin
}

void loop() {
   
  static unsigned long startTime; // time at action

  unsigned long now = millis();   // fancier 'currentMillis' ooh!
  int buttonState = !digitalRead(sensorPin);  // button pulled up!

  if (digitalRead(sensorPin)) {
    digitalWrite(outPin, HIGH);
    startTime = now;
    delay(500);
    digitalWrite(outPin, LOW);

    myservo1.write(40); //setting servo1 accept tilt (set to -40)
    delay(700); //wait
    myservo1.write(90); //setting servo1
  }
  if (now - startTime >=actionDelay){
    digitalWrite(outPin,LOW);
    delay (500);
    digitalWrite(outPin, LOW);

    myservo1.write(140); //setting servo1 accept tilt (set to -40)
    delay(700); //wait
    myservo1.write(90); //setting servo1
  }
}

I'm pretty close (you can ignore the motor controller stuff, as that is for a different motor contol)

This seems to work, but it doesn't pause long enough before it moves on the non-signal command. So when I press the button I have to hold it down to make it move in the signal case. Meaning I kind of have to catch it between movements to get it to trip to the signal motion.

Thank you everyone for helping out this poor ME with all this EE/computer engineering stuff.

I hope this all makes sense.

With "signal" being in the demo "the switch is pressed", positive indication:

I have to ask again, because this seems to imply that in the absence of a signal, you'll get a reject kick every two seconds.

If you get a signal, it is kicked left.

If you don't get a signal for two seconds, it is kicked right.

With nothing to say it is time to even think about kicking anything anywhere, your machine will sit there kicking left every two seconds.

So far I am seeing these devices under test arriving for inspection, inevitably that will be asynchronous to any process running on the servo control Arduino board.

I am still missing something, and I suggest again a timing diagram showing the range of possible scenarios.

Time on the X axis, signal and servo angle over time on the Y axis. Certainly MEs use timing diagrams of some kind, use your best common sense here.

And just now I am not able to draw an example of what would clarify this, even with my finger in the sand I take a picture and post it.

If you haven't by the time I look again, I will.

a7

what prevents this from happening repeatedly?

Okay, so I have screws being presented to an electric eye. It can take measurements in micro seconds and tell me if the diameter is in the range or out of the range. For some reason this electric eye can only put out one signal. I can set it up to send that signal if it is good, or bad. I will be triggering the electric eye to take measure, but I haven't gotten to that part of the code, as this is my current struggle.

If I can't get this to work with the one signal, then I'll break down and buy the serial cable that lets me have more signals off the controller. You would think for the cost of this thing you could get a good signal or a bad signal along with it making me breakfast in the morning.

I have the screws coming down from the top and sitting on top of a plate. The servo tips the plate left or right depending on the condition of the screw.

So screw goes in front of the eye, eye says "No Good" and sends a 5 v signal to my arduino and it tells the servo to tilt left, then return to "flat". Another screw comes in-front of the eye, it is "good" so no signal is sent to the arduino and after 2 seconds (time is arbitrary as it could be faster) the servo tilts to the right, then returns to "flat".

Does that help?

So I would like it to do this repeatedly as long as the machine is running.

Thanks again everyone. This is a super helpful group

I can't now, but this

completely changes everything answers the question I have several times posed.

After a measurement request is made (triggered)…

the signal from the electric eye should cause a good tip to issue. After a time 2 seconds or whatever, if a good tip hasn't occurred, a bad tip should issue, and the system resets waiting for

So the sketch is very simple. Using two button inputs as proxies for now:

A variable inTest says if we are in a test period or not. Starts false.

One button starts a timer when it is pressed, not because it is pressed. Simple edge detection. The inTest is set to true.

If the timer expires, and we are in a test (inTest is true) a bad tip is issued.

If the other button is pressed(electric eye signal) and we are in a test, a good tip is issued.

Either tip also sets inTest false, resetting the system.

A clever programmer could get away without the inTest variable by noticing that only when the timer is running are we testing. Watch out for clever programmers. It is much clearer to have a good separation and no interdependence of the timing and the state.

I honestly believe that all the pieces for this are in front of you, just not logically connected to do this now very clear and simple process, in my last simulation.

Seeing when a button gets pressed. Starting and monitoring a timer. All you have to add is the part you came in with, the servo tilt, which can remain a move to one side or the other, a delay, and a move back as the action items the logic shall inform.

If you don't want to figure out the button edge detecting that is part of that, I can recommend the ezButton library, which gets my highest praise for button handlers: it doesn't suck.

Naturally I would prefer that you come to understand debouncing and edge detection, even if you do use thereafter any library to do those jobs. The logic is simple,e and getting your brain around it will not be a waste of time if you plan on using microprocessors for more of what you do.

So either way, give it a shot, we look and see. Adding a button and a servo to the simulation should be, dare I say it, intuitive - be brave poke around drag some wires to the new button and servo and get a full up project that should just work IRL first using buttons, please, and then the hurdle of connecting it for realz to the electric eye and whatever it is that triggers the electric eye to take measure.

Reading this again just now, the button handling doesn't need much logic at all, and prolly doesn't need a library. The inTest variable can be used to ignore the first button after a test period is started, and the second button can just tip the servo, which would also only happen once because it, too, coukd be conditioned upon being inTest.

Code writes itself. :expressionless:

a7

Okay, so I set up a variable for "inTest", then do I need to set up my if statement as a "if &&" statement looking for both the button press and inTest variable? Then I wouldn't need that part of the code in the second if statement of (now - startTime >=acionDelay) As I'm looking for two conditions instead of counting the time.

then my "inTest" variable would be set via a second button press for testing purposes.

Am I reading that right?

Sounds like I should just bite the bullet and get the equipment for a second signal and all of these timing issues would go away...

Here's as simple as it can get but not code:

  if we are not in a test  (inTest is false)
    if a start signal arrives
      start a test  (set inTest to true)
      start the timer

  if we are in a test  (inTest is true)
    if the timer expires
      tip to bad part bin
      stop the test  (set inTest to false)

    if the food signal arrives
      tip to good part bin
      stop the test  (set inTest to false)

So yes, I think one might do part of that with && of some conditions. But

  if (A) {
    if (B) {

// this stuff gets done if A and B are true

    }
  }   

is the same as

  if (A && B) {

// this stuff gets done if A and B are true

  }

The && version doesn't work when you have two things to check after A is checked, so I expressed both as nested if statements.

  if (!A) {
    if (C) {

// this stuff gets done if A is false and C is true

    }

    if (D) {

// this stuff gets done if A is false and D is true

    }
  }   

The only assumption that pseudocode makes is that the start signal goes away before either servo tip has occurred. Since the servo tip lasts 500 milliseconds, this only means that the start signal has to be up for less than that time. Humans pressing a button typically do for way less than 500 milliseconds; you have not said anything about the source or nature of the signal you say will start a test.

I can't say you wouldn't be happier with more coming from the electric eye, and obvsly we have no clue as to the cost. But if it would continue to involve code and you doing that with an Arduino board, I suggest it would be best to write the code to do the job that can be done easily with the electric eye information that is availble now, no matter.

Yes, no. You still need the timer to see if it expires which is basically the problem you brought here, now more fleshed out but in essence "I gave you two seconds to say that part is OK, you did not, so Imma reject it". Part tipped to the bad bin and the test is terminated.

If the electric eye sez the part is good in time, the part tipped to the good bin and the test is terminated.

Take a shot at it, I say again you got this. Then maybe we can fix it, or someone will post the code that will work and we can wait for the part where you start connecting this to real sensors and servos and buttons and whatever, where there will almost certainly be more problems… easily solved we can almost be sure.

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.