Troubleshooting False Race Lane detection Finishes on Arduino Mega 2560

Hello everyone,

My name is Scott, and I run a non-profit organization that organizes soapbox derby races. First, I want to say how much I appreciate any help and resources. Being a nonprofit, we don’t have a lot of funding, but I’m very hands-on. If I don’t know how to do something, I find someone to teach me or figure it out myself.

I’m currently working on upgrading our race timing system from an Arduino Uno R3 to a Mega 2560 to allow for greater expandability. The core functionality of the system involves starting a race via a gate switch and detecting when each racer crosses the finish line using sensors.

Future Plans & Expansion:

Once I resolve this issue, I plan to expand the system in several ways. This is why I’m moving to the Mega from the Uno.

  • Timing Light System: A feature that will signal when racers should start braking after crossing the finish line. I’ve already created the code for this, which will connect to the Mega once the core timing system is stable.
  • Advanced Scoreboards: I’ve built a really big 5-digit, 7-segment display for each lane, which will connect via Serial1 (Lane 1) and Serial2 (Lane 2). The Mega communicates with an Uno inside the scoreboard via RS485, which then drives five SparkFun large digit drivers. These control five 8-channel relays to activate the segments. The system is working well, and once the race timing issue is fixed, I plan to integrate it fully. I have been using DeepSeek and ChatGPT to help me with this project, as I have only limited knowledge of coding.

What I Have Done So Far:

  • Migrated the existing timing code from the Uno to the Mega (not working).
  • Tried the Uno again (working fine).
  • Verified pin assignments and checked the Mega.
  • Used AI-based debugging tools to scan for potential coding issues.
  • Compared how serial communication and input handling differ between the Uno and Mega.
  • Tested the system with minimal connections using jumpers instead of physical detection switches.

The Issue:

When the race starts and the gate switch is released, both lanes instantly register as finished, showing a tie with a time of 0.103 seconds—even though no cars have crossed the finish line.

The reset function works fine, and just copying the code from the Uno to the Mega makes all the LEDs and serial messages to the PC work, but the finish detection is triggering incorrectly.

I suspect the problem could be related to how the Mega processes input signals differently.

This is the following links that will take you to a Pinewood Derby open-source timing system for Arduino, this is what were using.

pin out print
timer file

We change the following settings to work for our needs:

  1. #define NUM_LANES 1 to #define NUM_LANES 2 // number of lanes
  2. //#define LED_DISPLAY 1 to #define LED_DISPLAY 1 // Enable lane place/time displays
  3. all pins stay the same except the display (uno) SDA pin-A4 and SCL pin-A5 (mega )SDA pin-D20 and SCL pin-D21

If this issue has come up before or if you have any suggestions on where to look next, I’d really appreciate your guidance.

Thank you for your time!

Best regards,
Scott
Windsor Essex Soapbox Derby Association
WWW.WESDA.CA

/*================================================================================*
   Pinewood Derby Timer                                Version 3.10 - 12 Dec 2020
   www.dfgtec.com/pdt

   Flexible and affordable Pinewood Derby timer that interfaces with the 
   following software:
     - PD Test/Tune/Track Utility
     - Grand Prix Race Manager software

   Refer to the website for setup and usage instructions.


   Copyright (C) 2011-2020 David Gadberry

   This work is licensed under the Creative Commons Attribution-NonCommercial-
   ShareAlike 3.0 Unported License. To view a copy of this license, visit 
   http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to 
   Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 
   94041, USA.
 *================================================================================*/

/*-----------------------------------------*
  - TIMER CONFIGURATION -
 *-----------------------------------------*/
#define NUM_LANES    1                 // number of lanes
#define GATE_RESET   0                 // Enable closing start gate to reset timer

//#define LED_DISPLAY  1                 // Enable lane place/time displays
//#define DUAL_DISP    1                 // dual displays per lane (4 lanes max)
//#define DUAL_MODE    1                 // dual display mode
//#define LARGE_DISP   1                 // utilize large Adafruit displays (see website)

#define SHOW_PLACE   1                 // Show place mode
#define PLACE_DELAY  3                 // Delay (secs) when displaying place/time
#define MIN_BRIGHT   0                 // minimum display brightness (0-15)
#define MAX_BRIGHT   15                // maximum display brightness (0-15)

/*-----------------------------------------*
  - END -
 *-----------------------------------------*/


#ifdef LED_DISPLAY                     // LED control libraries
#include "Wire.h"
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
#endif

/*-----------------------------------------*
  - static definitions -
 *-----------------------------------------*/
#define PDT_VERSION  "3.10"            // software version
#define MAX_LANE     6                 // maximum number of lanes (Uno)
#define MAX_DISP     8                 // maximum number of displays (Adafruit)

