Stopwatch for Pinewood Derby

Ok, that makes a lot of sense. I had thought that you were only using the pull ups for testing, but wasn’t sure.
Here’s what I’m thinking of building, physically:

Then all I have to do is vary the resistor value until I get the TTL levels you describe.
I’d also need to change the setup function to this, right? (Or would I have to change them so as to not use the pullups?)

// Interrupt Driven Pinewood Derby Timer
// BDub @ Technobly.com 3/19/2014
//
// All inputs are pulled high with internal pullups.
// GND D2 to reset and run the timer for all lanes.
// GND A6, A7, D3 and D4 to stop the timer for each
// lane and display the results.
//
// D0-D3 were initially chosen, but because 
// individual control of dis/en'abling their
// interrupt handlers was not available, A6, A7
// D3 and D4 were chosen instead.  D0, D1, D2
// A0, A1, A3, A4 are all tied to one interrupt
// handler, so for the one remaining input any 
// of these will do.  D2 was chosen to allow
// for the most flexibility of the remaining inputs.
// 
// EXTI_ClearITPendingBit() is necessary to clear
// the interrupt Pending register, or interrupts 
// will fire immediately after enabling the 
// interrupt handlers again.
//
// DWT->CYCCNT was used for the timer instead of 
// micros() which wraps this hardware counter and
// returns the number of microseconds associated
// with it; because it's not clean to handle the 
// case where micros() wraps at 59.652323 seconds.
// This give us 72x more resolution on the timing
// anyway and it's super easy to deal with wrapping.
// It just works out through subtraction of unsigned
// 32-bit variables; as long as you don't time 
// something longer than 59.652323 seconds.
//
//==================================================

#include "application.h"

void startRace(void);
void lane1(void);
void lane2(void);
void lane3(void);
void lane4(void);
bool showStartMsg = false;
bool raceEnded = true; // start off assuming the race has not started.
volatile uint32_t timeStart1;
volatile uint32_t timeStart2;
volatile uint32_t timeStart3;
volatile uint32_t timeStart4;
volatile uint32_t timeEnd1;
volatile uint32_t timeEnd2;
volatile uint32_t timeEnd3;
volatile uint32_t timeEnd4;
uint32_t startTime;
const uint32_t DISQUALIFIED_TIME = 20 * 1000;  // in milliseconds (20 seconds)

void setup()
{
  Serial.begin(115200);
  while(!Serial.available()) SPARK_WLAN_Loop(); // Waiting for user to open terminal and press ENTER
  // Enter waiting state, waiting for D2 to go low.
  Serial.println("==================================================");
  Serial.println("Waiting for race to start.");

  //setup pins
  pinMode(D2, INPUT); // startRace trigger
  pinMode(A6, INPUT); // lane1
  pinMode(A7, INPUT); // lane2
  pinMode(D3, INPUT); // lane3
  pinMode(D4, INPUT); // lane4

  //setup actions to perform on interrupts
  attachInterrupt(D2, startRace, FALLING); // startRace
  attachInterrupt(A6, lane1, FALLING); // lane1
  attachInterrupt(A7, lane2, FALLING); // lane2
  attachInterrupt(D3, lane3, FALLING); // lane3
  attachInterrupt(D4, lane4, FALLING); // lane4

  //Stop listening to interrupts on all lanes
  NVIC_DisableIRQ(EXTI0_IRQn); // A6 "Lane 1"
  NVIC_DisableIRQ(EXTI1_IRQn); // A7 "Lane 2"
  NVIC_DisableIRQ(EXTI4_IRQn); // D3 "Lane 3"
  NVIC_DisableIRQ(EXTI3_IRQn); // D4 "Lane 4"
}

