Arduino RC car brake lights.

so, i bought a cheapo RC car from walmart with the intention of "fixing" it. so far ive put in a proper servo based steering, LM298 + new motor, LiPo, vreg, arduino, RC style RX, and a ULN2803 for headlights and left/right blinkers (working). the only thing i cant get to work is the brake LEDs. all ive been trying to do is get them to turn on when the car is decelerating.

Here is the fwd/rev code:

if(bUpdateFlags & thrFlag)
  {
    if(thrIn > midValues[0] + jitterThresh) //deadspace, forward
    {
      //FWD
      digitalWrite(In1, LOW); //set dir
      digitalWrite(In2, HIGH); //set dir

      //GO!
      thrIn = map(thrIn,midValues[0],calibValues[3],0,255);
      analogWrite(EnA, thrIn);
      prevTHR = thrIn;
      }
    }
    else if (thrIn < midValues[0] - jitterThresh)
    {

      //rev
      digitalWrite(In1, HIGH); //set dir
      digitalWrite(In2, LOW); //set dir

      //GO!
      thrIn = map(thrIn,midValues[0],calibValues[2],0,255);
      analogWrite(EnA, thrIn); 

      prevTHR = thrIn;
    }
    else if((thrIn > midValues[0] - jitterThresh) && (thrIn < midValues[0] + jitterThresh)) //stick is centered
    {
      digitalWrite(EnA,LOW); //disable LM298
      digitalWrite(brakeLEDPin, LOW);
    }        
  }

the code only runes when the RX channel values is updated via ISR (the if statement of course). so the obvious thing to do would be to say if(current value < prev value) turn on brake lights, but my RX signals are noisy(4ms flicker) and they would flicker on/off massively on any stick position.

whats the best way to do this? wait a few hundred ms and then compare current and prev?

quick update!
got it to work sort of. if i accelerate or accelerate in the neg direction (rev) and then move to stick towards center (decelerate), the brake ligts will come on with the following code, but if i accerate before going to 0 fully, the brake lights don't go off. but im definitely getting there after a few hours.

if(bUpdateFlags & thrFlag)
  {
    if(thrIn > midValues[0] + jitterThresh) //deadspace, forward
    {
      //FWD
      digitalWrite(In1, LOW); //set dir
      digitalWrite(In2, HIGH); //set dir

      //GO!
      thrIn = map(thrIn,midValues[0],calibValues[3],0,255);
      analogWrite(EnA, thrIn);
    }
    else if (thrIn < midValues[0] - jitterThresh)
    {

      //rev
      digitalWrite(In1, HIGH); //set dir
      digitalWrite(In2, LOW); //set dir

      //GO!
      thrIn = map(thrIn,midValues[0],calibValues[2],0,255);
      analogWrite(EnA, thrIn); 
    }
    else if((thrIn > midValues[0] - jitterThresh) && (thrIn < midValues[0] + jitterThresh)) //stick is centered
    {
      digitalWrite(EnA,LOW); //disable LM298
      digitalWrite(brakeLEDPin, LOW);
    }       

    if(millis() > brakeTime2)
    {
      brakeTime = millis();
      brakeTime2 = millis() + 200;
      if(thrIn < thrIn1)
      {
        digitalWrite(brakeLEDPin, HIGH);
      }
    }  
    if(millis() > brakeTime && millis() < brakeTime2)
    {
      thrIn1 = thrIn;
    } 
  }

this is the part i added:

 if(millis() > brakeTime2)
    {
      brakeTime = millis();
      brakeTime2 = millis() + 200;
      if(thrIn < thrIn1)
      {
        digitalWrite(brakeLEDPin, HIGH);
      }
    }  
    if(millis() > brakeTime && millis() < brakeTime2)
    {
      thrIn1 = thrIn;
    }

welp, looks like i didnt need help haha. got this to work. definitely not perfect though, as it is really touchy

