Photo-finish timer - First project

Newbie to Arduino, but had a lot of fun on a very useful project. My son (sixth grade) was doing a science fair project that required him to time how long it took to roll a skateboard down our driveway with various variables introduced. When I explained to him that a stopwatch would never be accurate enough for measurement, he asked me if I could help. Of course! I had bought an Arduino a while back but never used it for anything - here was my chance.

Here's how we set up the experiment - simple but effective. At both the top and bottom of the track there is a photoresistor (held in place by bricks) with a flashlight shining in it. The photoresistors are wired to the Arduino, which is connected by USB to my laptop to receive serial output. (The propane cylinder has nothing to do with it - I smoked a turkey for Thanksgiving Day.)

The photoresistors are inside the wire spools, which provide shielding from excessive ambient light.

I originally planned to solder it up, but once I prototyped it with a breadboard I decided that was good enough for our purpose. For each photoresistor, there is a potentiometer to set its light sensitivity and an LED to indicate whether it is on (receiving light) or off. There's a Reset button to get the timer ready for each test run. (The photoresistors were disconnected and I stuck them back on for the pic - looks like I got them in the wrong place. Oh well.)

Even though I don't write code on a daily basis anymore, I still think of myself as a programmer, so I hacked together a sketch that worked fine. If anyone is interested, let me know and I'll post the code here.

This was fun for me and a great father/son activity. The timer worked great. Thanks Arduino!

Very nice! Looks like something out of a MythBuster show!

I'm glad to see that you found a reason to use your Arduino and I love to hear about family projects like that. Have you handed over the keys to him yet? (Keyboard keys, not car keys!)

You have a good project writeup. Please post the code so we can look it over to get some new ideas for our projects. And what was the result of the experiment?

The results of the experiment were not what he hypothesized - that putting weight at the back would make it go faster (based on pinewood derby experience) - in fact that was the slowest. He is still mulling over his findings and conclusions. I provided "engineering" support, but I'm letting him do the "science" on his own. Regardless, he is thinking and learning, so I consider it a huge success.

Here's the code. Feedback welcome - it works, but there might be better ways to do it. I think it's pretty clean, but there might be superfluous fragments of trial and error code hanging around - don't let that confuse you if so.

// CONSTANTS
// state
const int stateStandby = 0;
const int stateError = -1;
const int stateReady = 1;
const int stateTiming = 2;
const int stateFinished = 3;
const int DARK = 0;
const int LIGHT = 1;

// SKETCH VARIABLES
//analog pins
int apinPr1 = 0;
int apinPr2 = 1;
int apinPot1a = 2;
int apinPot2a = 4;

//digital pins
int pinBtnInit = 2; // pin for the initialize button
int pinLedPr1 = 13;
int pinLedPr2 = 12;

//process control variables
int state;
unsigned long timeStart;
unsigned long timeStop;
unsigned long time;

void setup()
{
  pinMode(pinBtnInit, INPUT);
  pinMode(pinLedPr1, OUTPUT);
  pinMode(pinLedPr2, OUTPUT);
  
  state = stateStandby;
  
  Serial.begin(9600);           // set up Serial library at 9600 bps
  Serial.println("Initialized");
}


void loop()
{
 // read the photoresistors
 int pr1 = readPhotoresistor(apinPr1, apinPot1a, pinLedPr1); 
 int pr2 = readPhotoresistor(apinPr2, apinPot2a, pinLedPr2);
 
 // check the state of the pushbutton
 int btnState = digitalRead(pinBtnInit);
 
 // reset the state to Ready when the button is pushed
 // (don't reset if it is already Ready just to avoid cluttering the output stream)
 if (btnState == LOW) {
   if (pr1==LIGHT && pr2==LIGHT) {
     if (state!=stateReady) {
       state = stateReady;
       Serial.println("State = Ready");
     }
   }
   else {
     if (state!=stateError) {
       state = stateError;
       Serial.println("State = Error: Not Ready");
     }
   }
 }

 // if Ready, then check the photoresistors and record time
 if (state==stateReady) {
   if (pr2==DARK) {
     state=stateError;
     Serial.println("State = Error: PR2 out of sequence");
   }
   else {
     if (pr1==DARK) {
       state=stateTiming;
       timeStart = millis();
       Serial.println("State = Timing");
     }
   }
 }
 
 // control timer
 if (state==stateTiming && pr2==DARK) {
    state=stateFinished;
    timeStop = millis();
    time = timeStop - timeStart;
    Serial.print("State = Finished, time = ");
    Serial.print(time);
    Serial.print(" milliseconds");
    Serial.println();
 }
}

int readPhotoresistor(int pinLight, int pinPot, int pinLED) {
 int lightLevel = analogRead(pinLight); //Read the
                                        // lightlevel
 lightLevel = map(lightLevel, 0, 1023, 0, 255); 
         //adjust the value 0 to 1023 to span 0 to 255
 
 lightLevel = 255 - lightLevel; // reverse the value to make it more intuitive
 
 int potBoost = analogRead(pinPot);
  
 potBoost = map(potBoost, 0, 1023, -100, 100); // arbitrary range - trial and error

 lightLevel = lightLevel + potBoost;
 
 // arbitrary light level - trial and error
 if (lightLevel >= 200) {
   digitalWrite(pinLED, HIGH);
   return LIGHT;
 }
 else {
   digitalWrite(pinLED, LOW);
   return DARK;
 } 
}

Moderator edit:
</mark> <mark>[code]</mark> <mark>

</mark> <mark>[/code]</mark> <mark>
tags added.

Well done,

remarks on the code:

  1. use a higher baud rate as 9600 baud is rather slow, 115200 is 12x as fast so leaves more time to check the photoresistors

readPhotoResistor() does far more than that so yo might consider splitting so the function names cover their functionality.

int readPhotoresistor(int pinLight, int pinPot)
{
  int lightlevel = analogRead(pinLight);
  lightlevel = map(lightlevel , 0, 1023, 255, 0); //adjust the value 0 to 1023 to span 255 to 0 => implicit reversing!!
                                                        // or optimized  lightlevel = 255 - analogRead(pinLight)/4;
 
  int potBoost = analogRead(pinPot);
  potBoost = map(potBoost, 0, 1023, -100, 100); // arbitrary range - trial and error

  lightlevel = lightlevel + potBoost;
  return lightlevel ;
}

void setLed(int lightlevel, int pinLED)
{
  digitalWrite(pinLED, lightlevel >= 200);
}

int interpretLightLevel(int lightlevel)
{
  if (lightlevel >= 200) return LIGHT;
  return DARK;
}

You could consider to make int readPotBoost() a separate function too. If functions do one thing good the chance of reuse increases a lot (they become basic building blocks)