#define mREADY       0                 // program modes
#define mRACING      1
#define mFINISH      2
#define mTEST        3

#define START_TRIP   LOW               // start switch trip condition
#define NULL_TIME    99.999            // null (non-finish) time
#define NUM_DIGIT    4                 // timer resolution (# of decimals)
#define DISP_DIGIT   4                 // total number of display digits

#define PWM_LED_ON   220
#define PWM_LED_OFF  255
#define char2int(c) (c - '0') 

//
// serial messages                        <- to timer
//                                        -> from timer
//
#define SMSG_ACKNW   '.'               // -> acknowledge message

#define SMSG_POWER   'P'               // -> start-up (power on or hard reset)

#define SMSG_CGATE   'G'               // <- check gate
#define SMSG_GOPEN   'O'               // -> gate open

#define SMSG_RESET   'R'               // <- reset
#define SMSG_READY   'K'               // -> ready

#define SMSG_SOLEN   'S'               // <- start solenoid
#define SMSG_START   'B'               // -> race started
#define SMSG_FORCE   'F'               // <- force end
#define SMSG_RSEND   'Q'               // <- resend race data

#define SMSG_LMASK   'M'               // <- mask lane
#define SMSG_UMASK   'U'               // <- unmask all lanes

#define SMSG_GVERS   'V'               // <- request timer version
#define SMSG_DEBUG   'D'               // <- toggle debug on/off
#define SMSG_GNUML   'N'               // <- request number of lanes
#define SMSG_TINFO   'I'               // <- request timer information

/*-----------------------------------------*
  - pin assignments -
 *-----------------------------------------*/
byte BRIGHT_LEV   = A0;                // brightness level
byte RESET_SWITCH =  8;                // reset switch
byte STATUS_LED_R =  9;                // status LED (red)
byte STATUS_LED_B = 10;                // status LED (blue)
byte STATUS_LED_G = 11;                // status LED (green)
byte START_GATE   = 12;                // start gate switch
byte START_SOL    = 13;                // start solenoid

//                Display #    1     2     3     4     5     6     7     8
int  DISP_ADD [MAX_DISP] = {0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77};    // display I2C addresses

//                   Lane #    1     2     3     4     5     6
byte LANE_DET [MAX_LANE] = {   2,    3,    4,    5,    6,    7};                // finish detection pins

/*-----------------------------------------*
  - global variables -
 *-----------------------------------------*/
boolean       fDebug = false;          // debug flag
boolean       ready_first;             // first pass in ready state flag
boolean       finish_first;            // first pass in finish state flag

unsigned long start_time;              // race start time (microseconds)
unsigned long lane_time  [MAX_LANE];   // lane finish time (microseconds)
int           lane_place [MAX_LANE];   // lane finish place
boolean       lane_mask  [MAX_LANE];   // lane mask status

int           serial_data;             // serial data
byte          mode;                    // current program mode

float         display_level = -1.0;    // display brightness level

#ifdef LARGE_DISP
unsigned char msgGateC[] = {0x6D, 0x41, 0x00, 0x0F, 0x07};  // S=CL
unsigned char msgGateO[] = {0x6D, 0x41, 0x00, 0x3F, 0x5E};  // S=OP
unsigned char msgLight[] = {0x41, 0x41, 0x00, 0x00, 0x07};  // == L
unsigned char msgDark [] = {0x41, 0x41, 0x00, 0x00, 0x73};  // == d
#else
unsigned char msgGateC[] = {0x6D, 0x48, 0x00, 0x39, 0x38};  // S=CL
unsigned char msgGateO[] = {0x6D, 0x48, 0x00, 0x3F, 0x73};  // S=OP
unsigned char msgLight[] = {0x48, 0x48, 0x00, 0x00, 0x38};  // == L
unsigned char msgDark [] = {0x48, 0x48, 0x00, 0x00, 0x5e};  // == d
#endif
unsigned char msgDashT[] = {0x40, 0x40, 0x00, 0x40, 0x40};  // ----
unsigned char msgDashL[] = {0x00, 0x00, 0x00, 0x40, 0x00};  //   -
unsigned char msgBlank[] = {0x00, 0x00, 0x00, 0x00, 0x00};  // (blank)

#ifdef LED_DISPLAY                     // LED display control
Adafruit_7segment disp_mat[MAX_DISP];
#endif

#ifdef DUAL_MODE                       // uses 8x8 matrix displays
Adafruit_8x8matrix disp_8x8[MAX_DISP];
#endif

