Multi-Functional Arduino-Controlled Robotic Car

Introduction
This project centers around a multi-functional robotic car powered and controlled by an Arduino-based system. The car's core components include an Arduino Nano, HC-SR04 Ultrasonic Sensor, and a robust motor system featuring an L298N Motor Driver. A remote-control device, equipped with analog joysticks, switches, and potentiometers, communicates wirelessly with the car using the nRF24L01 module. The car boasts diverse functionalities, including anti-collision capabilities using the distance sensor, an MP3 player for music playback, a 3-dimensional control arm manipulated through joysticks, and police-like lights for added visual appeal.
Description
Features
Components Used
Schematic
Code
Transmitter Side
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Wire.h>
#include "MPU9250.h"
RF24 radio(9, 10);
const byte address[6] = "00001";
MPU9250 mpu;
#define Mode_SW 5
#define RIGHT_SW 7
#define LEFT_SW 4
#define Buzzer 6
#define LED 8
#define RIGHT_JOYSTICK_SW 3
#define LEFT_JOYSTICK_SW 2
#define CE_PIN 9
#define CSN_PIN 10
#define RIGHT_JOYSTICK_X A2
#define RIGHT_JOYSTICK_Y A3
#define LEFT_JOYSTICK_X A0
#define LEFT_JOYSTICK_Y A1
int melody[] = {
392, 150, // G4
523, 150, // C5
659, 150, // E5
784, 150, // G5
987, 150, // B5
1319, 150, // E6
1046, 150, // C6
1568, 150 // G6
};
struct Data {
int16_t Yaw;
int16_t Roll;
int16_t Bitch;
uint8_t Mode;
uint8_t R_SW;
uint8_t L_SW;
uint8_t R_J_SW;
uint8_t L_J_SW;
uint16_t R_J_X;
uint16_t R_J_Y;
uint16_t L_J_X;
uint16_t L_J_Y;
};
void setup() {
pinMode(Mode_SW, INPUT_PULLUP);
pinMode(RIGHT_JOYSTICK_SW, INPUT_PULLUP);
pinMode(LEFT_JOYSTICK_SW, INPUT_PULLUP);
pinMode(RIGHT_SW, INPUT_PULLUP);
pinMode(LEFT_SW, INPUT_PULLUP);
pinMode(Buzzer, OUTPUT);
pinMode(LED, OUTPUT);
Wire.begin();
playMelody();
delay(2000);
mpu.setup(0x68);
digitalWrite(LED, HIGH);
Serial.begin(115200);
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_LOW);
radio.stopListening();
}
void loop() {
Data data;
if (mpu.update()) {
data.Yaw = mpu.getYaw();
data.Roll = mpu.getRoll();
data.Bitch = mpu.getPitch();
}
data.R_SW = digitalRead(RIGHT_SW);
data.L_SW = digitalRead(LEFT_SW);
data.R_J_SW = digitalRead(RIGHT_JOYSTICK_SW);
data.L_J_SW = digitalRead(LEFT_JOYSTICK_SW);
data.Mode = digitalRead(Mode_SW);
data.R_J_X = analogRead(RIGHT_JOYSTICK_X);
data.R_J_Y = analogRead(RIGHT_JOYSTICK_Y);
data.L_J_X = analogRead(LEFT_JOYSTICK_X);
data.L_J_Y = analogRead(LEFT_JOYSTICK_Y);
radio.write(&data, sizeof(data));
}
void playMelody() {
for (int i = 0; i < sizeof(melody) / sizeof(melody[0]); i += 2) {
int noteDuration = melody[i + 1];
tone(Buzzer, melody[i], noteDuration);
delay(noteDuration);
noTone(Buzzer);
delay(20);
}
}
Receiver Side
#include <HCSR04.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#include <Adafruit_PWMServoDriver.h>
#define CE_PIN 9
#define CSN_PIN 10
RF24 radio(CE_PIN, CSN_PIN);
const byte address[6] = "00001";
HCSR04 hc(20, 21); // (trig pin, echo pin)
Adafruit_PWMServoDriver pca = Adafruit_PWMServoDriver(0x40);
SoftwareSerial softwareSerial(17, 8);
DFRobotDFPlayerMini player;
// Motor pins
int16_t m1a = 2;
int16_t m2a = 3;
int16_t m1b = 4;
int16_t m2b = 7;
int16_t ENA = 5;
int16_t ENB = 6;
// LEDs
int16_t Blue_Led = 14;
int16_t Red_Led = 15;
// Servo channels
int16_t X_Axis_Servo = 4;
int16_t Y_Axis_Servo = 3;
int16_t Z_Axis_Servo = 2;
int16_t Clamp_Servo = 1;
int16_t Ultrasonic_Servo = 0;
// Timing
unsigned long prevTime = millis();
unsigned long currentTime = millis();
unsigned long prevTime_2 = 0;
bool led1_state = false;
// Servo positions
float posX = 2023;
float posY = 1500;
float posZ = 1160;
float Clamp = 1300;
// Movement
int x, y, z;
int speed;
int pos;
int servoSpeed = 50;
int currentTrack = 1;
// Limits
int X_limit[2] = { 1750, 2250 };
int Y_limit[2] = { 1100, 2400 };
int Z_limit[2] = { 500, 2150 };
int C_limit[2] = { 1300, 2450 };
struct Data {
int16_t Yaw;
int16_t Roll;
int16_t Bitch;
uint8_t Mode;
uint8_t R_SW;
uint8_t L_SW;
uint8_t R_J_SW;
uint8_t L_J_SW;
uint16_t R_J_X;
uint16_t R_J_Y;
uint16_t L_J_X;
uint16_t L_J_Y;
};
void setup() {
pca.begin();
pca.setPWMFreq(60);
softwareSerial.begin(9600);
player.begin(softwareSerial);
pinMode(ENB, OUTPUT);
pinMode(ENA, OUTPUT);
pinMode(m1a, OUTPUT);
pinMode(m1b, OUTPUT);
pinMode(m2a, OUTPUT);
pinMode(m2b, OUTPUT);
pinMode(Blue_Led, OUTPUT);
pinMode(Red_Led, OUTPUT);
Serial.begin(115200);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_LOW);
radio.startListening();
player.volume(25);
Init();
}
void loop() {
delay(5);
currentTime = millis();
if (radio.available()) {
Data data;
radio.read(&data, sizeof(data));
Police_Leds(data);
avoid_collision();
if (data.Mode) {
Arm_Control(data);
Imu_Control(data);
} else {
Car_Control(data);
}
Play_Music(data);
}
}
void avoid_collision() {
if (hc.dist() < 10) {
Stop_F();
}
}
void Init() {
pca.writeMicroseconds(Z_Axis_Servo, 1260);
pca.writeMicroseconds(X_Axis_Servo, 2030);
pca.writeMicroseconds(Y_Axis_Servo, 1500);
pca.writeMicroseconds(Clamp_Servo, 1300);
pca.writeMicroseconds(Ultrasonic_Servo, 1500);
}
void Play_Music(Data data) {
if (!data.L_J_SW) {
currentTrack = (currentTrack > 1) ? currentTrack - 1 : 9;
playCurrentTrack();
}
if (!data.R_J_SW) {
currentTrack = (currentTrack < 9) ? currentTrack + 1 : 1;
playCurrentTrack();
}
}
void playCurrentTrack() {
player.play(currentTrack);
}
void Arm_Control(Data data) {
servoSpeed = 20;
// Y-axis up
if (between(data.L_J_Y, 560, 1023) && between(data.L_J_X, 500, 520)) {
posY += 10;
if (!between(posY, Y_limit[0], Y_limit[1])) posY -= 10;
controlServoWithSpeed(Y_Axis_Servo, posY);
}
// Y-axis down
else if (between(data.L_J_Y, 0, 490) && between(data.L_J_X, 500, 520)) {
posY -= 10;
if (!between(posY, Y_limit[0], Y_limit[1])) posY += 10;
controlServoWithSpeed(Y_Axis_Servo, posY);
}
// X-axis right
else if (between(data.L_J_X, 0, 500) && between(data.L_J_Y, 520, 525)) {
posX += 10;
if (!between(posX, X_limit[0], X_limit[1])) posX -= 10;
controlServoWithSpeed(X_Axis_Servo, posX);
}
// X-axis left
else if (between(data.L_J_X, 525, 1023) && between(data.L_J_Y, 520, 525)) {
posX -= 10;
if (!between(posX, X_limit[0], X_limit[1])) posX += 10;
controlServoWithSpeed(X_Axis_Servo, posX);
}
// Z-axis up
else if (between(data.R_J_X, 500, 530) && between(data.R_J_Y, 550, 1023)) {
posZ += 10;
if (!between(posZ, Z_limit[0], Z_limit[1])) posZ -= 10;
controlServoWithSpeed(Z_Axis_Servo, posZ);
}
// Z-axis down
else if (between(data.R_J_Y, 0, 495) && between(data.R_J_X, 500, 530)) {
posZ -= 10;
if (!between(posZ, Z_limit[0], Z_limit[1])) posZ += 10;
controlServoWithSpeed(Z_Axis_Servo, posZ);
}
// Clamp open
else if (between(data.R_J_Y, 495, 505) && between(data.R_J_X, 550, 1023)) {
Clamp += 10;
if (!between(Clamp, C_limit[0], C_limit[1])) Clamp -= 10;
controlServoWithSpeed(Clamp_Servo, Clamp);
}
// Clamp close
else if (between(data.R_J_X, 0, 480) && between(data.R_J_Y, 495, 505)) {
Clamp -= 10;
if (!between(Clamp, C_limit[0], C_limit[1])) Clamp += 10;
controlServoWithSpeed(Clamp_Servo, Clamp);
}
}
void Imu_Control(Data data) {
if (between(data.Roll, -5, 5) && between(data.Bitch, -40, -10)) {
speed = map(data.Bitch, 5, -40, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
Forward_W();
} else if (between(data.Roll, -5, 5) && between(data.Bitch, 10, 40)) {
speed = map(data.Bitch, 10, 40, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
Backward_S();
} else if (between(data.Roll, -5, 5) && between(data.Bitch, -9, 9)) {
Stop_F();
} else if (between(data.Roll, 10, 40) && between(data.Bitch, -9, 9)) {
pos = map(data.Roll, 10, 40, 1500, 800);
controlServoWithSpeed(Ultrasonic_Servo, pos);
speed = map(data.Roll, 10, 40, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
Right_D();
} else if (between(data.Roll, -40, -10) && between(data.Bitch, -9, 9)) {
pos = map(data.Roll, -10, -40, 1500, 2100);
controlServoWithSpeed(Ultrasonic_Servo, pos);
speed = map(data.Roll, -10, -40, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
Left_A();
}
}
void Car_Control(Data data) {
Init();
if (between(data.L_J_Y, 520, 525) && between(data.L_J_X, 0, 515)) {
speed = map(data.L_J_X, 512, 0, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
Forward_W();
} else if (between(data.L_J_Y, 520, 525) && between(data.L_J_X, 510, 515)) {
controlServoWithSpeed(Ultrasonic_Servo, 1500);
Stop_F();
} else if (between(data.L_J_Y, 500, 530) && between(data.L_J_X, 550, 1023)) {
speed = map(data.L_J_X, 512, 1023, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
Backward_S();
} else if (between(data.L_J_Y, 530, 1023) && between(data.L_J_X, 510, 515)) {
pos = map(data.L_J_Y, 520, 1023, 1500, 2100);
controlServoWithSpeed(Ultrasonic_Servo, pos);
speed = map(data.L_J_Y, 523, 1023, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
Left_A();
} else if (between(data.L_J_Y, 0, 520) && between(data.L_J_X, 510, 515)) {
pos = map(data.L_J_Y, 520, 0, 1500, 800);
controlServoWithSpeed(Ultrasonic_Servo, pos);
speed = map(data.L_J_Y, 522, 0, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
Right_D();
}
}
bool between(int number, int min, int max) {
return number >= min && number <= max;
}
void Debug(Data data) {
Serial.print("Yaw :"); Serial.print(data.Yaw);
Serial.print(" Roll :"); Serial.print(data.Roll);
Serial.print(" Bitch :"); Serial.print(data.Bitch);
Serial.print(" R_SW :"); Serial.print(data.R_SW);
Serial.print(" L_SW :"); Serial.print(data.L_SW);
Serial.print(" R_J_SW :"); Serial.print(data.R_J_SW);
Serial.print(" L_J_SW :"); Serial.print(data.L_J_SW);
Serial.print(" Mode :"); Serial.print(data.Mode);
Serial.print(" L_J_X :"); Serial.print(data.L_J_X);
Serial.print(" L_J_Y :"); Serial.print(data.L_J_Y);
Serial.print(" R_J_X :"); Serial.print(data.R_J_X);
Serial.print(" R_J_Y :"); Serial.println(data.R_J_Y);
}
void Police_Leds(Data data) {
if (data.L_SW == 1) {
unsigned long elapsedTime = currentTime - prevTime;
if (elapsedTime >= 150) {
led1_state = !led1_state;
digitalWrite(Blue_Led, led1_state ? HIGH : LOW);
digitalWrite(Red_Led, !led1_state ? HIGH : LOW);
prevTime = currentTime;
}
} else {
digitalWrite(Blue_Led, LOW);
digitalWrite(Red_Led, LOW);
}
}
void controlServoWithSpeed(int servoPin, int targetPos) {
unsigned long currentTime_2 = millis();
unsigned long elapsedTime_2 = currentTime_2 - prevTime_2;
if (elapsedTime_2 >= servoSpeed) {
pca.writeMicroseconds(servoPin, targetPos);
prevTime_2 = currentTime_2;
}
}
void Forward_W() {
digitalWrite(m1a, 1);
digitalWrite(m1b, 1);
digitalWrite(m2a, 0);
digitalWrite(m2b, 0);
}
void Backward_S() {
digitalWrite(m1a, 0);
digitalWrite(m1b, 0);
digitalWrite(m2a, 1);
digitalWrite(m2b, 1);
}
void Left_A() {
digitalWrite(m1a, 0);
digitalWrite(m1b, 1);
digitalWrite(m2a, 0);
digitalWrite(m2b, 0);
}
void Right_D() {
digitalWrite(m1a, 1);
digitalWrite(m1b, 0);
digitalWrite(m2a, 0);
digitalWrite(m2b, 0);
}
void Stop_F() {
digitalWrite(m1a, 0);
digitalWrite(m1b, 0);
digitalWrite(m2a, 0);
digitalWrite(m2b, 0);
}