need help with multiple timers

Hi guys, I am having trouble figuring out how to do this.

I have 2 fencers with weapons (fencing swords). There are 3 weapon types, with slightly different timimg requirements, I'm trying to figure the first one out, the others should then be similar.

I have a set of analog comparators set for each side, the arduino gets 2 inputs from both sides - touch, and touch+ground.

A fencer can score if his weapon makes contact for 2 to 10 mS. (touch) A fencer cannot score if his touch is against ground (his opponents bellguard or the grounded strip) (touch+ground).

The 2nd fencer can score as well if he can make contact for at least 2mS - the time interval must be less than 40mS from the 1st fencers valid touch. After 50 mS, the 2nd fencer is locked out. (I suppose if it took 10mS to register as valid, then 40mS more, yields the 50mS).

Further, fencer1 could have his 2mS timer started, then within 2mS fencer2 could start his, then fencer1's timer 2mS timer could go invalid and then restart within the 40mS going on from fencer2.

How do I code that? I had the 40ms timer working if one started or if the other started, I am trying to add the 2mS contact time in, with the added stop/restart feature.

What I was seeing was that there are occasions where one fencer could contact the opponent bellguard, the 'ground' light would go on accordingly, but after bouncing off the bellguard the valid light would go off also.

Maybe I need to simplify this - look for a left touch, start the 2ms, look for a right touch, start the the 2mS, if either 2mS completes set a corresponding flag, either one can start the 40mS, at the end of 40mS turn on the score light for the one or both flags. I guess keep track of who started the 40mS so if a 2mS fails, that 40mS can be dropped and the other 40mS can still be valid.

Well, thanks for listening :-)

Here's what I have going into my rewrite attempt of the last paragraph. Up to void loop() is probably ok, rest will be revised (foil & sabre not really started, just placeholder for now)

/* Program to test Touch detectors.
 Inputs are selectively grounded and/or compared to a set voltage using LM339 quad comparator.
 Outputs are read for high/low for high/low a change.
 Add in time requirements later.

 */
// define pins used _ adding extra I/Os here for debugging

// weapon select pins
int epee_in = 3; // D3, do in s/w later
int epee = 0; // holds the read value
int sabre_in = 4; // D4, do in s/w later
int sabre = 0; // holds the read value
int foil_in = 5; // D5, do in s/w later
int foil = 0; // holds the read value

// weapon input pins
int left_A = 6;  // D6
int leftA = 0; // holds the read value
int left_B = 7;  // D7
int leftB = 0; // holds the read value
int left_C = 8;  // D8
int leftC = 0; // holds the read value
int right_A = 9; // D9
int rightA = 0; // holds the read value
int right_B = 10; // D10
int rightB = 0;  // holds the read value
int right_C = 11; // D11
int rightC = 0; // holds the read value

// int ground_B = 12; // D18/A4   For controlling a transistor - drive direct here?
// int ground_C = 13; // D19/A5   For controlling a transistor - drive direct here?

// score display pins
int lefttouchlight = 14; // A0
int goodlefttouch = 0; // flag to show contact time was long enough
int leftgroundlight = 15; // A1
int leftofftargetlight = 16; // A2
int righttouchlight = 17;  // A3
int goodrighttouch = 0;  // flag o show contact time was long enough
int rightgroundlight = 18; // A4
int rightofftargetlight = 19; // A5

// only pins 0, 1, 2 left!!

unsigned long left_main_timer = 0; // main timer for lockout
unsigned long left_main_timerend = 0;  // end of main time

unsigned long right_main_timer = 0; // main timer for lockout
unsigned long right_main_timerend = 0;  // end of main time

unsigned long left_contact_timer = 0; // timer for contact time
unsigned long left_contact_timerend = 0; // timer for contact time

unsigned long right_contact_timer = 0; // timer for contact time
unsigned long right_contact_timerend = 0; // timer for contact time

unsigned long timer3 = 0; // timer for foil offtarget before target
unsigned long timer3end = 0; // timer for foil offtarget before target

