RC car speed trap - advice on streamlining or improvements?

Hi there folks,

I have been Arduinoing for a few weeks now, just messing about and trying to figure out how it all goes and I think I have just about completed my first proper project.

I say completed - the breadboarding and coding seems to be about on point (at least it compiles and seems to do what I want it to at least most of the time...) and I am currently 3D printing the prototype casing for the hardware ready to start building it in.

While it's printing I thought I'd jump in here, post the code and schematic to see if a) anyone can see any obvious mistakes or improvements and b) anyone wants to take it and have a go.

I'm using a Elegoo Uno R3 clone, 2 IR sensors (the type with 2 LEDs on the end (a dark and a clear one (although Tinkercad doesn't have that type of sensor)), and the 16x2 LCD that came in the Elegoo kit.

I've been using the arduino.cc references and all sorts of other code examples to get it where it now is - learning C++/Arduino language is kicking my butt but is very satisfying when you find that tiny syntax error or typo that's been erroring out all day...

It's for measuring the speed of a 1/10 scale RC car when passing the trap. The idea is that it measures the speed and displays it on the LCD screen. When the speed recorded is a new maximum recorded speed the idea is that the Uno saves the new top speed and flashes a couple of LEDs.

Well, that's the plan anyway. Here's the schematic...

And the code that goes with it...

//Load LCD library

#include <LiquidCrystal.h>

//Global pin assigners and defined containers

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);              //Assign pins for LCD
int SenOne = 6;                                     //Assign pin 6 to SenOne
int SenTwo = 7;                                     //Assign pin 7 to SenTwo
int BlueLEDPin = 8;                                 //Assign signal pin for blue LED
int RedLEDPin = 9;                                  //Assign signal pin for red LED
float SenOneTrigger;                                //Create float container named 'SenOneTrigger'
float SenTwoTrigger;                                //Create float container named 'SenTwotrigger'
float Difference;                                   //Create float container named 'Difference', value = 'SenTwoTrigger' minus 'SenOneTrigger' (time in ms to cover 33mm)
float DifferencePerMm;                              //Create float container named 'DifferencePerMm', value = Difference divided by 33 (time in ms to cover 1mm) ((Milliseconds per Millimetre))
float VelocityInMPS;                                //Create float container named 'VelocityInMPS', value = DifferencePerMm times 1000 (time in ms to cover 1m) ((Seconds per metre))
float FinalVelocity;                                //Create float container named 'FinalVelocity', value = VelocityInMPS times 2.237 to get MPH.
float Vmax;                                         //Create float container named 'Vmax', value = 0
float PersonalBest;                                 //Create flost container namec 'PersonalBest'

//-----START OF SETUP SECTION-----

void setup()
{

//Define pinModes and start the LCD

  lcd.begin(16, 2);                                 //Start LCD
  pinMode(SenOne, INPUT);                           //Set pinMode of SenOne to INPUT
  pinMode(SenTwo, INPUT);                           //Set pinMode of SenTwo to INPUT
  pinMode(BlueLEDPin, OUTPUT);                      //Set pinMode of BlueLEDPin to OUTPUT
  pinMode(RedLEDPin, OUTPUT);                       //Set pinMode of RedLEDPin to OUTPUT
  Vmax = 0;                                         //Set value of var 'Vmax' to 0
}

//-----START OF LOOP SECTION-----

void loop()
{
  lcd.setCursor(0, 0);                             //Set position of LCD cursor
  lcd.print("*RC speed trap*");                    //Display title



 
//Actions

  while (digitalRead(SenOne));                      //Read SenOne
  while (digitalRead(SenOne) == 0);                 //Not entirely sure TBH
  SenOneTrigger = millis();                         //Save timestamp value in 'SenOneTrigger'
  while (digitalRead(SenTwo));                      //Read SenTwo
  SenTwoTrigger = millis();                         //Save timestamp value in 'SenTwoTrigger'

//Calculations

  Difference = (SenTwoTrigger - SenOneTrigger);     //Set value of var 'Difference' to 'SenTwoTrigger' minus 'SenOneTrigger'
  DifferencePerMm = (Difference / 33);              //Set value of var 'DifferencePerMm' to 'Difference' divided by 33 (sensor distance in mm) to get per mm
  FinalVelocity = DifferencePerMm;                  //Set value of var 'FinalVelocity' to value of 'DifferencePerMm'

//VelocityInMPS = (DifferencePerMm * 1000);         //Unused calculation
//FinalVelocity = (VelocityInMPS * 2.237);          //Unused calculation

// Send ouput to LCD display

  lcd.setCursor(0, 1);
  lcd.print(FinalVelocity);                         //Var 'FinalVelocity' -> LCD
  lcd.print("MPH");                                 //Text "MPH" -> LCD

//Set vmax of the session
  
  if (FinalVelocity > Vmax)                         //If variable 'FinalVelocity' is higher than recorded Vmax...
  {
    Vmax = FinalVelocity;                           // Set variable Vmax to value in FinalVelocity
 
//Flashing lights for new Vmax

    digitalWrite(BlueLEDPin, HIGH);
    delay(50);
    digitalWrite(RedLEDPin, HIGH);
    delay(50);
    digitalWrite(BlueLEDPin, LOW);
    delay(50);
    digitalWrite(RedLEDPin, LOW);
    delay(50);
    digitalWrite(BlueLEDPin, HIGH);
    delay(50);
    digitalWrite(RedLEDPin, HIGH);
    delay(50);
    digitalWrite(BlueLEDPin, LOW);
    delay(50);
    digitalWrite(RedLEDPin, LOW);
    delay(50);
    digitalWrite(BlueLEDPin, HIGH);
    delay(50);
    digitalWrite(RedLEDPin, HIGH);
    delay(50);
    digitalWrite(BlueLEDPin, LOW);
    delay(50);
    digitalWrite(RedLEDPin, LOW);
    delay(50);
    digitalWrite(BlueLEDPin, HIGH);
    delay(50);
    digitalWrite(RedLEDPin, HIGH);
    delay(50);
    digitalWrite(BlueLEDPin, LOW);
    delay(50);
    digitalWrite(RedLEDPin, LOW);
    delay (1000);                                       //Wait 1 second
    lcd.clear();                                        //Clear LCD
  }
  else {                                                //If not a new 'Vmax'...
  lcd.clear();                                          //Clear LCD
}
}

