Gesture Controlled Car with Accelerometer

My project is the gesture controlled car with an accelerometer. This project incorporates the unique functions of the Arduino Uno and Arduino Nano with other devices such as a motor driver, an accelerometer, and NRF modules in order to perform impressive tasks. In this project the following components work together so that specific hand motions can be interpreted as commands for my robot car to move in a set direction.

Engineer

Fernando Rodriguez Leal

Area of Interest

Electrical/ Aerospace Engineering

School

NYC iSchool

Grade

Incoming Senior

Reflection

Final Code for__(Arduino Uno)__

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <L298N.h>

#include <Adafruit_NeoPixel.h>
#define LED_PIN 2
#define LED_COUNT 36
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

const unsigned int IN1 = 10;
const unsigned int IN2 = 6;
const unsigned int ENA = 9;
const unsigned int IN3 = 5;
const unsigned int IN4 = 4;
const unsigned int ENB = 3;
RF24 radio(7,8); // CE, CSN
int value = -1;
const byte address[6] = “00001”;
Adafruit_MPU6050 mpu;

void setup() {
Serial.begin(9600);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
pinMode (IN1, OUTPUT);
pinMode (IN2, OUTPUT);
pinMode (IN3, OUTPUT);
pinMode (IN4, OUTPUT);
pinMode (ENA, OUTPUT);
pinMode (ENB, OUTPUT);
delay(100);
strip.begin();
strip.show(); // Initialize all pixels to ‘off’
}

void loop() {
if (radio.available()) {
radio.read(&value, sizeof(value));
Serial.println(value);
if (value == 2) {
//Serial.print(text);
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
for (int n = 0; n < LED_COUNT; n++)
{
strip.setPixelColor(n, 251, 255, 5); //set pixel color to be yellow
}

strip.show();
}
else if (value == 3) {
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
for (int n = 0; n < LED_COUNT; n++)
{
strip.setPixelColor(n, 26, 127, 235); //set pixel color to be blue
}
strip.show();
}
else if (value == 0) {
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
for (int n = 0; n < LED_COUNT; n++)
{
strip.setPixelColor(n, 0, 200, 0); //set pixel color to be green
}
strip.show();

}
else if (value == 1) {
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
for (int n = 0; n < LED_COUNT; n++)
{
strip.setPixelColor(n, 235, 155, 26); //set pixel color to be orange
}
strip.show();

}
else if (value == 4) {
analogWrite(ENA, 0);
analogWrite(ENB, 0);
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);

for (int n = 0; n < LED_COUNT; n++)
{
strip.setPixelColor(n, 200, 0, 0); //set pixel color to be red
}
strip.show();
}
else {
analogWrite(ENA, 0);
analogWrite(ENB, 0);
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
//digitalWrite(10, LOW);
delay(2000);
}
}
}