if(bUpdateFlags & thrFlag)
  {
    if(thrIn > midValues[0] + jitterThresh) //deadspace, forward
    {
      //FWD
      digitalWrite(In1, LOW); //set dir
      digitalWrite(In2, HIGH); //set dir

      //GO!
      thrIn = map(thrIn,midValues[0],calibValues[3],0,255);
      analogWrite(EnA, thrIn);
    }
    else if (thrIn < midValues[0] - jitterThresh)
    {

      //rev
      digitalWrite(In1, HIGH); //set dir
      digitalWrite(In2, LOW); //set dir

      //GO!
      thrIn = map(thrIn,midValues[0],calibValues[2],0,255);
      analogWrite(EnA, thrIn); 
    }
    else if((thrIn > midValues[0] - jitterThresh) && (thrIn < midValues[0] + jitterThresh)) //stick is centered
    {
      digitalWrite(EnA,LOW); //disable LM298
      digitalWrite(brakeLEDPin, LOW);
    }       

    if(millis() > brakeTime2)
    {
      if(thrIn > thrIn1)
      {
        digitalWrite(brakeLEDPin, LOW);
      }

      brakeTime = millis();
      brakeTime2 = millis() + 200;

      if(thrIn < thrIn1)
      {
        digitalWrite(brakeLEDPin, HIGH);
      }
    }  
    if(millis() > brakeTime && millis() < brakeTime2)
    {
      if(thrIn > thrIn1)
      {
        digitalWrite(brakeLEDPin, LOW);
      }
      thrIn1 = thrIn;
    } 
  }

but, if any body has any critiques/tips etc, please don't hesitate. :slight_smile:

Hi,
It looks as if your code is based on the code at

However you are not using it correctly which may be one reason for your jitter.

// check shared update flags to see if any channels have a new signal
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;
   
    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.
   
    if(bUpdateFlags & THROTTLE_FLAG)
    {
      unThrottleIn = unThrottleInShared;
    }
   
    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }
   
    if(bUpdateFlags & AUX_FLAG)
    {
      unAuxIn = unAuxInShared;
    }
    
    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;
   
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }
 
  // do any processing from here onwards
  // only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared
  // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
  // the interrupt routines and should not be used in loop

See how inside the if block bUpdateFlagsShared is clearered to give ownership back to the ISR, then interrupts are turned back on. Only when we exit the if block should we do any processing and as per the comments, this should be based on the local copies we have in unAuxIn, unThrottleIn, unSteeringIn.

If you are using a low cost transmitter you can expect a wide variation in the input signal, 40us maybe -

But not 4ms. Try reordering your code as suggested and come back if you still have such a huge variation.

The original code from the link is the basis for the code thats running in this car -

Duane B

oops hah yeah 4us :smiley: i did the first rev off of pulseIn and then realized that it wouldn't work(servo slow) with other stuff happening. thats why i used your code on the next rev :wink: and what do you mean by mis-using? this is my main loop:

