Вы находитесь на странице: 1из 441

1

Automatic Drink Maker


ME 445 Final Project

Group 10

Fazil Rastam and Brian Bedoya

December 17, 2013


2

Table of Contents
Executive Summary ..................................................................................................................................... 3

1.0 Introduction ........................................................................................................................................... 4

1.1 System Design ................................................................................................................................. 4

2.0 Hardware/Circuitry ................................................................................................................................ 5

2.1 Bill of Materials for Major Components.......................................................................................... 5

2.2 Sparkfun 12 Button Keypad ............................................................................................................ 5

2.3 Step Motor ....................................................................................................................................... 6

2.4 Peristaltic Pump .............................................................................................................................. 7

2.5 Cds Sensor ...................................................................................................................................... 7

3.0 Software ................................................................................................................................................ 8

4.0 Discussion ............................................................................................................................................. 9

5.0 Conclusions .......................................................................................................................................... 10

Appendix A – Arduino Code ...….............................................................................................................. 11

Appendix B – Bill of Materials ......…………………............................................................................... 18

Appendix C – Data Sheet for Major Components ..................................................................................... 26


3

Executive Summary
We developed a device that will automatically dispense liquid and make a mixed drink in
a 1 oz. glass, depending on the user inputs. The user will enter which liquids they want to use
and then how many of that particular drink they would like, repeating a drink combination to up
to 5 times. Entered through a keypad, the user has the option of four different liquid choices and
can select up to a combination of 10 inputs. The device is intended to use with liquids of
different specific gravities. With liquids of different gravities, the user would be able to make
layered drinks, with the “heavier” liquid settling on the bottom of the glass.

Our device incorporates peristaltic pumps, controlled by MOSFET transistors, to


dispense the liquid into the glass. A peristaltic pump is a type of displacement pump that
incorporates a rotor with “lobes” attached to the external circumference of the rotor. As the rotor
turns, the “lobes” compresses the silicone tubing and forces fluid to move through the tube. We
chose this type of pump to dispense the liquids because with this method, consistent and precise
volume of fluid can be dispensed. To fill a 1 oz. glass, a peristaltic pump has to run for 25
seconds.

For each liquid or input that is selected, an incremental counter keeps track of how many
inputs are used. With the number of inputs known, the time it takes to fill a 1 oz. glass is then
divided by this number to allow for equal dispensation of each liquid type.

The 1 oz. glasses are placed upon a tray, which is attached to a step motor, and will rotate
to the position underneath the drink dispenser nozzles. Our device uses a light sensor to detect
whether there is a glass underneath the drink nozzles.
4

1.0 Introduction
The final project for ME 445 was to create a device that integrated electrical and
mechanical components and was controlled by an Arduino. Our group decided on creating an
automatic drink dispenser that dispenses liquids effortlessly and effectively. With our limited
experience with C and electronics, we decided on a project that was within what we could
realistic accomplish.

We wanted to work on a project that would be useful to us after the class ended. Because
one of our group members makes mixed drinks on the weekends, this project was suggested. The
group member commented on how long it takes to make layered drinks and when there is a high
volume of people, these layered drinks cause the line to back up. The intention of this project is
to allow the creation of mixed drinks automatically and quickly, while the user can attend to
other actions.

2.0 System Design


The project that ended up being built was a result of many design revisions we thought of
while writing our code. Our initial concept was that of a conveyor belt system that would simply
transport a container from dispenser to dispenser in linear path. We rejected this idea later on for
several reasons. First was the issue with the instability the containers would experience while on
a conveyor belt that we would have made ourselves and second was the length of the project
would have been too long to transport easily. A short conveyor was not an option because you
would not be able to efficiently fill containers consecutively because of the need to stop the
conveyor while we removed the finished product.

We then thought of a system that would do one drink at a time and have it securely
fastened to an arm that would move linearly and be able to raise the container closer to the
dispensers, but this idea would later be rejected because of it not falling into the original plans of
making several drinks quickly.

We created our final design when we changed our minds on how the dispensers would be
located. Once we moved all the dispensers to one location it was then simple to decide on the
current design of a tray that would simply rotate each container into position once it was time to.
5

3.0 Hardware
3.1 Bill of Materials for Major Components

Units Item Vendor Approx. Cost


1 Arduino Uno Jameco $30.00
4 Peristaltic Pump Adafruit $24.95 (each)
1 Stepper Motor
1 CdS Light Sensor Adafruit $0.95
1 Sparkfun Keypad Sparkfun $3.95
Total

To control the drink maker, we used an Arduino Uno to act as the brains for the device.
The keypad, from Sparkfun, allows the user to select which liquids they want to use, how much
of each liquid they want their drink to be made up of, and how times they want that drink to be
repeatedly made. The peristaltic pumps, one for each type of liquid, will dispense the liquids
based on which one the user selected, and in the order that the user selected. The step motor turns
a tray, which the 1 oz. glasses are placed upon, and allows for the repeated creation of the
particular drink. The CdS light sensor detects whether there is a glass underneath the liquid
dispenser. If the light sensor detects a glass, the peristaltic pumps will proceed to dispense the
liquids. However, if the light sensor does not detect a glass, the tray will continue to rotate until a
glass is detected.

3.2 Sparkfun 12 Button Keypad

This is the hardware that allows for user interaction. For this project, we selected the
Sparkfun 12 button keypad. We selected this keypad due to the availability of the part in the lab
and because there were wiring diagrams online. We allotted the keys 1 through 4 to correspond
to liquids 1 through 4. We also designated key ‘*’ to being a button one could press to move the
stepper motor 5 steps clockwise for realignment of the tray, ‘0’ to prep the liquids in the
peristaltic tube so that the liquids are at the face of the entrance, and ‘#’ to run the program after
the inputs were selected.

The keypad is wired directly to the Arduino, using seven digital pins. Four of the pins
correspond to a row and the remaining three pins correspond to a column. With this setup, if a
key is pressed, the combination of the two inputs, row and column, allow the program to
recognize which keystroke is pressed. To use a keypad in the program, we had download the
‘keypad’ library from the Arduino site. With this library, we used the functions ‘getKey()’,
which returns the key that is pressed into the Arduino, and ‘waitForKey()’, which causes the
program to wait forever until the user presses a key.
6

The Sparkfun keypad and keypad pins with its correlating Arduino digital pin

After we downloaded the keypad library, we discovered that the keystrokes were returned
to the Arduino as character data types. Because of this, to use the keystrokes inputted, we had to
convert the characters into integer data types. We had to use this when the user was inputting
how many repeated drinks they wanted to make.

3.3 Stepper Motor

We believed that the best way to do repeated drinks was to have the glasses on a
rotating tray. With a rotating tray, after one drink is made in one glass, the tray will rotate and
move the already made drink out of the way, while bringing a new glass to the liquid dispensing
position. We decided to use a stepper motor as the method to physically rotate the tray because
with a stepper motor, within the program, one can adjust the speed of the stepper easily and
program how many steps the stepper should move. Our particular stepper motor requires 200
steps to rotate a full 360 degrees. Therefore, since our tray had ten potential spaces to put a 1 oz.
glass in, we had our stepper motor rotate 20 steps to each space. We also set the stepper motor
speed to 15, within the program. Anything above this value, as the stepper speed, caused the
liquid with the glass to spill as the tray rotated.
7

3.4 Peristaltic Pump

A peristaltic pump from Adafruit

A peristaltic pump is a type of displacement pump that incorporates a rotor with “lobes”
attached to the external circumference of the rotor. Inside this particular pump is a ‘clover’
pattern of rollers. As the motor turns, the clover presses on the tube to press the fluid through.
This allowed for a consistent and precise dispensation of a predetermined volume of fluid. For
our project, we found that running a peristaltic pump, connected to a 15 volt power source, on
high would dispense 1 oz. of liquid in 25 seconds.

Inside of a peristaltic pump

We used MOSFET transistors to control the four peristaltic pumps used in the project.
With this motor control, we were able to direct when the pumps should be turned to on or off,
and for how long the pumps should be running for. Also, we could have used PWM to control
the speed of the pumps. Connected to the MOSFET transistors were flyback protection diodes to
eliminate sudden voltage spikes across and inductive load, if the supply voltage is suddenly
removed or reduced.

3.5 CdS Light Sensor

A cadmium sulfide (CdS) or light dependent resistor, this sensor provides resistance that
is inversely dependent on the light falling on it. This sensor is also known as a photoresistor or a
8

photoconductive cell. We connected one side of the photocell to 9V power and the other side to
an Arduino’s analog pin. Then we connected a 330 ohm pull-down resistor from that analog pin
to ground. With this set up, we were able to detect the voltage/light values when there was a 1
oz. glass in front of the sensor, and when there was no glass in front of the sensor.

Within the program, there is a while loop that indicates that if the light sensor reading is
above 50, the analog input reading we measured when there was no glass in front of the sensor,
the stepper motor will continue to turn 20 steps, to the next potential glass position. For our 1 oz.
glasses, we measured a light sensor reading of 20-35. Therefore, if the light sensor reading is
below 50, a glass has been detected and the peristaltic pumps can begin to dispense liquid.

Figure 2.1: CdS light sensor wiring schematic

4.0 Software
Our project’s code is split into two main while loops. The first part runs in a while loop
where the user can input the liquids they want dispensed. While the program is running this loop,
up to 10 inputs can be selected. The inputs are stored into an array and will be returned when the
‘run’ button is pressed. When each input button was pressed, an incremental counter in the code
would keep track of how many inputs were selected. When the amount of inputs is known, the
program will know to divide the volume of 1 oz. of liquid by the amount of inputs. Also, while
in this loop, the user can press the ‘*’ button to move the stepper motor 5 steps to realign itself
underneath the liquid dispenser and the ‘0’ button to prep the peristaltic pumps by having all the
pumps run on high for 5 seconds. When connected to the serial monitor on the computer, text
would appear after pressing each key to confirm to the user what was pressed.

When the ‘run’ button was pressed, designated as the ‘#’ key, the program would exit out
of the first while loop and enter the second while loop. First in this second while loop, the phrase
“# of drinks?” is displayed on the serial monitor. With the keypad library function,
waitForKey(), the program halts and doesn’t do anything until the number of drinks is selected.
Allowing for up to 5 repeats of a drink to be made, once keypad button 1 through 5 is selected,
the program will start to dispense the liquids. Before the project dispenses the liquids, a CdS
light sensor detects whether there is a glass underneath the liquid dispenser position. In the
9

program, we set the light sensor reading of a glass to be beneath 50. When light reading is below
50, the program will read the user selected inputs stored in the array and return them. Depending
on the specific order of the inputs selected, the coinciding peristaltic motors will run in the same
order. When the project makes the designated number of drinks, the program returns to the first
while loop and the counter value reverts back to zero, erasing the previous data stored in the
array. Finally, the stepper motor moves a final 20 steps clockwise to exit the position underneath
the liquid dispenser, to allow the user to pick up the glass easily.

5.0 Discussion
From the design process to the final construction, we constantly saw ways to improve
upon the project. The first step for improvement would be the overall construction of the
automatic drink maker. The tray we used is poorly connected to the stepper motor, attached by
hot glue and stabilized by electric tape. This hasty construction allowed for the stepper motor to
be misaligned often and angled. An angled tray would cause problems because it would lead to
spillage of the drinks. With a stronger tray and a flat, stable connection to the stepper motor, the
glasses would have a better chance at aligning with the liquid dispenser. Also, alongside the tray
improvements, there would be permanent holder for the glasses. Currently for our project, we
used plastic medicine cups to hold the 1 oz. glasses. The medicine cups were slightly larger than
the glasses and consequently, when the stepper motor moved, the glasses would reposition in the
medicine cups leading to possible misalignment.

Other improvements to the project would be on the hardware side. The light sensor, while
simple to implement, turned out to be temperamental. The light sensor range to detect the glasses
regularly changed depending on the overall brightness of the room and whether or not lights
were directly overhead. Because of this, the light sensor would only detect the glasses if the
setting was unchanged. If the light sensor worked previously, but then the project moved to
another area with different light brightness, the light sensor would not be able to detect the glass.
An improvement would be to use an infrared sensor to detect the glasses. With an IR sensor,
there would be consistent detections of glasses, regardless of room brightness. Another piece of
hardware to implement would be an LCD display. While connected to the serial monitor, the
user was able to confirm what keypad strokes were being pressed and what action the project
was on. However, without the serial monitor, the user is left unsure to what action the project is
on. With an LCD display, the user can see what inputs are being selected, how many inputs were
selected, and what action is being done.
10

6.0 Conclusion
Overall, we are pleased with our final project. We were satisfied that with our limited
knowledge in the field of mechatronics, we were still able to create a working project. There is
still a lot that there could be improved on with the automatic drink maker, however the idea of
the project is established.

At the beginning of the semester, we went into this project with little knowledge of how
to successfully integrate mechanical and electrical systems into a functional product. However,
during the process of creating our automatic drink maker, we gained invaluable information
related to our field of study that can only benefit our future professional endeavors. Through the
process of designing, implementing and completing our final product, we have not only given
ourselves an edge over career-related competition, but most importantly, we get to go home with
a free automatic drink maker.
11

Appendix A. Arduino Code

//Project code by Fazil Rastam and Brian Bedoya

//December 17, 2013

#include <Keypad.h>

#include <Stepper.h>

const int stepsPerRevolution = 200;

Stepper myStepper(stepsPerRevolution, 9, 10, 11, 12);

//Initializing analog pins 5, 4, and 3 as digital pins 17 -19

int input1Motor = 18;

int input2Motor = 19;

int input3Motor = 17;

int analogPin = 0; //Initializing light sensor as analog pin 0

// setting variables used in code to 0

int x = 0;

int val = 0;

int num = 0;

int counter = 0; //variable that will store the count

int liquid[10]; //array has a max size of 10

int inputType = 0;

int buttonPress = 0;

int drinkRun = 0;

//---------------------------------------------------------------------------------------------------------
12

// Initializing the keypad

const byte ROWS = 4; //four rows

const byte COLS = 3; //three columns

char keys[ROWS][COLS] = {

{'1','2','3'},

{'4','5','6'},

{'7','8','9'},

{'*','0','#'}

};

byte rowPins[ROWS] = {5, 4, 3, 13}; //connect to the row pinouts of the keypad

byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup()

myStepper.setSpeed(10); //sets stepper motor speed to 10

// setting the peristaltic motors to receive output data from Arduino

pinMode(input1Motor, OUTPUT);

pinMode(input2Motor, OUTPUT);

pinMode(input3Motor, OUTPUT);

Serial.begin(9600); //set up serial communication

}//end of setup

void loop()

while (drinkRun == 0){


13

char key = keypad.getKey(); // reads key from keypad

if (key != NO_KEY){ //detects change in key states

if(key == '0'){ //if the 0 key was pressed turn all motors high for 5 seconds

Serial.println("Run Motors");

digitalWrite(input1Motor, HIGH);

digitalWrite(input2Motor, HIGH);

digitalWrite(input3Motor, HIGH);

delay(5000);

digitalWrite(input1Motor, LOW);

digitalWrite(input2Motor, LOW);

digitalWrite(input3Motor, LOW);

if(key == '*'){ //if key * is pressed, turn the stepper motor 5 steps

Serial.println("Turn 5 Steps");

myStepper.step(5);

if(key == '1'){

counter = counter + 1; //increment the counter

Serial.print("input #1 ");

Serial.print("number of inputs: ");

Serial.println(counter); //print it on serial monitor

delay(100); //debounce delay

liquid[counter] = 1;

if(key == '2'){
14

counter = counter + 1; //increment the counter

Serial.print("input #2 ");

Serial.print("number of inputs: ");

Serial.println(counter); //print it on serial monitor

delay(100); //debounce delay

liquid[counter] = 2;

if(key == '3'){

counter = counter + 1; //increment the counter

Serial.print("input #3 ");

Serial.print("number of inputs: ");

Serial.println(counter); //print it on serial monitor

delay(100); //debounce delay

liquid[counter] = 3;

if(key == '4'){

counter = counter + 1; //increment the counter

Serial.print("input #4 ");

Serial.print("number of inputs: ");

Serial.println(counter); //print it on serial monitor

delay(100); //debounce delay

liquid[counter] = 4;

}
15

if (key == '#'){ //if the # key is pressed, exit the while loop and run the run() function

drinkRun = 1;

run();

void run(){

while (drinkRun == 1){

Serial.println("# of Drinks?");

char waitForKey(); // pauses program, waits for a key to be pressed.

char repeat = keypad.getKey();

if (repeat != NO_KEY){

Serial.println(repeat);

if (repeat == '1'){
num = 1;
}

if (repeat == '2'){
num = 2;
}

if (repeat == '3'){
num = 3;
}

if (repeat == '4'){
num = 4;
}

if (repeat == '5'){
num = 5;
}

val = analogRead(analogPin); //reads light sensor data


16

Serial.println(val); //displays the light sensor value

for (int x = 1 ; x <= num ; x++){

val = analogRead(analogPin);

while (val > 50){ //light sensor threshold, beneath this would detect a glass

myStepper.step(20); //turns stepper motor 20 steps

delay(500);

val = analogRead(analogPin); //reads and prints light sensor data for user benefit

Serial.println(val);

Serial.println(val);

for (int i = 1 ; i <= counter ; i++){

Serial.println(liquid[i]);

if (liquid[i] == 1){

delay(100);

digitalWrite(input1Motor, HIGH);

delay(25000/counter);

digitalWrite(input1Motor, LOW);

delay(100);

if (liquid[i] == 2){

delay(100);

digitalWrite(input2Motor, HIGH);

delay(25000/counter);

digitalWrite(input2Motor, LOW);

delay(100);
17

if (liquid[i] == 3){

delay(100);

digitalWrite(input3Motor, HIGH);

delay(25000/counter);

digitalWrite(input3Motor, LOW);

delay(100);

if (x == num){ //if x reaches the number of repeated drinks made, exit the while loop

drinkRun = 0;

counter = 0; //restarts counter, erases previous array data.

myStepper.step(20);

}
18

Appendix B. Bill of Materials

Bill of Materials

Hardware Vendor Quantity Cost/unit Cost

Arduino Uno Jameco 1 29.95 29.95

200 Stepper Motor TEC 1 12.95 12.95

Peristaltic Pump Adafruit 4 24.95 99.8

CDS Light Sensor Adafruit 1 0.95 0.95

Sparkfun Keypad Sparkfun 1 3.95 3.95

ULN2805 Octal Darlingtion Texas Instruments 1 2.23 2.23

Mosfet STMicroelectronics 4 0.54 2.16

Protection Flyback Diode - 4

330 ohm Resistor - 4

Total Cost 109.09


19

A.3 Datasheets for Major Components

Darlington Transistor for Stepper Motor Control


20

Stepper Motor
21

Sparkfun 12 Button Keypad


22
23

MOSFET Transistor
Autonomously
Navigating Car with
Obstacle Avoidance

By Ramya Cheruvu
ME 445: Microcomputer Interfacing
Prepared for: Dr. Henry J. Sommer III and Mike Robinson
Dec 17, 2013

1
Abstract

This report details the modification of a small scale automobile which will enable the vehicle to
maneuver around obstacles autonomously while traversing the distance towards a user set destination.
The objective of this project is to replace the outdated control system. The car will be fit with a
programmed Arduino which uses feedback from GPS sensor and Ultrasonic Rangefinder to gather
information about the surroundings. The micro controller will then plan the course and provide required
actuation to the motors. Motion of the car is monitored by an IMU sensor which checks if the course is
in line with the planned trajectory. Also, manual override capability has been provided to the end user
by introducing a IR sensor which generates the appropriate command according to the signal sent by the
a remote.

2
Table of Contents

Contents
Abstract ........................................................................................................................................................................................ 2
Introduction ............................................................................................................................................................................... 4
Overall design ........................................................................................................................................................................... 4
Body............................................................................................................................................................................... 4
Electronic Hardware............................................................................................................................................................... 5
Dual motor Driver.................................................................................................................................................... 5
MPU6050 ..................................................................................................................................................................... 6
Servo motor………………........................................................................................................................................... 6
Ultrasonic RangeFinder......................................................................................................................................... 7
GPS logger………………….......................................................................................................................................... 7
IR receiver………………….......................................................................................................................................... 8
Software. ..................................................................................................................................................................................... 9
Schematic................................................................................................................................................................................... 10
Integration................................................................................................................................................................................ 11
Conclusions and Future Work........................................................................................................................................... 11
Bill of Materials........................................................................................................................................................................ 12
Appendix................................................................................................................................................................................... 12

3
Introduction
The goal of this project falls under the category of find goal problem in sensor based motion planning. In
general, classical path planning algorithms assume prior knowledge of the configuration space and are
not amenable to sensor information. This is disadvantageous in the cases where the surroundings are
unknown or changing. Additionally, acquiring information about surroundings sometimes requires path
planning. In such cases, sensor based motion planning becomes important. The vehicle in this case does
not have prior knowledge of the surroundings. It uses the on-board information to obtain information
about the surroundings. The car has to find a collision free path from its current location to a user set
destination based on the information from sensors.

A basic algorithm is considered for controlling the course of the vehicle. The slope of an imaginary line
between current location and goal configuration is computed at every point to obtain the bearing of the
car. Distance from the goal point is also computed and these values are then translated into motion by
proportional control of actuators.

Goal Point(xf,yf)

-x x

Fig 1: Path planning

Overall Design
The design of the vehicle in this case is not of critical importance. Any reasonably scaled vehicle which
has two degrees of freedom is sufficient for this project.

Body

4
The metal chassis of a toy car with wheels actuated by DC motors has been chosen for this purpose. It
can be directed to any point in x-y plane by control and variation of the speed between left and right
wheels. It is also equipped with five 1.5V battery holders which give approximately 5~6V and can be
used to power the motors.

Connection
to DC
motor

Fig 2: Car wheels controlled by Dual motor Drivers

Electronic Components

Motor Drivers
Dual motor drivers have been used to control the speed and direction of the wheels. Two motor drivers
have been used to control the four wheels and Arduino provides the necessary analogue inputs to
drivers. Arduino provides two analogue inputs, one for both right wheels and left wheels each. The
motor 5V power needed for both logic inputs and running the motor (M+ and GND pins) are provided by
batteries in the car.

5
Fig 3: Dual Motor Driver

Now that actuators of car can be controlled, the car can be made to follow the course by running the car
at a particular speed till it reaches the goal, i.e., distance computed from algorithm becomes zero. More
difficult to account for is the direction in which the car is heading. To ensure that the car moves in the
correct direction, feedback of car bearing is of considerable importance. For this purpose, an Inertial
Measurement Unit sensor is introduced into the circuit to enable PID control of the bearing based on
sensor feedback.

MPU-6050
MPU-6050 is an inertial measurement unit combining both accelerometer and gyroscope. The breakout
board GY-521 is used for this project. It gives acceleration in x, y, z directions and rotation about x, y, z
axes. For control of bearing, rotation about z-axis is needed. Accelerometer does not give this value. It
has to be obtained from raw gyroscope data. MPU6055 gives angular velocity about z axis. We obtain
the angle by integrating raw angular velocity with respect to time. The raw sensor values have to be
calibrated before use. Calibration has been done by subtracting the raw values with average of first ten
readings before integration. Since we are integrating, the value of angle drifts with time and it it is not
accurate in long-term. Therefore, the car is made to stop and turn to the required angle every time it
has to change direction and then run straight when direction is correct. Since angle from gyroscope is
used for a short period of time during proportional control of bearing, the increasing drift is negligible.

Fig 4: MPU6050 breakout

6
Ultrasonic Range Finder
An ultrasonic rangefinder by MaxBotix is used for obstacle detection. This sensor determines the
distance by sending out a pulse and measuring the length of the reflected echo. It is good for long range
applications. The minimum distance measured by ultrasonic sensor is 30cm. Its readings fluctuate a lot,
so to get accurate readings the values have been filtered by using a mode/median filter. The mode filter
sorts out a set of readings and returns the value that has been read maximum number of times. In case
there is no common value, the middle value among the sorted readings is taken as the distance. The
ranges of values are taken at an interval of 30ms to prevent reading the same value twice. A range of 4
consecutive readings give an accurate estimation of distance. However, since this sensor does not give
information about width of the obstacle, mounting this sensor on a servo motor has been considered to
get collision free path.

Fig 5: Ultrasonic Sensor mounted on a Servo

Servo motor
Ultrasonic rangefinder is mounted on a servo motor which is made to rotate through angles 0 to 90 deg.
This enables the range finder to sweep the entire area and the micro controller computes the obstacle
free direction closest to the current direction based on information from the sensor. The result is in
terms of angle (of servo motor) of course which has no obstacles. If the rotation of the servo is too fast,
same distance is recorded for all the angles. A delay of one second between angles has been found to
give accurate readings.

GPS sensor
A GPS sensor has been selected for feedback about current location of the vehicle. The GPS logger shield
from Adafruit is included in the circuit. The sensor returns information about time, temperature, date,
latitude, longitude etc. Latitude and longitude are relevant to this project as they provide feedback of
current location. The latitude and longitude can be logged and retrieved either in XML format or KML
format.

7
Fig 6: Adafruit GPS logger shield

<dataset2 xmlns="xsdbooks"> <Count Data="173">


<UTC>Mon Dec 16 2013 10:17:33 GMT-0500 (Eastern Standard Time)</UTC>
<FixType>3D-Fix</FixType>
<Lat>40.791745</Lat>
<Lon>-77.865350</Lon>
<HGT>186</HGT>
</Count>
<Count Data="174">
<UTC>Mon Dec 16 2013 10:17:48 GMT-0500 (Eastern Standard Time)</UTC>
<FixType>3D-Fix</FixType>
<Lat>40.791761</Lat>
<Lon>-77.865175</Lon>
<HGT>191</HGT>
</Count>
<Count Data="175">
<UTC>Mon Dec 16 2013 10:18:03 GMT-0500 (Eastern Standard Time)</UTC>
<FixType>3D-Fix</FixType>
<Lat>40.791730</Lat>
<Lon>-77.864992</Lon>
<HGT>192</HGT>
</Count>
Table 1: XML data from GPS logger

IR receiver
An IR receiver has been included to allow for manual override by operator. This receives modulated IR
signals transmitted by a remote. The remote is equipped with IR led which blinks according to the

8
button pressed. The value of the signal for each button has been decoded using an Arduino script and
micro controller performs certain functions when a particular button is pressed. This remote transmits
random codes other than the value of the button. These codes will not fire the written sequence in
Arduino. The button has to be pressed 2-3 times to fire an expected operation.

Straight

Fig 7: IR receiver and Remote

Table 2: code for Decoding IR signals

#include <IRremote.h>

int IRpin = 11;


IRrecv irrecv(IRpin);
decode_results results;

void setup()
{
Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
}

void loop()
{
if (irrecv.decode(&results))
{
Serial.println(results.value, DEC); // Print the Serial 'results.value'
irrecv.resume(); // Receive the next value
}

9
Software

10
Schematic

Integration
There have been setbacks in integrating these components. Ultrasonic sensor with Servo and IR receiver
has been successfully incorporated in the main circuit. However, the first problem arose when trying to
use MPU6050 along with the remaining setup. Arduino hangs whenever the car is started. The same
problem has been observed with GPS sensor. Running both car (motor drivers) and GPS sensor at once
halts the execution of code and reduces the speed of car wheels. Several trouble shooting methods have
been employed to determine the source of the issue:

1. Manual checking of the schematic and circuits has been repeated to prevent mistake due to
wiring. Debugging of the code resulted in no errors. Even the basic code for obtaining raw values
for IMU sensor halts when DV motors are switched on. So, the problem seemed to be with the
circuit.
2. It was concluded that malfunction may be due to insufficient power supply as failure occurred
when starting the motor and also a corresponding decrease in motor speed was observed.
Separate power sources have been used for IMU, GPS and motors without success.
3. Capacitors were added to power sources to filter out any disturbances. Also, physical damping
was applied to rule out the possibility of effects due to physical disturbances.
4. The pins for PWM used for the motors in Arduino have been changed to avoid serial outputs on
the GPS shield without any results.

11
Conclusion and Future Work
The malfunction has finally been assumed to be caused by the way GPS shield is wired. Next course of
action would be to have IMU tested with different motors (car), or without GPS shield. Another option is
using a different position sensor. Testing with different GPS sensor also might allow for pinpointing the
source of the error.

Bill of Materials
Component Quantity Cost
GY-521 1 $39.95
MaxBotix MB 1023 1 $5.95
Adafruit ultimate GPS logger 1 $39.95
shield
IR receiver and remote 1
RC car 1
Servo Motor 1
Dual motor drivers 2

Appendix
/******Code for Autonomous Navigation********/

//Pins Dc Motor control with L293D chip


int rightsidePin = 3;
int leftsidePin = 5;
int rightdirectionPin = 12;
int leftdirectionPin = 13;
int proportionalGain = 15;
float angle = 90;//remove later
float targetPos;
float theta;
float Speed;
int xd;

//Goal Point in terms of Latitude and Longitude


float xf = 4041.6311;
float yf = 7751.8959;

//For GPS
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);
#define GPSECHO false

12
//For IR
#include <IRremote.h>
int IRpin = 11;
IRrecv irrecv(IRpin);
decode_results results;

// setup ultrasonic distance sensor


boolean debug = true;
const int pingPin = 4;
unsigned int duration, distance;
int sensorWait = 30;
int arraysize = 4;
int rangevalue[] = { 0, 0, 0, 0};
long pulse;
long timePrev = 0;
int chosenDirection;
int chosenAngle;

// setup servo under sensor


#include <Servo.h>
Servo sensingServo;
int servoPin = 9;
int pos = 1500;
int sensingServoMin = 700;
int sensingServoMax = 2300;
int sensingServoResolution = 100;

// setup array for storing directions and distances


// array length = ((sensingServoMax - sensingServoMin) / sensingServoResolution) + 1;
int directionsAndDistances[37][2];
int directionsAndDistancesLength = 37;
int i;
// setup arrray for choosing direction
int obstacleFreeDirections[37][3];
int minimumDistance = 45;//Set minimum according to how soon obstacles are to be detected
int waypointDirection = 1500;//The centre angle for servo

//For IMU
#include "Wire.h"//
#include "I2Cdev.h"
#include "MPU6050.h"

MPU6050 accelgyro;//Initialising from downloaded library

int16_t ax, ay, az;


int16_t gx, gy, gz;
// Use the following global variables and access functions
// to calibrate the acceleration sensor

13
float axBase;
float ayBase;
float azBase;

float gxBase;
float gyBase;
float gzBase;
unsigned long t;
// Use the following global variables and access functions to help store the overall
// rotation angle of the sensor
unsigned long tPrev = 0;
float axPrev = 0;
float ayPrev = 0;
float azPrev = 0;
float gxPrev = 0;
float gyPrev = 0;
float gzPrev = 0;
float gzAngle;

void setup()
{

Serial.begin(115200);
//For Car
pinMode(rightsidePin, OUTPUT);
pinMode(rightdirectionPin, OUTPUT);
pinMode(leftsidePin, OUTPUT);
pinMode(leftdirectionPin, OUTPUT);
digitalWrite(leftsidePin,LOW);
digitalWrite(rightsidePin,LOW);

//For IR
irrecv.enableIRIn(); // Start the receiver

//For Servo
sensingServo.attach(servoPin);

//For GPS
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
GPS.sendCommand(PGCMD_ANTENNA);

// Uncomment to start logging GPS


// if (GPS.LOCUS_StartLogger())
// Serial.println(" STARTED!");
// else

14
// Serial.println(" no response :(");
// delay(1000);

//For IMU
// join I2C bus (I2Cdev library doesn't do this automatically)
Wire.begin();
// initialize device
accelgyro.initialize();
calibrate_sensors();
}

uint32_t timer = millis();


void loop()
{
Serial.println("Loop");

if (irrecv.decode(&results)) // have we received an IR signal?

{
translateIR();
irrecv.resume(); // receive the next value
} //end of if IR
else{
driveStraight();
readSensor();
delay(100);
if(distance>=45){//Check for Obstacles before proceeding
driveStraight();
//delay(100);
// read data from the GPS
char c = GPS.read();

// Checks for sentences to parse.


if (GPS.newNMEAreceived()) {
if (!GPS.parse(GPS.lastNMEA()))
return; // we can fail to parse a sentence in which case we should just wait for another
}
// Serial.print("\nTime: ");
// Serial.print(GPS.hour, DEC); Serial.print(':');
// Serial.print(GPS.minute, DEC); Serial.print(':');
// Serial.print(GPS.seconds, DEC); Serial.print('.');
// Serial.println(GPS.milliseconds);
// Serial.print("Date: ");
// Serial.print(GPS.day, DEC); Serial.print('/');
// Serial.print(GPS.month, DEC); Serial.print("/20");
// Serial.println(GPS.year, DEC);
Serial.print("Fix: "); Serial.print((int)GPS.fix);
//Serial.print(" quality: "); Serial.println((int)GPS.fixquality);

15
if (GPS.fix) {
// Serial.print("Location: ");
// Serial.print(GPS.latitude, 4); Serial.print(GPS.lat);
// Serial.print(", ");
// Serial.print(GPS.longitude, 4); Serial.println(GPS.lon);
float x = (GPS.latitude,4);
float y = (GPS.longitude,4);
angle = atan((yf-y)/(xf-x));
Serial.print("angle");
Serial.print(" ");
Serial.print(angle);
Serial.print(" ");
Serial.print(distance);
xd = sqrt(sq((xf-x)+sq(yf-y)));
if(xd>0){
if (angle>0){
goToAngle((90-angle));
driveStraight();
}
else if(angle<0){
goToAngle(-(90-angle));
driveStraight();
}
}
else{driveStop();}

}//eND OF gps FIX


}
else{//In case of obstacles
i = 0;
for(pos = sensingServoMin; pos <= sensingServoMax; pos += sensingServoResolution)
{
readSensor();
sensingServo.writeMicroseconds(pos);//Change position of servo from 0-90(700-2300)
delay(1000);//delay to prevent dummy readings on Ultrasonic
i++;
}
//get the angle of collision free path
chosenDirection = chooseDirection();
sensingServo.writeMicroseconds(chosenDirection);
chosenAngle = sensingServo.read();
//Set the servo to original position
pos = 1500;
chosenAngle=map(chosenAngle,0,180,-90,90);
//Adjust the bearing of the car
goToAngle(chosenAngle);
}//end of else
}//enf of else for IR

16
}//End of Loop

////Function to adjust bearing with feedback from IMU


//void goToAngle(float angle){
// targetPos = map(angle,0,180,-90,90);//Mapping angle to coordinate system of car
// theta = measureOfAngle();//Get current angle fron IMU
// Speed = (targetPos - theta)*proportionalGain;
// Speed = map(abs(Speed),0,5,0,255);
// if(Speed>5){
// Speed = 255;
// }
// if(theta < targetPos){
// //move forward when current position is less than target position
// Serial.print("Left");
// turnLeft(Speed);
//
// }
// else if(theta > targetPos) {
// //move forward when current position is less than target position
// Serial.print("Right");
// turnRight(Speed);
// }
// else{
// driveStop();
// }
// delay(10);
//}

//Function to adjust bearing without IMU based on a scale of running one side for 300ms allows the car
to turn by one degree
void goToAngle(float angle){
float carAngle = angle;
int scale = carAngle*300;
if(carAngle>0){
turnLeft(255);
delay(scale);
driveStop();
}
else{
turnLeft(255);
delay(scale);
driveStop();
}
}

/******************Methods for driving the motors*************************/


void driveStraight()

17
{
//int motorSpeed = map(abs(Speed), 0, 100, 0, 255);
Serial.print("Straight");
digitalWrite(rightdirectionPin, LOW);
digitalWrite(leftdirectionPin, LOW);
analogWrite(leftsidePin, 255);
analogWrite(rightsidePin, 255);
}

void driveReverse()
{
//int motorSpeed = map(abs(Speed), 0, 100, 0, 255);
digitalWrite(rightdirectionPin, HIGH);
digitalWrite(leftdirectionPin, HIGH);
analogWrite(leftsidePin, 255);
analogWrite(rightsidePin, 255);
}

void turnLeft(int Speed)


{
int wheelSpeed = Speed;
Serial.print("Left");
digitalWrite(rightdirectionPin,LOW);
digitalWrite(leftdirectionPin,HIGH);
analogWrite(leftsidePin, wheelSpeed);
analogWrite(rightsidePin, 0);
}

void turnRight(int Speed)


{
int wheelSpeed = Speed;
Serial.print("Right");
digitalWrite(rightdirectionPin, HIGH);
digitalWrite(leftdirectionPin, LOW);
analogWrite(leftsidePin, 0);
analogWrite(rightsidePin, wheelSpeed);
}

void driveStop(){
Serial.println("Stop");
analogWrite(leftsidePin, 0);
analogWrite(rightsidePin, 0)
;
}
/******************End of Methods for driving the motors*************************/

18
/*******Methods for IR Receiver**************************************************/
// takes action based on IR code received
void translateIR()
// Numbers in case statement correspond to values of IR Remote
{
switch(results.value)
{
case 16605343:
driveStop();
Serial.println(" Stop ");
break;

case 16621663:
driveStraight();
Serial.println("Straight");
break;

case 16625743:
driveReverse();
Serial.println("Reverse");
break;

case 16584943:
turnLeft(100);
delay(10);
driveStop();
Serial.println(" Left ");
break;

case 16601263:
turnRight(100);
delay(10);
driveStop();
Serial.println(" Right ");
break;
default:
Serial.println(" other button ");

delay(500);

} //END translateIR

/***********Methods for Obstacle detection by ultrasonic sensors*********************/


//reads the value from Ultrasonic Sensor

19
void readSensor()
{
// move servo into position
sensingServo.writeMicroseconds(pos);

// read ultrasonic sensor


pinMode(pingPin, INPUT);
for(int i = 0; i < arraysize; i++)
{

pulse = pulseIn(pingPin, HIGH);


rangevalue[i] = pulse/10;
delay(40); //to account for resolution of sensor
}
isort(rangevalue,arraysize);
distance = mode(rangevalue,arraysize);
// write position and distance to array
directionsAndDistances[i][0] = pos;
directionsAndDistances[i][1] = distance;
}

//Sorting function
// sort function (Author: Bill Gentles, Nov. 12, 2010)
void isort(int *a, int n){
// *a is an array pointer function
for (int i = 1; i < n; ++i)
{
int j = a[i];
int k;
for (k = i - 1; (k >= 0) && (j < a[k]); k--)
{
a[k + 1] = a[k];
}
a[k + 1] = j;
}
}
//End Sort

//Mode function, returning the mode or median.


int mode(int *x,int n){

int i = 0;
int count = 0;
int maxCount = 0;
int mode = 0;
int bimodal;
int prevCount = 0;
while(i<(n-1)){

20
prevCount=count;
count=0;
while(x[i]==x[i+1]){

count++;
i++;
}
if(count>prevCount&count>maxCount){
mode=x[i];
maxCount=count;
bimodal=0;
}
if(count==0){
i++;
}
if(count==maxCount){//If the dataset has 2 or more modes.
bimodal=1;
}
if(mode==0||bimodal==1){//Return the median if there is no mode.
mode=x[(n/2)];
}
return mode;
}
}//End Mode filter for ultrasonic

int chooseDirection()
{
// filter out directions with obstacles
for(int i,j = 0; i < directionsAndDistancesLength; i++)
{
if (directionsAndDistances[i][1] >= minimumDistance)
{
obstacleFreeDirections[j][0] = directionsAndDistances[i][0];
obstacleFreeDirections[j][1] = directionsAndDistances[i][1];
}
else
{
obstacleFreeDirections[j][0] = 0;
obstacleFreeDirections[j][1] = 0;
}
obstacleFreeDirections[j][2] = 0;
j++;
}
// calculate the discrepancy between the waypoint direction and each direction
for(int i = 0; i < directionsAndDistancesLength; i++)
{
if(obstacleFreeDirections[i][0] != 0)
{

21
obstacleFreeDirections[i][2] = abs(waypointDirection - obstacleFreeDirections[i][0]);
}
}
// choose the direction closest to the direction of the goal with no obstacles
// find the first obstacle free direction
int solutionDirectionReference = 0;
while(obstacleFreeDirections[solutionDirectionReference][0] == 0)
{
solutionDirectionReference++;
}
// find the obstacle free direction closest to the direction of the goal
for(int i = 0; i < directionsAndDistancesLength; i++)
{
if(obstacleFreeDirections[i][0] != 0 && obstacleFreeDirections[i][2] <
obstacleFreeDirections[solutionDirectionReference][2])
{
solutionDirectionReference = i;
}
}
if(obstacleFreeDirections[solutionDirectionReference][0] > 15000)
{
return 0;
}
else
{
return obstacleFreeDirections[solutionDirectionReference][0];
}
}
/***********End of Methods for Obstacle detection from ultrasonic sensors*********************/

/***********Methods for reading from IMU(if it works)*********************/


//Method for calibration of GY 521
void calibrate_sensors() {
int num_readings = 10;
float ax_standard = 0;
float ay_standard = 0;
float az_standard = 0;
float gx_standard = 0;
float gy_standard = 0;
float gz_standard = 0;
// Read and average the raw values from the IMU
for (int i = 0; i < num_readings; i++) {
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
ax_standard += ax;
ay_standard += ay;
az_standard += az;
gx_standard += gx;
gy_standard += gy;

22
gz_standard += gz;
delay(100);
}
ax_standard /= num_readings;
ay_standard /= num_readings;
az_standard /= num_readings;
gx_standard /= num_readings;
gy_standard /= num_readings;
gz_standard /= num_readings;

// Store the raw calibration values globally


axBase = ax_standard;
ayBase = ay_standard;
azBase = az_standard;
gxBase = gx_standard;
gyBase = gy_standard;
gzBase = gz_standard;
}

float measureAngle(){
accelgyro.getRotation(&gx, &gy, &gz);//Get raw values from IMU
t=millis();
//Divide by 131 to convert to deg
gzAngle = (((gz-gzBase)/131)*((float)t-(float)tPrev)/1000)+gzPrev;
tPrev = t;
gzPrev = gzAngle;
return(gzAngle);
}

23
Final Project – Educational
Arduino Based Robots
ME 445: Microcomputer Interfacing

Team 12
Brad Wile and Mike Coia

Submitted: December 17th, 2013


Introduction
Background
Objective
Standard Robot Design
Design
Materials
Construction
Autonomously Controlled Robot Design
Materials
Hardware Modifications
Hardware Troubleshooting
Software Modifications
Software Troubleshooting
Conclusion
Wirelessly Controlled Robot Design
Design
Materials
Hardware Modifications
Hardware Troubleshooting
Software Modifications
Software Troubleshooting
Discussion
References/Parts
Appendix A: Code Appendix
Introduction
This was an open-ended project that required the use of an Arduino to control and integrate
software and hardware concepts that were covered in class. Applying the class material and the
labs that were performed in over the course of this semester allowed the team the skills necessary
to design and build these educational robots.

Background
For this project, Team 12 decided to integrate two different areas of interest. The team members
are part of the Engineering Ambassadors here at Penn State, a group of students who promote
engineering interest to high school and middle school students. The group goes into these
classrooms with a presentation of an engineering topic and a related activity. One example is a
presentation of biomimicry (using nature’s design in developed products), and then performing
an activity of creating a prototype prosthetic leg. This allows the students to understand a little
bit about what engineers do and some insight into the design process they use.

The group is always expanding its array of activities, so they entered an application to an IBM
and IEEE grant to promote engineering to the younger generations. This grant earned the
Engineering Ambassadors around $500 to purchase supplies needed to create the robots,
discussed below.

Objective
The objective of this project was to create another activity for the Engineering Ambassadors to
use in a school setting to demonstrate engineering. The activity needed to be informative,
showing the students what engineers can do, but not overly complicated, so as not to confuse the
students with too many engineering concepts and technology thrown at them at the same time.
The students that perform these activities should see how a little competition, creativity and
effort can create awesome results.

In order to complete these goals, two different robots were created to accomplish different tasks.
A demonstration robot that will be used by the Engineering Ambassadors will be a wireless robot
that will be controlled by an accelerometer in a glove worn by the user. This would allow them
to drive and turn the robot with just slight movements of their hands. The other set of robots
would be a set of robots used in the activity the students perform. Their objective is to get a
robot through a simple maze in as fast as possible.

Standard Robot Design


Design
The design of the robots was somewhat pre-set for this project. Our group was given the
structural components for 8 simple robots. The design for these robots has been floating around
the ME department for a while. Our team saw it beneficial to keep the general design. If we
intepret the high school and middle school classes to be our customer, one big design driver is
simplicity. We want these robots to work for the students. Since the current design only
encompassed the bare minimum number of parts, we saw no reason to complicate these robots.
Making the general design more complex was also a durability risk. Durability was important in
this case, because a wide range of students will be using them in these high schools, therefore we
do not want them to break.

Materials
The materials needed to construct the robot were easily available and found online. The list of
materials can be found below.

Part Quantity / (Per Unit)

Arduino Uno 1

Acrylic Body 1

Acrylic Wheels 2

Ball Bearing 1

Ball Bearing Socket 1

Non Continuous Servo 1

Continuous Servo 2

IR Sensor 1

Battery Pack 1

AA Batteries 4

Mounting Screws -

Connecting Wires -

Construction
Once the design and the materials for the robots were located, construction could begin.

Acrylic Body

Using the laser cutter located in the learning factory, the acrylic sheets that were purchased were
used to create the body of the robot and the power wheels. The body joined together without
hardware, but there was some trouble at points because acrylic is very brittle and can snap very
easily. Custom fitting was necessary for each robot using a belt sander and rubber mallet. While
Using acrylic were somewhat aesthetically pleasing, it would have been more beneficial to use a
stronger structural base for these robots. This would have also better aligned with our design
drivers that were previously mentioned.

Servos

The continuous servos were used to drive the power wheels, and were attached using pre
cut holes in the acrylic and screws. The non-continuous servo was used to swivel the IR sensor,
which sat atop the robot and could be used for a variety of applications. The ball bearing acted
as the front wheel, just stabilizing the robot and allowing it to roll easily.

Arduino/Power Connection

On the back of the robot was where the Arduino sat, which allowed easy access for all the wires
and other components to attach to it. The battery pack sat inside the robot, with a cutout for the
power switch cut into the bottom the robot for easy power control. The whole robot is powered
by 4 AA batteries to supply 6V to the Arduino.

Figure 1: Basic Robot Design

Autonomously Controlled Robot Design


The wirelessly controlled robots needed to be both simple and innovative. The entire
purpose of these robots is to spark a kids interest a science and technology field. This group felt
that developing a robot that could autonomously move would be a great way to show students
that engineering can be interesting. These robots are designed to navigate through a maze. The
high school students will interface with these robots using two potentiometers. One
potentiometer will adjust the speed of the robot. The other potentiometer will adjust the IR
sensor distance.

Materials

Part Quantity (Per Unit)

ARD-0119 protoshield KIT 1

Hardware Modifications
Using the preconstructed robots as a base, a ARD-0119 protoshield KIT was attached to
the arduino in order to deliver the necessary power to drive the robots. Thess protoshields
contained power and ground pins running across the circuit board. This shield allowed us to
power multiple components in an organized manner. For our specific purpose, both continuous
servos, the IR sensor, and the non continuous servo was connected to this protoshield

Figure 2: Diagram of ARD-0119 protoshield KIT

Hardware Troubleshooting
Soldering these protoshields together proved to be one of the most important parts of this
project. Improperly soldering a wire of connection to this board has the potential to cause the
arduino to incorrectly interface with the servos and IR sensor. There were multiple times where
the IR sensor was relaying incorrect data. Similar problems occurred when the arduino would
attempt to interface with the continuous servos.
Figure 3: Final Assembly of Autonomous Robots

Software Modifications
A simple maze navigation algorithm was designed to roughly navigate the robot around a
maze. When the arduino is powered on, the robot is commanded to move forward. While in
forward motion, the robots is continuously scanning it’s path in the forward facing direction.
Once the IR sensor detects an object in it’s path it ceases all motion. The algorithm then instructs
the non continuous servo to rotate in the 90 degrees in both directions (relative to the forward
direction). The robot then will then determine which direction contains the most open path. Once
this has been determined, the robot will make a 90 degree turn in that direction.

Software Troubleshooting
There will be between 5-7 Robots running this control algorithm when in these
classrooms. One issue that needed to be corrected was the minor speed adjustments for each
robot. The continuous servos are motionless when they are programed to have a speed of zero.
However, these servos are not perfectly accurate. Making sure each servo has been set it it’s
individual equilibrium point is crucial. The turning code also needs to be tailored to each robot.
Modifying the speeds for the continuous servos will ensure each robot rotates 90 degrees. This is
a tedious task, however, it is essential in order for this activity to run smoothly in a high school
environment.

Discussion
Completing these robots was successful, and they work just as expected. The only things that
could be added are potentiometers to adjust the gain of the IR sensor and the speed of the car.
This would allow the students to be able to tweak the car to allow it to traverse a maze and race
their classmates.
Wirelessly Motion Controlled Robot Design
Materials

The extra materials needed for the wireless robot were:

Part Quantity

XBee Series 1 wireless modules 2

Sparkfun XBee Shield WRL-10854 2

Adafruit ADXL335 3-Axis Accelerometer 1

Prototyping Board 1

Soccer Glove 1

Extra Arduino Uno 1

Extra battery pack and batteries 1

Hardware Modifications
After creating the standard robots, the wirelessly controlled one could be created from a
modified version. The IR sensor was removed, as it is not needed. The Xbee Shields needed
headers to be soldered to them, and then were connected with the Xbees and Arduinos. The
Shields were extremely helpful as they could just be dropped on the Arduino and then all of the
pins were still usable. The Xbee could then be dropped on the shield and act as a transmitter or a
receiver depending on the code; the shield allowed each to be used in both ways. Because of our
design however, each Xbee had its own specific role as one acted as the receiver and the other as
the transmitter.

In order to create the motion-controlled glove, the transmitter Xbee, battery pack, and
accelerometer were all physically attached to the soccer glove that was used. They were
velcroed on to allow for removal and easier storage and transportation. The accelerometer was
only used with 2-Axis, as the Z direction was unnecessary for our design. It was connected to
two of the Arduino’s Analog Inputs to send the data it collected from the movement of the glove
for the X- and Y-axis. The X-axis controls the forward and backward motion of the car, tilting
one’s hand forward would allow it to move forward, and tilting it backwards allows the car to
move backwards. If the hand is kept level, the car is stationary. The Y-axis controls the turning
of the car and its bearing. Rolling the hand to the left would turn the car in that direction, and the
opposite is true as well.
Figure 4. Arduino with Shield and Xbee

As the hand is more steeply moved, the speed of the car would increase. This means it would
move in the X-axis faster, or turn faster in the Y-axis. Turning would first be accomplished by
increasing the speed of the opposite wheel. As the roll of the hand is increased, the wheel on the
side of the turn would slowly decrease its speed, until it stops. This turns the car faster and
allows for tighter turns. Eventually, the wheel would eventually start going the opposite
direction to increase the turn speed even more.

Figure 5. ADXL 335 Accelerometer

With the accelerometer connected to the Arduino, the Xbee would then send the data obtained to
the car itself. The Xbee uses the IEEE 802.15.4 protocol to communicate short-range with the
other XBee. The range on the Xbee is advertised as 300ft, which is more than enough to
communicate with the car in a classroom setting.
Figure 6. XBee Module Series 1

Hardware Troubleshooting
Initially connecting the XBee was a bit of a problem at first. The XBee need to be set to the
same baud rate in order send and receive information effectively. This required the use of
special boards with USB connections to connect to the computer and the use of a data terminal
program to send specialized codes to the XBees.

Software Modification
As there were many hardware changes from the original robot, the software needed to be
different from the original as well. The code for the transmitter is not very complicated, just
taking the data from the accelerometer and printing out to the XBee which sends it to the other.
The code for receiving is a little more complicated however. The Xbees send info as strings, so
it must converted to an integer number in order to be read by the servos. In order for this to be
accomplished, the string must be read into the Arduino, then the string is read to a character
array. This then allows it to be broken down number by number into their ACSII codes. This
code can then be converted back into the number as an integer number. It was a little hiccup in
the process of using the Xbees, but it was an easy fix that allowed for easy communication.
These numbers were then scaled to work with the servos, as they accept numbers from 0-180,
while the accelerometer would send numbers typically between 315 and 480. This was taken
care of with the map function, and then the numbers were sent to the servos using the included
libraries. The map function was also used to convert the number to the opposite which could be
sent to the other servo, since positive for one is negative for the other based on the hardware
installation. This allowed both servos to move at the same speed in the same direction when
needed.

Software Troubleshooting
Unfortunately, the code for controlling the car wirelessly was a bit tricky, and something was
stopping both servos from working together. As seen in the in-class demo, only one wheel
would spin , causing the car to just spin in place. This was less than impressive, but did show
that the wireless communication was in fact working. The servos would work together when
simple number were inputted into them, but when the variables containing the data from the
XBee were used, one of the servos did not work. This problem could not be diagnosed but some
possible causes that need to be addressed would be the conversion from string to integers from
the XBee, or the fact that the integer was not “reconnected” after the string was converted. The
problem does not seem to be a hardware issue as both servos will work when the variables are
replaced with numbers.

Discussion
As the robot was not fully functional, there is definitely more work to be done. The code must
be exhaustively searched for any discrepancies that are preventing the servos from working
properly. Creating this robot was full of problems, from the Xbees not communicating, to the
servos not working, to the necessity of another Arduino and power supply, but it allowed the
team to learn from our mistakes and almost come up with a finished product. Fixing the code is
the biggest thing that needs to be shown attention, but there are other improvements that could
make it even better of a project. First, the XBee are only transmitting at 9600 bps. This causes
the robot to not move very fast when it is being controlled. It would make for an even better
activity if it was moving at normal speed. This could be improved by increasing the baud rate to
57600 bps. Also, the glove was bulky and not aesthetically pleasing. If the wiring connecting
the accelerometer to the Arduino was made using a PCB, it would reduce the size of the
hardware dramatically and perhaps could be an internal system inside of the glove. Also, using
motors instead of servos would have increased the speed and power of the robot, but the
design/material constraints did not allow for that.
References/Parts
www.BobbyLeary.com/r3
http://store.nkcelectronics.com/Protoshield-KIT-for-Arduino-UNO-R3_p_308.html
http://www.adafruit.com/products/163#Learn
https://www.sparkfun.com/products/10854
https://www.sparkfun.com/datasheets/Wireless/Zigbee/XBee-Datasheet.pdf
Appendix A: Code Appendix
Autonomous Code
#include <Servo.h>
Servo first; // create servo object
Servo second; // create servo object
Servo third; // create servo object;

int firstservopin = 9; // digital output to which signal wire(yellow) of first


servo is connected
int secondservopin= 10; // digital output to which signal wire(yellow) of second
servo is connected
int thirdservopin = 11;
int sensor;
int i;
int j;

void setup() {
Serial.begin(9600);
first.attach(firstservopin); // attach the servo to digital output 8
second.attach(secondservopin); // attach the servo to digitalt output 7
third.attach(thirdservopin); // attach the servo to digital output
first.write(93); // center the servo
second.write(94); // center the servo
third.write(90);
delay(1000);
}
void loop() {
sensor = analogRead(A2);
delay(1000);
Serial.println(sensor);
if(sensor > 300) {
first.write(93); // center the servo
second.write(94); // center the servo
third.write(0);
i = analogRead(A2);
delay(1000);
third.write(180);
j = analogRead(A2);
delay(1000);
if(i > j) {
first.write(85);
second.write(91);
delay(1000);
first.write(93); // center the servo
second.write(94);
}
else
{
first.write(111);
second.write(97);
delay(1000);
first.write(93); // center the servo
second.write(94);
}
third.write(90);
delay(1000);
}
else {
first.write(84);
second.write(105);
}
}

Wireless Receiver Code


#include <Servo.h>
Servo left; // create servo object
Servo right; // create servo object
int pos = 90;
int Xavg = 330;
int Yavg = 330;
int firstservopin = 9; // digital output to which signal wire(yellow) of first
servo is connected
int secondservopin= 7;
String readString;
String readString2;
char char1Array[7];
char char2Array[7];
int count = 60;
int one = 93;
int two = 93;
int val2 = 93;
int val1 = 93;
void setup() {
Serial.begin(9600);
left.attach(firstservopin); // attach the servo to digital output 8
right.attach(secondservopin); // attach the servo to digital output 7
//digitalWrite(7,HIGH);
//Serial.println("serial test 1"); // so I can keep track of what is loaded
}
void loop() {

while (Serial.available()) {
delay(10);
if (Serial.available() >0) {
if (readString.length() >2) {
char b = Serial.read();
readString2 += b;
}
else {
char c = Serial.read();
readString += c;
}
}
}
if (readString.length() >0) {

Serial.print(readString);
Serial.println(readString2);
readString.toCharArray(char1Array, (readString.length()+1));
val1 = atoi(char1Array);
readString2.toCharArray(char2Array, (readString2.length()+1));
val2 = atoi(char2Array);
val2 = map(val2, 315,395, 60,126);
//Serial.print(val1);
//Serial.println(val2);
one = val2;
two = map(one, 60,126,126,60);
Serial.print(one);
Serial.println(two);
if (one > 120){
one = 120;}
if (one < 60){
one = 60;}
}

left.write(one);
right.write(two);
//delay(50);
readString="";
readString2="";
}

Wireless Sender Code

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);

void loop() {
// put your main code here, to run repeatedly:
delay(250);
int x = analogRead(A3);
int y = analogRead(A2);
Serial.print(x);
Serial.println(y);
}
The Stacker Game

Technical Report Submitted to: Dept. of Mechanical & Nuclear Engineering


ME445
Prof. Henry Sommer
Mike Robinson
337 Leonhard Building
The Pennsylvania State University
University Park, PA 16802

Submitted by: Bradley Corbin


Henry Itani
Kathleen Prilutski

Date: 12/17/13
Abstract
This ME 445 Fall 2013 project sets out to create a micro-controller operated arcade game
commonly known as Stacker. The main Stacker arcade components include a panel of eight by
eight addressable RGB LEDs, an ATMega328P which is the micro-controller in an Arduino
Uno, a 16MHz resonator to provide a clock signal to the Atmega, two 10 micro-Farad capacitors
that act as power decouplers, a 7805 power regulator, a big button to trigger the LEDs, and a 9V
battery. When combined together, the components work together like an Arduino Uno to
interface the LEDs and trigger button to line the LEDs up on the box display.

In order to enable a different skill level for each row of the game, different speeds were
incorporated into the LED panel. Considering that human interaction with the game does take
some time and experience, the first level is set for a beginner at a very slow LED speed while the
last level is set for an expert at a very high LED speed.

Adding complexity to our project, the human triggered Stacker arcade game competes
against our simulated robot triggered Stacker arcade game. The robot works by using eight
phototransistors, a solenoid, two 9V batteries, a power MOSFET and eight resistors. Each of the
phototransistors line up with a row of the LED panel. By lining up the phototransistors, the
Stacker game will time each row of the LED panel, triggering the solenoid which presses the
button, ultimately, winning the game. This timing is imperative as the solenoid must be triggered
only when the phototransistor voltage output is highest. This occurs when the LED light is
directly below the phototransistor. If the LED were ahead or behind the phototransistor when the
solenoid triggered, the LED rows of the game would not be aligned, losing the game.

However, it should be noted that with such a high voltage power source the solenoid
should not be turned on at all times. This would cause the solenoid to become very hot over time.
Therefore, a power MOSFET, along with several resistors and a flyback diode, were utilized in
the circuit operating the solenoid. The power MOSFET will turn the solenoid on and off through
a digital Arduino pin. The flyback diode will be placed in parallel to the solenoid within the
circuit to minimize flyback, or the sudden voltage spike from an inductive load created when the
power supply is suddenly removed.

Both the robot triggered Stacker and human triggered Stacker arcade games were
designed inside 6” by 10” wooden box in order for easy transport. The human triggered Stacker
arcade was designed with intent as a portable arcade durable for people of any age group.
Contents
Abstract ......................................................................................................................................2
Introduction...............................................................................................................................4
Project Description ................................................................................................................4
Objective ................................................................................................................................4
LED Display ..............................................................................................................................4
Library ...................................................................................................................................5
Micro-controller ........................................................................................................................6
ATMega328P .........................................................................................................................6
16 MHz Resonator.................................................................................................................7
7805 Power Regulator ...........................................................................................................7
Power Decoupling Capacitor ................................................................................................8
Robot Trigger ............................................................................................................................9
MOSFET ...............................................................................................................................9
Solenoid................................................................................................................................ 10
Phototransistor .................................................................................................................... 11
Conclusion ............................................................................................................................... 12
Appendix ................................................................................................................................. 13
Stacker Project Code........................................................................................................... 13
Robot Code .......................................................................................................................... 35
Introduction

Project Description
A Stacker arcade game is a game whose goal is to align the rows of moving LED blocks
on top of each other. A player who can stack eight rows wins the game. However, the key is to
beat the robot that will be playing against the human player. The robot will work by using a
solenoid to trigger the button on the Stacker whenever the rows are properly aligned.

“picture of the Stacker box”

The two Stacker arcade boxes will race against one another being human and robot
triggered, respectively. Regardless of the player, the Stacker will display green light if the player
is successful in defeating the game or the Stacker will display a red light if the player is defeated
by the game.

Objective

This project will explore the following concepts: using the components of an Arduino to
simulate an Arduino as a controller, interfacing the button with a solenoid, understanding an
eight by eight LED panel of LEDs and their library, and learning about individual electronic
components and their various uses. There were several main objectives in order to fully
understanding how the LEDs worked and the robot would trigger and play the Stacker game.
 Create two Stacker arcade games within a 6” by 10” wooden box
 Develop a code that runs each of the LED rows at different times; the initial rows will be
run at a slower rate than the later rows
 Create a robot that simulates a “perfect human” playing the arcade game Stacker
 Develop a timing system for the trigger on the robot in order to succinctly trigger the
button

LED Display
“Picture of Illuminated LED on Game”
The LED display consists of an eight by eight LED matrix with each pixel being
individually addressable with assistance from the LED library. Therefore, only one micro-
controller pin is required to control all 64 LEDs. The LED display is connected with two three-
pin connection ports. These connections are 5VDC, ground and the digital input pin to the micro-
controller. Each of the 64 LED can draw as much as 60 mA.

Depending on the brightness of the R, G and B of the LED, different colors will be
displayed. For example, if the brightness were 200, 0, 0, the LED panel would show only Red
whereas if the brightness were 0,0,200 the LED panel would reveal only blue light. In order to
use all functions of the RGB LEDs, the three different colors must be initialized within the
coding. For this project specifically, a white light was deemed the most visible under a black
tinted piece of Plexiglas which would be utilized to protect the LED panel.
It should also be noted that there is a single data line that contains a very timing-specific
protocol. Therefore, it is necessary that the micro-controller must read in real-time. This is
imperative as the micro-controller must vary the speed of each line of the LED panel in order to
coordinate with each different row level.

Library

Within this project, the LED panel utilizes an LED library to address specific pins of the
LED panel. Libraries are files that provide sketches that help enable the control of an LED
matrix or help read an encoder for an Arduino. Existing libraries in a sketch can be used by
choosing “Import Library” and picking from the available libraries which will insert an #insert
statement at the top of each header file in the library’s folder. By inserting these #include
statements, the statements will become public functions and constants that are now available to
the Arduino sketch that a user writes. Also, the # include statements indicate to the Arduino the
link to that library’s code with the sketch compiled by the user.

Though the LED library can provide significant assistance in programming the LED
panel to operate in a manner that coordinates with the Stacker game, many different challenges
can arise. One specific challenge includes how fast the neo pixels can be refreshed. One neo
pixel requires 24 bits which lasts for approximately 30 microseconds while the last neo pixel can
take up to 50 microseconds. However, it should be noted that the actual refresh rate cannot be
estimated like this for all cases. Instead, it depends on how the frame of the program that you
have written to coordinate with the LED array. In this project specifically, the length of the
Stacker code will negatively impact the speed of the neo pixels. In order to avoid this issue, the
neo pixel can be run in parallel.
Micro-controller

Figure 1: Actual Micro-Controller Used to Control the Stacker

A micro-controller is a small computer that contains memory and input/output ports that
can be embedded in another device. Typically, micro-controllers support only one task or one
specific program. In the case of this project, our micro-controller contains the program that
enables the Stacker game to run. This program is stored inside the micro-controller’s memory
and does not change. Within this program, the micro-controller controls the light intensity and
timing of the LED array.

The micro-controller that controls the Stacker game was created to have the same basic
circuitry as the Arduino Uno. For this project, three Arduinos would have to be purchased to
operate both Stacker games and the robots. By creating a separate a device similar to the Arduino
Uno, there was a substantial cost savings.

ATMega328P

The ATmega series consists of micro-controllers that contain a program memory, pin
package, extended instruction set, and extensive peripheral set. The ATMega329P component is
part of this ATmega series. Today, the ATmega328P is commonly used throughout the
electronics industry as a simple, low-powered, low-cost micro-controller.

Figure 2: ATMega128P Microcontroller Schematic


16 MHz Resonator
A resonator is a device or system that oscillates at some frequencies with greater
amplitude than at other. An electrical circuit can operate as a resonator using both an inductor
and capacitor. Typically, resonators are used to generate waves at specific frequencies or to focus
on specific frequencies from an electrical signal. These resonant electrical circuits are commonly
known as RLC circuits. Resonators are similar to crystal oscillators in that they both are used to
keep time. For very precise timekeeping, a crystal should be used. A crystal also requires a
capacitor and resistor to operate at the correct frequency. While resonators are not quite as
accurate as a crystal, they operate well within the needs of the Stacker. In addition, they do not
require any external components to operate. A resonator was chosen for the clock circuit to
simplify the circuitry and streamline the production of the two Stacker controller boards.

Figure 3: 16MHz Resonator

7805 Power Regulator


A power regulator controls the voltage level of an electrical circuit. A typical voltage
regulator contains a negative feedback control loop to maintain AC or DC voltages. Voltage
regulators are used to control a steady voltage independent of how much power is drawn.
Figure 4: 7805 Power Regulator Schematic

Within the Stacker project, the micro-controller cannot handle voltage levels greater than
5V. However, the power supply utilized for the Stacker game is a 9V battery. By using a power
regulator, the voltage can be limited to stay within the constrained 5V.

Power Decoupling Capacitor


The Atmega328P requires a regulated power source. While the 7805 regulator provides a
step-down to 5VDC, there is still signal noise that can cause problems for the MCU, especially in
regards to the timing functions. To minimize this noise, 10uF electrolytic capacitors were used
as low pass filters. These capacitors were placed across the 9VDC power source and the 5VDC
regulated power to effectively eliminate detrimental noise.
Robot Trigger

Figure 5: Robot Connected to Power Source, Solenoid, Phototransistors, and Arduino

The robot part of this project operates by using an 18V power supply, solenoid,
MOSFET, phototransistors, several resistors, and a flyback diode. The MOSFET controls
whether the solenoid will be turned on or off. The flyback diode will eliminate flyback which is
also known as the sudden inductive spike that occurs when the supply voltage is suddenly
reduced or removed. The power supply provides the power for the solenoid to operate the button
trigger on the Stacker game.

MOSFET

Figure 6: MOSFET Configuration

A MOSFET is the common acronym for a metal-oxide-semiconductor field-effect


transistor that is used for amplifying or switching electronic voltage. The MOSFET is a four
terminal device that contains a source, a gate, a drain and a body terminal. Typically, a MOSFET
is used to turn on and off a device such as a DC motor or a solenoid. In this project, the
MOSFET was used to activate a solenoid which, in turn, presses the Stacker button. The
MOSFET was wired up with the solenoid connected between the drain and the power supply, the
source connected to ground, and the gate connected to an Arduino digital pin.
The MOSFET utilized in this Stacker game is an NPN MOSFET. Typically, MOSFETs
require very little current into the gate in order to turn the connected device on. It should be
noted that the MOSFET must be wired correctly to avoid problems with the solenoid. Though
the solenoid may operate for one cycle, if wired incorrectly the MOSFET will become extremely
hot and will begin smoking. Several common causes for improperly configured MOSFET
circuits include over-voltage, prolonged current overload, transient current overload, shoot-
through cross conduction, no free-wheel current path, slow reverse recovery of MOSFET body
diode, excessive gate drive, insufficient gate drive, slow switching transitions, spurious
oscillations, the “Miller” effect, conducted interference with controller, and static electricity
damage. Over-voltage may cause occur is the MOSFET undergoes voltages that are above the
voltage rating even for as little as a few nano-seconds. This is often caused by the
aforementioned flyback. Prolonged current overload causes damage to the MOSFET as the
device can be destroyed by excessive temperature rise. Transient current overload causes
progressive damage with little warning to failure. No free-wheel current path means that there is
flyback to the connected device. However, this scenario is avoided with a flyback diode. Slow
reverse recovery is similar to the no free-wheel current path implying that the flyback diode
would protect this situation as well. Excessive gate drive implies that a high voltage could
potentially render the MOSFET useless. Therefore, the voltage conducted into the MOSFET will
be controlled in the Stacker. Insufficient gate drive implies that the device was not fully turned
on. Slow switching transitions implies that the switch did not occur as occur between states as
smoothly as possible. Spurious oscillations occur due to stray inductance and capacitance. The
“Miller” effect is the capacitance between the gate and drain of a MOSFET. Conducted
interference with a controller can be prevented through transformer coupling. Static electricity
damage can be avoid once the devices are soldered. Therefore, all components of the robot and
Stacker were soldered onto a smaller breadboard.

Solenoid

Figure 7: Electromechanical Solenoid with Metal Armature


Within the robot, a solenoid is used as the device to mechanically trigger the button. A
solenoid is a coil wound tightly into a helix. Solenoids are important devices they act more or
less like DC motors as they can create controlled magnetic field and can be utilized to convert
energy into linear motion. Typically, most solenoids are electromechanical which actuates a
solenoid switch to operate an electrical switch. This electrical switch can trigger the solenoid to
be high or low, or more simply turned on or turned off. A solenoid is the perfect device to trigger
the button as the solenoid can be turned high or low at specific moments which enabled the
solenoid almost instantaneously.

The electromechanical solenoid used in this project contains a coil that is shaped such
that the armature moves in and out with the changing inductance. This armature used provides
enough mechanical force to trigger the button of the Stacker. The force applied to the armature is
proportional to the change in position of the armature to the change in inductance. This force
will always move the armature in the direction that increases the coil’s inductance.

Phototransistor

Figure 8: Phototransistors in Proper Placement for Being Mounted onto Stacker Plexiglas

Phototransistors are devices that change with light when more or less light shines on the
phototransistor. When more current passes through the resistor, the voltage output by the
phototransistor will be higher. In order to trigger the solenoid at the correct time, phototransistors
were placed on top of the LED panel on the Stacker game. When the Stacker LEDs flash by the
phototransistor, the voltage output to the micro-controller will change depending on how close
the LED light is to the phototransistor. In order to trigger the solenoid at the correct time, the
phototransistor will send a signal to the solenoid when the maximum voltage is output to the
micro-controller. Each level of the Stacker game contains one phototransistor to correlate with
the single trigger necessary to defeat each level. Below depicts the schematic of the
phototransistor.

Figure 9: Phototransistor Schematic

To ensure that the solenoid triggers instantaneously with the maximum voltage output by
the phototransistor, a wooden arm was designed to mount all eight phototransistors directly on
top of the LED panel. Therefore, proper placement was essential for the robot to successfully
defeat the Stacker.

Conclusion

Overall, the project worked effectively as an arcade game, however, the project did not
simulate how the Stacker arcade game is actually rigged. After further research was compiled, it
was found that the Stacker arcade game allows the real arcade games to allow a one in five
hundred games to be won. This implies that during the games that were not won, the LEDs were
not properly aligned with the trigger of the game even if the human interacting with the game
was able to properly align the LEDs.

Improvements can be made for the both the Stacker arcade game and the coding of the
robot. In order for the robot to run more efficiently, the lit LEDs could be identified quicker if
the Arduino were able to run more quickly, resulting in more accurate game-play. The following
bullets outline ways of improve the robot’s reaction time.
 Use a microcontroller that operates faster than an Arduino
 Use a more succinct code in the Stacker code when simulating with the LEDs

Appendix

Stacker Project Code


#include <Adafruit_NeoPixel.h> //NeoPixel library
#define PIN 6 //makes "PIN" pin 6 on arduino
int ButtonPin = 2;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(64, PIN, NEO_GRB + NEO_KHZ800); //sets everything up
uint32_t white = strip.Color(25, 25, 25);
uint32_t black = strip.Color(0, 0, 0);
uint32_t red = strip.Color(25,0,0);
uint32_t green = strip.Color(0,25,0);

int a, b, c = 0;
int a1,b1,c1 = -99;
int a2,b2,c2 = -99;
int a3,b3,c3 = -99;
int a4,b4,c4 = -99;
int a5,b5,c5 = -99;
int a6,b6,c6 = -99;
int a7,b7,c7 = -99;
int a8,b8,c8 = -99;
int button = 0;
int i = 0;
int place = 0;//holdes place for "run"

void setup()
{
strip.begin(); //prepares NeoPixel for use
strip.show(); //initially sets all pixels to off
Serial.begin(9600);
}

//****************************************************
//START THE LOOOOOOOOOOOOP!!!!!!!!!
//****************************************************

void loop()
{
int reset = 0;
int width = 3;//determines width of run
int game = 1;//determines if game continues
if(game == 1){

//****************************************************
//row 1
//****************************************************
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,1,80,white,black);
a1 = a;
b1 = b;
c1 = c;
button = digitalRead(ButtonPin);
}
strip.setPixelColor(a1, white);
strip.setPixelColor(b1, white);
strip.setPixelColor(c1, white);
strip.show();
while(button == HIGH)
{
delay(1);
button = digitalRead(ButtonPin);
}
width = 0;
if ((a1 != -99) && (a1 != -100)){
width++;
}
if ((b1 != -99) && (b1 != -100)){
width++;
}
if ((c1 != -99) && (c1 != -100)){
width++;
}
}
//****************************************************
//row 2
//****************************************************
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,2,75,white,black); //scrolls until button is pushed
a2 = a; //sets 2nd row placeholders equal to lit LED when button was pushed
b2 = b;
c2 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
if (((a2 == a1+8) || (a2 == b1+8) || (a2 == c1+8)) && (a2 != -99))//checks for vertical alignment
{
strip.setPixelColor(a2, white);
}//turns LED on if vertically aligned

else
{
a2 = -100;
}
if (((b2 == a1+8) || (b2 == b1+8) || (b2 == c1+8)) && (b2 != -99))
{
strip.setPixelColor(b2, white);
}
else
{
b2 = -100;
}
if (((c2 == a1+8) || (c2 == b1+8) || (c2 == c1+8)) && (c2 != -99))
{
strip.setPixelColor(c2, white);
}
else
{
c2 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}
width = 0;
if ((a2 != -99) && (a2 != -100)){
width++;
}
if ((b2 != -99) && (b2 != -100)){
width++;
}
if ((c2 != -99) && (c2 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}
//****************************************************
//row 3
//****************************************************
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,3,70,white,black); //scrolls until button is pushed
a3 = a; //sets 2nd row placeholders equal to lit LED when button was pushed
b3 = b;
c3 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a3 == a2+8) || (a3 == b2+8) || (a3 == c2+8)) && (a3 != -99))//checks for vertical alignment
{
strip.setPixelColor(a3, white);
width++;
}//turns LED on if vertically aligned
else
{
a3 = -100;
}
if (((b3 == a2+8) || (b3 == b2+8) || (b3 == c2+8)) && (b3 != -99))
{
strip.setPixelColor(b3, white);
width++;
}
else
{
b3 = -100;
}
if (((c3 == a2+8) || (c3 == b2+8) || (c3 == c2+8)) && (c3 != -99))
{
strip.setPixelColor(c3, white);
width++;
}
else
{
c3 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}

width = 0;
if ((a3 != -99) && (a3 != -100)){
width++;
}
if ((b3 != -99) && (b3 != -100)){
width++;
}
if ((c3 != -99) && (c3 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}

//****************************************************
//row 4
//****************************************************
if (width == 3){ //makes runner only 2 wide if user still has all 3
width = 2;
}
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,4,65,white,black); //scrolls until button is pushed
a4 = a; //sets 3nd row placeholders equal to lit LED when button was pushed
b4 = b;
c4 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a4 == a3+8) || (a4 == b3+8) || (a4 == c3+8)) && (a4 != -99))//checks for vertical alignment
{
strip.setPixelColor(a4, white);
width++;
}//turns LED on if vertically aligned
else
{
a4 = -100;
}
if (((b4 == a3+8) || (b4 == b3+8) || (b4 == c3+8)) && (b4 != -99))
{
strip.setPixelColor(b4, white);
width++;
}
else
{
b4 = -100;
}
if (((c4 == a3+8) || (c4 == b3+8) || (c4 == c3+8)) && (c4 != -99))
{
strip.setPixelColor(c4, white);
width++;
}
else
{
c4 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}

width = 0;
if ((a4 != -99) && (a4 != -100)){
width++;
}
if ((b4 != -99) && (b4 != -100)){
width++;
}
if ((c4 != -99) && (c4 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}

//****************************************************
//row 5
//****************************************************
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,5,60,white,black); //scrolls until button is pushed
a5 = a; //sets 4nd row placeholders equal to lit LED when button was pushed
b5 = b;
c5 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a5 == a4+8) || (a5 == b4+8) || (a5 == c4+8)) && (a5 != -99))//checks for vertical alignment
{
strip.setPixelColor(a5, white);
width++;
}//turns LED on if vertically aligned
else
{
a5 = -100;
}
if (((b5 == a4+8) || (b5 == b4+8) || (b5 == c4+8)) && (b5 != -99))
{
strip.setPixelColor(b5, white);
width++;
}
else
{
b5 = -100;
}
if (((c5 == a4+8) || (c5 == b4+8) || (c5 == c4+8)) && (c5 != -99))
{
strip.setPixelColor(c5, white);
width++;
}
else
{
c5 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}

width = 0;
if ((a5 != -99) && (a5 != -100)){
width++;
}
if ((b5 != -99) && (b5 != -100)){
width++;
}
if ((c5 != -99) && (c5 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}

//****************************************************
//row 6
//****************************************************
if (width == 2){
width = 1;
}
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,6,55,white,black); //scrolls until button is pushed
a6 = a; //sets 5nd row placeholders equal to lit LED when button was pushed
b6 = b;
c6 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a6 == a5+8) || (a6 == b5+8) || (a6 == c5+8)) && (a6 != -99))//checks for vertical alignment
{
strip.setPixelColor(a6, white);
width++;
}//turns LED on if vertically aligned
else
{
a6 = -100;
}
if (((b6 == a5+8) || (b6 == b5+8) || (b6 == c5+8)) && (b6 != -99))
{
strip.setPixelColor(b6, white);
width++;
}
else
{
b6 = -100;
}
if (((c6 == a5+8) || (c6 == b5+8) || (c6 == c5+8)) && (c6 != -99))
{
strip.setPixelColor(c6, white);
width++;
}
else
{
c6 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}

width = 0;
if ((a6 != -99) && (a6 != -100)){
width++;
}
if ((b6 != -99) && (b6 != -100)){
width++;
}
if ((c6 != -99) && (c6 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}

//****************************************************
//row 7
//****************************************************
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,7,50,white,black); //scrolls until button is pushed
a7 = a; //sets 6nd row placeholders equal to lit LED when button was pushed
b7 = b;
c7 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a7 == a6+8) || (a7 == b6+8) || (a7 == c6+8)) && (a7 != -99))//checks for vertical alignment
{
strip.setPixelColor(a7, white);
width++;
}//turns LED on if vertically aligned
else
{
a7 = -100;
}
if (((b7 == a6+8) || (b7 == b6+8) || (b7 == c6+8)) && (b7 != -99))
{
strip.setPixelColor(b7, white);
width++;
}
else
{
b7 = -100;
}
if (((c7 == a6+8) || (c7 == b6+8) || (c7 == c6+8)) && (c7 != -99))
{
strip.setPixelColor(c7, white);
width++;
}
else
{
c7 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}

width = 0;
if ((a7 != -99) && (a7 != -100)){
width++;
}
if ((b7 != -99) && (b7 != -100)){
width++;
}
if ((c7 != -99) && (c7 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}
//****************************************************
//row 8
//****************************************************
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,8,45,white,black); //scrolls until button is pushed
a8 = a; //sets 7nd row placeholders equal to lit LED when button was pushed
b8 = b;
c8 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a8 == a7+8) || (a8 == b7+8) || (a8 == c7+8)) && (a8 != -99))//checks for vertical alignment
{
strip.setPixelColor(a8, white);
width++;
}//turns LED on if vertically aligned
else
{
a8 = -100;
}
if (((b8 == a7+8) || (b8 == b7+8) || (b8 == c7+8)) && (b8 != -99))
{
strip.setPixelColor(b8, white);
width++;
}
else
{
b8 = -100;
}
if (((c8 == a7+8) || (c8 == b7+8) || (c8 == c7+8)) && (c8 != -99))
{
strip.setPixelColor(c8, white);
width++;
}
else
{
c8 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}

width = 0;
if ((a8 != -99) && (a8 != -100)){
width++;
}
if ((b8 != -99) && (b8 != -100)){
width++;
}
if ((c8 != -99) && (c8 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;

}
}

//****************************************************
//end of row check
//****************************************************

// sets whole grid green if game is won


if (game == 1){
for (int k = 0; k < 64; k++){
strip.setPixelColor(k,green);
}//closes for loop
strip.show();
}

// sets whole grid red if game is lost


if (game == 0){
for (int k = 0; k < 64; k++){
strip.setPixelColor(k, red);
}//closes for loop
strip.show();
}

//delays program until button has been pushed 3 times


while (reset < 3){ //delays program until button has been pushed 3 times
button = digitalRead(ButtonPin);
if (button == HIGH){
reset++;
}
while(button == HIGH){//delays program while button pushed
delay(1);
button = digitalRead(ButtonPin);
}//closes hold down while loop
}//closes reset while loop

//sets whole grid black


for (int k = 0; k < 64; k++){
strip.setPixelColor(k,black);
}//closes for loop
strip.show();
delay(800);

}//closes void loop

//****************************************************
//FUNTIONSSSSSSSSSSS
//****************************************************
//****************************************************
//scrolls 1 lights at a time
//****************************************************

void run (int width, int line, int Speed, long color1, long color2)
{

if (width == 1)
{
a = 0;
b = -99;
c = -99;
if (place == 14){
place = 1;
}
else {
place++;
}

if (place == 1){
a = (line-1)*8+0;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 2){
a = (line-1)*8+1;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 3){
a = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 4){
a = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 5){
a = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 6){
a = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 7){
a = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 8){
a = (line-1)*8+7;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 9){
a = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 10){
a = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 11){
a = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 12){
a = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 13){
a = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 14){
a = (line-1)*8+1;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}//closes place 14 if
}//closes width 1 if

//*************************************************************************************
//scrolls 2 lights at a time
//*************************************************************************************
if (width == 2)
{
a = 0;
b = 0;
c = -99;

if (place == 16){
place = 1;
}
else {
place++;
}

if (place == 1){
a = (line-1)*8+0;
b = -99;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 2){
a = (line-1)*8+0;
b = (line-1)*8+1;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 3){
a = (line-1)*8+1;
b = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 4){
a = (line-1)*8+2;
b = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 5){
a = (line-1)*8+3;
b = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 6){
a = (line-1)*8+4;
b = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 7){
a = (line-1)*8+5;
b = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 8){
a = (line-1)*8+6;
b = (line-1)*8+7;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 9){
a = (line-1)*8+7;
b = -99;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}

if (place == 10){
a = (line-1)*8+6;
b = (line-1)*8+7;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 11){
a = (line-1)*8+5;
b = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 12){
a = (line-1)*8+4;
b = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 13){
a = (line-1)*8+3;
b = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 14){
a = (line-1)*8+2;
b = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 15){
a = (line-1)*8+1;
b = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 16){
a = (line-1)*8+0;
b = (line-1)*8+1;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}//closes place 16 if
}//closes width 2 if

//*************************************************************************************
//scrolls 3 lights at a time
//*************************************************************************************

if (width == 3)
{
a = 0;
b = 0;
c = 0;
if (place == 14){
place = 1;
}
else {
place++;
}

if (place == 1){
a = (line-1)*8+0;
b = (line-1)*8+1;
c = -99;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 2){
a = (line-1)*8+0;
b = (line-1)*8+1;
c = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 3){
a = (line-1)*8+1;
b = (line-1)*8+2;
c = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 4){
a = (line-1)*8+2;
b = (line-1)*8+3;
c = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 5){
a = (line-1)*8+3;
b = (line-1)*8+4;
c = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 6){
a = (line-1)*8+4;
b = (line-1)*8+5;
c = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 7){
a = (line-1)*8+5;
b = (line-1)*8+6;
c = (line-1)*8+7;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 8){
a = (line-1)*8+6;
b = (line-1)*8+7;
c = -99;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}

if (place == 9){
a = (line-1)*8+5;
b = (line-1)*8+6;
c = (line-1)*8+7;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 10){
a = (line-1)*8+4;
b = (line-1)*8+5;
c = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 11){
a = (line-1)*8+3;
b = (line-1)*8+4;
c = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 12){
a = (line-1)*8+2;
b = (line-1)*8+3;
c = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 13){
a = (line-1)*8+1;
b = (line-1)*8+2;
c = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}

if (place == 14){
a = (line-1)*8+0;
b = (line-1)*8+1;
c = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}//closes place 14 if
}//closes width 3 if
}//closes void run

Robot Code
int push = 0; // placeholder to see if the button has been pushed yet for each level of the stacker game
int Solenoid = 10; // designating digital pin 10 to drive the solenoid
int light1 = 2; // designating digital pin 2 for first phototransistor
int light2 = 3; // designating digital pin 3 for second phototransistor
int light3 = 4; // designating digital pin 4 for third phototransistor
int light4 = 5; // designating digital pin 5 for fourth phototransistor
int light5 = 6; // designating digital pin 6 for fifth phototransistor
int light6 = 7; // designating digital pin 7 for sixth phototransistor
int light7 = 8; // designating digital pin 8 for seventh phototransistor
int light8 = 9; // designating digital pin 9 for eighth phototransistor

void setup()
{
Serial.begin(9600); // begin serial communication

pinMode(Solenoid, OUTPUT);
}// closes void setup

void loop()
{
int read1 = digitalRead(light1);
Serial.println(read1);
while(push == 0) // while push = 0, run the following while loop
{
if (digitalRead(light1) == LOW) // run code within if statement if digital pin 2 is high
{
digitalWrite(Solenoid,HIGH); // solenoid is turned on
delay(500);
digitalWrite(Solenoid, LOW);
delay(1500);
push = push + 1;
} // closes if
} // closes while

while(push == 1) // while push = 0, run the following while loop


{
if (digitalRead(light2) == LOW) // run code within if statement if digital pin 2 is high
{
digitalWrite(Solenoid,HIGH); // solenoid is turned on
delay(500);
digitalWrite(Solenoid, LOW);
delay(1500);
push = push + 1;
} // closes if
} // closes while

while(push == 2) // while push = 0, run the following while loop


{
if (digitalRead(light3) == LOW) // run code within if statement if digital pin 2 is high
{
digitalWrite(Solenoid,HIGH); // solenoid is turned on
delay(500);
digitalWrite(Solenoid, LOW);
delay(1500);
push = push + 1;
} // closes if
} // closes while

while(push == 3) // while push = 0, run the following while loop


{
if (digitalRead(light4) == LOW) // run code within if statement if digital pin 2 is high
{
digitalWrite(Solenoid,HIGH); // solenoid is turned on
delay(500);
digitalWrite(Solenoid, LOW);
delay(1500);
push = push + 1;
} // closes if
} // closes while

while(push == 4) // while push = 0, run the following while loop


{
if (digitalRead(light5) == LOW) // run code within if statement if digital pin 2 is high
{
digitalWrite(Solenoid,HIGH); // solenoid is turned on
delay(500);
digitalWrite(Solenoid, LOW);
delay(1500);
push = push + 1;
} // closes if
} // closes while

while(push == 5) // while push = 0, run the following while loop


{
if (digitalRead(light6) == LOW) // run code within if statement if digital pin 2 is high
{
digitalWrite(Solenoid,HIGH); // solenoid is turned on
delay(500);
digitalWrite(Solenoid, LOW);
delay(1500);
push = push + 1;
} // closes if
} // closes while

while(push == 6) // while push = 0, run the following while loop


{
if (digitalRead(light7) == LOW) // run code within if statement if digital pin 2 is high
{
digitalWrite(Solenoid,HIGH); // solenoid is turned on
delay(500);
digitalWrite(Solenoid, LOW);
delay(1500);
push = push + 1;
} // closes if
} // closes while

while(push == 7) // while push = 0, run the following while loop


{
if (digitalRead(light8) == LOW) // run code within if statement if digital pin 2 is high
{
digitalWrite(Solenoid,HIGH); // solenoid is turned on
delay(500);
digitalWrite(Solenoid, LOW);
delay(1500);
push = push + 1;
} // closes if
} // closes while
}
Tank Robot
ME 445
Fred Coulter
Matt Pizano
Fall 2013

1
Acknowledgements
The team would like to thank Dr. Sommer and Mike Robinson for the help and guidance
through the semester. This project and class introduced us to many of the concepts we have
used in this project and hopefully we will continue to use afterwards.

2
Contents
Introduction .................................................................................................................................................. 4
Concept Development .................................................................................................................................. 4
Detailed Design ............................................................................................................................................. 5
Mechanical ................................................................................................................................................ 6
Electrical Design ........................................................................................................................................ 8
MOSFET transistor ................................................................................................................................ 8
7805 Voltage Regulator ........................................................................................................................ 9
Motor Drivers ........................................................................................................................................ 9
PS2 Controller ..................................................................................................................................... 10
Complete Circuit Diagram………………………………………………………………………………………………………………………11

Software and Programming ........................................................................................................................ 11


Testing ......................................................................................................................................................... 11
Testing PS2 controller commands .......................................................................................................... 11
Calibration of Motors with PWM values................................................................................................. 12
Testing of Mechanical Design ................................................................................................................. 13
Issues and Lessons Learned ........................................................................................................................ 13
Conclusion ................................................................................................................................................... 14
Appendix A: Arduino Code………………………………………………………………………………………………………………….…15

3
Introduction
For the final project, Team 8 wanted to develop a tank that would be able to drive freely and
shoot a gun mounted above the chassis. In developing this tank, we realized there were many
goals we would have to achieve. One goal was to develop a running chassis for the tank which
would include interfacing DC drive motors on the front of the chassis to make it mobile. The
second goal was to develop a cannon that would be placed above the tank and be able to pivot
up and down.

Concept Development
The concept was developed by looking at two specific tanks used during World War II. This was
inspired by two tanks developed during World War II. The M2 Stuart was the inspiration for the
chassis used in the development of the tank which was used by the Allied Forces. The M2
Stuart was a light tank developed before World War II in 1935. It was used during the Battle of
Guadalcanal in 1942 and was used by the British in during the North African front of World War
II.
The Hummel was another tank used by the Axis Forces from 1943 to the end of World War II.
The Hummel was an inspiration for the gun of the tank robot, which had a 15 centimeter
Howitzer. A Howitzer is an artillery piece which was specifically used in the Hummel as mobile
artillery support.
The Arduino needed to also be interfaced wirelessly. The team decided to use a PS2 controller
with the Arduino to make the tank move and pivot. The PS2 controller is wireless and can be
programmed using a predefined library resource.

4
Detailed Design
After a large team effort, the tank robot was successfully created and has gone through
rigorous testing. Using the chassis foundation, we built a mobile artillery piece to be mounted
above the chassis.
Bill of Materials
Practice Golf Balls 12.99 1
Squash Balls 9.99 1
1/2" Sch. 40 Elbow 0.25 2
1-1/2" Sch. 40 Coupler 0.77 1
1-1/2" x 2' Sch. 40 Pipe 2.90 2
1-1/2" Sch. 40 Cap 0.96 1
Air Pressure Guage 7.99 1
3/4" Auto Sprinkler Valve 12.58 1
3/4" x 1/2" Sch. 40 Bushing 0.41 2
1/4" Air Intake Valve 4.99 1
1-1/2" x 1/2" Sch. 40 Reducer 1.26 2
1/4" Acrylic n/a 1.5' x 2'
1/8" Acrylic n/a 6" x 8"
Air Compressor n/a 1
3/4" Wood Dowel n/a 1
3/4" Bolt n/a 1
Pololu Motor Driver n/a 1
7805 Voltage Regulator n/a 1
100 Kohm Resistor n/a 2
330 Ohm Resistor n/a 1
LED n/a 1
PS2 Controller/Reciever n/a 1
Arduino Uno n/a 1
Terminal Block n/a 1
Proto Board n/a 1
Tank Chassis n/a 1
Mosfet n/a 2
Switch n/a 1
7.2 Volt Battery n/a 1
9V Battery n/a 1
AA Battery n/a 2
Hinge n/a 2
Wiring n/a n/a

Total Cost $95.01

5
Mechanical Design
The mechanical design of the entire tank robot falls under two main categories which are the
chassis and the gun. The chassis is a pre-owned 1/6th scale replica of the M2 Stuart chassis. We
used the motors provided from the chassis to run the vehicle. The DC drive motors run from
the front of the vehicle on the front gears in order for the tracks to turn at a functional rate.
The tracks are then carried on from the front to the back of the chassis in order for it to be
functional in all terrain. The power is enough to make the motors drive 1 MPH. The team used
Traxxas speed controllers provided by the lab in order to program the motors through the
Arduino Uno.

After the chassis was built, the team moved onto the upper base of the tank. The tank needed
a cover for all of the wiring and a base for the gun foundation to be placed on. As a result of
this necessity, an acrylic sheet was cut to cover the entire base of the chassis by using the Shop
in the basement of the Reber Building. The next step was building a cover for the front DC drive
motors. This was built in the same fashion as the acrylic sheet for the foundation. A hinge was
placed on the ends of the cover and attached to the front of the acrylic foundation sheet. This
allowed an ammunition pocket to be introduced to our design for the practice golf balls and
squash balls we used.

6
The next main step in building this tank was for the team to build a foundational pivot base for
the gun. This was built by using the shop in the Reber Building basement again to cut out
pieces of plywood to act as a foundational base. This along with holes on the two ends of the
plywood allowed the base to be used as a pivot point. After building the foundational pivot
base, a pivot point for the gun had to be made. This was made by using a gearbox attached to a
DC stepper motor for the pivot point. After attaching the gearbox to the pivot base, a threaded
hex rod was press fit into the gearbox to attain a pivot for the gun itself. The other side was
attached to a large hex bolt that threaded into the connector on the side of the gun. On the
gun itself, there is a solenoid that is attached to the sprinkler valve to release the air. This is
crucial for releasing the air and firing the gun. A pump is also connected to the air tank for
additional air pressure. The gun is the most crucial part of the tank because that is what a tank
is in essence.

7
Electrical Design
The tank would not be possible without the brains, which is the Arduino Uno microcontroller.
This piece of hardware was used to activate the switches for multiple transistors described
below. It was also used for the PS2 controller interfacing in order for the tank to be controlled
wirelessly. This Arduino Uno was placed underneath the layer of acrylic and mounted inside of
the chassis.

MOSFET transistor
The MOSFET transistors inside of the circuit built were used to control whether the pump and
solenoid were triggered. These transistors were made so that if there is no voltage supplied
from the Arduino digital input, the gate would be opened causing no voltage to travel across
from the battery to the pump or solenoid. This causes the solenoid or pump to be controlled
digitally and wirelessly using the Arduino interfaced with the PS2 controller. These are shown
in the detailed circuit diagram.

8
7805 Voltage Regulator
The Voltage regulator used in this application was hooked up to a 9 Volt battery source. This
voltage regulator takes the 9 Volts from the battery source and cuts it down to 5 Volts in order
to power the Arduino without USB assistance.

Motor Drivers
There were two different components used in driving the motor itself. The first motor driver
used to drive the main motors of the tank was the Traxxas XL-1 speed controller. This was
connected to the DC drive motors and the Arduino allowing control of drive speeds through
Arduino programming. They were connected to the positive and negative terminals of the
motor and the NiCd car battery used for the project. This allowed PWM control between the
speed controllers and Drive motors.

The second motor driver used was the Pololu Dual Motor Driver. This was used to drive the
pivot DC motor which was responsible for changing the elevation and depression of the
mounted gun. In retrospect, the Pololu Driver could have been used for all of the motors in the
tank because of its 3 Amp limit.

9
PS2 Controller
The PS2 controller is wireless and has a RF Transmitter and Receiver that can be hooked up
straight to the Arduino. This is done by using the external library for PS2 commands from Bill
Porter’s PS2 controller Arduino Library. The PS2 RF receiver uses 3.3 Volt logic rather than 5
volt logic. By using an Arduino 3.3 Volt output, the RF Receiver was wired to the tank and
microcontroller.

10
Complete Circuit Diagram

Drive Motors

7.2V
Elevation/
Depression

Valve

Pump

Software and Programming


The purpose of using the software is to allow the solenoid, and pump to turn on and off. Also,
the motors need to be programmed to drive, elevate, and depress. All of the interfacing that is
necessary for this project was done through an external PS2 controller library found at Bill
Porter’s PS2 controller Arduino Library found in the citations section. The Figure below shows
how the controller was used in the tank robot application. The pump and solenoid used a
digital input meaning only on and off were supported. Also, the motor drivers used PWM
control so a duty cycle could be designated for all motors involved.

Testing
Testing started with the individual parts of the project and branched out to the complete build.

Testing PS2 controller commands


After researching the coding using the PS2 wireless controller, example code was made in order
to test the most beneficial ways to use the controller. The first example made to check inputs
was making serial prints on the Arduino command window for individual buttons pressed.

11
After testing these commands, the team noticed slight interference between the transmitter
and receiver. This was caused by the analog button being activated. To be more descriptive,
the analog button causes the analog sticks in the middle of the controller to be activated and
four different analog inputs to be read. The larger amount of information to be transmitted to
the receiver caused slight interference and misfire of buttons not pressed.

Calibration of Motors with PWM values


The Traxxas speed controllers are used to drive the motors forward and reverse. This is done
by using the 0-50 Percent Duty cycle as forward drive speeds and 50-100 percent drive speeds
for reverse drive speeds. Calibration of motors was done by changing PWM values manually
using drive speed changes. The final result caused us to use a PWM value of 85 for the forward
drive speed and 250 for the reverse drive speed. Any higher of a drive speed for both values
causes no movement.
After calibration of the drive motors was finished, the DC motor responsible for elevation and
depression was necessary. This motor was controlled by a Pololu Motor Driver which had an

12
enable pin to control speed and counterclockwise and clockwise pins for direction. After wiring
the circuit, the motor was set to a 50 Percent duty cycle where it was sufficiently elevated and
depressed for our application.

Testing of Mechanical Design


The most vital part of the tank is the gun attached to the pivot base. This gun had to be tested
by pumping the tank full with sufficient air pressure to see where a leak may be. There was
leak behind the solenoid valve which required the addition of Teflon tape to cover the leak and
avoid future problems with the gun.

Final Testing
After all of the components were combined, the overall tank was tested in the computer lab as
a dry shot with no projectiles. The drive motors, elevation and depression motor, and the
solenoid and pump had been tested separately. The Arduino code had been programmed in
order for every button to handle a certain command allowing full control of the tank. After
using a digital input to test the solenoid and pump connected to a MOSFET transistor, the team
started to activate the pump to fill the air tank. After filling the tank up to 100 psi, the main
goal was to elevate the gun to a satisfying level and releasing the air using the solenoid. After
moving the tank using the drive motors to test the speed and reliability, the pump was turned
off and the solenoid was activated. After activating the solenoid, the air released and the test
was successful. The test was reproduced using live ammunition, a practice golf ball, and was
successful once again.

Issues and Lessons Learned


The main issue we had in this project stemmed from the soldering and creating of circuits for
the tank. The first circuit we soldered was recreated from a proto board test circuit. After all
was tested and confirmed to be correct, the team decided to solder the circuit on a board.
After soldering and confirming all circuits conceptually, the board didn’t work. This could have
been avoided by doing continuity tests on all adjacent busses in the circuit board before
soldering. After realizing the problem, the team built a more efficient circuit with an LED
attached to confirm battery power through the circuit.
All of the progression made by the team has been because of lessons learned in ME 445. The
circuits built including the MOSFET transistors, voltage regulator, and motor driver circuits have
been all taught to us through classwork and lab work. Interfacing using the Arduino software to
the Arduino microcontroller was demonstrated and taught to us through coursework and lab
work also. This allowed us to use the DC drive motors and elevation and depression motors
using the PWM (Pulse Width Modulation) with digital inputs

13
Conclusion
During this course, many of the principles and concepts learned through class and lab helped
the team be able to achieve all that was necessary for our success in this project. After many
trials and tribulations, the tank itself ran along with the execution of shooting the gun. This was
our main goal starting this project, and it has been achieved.
If the team had more time to work on the project, an additional servo could have been placed
below the pivot base in order to rotate the gun to the left and right for additional degrees of
freedom for the tank itself. This would have allowed increased maneuverability for the gun and
the function of the tank itself.

14
Appendix A: Arduino Code

#include <PS2X_lib.h>

PS2X ps2x; // This designates the function from the library to begin
int solenoid = 8; // The solenoid variable relates to the digital input the solenoid is
connected to
int pump = 7; // The pump variable relates to the digital input the pump is connected to
int enable= 3; // The enable pin is for the Pololu motor driver and the pivot motor
int up = 2; // The up pin designates the digital pin relating to elevating the pivot motor
int down = 4; // The down pin designates the digital pin relating to depressing the pivot
motor
int error = 0; // This error variable relates to whether or not the PS2 controller is read
byte vibrate = 0; // This is for vibrate commands for the controller if necessary
int motor1 = 5; // This is for the left drive motor of the tank
int motor2 = 6; // This is for the right drive motor of the tank
int triangle = 1; // This is a variable for prototyping drive speed changes in the drive motors
int select = 0; // This variable turns on and off access to all functions of the tank
int motorspeedf = 0; // This is the drive speed prototyping PWM values for testing
int motorspeedr = 0; // This is the drive speed prototyping PWM values for testing
int start= 0; // When this variable is 1, the functions of the tank are accessible
int time= 0; // For prototyping purposes

void setup(){

Serial.begin(57600); // Begins communication


pinMode(solenoid, OUTPUT); // These functions make the variables Outputs through a
digital output pin
pinMode(pump, OUTPUT);
pinMode(up, OUTPUT);

15
pinMode(down, OUTPUT);
error = ps2x.config_gamepad(9,11,10,12, true, true); //setup pins and settings:
GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error

void loop(){

ps2x.read_gamepad(false, vibrate);
// This command turns off vibrate

if(ps2x.Button(PSB_START)) // This function makes all other functions accessible at the press
of the start button
{
start= 1;
}

if(start == 1)
{

if(ps2x.ButtonPressed(PSB_R1))
{
digitalWrite(pump, HIGH);
// This makes the pump turn on with the press of the R1 Button
}

if(ps2x.ButtonPressed(PSB_L1))
{

16
digitalWrite(pump, LOW);
digitalWrite(solenoid, LOW);
// This makes the pump and solenoid turn off with the press of the L1 Button
}

if(ps2x.ButtonPressed(PSB_R2))
{
digitalWrite(solenoid, HIGH);
// This activates the solenoid and releases the air at the press of the R2 Button
}

if(ps2x.Button(PSB_PAD_UP)) //will be TRUE as long as button is pressed


{
analogWrite(motor1, 85);
analogWrite(motor2, 85);
// This function acts as a forward driving function if the up button is pressed on the dpad
}

if(ps2x.Button(PSB_PAD_RIGHT))
{
Serial.print("Right held this hard: ");
analogWrite(motor1, 95);
analogWrite(motor2, 250);
// This function acts as a Right turn driving function if the Right button is pressed on the
dpad
}

if(ps2x.Button(PSB_PAD_LEFT))

17
{
Serial.print("LEFT held this hard: ");
analogWrite(motor1, 250);
analogWrite(motor2, 95);
// This function acts as a Left turn driving function if the Left button is pressed on the dpad
}

if(ps2x.Button(PSB_PAD_DOWN))
{
Serial.print("DOWN held this hard: ");
analogWrite(motor1, 250);
analogWrite(motor2, 250);
// This function acts as a Reverse driving function if the Down button is pressed on the dpad
}

if(ps2x.ButtonPressed(PSB_RED)) //will be TRUE if button was JUST pressed


{
Serial.println("Circle just pressed");
analogWrite(motor1, 0);
analogWrite(motor2, 0);
digitalWrite(up, LOW);
digitalWrite(down, LOW);
analogWrite(enable, 0);
// This function turns off all driving motors and pivot motors by pressing the Circle button
}

18
if(ps2x.ButtonPressed(PSB_GREEN)){
analogWrite(enable, 54);
digitalWrite(up, HIGH);
digitalWrite(down, LOW);
// This function makes the pivot motor elevate at the designated speed with the press of the
Triangle button
}

if(ps2x.ButtonPressed(PSB_BLUE)){
analogWrite(enable, 96);
digitalWrite(up, LOW);
digitalWrite(down, HIGH);
// This function makes the pivot motor depress at the designated speed with the press of the X
button
}
}

if(ps2x.Button(PSB_SELECT) && start == 1)


{
start = 0;
analogWrite(motor1, 0);
analogWrite(motor2, 0);
// This function turns off all functions and restricts access to them by pressing the Select
Button
// This can be reversed only by pressing the Start Button
}
delay(50);
}

19
An Autonomous Vehicle
for Collecting Small Objects

Donald Docimo and Michelle Kehs


ME 445 Final Project, Fall 2013
December 17, 2013
Table of Contents
Introduction ..................................................................................................................................... 1
Zumo Robot .................................................................................................................................... 2
Controlling the Motors with the Zumo Robot ............................................................................ 2
Determining Position with the Zumo Robot ............................................................................... 3
Rotating to a Target Position with the Zumo Robot ................................................................... 3
IR Sensor ......................................................................................................................................... 4
Switch ............................................................................................................................................. 5
Collector Subsystem ....................................................................................................................... 6
Constructing the Collector Subsystem ........................................................................................ 7
Controlling the Collector Subsystem .......................................................................................... 9
Results and Conclusions ............................................................................................................... 10
References ..................................................................................................................................... 13
Appendix A – Bill of Materials .................................................................................................... 14
Appendix B – Arduino Code ........................................................................................................ 15
Appendix C – Datasheets .............................................................................................................. 23

List of Figures
Figure 1. The several major components of our project. The compass and Arduino are hidden
from view. ....................................................................................................................................... 1
Figure 2. The assembled Zumo robot, which our project is built upon [1]. ................................... 2
Figure 3. Rotation of the car, achieved by motor control. .............................................................. 3
Figure 4. The Sharp GP2Y0A21YK0F infrared sensor [2]. ........................................................... 4
Figure 5. Representation of the workings of a typical IR sensor [3]. ............................................. 4
Figure 6. The IR sensor attached to the back of the Zumo robot.................................................... 5
Figure 7. A schematic of the switch used to identify when a Lego is within reach. ...................... 5
Figure 8. On the left, a side view of the switch, where you can see how the switch is triggered.
On the right, an overall view of the switch, which was attached to the front of the car. ................ 6
Figure 9. Collector subsystem attached to Zumo robot. ................................................................. 7
Figure 10. A single arm and grip pad used to pick up small objects. ............................................. 7
Figure 11. DSM44 servos with balsa arms, attached to the same balsa piece. ............................... 8
Figure 12. Arms attached to servos, and back servo to lift the system. .......................................... 8
Figure 13. Top-down view of the bin used to hold Lego blocks. ................................................... 9
Figure 14. Process of collecting a Lego block. ............................................................................. 10
Figure 15. Pins used for connections with Arduino [7] and the Zumo’s battery connections [1].
Components labeled with * designate connections made directly through the Zumo shield (no
physical wires are connected to those pins). ................................................................................. 11
Figure 16. Autonomous robot collecting multiple Lego blocks. .................................................. 12

i
Introduction
Our goal for this project was to create an autonomous vehicle that could find and collect
small objects. The motivation behind this chosen topic was the relevance of autonomous drones
in a modern society, not only for military use, but potentially consumer use in the future.
However, we wanted to focus on the basics of this topic to maximize learning and build
something that was cheaper than most drones. To achieve our goal, we made a small,
autonomous car that found nearby Legos, picked the Legos up, and dropped them in an attached
compartment. Our car first rotated in a full circle while searching for the closest object. Then, it
rotated to the closest object’s position. After that, it moved forward until it reached the Lego,
where it stopped. Once stopped, the car picked up the Lego, lifted it over an attached bin, and
dropped the Lego. Then, the process repeated as the car found and collected the next closest
object.
Several components were used in the collection process, labeled in Figure 1. Our car was
built on a Zumo robot and controlled by an Arduino Uno. The Zumo robot included motored
wheels for movement and an internal compass for determining direction. Additional components
used were an IR sensor, a switch made of wire and a resistor, servo motors attached to Balsa
wood arms, a platform to place the collection device on the Arduino, and a bin to hold the Legos.

Servo-Powered
Arms

Switch

Motor-Powered
Wheels IR Sensor
Figure 1. The several major components of our project. The compass and Arduino are hidden from view.

The remainder of this report goes into more detail on our project. First, we include more
detail on the components of the project and their use. This includes sections for the Zumo robot
(including motor control and the compass), the IR sensor, the wire and resistor switch, and the
collection device. After that, we discuss how the components operate together and evaluate the
performance of the autonomous robot. Following that, the appendices include a bill of materials,
our Arduino code, and relevant datasheets.

Page 1
Zumo Robot
Our project was built upon an existing product—the Zumo robot by Pololu [1], shown in
Figure 2. The Zumo robot is a small car that includes two motors to control movement. The
Zumo shield is used to control a variety of the Zumo’s functions, including the motors. To send
signals to the shield and the robot, an Arduino can be placed on top of the Zumo shield. For our
project, we used two major functions of the Zumo robot: the motor control system and the
internal compass.

Figure 2. The assembled Zumo robot, which our project is built upon [1].

Controlling the Motors with the Zumo Robot

The Zumo robot includes two motors that are controlled by built in motor drivers. These
motor drivers use Arduino pins 7 and 8 as inputs to control the direction of the two motors, and
they use Arduino PWM pins 9 and 10 to control the speed of the two motors. Although these
motors could be controlled by programming directly to pins 7-10, we used the Zumo Motors
library to control the motors. After the Zumo Motor library was initialized, the left motor was
controlled with the command motors.setLeftSpeed(+/- speed) , and the right motor
was controlled with the command motors.setRightSpeed(+/- speed). In the
commands’ parenthesis, a positive speed between 1 and 400 moved the motor forward, and a
negative speed between -1 and -400 moved the motor backward. An input of 0 stopped the
motors.
We used the motor control for rotation and forward movement. Initially, our car rotated
in a full circle while scanning the area for nearby objects, as shown in Figure 3. For this
function, we set one motor to move forward while the other moved backward. After, the car
needed to rotate again, this time to get to the position where an object was sensed. Then, the car
needed to move forward until it reached the Lego. For forward movement, we set both motors to
the same direction and speed.

Page 2
Figure 3. Rotation of the car, achieved by motor control.

Especially during rotation, we wanted our car to move slowly so that many sensor
readings could be taken. However, if we set the motor speeds too low, we found that they would
not move. Furthermore, if we set the motor speeds high enough that they could move but still
very low, we found that our car would slip while rotating, which we did not want. Based on
these trade-offs, we chose to use motor speeds of 85 out of 400 (about 21%) for rotation and 150
out of 400 (about 37%) for forward motion.

Determining Position with the Zumo Robot

The Zumo shield includes a built-in compass, which we used to find an absolute angular
position. To find absolute position, the compass uses a 3-axis magnetometer [1]. Before the
compass could be used, it needed to be calibrated. We used the Calibrate example file from the
LSM303 Arduino library. After running the code and opening the serial monitor, we rotated our
car in a full circle where we planned to run the Lego collection. While we rotated the car, the
Calibrate file kept measurements of the minimum and maximum compass values with respect to
each axis. These minimum and maximum calibration values were used at the beginning of our
project’s Arduino code. For accurate readings, we found the compass needed to be calibrated
each day.
To use the compass, we used two libraries: Wire and LSM303. Both the wire and the
compass were initialized, and then we took readings using commands similar to ones found in
example files. To read the compass, the command compass.read() was used. Then, to
extract the position, the command compass.heading((LSM303::vector){0,-1,0})
was used, where the heading was taken with respect to the {0,-1,0} vector. In this manner,
we were able to keep track of the angular position of the car.

Rotating to a Target Position with the Zumo Robot

Many aspects of our project relied on rotating to a target position. For instance, when the
car initially rotated in search of nearby Legos, the car needed to end at the same position it
began. Coding this section was fairly straightforward. As the car rotated, it constantly took
compass readings. When the compass reading was within the set tolerance of the initial compass
reading, the car stopped. Another use of rotating to a target position is when the car rotated to
the position of the nearby object. For this part of the code, we first evaluated whether it was
Page 3
more efficient to rotate clockwise or counterclockwise. Then, we could rotate to the target in the
same manner as the initial rotation.

IR Sensor
There were several options that could have been used to detect object location, such as
ultrasound-based sensors and physical devices. An infrared (IR) sensor was ultimately chosen,
as most are compact and cheap devices that are easy to implement in our system. As shown in
Figure 4, most modern IR sensors have several different components. The emitter and detectors
are the components with lenses, and are the most important parts of the sensor.

Figure 4. The Sharp GP2Y0A21YK0F infrared sensor [2].

Figure 5 is a graphical representation of how a sensor, such as the one in Figure 4,


operates. The emitter first releases pulses of IR light. If the light hits an object, it is reflected
back and picked up by the detector. The location where the light reflects off the object is
referred to as the point of reflection. The detector picks up the light at an angle, which is
dependent on the distance the sensor is from the point of reflection. The lens of the detector
sends the light into the inner circuit of the system, a charge-coupled device, which is used to
determine the angle of the light coming in the detector. Based on this angle of light, the distance
to the object is determined [3]. An output voltage signal is sent from the IR sensor that is
proportional to the distance to the object. Due to the nature of how an IR sensor works, there is a
maximum and minimum distance in which the device functions properly.

Figure 5. Representation of the workings of a typical IR sensor [3].

The sensor chosen for implementation was the Sharp GP2Y0A21YK0F infrared sensor,
which is shown in Figure 4. The sensor has a range of approximately 10 to 80 cm, and outputs
an analog voltage. It is suitable to be powered by the Arduino board, as it requires a 4.5 to 5.5

Page 4
volt supply, and 30 milliamps [4]. Figure 6 shows the sensor attached to the back of the vehicle.
The sensor’s location on the back of the vehicle was chosen as to avoid measurement error from
the switch on the front of the vehicle, which is covered in the next section.

Figure 6. The IR sensor attached to the back of the Zumo robot.

For our project, the output of the sensor was connected to analog pin 0 of the Arduino
board. Using a simple analogRead() command for that pin, the sensor’s output was read.
As we only required relative measurements to find the closest object, we compared the output
signal at each position against the output signal at other positions. At the initial position, the
sensor’s output was recorded, as well as that location. During the full 360O rotation of the robot,
the Arduino would constantly receive inputs from the sensor. If, at any point, the sensor’s output
was greater than the previously found maximum output, the variable associated with this new
maximum output, as well as its heading, replaced the previous values. Once the full rotation was
complete, the vehicle was rotated to face the location of the maximum sensor output value. As
the sensor was on the back of the vehicle, the target heading was 180O plus the location the
sensor found. The number found from this was converted into a value between 0 O and 360O in
order to rotate properly, using the function AbsoluteHeading().

Switch
To let the car know when it reached a Lego, a switch was used. This switch was made
from wire and a 330 Ω resistor. The schematic for the switch is shown in Figure 7.

330

+5 V Signal
(Pin 13) (Pin 12) (GND)

Figure 7. A schematic of the switch used to identify when a Lego is within reach.

Page 5
Before the car reached a Lego, the switch was open. This prevented current from
traveling from the voltage source to ground. Because there was no current flow, the signal’s
voltage was +5 V. When the car reached the Lego, the force from the car moving towards the
Lego closed the switch and a connection from +5 V to ground was made. Because current
flowed when the switch was closed, the voltage dropped over the resistor, and the signal’s
voltage changed to 0 V. By keeping track of the signal’s voltage, we kept track of when the car
reached a Lego.
For the car’s design, the switch was positioned a few inches in front of the car so that
when triggered, the arms were within reach of picking the Lego up. For the switch, one piece of
wire was soldered to a resistor. One end of this wire was connected to Arduino pin 13, which
was used as a +5 V voltage source; the other end of this wire was connected to Arduino pin 12,
which was a digital input and sensed the switch’s position. Shown in Figure 8, the middle of this
wire was curled into a loop, and a section of this loop had exposed wire. The end of the ground
wire then fit into this loop. When the car ran into a Lego, the ground wire was forced backward,
hitting the exposed wire and closing the switch.

signal

+5 V

0V
Figure 8. On the left, a side view of the switch, where you can see how the switch is triggered. On the right, an
overall view of the switch, which was attached to the front of the car.

One of the biggest advantages of the switch design is that it is simple. Only inexpensive
materials (wire and a resistor) were used in its construction, and only digital pins were required
to run it. One of the biggest challenges associated with the switch was its sensitivity. Although
we wanted to avoid false readings, we needed the switch to be sensitive enough that it was
triggered when the car hit a Lego, instead of pushing the Lego along with the car. We found that
breaking the ground wire in two and soldering the parts together, as shown in Figure 8, increased
the sensitivity of the switch more than simply bending the wire. Still, making sure a Lego would
trigger the switch was a main concern, and we needed to make sure the switch was aligned
properly before running the program. Overall, the switch was a successful way of sensing a
Lego and triggering the collection process.

Collector Subsystem
In order to collect the objects, we required a system that could physically grab and lift a
Lego block, and then store multiple blocks on the body of the Zumo robot. A three servo system
with balsa wood arms and grip pads was used to pick up the Lego block. A balsa wood bin was
used to store collected objects. Both of these were attached to a balsa wood platform, which was

Page 6
secured to the Zumo robot. Figure 9 shows this subsystem assembled and attached to the
autonomous vehicle.

Figure 9. Collector subsystem attached to Zumo robot.

Constructing the Collector Subsystem

In order to grip the Lego blocks, arms were designed to attach to two servo motors, and
reach the ground. The arms, one of which is shown in Figure 10, had pads attached to the ends,
as it gave more contact area between the block and the arms. Rectangular pieces were attached
to the pads to prevent the block from slipping out of the grips while moving upward. Balsa
wood was chosen for these arms and pads, as it is light, which was necessary for the small servos
used. In addition, Balsa wood is fairly cheap, which kept the overall cost of the project down.
Cyanoacrylate glue was used to connect joints and to attach the balsa wood to the plastic
replaceable pieces of the servos, preventing permanent damage.

Figure 10. A single arm and grip pad used to pick up small objects.

Page 7
The two servos used for each arm were chosen to be DSM44 for their small size and are
shown in Figure 11. These servos require 4.8-6.0 volts to operate, which means the Arduino
would not be an ideal source to use for power [5]. Instead, the batteries used to power the car
were also used to power the servos. The Zumo robot’s shield had junctures to draw this power.
The DSM44 servos were taped onto a balsa wood piece in order to avoid damage to the servo
casing. An HTX900, another small servo, was used to lift the gripping arms and servos, as
shown in Figure 12.

Figure 11. DSM44 servos with balsa arms, attached to the same balsa piece.

Back Servo

Arm

Arm Servos

Figure 12. Arms attached to servos, and back servo to lift the system.

A storage bin was made of balsa wood to hold the Legos collected, as shown in Figure
13. Approximately four Lego blocks of the size shown in the picture could be held in the bin.
The bin was glued to a balsa wood platform, which rested on the Ardunio board and Zumo robot
securely, while not being glued to the system. The servo system was also glued to this,
completing the system shown in Figure 9.

Page 8
Figure 13. Top-down view of the bin used to hold Lego blocks.

Controlling the Collector Subsystem

Arduino has a built in servo control library that normally makes controlling multiple
servos an easy task. However, this library removes the capability to use pulse width modulation
on digital pins 9 and 10 [6]. As previously mentioned, the Zumo motors library uses these pins
for controlling the direction and speed of the motors. The overlap in use of these pins correlates
to both libraries making use of Timer 1, which is similar to a clock. As a result, an alternative
code had to be used to control the servo motors. The alternate code had to use Timer 2 to control
the servos, as outlined by the makers of the Zumo Robot. They had previously designed a servo
control method using Timer 2, which we implemented into our code [1].
The process for collecting the Lego was implemented using a function PickUp() after
the switch was triggered. Figure 14 shows the steps involved. First, the back servo moved
down, and placed the arms on the ground (Figure 14a). The arm servos would slowly close in on
the block, in a slow manner to avoid the block being pushed out of the proper position (Figure
14b). After gripping the Lego tightly, the back servo lifted the arms into an upright position
(Figure 14c). The block was then released, and dropped into the storage bin (Figure 14d). This
process required us to determine the proper limiting angles to set the servos at, such that the
object would be held tightly, but the servos would not cause the arms to crash together in case
the block was missed altogether.

Page 9
Figure 14a. Back servo rotates down. Figure 14b. Arm servos clamp Lego.

Figure 14c. Back servo lifts arms and Lego. Figure 14d. Arm servos release Lego into bin.

Figure 14. Process of collecting a Lego block.

Results and Conclusions


The final product, previously shown in Figure 1, was completed by combining the
separate subsystems we have outlined. Figure 15 represents the connections used on the Arduino
board for motor control, the IR sensor, and the switch. The full operation of the car, using each
of the subsystems, is shown in Figure 16 for an example test collecting three Lego blocks.
After the Zumo robot was turned on, it rotated 360O in place. The motor-powered wheels
were programmed to move the car in a circle as the IR sensor constantly took readings. The
maximum reading corresponded to the location of the closest Lego block. To measure the
position at each IR sensor reading and to make sure the car ended where it began, the compass
was used. The next task the car performed was to rotate to the position of the closest object,
which was completed by the wheels and compass. The direction for rotation was dependent on
what was faster: clockwise for objects between 0O and 180O and counterclockwise otherwise.
After that, the motors propelled the car forward.

Page 10
Figure 15. Pins used for connections with Arduino [7] and the Zumo’s battery connections [1]. Components labeled
with * designate connections made directly through the Zumo shield (no physical wires are connected to those pins).

Page 11
Figure 16a. Search Figure 16b. Rotate to position Figure 16c. Move to target

Figure 16d. Grip Lego Figure 16e. Lift Lego Figure 16f. Store Lego

Figure 16. Autonomous robot collecting multiple Lego blocks.

Once the Lego block was reached, it forced the switch to close, sending a signal to the
Arduino to stop the motors. The collection subsystem routine was then used to pick up the Lego
block and drop it into the storage bin. The entire process repeated until all the Lego blocks in
range were collected. As the robot was not programmed to stop collecting, the user needed to
turn it off manually.
The autonomous vehicle performed as designed, and, as show in Figure 16, was
successful in collecting multiple Lego blocks. This was tested multiple times to a high success
rate. However, there were a few minor flaws during a limited number of tests. The compass
required calibration nearly every day and every different location it was used. This was a
problem we did not realize at first, and caused several issues with the performance of the vehicle
in earlier tests. In addition, the switch also needed to be physically fixed if it was knocked out of
place. This never occurred during tests but occasionally happened while we were handling the
robot.
In one test, when rotating to point to the closest Lego block, the robot spun around a full
360 degrees before pointing to the object. This was caused by the robot not sending the data
quickly enough to the Arduino board, and the robot missed the angle it was supposed to stop at.
Giving a larger tolerance to the target angle was not an option, as the switch would not be
triggered properly. The motor was set to a lower rotation speed, in order to minimize the chance
of missing a target angle. The issue is uncommon, and only affects the speed at which the robot
picks up objects, and not how well it does so.

Page 12
The IR sensor used was cheap, and met our needs, but also had a very limited range. As
a result, the objects to be collected could only be placed within a short distance of the vehicle.
This was a problem that was best solved by limiting operation of the robot to the range of the
sensor. Another solution would be to replace the IR sensor with a more expensive and higher
quality one, though this would be unnecessary for the learning experience we sought.
We feel that our project was an incredible learning experience about the operation of
autonomous robots, and we recommend this or a similar project as starting point for engineers to
learn about this topic. The robot built is representative of more advanced autonomous robots that
have become more relevant recently. The vehicle’s performance was successful and consistent
with our expectations.

References
[1] Pololu, “Pololu Zumo Shield for Arduino User’s Guide,”
http://www.pololu.com/docs/0J57/all (Pololu Corporation, 2013).
[2] Sharp, “GP2Y0A21YK/GP2Y0D21YK: General Purpose Type Distance Measuring
Sensors,” https://www.sparkfun.com/products/242 (SHARP Corporation).
[3] Acroname Robotics, “Sharp IR Rangers Information,” http://www.acroname.com/robotics/
info/articles/sharp/sharp.html (Acroname, Inc., March 2011).
[4] Sharp, “GP2Y0A21YK0F: Distance Measuring Sensor Unit Measuring Distance,”
http://www.sharpsma.com/webfm_send/1489 (SHARP Corporation, December 2006).
[5] Power HD, “Digital Servo, Model DSM44,”
http://www.robotgear.com.au/Cache/Files/Files/186_DSM44.pdf.
[6] Arduino, “Servo library,” http://arduino.cc/en/reference/servo.
[7] Arduino, “Arduino Uno,” http://arduino.cc/en/Main/arduinoBoardUno.

Page 13
Appendix A – Bill of Materials

Item Description Qty. Price (total)


Arduino Uno 1 $20.00
Zumo Robot for Arduino (Pololu item #2506) 1 $99.95
IR sensor (Sharp GP2Y0A21YK0F) 1 $13.95
330 Ω resistor 1 $0.25
Servo motor for clamping (Power HD DSM44) 2 $25.90
Servo motor for up/down motion (HexTronik HXT900) 1 $2.69
Balsa wood (1/8” x 4” x 36” piece) 1 $3.50
Total 1 $166.24

Additional Materials: Wire, tape, glue, Legos

Note: This cost reflects building this project from scratch. Most materials we used were already
available in the mechatronics lab.

Page 14
Appendix B – Arduino Code
// Donald Docimo and Michelle Kehs
// ME445 Final Project

// Set-up of Libraries
#include <Wire.h> //for compass inside Zumo shield
#include <LSM303.h> //compass inside Zumo shield
LSM303 compass; //compass inside Zumo shield
#include <ZumoMotors.h> //include the Zumo Motors library
ZumoMotors motors; //initialize the motors from the Zumo Motors library

// Declare Pins
// Pin 7 Right motor direction control line
// Pin 8 Left motor direction control line
// Pin 9 Right motor PWM control line
// Pin 10 Left motor PWM control line
int sensorpin = A0; //A0 is connected to the Sharp IR sensor
int switchPin = 12; //pin read for the switch
int switchPower = 13; //set to high to power switch

// Global Variables
int OriginalHeading; //For rotating 360 deg, this is the starting position
int CurrentHeading; //Current position
int DiffHeading; //Difference between current position and target position
int TargetHeading; //Target position of where to move to
int CloseHeading; //Based on the IR sensor, heading of the closest object

int RotSpeedMid = 85; //Medium rotational speed


int RotSpeedLow = 85; //Low rotational speed
int ForwardSpeedMid = 150; //Medium forward movement speed

long StartTime; //Used for keeping track of elapsed time

int switchval; //value (high/low) for the switch (if object is in reach)

uint16_t volatile servoTime = 0; //Servo control: time since last rising edge
//in units of 0.5us.
uint16_t volatile servoHighTime = 3000; //Servo control: pulse width we want
//in units of 0.5us.
boolean volatile servoHigh = false; //Servo control: true if the servo pin
//is currently high.
int SERVO_PIN; //Servo control: initialize servo pin

int RServoSignal = 3; //Right servo signal pin (blue wire)


int LServoSignal = 5; //Left servo signal pin (green wire)
int BServoSignal = 6; //Back servo signal pin (yellow wire)

void setup(){
pinMode(switchPin,INPUT); //initialize the switch's input
pinMode(switchPower,OUTPUT); //initialize the switch's output
digitalWrite(switchPower,HIGH); //set the power to +5V

Page 15
Serial.begin(9600); //begin serial communication, baud rate = 9600 Hz

Wire.begin(); //initialize compass


compass.init(); //initialize compass
compass.enableDefault(); //initialize compass

// Compass values (found using the Calibrate example on 12/14/2013)


compass.m_min.x = -525; compass.m_min.y = -46; compass.m_min.z = -4096;
compass.m_max.x = +345; compass.m_max.y = +972; compass.m_max.z =-2048;
}

void loop(){
delay(2000); //wait 2 seconds before starting

// Make sure the servo starts at OPEN and UP


servocontrol(BServoSignal,-30); delay(500); //move up
servocontrol(LServoSignal,65); delay(100); //open (left servo)
servocontrol(RServoSignal,120); delay(1000); //open (right servo)

CloseHeading = RotateCW360(); //Rotate 360 deg CW to find closest object


delay(1000); //pause for 1 s before starting to rotate again

TargetHeading = AbsoluteHeading((CloseHeading-180)); //set the target to be


//opposite (IR sensor is on back)
RotateToPosition(); //rotate to the target position

StartTime = millis(); //start timing


while(millis()-StartTime < 15000) //loop runs the car straight for 15s
{
motors.setLeftSpeed(ForwardSpeedMid); //move forward
motors.setRightSpeed(ForwardSpeedMid); //move forward
switchval = digitalRead(switchPin); //check if switch is triggered
if(switchval==0){ //if switch is triggered (Lego in reach)
motors.setLeftSpeed(0); motors.setRightSpeed(0); //stop car
PickUp(); //run code to pick up object
break; //exit while loop once object is picked up
}
}
}

int RotateCW360(){ //Rotate CW 360 degrees to find closest object


int MaxObj; //the sensor value for the closest object
int MaxObjLocation; //the heading where MaxObj is reached

compass.read(); //find current heading


TargetHeading = compass.heading((LSM303::vector){0,-1,0});//current heading
//set to target position (want to end where started)
MaxObj = analogRead(sensorpin); //Originally, the current location has the
//maximum red value (no other readings)
MaxObjLocation = TargetHeading; //Corresponding location

motors.setLeftSpeed(RotSpeedMid); //set the motors to rotate CW

Page 16
motors.setRightSpeed(-RotSpeedMid); //set the motors to rotate CW

StartTime = millis(); //Start timing


int condition = 0; //condition for breaking loop (if fully rotated, = 1);
long elapsTime = millis()-StartTime; //elapsed time
while(condition==0 || elapsTime<1000){ //don't stop loop until within tol
//and sufficient time has passed
compass.read(); //update the current heading
CurrentHeading = compass.heading((LSM303::vector){0,-1,0});//update the
//heading
if(analogRead(sensorpin)>MaxObj){ //if true, found closer object
MaxObj = analogRead(sensorpin); //update new maximum reading
MaxObjLocation = CurrentHeading; //update new max reading location
}
DiffHeading = CurrentHeading - TargetHeading; //update how close
//heading is to the target
if (DiffHeading<-3 || DiffHeading>3){ //if heading is within tol
condition = 0; //set the condition variable to equal 0
}
else{ //if the heading is not within tol
condition = 1; //set the condition variable to equal 1
}
elapsTime = millis()-StartTime; //update elapsed time
}

motors.setLeftSpeed(0); motors.setRightSpeed(0); //once fully rotated, stop

return MaxObjLocation; //return the position of the closest object


}

void RotateToPosition(){ //Rotate (CW or CCW) to target position


compass.read(); //update the current heading
CurrentHeading = compass.heading((LSM303::vector){0,-1,0}); //update the
//heading
DiffHeading = CurrentHeading - TargetHeading; //update how close heading
//is to the target

// This next section decides if it is more efficient to rotate CW or CCW


// It also adjusts the target (calibrated values) for better accuracy
if (CurrentHeading>TargetHeading){ //if positive DiffHeading
if (DiffHeading <180){ //if the diff is + and less than 180 degrees

if (DiffHeading<60){ //if the target is close, adjust target


TargetHeading = AbsoluteHeading((TargetHeading-10));} //calibrated
else if (DiffHeading<120){ //if the target is middle, adjust target
TargetHeading = AbsoluteHeading((TargetHeading-11));} //calibrated
else { //if the target is far, adjust target
TargetHeading = AbsoluteHeading((TargetHeading-15));} //calibrated

RotateCCWtoTarget(TargetHeading); //it's most efficient to rotate CCW


}
else { //if the diff is + and more than 180 degrees

if (DiffHeading>300){ //if the target is close, adjust target


TargetHeading = AbsoluteHeading((TargetHeading-10));} //calibrated

Page 17
else if (DiffHeading>240){ //if the target is middle, adjust target
TargetHeading = AbsoluteHeading((TargetHeading-5));} //calibrated
else { //if the target is far, adjust target
TargetHeading = AbsoluteHeading((TargetHeading-5));} //calibrated

RotateCWtoTarget(TargetHeading); //it's most efficient to rotate CW


}
}
else { // negative DiffHeading
if (DiffHeading > -180){ //if the diff is - and "small"

if (DiffHeading > -60){ //if the target is close, adjust target


TargetHeading = AbsoluteHeading((TargetHeading-10));} //calibrated
else if (DiffHeading >-120){ //if the target is middle, adjust target
TargetHeading = AbsoluteHeading((TargetHeading-5));} //calibrated
else { //if the target is far, adjust target
TargetHeading = AbsoluteHeading((TargetHeading-5));} //calibrated

RotateCWtoTarget(TargetHeading); //it's most efficient to rotate CW


}
else { //if the diff is - and "large"

if (DiffHeading < -300){ //if the target is close, adjust target


TargetHeading = AbsoluteHeading((TargetHeading-10));} //calibrated
else if (DiffHeading <-240){ //if the target is middle, adjust target
TargetHeading = AbsoluteHeading((TargetHeading-11));} //calibrated
else { //if the target is far, adjust target
TargetHeading = AbsoluteHeading((TargetHeading-15));} //calibrated

RotateCCWtoTarget(TargetHeading); //it's most efficient to rotate CCW


}
}

delay(1000); //pause for 1 second after rotating to the target


}

void RotateCWtoTarget(int TargetHeading){ //Rotate CW until chosen position


is reached
compass.read(); //update the current heading
CurrentHeading = compass.heading((LSM303::vector){0,-1,0}); //update the
//heading
DiffHeading = CurrentHeading - TargetHeading; //update how close the
//heading is to the target

// Moderately slowly, rotate CW, continually taking heading readings


motors.setLeftSpeed(RotSpeedLow); //set the motors to rotate CW
motors.setRightSpeed(-RotSpeedLow); //set the motors to rotate CW

while (DiffHeading<-2 || DiffHeading >2){ //if close enough to target,


//stop (break while loop)
compass.read(); //update the current heading
CurrentHeading = compass.heading((LSM303::vector){0,-1,0}); //update
//the heading
DiffHeading = CurrentHeading - TargetHeading; //update how close
//heading is to target

Page 18
}

// When chosen heading is within threshhold, stop.


motors.setLeftSpeed(0); motors.setRightSpeed(0); //stop the motors
}

void RotateCCWtoTarget(int TargetHeading){ //Rotate CCW until chosen


//position is reached
compass.read(); //update the current heading
CurrentHeading = compass.heading((LSM303::vector){0,-1,0}); //update the
//heading
DiffHeading = CurrentHeading - TargetHeading; //update how close the
//heading is to the target

// Moderately slowly, rotate CCW, continually taking heading readings


motors.setLeftSpeed(-RotSpeedLow); //set the motors to rotate CCW
motors.setRightSpeed(RotSpeedLow); //set the motors to rotate CCW

while (DiffHeading<-2 || DiffHeading >2){ //if close enough to target,


//stop (break while loop)
compass.read(); //update the current heading
CurrentHeading = compass.heading((LSM303::vector){0,-1,0}); //update
//current heading
DiffHeading = CurrentHeading - TargetHeading; //update how close the
//heading is to the target
}

// When chosen heading is within threshhold, stop.


motors.setLeftSpeed(0); motors.setRightSpeed(0); //stop the motors
}

int AbsoluteHeading( int OffsetHeading){ //Readjusts the heading value to


//between 0 and 360 degrees
int AbsHeading; //variable to store the heading value (between 0 and 360)
if(OffsetHeading >= 0 && OffsetHeading <360){ //if the heading is already
//between 0 and 360
AbsHeading = OffsetHeading; //then the absolute heading equals the
//"offset" heading
}
else if(OffsetHeading < 0){ //if the offset heading is negative
AbsHeading = 360+OffsetHeading; //add 360 degrees, then the absolute
//heading will be in range
}
else{ //if the offset heading is >360 degrees
AbsHeading = OffsetHeading - 360; //subtract 360 degrees, then the
//absolute heading will be in range
}

return AbsHeading; //returns absolute heading (between 0 and 360)


}

Page 19
void PickUp(){ //control the servos to grip, pick up, and drop a Lego
// Start at OPEN and UP
servocontrol(BServoSignal,-30); delay(500); //back servo - up
servocontrol(LServoSignal,65); delay(100); //left servo - open
servocontrol(RServoSignal,120); delay(1000); //right servo - open

// Move DOWN
servocontrol(BServoSignal,180); delay(1000); //back servo - down

// CLOSE (slowly)
int r = 120; //right (r) starts at 120 - open
for(int l=65;l<=115;l){ //counter l changes each loop
servocontrol(LServoSignal,l); delay(100); //left servo - close a little
servocontrol(RServoSignal,r); //right servo - close a little
r = r - 5; l = l + 5; //update values for r (decrease) and l (increase)
delay(700); //pause before closing more
}
servocontrol(LServoSignal,123); delay(100); //finish closing left
servocontrol(RServoSignal,62); delay(700); //finish closing right

// Move UP
servocontrol(BServoSignal,-30); delay(1000); //back servo - up

// OPEN (slowly)
int l = 120; //left (l) starts at 120 - closed
for(int r=65;r<=115;r){ //counter r changes each loop
servocontrol(LServoSignal,l); delay(100); //left servo - open a little
servocontrol(RServoSignal,r); //right servo - open a little
r = r + 5; l = l - 5; //update values for r (increase) and l (decrease)
delay(300); //pause before opening more
}
servocontrol(LServoSignal,65); delay(100); //finish opening left
servocontrol(RServoSignal,120); delay(700); //finish opening right
}

void servocontrol(int pinNumber, int angleDeg){ //move servo at pinNumber to


//position angleDeg
SERVO_PIN = pinNumber; //set the servo pin
servoInit(); //initialize the servo
int pulse = map(angleDeg,30,170,1000,2000); //map the angle to a pulse time
servoSetPosition(pulse); //run the program to control the servo
}

/* The following code and functions control the servos.


Code was taken from the Pololu Zumo Shield for Arduino User's Guide.
The servo library interferes with the pins to control the Zumo's motor,
which is why an alternate servo control method is used. */

// This ISR runs after Timer 2 reaches OCR2A and resets.


// In this ISR, we set OCR2A in order to schedule when the next
// interrupt will happen.
// Generally we will set OCR2A to 255 so that we have an
// interrupt every 128 us, but the first two interrupt intervals

Page 20
// after the rising edge will be smaller so we can achieve
// the desired pulse width.
ISR(TIMER2_COMPA_vect)
{
// The time that passed since the last interrupt is OCR2A + 1
// because the timer value will equal OCR2A before going to 0.
servoTime += OCR2A + 1;
static uint16_t highTimeCopy = 3000;
static uint8_t interruptCount = 0;
if(servoHigh)
{
if(++interruptCount == 2)
{
OCR2A = 255;
}
// The servo pin is currently high.
// Check to see if is time for a falling edge.
// Note: We could == instead of >=.
if(servoTime >= highTimeCopy)
{
// The pin has been high enough, so do a falling edge.
digitalWrite(SERVO_PIN, LOW);
servoHigh = false;
interruptCount = 0;
}
}
else
{
// The servo pin is currently low.
if(servoTime >= 40000)
{
// We've hit the end of the period (20 ms),
// so do a rising edge.
highTimeCopy = servoHighTime;
digitalWrite(SERVO_PIN, HIGH);
servoHigh = true;
servoTime = 0;
interruptCount = 0;
OCR2A = ((highTimeCopy % 256) + 256)/2 - 1;
}
}
}

void servoInit()
{
digitalWrite(SERVO_PIN, LOW);
pinMode(SERVO_PIN, OUTPUT);
// Turn on CTC mode. Timer 2 will count up to OCR2A, then
// reset to 0 and cause an interrupt.
TCCR2A = (1 << WGM21);
// Set a 1:8 prescaler. This gives us 0.5us resolution.
TCCR2B = (1 << CS21);
// Put the timer in a good default state.
TCNT2 = 0;
OCR2A = 255;
TIMSK2 |= (1 << OCIE2A); // Enable timer compare interrupt.
sei(); // Enable interrupts.

Page 21
}
void servoSetPosition(uint16_t highTimeMicroseconds)
{
TIMSK2 &= ~(1 << OCIE2A); // disable timer compare interrupt
servoHighTime = highTimeMicroseconds * 2;
TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
}

Page 22
Appendix C – Datasheets
Website Link: http://www.sharpsma.com/webfm_send/1489

Page 23
Website Link: http://www.pololu.com/file/download/LSM303DLHC.pdf?file_id=0J564

Page 24
Website Link: http://www.robotgear.com.au/Cache/Files/Files/186_DSM44.pdf

Page 25
Website Link: http://www.pololu.com/docs/pdf/0J57/zumo_shield_for_arduino.pdf

Page 26
Page 27
Page 28
Page 29
Page 30
Page 31
Interactive LED Dance Floor

Prepared By:
David Manley & Matthew Duffy

Prepared For:
Dr. Sommer and Michael Robinson
Contents
Introduction .................................................................................................................................................. 4
Construction.................................................................................................................................................. 4
Lid .............................................................................................................................................................. 4
Base ........................................................................................................................................................... 5
Support ..................................................................................................................................................... 6
Power and Signal Strips............................................................................................................................. 6
Control Board ............................................................................................................................................ 7
Electronics ..................................................................................................................................................... 7
Adafruit NeoPixel Digital RGB LED Strip.................................................................................................... 7
MSGEQ7 Graphic Equalizer Display Filter ................................................................................................. 8
Force-Sensitive Resistors .......................................................................................................................... 8
Potentiometers and Switches ................................................................................................................... 8
Programming ................................................................................................................................................ 8
Inter-Integrated Circuit ............................................................................................................................. 8
Solid Color ................................................................................................................................................. 9
Touch Sensitive ......................................................................................................................................... 9
Tap Tempo ................................................................................................................................................ 9
Audio Responsive .................................................................................................................................... 10
Conclusion ................................................................................................................................................... 10
Appendix A: Code........................................................................................................................................ 12
Tile Code ................................................................................................................................................. 12
Control Board Code................................................................................................................................. 14
Appendix B: Controller Circuit Diagram ...................................................................................................... 19
Appendix C: MSGEQ7 Data Sheet ............................................................................................................... 20
Introduction
For our mechatronics project we decided to create an interactive LED dance floor. We both
love to dance, and love finding the latest equipment for dance parties. After seeing LED
dance floors on line, we thought that it would be a great project to try and create a dance
floor of our own, which is more cost effective, allows for increased user interfacing, and
increased modes of operation. The current products which are available are over 300
dollars per square. These squares are also only able to change colors, and are several feet
off of the ground making it dangerous to dance on. Another company offers a product which
is much more advanced and much sleeker, but costs 8,000 dollars to rest an 8’x8’ floor from
this company. Because of this we wanted to make a product which was much cheaper,
since currently this type of product is not reasonable for purchasing.

Beyond reducing cost, we came up with several goals for our product. The first mode that
we wanted to implement was a mode in which the tiles can be set to a solid color. However,
unlike our competitors, we wanted to make this option more interactive by making the colors
fully customizable so that users can mix their own colors. We also wanted to create a mode
in which the tile would change color when they were stepped on. We thought that this would
be a feature which could be used for dancing and for other game applications. This was
another goal, to not only create a dance floor, but also a game floor, for games similar to
“whack a mole,” “Simon says,” and other games. This would transform our product from
simply a dance floor to an entire party floor. Finally we wanted to create tiles which were
sensitive to audio inputs. This was a unique feature that we thought would separate our
design from that of other competitors. We wanted to achieve this by using the tiles to create
a large graphic equalizer, and to make them pulse with audio inputs in a way in which they
would be audio responsive.

Throughout the following paper we will discuss our individual components, materials,
construction, and programming process in order to explain the development of our product.

Construction
In order to complete the design of this project we first began by selecting various materials
to use for production. Since our project is a dance floor it is important to make sure that all
materials are durable and will not cause injury to the user if it breaks during use. In this
section we will also discuss how we constructed the various physical components using
these materials.

Lid
The first component of our project that we began constructing was the lid which houses all
of the internal components and is also the surface on which the user will stand. When
analyzing the material choices of competitors we noticed that the industry as a whole used
acrylic for the top surface of their designs. However, we wanted to steer away from this
material due to the fact that it can shatter, which would cause serious harm to the user. To
avoid this we decided to use a ⅛” Lexan, or polycarbonate, sheet. These sheets are
guaranteed to not break and can even be cold worked without snapping. The only downside
to these sheets is that they are able to flex, which makes the surface feel less safe even
though it is in fact a safer design, and that Lexan is much more expensive than acrylic.
Because of the increased cost of Lexan we decided to construct the walls of the lid out of
acrylic. Since it was crucial for the walls to be straight for the lid to sit flush with the bottom
surface, we decided to make a dxf file for the walls and to have them laser cut. We then
took the acrylic strips and bonded them to the Lexan sheet and to one another using a two
part epoxy to finish the construction of the lid.

Another way that we minimized our costs was by purchasing a large sheet of Lexan with no
surface finish. We began by cutting the large sheet into square feet. We then began to play
around with the surface finish of the material. We first thought that a “frosted” finish would
be the best design, so we began by purchasing a frosting spray paint. However, after
several coats the frosting reached a max limit and you could still see all of the individual
components and the individual LEDs which was not ideal. We then attempted to sandblast
the top of the sheets to intensify this frosted look. However, this only removed the spray
paint and began to turn the Lexan brown. In addition to this the effects of the sandblasting
were so minimal that it made it impossible to provide a consistent effect on the sheet. After
this be decided to attempt to scuff the inside of the Lexan. We used varying grits of
sandpaper to determine which was the most ideal and used a powered sander to make a
more uniform product. This made the sheets frosted, however, we realized that a
translucent finish was more appropriate than a frosted finish. In order to quickly modify our
tiles after this discovery, we decided to attach paper to the inside of the Lexan. Finally we
attached a thick piece of acrylic to the middle of the sheet to provide a support for the lid,
and to contact the force sensitive resistor beneath.

Base
After constructing the lid, we then turned our efforts to constructing the base of the tiles. We
had trouble at first selecting a base material which would help us to easily integrate our
electrical components, would not slide on the ground when placed, would be lightweight,
and would be inexpensive. After analyzing many different base materials including metal,
rubber, wood, and particle board, we decided to use particle board for our design. We
chose the particle board because it was very inexpensive and also came in a large sheet
with a thickness of ¾” which was ideal for our design. We began by cutting the sheet of
particle board into square feet. After this we began working on the modular element of our
design.

After analyzing many fastening techniques, we


decided to use metal door hinges and metal
pins in order to implement our modular design
as seen in Figure 1 to the right. We began by
using a router to cut grooves into the board at
the designed locations so that the hinges and
pins would be flush with the top of the board,
ensuring that the lid would seat flush with the
board. We then placed the hinges at
alternating locations and screwed them into the
board. After this we pre drilled holes and the
corresponding locations on the alternate Figure 1: Corner of floor tile
boards, and press fit the pins into the holes.
Because of the uniformity of our design we were able to create a system in which any board
is able to be mated with another board in any orientation. This makes the design fully
customizable to the user’s area, and allows them to attach as many as desired in any
shape.

After creating the modular component of our board we then began focusing on supporting
the lid and creating a mounting structure for the LEDs. We decided to use the excess
particle board pieces to achieve this goal. We strips of particle board which we the same
length as our LED strips, and positioned them in the middle of each wall. In addition to this
we moved these strips in from the edge of the board so that the lid would fit on the outside
of these structures. This provided support for the walls of the lid and ensured that the lid
was unable to slide laterally when being danced on. We also cut these strips to the exact
height of the acrylic walls of the lid so that they would provide additional support for the top
of the lid. The other use for these strips was to mount the LEDs. In order to optimize the
number of LEDs used per square we positioned the LED strips in a way so that they would
all face in towards the middle, horizontal with the board. In this way the light was able to
spread out and achieve a more uniform appearance before exiting the top of the board. We
also spray painted the board white to increase reflectivity. We also added a padded contact
material to the corners of the boards to increase the traction of the tiles.

Support
Unfortunately, with all these modifications, the structure still did not feel stable. We do not
think that the tiles would have broken under use, but we wanted to ensure that the user
“felt” safe while using our product. In order to increase support we cut additional pieces of
acrylic, similar to the one previously used for support and for contacting the force sensitive
resistor, and placed them throughout the structure to bear the load of the user.

Power and Signal Strips


We needed a way to link the power, ground, and two I2C lines between all of the modular
tiles. We decided to cut strips of thin metal which would be easily bent. We then took these
strips and bent them over the edge so that when the tiles are placed together these strips
come in contact with one another. We decided to use metal thumb tacks to fix these strips
to the board, however, these would not be used in the future since they were very poor for
soldering. These strips can be seen in Figure 1 above. The added benefit of bending the
strips was that they had a spring constant which helped to keep the squares connected
even with the marginal movement which was able to occur due to the tolerance of the
mechanical fasteners. We then wired these strips by soldering them to the 5V, ground, and
two I2C lines which were wired to the top of the board.
Control Board
Finally we created a control board to house all of our master circuitry and to create a
friendlier user interface. We wanted to incorporate LEDs, so that the user could preview
input colors, potentiometers, so that the user could mix their own colors, push button
switches, so that the user can set the desired mode, a large push button, so that the user
can tap in the desire tempo at which the LEDs will flash, and a master switch, which would
make it easy for the user to turn the system completely on and off. We began by selecting
the corresponding components for all of these applications. We used a “missile switch”
which has an internal LED for the on/off switch. We then purchased knobs which fit on the
top of the potentiometers in order to create a
sleeker and user-friendly design. We then
purchased push buttons for the control panel
which were designated to a specific mode.
Finally we found an LED illuminated button for
the user to tap in the temp. After this we
created a dxf file for the control board and had
it laser cut to fit the components perfectly. We
also added a brand name to the lower left
corner to add individual design to our product.
After this we used left over pieces of particle
board to create the bottom of the box, in which
the Arduino and other circuitry would be
Figure 2: Control board
housed.

Electronics
This section will discuss the different electronic components of our project; however, most
of the detail on how these components were implemented will be discussed in a later
section. The electronics of this project were based around the capabilities of Arduino Uno
microcontrollers. We used an Uno in the control box, giving users the power to control the
behavior of the entire LED grid; and Unos were used in each of the tiles to run all the
processing for each tile. In addition to the Arduino Uno we used LED strips, a graphic
equalizer chip, force-sensitive resistors, potentiometers and push-button switches to provide
functionality and user control of the floor tiles.

Adafruit NeoPixel Digital RGB LED Strip


The main component of our project was the LED strips we used in the floor tiles. We
decided to use a strip of LEDs as opposed to individual LEDs because a strip allowed us to
easily mount and orient the lights on our tiles. The NeoPixel strip is unique in that a
controller chip is inside each LED. Because of this it only requires three leads: supply,
ground and control; and is able to provide full RGB control to the entire strip. Also the
NeoPixel strip allows users to individually set the color of each LED pixel within an Arduino
program, meaning that the strip does not need to be one solid color.

For our design we separated our LED strips into segments of 7 LEDs and placed them on
the mounts inside each of our tiles. Each tile then had a total of 28 LED pixels (four
segments) all facing toward the center of the box. These four strips were all wired together
and controlled by a PWM digital pin on the Arduino inside each tile.

MSGEQ7 Graphic Equalizer Display Filter


In order to allow the tiles to be audio-responsive we included the MSGEQ7 which divides
the frequency spectrum of an input signal into seven frequency bands. It then can be used
to get the amplitude of the audio signal within each of these frequency bands. We used this
chip along with a ⅛” audio junction to find the frequency content of audio inputs and provide
that to the control Arduino. The datasheet for the MSGEQ7 can be found in Appendix C.

Force-Sensitive Resistors
The base of each floor tile has a force-sensitive resistor in the center. Force-sensitive
resistors provide varying resistance based on the force applied to them; the higher the force
the lower the resistance. These are wired in series with 100 ohm resistors, and an analog
input on the tile Arduino is used to measure the voltage in between the two resistors. As
force is applied to the force-sensitive resistor, the voltage being read by the Arduino
increases. This allows the tiles to respond to the act of someone stepping on them.

Potentiometers and Switches


In order to allow users to change between operating modes on the control Arduino, a variety
of potentiometers and switches were used. Three potentiometers were wired from +5V to
ground with the voltage divider lead going into analog inputs of the control Arduino. By
varying the position of the potentiometer knob a user can provide voltages varying from 0 to
5 volts to the Arduino. Also we wired 5 momentary push-button switches in between the
supply voltage and resistors leading to ground. Leads between the switches and resistors
go to the digital pins in the control Arduino. The control Arduino reads high on the digital
pins when the buttons are pressed and low when they are not. Finally, we used a flashy
toggle switch to control the power for the entire system.

Programming
In this section we will first give an explanation of I2C which was used to communicate
between our multiple Arduinos. After this we will provide a brief explanation of each of our
modes of operations and then go into more detail as to how we programmed our Arduinos
to achieve these results.

Inter-Integrated Circuit
Inter-Integrated Circuit, commonly referred to as I2C, allows for the communication between
multiple devices. I2C makes it possible for our controller to send commands to all of the
tiles. The basic idea of I2C is that there is one device that is designated as the “master”; all
other devices are “slaves”. The master device is in charge of all the communication that
occurs on the communication lines. Each slave has an ID, and if the master has data to
send to a particular slave it will first send the ID of that slave followed by the data. All the
slaves listen for their ID, and if the master calls their ID, they collect the data that follows.
Because the user controls take up most of the digital and analog pins on the control
Arduino, the benefit of I2C is that it only uses two lines on the Arduino: one for data (SDA)
and one for clock (SCL). The controller Arduino and all of the tile Arduinos are connected to
these two lines. This was crucial for our project because we did not want the number of
output leads on our control Arduino to be a function of the number of tiles used. In addition,
Arduino has an already constructed I2C function library called “Wire” making implementing
this into our project relatively simple. In our application the control Arduino sent 4 bytes to
each tile Arduino; the first byte contained the selected mode of operation, and the last three
bytes contained the color data for that mode of operation.

Solid Color
For our first mode of operation we decided to create a mode which would allow the user to
set all of the tiles to a constant color. Because of the nature of our LEDs we were given the
ability to make the color fully customizable for the user. The color for our LEDs is controlled
by varying the intensity of the Red, Green, and Blue inputs. To make this fully customizable
we attached three potentiometers to the circuit which were used to control the intensity of
these spectrums. The analog inputs on the control Arduino read the input voltage intensity
of the potentiometers from 0 to 1023. The voltage intensity of each potentiometer is then
mapped from 0 to 255, the minimum and maximum intensity for each of the color bands. In
this way the user is able to actually “mix” the colors to achieve literally any desired
color. For increased usability we attached three LEDs onto the control box, so that the user
can mix the inputs live and visibly see the results before sending the signal to the
tiles. After the desired color is achieved the user can simply press the solid color button to
send this color to all of the tiles.

Touch Sensitive
For our second mode, we made a touch sensitive application. This was the mode which first
inspired us to attempt to create this product. We placed a force sensitive resistor in the
middle of the tile, which was impacted by the support of the lid when a user stepped onto
the tile. This force sensor had a threshold for the force input, and above this force, the LED
input would be switched to a corresponding color based off of the previously set color for
the tiles. The color for this mode is set in the same way as for the solid color mode using
the potentiometer voltages. The color that the tile alternated to when the switch was
pressed was calculated as the complementary color of the set color using this equation:

Complementary Color = [255 - Red Intensity, 255 - Green Intensity, 255 - Blue Intensity]

The color for the tiles alternates between the set color and this complementary color as the
user steps on the tiles.

Tap Tempo
For our next mode, we decided to make an application where the user would be able to
input a tempo in the form of beats per minute, or bpm, at which the LEDs would pulse. This
application could be used if the user is listening to music and wants to tap in the bass
tempo. When the user taps the large red button in the center of the control board the control
Arduino calculates the difference between the current time and the time that the button was
last tapped. This value is stored in an integer array, and the Arduino calculates the bpm by
averaging the most recent four values in the array. When the user reaches a desired bpm,
they can then send this to all the tiles by pressing the tap tempo select button. The control
Arduino sends five bytes of information, containing the mode selection, the color, and the
bpm, to each of the tile Arduinos. After the tiles have been set to this function they all
individually calculate the time until the next beat using the bpm, and flash on and off to this
tempo.

Audio Responsive
For our final mode we wanted to create a mode which would enable the user to fully
experience the music by creating a graphic equalizer which would cause the LEDs to react
to the input audio. We began by implementing a MSGEQ7 chip into our circuitry. We used
this chip to break the audio inputs into its given frequency bands. We then attenuated low
band levels and boosted high ones, in order to intensify the desired effect. Unlike all other
modes of operation, the audio responsive mode does not allow users to set a color using
the potentiometer knobs. We instead assigned certain frequency ranges to color values; for
example, low frequencies were assigned to red. The controller Arduino then sets the
intensity of each color based off of the magnitude of the audio signal for each frequency
range. While in this mode of operation, the control Arduino is continuously communicating
with the tile Arduinos on each iteration in order to provide instantaneous response to the
music.

Conclusion
Overall we were able to achieve a product which was able to match many of our desired
goals. We were able to create all of the desired dance floor modes that we set out to create,
however due to time constraints we were unable to create the game interfaces as intended.
With additional time we hope to be able to create these additional games. With our set up
and I2C capabilities it should be a simple addition to our platform. In the future we would
like to add a games similar to “whack a mole” and “Simon says.”

In addition to this there are several mechanical components which were would change if we
were to take this product to production. First we would change the lid material. We would go
to an acrylic which is much thicker and already has the desired translucent finish. In addition
to this we would either retrofit the modular design by using larger hinges to increase the
ease of assembly, or we would create a new feature such as a rotational latch. We would
also seek to improve the touch sensitive setting, by either changing the backing of the force
sensitive resistor, or by using a piezo or other component for this sensor.

Also in the future we would most likely change some of our electrical components. One of
the changes that we would likely implement would be to add parallel computing for the tap
tempo setting. Currently there is drift in the LED response due to variable processing
speeds. This change would help to make a more uniform final product. In addition to this we
would consider using an Arduino with more inputs to allow for more modes.
Overall, we were very pleased with the results of our product and think that it is a very
marketable design. In fact we plan to try to market our design next semester in our new
venture creation class. Beyond this, this project has given us experiences outside of our
usual mechanical engineering classes, and has equipped us with unique skills and
understand of electronic systems which are not typical for mechanical engineers. This
experience will help us to succeed in our future careers allowing us to work on more diverse
projects, and also to be able to understand electrical designs, and to interface more
effectively with electrical engineers in the future.
Appendix A: Code

Tile Code
//DanceFloorSlave is the code for each of the floor tiles

#include <Adafruit_NeoPixel.h>
#include <Wire.h>

#define PIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(28, PIN, NEO_GRB + NEO_KHZ800);

byte currentMode = 1; //stores the MOO

byte R = 0; //stores the intensity of red


byte G = 0; //stores the intensity of green
byte B = 0; //stores the intensity of blue

byte forcePin = A0; //pin for the force-sensitive resistor


byte forceReset = 1; //checks for the pressure pin to reset to avoid noise
byte forceThreshold = 50; //threshold for the force resistor

int nextBeat = millis(); //time stamp of last beat


int tempoTime = 500; //tempo time in millis

void setup()
{
Wire.begin(2); // join i2c bus with address #2 (must change address for each arduino)
Wire.onReceive(incomingMessage); // register event
strip.begin();
//strip.show(); // Initialize all pixels to 'off'
Serial.begin(9600); // start serial for output
}

void loop()
{

if(currentMode==2){ //if touch sensitive mode


touchSensitive(); //run touch sensitive function
} else if (currentMode==3){ //if tap tempo mode
tapTempo(); //run tap tempo function
}
}

void incomingMessage(int howMany){


currentMode = Wire.read(); // first byte is the mode set
if(currentMode == 1 || currentMode == 2 || currentMode == 4){ // solid color or touch
R = Wire.read(); G = Wire.read(); B = Wire.read(); //next 3 bytes are the color
colorFill(strip.Color(R,G,B)); //initialize the LEDs

}else if(currentMode == 3){


R = Wire.read(); G = Wire.read(); B = Wire.read(); //next 3 bytes are the color
tempoTime = Wire.read(); //fourth byte is the tempo in bpm
//nextBeat = Wire.read(); //last byte contains info for time till next beat in bpm
tempoTime = 60000/tempoTime; //converts bpm to milliseconds
//nextBeat = 60000/nextBeat; //converts bpm to milliseconds
//delay(nextBeat); //delays till next beat, will light LEDs for the first time in the void loop

}
}

void touchSensitive(){
int forceMagnitude = analogRead(forcePin); //reads the force sensor voltage
Serial.println(forceMagnitude);
if (forceMagnitude > forceThreshold && forceReset){ //if the button is being pressed and has been
reset
forceReset = 0; //trigger has to be reset
R = 255 - R; // switch the value of the colors to their complement to 255
G = 255 - G;
B = 255 - B;
colorFill(strip.Color(R,G,B)); //set the color of the tile
}else if(forceMagnitude < 10){ //no longer has force on it
forceReset = 1; //reset the trigger
}
}

void tapTempo(){ //tap tempo function


colorFill(strip.Color(R,G,B)); //turn on the LEDs for half the beat
delay(tempoTime/2);
colorFill(strip.Color(0,0,0)); //turn off the LEDs for half the beat
delay(tempoTime/2);
} //end tapTempo

void colorFill(uint32_t c) { //function to turn all the pixels in the strip to a set color
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
}
strip.show();
}
Control Board Code
//DanceFloorMaster is the code for the control board of the dance floor

#include <Wire.h>
#include <Adafruit_NeoPixel.h>

#define rgbLED 6
#define msg7RESET 11 //terminal 7 on chip
#define msg7Strobe 12 //terminal 4 on chip
#define msg7DCout 0 //terminal 3 on chip

Adafruit_NeoPixel strip = Adafruit_NeoPixel(3, rgbLED, NEO_GRB + NEO_KHZ800);

byte R = 255; //red intensity


byte G = 255; //green intensity
byte B = 255; //blue intensity

byte buttonState; //button variable


byte modeReset; // variable used to check if the mode selection reset

byte solidModePin = 2; //pin for the solid color function mode


byte solidModeOn = LOW; //start with solidMode off

byte touchModePin = 3; //pin for the touch sensitive function mode


byte touchModeOn = LOW; //start with touch mode off

byte tapModePin = 4; //pin for the tap tempo mode


byte tapModeOn = LOW; //start with tap tempo off
byte tapPin = 7; //pin for the tap tempo button
int lastBeat = millis(); //last time the beat was checked
byte BPM = 60; //current bpm
int tempoTime = 1000; //average tempo in millis
byte tapReset = 1; //checks for the tap tempo button to reset
int tapArray[] = {1000,1000,1000,1000}; //stores the past tempo times

byte beatModePin = 5; //pin for beat mode


byte beatModeOn = LOW; //start as low

byte RPin = A1; //pin for red potentiometer


byte GPin = A2; //pin for green potentiometer
byte BPin = A3; //pin for blue potentiometer

byte matrixDims[] = {2,2}; //dimensions of the floor


byte numTiles = 4; //number of tiles
byte slaveIDs[] = {1,2,3,4}; //tile i2c ids

byte mode = 1; //variable to control switching between modes


//unsigned long currentColor2 = strip.Color(50,0,0);

void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
strip.begin();
Serial.begin(9600);
pinMode(msg7RESET, OUTPUT);
pinMode(msg7Strobe, OUTPUT);
pinMode(solidModePin, INPUT);
pinMode(touchModePin, INPUT);
pinMode(tapModePin, INPUT);
pinMode(tapPin, INPUT);
pinMode(beatModePin, INPUT);

void loop(){
//check the color pots
int potValue = analogRead(RPin); //check red
R = map(potValue,0,1023,0,255);
potValue = analogRead(GPin); //check green
G = map(potValue,0,1023,0,255);
potValue = analogRead(BPin); //check blue
B = map(potValue,0,1023,0,255);
colorFill(strip.Color(R,G,B)); //set color on master's LEDs

//checkbuttons
solidModeOn = digitalRead(solidModePin);
touchModeOn = digitalRead(touchModePin);
tapModeOn = digitalRead(tapModePin);
beatModeOn = digitalRead(beatModePin);
buttonState = digitalRead(tapPin);

//change the state if a button is HIGH


if(solidModeOn == HIGH){
if(modeReset){ //if mode buttons have been reset
modeReset = 0; //buttons have to be reset
mode = 1; //set the mode to 1
changeMode(); //run changeMode function
}
}else if(touchModeOn == HIGH){
if(modeReset){ //if mode buttons have been reset
modeReset = 0; //buttons have to be reset
mode = 2; //set the mode to 2
changeMode(); //run changeMode function
}
}else if(tapModeOn == HIGH){
if(modeReset){ //if mode buttons have been reset
modeReset = 0; //buttons have to be reset
mode = 3; //set the mode to 3
changeMode(); //run changeMode function
}
}else if(beatModeOn == HIGH){
if(modeReset){ //if mode buttons have been reset
modeReset = 0;
mode = 4;
changeMode();
}
} else { //none of the mode buttons are pressed
modeReset = 1; //reset the mode trigger
}
//end of change state if button is pressed

if(buttonState == HIGH){ //tap tempo button is being pressed


if(tapReset==1){ //if trigger has been reset
int currentBeat = millis();
//update the tap array with the new beat and delete old data
tapArray[3] = tapArray[2];
tapArray[2] = tapArray[1];
tapArray[1] = tapArray[0];
tapArray[0] = currentBeat - lastBeat;
lastBeat = currentBeat;
tempoTime = tapArray[0]+tapArray[1]+tapArray[2]+tapArray[3]/4;
BPM = tempoTime/60000; //calculate bpm
tapReset = 0; //tap tempo button needs to be reset
}
}else{
tapReset = 1; //tap tempo button not pressed, reset trigger
}

} //end void loop

void changeMode(){ //only runs when a button is pressed to change the mode
Serial.println("Sent Signal");
if(mode == 1){ //solid color function
byte message[] = {1,R,G,B}; //message is 1 for solid, then the color
for (byte i=0; i<numTiles; i++){ //send it to all the tiles
Wire.beginTransmission(slaveIDs[i]); //transmit to device
Wire.write(message,4); //send 4 bytes
Wire.endTransmission(); //stop transmitting
}
} else if(mode ==2){ //touch sensitive function
byte message[] = {2, R, G, B}; //message is 2 for touch, then the color
for (byte i=0; i<numTiles; i++){ //send it to all the tiles
Wire.beginTransmission(slaveIDs[i]); //transmit to device
Wire.write(message,4); //send 4 bytes
Wire.endTransmission(); //stop transmitting
}
} else if(mode == 3){ //tap tempo function
byte message[] = {3,R,G,B,BPM};
for (byte i = 0; i<numTiles; i++){
Wire.beginTransmission(slaveIDs[i]); //transmit to device
Wire.write(message,5); //send 4 bytes
Wire.endTransmission(); //stop transmitting
}
} //end of mode checker
} //end of changeMode

void colorFill(uint32_t c) { // function to set all pixels to one color


for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
}
strip.show();
}

void BassBeat(){
digitalWrite(msg7RESET, HIGH); // reset the MSGEQ7's counter
delay(5);
digitalWrite(msg7RESET, LOW);
byte PWMvalues[7];
for (int x = 0; x < 7; x++){
digitalWrite(msg7Strobe, LOW); // output each DC value for each freq band
delayMicroseconds(35); // to allow the output to settle
int spectrumRead = analogRead(msg7DCout);

PWMvalues[x] = map(spectrumRead, 0, 1024, 0, 255); // scale analogRead's value to Write's 255


max
if (PWMvalues[x] < 50){
PWMvalues[x] = PWMvalues[x] / 2; // bit of a noise filter, so the LEDs turn off at low levels
PWMvalues[x] = 0;
} else if(PWMvalues[x] > 200){
PWMvalues[x] = 255;
}
digitalWrite(msg7Strobe, HIGH);
}
byte message[] = {4, PWMvalues[1], PWMvalues[3], PWMvalues[5]};
for (byte i=0; i<numTiles; i++){ //send it to all the tiles
Wire.beginTransmission(slaveIDs[i]); //transmit to device
Wire.write(message,4); //send 4 bytes
Wire.endTransmission(); //stop transmitting

} //end write to tiles


} //end BassBeat
Appendix B: Controller Circuit Diagram
Appendix C: MSGEQ7 Data Sheet
ME 445 Final Project Report
Electronic Bicycle Gear Shifter

Technical Report Submitted to: Dept. of Mechanical & Nuclear Engineering


Prof. H.J. Sommer III
337 Leonhard Building
The Pennsylvania State University
University Park, PA 16802

Submitted by: Aaron Felling and Jonathan Rhone

Date: December 17, 2013


1

Executive Summary
The inspiration for this design came from an individual with a nerve injury resulting in the
loss of use of his left arm. This made it difficult for him to use a bike with conventional gear
shifters on both sides of the handlebars. The goal was to make an electronic gear shifter
with push button switches on the right side of the handlebars so the individual could initiate
the gear change sequence with only his right hand. Additionally, an automatic gear
changing system was implemented to move the rear derailleur using a Hall Effect sensor
and two magnets placed on the rear tire to sense changes in rotational speed. Two linear
actuators, one attached to each gear shifter cable, converts the electrical signal to
mechanical motion. While all code and electrical signals worked correctly, the linear
actuators used on this project were not strong enough to move the derailleur on the bike.
Replacing the linear actuators with a model strong enough to effectively change the bike
gears would result in a fully functioning electronic gear shifter with an automatic
transmission on the rear derailleur.
2

Table of Contents
1.0 Introduction ............................................................................................................................................ 3
2.0 Concept ................................................................................................................................................... 3
2.1 Function .............................................................................................................................................. 4
3.0 Components ............................................................................................................................................ 6
3.1 L293D Motor Driver Chip .................................................................................................................... 6
3.2 Linear Actuator ................................................................................................................................... 6
3.3 Arduino ............................................................................................................................................... 7
3.4 Hall Effect Sensor ................................................................................................................................ 7
3.5 Bike...................................................................................................................................................... 8
4.0 Initial Testing ........................................................................................................................................... 8
5.0 Fabrication .............................................................................................................................................. 8
5.1 Actuator Mounting.............................................................................................................................. 8
5.2 Circuit Construction........................................................................................................................... 10
6.0 Results and Discussion .......................................................................................................................... 12
References .................................................................................................................................................. 13
Appendix A – BOM ...................................................................................................................................... 14
Appendix B - Code ....................................................................................................................................... 15
Appendix C – Datasheets ............................................................................................................................ 23
3

1.0 Introduction
The project was to fabricate an electronic bicycle gear shifter system. The gear shifter could
be initiated by the bicyclist manually with two sets of switches or automatically controlled by
sensing the change in speed of the bike.

The motivation for creating an electronic bicycle gear shifter came from Jon Rhone’s Senior
Design project, which was to design a bicycle for a handicapped individual suffering from
brachial plexopathy. Brachial plexopathy is an injury to the brachial plexus, a network of
nerves connecting the spine to the shoulder, arm, and hand. The brachial plexus injury
occurred from a construction accident where a piece of drywall fell onto the top of his
shoulder. The trauma caused the nerves to detach from the spine, rendering his arm
effectively useless. With only the use of one arm, it is impossible to shift both the front and
rear gears using the traditional setup of a gear shifter lever on each handlebar. By having
an electronic gear shifter either controlled by a set of switches or automatically, the
individual would have an enhanced ability to shift gears while also controlling the bicycle.

2.0 Concept
The basic concept for the project was to replace the conventional bicycle gear shifter
system with one that is electronically controlled. The end result, increasing or decreasing
the tension on the gear shifter cable to move a derailleur to push the chain onto a new
sprocket, does not change by implementing an electronically controlled system. However,
by using a linear actuator as the core concept of the project the manner by which the
tension on the gear shifter cable is changed was to now be completed using electronics.
4

2.1 Function

The concept flow chart showing how the code works can be found in Figure 2.1. The full set
of code is shown in Appendix B. Following powering up the system, the current gear for
both the front and rear sprockets is calculated by reading the potentiometer built-in to each
linear actuator and comparing the voltage reading to an associated speed. Knowing the
initial gear setting was critical for later displacing the actuator by the correct amount to
achieve the desired gear setting. Once this initial step is completed, the program waits.

Figure 2.1: Concept flow chart. Note that the loop denoted by orange boxes is repeated until the target
position is achieved.

There are two ways that changing a gear can be initiated: a user command via a push-
button switch and an automatic signal based upon on the bicycle’s speed.

The push-button switch is very similar to the manual gear shifter in its use. The rider
presses a button switch to shift the bike to the desired gear ratio. The front and rear gear
settings are controlled independently by two sets of two switches, shown in Figure 2.2.
Once the switch has been pushed the Arduino increases or decreases the gear setting
number. Based upon the new gear setting number, the Arduino determines the target
position of the actuator for the new gear from a lookup table.

Once the target position is known the Arduino executes the following loop. First, the current
position is calculated from the actuator potentiometer. The difference between the current
5

position and target position is then calculated and is used as the basis for a proportional
motor controller. For a relatively large delta between current position and target position the
Arduino sends maximum voltage to the motor driver chip, the L293D, using a PWM motor
control function. As the delta decreases the voltage signal decreases to slow the actuator’s
displacement very close to the target position. Once the program runs through this set of
commands once, the current position of the actuator is recalculated and then the loop is
repeated until the target position is achieved. The program then waits until the next gear
change signal is given.

Rear Up

Front Up

Rear Down

Front Down
Figure 2.2: Pushbutton switch mount. Each set of two switches controls the front and rear gear settings.

Alternatively, the gear change can be initiated based upon the rotational speed of the bike
wheel using a Hall Effect sensor. The Hall Effect sensor, mounted on the rear of the bike
frame, reads a change in magnetic field caused by a magnet mounted on the wheel
sweeping past the sensor. The Arduino calculates the rotational speed of the bike wheel by
measuring the time interval between Hall Effect readings and dividing that into a half
revolution (two magnets spaced 180° apart). The program stores the most recent five
values of wheel speed. The difference between the most recent and oldest rotational
speeds is calculated every time a reading is taken. If this difference is outside of a range of
±10 RPM, the Arduino sends a command to initiate a gear setting change on the rear set of
gears The bike design has implemented an automatic gear switch on the rear gears to
account for small changes in speed. This takes away the need for the rider to constantly
change gears when only a small change in the gear ratio is needed. However, we have
reserved the front gears for manual control only. That way the rider has control over large
changes in speed which would be useful when going up or down steep hills.
Once the gear change command is initiated the Arduino executes the same set of
commands to reach the target actuator position as previously discussed.
6

3.0 Components
The components used to complete this project were the L293D motor driver chip, two
Firgelli linear actuators, a Hall Effect sensor, the Arduino Micro board, and a bicycle. The
datasheets for all electronic components are shown in Appendix C.

3.1 L293D Motor Driver Chip

Figure 3.1: L293D chip. [1]

The L293D motor driver chip was used to drive the two linear actuators. The advantage of
this chip is that it can control two motors simultaneously. This is ideal for this application
because it is possible that both actuators will be moving at the same time in order to attain
the desired gear ratio. The chip can also drive each actuator both forward and backwards,
an obvious need for driving an actuator. The datasheet can be found in Appendix C.

3.2 Linear Actuator

Figure 3.2: Firgelli PQ12-100-12-P Mini Actuator. [2]

Two Firgelli PQ12-100-12-P actuators were used to move the gear shifter cables. The
actuators are powered from DC, which is ideal for use on a bicycle powered by a typical
battery. The nominal voltage for each actuator is 12V, which was supplied by stacking 8 AA
batteries in series. The actuators feature a 100:1 gear ratio providing relatively high output
force, approximately 35 N. The maximum stroke length of approximately 0.787 inches
makes the actuator compact enough to be easily stored. Another important feature of this
actuator is that it features potentiometer position feedback. This provides the Arduino with
feedback on the current actuator position, making accurate proportional control possible.
7

3.3 Arduino

The Arduino Uno was selected as the microcontroller for this project because of the team
member’s familiarity with it. It is used to read the potentiometers on the actuators, read the
switches, read the Hall Effect sensor and, based upon the code, calculate the rotational
speed of the bike, determine the current bike gear ratio, control the actuators.

3.4 Hall Effect Sensor

Figure 3.3: Hall Effect sensor. [3]

The Hall Effect sensor detects when the magnets pass by due to the high magnetic field.
This varies the output from the Hall Effect sensor. By simultaneously calculating the time
between each magnet passing by the Hall Effect sensor, the rotational speed can be
calculated. The velocity of the bicycle can then be calculated by multiplying the rotational
speed by the radius of the wheel.

Magnet

Hall Effect
Sensor

Magnet

Figure 3.4: Rear bicycle wheel showing Hall Effect sensor and magnet mounting.
8

3.5 Bike
The bicycle that was used was a Mongoose Crossway, which was donated by team
member Jon Rhone. It is similar in style to the bicycle used by the handicapped individual.

4.0 Initial Testing


Prior to altering the bicycle, the team performed testing to determine the relative change in
length of the gear shifter cable required to move between gears. A datum was established
on each gear shifter cable and the distance required to move from one speed to the next
was measured to an accuracy of +/- .01 inches. The results of the testing are shown in
Table 4.1. This information was essential for later programming the Arduino to move the
actuator to the correct position for each speed.

Table 4.1. Position change required to move from one speed to another relative to first speed.

Speed Front (in.) +/- .01 in. Rear (in.) +/- .01 in.

1 0 0

2 .14 .12

3 .14 .08

4 .3 .08

5 – .10

6 – .08

7 – .12

5.0 Fabrication

The team initially constructed a prototype of the project using the bicycle as the base. The
physical mounting of the hardware, the circuits, and the code were all prototyped and then
utilized their troubleshooting abilities to evolve towards the final prototype. This section
details the initial work that was performed to complete the first prototype.

5.1 Actuator Mounting

To determine if the prototype would function, the actuators were temporarily mounted to the
bike. To accomplish this, the original gear shifter cable was removed from the bike. The
actuators were mounted to the bicycle frame with zip ties on the lower center tube, as
9

shown in Figure 5.1. New gear shifter cables were connected from each actuator to the
respective derailleur, as shown in Figure 5.2. This enabled the team to test the circuitry and
code with the actuators attached to the bicycle.

Figure 5.1: Side view of linear actuator temporarily mounted to bicycle frame. The gear shifter cable is
attached to the actuator.

Figure 5.2: Bottom view of bike showing both linear actuators temporarily connected to bicycle frame.
10

5.2 Circuit Construction

The circuit was initially constructed on a breadboard, shown in Figure 5.3, so the team
could easily troubleshoot it. The circuit consisted of the L293D motor driver chip connected
to the two actuator wiring harnesses. Some of the Arduino digital pins were connected to
the L293D to send the signals to turn on the correct motor driver pins. Four pushbutton
switches were wired in to the Arduino digital pins as well to send the signal to initiate the
gear change sequence. Alternatively, the output from the Hall Effect sensor was connected
to digital pin 2 on the Arduino so an interrupt could occur every time the magnet swept by
the sensor.

Figure 5.3: Actuator driver circuits connected to Arduino board on the breadboard. Pushbutton switches
are not shown.

Once it was determined that circuit system and code (Appendix B) functioned as expected,
the next step was to determine if the actuators could supply enough force on the gear
shifter cable to move the derailleur. The actuators were rigidly constrained to the bicycle
and the system was performance tested. Testing confirmed that the linear actuators were
not strong enough to move either the front or rear derailleur. However, this test also
confirmed that all of the systems functioned as anticipated. With an actuator that could
supply greater force, derailleur would have moved.

Other than the actuators not being able to supply the necessary force to the derailleur, the
prototyped system worked. The next phase of the project was to disassemble the circuits
from the breadboard and attach them to the bicycle. Using a through-hole breadboard the
circuit was transferred to the bicycle. Figure 5.4 shows the package containing the through-
hole breadboard with the soldered connections, the Arduino, and the power supply–8 AA
batteries supplying around 12 V DC.
11

The switches were attached to the bicycle by constructed a wooden mount that clamps on
the handlebar, as shown in Figure 2.2. The switches fit snugly in holes drilled through the
mount.

Figure 5.4: Arduino, through-hole breadboard, and battery pack mounted on bicycle

A diagram showing the entire circuit is shown in Figure 5.5.

Figure 5.5: Complete circuit diagram


12

6.0 Results and Discussion

After mounting the circuit and components on the bicycle one of the actuators was not
functioning due to a bad connector at the end of the ribbon wire. The connection failure did
not occur until shortly before the project was due, therefore a new part could not be ordered
and delivered in time. Attempts were made to adjust the connector, however this ultimately
broke the wire leads going from the L293D to the connector. In an attempt to fix the
connection problem, the connector was removed and the ribbon wire was cut length-wise to
isolate each of the five lines leading to the actuator. Wire leads were soldered to each of the
ribbon wire lines to manually make connections to the rest of the circuit. However, the
ribbon cable is susceptible to fracture due to bending, and soldering wire leads to the ribbon
cable proved unsuccessful.

The project as a whole was a success in the fact that the concept functions as expected.
The user can initiate the gear change sequence by hitting a switch or the sequence can be
initiated automatically by sensing the change in rotational speed. The actuators are able to
go to a set location based upon the gear setting. The only two factors impeding the project
from being fully functional are the actuators not supplying enough force to move the
derailleur and the bad actuator connector that could easily be replaced if there was
sufficient time.
13

References
[1] “L293D Motor Driver”. http://commons.wikimedia.org/wiki/File:L293D_Motor_Driver.jpg

[2] “Firgelli PQ 12 P Linear Actuator”.


http://store.firgelli.com/Firgelli_PQ_12_P_Linear_Actuator_p/pq12-p.htm

[3] “Hall Effect Sensor”.


http://www.opensprints.com/howto_parts_list.php

[4] “L293D Datasheet”.


http://www.me.psu.edu/sommer/me445/L293D.pdf

[5] “Firgelli PQ-12-P Linear Actuator Datasheet.


http://store.firgelli.com/Firgelli_PQ_12_P_Linear_Actuator_p/pq12-p.htm

[6] “Hall Effect Sensor Datasheet”.


http://www.datasheets.pl/hall_effect_sensors/HAL535.pdf
14

Appendix A – BOM

Part name Price ($) Quantity


Arduino UNO Donated 1

PQ12 Linear Actuator 20mm, 100:1, 12V,


65.00 2
Potentiometer

Gear Shifter Cable 3.50 2

Bolts 1/4 X 20 Machine Screws Donated 2

1/4 X 20 Nuts Donated 2

Perma Proto Board Donated 1

L293 Chip Donated 1

Switch Mount (wood) Donated 1

Hall Effect Sensor Donated 1

Magnets (from Mike) Donated 2

Push Button Switches Donated 4

Misc. Wire Donated 10 ft.

AA Battery 9.00 8

Plastic AA Battery Holder Donated 1

Plastic Box Case Donated 1


15

Appendix B - Code
//LinearActuatorControl
/*Control the position of a linear actuator using 12V on pin 8,
L293D controlled. */

//Variables
float motorspeedfront;
float motorspeedrear;
float motorvoltagefront;
float motorvoltagerear;
float value;
float currentpositionrear;
float currentpositionfront;
float targetpositionrear;
float targetpositionfront;
float refrear;
float reffront;
float potvoltage;
int gain;
int gearrear;
int gearfront;
float deltafront;
float deltarear;
float highrange = .02;
float lowrange = -.02;
int readswitchfrontup;
int readswitchfrontdown;
int readswitchrearup;
int readswitchreardown;
float i;

int rpmcount;
float rpm1 = 5;
float rpm2 = 15;
float rpm3 = 20;
float rpm4 = 25;
float rpm5 = 25;
float rpmdiff;
float timeold;

//pins
int pwm2rear = 9;
int pwm2front = 6;
int pwm7rear = 10;
int pwm7front = 3;
int rearread = A2;
16

int frontread = A3;


int rearup = 4;
int reardown = 7;
int frontup = 11;
int frontdown = 12;

void setup() {
delay(4000);

pinMode(pwm2rear,OUTPUT); //Pin 2 on L293D


pinMode(pwm7rear,OUTPUT); //Pin 7 on L293D

pinMode(pwm2front,OUTPUT); //Pin 15 on L293D


pinMode(pwm7front,OUTPUT); //Pin 10 on L293D

pinMode(rearup, INPUT_PULLUP);
pinMode(reardown, INPUT_PULLUP);
pinMode(frontup, INPUT_PULLUP);
pinMode(frontdown, INPUT_PULLUP);

//next 4 lines for Hall effect


attachInterrupt(0, rpm_fun, RISING); //interrupt for Hall Effect
rpmcount = 0;
rpm5 = 0;
timeold = 0;

Serial.begin(115200);

gain = 10000;

refrear = analogRead(rearread);
reffront = analogRead(frontread);

currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins


currentpositionfront = reffront*(.00076856); //.787 in/ 1024 bins

if (currentpositionfront>.66 && currentpositionfront < .7) {


gearfront = 1;
}
else if (currentpositionfront>.52 && currentpositionfront < .56) {
gearfront = 2;
}
else if (currentpositionfront>.38 && currentpositionfront < .42) {
gearfront = 3;
17

}
else {
gearfront = 4;
}

if (currentpositionrear >.66 && currentpositionrear < .70) {


gearrear = 1;
}
else if (currentpositionrear > .54 && currentpositionrear < .58) {
gearrear = 2;
}
else if (currentpositionrear >.46 && currentpositionrear < .5) {
gearrear = 3;
}
else if (currentpositionrear >.38 && currentpositionrear < .42) {
gearrear = 4;
}
else if (currentpositionrear >.28 && currentpositionrear < .32) {
gearrear = 5;
}
else if (currentpositionrear>.20 && currentpositionrear < .24) {
gearrear = 6;
}
else {
gearrear = 7;
}
delay(200);

void loop() {
if (rpmcount >= 2) {
//Update RPM every 1 counts, increase this for better RPM resolution,
//decrease for faster update
rpm1 = rpm2;
rpm2 = rpm3;
rpm3 = rpm4;
rpm4 = rpm5;
rpm5 = 30*1000/(millis() - timeold)*rpmcount;
timeold = millis();
rpmcount = 0;
rpmdiff = rpm5 - rpm1;
Serial.println(rpm5);
if (rpmdiff > 10 && gearrear <7) {
gearrear = gearrear + 1;
Serial.println("moveup");
18

delay(700);
currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins
reartargetpositionsequence();
deltarear = (currentpositionrear-targetpositionrear);
rearsequence();
}

else if (rpmdiff < -10 && gearrear >1) {


gearrear = gearrear - 1;
Serial.println("movedown");
delay(700);
currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins
reartargetpositionsequence();
deltarear = (currentpositionrear-targetpositionrear);
rearsequence();
}
Serial.println(rpm5,DEC);
Serial.println(rpmdiff);
Serial.println(targetpositionrear);
}

refrear = analogRead(rearread); //reads the pot on the rear actuator


reffront = analogRead(frontread); //reads the pot on the front actuator

readswitchfrontup = digitalRead(frontup);
readswitchfrontdown = digitalRead(frontdown);
readswitchrearup = digitalRead(rearup);
readswitchreardown = digitalRead(reardown);

//Serial.println(readswitchfrontup);
// Serial.println(readswitchfrontdown);

//move front gear up 1


if (readswitchfrontup == LOW && gearfront<4) {
gearfront = gearfront + 1;
delay(700);
currentpositionfront = reffront*(.00076856); //.787 in/ 1024 bins
fronttargetpositionsequence();
deltafront = (currentpositionfront-targetpositionfront);
frontsequence();
}

//front gear down 1


else if (readswitchfrontdown == LOW && gearfront >1) {
gearfront = gearfront - 1;
delay(700);
19

currentpositionfront = reffront*(.00076856); //.787 in/ 1024 bins


fronttargetpositionsequence();
deltafront = (currentpositionfront-targetpositionfront);
frontsequence();
}

//rear gear up 1
else if (readswitchrearup == LOW && gearrear<7 ) {
gearrear = gearrear + 1;
delay(700);
currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins
reartargetpositionsequence();
deltarear = (currentpositionrear-targetpositionrear);
rearsequence();
}

//rear gear down 1


else if (readswitchreardown == LOW && gearrear >1) {
gearrear = gearrear - 1;
delay(700);
currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins
reartargetpositionsequence();
deltarear = (currentpositionrear-targetpositionrear);
rearsequence();
}
else {
}
}

//The function sets the motor speed for rear motor


void motorrear(float motorspeedrear) {//
//Arduino pins 6, 5, and 3 are set to outputs

//Clockwise Rotation
if (motorspeedrear>0) { //motor rotates CW for positive values
analogWrite(pwm7rear,0); //L293D pin 7 LOW
value = value*2.55; //converts from 0 to 100% scale to 0 to 255 scale, the input for PWM
analogWrite(pwm2rear,motorspeedrear); //sends this duty cycle of PWM to enable
}

//Counter-Clockwise Rotation
else if (motorspeedrear<0) { //motor rotates CCW for negative values
analogWrite(pwm2rear,0); //L293D pin 2 LOW
value = -value*2.55; //converts from 0 to -100% scale to 0 to 255 scale, the input for PWM
analogWrite(pwm7rear,motorspeedrear); //sends this duty cycle of PWM to enable
20

}
else {
analogWrite(pwm2rear,0);
analogWrite(pwm7rear,0);
}
}

//The function sets the motor speed for rear motor


void motorfront(float motorspeedfront) {//

//Clockwise Rotation
if (motorspeedfront>0) { //motor rotates CW for positive values
analogWrite(pwm7front,0); //L293D pin 7 LOW
value = value*2.55; //converts from 0 to 100% scale to 0 to 255 scale, the input for PWM
analogWrite(pwm2front,motorspeedfront); //sends this duty cycle of PWM to enable
}

//Counter-Clockwise Rotation
else if (motorspeedfront<0) { //motor rotates CCW for negative values
analogWrite(pwm2front,0); //L293D pin 2 LOW
value = -value*2.55; //converts from 0 to -100% scale to 0 to 255 scale, the input for PWM
analogWrite(pwm7front,motorspeedfront); //sends this duty cycle of PWM to enable
}
else {
analogWrite(pwm2front,0);
analogWrite(pwm7front,0);
}
}

//controls the front linear actuator until it reaches the proper position
void frontsequence() {
for (i=1;i<10000; i++) {
if ((deltafront>highrange) || (deltafront<lowrange)) {
reffront = analogRead(frontread); //reads the pot on the front actuator
currentpositionfront = reffront*(.00076856); //.787 in/ 1024 bins
motorvoltagefront = (currentpositionfront-targetpositionfront)*gain; //keeps out to be positive
input

if (motorvoltagefront>100) {
motorspeedfront = 100;
}
else if (motorvoltagefront<-100) {
motorspeedfront = -100;
}
else {
motorspeedfront = motorvoltagefront;
21

}
motorfront(motorspeedfront);

reffront = analogRead(frontread);
currentpositionfront = reffront*(.00076856); //.787 in/ 1024 bins
deltafront = (currentpositionfront-targetpositionfront);
}
else {
motorspeedfront = 0;
motorfront(motorspeedfront);
}
}
}

//controls the rear linear actuator until it reaches the proper position
void rearsequence() {
for (i=1;i<10000;i++) {
if ((deltarear>highrange) || (deltarear<lowrange)) {
currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins
motorvoltagerear = (currentpositionrear-targetpositionrear)*gain; //keeps out to be positive
input

if (motorvoltagerear>100) {
motorspeedrear = 100;
}
else if (motorvoltagerear<-100) {
motorspeedrear = -100;
}
else {
motorspeedrear = motorvoltagerear;
}

motorrear(motorspeedrear);
refrear = analogRead(rearread);
currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins
deltarear = (currentpositionrear-targetpositionrear);

}
else{
motorspeedrear = 0;
motorrear(motorspeedrear);
}
}
}
22

// determines the target position of the front linear actuator depending upon which gear the user
is in
void fronttargetpositionsequence() {
if (gearfront == 1) {
targetpositionfront = .68;
}
else if (gearfront == 2) {
targetpositionfront = .54;
}
else if (gearfront == 3) {
targetpositionfront = .40;
}
else {
targetpositionfront = .10;
}
}
// determines the target position of the rear linear actuator depending upon which gear the user is
in
void reartargetpositionsequence() {
if (gearrear == 1) {
targetpositionfront = .68;
}
else if (gearrear == 2) {
targetpositionrear = .56;
}
else if (gearrear == 3) {
targetpositionrear = .48;
}
else if (gearrear == 4) {
targetpositionrear = .40;
}
else if (gearrear == 5) {
targetpositionrear = .30;
}
else if (gearrear == 6) {
targetpositionrear = .22;
}
else {
targetpositionrear = .10;
}
}
void rpm_fun()
{
rpmcount++;
//Each rotation, this interrupt function is run twice
}
23

Appendix C – Datasheets
First page of L293D data sheet. [4]
24

First two pages of Firgelli PQ12-100-12-P Linear Actuator. [5]


25
26

Representative datasheet for Hall Effect Sensor. [6]


The Ultimate Useless Box
ME 445 – Microcomputer Interfacing: Final Project Report

Prepared for: Dr. Henry Sommer


Mike Robinson
Prepared by: Michael Goodspeed
Brett Simmons
Date: December 17, 2013
Table of Contents
Introduction .................................................................................................................................................. 2
Concept Development .................................................................................................................................. 2
Design............................................................................................................................................................ 3
Components .............................................................................................................................................. 3
Mechanical Design .................................................................................................................................... 3
Electrical Design ........................................................................................................................................ 5
Programming ............................................................................................................................................ 8
Performance ................................................................................................................................................. 9
Issues and Difficulties.................................................................................................................................. 10
Conclusions ................................................................................................................................................. 11
Appendix A: Schematic Diagram ................................................................................................................. 12
Appendix B: System Flow Chart .................................................................................................................. 13
Appendix C: Extra Photographs .................................................................................................................. 13
Appendix D: IR Sensor Calibration .............................................................................................................. 15
Appendix E: Arduino Code .......................................................................................................................... 16

1
Introduction
For the ME 445 final project, Team 11 designed “the ultimate useless box”. As a simple wooden
box with a hinged top and single input switch, the useless box can be boiled down to a novel
toy that turns itself off. Whenever the switch is triggered, the machine performs one of its
several sequences, ultimately resulting in an automated arm striking the switch to the off
position. The project presented an opportunity to enhance learning by implementing a variety
of mechatronic concepts in a user-friendly system. In addition, project goals were to expand
upon current online functions for a useless box, creating a device that is fun and amusing for
those of all ages.

Concept Development
The idea for this project was inspired by the team’s discovery of a video of a useless box online.
The box in this video was small, and the top surface was cut into two halves. A switch was also
located on one of the halves of the top surface. When the user pushed the switch, the
opposing part of the lid would lift up, and a
mechanical arm would come out of the box
and press the switch back into its original
position. In Figure 1 to the right, the original
electircal circuit for a useless box can be seen.
Here the electrical layout is simple and thus
can utilize a 555 timer chip to control the servo
motion. The team enjoyed watching the video
of this original useless box, but felt that
interesting improvements and functions could
be made.
With the current design, the single servo is the
only component that can move and the
electrical components are relatively basic. For
the “ultimate useless box,” it was desired to
keep the single input component, but greatly Figure 1. Frivolous Engineering original useless box design
expand on the responses the box can perform. Therefore, Team 11 set out to add a variety of
new features and different sequences that the useless box can output every time the switch is
triggered.
A key component required in the development of the ultimate useless box is an Arduino Uno.
Incorporation of this microcontroller allows for more advanced sequences and box
performance. From initial brainstorming, Team 11 set out to include a series of motors that can
translate the box on a flat surface during certain output sequences. Coupled with the motors,
distance or infrared sensors will be able to obtain certain distance values when the user
reaches their hand in to hit the switch. These distance values can be interpreted by the

2
Arduino and converted into unique motor outputs to prevent stagnant sequences. LEDs are
also to be integrated, illuminating the interior of the box and creating a variety of lighting
conditions. Furthermore, three servos are to be used instead of one. Alternate design plans to
Figure 1 call for a second servo to lift the lid while the first servo strikes the switch. Besides
simulating this alternate two servo design, a third servo will be added to the ultimate useless
box for some trivial sequence.
Success in creating the ultimate useless box is contingent on a variety of factors. First and
foremost, appropriate mechanical and electrical components need to be selected. Proper
communication among team members will allow for smart design and assembly of the system.
Finally, testing the servos, sensors, and other internal components will ensure smooth
performance of the ultimate useless box.

Design
Components
The various components utilized in this project are listed below in the Bill of Materials.
Table 1. Bill of Materials

Component Amount Unit Cost Total Cost


1/2" Thick Laminate Wood 1 Board $9.99 $9.99
Arduino 1 $29.95 $29.95
Adafruit 16-Channel Servo Driver 1 $14.95 $14.95
Zumo Shield, Chassis, & Motors 1 $99.95 $99.95
Tower Hobbies Servos 2 $8.99 $17.98
Hextronik Servo 1 $2.69 $2.69
Sharp IR Distance Sensors 2 $12.99 $25.98
Switch 1 $0.50 $0.50
RGB LEDs 4 $1.00 $4.00
Miscellaneous Wood Screws X X $4.00
Screw Hooks 4 $0.25 $1.00
Rubber Bands 4 X $0.25
Basic Electrical Components X X X
Total Cost $211.24

Mechanical Design
Although the mechanical components seem relatively simple, the construction of the box and
its interior components was quite difficult. It required that the assembly of the box was well-
planned to easily implement and integrate the internal components. From the outside, the box
looks like a simple wooden box with a lid enclosed by four side walls, which are fixed to the
bottom face (See Figure 2). In order to easily access the internal components, one side wall is

3
completely removable from the assembly, while the other three walls are screwed in through
the bottom. Holes were drilled into the bottom of the sidewall to allow the side wall to rest
over the screws without fastening, which can be seen in Figure 14 of Appendix B. This led to
the sidewall being relatively loose, so it is fastened to the adjacent interior walls with rubber
bands during final assembly.

Figure 2. Exterior wooden construction

When creating the lids, both lids were designed to sit flush with the top of the side walls. One
lid also had to pivot to allow for the servo arm to strike the switch. Internal wooden posts were
glued onto the side walls to act as supports for the lids. Since there would be considerable
adjustments after construction, it was desired that both lids could be removed (eliminating the
possibility of hinging the lids on the side walls) to allow for easier maintenance on internal
components. The team decided to attach small screw hooks onto both lids and onto the back
walls. Rubber bands were strung between each pair of hooks. The rubber bands hold the lids
and walls together and allow the lid to pivot as desired at the rear support. Choice in different
strengths of rubber bands allowed for variability in the sturdiness of the box construction.
Another issue with mechanical construction of the box was determining how to attach the box
to the chassis of the Zumo Bot. The team hoped to make the box sit around the Zumo Bot, with
the bottom of the box as close to the ground as possible. Therefore, a hole slightly larger than
the Zumo was cut in the bottom of the box to allow the majority of the Zumo to be hidden
within the box. However, this initially caused a struggle as the team searched for ways to
physically attach the box. It was determined that the best course of action was to fasten a
wood board inside the box approximately one inch above the previously cut hole. The Arduino

4
board could then be bolted to this wood, and the Zumo Bot would attach directly to the
Arduino. This design allowed for the bottom of the box to sit approximately ½” above the
ground, satisfying the team’s initial design hopes.
The final mechanical parts that
needed to be manufactured
were the two arms that are
attached to the servos inside the
box. One arm is used to lift the
lid, while the other arm is
utilized to rotate through the
opening of the lid and hit the
switch back to the original
position. These arms were also
manufactured using the ½” thick
laminate wood, like the exterior
box.
Figure 3. Switch-hitting arm
Electrical Design
The box is driven and controlled by an Arduino Uno with an ATMEGA_328 chip produced by
Atmel. The Arduino microcontroller connects directly to the top of the Zumo Bot by mating the
pins from the Arduino to the corresponding pinholes of the Zumo. The Bot also has a 4xAA
battery case that the team utilized to provide power to the Arduino. The four AA batteries
inserted into the Zumo Bot provide enough power to fuel every component within the circuit.

Figure 4. Pololu Zumo Bot

5
The Zumo Bot also has several internal
components that have interesting capabilities that
will be utilized by the useless box. The most
important function of the Zumo is the two internal
motors. As the schematic diagrams show, these
motors use digital pins 7, 8, 9, 10 of the Arduino
for direction control and speed control for each
motor. The speed of the motors is determined
using pulse-width-modulation, with a maximum
speed value of 400. Since the motors use pins 7-
10, these pins are unavailable for any other
function in the system. Each motor is connected Figure 5. Individual Zumo Bot Motor

to a tread that rotates to move the Bot. Another component of the Zumo Bot that the team
used was the buzzer function. There is a small buzzer that generates sounds that the team can
utilize to play simple musical sequences. To utilize this function, a jumper connects the two
pins around the buzzer, and the buzzer is attached to digital pin 3.
With the Arduino placed upside down into the Zumo Bot, all pin-outs from the Zumo relate
directly to the pin-outs of the Arduino. Therefore all other components are to be connected
directly to the Zumo Bot’s pins. The first component that was interfaced with the Arduino and
Zumo was the switch. One of the switch’s leads is connected to ground, while the other is
attached to digital pin 2. The switch utilizes the Arduino’s internal pull-up resistor on pin 2 in
order to give the board an accurate reading about the state of the switch.
The most important electronic components of the box are the two servos that lift the lid and
flip the switch on the top of the box. If these servos were to function incorrectly, the box would
be unable to turn itself off, leaving the system continuously running programmed sequences.
Another small servo was integrated into the system for one of the box’s specific sequences.
To gain a greater capability for the box to interact with the user, the team added two infrared
sensors to the box’s electronic system. With these sensors, the box can “see” how far away the
user’s hand is, and they have a range of up to 80cm. The signal output wires for the sensors
were placed in the first two analog pins of the Arduino, allowing the Arduino to read an analog
voltage from the distance sensor. The voltage that is read by Arduino correlates to the distance
of the object that the sensor is reading. The addition of these sensors allowed the team to
create sequences for the box that adjust based on the position of the user. The final set of
internal components to be interfaced in the box was the four RGB LEDs, wired in parallel to
make two pairs of LEDs. These LEDs provide color to the inside of the box while the lid is open.
The servos and the LEDS were integrated into the system using an Adafruit 16-Channel Servo
Driver. With the addition of the servo driver, all three servos and the two sets of LEDs could be
controlled simultaneously while using only two analog pins, A4 and A5. The driver has 16
channels that are controlled through I2C, so the signal is communicated to the entire servo
6
driver. However, each signal has an address, and only the desired channels will accept the
signal and complete the action. In the electronic system of this project, the Arduino board is
considered the master, while the servos and LEDs are the slaves. The use of I2C made the
required communication between the Arduino and the other components much simpler.

Figure 6. Connection of Adafruit Servo Driver to Arduino Uno

The servo driver allows the team to control the servos and LEDS using a PWM signal, which
could have a minimum value of 0 and a maximum of 4095. Each individual servo must be
calibrated to find the PWM values that correlate to their minimum and maximum angles. For
the LEDs, the PWM defines the intensity of each color within the LED. For instance, setting
channel 6 to a PWM value of 4095 would light the LED to green at maximum intensity, and
setting channel 6 and 7 to 7 to a PWM of 4095 would create a blended blue-green color.
Another nice feature of the servo driver is its implementation of internal resistors. Each
channel has a 220-ohm internal resistor, which helps limit the current that enters each of the
components. The internal resistors made it very convenient to attach the LEDS and servos to
the board.
Figures 7 and 8 show the internal layout of the box with each component labeled. A full
schematic diagram can be found in Appendix A.

7
1. Arduino (underside of wood)
2. Pololu Zumo Shield
3. Adafruit 16-Channel Servo Driver
4. Servo and lid-lifting arm
5. Servo and switch-hitting arm
6. Two Infrared Distance Sensors
7. Small Servo for Flag
8. Switch
9. RGB LEDs

Figure 7. Switch-side of box Figure 8. Lid-lifting side of box

Programming
The major difficulty the team faced with the programming was related to the timing of the
servo movements for each sequence. If the timing of a sequence is incorrect, the arm that
turns off the switch could collide with other internal components. This action could break the
arm or cause a different failure within the box. With the inclusion of the library for the Adafruit
servo driver, the command “pwm.setPWM(channel, PWM_on, PWM_off)” will move the servo
from the position related to the on PWM to the position related to the off PWM. However, this
did not give the team any control over the speed of movement of the servos. To resolve this
issue and give the team the desired control, the following for loop was developed:

for (i=armd; i>armu;i--) {

pwm.setPWM(3,0,i);

delay(2);

This code was utilized throughout the project because it allowed the team to easily control the
speed of the servos by adjusting the delay within the loop. Delays were typically set between 1
and 10 milliseconds for the rotation of the three servos.
In order to organize the coding of the project, each sequence is set as its own function named
“SequenceX,” in which X is the order number of the sequence. These sequences are called

8
based on a digitalRead function for the switch located in digital pin 2 of the Arduino. If the
switch is in its original position, the Arduino reads the value as HIGH. When the switch is
flipped, the Arduino reads the pin as LOW, which triggers a sequence being called. The
sequence that is called is determined by a random number generator through Arduino’s
“random(min,max)” function. Please see Appendix E to find the Arduino code for how the
program calls sequences, as well as the rest of the project.

Performance
Each programmed sequence has a unique response before closing the switch. The following
table identifies how well the box executes each sequence with respect to the team’s
expectations.
Sequence – Desired Result Sequence Performance
Sequence 1 –Simple first progression of opening Once the team calibrated the sensors for use
the box, hitting the switch, and closing the box. with the servo driver, the sequence performed
well without any issues.
Sequence 2 – Flips the switch back to its original The distance sensors function well in this
position, but then leave the box’s lid open. The sequence and can easily detect where the user’s
readings of the two distance sensors determine hand is located. However, the box does not
which side of the box the user’s hand is coming always rotate back to the exact initial position as
from. When one of the sensors reads a voltage programmed.
much larger than the other sensor, the box will
rotate away from the user’s hand. This occurs
three times followed by the box rotating to its
original position.
Sequence 3 – The lid fluctuates up and down This sequence performs up to the anticipated
several times before closing. standards.
Sequence 4 – The box spins 180°, closes, and The box is successful in completing this sequence.
then continues to spin back to the user.
Sequence 5 – The box’s lid peaks up and down Sequence is completely successful.
several times. With each peak, the box pulses
forward, then drifts backwards while emitting
different colors.
Sequence 6 – The switch-arm oscillates multiple Sequence is completely successful.
times before finally pressing the switch.
Sequence 7 – Utilizes proportional control by This sequence works extremely well when the
using the IR sensors to determine distance user’s hand is near the box. If the user’s hand is
between the box and user. The box moves either not in view of the sensors, the sensors read a
forward or backward with velocity determined by voltage correlating to a distance of over 3 meters,
an equilibrium distance set in the code. causing the control to be slightly unsteady.
Sequence 8 – The box moves forward while The Zumo’s buzzer is successful in playing the
playing the melody of “Can’t Touch This” through song while the motors spin.
the Zumo’s buzzer.
Sequence 9 – The final sequence yields a waving The sequence is successful.
white flag from the interior of the box.

9
Issues and Difficulties
The team had a few difficulties and problems throughout the project. The first main problem
occurred when initially trying to interface all of the components in the electronic system
together. The team attempted to attach each individual component directly to the
Arduino/Zumo through the necessary analog or digital pins. During these first tests, all
components would try to run at the same time, resulting in the box shaking uncontrollably. In
these situations, the box would not run any of the sequences or the functions that the team
desired. It was this issue that caused the team to decide to use a servo driver to control many
of the system’s electronic components. With the addition of the Adafruit 16-Channel Servo
Driver, the entire system ran much smoother and easily allowed the team to control the box’s
functions.
The other major problem that the team faced occurred a few days
before the final project due date. While testing some of the
designed sequences on the box, the tread on the left side of the
Zumo rubbed up against the body of the Zumo. In an attempt to
pull the wheel outwards away from the Zumo body, the left motor
was accidentally dislodged and the connection severed. To alleviate
this problem, the Zumo had to be opened up to gain access to the
connections. Wires were then soldered between the motor leads
and the internal dual motor pin holes where the motor was initially
Figure 9. Zumo motor fix detached (See Figure 9). The Zumo was then reassembled and
attached to the box. After reattachment, battery performance was extremely poor. This
suggests a short in the circuit might have been created when repairing the Zumo’s motor.
Fixing this problem and replacing the AA batteries with a rechargeable 6V battery is a future
step that would greatly improve on the existing battery performance.
An annoying issue with our ultimate box is the design
of the assembly. Currently, the Arduino is bolted onto
the bottom of the box. In this location, there is no USB
cord access to upload code. Therefore, the entire box
needs to be disassembled to upload new code after
testing. Another problem with this setup is that the
power switch on the Zumo, which provides power to
the entire box, is located in a difficult to reach position
in the interior of the box (See Figure 10).
Next steps for the project would include drilling out a
hole through the box to allow for the Arduino cord to
reach the Arduino. This, paired with the addition of an
on-off switch, would prevent the need to open the box
Figure 10. Limited access to power switch and
Arduino USB port
10
to turn the power on or upload updated code. Also, incorporating the built in compass on the
Zumo shield could help refine several of our sequences. In the current useless box, sequences
where the box rotates or spins are based off of timing delays. These timing delays were
calibrated at a specific battery voltage. When the batteries dip in voltage, the spinning
sequences often do not reach the desired angle. Thus, if the compass was implemented, the
box could spin based upon angle instead of time, providing more accurate final results.

Conclusions
For Team 11, the ultimate useless box represented the culmination of mechatronic knowledge
gained during the duration of ME 445. Not only did the final project reinforce mechatronic
concepts, but it also illuminated the complexities and time required to create a project
controlled by a microcomputer interface. The useless box incorporated features from
mechanical, electrical, and computer programming disciplines. Meshing these three fields
within one project was a considerably difficult obstacle that promoted troubleshooting,
problem solving, and hands-on learning. Though the finished useless box’s appearance was
relatively simple, constructing the box was far from elementary. Countless programming,
electrical, and structural problems were encountered and overcome through persistent
troubleshooting and research. Even though the useless box is a novel toy, the experience
gained during the project can be applied to countless real world applications.

11
Appendix A: Schematic Diagram

Figure 11. Circuit Schematic

12
Appendix B: System Flow Chart

Figure 12. Useless Box Flow Chart

Appendix C: Extra Photographs

Figure 13. Internal Layout

13
Figure14. Removable Side Wall

Figure 15. Lid-Lifting Arm

14
Figure 16. Connection of Zumo Shield to Bottom of Box

Appendix D: IR Sensor Calibration


The following figure plots data provided in the specification sheet for the Sharp infrared sensor. The
graphs shows the relationship between the distance between the sensor and an object and the voltage
reading caused by that object. Microsoft Excel was utilized to find an accurate equation to allow the
team to calculate the distance the user’s hand is away from the box.

Sharp IR Sensor Calibration


90
80
70
y = 28.382x-1.112
Distance (cm)

60 R² = 0.9947
50
40
30
20
10
0
0 0.5 1 1.5 2 2.5 3 3.5
Voltage Reading

Figure 17. IR Sensor Calibration Plot

15
Appendix E: Arduino Code
//Call libraries
#include <ZumoMotors.h> // Assigns notes
ZumoMotors motors; unsigned char note[CANT_TOUCH] =
#include <ZumoBuzzer.h> {
#include <Pushbutton.h> NOTE_D(4), NOTE_C(4),NOTE_B(3),
NOTE_A(3),NOTE_A(4), NOTE_A(4),
#include <Wire.h>
NOTE_E(3), NOTE_G(3),NOTE_A(5),
#include <Adafruit_PWMServoDriver.h> NOTE_A(5),NOTE_C(4), NOTE_A(3),
Adafruit_PWMServoDriver pwm = /*NOTE_C(5), NOTE_D(5),NOTE_B(4),*/
Adafruit_PWMServoDriver(); SILENT_NOTE,
NOTE_D(4), NOTE_C(4),NOTE_B(3),
NOTE_A(3),NOTE_A(4), NOTE_A(4),
//Define distance variables
NOTE_E(3), NOTE_G(3),NOTE_A(5),
int LsensorValue; NOTE_A(5),NOTE_C(4), NOTE_A(3)
int RsensorValue; };

//Define motor variables //Assigns duration of each note


int diff=0; unsigned int duration[CANT_TOUCH] =
int motspeed; {
int kp; 500, 250, 250, 250, 200, 200,
250, 250, 200, 200, 250, 250, /*225, 225,
//Define servo limits in PWM length 225,*/ 750,

int lidu = 200; 500, 250, 250, 250, 200, 200,

int lidd = 500; 250, 250, 200, 200, 250, 250

int armu = 360; };

int armd = 600;


int flagu = 430; //Sets up zumo buzzer

int flagd = 155; ZumoBuzzer buzzer;

int n=1; Pushbutton button(ZUMO_BUTTON);


unsigned char currentIdx;

//Sets up buzzer song


#define CANT_TOUCH 25 void setup() {

16
//Initial Setup protocol pwm.setPWM(11,0,0);
Serial.begin(9600);
motors.flipRightMotor(true); //Sets sequence to zero to prevent repetetive
sequences
pinMode(2, INPUT_PULLUP); //Internal
pullup for switch int seq=0;
pwm.begin(); //Begins pwm for int switchVal = digitalRead(2); //reads the
adafruit value of the switch as HIGH or LOW
pwm.setPWMFreq(60); // This is the //Serial.println(switchVal);
average analog frequency
//int pot=analogRead(A2); //reads the
uint8_t twbrbackup = TWBR; // save I2C amount of voltage through the potentiometer
bitrate
//int potmult=pot/1024; //finds the
// must be changed after calling Wire.begin() percentage of the maximum 5 volts that passes
(inside pwm.begin()) through the pot
TWBR = 12; // upgrade to 400KHz! //Serial.println(switchVal);
currentIdx = 0;
//Creates sequence number when switch is
triggered
//Set initial servo positions
if (switchVal == LOW) { //if the switch is
pwm.setPWM(0, 0, lidd); in the on position
pwm.setPWM(3, 0, armd); randomSeed(analogRead(5)); //reads an
pwm.setPWM(15,0, flagd); unused pin to make random numbers different
for each use
if (n==10)
{
delay(2000);
n=1;
}
}
seq=n; //random(1,7); //calls a random
void loop() { number to pick the sequence
//Sets pwm on LEDs to zero Serial.println(seq);
pwm.setPWM(5,0,0); //Sequence6();
pwm.setPWM(6,0,0); }
pwm.setPWM(7,0,0); if (seq==1){ //calls sequence 1
pwm.setPWM(9,0,0); Sequence1();
pwm.setPWM(10,0,0); delay(100);

17
n=n+1; n=n+1;
} }
if (seq==2){ //calls sequence 2 if (seq==8){ //calls sequence 2
Sequence2(); Sequence8();
delay(100); delay(100);
n=n+1; n=n+1;
} }
if (seq==3){ //calls sequence 2 if (seq==9){ //calls sequence 2
//Sequence3(); Sequence9();
delay(100); delay(100);
n=n+1; n=n+1;
} }
if (seq==4){ //calls sequence 2 delay(100);
Sequence4(); //Serial.println(seq);
delay(100); }
n=n+1;
} //Simple open close sequence
if (seq==5){ //calls sequence 2 void Sequence1(){
Sequence5(); int i;
delay(100);
n=n+1; //Open box
} for (i=lidd; i>lidu; i--)
if (seq==6){ //calls sequence 2 {
Sequence6(); pwm.setPWM(0,0,i);
delay(100); delay(5);
n=n+1; }
}
if (seq==7){ //calls sequence 2 //Lift arm
Sequence7(); for (i=armd; i>armu;i--)
delay(100); {

18
pwm.setPWM(3,0,i); for (i=lidd; i>lidu+10; i--)
delay(2); {
} pwm.setPWM(0,0,i);
}
//Close arm delay(500);
for (i=armu; i<armd;i++) //Lift arm
{ for (i=armd; i>armu;i--)
pwm.setPWM(3,0,i); {
delay(2); pwm.setPWM(3,0,i);
} }
//Close arm
//Close box for (i=armu; i<armd;i++)
for (i=lidu ; i<lidd; i++) {
{ pwm.setPWM(3,0,i);
pwm.setPWM(0,0,i); delay(10);
delay(5); }
}
} //Rotates box based on direction of hand
coming in
while (counttot<3)
{
//Box moves away depending on direction the
operator moves towards //Read sensors
void Sequence2(){ int LsensorValue = analogRead(A1);
//Define count variables int RsensorValue = analogRead(A0);
int i;
int counttot=0; //If closer to right sensor
int countL=0; if (RsensorValue > LsensorValue + 150)
//Accounts in variability of sensors
int countR=0;
{
//Turns on specific lights and motor
//Open box direction for a second

19
pwm.setPWM(9,0,0); delay(10); //Delay in between
reads for stability
pwm.setPWM(6,0,0);
counttot=countR+countL; //Creates
pwm.setPWM(11,0,4000); total count
pwm.setPWM(7,0,4000); }
motors.setLeftSpeed(-250); //Turns off LEDs
motors.setRightSpeed(250); pwm.setPWM(9,0,4000);
delay(1000); pwm.setPWM(6,0,4000);
motors.setLeftSpeed(0); pwm.setPWM(11,0,4000);
motors.setRightSpeed(0); pwm.setPWM(7,0,4000);
pwm.setPWM(11,0,0);
pwm.setPWM(7,0,0); //Returns box to initial position after moving
countL = countL +1; //Adds to above
count if (countL>0)
} {
if (LsensorValue > RsensorValue + 150) //If motors.setLeftSpeed(250);
something is closer to left comp than right one
motors.setRightSpeed(-250);
{
delay(countL*1000);
pwm.setPWM(9,0,4000);
motors.setLeftSpeed(0);
pwm.setPWM(6,0,4000);
motors.setRightSpeed(0);
pwm.setPWM(11,0,0);
countL=0;
pwm.setPWM(7,0,0);
}
motors.setLeftSpeed(250);
if (countR>0)
motors.setRightSpeed(-250);
{
delay(1000);
motors.setRightSpeed(250);
motors.setRightSpeed(0);
motors.setLeftSpeed(-250);
motors.setLeftSpeed(0);
delay(countR*1000);
pwm.setPWM(9,0,0);
motors.setRightSpeed(0);
pwm.setPWM(6,0,0);
motors.setLeftSpeed(0);
countR = countR +1; //Adds to
count countR=0;
} }

20
//Shuts off all LEDs {
pwm.setPWM(9,0,0); pwm.setPWM(3,0,i);
pwm.setPWM(6,0,0); delay(15);
pwm.setPWM(11,0,0); }
pwm.setPWM(7,0,0); for (int n=0; n<6; n++)
pwm.setPWM(10,0,0); {
pwm.setPWM(5,0,0); //Turn off red lights
//Close box pwm.setPWM(10,0,0);
for (i=lidu+10 ; i<lidd; i++) pwm.setPWM(5,0,0);
{ //Vibrate lid
pwm.setPWM(0,0,i); for (i=lidu; i>250; i--)
} {
} pwm.setPWM(0,0,i);
}
delay(100);
//Vibrating lid with flashing red lights pwm.setPWM(10,0,4000);
void Sequence3(){ pwm.setPWM(5,0,4000);
int i; for (i=250; i<lidu; i++)
{
//Open box and turn on red lights pwm.setPWM(0,0,i);
pwm.setPWM(10,0,4000); }
pwm.setPWM(5,0,4000); delay(100);
for (i=lidd; i>lidu; i--) }
{
pwm.setPWM(0,0,i); delay(1050);
delay(1); //Hit switch with arm
} for (i=460; i>360;i--)
{
//Lift arm, but not all the way pwm.setPWM(3,0,i);
for (i=650; i>460;i--) delay(2);

21
} pwm.setPWM(0,0,i);
//Turn off red lights delay(6);
pwm.setPWM(10,0,0); }
pwm.setPWM(5,0,0); delay(100);
//Close arm
for (i=360; i<650;i++) //Lift arm
{ for (i=armd; i>armu;i--)
pwm.setPWM(3,0,i); {
delay(2); pwm.setPWM(3,0,i);
} delay(2);
}
//Close box delay(100);
for (i=lidu ; i<540; i++) //Close arm
{ for (i=armu; i<armd;i++)
pwm.setPWM(0,0,i); {
} pwm.setPWM(3,0,i);
} }
delay(100);
//Close box
//Sequence spins box and hit switch for (i=lidu ; i<lidd; i++)
void Sequence4(){ {
int i; pwm.setPWM(0,0,i);
//Spins the box delay(4);
motors.setLeftSpeed(-200); }
motors.setRightSpeed(200); delay(100);
delay(800); //Turn off Motors
motors.setLeftSpeed(0);
//Opens the box motors.setRightSpeed(0);
for (i=lidd; i>lidu; i--) }
{

22
//Box peeks up and down //Sets colors and peeks the box up/down
void Sequence5(){ for (int n=0; n<2; n++)
int i; {
delay(300); delay(1500);
//Open box if (n==0)
for (i=lidd; i>250; i--) {
{ pwm.setPWM(9,0,0);
pwm.setPWM(0,0,i); pwm.setPWM(6,0,0);
} pwm.setPWM(11,0,4000);
delay(100); pwm.setPWM(7,0,4000);
pwm.setPWM(10,0,0);
//Lift arm pwm.setPWM(5,0,0);
for (i=armd; i>420;i--) }
{ if (n==1)
pwm.setPWM(3,0,i); {
} pwm.setPWM(9,0,4000);
pwm.setPWM(6,0,4000);
//Close arm pwm.setPWM(11,0,0);
for (i=420; i<armd;i++) pwm.setPWM(7,0,0);
{ pwm.setPWM(10,0,0);
pwm.setPWM(3,0,i); pwm.setPWM(5,0,0);
delay(10); }
}
//Set motors in motion while lid lifts
//Close box motors.setLeftSpeed(-300);
for (i=250 ; i<lidd; i++) motors.setRightSpeed(-300);
{ //Peek box up
pwm.setPWM(0,0,i); for (i=lidd; i>300; i--)
delay(10); {
} pwm.setPWM(0,0,i);

23
}
delay (200); //Lift arm
motors.setLeftSpeed(0); for (i=armd; i>380;i--)
motors.setRightSpeed(0); {
delay(700); pwm.setPWM(3,0,i);
motors.setLeftSpeed(100); }
motors.setRightSpeed(100); //Hold and move forward
//Peek box down motors.setLeftSpeed(-100);
for (i=300; i<lidd; i++) motors.setRightSpeed(-100);
{ delay(1000);
pwm.setPWM(0,0,i); motors.setLeftSpeed(0);
delay(10); motors.setRightSpeed(0);
} //Close arm
motors.setLeftSpeed(0); for (i=380; i<armd;i++)
motors.setRightSpeed(0); {
} pwm.setPWM(3,0,i);
//Set red lights to on delay(10);
pwm.setPWM(9,0,0); }
pwm.setPWM(6,0,0);
pwm.setPWM(11,0,0); //Close box
pwm.setPWM(7,0,0); for (i=lidu ; i<lidd; i++)
pwm.setPWM(10,0,4000); {
pwm.setPWM(5,0,4000); pwm.setPWM(0,0,i);
delay(5);
//Open box }
for (i=lidd; i>lidu; i--)
{ //Turns off lights
pwm.setPWM(0,0,i); pwm.setPWM(9,0,0);
} pwm.setPWM(6,0,0);
delay(10); pwm.setPWM(11,0,0);

24
pwm.setPWM(7,0,0); }
pwm.setPWM(10,0,0);
pwm.setPWM(5,0,0); //Box uses sensors to move towards or away
from the operator
}
void Sequence7(){
//Simultaneous movement, and the trigger arm
vibrates //Define step variable
void Sequence6(){ int i;
pwm.setPWM(0,0,220);
delay(100); //Open box
pwm.setPWM(3,0,420); //pwm.setPWM(0,lidd,lidu);
delay(400); pwm.setPWM(0,0,lidd);
pwm.setPWM(3,0,450); delay(300);
delay(200); for (i=lidd; i>lidu; i--)
pwm.setPWM(3,0,420); {
delay(200); pwm.setPWM(0,0,i);
pwm.setPWM(3,0,490); delay(5);
delay(200); }
pwm.setPWM(3,0,420); delay(500);
delay(200);
pwm.setPWM(3,0,450); //Record sensor values to create proportional
control
delay(200);
for(int count=0; count<=100; count++)
pwm.setPWM(3,0,420);
{
delay(200);
float LpsensorValue = analogRead(A1);
pwm.setPWM(3,0,450); //Read left sensor value on pin A0
delay(200); float RpsensorValue = analogRead(A0);
pwm.setPWM(3,0,360); //Read right sensor value on pin A1

delay(200); float TsensorValue;

pwm.setPWM(3,0,600); //Calculate differences in readings

delay(200); if (LpsensorValue>= RpsensorValue) //Uses


the highest reading of the two sensors
pwm.setPWM(0,0,500);
{TsensorValue=RpsensorValue;

25
} if (diff<=0)
else { pwm.setPWM(9,0,0);
{TsensorValue<LpsensorValue; pwm.setPWM(6,0,0);
} pwm.setPWM(11,0,4000);
float Tvolt = TsensorValue/1024*5; //Finds pwm.setPWM(7,0,4000);
voltage of sensor
}
float Tpow = pow(Tvolt,-1.12);
//Intermediate calculation else{

float Tdist = Tpow*28.4; //Finds pwm.setPWM(11,0,0);


distance based on derived equation pwm.setPWM(7,0,0);
diff = (Tdist-50); // Sets equilibrium pwm.setPWM(9,0,4000);
position as 50cm
pwm.setPWM(6,0,4000);
kp=5; //Define proportional
value }

motspeed=diff*kp; //Set motorspeed


//Set motor speed for both sides

if (Tdist>=200){
motors.setLeftSpeed(-200); delay(50);

motors.setRightSpeed(-200); }

} //Stop motors

if (Tdist>=100){ motors.setLeftSpeed(0);

motors.setLeftSpeed(-100); motors.setRightSpeed(0);

motors.setRightSpeed(-100);
} //Lift arm

if (Tdist<=100) for (i=armd; i>armu;i--)

{ {

motspeed=diff*kp; pwm.setPWM(3,0,i);

motors.setLeftSpeed(-motspeed); }

motors.setRightSpeed(-motspeed);
} //Close arm

//Set colors for LED when moving in certain for (i=armu; i<armd;i++)
direction {

26
pwm.setPWM(3,0,i); //motors.setRightSpeed(100); //sets speed
of right motor to defined value
delay(10);
}
buzzer.playNote(note[currentIdx],
duration[currentIdx], 15);
//Close box currentIdx++;
for (i=lidu ; i<lidd; i++) }
{
pwm.setPWM(0,0,i); // Insert some other useful code here...
} // the melody will play normally while the rest
} of your code executes
// as long as it executes quickly enough to
keep from inserting delays
void Sequence8(){
// between the notes.
motors.setLeftSpeed(80);
motors.setRightSpeed(80);
// For example, let the user pushbutton
pwm.setPWM(0,0, 200); function as a stop/reset melody button
delay (300); if (button.isPressed())
int num=0; {
buzzer.stopPlaying(); // silence the buzzer
//Plays the notes if (currentIdx < CANT_TOUCH)
while(num<1800) currentIdx = CANT_TOUCH; // terminate
{ the melody

Serial.println(ZUMO_BUTTON); else

// if we haven't finished playing the song and currentIdx = 0; // restart the


melody
// the buzzer is ready for the next note, play
the next note button.waitForRelease(); // wait here for the
button to be released
if (currentIdx < CANT_TOUCH &&
!buzzer.isPlaying()) }

{ num = num +1;

// play note at max volume }

//motors.setLeftSpeed(100); //sets speed of pwm.setPWM(5,0,4000);


left motor to defined value pwm.setPWM(6,0,4000);

27
pwm.setPWM(7,0,4000); {
pwm.setPWM(9,0,4000); pwm.setPWM(0,0,i);
pwm.setPWM(10,0,4000); delay(10);
pwm.setPWM(11,0,4000); }
delay(300); delay(1000);
pwm.setPWM(3,0,340);
delay(500); //Raise flag
motors.setLeftSpeed(-100); pwm.setPWM(15,0,flagu+50);
motors.setRightSpeed(-100); //Wave flag
pwm.setPWM(3,0,600); for (count=0; count<3; count++)
delay(3000); {
pwm.setPWM(0,0,500); for (i=flagu+50; i>300 ;i--)
pwm.setPWM(5,0,0); {
pwm.setPWM(6,0,0); pwm.setPWM(15,0,i);
pwm.setPWM(7,0,0); delay(7);
pwm.setPWM(9,0,0); }
pwm.setPWM(10,0,0); delay(100);
pwm.setPWM(11,0,0); for (i=300; i<flagu+50;i++)
motors.setLeftSpeed(0); {
motors.setRightSpeed(0); pwm.setPWM(15,0,i);
} delay(7);
}
//Flag wave delay(100);
void Sequence9(){ }
//Define count variables //Lower flag
int i; for (i=flagu+50; i>flagd; i--)
int count = 0; {
pwm.setPWM(15,0,i);
//Open box delay(1);
for (i=lidd; i>lidu; i--) }

28
//Lift arm partially
for (i=armd; i>460;i--)
{
pwm.setPWM(3,0,i);
delay(15);
}
//Fully raise arm
for (i=460; i>armu;i--)
{
pwm.setPWM(3,0,i);
delay(2);
}
//Close arm
for (i=armu; i<armd;i++)
{
pwm.setPWM(3,0,i);
delay(2);
}
//close box
for (i=lidu ; i<lidd; i++)
{
pwm.setPWM(0,0,i);
delay(2);
}
}

29
Final Report
Linear Course Adjusting Hovercraft
Brushless Motoring and Arduino Processor

 Nam Pham
 Ben Gorenc

December 12th, 2013


ME 445
Final Report: Remote Control Hovercraft

EXECUTIVE SUMMARY
Our team has developed a unique design of a remote control hovercraft. The
proposed designed only required one motor powerful enough to control both the
throttle and steering of the hovercraft in addition to providing enough power for lift.
Such a unique design essentially prevents the need to have two separate motors to
control the throttle and lift. With the usage of a digital proportional device, the
controller will be able to command the hovercraft to travel as desired.
In addition, the hovercraft also has an implementation of an additional feature that
enable the hovercraft to auto adjust its linear course of travel. If no steering signal
is received by the arduino, the hovercraft will automatically adjust itself to travel in
a straight path to mitigate drift.

1
ME 445
Final Report: Remote Control Hovercraft

ACKNOWLEDGEMENT
Our team would like to thank Dr. Sommer for providing us with the necessary
resources outside of class and in lecture to complete our project. We would also like
to thank Michael Robinson for helping us with our laboratories and project. His
guidance and resources were necessary in troubleshooting and completing the
hovercraft. Without such guidance from both, none of this would be made possible
and we would have never realized how important mechatronics are to the world.

2
ME 445
Final Report: Remote Control Hovercraft

TABLE OF CONTENTS

EXECUTIVE SUMMARY ....................................................................................................................... 1


ACKNOWLEDGEMENT ........................................................................................................................ 2
1. INTRODUCTION ............................................................................................................................. 4
2. MATERIALS AND ELECTRICAL COMPONENTS ................................................................ 5
2.1 Materials .......................................................................................................................................... 5
2.2 Electrical Components .................................................................................................................. 6
2.3 Cost.................................................................................................................................................... 7
3. PROTOTYPE DESIGN.................................................................................................................... 9
3.1 How it Works .................................................................................................................................. 9
3.2 Prototype Result ............................................................................................................................. 9
4. ELECTRICAL DESIGN ................................................................................................................ 12
4.1 Circuitry Application ................................................................................................................... 12
4.2 Result .............................................................................................................................................. 14
5. CODING AND ITS APPLICATION ........................................................................................... 14
5.1 Servo ............................................................................................................................................... 14
5.2 Gyroscope ....................................................................................................................................... 15
6. CONCLUSION ................................................................................................................................ 15
APPENDIX A: SOURCE CODE .......................................................................................................... 16
APPENDIX B: CIRCUIT SCHEMATIC ............................................................................................ 19

3
ME 445
Final Report: Remote Control Hovercraft

1. INTRODUCTION
The intention of the project is to build a remote control hovercraft that has the
capability of traveling across almost any surface. The basic idea of a hovercraft is its
ability to significantly reduce friction by generating a cushion of air between the
craft and a surface.
Normally, the design of a hovercraft consists of two motors that provide the
necessary power and lifting. One motor would be focusing on the maneuvering and
acceleration of the hovercraft, while the other motor would focus on providing
enough air to inflate the skirt and produce lift. Instead of using the standard two
motor design, our team opted to use only one. This means that the motor needs to
have enough power to generate lift and accelerate the hovercraft. A stepping motor
would not provide the necessary power that the team needed. Thus, using a
brushless motor to duct the air flow between thrust, lift, and skirt inflation is the
most sensible option. The final design of the hovercraft is shown below in Figure 1.

Figure 1. Final Design Prototype

4
ME 445
Final Report: Remote Control Hovercraft

The initial design and construction objectives in which our team set to complete
were as follow:
1. Functional one motor design that is as effective as possible
2. Produce enough power to lift and accelerate the hovercraft
3. Effectively using the ESC, Wireless Receiver, and RC controller to
communicate with the hovercraft
Throughout the course of the designing, constructing, and testing the prototype, our
team set and met additional objectives:
1. Usage of a gyroscope to aid in the automatic straightness maneuvering of the
hovercraft while traveling in a straight path
a. Find the best location for the installation of the gyroscope
b. Reduce vibration transmitted to the gyroscope to get accurate readings
2. Usage of Arduino to effectively control the steering of the servo
There are two main categories existing within the system. The physical category
which involves components such as the materials used for construction of the body
and the physical form of the circuit in the hovercraft. The other category within the
system is the electrical process which contains the communication between the
coded Arduino processor and the physical electrical components.

2. MATERIALS AND ELECTRICAL COMPONENTS

2.1 Materials
The materials that are required to complete the design and construction of the
hovercraft are as follow:
1. Finishing Plywood – Use to create the majority of the main structure of the
hovercraft due to its lightweight/sturdy feature.
2. Polystyrene Styrofoam Board – Shroud cover for the fan of the motor. This
helps to focus the air flow which increases the efficiency of the power used to
lift and propel the vehicle.
3. White Cardboard Paper – Lightweight material used for rudders
4. Parachute Nylon Fabric – Light, flexible, and tough fabric used for the skirt
5. Heavy Duty Tape – For fixture and tie down components
6. Seven Blade Computer Fan 120mm – Generate a more evenly distributed air
airflow at a higher efficiency than traditional props.
7. Epoxy Glue – For fixture of hovercraft structure

5
ME 445
Final Report: Remote Control Hovercraft

2.2 Electrical Components


The electrical components are listed as follow:
1. Futaba Digital Proportional Controller – Responsible for powering and
steering commands
2. Swift 20A ESC – Electronic speed control helps to vary the motor’s speed
3. Futaba R168DF Wireless Receiver – Communicate between controller and
motoring from servo and brushless motor
4. Arduino Uno
5. LISY 300AL 3-Axis Gyroscope – Measure angular rate
6. Traxxas 2018 Servo – In charge of steering functionality
7. Tenergy NiMH 9.6V 2000mAh Battery – Provide power for brushless motor
8. Futaba NR-4QB 4.8VDC 600mAh Battery – Provide power for the Arduino
9. 330 uF Capacitor – Noise filtering purposes
10. 1k Ohms Resistor – Noise filter purposes
11. Brushless Motor – Use to produce air for throttling and lift
The electrical components are shown in Figure 2.

Figure 2. A list of component identifications

6
ME 445
Final Report: Remote Control Hovercraft

The physical circuitry of the hovercraft on a mini breadboard is shown below in


Figure 3. The connection and wiring are pinched in such a way to minimize the
transfer of vibrations through the hovercraft. The overall circuitry is laying on top
of a piece of Styrofoam which serves as a lightweight mount.

Figure 3. A view of the circuitry with completed and assembled wiring and connection

Every electrical component is tightly packed to maximize space efficiency without


affecting the performance of the hovercraft while in operation. Components are laid
as low as possible to prevent any air blockage at the front of the fan intake.

2.3 Cost
Many of the materials and electronic components were found within the lab.
Therefore, the actually cost is a lot less than what is presented in the bill of
materials as seen from Table 1. Nevertheless, the estimated costs for these
components are shown to give a rough estimate of the overall cost to build this
particular prototype if one were have to buy the majority of these materials and
components.

7
ME 445
Final Report: Remote Control Hovercraft

Some items are discontinued, therefore, the prices are not presented. In this
particular case, the LISY 300AL 3-Axis Gyroscope has been discontinued and the
price could not be estimated. This is the same for the digital proportional controller.

Bill of Materials:
Material
Item(s) Quantity Vendor Price(s)
Balsa Hard Wood Sheet (1/4") 1 Lowes $ 14.99
Lightweight Polystyrene Foam Insulation 1 McMaster-Carr $ 18.11
Recycled Nylon Tarp 1 -- $ -
Recycled White Cardboard Paper 1 -- $ -
Heavy Duty Tape 1 -- $ -
Recycled Computer Fan 120mm 1 -- $ -
Wood Glue 1 -- $ -

Electronic Components
Item(s) Quantity Vendor Price(s)
Futaba Digital Proportional Controller 1 -- $ -
Swift 20A ESC 1 Ebay $ 16.73
Futaba R168DF Wireless Receiver 1 Ebay $ 29.99
Arduino Uno 1 Spark Fun $ 29.95
Brushless Motor 1 -- $ -
LISY 300AL 3-Axis Gyroscope 1 -- $ -
Traxxas 2018 Servo 1 Active Powersport $ 15.17
Tenergy NiMH 9.6V 2000mAh Battery 1 All-Battery $ 14.99
Futaba NR-4QB 4.8VDC 600mAh Battery 1 Tower Hobbies $ 17.99
330 uF Capacitor 1 Amazon $ 0.36
1k Ohms Resistor 1 Amazon $ 0.05
Total: $ 176.33
Table 1. An estimated bill of materials. The prices may vary depending on the chosen vendors

As presented in the bill of materials, the total price comes out to be roughly $175.
This total may vary depending on the chosen vendors. According to our
interpretation, the gyroscope would be the one of the most expensive parts to buy.
Unfortunately, we could not find a reasonable vendor that gives a reasonable price
estimate. In addition, this particular gyroscope model has been discontinued as
mentioned above.
Our team was fortunate enough to find a spare brushless motor in the lab. So the
total cost is drastically reduced. Without any type of brand/model number, the
estimated price of the brushless motor could not be predicted.

8
ME 445
Final Report: Remote Control Hovercraft

3. PROTOTYPE DESIGN

3.1 How it Works

Figure 4. Air flow schematic and mechanical damping system. [1] represents the airflow from front
to rudders. [2] represents the airflow distribution from the front to the tarp and the air hole at the
bottom. [3] shows the mechanical damping system.

Ideally the proposed design consists of having one brushless motor to both throttle
and lift the hovercraft. The air flow schematic shown above in Figure 4 shows the
theory behind the design. Air intake from the top half of the motor [1] will be
utilized to produce thrust. The attached rudders on the back allow the hovercraft to
angle the airflow and rotate the hovercraft.
The air intake from the bottom half of the motor [2] will be used to inflate the skirt,
as well as providing lift through a vent in the bottom of the hovercraft. Both
functions work together to produce the necessary lift for the hovercraft.

3.2 Prototype Result


The initial design consists of a basic structure of the hovercraft with the motor and
fan blade attached. A temporary tarp made out of a stiff plastic bag was used as a
skirt. This is shown in Figure 5.
Such a basic design, however, is not efficient. The hovercraft would move but would
not provide any actual lift for the hovercraft. This is due to the bare-bone design of
the hovercraft. For the motor, there was nothing to aid the air through the back and
to the skirt at the bottom. In addition, the attaching method of the plastic tarp to
the surrounding structure was done poorly. There were leaks due to holes in the
bag. The material of the skirt is also a lot stiffer and harder to inflate which

9
ME 445
Final Report: Remote Control Hovercraft

prevented the hovercraft from generating any actual lift. In addition, the skirt was
not large enough, which limited the inflation size of the skirt. This hindered the
creation of an air pocket under the hovercraft.

Figure 5. The initial hovercraft bare-bone design

Through further improvements, the design became more efficient and came close to
completing our initial objectives. This can be seen from Figure 6.
As one can see from the provided figure, our team has made several drastic
additions to the initial design. The motor area now has a shroud, which acts like a
funnel, to focus air intake making the movement of air much more efficient.
The rudder system is installed behind the motor. As a result, the hovercraft is now
able to turn left and right in response to the remote controller. The servo is fixed
behind the motor, and serves to rotate the two rudders. This system is connect to
both rudders, and as the servo rotate to one side, the connected arm, while moving
with the servo rotation, rotates both rudders at the same time and at the same rate.
Using two rudders as oppose to one will give us better control over the steering of
the hovercraft as it will displace more air to the desireable side.

10
ME 445
Final Report: Remote Control Hovercraft

Figure 6. The improved prototype

The improved prototype design has an additional implementation of a gyroscope.


The purpose of the integration of the gyroscope is to automatically adjust the course
of the hovercraft when it diverges from a straight path. Such implementation came
about after the discovery of the noticeable drifting while the hovercraft traveled in a
straight path while receiving no steering command from the remote controller.
A big problem arises with the placement of the gyroscope. Due to the high RPM of
the brushless motor, the motor produces a large vibration noise and transfers it
throughout the structure. As a result of this, the gyroscope is unable to give
accurate data. Thus our team had to figure out an alternative solution to reduce the
vibration noise being translated to the gyroscope.
The solution our team came up with was to create a mechanical isolator for the
gyro. The isolator system, reduces the vibrational noise translated to the gyroscope.
The mechanical isolator design is shown in Figure 7 below.

11
ME 445
Final Report: Remote Control Hovercraft

Figure 7. The mechanical isolator system design

The silicon tube attachments shown from the figure are the main components
within of the isolation system. The vibration is absorbed in the four silicon tubes
which are soft and flexible. The vibration is transferred to the tubes from the
bottom base, and is drastically reduced by the time it reaches the gyro. Therefore,
the noise in the gyroscope is greatly reduced.
Aside from the gyroscope implementation, the skirt was also replaced to a more
desirable material. Instead of hard plastic, the new skirt is made out of parachute
nylon material. This alternative is much lighter, less stiff, and stronger. In addition,
our team was able to make the skirt bigger without having more material to work
with. Consequently, these changes allow more air to build up within the inflated
skirt and increase the hovercraft lifting functionality. The method the team used to
attach the skirt to the structure has also been improved, drastically reducing and
preventing air leakage.

4. ELECTRICAL DESIGN

4.1 Circuitry Application


Our initial design objective was to have the ESC, wireless receiver, and proportional
control to effectively communicate with each other. This is shown in Figure 5, the
bare-bone design. The communication between these devices was functional.

12
ME 445
Final Report: Remote Control Hovercraft

Unfortunately, there existed a lot of noise. Such noise caused the motor to operate
on its own without the user’s input. A low pass filter was implemented in attempt to
eliminate some noise. We were unable to completely eliminate the noise in our
circuit but did manage to eliminate enough of it to make our circuit functional.
As we improve the circuit design, our team were able to narrow down the exact
objectives in which we want to accomplish. The following flow-chart (Figure 8)
represents the command hierarchy of the communication between the electronic
components. The communication starts when we connect the batteries and turn on
the proportional controller. This is when the signal is on.

Signal (Off) Signal (On)

Power Arduino

Source Code

Servo Gyroscope

Record
Rotate L/R
Coordinates

Motor

Throttle

Figure 8. The communication hierarchy flow-chart between the electronic devices

The communication structure is split into three categories. One is the


communication between the controller and servo, while the other focus on the
signals between the controller and gyroscope. Lastly, there is also the direct
signaling process coming from the controller to the motor.

13
ME 445
Final Report: Remote Control Hovercraft

4.2 Result
The finalize circuitry design consists of using two batteries to power the devices.
The first battery, which is the Tenergy 9.6V 2000mAh NiMH, is used as the main
power source for the brushless motor. The reason our team decided to go with such
method is due to the large power consumption of the brushless motor. The other
smaller capacity battery (Futaba 4.8VDC 600mAh) is the main power source for the
Arduino. The servo and the gyroscope pull the necessary power from this smaller
capacity battery.
While in operation, the servo tended to twitch to extreme positions sometime. This
is mostly due to unaccountable noises from the surrounding. To tackle such
problem, our team decided to add in a capacitor (330 uF) parallel to that of the
source and ground to filter out some of the noise. As a result of such filtering, the
noises are reduced drastically and the communication between the controller and
the servo are much more responsive.
For further detail on the circuit of this project, please refer to Appendix B.

5. CODING AND ITS APPLICATION


Our code consists of two main functions. The first part involves the servo control
communication from the controller to the servo device. The second involves the
gyroscope readings and a function to auto adjust the rudders so that the hovercraft
will regain its straight travel path. The code included a function that would take
averages of the gyro data to decrease the noise from inaccurate readings. The source
code can be found in Appendix A.

5.1 Servo
With using the built-in servo library, we were able to successfully communicate the
proportional controller with the installed servo on the hovercraft. We assigned a pin
to read the pulse signal from the controller that is then sent to the wireless receiver
(input), and declared a variable for the pin that is connected to the signal line
(output) of the servo. The servo pulse-in signal ranges between values of 1100 to
1900. We decided to increase the range from 1000 to 2000 to cover all potential
values in the case of noise affecting the source code commands.
Since our pulse-in signal ranges between values in thousands, we converted the
signal to values in which is easier to understand. In this case, the converted values
are in degrees. “Serial.println()” command was used during the process of testing
the servo, but later taken out after the calibration is completed.
The code also consist of a function with several “if” statements to satisfy the
mechanical steering. One goal of the steering is to have the rudders reset to the

14
ME 445
Final Report: Remote Control Hovercraft

neutral (center) position if no command is sent. The other goal is to have the
rudders turn to the desired position, limiting its ability to over-steer and damage
the fragile rudders.
The communication between the controller and the servo produced a lot of noise
even while in neutral position and no command is sent. The noises range between
85-95 degrees. As a result of this, the servo twitched back and forth between the
mentioned values. To combat this noise, we put in “else-if” statements to set the
servo at the neutral position if the readings fall between the range of these two
values. We increased the high noise value to 97 and low to 83 to prevent possible
signal spiking from affecting the desired modifications.

5.2 Gyroscope
The gyro was simple to implement because we only desired the angular rate in the
horizontal plane. All that was required as a 3.3V input, a ground, and an out signal
from the gyro. The signal from the gyro was then converted into an angle that was
used to set the parameters in our code that would force the hovercraft to self-align
when attempting to move straight.
There was initially a large amount of noise in the gyro data from the vibration of
the brushless motor. This was greatly reduced with the addition of the mechanical
isolator and made the data readable from the gyro.
However, an issue that came about with the gyro was that the gyro gave more
sensitive readings in the CCW direction by a factor of roughly 3. This made it
incredibly difficult to write a code to effectively make the hovercraft move in a
straight line. This is because the gyro would never return to a zero position after
drifting off course. Unfortunately, this discovery was made too late in the course of
the project to order a new gyro. That being said the hovercraft is capable of moving
relatively straight without being steered for roughly 10-15 feet.

6. CONCLUSION
Overall, we were both very happy with the outcome of our project. We tackled a
unique project, overcame many unforeseen obstacles, and gained incredible
knowledge in the field of mechatronics.
In addition, we have learned how to read through data sheets, interface and
program an Arduino with a multitude of components. This knowledge is absolutely
going to benefit us in the future through many facets of our life. In fact, both my
partner and I are going to purchase Arduinos so that we can continue to explore the
field of mechatronics. We both are pleased with the outcome of our project and
thoroughly enjoyed this class and thankful that we took it.

15
ME 445
Final Report: Remote Control Hovercraft

APPENDIX A: SOURCE CODE

// servo
int pulse_servo = 8; // set servo pin for wireless transmitter
int val_servo;
int PWMval_servo;
#include <Servo.h> // calls servo library
Servo servoMain;

// gyro
int gyroPin = A0; //Gyro is connected to analog pin A0
float gyroVoltage = 3.3; //Gyro is running at 3.3V
float gyroZeroVoltage = 1.0; //Gyro is zeroed at 1.0V
float gyroSensitivity = .0033; //3.3mV/deg/sec
float rotationThresholdHigh = 40; //Minimum deg/sec to keep track of - helps with gyro drifting
float rotationThresholdLow = 5; //Minimum deg/sec to keep track of - helps with gyro drifting

float currentAngle = 0; //Keep track of our current angle


float avgAngle = 0; //Used to take average to mitigate noise

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

servoMain.attach(10); // pin servo is attached to


pinMode(pulse_servo, INPUT); // pin wireless transmitter is connected to for servo
pinMode(10, OUTPUT);
digitalWrite(10,HIGH);
}

void loop()
{
val_servo = pulseIn(pulse_servo, HIGH); // used to send wireless signal to servo
PWMval_servo = map(val_servo, 1000, 2000, 0, 179); // scale it to use it with the servo (value between
0 and 180)
steer(PWMval_servo); // calls steer function and sends servo value to it
}

void steer(int b)
{ // steers servo based on value passed to it

for(int j=1; j<100;j++)


{ // used to steer servo using controller
if (b > 97 && b < 179 )
{ // Turn the servo CCW
servoMain.write(PWMval_servo);

16
ME 445
Final Report: Remote Control Hovercraft

currentAngle = 0;
}
else if (b > 152 && b < 179)
{ // limit maximum servo angle CCW
servoMain.write(152);
currentAngle = 0;
}
else if (b < 42 && b > 0)
{ // limit maximum servo angle CW
servoMain.write(42);
currentAngle = 0;
}
else if (b < 83 && b > 0)
{ // Turn the servo steering CW
servoMain.write(PWMval_servo);
currentAngle = 0;
}
else if (b > 83 && b < 97) // moving straight no direction sent from controller
{ // align function used to mitigate hovercraft drift
align();
}
else
{
}

void align()
{
for(int i=0;i<10;i++) // used to find average gyro reading
{
//This line converts the 0-1023 signal to 0-5V
float gyroRate = (analogRead(gyroPin) * gyroVoltage) / 1023;

//This line finds the voltage offset from sitting still


gyroRate -= gyroZeroVoltage;

//This line divides the voltage we found by the gyro's sensitivity


gyroRate /= gyroSensitivity;

//Ignore the gyro if our angular velocity does not meet our threshold
if (gyroRate >= rotationThresholdHigh || gyroRate <= -rotationThresholdLow)
{
//This line divides the value by 100 since we are running in a 10ms loop (1000ms/10ms)
gyroRate /= 100;

17
ME 445
Final Report: Remote Control Hovercraft

avgAngle += gyroRate;
}
currentAngle = currentAngle + avgAngle/10; //adds average angle to current angle
avgAngle=0;
}
//DEBUG prints current angle
Serial.println(currentAngle);

for(int j=1; j<25;j++)


{
if (currentAngle > 15 && currentAngle < 35)
{ // CCW sensitive direction of gyro, limited to dampen sensitivity
for(int i=0;i<10000;i++)
{ // turns servo CCW to negate drift
servoMain.write(152);
}
currentAngle = 0;
}

else if (currentAngle < -10 && currentAngle > -35)


{ // CW insensitive direction of gyro, limits used to amplify response
for(int i=0;i<10000;i++)
{ // turns servo CW to negate drift
servoMain.write(42);
}
currentAngle = 0;
}

else if (currentAngle > -15 && currentAngle < 15)


{ // keep servo pointed straight
servoMain.write(90); //stop
}

else
{ // reset angle to catch errors
currentAngle = 0;
}
}

18
ME 445
Final Report: Remote Control Hovercraft

APPENDIX B: CIRCUIT SCHEMATIC

19
ME445 F2013

ARDUINO BASED TURRET PLATFORM


FOR USE WITH PAINTBALL MARKERS
For the period of
August 26, 2013 through December 17, 2013

Submitted to
Dr. H.J. Sommer
as a part of
ME 445

by

Kyle Maguire & Garrett Kline

The Pennsylvania State University


Departments of Aerospace Engineering, Electrical Engineering and
Mechanical & Nuclear Engineering
University Park, PA 16802
12/17/2013
ACKNOWLEDGEMENTS

We would like to give special thanks to the following individuals for their support and guidance
throughout this project for without them this project would not have been possible:

 Dr. H.J. Sommer – Professor of Mechanical Engineering, Department Mechanical and


Nuclear Engineering, The Pennsylvania State University, University Park, Pennsylvania

 Michael Robinson – ME 445 Teaching Assistant, Department Mechanical and Nuclear


Engineering, The Pennsylvania State University, University Park, Pennsylvania

 Phil Irwin – Machinist, Department Mechanical and Nuclear Engineering, The


Pennsylvania State University, University Park, Pennsylvania

 George Otto – Visualization Manager for Research Computing & Cyberinfrastructure,


The Pennsylvania State University, University Park, Pennsylvania

 Jack Gundrum – Systems Administrator for Research Computing & Cyberinfrastructure,


The Pennsylvania State University, University Park, Pennsylvania
TABLE OF CONTENTS

EXECUTIVE SUMMARY ............................................................................................................ v


CHAPTER 1. INTRODUCTION ................................................................................................... 1
1.1. Historical Background...................................................................................................... 1
1.2. Modern Applications ........................................................................................................ 1
CHAPTER 2. BENCHMARKING ................................................................................................. 2
2.1. Project Sentry Gun ............................................................................................................... 2
2.2. Portal Turret ......................................................................................................................... 2
2.3. Real-Life Sentry Gun ........................................................................................................... 2
CHAPTER 3. Turret Design ........................................................................................................... 3
3.1. Performance Design Objectives ........................................................................................... 3
3.1.1. Structural Design Objectives ........................................................................................ 3
3.1.2. Control Design Objectives ............................................................................................ 3
CHAPTER 4. Materials and Construction ...................................................................................... 5
4.1. Structural Materials .............................................................................................................. 5
4.2. Motor Selection .................................................................................................................... 5
4.3. Battery Selection .................................................................................................................. 6
4.4. Electronics Selection ............................................................................................................ 7
CHAPTER 6. RISK AND SAFETY .............................................................................................. 8
REFERENCES ............................................................................................................................... 9
APPENDIX A. Arduino Code ...................................................................................................... 10
Appendix B. 2D Technical Part Drawings ................................................................................... 23
FINAL IMAGES .......................................................................................................................... 25
EXECUTIVE SUMMARY
The paintball turret assembly was constructed with two basic design goals in mind: mount a
paintball gun to the frame such that it could be rotated on a 2 axis system and fired using an
externally integrated Arduino Mega.

The turrets main components are two servo gearboxes mounted in the channels that make up the
turret arms. The horizontal gearbox is attached directly to the turret’s tripod core while the
vertical gearbox is connected to a custom bracket that holds the paintball gun in place. The turret
frame and mount were constructed mainly from machined Aluminum components. The paintball
mounting bracket and tripod core we hand machined from Aluminum stock while the turret arms
and tripod legs were purchased. The power source for the system is a 6 volt sealed lead acid
battery.

The complete design included a control box that would allow the user to perform more advanced
functions. Two toggle switches controlled the power and firing safety, while two standard push
button switches control the electronic firing mechanism and change the firing mode. In order to
fire the gun remotely an electronic switch was inserted into the gun handle and connected to the
Arduino system. Initially the code allowed for three firing modes: semi-automatic, three shot
burst and fully automatic. However, due to coding errors only the semi-automatic and three shot
burst mode could be demonstrated. The control box also supports an LCD feedback screen that
shows the user which firing method and control mode they had switched to, as well as the
number of shots they had fired. This system functioned properly, although the number of shots
fired was not always accurately displayed. The control box also contained a LED ring that would
track the motion of the motor as it rotated. However, near the end of the constructions process it
was apparent that the LED ring’s Arduino library created a non-negligible amount of
interference in the motors, causing them to oscillate.

The motion of this turret was intended to by controlled through three different methods: Joystick
input, Wii Nunchuck motion control and External Motion Tracking Software. The joystick
contained two potentiometers that sent control signals to the Arduino; these controls were then
coded and sent to the motors. This system provided effective control for the system and allowed
the turret to rotate 180 degrees horizontally and 120 degrees vertically. Due to time constraints,
it was not possible to incorporate the other two motion control systems. However, the control
box was built with the necessary components and the control methods would not be difficult to
add to the system given more time.

Page v
CHAPTER 1. INTRODUCTION

1.1. Historical Background


Gun turrets date back to the civil war aboard the USS Monitor which was the first successful
application of a gun turret aboard a naval vessel. Over the last 160 years the naval turret has
evolved significantly. One of the most notable applications of a naval turret was for use with
medium to large bore cannon aboard battleships from the early to mid-1900s. With the
decommissioning of all four Iowa class battleships in the 1990s the use of naval turrets has
become limited to cannons in the 3 to 5 inch range whereas the Iowa class battleships utilized
turrets of various calibers up to 16”.

The first aircraft to utilize the turret concept was the British Royal Airforce’s Boulton & Paul
Overstrand in 1933. Since then, aircraft turret designs, like those used on naval vessels, have
been modified and improved allowing for the use of larger bore cannon and more effective
control systems. Last but not least, the ground combat vehicle put the turret concept into use in
1914 on the Rolls-Royce armored car. The first tracked vehicle to use a turret was the French
Renault FT light tank which debuted in the latter part of World War I.

1.2. Modern Applications


Throughout history, the application of turret platforms has been used on all types of land, sea,
and air vehicle with an emphasis in weapons systems. As a result, applications for turrets
beyond the scope of military application are not as commonplace. That being said, the concept
of automated turret platform is not limited to military or weapons applications. In non-military
applications, the term “pan-tilt” platform is often utilized rather than turret. This terminology is
often associated with remote camera systems. These pan-tilt platforms can be found for a wide
variety of applications for small hobby projects or as professional grade studio camera mounts.

The original inspiration for this project was the result of a variety of interests and hobbies. Most
importantly, an interest in both mechatronics, paintball and modern next generation military
technology was the deciding factor in the pursuit of this project. The idea for a turret capable of
moving on two axis with user controlled inputs came from the AH-64 Apache’s Hughes M320
Chain Gun targeting system. That control system for the M320 is integrated into the gunner’s
flight helmet to provide real time targeting information allowing the gunner to simply turn his
head and look at his target to aim the cannon.

Sporting grade turrets such as those used to aim and shoot a paintball marker differ from
traditional military turrets in that they are intended for non-lethal applications and do not enclose
or protect the paintball marker or user. These turrets are generally non-commercialized and very
unique to the person who designed/built the turret. As a result, the availability of a turret
platform that can be easily modified for additional functionality is severely limited.

Page 1
CHAPTER 2. BENCHMARKING

During our initial research we found several paintball turret designs available. Of the designs
found, only one was a commercially available product. Most of the designs found were just the
final product with some discussion on performance and construction. Of the designs found, the
following three provided enough information to give some insight into their construction and
performance. These three designs represent some of the more advanced concepts which utilize
various forms of targeting systems via a video camera and computer interface.

2.1. Project Sentry Gun


This turret utilizes an arduino based control system in combination with a computer based
program to track targets and anticipate target path. The computer based software has various
features including but not limited to interfacing with a joystick, restrict fire zones and anticipate
target path. The programs used for this turret are free and open for public use. For motion
control, this design utilizes two Hitec HS-805BB servos with 343 oz-in of torque. The design for
this turret allows the use of either a paintball or airsoft gun and has a total weight of
approximately 60 lbs.

2.2. Portal Turret


This project was part of a graduate level Mechatronics course at The
Pennsylvania State University in the spring of 2012. This version of
the turret design used two USB desktop Nerf-style turrets. The
motion of each turret was controlled via two small pan-tilt
platforms. The turrets were aimed using an arduino microcontroller
in combination with MATLAB image processing to determine target
location. The turret design is mimics that of the turrets in the video
game “Portal”. Additional features such as laser pointers gave a
visual indications of what the turret was aiming at.

2.3. Real-Life Sentry Gun Figure 1: : Portal Turret

This turret uses two geared servo motors to control the yaw and pitch motions of the gun. Unlike
other designs, the control system for this turret does not utilize an arduino platform. This system
uses a pololu servo controller which receives commands from a computer based motion tracking
software program. A base structure made from iron pipe was utilized to which the yaw direction
servo mounter was fixed. From there the vertical upright holding the pitch direction motor and
paintball gun was attached. Targeting and firing for this turret was controlled using the motion
tracking program developed by RealSentryGun.com. This turret platform is also the only design
found that can be purchased commercially.

Page 2
CHAPTER 3. Turret Design

3.1. Performance Design Objectives


This project will be a cross between both mechanical and microelectronics using an Arduino
Microcontroller. The Arduino will serve as the brains of the system controlling the turret motion
and firing based on user input from various input devices. The final performance of the turret
will be based upon the following core ideas:

 2-axis rotation
 Target acquisition and tracking
 Multiple firing rates
 Simplicity of control input
 System feedback to the user

3.1.1. Structural Design Objectives


The structural portion of the turret was driven by the desire to have 2-axis motion capability, the
ability to easily mount and dismount the paintball gun to allow for fast and easy tear-down and
setup, and finally the ability to dismount and take the paintball gun mobile during play.
Furthermore, due to potential vibrations resulting from firing the paintball gun, the structure
needed to be rigid and capable of holding steady under repetitive firing.

3.1.2. Control Design Objectives


The main control features of this project include a simple security system, an LCD feedback
screen, a push button system that can switch between firing modes, a joy stick input motion
control system with an LED tracking system and a toggle switch that allows transition from
manual control to motion control.

The security system consists of 3 levels: a key, a keypad and an RFID card scanner. The key is
inserted next to the power button and closes the connection between the control box and the
battery when turned. After the key has been inserted, the power button lights up, and the user can
press it activate the control systems. Once the power button has been pressed, the user will be
prompted to enter a 3 digit passcode using the 3x4 membrane keypad. Finally, if the correct
passcode has been entered, the user will be prompted to swipe an RFID entry card over the box.
After the correct card has been scanned the user will be granted access to the turret’s control
systems.

The 2-axis motion of the turret is controlled by the small joystick in the lower left hand corner of
the box. This component has two potentiometers that send analog signals to either the vertical or
horizontal motor. Whenever one of these signals causes motion in the motors, it also affects the
small ring of LED’s in the upper left hand corner of the box. The right half of this ring is
composed of red LED’s that indicate the vertical position of the gun, while the top half is

Page 3
composed of blue LED’s that indicate the horizontal position of the gun. This allows the user to
get some sense of where the gun is pointed if they cannot see it.

The final aspect of the control system is the LCD feedback and the firing control buttons. Once
the system has been activated and the security code has been entered, the screen will indicate
how many shots have been fired, which control mode is being used and which firing mode the
gun is set too. Three buttons on the right side box control the firing code: a red toggle switch, a
white push button and a green push button. The green push button fires the gun, the red toggle
switch acts as the safety, preventing the gun from being fired if it isn’t pressed, and the white
push button switches between the firing modes. The three firing modes for this system are semi-
automatic, three shot burst and fully automatic. However, if the mode button is pressed a fourth
time and the motion control toggle switch is flipped, the turret will shift to motion tracking mode
and both the motion and firing controls will be overwritten by an external program until the
switch is flipped again.

Page 4
CHAPTER 4. Materials and Construction

4.1. Structural Materials


The structure of the turret was made of a 6061 aluminum. Several options including hardwoods,
steels, composites and aluminum alloys were initially laid out as possible materials for the turret
structure. Research into the availability and pricing for each of these materials was conducted
and ultimately aluminum was chosen for its low cost and availability.

Several vendors dealing in aluminum stock and prefabricated


parts were examined and a prefabricated aluminum channel
was found available from ServoCity.com. This aluminum
channel was chosen for its standardized hole patterns
allowing for the joining of multiple pieces using a
standardized bracket, also available from ServoCity.
Furthermore, this channel was compatible with several motor
mounts as well.

In addition the aluminum channel which was used for the


upper portion of the turret, a base structure was also needed.
Again aluminum was chosen here for its low cost as well as Figure 2: Aluminum Channel
its availability. The base components unlike the upper
structure were constructed from fawn aluminum stock. The base structure consisted of a central
mounting hub and three leg to provide balance and elevation (The hub design in shown in
Appendix B).

4.2. Motor Selection


As mentioned previously, the aluminum channel chosen for the upper portion of the turret
structure was compatible with several motors which were also available from ServoCity. Before
committing to any specific motor the exact performance criteria for the motors had to be
determined. Rotational speed requirements for the motors were determined by examining linear
velocities ranging from 1 to 15 mph at ranges of 1 to 80 feet to determine angular velocities for
each situation. Research into average running speeds for individuals resulted in values of 12-15
mph for open terrain. A speed of 10 mph was chosen as the design criteria for targets at 20ft as
during paintball play. Competitors are typically stationary and shooting from some sort of cover,
short sprints form one location to the next do occur but are often more difficult to do in wooded
play as a result of ground foliage and debris. As a result, it is unlikely that the turret will be
required to track objects exceeding 10mph at ranges closer than 20ft. At this speed and distance,

Page 5
it was determined that the turret would need to be capable of rotating 78.13 degrees/s in order to
keep up with a target.

Figure 4: Central Hub with Channel and Motors Mounted. Figure 3: Servo Gearbox

In choosing the motors for our project we decided to determine the feasibility of the channel
mount servo gearboxes available from ServoCity. These motors were available with various
gear ratio options. Furthermore, there were three options available for the motor in the gearbox
assembly giving a wide range of torque options. It was found that a gear ration of 3.8:1 would
be satisfactory as this yielded a rotation rate of 78.9474 degrees/second with a torque value of
680.2 oz-in. This motor was chosen for use with the horizontal direction. A smaller motor also
using a 3.8:1 gear ratio providing 338.2 oz-in. at 6.0 volts was chosen for the vertical motion
control.

4.3. Battery Selection


In order to power the turret we knew we would need a power supply capable of providing a
minimum of 6.0 volts and up to 7 Amperes in order to power the control system and each motor
which could potentially draw up to 3.2 Amps at stall.

Various battery types were investigated ranging from high energy


density LiPo (Lithium Polymer), Nickel-Metal Hydride (NiMH) and
Lead-Acid batteries. Key factors in the battery selection were safety
and cost. The LiPo batteries provided a large Amp-Hour rating in a
small compact form but are known to be dangerous if handled or
used improperly. Furthermore, LiPo batteries are typically more
expensive. NiMH batteries were readily available for 6.0 volt
applications but when compared to Lead-Acid batteries they fell
behind in cost. Ultimately, a sealed Lead-Acid 6.0 volt battery with
a 4.5 amp-hr. rating was chosen. This battery provided ample
voltage to power both of the motors and fell within the supply
voltage range for the arduino microcontroller. Figure 5: : 6.0 Volt Sealed Lead-
Acid Battery

Page 6
4.4. Electronics Selection
LCD Screen

The LCD monitor is a 16 x 2 RGB backlit LCD Serial Monitor


that receives its signal from a basic serial input. This module was
chosen because it was inexpensive and easy to integrate with the
turret’s other systems.
Figure 6: LCD Screen
Membrane Keypad

This 3x4 keypad was chosen because it could be attached easily to the surface of
control box with its adhesive backing and interface with the Arduino through a
seven pin system. It also runs off a library that seemed to cause little interference
with the rest of the system.

Figure 7: Security Keypad

Firing Control Switches

These switches connected easily to the pin system of the Arduino and allowed
for a choice between toggle or push button switches without a difference in cost.
Figure 8: Control Switch

2-Axis Joystick

This component provided some slight difficulty as the joystick returned to its
original position and reset its potentiometer values to zero. However, this problem
was easily solved by some slight adjustment to the Arduino code and the wiring on
the board made it much easier to connect to the Arduino than some of the library
Figure 9: Joystick based controls that were considered.

Page 7
CHAPTER 6. RISK AND SAFETY

The main risks of operating a paintball gun come from unintentional discharge. This can be
prevented by ensuring that the safety is on unless you are preparing to fire. Furthermore,
protective clothing and head gear should always be worn when operating a system like this as
paintballs can cause minor injury to unprotected skin.

Safety concerns for this particular system include possible electrocution from the battery and
accidental contact with the turret as it rotates. In order to prevent electrical injury be sure that the
battery is disconnected before attempting any rewiring of the control box. In most cases, contact
with the turret is also not a large concern. However, when the system is activated the turret will
reset and rotate to its resting position. For this reason, turret users should step at least 10 feet
away from the system when turning on the main power switch.

Page 8
REFERENCES
1. http://www.tippmann.com/SafetyTips.aspx

2. http://mne.psu.edu/brennan/ME545/2012/FinalProjects/Swanson_WorkingPortalTurr
et/M%20E%20597D%20Final%20Project.htm

3. http://www.instructables.com/id/Autonomous-Paintball-Sentry-Gun/

4. http://realsentrygun.com/

5. http://projectsentrygun.rudolphlabs.com/home

6. http://www.instructables.com/id/Autonomous-Paintball-Sentry-Gun/

7. http://en.wikipedia.org/wiki/Gun_turret

8. http://en.wikipedia.org/wiki/Rolls-Royce_Armoured_Car

9. http://en.wikipedia.org/wiki/Battleship

Page 9
APPENDIX A. Arduino Code
//Declare Included Libraries------------------------------------------------
#include <Servo.h>
#include <SoftwareSerial.h>
#include <Keypad.h>
#include <Adafruit_NeoPixel.h>

//Declare Pin Number Variables-----------------------------------------------


const int FiringPin = 50; //Pin number for firing output command
const int FireCmdPin = 18; //Pin number for receiving fire command (Fire
Switch)
const int ModePin = 19; //Pin number for mode switch (White Momentary)
const int YawPin = 46; //Pin number for YawServo
const int PitchPin = 44; //Pin number for PitchServo
const int sensorPin1 = A0; //Joystic UP/Down
const int sensorPin2 = A1; //Joystick LEFT RIGHT
const int SafetyPin = 33; //Pin number for Safety Switch
const int TogglePin = 10; //Pin number for Toggle Switch

const int FireLED = 25; //Pin Number for Fire Switch LED
const int SafetyLED = 31; //Pin number for Safety Switch LED
const int ModeLED = 45; //Pin number for ModeSwitch LED
const int VoltGreenLED = 11; //Pin mumber for Green Battery LED indicator
const int VoltYellowLED = 12; //Pin mumber for Yellow Battery LED indicator
const int VoltRedLED = 13; //Pin mumber for Red Battery LED indicator
const int ToggleLED = 9; //Pin number for Toggle Switch LED

const int PCYaw = 6; //Pin number for computer input yaw


const int PCPitch = 5; //Pin number for computer input pitch
const int PCFire = 4; //Pin number for computer input fire

const int RFIDPin = 38; //Pin mumber for RFID trigger input

//Define Serial use on pins---------------------------------------


SoftwareSerial lcd = SoftwareSerial(0,14); //LCD Pin 14
#define NeopixelPIN 8
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, NeopixelPIN, NEO_GRB +
NEO_KHZ800);

//Declare Objects-----------------------------------------------------------
Servo YawServo; //Create servo object for rotation about z-axis
(Left/Right)
Servo PitchServo; //Create servo object for rotation about y-axis (Up/Down)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, NeopixelPIN, NEO_GRB +
NEO_KHZ800);

Page 10
//Declare Global Variables---------------------------------------------------
int KeyInput; //Accepts Keypad Input for Security loop
int CodeKey1; //1st digit input for passcode
int CodeKey2; //1st digit input for passcode
int CodeKey3; //1st digit input for passcode
float RFID; //Detects change in RFID signal
int sec; //Changes with RFID signal to open second
security loop
int j; //Counting variable (security loop)

int SystemMode; //Variable to track system mode


int Automatic; //Variable for setting control method (0 for
user, 1 for computer)
int ShotQue = 0; //Variable to track how many shots are to be
fired
int NumShots; //Variable to track how many shots have been
fired
float Voltage; //Variable to store battery voltage
long previousMillis = 0; //Variable to store last time
long ModePreviousMillis = 0;//Variable to store last time mode updated
int FireControl;
int ToggleState;

int SafetyState; //Variable to store state of safety switch


int FireState;
int ModeState;

const int PitchLowerBound = 0;


const int PitchUpperBound = 180;
const int YawLowerBound = 0;
const int YawUpperBound = 180;

int HorzPos=90;
int VertPos=90;
int LCDInitial = 1;

//Keypad Setup---------------------------------------------------------------
//Keypad Prep Code (KeyPad Pins: Keypad Face Up --> 2 through 8, right to
left)
const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
{1,2,3},
{4,5,6},
{7,8,9},
{0,0,0}
};
byte rowPins[ROWS] = {34, 32, 30, 28}; //connect to the row pinouts of the
keypad
byte colPins[COLS] = {26, 24, 22}; //connect to the column pinouts of the
keypad

Keypad customKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS,


COLS );

Page 11
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

void setup(){
Serial.begin(9600); //Initilize Serial Communication
lcd.begin(9600); //Initialize LCD Communication
strip.begin(); //Initialize LED Ring
strip.show(); // Initialize all pixels to 'off'

//Define Input Pins


pinMode(FireCmdPin, INPUT_PULLUP); //Firing command input
pinMode(ModePin, INPUT_PULLUP); //Mode switch input
pinMode(RFIDPin, INPUT);
pinMode(SafetyPin, INPUT_PULLUP);
pinMode(TogglePin, INPUT_PULLUP);

//Define Interrupts
attachInterrupt(4, ModeSelection, RISING); //Mode switch

//Define Functional Output Pins


pinMode(FiringPin, OUTPUT); //Electronic firing pin ouput
YawServo.attach(YawPin); //Attatch YawServo to YawPin
PitchServo.attach(PitchPin); //Attatch PitchServo to PitchPin

//Define LED OUTPUT PINS


pinMode(VoltGreenLED, OUTPUT);
pinMode(VoltYellowLED, OUTPUT);
pinMode(VoltRedLED, OUTPUT);
pinMode(FireLED, OUTPUT);
pinMode(SafetyLED, OUTPUT);
pinMode(ModeLED, OUTPUT);
pinMode(ToggleLED, OUTPUT);

//Set Initial Conditions


digitalWrite(ModeLED, HIGH);
digitalWrite(ToggleLED, HIGH);

//Set Initial Parameters


lcd.println("Enter Passcode");
digitalWrite(FiringPin, LOW); //Initilize output state

SystemMode = 1; //Set initial system mode semi-auto


NumShots = 0; //Set NumShots to zero
j = 0;
sec = 0;
CodeKey1 = 0;
CodeKey2 = 0;
CodeKey3 = 0;

//LCD Set up Code (15 characters per line on LCD Screen)

//set the size of the display if it isn't 16x2 (you only have to do this
once)

Page 12
lcd.write(0xFE);
lcd.write(0xD1);
lcd.write(16); // 16 columns
lcd.write(2); // 2 rows
delay(10);
// we suggest putting delays after each command to make sure the data
// is sent and the LCD is updated.

// set the contrast, 200 is a good place to start, adjust as desired


lcd.write(0xFE);
lcd.write(0x50);
lcd.write(200);
delay(10);

// set the brightness - we'll max it (255 is max brightness)


lcd.write(0xFE);
lcd.write(0x99);
lcd.write(255);
delay(10);

// turn off cursors


lcd.write(0xFE);
lcd.write(0x4B);
lcd.write(0xFE);
lcd.write(0x54);

// clear screen
lcd.write(0xFE);
lcd.write(0x58);
delay(10); // we suggest putting delays after each command

// go 'home'
lcd.write(0xFE);
lcd.write(0x48);
delay(10); // we suggest putting delays after each command

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

void loop(){

security_loop(); //Promts Entry of Security code before program starts

if(LCDInitial == 1){
LCD(); //Set initial LCD readout
LCDInitial = 0; //Prevent LCD() from being called in void loop again
}

SafetyState = digitalRead(SafetyPin); //Read in state of the safety switch


FireState = digitalRead(FireCmdPin); //Read in State of the fire switch
//If safety switch is pressed the state is low, allow the fire function and
que to be updated.
//Otherwise, ensure no shots are qued and turn off lights.
if(SafetyState == HIGH){

Page 13
digitalWrite(SafetyLED, HIGH);
digitalWrite(FireLED, HIGH);
//For a valid fire mode call function to initiate firing
if(SystemMode < 3 && SystemMode > 0){
FireControl = Fire(SystemMode, FireControl); //Call Fire to begin
firing.
}
}else{
digitalWrite(SafetyLED, LOW);
digitalWrite(FireLED, LOW);
}

motion();
Voltage = BatteryCheck(Voltage);

}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void security_loop(){

//Hold system in loop until access code is entered


while (CodeKey1 != 5 || CodeKey2 != 6 || CodeKey3 != 2){

KeyInput = customKeypad.getKey();

if (KeyInput){
j = j + 1;
}

if (KeyInput && j == 1){


CodeKey1 = KeyInput;

lcd.print(CodeKey1);
lcd.print(CodeKey2);
lcd.println(CodeKey3);
}

if (KeyInput && j == 2){


CodeKey2 = KeyInput;

lcd.print(CodeKey1);
lcd.print(CodeKey2);
lcd.println(CodeKey3);
}

if (KeyInput && j == 3){


CodeKey3 = KeyInput;

lcd.print(CodeKey1);
lcd.print(CodeKey2);
lcd.println(CodeKey3);
}

if (j == 3 && (CodeKey1 != 5 || CodeKey2 != 6 || CodeKey3 != 2)){


j = 0;

Page 14
CodeKey1 = 0;
CodeKey2 = 0;
CodeKey3 = 0;

lcd.println("Incorrect");
delay(1000);

lcd.print(CodeKey1);
lcd.print(CodeKey2);
lcd.println(CodeKey3);
}

if (CodeKey1 == 5 && CodeKey2 == 6 && CodeKey3 == 2){


lcd.println("Correct");
delay(1000);
lcd.println("Scan Card Now");
}
}

while(sec != 7){

RFID = digitalRead(RFIDPin); //RFID output pin 38

if(RFID < 1){


sec = 7;
lcd.println("Access Granted");
delay(1000);
lcd.print("Shots Fired: ");
lcd.print(NumShots);
lcd.print(" "); //If shots go to double digits, make " ", cannot
go triple digits
lcd.print("Ctrl "); // "Manual" or "Auto " (MUST be 6 characters)
lcd.print(" ");
lcd.print("FireMthd"); //"Normal", "FullAuto" or "SemiAuto" (must be 8
characters or less)
}
}

}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

void ModeSelection(){
//PRE: SystemMode mut have an integer value [0, 4].
//POST: Changes the value of SystemMode by 1 with values between 1 and 4.
unsigned long currentMillis = millis(); //check the local time stamp in
milliseconds
ToggleState = digitalRead(TogglePin);

if(currentMillis - ModePreviousMillis > 750 ) {


ModePreviousMillis = currentMillis; //Save the last time the mode switch
was pressed
if(ToggleState == LOW)
if(SystemMode < 3){
SystemMode = SystemMode +1; //Update SystemMode
}else{
SystemMode = 1; //Update SystemMode

Page 15
}else if(ToggleState == HIGH){
if(SystemMode < 2){
SystemMode = SystemMode +1; //Update SystemMode
}else{
SystemMode = 1;
}
}
LCD();
tracking_ring();
}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

int Fire(int Mode, int PreState){


//-----------------------------------
int State = 1; //Button State
int i = 1; //Burst mode Counter
const int NoChop = 100; //Time in ms to delay firing to prevent ball chop.
const int Burst = 200; //TIme in ms to delay between shots for burst and
automatic firing
const int Dwell = 8; //Time in ms to wait for firing solenoid to cycle
unsigned long currentMillis = millis(); //check the local time stamp in
milliseconds
//-----------------------------------

State = digitalRead(FireCmdPin); //Read in button state of fire command


button

//Perform for
if (State == HIGH){
switch(Mode){

//Semi-auto fire (1 round per activation)


case 1:
//Perform only If enough time has elapsed
if(currentMillis - previousMillis > NoChop) {
previousMillis = currentMillis; //Save the last time a round was
fired
digitalWrite(FiringPin, HIGH); //Send Fire Signal
}
break; //End Case

//3-round burst firing. Shoots 3 rounds per activation------------------


case 2:
//Perform the following 3 times ***CURRENTLY FIRST 2 SHOT
FIRE BUT 3rd too fast... work the code to fix that based on time diff.
if(PreState < State){
do{
currentMillis = millis(); //Read in the current local timestamp in
milliseconds

//Perform only If enough time has elapsed


if(currentMillis - previousMillis > Burst) {

Page 16
previousMillis = currentMillis; //Save the last time a round was
fired
digitalWrite(FiringPin, HIGH); //Send Fire Signal
//delay(Dwell); //Delay is used here because code will not exit
loop until counter is satisfied and the timing must be kept to turn off the
fire command
//digitalWrite(FiringPin, LOW); //Output Low - no fire value
i = i + 1; //Update counter
//Output for debugging
Serial.print(" i = ");
Serial.print(i);
}
if(currentMillis - previousMillis > Dwell){
digitalWrite(FiringPin, LOW); //Output Low - no fire value
}

}while (i < 4); //Loop

if(currentMillis - previousMillis > Dwell){


digitalWrite(FiringPin, LOW); //Output Low - no fire value
}
}
break; //End Case
} //End of Select Case Structure
}else{
if(currentMillis - previousMillis > Dwell){
digitalWrite(FiringPin, LOW); //Output Low - no fire value
}
}

PreState = State; //Update PreState

return PreState;
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

int motion(){
int Vertical = analogRead(A1); //Read in value for vertical motion
int Horizontal = analogRead(A0); //Read in value for horizontal motion
int HorzVal;
int VertVal;

HorzVal = map(Horizontal, 0, 1023, 180, 0)-88; //Map the Horiztonal input


to a degree range
VertVal = map(Vertical, 0, 1023, 180, 0)-88; //Map the Vertical input to a
degree range

//Horizontal (Yaw Control)-------------------------------------------------


----
if(HorzVal > 5 && HorzPos < YawUpperBound) {
if(HorzPos < YawUpperBound){
if(HorzVal < 20) { //For
slight stick motion change position slowly

Page 17
HorzPos = HorzPos +1;
}else if(HorzVal < 60 and HorzPos < (YawUpperBound - 4)) { //For
moderate stick motion change position faster so long as the pos change
doesn't exceed bounds
HorzPos = HorzPos + 3;
}else if(HorzVal>60 and HorzPos < (YawUpperBound - 9)) { //For large
stick motion change position fast so long as the pos change doesn't exceed
bounds
HorzPos = HorzPos + 10;
}else { //If
stick position otherwise exceed bounds, adjust by one
HorzPos = HorzPos + 1;
}
}
}else if (HorzVal < -5 && HorzPos > YawLowerBound) {
if(HorzPos > YawLowerBound){
if(HorzVal > -20) { //For
slight stick motion change position slowly
HorzPos = HorzPos - 1;
}else if(HorzVal > -60 and HorzPos > (YawLowerBound + 4)) { //For
moderate stick position change position faster so long as the pos change
doesn't exceed bounds
HorzPos = HorzPos - 3;
}else if(HorzVal < -60 and HorzPos > (YawLowerBound + 9)){ //For
large stick motion change position fast so long as the pos change doesn't
exceed bounds
HorzPos = HorzPos -10;
}else { //If
stick position otherwise exceed bounds, adjust by one
HorzPos = HorzPos -1;
}
}
}

YawMotion(HorzPos); //Call Motion funciton

//Vertical Control (Pitch) ------------------------------------------------


--------------------
if(VertVal > 5 && VertPos < PitchUpperBound) {
if(VertPos < PitchUpperBound){
if(VertVal < 20) { //For
slight stick motion change position slowly
VertPos = VertPos +1;
}else if(VertVal < 40 and VertPos < (PitchUpperBound - 4)) { //For
moderate stick motion change position faster so long as the pos change
doesn't exceed bounds
VertPos = VertPos + 3;
}else if(VertVal > 40 and VertPos < (PitchUpperBound - 9)) { //For
large stick motion change position fast so long as the pos change doesn't
exceed bounds
VertPos = VertPos + 10;
}else { //If
stick position otherwise exceed bounds, adjust by one
VertPos = VertPos + 1;
}
}
}else if (VertVal < -5 && VertPos > PitchLowerBound) {

Page 18
if(VertPos > PitchLowerBound){
if(VertVal > -20) { //For
slight stick motion change position slowly
VertPos = VertPos - 1;
}else if(VertVal > -40 and VertPos > (PitchLowerBound + 4)) { //For
moderate stick position change position faster so long as the pos change
doesn't exceed bounds
VertPos = VertPos - 3;
}else if(VertVal < -40 and VertPos > (PitchLowerBound + 9)){ //For
large stick motion change position fast so long as the pos change doesn't
exceed bounds
VertPos = VertPos -10;
}else { //If
stick position otherwise exceed bounds, adjust by one
VertPos = VertPos -1;
}
}
}
PitchMotion(VertPos); //Call Motion funciton

}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//Function to output signal to Servo controlling Yaw (Left/Right)
void YawMotion(int Position){
//PRE: Takes in a posiiton signal in degrees.
//POST: Maps the input variable to a desired PWM value in microseconds and
outputs the position as a microsecond PWM signal to motor
if(Position > YawLowerBound && Position < YawUpperBound){
Position = map(Position, 0, 180, 1350, 1820); //Map degree position to a
microsecond value and correct for central position
YawServo.writeMicroseconds(Position);
}
}
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
//Function to output signal to Servo controlling Pitch (Up/Down)
void PitchMotion(int Position){
//PRE: Takes in a position value in degrees.
//POST: Maps the input variable to a desired PWM value in microseconds and
outputs the position as a microsecond PWM signal to motor

if(Position > PitchLowerBound && Position < PitchUpperBound){


Position = map(Position, 0, 180, 1250, 1650); //Map degree position to a
microsecond value and correct for central position
PitchServo.writeMicroseconds(Position);
Serial.print( "Position ");
Serial.print(Position);
}
}
//---------------------------------------------------------------------------
-----------------------------------------------------

Page 19
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
int BatteryCheck(float Volts){
const int R1 = 5280000; //Resistor 1 value
const int R2 = 5240000; //Resistor 2 value

int SensorValue = analogRead(A8); //Read the input on analog pin 1


Volts = ((float)SensorValue/505)*6.56; //Convert from 0 - 1023 integer
input to 0 - 6 volt range floating input

if(Volts > 6.3){


digitalWrite(VoltGreenLED, HIGH);
digitalWrite(VoltYellowLED, LOW);
digitalWrite(VoltRedLED, LOW);
}else if(Volts < 5.9){
digitalWrite(VoltGreenLED, LOW);
digitalWrite(VoltYellowLED, HIGH);
digitalWrite(VoltRedLED, LOW);
}else{
digitalWrite(VoltGreenLED, LOW);
digitalWrite(VoltYellowLED, LOW);
digitalWrite(VoltRedLED, HIGH);
}
return Volts;
}

//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
void LCD(){
switch(SystemMode){
case 1:
lcd.println();
lcd.print("Shots Fired: ");
lcd.print(NumShots);
lcd.print(" "); //If shots go to double digits, make " ",
cannot go triple digits
lcd.print(" Manual"); //"Manual" or "Auto " (MUST be 6 characters)
lcd.print("-");
lcd.print("Single"); //"Normal", "FullAuto" or "SemiAuto" (must be 8
characters or less)
break;
case 2:
lcd.println();
lcd.print("Shots Fired: ");
lcd.print(NumShots);
lcd.print(" "); //If shots go to double digits, make " ",
cannot go triple digits
lcd.print(" Manual"); //"Manual" or "Auto " (MUST be 6 characters)
lcd.print("-");
lcd.print("Burst"); //"Normal", "FullAuto" or "SemiAuto" (must be 8
characters or less)

Page 20
break;
case 3:
lcd.println();
lcd.print("Shots Fired: ");
lcd.print(NumShots);
lcd.print(" "); //If shots go to double digits, make " ",
cannot go triple digits
lcd.print(" Motion"); //"Manual" or "Auto " (MUST be 6 characters)
lcd.print(" ");
lcd.print("Track"); //"Normal", "FullAuto" or "SemiAuto" (must be 8
characters or less)
break;

}
}

//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
void tracking_ring(){

int PentInput1 = VertPos;


int PentInput2 = HorzPos;

int SenValVert = (PentInput1 / 22.5); // vertical angle LED's go from 0 to


8
int SenValHor = ((PentInput2 / 22.5) + 4); // horizontal angle LED's go
from 4 to 12

int LED_BackHor = SenValHor - 1;


int LED_FrontHor = SenValHor +1;

int LED_BackVert = SenValVert - 1;


int LED_FrontVert = SenValVert + 1;

colorSet(strip.Color(0, 0, 255), SenValHor); // Set LED color to Blue


colorSet(strip.Color(255, 0, 0), SenValVert); // Set LED color to Red

if (LED_BackHor != SenValVert){
colorSet(strip.Color(0, 0, 0), LED_BackHor);
}

if (LED_FrontHor != SenValVert){
colorSet(strip.Color(0, 0, 0), LED_FrontHor);
}

if (LED_BackVert != SenValHor){
colorSet(strip.Color(0, 0, 0), LED_BackVert);
}

if (LED_FrontVert != SenValHor){
colorSet(strip.Color(0, 0, 0), LED_FrontVert);

Page 21
}
}

//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
// Fill the dots one after the other with a color
void colorSet(uint32_t c, uint16_t i) {

uint8_t bright=25;

strip.setPixelColor(i, c);
strip.setBrightness(bright);
strip.show();

Page 22
Appendix B. 2D Technical Part Drawings

Page 23
Page 24
FINAL IMAGES

Figure 11: final Control Box

Figure 10: Final Turret

Page 25
Final Project Report
Hybridized RC Toy Car
Submitted By: Sergio Mendoza
Ji Liu

December 17, 2013

ME 445

Department of Mechanical and Nuclear Engineering


The Pennsylvania State University
Table of Contents
Section I......................................................................................................................................................... 4

1.1 Background ................................................................................................................................... 4

1.2 Series HEV Architecture ................................................................................................................ 5

1.3 Problem Statement ....................................................................................................................... 5

1.4 Original Contributions ................................................................................................................... 6

Section II........................................................................................................................................................ 7

2. Subsystems ....................................................................................................................................... 7

2.1 Motor selection ............................................................................................................................. 7

2.2 DC Brushless Motor ...................................................................................................................... 7

2.3 Basis of a BLDC Motor Speed Control ........................................................................................... 8

2.4 Design of a BLDC Motor Control ................................................................................................... 9

2.5 Regenerative Breaking ................................................................................................................ 11

2.6 Battery Pack ................................................................................................................................ 12

2.7 Battery Charger ........................................................................................................................... 17

2.8 Hardware Modifications ............................................................................................................. 19

2.9 Power management Strategy ..................................................................................................... 20

Section III..................................................................................................................................................... 22

3. Results and Discussion .................................................................................................................... 22

Section IV .................................................................................................................................................... 26

4. Conclusions & Future Work ............................................................................................................ 26

Works Cited ................................................................................................................................................. 27

Appendix A .................................................................................................................................................. 28

Bill of Material......................................................................................................................................... 28

Appendix B .................................................................................................................................................. 29

Code ........................................................................................................................................................ 29
Matlab – Battery Capacity test ........................................................................................................... 29

Matlab – Look up Tables ..................................................................................................................... 34

Battery Resistance Test ....................................................................................................................... 36

Arduino Code for Motor Control – Power Management.................................................................... 40

Appendix C .............................................................................................................................................. 47

Cutsheets ................................................................................................................................................ 47

Losi-Ten Exploded View ...................................................................................................................... 48

Spektrum Radio Communication System........................................................................................... 51

Novak Brushless DC Motor ................................................................................................................. 53

Plazma LiPo Battery Pack .................................................................................................................... 58

LT 1513 Battery Charger ..................................................................................................................... 61


Section I
1.1 Background
Since the introduction of the first electric vehicle in the U.S. automotive market,
batteries, power management strategies, drive train components, etc. have evolved
significantly. The technological evolution that promoted the development of sophisticated
mechanical and electrical systems for hybrid electric vehicles (HEV) and plug in electric vehicles
(PHEV) is the response to an economical demand. Through the 1980’s and 1990’s the price of
oil was low and stable; in the early 2000s different factors, including an accelerated growth in
developing countries, the falling of the U.S dollar, geo-political events and even some natural
disasters, promoted an accelerated increase in the price of oil. The historical climb in oil prices,
the threat of a new energy crisis, a global concern on global warming, and a staggered economy
forced policy makers and auto makers to transform the automotive industry. According to the
U.S. department of energy, there are over fifty six different models of HEV and PHEV in the U.S.
today [1]. Even more impressive than the number of models available to the consumers is the
trend in sales for these types of vehicles. When Honda launched the insight in 1999 Honda, the
company sold seventeen units in the U.S. In 2012 the total number of HEV alone in the U.S.
reached 382,704 [2]. The tendencies in the market for HEV and EV coupled with current energy
demand call for the development of alternative technologies to address the transportation
industry. The purpose of this project is to provide an educational representation of the systems
involved in a series hybrid electric vehicle for current and future students.
Hybrid vehicles exist in three main configurations: series, parallel and power split. Each
of these architectures is particularly fitted for different applications. The series configuration
offers better performance in stop and go, city driving conditions. The parallel configuration, on
the other hand is more efficient in highway settings. Finally, the power split (the option used by
the Toyota Prius) falls somewhere in between the other two architectures offering benefits
from both ends of the spectrum. Each one of these options has unique mechanical and
electrical characteristics. Figure 1.1 provides a representation of the components and
connections in each one of these configurations.

4
a. Series HEV b. Parallel HEV c. Power Split HEV
Figure 1.1 Schematic of the different components and connections in the different types of hybrid
electric vehicle architectures. [3]

1.2 Series HEV Architecture


For this project, the series configuration was selected because of its flexibility to the control
strategy. This type of configuration was found in the EV-1 produced by General motors from
1996 – 1999 [3]. A series hybrid electric vehicle delivers power to the wheels exclusively via an
electric motor. In this configuration the mechanical energy from the combustion energy is
converted to electrical energy by the inverter, stored as electrochemical energy in the battery
pack, and then transformed back into mechanical energy by an electric motor. Because the
internal combustion engine is mechanically decoupled from the electric motor, this architecture
allows the controller to run each component at its most efficient point separately.

1.3 Problem Statement


In this project, parts of a commercially available RC car were used to build a hybrid
electric toy car. This model is to be representative of all the main systems of a series hybrid
electric vehicle. The internal combustion engine, chassis, suspension, radio communication
system and servos were taken from The Losi® 1/10-scale TEN-T™ Truggy RTR. The engine is a
1.8 horsepower small internal combustion engine. The Ten-T is controlled through the
Spektrum DX3S radio system, which also controls the starting system mounted on this vehicle,

5
two servos that control throttling and the steering capabilities of the car and displays
information such as speed, temperature and receiver battery voltage. In this project the
original radio communication of the base model was used to control steering, ignition and read
temperature and angular speed of the main axle. The signal to the high torque servo is hacked
and transmitted to an Arduino Uno to control a Brushless DC motor, implement regenerative
braking and charge a Lithium polymer battery pack.

1.4 Original Contributions


This project presents a systematic process to transform a commercial RC car in to a series
hybrid electric toy vehicle. The end product of this project is intended to be an educational tool
for students in a wide range of degrees to understand the different systems in a hybrid electric
car.
A control using an Arduino, transistors and resistors is designed and built to control the
speed of a sensored DC brushless motor. A modification to this circuit is also presented to add
regenerative breaking capabilities to the system. Furthermore, a series of experiments are run
to characterize the battery pack being used in this application. The results of these experiments
include an open circuit voltage (OCV) as a function of state of charge (SOC) look up table and
data of the battery internal resistance. Knowledge of the battery behavior is used to first order
equivalent circuit to model the SOC dynamics of the battery pack and enforce more effective
power management. Finally, a battery charger is built to safely charge the battery pack via the
regenerative breaking and the internal combustion engine.
The rest of this report is structured as follows: Section II includes a detailed description of
each of the subsystems contained in the hybrid RC car. Section III discusses the results and
challenges encountered in this project. Section IV will draw conclusions from the results and
will include any potential future work.

6
Section II
2. Subsystems
2.1 Motor selection
The alternatives in the market for electric machines include steppers, servo motors,
brushed and brushless electric motors (BLDC). The high accelerations and speeds that
characterize an RC vehicle require the motor to deliver a large amount of torque to the final
drive. Furthermore, it is desired to minimize the maintenance requirements. Based on these
objectives, steppers and servomotors are not appropriate choices for this work. DC motors,
both brushed and brushless, represent sound alternatives to meet the desired requirements of
the toy car. The main advantage of brushed DC motors is the existence of simple strategies for
speed control and regenerative breaking. Additionally, brushed motors do not require
commutation; thus, requiring no sensors for operation. However, the tradeoff between torque
and angular speed of the shaft is large due to brush friction. Friction also causes brushes to
wear out and maintenance for this type of motors is usually higher. In the absence of brushes,
BLDC motors demand less maintenance. The main benefit of a flatter torque versus speed curve
on BLDC motors is that power output can be approximated as constant for a larger range of
angular speeds. Based on these characteristics, the motor chosen was BLDC motor.
2.2 DC Brushless Motor
In a series hybrid electric vehicle, there are two electric machines serving different purposes
on the car. The inverter is mechanically connected to the internal combustion engine and
electrically connected to the battery pack and the motor. The purpose of the inverter is to
convert the mechanical energy into electrical energy. The second electric machine, commonly
referred as the motor is powered by the battery pack and connected to the final drive through
the differential. The motor is used to generate the torque required for vehicle propulsion, and
also to convert the vehicle’s kinetic energy into electrical energy to be stored in the battery
pack during regenerative breaking.
A BLDC motor is composed of a permanent magnet rotor and wire wound state poles.
Electrical energy is transformed to mechanical energy by the torque generated by the magnetic
interaction between the rotor and the coils of wire on the stator. The simplified BLDC motor

7
diagram shown in Figure 2.1 reduces the BLDC motor to a three coils with star connection
representation with three pairs of North and South magnetic poles. A motor with this type of
configuration is set in motion by energizing two phases simultaneously [5].

Figure 2.1 Schematic of a simplified BLDC motor [5]

2.3 Basis of a BLDC Motor Speed Control


The fundamental aspect to BLDC commutation is to estimate the position of the rotor to
energize the phases enabling the largest torque magnitudes. [5] To set the motor in Figure 2.1
into motion, coils A and B (located between terminals AB) are energized. Coils are energized by
generating a current flow from one terminal to the other. Therefore, the rotor should be
aligned in the same position as shown in Figure 2.1. To drive the motor continuously, it is
important to activate the right terminals in the right sequence. The activation sequence is
determined by the rotor position at any instance in time. To accurately estimate the position of
the rotor and energize the coils in the correct sequence, BLDC motors use Hall Effect sensors.
Hall Effect sensors can detect the magnetic field generated by the activated coils and thus
estimate the position of the rotor. Hall Effect sensors then feedback the rotor state to the BLDC
motor controller so that the controller activates the coils in the right sequence. Three-phase
BLDC motors usually contain three Hall Effect sensors. Each one these sensors have a digital
high level output for 180 electrical degrees of electrical rotation and a digital low level for the
other 180 degrees. [5] To pair the output of each sensor with each of the electromagnetic
circuits, the sensors are located every 60 degrees. The diagram shown in Figure 2.2 shows the
relationship between sensor output voltage and the desired motor input voltage.

8
Figure 2.2 Sensor Voltage Output versus Drive Time for a Three Phase BLDC Motor

The top axis in Figure 2.2 indicates the current phase as designated in Figure 2.1. From this
diagram it can be inferred that the output of the three sensors give a unique combination of
high or low level for each drive phase. The bottom axis in Figure 2.2 designates the sensor
position code matching with those subscribed around the motor in Figure 2.1. This three-bit
code is used to feed the position of the rotor back to the motor controller. For instance, when
the digital level of sensor A, B, and C are high, low and high respectively, that is the bit number
is 101, the terminals A and B are energized. So the rotor rotates into a new position where
sensors output is 001, then the terminal B is disconnected and terminal C is energized while
terminal A is still energized. In this way, continuous rotation is achieved.
2.4 Design of a BLDC Motor Control
As discussed earlier, BLDC motors require the coils in the stator to be activated in a logic
sequence to maintain continuous rotation. The overall circuit architecture for motor speed
control used in this project is shown in Figure 2.3. The commutation circuit design is based on
the work earlier done by [6]. This circuit is composed of N-channel and P-channel transistors
(or Darlingtons), resistors and diodes. The diodes are used to reroute current flow required for
regenerative braking, which will be discussed in Section 2.5.
9
Figure 2.3 Overall Architecture of a Speed Control Circuit for a BLDC Motor

Figure 2.4 shows a more detailed image of the circuit design for energizing a single coil. The
circuits for the other two coils are identical.

Figure 2.4 Commutating Circuit Design for a Single Coil of a BLDC Motor
Commutation in this circuit is done by switching the corresponding A High, B High, and C
High transistors by using pulse width modulation (PWM) while keeping all of the A Low, B Low,
and C Low transistors open all the time, or the other way around.
Figure 2.5 shows the current flow path when the sensor signal is 101. In this case, coils A
and B are energized. The A High transistor is turned on with a PWM determined by the position
of the gas pedal. The B Low transistor is kept open until the sensor signal changes.

10
Figure 2.5 Current Flow through the Commutation Circuit When Sensor Signal is 101
2.5 Regenerative Breaking
Regenerative braking improves fuel consumption by harvesting the kinetic of the HEV and
storing it in the form of electrochemical energy in the battery pack. In modern HEV,
regenerative braking is done by reversing the current path in the motor-battery circuit while
the car decelerates. During this operation, the motor plays the role of a generator and redirects
the current back to the battery pack. To accomplish regenerative breaking, previous authors in
the literature [7] modify the commutating circuit by adding components (diodes) that allow
current switching.
Figure 2.6 shows an example of the current trajectory during regenerative braking. In this
case, current flows through the diode on A High, through the battery and through B Low. This
allows current to be stored in the battery.

Figure 2.6 Example of Current Trajectory during Regenerative Braking

11
The essential part for regenerative braking is to switch A Low, B Low, and C Low transistors
on and off with a PWM signal, while A High, B High, and C High transistors are off. The diodes
are placed to prevent the current from the battery to flow into the coils and allow current to
flow through when A High, B High, and C High transistors are off. During regenerative braking,
the current should flow in the opposite direction to allow current to be stored in the battery
pack. Regenerative braking works only if the motor slows down faster than in the absence
regenerative braking, and if current is allowed to flow into the battery.
2.6 Battery Pack
Table 2.1. Battery Parameters
Parameter Value
Nominal Capacity 5.3 A·hr
Nominal Voltage 7.4 V
Max Discharge Voltage 8.4 V
Max Charge Voltage 6.2 V

This project uses a 5.3 A·hr (nominal capacity) lithium-polymer battery pack with
specifications listed in Table 2.3. Lithium-polymer batteries bend easily to the power demands
of a hybrid electric vehicle. To design an efficient power management strategy, it is incumbent
to have a deeper understanding of the battery SOC dynamics. In the context of this work, SOC is
defined as the ratio of the electrochemical energy stored in the battery to the maximum
storage capacity of the pack. To achieve adequate knowledge of the battery pack while
maintaining simplicity, this work uses a first order equivalent circuit as shown in Figure 2.7 and
Characterized by Equation 2.1. In this type of model the battery open circuit voltage, OCV, is a
function of the battery’s SOC. The internal resistance of the battery is a function of the pack’s
SOC and direction of the current.

R
+
+ I
V V
_
_
Figure 2.7 First Order Equivalent Circuit of a Battery

12
The first step in constructing a battery model is to determine the pack electrochemical
energy storage capacity. To determine battery capacity an experiment was conducted. In this
experiment, the batteries were charged and discharged at constant current, constant using an
Arbin cycler. Figure 2.8 below shows a schematic of the experimental setup. Battery cycling
was done at a slow rate of 0.2C rate for one cycle. The rate of charge and discharge wass
measured as the applied current relative to the battery capacity. [5] Table 2.2 below
summarizes the experiment schedule used for capacity estimation.

BT-2000 Arbin Cycler

Voltage sensors
Battery pack

Figure 2.8 Battery Experimental Setup


Table 2.2. Battery Capacity CCCV Test
Step Description
1 Constant current discharge to max discharge voltage (6.2 V)
2 Constant voltage discharge with -0.01A current cut off
3 Rest for 2 hours
4 Constant current charge to max charge voltage (8.4 V)
5 Constant voltage charge with 0.01A current cut off
6 Rest for 2 hours
7 Constant current discharge to max discharge voltage (6.2 V)
8 Constant voltage discharge with -0.01A current cut off
9 Rest for 30 minutes

13
Battery charge and discharge capacities vary depending on chemistry of the positive and
negative electrodes and also on the charge and discharge rates. To minimize deviation in charge
and discharge capacities and accurately approximate measured voltage to OCV, this experiment
was done at low current rates. Figure 2.9.a shows the battery pack voltages during charge and
discharge at 0.2 C-rate. Figure 2.9.b shows the current and voltage profiles applied to the
battery pack during the battery capacity test.

Voltage During Charge at Different SOC Levels for a LiPo Pack


8.5

8
Voltage (V)

7.5

Pack#1
6.5
Pack#2

6
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
SOC, Charging

Voltage During Discharge at Different SOC Levels for a LiPo Pack


8.5

8
Voltage (V)

7.5

Pack#1
6.5
Pack#2

6
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
SOC, Discharging

Figure 2.9.a Voltage curves for a 5.3A-h nominal capacity Lipo pack at 0.2 charge-discharge rates

14
Experimental Voltage Profile for a LiPo Battery Pack
8.5
Pack#1
Pack#2
8
Voltage(V)

7.5

6.5

6
0 5 10 15
time(h)

Experimental Current Profile for a LiPo Battery Pack


2
Pack#1
1.5
Pack#2
1
Current (A)

0.5

-0.5

-1

-1.5

-2
0 5 10 15
time (h)

Figure 2.9.b Constant-current and constant-voltage charge and discharge curves for a Lipo pack
The charge and discharge capacity of the battery pack is estimated by integrating the
area under the current versus time profiles. In order to approximate the area under the curve,
the numerical method used was the trapezoidal rule. Once the charge and discharge capacities
have been obtained, the battery pack capacity is taken as the average of these two capacities.
After conducting charge-discharge measurements, the battery pack capacity was found to be
5.24 A·hr. The data collected for the pack capacity experiment also serves to generate a look
up table relating OCV to the SOC of the battery pack. The sampling frequency for this
experiment was 100 Hz. To generate a look up table at predetermined values of SOC, a cubic
spline is function is fitted to the raw voltage signal. Figure 2.10 shows the experimental
voltages measured during charge and discharge, the smooth polynomial fitted to the raw signal,
and the average voltage between the charge and discharge measured voltage or OCV.

15
8.5

Voltage [Volts] 7.5


Exp. Charge
Spline Charge
Exp. Discharge
Spline Discharge
VoC
7

6.5

6
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
SOC

Figure 2.10 Open circuit voltage as a function of SOC curves during charge and discharge
As seen in Equation #2.1, battery voltage in a first order equivalent circuit model is a
function of open circuit voltage and battery internal resistance. To identify the internal
resistance of the battery, a second experiment was conducted. In this experiment current
pulses with a magnitude of 1.05 were applied to the battery after the battery had been allowed
to relax for a 30 minute period. The relaxation period was chosen after observing that this
particular chemistry does not seem to display large relaxation dynamics. This behavior can be
seen both in Figure 2.9b and 2.11. As shown in these figures, the voltage open circuit voltage of
the battery does not show significant immediately after charge or discharge.
Current is applied until the battery reaches a desired state of charge and allowed to
relax for 30 minutes before a new pulse is applied. This procedure was repeated for SOC 0-
100%. The battery internal resistance is calculated as the change in voltage divided by the
magnitude of the current applied at each SOC. The resistance value found in this experiment
does not vary much within the SOC operating window and the value used for SOC estimation is
0.011 Ohm. Figure 2.11 shows the current and voltage profiles applied to the battery pack to
identify battery internal resistance.

16
Experimental Voltage Profile for a LiPo Battery Pack
8.5

Experimental Voltage Profile for a LiPo Battery Pack


8 8.5
Voltage(V)

8
7.5
Voltage(V)

7.5
7
7
Pack#1
6.5 6.5
Pack#1
Pack#2 Pack#2

6 6
0 2 4
0 2 4 66 8
8 10
10 12
12 14
14 16
16 18 18
time(h)
time(h)
Experimental Current Profile for a LiPo Battery Pack
1.5
Experimental Current Profile for a LiPo Battery Pack
1.5
1

1 0.5
Current (A)

0
0.5
Current (A)

-0.5
0
-1 Pack#1
Pack#2
-0.5 -1.5
0 2 4 6 8 10 12 14 16 18
-1 time (h) Pack#1
Pack#2
-1.5
0 2 4 6 8 10 12 14 16 18
time (h)

Figure 2.11 Experimental Current and Voltage Profiles for a Pulse Test
2.7 Battery Charger
The Lithium Polymer battery pack used in this project is prone to cell imbalance during
cycling. Over time, cell imbalances lead to capacity fade and thermal gradients within the
battery which can result in thermal runaway. To avoid cell imbalances, the terminals of the
generator were connected to a charging circuit before any charge was stored. The battery
charger implemented uses an LT 1513-IR Single-ended Primary-Inductor Converter (SEPIC)
circuit designed by Linear Technology as presented in Figure 2.12.

17
Figure 2.12 Schematic of the Charging Mechanism for the Battery Pack.
The portion of the circuit within the green dotted lines is the SEPIC topology which allows
the voltage output to match a desired reference point for input voltages greater than, less than
or equal to the reference voltage. The R5C5 pair connected to pin 1 of the LT1513 chip enforces
loop frequency compensation. Similarly, the R4C4 filter connected to pin 3 convert the current
sense to a smooth signal and feeds it back to the chip current feedback pin. The charging
current is controlled by pin 3 and is equal to the regulating voltage (100mV) over R 3, in this case
1.25A. The voltage limit is set by the ratio of R1 and R2. R2 is selected according to the
suggested value by Linear and R1 is calculated based on the maximum charging voltage
recommended by the manufacturer of 8.4 volts. Figure 2.13 is a picture of the battery charger
being tested at different input voltages.

Figure 2.13 Battery Charger Being Tested at Different Input Voltages

18
2.8 Hardware Modifications
The layout of the modified Losi model in this project is shown in Figure 2.14. As discussed
earlier, a series HEV requires additional mechanical and electrical components in the vehicles
drive train. To accommodate the new parts, it is necessary to make changes to the structure of
the chassis and manufacture additional gears to connect the electric machines to the internal
combustion engine and the differential center gear. The gears used in the original mode are
custom made. In this work, a new pinion gear for the IC engine and center differential were
designed using Solid Works and manufactured using 3D printing. Figure 2.15 shows the four
manufactured gears for this project.

1
Chassis Layout
1 Anodized Aluminum Chassis 2
2 On Board Starting System 3
3 Internal Combustion Engine 4
4 Centrifugal Clutch 5
5 Fuel Throttling Servo
6 Steering Servo 6

Figure 2.14 1/10 Ten-T Truggy RTR Assembled

Figure 2.15 Manufactured Gears


Clutch Bell and Clutch Pinion Inverter Pinion Gear Center Differential Gear Motor Pinion Gear
Gear

19
Adding a second platform to the existing model is the main modification done to the
original chassis. The second platform serves as a base for the internal combustion engine, the
fuel throttling servo, the battery pack and the circuitry required for motor control and battery
charging The differential and the electric motor are installed on the original chassis alone with
the telemetry equipment, gas tank, tail-pipe, suspension and transmission systems. This
platform is of acrylic and is connected to the original chassis by three sustaining rods. The rods
are drilled in to the car chassis at key locations to maintain the chassis strength.
2.9 Power management Strategy
When operating a car, the user has basic input commands such as pedal position for speed
control. The driver has no control about the engine operation or battery charge-discharge
scheduled. To guarantee that the performance demanded by the user is met, a power
management strategy is needed.
The goal of HEV power management is to maximize the performance of the vehicle’s power
train while meeting the user’s power demands. The literature in HEV is rich in sophisticated
and complex power management strategies for this type of vehicles. This work is based on the
primacy that series HEV allow the controller to run each component at its most efficient point
separately. This strategy is limited by the accuracy of SOC estimation of the equivalent circuit
approximation used to model the battery pack. The two main objectives of this power
management are to minimize the consumption of fuel by the internal combustion engine, and
to eliminate the risk of battery over charge/discharge and thus reduce battery degradation.
Figure 2.16 captures the logic used to code the power management algorithm in the Arduino.
The SOC range of operation for this work is limited to a minimum of 20% and a maximum 80%
of the battery capacity. This limitation imposed by the accuracy of the battery model being
used to estimate SOC, but it also serves to minimize battery aging.

20
Figure 2.16 Power Management Algorithm Logic Diagram
This section presented the basis for motor selection, the circuit design for motor control
and modifications needed for regenerative braking. Also, two experimental procedures were
discussed to characterize a battery model to capture SOC dynamics and implement a power
management algorithm. Furthermore the required hardware modifications required to
accommodate additional components into the car structure were discussed. Finally, a simple
multi-objective power management strategy to meet the user’s demand was introduced.

21
Section III
3. Results and Discussion
This project is divided in six stages to facilitate progress tracking. Each stage is assigned a
main task as follows:
1- Diagnosis of mechanical and electrical components.
2- Design and build circuitry to operate all subsystems on the vehicle.
3- Create a Power management strategy.
4- Build any additional circuitry.
5- Coupling of the mechanical and electrical components and implement control
strategy.
6- Test & troubleshoot.
During the first stage of this project, the main components and parts from an
abandoned RC car are collected. A conventional RC car was then assembled to test the gear
coupling in the drivetrain and the operability of the internal combustion engine and the
automatic starting system. At this point the RC car running on the internal combustion engine
was successfully tested. It was during this stage that the motor was selected. The brushless DC
motors were tested using a motor control manufactured by Novak. This motor control was
discarded because such controller does not allow regenerative braking.
The second phase of this project involved the design and construction of different
circuits. The first step was to identify an architecture that allowed regenerative braking. The
commutation circuit for a BLDC motor requires an estimate of the rotor’s position. The first step
taken in writing the code to operate the motor was testing the output of Hall Effect sensor to
obtain a relationship between rotor position and sensor output. The next step was to turn on
the transistors in the right sequence according to the relationship obtained in step one. The
transistor is turned on by giving a digital high level signal from the Arduino. To allow
regenerative braking, the commutation circuit was modified by adding diodes. This modification
permits charge to be stored in the battery by switching the direction of the current flow. At this
point, a simple test was conducted to test the regenerative braking capabilities by comparing
the time it takes the motor to go from full speed to zero with and without regenerative braking.

22
When regenerative braking was enforced this time was found to be smaller. The pedal position
in this case was simulated with a potentiometer varying the PWM signal that controls motor
speed. Another indication that the regenerative braking is operational is that when the power
supply to the motor was cut and the motor was allowed to decelerate, a voltage with a
magnitude around 6 volts was measured across the motor terminals. Within this stage the
signal from the radio communication system of the Losi-Ten to the throttling servo was hacked
and routed to the Arduino. The Arduino read the pulse width of the signal and map it to a PWM
signal that controls the speed of the electric motor. The final deliverable of this stage was the
selection and implementation of a battery charger.
Once the circuitry needed for each of the subsystem was selected and tested, in stage
three, a simple power management strategy based on available the sensor signals and
actuators was determined. As part of this stage, the experiments for battery characterization
were conducted. Such experiments required the construction of special battery connectors to
hook the battery packs to an Arbin cycler for testing. The experiment to measure battery
capacity took 15 hours and the experiment to identify the internal resistance of the battery
lasted approximately 18 hours. From these experiments, SOC- VoC tables needed for SOC
estimation were obtained and included in the Arduino code.
The next stage involved modifying the terminals of the BLDC motor to connect to the
breadboard and Arduino to measure each of the Hall Effect sensors and the temperature
sensors. Additionally at this point in the project, the need to design and build additional gears
was identified. Most threads and gears on the original RC car are custom made which
represented a major set-back. At this point new gears were designed and prototyped using a 3D
plotter. However, the strength of the material used in 3D plotting is not optimal and the
components started to show wear after simple testing. Gears continue to be an issue and need
to be manufactured using a different material.
The fifth stage of the project demanded the coupling of the mechanical and electrical
systems. All mechanical connections were tested for the RC car to run on the IC engine on stage
one. The electrical motor was tested individually and its capability to produce enough torque
was assessed in stage one as well, however due to the issues with the prototyped gears; it was

23
not possible to run the car using the electric motor. The last step in this stage was to find a
strategy to fit the additional components on the car. Holes were drilled on metallic L-shaped
brackets as the one shown in Figure 3.1 to hold the electric machines and bolt down on the car
chassis.

Figure 3.1 Simpson Strong-Tie 4 9/16 in. x 4 3/8 in. x 1-1/2 in. Angle
An acrylic plate was to serve as a platform that holds the internal combustion engine, the
throttling servo and the generator. This platform is held by three ¼” threaded rods that tie on
to the car chassis. All the required parts to mount the additional components are ready at this
time.
The final stage of this project comprises testing and troubleshooting; currently there are
still some bugs associated with the motor commutation control. Particularly motor start up is
not always smooth. The main limitation on the mechanical part continues to be the gears and
using an alternative material is required before the mechanical connections are finalized. At this
point the time to complete this project is approximately two weeks. Table 3.1 summarizes each
one of the tasks and subtasks as well as the deliverables. The items shown as green in the
deliverables column have been completed and those in orange still need work.

24
Table 3.1 Project Matrix
Main Task Sub tasks Deliverables
• Diagnosis of existing electromechanical
• Test gear coupling in the drivetrain.
Diagnosis of mechanical and electrical components.
• Test DC motor
components • Inventory of all required circuitry
• Test IC engine and automatic starter
components for coupling
• Individual circuits to operate the DC
• Design and build circuitry to control:
motor
• Hack into the starter
Design and build circuitry to operate • Ability to turn on and off the IC engine
• Brushless DC motor
all subsystems in the vehicle with an external controller
• Design corresponding part for remote control
• Modified and implemented a battery
• Select a circuit architecture for battery charging
charger
• Create a simple power management strategy to
determine usage of battery vs. IC engine • SOC estimator
Create a power management strategy
• Develop an SOC estimator • Power management strategy
• Design a circuit for regenerative breaking
• Build circuits for throttle control
Build additional circuitry and • Operational circuits that can be controlled
• Build circuits for sensor monitoring
mechanical from an Arduino or radio receiver.
• Build Circuit for regenerative circuit

• Coupled drivetrain operational 100% on


battery.
• Connect all components and have them respond
• Coupled drivetrain operational 100% on
to individual commands
Coupling of the mechanical, electrical ICE engine.
• Connect all components and have them respond
and control components • Coupled drivetrain operational as a hybrid
to the power management strategy
vehicle.
• Design corresponding part for remote controller
• Produce mechanical components needed
for mechanical coupling.

• Debug any software problem


Test & Troubleshoot • Working model
• Tune up any mechanical or electrical problems

25
Section IV
4. Conclusions & Future Work
1. This project is intended to be an educational tool for students with different backgrounds to
understand the different systems in a hybrid electric car.
2. A systematic process including the design considerations to transform a commercial RC car in to
a series hybrid electric toy is presented.
3. A motor control using an Arduino, transistors, resistors and diodes is built to control the speed
and implement regenerative braking.
4. A series of experiments are run to characterize the battery pack being used in this application.
5. A first order equivalent circuit model is used to understand the SOC dynamics of the battery
pack and enforce more effective power management.
6. A simple multi-objective power management strategy is designed and implemented.
7. A battery charger is built to safely charge the battery pack via the regenerative breaking and the
internal combustion engine.
8. Complete all mechanical coupling required and test fully functional model
9. Design PCB board for motor control and regenerative braking
10. Configure Wi-Fi shield
11. Manufacture gears using a titanium alloy
12. Add a flow meter to the gas lines to estimate gas consumption

26
Works Cited

[1] U. D. o. Energy, "Energy Efficiency and Renewable Energy," Alternative Fuel Data Center, [Online].
Available: http://www.afdc.energy.gov/data/?q=electricity. [Accessed 14 December 2013].

[2] U. D. o. Energy, "Energy Efficiency & Renewable Energy," Alternative Fuels Data Center, [Online].
Available: http://www.afdc.energy.gov/data/10301. [Accessed 14 December 2013].

[3] M. Rothenberger, An Interactive Framework for Education on Vehicle Electrification, State College,
PA: The Pennsylvania State University, 2012.

[4] A. Mendoza and J. Argueta, "Performance Characterization GM EV-1," Southern California Edison -
Electric Transportation Division, 2000.

[5] W. Brown, "Brushless DC Motor Control Made Easy," Microchip Technology, Inc.

[6] J.-X. Chen , J.-Z. Jiang and X.-Y. Wang, "Research of Energy Regeneration Technology in Electric
Vehicle," Journal of Shanghai University, vol. 7, no. 2, pp. 173-177, 2003.

[7] J. Cody, G. Ozdemir , Z. Nedic, A. Nafalski and Aaron Mohtar, "Regenerative Nraking in an Electric
Vehicle," Zeszyty Problemowe Maszyny Elektryczne , vol. 81, pp. 113-118, 2009.

[8] C. Rahn and C.-Y. Wang, Battery Systems Engineering, John Wiley & Sons, 2013.

27
Appendix A
Bill of Material

Bill of Materials - Hybrid Car


Qty Part Description Manufacturer Part Number Unit Cost Total Cost
Base Model
1 1/10 Ten-T 4WD Nitro Truggy RTR LOSB2021 $ 499.99 $ 499.99
Motors
2 Novak Vulcan 21.5 Motor Novak $ 98.99 $ 197.98
Chassis
2 Simpson Mounting Brackets Simpson 44315047008 $ 4.38 $ 8.76
1 Clear polycarbonate Sheet Lexan GE-33 $ 14.28 $ 14.28
2 12"X1/4-20 Threaded Rod Home Depot $ 0.98 $ 1.96
1 Mis 1/4 Washers & Nuts Home Depot $ 7.68 $ 7.68
Hex 8-32 1-1/2" Bolts to mount
1 servo Home Depot $ 2.10 $ 2.10
Battery Pack
1 Plazma 5300 mAh Plazma HPI101945 $ 86.95 $ 86.95
Circuitry
1 LT1513 SEPIC Chip Linear LT1513 $ 9.42 $ 9.42
1 Inductor Toroid Cooper Bussmann CTX10-4 $ 3.13 $ 3.13
3 22 uF Tantalum Capacitors Vishay Sprague 593D226X9025D2TE3 $ 0.57 $ 1.71
1 Schotky Diode ON Semiconductor BBRD340G $ 0.73 $ 0.73
1 0.1 uF Ceramic Capacitor TDK Corporation FK18Y5V1H104Z $ 0.20 $ 0.20
1 0.22uF Ceramic Capacitor Vishay BC Components K104K15X7RF5TL $ 0.04 $ 0.04
1 4.7uF Ceramic Capacitor TDK Corporation FK25X5R1A475k $ 0.32 $ 0.32
1 270 Ohm Resistor Panasonic Electric EROS2PHF2700 $ 0.01 $ 0.01
1 39 Ohm Resistor Stackpole CFM12JT39R0 $ 0.11 $ 0.11
1 0.08 Ohm Resistor Vishay Dale MTL03R0800FE66 $ 2.80 $ 2.80
1 12.4K Ohm Resistor $ 0.01 $ 0.01
1 1.62K Ohm Resistor Yageo MFR-25FBFF52-1K62 $ 0.10 $ 0.10
2 Half Size Breadboard Adafruit $ 5.00 $ 10.00
1 Arduino Uno Arduino Uno $ 29.95 $ 29.95
1 USB Cable Adafruit $ 4.00 $ 4.00
Custom Made Parts
1 Gears $ 25.00 $ 25.00

Total $ 907.22

28
Appendix B
Code
Matlab – Battery Capacity test
% This script imports the Excel file generated by an Arbin cycler during
% testing of lithium polymer cells and calculates its capacity

% Control Optimization Laboratory - RC Hybrid Car Project

% Last Edit: 11/26/2013 Sergio Mendoza & Ji Liu

% The channels in the cycler correspond to each of the following SOC's:

% Channel 2 = Battery Pack #1


% Channel 4 = Battery Pack #2

% Arbin Data:
% Col 1: Data_Point
% Col 2: Test_Time(s)
% Col 3: Date_Time
% Col 4: Step_Time(s)
% Col 5: Step_Index
% Col 6: Cycle_Index
% Col 7: Current(A)
% Col 8: Voltage(V)
% Col 9: Charge_Capacity(Ah)
% Col 10: Discharge_Capacity(Ah)
% Col 11: Charge_Energy(Wh)
% Col 12: Dischare_Energy(Wh)
% Col 13: dV/dt(V/s)
% Col 14: Internal_Resistance(Ohms)
% Col 15: Is_FC_Data
% Col 16: AC_Impedance(Ohm)
% Col 17: ACI_Phase_Angle(Deg)
%%
clc
clear all
close all
%% Loading the file from Excel

tic
filename = 'ME_445_Battery'; %Name of the file being imported
channels = [2 4]; % Channels being analyzed
sheets = 5 ; %Number of sheets per channel in the original Excel file
num_ofsteps = zeros(1,length(channels)); % Preallocates space to have a
vector with the number of steps per channel

Arbin_data = cell(1,length(channels)); % Generates a cell with as many


entries as channels being analyzed
[r,c] = size(Arbin_data);
StepData = cell(10,c);%Preallocates space to have a cell with as many rows as
steps in the experiment and as many columns as channels being tested

29
%% Imports the data from the Excel file first into a cell containing all
steps per channel and then creates an array containing where each row is a
step and each column a channel
for i=1:length(channels)
Arbin_data(1,i) = arbin_excelimport_ver4(filename, channels(i), sheets);
num_ofsteps(1,i) = max(Arbin_data{i}(:,5));
StepData(:,i) = arbin_bystep(Arbin_data(1,i));
end
%% Generating Matrices for Time, Voltage and Current

time_SOC = cell(1,length(channels)); %Preallocates space to have a cell


containing the test time signal for each channel
current_SOC = cell(1,length(channels)); %Preallocates space to have a cell
containing the current signal for each channel
voltage_SOC = cell(1,length(channels));%Preallocates space to have a cell
containing the voltage signal for each channel

for j = 1:length(channels)
time_SOC{j} = Arbin_data{j}(:,2);
current_SOC{j} = Arbin_data{j}(:,7); % time, current and voltage cells for
the entire duration of the experiment
voltage_SOC{j} = Arbin_data{j}(:,8);
end

%% plots

color = [0 0 1;
0 .5 0];

figure(1)
subplot (2,1,1) % Voltage Subplot
hold on
for plots = 1:length(channels)
plot(time_SOC{plots}./3600,voltage_SOC{plots},'Color',color(plots,:))
title ('Experimental Voltage Profile for a LiPo Battery Pack','FontSize',12)
xlabel('time(h)','Color','Red','FontSize',12)
ylabel('Voltage(V)','Color','Blue','FontSize',12)
legend('Pack#1','Pack#2')
end

subplot (2,1,2) %Current Subplot


hold on
for plots = 1:length(channels)
plot(time_SOC{plots}./3600,current_SOC{plots},'Color',color(plots,:))
title ('Experimental Current Profile for a LiPo Battery Pack','FontSize',12)
xlabel('time (h)','Color','Red','FontSize',12)
ylabel('Current (A)','Color','Blue','FontSize',12)
legend('Pack#1','Pack#2')
end

%% Calculation 1: SOC, Charging

% Charging: Steps 5-7


% At the beginning of step 4, SOC = 0

30
% Preallocating space for current - voltage - charge arrays

current_ch = cell(1,length(channels));
time_ch = cell(1,length(channels));
current_ch = cell(1,length(channels));
SOC_ch = cell(1,length(channels));
SOC_dis = cell(1,length(channels));
q_dis = cell(1,length(channels));
q_ch = cell(1,length(channels));

for channel = 1:length(channels)


current_ch{channel} = [StepData{5,channel}(:,7);
StepData{6,channel}(:,7)];
%StepData{7,channel}(:,7)]; % Array for all
current readings during charging
time_ch{channel} = [StepData{5,channel}(:,2);
StepData{6,channel}(:,2)];
%StepData{7,channel}(:,2)];
end

for channel = 1:length(channels)


q_ch{channel}(1) = 0;
end

for channel = 1:length(channels)


for row_pos = 2:length(current_ch{channel})
q_ch{channel}(row_pos) = q_ch{channel}(row_pos-1) + ...
(current_ch{channel}(row_pos) + current_ch{channel}(row_pos-
1))/2 * ... %average current
(time_ch{channel}(row_pos) - time_ch{channel}(row_pos-1));
% dt
end
end

% At the end of step 7, SOC is 1 and q_ch at that point is the max
for channel = 1:length(channels)
SOC_ch{channel} = q_ch{channel}/max(q_ch{channel});
end

figure(2)
subplot(2,1,1)
grid on
hold on
for channel = 1:length(channels)

plot(SOC_ch{channel},[StepData{5,channel}(:,8);StepData{6,channel}(:,8)],'Col
or',color(channel,:))
end
xlabel('SOC, Charging','Color','Red','FontSize',12)
ylabel('Voltage (V)','Color','Blue','FontSize',12)
legend('Pack#1','Pack#2','Location','EastOutside')
title ('Voltage During Charge at Different SOC Levels for a LiPo Pack',
'FontSize',12)
% Calculation 2: SOC, Discharging

31
% Discharging: Steps 8-10
% At the beginning of step 8, SOC = 1 and q is the same as at the end of
% step 7.

current_dis = cell(1,length(channels));
time_dis = cell(1,length(channels));

for channel = 1:length(channels)


current_dis{channel} = [StepData{8,channel}(:,7);
StepData{9,channel}(:,7)];
%StepData{10,channel}(:,7)]; % array for all
current readings during charging
time_dis{channel} = [StepData{8,channel}(:,2);
StepData{9,channel}(:,2)];
%StepData{10,channel}(:,2)];
end

for channel = 1:length(channels)


q_dis{channel}(1) = max(q_ch{channel});
end

for channel = 1:length(channels)


for row_pos = 2:length(current_dis{channel})
q_dis{channel}(row_pos) = q_dis{channel}(row_pos-1) + ...
(current_dis{channel}(row_pos) +
current_dis{channel}(row_pos-1))/2 * ... %average current
(time_dis{channel}(row_pos) - time_dis{channel}(row_pos-1));
% dt
end
end

% At the end of step 7, SOC is 1 and q_ch at that point is the max
for channel = 1:length(channels)
SOC_dis{channel} = (q_dis{channel}-
min(q_dis{channel}))/(max(q_dis{channel})-min(q_dis{channel}));
end

subplot(2,1,2)
hold on
grid on
for channel = 1:length(channels)

plot(SOC_dis{channel},[StepData{8,channel}(:,8);StepData{9,channel}(:,8)],'Co
lor',color(channel,:))
end

xlabel('SOC, Discharging','Color','Red','FontSize',12)
ylabel('Voltage (V)','Color','Blue','FontSize',12)
legend('Pack#1','Pack#2','Location','EastOutside')
title ('Voltage During Discharge at Different SOC Levels for a LiPo Pack',
'FontSize',12)

32
%% Calculation 3: Charge/Discharge Capacity

charge_cap = zeros(1,length(channels));
discharge_cap = zeros(1,length(channels));
current_ch = cell(1,length(channels));
capacity = zeros(1,length(channels));

for channel = 1:length(channels)


charge_cap(channel) = max(q_ch{channel})/3600;
discharge_cap(channel) = (max(q_dis{channel})-min(q_dis{channel}))/3600;
capacity(channel) = (charge_cap(channel)+discharge_cap(channel))/2;
end

cap = [charge_cap.',discharge_cap.',capacity.']

%% Renaming Variables

% Differentiating between different C Rates for later plots.

for channel = 1:length(channels)


ch_voltage{channel} =
[StepData{5,channel}(:,8);StepData{6,channel}(:,8)];
end
for channel = 1:length(channels)
dis_voltage{channel} =
[StepData{8,channel}(:,8);StepData{9,channel}(:,8)];
end

discharge_VoC = dis_voltage{1,1};
charge_VoC = ch_voltage{1,1};
SOC_dis = SOC_dis{1,1};
SOC_ch = SOC_ch{1,1};

toc
save('BattTestRawData_File','time_SOC','current_SOC','voltage_SOC')
save('BattDataFile','SOC_ch','SOC_dis','ch_voltage','dis_voltage','cap')
save('VoC_data','SOC_ch','SOC_dis','discharge_VoC','charge_VoC')

33
Matlab – Look up Tables
% This script loads the charge and discharge measured voltages of a Plazma
% 5.3 Ah (nominal capacity) and generates a table of VoC as a function of
% SOC

clear all
close all
clc

% Uploading the experimental saved from script ArbinBattTest


load('VoC_data')

% Due to the high sampling rate used during this experiment,there are
% repeated SoC values with non-unique voltage values. To avoid any
% conflicts, we are taking every ten SoC points and its corresponding
% voltage value.

rawSOC_ch = SOC_ch(1:10:end);
rawSOC_dis = SOC_dis(1:10:end);

rawVoC_ch = charge_VoC(1:10:end);
rawVoC_dis = discharge_VoC(1:10:end);

% Generating an evenly spaced SOC (0,1) with n datapoints

n = 20;
SOC_chVec = linspace(0,1,n);
SOC_disVec = linspace(1,0,n);
SOC_vec = SOC_chVec;

VoC_ch = spline(rawSOC_ch,rawVoC_ch, SOC_chVec);


VoC_dis = spline(rawSOC_dis,rawVoC_dis, SOC_disVec);
VoC_disbar = VoC_dis(end:-1:1);

% We define VoC(SOC) as the average voltage measured during charge


% and discharge

VoC = zeros(1,length(SOC_vec));

for i=1:length(SOC_vec)
VoC(i) = mean([VoC_ch(1,i) VoC_disbar(1,i)]);
end

%% Figures
figure(1)
grid on
hold on

plot(SOC_ch, charge_VoC,'ob')
plot(SOC_chVec, VoC_ch,'g')
plot(SOC_dis,discharge_VoC,'or')
plot(SOC_disVec, VoC_dis,'y')

34
plot(SOC_vec,VoC,'k','LineWidth',2)

xlabel('SOC')
ylabel('Voltage [Volts]')

legend('Exp. Charge', 'Spline Charge', 'Exp. Discharge', 'Spline


Discharge','VoC', 'location', 'East')

save('parameter_data','SOC_vec','VoC')

35
Battery Resistance Test
% This script imports the Excel file generated by an Arbin cycler during
% testing of lithium polymer cells and calculates its capacity

% Control Optimization Laboratory - RC Hybrid Car Project

% Last Edit: 11/26/2013 Sergio Mendoza & Ji Liu

% The channels in the cycler correspond to each of the following SOC's:

% Channel 2 = Battery Pack #1


% Channel 4 = Battery Pack #2

% Arbin Data:
% Col 1: Data_Point
% Col 2: Test_Time(s)
% Col 3: Date_Time
% Col 4: Step_Time(s)
% Col 5: Step_Index
% Col 6: Cycle_Index
% Col 7: Current(A)
% Col 8: Voltage(V)
% Col 9: Charge_Capacity(Ah)
% Col 10: Discharge_Capacity(Ah)
% Col 11: Charge_Energy(Wh)
% Col 12: Dischare_Energy(Wh)
% Col 13: dV/dt(V/s)
% Col 14: Internal_Resistance(Ohms)
% Col 15: Is_FC_Data
% Col 16: AC_Impedance(Ohm)
% Col 17: ACI_Phase_Angle(Deg)
%%
clc
clear all
close all
%% Loading the file from Excel

tic
filename = '445Resistance'; %Name of the file being imported
channels = [2 4]; % Channels being analyzed
sheets = 3 ; %Number of sheets per channel in the original Excel file
num_ofsteps = zeros(1,length(channels)); % Preallocates space to have a
vector with the number of steps per channel

Arbin_data = cell(1,length(channels)); % Generates a cell with as many


entries as channels being analyzed
[r,c] = size(Arbin_data);
StepData = cell(25,c);%Preallocates space to have a cell with as many rows as
steps in the experiment and as many columns as channels being tested

%% Imports the data from the Excel file first into a cell containing all
steps per channel and then creates an array containing where each row is a
step and each column a channel
for i=1:length(channels)

36
Arbin_data(1,i) = arbin_excelimport_ver4(filename, channels(i), sheets);
num_ofsteps(1,i) = max(Arbin_data{i}(:,5));
StepData(:,i) = arbin_bystep(Arbin_data(1,i));
end
%% Generating Matrices for Time, Voltage and Current

time_SOC = cell(1,length(channels)); %Preallocates space to have a cell


containing the test time signal for each channel
current_SOC = cell(1,length(channels)); %Preallocates space to have a cell
containing the current signal for each channel
voltage_SOC = cell(1,length(channels));%Preallocates space to have a cell
containing the voltage signal for each channel

for j = 1:length(channels)
time_SOC{j} = Arbin_data{j}(:,2);
current_SOC{j} = Arbin_data{j}(:,7); % time, current and voltage cells for
the entire duration of the experiment
voltage_SOC{j} = Arbin_data{j}(:,8);
end

%% plots

color = [0 0 1;
0 .5 0];

figure(1)
subplot (2,1,1) % Plot of the voltage profile during the entirety of the
experiment
hold on
for plots = 1:length(channels)
plot(time_SOC{plots}./3600,voltage_SOC{plots},'Color',color(plots,:))
title ('Experimental Voltage Profile for a LiPo Battery Pack','FontSize',12)
xlabel('time(h)','Color','Red','FontSize',12)
ylabel('Voltage(V)','Color','Blue','FontSize',12)
legend('Pack#1','Pack#2')
end

subplot (2,1,2) %Current Subplot


hold on
for plots = 1:length(channels)
plot(time_SOC{plots}./3600,current_SOC{plots},'Color',color(plots,:))
title ('Experimental Current Profile for a LiPo Battery Pack','FontSize',12)
xlabel('time (h)','Color','Red','FontSize',12)
ylabel('Current (A)','Color','Blue','FontSize',12)
legend('Pack#1','Pack#2')
end

%% Creating voltage and current matrices to calculate battery resistance


% Charging steps: [5:2:23] CC -Charge - To determine the Resistance we
% apply Ohm's law (R = DeltaV/I), Relaxation periods of 1 hour follow each
% charging interval, the resiatance for this experiment is defined ad the
% change in voltage immediately after and before any current is applied
% divided by the magnitude of the applied current

37
voltageH = zeros(10,2);
voltageL = zeros(10,2); % Preallocates vector space
current = zeros(10,2);
Resistance = zeros(10,2);

for j = 1:length(channels) % Generating a voltage vector with the voltage


values measured right after a current was applied to the battery pack
w = 1;
for k = 5:2:length(StepData)
voltageH(w,j) = StepData{k,j}(1,8);
w = w+1;
end
end

for j = 1:length(channels) % Generating a voltage vector with the voltage


values measured right before a current was applied to the battery pack
w = 1;
for k = 4:2:(length(StepData)-1)
voltageL(w,j) = StepData{k,j}(end,8);
w = w+1;
end
end

DeltaV = voltageH - voltageL; % Voltage difference (Vafter - Vbefore)

for j = 1:length(channels) % Generates a vector with the magnitude of the


current applied to the battery pack
w = 1;
for k = 5:2:length(StepData)
current(w,j) = StepData{k,j}(1,7);
w = w+1;
end
end

Resistance = DeltaV./current;

%% Spline fit

SOC = [0:0.1:1];
n = 20;
SOCvector = linspace(0,1,n);
ResistanceFit = spline(SOC,Resistance(:,2),SOCvector);

figure(2)
hold on
grid on
plot(SOC,Resistance(:,2),'bo')
plot(SOCvector, ResistanceFit,'Color',[0 0.5 0])

title ('Experimental Internal Resistance a LiPo Battery Pack','FontSize',12)


xlabel('SOC (%)','Color','Red','FontSize',12)
ylabel('Resistance (Ohm)','Color','Blue','FontSize',12)

38
toc

%Saving variables

save('Resistance_data','SOCvector','ResistanceFit')

39
Arduino Code for Motor Control – Power Management
/* Hybrid RC car Controller
ME 445 Final Project
Ji Liu
Sergio Mendoza

PIN Connection
2 pwm input signal from telemetry receiver (have to be pin 2, since we need to use the interrupt
3-5 Hall Effect Sensors
6-8 motor control high end
9-11 motor control low end
12 battery voltage read in
13 IC engine throttle servo in
A1 current sensor read in

connection:
sensors
green-- 3
orange--4
white-- 5
wires connecting to high end of the circuit
blue----6
orange--7
yellow--8
wires connecting to low end of the circuit
blue----9
orange--10
yellow--11

to do: add the current sensor V vs I relationship


*/

//#include <MemoryFree.h>

// PINS

40
const byte throt = 13;
const int inputpwm = 2;
const byte hall1 = 3;
const byte hall2 = 4;
const byte hall3 = 5;
const int battery_vol=12; // the pin used to read voltage for estimation
// voltage divider resistors
const int Rmeasure=1; // the resistor measured in the voltage divider
const int Rnotmeasure=1;// the one not measured
// charging parameters
int fixed_speed = 100; //the voltage for the servo to get IC engine's fixed speeds
const int idle_speed = 10;
const float soc_lb = 0.3;
const float soc_ub = 0.7;
const float Internal_R = 0.012; // we assume the internal resistance is not changing with time
float soc = 0;
float Voc = 0;
float I = 0;
const int current_sensor = A1;
float Vread=0;
unsigned long time;
const int delaytime = 100;
// interrupt paremeters
volatile unsigned long pwm_width;// variable to record the pwm signal
volatile unsigned long Time1;
volatile unsigned long Time2;

void setup()
{ //set pins to be inputs and outputs, initialize libraries, etc.
// pinmode for motor driver
pinMode(throt, OUTPUT);
pinMode(current_sensor, INPUT);
pinMode(hall1, INPUT);
pinMode(hall2, INPUT);
pinMode(hall3, INPUT);

41
pinMode(battery_vol, INPUT); // pinmode for the voltage lookup table
attachInterrupt(0, pwm_and_motor_drive, CHANGE);
Serial.begin(9600);
}

void loop () {
// first run the motor
// Serial.print("freeMemory()=");// used to check free memory since we have a lot of data
// Serial.println(freeMemory());
// sanety check: the pwm signal sent from remote controller ranges approximately from 1300 to 1900
microseconds
if (pwm_width>1900) pwm_width = 1900;
if (pwm_width<1300) pwm_width = 1300;

float dutycycle = map(pwm_width,1300,1900,0,1023);


byte hallposition=0; //variable to record the hall sensor signal
hallposition=sense();
if (dutycycle <682) turn(hallposition, dutycycle); //call function to turn the motor.
else
regenerativebraking(hallposition, dutycycle); //call function to do regenerative braking.
Serial.println(hallposition);
//********************************************************************************************
***************//
// second, check the Battery SOC: Vread = Voc+IR, where R is assumed to be constant

Vread=analogRead(battery_vol);// the divided voltage measured by Arduino


//now the Vread is converted into the "real voltage" of battery pack
Vread = Vread/Rmeasure*(Rmeasure+Rnotmeasure);
I = analogRead(current_sensor); // signal from the analog read
I = map(I,0,1023,0,5); // convert to the current value
// used for test
// I=0.1;
// Vread = 7.1793;
Voc = Vread -I*Internal_R;
// lookup table. this is gotten from experiment data.

42
float
lookup_V[]={6.2539,7.1793,7.3976,7.4535,7.5054,7.5429,7.5738,7.5985,7.6238,7.6557,7.6959,7.7473,7.8097,7.87
52,7.9353,7.9858,8.1094,8.1960,8.2894,8.3913};
float lookup_soc[]={0, 0.0526, 0.1053, 0.1579, 0.2105, 0.2632, 0.3158, 0.3684, 0.4211, 0.4737, 0.5263, 0.5789,
0.6316, 0.6842, 0.7368, 0.7895, 0.8421, 0.8947, 0.9474, 1.0000 };
int size = 20; // size of the lookup table
// float re = lookup_table( Vread, V, soc, size);
soc = FmultiMap( Vread, lookup_V, lookup_soc, size);
// Serial.println("one more time");
// Serial.println(soc,5);

/***********************************************
then check the SOC and determine the IC engine speed
if the soc<30%, the ic engine should be speeded up to the fixed, which should
be the most efficient speed in the IC engine's BSFC map. Here, we just choose a fixed rpm to
pretend that this speed is the most efficient speed.
*/
if (soc >= soc_lb) analogWrite (throt,idle_speed);
/* we want to charge the battery when soc<soc_lb and stop charging when soc>soc_ub */
if (soc < soc_lb)
{
if (soc < soc_ub)
{

float old_soc = soc;


analogWrite (throt,fixed_speed);
delay (delaytime); // delay 100s and test battery voltage again to see whether this speed is fast enough
soc = FmultiMap( Vread, lookup_V, lookup_soc, size);
// Serial.println(soc,5);
/* if the soc is still decreasing then we need to speed up the IC engine so that we have enough power
to charge the battery. to do this, we increase the speed by 10/255 (which is determined by us), and
delay 100s to see whether this speed is fast enough. if not, increase again until we can reach the maximum
*/
if (soc < old_soc)

43
{
fixed_speed = fixed_speed +10;
analogWrite (throt,fixed_speed);
delay(delaytime);
}
}
}
}

void pwm_and_motor_drive()
{
if ( digitalRead( inputpwm ) == HIGH )
{
Time1 = micros(); //get time of pulse going down
}
else
// if ( digitalRead( inputpwm ) == HIGH )
{
Time2 = micros(); //get time of pulse going up
pwm_width = Time2 - Time1; //measure time between down and up
}
Serial.println(pwm_width);
}

//function to determine position of motor.


byte sense(){
byte x=6; // local variable to be returned.
//Hall Position
byte hallin1 = digitalRead(hall1); // get reading from hall sensor one
byte hallin2 = digitalRead(hall2); // get reading from hall sensor two
byte hallin3 = digitalRead(hall3); // get reading from hall sensor three
// Put all of the readings into one variable, x.
bitWrite(x, 0, hallin1); //syntax is: [variable, bit to write to, value (0 or 1)]
bitWrite(x, 1, hallin2);

44
bitWrite(x, 2, hallin3);
//Serial.print("hall position "); Serial.println(x);
return x; //return hall sensor reading.s

//function to turn the motor


byte turn( int hall, int dc){
int coilout[]= {0, 8, 6, 8, 7, 7, 6}; // array for the current in transistors
int coilin[]= {0, 9, 10, 10, 11, 9, 11}; //array for the current out transistors.
// Serial.println("normal");

while (hall==0 | hall == 7) {


// Serial.println("error");
hall = sense(); // check if motor position had an error. Repeat until otherwise.
}
// Serial.print(coilin[hall]); Serial.println(" "); Serial.println(coilout[hall]);
//actually turn the motor
digitalWrite(coilout[hall], HIGH); //turn on current out transistor
analogWrite(coilin[hall], dc); // turn on the current in transistor with the proper duty cycle
delay(1);
digitalWrite(coilout[hall], LOW); //turn current in transistor off
digitalWrite(coilin[hall], LOW); //turn current out transistor off
}

// for regenerative braking


byte regenerativebraking( int hall, int dc){
digitalWrite(6,LOW);
digitalWrite(7,LOW);
digitalWrite(8,LOW);

int coillow[]= {0,11,9,9,10,10,11}; //array for the current out transistors.


int coilhigh[]= {0, 8,6,8,7,7,6}; // array for the current in transistors
while (hall==0 | hall == 7) {hall = sense();} // check if motor position had an error. Repeat until otherwise.
digitalWrite(coilhigh[hall], LOW); //turn current out transistor off

45
// Serial.print(coilin[hall]); Serial.println(" "); Serial.println(coilout[hall]);
analogWrite(coillow[hall], dc); // turn on the current in transistor with the proper duty cycle

delay(delaytime);
digitalWrite(coillow[hall], LOW); //turn current out transistor off
}
// function for lookup table.note: the in array should have increasing values
float FmultiMap(float val, float * _in, float * _out, byte size)
{
delay(delaytime);
// take care the value is within range
// val = constrain(val, _in[0], _in[size-1]);

if (val <= _in[0]) return _out[0];


if (val >= _in[size-1]) return _out[size-1];

// search right interval


uint8_t pos = 1; // _in[0] allready tested

while(val > _in[pos]) pos++;


// this will handle all exact "points" in the _in array
if (val == _in[pos]) return _out[pos];

// interpolate in the right segment for the rest


return (val - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1];
}

46
Appendix C

Cutsheets

47
Losi-Ten Exploded View

48
B3536
A6956 A6956 A6277
B3570 B2403
B3563 B3570
B4210
B3568 B3553
B3542 B3542 A6299 A6229
B3571 B3568 B2504
B3569 B4210
Front B3553
B3572 B3542 B4023
Rear B3536
B3553 B4023
B3542 B4210
B3542 B4210
B3553 B3542 B3613 A6278
B3542 B3613
B3568
B3542 B3568 B3542
B3563 B3568
B3563 B4210 B3613
B3553
B3542
B3569 B2504 B3555 B3568 B3612
B3556 A6306 B2504
A8200 A6302
B4210 A6956 B3613 B3536 A6229
A6277 A6278
B2504 B3556
B3563 A6229
A6278 B2170
B3542 A6256 B4023
A6282 B2220 B3536
B2163 A6277 B4210
B3568 B3612 B2906
B5010
A6277 A6256 A8200
B3563 A6302 A6278
A6275
B3568 B2278 A2278 A6302 B2504 B2211
B3563 B2906 B3104 B2211 A6907 B4210 B4210 A2278 A6272
A6278 B2213
B2124 B3104 B2222
B6947 B2213 A6282 A6272
B4022 A6254
B2163 B3571 A6947
B2123 A6302 B4023
A6302 B3104
A6286 B2222
A6278 A6275 B3572 A6302
A6907
B4003 B5010 A6947
B4022
B2123
A6272 B4023
B4023 B4023
B3565 B2124 A6302 B3564 B4003
B3564
B2222 B3564 B2123 A6298 B4022
B3104 B4003 A6947 B4023
B3566 B3104 B3556
A6254 B3565 A3565
A6227 B4022 A6907 B3554
B2222 B3564 B4023 A6295 B3564 A6957
B2213 A6240 B2279 B2124 A6279
A6295 A6302 A6272 A6279
B2211 B4022 B2220 B3556
A6282 A6286
B2222 A6278 B3104 B2103
B4022 B5071
B2100 B4023 B2213 A6958
A6302 A6271
B2100 B2279 A6270 B2211
A6958 B3556
A6275 B4022 B4109
B4109 B4003 A6302
B2021 A6264 A6286 B2023
A6957 A6271 B3566 B3519
A6286 B4023 B2278 B4109
A6270 A6271
B2021 B4109 B3519
A6290 A6302 A6270
B3566

Radio Components LOSA99015 Losi Pit Apron............................................................................ 19.99


LOSB4109 Suspension Hinge and King Pin Set (10): 10-T.............. 11.99 LOSB7013 320S Force Wheels, Chrome (2): 10-T............................... 10.99 LOSA6254 2-56 x 1/4 Cap Screws...............................................................2.99 LOSA6298 8-32 x 1/8 Setscrews..................................................................2.99 LOSB0818 MSX Digital Servo.................................................................... 16.99 LOSA99104 Losi Wrench Set US (4 pc)..................................................... 52.99
LOSB4210 Steering/Throttle/Brake Link Set: 10-T...............................9.99 LOSB7213 320S Zombie Max Tires with Foam (2): 10-T.................. 16.99 LOSA6256 4-40 x 1/2 Button Head Screws ............................................1.99 LOSA6299 5-40 x 1/8 Setscrews..................................................................2.99 LOSB0819 MSX Digital High Torque Servo.......................................... 26.99 LOSA99165 Aluminum Turnbuckle Wrench.............................................6.99
LOSB5010 Fuel Tank (Assembled): 10-T................................................ 15.99 LOSB7413 320S Zombie Max Tires with Force Wheels (2): 10-T... 28.99 LOSA6264 8-32 x 3/8 Flat Head Screws...................................................2.99 LOSA6302 5-40 Steel Lock Nuts..................................................................1.99 LOSB0899 On/Off Switch..............................................................................4.99 LOSA99167 Losi Tuning Screwdriver........................................................ 17.99
LOSB5033 Engine Mount and Hardware Set: 10-T .............................8.99 LOSB8026 Ten-T Green/Black Body with Stickers: 10-T................... 49.99 LOSA6270 5-40 x 3/8 Flat Head Screws...................................................2.99 LOSA6350 #4 and 1/8-Inch Hardened Washer (24).............................3.99 SPMSR3300T DSM 3Ch Receiver with Telemetry..................................129.99 LOSA99772 Camber Gauge......................................................................... 10.99
LOSB5068 Tuned Pipe – Losi 3.4 Engine.............................................. 39.99 LOSB8027 Ten-T Orange/Black Body with Stickers: 10-T................ 49.99 LOSA6271 5-40 x 1/2 Flat Head Screws...................................................2.99 LOSA6907 5 x 8 x 2.5 Ball Bearing (2)........................................................7.99 LOSA99173 Ride Height Gauge................................................................. 10.99
Option Parts
LOSB5069 Exhaust Coupler and Header Spring 3.4 Engine.............3.99 LOSB8154 Rear Wing and Hardware: 10-T..............................................7.99 LOSA6272 5-40 x 3/4 Flat Head Screws...................................................3.99 LOSA6947 5 x 11 Sealed Ball Bearings (2)............................................ 13.99 LOSA99174 Car Stand.................................................................................... 14.99
LOSB5070 Exhaust Header 180° : 10-T.................................................. 17.99 LOSB9633 LiPo Charger 110-240V with US Plug: 10-T.................... 14.99 LOSA6275 5-40 x 5/8 Flat Head Screws...................................................3.99 LOSA6954 5 x 10 x 4 Sealed Ball Bearings (2)........................................6.99 LOSA5224 Certified Shock Fluid 30 wt.....................................................3.99 LOSA99202 Losi-Lok Thread Lock, Blue......................................................4.99
LOSB5071 Exhaust Pipe Mount and Wire: 10-T.....................................4.99 LOSB9818 LiPo Battery 7.4V/1000mAh: 10-T...................................... 29.99 LOSA6277 5-40 x 3/8 Button Head Screws.............................................4.49 LOSA6956 12 x 18 x 4 Sealed Ball Bearings (2)......................................7.99 LOSA5225 Certified Shock Fluid 35 wt.....................................................3.99 LOSA99203 High-Pressure Black Grease....................................................3.99
LOSB5115 ROSS Starter Unit Assembled............................................. 29.99 LOSA4006 Antenna Kit...................................................................................1.99 LOSA6278 5-40 x 1/2 Button Head Screws.............................................4.49 LOSA6957 10 x 15 x 4 Sealed Ball Bearings with Plas. Ret.(2)..........6.99 LOSA5226 Certified Shock Fluid 40 wt.....................................................3.99 LOSA99217 Nitrotec Cleaner Spray.............................................................7.49
LOSB5116 ROSS Starter Housing and Hardware Set..........................6.99 LOSA6204 4-40 x 1/2 Cap Screws...............................................................1.99 LOSA6279 5-40 x 3/4 Button Head Screws.............................................4.49 LOSA6958 6 x 12 x 4 Sealed Ball Bearings with Plas. Ret.(2).............5.99 LOSA7688 320 Series Eclipse Tires with Foam (Red)........................ 18.99 LOSB2960 Front and Rear Spring Set (4)–Soft (Silver): 10-T.............6.99
LOSB5117 ROSS Starter Gear Set (9)...................................................... 11.99 LOSA6206 4-40 x 3/8 Cap Screws...............................................................1.99 LOSA6282 5-40 x 7/8 Button Head Screws.............................................4.49 LOSA9150 Air Cleaner Set: 8B/T/10-T.................................................... 10.99 LOSA7689 320 Series King-Pin Tires with Foam (Red)..................... 18.99 LOSB2962 Front and Rear Spring Set (4)–Firm (Gold): 10-T..............6.99
LOSB5118 ROSS Starter One-Way Set................................................... 16.99 LOSA6227 4-40 x 1/8 Set Screws................................................................2.49 LOSA6286 5-40 x 5/8 Button Head Screws.............................................4.49 LOSA9151 Air Cleaner Foams - Oiled: 8B/T/10-T..................................8.99 LOSA99004 Losi Cargo Bag.......................................................................... 69.99 LOSB5201 Losi Fuel Bottle 500cc...............................................................9.99
LOSB5120 ROSS Starter Motor with Pinion Gear.............................. 11.99 LOSA6229 4-40 x 3/8 Button Head Screws.............................................2.49 LOSA6290 8-32 x 1/2 Button Head Screws.............................................4.49 LOSA99006 Losi Pit Roller Carrier/Pit Boxes........................................149.99 LOSB7014 320S Force Wheels, Black Chrome (2): 10-T.................... 12.99
LOSB5121 ROSS Starter Control Unit.................................................... 29.99 LOSA6240 5-40 x 1/2 Cap Screws...............................................................2.99 LOSA6293 8-32 x 1/4 Setscrews..................................................................3.99 LOSA99007 Losi 1/10 Truck Diaper/Car Carrier.................................... 24.99 LOSB8025 Ten-T Clear Body with Stickers: 10-T................................. 29.99
LOSB5122 ROSS Engine Wiring Harness.................................................5.99 LOSA6250 4mm and 5mm Setscrews.......................................................2.99 LOSA6295 10-32 x 3/8 Setscrews...............................................................3.49 LOSA99013 Losi Large Pit Mat.................................................................... 21.99
B5117
B5122 B2904 B5120 B5117
B5121 B2904
B5116

B2360 B2906
B2906
A6206 B5120
B2360
B5116
B2845 B5116
B2845 B2960 B5117
B2960
Soft
Soft
B2360 B2961 B5118
B2961 B2826 B5116
Medium
B0899 Medium
B5116
B2360 B2825 B2962 B5116
B2962
A6204 Firm B2906
B5121 Firm
B2360
B2906 B2904
B5116
B2904 B5116
A6350 B5118
SPM1452 B5117
A4006
B4210
B2905
B2360 B2905
SPMSR3300T

B2360 B7213
A2360 B2360

B0818 B2904
B2904 B2904
B2904
B2360
B0819

B7013 #800-0410/17051 Printed 09/2009


© Losi, A division of Horizon Hobby, Inc. Not Responsible for Typographical Errors All Prices Subject to Change Without Notice
R2100

B7413
TEN-T Exploded View and Price List
Ten-T Replacement Parts LOSB3312 Flywheel and Collet: 10-T........................................................9.99
B5070 B3991 LOSB3313 Clutch Nut and Hardware: 10-T.............................................4.99
LOSB2021 Front Suspension Arm Set: 10-T............................................9.99
LOSB2023 Rear Suspension Arm Set: 10-T.............................................9.99 LOSB3314 Clutch Shoes and Spring: 10-T...............................................9.99
LOSB2100 Front Spindles and Carriers: 10-T....................................... 10.99 LOSB3348 17T Clutch Pinion Gear: 10-T..................................................4.99
LOSB2103 Rear Hubs and Spacers: 10-T..................................................7.99 LOSB3370 Clutch Bell (Only): 10-T.......................................................... 10.99
LOSB2123 Steering Bellcrank Set: 10-T....................................................5.99 LOSB3519 Wheel Hex Drive Set: 10-T . ....................................................4.99
LOSB2124 Steering Posts/Tube and Hardware: 10-T..........................9.99 LOSB3536 Center Differential Mount and Shock Tool Set: 10-T......4.99
A9151 LOSB3542 Differential Housing and Seal Set: 10-T..............................5.99
LOSB2163 Front Shock Towers and Caps: 10-T.....................................9.99
A6270 LOSB2170 Rear Shock Tower: 10-T......................................................... 11.99 LOSB3553 Center Differential Outdrive Set: 10-T.................................8.99
B5033 LOSB2211 Front and Rear Pin Mount Cover Set: 10-T........................3.99 LOSB3554 Center Rear CV Driveshaft Assmembly: 10-T................. 11.99
A9150 B3312 LOSB3555 Center Front CV Driveshaft Assmembly: 10-T . ............ 11.99
B2403 A6271 LOSB2213 Pivot Pin Mount Set (4): 10-T..................................................8.99
LOSB2220 Side Guard Set: 10-T..................................................................6.99 LOSB3556 Center CV Driveshaft Couplers: 10-T...................................4.99
B5033 LOSB2222 Front and Rear Sway Bar Set: 10-T..................................... 16.99 LOSB3563 F/R Differential Outdrive and Hard Set (2) .......................8.99
LOSB2278 Chassis Brace and Spacer Set (3): 10-T................................5.99 LOSB3564 F/R CV Driveshafts and Couplers (2): 10-T...................... 21.99
LOSB2279 Steering Drag Link and Hardware: 10-T.............................7.99 LOSB3565 F/R CV Couplers (4): 10-T.........................................................5.99
LOSB2286 Main Chassis Plate – Aluminum: 10-T.............................. 64.99 LOSB3566 F/R Axles (2): 10-T.......................................................................5.99
B5069
LOSB2360 Radio Tray and Hardware Set: 10-T................................... 12.99 LOSB3568 Differential Seals/Pin and Gasket: 10-T...............................4.99
B3370 LOSB2403 Fr Bumper and Tank Guard Set: 10-T...................................4.99 LOSB3569 Differential Gear Set with Hardware: 10-T.........................9.99
B3314 LOSB2504 Wing and Body Mount Set: 10-T...........................................4.99 LOSB3570 48T Center Spur Gear and Screws: 10-T.............................4.99
LOSB2825 Front Shock Body Set: 10-T.................................................. 13.99 LOSB3571 Front Ring and Pinion Gear Set: 10-T............................... 23.99
B3313 A6264
B3313 LOSB2826 Rear Shock Body Set: 10-T.................................................... 14.99 LOSB3572 Rear Ring and Pinion Gear Set: 10-T................................. 23.99
B5068 A6264 LOSB2845 Front and Rear Shock Shaft Set: 10-T..................................9.99 LOSB3612 Brake Disc Set (2): 10-T.............................................................7.99
A6264 A6270
LOSB2904 Front and Rear Shock Plastics & Ball Set (4): 10-T...........6.99 LOSB3613 Brake Pads, Cams, and Hardware: 10-T............................ 14.99
LOSB2905 Front and Rear Shock Boots (8): 10-T..................................5.99 LOSB3991 M5 Locknuts (6) RH Thread: 10-T..........................................3.49
A6954 A6250 LOSB2906 Shock Rebuild Set (2): 10-T.....................................................4.99 LOSB4003 F/R Camber/Steering Turnbuckle Set: 10-T.................... 12.99
B3348 LOSB2961 Front and Rear Spring Set (4)–Med (Black): 10-T.............6.99 LOSB4023 Rod End Set (15): 10-T...............................................................5.99
LOSB3104 Front and Rear Gearbox Set: 10-T.........................................8.99 LOSB4022 Camber and Steering Ball Set (12): 10-T.......................... 11.99
Spektrum Radio Communication System

51
DX3S Quick Start Guide 4. Bind Transmitter to the Receiver
A. Insert the bind plug in the receiver’s BIND port.
6. Check Servo Direction
Turn on the transmitter followed by the receiver and check that the
B. Power the receiver through any port that is not a 3.3V telemetry port. direction of each channel is correct by rotating the steering wheel,
1. Install Receiver Warning: Do not power the SR3300T through the LAP, TEMP, or slightly pulling the throttle trigger and switching the aux channel, if
RPM port. The receiver will be damaged! applicable. (Nitro engines should not be running.) Use the REVERSE
screen of the DX3S to change the direction of any channel.
Nitro - Rx uses a receiver pack for power. Electric - Rx uses the ESC for power.
Note: If servo reverse direction is changed, rebind to re-establish the
fail-safe positions.

SR3300T installation SR300 installation

Mount the receiver in the vehicle using double-sided servo tape.


C. Turn on the transmitter and make sure the transmitter is in the
Although the antenna on a DSM receiver is shorter, it is still desired model number that you intend to use. Using the rolling
important to route the antenna in an antenna tube so that it is up away selector, go to the BIND screen and highlight bind. Rotate the
from the vehicle. The antenna tube also provides protection for the Rolling Selector to highlight BIND.
antenna so that it doesn’t get damaged.

2. Install AA Alkaline Batteries (included)


Note: Observe polarity when installing batteries.
7. Check Servo Travel
With the transmitter and receiver powered on, check the travel of each
servo by fully rotating the steering wheel, pulling/pushing the throttle
trigger and switching the aux channel if applicable. (Nitro engines
should not be running.) Use the TRAVEL screen of the DX3S to change
the amount of travel of each channel if more or less travel is necessary.

D. With the steering wheel, throttle trigger and aux channel (if
applicable) in the desired preset failsafe positions (normally full brake
and straight steering), press the rolling selector to initiate the bind
3. Using the Rolling Selector process and to store the failsafe positions.
The Rolling Selector is pressed to access functions and rolled to select E. BIND will flash for a few seconds then stop, indicating the process is
specific features or to change settings or values. Pressing and holding complete. The LED on the receiver should now be solid, indicating a
the Rolling Selector for more than 3 seconds returns the display to the successful bind has taken place.
main screen. F. Once the bind process is complete and before power is cycled on the
receiver, remove the bind plug and store it in a convenient place.
Failure to do so will result in the receiver going back into bind mode.

5. Plug in Servos to the Receiver
Plug in servos to the appropriate port on the receiver. Be sure the
polarity of the connections is correct. For information on installing
telemetry sensors (SR3300T only), please see the “Installing the
Telemetry Sensors in Your Vehicle” section of the DX3S User Guide.

AUX
Throttle Leaders in Spread Spectrum Technology
Steering
Bind The Spektrum trademark is used with permission of Bachmann Industries, Inc.
13955
Novak Brushless DC Motor

53
Plazma LiPo Battery Pack

58
12/17/13 #101942 Plazma 7.4v 5300mah 30c Lipo Battery Pack

We use cookies to help make this website better. You can change your cookie settings at any time. Otherwise, we'll assume you're OK to continue. For more
information about this site's cookies, read our Privacy Policy

enter search terms here... SEARCH choose your location:

HOME NEW S CARS ACCESSORIES SUPPORT W HERE TO BUY MEDIA COMPANY

#101942 - PLAZMA 7.4V 5300mAh 30C LIPO BATTERY RELATED PRODUCTS


PACK

Plastic Hard Case with Deans Plug & JST-XH Balance


Lead

The range of HPI Plazma batteries is designed to be the ideal match for all
HPI kits that require a long-lasting, reliable battery pack featuring reliable
lithium-polymer (LiPo) cell technology. These LiPo battery packs are ideal #101943 PLAZMA 11.1V
for kits that demand loads of power, like the E-Firestorm Flux, Sprint 2 3800mAh 30C LIPO BATTERY
PACK
Flux as well as kits like the Cyclone S, Cyber, and others!

Voltage: 7.4V Capacity: 5300mAh Current Rating: 30C Case Type:


Rectangular LiPo case, sealed plastic Dimensions: 138.6 x 45.8 x
THIS PART FITS THE FOLLOWING KITS / PARTS: 25.1mm (conforms to BRCA 139 x 47 x 25.1mm) Plug: Deans Ultra Plug
for extreme high-power applications Note: LiPo batteries must be charged
Blitz Flux RTR OPT with a LiPo-compatible charger such as the #101971 - HPI Reactor 500
RTR Sprint 2 Flux w/ 2010 Chevrolet OPT Charger This battery will fit most HPI and Hot Bodies electric kits,
Ken Block WR8 with Ford Fiesta H.F... OPT however there are special considerations to note: Sprint 2 - the front drive #101941 PLAZMA 7.4V 4000mAh
belt will rub on the battery case, so one of the round case LiPo batteries 20C LIPO BATTERY PACK
RTR WR8 Flux Rally OPT
is a better choice Vorza - two batteries fit best laid side by side HB Ve8 -
RTR Super 5SC Flux OPT two batteries fit best laid on top of each other
Savage XS SS OPT
RTR APACHE C1 OPT

RTR APACHE SC OPT Check out the full range of HPI Plazma batteries!
RTR Sprint 2 Flux w/ Porsche 911... OPT
Plazma Ni-MH Range
RTR Sprint 2 Flux w/ BMW M3 OPT #101940 PLAZMA 7.4V 3000mAh
#106388 PLAZMA 7.2V 4700mAh Ni-MH Battery Pack 20C LIPO BATTERY PACK
RTR Bullet ST Flux w/ 2.4GHz OPT #106390 PLAZMA 8.4V 3300mAh Ni-MH Battery Pack
RTR Bullet MT Flux w/ 2.4GHz OPT #101935 HPI Plazma 4.8V 4300mAh NiMH Stick Pack
RTR Savage XS OPT #101931 HPI Plazma 7.2V 2400mAh NiMH Stick Pack
#101932 HPI Plazma 7.2V 3300mAh NiMH Stick Pack
RTR E-Firestorm 10T Flux OPT
#101933 HPI Plazma 7.2V 4300mAh NiMH Stick Pack
Savage Flux HP RTR 2.4GHz OPT

SHOW DISCONTINUED KITS Plazma LiPo Range


#106397 PLAZMA 7.4V 8000mAh 35C LiPo Battery Pack #106397 PLAZMA 7.4V 8000mAh
#106401 PLAZMA 11.1V 3200mAh 95C LiPo Battery Pack 35C LiPo Battery Pack 59.2Wh
#107222 PLAZMA 11.1V 5600mAh 50C LiPo Battery Pack
SHARE
#107225 PLAZMA 14.8V 5100mAh 40C LiPo Battery Pack
#101940 HPI Plazma 7.4V 3000mAh 20C Lipo Round Case Stick Pack
#101941 HPI Plazma 7.4V 4000mAh 20C Lipo Round Case Stick Pack
#101942 HPI Plazma 7.4V 5300mAh 30C Lipo Stick Pack
#101943 HPI Plazma 11.1V 3800mAh 30C Lipo Stick Pack

PlazmaPro Competition Line!


#106399 PLAZMAPRO 7.4V 6500mAh 95C LiPo Battery Pack
#106400 PLAZMAPRO 7.4V 5600mAh 95C LiPo Saddle Pack
#101938 HPI Plazma 1.2V 2700mAh Nimh AA Re-Chargeable Batteries

Don't forget to check out our receiver packs which fit all nitro-powered
vehicles, plus our special Baja pack!
#101936 HPI Plazma 6.0V 1600mAh Nimh Receiver Pack
#101937 HPI Plazma 6.0V 4300mAh Nimh Baja Receiver Pack

www.hpiracing.com/en/part/101942 1/2
12/17/13 #101942 Plazma 7.4v 5300mah 30c Lipo Battery Pack

What is RC? About HPI Suppor t F.A.Q. Jobs at HPI Ter ms and Conditions Pr ivacy Policy

Copyr ight 2013 HPI Racing. All r ights r eser ved. Images may not be used without expr ess per mission.

www.hpiracing.com/en/part/101942 2/2
LT 1513 Battery Charger

61
LT1513/LT1513-2
SEPIC Constant- or
Programmable-Current/
Constant-Voltage Battery Charger
U
FEATURES DESCRIPTION
■ Charger Input Voltage May Be Higher, Equal to or The LT ®1513 is a 500kHz current mode switching regula-
Lower Than Battery Voltage tor specially configured to create a constant- or program-
■ Charges Any Number of Cells Up to 20V mable-current/constant-voltage battery charger. In addition
■ 1% Voltage Accuracy for Rechargeable Lithium to the usual voltage feedback node, it has a current sense
Batteries feedback circuit for accurately controlling output current
■ 100mV Current Sense Voltage for High Efficiency of a flyback or SEPIC (Single-Ended Primary Inductance
(LT1513) Converter) topology charger. These topologies allow the
■ 0mV Current Sense Voltage for Easy Current current sense circuit to be ground referred and completely
Programming (LT1513-2) separated from the battery itself, simplifying battery switch-
■ Battery Can Be Directly Grounded ing and system grounding problems. In addition, these
■ 500kHz Switching Frequency Minimizes topologies allow charging even when the input voltage is
Inductor Size lower than the battery voltage. The LT1513 can also drive
■ Charging Current Easily Programmable or Shut Down a CCFL Royer converter with high efficiency in floating or
U grounded mode.
APPLICATIONS Maximum switch current on the LT1513 is 3A. This allows
■ Charging of NiCd, NiMH, Lead-Acid or Lithium battery charging currents up to 2A for a single lithium-ion
Rechargeable Cells cell. Accuracy of 1% in constant-voltage mode is perfect
■ Precision Current Limited Power Supply for lithium battery applications. Charging current can be
■ Constant-Voltage/Constant-Current Supply easily programmed for all battery types.
■ Transducer Excitation , LTC and LT are registered trademarks of Linear Technology Corporation.
■ Universal Input CCFL Driver

U
TYPICAL APPLICATION Maximum Charging Current
WALL L1A*
• 2.4
ADAPTER C2**
INPUT + C3 7 4.7µF D1 † 2.2
22µF SINGLE Li-Ion CELL
VIN 5
25V VSW 1.25A 2.0 (4.1V)

CHARGE 1.8 DOUBLE Li-Ion


LT1513 L1B*
R1
CURRENT (A)

CELL (8.2V)
SYNC 6 2 1.6
S/S VFB •
AND/OR
1.4
SHUTDOWN SHUTDOWN GND VC IFB
R4 R2
+ C1
12V
22µF
4 TAB 1 3 39Ω 25V 1.2
×2 16V
R5 1.0
270Ω 20V
C4 R3 0.8
C5 0.22µF 0.08Ω
0.1µF 0.6 BATTERY
VOLTAGE
* L1A, L1B ARE TWO 10µH WINDINGS ON A
LT1513 • TA01 0.4
0 5 10 15 20 25 30
COMMON CORE: COILTRONICS CTX10-4
** CERAMIC MARCON THCR40EIE475Z OR TOKIN 1E475ZY5U-C304 INPUT VOLTAGE (V)
† MBRD340 OR MBRS340T3. MBRD340 HAS 5µA TYPICAL INDUCTOR = 10µH
ACTUAL PROGRAMMED CHARGING CURRENT WILL BE
LEAKAGE, MBRS340T3 50µA TYPICAL INDEPENDENT OF INPUT VOLTAGE IF IT DOES NOT
EXCEED VALUES SHOWN LT1513 • TA02
Figure 1. SEPIC Charger with 1.25A Output Current
sn1513 1513fas

1
Preventive Braking and Safety Control RC Car
ME 445 Final Project

By: Anil Senturk


Kevin Moyer

Prepared for: Dr. Henry Sommer III


Michael Robinson
The Pennsylvania State University
Department of Mechanical and Nuclear Engineering

12/16/2013
Table of Contents
1.0 Acknowledgments 3
2.0 Introduction 3
3.0 Overall Design 3
3.1 Mechanical Setup 3
3.2 Electrical Setup and Components 5
3.3 Software Programming 8
4.0 Testing and Results 12
5.0 Discussion 15
6.0 Conclusion 15
Appendix A Code 17
Appendix B Data Sheets 23

  2  
1.0 Acknowledgments
We would like to thank our professor Dr. Sommer for teaching us the various
aspects of mechatronics during ME 445, as well as our TA Michael Robinson for his help
throughout the semester with our labs and final project.

2.0 Introduction
The primary goal of this project was to create an RC car implementing
preventative breaking using an ultrasonic sensor, proportional throttle control, and three
driving modes. Our motivation was to apply the recent surge of research and
development within the automotive field in preventative braking and safety systems on a
smaller scale using our knowledge of mechatronics and Arduino that we developed
during the semester.
Volvo has been developing technologies to increase car safety called “Volvo Auto
Brake Technology,” and our goal was to use an ultrasonic sensor to replicate emergency
stopping by using proportional throttle control, as well as mimic accident prevention
technology when approaching an object. By using a potentiometer, another form of
proportional control for three different driving modes was implemented. These three
driving modes were economy, normal, and sport, which were inspired by the Honda CR-
Z, which had recently developed such driving modes. Economy mode would be
analogous to energy saving mode in a vehicle, and resulted in the lowest throttle response
when simulating the vehicle. Normal mode represented moderate throttle control, which
would be analogous to an everyday setting in a vehicle. The last mode employed was
sport, which would be similar to a performance setting in a vehicle, which allowed
quicker acceleration and throttle response.  
Our results showed that while there was some limitation due to sensor delay and
low sampling frequency of the ultrasonic sensor, throttle proportional control at each
driving mode was implemented and functioned correctly during testing. By recording the
revolutions per minute of the RC car wheels with each driving mode by using a
tachometer at various distances from the sensor, we confirmed that the coupled driving
modes and proportional control worked seamlessly. While testing indoors was difficult
due to the large sensor range due surrounding object interference, the implementation of a
brief burst or reverse throttle aided in stopping the vehicle consistently before both static
and moving objects.

3.0 Overall Design


3.1 Mechanical Setup
For this project, the team was given the body of a Traxxas RC car with a DC
motor powered by a NiMH battery. The base plate that was used to mount the
components was composed of hard plate foam board. A rear spoiler was used for
aesthetics composed of the same material to cover some of the wiring, as well as a
forward mounting portion for the ultrasonic sensor. The ultrasonic sensor was used to
determine the distance from the object was approaching was mounted on the front of the
vehicle. For the microprocessor, we used an Arduino Uno to run the subsequent code,
and interfaced with a Polulu motor driver for DC motor control. A FM receiver aided in
the throttle control and steering whose signals was then run through the Arduino for

  3  
proportional control. The RC servo motor was connected to each shaft of the front wheels
to rotate and turn the wheels, which was also processed through the Arduino code and
microcontroller. As for the tires, they are low profile rubber tires. For easy access to
change the driving modes, the potentiometer was mounted on the main base plate portion.
Lastly, a prototyping board was used to complete the various circuit connections.

Prototypingboard   FM Receiver   Ultrasonic Sensor  

Servo  

Motor Driver   Potentiometer   Arduino Uno  

Figure 1 – Mechanical Setup and Components

  4  
3.2 Electrical Setup and Components

Figure 2 - Project Schematic

Figure 2 above shows the schematic used in this project. The 7.6V NiMH batter
powers up the DC motor via the Pololu motor driver, as well as the Arduino to power it
remotely. Common 5V and GND pins were used to power the RC receiver, ultrasonic
sensor, motor driver, and steering servo of the vehicle.

  5  
Source:  http://www.pololu.com
Figure 3 - Pololu VNH 5019 Motor Driver

Typical electrical speed control is used along for this project with an RC receiver
to control the steering and throttle of the vehicle. However, the team wanted to invest
more time implementing a code that would work based on PWM values with proportional
control used in basic motor controls using Arduino. This component could be described
as being a more advanced version of the L293d, or similar to the other Pololu drivers
used in lab that could provide up to 30A of current along with up to a 24 V supply.

Source:  www.hobbyman.com.au
Figure 4 - HiTEC HFD-08RO FM Receiver

  6  
The team originally planned to use a Bluetooth interface to control the vehicle and
the software wirelessly. Due to the complexities of applying this kind of communication
between the Arduino and an Android cell phone, the traditional RC receiver was used..
Two channels for throttle and steering coupled with two outputs signals were connected
to the interrupt pins on the on the Arduino for processing.

Source:  http://arduino.cc/
Figure 5 - Arduino Uno

The Arduino UNO was implemented since it provided enough processing power
and input pins needed to complete this project. If another interrupt pin had been required,
or more PWM digital pins were needed, another microprocessor with better interrupt
capabilities and available pin would need to be used.

  7  
Source: http://www.adafruit.com
Figure 6 - HRLV-MaxSonar Ultrasonic Sensor

An ultrasound sensor was used to as it provides a larger sensing area and it also
can take measurements in different light levels. This model uses two different modes of
operation, which either sends pulses with varying lengths or analog voltage proportional
to the distance. The analog voltage mode was used as the team faced coding issues using
several pulse in codes that greatly limited sampling frequency. During testing, a major
disadvantage to this sensor was discovered that this component has a low sampling
frequency, which caused delays in distance measurements. Also, it had a minimum
reading of 28cm when a 5V power source was used which was another important factor
to consider while implementing proportional control

3.3 Software Programming


A flow diagram of the programming and logic involved in this project is seen below
in Figure 7, and descriptions of these aspects are continued within this section of the
report.

  8  
Figure 7 – Flow Diagram

The team aimed to create a reliable code that could be calibrated easily to fully
implement a safe driving controller for the RC car. The main portion of the Arduino
code involved the processing of the RC signal received by the interrupt pins on the
microcontroller chip. The RC remote controller sent pulses to the FM receiver, and the
width of these pulses determined the level of throttle in one channel, while the other
channel controlled steering with the use of an electric servo. The RC controller works by
sending a series of radio pulses about 50 times a second, with each pulse lasting between
one and two milliseconds. Typically, there is a gap of ten times the length of the longest
pulse in between each peak. The Arduino code receives the pulse train, processes it, and
generates a digital output to control the servo and DC motor on the vehicle.
The first version of the code created to control the vehicle steering and throttle
was done using the Arduino “pulseIn” function, which essentially measures the time of a
designated pin in its HIGH state. It is a very useful function used to measure the pulse

  9  
widths of many different types of pulsating signals. However, when more functions and
loops were implemented in the main code, the Arduino UNO failed to execute all the
loops at the same time. With the ability of the Arduino to perform 32,000 cycles in a two
millisecond time period, and the fact that the continuous “pulseIn” function is running
while the pulse is LOW, 320,000 cycles were used to only send a single useful full
throttle pulse. Nearly half of the processing power of the UNO was wasted by just
waiting for the next pulse to arrive and be completed, and was an unacceptable approach.
Considering two pulse functions, motor drivers and other loops executed to control the
vehicle dynamics, the entire code wasn’t able to perform at this stage without a solution.
In order to save a majority of the raw processing power and make the software
run smoothly, the interrupt capabilities for the Arduino was used. Our approach was to
use an interrupt function that calculated the pulse width only when the pin is set in HIGH
state. A separate timing function was created to measure the length of time whenever the
interrupt pin changes from LOW to HIGH and HIGH to LOW. These state changes were
used in the timing function that calculates the width of the incoming signal in HIGH
state. Figure 8 below illustrates the three different states of throttle where the pulse is in
HIGH state for a very short period.

Source:  http://rcarduino.blogspot.co.uk/2012/01/how-­‐to-­‐read-­‐rc-­‐receiver-­‐with.html  
Figure 8 - The shape of the pulse received by the microcontroller

In order to fully digitize the driving mode of the vehicle, a high performance
Pololu VNH 5019 motor driver was used to replace the traditionally used pulse width
based electronic speed control. This component requires a voltage source between 2.5V
to 5V, and used the same range for logic levels. The motor driver code used for this
project was very similar to the motor functions we created for the L293d motor drivers
created for previous lab work. The enable pin on the driver receives the PWM value
corresponding to a pulse width. The “Map” function is used to convert the pulse width
into the corresponding PWM value (i.e. Map(throttle_pulse_width, 1000, 2000, -255,
255)). Since the radio signal received by the Arduino generates some noise, this caused
the natural pwm throttle level to vary between -4 and 4 at natural level. However, this

  10  
noise is not noticeable when the DC motor is running at PWM values greater than 10.
The motor driver code was modified to make the motor run at values greater than 10 and
lower than -10 to reduce the electrical noise that this natural level.
The three different types driving modes were implemented to vary the maximum
PWM value delivered to DC motor, and this helps the user determine the maximum
speed and performance for the vehicle by simply rotating the knob of the potentiometer.
The potentiometer reads a value between 0-1024 which is mapped to the three
multiplying factors of 1, 2 and 3. These numbers are multiplied by a reduced PWM value
to provide maximum PWM values of 76, 153 and 230. With the addition of proportional
control within the code controlled by the distance to a target object from the front of the
vehicle, the PWM values was scaled further.
The steering of the remote controlled vehicle is controlled using “Servo.h” from
the Arduino library. Similar to the interpolation used for converting the pulse width into
corresponding PWM values to run the motor, the “Map” function converts the pulse
width into the corresponding angle turn of the wheels ((steer_pulse_width, 1000, 2000, 0,
179)). At the natural level (90 degrees) noise similar to the generated while the throttle
channel was observed. A similar noise reduction technique was used by employing “if”
statements that only rotates the servo above 95 and below 85 degrees. Noise levels were
also significantly reduced after the interrupt function were employed instead of “pulseIn.”
This happens because the microcontroller interrupt pin is only activated when the pulse is
received, which greatly reduces the number of cycles run on the designated pin, and
therefore provides more refined output. Additionally, an unexpected glitch was caused
when calling the servo library which disables pins 9 and 10 that were originally used to
attached the leads of the to a motor driver.
The ultrasound range detector used in the assembly has two different modes of
operation. It uses the shared 5V from the microcontroller as a source of power, and has
two main outputs of pulse width and proportional analogue voltage. Since the main part
of the code implemented two interrupt functions instead of the less desired pulse in
function, the analogue voltage output was used to measure the distance. The 5V supply
yields approximately 9.8mV/cm, and since the Arduino analog pin goes from 0 to 1024,
the output value has to be divided by 2 to get the actual distance in centimeters. To
increase the accuracy of the reading from the analog pin, the calculation was placed in a
loop that runs each distance calculation 20 times and then takes the average of the total
number and prints the average reading. Although the Arduino executes the loop
immediately this ultrasound detects change in distances in noticeable lag, which will be
discussed further within the testing portion of the report.
Below, Figure 9 shows the range of ultrasound range detection for the sensor. The
maximum range in width and length was suitable for our use. Several “if” statements
were used to implement a gradual reduction of vehicle speed until it stops. This portion of
the code was designed so that the vehicle would adjust its speed based on either a static
or a moving obstacle in front of it. Tests were also performed to recalibrate the settings so
that the vehicle would maintain a safe distance and stop to prevent an unwanted collision.
Automatic reversed throttle for breaking was included in the same portion of code as the
speed reduction portion. It works by applying a pulse of high throttle in reversed
direction in a narrow range to help the vehicle stop immediately as it approaches an
obstacle.

  11  
 

Source: http://www.maxbotix.com/Ultrasonic_Sensors
Figure 9 – Range of Ultrasonic Sensor

4.0 Testing and Results


To ensure that this project was working properly with the implimented software
and components, the team conducted various tests for the RC for proportional throttle
control and preventative braking. The first test we conducted once our propotional
control of the PWM signal associated with the throttle was complete. A tachomoter was
used to test each fo the driving modes, which also played an important role in scaling the
PWM signal for proportional control. As seen in Figure 10 below, a small band of
aluminum was placed on the rear wheel for detection by a tachometer. As seen in Figure
11 in which the speeds were recorded against the set distance used for proportional
control, the trends of each line were quite similar, and we could confirm our logic and
coding were correct. Values less than 45cm were ommited due to the fact that after this
threshold, preventive breaking would occur due to a short pulse of full throttle reverse,
and by the time the sensor reads 28cm (minimum sample length), the throttle power is
zero.

  12  
Figure 10 - Tachometer Test

Throttle  Proportional  Control  Test  


3500  
Wheel  Roation  Speed  (  RPM)  

3000  
2500  
2000  
1500  
1000  
500  
0  
40   50   60   70   80   90   100   110   120   130  

Distance  From  Sensor  (cm)  

Drive  Mode  1  (High)   Drive  Mode  2  (Medium)   Drive  Mode  3  (Low)  

Figure 11 - Proportional Control Test with Tachometer

After confirming the RC car functioned properly with proportional control at


associated distances, as well as driving modes, another key aspect to our testing was

  13  
performed by using both static and moving obstacles to test a more realistic scenario for
preventative braking. This was performed by driving the RC towards a shoe box to
simulate an obstacle (seen in Figure 12 below), and allowed a soft surface to impact if the
car slide during the troubleshooting process. We found after a few tests that while each
distance range and associated proportional scaling was correct, as well as the brief period
of revered throttle for breaking (35cm-30cm), the low sampling frequency of the
ultrasonic sensor was the only cause of error. The team conducted a few hours worth of
testing to choose the distance range for proportional control, and the main point of error
was the sensor sampling too slowly. Even with correct code to have the RC slow down
and brake before striking an object, the unreliability of the sensor resulted in some slow
collisions with the shoe box. Another issue was the slick tires and dusty floors of the
Reber building that caused a large amount of wheel slip. This is logical due to the high
power to weight ratio of RC cars, but testing on rougher surfaces like asphalt would have
been able to negate this factor during testing. A more refined method for braking could be
implemented by using derivative control, but after research into this idea, the team
determined that undertaking would be well out of the scope of this course. In subsequent
attempts of this project, this would be included, as well as an improved distance sensor
(ultrasonic or infrared).

Figure 12 - Static and Moving Obstacle Avoidance

  14  
5.0 Discussion
In the preliminary testing of our vehicle, we encountered a few problems with our
servo and subsequent servo code that resulted in changes that improved performance. We
noticed the servo was grinding during preliminary tests, and after opening it up,
determined that the spring used to protect the gears during rotation had fallen out, which
we then replaced to improve function and ensure future reliability.
Another issue that was resolved was from the servo code library that resulted in
the steering and throttle not running at the same time. Through debugging the motor
driver and subsequent code, it was determined that the servo code within the library
disabled digital pins 9 and 10, which were the pins we had originally used for the motor.
Once the pins were rearranged, we were able to drive and steer the RC using the
controller.
While running the code for servo control, we found that due to various factors of
noise in the system and surrounding, that the wheels would turn slightly back and forth
from the central resting position for forward movement while using the pulseIn function.
This issue was resolved by implementing the two available interrupt pins, as well as
limiting a range of central PWM values at the neutral position for the motor. As discussed
in the software programming section, these interrupt pins, along with the interrupt
functions, allowed the software to run more smoothly as it required less cycles to
measure the incoming widths of pulses, and aided in minimizing noise within out system.
Originally, we had considered using Bluetooth technology and applications from
and Android phone to primarily control the RC car, but due to program limitations,
control was ultimately transitioned to RC control and receivers. Currently, no application
that was applicable to Bluetooth communication could steer and provide throttle to the
vehicle at the same time. Due to this issue, and the need for seamless steering and driving
of an RC test vehicle, this preliminary idea was neglected.
It was unfortunate that the main issue the team had with this project was the
sensor not being able to sample at a high enough frequency with a more focused
detection field. Aside from the delay issues of the distance reading not being recorded
quickly enough to engage the various ranges of proportional control, another troublesome
aspect during testing indoors was the sensor detecting surrounding objects within the
laboratory. With this knowledge know, if our team were to revamp this project, a much
more powerful ultrasonic or infrared sensor would be used with a high sampling
frequency. However, we also considered other improvements that could be made to this
project in the future.
While the pulse of reverse throttle was helpful in stopping the vehicle, it was a
rather primitive approach, but was within the scope of our current understanding of
mechatronics. Derivative control for braking had been discussed, but upon some external
research, we had determined that this solution was far too complex with our current
knowledge. However, this would be a challenging and beneficial addition to further this
project in future adaptations.

6.0 Conclusion
Overall, the team was very pleased with the final result, and proud that the RC car
was able to implement our main objects of proportional throttle control, preventative
braking, and driving modes. Unfortunately, the ultrasonic sensor was a major limiting

  15  
factor in the overall performance during testing, and a sensor with a more focused visual
field and higher sampling frequency would alleviate the issues of delayed reading and
exogenous sensor interference. However, the vehicle slowed down each trial and
normally braked as planned during the approach of obstacles with the current sensor.
With the addition of a better sensor, the team is confident that the RC car with associated
software could perform as planned each trial.
Upon completion of this project, the team has gained a deeper appreciation for the
field of mechatronics, and felt that we gained a valuable skill set in microcomputer
interfacing throughout the semester. With the various labs where were learned important
debugging techniques and Arduino processes, as well as lectures to gain understanding
about the components we used for the project, we were able to gain confidence in
interfacing with electronics as mechanical engineers.

  16  
Appendix A: Code
 
//ME 445 FINAL PROJECT CODE
//KEVIN MOYER - ANIL SENTURK
//INTRUCTOR : Dr. Sommer
//TEACHING ASSISTANT: Michael Robinson

//Multi-RC-Channel Code Reference:


//http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
//This code enables an FM RC receiver to send two different signals from two channels
//Two interrupt functions were used to receive and process incoming signal
//A Pololu VNH 5019 high performance motor driver was used manipulate motor performance
through proportional control.

//Servo setup and Variables


#include <Servo.h> //Call Servo.h file from Arduino library to implement servo control
int sensorValue;
float limit;
int limit_value;
Servo myservo; //Designate name of servo
const int anPin = 1;
long anVolt, inches, cm;
int sum=0; //Create sum variable so it can be averaged
int avgrange=20; //Quantity of values to average (sample size)
int val;
float pwm;
int b;

//Defining throttle control communication


#define THROTTLE_SIGNAL_IN 0 //Use interrupt number in attachInterrupt
#define THROTTLE_SIGNAL_IN_PIN 2 //Use PIN number in digitalRead
#define NEUTRAL_THROTTLE 1500 //Duration of neutral throttle (microseconds)
#define NEUTRAL_STEER 1500 //For recalibration

//Defining steering control communication


#define STEER_SIGNAL_IN 1 //Use interrupt number in attachInterrupt
#define STEER_SIGNAL_IN_PIN 3 //Use PIN number in digitalRead

volatile int nThrottleIn = NEUTRAL_THROTTLE; //Set in the Interrupt and read within loop
once it is declared volatile
volatile unsigned long ulStartPeriod = 0; //Set interrupt
volatile boolean bNewThrottleSignal = false; //Set interrupt within loop

volatile int nSteerIn = NEUTRAL_STEER; //Set in the Interrupt and read within loop once
it is declared volatile
volatile unsigned long ulStartPeriod1 = 0; //Set interrupt
volatile boolean bNewSteerSignal = false; //Set interrupt within loop

  17  
//This loop sets the function calcInput to be called whenever digital pin 2 changes from
HIGH/LOW or LOW/HIGH
void setup()
{
pinMode(5,OUTPUT); //Motor B
pinMode(6,OUTPUT); //Motor A
pinMode(11,OUTPUT); //Enable Pin (11)
attachInterrupt(THROTTLE_SIGNAL_IN,calcInput,CHANGE); //Track changes in throttle
signal
attachInterrupt(STEER_SIGNAL_IN,calcInput1,CHANGE); //Track changes in steering
signal
myservo.attach(9);
Serial.begin(9600); //Serial communication at 9600 bits per second
}

void loop()
{
{
int sensorValue = analogRead(A0); //Read input on analog pin 0
limit=map(sensorValue, 0, 1023, 1, 3); //Set for 3 driving modes
}

//Ultrasonic Sensor Loop


//This loop is used to read in the analog voltage output that is being sent by the MaxSonar sensor
//Scale factor is (Vcc/512) per cm, and 5V supply yields ~9.8mV/cm
//Due to analog pin range (0 to 1024), the value must be scaled by 2 to get the actual cm value
for(int q = 0; q < avgrange ; q++)
{
anVolt = analogRead(anPin)/2;
sum += anVolt;
}
cm = sum/avgrange; //Length reading averaged from sensor
sum = 0; //Resets the sum

//Servo
val = map(nSteerIn, 1000, 2000, 0, 179);
stir(val);

/*Throttle proportional control was implimented within this loop, in which the pwm value was
scaled by set length ranges as well as driving mode. To account for
the inertia of the car approaching an obstacle, a brief 5 cm range in which full throttle reverse was
implemented while approaching the wall to aid in breaking. The resolution of the sensor limied
repeatability regardless of range or sample rate*/

//Motor
pwm = map(nThrottleIn, 1000, 2000, -255, 255); //PWM value throttle range for motor
pwm=pwm*0.3*limit; //PWM scaling according to driving mode and sensor
distance

//The following ranges scaled the pwm value that determined throttle power

  18  
if (cm > 120)
{pwm=pwm;
}
else if (cm<120 && cm>105)
{pwm=pwm*0.8;
}
else if (cm<105 && cm>90)
{pwm=0.6*pwm;
}
else if(cm<90 && cm>75)
{pwm=0.5*pwm;
}
else if(cm<75 && cm>50)
{pwm=0.3*pwm;
}
else if(cm<50 && cm>35)
{pwm=0.2*pwm;
}
else if(cm<35 && cm>30) //Brief reverse throttle portion for braking
{pwm=-65;
}
else if(cm<30)
{pwm=0;
}
function1(pwm);

if (pwm<-10) //Reverse throttle direction


{
function2(pwm);
}
//Serial.println(cm); //Display length by sensor in serial monitor
//Serial.println(limit);
//Serial.print(limit_value);
Serial.println(val);
//Serial.print("DEGREE");
//Serial.print(pwm);
//Serial.print("PWM");

//Serial.println(cm);

if(bNewThrottleSignal)
{
bNewThrottleSignal = false; //While nThrottleIn is true calcInput will not update
}

if(bNewSteerSignal)
{
bNewSteerSignal = false; //While bNewSteerSignal is true calcInput will not
update
}
}

  19  
//Throttle interrupt portion
//This function calculates the width of a HIGH state in microseconds which essentially returns a
pulse width
//between the values of 1000 and 2000
void calcInput()
{
if(digitalRead(THROTTLE_SIGNAL_IN_PIN) == HIGH) //Interrupt starts when pin is HIGH
{
ulStartPeriod = micros(); //Calculate time using micros
}
else
{
if(ulStartPeriod && (bNewThrottleSignal == false)) //ulStartPeriod time difference
calculation
{
nThrottleIn = (int)(micros() - ulStartPeriod);
ulStartPeriod = 0;
bNewThrottleSignal = true; //Communicates new throttle signal when set back
to false
}
}
}

//Steering interrupt portion


void calcInput1()
{
if(digitalRead(STEER_SIGNAL_IN_PIN) == HIGH) //Acts as interrupt when HIGH
{
ulStartPeriod1 = micros(); //Calculate time using micros
}
else
{
if(ulStartPeriod1 && (bNewSteerSignal == false)) //ulStartPeriod1 time difference
calculation
{
nSteerIn = (int)(micros() - ulStartPeriod1);
ulStartPeriod1 = 0;
bNewSteerSignal = true; //Communicates new steering signal when set back
to false
}
}
}

//Motor Control Loop


void function1(int b)
{ //This portion turns the motor CW
for(int k=1; k<500;k++)
{
if (b > 10)
{

  20  
analogWrite(11, b);
digitalWrite(5, LOW);
digitalWrite(6, HIGH);
}
else if(b < -10)
{ b = abs(b);
analogWrite(11, b);
digitalWrite(6, LOW);
digitalWrite(5, HIGH);
}
else //When zero the motor is set to stop
{ digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
}
}
}
void function2(int c)
{//This portion turns the motor CCW
for(int k=1; k<500;k++)
{
if (c > 10)
{
analogWrite(11, c);
digitalWrite(5, LOW);
digitalWrite(6, HIGH);
}
else if(c < -10)
{ b = abs(b);
analogWrite(11, c);
digitalWrite(6, LOW);
digitalWrite(5, HIGH);
}
else //When zero the motor is set to stop
{ digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
}
}
}

//A separate void was created to reduce noise created the in servo
void stir(int b)
{
for(int j=1; j<500;j++)
{
if (b > 95)
{
myservo.write(val);
}
else if(b < 85)
{
myservo.write(val);

  21  
}
else
{
myservo.write(90);//stop
}
}
}

/* Unused portion used for development of yaw control for a safer and more controlled driving
experience to be incorporated during second attempt at this project:
const int xpin = 0;
//const int ypin = 1;
//const int zpin = 2;// analog: Z axis output from accelerometer

const float arduino_power_supply = 5;


const float sensor_power_supply = 3.3;
const float zero_g_bias = sensor_power_supply / 2;

void setup() {
Serial.begin (9600);
Serial.print ("x axis g's");
Serial.print ("\t");
Serial.println();

}
void loop() {
float voltage_x = (analogRead(xpin)) * arduino_power_supply / 1024;
float x = (voltage_x - zero_g_bias) * 1000 / 330;
Serial.print(x);
Serial.print ("\t");
Serial.println();

}
*/

  22  
Appendix B: Data Sheets

  23  
  24  
  25  
  26  
PENNSTATE
College of Mechanical Engineering

Automated Pipe Outer Diameter Measurement Device


Final Report

December 13, 2013

Matt Tarabulski
Todd Troutman
Executive Summary
Team “Snakes in 2 Planes” was asked by Air Products Inc. to design and build a device capable
of measuring and storing/transmitting the outer diameter (OD) of pipes in a natural gas reforming
plant. The data the device collects will be used to study creep effects over the operating life of
the pipes to prevent catastrophic failure.

Our team gathered customer needs information on a site visit on Sept. 10th, 2013. The device will
be used on “pigtails”. Pigtails are pipes with many bends designed to accommodate expansion
due to high-temperature operation, with bend radii as small as 6.5 inches. Ideally the device will
not protrude more than 3 inches off of the surface of the pipe so that it will not be obstructed by
pigtail supports. The device must be able to climb vertically and measure OD within +/- 0.001
inches. Measurements must be taken in two planes. Data should be transmitted to a PC or stored
in the device in a format that is widely useable. The device should be battery powered.

The team surveyed patents related to pipe OD measurement and developed design concepts. Our
official brainstorming meeting occurred on September 18th. We later scored the concepts using
the Analytical Hierarchy Process to determine the optimal concept. The concept selected was a
device which uses rolling shafts which clamp onto the pipe using a spring suspension. The travel
of this suspension is measured by modified digital calipers, which allows for computation of the
OD. Solidworks was used to design the prototype from initial concept to the final machining and
construction drawings.

We took steps to mitigate risk which might have lead to unfavorable outcomes for both our team
and our sponsor. Technical risk was also reduced by creating an alpha prototype that exposed
some unforeseen problems before construction on the final prototype began. Risk of schedule
slip was mitigated by weekly meetings and weekly progress reports. Before making purchases
for the prototype, we consulted with Dr. Catanach to prevent budgetary losses.

Test Results of the prototype revealed a some target specifications were met. The device was
capable of very effective wireless communication, exporting to excel, a high level of accuracy of
measurement in 2 planes, as well as being under the 5 lb. goal weight, and within the 3" radius
specified by the customer. Due to unforeseen frictional effects and an overemphasis on power
conservation, the device was unable to climb vertically using the motors shown in this report.
Suggestions for improvement in further development cycles are included in Section 10.2

Overall, the project was a success and a valuable learning experience. A working prototype was
shown at the Design Showcase on December 12th, and afterwards it was delivered on-time and
on-budget to the Air Products sponsors, Eric Blanco and Ross Beidleman.

2
Table of Contents
Table of Contents ............................................................................................................................ 3
1.0 Introduction ............................................................................................................................... 5
1.1 Initial Problem Statement ............................................................................................................ 5
1.2 Objectives .................................................................................................................................... 5
2.0 Concepts .................................................................................................................................... 6
2.1 Concept Generation ..................................................................................................................... 6
2.2 Concept Selection ........................................................................................................................ 7
3.0 Final Design .............................................................................................................................. 9
3.1 Manufacturing Process ................................................................................................................ 9
3.2 Components and Component Selection ..................................................................................... 10
3.2.1 Electrical........................................................................................................................ 10
3.2.2 Drive Components......................................................................................................... 11
3.3 Schematics ................................................................................................................................. 12
3.3.1 Electrical Components .................................................................................................. 12
3.5 Testing ....................................................................................................................................... 13
3.5.1 Test Procedures ............................................................................................................. 13
4.0 Final Discussion ...................................................................................................................... 14
4.1 Construction Process ................................................................................................................. 14
4.1.1 Electrical........................................................................................................................ 14
Motor wiring .......................................................................................................................... 17
Wheel Encoder ....................................................................................................................... 17
4.2 Test Results and Discussion ...................................................................................................... 18
4.2.1 Accuracy........................................................................................................................ 18
4.2.2 Run Time Test ............................................................................................................... 18
4.2.3 Wireless Range Test ...................................................................................................... 19
5.0 Conclusions and Recommendations ....................................................................................... 19
5.1 Project Summary ....................................................................................................................... 19
References ..................................................................................................................................... 21
Appendix A: Gantt Chart .............................................................................................................. 22
Appendix B: Bill of Materials....................................................................................................... 23
Appendix C: Software Block Diagram ......................................................................................... 25
Appendix D: Code ........................................................................................................................ 26
Communication Protocols ............................................................................................................... 26

3
Arduino Code .................................................................................................................................. 27
Visual Basic Code ........................................................................................................................... 36

4
1.0 Introduction
1.1 Initial Problem Statement
Pipe systems that operate at a high temperature are susceptible to creep damage. One of the
methods of evaluating the onset of creep is by taking outside diameter measurements. This is
typically done by the use of "go/no-go" gages. These gages wear with use which renders them
inaccurate and do not allow real time OD data that can be tracked over time. A multidisciplinary
team of mechanical and electrical engineering students will design and prototype an automated
pipe crawler that can take OD measurements and crawl around bends.

1.2 Objectives

Physical  Ideally less than 3" high, radially from pipe surface
Constraints  Must negotiate pipe bends with radius as small as 6.5"
 Must be able to climb pipe vertically
 Measures orthogonally to axis of pipe even in bends, avoiding "out-of-
plane"

Applications  1" & 1.5" nominal pipe


 1 pipe size per plant, therefore 1 device per plant

Operation  May be manually moved along pipe or automatically driven


 Should be easy to apply to pipe due to cramped work area
 Must be battery powered
 Minimum process speed, 400 pipes in 3 weeks of operation (current rate)

Data  1 OD measurement per 1"


Acquisition  Data must be in 2 orthogonal axial planes, or 3 points on the
transverse plane
 Must take data for full length of the pipe
 The most critical data is from pipe diameters 3% larger than specified
OD
 Should transmit data wirelessly or store data
 Data should be in readily accessible format, Excel ideally

5
2.0 Concepts
2.1 Concept Generation
Concept A consists of two separable halves held tight to
the pipe with a spring force. Each half has a wheel that is
driven along the pipe.
As the outer diameter changes, the spacing between the
two halves increases as well. This distance will be
measured and recorded using a linear displacement sensor
such as a capacitive distance sensor, commonly found in
digital callipers, or a slide potentiometer.
A.
Concept B has two rings with grips on each ring. The
device will move when one ring has grasped onto the
pipe, the other ring will slide towards or away from the
ring depending on its location. Then the other ring will
grip the pipe while the first lets go, allowing it to move
forward. While it has gripped the pipe, it will measure
how far it had to move the grips to touch the pipe in order
to get a diameter measurement.
B.
Concept C consists of a hinged pair of rollers, which are
sprung together to provide a clamping force on the pipe.
When the spring, (seen on bottom right) is compressed,
the device will be able to slide onto the pipe. It uses steel
measurement wheels and urethane wheels for drive. A
potentiometer measures displacement of the wheels, on a
C. moment arm which magnifies the reading mechanically.
The center of gravity would be positioned as close as
possible to the center axis of the pipe, to provide stability
and traction.

Concept D consists structurally of a shell that hinges open


and closes. It has the ability to take non-contact
measurements. The optical sensing structure will stop at
each measuring point and move the sensing array along
the diameter to be measured. As the sensor passes the
pipe, the beam will be obscured. The processing unit will
then calculate the distance that the beam is obscured to
find the diameter. This will assure the diameter is found
through the property that the diameter is the longest chord
in a circle. The processing, drive, and energy storage will
be in a larger unit that will either follow or lead the
D. sensors pending orientation.

6
2.2 Concept Selection
Ease of
Cost Accuracy Speed Ease of Use
Construction

Component Positional Quick Off the shelf


Easy to Mount
Cost Accuracy Deployment components

Minimal
Maintenance OD Measurements Intuitive user
Welding/mach
Cost Measurement / minute interface
ining

Construction Simple
Cost Circuitry

Figure 2.1
In figure 2.1 the major design needs are listed in a hierarchy which allows for scoring the relative
importance of each need using a weighted average procedure called the Analytical Hierarchy
Process.

Ease of Construction
Ease of Use
Accuracy
Low Cost

Speed

Summary

Final Score
Weighting

Weighting

Weighting

Weighting

Weighting
Score

Score

Score

Score

Spool Concept 0.146 0.275 0.346 0.502 0.171 0.389 0.182 0.323 0.154 Score
0.377 0.398
Scissor Concept 0.146 0.233 0.346 0.245 0.171 0.389 0.182 0.409 0.154 0.353 0.314
Caterpillar Concept 0.146 0.383 0.346 0.180 0.171 0.069 0.182 0.143 0.154 0.211 0.189
Hinge-Wormgear Concept 0.146 0.110 0.346 0.073 0.171 0.153 0.182 0.125 0.154 0.059 0.099
Table 2.1

Table 2.1 shows the results of scoring using pairwise comparison, where each criteria was
compared with every other criteria. For example, A) Accuracy vs. B) Low Cost would produce a
score of 5 out of 10, which means A is significantly more important than B. This is reflected in
the weighting of accuracy which has the highest importance weighting at 0.346. Each concept is
then scored in terms of these criteria, and the weights are applied to produce a final score. The
spool roller concept won out because it was seen as easy to build, low cost and had the potential
for high accuracy.

7
Low Cost
0.600
0.500
0.400
0.300
Ease of Construction 0.200 Accuracy
0.100
0.000 Spool Concept

Scissor Concept

Caterpillar Concept
Ease of Use Speed
Hinge-Wormgear
Concept
Figure 2.2

Figure 2.2 shows a visual representation of the AHP Scoring process. The concepts with the
largest pentagon area scored the highest. The strength of the spool concept is clearly visible in
this visual.

8
3.0 Final Design
3.1 Manufacturing Process
Chassis
Left Vertical - Raw stock 12” x 12” x 3/8” plate 6061 aluminium
Cut to shape on water jet
Drill and tap #10-24 hole
Apply lock tight to hole and insert thread rod until seated
Install stationary roller bearings and drill rod with 2 bolts and nuts each

Right Vertical - Raw stock 12” x 12” x 3/8” plate 6061 aluminium
Cut to shape on water jet
Drill and tap #10-24 hole
Apply lock tight to hole and insert thread rod until seated
Slide linear bearings into place and fasten with 4 bolts and nuts each
Insert measuring assembly into place

Bottom Piece - Raw stock 12” x 12” x 3/8” plate 6061 aluminium
Cut to shape on water jet
Cut and tap (4) #10-24 holes

Top Piece - Raw stock 12” x 12” x 3/8” plate 6061 aluminium
Cut to shape on water jet

Body Assembly
Screw left and right verticals onto the bottom piece with the top of the bearings facing
inward using .5 in #10-24 fasteners

Drive System
Torsional Suspension Arm - Raw stock 12” x 12” x 3/8” plate 6061 aluminium
Cut main arm with water jet
Cut out motor mount with band saw
TIG Weld bracket to main arm
Drill through holes for hinge and wheel axle - 1/4"
Drill clearance holes for motor bracket through motor mount - #2

Motor
Mount motor to motor mount using motor bracket with #2 machine screws

Transmission
Mount drive-shaft adapter to wheel hub using wood screws
Affix drive-shaft to adapter using set screw

Wheels
Using belt sander, flatten the rounded tread profile of the stock wheels and affix to wheel
hub, and support using drive axle

9
3.2 Components and Component Selection
3.2.1 Electrical
Legend
Drive Drive  Caliper T indicates
7.2 measurement in the
Motor Motor
5VV
perpendicular plane
 Caliper II represents the
1.5 parallel plane
V

Motor Caliper Caliper


Control T II

Logic
Battery Arduino
Block

Figure 3.1 – Electrical Flow Chart


Arduino Mega
The Arduino family was chosen for its quick ability for rapid prototyping and its familiarity
among the group. The platform allows for quick implementation of a multitude of accessories
thanks to the wide range of software libraries and hardware shields available. The Mega version
was chosen over the Uno due to its expanded capabilities. The Mega version has 5 hardware
interrupts and 256 KB of flash memory for data storage. A selection of the code is provided in
Appendix G.

Digital Calipers
The digital calipers selected have a resolution of 0.0005" and are accurate to 0.001”. They
provide a serial output that can be interfaced with the Arduino to take readings.

2N3904 Transistors
The caliper signal output is a normal low 1.5 Vpp square wave. Through the use of a BJT
transistor and some pull up resistors output signal is inverted and the voltage is raised to a 5 Vpp
signal that registers on the Arduino. The 2N2222 transistor is also a viable candidate for the
application but the 2N3904 was selected on convenience since some were already on hand.

Wixel wireless USB


The wireless communication protocol of choice is Wireless USB which has a range of 50ft. Its
advantages lie in its extended range over Bluetooth and its ability to reprogram the Arduino
board over the wireless connection.
PC/Laptop
The pipe crawler will be controlled through the use of a Visual Basic Application running on a
laptop or tablet. This application will be able to be run on any version of windows and along

10
with either the wireless USB or Bluetooth it will be very transferable and able to be operated
from almost any computer. A selection of the code is provided in Appendix G.

3.2.2 Drive Components

Batteries
We selected 1200mAh Lithium polymer batteries available from Pololu.com. Batteries were
selected on the basis of size and energy storage density. Lithium polymer batteries were selected
due to high storage capacity and light weight.

Motor
We selected the Micro Metal Gear Motor with gear ratio 298:1 available from pololu.com.

Drive
Arm Motor Mount

Micro Metal Metal Gear Hub Adapter


Specification Gear Motor Motor
No-Load Free Axle
100 RPM 120 rpm Electric
Speed Motor
Torque 70 oz-in 50 oz-in
Stall
1.6 A 2.2 A
Current Rubber Motor
Weight 0.34 oz 3.1 oz Wheel Bracket
Figure 3.2 Motor Choice and Mount Design
As can be seen in the table above, the Micro motor offers similar performance with nearly 10X
weight reduction compared to a standard geared motor. The torque requirement was computed
assuming each motor (there are 2) should be strong enough to propel the device vertically. Each
micromotor will be capable of lifting 70(oz-in)*(1 lb./16oz)*1” radius = 4.375 lbs.

Figure 3.4 Full Assembly with drive arms, Arduino, and calipers

11
3.3 Schematics
3.3.1 Electrical Components

Figure 8.4 shows the logic bock circuitry. This is the array of transistors that will convert the 1.5
Vpp caliper signal into a 5 Vpp signal that the Arduino can read. As the caliper signal goes high,
it will turn on the BJT pulling the Arduino pin to ground. When the caliper signal falls, the BJT
will turn off and the pull up resistor will pull the Arduino pin to a high signal of 5V.

Figure 3.3: "Logic Block" Circuitry

Figure 3.5 shows the Visual Basic GUI that will handle the graphical display of the crawler’s
status, control options, and data handling and export. The current control scheme will be a
simple GO button that will put the crawler into measure mode and it will proceed to measure the
entire pigtail. The GUI will include a visual graph that will show a cross section of the pipe
alerting the operator of any large bulges immediately. The data exporting will also be handled
inside the app and will export the data into a CSV file that can be readily manipulated by most
data handling software packages.

Figure 3.5 GUI

12
3.5 Testing
Metrics to test:
1. Accuracy
2. Repeatability
3. Speed
4. Run Time
5. Wireless Range
6. Signal Conversion

3.5.1 Test Procedures

1. Accuracy
a. Randomly distribute 15 test points along a pipe.
b. Record the diameter at each mark using a digital caliper or comparable measuring
device accurate to 1/10000th of an inch.
c. Place Pipe Surfer at the first mark and record the reading.
d. Repeat until all marks have been measured.
e. All measurements taken must be within 1/1000th of the accepted diameter found
by the control measurement.

2. Repeatability
a. Using the same marks as test 1, take 30 measurements at each mark moving the
Pipe Surfer off of the spot by a minimum of 2 in and then back onto the
measurement spot.
b. Compare measurements to the actual values measured by a device accurate to
1/10000th of an inch. The difference should be no greater than 1%.

3. Speed
a. Run Pipe surfer at maximum speed setting of motors controller chip
b. Record time for a round trip along pigtails
c. Run the Pipe Surfer along pigtails at 10% of maximum speed setting and take
measurements
d. Run it along the pigtails 4 different speeds, (25%, 50%, 75%, Max) the final run
being the max speed of the motors. Run the pigtail along the pipe 5 times for each
speed level.
e. Compare the data of each run to that of the 10% run speed. The highest speed that
maintains accurate and repeatable data based off of previous tests will be the
maximum operating speed of the device.

4. Run Time
a. Run pipe surfer up and down the test pipe at set max speed until only 20%
remains in battery.
b. Recharge the battery and run test 9 more times.
c. Take the lowest time and set that as the run time.

13
5. Wireless Range
a. Connect pipe surfer to computer through wireless connection.
b. Move pipe surfer one foot away, linearly, from the computer every thirty seconds.
c. Watch for the connection to drop, record distance.
d. Repeat 4 more times for verification.
e. Wireless USB standard is 50 feet; the real world distance should be more than 45
feet.

6. Signal Conversion
a. Connect caliper to logic block.
b. Attach oscilloscope to both output and input side of logic block.
c. Compare input and output signals.
i. Correct operation will be an inverted 5 Vpp signal.
d. Repeat for all four circuits.

4.0 Final Discussion


4.1 Construction Process
4.1.1 Electrical

There are three main electrical components that need to be assembled; the shield, the motors and
accessories. There are two shields that need to be assembled, the Mega Protoshield that houses
the motor components and the Wixel shield that is has the wirelessUSB module and caliper
modules.

Mega Shield Components Mega Shield Wire Colors


1) Dual motor driver Blue – 7.2 V
2) Motor signal wires Red – 5 V
3) Motor connections Black – Ground
4) Motor drive wires Brown – Motor A
5) Power input Orange – Motor B
6) Voltage divider Green – Reference Voltage
7) Assorted wires

14
3
4

2 5
6

Mega Shield Motor Components

The first step in building the Mega Shield is to lay out the components. The wires, connectors
and components are then soldered into their respective places. The first item to solder is the 6
position right angle headers for the motor wires. These are placed at the end of the proto shield
angled to the top as shown. Next the motor control wires are soldered from pins 9, 10, 11, and
12 to the M1IN1, M1IN2, M2IN1, andM2IN2 pins on the motor driver chip, respectively. After
the motor control wires, the motor driver chip and motor power wires can be connected. The
pinout of the motor driver chip is as follows, top to bottom, M2OUT2, M2OUT1, Vin, Gnd,
M1OUT2, and M1OUT1. The pin out of the motor wire from left to right is; M1, M2, Vcc,
Encoder A, NC, and Gnd. The last component of the Mega shield is the voltage divider that the
Arduino to read the high 7-8.5V motor voltage. This is placed on the remaining three pads on
the bottom row of 4 hole pads.

Wixel Shield Components Wixel Shield Wire Colors


1) Caliper Connectors Blue – 1.5 V
2) Logic block Red – 5 V
a) Resistors Black – Ground
b) MOSFETs Yellow – Clock
c) Jumpers Green– Data
3) 1.5 V supply
4) Wixel wirelessUSB
5) Assorted wires

15
3 4
1 (Hidden)

2
1

Wixel Shield – Calipers, Logic, wirelessUSB

The wixel shield hosts the caliper connections, logic block, and wirelessUSB module. The
wirelessUSB module comes preassembled and simply needs to be plugged into its socket on the
right side of the shield. The caliper connections are two simple male 4 position connectors these
are soldered into the three position pads in the middle of the board on the left side. The pin out
from left to right is Gnd, Clock, Data, 1.5V. The logic block is to be repeated four times for both
clock pins and both data pins. The center of the block is the transistor. The base of the transistor
is connected to the signal pin through a 20 kOhm resistor the emitter is connected directly to
ground. On the collector pin, one connection is made to the Arduino digital pin corresponding to
the appropriate signal and a second one is made through a 20 kOhm pull up resistor to the 5 V
bus. The last component on the Wixel shield is the 1.5 V supply for the calipers. This is built
under the wirelessUSB module with a voltage divider that creates a 2.2 V level on the base of a
transistor. The transistor then clamps the voltage of the emitter to 1.5 V through a pull down
resistor to ground. The collector is connected directly to the 5 V bus.

There are several programming steps in the project including the Arduino, Visual Basic
application and the wireless USB modules. The code to program both the Visual Basic
application and Arduino microcontroller is located in Appendix H. The GUI layout and button
names are also included in Appendix H. The wirelessUSB modules are programmed with the
basic serial communication apps provided by Pololu.com. The only modification is the serial
data rate was changed to 115200 baud to enable wireless Arduino programming.

Two 3.7V 1200 mAh LiPo batteries were used to power the pipe surfer. They were wired in
series to provide a total of 7.2V and 1200 mAh and are the main source of power for the entire
device. The batteries were wired to a polarized connector to make it impossible to accidently
plug them in with reverse polarity and a standard LiPo charger was used to safely recharge them.

16
Motor wiring
The motor was driven using a Dual MC33926 from Freescale semiconductor purchased from
pololu.com. The team used jumpers to change the default states of the disable pins, and the
enable pin, so that PWM could be used on the logic inputs (M1 IN1/IN2 and M2 IN1/IN2) to
control the speed of the motor. Vin was supplied from the batteries at approximately 8.4V when
fully charged. Vdd (+5V) and GND came from the Arduino to run the TTL Logic on the chip.
Finally, the motors were connected via the outputs on the MC33926 to connections on the wheel
encoder chip which was mounted on the motor.

The Dual MC33926 Motor Driver

Wheel Encoder
The wheel encoder boards were connected via 6 pins each using a ribbon cable: 2 motor power
lines, +5V and ground, and encoder A and B outputs. These were read using interrupts which
triggered an increment function, allowing position and direction tracking of the device.

The Optical Wheel Encoders

17
4.2 Test Results and Discussion
4.2.1 Accuracy

The accuracy test procedures were followed to gain an understanding of how accurate the pipe
surfer is. Fifteen spots were marked on the pipe with chalk and then measure accurately with a
pair of unmodified calipers. The pipe surfer was then moved to each location and a measurement
was taken. This data was then analyzed to quantify the amount of error. It was calculated that the
pipe surfer has an average error of 0.11%. This is within the limits set for the device.

Accepted Device
Test Measurement Measurement
Point # (inches) (inches) Error (%)
1 1.682 1.6836 0.100744
2 1.688 1.6872 -0.06182
3 1.689 1.6917 0.189714
4 1.684 1.6846 0.024905
5 1.683 1.6857 0.143331
6 1.690 1.6931 0.172911
7 1.682 1.6801 -0.08532
8 1.685 1.6874 0.134704
9 1.686 1.6896 0.223822
10 1.682 1.6838 0.119099
11 1.689 1.6902 0.043235
12 1.689 1.6869 -0.10547
13 1.682 1.6812 -0.04153
14 1.685 1.6862 0.048873
15 1.689 1.6869 -0.10487

4.2.2 Run Time Test

The pipe surfer is powered by two 3.7V 1200mAh lithium polymer batteries in series. This
supplies 8.4V when fully charged. The test procedure was followed using a voltage divider to
measure battery voltage. The Arduino was coded to stop the surfer and throw up a warning when
the minimum battery voltage was reached which is 7V. Although these batteries provided
enough power to run the device, the run time was disappointing. A final average runtime was
calculated to be 9 minutes. It takes two and a half hours to charge the battery packs when
charged at 1000mA. With a pipe measurement time of 5 minutes a pipe, this only allows for 1
pipe before the batteries must be replaced and recharged. This is not practical for the real world
and a higher capacity battery would be needed. To run for 1 hour, 12 pipes, between battery
changes, an estimated capacity of 8400 mAh would be needed.

18
4.2.3 Wireless Range Test

This test was conducted in two environments to take into account the different location within
the HYCO plant it will operate. The first was inside a building in a long hallway. With the
surfer’s transceiver facing away from the controlling computer, a distance of 105’ was obtained
with only the body of the device and a human body in between. This was much better than the
listed 50’ of range listed on the transceiver.

The second location was in a parking lot full of cars. This was chosen because it allowed for long
distance line of sight testing in an open space and metal interference by holding the device
behind cars at varying distances. With interference from cars, the device was able to work
reliably at a distance of 45’. This is an acceptable distance and passes the needed range
requirement. When tested line of sight, nothing in between the surfer and the controlling
computer, the surfer reliably worked up to 216’.

5.0 Conclusions and Recommendations

5.1 Project Summary


The goal of the Air Products Capstone project was to gather accurate OD measurements at many
points to create actionable data for the prevention of creep failure of methane reformer pipes.
Team Snakes in 2 Planes began by visiting Air Products headquarters in Allentown, PA to better
understand the customer needs. The focus of the project quickly began to revolve around the
difficulty of measuring to +/- 0.001”.

The team began brainstorming shortly after the visit to Air Products. Many concepts were
created without any restrictions, allowing a full sample of the design space. When selecting the
final concept, the most difficult consideration was the sensor type to choose to take the
measurement. Optical sensors were considered the ideal solution due to the lack of wear due to
use, and therefore long, repeatable lifetime. When evaluating these sensors for accuracy, weight
and cost, the team found that optical distance sensors had very high price tags and required
expensive electronics to process the signals they produce. The team moved forward with the
capacitive measurement sensor contained in a digital calliper that could be easily interfaced, was
inexpensive, and had the required accuracy.

Patents were researched to prevent infringement and to inspire new ideas for concept generation.
The team found no patents that directly competed with the device Air Products requested. The
Air Products device would perform many more functions than any of the devices shown, and
therefore the team moved forward with little fear of patent infringement.

The final concept was selected using the AHP scoring method and the design was refined over
the course of several weeks. The rationale for the design decisions can be found in section 8.1-
8.4. Solidworks provided the virtual design space. The team started by working with geometric
components without regard for the off-the shelf components that would be used for construction.
Using these placeholder components, the general size and spacing was determined. Components

19
were selected to be generally very robust as to provide a rigid measurement platform which
could perform very accurate measurement for the customer.

The team then converted this generalized designed into a design which considered the
construction process more fully. Off-the-shelf components were selected for the mechanical
design and calipers were selected. 3D models gathered from McMaster-Carr were then
incorporated into the final Solidworks model. To build the chassis upon which the measurement
and drive components would be mounted, the team chose to use the water jet machine, because it
allowed for the automated drilling of holes, very close tolerance, and the ability to reduce weight
by removing unnecessary material, all from a Solidworks drawing and a single plate of stock
6061 Aluminum. A Bill of Materials was drafted and the plan for construction was completed.

The electrical design was completed in parallel with the mechanical design. Interfacing the
callipers presented a challenge because they operate on a 1.5 V logic circuit whereas the Arduino
operates on 5V logic. A circuit was designed to convert the signal from 1.5 to 5V and Arduino
code was written to interpret the serial output of the calipers through the use of interrupts. Motor
drivers and optical encoders were integrated using code adapted from the motor control lab from
ME 445 Lab.

Finally, Testing was performed to identify any issues and to compare performance with the
objectives of the project. The performance of the device was acceptable. The motors turned out
to be underpowered due to unforeseen frictional effects, rendering the crawler incapable of
climbing vertically. The accuracy of the device, a key design goal, was highly successful. The
level of error when the device was properly calibrated was very close to the ambitious goal of +/-
0.001”. Overall, the team worked together well and learned a variety of skills which combined
the disciplines of mechanical engineering, controls engineering, and electrical design.

20
References
Note: That for the author-date system, references are listed in alphabetical order.

Muriru, P.K. and Daewoo, R., “Prediction of the Heat Transfer Characteristics of a Multi-
Flame Injector'”, Combustion and Flame, vol. 100, no. 2, pp. 123-135, 2002.

Peters, L., Johnson, M., and Davidson, K., “A Novel Approach to Four-Bar Synthesis'”, 10th
ASME Design Automation Conference, pp. 234-250, Pittsburgh, PA, 2001.

Swanson Inc., “Online Users Manual for ANSYS 5.0'', http://www.ansys.com/ manual, viewed
on March 1999.

Wen-Cheng, C., “Electric Bicycle'”, US Patent no. 5,368,122, November 29, 1994.

Zacharia, M. and Daudi, P.K., The Effect of Multi-materials on Conventional Finite Element
Formulations, New York: Wiley and Sons, 2001.

21
Appendix A: Gantt Chart

22
Appendix B: Bill of Materials
Item Item # Source Description Quantit Price Per Total
y Unit
Drive
Components
298:1 Micro 2218 Pololu 100 RPM 70 oz-in 2 $15.95 $31.90
Metal Gearmotor
HP
Drive Shafts 1257K68 McMast 24"L .25"D 1 $15.75 $15.75
er
Polyurethane 2337T2 McMast 2" Diameter 15/16" 4 $2.33 $9.32
Wheels er hub, 7/8" Width
Micro Metal Gear 989 Pololu Set of 2 1 $4.99 $4.99
Holder
Potentiometer $0.00
angular Position
Sensor
Battery/Power $0.00
Supply
Micro Metal Gear 2590 Pololu Set of 2, 3 and 5 1 $8.95 $8.95
Optical Encoder spokes
Cut to Width $0.00
Polyurethane
Wheels
6 Pin Connector - 30K6870 Newark TE - Amp - 2 $1.02
Male - Through Through hole
6 Pin Connector - 52K4340 Newark Te- Amp - 2 $0.35
Female- Wire Wire/Crimp
Music Wire 9271K53 McMast 90 Degree Angle, 1 pack $7.63
Torsion Spring er .484" Spring OD,
.054" Wire, Left-
Hand
Music Wire 9271K52 McMast 90 Degree Angle, 1 pack $7.63
Torsion Spring er .484" Spring OD,
.054" Wire, Right-
Hand

Logic Block $0.00


Mega Protoshield 192 Adafruit ProtoShield 1 $14.95 $14.95
2N3904 33C8259 Newark NPN Transistor 5 $0.13 $0.65
100 kOhm resistor - Rb/R2 6 $0.00
resistor
20 kOhm resistor resistor - Rp 4 $0.00
120 kOhm resistor - R1 1 $0.00
resistor

23
4 Pin, TE, Male- 08N3025 Newark 4 Pin Connector - 3 $0.68 $2.04
Through, .1" Male - Through
4 Pin, JST, Male - 87R0314 Newark 4 Pin Connector - 3 $0.40 $1.20
SMD, 1mm Male - SMD
4 Pin, TE, Female 52K4345 Newark 4 Pin Connector - 3 $0.15 $0.45
- Wire, .1" Female -
Wire/Crimp
4 Pin, JST, 33P8106 Newark 4 Pin Connector - 3 $0.40
Female - Wire, Female -
1mm Wire/Crimp
5MOhm resistor - battery 2
meter
$0.00
$0.00
Physical $0.00
Components
Sheet Aluminum 9246K23 McMast 12" x 12", 3/8" 1 $36.92 $36.92
er thick
Measurement 9657K31 McMast OD .66", Length 1" 1 $8.38 $8.38
Suspension 7 er (1 Pack of 6)
Spring
Linear Bearing 6483K51 McMast 1" height 1/4" Shaft 4 $26.87 $107.4
er 8
Roller Bearing 2342K18 McMast OD 5/8" ID 1/4" 4 $17.66 $70.64
3 er High precision
Mini Aluminum 8600N3 McMast Aluminum Base- 4 $15.14 $60.56
Mounted Bearing er Mount l Ball
Bearing
$0.00
Donated by ME $0.00
445
Digital Calipers N/A Amazon hornady digital 3 $25.73 $77.19
calipers
Wixel Pair + 1339 Pololu Wireless USB 1 $39.95 $39.95
USB Cable
Wixel Shield for 2500 Pololu Wireless USB 1 $12.95 $12.95
Arduino Protoboard

24
Appendix C: Software Block Diagram

NO

YES

NO

YES

NO YES

25
Appendix D: Code
Communication Protocols
The Visual Basic App and Arduino Microcontroller communicate over a basic USB connection
using the serial protocol. While all of the commands are built into the system, they are also
outlined below to aid in troubleshooting.

Visual Basic to Arduino

Command Name Notes


0 Stop Stops the motors
1 Spot Measure Takes a spot measure
2 Forward Runs the motors forward
3 Reverse Runs the motors backwards
4 Go Starts auto mode
5B Battery Check Checks battery voltage
5R Reset Resets data
5CIIII:TTTT Calibrate Calibrates measurements; where IIII is the measurement in
1/1000ths of an inch for the parallel roller and TTTT is the
measurement in 1/1000ths of an inch for the perpendicular
roller.

Arduino to Visual Basic

Command Name Notes


“C:xxxxxxx” Control Where xxxxxx is string message
“M:IIII:TTTT” Spot Measure Where IIII is the parallel measurement and TTTT is the
perpendicular measurement both in 1/1000ths of an inch.
“A:D:IIII:TTTT” Auto Where D is the traverse measurement in inches with IIII
Measure being the parallel measurement and TTTT the
perpendicular measurement both in 1/1000ths of an inch.

26
Arduino Code
/*Arduino Code
Pipe Surfer

Team Snakes in 2 Planes


Chris Dickson
Adam Menko
Matt Tarabulski
Todd Troutman */

/*
struct dataPacket
{
int distance;
int Idiameter;
int Tdiameter;
};

dataPacket measurements[1000];
*/

//Instructions
const int ST = 48; //0
const int MS = 49; //1
const int FW = 50; //2
const int RV = 51; //3
const int GO = 52; //4
const int CT = 53; //5
//Control Commands
const int CAL = 67; //C
const int RST = 82; //R
const int BAT = 66; //B
const int r1 = 977;
const int r2 = 507;
const float vRatio = 5.0 / 1023;

//Pin Definitions
const int calClkI = 2;
const int calClkT = 3;
const int calDataI = 4;
const int calDataT = 5;
const int LEDpin = 13;
const int encoderA = 19;
const int encoderB = 18;
const int pwmAA = 9;
const int pwmAB = 10;
const int pwmBA = 11;
const int pwmBB = 12;

//Volatile vars for intterupt


volatile int dataIn = 0;
volatile unsigned long now = 0;
volatile unsigned long lastPulse = -1;
volatile int value = 0;

27
volatile int bitsIn = 0;
volatile int odd = 0;
volatile int neg = 0;
volatile unsigned int data;
//Flags
volatile int flag = LOW;
volatile int tail = HIGH;
volatile int autoMeasure = LOW;
//Rotational Int Vars
volatile int encoderRev = 0;
volatile int wheelRev = 0;
const int encoderLimit = 3 * 298;
volatile int running = LOW;

//Data vars
int valueT = -1;
int valueI = -1;
int traverse = 0;
int inst = -1;
int calibrationI = 0;
int calibrationT = 0;
String dataOut;
float batteryVoltage;
int batteryDelay = 0;

void setup()
{
Serial.begin(115200);
// Data in pins
pinMode(calClkI, INPUT); // Clock I pin = 2
pinMode(calClkT, INPUT); // Clock T pin = 3
pinMode(calDataI, INPUT); // Data I pin = 4
pinMode(calDataT, INPUT); // Data T pin = 5
pinMode(LEDpin, OUTPUT); // LED Pin
pinMode(encoderA, INPUT); // Encoder A pin = 19
pinMode(encoderB, INPUT); // Encoder B pin = 18

// Motor Drive Pins


pinMode(pwmAA, OUTPUT); //pwm motor A, line A = 9
pinMode(pwmAB, OUTPUT); //pwm motor A, line B = 10
pinMode(pwmBA, OUTPUT); //pwm motor B, line A = 11
pinMode(pwmBB, OUTPUT); //pwm motor B, line B = 12

Serial.println("C:Power On!");

void loop()
{
delay(100);
//read serial for command
while(Serial.available() > 0) //If only two characters in
{
//Serial.println("Data In");
inst = Serial.read();
//Serial.println(inst);

28
//Switch case for instruction
switch (inst)
{
case MS:
// Take spot measurement
//Serial.println("C:Spot Measurement.");
spotMeasure();
break;
case FW:
// Forward
//Serial.println("C:Move forward.");
mForward();
break;
case RV:
// Reverse
//Serial.println("C:Move reverse.");
mReverse();
break;
case ST:
// Stop
//Serial.read("C: Stop.");
mStop();
break;
case GO:
// GO - Enter Auto Mode
//Serial.read("C: Auto Mode.");
autoMode();
break;
case CT:
//control mode
//Serial.println("C:In Control Mode.");
control();
break;
default:
// Ask for retransmission?
Serial.println("C:Incorrect Command. Please Try again.");
Serial.flush();
}
Serial.flush();
}
//Check battery
if(batteryDelay > 599)
{
batteryDelay = 0;
checkBattery();
//if less than 7
if(batteryVoltage < 7.25)
Serial.println("C:Low Voltage!");
}
else
batteryDelay++;

void spotMeasure()
{

29
// Measure - Take measurement
takeMeasure();

//Send Measurement
dataOut = "M:" + (String)valueI + ":" + (String)valueT;
Serial.println(dataOut);
}

void autoMode()
{
// GO - Enter Auto Mode
Serial.println("C:Auto Mode");
//attach position interupt?
// Encoder A
traverse = 0;
//clearData();
//autoMeasure = HIGH;
do
{
//initilize motors
running = HIGH;
attachInterrupt(4, rotate, FALLING);
mForward();
//wait while running
while(running);
//Serial.println("PC");

//change position
traverse++;
Serial.println(traverse);
//take measurement
takeMeasure();

//package and send measurement


//measurements[traverse].distance = traverse;
//measurements[traverse].Idiameter = valueI;
//measurements[traverse].Tdiameter = valueT;

dataOut = "A:" + (String)traverse + ":"+ (String)valueI + ":" + (String)valueT;


Serial.println(dataOut);

delay(100);

}while(traverse < 10);

Serial.println("C: Auto Mode Finished!");


}

void control()
{
// Control Items
while(Serial.available())
{
//Serial.println("Data In");
inst = Serial.read();
//Serial.println(inst);

30
//Switch case for instruction
switch (inst)
{
case CAL:
int newCalibrationI;
int newCalibrationT;
//Calibration value
//intake calibration value
newCalibrationI = readInt() * 10;
newCalibrationT = readInt() * 10;
//set calibration
takeMeasure();

calibrationI = newCalibrationI - valueI - calibrationI;


calibrationT = newCalibrationT - valueT - calibrationT;

dataOut = "C:New calibration is " + (String)calibrationI;


Serial.println(dataOut);
break;

case RST:
traverse = 0;
Serial.println("C:Traverse Reset");
break;

case BAT:
//Serial.println("C:Check Battery");
checkBattery();
Serial.print("C:Battery Voltage is ");
Serial.print(batteryVoltage);
Serial.println(" V.");
break;

default:
Serial.flush();
}
}

int readInt()
{
//Function to read int off serial
boolean negative = false;
int numberIn = 0;
while(Serial.available())
{
byte in = Serial.read();

switch (in)
{
case ':':
if (negative)
return (-1 * numberIn);
else
return (numberIn);

31
break;

case '0' ... '9':


numberIn *= 10;
numberIn += in - '0';
break;

case '-':
negative = true;
break;
}
}
if (negative)
return (-1 * numberIn);
else
return (numberIn);

void checkBattery()
{
// Simple voltage divider to check battery level.
//Serial.println("C:Todd's Battery Code Here");

batteryVoltage = analogRead(0) * vRatio * (r1+r2)/r2;

void rotate()
{
encoderRev++;
if(encoderRev > encoderLimit)
{
mStop();
detachInterrupt(4);
//Serial.println("IR");
//set flag
running = LOW;
encoderRev = 0;
//wheelRev++;
}

void mForward()
{
// motor forward
analogWrite(pwmAA, 127);
digitalWrite(pwmAB, LOW);
analogWrite(pwmBA, 127);
digitalWrite(pwmBB, LOW);

void mReverse()
{

32
// motor stop
analogWrite(pwmAB, 127);
digitalWrite(pwmAA, LOW);
analogWrite(pwmBB, 127);
digitalWrite(pwmBA, LOW);
}

void mStop()
{
// motor stop
//Serial.println("C:Stop!");
digitalWrite(pwmAA, LOW);
digitalWrite(pwmAB, LOW);
digitalWrite(pwmBA, LOW);
digitalWrite(pwmBB, LOW);
}

void readCaliper()
{
if(tail)
{
//Check for starting bit
now = millis();
//Serial.println(now);
if((now - lastPulse) > 100)
{
tail = LOW;
odd = !digitalRead(dataIn);
}
else
lastPulse = now;
}
else
{

bitsIn++;
//Serial.print(bitsIn);
data = digitalRead(dataIn); // read DATA value as soon as possible

if(bitsIn < 15)


{
//Serial.println(data);
if (!data) // if 1 is received
{
value |= 0x8000; // then set most significant bit to 1
}
value = (unsigned int)value >> 1; // and move it one right
}
else if (bitsIn == 20)
{
// negative bit
if(!data)
{
neg = -1;
}
else

33
{
neg = 1;
}
}
else if (bitsIn > 23)
{
if(dataIn == calDataI)
detachInterrupt(0);
else if (dataIn == calDataT)
detachInterrupt(1);
//Serial.println("Interrupt Removed!");
//Serial.print("N: ");
//Serial.println(neg);

// reset variables
tail = HIGH;
flag = HIGH;
bitsIn = 0;

//correct for negative and odd


value = value * neg + odd;

//Serial.println(value, BIN);
//Serial.println(value, DEC);
}
}
}

void takeMeasure()
{
//Serial.println("Taking Measurement");
int valid = LOW;
int unstable = 0;
//reset measurements
valueI = -1;
valueT = -1;
// Signal starting measurement
digitalWrite(13, HIGH);
//Test data
//valueI = random(500, 1000);
//valueT = random(500, 1000);

// Measure First Caliper


dataIn = calDataI;
tail = HIGH;
lastPulse = millis();
attachInterrupt(0, readCaliper, FALLING);
//wait for reading
flag = LOW;
while(!flag);
flag = LOW;

// Set value
valueI = mmTOin(value) + calibrationI;
//Serial.print("I Value: ");
//Serial.println(valueI, DEC);

34
// Measure Second Calliper
dataIn = calDataT;
tail = HIGH;
lastPulse = millis();
attachInterrupt(1, readCaliper, FALLING);
//wait for reading
flag = LOW;
while(!flag);
flag = LOW;

// Set value
valueT = mmTOin(value) + calibrationT;
//Serial.print("I Value: ");
//Serial.println(valueI, DEC);
//End of measurement
digitalWrite(13, LOW);

/*
void clearData()
{
for(int x = 0; x < 1000; x++)
{
measurements[traverse].distance = 0;
measurements[traverse].Idiameter = 0;
measurements[traverse].Tdiameter = 0;
}
}
*/
int mmTOin(int mm)
{
return mm * 3.94;
}

35
Visual Basic Code
' Pipe Surfer
' EDSGN 460W
' Air Products OD Measurement Crawler
' Team Snakes in 2 Planes
' "The Pipe Surfer"
' Chris Dickson
' Adam Menko
' Matt Tarabulski
' Todd Troutman

' Arduino Controls


' 0 - Stop
' 1 - Spot Measurement
' 2 - Forward
' 3 - Reverse
' 4 - Automatic Mode
' 5 - Control Mode
‘5C####:#### - Calibration with #### value

Imports System.Drawing

Public Class Form1

Public Structure measurement


Public distance As Integer
Public DiameterI As Double
Public DiameterT As Double
Public PtI As Integer
Public PtT As Integer

Public Function newPoint(ByVal x As Integer, ByVal y1 As Double, ByVal y2 As Double) As measurement


distance = x
DiameterI = y1
DiameterT = y2
End Function

Public Function exportData() As String


Return distance.ToString + ", " + DiameterI.ToString + ", " + DiameterT.ToString
End Function

End Structure

Public dataIn(1000) As measurement


Public test As Boolean

Private Sub DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs)


Handles ArduinoPort.DataReceived
Invoke(New myDelegate(AddressOf newMeasurement), New Object() {})

End Sub

Public Delegate Sub myDelegate()

36
Public Sub newMeasurement()
'read serial port and update label
Dim newData As String
Dim trans As Integer
Dim valueI As Double
Dim valueT As Double
Dim part() As String
Dim controlChar As Char
If (Not test) Then
newData = ArduinoPort.ReadLine
Else
newData = txbDataTest.Text
End If

'check if data or control - first char?


'MsgBox(newData)
controlChar = newData(0)
part = Split(newData, ":")
Select Case controlChar
Case "A"
'A - auto data message
'convert data string to value then to double, divide by 1000 to get inches
trans = part(1)
valueI = CDbl(Conversion.Val(part(2))) / 1000
'convert to drawing point
valueT = CDbl(Conversion.Val(part(3))) / 1000
'convert to drawing point

lblDisplay.Text = newData + ", " + valueI.ToString()


'save measurement and location
dataIn(trans).newPoint(trans, valueI, valueT)

Case "M"
'M - spot measurement
MessageBox.Show("Spot Measurement" & vbCrLf & "Distance: " & vbCrLf & "I Diameter: " &
part(1).ToString & vbCrLf &
"T Diameter: " & part(2).ToString, "Spot Measurement", MessageBoxButtons.OK,
MessageBoxIcon.Information)
Case "C"
'C - control/special message
MessageBox.Show(part(1), "Control Message", MessageBoxButtons.OK, MessageBoxIcon.Hand)
End Select

End Sub

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


MyBase.Load
'Loading
'Poll available COM ports and update cboComPort
test = False

37
End Sub

Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


btnConnect.Click
Dim comName As String
comName = "COM" + nudCOM.Value.ToString
If Not ArduinoPort.IsOpen Then
ArduinoPort.PortName = comName
Try
ArduinoPort.Open()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
btnConnect.Text = "Disconnect"
Else
ArduinoPort.Close()
'ArduinoPort.PortName = comName
'ArduinoPort.Open()
btnConnect.Text = "Connect"
End If
End Sub

Private Sub sendMessage(ByVal msg As String)


'check if port is open
If ArduinoPort.IsOpen() Then
'send message
ArduinoPort.Write(msg)
Else
'if not alert user
'maybe try to open serial port?
MessageBox.Show("COM Port Not Open", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If

End Sub

Private Sub btnMeasure_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


btnMeasure.Click
sendMessage("1")

End Sub

Private Sub mnuExit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


mnuExit.Click
ArduinoPort.Close()
Me.Close()

End Sub

Private Delegate Sub PlotValueDelegate(ByVal old_y As Integer, ByVal new_y As Integer)

Private Sub mnuAdvanced_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


mnuAdvanced.Click
If mnuAdvanced.Checked Then

38
grpAdvanced.Visible = False
mnuAdvanced.Checked = False
Else
grpAdvanced.Visible = True
mnuAdvanced.Checked = True
End If
End Sub

Private Sub btnExport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


btnExport.Click
'Export data
'file picker?
'Get file name items
If txbPlant.Text = "" Then
MsgBox("Please enter a plant name.")
Exit Sub
End If
If txbRow.Text = "" Then
MsgBox("Please enter a row number.")
Exit Sub
End If
If txbTube.Text = "" Then
MsgBox("Please enter a tube number.")
Exit Sub
End If

Dim fileName As String = vbNullString


Dim filePath As String = vbNullString
fileName = txbPlant.Text + "-" + txbRow.Text + "-" + txbTube.Text + "-" + datePicker.Text
MsgBox(fileName)
sfdExport.FileName = fileName

'pick file location and start to write file


Dim dataOut As System.IO.StreamWriter
If sfdExport.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
Try
filePath = sfdExport.FileName
Catch ex As Exception
MsgBox("error in file name!")
Exit Sub
End Try

MsgBox(fileName)

'open or create csv


'title - plant, row, tube
dataOut = My.Computer.FileSystem.OpenTextFileWriter(filePath, True)

'Header
dataOut.WriteLine("Linear, T Diameter, I Diameter")
'write data
'iterate on measurement struct
For x As Integer = 1 To 10
'measurement, I diam, T diam
dataOut.WriteLine(dataIn(x).exportData)
Next

39
'close file
dataOut.Close()

Else
'cancelled
End If
End Sub
#Region "Advanced Buttons"
Private Sub btnTestData_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnTestData.Click
test = True
newMeasurement()
test = False
End Sub

Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


btnStop.Click
sendMessage("0")
End Sub

Private Sub btnFwd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


btnFwd.Click
sendMessage("2")
End Sub

Private Sub btnRev_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


btnRev.Click
sendMessage("3")
End Sub

Private Sub btnGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGo.Click


sendMessage("4")
End Sub

Private Sub btnControl_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


btnCalibrate.Click
Dim calPoint As Double
calPoint = CDbl(InputBox("Please enter calibration diameter: (1/1000 in)", "Calibration", "1500 = 1.5"))
Dim message As String
message = "5C" + calPoint.ToString

'MsgBox(message)
sendMessage(message)
End Sub
#End Region

Private Sub mnuPoll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


mnuPoll.Click
Dim ports() As String = {"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8",
"COM9", "COM10", "COM11"}
For x As Integer = 1 To 11
'test port open
'compile list of good
Next
'export good ports
End Sub

40
End Class

'Things to do!
'Decipher Data
'Store Data
'Export Data
'Clear Data
'Graphingz
'Calibration? This end?

41
42
ME  445  FINAL  REPORT:    
THE  LIGHTRIDER  
Katie  Vaughan  
Jared  Yarnall-­‐Schane  
 

Abstract  
The  purpose  of  this  project  was  to  create  an  animated  picture  on  the  spokes  of  a  bike  
wheel.  To  accomplish  this,  three  sets  of  LED  strips  containing  11  LED  lights  each  were  
attached  to  the  bike  wheel  at  120-­‐degree  intervals.  These  lights  were  controlled  by  
information  obtained  from  an  accelerometer/gyroscope  combination.  The  raw  data  
obtained  from  the  gyroscope  was  converted  into  a  readable  format  to  obtain  the  
orientation  of  the  bike  wheel,  and  thus  position  of  each  LED  light,  at  any  point  in  time.  A  
Hall-­‐Effect  sensor  was  used  to  prevent  gyroscope  drift.  The  lights  turn  off/on  at  a  given  
time  determined  by  the  position  of  the  wheel  to  display  a  specific  picture  from  the  
corresponding  code.    
 
Table   o f   C ontents  
 
ABSTRACT  ..............................................................................................................................................................  0  
1.0  INTRODUCTION  .............................................................................................................................................  2  
1.1  MOTIVATION  ......................................................................................................................................................................  2  
2.0  ELECTRICAL  CIRCUIT  ..................................................................................................................................  3  
2.1  LED  STRIPS  ........................................................................................................................................................................  3  
2.2  HALL  EFFECT  SENSOR  ......................................................................................................................................................  3  
2.3  GYROSCOPE  .........................................................................................................................................................................  4  
3.0  MECHANICAL  SETUP  ....................................................................................................................................  4  
3.1  BILL  OF  MATERIALS  ..........................................................................................................................................................  5  
4.0  COMPUTER  PROGRAMMING  .....................................................................................................................  6  
5.0  DISCUSSION  ....................................................................................................................................................  7  
6.0  CONCLUSION  &  FUTURE  CONSIDERATIONS  ........................................................................................  9  
APPENDIX  A:  CIRCUIT  DIAGRAM  ..................................................................................................................  11  
APPENDIX  B:  SYSTEM  PHOTOGRAPHS  .......................................................................................................  12  
APPENDIX  C:  SOLIDWORKS  MODEL  PHOTOS  ...........................................................................................  17  
APPENDIX  D:  ARDUINO  CODE  ........................................................................................................................  22  
APPENDIX  E:  DATA  SHEETS  ...........................................................................................................................  28  

   

1    
 
1.0  Introduction  
 
Since  a  young  age,  many  bicycle  riders  have  used  various  methods  of  decorating  their  
bikes.  From  baseball  cards  to  Christmas  lights  and  everything  in  between,  cyclists  have  
prided  themselves  in  their  ornaments.  Our  group  wanted  to  take  this  tradition  a  step  
further  and  set  a  new  standard  for  bicycle  decorations.  Not  only  is  an  animated  picture  on  a  
bike  wheel  fun  and  attractive,  it  is  also  a  safety  method  for  nightriders.  Given  their  limited  
visibility  to  vehicles  on  the  road,  LED  lights  would  provide  an  excellent  way  to  see  the  
cyclist  in  the  dark.    
 
The  system  consists  of  three  LED  strips  connected  to  an  Arduino  Uno  powered  by  a  single  
9V  battery.  After  the  LEDs  are  turned  on,  a  GY-­‐521  MPU-­‐6050  gyroscope/accelerometer  
breakout  board  will  calculate  the  orientation  of  the  wheel  at  any  point  in  time,  and  
therefore  the  position  of  the  LED  lights  at  any  point  in  time.  The  raw  data  acquired  from  the  
gyroscope  is  then  converted  by  the  Arduino  code  into  information  that  can  be  read  by  the  
group,  which  is  one  of  the  most  difficult  aspects  of  the  project.  Once  the  data  is  converted,  it  
is  used  to  turn  on/off  the  individual  LED  lights  depending  on  the  angular  velocity  of  the  
bike  wheel  so  as  to  keep  the  rotating  image  in  the  proper  orientation.  A  Hall-­‐Effect  sensor  
was  used  to  reset  the  position  every  180°  so  as  to  prevent  gyroscope  drift.    
 
Once  the  mechanical  and  electrical  setup  was  complete,  our  group  decided  to  create  a  
product  based  on  the  project.  We  named  our  product  the  LightRider  and  conducted  a  
comparison  of  our  product  to  others  currently  on  the  market.  This  analysis  can  be  found  in  
the  Discussion  section.    
 

1.1  Motivation  
The  motivation  for  this  project  came  from  a  combination  of  interests  in  business  and  
innovation.  Our  group  wanted  to  create  a  product  that  could  be  sold  for  cheaper  than  what  
is  currently  on  the  market,  but  that  is  innovative  enough  to  be  an  enjoyable  challenge.  Our  

2    
 
combined  interests  directed  us  to  a  moving  component  that  lights  up  with  LED  strips.  From  
there  we  considered  an  LED  propeller  clock,  a  light  display  based  on  musical  input,  and  
various  other  options.  We  decided  on  the  LED  bicycle  GIF  because  one  of  our  group  
members  is  an  avid  rider  and  found  deep  interest  in  the  subject.  

2.0  Electrical  Circuit  


 
The  circuit  used  for  this  project  can  be  broken  into  three  major  components:  the  LED  
strips,  the  Hall-­‐Effect  sensor,  and  the  gyroscope.  The  circuit  diagram  can  be  found  in  
Appendix  A.  
 

2.1  LED  Strips  


A  single  Adafruit  Neopixel  Digital  RGB  LED  weatherproof  strip  was  used  for  this  project.  
The  strip  consisted  of  60  LED  lights,  which  were  cut  into  strips  of  11  LEDs.    Three  of  these  
strips  were  used  in  the  construction  of  the  LightRider.  After  the  strips  were  cut,  wires  were  
soldered  to  each  at  the  severed  end.  Once  the  wires  were  attached  to  the  Arduino,  the  
provided  Arduino  library  was  used  to  code  the  necessary  pixels  to  turn  on/off  at  the  
desired  times.    
 

2.2  Hall  Effect  Sensor  


An  AH180  Hall-­‐Effect  sensor  switch  was  used  to  trigger  the  LED  lights  on.  (The  data  sheet  
can  be  found  in  Appendix  E.)  To  do  this,  two  1/4”  x  1/16”  magnets  were  placed  on  the  
frame  of  the  bicycle  at  a  specified  distance  away  from  where  the  Hall-­‐Effect  sensor  is  
located.  Once  the  Hall-­‐Effect  sensor  passes  the  first  magnet,  it  changes  the  value  of  the  
sensor  from  LOW  to  HIGH.  In  our  Arduino  code,  we  attached  an  interrupt  to  the  Hall-­‐Effect  
sensor’s  pin,  which  will  read  at  what  point  the  Hall-­‐Effect  changes  value.  When  this  
happens,  another  portion  of  the  code  will  turn  the  LED  lights  on.  Using  the  Hall-­‐Effect  
switch  allows  us  to  know  at  exactly  what  position  the  LED  strips  are  when  they  first  turn  

3    
 
on.  This  provides  a  zero  point  for  the  gyroscope,  which  makes  initializing  the  orientation  of  
the  picture  on  the  bike  wheel  much  easier.  It  also  provides  a  backup  position  data  source.  
While  the  gyroscope  is  an  excellent  method  of  acquiring  the  angular  velocity  and  position  
of  the  wheel,  there  tends  to  be  a  slight  drift  in  the  data  over  time.  By  using  the  Hall-­‐Effect  
sensor  as  a  reset  for  the  gyroscope,  we  can  eliminate  this  error.    
 

2.3  Gyroscope  
Once  the  LED  lights  are  turned  on,  a  GY-­‐521  MPU-­‐6050  gyroscope  is  used  to  determine  the  
location  and  speed  of  each  of  the  LED  strips.  (The  data  sheet  can  also  be  found  in  Appendix  
E.)  The  raw  data  outputted  by  the  gyroscope  is  unable  to  be  read  by  a  human  without  first  
converting  the  data  to  something  meaningful.  Using  the  I2Cdevlib  library,  we  successfully  
converted  the  code  and  were  able  to  retrieve  the  angular  speed  of  the  bike  wheel  in  
degrees/second.  Using  the  angular  velocity,  we  were  able  to  integrate  this  data  to  obtain  
the  position.  For  our  particular  project,  a  picture  of  Pacman  was  used  to  demonstrate  how  
certain  LEDs  could  be  turned  off  a  specific  location  (see  Figure  7  in  Appendix  B).  With  the  
position  of  the  LEDs  known,  we  were  able  to  turn  the  LEDs  off  for  a  60-­‐degree  interval  for  
every  rotation,  creating  the  Pacman  image  on  the  bicycle  wheel.  A  photo  of  the  image  can  
be  found  in  Appendix  B.  The  image  is  slightly  inaccurate  because  of  the  camera  used  to  take  
the  photo.  We  would  have  required  a  camera  with  a  longer  exposure  time,  and  such  a  
camera  was  unavailable.    
 

3.0  Mechanical  Setup  


 
The  bicycle  conveniently  provided  most  of  the  mechanical  aspect  of  the  project.  The  only  
setup  required  by  the  group  was  to  mount  the  LED  strips  and  Arduino/circuit  components  
to  the  bike  wheel.  The  group  accomplished  this  by  attaching  wooden  boards  via  screws  to  
both  the  front  and  back  of  the  wheel  at  120-­‐degree  intervals  (see  Figure  3  in  Appendix  B).  
Then  the  LED  strip  was  cut  into  three  equal  pieces  and  attached  to  the  wooden  boards  with  

4    
 
superglue  (Figure  4).  A  hole  was  then  cut  into  each  of  the  wooden  boards  to  feed  the  wires  
through  so  they  are  hidden  from  sight  and  are  protected  from  exogenous  conditions.  Next  
the  Arduino  and  battery  pack  were  mounted  perpendicular  to  the  wooden  boards  (Figures  
3  &  5).  This  was  done  so  the  gyroscope  was  in  the  correct  orientation  to  retrieve  the  
desired  data  and  so  less  force  from  air  drag  was  exerted  on  the  Arduino  and  electrical  
components.  After  the  Hall-­‐Effect  sensor  was  positioned  (Figure  6),  the  magnets  were  
glued  to  the  inner  part  of  the  bike  frame  at  a  distance  that  was  acceptable  for  the  sensor  to  
still  experience  the  magnetic  fields.    
 
For  the  purpose  of  the  class  project,  superglue  and  electrical  tape  were  used  to  attach  most  
of  the  components.  For  our  LightRider  product  we  would  instead  use  the  SolidWorks  
model  located  in  Appendix  C  to  create  the  parts  needed.  This  model  can  easily  be  modified  
to  fit  any  bike  size,  and  is  made  to  attach  to  spokes  rather  than  to  the  type  of  wheel  used  in  
our  project  (see  Figure  8  in  Appendix  C).  The  LightRider  includes  two  wooden  pieces  
shaped  to  fit  the  curve  of  the  bike  spokes  (Figure  11).  These  pieces  are  attached  via  rod-­‐
shaped  cutouts  in  the  boards.  Back  plates  (not  shown)  would  then  be  screwed  on  to  ensure  
the  wooden  boards  are  secure  to  the  bike.  On  the  front  wooden  board  a  housing  
compartment  for  the  Arduino  is  built  on,  which  will  be  screwed  into  the  other  wooden  
piece  once  they  are  attached  to  the  spokes  (Figure  11).  Also  located  on  the  back  of  the  front  
board  is  a  battery  compartment  (Figure  12),  which  will  already  include  the  battery  
terminal  attachments  inside  to  create  less  setup  work  for  the  consumer.    On  the  front  of  
each  wooden  board  the  LED  strips  are  located,  whose  wires  are  fed  through  three  separate  
holes  cut  into  the  boards  and  then  through  hollowed  channels  to  the  Arduino  housing.  This  
ensures  the  wires  are  protected  from  outside  conditions.    
 

3.1  Bill  of  Materials  


The  following  table  (Table  1)  lists  the  materials  used  to  construct  our  project:  
 

5    
 
Table  1.  Bill  of  Material  

Item   Quantity   Cost  


LED  strip   1   $24.95  
Wooden  Boards   6   $8.00  
Screw   24   $2.00  
Battery   1   $3.00  
Hall  Effect  Sensor   1   $0.95  
Magnet   2   $1.90  
Gyroscope/Accelerometer   1   $6.30  
Arduino  Uno   1   $27.50  
Protoboard   1   $14.50  
Totals   38   $89.10  
 

4.0  Computer  Programming  


 
Our  Arduino  code  essentially  consists  of  one  large  loop.  A  flowchart  of  our  code  can  be  seen  
below  in  Figure  1.  When  the  Arduino  is  turned  on,  it  initializes  the  gyroscope  using  the  
I2Cdevlib  library.  After  the  gyroscope  is  confirmed  as  connected  to  the  Arduino,  it  begins  
taking  data  in  all  three  degrees  of  freedom.  Due  to  the  orientation  of  the  Arduino  and  
gyroscope  breakout  board,  only  the  z-­‐axis  information  is  needed  and  is  thus  the  only  data  
extracted  from  the  gyroscope.    
 
Simultaneously  at  the  start  of  the  program,  the  Hall-­‐Effect  sensor  is  initially  in  the  LOW  
state.  An  interrupt  is  attached  to  the  sensor  to  detect  when  it  changes  to  a  HIGH  value.  This  
occurs  when  the  Hall-­‐Effect  passes  one  of  the  two  small  magnets  placed  on  the  bike  frame.  
When  this  happens,  two  things  occur:  the  LED  portion  of  the  code  begins  and  the  
gyroscope’s  position  data  is  reset  to  zero.  When  the  Hall-­‐Effect  passes  the  other  magnet,  it  
changes  back  to  a  LOW  state  and  sets  the  gyroscope  position  value  to  180  degrees.  As  

6    
 
mentioned  above,  using  the  sensors  to  reset  the  gyroscope  information  ensures  that  data  
drift  does  not  occur.  This  process  is  repeated  until  the  Arduino  is  powered  off.      

 
Figure  1.  Computer  Program  Flowchart  

5.0  Discussion  
 
There  are  a  few  criteria  set  to  determine  whether  our  project  is  a  success  or  not.  These  
criteria  include  functionality  and  economic  dominance.    

7    
 
 
The  LightRider  worked  as  intended.  The  Hall-­‐Effect  sensor  turned  the  LED  strips  on  at  the  
correct  time,  and  the  information  received  from  the  gyroscope  was  successfully  read  to  
obtain  the  location/speed  of  the  LEDs  at  any  point  in  time  while  riding  the  bike.  Using  this  
information,  the  LEDs  were  coded  to  turn  on/off  at  specific  times  to  create  the  rotating  
image.  Overall  the  LightRider  was  a  success  in  the  criteria  of  functionality.    
 
To  determine  if  our  product  was  an  economic  success,  we  compared  the  LightRider  to  
other  products  on  the  current  market  using  a  piecewise  design  matrix.  The  criteria  for  
comparison  included  cost,  ease  of  setup,  the  ability  to  add  different  pictures,  and  the  ability  
to  be  designed  for  different  bike  sizes.  Our  weighting  analysis  and  design  matrix  can  be  
seen  below  in  Tables  2  and  3,  respectively.  
 
Table  2 .  Design  Matrix  Weighting  Analysis  
 

Table  3.  Design  Matrix  

 
 
 

8    
 
 
Note  that  the  cost  for  our  product  was  calculated  in  Table  1.  The  cost  of  machining  the  
product  was  omitted  from  the  analysis  because  the  group  did  not  conduct  a  first-­‐year  profit  
analysis.  We  assumed  that  the  necessary  equipment  to  create  the  product  would  be  
purchased  and  this  cost  would  be  returned  through  profit.    
 
From  the  design  matrix,  it  can  be  observed  that  our  product  would  excel  in  the  current  
market  and  could  be  a  successful  company  when  compared  to  the  Monkey  Light  Pro  and  
the  Adafruit  SpokePOV  products,  which  were  the  only  two  products  found  that  are  similar  
to  the  LightRider.    
 

6.0  Conclusion  &  Future  Considerations  


 
After  assessing  our  project’s  ability  to  function  and  comparing  the  LightRider  to  other  
products  on  the  market,  we  have  determined  that  our  project  was  a  success.  Had  we  
created  an  actual  product  to  be  sold  on  the  market,  more  consideration  would  have  been  
taken  into  the  physical  construction  of  the  project.  For  the  purpose  of  this  class,  however,  
the  components  held  together  well  enough  using  the  methods  discussed  above  to  be  tested  
and  displayed  multiple  times.    
 
A  further  improvement  to  our  project  could  be  creating  a  separate  program  to  upload  the  
consumer’s  desired  picture  so  they  could  create  their  own  display  without  needing  to  
contact  the  company.  Also,  to  ensure  errors  would  not  arise  if  the  system  encounters  
unfavorable  ground  topography  while  riding,  more  Hall-­‐Effect  switches  could  be  used  to  
serve  as  an  improved  backup  position  sensor  for  the  gyroscope.  A  major  limitation  when  
conducting  tests  on  our  project  was  the  bicycle  used.  It  was  not  fully  functional  and  could  
not  be  used  in  a  setting  that  would  mirror  a  realistic  situation.  This  prevented  the  group  
from  testing  what  would  happen  when  the  system  encountered  bumps  and  ditches.  If  there  

9    
 
were  glitches  in  the  image  due  to  bumps,  we  would  need  to  modify  our  project  to  account  
for  this  problem.      

   

10  
 
 
Appendix  A:  Circuit  Diagram  
 
 

Figure  2.  Circuit  Diagram  

   

11  
 
 
Appendix  B:  System  Photographs  
 

Figure  3 .  Electrical  Setup  

 
 

12  
 
 
Figure  4.  LED  Strip  Setup  

 
 
 

13  
 
 
Figure  5.  Arduino  Setup  

14  
 
 
 
 

 
Figure  6.  Hall-­‐Effect  Position  

15  
 
 
 
Figure  7.  Long-­‐Exposure  Pacman  Photo  

   

16  
 
 
Appendix  C:  SolidWorks  Model  Photos  
 

Figure  8.  LightRider  with  Wheel  

17  
 
 
Figure  9.  LightRider  without  Wheel  

18  
 
 
 
 

Figure  10.  Side  V iew  

19  
 
 
Figure  11.  Arudino  Housing  

20  
 
 
 
 

 
   
Figure  12.  Battery  Compartment  

21  
 
 
Appendix  D:  Arduino  Code  
 
Setup  
 
//  Begin  Gyroscope  setup  
 
//  I2Cdev  and  MPU6050  must  be  installed  as  libraries,  or  else  the  .cpp/.h  files  
//  for  both  classes  must  be  in  the  include  path  of  your  project  
#include  "I2Cdev.h"  
#include  "MPU6050.h"  
 
//  Arduino  Wire  library  is  required  if  I2Cdev  I2CDEV_ARDUINO_WIRE  implementation  
//  is  used  in  I2Cdev.h  
#if  I2CDEV_IMPLEMENTATION  ==  I2CDEV_ARDUINO_WIRE  
       #include  "Wire.h"  
#endif  
 
//  class  default  I2C  address  is  0x68  
//  specific  I2C  addresses  may  be  passed  as  a  parameter  here  
//  AD0  low  =  0x68  (default  for  InvenSense  evaluation  board)  
//  AD0  high  =  0x69  
MPU6050  accelgyro;  
//MPU6050  accelgyro(0x69);  //  <-­‐-­‐  use  for  AD0  high  
 
int16_t  ax,  ay,  az;  
int16_t  gx,  gy,  gz;  
 
//  uncomment  "OUTPUT_READABLE_ACCELGYRO"  if  you  want  to  see  a  tab-­‐separated  
//  list  of  the  accel  X/Y/Z  and  then  gyro  X/Y/Z  values  in  decimal.  Easy  to  read,  
//  not  so  easy  to  parse,  and  slow(er)  over  UART.  
#define  OUTPUT_READABLE_ACCELGYRO  
 
//  uncomment  "OUTPUT_BINARY_ACCELGYRO"  to  send  all  6  axes  of  data  as  16-­‐bit  
//  binary,  one  right  after  the  other.  This  is  very  fast  (as  fast  as  possible  
//  without  compression  or  data  loss),  and  easy  to  parse,  but  impossible  to  read  
//  for  a  human.  
//#define  OUTPUT_BINARY_ACCELGYRO  
 
#define  LED_PIN  13  
bool  blinkState  =  false;  
 
//  End  Gyroscope  setup  
 
//  Begin  LED  setup  
 

22  
 
 
#include  <Adafruit_NeoPixel.h>  
 
#define  PIN  6  
#define  PIN2  5  
#define  PIN3  9  
 
//      Parameter  1  =  number  of  pixels  in  strip  
//      Parameter  2  =  pin  number  (most  are  valid)  
//      Parameter  3  =  pixel  type  flags,  add  together  as  needed:  
//      NEO_KHZ800    800  KHz  bitstream  (most  NeoPixel  products  w/WS2812  LEDs)  
//      NEO_KHZ400    400  KHz  (classic  'v1'  (not  v2)  FLORA  pixels,  WS2811  drivers)  
//      NEO_GRB          Pixels  are  wired  for  GRB  bitstream  (most  NeoPixel  products)  
//      NEO_RGB          Pixels  are  wired  for  RGB  bitstream  (v1  FLORA  pixels,  not  v2)  
Adafruit_NeoPixel  strip  =  Adafruit_NeoPixel(11,  PIN,  NEO_GRB  +  NEO_KHZ800);  
Adafruit_NeoPixel  strip2  =  Adafruit_NeoPixel(11,  PIN2,  NEO_GRB  +  NEO_KHZ800);  
Adafruit_NeoPixel  strip3  =  Adafruit_NeoPixel(11,  PIN3,  NEO_GRB  +  NEO_KHZ800);  
 
//  End  LED  setup  
 
//  Begin  declaring  variables  
 
const  int  hallPin  =  2;        //  the  number  of  the  hall  effect  sensor  pin  
float  change=0;                      //  used  in  the  interrupter  to  determine  the  actual  location  of  the  
wheel  
float  angle=0;                        //  degpsec  times  the  difference  in  time  between  the  samples  =  angle  of  
the  tire  with  respect  to  initial  position  
float  angle1=0;                      //  Used  to  add  angles  onto  each  other  in  void  loop  
float  degpsec=0;                    //  Variable  holding  the  raw  data  divided  by  a  scaling  factor  
float  t1=0;                              //  takes  the  time  right  after  finding  the  degpsec  
float  t2=0;                              //  t2  is  equal  to  t1  at  the  end  of  the  loop,  thus  it  holds  the  place  of  the  last  
time  taken  
int  light_delay=100;            //Setslightdelay  delay  
int  brightness=64;                //sets  brightness  
 
//  End  declaring  variables  
 
void  setup()  {  
     
   //  Gyroscope  
         //  join  I2C  bus  (I2Cdev  library  doesn't  do  this  automatically)  
       #if  I2CDEV_IMPLEMENTATION  ==  I2CDEV_ARDUINO_WIRE  
               Wire.begin();  
       #elif  I2CDEV_IMPLEMENTATION  ==  I2CDEV_BUILTIN_FASTWIRE  
               Fastwire::setup(400,  true);  
       #endif  
 
23  
 
 
       //  initialize  serial  communication  
       //  (38400  chosen  because  it  works  as  well  at  8MHz  as  it  does  at  16MHz,  but  
       //  it's  really  up  to  you  depending  on  your  project)  
       Serial.begin(38400);  
 
       //  initialize  device  
       Serial.println("Initializing  I2C  devices...");  
       accelgyro.initialize();  
 
       //  verify  connection  
       Serial.println("Testing  device  connections...");  
       Serial.println(accelgyro.testConnection()  ?  "MPU6050  connection  successful"  :  
"MPU6050  connection  failed");  
       
   //  Hall  effect            
       pinMode(hallPin,  INPUT);            //  initialize  the  hall  effect  sensor  pin  as  an  input:  
       attachInterrupt(0,  timing,  CHANGE);          //  Sets  an  interrupt  to  trigger  whenever  the  pin  
changes  value  
         
   //LED  
     //  Initialize  all  pixels  to  'off'  
   strip.begin();  
   strip.show();    
   strip2.begin();  
   strip2.show();  
   strip3.begin();  
   strip3.show();  
     
   }  
 
void  loop(){  
     //  read  raw  accel/gyro  measurements  from  device  
       //accelgyro.getMotion6(&ax,  &ay,  &az,  &gx,  &gy,  &gz);  
 
       //  these  methods  (and  a  few  others)  are  also  available  
       //accelgyro.getAcceleration(&ax,  &ay,  &az);  
       accelgyro.getRotation(&gx,  &gy,  &gz);  
 
       #ifdef  OUTPUT_READABLE_ACCELGYRO  
               //  display  tab-­‐separated  accel/gyro  x/y/z  values  
               //Serial.print("a/g:\t");  
               //Serial.print(ax);  Serial.print("\t");  
               //Serial.print(ay);  Serial.print("\t");  
               //Serial.print(az);  Serial.print("\t");  
               //Serial.print(gx);  Serial.print("\t");  
               //Serial.print(gy);  Serial.print("\t");  
24  
 
 
               //Serial.println(gz);  
       #endif  
 
       #ifdef  OUTPUT_BINARY_ACCELGYRO  
             //  Serial.write((uint8_t)(ax  >>  8));  Serial.write((uint8_t)(ax  &  0xFF));  
             //  Serial.write((uint8_t)(ay  >>  8));  Serial.write((uint8_t)(ay  &  0xFF));  
             //  Serial.write((uint8_t)(az  >>  8));  Serial.write((uint8_t)(az  &  0xFF));  
               Serial.write((uint8_t)(gx  >>  8));  Serial.write((uint8_t)(gx  &  0xFF));  
               Serial.write((uint8_t)(gy  >>  8));  Serial.write((uint8_t)(gy  &  0xFF));  
               Serial.write((uint8_t)(gz  >>  8));  Serial.write((uint8_t)(gz  &  0xFF));  
       #endif  
         
       degpsec=gz/131;  //converts  raw  data  to  degrees/sec  
                   //Serial.println(degpsec,6);  //prints  degree/sec  
     
       t1=millis()/1000.00;            //Time  at  collecting  the  degpsec  value      
       angle1  =  degpsec  *  (t1-­‐t2);  //Integrating  to  find  the  angular  position  
       angle  =  angle1  +  angle;        //Adding  the  new  angular  position  onto  the  old  angle,  thus  
displaying  the  current  location  of  the  wheel  
       angle  =  abs(angle);              //Ensures  that  the  angle  is  always  a  positive  value  
       Serial.println(angle);  //Prints  out  angle    
         
   //Resets  the  angle  to  0  if  the  angle  is  larger  than  360  
   if  (angle  >  360){  
       angle=0;  
   }          
                   
   //  LED  light  code  for  Pac-­‐Man  Mouth  Open  
   //  Code  is  setup  so  the  lights  will  always  turn  off  for  the  same  60  degree  position  when  
viewing  the  bike.  
   if  (angle  <=  300){  
       strip.setBrightness(brightness);  
       strip.setPixelColor(0,255,238,0);  //  Pac-­‐Man  yellow  
       strip.setPixelColor(1,255,238,0);  
       strip.setPixelColor(2,255,238,0);  
       strip.setPixelColor(3,255,238,0);  
       strip.setPixelColor(4,255,238,0);  
       strip.setPixelColor(5,255,238,0);  
       strip.setPixelColor(6,255,238,0);  
       strip.setPixelColor(7,255,238,0);  
       strip.setPixelColor(8,255,238,0);  
       strip.setPixelColor(9,255,238,0);  
       strip.setPixelColor(10,255,238,0);  
       strip.show();  
   }  
   else{  
25  
 
 
       strip.setPixelColor(0,0,0,0);  
       strip.setPixelColor(1,0,0,0);  
       strip.setPixelColor(2,0,0,0);  
       strip.setPixelColor(3,0,0,0);  
       strip.setPixelColor(4,0,0,0);  
       strip.setPixelColor(5,0,0,0);  
       strip.setPixelColor(6,0,0,0);  
       strip.setPixelColor(7,0,0,0);  
       strip.setPixelColor(8,0,0,0);  
       strip.setPixelColor(9,0,0,0);  
       strip.setPixelColor(10,0,0,0);  
       strip.show();        
   }  
     
   if  (angle  >=  60){  
       strip2.setBrightness(brightness);  
       strip2.setPixelColor(0,255,238,0);  
       strip2.setPixelColor(1,255,238,0);  
       strip2.setPixelColor(2,255,238,0);  
       strip2.setPixelColor(3,255,238,0);  
       strip2.setPixelColor(4,255,238,0);  
       strip2.setPixelColor(5,255,238,0);  
       strip2.setPixelColor(6,255,238,0);  
       strip2.setPixelColor(7,255,238,0);  
       strip2.setPixelColor(8,255,238,0);  
       strip2.setPixelColor(9,255,238,0);  
       strip2.setPixelColor(10,255,238,0);  
       strip2.show();  
   }  
   else{  
       strip2.setPixelColor(0,0,0,0);  
       strip2.setPixelColor(1,0,0,0);  
       strip2.setPixelColor(2,0,0,0);  
       strip2.setPixelColor(3,0,0,0);  
       strip2.setPixelColor(4,0,0,0);  
       strip2.setPixelColor(5,0,0,0);  
       strip2.setPixelColor(6,0,0,0);  
       strip2.setPixelColor(7,0,0,0);  
       strip2.setPixelColor(8,0,0,0);  
       strip2.setPixelColor(9,0,0,0);  
       strip2.setPixelColor(10,0,0,0);  
       strip2.show();  
   }  
     
   if  (angle  >=  180  ||  angle  <=  120){  
       strip3.setBrightness(brightness);  
26  
 
 
       strip3.setPixelColor(0,255,238,0);  
       strip3.setPixelColor(1,255,238,0);  
       strip3.setPixelColor(2,255,238,0);  
       strip3.setPixelColor(3,255,238,0);  
       strip3.setPixelColor(4,255,238,0);  
       strip3.setPixelColor(5,255,238,0);  
       strip3.setPixelColor(6,255,238,0);  
       strip3.setPixelColor(7,255,238,0);  
       strip3.setPixelColor(8,255,238,0);  
       strip3.setPixelColor(9,255,238,0);  
       strip3.setPixelColor(10,255,238,0);  
       strip3.show();  
   }  
   else{  
       strip3.setPixelColor(0,0,0,0);  
       strip3.setPixelColor(1,0,0,0);  
       strip3.setPixelColor(2,0,0,0);  
       strip3.setPixelColor(3,0,0,0);  
       strip3.setPixelColor(4,0,0,0);  
       strip3.setPixelColor(5,0,0,0);  
       strip3.setPixelColor(6,0,0,0);  
       strip3.setPixelColor(7,0,0,0);  
       strip3.setPixelColor(8,0,0,0);  
       strip3.setPixelColor(9,0,0,0);  
       strip3.setPixelColor(10,0,0,0);  
       strip3.show();  
   }  
     
   t2=t1;          //Sets  the  t2=t1  so  that  we  know  how  long  each  loop  takes  and  thus  can  integrate  
to  find  the  angle  
}  
 
//  Function  for  fixing  the  gyroscopes  drift  
void  timing(){  
     
   change  =  digitalRead(hallPin);  
     
   //Sets  the  angle  to  0  whenever  the  hall  effect  sensor  changes  to  a  LOW  voltage  reading  
   if  (change  ==  0){  
       angle=0;  
   }  
   //Sets  the  angle  to  180  whenever  the  hall  effect  sensor  changes  to  a  HIGH  voltage  reading  
   else  {  
       angle=180;  
   }  
   }  
27  
 
 
Appendix  E:  Data  Sheets  
 

 
 

28  
 
 
 
 
 

29  
 
 

Вам также может понравиться