void initialize(boolean powerup=false);
void dbg(int, const char * msg, int val=-999);
void smsg(char msg, boolean crlf=true);
void smsg_str(const char * msg, boolean crlf=true);

/*================================================================================*
  SETUP TIMER
 *================================================================================*/
void setup()
{  
/*-----------------------------------------*
  - hardware setup -
 *-----------------------------------------*/
  pinMode(STATUS_LED_R, OUTPUT);
  pinMode(STATUS_LED_B, OUTPUT);
  pinMode(STATUS_LED_G, OUTPUT);
  pinMode(START_SOL,    OUTPUT);
  pinMode(RESET_SWITCH, INPUT);
  pinMode(START_GATE,   INPUT);
  pinMode(BRIGHT_LEV,   INPUT);
  
  digitalWrite(RESET_SWITCH, HIGH);    // enable pull-up resistor
  digitalWrite(START_GATE,   HIGH);    // enable pull-up resistor

  digitalWrite(START_SOL, LOW);

#ifdef LED_DISPLAY
  for (int n=0; n<MAX_DISP; n++)
  {
    disp_mat[n] = Adafruit_7segment();
    disp_mat[n].begin(DISP_ADD[n]);
    disp_mat[n].clear();
    disp_mat[n].drawColon(false);
    disp_mat[n].writeDisplay();

#ifdef DUAL_MODE
    disp_8x8[n] = Adafruit_8x8matrix();
    disp_8x8[n].begin(DISP_ADD[n]);
    disp_8x8[n].clear();
    disp_8x8[n].writeDisplay();
#endif
  }
#endif

  for (int n=0; n<MAX_LANE; n++)
  {
    pinMode(LANE_DET[n], INPUT);

    digitalWrite(LANE_DET[n], HIGH);   // enable pull-up resistor
  }
  set_display_brightness();

/*-----------------------------------------*
  - software setup -
 *-----------------------------------------*/
  Serial.begin(9600);
  smsg(SMSG_POWER);

/*-----------------------------------------*
  - check for test mode -
 *-----------------------------------------*/
  if (digitalRead(RESET_SWITCH) == LOW)
  {
    mode = mTEST;
    test_pdt_hw();
  }

/*-----------------------------------------*
  - initialize timer -
 *-----------------------------------------*/
  initialize(true);
  unmask_all_lanes();
}


/*================================================================================*
  MAIN LOOP
 *================================================================================*/
void loop()
{
  process_general_msgs();

  switch (mode)
  {
    case mREADY:
      timer_ready_state();
      break;
    case mRACING:
      timer_racing_state();
      break;
    case mFINISH:
      timer_finished_state();
      break;
  }
}


/*================================================================================*
  TIMER READY STATE
 *================================================================================*/
void timer_ready_state()
{
  if (ready_first)
  {
    set_status_led();
    clear_displays();

    ready_first = false;
  }

  if (serial_data == int(SMSG_SOLEN))    // activate start solenoid
  {
    digitalWrite(START_SOL, HIGH);
    smsg(SMSG_ACKNW);
  }
  
  if (digitalRead(START_GATE) == START_TRIP)    // timer start
  {
    start_time = micros();

    digitalWrite(START_SOL, LOW);

    smsg(SMSG_START);
    delay(100); 

    mode = mRACING; 
  }

  return;
}
  

/*================================================================================*
  TIMER RACING STATE
 *================================================================================*/
void timer_racing_state()
{
  int lanes_left, finish_order, lane_status[NUM_LANES];
  unsigned long current_time, last_finish_time;


  set_status_led();
  clear_displays();

  finish_order = 0;
  last_finish_time = 0;

  lanes_left = NUM_LANES;
  for (int n=0; n<NUM_LANES; n++)
  {
    if (lane_mask[n]) lanes_left--;
  }

  while (lanes_left)
  {
    current_time = micros();

    for (int n=0; n<NUM_LANES; n++) lane_status[n] = bitRead(PIND, LANE_DET[n]);    // read status of all lanes

    for (int n=0; n<NUM_LANES; n++)
    {
      if (lane_time[n] == 0 && lane_status[n] == HIGH && !lane_mask[n])    // car has crossed finish line
      {
        lanes_left--;

        lane_time[n] = current_time - start_time;

        if (lane_time[n] > last_finish_time)
        {
          finish_order++;
          last_finish_time = lane_time[n];
        }
        lane_place[n] = finish_order;

        update_display(n, lane_place[n], lane_time[n], SHOW_PLACE);
      }
    }
    
    serial_data = get_serial_data();

    if (serial_data == int(SMSG_FORCE) || serial_data == int(SMSG_RESET) || digitalRead(RESET_SWITCH) == LOW)    // force race to end
    {
      lanes_left = 0;
      smsg(SMSG_ACKNW);
    }
  }
    
  send_race_results();

  mode = mFINISH;

  return;
}


