1

I have an HC-SR04 attached to a servo. When the 90 degree reading is less than 30 cm, I want the motor to take 2 seconds to turn to 130 degrees, get a measurement on the right, and turn back to 90 degrees. My code does not seem to work. Even though I tell it to wait for 2000 ms, it skips directly to the if statement where the turning to 90 degrees is done. Here is the main code:

 #include <Servo.h>
#define trig 22
#define echo 24
long duration;
boolean turned130 = false;
int cm50, cm, cm130;
Servo servo;
boolean checkRight = false;
boolean mainPing = true;
unsigned long counter;
unsigned long servoTimer = millis();
void setup() {
 // put your setup code here, to run once:
Serial.begin(9600);
pinMode(echo, INPUT);
pinMode(trig, OUTPUT);
servo.attach(12);
servo.write(90);
delay(2000);
}
void loop(){
counter=millis();//my timer
 if(((counter-servoTimer) >= 250) && mainPing){//distance measured 4 times a second
 ping();
 cm=duration/29.1/2;
 Serial.println(cm);
 servoTimer=counter;//this if statement works fine
 }
 if((cm<30) && mainPing){
 mainPing=false;//makes the 90 degree ping and this if statement unable to run
 checkRight = true;//enables next if statement to run
 servoTimer=millis();
 servo.write(130);
 cm=30;//so that this if statement does not get called again until distance is obtained
 Serial.println("Inside less than 30 loop");//I added this for debugging, to let me know when the loop is called
 }
 if(checkRight && ((counter-servoTimer) >= 2000)){//after 2 seconds passes
 checkRight=false;
 ping();
 cm130 = duration/29.1/2;
 mainPing = true;//enable to other 2 if statements to run
 servoTimer=millis();
 Serial.println(cm130);
 servo.write(90);//turn motor back
 Serial.println("Inside other loop");//for debugging
 }
 delay(2);
 }
void ping(){
 digitalWrite(trig, LOW);
 delayMicroseconds(2);
 digitalWrite(trig, HIGH);
 delayMicroseconds(10);
 digitalWrite(trig, LOW);
 duration = pulseIn(echo, HIGH);
}

This is what I got in the Serial monitor:

25//distance triggers 2nd if statement
Inside less than 30 loop
261
Inside other loop
13
Inside less than 30 loop
14
Inside other loop
14
Inside less than 30 loop
14
Inside other loop
3242
13
Inside less than 30 loop
14
Inside other loop

This all happened within milliseconds. There was no 2 second pause and the servo never turned. What is wrong with my code that prevents the motor from turning and waiting 2 seconds? The order of the Serial messages is correct, but it is happening fast.

EDIT:

When I remove the Serial messages and disconnect from the computer, it kind of works. By that, I mean it turns and waits for 2 seconds 50% of the time. Other times, the servo just shakes a little bit and stops.

Another thing I noticed is that if I leave only Serial.println(cm) message and Serial.println("Inside less than 30 loop"), this is the result that I observe.

 12
 13
 Inside less than 30 loop
 15
 3
 Inside less than 30 loop

For some reason the loop does not get triggered the first time. Is there a possibility that I have a defective Arduino Mega?

EDIT 2:

I tested the same setup on an official Arduino Uno and it did not work either. This means that I do not have a broken Arduino. Could the servo motor be broken? That won't explain the Serial messages though.

asked Aug 18, 2016 at 23:42
9
  • Try adding some parentheses; if( checkRight && ((counter-servoTimer)>=2000) ) Commented Aug 19, 2016 at 9:23
  • @Gerben I added parentheses. When I removed the Serial messages, it kind of worked like 50% of the time. When I add them back in, the same thing happens. There is no interval between the messages and the servo just shakes, but does not rotate. Commented Aug 19, 2016 at 16:34
  • 1
    Could you post the entire sketch? Make sure servoTimer isn't a local variable. Commented Aug 19, 2016 at 17:55
  • @Gerben I posted the entire sketch. Commented Aug 20, 2016 at 0:56
  • 2
    That isn't the whole sketch. The lines counter=millis();//my timer onwards are outside any function. Try compiling what you posted. Commented Aug 20, 2016 at 4:36