void setup()
{
// enable the inputs and their internal pullup resistors
  pinMode (left_A, INPUT);
  digitalWrite (left_A, HIGH);
  pinMode (left_B, INPUT);
  digitalWrite (left_B, HIGH);
  pinMode (left_C, INPUT);
  digitalWrite (left_C, HIGH);
  pinMode (right_A, INPUT);
  digitalWrite (right_A, HIGH);
  pinMode (right_B, INPUT);
  digitalWrite (right_B, HIGH);
  pinMode (right_C, INPUT);
  digitalWrite (right_C, HIGH);
  pinMode (ground_B, OUTPUT);
  digitalWrite (ground_B, HIGH);
  pinMode (ground_C, OUTPUT);
  digitalWrite (ground_C, HIGH);
  pinMode (epee_in, INPUT);
  digitalWrite (epee_in, HIGH);
  pinMode (sabre_in, INPUT);
  digitalWrite (sabre_in, HIGH);
  pinMode (foil_in, INPUT);
  digitalWrite (foil_in, HIGH);

  pinMode  (lefttouchlight, OUTPUT);
  pinMode  (leftgroundlight, OUTPUT); 
  pinMode  (leftofftargetlight, OUTPUT);
  pinMode  (righttouchlight, OUTPUT);   
  pinMode  (rightgroundlight, OUTPUT); 
  pinMode  (rightofftargetlight, OUTPUT);
  
  // turn all the lights off
  digitalWrite(lefttouchlight, HIGH);
  digitalWrite(righttouchlight, HIGH);
  digitalWrite(leftgroundlight, HIGH);
  digitalWrite(rightgroundlight, HIGH);
  digitalWrite(leftofftargetlight, HIGH);
  digitalWrite(rightofftargetlight, HIGH);

  leftA = digitalRead(left_A);
  leftB = digitalRead(left_B);
  leftC = digitalRead(left_C);
  rightA = digitalRead(right_A);
  rightB = digitalRead(right_B);
  rightC = digitalRead(right_C);
  foil = digitalRead(foil_in);
  epee = digitalRead(epee_in);
  sabre = digitalRead(sabre_in);

  Serial.begin(57600);
}

see void loop in next post