/*================================================================================*
  TIMER FINISHED STATE
 *================================================================================*/
void timer_finished_state()
{
  if (finish_first)
  {
    set_status_led();
    finish_first = false;
  }

  if (GATE_RESET && digitalRead(START_GATE) != START_TRIP)    // gate closed
  {
    delay(500);    // ignore any switch bounce

    if (digitalRead(START_GATE) != START_TRIP)    // gate still closed
    {
      initialize();    // reset timer
    } 
  } 

  if (serial_data == int(SMSG_RSEND))    // resend race data
  {
      smsg(SMSG_ACKNW);
      send_race_results();
  } 

  set_display_brightness();
  display_race_results();

  return;
}


/*================================================================================*
  PROCESS GENERAL SERIAL MESSAGES
 *================================================================================*/
void process_general_msgs()
{
  int lane;
  char tmps[50];


  serial_data = get_serial_data();

  if (serial_data == int(SMSG_GVERS))    // get software version
  {
      sprintf(tmps, "vert=%s", PDT_VERSION);
      smsg_str(tmps);
  } 

  else if (serial_data == int(SMSG_GNUML))    // get number of lanes
  {
      sprintf(tmps, "numl=%d", NUM_LANES);
      smsg_str(tmps);
  } 

  else if (serial_data == int(SMSG_TINFO))    // get timer information
  {
      send_timer_info();
  } 

  else if (serial_data == int(SMSG_DEBUG))    // toggle debug
  {
    fDebug = !fDebug;
    dbg(true, "toggle debug = ", fDebug);
  } 

  else if (serial_data == int(SMSG_CGATE))    // check start gate
  {
    if (digitalRead(START_GATE) == START_TRIP)    // gate open
    {
      smsg(SMSG_GOPEN);
    } 
    else
    {
      smsg(SMSG_ACKNW);
    } 
  } 

  else if (serial_data == int(SMSG_RESET) || digitalRead(RESET_SWITCH) == LOW)    // timer reset
  {
    if (digitalRead(START_GATE) != START_TRIP)    // only reset if gate closed
    {
      initialize();
    } 
    else
    {
      smsg(SMSG_GOPEN);
    } 
  } 

  else if (serial_data == int(SMSG_LMASK))    // lane mask
  {
    delay(100);
    serial_data = get_serial_data();

    lane = serial_data - 48;
    if (lane >= 1 && lane <= NUM_LANES)
    {
      lane_mask[lane-1] = true;

      dbg(fDebug, "set mask on lane = ", lane);
    }
    smsg(SMSG_ACKNW);
  }

  else if (serial_data == int(SMSG_UMASK))    // unmask all lanes
  {
    unmask_all_lanes();
    smsg(SMSG_ACKNW);
  }

  return;
}


/*================================================================================*
  TEST PDT FINISH DETECTION HARDWARE
 *================================================================================*/
void test_pdt_hw()
{
  int  lane_status[NUM_LANES];
  char ctmp[10];


  smsg_str("TEST MODE");
  set_status_led();
  delay(2000); 

/*-----------------------------------------*
   show status of lane detectors
 *-----------------------------------------*/
  while(true)
  {
    for (int n=0; n<NUM_LANES; n++)
    {
      lane_status[n] = bitRead(PIND, LANE_DET[n]);    // read status of all lanes

      if (lane_status[n] == HIGH)
      {
        update_display(n, msgDark);
      }
      else
      {
        update_display(n, msgLight);
      }
    }

    if (digitalRead(RESET_SWITCH) == LOW)  // break out of this test
    {
      clear_displays();
      delay(1000); 
      break;
    }
    delay(100);
  }

/*-----------------------------------------*
   show status of start gate switch
 *-----------------------------------------*/
  while(true)
  {
    if (digitalRead(START_GATE) == START_TRIP) 
    {
      update_display(0, msgGateO);
    }
    else
    {
      update_display(0, msgGateC);
    }

    if (digitalRead(RESET_SWITCH) == LOW)  // break out of this test
    {
      clear_displays();
      delay(1000); 
      break;
    }
    delay(100);
  }

/*-----------------------------------------*
   show pattern for brightness adjustment
 *-----------------------------------------*/
  while(true)
  {
#ifdef LED_DISPLAY
    set_display_brightness();

    for (int n=0; n<NUM_LANES; n++)
    {
      sprintf(ctmp,"%d%03d", (n+1), (int)display_level);

      disp_mat[n].clear();

      disp_mat[n].writeDigitNum(0, char2int(ctmp[0]), false);
      disp_mat[n].writeDigitNum(1, char2int(ctmp[1]), false);
      disp_mat[n].writeDigitNum(3, char2int(ctmp[2]), false);
      disp_mat[n].writeDigitNum(4, char2int(ctmp[3]), false);

      disp_mat[n].drawColon(false);
      disp_mat[n].writeDisplay();

#ifdef DUAL_DISP
#ifdef DUAL_MODE
      disp_8x8[n+4].clear();
      disp_8x8[n+4].setTextSize(1);
      disp_8x8[n+4].setRotation(3);
      disp_8x8[n+4].setCursor(2, 0);
      disp_8x8[n+4].print("X");
      disp_8x8[n+4].writeDisplay();
#else
      disp_mat[n+4].clear();

      disp_mat[n+4].writeDigitNum(0, char2int(ctmp[0]), false);
      disp_mat[n+4].writeDigitNum(1, char2int(ctmp[1]), false);
      disp_mat[n+4].writeDigitNum(3, char2int(ctmp[2]), false);
      disp_mat[n+4].writeDigitNum(4, char2int(ctmp[3]), false);

      disp_mat[n+4].drawColon(false);
      disp_mat[n+4].writeDisplay();
#endif
#endif
    }
#endif

    if (digitalRead(RESET_SWITCH) == LOW)  // break out of this test
    {
      clear_displays();
      break;
    }
    delay(1000);
  }
}


