millis() problems - UNO R3 - timing difference

Got a problem with millis().

I have created a little set up for my classroom. A ball (I use a golf ball) is held by a clamp and holds a switch HIGH. When the switch goes LOW, the program takes millis then waits for a peizo sensor below to detect a knock. When a knock occurs, take another millis, do some math and display to an LCD. We take this time and use it to solve for the acceleration due to gravity. The times are in the range from 0.350 (drop off a desk) seconds up to 2.5 seconds (drop off a balcony).

Physically, everything seems to work perfect, but the time keeps coming up short by 6-10%, so either millis is not keeping good time, or the acceleration due to gravity happens to be significantly greater wherever my Arduino and laptop seem to go. If it was coming in long, I would assume hardware or circuitry, but the time is less than it should be.

In anticipation of questions:
I have had it display the raw "long" xt2 variable on the lcd with the calculated millis and they are identical, so i don't think it is a change in variable type causing any issues.

I have monitored it thoroughly through the serial monitor with other people around and nothing in it looks unusual.

I have reworked and reworked and reworked the code to find anywhere I could be losing time and nothing changes.

I have reloaded the code to multiple Arduinos and get identical results each time.

In the long run, my goal is to create a simple and inexpensive open source lab sensors for use in science classrooms, especially in the developing world. I am happy to provide schematics, pictures, etc., but I believe this to be a software problem at this point.

So here is the code. I have tried to annotate it well, and near the end, there is some code that translates the numbers into a readable format for students and sets the system up for the next trial.

/*
CAPOSC: December 2013
Uses a clip with a switch to hold a golfball.  While switch is closed, program waits.
Once switch is opened, the program takes millis and starts to look at knock sensor.
Once knock is detected, take a second millis, do some math, and spit out a time to an LCD.
 */
#include <LiquidCrystal.h>
//timing vars
unsigned long xt;
unsigned long xt2;
//LED pins
int led = 3;
int com = 2;

int nc = A3;  //Switch pin, when switch is closed, an object is ready to drop
int knock = A2;  //knock sensor, when struck it ends the timer
int threshold = 10;  //threshold of the knock sensor to prevent background noise from setting it off

int sensor = 0;
int tran = 0;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);  //using a SainSmart LCD button board

void setup() {                
  pinMode(com, OUTPUT);     //Sets LED pins
  pinMode(led, OUTPUT);
  pinMode(nc, INPUT); //sets analog pin as input from switch
  pinMode(knock, INPUT); //sets analog to knock sensor
  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Place a ball in ");
  lcd.setCursor(0,1);
  lcd.println("the clip.       ");
  //Serial.begin(9600);
}

void loop() {
  digitalWrite(led,HIGH);  //external power indicator

  //this while loop waits for an object, golf ball, to be placed in the clamp.
  while (tran < threshold){
    tran = analogRead(nc);
    delay(1);}
  digitalWrite(led,LOW);

  //some idiot proofing code so my students think before they do something
  lcd.setCursor(0,0);
  lcd.print("WAIT            ");
  lcd.setCursor(0,1);
  lcd.print("                ");
  delay(500);
  lcd.setCursor(0,0);

  //Ready for the drop to occur
  lcd.print("READY TO GO!!!!>");
  digitalWrite(led,HIGH);
  digitalWrite(com,HIGH);

  //waits for the switch to open
  while (tran > threshold){
    tran = analogRead(nc);
    delay(0);
  }
  xt = millis();  //takes time just after switch opens
  digitalWrite(led,LOW);
  digitalWrite(com,LOW);

    //now we are waiting for the knock sensor to be triggered
    while (sensor < threshold){
      sensor = analogRead(knock);
      Serial.println(sensor);
    }
// The knock has been triggered, we take another millis and subtract
// the initial millis for elapsed time
    xt2 = millis()-xt;

//the code here simply makes the millis easy to read on the screen
//again, idiot proofing
    int one = xt2/1000;
    int two = (xt2 - (one*1000));
    lcd.setCursor(0, 1);  //line=2, x=0
    lcd.print("Time: ");
    lcd.print(one);
    lcd.print(".");
    if (two < 10){
      lcd.print("00");
    }
    else{
      if(two < 100){
        lcd.print("0");
      }
    }
    lcd.print(two);
    lcd.println("           ");

//Gets ready for the next trial
    digitalWrite(com,HIGH);
    sensor = 0;
    tran = 0;
    delay(5000);
    digitalWrite(led,HIGH);
    digitalWrite(com,LOW);
    lcd.setCursor(0,0);
    lcd.print("Replace ball....");
}

short by 6-10%

Air resistance?

Don't forget an analogRead take around 100us

delay(0);WTVF?

@AWOL

Sorry about the delay (0), leftover from a previous version and I didn't ever delete it.

