Multi-Functional Arduino-Controlled Robotic Car

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);
}

Summary