Final Code for__(Arduino Nano)__

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
int command = -1;//0=foward
//1=backwards
//2=right
//3=left
RF24 radio(7, 8); // CE, CSN
const byte address[6] = “00001”;
Adafruit_MPU6050 mpu;
void setup() {
Serial.begin(115200);
while (!Serial)
delay(10); // will pause Zero, Leonardo, etc until serial console opens
Serial.println(“Adafruit MPU6050 test!”);
// Try to initialize!
if (!mpu.begin()) {
Serial.println(“Failed to find MPU6050 chip”);
while (1) {
delay(10);
}
}
Serial.println(“MPU6050 Found!”);
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
Serial.print(“Accelerometer range set to: “);
switch (mpu.getAccelerometerRange()) {
case MPU6050_RANGE_2_G:
Serial.println(“+-2G”);
break;
case MPU6050_RANGE_4_G:
Serial.println(“+-4G”);
break;
case MPU6050_RANGE_8_G:
Serial.println(“+-8G”);
break;
case MPU6050_RANGE_16_G:
Serial.println(“+-16G”);
break;
}
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
Serial.print(“Gyro range set to: “);
switch (mpu.getGyroRange()) {
case MPU6050_RANGE_250_DEG:
Serial.println(“+- 250 deg/s”);
break;
case MPU6050_RANGE_500_DEG:
Serial.println(“+- 500 deg/s”);
break;
case MPU6050_RANGE_1000_DEG:
Serial.println(“+- 1000 deg/s”);
break;
case MPU6050_RANGE_2000_DEG:
Serial.println(“+- 2000 deg/s”);
break;
}
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
Serial.print(“Filter bandwidth set to: “);
switch (mpu.getFilterBandwidth()) {
case MPU6050_BAND_260_HZ:
Serial.println(“260 Hz”);
break;
case MPU6050_BAND_184_HZ:
Serial.println(“184 Hz”);
break;
case MPU6050_BAND_94_HZ:
Serial.println(“94 Hz”);
break;
case MPU6050_BAND_44_HZ:
Serial.println(“44 Hz”);
break;
case MPU6050_BAND_21_HZ:
Serial.println(“21 Hz”);
break;
case MPU6050_BAND_10_HZ:
Serial.println(“10 Hz”);
break;
case MPU6050_BAND_5_HZ:
Serial.println(“5 Hz”);
break;
}
Serial.println(“”);
delay(100);
}
void loop() {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
Serial.print(a.acceleration.x);
Serial.print(” “);
Serial.println(a.acceleration.y);
delay(250);
if (a.acceleration.x < -3) {
command = 2;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (a.acceleration.x > 3) {
command = 3;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (a.acceleration.y < -3) {
command = 0;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (a.acceleration.y > 3) {
command = 1;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (-3 < a.acceleration.y && a.acceleration.x < 3) {
command = 4;
Serial.println(command);
radio.write(&command, sizeof(command));
}
}

When applying to BlueStamp Engineering, I wasn’t sure whether or not I wanted to pursue a degree in the field of engineering. Because I had no prior experience with robotics and code, I was hesitant to enter a complex field where I knew my lack of knowledge would put me at a disadvantage. I came into this program with the mindset that I would get a taste of what it is like to work with electronics, and ultimately decide whether or not engineering was something that I wanted to pursue in college. I’m happy to say that thanks to the amazing staff at BlueStamp Engineering, I’m convinced now more than ever that electrical engineering is something that I want to study in college. Throughout my time in this program, I was able to grasp and work on a variety of different aspects of engineering such as mechanical, electrical, and computer science. Furthermore, I was able to learn about a variety of devices and their unique functions such as NRF modules, accelerometers, motor drivers, motors, sensors, breadboards, perf boards, multimeters, soldering iron, alligator clips, micro servers, and most importantly, Arduino Unos. In addition to my new found knowledge of engineering, BlueStamp Engineering has also reinforced important life lessons that I will continue to carry with me not only in college, but in life as well. The most notable lesson was of course patience. Given the already difficult impediments set forward from COVID-19, participating in the program remotely presented even more challenges that required its students to become both tenacious and patient. There have been many times in which I felt frustrated with the fact that I couldn’t get certain components of my project to work, and to add on, my instructors would often be busy handling all their students at once with their projects. In addition, the ability to work alongside and exchange ideas with other students wasn’t possible since everyone was busy doing their own thing most of the time. Nevertheless, I learned to pull through with my project spending time researching and problem solving. I’m once again extremely grateful for the opportunity that BlueStamp Engineering has offered this summer, and I hope to be in touch with the wonderful staff. 

Final Milestone

Final Code for__(Arduino Uno)__

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <L298N.h>

#include <Adafruit_NeoPixel.h>
#define LED_PIN 2
#define LED_COUNT 36
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

const unsigned int IN1 = 10;
const unsigned int IN2 = 6;
const unsigned int ENA = 9;
const unsigned int IN3 = 5;
const unsigned int IN4 = 4;
const unsigned int ENB = 3;
RF24 radio(7,8); // CE, CSN
int value = -1;
const byte address[6] = “00001”;
Adafruit_MPU6050 mpu;

void setup() {
Serial.begin(9600);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
pinMode (IN1, OUTPUT);
pinMode (IN2, OUTPUT);
pinMode (IN3, OUTPUT);
pinMode (IN4, OUTPUT);
pinMode (ENA, OUTPUT);
pinMode (ENB, OUTPUT);
delay(100);
strip.begin();
strip.show(); // Initialize all pixels to ‘off’
}

void loop() {
if (radio.available()) {
radio.read(&value, sizeof(value));
Serial.println(value);
if (value == 2) {
//Serial.print(text);
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
for (int n = 0; n < LED_COUNT; n++)
{
strip.setPixelColor(n, 251, 255, 5); //set pixel color to be yellow
}

strip.show();
}
else if (value == 3) {
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
for (int n = 0; n < LED_COUNT; n++)
{
strip.setPixelColor(n, 26, 127, 235); //set pixel color to be blue
}
strip.show();
}
else if (value == 0) {
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
for (int n = 0; n < LED_COUNT; n++)
{
strip.setPixelColor(n, 0, 200, 0); //set pixel color to be green
}
strip.show();

}
else if (value == 1) {
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
for (int n = 0; n < LED_COUNT; n++)
{
strip.setPixelColor(n, 235, 155, 26); //set pixel color to be orange
}
strip.show();

}
else if (value == 4) {
analogWrite(ENA, 0);
analogWrite(ENB, 0);
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);

for (int n = 0; n < LED_COUNT; n++)
{
strip.setPixelColor(n, 200, 0, 0); //set pixel color to be red
}
strip.show();
}
else {
analogWrite(ENA, 0);
analogWrite(ENB, 0);
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
//digitalWrite(10, LOW);
delay(2000);
}
}
}

Final Code for__(Arduino Nano)__

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
int command = -1;//0=foward
//1=backwards
//2=right
//3=left
RF24 radio(7, 8); // CE, CSN
const byte address[6] = “00001”;
Adafruit_MPU6050 mpu;
void setup() {
Serial.begin(115200);
while (!Serial)
delay(10); // will pause Zero, Leonardo, etc until serial console opens
Serial.println(“Adafruit MPU6050 test!”);
// Try to initialize!
if (!mpu.begin()) {
Serial.println(“Failed to find MPU6050 chip”);
while (1) {
delay(10);
}
}
Serial.println(“MPU6050 Found!”);
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
Serial.print(“Accelerometer range set to: “);
switch (mpu.getAccelerometerRange()) {
case MPU6050_RANGE_2_G:
Serial.println(“+-2G”);
break;
case MPU6050_RANGE_4_G:
Serial.println(“+-4G”);
break;
case MPU6050_RANGE_8_G:
Serial.println(“+-8G”);
break;
case MPU6050_RANGE_16_G:
Serial.println(“+-16G”);
break;
}
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
Serial.print(“Gyro range set to: “);
switch (mpu.getGyroRange()) {
case MPU6050_RANGE_250_DEG:
Serial.println(“+- 250 deg/s”);
break;
case MPU6050_RANGE_500_DEG:
Serial.println(“+- 500 deg/s”);
break;
case MPU6050_RANGE_1000_DEG:
Serial.println(“+- 1000 deg/s”);
break;
case MPU6050_RANGE_2000_DEG:
Serial.println(“+- 2000 deg/s”);
break;
}
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
Serial.print(“Filter bandwidth set to: “);
switch (mpu.getFilterBandwidth()) {
case MPU6050_BAND_260_HZ:
Serial.println(“260 Hz”);
break;
case MPU6050_BAND_184_HZ:
Serial.println(“184 Hz”);
break;
case MPU6050_BAND_94_HZ:
Serial.println(“94 Hz”);
break;
case MPU6050_BAND_44_HZ:
Serial.println(“44 Hz”);
break;
case MPU6050_BAND_21_HZ:
Serial.println(“21 Hz”);
break;
case MPU6050_BAND_10_HZ:
Serial.println(“10 Hz”);
break;
case MPU6050_BAND_5_HZ:
Serial.println(“5 Hz”);
break;
}
Serial.println(“”);
delay(100);
}
void loop() {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
Serial.print(a.acceleration.x);
Serial.print(” “);
Serial.println(a.acceleration.y);
delay(250);
if (a.acceleration.x < -3) {
command = 2;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (a.acceleration.x > 3) {
command = 3;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (a.acceleration.y < -3) {
command = 0;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (a.acceleration.y > 3) {
command = 1;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (-3 < a.acceleration.y && a.acceleration.x < 3) {
command = 4;
Serial.println(command);
radio.write(&command, sizeof(command));
}
}

For my final milestone, I was able to add neopixel lights onto my robot car that would change colors for every direction the car could move. I didn’t have enough time to work on my initial modification idea which involved a sensor that could blink colors under close proximity to an object, in addition to performing an automatic rotation of 180 degrees, but I intend to continue modifying my project after demo night at BlueStamp Engineering. Anyways, I started off this modification by researching how exactly neopixels worked with an Arduino Uno. I learned that neopixel strands have many unique functions that could be integrated with a current set of code inside of “if” and “else” statements. One can make the lights blink, flow through a set length, correspond to a specific function, change the color of the lights using a rgb color picker, and so much more. I opted for my robot car to be able to change color upon moving a new direction. I didn’t really face any challenges as this was a simple modification. There is still so much more that I would like to improve upon my robot car, and I hope to be able to use the skills I learned to implement my ideas.  

Third Milestone

Code for__(Arduino Nano)__

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
int command = -1;//0=foward
//1=backwards
//2=right
//3=left
RF24 radio(7, 8); // CE, CSN
const byte address[6] = “00001”;
Adafruit_MPU6050 mpu;
void setup() {
Serial.begin(115200);
while (!Serial)
delay(10); // will pause Zero, Leonardo, etc until serial console opens
Serial.println(“Adafruit MPU6050 test!”);
// Try to initialize!
if (!mpu.begin()) {
Serial.println(“Failed to find MPU6050 chip”);
while (1) {
delay(10);
}
}
Serial.println(“MPU6050 Found!”);
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
Serial.print(“Accelerometer range set to: “);
switch (mpu.getAccelerometerRange()) {
case MPU6050_RANGE_2_G:
Serial.println(“+-2G”);
break;
case MPU6050_RANGE_4_G:
Serial.println(“+-4G”);
break;
case MPU6050_RANGE_8_G:
Serial.println(“+-8G”);
break;
case MPU6050_RANGE_16_G:
Serial.println(“+-16G”);
break;
}
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
Serial.print(“Gyro range set to: “);
switch (mpu.getGyroRange()) {
case MPU6050_RANGE_250_DEG:
Serial.println(“+- 250 deg/s”);
break;
case MPU6050_RANGE_500_DEG:
Serial.println(“+- 500 deg/s”);
break;
case MPU6050_RANGE_1000_DEG:
Serial.println(“+- 1000 deg/s”);
break;
case MPU6050_RANGE_2000_DEG:
Serial.println(“+- 2000 deg/s”);
break;
}
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
Serial.print(“Filter bandwidth set to: “);
switch (mpu.getFilterBandwidth()) {
case MPU6050_BAND_260_HZ:
Serial.println(“260 Hz”);
break;
case MPU6050_BAND_184_HZ:
Serial.println(“184 Hz”);
break;
case MPU6050_BAND_94_HZ:
Serial.println(“94 Hz”);
break;
case MPU6050_BAND_44_HZ:
Serial.println(“44 Hz”);
break;
case MPU6050_BAND_21_HZ:
Serial.println(“21 Hz”);
break;
case MPU6050_BAND_10_HZ:
Serial.println(“10 Hz”);
break;
case MPU6050_BAND_5_HZ:
Serial.println(“5 Hz”);
break;
}
Serial.println(“”);
delay(100);
}
void loop() {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
Serial.print(a.acceleration.x);
Serial.print(” “);
Serial.println(a.acceleration.y);
delay(250);
if (a.acceleration.x < -3) {
command = 2;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (a.acceleration.x > 3) {
command = 3;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (a.acceleration.y < -3) {
command = 0;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (a.acceleration.y > 3) {
command = 1;
Serial.println(command);
radio.write(&command, sizeof(command));
}
else if (-3 < a.acceleration.y && a.acceleration.x < 3) {
command = 4;
Serial.println(command);
radio.write(&command, sizeof(command));
}
}

Code for__(Arduino Uno)__

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <L298N.h>
const unsigned int IN1 = 10;
const unsigned int IN2 = 6;
const unsigned int ENA = 9;
const unsigned int IN3 = 5;
const unsigned int IN4 = 4;
const unsigned int ENB = 3;
RF24 radio(7,8); // CE, CSN
int value = -1;
const byte address[6] = “00001”;
Adafruit_MPU6050 mpu;
void setup() {
Serial.begin(9600);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
pinMode (IN1, OUTPUT);
pinMode (IN2, OUTPUT);
pinMode (IN3, OUTPUT);
pinMode (IN4, OUTPUT);
pinMode (ENA, OUTPUT);
pinMode (ENB, OUTPUT);
delay(100);
}
void loop() {
if (radio.available()) {
radio.read(&value, sizeof(value));
Serial.println(value);
if (value == 2) {
//Serial.print(text);
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
}
else if (value == 3) {
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
else if (value == 0) {
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
}
else if (value == 1) {
analogWrite(ENA, 255);
analogWrite(ENB, 255);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
else if (value == 4) {
analogWrite(ENA, 0);
analogWrite(ENB, 0);
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
}
else {
analogWrite(ENA, 0);
analogWrite(ENB, 0);
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
//digitalWrite(10, LOW);
delay(2000);
}
}
}

My third milestone was being able to finally finish my project. I was able to create a code so that the values that the accelerometer reads from hand motions can be sent through the NRF modules and onto the Arduino Uno, where the values would ultimately be interpreted as commands for directions in which the robot car needed to move. My first step in developing a code for both my Arduino Uno and Arduino Nano involved incorporating the previous codes I had used for the NRF modules and accelerometer. These codes would act as a basis where I was able to make alterations, in addition to “if” and “else” statements. “if statements” are essentially a condition that if met, will result in a function. In the case of my robot car, the “if statements” in the receiver code would tell the Arduino Uno to move a specific direction if it reads a number that is correlated to that direction. For example, moving the accelerometer forward results in a value of 0, a number of which the Arduino Uno would then have to interpret as a command to move forward. In the end, I programmed “0” to read forward, “1” to read backwards, “2” to read right, “3” to read left, and “4” to read stop. I also included “else statements,” which are backup commands in case the condition in the “if statement” was not met. This proved especially useful as I was able to narrow down my problems as to why certain functions weren’t working properly. For instance, if a function wasn’t working I would write a phrase in my “else statement” such as “not working” that I would be able to read on a serial monitor. From there I would recheck my wiring and code to figure out what exactly was my problem. For my final milestone, I plan to work on modifications for my project. If time allows, I would want to be able to add a sensor that would detect nearby objects, and if my car gets close enough to an object, it would make the robot car perform a certain action like blinking lights or an automatic rotation of 180 degrees. 

Second Milestone

Code for__(Accelerometer MPU6050)__
/*
   Arduino and MPU6050 Accelerometer and Gyroscope Sensor Tutorial
   by Dejan, https://howtomechatronics.com
*/

#include <Wire.h>

const int MPU = 0x68; // MPU6050 I2C address
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY, gyroAngleZ;
float roll, pitch, yaw;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY, GyroErrorZ;
float elapsedTime, currentTime, previousTime;
int c = 0;

void setup() {
  Serial.begin(19200);
  Wire.begin();                      // Initialize comunication
  Wire.beginTransmission(MPU);       // Start communication with MPU6050 // MPU=0x68
  Wire.write(0x6B);                  // Talk to the register 6B
  Wire.write(0x00);                  // Make reset - place a 0 into the 6B register
  Wire.endTransmission(true);        //end the transmission
  /*
  // Configure Accelerometer Sensitivity - Full Scale Range (default +/- 2g)
  Wire.beginTransmission(MPU);
  Wire.write(0x1C);                  //Talk to the ACCEL_CONFIG register (1C hex)
  Wire.write(0x10);                  //Set the register bits as 00010000 (+/- 8g full scale range)
  Wire.endTransmission(true);
  // Configure Gyro Sensitivity - Full Scale Range (default +/- 250deg/s)
  Wire.beginTransmission(MPU);
  Wire.write(0x1B);                   // Talk to the GYRO_CONFIG register (1B hex)
  Wire.write(0x10);                   // Set the register bits as 00010000 (1000deg/s full scale)
  Wire.endTransmission(true);
  delay(20);
  */
  // Call this function if you need to get the IMU error values for your module
  calculate_IMU_error();
  delay(20);

}

void loop() {
  // === Read acceleromter data === //
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  //For a range of +-2g, we need to divide the raw values by 16384, according to the datasheet
  AccX = (Wire.read() << 8 | Wire.read()) / 16384.0; // X-axis value
  AccY = (Wire.read() << 8 | Wire.read()) / 16384.0; // Y-axis value
  AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0; // Z-axis value
  // Calculating Roll and Pitch from the accelerometer data
  accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) - 0.58; // AccErrorX ~(0.58) See the calculate_IMU_error()custom function for more details
  accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) + 1.58; // AccErrorY ~(-1.58)

  // === Read gyroscope data === //
  previousTime = currentTime;        // Previous time is stored before the actual time read
  currentTime = millis();            // Current time actual time read
  elapsedTime = (currentTime - previousTime) / 1000; // Divide by 1000 to get seconds
  Wire.beginTransmission(MPU);
  Wire.write(0x43); // Gyro data first register address 0x43
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 4 registers total, each axis value is stored in 2 registers
  GyroX = (Wire.read() << 8 | Wire.read()) / 131.0; // For a 250deg/s range we have to divide first the raw value by 131.0, according to the datasheet
  GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
  GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
  // Correct the outputs with the calculated error values
  GyroX = GyroX + 0.56; // GyroErrorX ~(-0.56)
  GyroY = GyroY - 2; // GyroErrorY ~(2)
  GyroZ = GyroZ + 0.79; // GyroErrorZ ~ (-0.8)

  // Currently the raw values are in degrees per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees
  gyroAngleX = gyroAngleX + GyroX * elapsedTime; // deg/s * s = deg
  gyroAngleY = gyroAngleY + GyroY * elapsedTime;
  yaw =  yaw + GyroZ * elapsedTime;

  // Complementary filter - combine acceleromter and gyro angle values
  roll = 0.96 * gyroAngleX + 0.04 * accAngleX;
  pitch = 0.96 * gyroAngleY + 0.04 * accAngleY;
  
  // Print the values on the serial monitor
  Serial.print(roll);
  Serial.print("/");
  Serial.print(pitch);
  Serial.print("/");
  Serial.println(yaw);
}


void calculate_IMU_error() {
  // We can call this funtion in the setup section to calculate the accelerometer and gyro data error. From here we will get the error values used in the above equations printed on the Serial Monitor.
  // Note that we should place the IMU flat in order to get the proper values, so that we then can the correct values
  // Read accelerometer values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    AccX = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    AccY = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    // Sum all readings
    AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
    AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
    c++;
  }
  //Divide the sum by 200 to get the error value
  AccErrorX = AccErrorX / 200;
  AccErrorY = AccErrorY / 200;
  c = 0;
  // Read gyro values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x43);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    GyroX = Wire.read() << 8 | Wire.read();
    GyroY = Wire.read() << 8 | Wire.read();
    GyroZ = Wire.read() << 8 | Wire.read();
    // Sum all readings
    GyroErrorX = GyroErrorX + (GyroX / 131.0);
    GyroErrorY = GyroErrorY + (GyroY / 131.0);
    GyroErrorZ = GyroErrorZ + (GyroZ / 131.0);
    c++;
  }
  //Divide the sum by 200 to get the error value
  GyroErrorX = GyroErrorX / 200;
  GyroErrorY = GyroErrorY / 200;
  GyroErrorZ = GyroErrorZ / 200;
  // Print the error values on the Serial Monitor
  Serial.print("AccErrorX: ");
  Serial.println(AccErrorX);
  Serial.print("AccErrorY: ");
  Serial.println(AccErrorY);
  Serial.print("GyroErrorX: ");
  Serial.println(GyroErrorX);
  Serial.print("GyroErrorY: ");
  Serial.println(GyroErrorY);
  Serial.print("GyroErrorZ: ");
  Serial.println(GyroErrorZ);
}
Code for __(NRF module transmitter)__
/*
* Arduino Wireless Communication Tutorial
*     Example 1 - Transmitter Code
*                
* by Dejan Nedelkovski, www.HowToMechatronics.com
* 
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(7, 8); // CE, CSN

const byte address[6] = "00001";

void setup() {
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
}

void loop() {
  const char text[] = "Hello World";
  radio.write(&text, sizeof(text));
  delay(1000);
}
Code for __(NRF module receiver)__
/*
* Arduino Wireless Communication Tutorial
*       Example 1 - Receiver Code
*                
* by Dejan Nedelkovski, www.HowToMechatronics.com
* 
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(7, 8); // CE, CSN

const byte address[6] = "00001";

void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();
}

void loop() {
  if (radio.available()) {
    char text[32] = "";
    radio.read(&text, sizeof(text));
    Serial.println(text);
  }
}
Code for __(NRF Connection Check)__

// 18 Mar 2018 – simple program to verify connection between Arduino
//      and nRF24L01+
//  This program does NOT attempt any communication with another nRF24

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

#include <printf.h>

#define CE_PIN   9
#define CSN_PIN 10

const byte thisSlaveAddress[5] = {‘R’,’x’,’A’,’A’,’A’};

RF24 radio(CE_PIN, CSN_PIN);

char dataReceived[10]; // this must match dataToSend in the TX
bool newData = false;

void setup() {
Serial.begin(9600);
printf_begin();

Serial.println(“CheckConnection Starting”);
Serial.println();
Serial.println(“FIRST WITH THE DEFAULT ADDRESSES after power on”);
Serial.println(”  Note that RF24 does NOT reset when Arduino resets – only when power is removed”);
Serial.println(”  If the numbers are mostly 0x00 or 0xff it means that the Arduino is not”);
Serial.println(”     communicating with the nRF24″);
Serial.println();
radio.begin();
radio.printDetails();
Serial.println();
Serial.println();
Serial.println(“AND NOW WITH ADDRESS AAAxR  0x41 41 41 78 52   ON P1″);
Serial.println(” and 250KBPS data rate”);
Serial.println();
radio.openReadingPipe(1, thisSlaveAddress);
radio.setDataRate( RF24_250KBPS );
radio.printDetails();
Serial.println();
Serial.println();
}

void loop() {

}

My second mile stone includes being able to work both the accelerometer and the NRF modules with my Arduino uno and Arduino nano. My biggest challenge in working with these two components was having to incorporate a breadboard to make my connections to the Arduinos. This was especially difficult considering the wires I used were extremely loose, many of my pins weren’t able to stay stable, and the fact that the wires would constantly move. Evidently, the breadboard wasn’t a viable tool that I could use and as such, I switched to using male and female wires in order to make direct connections instead of using a third party like the breadboard. From there I was able to upload code to properly test my accelerometer so that it could detect motion and the acceleration of motion when it is moved around. As for my NRF modules, even after using male and female wires, I still had issues with being able to test them. My main goal with working with the NRF modules was being able to get them to send a message to each other through a serial monitor that reads “hello world.” After using several different codes and wiring positions, I figured out that because I had plugged an adaptor to my NRF modules, I needed to connect the VCC pin into 5V instead of the 3.3V that I had been using before hand. After I was finally able to test both the accelerometer and NRF modules for their function, I began designing a code that would allow for the NRF module to send the values the accelerometer reads from hand movements onto the Arduino, and for the accelerometer’s values to be interpreted as commands for the Arduino to move the motors of the car into certain directions and speeds. 

First Milestone

My first milestone was being able to create a circuit among a battery pack, an arduino, a motor driver (L298N), and 2 deceleration DC motors in order to get the wheels on my chassis to be running. I was able to do this by first connecting wires from the flaps of the DC motors to OUT 1-4 on the motor driver. A motor driver is important in this instance as it is what will take a low-current control signal from the control circuits, and turn it into a higher current signal that will power the motors. This is done so through this next step, which involves using male wires to connect the motor driver to an arduino through a specific set of numbers on the digital side of the arduino. The main purpose of the arduino is to add more power to the motor driver and to insert computer code that will be able to control the wheels. From there I connected my battery pack to my motor driver with the voltage in mind. Because a set of 4 batteries contains 6 volts, I connected the red wire to the 12V section of the motor driver, grounded the black wire, and connected the 5V on the motor driver to GND on the arduino. Finally, I inserted a specific set of code onto the arduino to be able to change the direction of the wheels, their speed, and the lag time.

Leave a Comment

Start typing and press Enter to search

Bluestamp Engineering