/*================================================================================*
  SEND RACE RESULTS TO COMPUTER
 *================================================================================*/
void send_race_results()
{
  float lane_time_sec;


  for (int n=0; n<NUM_LANES; n++)    // send times to computer
  {
    lane_time_sec = (float)(lane_time[n] / 1000000.0);    // elapsed time (seconds)

    if (lane_time_sec == 0)    // did not finish
    {
      lane_time_sec = NULL_TIME;
    }

    Serial.print(n+1);
    Serial.print(" - ");
    Serial.println(lane_time_sec, NUM_DIGIT);  // numbers are rounded to NUM_DIGIT
                                               // digits by println function
  }

  return;
}


/*================================================================================*
  RACE FINISHED - DISPLAY PLACE / TIME FOR ALL LANES
 *================================================================================*/
void display_race_results()
{
  unsigned long now;
  static boolean display_mode;
  static unsigned long last_display_update = 0;


  if (!SHOW_PLACE) return;

  now = millis();

  if (last_display_update == 0)  // first cycle
  {
    last_display_update = now;
    display_mode = false;
  }

  if ((now - last_display_update) > (unsigned long)(PLACE_DELAY * 1000))
  {
    dbg(fDebug, "display_race_results");

    for (int n=0; n<NUM_LANES; n++)
    {
      update_display(n, lane_place[n], lane_time[n], display_mode);
    }

    display_mode = !display_mode;
    last_display_update = now;
  }

  return;
}


/*================================================================================*
  SEND MESSAGE TO DISPLAY
 *================================================================================*/
void update_display(int lane, unsigned char msg[])
{

#ifdef LED_DISPLAY
  disp_mat[lane].clear();
#ifdef DUAL_DISP
#ifdef DUAL_MODE
  disp_8x8[lane+4].clear();
#else
  disp_mat[lane+4].clear();
#endif
#endif

  for (int d = 0; d<=4; d++)
  {
    disp_mat[lane].writeDigitRaw(d, msg[d]);
#ifdef DUAL_DISP
#ifdef DUAL_MODE
    if (d == 3)
    {
      disp_8x8[lane+4].setTextSize(1);
      disp_8x8[lane+4].setRotation(3);
      disp_8x8[lane+4].setCursor(2, 0);
      if (msg == msgBlank)
         disp_8x8[lane+4].print(" ");
      else
         disp_8x8[lane+4].print("-");
    }
#else
    disp_mat[lane+4].writeDigitRaw(d, msg[d]);
#endif
#endif
  }

  disp_mat[lane].writeDisplay();
#ifdef DUAL_DISP
#ifdef DUAL_MODE
  disp_8x8[lane+4].writeDisplay();
#else
  disp_mat[lane+4].writeDisplay();
#endif
#endif
#endif

  return;
}


/*================================================================================*
  UPDATE LANE PLACE/TIME DISPLAY
 *================================================================================*/
