Monday, November 10, 2008

Timecode Based Stopwatch

A stopwatch that is timecode based. This will be the foundation for all of my motion control R and D. Eventually I hope to drive all of the mechanics by a sync pulse from a video camera. Until then this setup will have to suffice.



Here's a copy of the code used in this project.


/* Frame Rate Stopwatch By Daniel Thompson http://danthompsonsblog.blogspot.com
*
* This sketch is based on Paul Badger's StopWatch sketch which can be found here:
*
http://arduino.cc/en/Tutorial/Stopwatch
*
* The Frame Rate Stopwatch sketch runs at 25fps (PAL Video) by default and has an adjustable frame rate.
* Just change the framerate variable to suit your needs.
* Demonstrates using millis(), pullup resistors,
* making two things happen at once, printing fractions
*
* Physical setup: momentary switch connected to pin 2, other side connected to ground
* LED with series resistor between pin 13 and ground
* LCD Display setup as described in this tutorial:
*
http://www.ladyada.net/learn/arduino/lcd.html
* Easydriver v3.1 board. by Brian Schmalz
http://schmalzhaus.com/EasyDriver/
* Sparkfun Stepper Motor Connected to the Easydriver as described in this tutorial:
*
http://danthompsonsblog.blogspot.com/2008/09/easydriver-v31-tutorial.html
* 12v 0.4a regulated DC power supply.
*
* ///////////////////////////////////////////////////////////////////////////////////////
* Hardware hookup guide for Jaycar's 2x16 character backlit LCD Display MODEL No.QP5518
* ///////////////////////////////////////////////////////////////////////////////////////
*
* Note: Includes hookup info for use with a 10k Pot for contrast control.
*
* connect LCD PIN DB11 ------> Output PIN 7
* connect LCD PIN DB12 ------> Output PIN 8
* connect LCD PIN DB13 ------> Output PIN 9
* connect LCD PIN DB14 ------> Output PIN 10
* connect LCD PIN R/W -------> Ground
* connect LCD PIN 5v ------------> 10k Pot (left pin)and 5v supply
* connect LCD PIN Contrast ------> 10k Pot (middle pin)
* connect Ground -------> 10k POT (right pin)
* connect LCD PIN Gnd 0v ------> Ground
* connect LCD PIN REGISTOR SELECT ------> Output PIN 11
* connect LCD PIN ENABLE SIGNAL ------> Output PIN 12
* connect LCD PIN Gnd 0v ------> Ground
*/




#include
<LCD4Bit.h>
//create object to control an LCD.
//number of lines in display=1
LCD4Bit lcd = LCD4Bit(1);

#define ledPin 13 // LED connected to digital pin 13
#define buttonPin 2 // button on pin 2
#define steppin 5 // Easydriver step pin on pin 5
#define dirpin 3 // Easydriver step pin on pin 3

int value = LOW; // previous value of the LED
int buttonState; // variable to store button state
int lastButtonState; // variable to store last button state
int blinking; // condition for blinking - timer is timing
int frameRate = 25; // the frame rate (frames per second) at which the stopwatch runs - Change to suit
long interval = (1000/frameRate); // blink interval
long previousMillis = 0; // variable to store last time LED was updated
long startTime ; // start time for stop watch
long elapsedTime ; // elapsed time for stop watch
int fractional; // variable used to store fractional part of Frames
int fractionalSecs; // variable used to store fractional part of Seconds
int fractionalMins; // variable used to store fractional part of Minutes
int elapsedFrames; // elapsed frames for stop watch
int elapsedSeconds; // elapsed seconds for stop watch
int elapsedMinutes; // elapsed Minutes for stop watch
char buf[10]; // string buffer for itoa function

void setup()
{
Serial.begin(
9600);
pinMode(ledPin, OUTPUT);
// sets the digital pin as output
lcd.init(); // intialise the LCD.
pinMode(buttonPin, INPUT); // not really necessary, pins default to INPUT anyway
digitalWrite(buttonPin, HIGH); // turn on pullup resistors. Wire button so that press shorts pin to ground.
pinMode(dirpin, OUTPUT);
pinMode(steppin, OUTPUT);
}