void loop()
{
  //wait for race to end
  if(!raceEnded) {
    //wait for all lanes to have times (this includes any empty lanes, which will timeout at 20 seconds)
    if((timeEnd1 != timeStart1 && timeEnd2 != timeStart2 && timeEnd3 != timeStart3 && timeEnd4 != timeStart4) || (millis() - startTime) > DISQUALIFIED_TIME) {

      double tempTime;

      Serial.println("Race Finished!");
      Serial.println("======= Lane Times in seconds =======");
      
      //output lane times
      Serial.print("Lane 1: "); 
      tempTime = (double)(timeEnd1 - timeStart1)/72000000.0;
      if(tempTime != 0.0) Serial.println(tempTime,8);
      else Serial.println("DNF");
      delay(50);
      Serial.print("Lane 2: "); 
      tempTime = (double)(timeEnd2 - timeStart2)/72000000.0;
      if(tempTime != 0.0) Serial.println(tempTime,8);
      else Serial.println("DNF");
      delay(50);
      Serial.print("Lane 3: "); 
      tempTime = (double)(timeEnd3 - timeStart3)/72000000.0;
      if(tempTime != 0.0) Serial.println(tempTime,8);
      else Serial.println("DNF");
      delay(50);
      Serial.print("Lane 4: "); 
      tempTime = (double)(timeEnd4 - timeStart4)/72000000.0;
      if(tempTime != 0.0) Serial.println(tempTime,8);
      else Serial.println("DNF");
      delay(50);
      
      //cleanup
      raceEnded = true; // prevents results from being displayed over and over  
      EXTI_ClearITPendingBit(EXTI_Line5); // D2 "startRace"
      NVIC_EnableIRQ(EXTI9_5_IRQn); // D2
      
      // Enter waiting state, waiting for D2 to go low.
      Serial.println("==================================================");
      Serial.println("Waiting for race to start.");
    }
  }

  if(showStartMsg) {
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("==================================================");
    Serial.println("Race started!");
    showStartMsg = false;
    raceEnded = false;
    startTime = millis(); // Capture the rough start time, for disqualification timer
  }
}

void lane1()
{
  NVIC_DisableIRQ(EXTI0_IRQn); // stop listening for an interrupt on A6
  timeEnd1 = DWT->CYCCNT; //get the lane time (instead of using micros();)
}

void lane2()
{
  NVIC_DisableIRQ(EXTI1_IRQn); // stop listening for an interrupt on A7
  timeEnd2 = DWT->CYCCNT; //get the lane time (instead of using micros();)
}

void lane3()
{
  NVIC_DisableIRQ(EXTI4_IRQn); // stop listening for an interrupt on D3
  timeEnd3 = DWT->CYCCNT; //get the lane time (instead of using micros();)
}

void lane4()
{
  NVIC_DisableIRQ(EXTI3_IRQn); // stop listening for an interrupt on D4
  timeEnd4 = DWT->CYCCNT; //get the lane time (instead of using micros();)
}

void startRace()
{
  NVIC_DisableIRQ(EXTI9_5_IRQn); // stop listening for an interrupt on D2
  timeStart1 = DWT->CYCCNT; // get the start time for each lane
  timeStart2 = timeStart1; // set the start time for each line the same
  timeStart3 = timeStart1; // set the start time for each line the same
  timeStart4 = timeStart1; // set the start time for each line the same
  timeEnd1 = timeStart1; // set the end time to the start time temporarily
  timeEnd2 = timeStart1; // set the end time to the start time temporarily
  timeEnd3 = timeStart1; // set the end time to the start time temporarily
  timeEnd4 = timeStart1; // set the end time to the start time temporarily
  EXTI_ClearITPendingBit(EXTI_Line0); // Reset the interrupt for A6 "Lane 1"
  EXTI_ClearITPendingBit(EXTI_Line1); // Reset the interrupt for A7 "Lane 2"
  EXTI_ClearITPendingBit(EXTI_Line4); // Reset the interrupt for D3 "Lane 3"
  EXTI_ClearITPendingBit(EXTI_Line3); // Reset the interrupt for D4 "Lane 4"
  NVIC_EnableIRQ(EXTI0_IRQn); // Start listening for an interrupt on A6 "Lane 1"
  NVIC_EnableIRQ(EXTI1_IRQn); // Start listening for an interrupt on A7 "Lane 2"
  NVIC_EnableIRQ(EXTI4_IRQn); // Start listening for an interrupt on D3 "Lane 3"
  NVIC_EnableIRQ(EXTI3_IRQn); // Start listening for an interrupt on D4 "Lane 4"
  showStartMsg = true; //indicate that the race has started
}

Here is the code I used to test the photoresistors to make sure the swing was enough into the TTL ranges to register. After I got everything hooked up and LED flashlights (just as cheap as lasers but more visible), I ran this code and triggered each sensor just to make sure everything works.

void setup()
{
  Serial.begin(115200);
  while(!Serial.available()) SPARK_WLAN_Loop(); // Open terminal and press ENTER
  Serial.println("Reading...");
  pinMode(D2, INPUT);
  pinMode(D3, INPUT);
  pinMode(D4, INPUT);
  pinMode(A6, INPUT);
  pinMode(A7, INPUT);
  
}

void loop()
{
    Serial.print("D2: ");Serial.println(digitalRead(D2));
    Serial.print("D3: ");Serial.println(digitalRead(D3));
    Serial.print("D4: ");Serial.println(digitalRead(D4));
    Serial.print("A6: ");Serial.println(digitalRead(A6));
    Serial.print("A7: ");Serial.println(digitalRead(A7));
    Serial.println("====================");
    delay(1000);
}