void update_display(int lane, int display_place, unsigned long display_time, int display_mode)
{
  int c;
  char ctime[10], cplace[4];
  double display_time_sec;
  boolean showdot;

//  dbg(fDebug, "led: lane = ", lane);
//  dbg(fDebug, "led: plce = ", display_place);
//  dbg(fDebug, "led: time = ", display_time);

#ifdef LED_DISPLAY
  if (display_mode)
  {
    if (display_place > 0)  // show place order
    {
      sprintf(cplace,"%1d", display_place);

      disp_mat[lane].clear();
      disp_mat[lane].drawColon(false);
      disp_mat[lane].writeDigitNum(3, char2int(cplace[0]), false);
      disp_mat[lane].writeDisplay();

#ifdef DUAL_DISP
      disp_mat[lane+4].clear();
      disp_mat[lane+4].drawColon(false);
      disp_mat[lane+4].writeDigitNum(3, char2int(cplace[0]), false);
      disp_mat[lane+4].writeDisplay();
#endif
    }
    else  // did not finish
    {
      update_display(lane, msgDashL);
    }
  }
  else                      // show finish time
  {
    if (display_time > 0)
    {
      disp_mat[lane].clear();
      disp_mat[lane].drawColon(false);

#ifdef DUAL_DISP
#ifdef DUAL_MODE
      disp_8x8[lane+4].clear();
      disp_8x8[lane+4].setTextSize(1);
      disp_8x8[lane+4].setRotation(3);
      disp_8x8[lane+4].setCursor(2, 0);
#else
      disp_mat[lane+4].clear();
      disp_mat[lane+4].drawColon(false);
#endif
#endif

      display_time_sec = (double)(display_time / (double)1000000.0);    // elapsed time (seconds)
      dtostrf(display_time_sec, (DISP_DIGIT+1), DISP_DIGIT, ctime);     // convert to string

//      Serial.print("ctime = ["); Serial.print(ctime); Serial.println("]");
      c = 0;
      for (int d = 0; d<DISP_DIGIT; d++)
      {
#ifdef LARGE_DISP
        showdot = false;
#else
        showdot = (ctime[c + 1] == '.');
#endif
        disp_mat[lane].writeDigitNum(d + int(d / 2), char2int(ctime[c]), showdot);    // time
#ifdef DUAL_DISP
#ifdef DUAL_MODE
        sprintf(cplace,"%1d", display_place);
        disp_8x8[lane+4].print(cplace[0]);
#else
        disp_mat[lane+4].writeDigitNum(d + int(d / 2), char2int(ctime[c]), showdot);    // time
#endif
#endif

        c++; if (ctime[c] == '.') c++;
      }

#ifdef LARGE_DISP
      disp_mat[lane].writeDigitRaw(2, 16);
#ifdef DUAL_DISP
#ifndef DUAL_MODE
      disp_mat[lane+4].writeDigitRaw(2, 16);
#endif
#endif
#endif

      disp_mat[lane].writeDisplay();
#ifdef DUAL_DISP
#ifdef DUAL_MODE
      disp_8x8[lane+4].writeDisplay();
#else
      disp_mat[lane+4].writeDisplay();
#endif
#endif
    }
    else  // did not finish
    {
      update_display(lane, msgDashT);
    }
  }
#endif

  return;
}


/*================================================================================*
  CLEAR LANE PLACE/TIME DISPLAYS
 *================================================================================*/
void clear_displays()
{
  dbg(fDebug, "led: CLEAR");

  for (int n=0; n<NUM_LANES; n++)
  {
    if (mode == mRACING || mode == mTEST)
    {
      update_display(n, msgBlank);     // racing
    }
    else
    {
      update_display(n, msgDashT);     // ready
    }
  }

  return;
}


/*================================================================================*
  SET LANE DISPLAY BRIGHTNESS
 *================================================================================*/
void set_display_brightness()
{
  float new_level;

#ifdef LED_DISPLAY
  new_level = long(1023 - analogRead(BRIGHT_LEV)) / 1023.0F * 15.0F;
  new_level = min(new_level, (float)MAX_BRIGHT);
  new_level = max(new_level, (float)MIN_BRIGHT);

  if (fabs(new_level - display_level) > 0.3F)    // deadband to prevent flickering 
  {                                              // between levels
    dbg(fDebug, "led: BRIGHT");

    display_level = new_level;

    for (int n=0; n<NUM_LANES; n++)
    {
      disp_mat[n].setBrightness((int)display_level);
#ifdef DUAL_DISP
#ifdef DUAL_MODE
      disp_8x8[n+4].setBrightness((int)display_level);
#else
      disp_mat[n+4].setBrightness((int)display_level);
#endif
#endif
    }
  }
#endif

  return;
}


/*================================================================================*
  SET TIMER STATUS LED
 *================================================================================*/