void loop(){

///////////////////////////////////////
// Initiate LED and Step Pin States
///////////////////////////////////////

digitalWrite(steppin, LOW);
// Initiate the Easy Driver step pin ready for a rising edge
digitalWrite(ledPin, LOW); // set the Led to Low

//////////////////////////////
// Check for button press
//////////////////////////////
buttonState = digitalRead(buttonPin); // read the button state and store

// check for a high to low transition if true then found a new button press while clock is not running - start the clock
if (buttonState == LOW && lastButtonState == HIGH && blinking == false){
startTime
= millis(); // store the start time
blinking = true; // turn on blinking while timing
delay(5); // short delay to debounce switch
lastButtonState = buttonState; // store buttonState in lastButtonState, to compare next time
}

// check for a high to low transition if true then found a new button press while clock is running - stop the clock and report
else if (buttonState == LOW && lastButtonState == HIGH && blinking == true){
blinking
= false; // turn off blinking, all done timing
lastButtonState = buttonState; // store buttonState in lastButtonState, to compare next time

/////////////////////////////////////////////
// Routine to report elapsed time
/////////////////////////////////////////////
elapsedTime = millis() - startTime; // store elapsed time
elapsedMinutes = (elapsedTime / 60000L);
elapsedSeconds
= (elapsedTime / 1000L); // divide by 1000 to convert to seconds - then cast to an int to print
elapsedFrames = (elapsedTime / interval); // divide by 40 to convert to 1/25 of a second - then cast to an int to print
fractional = (int)(elapsedFrames % frameRate); // use modulo operator to get fractional part of 25 Frames
fractionalSecs = (int)(elapsedSeconds % 60L); // use modulo operator to get fractional part of 60 Seconds
fractionalMins = (int)(elapsedMinutes % 60L); // use modulo operator to get fractional part of 60 Minutes
lcd.clear(); // clear the LDC

if (fractionalMins < 10){ // pad in leading zeros
lcd.printIn("0"); // add a zero
}

lcd.printIn(itoa(fractionalMins, buf,
10)); // convert the int to a string and print a fractional part of 60 Minutes to the LCD
lcd.printIn(":"); //print a colan.

if (fractionalSecs < 10){ // pad in leading zeros
lcd.printIn("0"); // add a zero
}

lcd.printIn(itoa(fractionalSecs, buf,
10)); // convert the int to a string and print a fractional part of 60 Seconds to the LCD
lcd.printIn(":"); //print a colan.



if (fractional < 10){ // pad in leading zeros
lcd.printIn("0"); // add a zero
}

lcd.printIn(itoa(fractional, buf,
10)); // convert the int to a string and print a fractional part of 25 Frames to the LCD
}

else{
lastButtonState
= buttonState; // store buttonState in lastButtonState, to compare next time
}

////////////////////////////////////////////////////
// run commands at the specified time interval
////////////////////////////////////////////////////

// blink routine - blink the LED while timing
// check to see if it's time to blink the LED; that is, the difference
// between the current time and last time we blinked the LED is larger than
// the interval at which we want to blink the LED.

if ( (millis() - previousMillis > interval) ) {

if (blinking == true){
previousMillis
= millis(); // remember the last time we blinked the LED

digitalWrite(ledPin, HIGH);
// Pulse the LED for Visual Feedback
digitalWrite(steppin, HIGH); // create a rising edge for the Easydriver to step the motor once


elapsedTime
= millis() - startTime; // store elapsed time
elapsedMinutes = (elapsedTime / 60000L); // divide by 60000 to convert to minutes - then cast to an int to print
elapsedSeconds = (elapsedTime / 1000L); // divide by 1000 to convert to seconds - then cast to an int to print
elapsedFrames = (elapsedTime / interval); // divide by 40 to convert to 1/25 of a second - then cast to an int to print
fractional = (int)(elapsedFrames % frameRate);// use modulo operator to get fractional part of 25 Frames
fractionalSecs = (int)(elapsedSeconds % 60L); // use modulo operator to get fractional part of 60 Seconds
fractionalMins = (int)(elapsedMinutes % 60L); // use modulo operator to get fractional part of 60 Minutes
lcd.clear(); // clear the LDC

if (fractionalMins < 10){ // pad in leading zeros
lcd.printIn("0"); // add a zero
}

lcd.printIn(itoa(fractionalMins, buf,
10)); // convert the int to a string and print a fractional part of 60 Minutes to the LCD
lcd.printIn(":"); //print a colan.

if (fractionalSecs < 10){ // pad in leading zeros
lcd.printIn("0"); // add a zero
}

lcd.printIn(itoa(fractionalSecs, buf,
10)); // convert the int to a string and print a fractional part of 60 Seconds to the LCD
lcd.printIn(":"); //print a colan.

if (fractional < 10){ // pad in leading zeros
lcd.printIn("0"); // add a zero
}
lcd.printIn(itoa((fractional), buf,
10)); // convert the int to a string and print a fractional part of 25 Frames to the LCD
}

else{
digitalWrite(ledPin, LOW);
// turn off LED when not blinking
}
}

}