1 Answer 1

2

I added some more debugging, and I think I have your issues detected.

Amended code:

#include <Servo.h>
#define trig 22
#define echo 24
long duration;
boolean turned130 = false;
int cm50, cm, cm130;
Servo servo;
boolean checkRight = false;
boolean mainPing = true;
unsigned long counter;
unsigned long servoTimer = millis();
void setup() {
 // put your setup code here, to run once:
 Serial.begin(115200);
 pinMode(echo, INPUT);
 pinMode(trig, OUTPUT);
 servo.attach(12);
 servo.write(90);
 delay(2000);
}
void loop() {
 counter = millis(); //my timer
 if (((counter - servoTimer) >= 250) && mainPing) { //distance measured 4 times a second
 Serial.print ("Doing ping at time ");
 Serial.println (counter);
 ping();
 cm = duration / 29.1 / 2;
 Serial.println(cm);
 servoTimer = counter; //this if statement works fine
 }
 if ((cm < 30) && mainPing) {
 mainPing = false; //makes the 90 degree ping and this if statement unable to run
 checkRight = true;//enables next if statement to run
 servoTimer = millis();
 servo.write(130);
 cm = 30; //so that this if statement does not get called again until distance is obtained
 Serial.println("Inside less than 30 loop");//I added this for debugging, to let me know when the loop is called
 }
 if (checkRight && ((counter - servoTimer) >= 2000)) { //after 2 seconds passes
 Serial.print ("counter = ");
 Serial.println (counter);
 Serial.print ("servoTimer = ");
 Serial.println (servoTimer);
 checkRight = false;
 ping();
 cm130 = duration / 29.1 / 2;
 mainPing = true;//enable to other 2 if statements to run
 servoTimer = millis();
 Serial.print ("cm130 = ");
 Serial.println(cm130);
 servo.write(90);//turn motor back
 Serial.println("Inside other loop");//for debugging
 }
 delay(2);
}
void ping() {
 digitalWrite(trig, LOW);
 delayMicroseconds(2);
 digitalWrite(trig, HIGH);
 delayMicroseconds(10);
 digitalWrite(trig, LOW);
 duration = pulseIn(echo, HIGH);
}

With no server (or anything) attached I get this output:

Doing ping at time 1999
0
Inside less than 30 loop
counter = 1999
servoTimer = 2693
cm130 = 0
Inside other loop

First note that servoTimer is somewhat higher than counter because the pulseIn function takes a while to execute (times out after 1 second allegedly, although that was less than a second).

Now we come to the problem "if" statement.

 if (checkRight && ((counter - servoTimer) >= 2000)) { //after 2 seconds passes

That can't be right, eh? counter is 1999 and servoTimer is 2693, and there is not 2000 difference. However you are doing the maths the other way:

counter - servoTimer = 1999 -ひく 2693 = -ひく694

Since we are using unsigned long, it cannot hold negative numbers so the result of the subtraction is 4294966602.

Unsigned long calculation

That is much larger than 2000, so the "if" branch is taken.

answered Aug 20, 2016 at 23:06
2
  • Your answer makes a lot of sense. What I did to address the issue is put counter=millis(); after each ping before I set the servoTimer. This seemed to fix the issue. Works really well now! Now I have to make it turn left as well and record distances. It should not be a problem with this issue taken care of. Quick question, what is "<!-- language: lang-c++ -->" for? Is it necessary? Thank you for your help! Commented Aug 20, 2016 at 23:20
  • 1
    The language tag is just a hint to Stack Exchange to syntax-colour the code. Look at the posts. Mine is coloured, yours isn't. :) However I notice now that I put it in the wrong place. I must have hit "paste" at the wrong time. I've edited it out of the code. Commented Aug 20, 2016 at 23:39

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.