Simple While Loop Problem

Hi, I’ve never been any good with while loops but this one is not working and I can’t figure out why. Basically, whenever pin 9 is read as HIGH the while loop begins, and begins incrementing time by 10 microseconds. Then when pin 9 is no longer HIGH (removed from 5v), the while loop ends and the a constant is then divided by the time, to give a rather accurate speed measurement. Here is my code, hopefully someone can help.

Thanks everyone.

int speed1 = 9;
int tripped;
float time = 0;
float Speed;

void setup()
{
  pinMode(speed1, INPUT);
  Serial.begin(9600);
  Serial.println("Ready to Test Speed");
}

void loop()
{
  tripped = digitalRead(speed1);
  Serial.println(tripped);
  while (tripped !=0)
  {
    delayMicroseconds(10);
    time + .000010;
    Serial.println("Reading...");
  }
  Serial.println(time);
  time = 1000000*time;
  Serial.println(time);
  Speed = 5.0/time;
  Serial.println(Speed);
}

anyone?

I think the problem is that once you enter your while loop you never take a new reading of the digital input pin, so the variable tripped never gets updated. It's stuck forever in your while loop. Is that the symptom your seeing?

On the hardware side, if tripped is coming from some kind of switch contact, be sure to wire a pull-down resistor to ground at the input pin.