// ****************************************************************
void loop()
{
// *****************************************************************
  if (epee == 0)
  {
    // set 'sense' transistors - not used, grounding manually for now
//    digitalWrite (ground_C, HIGH);
//    digitalWrite (ground_B, LOW);
    
    // read the touch status's
    
    leftA = digitalRead(left_A); // 0 = left touch
    // leftB = digitalRead(left_B); // always low for epee
    leftC = digitalRead(left_C); // 0 = right ground
    rightA = digitalRead(right_A);  // 0 = right touch
    // rightB = digitalRead(right_B);  // always low for epee
    rightC = digitalRead(right_C);  // 0 = left ground
// *****************************************************************
    // check if left ground
    if (leftA == 0 && rightC == 0) // no touch for left, show ground light for duration
    { 
      digitalWrite(leftgroundlight, LOW); // turn it on
    }
    else // turn it off
    {
      digitalWrite(leftgroundlight, HIGH); // turn it off
    }    
    // check if right ground
    if (rightA == 0 && leftC == 0) // no touch for right, show ground light for duration
    { 
      digitalWrite(rightgroundlight, LOW); // turn it on
    }
    else // turn it off
    {
      digitalWrite(rightgroundlight, HIGH); // turn it off
    }

// ****************************************************************************    
    // check if left touch    
    if (leftA == 0 && rightC == 1) // good touch for left if contact time > 2mS
    { 

      left_main_timer = millis();  // lockout timer - double touch in 40 mS
      left_main_timerend = millis();
      left_contact_timer = left_main_timer; // contact time - no touch if contact <2 mS
      left_contact_timerend = left_main_timerend;
      
      while ((left_contact_timerend - left_contact_timer)<3)  // start duration timer, start watching for double touch)
      {
      while ((left_main_timerend - left_main_timer1)<41 ) // wait to see if other touch occurs
      {
        rightA = digitalRead(right_A);  // 0 = right touch
        leftC = digitalRead(left_C); // 0 = right ground
        if (rightA == 0 && leftC == 1) // right touch scored in time
        {     
          digitalWrite(righttouchlight, LOW);
        }  // turn it on
        timer1end = millis();
      }
      } // finish waiting for time
      digitalWrite(lefttouchlight, LOW); // turn it on    <<<< Now add contact time!!
      delay (3000);  // hold lights on for 3 seconds
      digitalWrite(lefttouchlight, HIGH);
      digitalWrite(righttouchlight, HIGH);
    }

// *********************************************************************    
    // check if right touch - this is the original test, before I started 
    // trying to mess with the 2mS above
    if (rightA == 0 && leftC == 1) // good touch for right
    { 
      digitalWrite(righttouchlight, LOW); // turn it on
      timer1 = millis(); // these were renamed above
      timer1end = millis();
      while ((timer1end-timer1)<41)  // wait to see if other touch occurs
      {
        leftA = digitalRead(left_A);  // 0 = right touch
        rightC = digitalRead(right_C); // 0 = right ground
        if (leftA == 0 && rightC == 1) // right touch scored in time
        {
          digitalWrite(lefttouchlight, LOW);
        }  // turn it on
        timer1end = millis();
      } // finish waiting for time
      delay (3000);  //   hold lights on for 3 seconds
      digitalWrite(righttouchlight, HIGH);
      digitalWrite(lefttouchlight, HIGH);
    }    
    
// *******************************************************************  
  } // end of epee loop
  
// *********************************************************************
  if (sabre == 0)
  {
    // set 'sense' transistors
    digitalWrite (ground_C, LOW);
    digitalWrite (ground_B, HIGH);
    leftA = digitalRead(left_A);
    leftB = digitalRead(left_B);
    leftC = digitalRead(left_C);
    rightA = digitalRead(right_A);
    rightB = digitalRead(right_B);
    rightC = digitalRead(right_C);
  }  // end of sabre loop
// **********************************************************************
    if (foil == 0)
  {
    // set 'sense' transistors
    digitalWrite (ground_C, LOW);
    digitalWrite (ground_B, HIGH);
    leftA = digitalRead(left_A);
    leftB = digitalRead(left_B);
    leftC = digitalRead(left_C);
    rightA = digitalRead(right_A);
    rightB = digitalRead(right_B);
    rightC = digitalRead(right_C);

  }  // end of foil loop

// ***********************************************************************
}  // end of void loop

How do I code that?

State machine.

Have you ever created a "state diagram"?

How much experience do you have with classes / objects?

No state machine usage in ages, and then was in a PAL type device that had 32 states and one defined the outputs for each state and where the state went to based on inputs (or something along those lines, was back in 1990!)

I guess this is sort of a state machine - I have 7 states 1. no touches detected, read inputs again 2. left is grounded, turn on ground light, read inputs again 3. right is grounded, turn on ground light, read inputs again 4. left valid touch started, see if stays on 2 mS, read inputs again 5. right valid touch started, see if stays on 2 mS, read inputs again 6. left or right valid touch detected, watch for the other for 40mS,read inputs again 7. 40ms passed, turn on one or both lights, then reset goodtouch flag, go read inputs again

Here's what I have now, that isn't working at the moment. I compiled & tried it, haven't done any debug yet.

Classes - objects? I'm a former digital logic designer, so just kind bumbling along and figuring out software stuff as I go. Had fun programming in college, but was always a hardware designer.