void set_status_led()
{
  int r_lev, b_lev, g_lev;

  dbg(fDebug, "status led = ", mode);

  r_lev = PWM_LED_OFF;
  b_lev = PWM_LED_OFF;
  g_lev = PWM_LED_OFF;

  if (mode == mREADY)         // blue
  {
    b_lev = PWM_LED_ON;
  }
  else if (mode == mRACING)  // green
  {
    g_lev = PWM_LED_ON;
  }
  else if (mode == mFINISH)  // red
  {
    r_lev = PWM_LED_ON;
  }
  else if (mode == mTEST)    // yellow
  {
    r_lev = PWM_LED_ON;
    g_lev = PWM_LED_ON;
  }

  analogWrite(STATUS_LED_R,  r_lev);
  analogWrite(STATUS_LED_B,  b_lev);
  analogWrite(STATUS_LED_G,  g_lev);

  return;
}


/*================================================================================*
  READ SERIAL DATA FROM COMPUTER
 *================================================================================*/
int get_serial_data()
{  
  int data = 0;
  
  if (Serial.available() > 0)
  {
    data = Serial.read();
    dbg(fDebug, "ser rec = ", data);
  }

  return data;
}  


/*================================================================================*
  INITIALIZE TIMER
 *================================================================================*/
void initialize(boolean powerup)
{  
  for (int n=0; n<NUM_LANES; n++)
  {
    lane_time[n] = 0;
    lane_place[n] = 0;
  }

  start_time = 0;
  set_status_led();
  digitalWrite(START_SOL, LOW);

  // if power up and gate is open -> goto FINISH state
  if (powerup && digitalRead(START_GATE) == START_TRIP) 
  {
    mode = mFINISH;
  }
  else
  {
    mode = mREADY;

    smsg(SMSG_READY);
    delay(100);
  }
  Serial.flush();

  ready_first  = true;
  finish_first  = true;

  return;
}


/*================================================================================*
  UNMASK ALL LANES
 *================================================================================*/
void unmask_all_lanes()
{  
  dbg(fDebug, "unmask all lanes");

  for (int n=0; n<NUM_LANES; n++)
  {
    lane_mask[n] = false;
  }  

  return;
}  


/*================================================================================*
  SEND DEBUG TO COMPUTER
 *================================================================================*/
void dbg(int flag, const char * msg, int val)
{  
  char tmps[50];


  if (!flag) return;

  smsg_str("dbg: ", false);
  smsg_str(msg, false);

  if (val != -999)
  {
    sprintf(tmps, "%d", val);
    smsg_str(tmps);
  }
  else
  {
    smsg_str("");
  }

  return;
}


/*================================================================================*
  SEND SERIAL MESSAGE (CHAR) TO COMPUTER
 *================================================================================*/
void smsg(char msg, boolean crlf)
{  
  if (crlf)
  {
    Serial.println(msg);
  }
  else
  {
    Serial.print(msg);
  }

  return;
}


/*================================================================================*
  SEND SERIAL MESSAGE (STRING) TO COMPUTER
 *================================================================================*/
void smsg_str(const char * msg, boolean crlf)
{  
  if (crlf)
  {
    Serial.println(msg);
  }
  else
  {
    Serial.print(msg);
  }

  return;
}


/*================================================================================*
  SEND TIMER INFORMATION TO COMPUTER
 *================================================================================*/
void send_timer_info()
{
  char tmps[50];

  Serial.println("-----------------------------");
  sprintf(tmps, " PDT            Version %s", PDT_VERSION);
  Serial.println(tmps);
  Serial.println("-----------------------------");

  sprintf(tmps, "  NUM_LANES     %d", NUM_LANES);
  Serial.println(tmps);
  sprintf(tmps, "  GATE_RESET    %d", GATE_RESET);
  Serial.println(tmps);
  sprintf(tmps, "  SHOW_PLACE    %d", SHOW_PLACE);
  Serial.println(tmps);
  sprintf(tmps, "  PLACE_DELAY   %d", PLACE_DELAY);
  Serial.println(tmps);
  sprintf(tmps, "  MIN_BRIGHT    %d", MIN_BRIGHT);
  Serial.println(tmps);
  sprintf(tmps, "  MAX_BRIGHT    %d", MAX_BRIGHT);
  Serial.println(tmps);

  Serial.println("");

#ifdef LED_DISPLAY
  Serial.println("  LED_DISPLAY   1");
#else
  Serial.println("  LED_DISPLAY   0");
#endif

#ifdef DUAL_DISP
  Serial.println("  DUAL_DISP     1");
#else
  Serial.println("  DUAL_DISP     0");
#endif
#ifdef DUAL_MODE
  Serial.println("  DUAL_MODE     1");
#else
  Serial.println("  DUAL_MODE     0");
#endif

#ifdef LARGE_DISP
  Serial.println("  LARGE_DISP    1");
#else
  Serial.println("  LARGE_DISP    0");
#endif

  Serial.println("");

  sprintf(tmps, "  ARDUINO VERS  %04d", ARDUINO);
  Serial.println(tmps);
  sprintf(tmps, "  COMPILE DATE  %s", __DATE__);
  Serial.println(tmps);
  sprintf(tmps, "  COMPILE TIME  %s", __TIME__);
  Serial.println(tmps);

  Serial.println("-----------------------------");

  return;
}
1 Like