tripped = digitalRead(speed1); Serial.println(tripped); while (tripped !=0) { delayMicroseconds(10); time + .000010; Serial.println("Reading..."); tripped = digitalRead(speed1); // add this line }

Lefty

Inside your loop you are not testing "tripped". Thus it will never change.

You could say:

 while (digitalRead(speed1) !=0)
  {
    delayMicroseconds(10);
    time + .000010;
    Serial.println("Reading...");
  }

Also this doesn't make sense:

time + .000010;

Do you mean:

time += .000010;

Yeah i noticed that i wasnt testing tripped inside the loop so i fixed that. now the problem i am having is that time is not actually updated and after the while loop it continues to be 0 as defined. Here is my new code:

int speed1 = 9;
int tripped;
float time = 0;
float Speed;

void setup()
{
  pinMode(speed1, INPUT);
  Serial.begin(9600);
  Serial.println("Ready to Test Speed");
}

void loop()
{
  tripped = digitalRead(speed1);
  if (tripped ==1)
  {
  while (tripped == 1)
  {
    delayMicroseconds(10);
    time + .000010;
    tripped = digitalRead(speed1);
  }
  Serial.println(time);
  time = 1000000*time;
  Serial.println(time);
  Speed = 5.0/time;
  Serial.println(Speed);
  }
}

i wish to increase the floating integer time by 10 micro seconds. so that for example, if pin 9 is high for 1 second, i would have a sampling rate of 10microseconds and so i could get a pretty accurate speed value.

as for the hardware, i have pin 9 directly into 5v on the arduino uno

Check Nick's last post, your are not incrementing the time correctly.

On this subject:

as for the hardware, i have pin 9 directly into 5v on the arduino uno

Wired like that, pulling the wire from pin 9 will not result in a reliable low reading for the input pin. The pin will just be left 'floating' and can read just noise. You need to wire a 10k ohm (value not critical) from pin 9 to ground to establish a valid electrical low logic level. Then when you run a jumper from +5vdc pin to pin 9 it will switch to reading as a valid high logic level.

Lefty

I have changed the time incrementation to:

time = time + .00010

but now I am just getting some really strange and inconsistent results...is that because i dont have it with 10k pullup resistor?

but now I am just getting some really strange and inconsistent results...is that because i dont have it with 10k pullup resistor?

No, it's because you don't have it with 10k pull-down resistor wired from the digital input pin to ground. A pull-up resistor is one wired to +5vdc, a pull-down resistor is one wired to ground.

If you don't want to use an external resistor you can learn how to enable the internal pull-up resistor for the input pin, and use a jumper wire from the pin to ground. Pull the wire you read a high, insert the wire and you read a low.

Lefty

If you don't want to use an external resistor you can learn how to enable the internal pull-up resistor for the input pin, and use a jumper wire from the pin to ground. Pull the wire you read a high, insert the wire and you read a low.

Im sorry but i don't really understand this part. jumper pin 9 to ground, thats about all i got

If the pin is configured as an INPUT, writing a HIGH value with digitalWrite() will enable an internal pullup resistor. All the Arduino digital I/O pin have an optional internal pull-up resister that you can enable pin by pin if you wish. The default for pins is internal pull-ups are disabled.

So in your setup function:

pinMode(speed1, INPUT); digitalWrite(speed1, HIGH); // This enables the internal pull-up resistor

This forces the input pin to read as a valid high with nothing wired to the pin. Now if you jumper pin 9 to a arduino ground pin, it will read as a valid low.

That make sense?

Lefty

yes that makes more sense. but is there a way to give the arduino input pin a valid low without explicitly tying it to ground? this pin will be switching on and off VERY quickly (probably less than 5ms) so it would be impossible to do it by hand

but is there a way to give the arduino input pin a valid low without explicitly tying it to ground?

Of course. The jumper to ground was just a way to test your program. In the real world you could wire a momentary normally open switch contact between pin 9 and ground, press the switch you read a low, release the switch you get a high.

What are you actually planning on wiring to pin 9, what kind of electrical signal or component?

Lefty

what i actually plan on hooking up is a jumper to pin 9 and a jumper to arduino 5v and when a steel rod passes through the two jumpers it completes the circuit, Pin 9 is HIGH and the while loop is started. then when the rod is gone pin 9 goes back to LOW and the while loop ends. then with the appropriately incremented "time", I divide the length of my rod by "time" and there is my speed

this is what im goin for, please excuse my really bad drawing capabilities

Think of the 10K resistor as a "default" value. If you connect it to ground, the default is 0. If you connect it to +5v the default is 1.

Which way around you want the default depends on what the "non-default" is. If your rod connects the pin to +5v, then you want the default to be ground. if your rod connects the pin to ground, you want the default to be +5v.

The default is a "gentle" pull in that direction. However it is still fast. You want the resistor there otherwise there is a "tug of war" to see which is more powerful, your rod or the direct connection.

Your drawing is fine, just change the wire going to +5vdc to a ground pin, if you have enabled the internal pull-up as I showed you. When the rod is in place your program will read it as a low and when the rod passes it will read as a high.

Lefty

Based on your sketch, then you need to tie pin 9 to ground via a 10K (or thereabouts) resistor. Otherwise you get a "1" from the rod, interspersed with random values from noise. If you don't use a resistor then effectively the rod is shorting your +5v supply to ground intermittently, which won't be a good idea.

(However what retrolefty posted while I was typing that is an alternative approach which avoids using a resistor).

Looks to me like you are trying to print every 10 microseconds. But your connection speed to the PC is 9600, which is like 104 microseconds. Your output display will get all backed up in no time. If you set it to 115200,thats 8.6microseconds, you may a have a chance. I wouldn't bother.

The 'speed' you are measuring, it is the width of a pulse? something sitting high, then goes low and back high?

EDIT: Ah, I see your drawing now. I suspect you are going to have the equivalent of mechanical switch bounce issues as the initial contact is made, and then again when it is released. I am thinking you will have better results with 2 switches, start the time when the first switch changes state (and ignoring any bouncing after that) and then end the time when the 2nd switch changes state.

You could do it more accurately, I'm thinking along these lines:

// starttime, endtime, elapsedtime should all be type unsigned long

Serial.println ("ready to capture switch closures");

EDIT 3 Feb: fixed up the digital read lines (nice catch Nick Gammon):
void loop(){
Speed1state = digitalRead (Speed1); // read switch1
while (Speed1state == 1){
// do nothing except read it again
Speed1state = digitalRead(Speed1); 
}
// while loop kicks out when pin goes low
starttime=microSeconds(); //capture the time

Speed2state = digitalRead (Speed2); // read 2nd switch
while (Speed2state == 1){
// do nothing except read it again
Speed2state = digitalRead(Speed2); 
}
// while loop kicks out when it goes low
endtime = microSeconds(); // capture the time
Serial.print ("elapsed time  in microseconds = ");
elapsedtime = endtime-starttime;
Serial.println (elapsedtime);
} // end void loop