// ****************************************************************
void loop()
{
  // *****************************************************************
  if (epee == 0)
  {
    // set 'sense' transistors - not used, grounding manually for now
    //    digitalWrite (ground_C, HIGH);
    //    digitalWrite (ground_B, LOW);

    // read the touch status's

    leftA = digitalRead(left_A); // 0 = left touch
    // leftB = digitalRead(left_B); // always low for epee
    leftC = digitalRead(left_C); // 0 = right ground
    rightA = digitalRead(right_A);  // 0 = right touch
    // rightB = digitalRead(right_B);  // always low for epee
    rightC = digitalRead(right_C);  // 0 = left ground
    // *****************************************************************
    // check if left ground
    if (leftA == 0 && rightC == 0) // no touch for left, show ground light for duration
    { 
      digitalWrite(leftgroundlight, LOW); // turn it on
      left_contact_timer_running == 0; // stop 2mS timer if it was running
    }
    else // turn it off
    {
      digitalWrite(leftgroundlight, HIGH); // turn it off
    }    
    // check if right ground
    if (rightA == 0 && leftC == 0) // no touch for right, show ground light for duration
    { 
      digitalWrite(rightgroundlight, LOW); // turn it on
      right_contact_timer_running == 0; // stop 2mS timer if it was running
    }
    else // turn it off
    {
      digitalWrite(rightgroundlight, HIGH); // turn it off
    }

    /*
Simplify this - look for a left touch, start the 2ms, 
     look for a right touch, start the the 2mS, 
     if either 2mS completes set a corresponding flag, either one can start the 40mS, 
     at the end of 40mS turn on the score light for the one or both flags. 
     Keep track of who started the 40mS so if a 2mS fails, that 40mS can be dropped and the other 40mS can still be valid.
     */
    if (leftA == 0 && rightC == 1 && left_contact_timer_running == 0) // start left touch timer
    {
      left_contact_timer = millis();
      left_contact_timer_running = 1;
    }

    if (rightA == 0 && leftC ==1 && right_contact_timer_running == 0) // start right touch timer
    {
      right_contact_timer = millis();
      right_contact_timer_running = 1;
    }

    left_contact_timerend = millis();
    right_contact_timerend = millis();
    if (leftA == 0 && rightC == 1 && left_contact_timer_running == 1) // check left touch timer
    {
      if (left_contact_timerend - left_contact_timer >2)
      {
        goodlefttouch = 1;
        left_main_timer = millis();
        left_contact_timer_running = 0;
      }
    }
    if (leftA == 0 && rightC == 1 && left_contact_timer_running == 1) // check left touch timer
    {
      if (right_contact_timerend - right_contact_timer >2)
      {
        goodrighttouch = 1;
        right_main_timer = millis();
        right_contact_timer_running = 0;
      }
    }
    if (goodlefttouch == 1 )
    {
      left_main_timer = millis();
      left_main_timer_running = 1;
    }
    if (goodrighttouch == 1)
    {
      right_main_timer == millis();
      right_main_timer_running = 1;
    }
    left_main_timerend = millis();  // check the 40mS timer
    right_main_timerend = millis(); // check the 40mS timer

    if ((left_main_timer_running == 1 && left_main_timerend - left_main_time > 40) || (right_main_timer_running == 1 && right_main_timerend - right_main_timer > 40))
    {
      if (goodlefttouch == 1)
      {
        digitalWrite (lefttouchlight, LOW);
      } // turn on light if had valid touch
      if (goodrighttouch == 1)
      { 
        digitalWrite (righttouchlight, LOW);
      }  // turn on light if had valid touch
      delay (3000);
      goodlefttouch = 0;  // reset for next pass
      digitalWrite (lefttouchlight, HIGH);  // turn off it was on
      goodrighttouch = 0;  // reset for next pass
      digitalWrite (righttouchlight, HIGH);  // turn off it was on

    }

Okay, Found some logic mistakes, some cut & paste errors. Seems to work prett well. What I can't fathom is why, on the right side only, why sometimes a quick touch against ground, such as bouncing off the bellguard quickly, can show up as touch right after the ground light goes off. Left side doesn't it. I tried swapping weapons, seems to follow the right side.

I have to really work at it to make it happen, lots of glancing blows. Do you see any code differences that would cause that? I'll try some other weapons tomorro as well. Hard to duplicate.

// ****************************************************************
void loop()
{
  // *****************************************************************
  if (epee == 0)
  {
    // set 'sense' transistors - not used, grounding manually for now
    //    digitalWrite (ground_C, HIGH);
    //    digitalWrite (ground_B, LOW);

    // read the touch status's

    leftA = digitalRead(left_A); // 0 = left touch
    // leftB = digitalRead(left_B); // always low for epee
    leftC = digitalRead(left_C); // 0 = right ground
    rightA = digitalRead(right_A);  // 0 = right touch
    // rightB = digitalRead(right_B);  // always low for epee
    rightC = digitalRead(right_C);  // 0 = left ground
    // *****************************************************************
    // check if left ground
    if (leftA == 0 && rightC == 0) // no touch for left, show ground light for duration
    { 
      digitalWrite(leftgroundlight, LOW); // turn it on
      left_contact_timer_running = 0; // stop 2mS timer if it was running
    }
    else // turn it off
    {
      digitalWrite(leftgroundlight, HIGH); // turn it off
    }    
    // check if right ground
    if (rightA == 0 && leftC == 0) // no touch for right, show ground light for duration
    { 
      digitalWrite(rightgroundlight, LOW); // turn it on
      right_contact_timer_running = 0; // stop 2mS timer if it was running
    }
    else // turn it off
    {
      digitalWrite(rightgroundlight, HIGH); // turn it off
    }

    /*
Simplify this - look for a left touch, start the 2ms, 
     look for a right touch, start the the 2mS, 
     if either 2mS completes set a corresponding flag, either one can start the 40mS, 
     at the end of 40mS turn on the score light for the one or both flags. 
     Keep track of who started the 40mS so if a 2mS fails, that 40mS can be dropped and the other 40mS can still be valid.
     */
    if (leftA == 0 && rightC == 1 && left_contact_timer_running == 0 && left_main_timer_running == 0) // start left contact timer
    {
      left_contact_timer = millis();
      left_contact_timer_running = 1;
    }

    if (rightA == 0 && leftC ==1 && right_contact_timer_running == 0 && right_main_timer_running == 0) // start right contact timer
    {
      right_contact_timer = millis();
      right_contact_timer_running = 1;
    }
    
    if (leftA == 0 && rightC == 1 && left_contact_timer_running == 1 && left_main_timer_running == 0) // check left contact timer
    {
      left_contact_timerend = millis();
      if (left_contact_timerend - left_contact_timer >2) // have a valid touch!
      {
        goodlefttouch = 1; // set flag for a good left touch
        left_main_timer = millis(); // start 40mS timer
        left_main_timer_running = 1; // flag for 40mS timer running
        left_contact_timer_running = 0; // reset for next pass
      }
    }
    
    if (rightA == 0 && leftC == 1 && right_contact_timer_running == 1 && right_main_timer_running == 0) // check right contact timer
    {
      right_contact_timerend = millis();
      if (right_contact_timerend - right_contact_timer >2)  // have a valid touch!
      {
        goodrighttouch = 1; // set flag for a good right touch
        right_main_timer = millis(); // start 40mS timer
        right_main_timer_running = 1; // flag for 40omS timer running
        right_contact_timer_running = 0; // reset for next pass
      }
    }
    
    if (goodlefttouch == 1){left_main_timerend = millis();}  // check the 40mS timer
    if (goodrighttouch == 1){right_main_timerend = millis();} // check the 40mS timer

    if ((left_main_timer_running == 1 && left_main_timerend - left_main_timer > 40) || (right_main_timer_running == 1 && right_main_timerend - right_main_timer > 40))
    {
      if (goodlefttouch == 1)
      {
        digitalWrite (lefttouchlight, LOW);
      } // turn on light if had valid touch
     
      if (goodrighttouch == 1)
      { 
        digitalWrite (righttouchlight, LOW);
      }  // turn on light(s) if had valid touch(es)
      
      delay (3000);
      goodlefttouch = 0;  // reset for next pass
      left_main_timer_running = 0;
      goodrighttouch = 0;  // reset for next pass
      right_main_timer_running = 0;
      digitalWrite (lefttouchlight, HIGH);  // turn off if it was on
      digitalWrite (righttouchlight, HIGH);  // turn off if it was on

    }

The code looks perfectly symmetrical to me. I don't see any traps that would cause one side to behave differently than the other.

Thanks for looking. I wasn't able to duplicate it yesterday, think maybe it was the particular weapon, maybe it needs the tip cleaned out. http://saf.pair.com/epeetip.html Wife finally asked me to stop banging them together, have moved on to the next weapon now.