As for air resistance, very unlikely a significant cause of error. Air resistance increases with velocity and most drops are from 1 to 1.25 meters. Also, the percentage variance does not change with larger distances of 5 meters.

A question I have is, does taking an analog read interfere with the count of millis in the chip? If so, I could build in a counter to correct, but it seems unlikely that a chip would be designed with a timer inside that works unless the chip is actually running instructions.

Thanks for the quick response.
SteveC

remove

Serial.println(sensor);

within the timing loop.

Air resistance increases with velocity

Sp. "by the cube of velocity" IIRC.
No, taking an analogRead does not affect millis(), but it does increase latency, and I don't really see why it is an analogRead for a switch.

@knut_ny

Thanks for your input, again, sorry for the sloppy code. I have made about 15 versions of this code and was with a local Arduino Group yesterday where we inserted that to watch it in serial mode trying to diagnose. With or without a serial output, the results were consistently low.

@AWOL
No real reason for the analog input except that is the way I started it. I could easily change that to a digital pin, but I don't see how that would change the timing problem I am having.

Thanks all for the input.

Well, you could use a digital pin, and a pin change interrupt. You will then get no effects from a quickly repeating analog read.

Thanks lar3ry,

For the switch, a digital pin may work, but that is prior to the timing. The peizo requires an analog read so I am stuck with using analogRead in my while loop.

SC

..and xtal is 16.000 ?
I'd like to see your setup for this experiment, so I could test my local g

I actually don't have my camera with me right now, I will post pics tomorrow for you knut_ny. Maybe I can get a student to help me with a video as well.

As for measuring your local g, if you use this code, you can expect it to be high.

Is it possible that the spring in the top switch is providing some acceleration?

Shpaget

Thought of that idea already. I am using a standard micro switch and it is oriented in the horizontal plane, so the only acceleration it could provide would be sideways. In a drop of 5 meters it only moves about 2-3 cm sideways.

Presumably the ball has to move a finite distance before the first switch triggers so the ball will already be moving at the start of the time measurement. Because of this it will take less time than you'd calculate assuming a stationary initial condition. I suggest you look carefully at the geometry of the ball release sensor and try to estimate the distance traveled before the switch triggers, and then see if you can estimate how far the ball would have moved during the 'missing' time and see whether they are similar. Perhaps you can design the switch to minimise this 'rollout' effect. Bear in mind that there are probably other factors at work introducing other errors so you shouldn't expect to get a perfect answer.

  pinMode(nc, INPUT); //sets analog pin as input from switch

No, it doesn't. Analog pins are, by definition, input only. The pinMode() function does NOTHING for analog pins. You are, instead, diddling with a digital pin in a way that you may not want to be doing.

caposc:
For the switch, a digital pin may work, but that is prior to the timing. The peizo requires an analog read so I am stuck with using analogRead in my while loop.

The piezo itself doesn't care. If the voltage swing when the knock happens is enough to make the Arduino digital pin transition from LOW to HIGH (or HIGH to LOW), then an interrupt would be a perfect way to get an accurate time of the ball landing, since there is no polling to take up time until the knock is detected. It would also be possible to use photosensing to see when the ball hits bottom.

As well, you might want to look at a better way to trigger the beginning of the fall. If you use a photodetector, there would be no question of springiness or deflection of the ball at release time.

Another possibility is to use a platform on which the ball sits, and have the Arduino release the platform with a solenoid, allowing it to fall (rotating out of the way around a hinge point).

Bottom line, of course, is that gravity, unless it is wildly out of whack at your location, serves as its own calibrarion. Drop the ball, time it, and scale the calculation. Do this in a number of places, preferably in areas that have been tested, and for which you know the value, and you'll have your demonstration machine operating perfectly.

PeterH - You may be onto something. I never considered a 3mm initial drop to be significant, but it may just be the difference here. I will take some data with my students this week and get back to you. This may be a hardware problem after all...

lar3ry - Thanks for the idea, but the piezo only rarely spikes above 3V, usually around 1.0-2.0V. As for the workaround of just programming in a fudge-factor, I have thought about it, but wanted to get the system working properly when I start to incorporate other sensors. Devising a fudge-factor for each one would be a problem. Eventually, I would like to get this deployable so anyone anywhere can build a basic secondary school physics lab with very little money out of a few parts, some wood and an Arduino. Variations in the sensors they have available or choose could present a whole new level of problems.

As for the solenoid trap door, if PeterH turns out to be right, then the physical response time from a solenoid would be comparable to the 2-3 hundredths of a second for that first 3mm and I am back to square one.

One more thing, just in case.
Is your distance measurement accurate? Are you measuring from the top or the bottom of the ball in the top position (should be bottom, of course).