10 comments:

dbw said...

Hi, Dan,
Thanks so much for this.
I need to build one of these with 8 stop watches all moving in sync. Any ideas on how to do that?

search engine positioning said...

These is great to share with us. This will be the basis of my motion control R and D. Finally, I hope to control all the mechanical timing by a pulse of a video camera. So far, this service will suffice.

jon said...

hey do you think we can implement lap timings into it? if yes, do you have the codes? email me at mat_latine@hotmail.com

Dan Thompson said...

Hi Jon,

Yes it could be implented and no I do not have the code. Why don't you have a try yourself!? Use modulo operator to get fractional of whatever you lap would be.

Regards,

Dan.

jon said...

Hi Dan, i have just started programming a few weeks ago and i am required to implement lap timings to my project work. this is a lil overwhelming for me! haha, i was wondering if you could help me out.

Dan Thompson said...

hey jon, I understand, I would say this example is aimed at intermediate arduino users which is why you are probably struggling with it. What is your specific question about this piece of code? What part of it are you trying to understand?

jon said...

Hey Dan,

pardon me for the late reply. exactly..
elapsedTime = millis() - startTime; // store elapsed time
elapsedMinutes = (elapsedTime / 60000L); // divide by 60000 to convert to minutes - then cast to an int to print
elapsedSeconds = (elapsedTime / 1000L); // divide by 1000 to convert to seconds - then cast to an int to print
elapsedFrames = (elapsedTime / interval); // divide by 40 to convert to 1/25 of a second - then cast to an int to print
fractional = (int)(elapsedFrames % frameRate);// use modulo operator to get fractional part of 25 Frames
fractionalSecs = (int)(elapsedSeconds % 60L); // use modulo operator to get fractional part of 60 Seconds
fractionalMins = (int)(elapsedMinutes % 60L); // use modulo operator to get fractional part of 60 Minutes
lcd.clear(); // clear the LDC



i understand this part of the code is the main function of the stopwatch but i do not know how it exactly works. can you enlighten me on that part? i might probably be able to implement it if i understand. god bless me, the panel review for my project work it tmrw..i have no improvements to my work..

Dan Thompson said...

Jon, you are a bad student leaving this until the last minute.

It's basically a counter but the modulo operator resets the counter every time it reaches the limit.

Did you look up the commands?
http://arduino.cc/en/Reference/Modulo
http://arduino.cc/en/Reference/LiquidCrystalClear
http://arduino.cc/en/Reference/Millis

and for the elapsedTime variable, look at this simple example:
http://arduino.cc/en/Tutorial/BlinkWithoutDelay

Once you understand the links above, the code and comments should make sense.

Good Luck!

Dan.

jon said...

hey Dan,
i have tons of assignments to complete, i know i shouldnt be giving excuses but programming is not my forte.

i have read it, but i have yet to decipher the codes..

Dan Thompson said...

Jon,

Programming is not my forte either.
Your question was "i do not know how it exactly works. "

Every line of the code above is commented. What comment don't you understand? Be more specific with your questions please. I work full time and my time is precious to me, you need to understand that unless you can frame your questions clearly, I cannot afford the time it takes to try to intemperate them.

Regards,

Dan.

p.s. if you are struggling with this, maybe you should get some advice from your teacher on how to tackle it? He may have a better idea of you problem and it's context.