Any ideas/comments/suggestions?

Thanks folks,

Drew.

The most obvious improvement would be to use variables of the appropriate type. For instance, the pin numbers could be byte instead of int and could also be const as they will not change

Why are the Trigger variables float and not unsigned long as they should be ?

  DifferencePerMm = (Difference / 33);

will be done as an integer calculation.

To force it to be calculated as a float use

  DifferencePerMm = (Difference / 33.0);

Nice, I'll look closer but it is never too early to learn about (or use) the for loop in C/C++.

//Flashing lights for new Vmax
  for (int nn = 0; nn < 17; nn++) {
      digitalWrite(BlueLEDPin, HIGH);
      delay(50);
      digitalWrite(RedLEDPin, HIGH);
      delay(50);
  }

will work and (unless you are being paid by the line of code) be better.

CU

a7

Thank you for looking through my project, most appreciated :slight_smile:

Pretty much because I'm still figuring out which types of variables to use for which application. I was advised to use float variables for a different project I was playing with earlier on and 'float' seemed a good fit :man_shrugging:

To force it to be calculated as a float use

  DifferencePerMm = (Difference / 33.0);

That makes sense, will add that :+1:

Awesome, that's one command I haven't come across yet (and looks uber complicated :rofl: ). Will do some digging around about it and get it on a loop - you're right, it's got to be better.

Thanks again :+1:

Nothing in that code so far suggests any need for "float" variables. Unsigned longs are just fine.

So you reckon all the float variables ought to be changed over to unsigned longs?

And is there an advantage to using an unsigned long variable as opposed to the float?

Not picking at you or your advice, just clarifying in my own brain hole :slight_smile:

If you do not need values and/or results with decimal places then use the appropriately sized integers

Calculations with integers are inherently faster

Excellent, thank you.

While doing this variable research I came across this on a tutorial site (Data Types in Arduino - SparkFun Learn)... yeah, I'm researching in like 35 windows at once...

Below is a list of the data types commonly seen in Arduino, with the memory size of each in parentheses after the type name. Note: signed variables allow both positive and negative numbers, while unsigned variables allow only positive values.

  • boolean (8 bit) - simple logical true/false
  • byte (8 bit) - unsigned number from 0-255
  • char (8 bit) - signed number from -128 to 127. The compiler will attempt to interpret this data type as a character in some circumstances, which may yield unexpected results
  • unsigned char (8 bit) - same as 'byte'; if this is what you're after, you should use 'byte' instead, for reasons of clarity
  • word (16 bit) - unsigned number from 0-65535
  • unsigned int (16 bit)- the same as 'word'. Use 'word' instead for clarity and brevity
  • int (16 bit) - signed number from -32768 to 32767. This is most commonly what you see used for general purpose variables in Arduino example code provided with the IDE
  • unsigned long (32 bit) - unsigned number from 0-4,294,967,295. The most common usage of this is to store the result of the millis() function, which returns the number of milliseconds the current code has been running
  • long (32 bit) - signed number from -2,147,483,648 to 2,147,483,647
  • float (32 bit) - signed number from -3.4028235E38 to 3.4028235E38. Floating point on the Arduino is not native; the compiler has to jump through hoops to make it work. If you can avoid it, you should. We'll touch on this later.

Got to say, the amount of information out there is astounding, but sometimes you just need a live person to clarify exactly what the tutorials can mean.

Does this seem an accurate reference?

Mostly true, but be careful with things like int. Different CPUs may use different sizes e.g. the Due uses a four byte int.

Sparkfun is generally a good source of information

Probably not a concern to you at the moment, but the list of data types and sizes relates to 8 bit AVR architecture such as the Arduino Uno, Nano and other boards using the 328 chip

Data sizes for other boards, such as the 32 bit ESP32 (not strictly an Arduino) are different, so an int on one chip may be a different size (number of bytes) on another chip

You may also come across data types expressed in the form

  • uint8_t - an unsigned 8 bit integer
  • int16_t - a 16 bit signed integer
  • uint32_t - an unsigned 32 bit integer

etc, etc

This may be confusing at first sight but it unambiguously described the type and size of teh data type

Awesome.

I'm sticking with Unos and Nanos for the time being - reckon it'll be a little while before I'll be onto projects that need any more than that.

Besides, if I start getting into different chipsets I'm just gonna fry my brain :rofl:

And though this project presently is far from the limits, takes less code space.

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