void loop()
{
  // create local variables to hold a local copies of the channel inputs
  // these are declared static so that thier values will be retained 
  // between calls to loop.
  static uint16_t thrIn;
  static uint16_t ailIn;
  static uint16_t aux1In;
  static uint16_t aux2In;
  // local copy of update flags
  static uint8_t bUpdateFlags;

  // check shared update flags to see if any channels have a new signal
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

      // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;

    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.

    if(bUpdateFlags & ailFlag)
    {
      ailIn = ailShared;
    }

    if(bUpdateFlags & thrFlag)
    {
      thrIn = thrShared;
    }

    if(bUpdateFlags & aux1Flag)
    {
      aux1In = aux1Shared;
    }

    if(bUpdateFlags & aux2Flag)
    {
      aux2In = aux2Shared;
    }

    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;

    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the 
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }

  // we are checking to see if the channel value has changed, this is indicated  
  // by the flags. For the simple pass through we don't really need this check,
  // but for a more complex project where a new signal requires significant processing
  // this allows us to only calculate new values when we have new inputs, rather than
  // on every cycle.
  if(bUpdateFlags & thrFlag)
  {
    if(thrIn > midValues[0] + jitterThresh) //deadspace, forward
    {
      //FWD
      digitalWrite(In1, LOW); //set dir
      digitalWrite(In2, HIGH); //set dir

      //GO!
      thrIn = map(thrIn,midValues[0],calibValues[3],0,255);
      analogWrite(EnA, thrIn);
    }
    else if (thrIn < midValues[0] - jitterThresh)
    {

      //rev
      digitalWrite(In1, HIGH); //set dir
      digitalWrite(In2, LOW); //set dir

      //GO!
      thrIn = map(thrIn,midValues[0],calibValues[2],0,255);
      analogWrite(EnA, thrIn); 
    }
    else if((thrIn > midValues[0] - jitterThresh) && (thrIn < midValues[0] + jitterThresh)) //stick is centered
    {
      digitalWrite(EnA,LOW); //disable LM298
      digitalWrite(brakeLEDPin, LOW);
    }       

    if(millis() > brakeTime2)
    {
      if(thrIn > thrIn1)
      {
        digitalWrite(brakeLEDPin, LOW);
      }

      brakeTime = millis();
      brakeTime2 = millis() + 100;

      if(thrIn < thrIn1)
      {
        digitalWrite(brakeLEDPin, HIGH);
      }
    }  
    if(millis() > brakeTime && millis() < brakeTime2)
    {
      if(thrIn > thrIn1)
      {
        digitalWrite(brakeLEDPin, LOW);
      }
      thrIn1 = thrIn;
    } 
  }

  if(bUpdateFlags & ailFlag)
  {
    //servo values: 12, left    42 ,right
    TurnServ.write(map(ailIn,calibValues[0],calibValues[1],12,42));

    //TURNING-----------------------------------------------------------
    //RIGHT--------------------
    if(ailIn > (calibValues[3] - ((calibValues[3] - midValues[1])/4))) //if current ailvalue is greater than 3/4 of mid to max value, turn on right blinker
    {
      if(millis() > lastTimeR)
      {
        blinkR =!blinkR;
        digitalWrite(rightLEDPin,blinkR);
        lastTimeR = millis() + 100;
      }
    }
    else
    {
      digitalWrite(rightLEDPin, LOW);
    }

    //LEFT--------------------
    if(ailIn < (calibValues[2] + ((midValues[1] - calibValues[2])/4))) //if current ailvalue is less than 3/4 of mid to min value, turn on left blinker
    {
      if(millis() > lastTimeL)
      {
        blinkL = !blinkL;
        digitalWrite(leftLEDPin,blinkL);
        lastTimeL = millis() + 100;
      }
    }
    else
    {
      digitalWrite(leftLEDPin, LOW);
    }    
  }

  if(bUpdateFlags & aux1Flag)
  {
    //HEADLIGHTS--------------------------------------------------------
    if(aux1In < ((calibValues[4] + calibValues[5])/2))
    {
      digitalWrite(headLEDPin, LOW);
    }
    else //high
    {
      digitalWrite(headLEDPin, HIGH);
    }
  }

  if(bUpdateFlags & aux2Flag)
  {
    //aux2In
  }

you can see that i copy them to a new variable (ailIn) from the shared (ailInShared) while interrupts are disabled and then use the copied ones in the rest of the loop. i changed the reg names because they bothering me with all caps and their length. :smiley:

i'm using a turnigy 9x with stock RX that im using for my quad so maybe it just a cheapness issue. maybe frsky will help eventually?

Hi,
Your quite right, from the original section of code you posted it looked as if you were doing all of your work while interrupts were blocking, but I see that you were using it correctly.

As for the 4us variation, I am afraid its there to stay, its due to the Arduino having to service other interrupts as well, these include the servo library interrupt and the interrupts that drive millis() and micros().

My conclusion is that this variation of less 0.5% of signal has no effect as it averages to zero before any realworld project would feel the effect, it is especially insignificant when you consider that a realworld signal can look like the two signals in this post -

I see that you have some calibration, did you have a look at the calibration code here, it has EEPROM storage so you only need to calibrate once -

Duane B