i don't see inputs for you lane sensors on pins 2-7. I only see the start and reset switch inputs

maybe someone can reproduce the problem

Hi, @saw133
Welcome to the forum.

Looks like a good project to do.

Tom.... :smiley: :+1: :coffee: :australia:

this is the input for the lanes 1-6 but i only use pin 2 and 3 on the uno and was also using same pins on the mega

thanks for showing this i never new this program was out there i will take the sketch you started with and i will expand for a better flow of what it should look like and label switched and what now how do you set buttons to (no) or (nc) and the seven seg i need to provide it an addres how is that done lane 1 is 0x70 lane 2 is 0x71

it looks like the "lane detectors" are read from directly from the I/O registers. Are they the same on the Mega as the UNO? why not read the pins with digitalRead()?

i believe it works after changing the following which reads the lane pin instead of using the lane # as a bit in a register

    while (lanes_left) {
        current_time = micros();

        // read status of all lanes
        for (int n=0; n<NUM_LANES; n++)
#ifdef GREGC
            lane_status[n] = digitalRead (LANE_DET[n]);
#else
            lane_status[n] = bitRead(PIND, LANE_DET[n]);
#endif

the optical lane detectors are normally pull the input LOW until the car breaks the beam and it goes HIGH, hence the toggle switches

i added additional prints can got the following output

P
K
B
timer_racing_state: NUM_LANES 4,  3047676 usec
 car 1 time  1732176 usec
 car 0 time  3181108 usec
 car 3 time  4680276 usec
 car 2 time  5680168 usec
1 - 3.1811
2 - 1.7322
3 - 5.6802
4 - 4.6803
 timer_racing_state: finished

reading usec instead of msec seems unnecessary

Arduino Mega (ATmega2560)

  • Pin 2:

    • Port: Port E
    • Bit: PE4
    • Output Register: PORTE
    • Input Register: PINE
    • Direction Register: DDRE
  • Pin 3:

    • Port: Port E
    • Bit: PE5
    • Output Register: PORTE
    • Input Register: PINE
    • Direction Register: DDRE

Arduino Uno (ATmega328P)

  • Pin 2:

    • Port: Port D
    • Bit: PD2
    • Output Register: PORTD
    • Input Register: PIND
    • Direction Register: DDRD
  • Pin 3:

    • Port: Port D
    • Bit: PD3
    • Output Register: PORTD
    • Input Register: PIND
    • Direction Register: DDRD

Key Differences

  1. Port:

    • Mega uses Port E for pins 2 and 3.
    • Uno uses Port D for pins 2 and 3.
  2. Bit Mapping:

    • Mega: Pin 2 = PE4, Pin 3 = PE5.
    • Uno: Pin 2 = PD2, Pin 3 = PD3.
  3. Registers:

    • Mega: Uses PORTE, PINE, and DDRE.
    • Uno: Uses PORTD, PIND, and DDRD.

so the Uno and Mega use different registers for the same pin #s and therefore the code won't be the same.

that's why you should used digitalWrite(). It's more than fast enough

Okay I will start researching how to change this information in the code Greg thank you so much for helping think outside the box and point me in the right direction


it is greatly appreciated and I can't wait to get this connected to it

i posted the one line change above, that needs to be made in timer_racing_state()

I'm at work and then out of town today but I will try to make these changes as soon as I can and I will let you know again thank you so much

thanks greg it is working like normal on the ardunio uno only diffrance is load time on the mega for startup is longer but fuctionality is right on

Do you have a link for that 7-segment display?

The large seven segment display that I created is all completely homemade once I have everything working I will be posting it here and also on the spark fund page cuz I used most of their parts as well I will most likely as well create on our website the construction of this project along with all the code for it stay tuned it may take me sometime this has been so far a project over the course of a year and a half from idea design till now I just got the main controller reconfigured so it now works on a mega instead of just the Uno so over the next few days I'll be rolling in all the code for the RS modules to communicate to the actual scoreboard itself the scoreboard is approximately 16 in tall by 48 in wide 5 in deep double sided

I thought perhaps that was the case. It looks like it is composed of hundreds of SMD LEDS or perhaps a lens that gives that effect.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.