//*****************************************************************************************************************************
//Designed by Alex Pasat
//2021-06-24
//*****************************************************************************************************************************
//Infared Remote Control Car
//A RC car which is controlled by infared signals. The car has many abilities such as being able to move, surronding check,
//lap racing, sound, RGB, and more. The keycodes on the infared remote do a specific command which is held in a switch case.
//*****************************************************************************************************************************
//Global Pins and Variables - Things that are used throughout the code are written here
//*****************************************************************************************************************************
//Following are used to be able to use downloaded libraries to be able to use new commands.
//*****************************************************************************************************************************
#include <IRremote.h> //Includes in the infrared remote libarary.
#include "Freenove_WS2812B_RGBLED_Controller.h" //Includes in the RGB controller libarary which also holds the pin number of RGB module which is connected to A4 and A5.
#include <Servo.h> //Includes in the servo libarary.
//*****************************************************************************************************************************
//Following are the constants/variables which represent the pin Numbers.
//*****************************************************************************************************************************
const int PIN_DIRECTION_LEFT = 4; //The motor that controlles the left direction of rc is connected to digital pin 4.
const int PIN_DIRECTION_RIGHT = 3; //The motor that controlles the right direction of rc is connected to digital pin 3.
const int PIN_MOTOR_PWM_LEFT = 6; //The motor that controlles the left speed is connected to digital PMW pin 6 so that the speed is adjustable between 0-225.
const int PIN_MOTOR_PWM_RIGHT = 5; //The motor that controlles the right speed is connected to digital PMW pin 5 so that the speed is adjustable between 0-225.
const int lineTrackingLeft = A1; //The left detection section of the tracking module is connected to pin Analog input 1.
const int lineTrackingCenter = A2; //The center detection section of the tracking module is connected to pin Analog input 2.
const int lineTrackingRight = A3; //The right detection section of the tracking module is connected to pin Analog input 3.
int sonicTrig = 7; //The ultrasonic sensor Trig which emits the signal out is connected to digital pin 7.
int sonicEcho = 8; //The ultrasonic sensor Echo which recieves the signal in is connected to digital pin 8.
const int piezoSpeaker = A0; //The piezoSpeaker is connected to pin analog input 0.
int PIN_IRREMOTE_RECV = 9; //The Infrared Reciever which is an LED which detects infrared signals from the remote is connected to pin 9.
//*****************************************************************************************************************************
//Flags, Variables, library specifics setups.
//*****************************************************************************************************************************
Servo myServo; //Servo command is used to set the variable name myServo as the servo in the circuit. All commands are directed to this variable as Servo provides the ability to do such.
int pos = 90; //Variable which represents the starting position which is 90 degrees as the ultrasonic sensor should point straight on the car.
long duration; //Variable used to represent the duration it takes the signal to reach back to the ultrasonic sensor.
int distance; //Variable used to represent the distance the obstacle is from the sonic sensor/car.
int leftTrackingSensVal; //Holds the binary signal (1 or 0) from the left component of the tracking sensor.
int centerTrackingSensVal; //Holds the binary signal (1 or 0) from the center component of the tracking sensor.
int rightTrackingSensVal; //Holds the binary signal (1 or 0) from the right component of the tracking sensor.
int IR_UPDATE_TIMEOUT = 110; //Set numerical value which is used in the millis so that it could switch to the command. The timeout is very short so that the millis could beat it quickly to move fluidly.
bool flag0 = 1; //First flag is set as 1 or true. Used to cause some commands to become unvalid until the other flips.
bool flag1 = 1; //Second flag is set as 1 or true.
bool flag2 = 0; //Third flag is set to 0 or false.
bool flag3 = 0; //Fourth flag is set as 0 or false.
int score = 0; //Variable which represents the score is set to 0. This will be used in one of the switch cases which allows you to play a mario kart style.
const int numbrLeds = 10; //Represents the number of LEDs in the total RGB modular component (2 5 RGB strips).
const int I2C_ADDRESS = 0x20; //Bit range of RGB module.
int x = 0; //Variable that will be useful later in the tracking sensor code so that the command only occurs once.
int y = 0; //Variable that will be useful later in the tracking sensor code so that the command only occurs once.
IRrecv irrecv(PIN_IRREMOTE_RECV); //A way to make a variable which will represent the infrared reciever. The IRrecv is used to be able to use infrared specific commands onto the infrared reciever.
decode_results results; //Allows the arduino to decode the infrared signals into results which will be used to start commands.
unsigned long currentKeyCode, lastKeyCode; //Used to check whether a keycode was pressed and the results will be stored in here.
unsigned long lastIRUpdateTime = 0; //The last Ir update time is a variable which will equal to millis and subtract from it to see if it is greater than the timeout.
Freenove_WS2812B_Controller strip(I2C_ADDRESS, numbrLeds, TYPE_GRB);
//RGB controller command used to give the "strip" the ability to set colours to the RGB module.
//*****************************************************************************************************************************
//These are constant unsigned long variables which are equal to hexadecimal values from the infrared remote.
//*****************************************************************************************************************************
const unsigned long IR_REMOTE_BUTTON_UP = 0xFF02FD; //Value if IR Remote UP button is pressed.
const unsigned long IR_REMOTE_BUTTON_DOWN = 0xFF9867; //Value if IR Remote DOWN button is pressed.
const unsigned long IR_REMOTE_BUTTON_LEFT = 0xFFE01F; //Value if IR Remote LEFT button is pressed.
const unsigned long IR_REMOTE_BUTTON_RIGHT = 0xFF906F; //Value if IR Remote RIGHT button is pressed.
const unsigned long IR_REMOTE_BUTTON_CENTER = 0xFFA857; //Value if IR Remote CENTER button is pressed.
const unsigned long IR_REMOTE_BUTTON_TEST_SERVO = 0xFF22DD; //Value if IR Remote TEST_SERVO(test) button is pressed.
const unsigned long IR_REMOTE_BUTTON_1 = 0xFF30CF; //Value if IR Remote 1 button is pressed.
const unsigned long IR_REMOTE_BUTTON_2 = 0xFF18E7; //Value if IR Remote 2 button is pressed.
const unsigned long IR_REMOTE_BUTTON_3 = 0xFF7A85; //Value if IR Remote 3 button is pressed.
const unsigned long IR_REMOTE_BUTTON_0 = 0xFF6897; //Value if IR Remote 0 button is pressed.
const unsigned long IR_REMOTE_BUTTON_OK = 0xFFB04F; //Value if IR Remote OK(C) button is pressed.
const unsigned long IR_REMOTE_KEYCODE_4 = 0xFF10EF; //Value if IR Remote 4 button is pressed.
const unsigned long IR_REMOTE_KEYCODE_6 = 0xFF5AA5; //Value if IR Remote 6 button is pressed.
//*****************************************************************************************************************************
//Setup Function - Can only run once, initializes the code, and states the behaviours and attaches the pins
//*****************************************************************************************************************************
void setup()
{
irrecv.enableIRIn(); //Enables the infrared input to be detected.
strip.begin(); //Begins the ability to code the strip LEDs.
Serial.begin (9600); //Sets the speed of printing in the serial monitor as 9600 bits/second. This is used to be able to decoding and testing.
pinMode(PIN_DIRECTION_LEFT, OUTPUT); //Sets the left direction motor as an OUTPUT to exert a signnal/move.
pinMode(PIN_MOTOR_PWM_LEFT, OUTPUT); //Sets the right direction motor as an OUTPUT to exert a signnal/move.
pinMode(PIN_DIRECTION_RIGHT, OUTPUT); //Sets the right PMW speed motor as an OUTPUT to exert a signnal/move.
pinMode(PIN_MOTOR_PWM_RIGHT, OUTPUT); //Sets the left PMW speed motor as an OUTPUT to exert a signnal/move.
pinMode(piezoSpeaker, OUTPUT); //Sets the piezoSpeaker as an OUTPUT to exert a signal/sound.
pinMode(sonicTrig, OUTPUT); //Sets the sonic Trig part as an OUTPUT to be able to exert the ultrasonic signal.
pinMode(sonicEcho, INPUT); //Sets the sonic Echo part as an INPUT to be able to recieve the ultrasonic signal.
pinMode(lineTrackingLeft, INPUT); //Sets the left part of tracking device as an input to recieve the signals.
pinMode(lineTrackingCenter, INPUT); //Sets the center part of tracking device as an input to recieve the signals.
pinMode(lineTrackingRight, INPUT); //Sets the right part of tracking device as an input to recieve the signals.
myServo.attach(2); //Servo is attached to digital pin 2.
}
//*****************************************************************************************************************************
//Loop Function - Main function of the code which loops continually
//*****************************************************************************************************************************
void loop()
{
if (irrecv.decode(&results)) //Checks whether a code has been recieved by the ir reciever. If so, then...
{
currentKeyCode = results.value; //The results value from the ir reciever is stored in the currentKeyCode variable.
if (currentKeyCode != 0xFFFFFFFF) //0xFFFFFFFF is the largest hexadecimal that could be made into 32 bits. It could cause issues if it is used so we make sure we don't use it
{ //by only allowing the following code to run if the results/currentKeyCode is not equal to it.
lastKeyCode = currentKeyCode; //The currentKeyCode is then stored in the lastKeyCode.
}
switch (lastKeyCode) //Switch case is used to make the code easier and cleaner. It works by checking if the switch variable which is "lastKeyCode" is true with the
{ //case statement. Because all the case use the same "lastKeyCode" it helps make the code simpler.
case IR_REMOTE_BUTTON_UP: //If the UP button is pressed...
if (flag1 == 1) //Flag1 is used in the obstacle avoidance mode which moves the car away from the obstacle. The flag causes the car only to move after the the car has fully moved away from the obstacle.
{ //If the flag is true, then...
digitalWrite (PIN_DIRECTION_LEFT, 0); //Left motor is set to 0.
digitalWrite (PIN_DIRECTION_RIGHT, 1); //Right motor is set to 1. Together this allows the car to move forward.
analogWrite (PIN_MOTOR_PWM_LEFT, 200); //Sets speed of left pmw motor to 200.
analogWrite (PIN_MOTOR_PWM_RIGHT, 200); //Sets speed of right pmw motor to 200.
movementLights(); //Runs the movementLights function which specifically changes the lights whenever the car moves/motors move.
}
surrondingCheck(); //Runs the surrondingCheck function to check the surronding of the car.
lineTrackingMode(); //Runs the lineTrackingMode function which only works if another IR keyCode is pressed to allow the flag to flip and cause this function to work.
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_BUTTON_DOWN: //If the DOWN button is pressed...
if (flag1 == 1) //If the flag is true, then...
{
digitalWrite (PIN_DIRECTION_LEFT, 1); //Left motor is set to 1.
digitalWrite (PIN_DIRECTION_RIGHT, 0); //Right motor is set to 0. Together this allows the car to move back.
analogWrite (PIN_MOTOR_PWM_LEFT, 200); //Sets speed of left pmw motor to 200.
analogWrite (PIN_MOTOR_PWM_RIGHT, 200); //Sets speed of right pmw motor to 200.
movementLights();
}
surrondingCheck();
lineTrackingMode();
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_BUTTON_LEFT: //If the Left button is pressed...
if (flag1 == 1) //If the flag is true, then...
{
digitalWrite (PIN_DIRECTION_LEFT, 1); //The left motor is set to 1.
digitalWrite (PIN_DIRECTION_RIGHT, 1); //The right motor is set to 1. Together this allows the car to turn to the left.
analogWrite (PIN_MOTOR_PWM_LEFT, 200); //Sets speed of left pmw motor to 200.
analogWrite (PIN_MOTOR_PWM_RIGHT, 200); //Sets speed of right pmw motor to 200.
movementLights();
}
surrondingCheck();
lineTrackingMode();
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_BUTTON_RIGHT: //If the right button is pressed...
if (flag1 == 1) //If the flag is true, then...
{
digitalWrite (PIN_DIRECTION_LEFT, 0); //The left motor is set to 0.
digitalWrite (PIN_DIRECTION_RIGHT, 0); //The right motor is set to 0. Together this allows the car to turn to the right.
analogWrite (PIN_MOTOR_PWM_LEFT, 200); //Sets speed of left pmw motor to 200.
analogWrite (PIN_MOTOR_PWM_RIGHT, 200); //Sets speed of right pmw motor to 200.
movementLights();
}
surrondingCheck();
lineTrackingMode();
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_BUTTON_CENTER: //If the center button is pressed...
honkSound(); //Runs honkSound function so that the button could act as the horn.
surrondingCheck(); //This surrondingCheck function is ran in all cases so that the car is always checking its surrondings.
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_BUTTON_TEST_SERVO: //If the test button is pressed...
myServo.write(pos); //Sets the position of the servo/sensor to 90 degrees to point forward.
for (pos = 0; pos <= 180; pos++) //Moves the servo from position 0 to 180 by using a for loop. It adds one every next loop.
{
myServo.write(pos); //Writes the servo to the pos from the for loop.
delay(15); //Pauses 15 milleseconds so that it could actually move and register the motion.
surrondingCheck(); //Checks surrondings after every click so that it is checking when it is moving.
}
for (pos = 180; pos <= 0; pos--) //Does the oppostite than above and starts from 180 and moves back to pos 0. It subtracts 1 from the pos after every next loop.
{
myServo.write(pos); //Writes the servo to the pos from the for loop.
delay(10); //Pauses 15 milleseconds so that it could actually move and register the motion.
surrondingCheck(); //Checks surrondings after every click so that it is checking when it is moving.
}
pos = 90; //After it fully checks its surrondings, the servo moves back to pos 90 to be facing directly forward of the car.
myServo.write(pos); //Writes the above variable change into the servo.
surrondingCheck();
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_BUTTON_1: //If button 1 is pressed...
pos = 180; //Set the position var to 180.
myServo.write(pos); //Write the above position into the servo so that it moves to the 180 position.
surrondingCheck();
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_BUTTON_2: //If button 2 is pressed...
pos = 90; //Set the position var to 90.
myServo.write(pos); //Write the above position into the servo so that it moves to the 90 position.
surrondingCheck();
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_BUTTON_3: //If button 3 is pressed...
pos = 0; //Sets the position var to 0.
myServo.write(pos); //Write the above position into the servo so that it moves to the 0 position.
surrondingCheck();
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_BUTTON_0: //If button 0 is pressed...
flag0 = 1; //Sets the flag0 to 1 so that the car only checks surronding but doesn't obstacle avoid or move from them, only beeps.
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_BUTTON_OK: //If the OK button is pressed...
flag0 = 0; //Sets the flag0 to 0 so that the car can go into surronding avoidiance mode which causes the car to move away from obstacle.
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_KEYCODE_4: //If button 4 is pressed...
flag2 = 1; //Sets the flag2 to 1 so that the car could drive in lineTrackingMode which allows you to play with laps/Mario Kart theme game.
break; //Exits the case and doesn't continue to loop it.
case IR_REMOTE_KEYCODE_6: //If button 6 is pressed...
flag2 = 0; //The flag2 is set to 0 so that the car could drive in normal mode/not the game.
score = 0; //Resets the score to 0.
break; //Exits the case and doesn't continue to loop it.
}
irrecv.resume(); //After breaks out of a case, the infrared recieve resumes and continues to detect a new result.
lastIRUpdateTime = millis(); //The time that the program has been running is stored in lastIRUpdateTime variable. This is done so that the car only stops when no buttons are pressed and this command is not ran.
}
else //If no results are detected in the IR reciever.
{
if (millis() - lastIRUpdateTime > IR_UPDATE_TIMEOUT) //If the millis- lastIRUpdateTime is larger than the timeout, the the following occurs. This is done so that it could act as a timer,
{ //Which would only allow this command to run 110 milleseconds (timeout) after the last button that was pressed.
digitalWrite (PIN_DIRECTION_LEFT, 0); //Sets all motors to 0 so that they stop and the car does not move.
digitalWrite (PIN_DIRECTION_RIGHT, 0);
analogWrite (PIN_MOTOR_PWM_LEFT, 0);
analogWrite (PIN_MOTOR_PWM_RIGHT, 0);
analogWrite (piezoSpeaker, 0); //The piezoSpeaker is set to 0 so that nothing comes out.
defaultLights(); //Runs the defaultLights function which turns the RGB to a light which is in accordance when the car is not moving.
surrondingCheck();
lineTrackingMode();
lastIRUpdateTime = millis(); //The time that the program has been running is stored in lastIRUpdateTime variable.
} //This is done so that it does not always stay in this loop but continouslly checks as it must suprass the timeout.
}
}
//*****************************************************************************************************************************
//Function that represents the honk sound played by piezoSpeaker.
//*****************************************************************************************************************************
void honkSound()
{
analogWrite (piezoSpeaker, 200); //Sets the piezoSpeaker on to exert a sound with a value of 200.
delay (10); //Delays 10 milleseconds so that the sound can be heard and is generated.
}
//*****************************************************************************************************************************
//Function that represents the beep sound played by piezoSpeaker for the laps and surronding detection.
//*****************************************************************************************************************************
void beepSound()
{
analogWrite (piezoSpeaker, 200); //Sets the piezoSpeaker on to exert a sound with a value of 200.
delay (100); //Delays 100 milleseconds so that the sound can be heard and is generated. Also so that there are distinctions between on and off so that it sounds like beeping.
analogWrite (piezoSpeaker, 0); //Sets the piezoSpeaker off to exert a "sound" with a value of 0 (no sound).
delay (100); //Delays 100 milleseconds so that the sound can be heard and is generated. Also so that there are distinctions between on and off so that it sounds like beeping.
}
//*****************************************************************************************************************************
//Function that represents surronding check which checks for obstacles, beeps, changes rbg colour, and also can do avoidance.
//*****************************************************************************************************************************
void surrondingCheck()
{
digitalWrite (sonicTrig, LOW); //Sets the sonicTrig to LOW to send out no signal. This is done so that when it is high, the signal is more defined.
delay (2); //Pauses for 2 milleseconds to wait that refresh sonic quickly before sending out signal.
digitalWrite (sonicTrig, HIGH); //Sets the sonicTrig to HIGH to send out a signal.
delay (2); //Pauses for 2 milleseconds so that it sends out a quick wave.
digitalWrite (sonicTrig, LOW); //Sets the sonicTrig back to LOW to stop it from sending out more waves.
duration = pulseIn (sonicEcho, HIGH); //Sets the sonicEcho to HIGH so that it can recieve back the signal and absorb it by the sensor. The result is stored in the duration variable.
distance = duration * 0.034 / 2; //The duration variable value is multiplied by 0.034 to change it to centimeters. Then it is divided by 2 as a wave goes forward and then back, so double the distance.
if (distance <= 10) //If the distance of the obstacle is 10 centimeters or less from the sensor, then...
{
beepSound(); //Run the beepSound function to alert the driver that they are next to an obstacle.
detectionLights(); //Runs the detectionLights function so that the RGB change to red to show that the car is near an obstacle.
if (flag0 == 0) //If the flag0 is equal to 0, which means that the obstacle avoidance mode is on, then...
{
flag1 = 0; //The flag1 is changed to 0 so that the user can not manually move the car using the remote until the car moves away from the obstacle.
if (pos > 90 && distance <= 20) //If the position of the sensor is greater than 90 degrees (obstacle on left)
{ //and the distance is still less than/equal to 20 centimeter from ostacle, then...
digitalWrite (PIN_DIRECTION_LEFT, 0); //The left motor is set to 0.
digitalWrite (PIN_DIRECTION_RIGHT, 0); //The right motor is set to 0. Together this allows the car to turn to the right.
analogWrite (PIN_MOTOR_PWM_LEFT, 200); //Sets speed of left pmw motor to 200.
analogWrite (PIN_MOTOR_PWM_RIGHT, 200); //Sets speed of right pmw motor to 200.
}
if (pos == 90 && distance <= 20) //If the position of the sensor is equal to 90 degrees (obstacle in front)
{ //and the distance is still less than/equal to 20 centimeter from ostacle, then...
digitalWrite (PIN_DIRECTION_LEFT, 1); //The left motor is set to 1.
digitalWrite (PIN_DIRECTION_RIGHT, 0); //The right motor is set to 0. Together this allows the car to move back.
analogWrite (PIN_MOTOR_PWM_LEFT, 200); //Sets speed of left pmw motor to 200.
analogWrite (PIN_MOTOR_PWM_RIGHT, 200); //Sets speed of right pmw motor to 200.
}
if (pos < 90 && distance <= 20) //If the position of the sensor is less than 90 degrees (obstacle on right)
{ //and the distance is still less than/equal to 20 centimeter from ostacle, then...
digitalWrite (PIN_DIRECTION_LEFT, 1); //The left motor is set to 1.
digitalWrite (PIN_DIRECTION_RIGHT, 1); //The right motor is set to 1. Together this allows the car to move to the left.
analogWrite (PIN_MOTOR_PWM_LEFT, 200); //Sets speed of left pmw motor to 200.
analogWrite (PIN_MOTOR_PWM_RIGHT, 200); //Sets speed of right pmw motor to 200.
}
}
}
else if (distance >= 19) //If the distance is greater or equal to 19 (the reason I used 19 and not 20/21 is because if the car stops at 20,
{ // I would not be able to move as I would be stuck in the above loop, therefore I had to use 1 cm below to avoid bugs, then...
flag1 = 1; //Sets the flag1 to 1 so that the IR remote could be used to manually move the car again.
}
}
//*****************************************************************************************************************************
//Function that represents the default lights when the car is not moving
//*****************************************************************************************************************************
void defaultLights()
{
strip.setAllLedsColor(0, 225, 0); //Sets all of the LEDs in the RGB strip to green to represent stationary.
delay(1); //Pauses for 1 second to allow it to light up.
}
//*****************************************************************************************************************************
//Function that represents the movement lights when the car is moving
//*****************************************************************************************************************************
void movementLights()
{
strip.setAllLedsColor(255, 255, 0); //Sets all of the LEDs in the RGB strip to yellow to represent movement.
delay(1); //Pauses for 1 second to allow it to light up.
}
//*****************************************************************************************************************************
//Function that represents the detection Lights when the sonic detects and obstacle
//*****************************************************************************************************************************
void detectionLights()
{
strip.setAllLedsColor(225, 0, 0); //Sets all of the LEDs in the RGB strip to red to represent an obstacle.
delay (1); //Pauses for 1 second to allow it to light up.
}
//*****************************************************************************************************************************
//Function that represents the lineTrackingMode/ Lap or Mario Kart Game mode
//*****************************************************************************************************************************
void lineTrackingMode()
{
if (flag2 == 1) //If flag2 is pressed (if the IR keycode to turn on the mode was pressed), then...
{
leftTrackingSensVal = digitalRead (lineTrackingLeft); //Reads the binary value from the left component of tracking module and stores it into the left tracking variable.
centerTrackingSensVal = digitalRead (lineTrackingCenter); //Reads the binary value from the center component of tracking module and stores it into the center tracking variable.
rightTrackingSensVal = digitalRead (lineTrackingRight); //Reads the binary value from the right component of tracking module and stores it into the right tracking variable.
if (leftTrackingSensVal == 1 && centerTrackingSensVal == 1 && rightTrackingSensVal == 1)
{ //If all all parts of tracking sensor detect 1, which means black (lap line)...
flag3 = 1; //The flag3 is equal to 1 so that you can add to the score.
y = 0; //y is equal to 0 to reset the variable used in the for loop.
}
else if (leftTrackingSensVal == 0 || centerTrackingSensVal == 0 || rightTrackingSensVal == 0)
{ //If any part of the tracking sensor detects 0, which means white/colour (just the terrain)...
flag3 = 0; //The flag3 is equal to 1 so that you do not add to the score.
x = 0; //x is equal to 0 to reset the variable used in the for loop.
if (y < 1) //Done so that the following only loops once and does not continue beeping.
{ //If y is less than 1 (so 0), then...
for (int j = 0; j < score; j++) //for loop is used to beep the same number of times as your score. This will tell the driver what their score is by the number of beeps.
{ //It starts at 0 and adds 1 to it each time while the variable j is less than the score.
beepSound(); //Runs the beepSound function to represent the score.
}
y++; //Adds 1 to variable y so that this loop stops and only executes once.
}
}
if (flag3 == 1) //If flag3 equals to 1, so the black line is detected, then...
{
if (x < 1) //Done so that the following only loops once and does not continue adding to the score.
{ //If x is less than 1 (so 0), then...
score++; //Add one to the score to represent that you finished a lap.
Serial.print (score); //Print the score into the serial monitor. Used for debugging and testing.
x++; //Adds 1 to variable x so that this loop stops and only executes once.
}
}
}
}