You are on page 1of 108

PROFORMA FOR THE APPROVAL PROJECT PROPOSAL

PNR No.: Roll No:

1. Name of the Student

2. Title of the Project

3. Name of the Guide

4. Teaching experience of the Guide

5. Is this your first Submission? Yes No

Signature of the Student Signature of the Guide

Date: Date:

Signature of the Coordinator

Date:
DRONE USING ARDUINO
MICROCONTROLLER
A Project Report
Submitted in partial fulfillment of the
Requirements for the award of the Degree of

BACHELOR OF SCIENCE (INFORMATION TECHNOLOGY)


By

Vikram Roy
(Seat Number: )

Under the esteemed guidance of


Mrs. Awantika Deshpande
Asst. Professor

DEPARTMENT OF INFORMATION TECHNOLOGY

DNYAN GANGA EDUCATION TRUST’S


DEGREE COLLEGE OF ARTS, COMMERECE AND SCIENCE
Affiliated to University of Mumbai
THANE, 400615
MAHARASHTRA
2019-2020
DNYAN GANGA EDUCATION TRUST’S
DEGREE COLLEGE OF ARTS, COMMERCE AND SCIENCE
Affiliated to University of Mumbai
THANE-MAHARASHTRA-400615

DEPARTMENT OF INFORMATION TECHNOLOGY

CERTIFICATE

This is to certify that the project entitled “DRONE USING ARDUINO MICROCONTROLLER”
is bonafied work of Vikram Roy bearing Seat No:
submitted in partial fulfillment of the requirements for the award of Degree of BACHELOR OF
SCIENCE in INFORMATION TECHNOLOGY from University of Mumbai

Internal Guide Coordinator

External Examiner

Date: College Seal


ABSTRACT
Technological advancements in fields of security operations as well as in remote package
delivering systems has led us to the development of a quadcopter. A quadcopter can achieve vertical
flight in a stable manner and be used to monitor or collect data in a specific region. Technologies
advances have reduced the cost and increase the performance of the low power microcontrollers that
allows general public to develop their own quadcopter.
The goal of the project is to build, modify, and improve an existing quadcopter kit to obtain
stable flight, for delivering purpose and security purpose.
The project is based on Remotely Operated Quadcopter System. The Quadcopter is controlled
through wireless communication system. The Quadcopter balancing condition is sensed by MPU
6050 controller sensor. All signals from sensors are processed by Arduino microcontroller board.
Output from Arduino microcontroller board is used to control the four brushless motor of the
Quadcopter propellers. The Transmitter and receiver are used to control the movement of the
Quadcopter. The experiment shows that Quadcopter can hover with maintain it balancing and
stability. Quadcopter can accept load distribution up to 250g approximately during it hover condition.
Maximum operated time of Quadcopter is six minutes using 2200mAh LiPo battery and operate time
can be increase by using largest battery capacity.
ACKNOWLEDGEMENT

The goal is best achieved by treading the path of excellence with discipline and deep
insight. We would have never succeeded in completing our task without the cooperation,
encouragement and help provided to us by various teachers and guides.
With deep sense of gratitude, we express our sincere thanks to our esteemed and worthy
guide, Mrs. Awantika Deshpande, for her valuable guidance in carrying out this project under
his/her effective supervision, encouragement, enlightenment and cooperation.
We feel indebted to express our deep sense of gratitude towards Mrs. Vandana Sharma
(Principal), Mrs. Vanita Sabharwal (HOD Information Technology) and Mrs. Awantika
Deshpande (Project Coordinator) who have been a constant source of inspiration for us throughout
this project.
We would also like to thank all faculty members and non-teaching staff for their kind
support without vacillation.
The acknowledgement would be incomplete if we do not mention the emotional support
and blessings provided by our parents and friends. We had a pleasant enjoyable and fruitful
company with them. Last but not the least we would like to thank all the people who directly or
indirectly helped us in this project.
DECLARATION
I hereby declare that the project entitled, “DRONE USING ARDUINO
MICROCONTROLLER” done at Department Of Information & Technology, Dnyan Ganga
Education Trust’s Degree College Of Arts, Commerce, & Science, University Of Mumbai in
partial fulfillment of the requirements for the award of degree of BACHELOR OF SCIENCE
(INFORMATION TECHNOLOGY) is a record of original dissertation work done by me, under
the guidance and supervision of Mrs. Awantika Deshpande, and it has not formed the basis for the
award of any Degree/Diploma/Associate ship/ Fellowship or other similar title to any candidate of
any University.

Place: Vikram Roy

Signature of Candidate

Date:
TABLE OF CONTENT
Chapter 1: Introduction .......................................................................................................... 1
1.1 Backgrounds .................................................................................................................... 1
1.2 Objectives ......................................................................................................................... 2
1.3 Purpose, Scope, and Applicability .................................................................................2
1.3.1 Purpose ........................................................................................................................2
1.3.2 Scope ............................................................................................................................2
1.3.3 Applicability ................................................................................................................ 3
1.3.4 Achievement ............................................................................................................... 3
Chapter 2: Survey of Technologies ....................................................................................... 4
2.1 Existing system ............................................................................................................... 4
2.2 Purpose System .............................................................................................................. 4
2.3 Requirements Specification .......................................................................................... 4
2.4 Hardware Requirements ............................................................................................... 5
2.5 Software Requirements ................................................................................................. 5
2.6 Justification of selection of Technology ...................................................................... 5
2.6.1 Arduino unoR3… ...................................................................................................... 5
2.6.2 MPU 6050….............................................................................................................. 10
2.6.3 Camera Module OV6770 .......................................................................................... 11
2.6.4 Micro SD card Reader Module .................................................................................. 13
2.6.5 Transmitter and Receiver ...........................................................................................14
2.6.6 A2212/10T 1400KV BLDC Motors .......................................................................... 17
2.6.7 ESC’s (Electronic Speed Controller) ......................................................................... 18
2.6.8 Frame ............................................................................................................................. 19
2.6.9 Propellers ................................................................................................................... 20
2.6.10 Battery......................................................................................................................21
2.6.11 Battery charger ........................................................................................................ 22
2.6.12 Some Small Components .......................................................................................... 23
2.6.13 Arduino IDE ............................................................................................................ 24
2.6.14 C++ ......................................................................................................................... 25
2.6.15 MS excel… .............................................................................................................. 26
Chapter 3: System design......................................................................................................28
3.1 Module Division ............................................................................................................. 28
3.2 Entity Relationship Diagram ....................................................................................... 30
3.3 Work Flow Diagram ...................................................................................................... 31
3.4 Data Flow Diagram ....................................................................................................... 32
3.5 Class Diagram ................................................................................................................ 33
3.6 Component Diagram ..................................................................................................... 34
3.7 Deployment Diagram ....................................................................................................35
3.8 User Interface Design ................................................................................................... 36
3.9 Schematics Diagram ......................................................................................................37
Chapter 4: Implementation and Testing… ......................................................................... 38
4.1 Coding ............................................................................................................................ 38
4.2 Testing Approach .......................................................................................................... 98
4.2.1 Units Testing…......................................................................................................... 98
4.2.2 Integrated Testing… ................................................................................................ 100
Chapter 5: Result and Discussion ...................................................................................... 101
5.1 Test Reports ................................................................................................................. 101
5.2 User Documentation .................................................................................................... 101
5.2.1 Screen short .............................................................................................................102
Chapter 6: Conclusions ....................................................................................................... 106
6.1 Conclusion .................................................................................................................... 106
6.2 Limitation of the System ............................................................................................. 106
6.4 Future Scope ................................................................................................................ 106
Chapter 7: Bibliography… ................................................................................................. 107
LIST OF TABLES
Table 1. Arduino Summary ...................................................................................................... 6
Table 2. MPU 6050Pin configuration ..................................................................................... 11
Table 3. Units Testing…......................................................................................................... 98
LIST OF FIGURES
Fig 1. Arduino Uno R3 ............................................................................................................. 7
Fig 2. MPU 6050 ....................................................................................................................... 10
Fig 3. OV7670 Camera Module ................................................................................................12
Fig 4 Micro SD Card Reader Module. ..................................................................................... 13
Fig 5. Fly sky CT6B 6 Channel Transmitter ................................................................................ 16
Fig 6. FS-R6B Receiver ............................................................................................................ 17
Fig 7. 2212/10T 1400kv BLDC Motors .................................................................................... 18
Fig 8. 30A Electronic Speed Controller ....................................................................................... 19
Fig 9. Frame ............................................................................................................................. 20
Fig 10. Propellers ...................................................................................................................... 21
Fig 11. LiPo battery ................................................................................................................... 22
Fig 12. Imax B3PRO Charger .................................................................................................... 23
Fig 13. Diode and Registers ....................................................................................................... 24
Fig 14. Arduino IDE ...................................................................................................................... 24
Fig 15. C++ ..............................................................................................................................25
Fig 16. Basic Modules ........................................................................................................... 28
Fig 17. Entity Relationship Diagram ......................................................................................... 30
Fig 18. Work Flow Diagram ..................................................................................................... 31
Fig 19. Data Flow Diagram ....................................................................................................... 32
Fig 20. Class Diagram ............................................................................................................... 33
Fig 21. Component Diagram ...................................................................................................... 34
Fig 22. Deployment Diagram .................................................................................................... 35
Fig 23. User Interface Diagram ................................................................................................. 36
Fig 24. Schematics Diagram for Drone .................................................................................. 37
Fig 25. Schematics Diagram for Camera .....................................................................................37
Fig 26. Drone Setup coding and Uploading ........................................................................ 102
Fig 27. Calibration of Setup Code with Transmitter .............................................................102
Fig 28. Searching For MPU-6050 ......................................................................................... 103
Fig 29. Calibrating PID with the help of Gyroscope ............................................................. 103
Fig 30. Esc’s Calibrating coding and uploading .................................................................. 104
Fig 31. Flight Controller Code for Drone ............................................................................. 104
Fig 32. Connection to Arduino .............................................................................................105
Fig 33. Drone ........................................................................................................................ 105
CHAPTER 1
INTRODUCTION
1.1 Background
In this modern age of technology, quadcopter has become one of the most popular inventions
in the field of science. A quadcopter is also known as UAV (Unmanned Aerial Vehicle) uses four
Propellers for lift and stabilization.
Research and development of Unmanned Aerial Vehicle (UAV) and Micro Aerial Vehicles
(MAV) are getting high encouragement nowadays, since the application of UAV and MAV can apply
to variety of area such as rescue mission, military, film making, agriculture and others. In U.S. Coast
Guard maritime search and rescue mission, UAV that attached with infrared cameras assist the mission
to search the target.
Quadcopter or quad rotor aircraft is one of the UAV that are major focuses of active researches
in recent years. Compare to terrestrial mobile robot that often possible to limit the model to kinematics,
Quadcopter required dynamics in order to account for gravity effect and aerodynamic forces.
Quadcopter operated by thrust that produce by four motors that attached to it body. It has four input
force and six output states, and it is an under-actuated system, since this enable Quadcopter to carry
more load
Recreational use is increasing as well, a small remote-controlled quadcopter can be bought to
flight in our living room or garden. There is no physical contact between the surroundings and the
quadcopter and no coordination between the quadcopter. It would have the capabilities to collaborate
the number of possibilities grows even further. For example, a group of Drone would be able to
efficiently and autonomously search a missing person in a large area by sharing data between. Or, the
combined load capacity of a group of quadcopters can be used to deliver medicine in remote areas.
This bachelor thesis focuses on the use of a commercially available quadcopter platform.
Drone, to perform a task that requires physical collaboration and interaction: moving a mass. In this
way a clear interaction between the quadcopters and their surroundings is present. As preliminary step
towards the view of collaborating aerial robots the choice was made to perform this task in an indoor
scenario where position feedback is present. Starting off with position control, additional controller
logic can be implemented to counteract the forces imposed by a mass connected to the quadcopter. The
choice is made for the Drone, a generalized approach is chosen where possible to encourage reuse of
this research’s outcome and deliverables.

1.2 Objectives
The main objective of this project is to make a quadcopter which could be used in security as
well as in package delivering operations. The quadcopter will be controlled by an Arduino
microcontroller which will be program flexible according to the user. The flight will be made stable
with the accumulation of various sensors like the gyroscope, accelerometer. A camera is attached to
capture the photos or videos for security and for some occasions. The quadcopter is controlled using
Transmitter and receiver to increase its range.

1.3 Purpose, Scope, and Applicability

1.3.1 Purpose
The goal of the project is to develop a quadcopter that can deliver food and product through
drone. Big companies such as DHL, Amazon, Dominos, and FedEx are testing using the drone for local
transport of their items. It could significantly lower human labour and speed up delivery times.
Drones have numerous uses in the defence, military, tactical, and modern warfare
world. These unmanned aerial systems are used to the air strikes purpose. They travel around
suspected locations as controlled from the navy individual and they are operated in certain
areas to fulfil army operations of government. Small size and powerful cameras make them
suitable for bomb detection purposes. Thus, these drones are apt to make us informed about
unexploded bombs thus saving lives.

1.3.2 Scope
The scope of the project is:
i. Quadcopter stability during unconditional weather.
ii. Quadcopter is controlled by Arduino base microcontroller and MPU 6050
(gyroscope + accelerometer) sensor.
iii. Quadcopter is operated by brushless motor controlled by electronic speed controller.

1.3.3 Applicability
 This project will help the police for security reasons.
 A Group of Drone can be used for finding the criminals or persons.
 Drone can be used to keep watch on some isolated locations to make sure all
the areas are safe.
 It will also help in delivery of good within a short span of time from anywhere
we need a parcel or the package.
 It can be used for Photography of some occasions.

1.4 Achievements
The goal of the project is to construct an Arduino system that includes the principle of
aviation. The system should be user friendly for the operator. The main goals have been
allocated to following sub goals:
1. Building a quadcopter regarding the security and delivering purpose.
2. To obtain the stability of the quadcopter in bad weather.
Implementing camera in addition to security purpose
CHAPTER 2
SYSTEM ANALYSIS
2.1 Existing System
The most commonly used system for delivery of goods is via delivery boys. The time taken for
delivery is much more time consuming. With the help of drones the delivery would be much more
faster and efficient.
The existing system can only be used for entertainment purpose not for commercial use.
The existing system does not have the ability to get a stable flight and the controlling of the drone
is much harder than it looks.
The existing system does not have the camera module hence it cannot be used for security
purposes.

2.2 Proposed System


The current system will help to get the stability and easier control during flight.
The system can be of much more useful in commercial use.
The camera used in the system will help in security and photography.

2.3 Requirement Analysis

2.3.1 Hardware Requirement


 Arduino UNO R3
1. Microcontroller: ATmega328
2. Digital I/O Pins: 14 (of which 6 provide PWM output)
3. Flash Memory: 32 KB (ATmega328) of which 0.5 KB used by bootloader
4. SRAM: 2 KB (ATmega328)
5. EEPROM: 1 KB (ATmega328)
6. Clock Speed: 16 MHz
2.4 Hardware Requirement
 Arduino UNO R3.
 MPU 6050
 Camera Module OV7670
 Micro SD Card Reader Module
 Transmitter and Receiver
 4 x 2212/10T 1400kv BLDC Motors
 4 x ESC’s (Electronic Speed Controller)
 Frame
 2 pairs Propellers
 Battery
 Battery Charger
 Some small Components.

2.5 Software Requirements


 Arduino IDE
 C++
 M S Excel.

2.6 Justification of selection of Technology


2.6.1 Arduino UNO R3
The Arduino Uno R3 is a microcontroller board based on the ATmega328 (datasheet). It has
14 digital input/output pins (of which 6 can be used as PWM outputs), 6 analog inputs, a 16 MHz crystal
oscillator, a USB connection, a power jack, an ICSP header, and a reset button. It contains everything
needed to support the microcontroller; simply connect it to a computer with a USB cable or power it
with a AC-to-DC adapter or battery to get started. The Uno differs from all preceding boards in that it
does not use the FTDI USB-to-serial driver chip. Instead, it features the Atmega16U2 (Atmega8U2 up
to version R2) programmed as a USB-to-serial converter. Revision 2 of the Uno board has a resistor
pulling the 8U2 HWB line to ground, making it easier to put into DFU mode.

Revision 3 of the board has the following new features:

 1.0 pin out: added SDA and SCL pins that are near to the AREF pin and two other new pins placed
near to the RESET pin, the IOREF that allow the shields to adapt to the voltage provided from the
board. In future, shields will be compatible both with the board that use the AVR, which operate
with 5V and with the Arduino Due that operate with 3.3V. The second one is a not connected pin
that is reserved for future purposes.
 Stronger RESET circuit.
 Atmega 16U2 replace the 8U2.

"Uno" means one in Italian and is named to mark the upcoming release of Arduino 1.0. The Uno and
version 1.0 will be the reference versions of Arduino, moving forward. The Uno is the latest in a series
of USB Arduino boards, and the reference model for the Arduino platform; for a comparison with
previous versions, see the index of Arduino boards.

SUMMARY: -

Microcontroller ATmega328

Operating Voltage 5V

Input Voltage (recommended) 7-12V

Input Voltage (limits) 6-20V

Digital I/O Pins 14 (of which 6 provide PWM output)

Analog Input Pins 6

DC Current per I/O Pin 40 mA

DC Current for 3.3V Pin 50 mA

Flash Memory 32 KB (ATmega328) of which 0.5 KB used by bootloader

SRAM 2 KB (ATmega328)

EEPROM 1 KB (ATmega328)

Clock Speed 16 MHz


Table 1. Summary
Fig. 1 ARDUINO UNO R3

POWER

The Arduino Uno can be powered via the USB connection or with an external power supply.
The power source is selected automatically. External (non-USB) power can come either from an AC-
to-DC adapter (wall-wart) or battery. The adapter can be connected by plugging a 2.1mm centre-
positive plug into the board's power jack. Leads from a battery can be inserted in the Gnd and Vin pin
headers of the POWER connector.

The board can operate on an external supply of 6 to 20 volts. If supplied with less than 7V, however,
the 5V pin may supply less than five volts and the board may be unstable. If using more than 12V, the
voltage regulator may overheat and damage the board. The recommended range is 7 to 12 volts.

The power pins are as follows:

 VIN: The input voltage to the Arduino board when it's using an external power source (as opposed
to 5 volts from the USB connection or other regulated power source). You can supply voltage
through this pin, or, if supplying voltage via the power jack.
 5V: the regulated power supply used to power the microcontroller and other components on the
board. This can come either from VIN via an on-board regulator, or be supplied by USB or another
regulated 5V supply.
 3V3: A 3.3-volt supply generated by the on-board regulator. Maximum current draw is 50 mA.
 GND: Ground pins.

MEMORY

The ATmega328 has 32 KB (with 0.5 KB used for the bootloader). It also has 2 KB of Static
Random Access Memory (SRAM) and 1 KB of Electrically Erasable Programmable Read-Only
Memory (EEPROM) which can be read and written with the EEPROM library.

INPUT AND OUTPUT

Each of the 14 digital pins on the Uno can be used as an input or output, using pin
Mode(), digital Write(), and digital Read() functions. They operate at 5 volts. Each pin can provide or
receive a maximum of 40 mA and has an internal pull-up resistor (disconnected by default) of 20-50 K
Ohms.

In addition, some pins have specialized functions:

 Serial: 0 (RX) and 1 (TX). Used to receive (RX) and transmit (TX) TTL serial data. These pins are
connected to the corresponding pins of the ATmega8U2 USB-to-TTL Serial chip.
 External Interrupts: 2 and 3. These pins can be configured to trigger an interrupt on a low value, a
rising or falling edge, or a change in value. See the attachInterrupt () function for details.
 PWM: 3, 5, 6, 9, 10, and 11. Provide 8-bit PWM output with the analogWrite() function.
 SPI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK). These pins support SPI communication using
the SPI library.
 LED: 13. There is a built-in LED connected to digital pin 13. When the pin is HIGH value, the
LED is on, when the pin is LOW, it's off.

The Uno has 6 analog inputs, labelled A0 through A5, each of which provide 10 bits of
resolution (i.e. 1024 different values). By default they measure from ground to 5 volts, though is it
possible to change the upper end of their range using the AREF pin and the analogReference() function.
Additionally, some pins have specialized functionality:

 TWI: A4 or SDA pin and A5 or SCL pin. Support TWI communication using the Wire library.

There are a couple of other pins on the board:

 AREF. Reference voltage for the analog inputs. Used with analog Reference ().
 Reset. Bring this line LOW to reset the microcontroller. Typically used to add a reset button to
shields which block the one on the board.

COMMUNICATION

The Arduino Uno has a number of facilities for communicating with a computer, another
Arduino, or other microcontrollers. The ATmega328 provides UART TTL (5V) serial communication,
which is available on digital pins 0 (RX) and 1 (TX). An ATmega16U2 on the board channels this serial
communication over USB and appears as a virtual com port to software on the computer. The '16U2
firmware uses the standard USB COM drivers, and no external driver is needed. However, on Windows,
a .inf file is required. The Arduino software includes a serial monitor which allows simple textual data
to be sent to and from the Arduino board. The RX and TX LEDs on the board will flash when data is
being transmitted via the USB-to-serial chip and USB connection to the computer (but not for serial
communication on pins 0 and 1).

A Software Serial library allows for serial communication on any of the Uno's digital pins.

The ATmega328 also supports I2C (TWI) and SPI communication. The Arduino software
includes a Wire library to simplify use of the I2C bus; see the documentation for details. For SPI
communication, use the SPI library.

PROGRAMMING

The Arduino Uno can be programmed with the Arduino software (download). Select "Arduino
Uno from the Tools > Board menu (according to the microcontroller on your board). For details, see
the reference and tutorials.

The ATmega328 on the Arduino Uno comes pre-burned with a bootloader that allows you to
upload new code to it without the use of an external hardware programmer. It communicates using the
original STK500 protocol (reference, C header files).

You can also bypass the bootloader and program the microcontroller through the ICSP (In-
Circuit Serial Programming) header; see these instructions for details.

The ATmega16U2 (or 8U2 in the rev1 and rev2 boards) firmware source code is available.
The ATmega16U2/8U2 is loaded with a DFU bootloader, which can be activated by:

 On Rev1 boards: connecting the solder jumper on the back of the board (near the map of Italy) and
then resetting the 8U2.
 On Rev2 or later boards: there is a resistor that pulling the 8U2/16U2 HWB line to ground, making
it easier to put into DFU mode.

You can then use Atmel's FLIP software (Windows) or the DFU programmer (Mac OS X and
Linux) to load a new firmware. Or you can use the ISP header with an external programmer (overwriting
the DFU bootloader). See this user-contributed tutorial for more information.

2.6.2 MPU 6050


The MPU6050 is a Micro Electro-Mechanical Systems (MEMS) which consists of a 3-
axis Accelerometer and 3-axis Gyroscope inside it. This helps us to measure acceleration,
velocity, orientation, displacement and many other motions related parameter of a system or
object. This module also has a (DMP) Digital Motion Processor inside it which is powerful
enough to perform complex calculation and thus free up the work for Microcontroller.

The module also has two auxiliary pins which can be used to interface external IIC modules like a
magnetometer, however it is optional. Since the IIC address of the module is configurable more than
one MPU6050 sensor can be interfaced to a Microcontroller using the AD0 pin. This module also has
well documented and revised libraries available hence it’s very easy to use with famous platforms like
Arduino.

Fig. 2 MPU 6050


Pin Pin Name Description
Number

1 Vcc Provides power for the module, can be +3V to +5V.


Typically +5V is used

2 Ground Connected to Ground of system

3 Serial Clock (SCL) Used for providing clock pulse for I2C Communication

4 Serial Data (SDA) Used for transferring Data through I2C communication

5 Auxiliary Serial Data Can be used to interface other I2C modules with
(XDA) MPU6050. It is optional

6 Auxiliary Serial Can be used to interface other I2C modules with


Clock (XCL) MPU6050. It is optional

7 AD0 If more than one MPU6050 is used a single MCU, then this
pin can be used to vary the address

8 Interrupt (INT) Interrupt pin to indicate that data is available for MCU to
read.

Table 2. MPU6050 Pin Configuration

2.6.3 Camera Module OV7670


OV7670 Camera Module is a FIFO camera Module available from different
Manufacturers with different pin Configurations. TheOV7670 provides full frame, windowed
8-bit images in a wide range of formats. The image array is capable of operating at up to 30
frames per second (fps) in VGA. The OV7670 includes

 Image Sensor Array (of about 656 x 488 pixels)


 Timing Generator
 Analog Signal Processor
 A/D Converters
 Test Pattern Generator
 Digital Signal Processor (DSP)
 Image Scaler
 Digital Video Port
 LED and Strobe Flash Control Output

The OV7670 image sensor is controlled using Serial Camera Control Bus (SCCB) which is
an I2C interface (SIOC, SIOD) with a maximum clock frequency of 400KHz.

Fig. 3 OV7670 Camera Module

The Camera comes with handshaking signals such as:

 VSYNC: Vertical Sync Output – Low during frame


 HREF: Horizontal Reference – High during active pixels of row
 PCLK: Pixel Clock Output – Free running clock. Data is valid on rising edge

In addition to this, it has several more signals such as

 D0-D7: 8-bit YUV/RGB Video Component Digital Output


 PWDN: Power Down Mode Selection – Normal Mode and Power Down Mode
 XCLK: System Clock Input
 Reset: Reset Signal
2.6.4 Micro SD Card Reader Module
The module (MicroSD Card Adapter) is a Micro SD card reader module, and the SPI
interface via the file system driver, microcontroller system to complete the MicroSD card read
and write files. Arduino users can directly use the Arduino IDE comes with an SD card to
complete the library card initialization and read-write.

Fig. 4 Micro SD Card Reader Module

Pins of SD Card Module

Talking about pins, as I have mentioned that a Micro SD Card supports only SPI
Communication, the SD Card Module has pins for SPI Communication. So, the pins on an
SD Card Module are as follows.

 VCC – 5V
 GND – GND
 MOSI – Master OUT Slave IN (Input)
 MISO – Master IN Slave OUT (Output)
 SCK – SPI Clock (Input)
 CS – Chip Select (Input)

2.6.5 Transmitter and Receiver


A radio control system is made up of two elements, the transmitter you hold in your hands and
the receiver you put inside your drone. Dramatically simplifying things here, your drone transmitter
will read your stick inputs and send them through the air to your receiver in near real time. Once the
receiver has this information it passes it on to your drones flight controller which makes the drone move
accordingly. A radio will have four separate channels for each direction on the sticks along with some
extra ones for any auxiliary switches it may have.

RANGE TECHNOLOGY
The limit of range is normally where the receiver can no longer clearly hear what the
transmitter telling it and typically falls in the 1km range in normal conditions. Imagine trying
to talk to someone across a field. The range of your radio link will be dependent on a few
factors:

1. The output power of your transmitter - Many runs just below the legal maximum to be compliant
with international standards.
2. The sensitivity of the Receiver - A more sensitive receiver is like having better hearing, the signal
will travel further however it may pick up more noise in certain conditions
3. The quality of your antennas at both ends - Antennas could be an entire article on their own but
basically a larger antenna will send and receive a better signal. Often optimising your antenna
placement will make a huge difference to the performance to the system.

Although typical radio systems use the 2.4Ghz band, specialist long range systems such as the
TBS Crossfire can run on much lower frequencies which are able to travel much further at the same
power.

Transmitter
The transmitter used in the project is Fly Sky CT6B 2.4Ghz 6CH Transmitter
CT6B FLYSKY 2.4GHZ 6CH TRANSMITTER is an entry-level 2.4 GHz radio system offering the
reliability of 2.4 GHz signal.
CT6B FLYSKY 2.4GHZ 6CH TRANSMITTER radio is a value for money, entry level 6
channel transmitter, ideal for quadcopters and multicopper that require the 6ch operation.
This radio has two retract switches and proportional flap dials in easy reach for channels 5 and 6. It can
be powered by 8 x AA Size Batteries or a 12V Power Supply.
The T6 channel arrangement is:
 CH 1 ailerons
 CH 2 elevator
 CH 3 throttle
 CH 4 rudder
 CH 5 aux
 CH 6 aux

Specifications:

 Super active and passive anti-jamming capabilities.


 Very low power consumption.
 High receiving sensitivity.
 8 model memory, digital control.
 Can be programmed by PC with the included software.
 Full range 2.4GHz 6-channel radio.
 4-Model
 4 Types (Airplane, Heli90, Heli120, Heli140).
 Integrated
 Contrast Adjustment.
 Throttle cut.
 Computer
 USB Socket.
 Use a linear spread of fine paragraph by the excess antenna.
 It covers the entire bandwidth of the antenna bandwidth range.
 High quality and stability.

Fig. 5 Fly sky CT6B 6 Channel Transmitter


Receiver
The receiver that is compatible with the CT6B 6 Channel Transmitter is FS-R6B Receiver. It
has 6 channels as same as the transmitter. The receiver has an inbuilt voltage detection that is the LED
to show the voltage is low.

Specifications:
 Channels: 6 Channels
 Model type: heli/airplane
 RF power: less than 20DBm
 modulation: GFSK
 Code type: PCM
 Sensitivity: 1024
 Low voltage warning: LED
 Charger port: yes
 ANT length: 26mm
 Frequency: 2.4G
 Power: 5V DC(1.5V AAA*4)
 weight: 25g
 size: 30*25*8mm

Fig. 6 FS-R6B Receiver

2.6.6 A2212/10T 1400KV BLDC Motors


A motor converts supplied electrical energy into mechanical energy. Various types of
motors are in common use. Among these, brushless DC motors (BLDC) feature high efficiency
and excellent controllability, and are widely used in many applications. The BLDC motor has
power-saving advantages relative to other motor types.
Specification of 2212/10T 1400KV BLDC Motor
 KV Values: 1400KV
 Voltage: DC 8~12V
 Current: 11.9~20.6A
 Trust: 520~940G
 Power: 95.2~247.2W
 Efficiency: 5.5~3.8G/W
 Rotation Speed: 8250~12020RPM.

Fig. 7 2212/10T 1400kv BLDC Motors

2.6.7 ESC’s (Electronic Speed Controller)


This is fully programmable 30A BLDC ESC with 5V, 3A BEC. Can drive motors with
continuous 30Amp load current. It has sturdy construction with 2 separate PCBs for Controller
and ESC power MOSFETs. It can be powered with 2-4 lithium Polymer batteries or 5-12
NiMH / NiCd batteries. It has separate voltage regulator for the microcontroller for providing
good anti-jamming capability. It is most suitable for UAVs, Aircrafts and Helicopters.
Specifications:
 Output: 30A continuous; 40Amps for 10 second.
 Input voltage: 2-4 cells Lithium Polymer.
 BEC: 5V, 3Amp for external receiver and servos.
 Max Speed: 2 Pole: 210,000rpm; 6 Pole: 70,000rpm; 12 Pole: 35,000rpm.
 Weight: 32gms • Size: 55mm x 26mm x 13mm.
Features:
 High quality MOSFETs for BLDC motor drive.
 High performance microcontroller for best compatibility with all types of motors at greater
efficiency.
 Fully programmable with any standard RC remote control.
 Heat sink with high performance heat transmission membrane for better thermal
management.
 3 start modes: Normal / Soft / Super-Soft, compatible with fixed wing aircrafts.
 Throttle range can be configured to be compatible with any remote control available in the
market Figure 1: 30A BLDC ESC.
 Output: 30A continuous; 40Amps for 10 seconds.
 Input voltage: 2-4 cells Lithium Polymer.
 Smooth, Linear and Precise throttle response.
 Low-Voltage cut-off protection • Over-heat protection.
 Separate voltage regulator IC for the microcontroller to provide anti-jamming capability.
 Supported Motor Speed (Maximum): 210000RPM (2 poles), 70000RPM (6poles),
35000RPM (12 poles).

Fig. 8 30A Electronic Speed Controller

2.6.8 Frame
The frame of the drone should be rigid, as when we test the drone for flying it may fall from a
height and may break. The frame should also be light and can fly with the help for the propellers.
Specifications:
 The frame is made up of quality glass fibre and polyamide nylon.
 Integrated PCB connections for direct soldering of your esc's.
 Pre-threaded brass sleeves for all of the frame bolts.
 Coloured arms for orientation to keep you flying in the right direction.
 Large mounting tabs on main frame bottom plate for easy camera mounting.

Fig. 9 Frame

2.6.9 Propellers
The purpose of your quadcopter propellers is to generate thrust and torque to keep your
drone flying, and to hover.
The upward thrust force generated by the propellers is usually measured in pounds or grams. To keep
your drone flying at a hover, the upward thrust needs to equal the weight of your drone. The thrust to
weight ratio TWR (thrust divided by weight), indicates how much thrust your drone generates relative
to its weight. A good rule of thumb is to design the TWR to be at least a value of two.
Typically, quadcopter propellers produce more thrust the faster they spin. They are also
influenced by the flight dynamics of your quadcopter. Some propellers produce much more thrust when
the drone is stationary, as opposed to when it is flying. Other props perform much better at higher
speeds.

Specification:
 Made up of carbon nylon material which makes it strong.
 Size: 1 x 4.5". Diameter of Shaft: 6.mm
 Thickness of centre: 9.7mm

Fig. 10 Propellers

2.6.10 Battery
The community standard is the Lithium-Polymer or LiPo battery. These kinds of
batteries are light weight, compact and offer high discharge rates. These kinds of batteries are
made up of multiple cells. A single cell provides a nominal 3.5 Volts. To provide higher
voltages these cells are connected in series and have a “S” designation. A 3S battery holds 3.5
x 3 = 11.1 Volts. LiPo batteries also carry a “C” rating which indicates the maximum discharge
rate. The “C” stands for capacity. So, a 25C battery can discharge at 25 times its capacity. The
third important measurement for a battery is its capacity which is provided in milli-Amp hour
(mAh) values. A 2200 mAh battery with a 25C rating can discharge 25 x 2200 = 55000 mA or
55 Amperes. The discharge rate should be higher than the combined draw from your ESC’s
and motors.

Measures that should be taken while charging the battery


1. Don't over-charge, or over-discharge batteries, don't put it beside the high temperature
condition.
2. Avoid over charging and placing beside high temperature sources; Never put the load above
4A or it will render the battery unusable.
Fig. 11 LiPo battery

2.6.11 Battery Charger


It's important to use a LiPo compatible charger for LiPos. As the LiPo batteries require
specialized care. They charge using a system called CC/CV charging. It stands for Constant
Current / Constant Voltage. Basically, the charger will keep the current, or charge rate, constant
until the battery reaches its peak voltage (3.5v per cell in a battery pack). Then it will maintain
that voltage, while reducing the current. NiMH and NiCad batteries charge best using a pulse
charging method. Charging a LiPo battery in this way can have damaging effects, so it's
important to have a LiPo-compatible charger.
The charger that is used in the project is Imax B3PRO. The reason that I have choose this charger
because it full-fill all the need to charge my battery and it is cheap.
Specifications:

 Input: 110v or 240v A/C (50/60Hz)


 Cell Count: 2~3s
 Battery Type: Li-Po
 Output Current: 3 x 800mA
Fig. 12 Imax B3PRO Charger

2.6.12 Some Small Components.

The diode D1 protects the USB port of the computer when the Arduino is connected
to the computer. This diode has an important function and cannot be excluded.

The resistors divide the flight battery voltage by 2.5. This way it is possible to
measure the battery voltage during flight. The LED will light up when the battery voltage gets
to low and the motor rpm automatically increase to compensate the dropping battery voltage
during flight.

The 1kΩ and 1.5kΩ resistors need to be installed correctly otherwise the quadcopter
will not fly perfect.

The 10kΩ and 4.7kΩ resistors need to be installed correctly to the OV7670 module
to maintain the voltage of the module.
Fig. 13 Diode and Registers

2.6.13 Arduino IDE


The Arduino integrated development environment (IDE) is a cross-
platform application (for Windows, macOS, Linux) that is written in the programming
language Java. It is used to write and upload programs to Arduino compatible boards, but also,
with the help of 3rd party cores, other vendor development boards.

Fig. 14 Arduino IDE

The source code for the IDE is released under the GNU General Public License,
version The Arduino IDE supports the languages C and C++ using special rules of code
structuring. The Arduino IDE supplies a software library from the Wiring project, which
provides many common input and output procedures. User-written code only requires two basic
functions, for starting the sketch and the main program loop, that are compiled and linked with
a program stub main() into an executable cyclic executive program with the GNU toolchain,
also included with the IDE distribution. The Arduino IDE employs the program avrdude to
convert the executable code into a text file in hexadecimal encoding that is loaded into the
Arduino board by a loader program in the board's firmware.

2.6.14 C++

C++ is a general-purpose programming language created by Bjarne Stroustrup as an


extension of the C programming language, or "C with Classes". The language has expanded
significantly over time, and modern C++ has object-oriented, generic, and functional features
in addition to facilities for low-level memory manipulation. It is almost always implemented
as a compiled language, and many vendors provide C++ compilers, including the Free
Software Foundation, LLVM, Microsoft, Intel, Oracle, and IBM, so it is available on many
platforms.

C++ was designed with a bias toward system programming and embedded, resource-
constrained software and large systems, with performance, efficiency and flexibility of use as
its design highlights. C++ has also been found useful in many other contexts, with key strengths
being software infrastructure and resource-constrained applications, including desktop
applications, servers (e.g. e-commerce, Web search or SQL servers), and performance-critical
applications (e.g. telephone switches or space probes).

Fig.15 C++
C++ is standardized by the International Organization for Standardization (ISO), with
the latest standard version ratified and published by ISO in December 2017 as ISO/IEC
14882:2017 (informally known as C++17). The C++ programming language was initially
standardized in 1998 as ISO/IEC 14882:1998, which was then amended by
the C++03, C++11 and C++14 standards. The current C++17 standard supersedes these with
new features and an enlarged standard library. Before the initial standardization in 1998, C++
was developed by Danish computer scientist Bjarne Stroustrup at Bell Labs since 1979 as an
extension of the C language; he wanted an efficient and flexible language similar to C that also
provided high-level features for program organization. C++20 is the next planned standard,
keeping with the current trend of a new version every three years.

2.6.15 MS Excel

Microsoft Excel has the basic features of all spreadsheets, using a grid of cells arranged
in numbered rows and letter-named columns to organize data manipulations like arithmetic
operations. It has a battery of supplied functions to answer statistical, engineering and financial
needs. In addition, it can display data as line graphs, histograms and charts, and with a very
limited three-dimensional graphical display. It allows sectioning of data to view its
dependencies on various factors for different perspectives (using pivot tables and the scenario
manager). It has a programming aspect, Visual Basic for Applications, allowing the user to
employ a wide variety of numerical methods, for example, for solving differential equations of
mathematical physics, and then reporting the results back to the spreadsheet. It also has a
variety of interactive features allowing user interfaces that can completely hide the spreadsheet
from the user, so the spreadsheet presents itself as a so-called application, or decision support
system (DSS), via a custom-designed user interface, for example, a stock analyser, or in
general, as a design tool that asks the user questions and provides answers and reports. In a
more elaborate realization, an Excel application can automatically poll external databases and
measuring instruments using an update schedule, analyse the results, make a Word report
or PowerPoint slide show, and e-mail these presentations on a regular basis to a list of
participants. Excel was not designed to be used as a database.

Microsoft allows for a number of optional command-line switches to control the manner
in which Excel starts.

Functions
Excel 2016 has 484 functions. Of these, 360 existed prior to Excel 2010. Microsoft
classifies these functions in 14 categories. Of the 484 current functions, 386 may be called
from VBA as methods of the object "Worksheet Function" and 44 have the same names as
VBA functions.
CHAPTER 3
SYSTEM DESIGN
3.1 Module Division

Fig. 16 Basic Modules


1. Transmitter
The transmitter transmits the signal in the form of waves. The transmitter will read your stick
inputs and send them through the air to your receiver in near real time
2. Receiver
Once the receiver has this information it passes it on to your drone’s flight controller that is
Arduino.

3. Arduino Uno R3
Arduino is a microcontroller which is programable using Arduino ide. Arduino is programmed
to send the signals of the transmitter to MPU 6050 for the movement of the drone.

4. MPU6050
The MPU6050 is a Micro Electro-Mechanical Systems (MEMS) which consists of a 3-axis
Accelerometer and 3-axis Gyroscope inside it. This helps us to measure acceleration, velocity,
orientation, displacement and many other motions related parameter of a system or object. It
measures the balancing of the drone and calibrates the ESCs of the drone. If the drone is not stable
then the signals are then sent to Arduino till the stability is maintained

5. ESCs
Electronic Speed Controllers (ESCs) gets the signals from the MPU 6050 to maintain the
rotation of the motor according to the angle of the drone.
3.2 Entity Relationship Diagram

Fig. 17 Entity Relationship Diagram


The given diagram is line the relation between the entity and the next entity one to one relation and
one to one relationship between and they also give a flow diagram of working the machine
3.3 Work Flow Diagram

Fig. 18 Work Flow Diagram


3.4 Data Flow Diagram

Fig.19 Data Flow Diagram


This diagram is show the way the data will flow in the Drone to save the picture and the PID control
in the drone and the working.
3.5 Class Diagram

Fig.20 Class Diagram


This diagram is show that the all the attribute are use in this project
3.6 Component Diagram

Fig.21 Component Diagram


3.7 Deployment Diagram

Fig.22 Deployment Diagram


3.8 User Interface Diagram

Fig. 23 User Interface Diagram


The user gives the command with the help of transmitter and that is received by the
receiver. The then forwards the signal to Arduino and then its is processed by MPU 6050 to
maintain the stability of the drone and the drone than hovers properly.
3.9 Schematics Diagrams

Fig. 24 Schematics Diagram for Drone


Circuit diagram for connecting Arduino with MPU6050, Receive, and the ESC to make
Arduino a flight controller

Fig 25 Schematics Diagram for Camera


Circuit diagram for connecting of Arduino with the OV7670 Camera module to take images
and save the same to the SD card Module
CHAPTER 4
IMPLEMENTATION AND TESTING
4.1 Coding
Setup.cpp
#include <Wire.h> //Include the Wire.h library so we can communicate with the gyro
#include <EEPROM.h> //Include the EEPROM.h library so we can store information onto the
EEPROM

//Declaring Global Variables


byte last_channel_1, last_channel_2, last_channel_3, last_channel_4;
byte lowByte, highByte, type, gyro_address, error, clockspeed_ok;
byte channel_1_assign, channel_2_assign, channel_3_assign, channel_4_assign;
byte roll_axis, pitch_axis, yaw_axis;
byte receiver_check_byte, gyro_check_byte;
volatile int receiver_input_channel_1, receiver_input_channel_2, receiver_input_channel_3,
receiver_input_channel_4;
int center_channel_1, center_channel_2, center_channel_3, center_channel_4;
int high_channel_1, high_channel_2, high_channel_3, high_channel_4;
int low_channel_1, low_channel_2, low_channel_3, low_channel_4;
int address, cal_int;
unsigned long timer, timer_1, timer_2, timer_3, timer_4, current_time;
float gyro_pitch, gyro_roll, gyro_yaw;
float gyro_roll_cal, gyro_pitch_cal, gyro_yaw_cal;

//Setup routine
void setup(){
pinMode(12, OUTPUT);
//Arduino (Atmega) pins default to inputs, so they don't need to be explicitly declared as inputs
PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); // set PCINT0 (digital input 8) to trigger an interrupt on state change
PCMSK0 |= (1 << PCINT1); // set PCINT1 (digital input 9)to trigger an interrupt on state change
PCMSK0 |= (1 << PCINT2); // set PCINT2 (digital input 10)to trigger an interrupt on state change
PCMSK0 |= (1 << PCINT3); // set PCINT3 (digital input 11)to trigger an interrupt on state change
Wire.begin(); //Start the I2C as master
Serial.begin(57600); //Start the serial connetion @ 57600bps
delay(250); //Give the gyro time to start
}
//Main program
void loop(){
intro();

Serial.println(F(""));
Serial.println(F("==================================================="));
Serial.println(F("System check"));
Serial.println(F("==================================================="));
delay(1000);
Serial.println(F("Checking I2C clock speed."));
delay(1000);

TWBR = 12; //Set the I2C clock speed to 400kHz.

#if F_CPU == 16000000L //If the clock speed is 16MHz include the next code line when
compiling
clockspeed_ok = 1; //Set clockspeed_ok to 1
#endif //End of if statement

if(TWBR == 12 && clockspeed_ok){


Serial.println(F("I2C clock speed is correctly set to 400kHz."));
}
else{
Serial.println(F("I2C clock speed is not set to 400kHz. (ERROR 8)"));
error = 1;
}

if(error == 0){
Serial.println(F(""));
Serial.println(F("==================================================="));
Serial.println(F("Transmitter setup"));
Serial.println(F("==================================================="));
delay(1000);
Serial.print(F("Checking for valid receiver signals."));
//Wait 10 seconds until all receiver inputs are valid
wait_for_receiver();
Serial.println(F(""));
}
//Quit the program in case of an error
if(error == 0){
delay(2000);
Serial.println(F("Place all sticks and subtrims in the center position within 10 seconds."));
for(int i = 9;i > 0;i--){
delay(1000);
Serial.print(i);
Serial.print(" ");
}
Serial.println(" ");
//Store the central stick positions
center_channel_1 = receiver_input_channel_1;
center_channel_2 = receiver_input_channel_2;
center_channel_3 = receiver_input_channel_3;
center_channel_4 = receiver_input_channel_4;
Serial.println(F(""));
Serial.println(F("Center positions stored."));
Serial.print(F("Digital input 08 = "));
Serial.println(receiver_input_channel_1);
Serial.print(F("Digital input 09 = "));
Serial.println(receiver_input_channel_2);
Serial.print(F("Digital input 10 = "));
Serial.println(receiver_input_channel_3);
Serial.print(F("Digital input 11 = "));
Serial.println(receiver_input_channel_4);
Serial.println(F(""));
Serial.println(F(""));
}
if(error == 0){
Serial.println(F("Move the throttle stick to full throttle and back to center"));
//Check for throttle movement
check_receiver_inputs(1);
Serial.print(F("Throttle is connected to digital input "));
Serial.println((channel_3_assign & 0b00000111) + 7);
if(channel_3_assign & 0b10000000)Serial.println(F("Channel inverted = yes"));
else Serial.println(F("Channel inverted = no"));
wait_sticks_zero();

Serial.println(F(""));
Serial.println(F(""));
Serial.println(F("Move the roll stick to simulate left wing up and back to center"));
//Check for throttle movement
check_receiver_inputs(2);
Serial.print(F("Roll is connected to digital input "));
Serial.println((channel_1_assign & 0b00000111) + 7);
if(channel_1_assign & 0b10000000)Serial.println(F("Channel inverted = yes"));
else Serial.println(F("Channel inverted = no"));
wait_sticks_zero();
}
if(error == 0){
Serial.println(F(""));
Serial.println(F(""));
Serial.println(F("Move the pitch stick to simulate nose up and back to center"));
//Check for throttle movement
check_receiver_inputs(3);
Serial.print(F("Pitch is connected to digital input "));
Serial.println((channel_2_assign & 0b00000111) + 7);
if(channel_2_assign & 0b10000000)Serial.println(F("Channel inverted = yes"));
else Serial.println(F("Channel inverted = no"));
wait_sticks_zero();
}
if(error == 0){
Serial.println(F(""));
Serial.println(F(""));
Serial.println(F("Move the yaw stick to simulate nose right and back to center"));
//Check for throttle movement
check_receiver_inputs(4);
Serial.print(F("Yaw is connected to digital input "));
Serial.println((channel_4_assign & 0b00000111) + 7);
if(channel_4_assign & 0b10000000)Serial.println(F("Channel inverted = yes"));
else Serial.println(F("Channel inverted = no"));
wait_sticks_zero();
}
if(error == 0){
Serial.println(F(""));
Serial.println(F(""));
Serial.println(F("Gently move all the sticks simultaneously to their extends"));
Serial.println(F("When ready put the sticks back in their center positions"));
//Register the min and max values of the receiver channels
register_min_max();
Serial.println(F(""));
Serial.println(F(""));
Serial.println(F("High, low and center values found during setup"));
Serial.print(F("Digital input 08 values:"));
Serial.print(low_channel_1);
Serial.print(F(" - "));
Serial.print(center_channel_1);
Serial.print(F(" - "));
Serial.println(high_channel_1);
Serial.print(F("Digital input 09 values:"));
Serial.print(low_channel_2);
Serial.print(F(" - "));
Serial.print(center_channel_2);
Serial.print(F(" - "));
Serial.println(high_channel_2);
Serial.print(F("Digital input 10 values:"));
Serial.print(low_channel_3);
Serial.print(F(" - "));
Serial.print(center_channel_3);
Serial.print(F(" - "));
Serial.println(high_channel_3);
Serial.print(F("Digital input 11 values:"));
Serial.print(low_channel_4);
Serial.print(F(" - "));
Serial.print(center_channel_4);
Serial.print(F(" - "));
Serial.println(high_channel_4);
Serial.println(F("Move stick 'nose up' and back to center to continue"));
check_to_continue();
}

if(error == 0){
//What gyro is connected
Serial.println(F(""));
Serial.println(F("==================================================="));
Serial.println(F("Gyro search"));
Serial.println(F("==================================================="));
delay(2000);

Serial.println(F("Searching for MPU-6050 on address 0x68/104"));


delay(1000);
if(search_gyro(0x68, 0x75) == 0x68){
Serial.println(F("MPU-6050 found on address 0x68"));
type = 1;
gyro_address = 0x68;
}

if(type == 0){
Serial.println(F("Searching for MPU-6050 on address 0x69/105"));
delay(1000);
if(search_gyro(0x69, 0x75) == 0x68){
Serial.println(F("MPU-6050 found on address 0x69"));
type = 1;
gyro_address = 0x69;
}
}
if(type == 0){
Serial.println(F("No gyro device found!!! (ERROR 3)"));
error = 1;
}

else{
delay(3000);
Serial.println(F(""));
Serial.println(F("==================================================="));
Serial.println(F("Gyro register settings"));
Serial.println(F("==================================================="));
start_gyro(); //Setup the gyro for further use
}
}

//If the gyro is found we can setup the correct gyro axes.
if(error == 0){
delay(3000);
Serial.println(F(""));
Serial.println(F("==================================================="));
Serial.println(F("Gyro calibration"));
Serial.println(F("==================================================="));
Serial.println(F("Don't move the quadcopter!! Calibration starts in 3 seconds"));
delay(3000);
Serial.println(F("Calibrating the gyro, this will take +/- 8 seconds"));
Serial.print(F("Please wait"));
//Let's take multiple gyro data samples so we can determine the average gyro offset (calibration).
for (cal_int = 0; cal_int < 2000 ; cal_int ++){ //Take 2000 readings for calibration.
if(cal_int % 100 == 0)Serial.print(F(".")); //Print dot to indicate calibration.
gyro_signalen(); //Read the gyro output.
gyro_roll_cal += gyro_roll; //Ad roll value to gyro_roll_cal.
gyro_pitch_cal += gyro_pitch; //Ad pitch value to gyro_pitch_cal.
gyro_yaw_cal += gyro_yaw; //Ad yaw value to gyro_yaw_cal.
delay(4); //Wait 3 milliseconds before the next loop.
}
//Now that we have 2000 measures, we need to devide by 2000 to get the average gyro offset.
gyro_roll_cal /= 2000; //Divide the roll total by 2000.
gyro_pitch_cal /= 2000; //Divide the pitch total by 2000.
gyro_yaw_cal /= 2000; //Divide the yaw total by 2000.

//Show the calibration results


Serial.println(F(""));
Serial.print(F("Axis 1 offset="));
Serial.println(gyro_roll_cal);
Serial.print(F("Axis 2 offset="));
Serial.println(gyro_pitch_cal);
Serial.print(F("Axis 3 offset="));
Serial.println(gyro_yaw_cal);
Serial.println(F(""));

Serial.println(F("==================================================="));
Serial.println(F("Gyro axes configuration"));
Serial.println(F("==================================================="));

//Detect the left wing up movement


Serial.println(F("Lift the left side of the quadcopter to a 45 degree angle within 10 seconds"));
//Check axis movement
check_gyro_axes(1);
if(error == 0){
Serial.println(F("OK!"));
Serial.print(F("Angle detection = "));
Serial.println(roll_axis & 0b00000011);
if(roll_axis & 0b10000000)Serial.println(F("Axis inverted = yes"));
else Serial.println(F("Axis inverted = no"));
Serial.println(F("Put the quadcopter back in its original position"));
Serial.println(F("Move stick 'nose up' and back to center to continue"));
check_to_continue();

//Detect the nose up movement


Serial.println(F(""));
Serial.println(F(""));
Serial.println(F("Lift the nose of the quadcopter to a 45 degree angle within 10 seconds"));
//Check axis movement
check_gyro_axes(2);
}
if(error == 0){
Serial.println(F("OK!"));
Serial.print(F("Angle detection = "));
Serial.println(pitch_axis & 0b00000011);
if(pitch_axis & 0b10000000)Serial.println(F("Axis inverted = yes"));
else Serial.println(F("Axis inverted = no"));
Serial.println(F("Put the quadcopter back in its original position"));
Serial.println(F("Move stick 'nose up' and back to center to continue"));
check_to_continue();

//Detect the nose right movement


Serial.println(F(""));
Serial.println(F(""));
Serial.println(F("Rotate the nose of the quadcopter 45 degree to the right within 10 seconds"));
//Check axis movement
check_gyro_axes(3);
}
if(error == 0){
Serial.println(F("OK!"));
Serial.print(F("Angle detection = "));
Serial.println(yaw_axis & 0b00000011);
if(yaw_axis & 0b10000000)Serial.println(F("Axis inverted = yes"));
else Serial.println(F("Axis inverted = no"));
Serial.println(F("Put the quadcopter back in its original position"));
Serial.println(F("Move stick 'nose up' and back to center to continue"));
check_to_continue();
}
}
if(error == 0){
Serial.println(F(""));
Serial.println(F("==================================================="));
Serial.println(F("LED test"));
Serial.println(F("==================================================="));
digitalWrite(12, HIGH);
Serial.println(F("The LED should now be lit"));
Serial.println(F("Move stick 'nose up' and back to center to continue"));
check_to_continue();
digitalWrite(12, LOW);
}

Serial.println(F(""));
if(error == 0){
Serial.println(F("==================================================="));
Serial.println(F("Final setup check"));
Serial.println(F("==================================================="));
delay(1000);
if(receiver_check_byte == 0b00001111){
Serial.println(F("Receiver channels ok"));
}
else{
Serial.println(F("Receiver channel verification failed!!! (ERROR 6)"));
error = 1;
}
delay(1000);
if(gyro_check_byte == 0b00000111){
Serial.println(F("Gyro axes ok"));
}
else{
Serial.println(F("Gyro exes verification failed!!! (ERROR 7)"));
error = 1;
}
}

if(error == 0){
//If all is good, store the information in the EEPROM
Serial.println(F(""));
Serial.println(F("==================================================="));
Serial.println(F("Storing EEPROM information"));
Serial.println(F("==================================================="));
Serial.println(F("Writing EEPROM"));
delay(1000);
Serial.println(F("Done!"));
EEPROM.write(0, center_channel_1 & 0b11111111);
EEPROM.write(1, center_channel_1 >> 8);
EEPROM.write(2, center_channel_2 & 0b11111111);
EEPROM.write(3, center_channel_2 >> 8);
EEPROM.write(4, center_channel_3 & 0b11111111);
EEPROM.write(5, center_channel_3 >> 8);
EEPROM.write(6, center_channel_4 & 0b11111111);
EEPROM.write(7, center_channel_4 >> 8);
EEPROM.write(8, high_channel_1 & 0b11111111);
EEPROM.write(9, high_channel_1 >> 8);
EEPROM.write(10, high_channel_2 & 0b11111111);
EEPROM.write(11, high_channel_2 >> 8);
EEPROM.write(12, high_channel_3 & 0b11111111);
EEPROM.write(13, high_channel_3 >> 8);
EEPROM.write(14, high_channel_4 & 0b11111111);
EEPROM.write(15, high_channel_4 >> 8);
EEPROM.write(16, low_channel_1 & 0b11111111);
EEPROM.write(17, low_channel_1 >> 8);
EEPROM.write(18, low_channel_2 & 0b11111111);
EEPROM.write(19, low_channel_2 >> 8);
EEPROM.write(20, low_channel_3 & 0b11111111);
EEPROM.write(21, low_channel_3 >> 8);
EEPROM.write(22, low_channel_4 & 0b11111111);
EEPROM.write(23, low_channel_4 >> 8);
EEPROM.write(24, channel_1_assign);
EEPROM.write(25, channel_2_assign);
EEPROM.write(26, channel_3_assign);
EEPROM.write(27, channel_4_assign);
EEPROM.write(28, roll_axis);
EEPROM.write(29, pitch_axis);
EEPROM.write(30, yaw_axis);
EEPROM.write(31, type);
EEPROM.write(32, gyro_address);
//Write the EEPROM signature
EEPROM.write(33, 'J');
EEPROM.write(34, 'M');
EEPROM.write(35, 'B');

//To make sure evrything is ok, verify the EEPROM data.


Serial.println(F("Verify EEPROM data"));
delay(1000);
if(center_channel_1 != ((EEPROM.read(1) << 8) | EEPROM.read(0)))error = 1;
if(center_channel_2 != ((EEPROM.read(3) << 8) | EEPROM.read(2)))error = 1;
if(center_channel_3 != ((EEPROM.read(5) << 8) | EEPROM.read(4)))error = 1;
if(center_channel_4 != ((EEPROM.read(7) << 8) | EEPROM.read(6)))error = 1;

if(high_channel_1 != ((EEPROM.read(9) << 8) | EEPROM.read(8)))error = 1;


if(high_channel_2 != ((EEPROM.read(11) << 8) | EEPROM.read(10)))error = 1;
if(high_channel_3 != ((EEPROM.read(13) << 8) | EEPROM.read(12)))error = 1;
if(high_channel_4 != ((EEPROM.read(15) << 8) | EEPROM.read(14)))error = 1;

if(low_channel_1 != ((EEPROM.read(17) << 8) | EEPROM.read(16)))error = 1;


if(low_channel_2 != ((EEPROM.read(19) << 8) | EEPROM.read(18)))error = 1;
if(low_channel_3 != ((EEPROM.read(21) << 8) | EEPROM.read(20)))error = 1;
if(low_channel_4 != ((EEPROM.read(23) << 8) | EEPROM.read(22)))error = 1;

if(channel_1_assign != EEPROM.read(24))error = 1;
if(channel_2_assign != EEPROM.read(25))error = 1;
if(channel_3_assign != EEPROM.read(26))error = 1;
if(channel_4_assign != EEPROM.read(27))error = 1;

if(roll_axis != EEPROM.read(28))error = 1;
if(pitch_axis != EEPROM.read(29))error = 1;
if(yaw_axis != EEPROM.read(30))error = 1;
if(type != EEPROM.read(31))error = 1;
if(gyro_address != EEPROM.read(32))error = 1;

if('J' != EEPROM.read(33))error = 1;
if('M' != EEPROM.read(34))error = 1;
if('B' != EEPROM.read(35))error = 1;

if(error == 1)Serial.println(F("EEPROM verification failed!!! (ERROR 5)"));


else Serial.println(F("Verification done"));
}

if(error == 0){
Serial.println(F("Setup is finished."));
Serial.println(F("You can now calibrate the esc's and upload the YMFC-AL code."));
}
else{
Serial.println(F("The setup is aborted due to an error."));
}
while(1);
}

//Search for the gyro and check the register


byte search_gyro(int gyro_address, int who_am_i){
Wire.beginTransmission(gyro_address);
Wire.write(who_am_i);
Wire.endTransmission();
Wire.requestFrom(gyro_address, 1);
timer = millis() + 100;
while(Wire.available() < 1 && timer > millis());
lowByte = Wire.read();
address = gyro_address;
return lowByte;
}

void start_gyro(){
//Setup the L3G4200D or L3GD20H
if(type == 2 || type == 3){
Wire.beginTransmission(address); //Start communication with the gyro with the
address found during search
Wire.write(0x20); //We want to write to register 1 (20 hex)
Wire.write(0x0F); //Set the register bits as 00001111 (Turn on the gyro
and enable all axis)
Wire.endTransmission(); //End the transmission with the gyro

Wire.beginTransmission(address); //Start communication with the gyro (adress


1101001)
Wire.write(0x20); //Start reading @ register 28h and auto increment with
every read
Wire.endTransmission(); //End the transmission
Wire.requestFrom(address, 1); //Request 6 bytes from the gyro
while(Wire.available() < 1); //Wait until the 1 byte is received
Serial.print(F("Register 0x20 is set to:"));
Serial.println(Wire.read(),BIN);

Wire.beginTransmission(address); //Start communication with the gyro with the


address found during search
Wire.write(0x23); //We want to write to register 4 (23 hex)
Wire.write(0x90); //Set the register bits as 10010000 (Block Data Update
active & 500dps full scale)
Wire.endTransmission(); //End the transmission with the gyro

Wire.beginTransmission(address); //Start communication with the gyro (adress


1101001)
Wire.write(0x23); //Start reading @ register 28h and auto increment with
every read
Wire.endTransmission(); //End the transmission
Wire.requestFrom(address, 1); //Request 6 bytes from the gyro
while(Wire.available() < 1); //Wait until the 1 byte is received
Serial.print(F("Register 0x23 is set to:"));
Serial.println(Wire.read(),BIN);

}
//Setup the MPU-6050
if(type == 1){

Wire.beginTransmission(address); //Start communication with the gyro


Wire.write(0x6B); //PWR_MGMT_1 register
Wire.write(0x00); //Set to zero to turn on the gyro
Wire.endTransmission(); //End the transmission

Wire.beginTransmission(address); //Start communication with the gyro


Wire.write(0x6B); //Start reading @ register 28h and auto increment with
every read
Wire.endTransmission(); //End the transmission
Wire.requestFrom(address, 1); //Request 1 bytes from the gyro
while(Wire.available() < 1); //Wait until the 1 byte is received
Serial.print(F("Register 0x6B is set to:"));
Serial.println(Wire.read(),BIN);
Wire.beginTransmission(address); //Start communication with the gyro
Wire.write(0x1B); //GYRO_CONFIG register
Wire.write(0x08); //Set the register bits as 00001000 (500dps full scale)
Wire.endTransmission(); //End the transmission

Wire.beginTransmission(address); //Start communication with the gyro (adress


1101001)
Wire.write(0x1B); //Start reading @ register 28h and auto increment with
every read
Wire.endTransmission(); //End the transmission
Wire.requestFrom(address, 1); //Request 1 bytes from the gyro
while(Wire.available() < 1); //Wait until the 1 byte is received
Serial.print(F("Register 0x1B is set to:"));
Serial.println(Wire.read(),BIN);

}
}

void gyro_signalen(){
if(type == 2 || type == 3){
Wire.beginTransmission(address); //Start communication with the gyro
Wire.write(168); //Start reading @ register 28h and auto increment with
every read
Wire.endTransmission(); //End the transmission
Wire.requestFrom(address, 6); //Request 6 bytes from the gyro
while(Wire.available() < 6); //Wait until the 6 bytes are received
lowByte = Wire.read(); //First received byte is the low part of the angular
data
highByte = Wire.read(); //Second received byte is the high part of the angular
data
gyro_roll = ((highByte<<8)|lowByte); //Multiply highByte by 256 (shift left by 8)
and ad lowByte
if(cal_int == 2000)gyro_roll -= gyro_roll_cal; //Only compensate after the calibration
lowByte = Wire.read(); //First received byte is the low part of the angular
data
highByte = Wire.read(); //Second received byte is the high part of the angular
data
gyro_pitch = ((highByte<<8)|lowByte); //Multiply highByte by 256 (shift left by 8)
and ad lowByte
if(cal_int == 2000)gyro_pitch -= gyro_pitch_cal; //Only compensate after the calibration
lowByte = Wire.read(); //First received byte is the low part of the angular
data
highByte = Wire.read(); //Second received byte is the high part of the angular
data
gyro_yaw = ((highByte<<8)|lowByte); //Multiply highByte by 256 (shift left by 8)
and ad lowByte
if(cal_int == 2000)gyro_yaw -= gyro_yaw_cal; //Only compensate after the calibration
}
if(type == 1){
Wire.beginTransmission(address); //Start communication with the gyro
Wire.write(0x43); //Start reading @ register 43h and auto increment with
every read
Wire.endTransmission(); //End the transmission
Wire.requestFrom(address,6); //Request 6 bytes from the gyro
while(Wire.available() < 6); //Wait until the 6 bytes are received
gyro_roll=Wire.read()<<8|Wire.read(); //Read high and low part of the angular data
if(cal_int == 2000)gyro_roll -= gyro_roll_cal; //Only compensate after the calibration
gyro_pitch=Wire.read()<<8|Wire.read(); //Read high and low part of the angular data
if(cal_int == 2000)gyro_pitch -= gyro_pitch_cal; //Only compensate after the calibration
gyro_yaw=Wire.read()<<8|Wire.read(); //Read high and low part of the angular data
if(cal_int == 2000)gyro_yaw -= gyro_yaw_cal; //Only compensate after the calibration
}
}

//Check if a receiver input value is changing within 30 seconds


void check_receiver_inputs(byte movement){
byte trigger = 0;
int pulse_length;
timer = millis() + 30000;
while(timer > millis() && trigger == 0){
delay(250);
if(receiver_input_channel_1 > 1750 || receiver_input_channel_1 < 1250){
trigger = 1;
receiver_check_byte |= 0b00000001;
pulse_length = receiver_input_channel_1;
}
if(receiver_input_channel_2 > 1750 || receiver_input_channel_2 < 1250){
trigger = 2;
receiver_check_byte |= 0b00000010;
pulse_length = receiver_input_channel_2;
}
if(receiver_input_channel_3 > 1750 || receiver_input_channel_3 < 1250){
trigger = 3;
receiver_check_byte |= 0b00000100;
pulse_length = receiver_input_channel_3;
}
if(receiver_input_channel_4 > 1750 || receiver_input_channel_4 < 1250){
trigger = 4;
receiver_check_byte |= 0b00001000;
pulse_length = receiver_input_channel_4;
}
}
if(trigger == 0){
error = 1;
Serial.println(F("No stick movement detected in the last 30 seconds!!! (ERROR 2)"));
}
//Assign the stick to the function.
else{
if(movement == 1){
channel_3_assign = trigger;
if(pulse_length < 1250)channel_3_assign += 0b10000000;
}
if(movement == 2){
channel_1_assign = trigger;
if(pulse_length < 1250)channel_1_assign += 0b10000000;
}
if(movement == 3){
channel_2_assign = trigger;
if(pulse_length < 1250)channel_2_assign += 0b10000000;
}
if(movement == 4){
channel_4_assign = trigger;
if(pulse_length < 1250)channel_4_assign += 0b10000000;
}
}
}

void check_to_continue(){
byte continue_byte = 0;
while(continue_byte == 0){
if(channel_2_assign == 0b00000001 && receiver_input_channel_1 > center_channel_1 +
150)continue_byte = 1;
if(channel_2_assign == 0b10000001 && receiver_input_channel_1 < center_channel_1 -
150)continue_byte = 1;
if(channel_2_assign == 0b00000010 && receiver_input_channel_2 > center_channel_2 +
150)continue_byte = 1;
if(channel_2_assign == 0b10000010 && receiver_input_channel_2 < center_channel_2 -
150)continue_byte = 1;
if(channel_2_assign == 0b00000011 && receiver_input_channel_3 > center_channel_3 +
150)continue_byte = 1;
if(channel_2_assign == 0b10000011 && receiver_input_channel_3 < center_channel_3 -
150)continue_byte = 1;
if(channel_2_assign == 0b00000100 && receiver_input_channel_4 > center_channel_4 +
150)continue_byte = 1;
if(channel_2_assign == 0b10000100 && receiver_input_channel_4 < center_channel_4 -
150)continue_byte = 1;
delay(100);
}
wait_sticks_zero();
}

//Check if the transmitter sticks are in the neutral position


void wait_sticks_zero(){
byte zero = 0;
while(zero < 15){
if(receiver_input_channel_1 < center_channel_1 + 20 && receiver_input_channel_1 >
center_channel_1 - 20)zero |= 0b00000001;
if(receiver_input_channel_2 < center_channel_2 + 20 && receiver_input_channel_2 >
center_channel_2 - 20)zero |= 0b00000010;
if(receiver_input_channel_3 < center_channel_3 + 20 && receiver_input_channel_3 >
center_channel_3 - 20)zero |= 0b00000100;
if(receiver_input_channel_4 < center_channel_4 + 20 && receiver_input_channel_4 >
center_channel_4 - 20)zero |= 0b00001000;
delay(100);
}
}

//Checck if the receiver values are valid within 10 seconds


void wait_for_receiver(){
byte zero = 0;
timer = millis() + 10000;
while(timer > millis() && zero < 15){
if(receiver_input_channel_1 < 2100 && receiver_input_channel_1 > 900)zero |= 0b00000001;
if(receiver_input_channel_2 < 2100 && receiver_input_channel_2 > 900)zero |= 0b00000010;
if(receiver_input_channel_3 < 2100 && receiver_input_channel_3 > 900)zero |= 0b00000100;
if(receiver_input_channel_4 < 2100 && receiver_input_channel_4 > 900)zero |= 0b00001000;
delay(500);
Serial.print(F("."));
}
if(zero == 0){
error = 1;
Serial.println(F("."));
Serial.println(F("No valid receiver signals found!!! (ERROR 1)"));
}
else Serial.println(F(" OK"));
}

//Register the min and max receiver values and exit when the sticks are back in the neutral position
void register_min_max(){
byte zero = 0;
low_channel_1 = receiver_input_channel_1;
low_channel_2 = receiver_input_channel_2;
low_channel_3 = receiver_input_channel_3;
low_channel_4 = receiver_input_channel_4;
while(receiver_input_channel_1 < center_channel_1 + 20 && receiver_input_channel_1 >
center_channel_1 - 20)delay(250);
Serial.println(F("Measuring endpoints...."));
while(zero < 15){
if(receiver_input_channel_1 < center_channel_1 + 20 && receiver_input_channel_1 >
center_channel_1 - 20)zero |= 0b00000001;
if(receiver_input_channel_2 < center_channel_2 + 20 && receiver_input_channel_2 >
center_channel_2 - 20)zero |= 0b00000010;
if(receiver_input_channel_3 < center_channel_3 + 20 && receiver_input_channel_3 >
center_channel_3 - 20)zero |= 0b00000100;
if(receiver_input_channel_4 < center_channel_4 + 20 && receiver_input_channel_4 >
center_channel_4 - 20)zero |= 0b00001000;
if(receiver_input_channel_1 < low_channel_1)low_channel_1 = receiver_input_channel_1;
if(receiver_input_channel_2 < low_channel_2)low_channel_2 = receiver_input_channel_2;
if(receiver_input_channel_3 < low_channel_3)low_channel_3 = receiver_input_channel_3;
if(receiver_input_channel_4 < low_channel_4)low_channel_4 = receiver_input_channel_4;
if(receiver_input_channel_1 > high_channel_1)high_channel_1 = receiver_input_channel_1;
if(receiver_input_channel_2 > high_channel_2)high_channel_2 = receiver_input_channel_2;
if(receiver_input_channel_3 > high_channel_3)high_channel_3 = receiver_input_channel_3;
if(receiver_input_channel_4 > high_channel_4)high_channel_4 = receiver_input_channel_4;
delay(100);
}
}

//Check if the angular position of a gyro axis is changing within 10 seconds


void check_gyro_axes(byte movement){
byte trigger_axis = 0;
float gyro_angle_roll, gyro_angle_pitch, gyro_angle_yaw;
//Reset all axes
gyro_angle_roll = 0;
gyro_angle_pitch = 0;
gyro_angle_yaw = 0;
gyro_signalen();
timer = millis() + 10000;
while(timer > millis() && gyro_angle_roll > -30 && gyro_angle_roll < 30 && gyro_angle_pitch >
-30 && gyro_angle_pitch < 30 && gyro_angle_yaw > -30 && gyro_angle_yaw < 30){
gyro_signalen();
if(type == 2 || type == 3){
gyro_angle_roll += gyro_roll * 0.00007; //0.00007 = 17.5 (md/s) / 250(Hz)
gyro_angle_pitch += gyro_pitch * 0.00007;
gyro_angle_yaw += gyro_yaw * 0.00007;
}
if(type == 1){
gyro_angle_roll += gyro_roll * 0.0000611; // 0.0000611 = 1 / 65.5 (LSB degr/s) / 250(Hz)
gyro_angle_pitch += gyro_pitch * 0.0000611;
gyro_angle_yaw += gyro_yaw * 0.0000611;
}

delayMicroseconds(3700); //Loop is running for communication with the gyro


}
//Assign the moved axis to the orresponding function (pitch, roll, yaw)
if((gyro_angle_roll < -30 || gyro_angle_roll > 30) && gyro_angle_pitch > -30 && gyro_angle_pitch
< 30 && gyro_angle_yaw > -30 && gyro_angle_yaw < 30){
gyro_check_byte |= 0b00000001;
if(gyro_angle_roll < 0)trigger_axis = 0b10000001;
else trigger_axis = 0b00000001;
}
if((gyro_angle_pitch < -30 || gyro_angle_pitch > 30) && gyro_angle_roll > -30 && gyro_angle_roll
< 30 && gyro_angle_yaw > -30 && gyro_angle_yaw < 30){
gyro_check_byte |= 0b00000010;
if(gyro_angle_pitch < 0)trigger_axis = 0b10000010;
else trigger_axis = 0b00000010;
}
if((gyro_angle_yaw < -30 || gyro_angle_yaw > 30) && gyro_angle_roll > -30 && gyro_angle_roll
< 30 && gyro_angle_pitch > -30 && gyro_angle_pitch < 30){
gyro_check_byte |= 0b00000100;
if(gyro_angle_yaw < 0)trigger_axis = 0b10000011;
else trigger_axis = 0b00000011;
}

if(trigger_axis == 0){
error = 1;
Serial.println(F("No angular motion is detected in the last 10 seconds!!! (ERROR 4)"));
}
else
if(movement == 1)roll_axis = trigger_axis;
if(movement == 2)pitch_axis = trigger_axis;
if(movement == 3)yaw_axis = trigger_axis;

//This routine is called every time input 8, 9, 10 or 11 changed state


ISR(PCINT0_vect){
current_time = micros();
//Channel 1=========================================
if(PINB & B00000001){ //Is input 8 high?
if(last_channel_1 == 0){ //Input 8 changed from 0 to 1
last_channel_1 = 1; //Remember current input state
timer_1 = current_time; //Set timer_1 to current_time
}
}
else if(last_channel_1 == 1){ //Input 8 is not high and changed from 1 to 0
last_channel_1 = 0; //Remember current input state
receiver_input_channel_1 = current_time - timer_1; //Channel 1 is current_time - timer_1
}
//Channel 2=========================================
if(PINB & B00000010 ){ //Is input 9 high?
if(last_channel_2 == 0){ //Input 9 changed from 0 to 1
last_channel_2 = 1; //Remember current input state
timer_2 = current_time; //Set timer_2 to current_time
}
}
else if(last_channel_2 == 1){ //Input 9 is not high and changed from 1 to 0
last_channel_2 = 0; //Remember current input state
receiver_input_channel_2 = current_time - timer_2; //Channel 2 is current_time - timer_2
}
//Channel 3=========================================
if(PINB & B00000100 ){ //Is input 10 high?
if(last_channel_3 == 0){ //Input 10 changed from 0 to 1
last_channel_3 = 1; //Remember current input state
timer_3 = current_time; //Set timer_3 to current_time
}
}
else if(last_channel_3 == 1){ //Input 10 is not high and changed from 1 to 0
last_channel_3 = 0; //Remember current input state
receiver_input_channel_3 = current_time - timer_3; //Channel 3 is current_time - timer_3

}
//Channel 4=========================================
if(PINB & B00001000 ){ //Is input 11 high?
if(last_channel_4 == 0){ //Input 11 changed from 0 to 1
last_channel_4 = 1; //Remember current input state
timer_4 = current_time; //Set timer_4 to current_time
}
}
else if(last_channel_4 == 1){ //Input 11 is not high and changed from 1 to 0
last_channel_4 = 0; //Remember current input state
receiver_input_channel_4 = current_time - timer_4; //Channel 4 is current_time - timer_4
}
}
ESC_Calibration.cpp:-
#include <Wire.h> //Include the Wire.h library so we can communicate with the gyro.
#include <EEPROM.h> //Include the EEPROM.h library so we can store information onto the
EEPROM
//Declaring global variables
byte last_channel_1, last_channel_2, last_channel_3, last_channel_4;
byte eeprom_data[36], start, data;
boolean new_function_request,first_angle;
volatile int receiver_input_channel_1, receiver_input_channel_2, receiver_input_channel_3,
receiver_input_channel_4;
int esc_1, esc_2, esc_3, esc_4;
int counter_channel_1, counter_channel_2, counter_channel_3, counter_channel_4;
int receiver_input[5];
int loop_counter, gyro_address, vibration_counter;
int temperature;
long acc_x, acc_y, acc_z, acc_total_vector[20], acc_av_vector, vibration_total_result;
unsigned long timer_channel_1, timer_channel_2, timer_channel_3, timer_channel_4, esc_timer,
esc_loop_timer;
unsigned long zero_timer, timer_1, timer_2, timer_3, timer_4, current_time;

int acc_axis[4], gyro_axis[4];


double gyro_pitch, gyro_roll, gyro_yaw;
float angle_roll_acc, angle_pitch_acc, angle_pitch, angle_roll;
int cal_int;
double gyro_axis_cal[4];

//Setup routine
void setup(){
Serial.begin(57600); //Start the serial port.
Wire.begin(); //Start the wire library as master
TWBR = 12; //Set the I2C clock speed to 400kHz.

//Arduino Uno pins default to inputs, so they don't need to be explicitly declared as inputs.
DDRD |= B11110000; //Configure digital poort 4, 5, 6 and 7
as output.
DDRB |= B00010000; //Configure digital poort 12 as output.

PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan.


PCMSK0 |= (1 << PCINT0); // set PCINT0 (digital input 8) to
trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT1); // set PCINT1 (digital input 9)to
trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT2); // set PCINT2 (digital input 10)to
trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT3); // set PCINT3 (digital input 11)to
trigger an interrupt on state change.

for(data = 0; data <= 35; data++)eeprom_data[data] = EEPROM.read(data); //Read EEPROM


for faster data access

gyro_address = eeprom_data[32]; //Store the gyro address in the


variable.

set_gyro_registers(); //Set the specific gyro registers.


//Check the EEPROM signature to make sure that the setup program is executed.
while(eeprom_data[33] != 'J' || eeprom_data[34] != 'M' || eeprom_data[35] != 'B'){
delay(500); //Wait for 500ms.
digitalWrite(12, !digitalRead(12)); //Change the led status to indicate
error.
}
wait_for_receiver(); //Wait until the receiver is active.
zero_timer = micros(); //Set the zero_timer for the first loop.

while(Serial.available())data = Serial.read(); //Empty the serial buffer.


data = 0; //Set the data variable back to zero.
}

//Main program loop


void loop(){
while(zero_timer + 4000 > micros()); //Start the pulse after 4000 micro
seconds.
zero_timer = micros(); //Reset the zero timer.

if(Serial.available() > 0){


data = Serial.read(); //Read the incomming byte.
delay(100); //Wait for any other bytes to come in
while(Serial.available() > 0)loop_counter = Serial.read(); //Empty the Serial buffer.
new_function_request = true; //Set the new request flag.
loop_counter = 0; //Reset the loop_counter variable.
cal_int = 0; //Reset the cal_int variable to undo the
calibration.
start = 0; //Set start to 0.
first_angle = false; //Set first_angle to false.
//Confirm the choice on the serial monitor.
if(data == 'r')Serial.println("Reading receiver signals.");
if(data == 'a')Serial.println("Print the quadcopter angles.");
if(data == 'a')Serial.println("Gyro calibration starts in 2 seconds (don't move the quadcopter).");
if(data == '1')Serial.println("Test motor 1 (right front CCW.)");
if(data == '2')Serial.println("Test motor 2 (right rear CW.)");
if(data == '3')Serial.println("Test motor 3 (left rear CCW.)");
if(data == '4')Serial.println("Test motor 4 (left front CW.)");
if(data == '5')Serial.println("Test all motors together");

//Let's create a small delay so the message stays visible for 2.5 seconds.
//We don't want the ESC's to beep and have to send a 1000us pulse to the ESC's.
for(vibration_counter = 0; vibration_counter < 625; vibration_counter++){ //Do this loop 625
times
delay(3); //Wait 3000us.
esc_1 = 1000; //Set the pulse for ESC 1 to 1000us.
esc_2 = 1000; //Set the pulse for ESC 1 to 1000us.
esc_3 = 1000; //Set the pulse for ESC 1 to 1000us.
esc_4 = 1000; //Set the pulse for ESC 1 to 1000us.
esc_pulse_output(); //Send the ESC control pulses.
}
vibration_counter = 0; //Reset the vibration_counter variable.
}

receiver_input_channel_3 = convert_receiver_channel(3); //Convert the actual


receiver signals for throttle to the standard 1000 - 2000us.
if(receiver_input_channel_3 < 1025)new_function_request = false; //If the throttle is in
the lowest position set the request flag to false.

////////////////////////////////////////////////////////////////////////////////////////////
//Run the ESC calibration program to start with.
////////////////////////////////////////////////////////////////////////////////////////////
if(data == 0 && new_function_request == false){ //Only start the calibration
mode at first start.
receiver_input_channel_3 = convert_receiver_channel(3); //Convert the actual receiver
signals for throttle to the standard 1000 - 2000us.
esc_1 = receiver_input_channel_3; //Set the pulse for motor 1 equal to
the throttle channel.
esc_2 = receiver_input_channel_3; //Set the pulse for motor 2 equal to
the throttle channel.
esc_3 = receiver_input_channel_3; //Set the pulse for motor 3 equal to
the throttle channel.
esc_4 = receiver_input_channel_3; //Set the pulse for motor 4 equal to
the throttle channel.
esc_pulse_output(); //Send the ESC control pulses.
}

////////////////////////////////////////////////////////////////////////////////////////////
// send 'r' print the receiver signals.
////////////////////////////////////////////////////////////////////////////////////////////
if(data == 'r'){
loop_counter ++; //Increase the loop_counter variable.
receiver_input_channel_1 = convert_receiver_channel(1); //Convert the actual receiver
signals for pitch to the standard 1000 - 2000us.
receiver_input_channel_2 = convert_receiver_channel(2); //Convert the actual receiver
signals for roll to the standard 1000 - 2000us.
receiver_input_channel_3 = convert_receiver_channel(3); //Convert the actual receiver
signals for throttle to the standard 1000 - 2000us.
receiver_input_channel_4 = convert_receiver_channel(4); //Convert the actual receiver
signals for yaw to the standard 1000 - 2000us.

if(loop_counter == 125){ //Print the receiver values when the


loop_counter variable equals 250.
print_signals(); //Print the receiver values on the serial monitor.
loop_counter = 0; //Reset the loop_counter variable.
}

//For starting the motors: throttle low and yaw left (step 1).
if(receiver_input_channel_3 < 1050 && receiver_input_channel_4 < 1050)start = 1;
//When yaw stick is back in the center position start the motors (step 2).
if(start == 1 && receiver_input_channel_3 < 1050 && receiver_input_channel_4 > 1450)start = 2;
//Stopping the motors: throttle low and yaw right.
if(start == 2 && receiver_input_channel_3 < 1050 && receiver_input_channel_4 > 1950)start = 0;

esc_1 = 1000; //Set the pulse for ESC 1 to 1000us.


esc_2 = 1000; //Set the pulse for ESC 1 to 1000us.
esc_3 = 1000; //Set the pulse for ESC 1 to 1000us.
esc_4 = 1000; //Set the pulse for ESC 1 to 1000us.
esc_pulse_output(); //Send the ESC control pulses.
}
///////////////////////////////////////////////////////////////////////////////////////////
//When user sends a '1, 2, 3, 4 or 5 test the motors.
////////////////////////////////////////////////////////////////////////////////////////////
if(data == '1' || data == '2' || data == '3' || data == '4' || data == '5'){ //If motor 1, 2, 3 or 4 is selected
by the user.
loop_counter ++; //Add 1 to the loop_counter variable.
if(new_function_request == true && loop_counter == 250){ //Wait for the throttle to
be set to 0.
Serial.print("Set throttle to 1000 (low). It's now set to: "); //Print message on the serial
monitor.
Serial.println(receiver_input_channel_3); //Print the actual throttle position.
loop_counter = 0; //Reset the loop_counter variable.
}
if(new_function_request == false){ //When the throttle was in the lowest
position do this.
receiver_input_channel_3 = convert_receiver_channel(3); //Convert the actual receiver
signals for throttle to the standard 1000 - 2000us.
if(data == '1' || data == '5')esc_1 = receiver_input_channel_3; //If motor 1 is requested set
the pulse for motor 1 equal to the throttle channel.
else esc_1 = 1000; //If motor 1 is not requested set the pulse for
the ESC to 1000us (off).
if(data == '2' || data == '5')esc_2 = receiver_input_channel_3; //If motor 2 is requested set
the pulse for motor 1 equal to the throttle channel.
else esc_2 = 1000; //If motor 2 is not requested set the pulse for
the ESC to 1000us (off).
if(data == '3' || data == '5')esc_3 = receiver_input_channel_3; //If motor 3 is requested set
the pulse for motor 1 equal to the throttle channel.
else esc_3 = 1000; //If motor 3 is not requested set the pulse for
the ESC to 1000us (off).
if(data == '4' || data == '5')esc_4 = receiver_input_channel_3; //If motor 4 is requested set
the pulse for motor 1 equal to the throttle channel.
else esc_4 = 1000; //If motor 4 is not requested set the pulse for
the ESC to 1000us (off).

esc_pulse_output(); //Send the ESC control pulses.

//For balancing the propellors it's possible to use the accelerometer to measure the vibrations.
if(eeprom_data[31] == 1){ //The MPU-6050 is installed
Wire.beginTransmission(gyro_address); //Start communication with the gyro.
Wire.write(0x3B); //Start reading @ register 43h and auto
increment with every read.
Wire.endTransmission(); //End the transmission.
Wire.requestFrom(gyro_address,6); //Request 6 bytes from the gyro.
while(Wire.available() < 6); //Wait until the 6 bytes are received.
acc_x = Wire.read()<<8|Wire.read(); //Add the low and high byte to the
acc_x variable.
acc_y = Wire.read()<<8|Wire.read(); //Add the low and high byte to the
acc_y variable.
acc_z = Wire.read()<<8|Wire.read(); //Add the low and high byte to the
acc_z variable.

acc_total_vector[0] = sqrt((acc_x*acc_x)+(acc_y*acc_y)+(acc_z*acc_z)); //Calculate the total


accelerometer vector.

acc_av_vector = acc_total_vector[0]; //Copy the total vector to the


accelerometer average vector variable.

for(start = 16; start > 0; start--){ //Do this loop 16 times to create an array of
accelrometer vectors.
acc_total_vector[start] = acc_total_vector[start - 1]; //Shift every variable one position
up in the array.
acc_av_vector += acc_total_vector[start]; //Add the array value to the acc_av_vector
variable.
}

acc_av_vector /= 17; //Divide the acc_av_vector by 17 to get the


avarage total accelerometer vector.

if(vibration_counter < 20){ //If the vibration_counter is less than 20 do


this.
vibration_counter ++; //Increment the vibration_counter variable.
vibration_total_result += abs(acc_total_vector[0] - acc_av_vector); //Add the absolute difference
between the avarage vector and current vector to the vibration_total_result variable.
}
else{
vibration_counter = 0; //If the vibration_counter is equal or larger
than 20 do this.
Serial.println(vibration_total_result/50); //Print the total accelerometer vector
divided by 50 on the serial monitor.
vibration_total_result = 0; //Reset the vibration_total_result variable.
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////
//When user sends a 'a' display the quadcopter angles.
////////////////////////////////////////////////////////////////////////////////////////////
if(data == 'a'){

if(cal_int != 2000){
Serial.print("Calibrating the gyro");
//Let's take multiple gyro data samples so we can determine the average gyro offset (calibration).
for (cal_int = 0; cal_int < 2000 ; cal_int ++){ //Take 2000 readings for calibration.
if(cal_int % 125 == 0){
digitalWrite(12, !digitalRead(12)); //Change the led status to indicate calibration.
Serial.print(".");
}
gyro_signalen(); //Read the gyro output.
gyro_axis_cal[1] += gyro_axis[1]; //Ad roll value to gyro_roll_cal.
gyro_axis_cal[2] += gyro_axis[2]; //Ad pitch value to gyro_pitch_cal.
gyro_axis_cal[3] += gyro_axis[3]; //Ad yaw value to gyro_yaw_cal.
//We don't want the esc's to be beeping annoyingly. So let's give them a 1000us puls while calibrating
the gyro.
PORTD |= B11110000; //Set digital poort 4, 5, 6 and 7 high.
delayMicroseconds(1000); //Wait 1000us.
PORTD &= B00001111; //Set digital poort 4, 5, 6 and 7 low.
delay(3); //Wait 3 milliseconds before the next loop.
}
Serial.println(".");
//Now that we have 2000 measures, we need to devide by 2000 to get the average gyro offset.
gyro_axis_cal[1] /= 2000; //Divide the roll total by 2000.
gyro_axis_cal[2] /= 2000; //Divide the pitch total by 2000.
gyro_axis_cal[3] /= 2000; //Divide the yaw total by 2000.
}
else{
///We don't want the esc's to be beeping annoyingly. So let's give them a 1000us puls while calibrating
the gyro.
PORTD |= B11110000; //Set digital poort 4, 5, 6 and 7 high.
delayMicroseconds(1000); //Wait 1000us.
PORTD &= B00001111; //Set digital poort 4, 5, 6 and 7 low.

//Let's get the current gyro data.


gyro_signalen();

//Gyro angle calculations


//0.0000611 = 1 / (250Hz / 65.5)
angle_pitch += gyro_pitch * 0.0000611; //Calculate the traveled pitch angle
and add this to the angle_pitch variable.
angle_roll += gyro_roll * 0.0000611; //Calculate the traveled roll angle and
add this to the angle_roll variable.

//0.000001066 = 0.0000611 * (3.142(PI) / 180degr) The Arduino sin function is in radians


angle_pitch -= angle_roll * sin(gyro_yaw * 0.000001066); //If the IMU has yawed
transfer the roll angle to the pitch angel.
angle_roll += angle_pitch * sin(gyro_yaw * 0.000001066); //If the IMU has yawed
transfer the pitch angle to the roll angel.

//Accelerometer angle calculations


acc_total_vector[0] = sqrt((acc_x*acc_x)+(acc_y*acc_y)+(acc_z*acc_z)); //Calculate the total
accelerometer vector.

//57.296 = 1 / (3.142 / 180) The Arduino asin function is in radians


angle_pitch_acc = asin((float)acc_y/acc_total_vector[0])* 57.296; //Calculate the pitch angle.
angle_roll_acc = asin((float)acc_x/acc_total_vector[0])* -57.296; //Calculate the roll angle.

if(!first_angle){
angle_pitch = angle_pitch_acc; //Set the pitch angle to the accelerometer
angle.
angle_roll = angle_roll_acc; //Set the roll angle to the accelerometer
angle.
first_angle = true;
}
else{
angle_pitch = angle_pitch * 0.9996 + angle_pitch_acc * 0.0004; //Correct the drift of the gyro
pitch angle with the accelerometer pitch angle.
angle_roll = angle_roll * 0.9996 + angle_roll_acc * 0.0004; //Correct the drift of the gyro
roll angle with the accelerometer roll angle.
}

//We can't print all the data at once. This takes to long and the angular readings will be off.
if(loop_counter == 0)Serial.print("Pitch: ");
if(loop_counter == 1)Serial.print(angle_pitch ,0);
if(loop_counter == 2)Serial.print(" Roll: ");
if(loop_counter == 3)Serial.print(angle_roll ,0);
if(loop_counter == 4)Serial.print(" Yaw: ");
if(loop_counter == 5)Serial.println(gyro_yaw / 65.5 ,0);

loop_counter ++;
if(loop_counter == 60)loop_counter = 0;
}
}
}

//This routine is called every time input 8, 9, 10 or 11 changed state.


ISR(PCINT0_vect){
current_time = micros();
//Channel 1=========================================
if(PINB & B00000001){ //Is input 8 high?
if(last_channel_1 == 0){ //Input 8 changed from 0 to 1.
last_channel_1 = 1; //Remember current input state.
timer_1 = current_time; //Set timer_1 to current_time.
}
}
else if(last_channel_1 == 1){ //Input 8 is not high and changed from 1 to 0.
last_channel_1 = 0; //Remember current input state.
receiver_input[1] = current_time - timer_1; //Channel 1 is current_time - timer_1.
}
//Channel 2=========================================
if(PINB & B00000010 ){ //Is input 9 high?
if(last_channel_2 == 0){ //Input 9 changed from 0 to 1.
last_channel_2 = 1; //Remember current input state.
timer_2 = current_time; //Set timer_2 to current_time.
}
}
else if(last_channel_2 == 1){ //Input 9 is not high and changed from 1 to 0.
last_channel_2 = 0; //Remember current input state.
receiver_input[2] = current_time - timer_2; //Channel 2 is current_time - timer_2.
}
//Channel 3=========================================
if(PINB & B00000100 ){ //Is input 10 high?
if(last_channel_3 == 0){ //Input 10 changed from 0 to 1.
last_channel_3 = 1; //Remember current input state.
timer_3 = current_time; //Set timer_3 to current_time.
}
}
else if(last_channel_3 == 1){ //Input 10 is not high and changed from 1 to 0.
last_channel_3 = 0; //Remember current input state.
receiver_input[3] = current_time - timer_3; //Channel 3 is current_time - timer_3.
}
//Channel 4=========================================
if(PINB & B00001000 ){ //Is input 11 high?
if(last_channel_4 == 0){ //Input 11 changed from 0 to 1.
last_channel_4 = 1; //Remember current input state.
timer_4 = current_time; //Set timer_4 to current_time.
}
}
else if(last_channel_4 == 1){ //Input 11 is not high and changed from 1 to 0.
last_channel_4 = 0; //Remember current input state.
receiver_input[4] = current_time - timer_4; //Channel 4 is current_time - timer_4.
}
}

//Checck if the receiver values are valid within 10 seconds


void wait_for_receiver(){
byte zero = 0; //Set all bits in the variable zero to 0
while(zero < 15){ //Stay in this loop until the 4 lowest bits are set
if(receiver_input[1] < 2100 && receiver_input[1] > 900)zero |= 0b00000001; //Set bit 0 if the receiver
pulse 1 is within the 900 - 2100 range
if(receiver_input[2] < 2100 && receiver_input[2] > 900)zero |= 0b00000010; //Set bit 1 if the receiver
pulse 2 is within the 900 - 2100 range
if(receiver_input[3] < 2100 && receiver_input[3] > 900)zero |= 0b00000100; //Set bit 2 if the receiver
pulse 3 is within the 900 - 2100 range
if(receiver_input[4] < 2100 && receiver_input[4] > 900)zero |= 0b00001000; //Set bit 3 if the receiver
pulse 4 is within the 900 - 2100 range
delay(500); //Wait 500 milliseconds
}
}

//This part converts the actual receiver signals to a standardized 1000 – 1500 – 2000 microsecond value.
//The stored data in the EEPROM is used.
int convert_receiver_channel(byte function){
byte channel, reverse; //First we declare some local variables
int low, center, high, actual;
int difference;

channel = eeprom_data[function + 23] & 0b00000111; //What channel corresponds


with the specific function
if(eeprom_data[function + 23] & 0b10000000)reverse = 1; //Reverse channel when most
significant bit is set
else reverse = 0; //If the most significant is not set there is no
reverse

actual = receiver_input[channel]; //Read the actual receiver value for the


corresponding function
low = (eeprom_data[channel * 2 + 15] << 8) | eeprom_data[channel * 2 + 14]; //Store the low value
for the specific receiver input channel
center = (eeprom_data[channel * 2 - 1] << 8) | eeprom_data[channel * 2 - 2]; //Store the center value
for the specific receiver input channel
high = (eeprom_data[channel * 2 + 7] << 8) | eeprom_data[channel * 2 + 6]; //Store the high value for
the specific receiver input channel

if(actual < center){ //The actual receiver value is lower than the
center value
if(actual < low)actual = low; //Limit the lowest value to the value that was
detected during setup
difference = ((long)(center - actual) * (long)500) / (center - low); //Calculate and scale the actual
value to a 1000 - 2000us value
if(reverse == 1)return 1500 + difference; //If the channel is reversed
else return 1500 - difference; //If the channel is not reversed
}
else if(actual > center){ //The actual receiver value is higher
than the center value
if(actual > high)actual = high; //Limit the lowest value to the value that was
detected during setup
difference = ((long)(actual - center) * (long)500) / (high - center); //Calculate and scale the actual
value to a 1000 - 2000us value
if(reverse == 1)return 1500 - difference; //If the channel is reversed
else return 1500 + difference; //If the channel is not reversed
}
else return 1500;
}

void print_signals(){
Serial.print("Start:");
Serial.print(start);

Serial.print(" Roll:");
if(receiver_input_channel_1 - 1480 < 0)Serial.print("<<<");
else if(receiver_input_channel_1 - 1520 > 0)Serial.print(">>>");
else Serial.print("-+-");
Serial.print(receiver_input_channel_1);

Serial.print(" Pitch:");
if(receiver_input_channel_2 - 1480 < 0)Serial.print("^^^");
else if(receiver_input_channel_2 - 1520 > 0)Serial.print("vvv");
else Serial.print("-+-");
Serial.print(receiver_input_channel_2);

Serial.print(" Throttle:");
if(receiver_input_channel_3 - 1480 < 0)Serial.print("vvv");
else if(receiver_input_channel_3 - 1520 > 0)Serial.print("^^^");
else Serial.print("-+-");
Serial.print(receiver_input_channel_3);

Serial.print(" Yaw:");
if(receiver_input_channel_4 - 1480 < 0)Serial.print("<<<");
else if(receiver_input_channel_4 - 1520 > 0)Serial.print(">>>");
else Serial.print("-+-");
Serial.println(receiver_input_channel_4);
}

void esc_pulse_output(){
zero_timer = micros();
PORTD |= B11110000; //Set port 4, 5, 6 and 7 high at once
timer_channel_1 = esc_1 + zero_timer; //Calculate the time when digital port 4 is set
low.
timer_channel_2 = esc_2 + zero_timer; //Calculate the time when digital port 5 is set
low.
timer_channel_3 = esc_3 + zero_timer; //Calculate the time when digital port 6 is set
low.
timer_channel_4 = esc_4 + zero_timer; //Calculate the time when digital port 7 is set
low.

while(PORTD >= 16){ //Execute the loop until digital port 4 to 7 is low.
esc_loop_timer = micros(); //Check the current time.
if(timer_channel_1 <= esc_loop_timer)PORTD &= B11101111; //When the delay time is expired,
digital port 4 is set low.
if(timer_channel_2 <= esc_loop_timer)PORTD &= B11011111; //When the delay time is expired,
digital port 5 is set low.
if(timer_channel_3 <= esc_loop_timer)PORTD &= B10111111; //When the delay time is expired,
digital port 6 is set low.
if(timer_channel_4 <= esc_loop_timer)PORTD &= B01111111; //When the delay time is expired,
digital port 7 is set low.
}
}

void set_gyro_registers(){
//Setup the MPU-6050
if(eeprom_data[31] == 1){
Wire.beginTransmission(gyro_address); //Start communication with the address found
during search.
Wire.write(0x6B); //We want to write to the PWR_MGMT_1 register (6B
hex)
Wire.write(0x00); //Set the register bits as 00000000 to activate the gyro
Wire.endTransmission(); //End the transmission with the gyro.

Wire.beginTransmission(gyro_address); //Start communication with the address found


during search.
Wire.write(0x1B); //We want to write to the GYRO_CONFIG register (1B
hex)
Wire.write(0x08); //Set the register bits as 00001000 (500dps full scale)
Wire.endTransmission(); //End the transmission with the gyro

Wire.beginTransmission(gyro_address); //Start communication with the address found


during search.
Wire.write(0x1C); //We want to write to the ACCEL_CONFIG register (1A
hex)
Wire.write(0x10); //Set the register bits as 00010000 (+/- 8g full scale range)
Wire.endTransmission(); //End the transmission with the gyro

//Let's perform a random register check to see if the values are written correct
Wire.beginTransmission(gyro_address); //Start communication with the address found
during search
Wire.write(0x1B); //Start reading @ register 0x1B
Wire.endTransmission(); //End the transmission
Wire.requestFrom(gyro_address, 1); //Request 1 bytes from the gyro
while(Wire.available() < 1); //Wait until the 6 bytes are received
if(Wire.read() != 0x08){ //Check if the value is 0x08
digitalWrite(12,HIGH); //Turn on the warning led
while(1)delay(10); //Stay in this loop for ever
}

Wire.beginTransmission(gyro_address); //Start communication with the address found


during search
Wire.write(0x1A); //We want to write to the CONFIG register (1A hex)
Wire.write(0x03); //Set the register bits as 00000011 (Set Digital Low Pass
Filter to ~43Hz)
Wire.endTransmission(); //End the transmission with the gyro

}
}

void gyro_signalen(){
//Read the MPU-6050
if(eeprom_data[31] == 1){
Wire.beginTransmission(gyro_address); //Start communication with the gyro.
Wire.write(0x3B); //Start reading @ register 43h and auto increment with
every read.
Wire.endTransmission(); //End the transmission.
Wire.requestFrom(gyro_address,14); //Request 14 bytes from the gyro.
while(Wire.available() < 14); //Wait until the 14 bytes are received.
acc_axis[1] = Wire.read()<<8|Wire.read(); //Add the low and high byte to the acc_x variable.
acc_axis[2] = Wire.read()<<8|Wire.read(); //Add the low and high byte to the acc_y variable.
acc_axis[3] = Wire.read()<<8|Wire.read(); //Add the low and high byte to the acc_z variable.
temperature = Wire.read()<<8|Wire.read(); //Add the low and high byte to the temperature
variable.
gyro_axis[1] = Wire.read()<<8|Wire.read(); //Read high and low part of the angular data.
gyro_axis[2] = Wire.read()<<8|Wire.read(); //Read high and low part of the angular data.
gyro_axis[3] = Wire.read()<<8|Wire.read(); //Read high and low part of the angular data.
}

if(cal_int == 2000){
gyro_axis[1] -= gyro_axis_cal[1]; //Only compensate after the calibration.
gyro_axis[2] -= gyro_axis_cal[2]; //Only compensate after the calibration.
gyro_axis[3] -= gyro_axis_cal[3]; //Only compensate after the calibration.
}
gyro_roll = gyro_axis[eeprom_data[28] & 0b00000011]; //Set gyro_roll to the correct axis that
was stored in the EEPROM.
if(eeprom_data[28] & 0b10000000)gyro_roll *= -1; //Invert gyro_roll if the MSB of EEPROM
bit 28 is set.
gyro_pitch = gyro_axis[eeprom_data[29] & 0b00000011]; //Set gyro_pitch to the correct axis that
was stored in the EEPROM.
if(eeprom_data[29] & 0b10000000)gyro_pitch *= -1; //Invert gyro_pitch if the MSB of EEPROM
bit 29 is set.
gyro_yaw = gyro_axis[eeprom_data[30] & 0b00000011]; //Set gyro_yaw to the correct axis that
was stored in the EEPROM.
if(eeprom_data[30] & 0b10000000)gyro_yaw *= -1; //Invert gyro_yaw if the MSB of EEPROM
bit 30 is set.

acc_x = acc_axis[eeprom_data[29] & 0b00000011]; //Set acc_x to the correct axis that was
stored in the EEPROM.
if(eeprom_data[29] & 0b10000000)acc_x *= -1; //Invert acc_x if the MSB of EEPROM bit
29 is set.
acc_y = acc_axis[eeprom_data[28] & 0b00000011]; //Set acc_y to the correct axis that was
stored in the EEPROM.
if(eeprom_data[28] & 0b10000000)acc_y *= -1; //Invert acc_y if the MSB of EEPROM bit
28 is set.
acc_z = acc_axis[eeprom_data[30] & 0b00000011]; //Set acc_z to the correct axis that was
stored in the EEPROM.
if(eeprom_data[30] & 0b10000000)acc_z *= -1; //Invert acc_z if the MSB of EEPROM bit
30 is set.
}

Flight_Control.cpp:-

#include <Wire.h> //Include the Wire.h library so we can communicate with the gyro.
#include <EEPROM.h> //Include the EEPROM.h library so we can store information
onto the EEPROM

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//PID gain and limit settings
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
float pid_p_gain_roll = 1.3; //Gain setting for the roll P-controller
float pid_i_gain_roll = 0.04; //Gain setting for the roll I-controller
float pid_d_gain_roll = 18.0; //Gain setting for the roll D-controller
int pid_max_roll = 400; //Maximum output of the PID-controller (+/-)

float pid_p_gain_pitch = pid_p_gain_roll; //Gain setting for the pitch P-controller.


float pid_i_gain_pitch = pid_i_gain_roll; //Gain setting for the pitch I-controller.
float pid_d_gain_pitch = pid_d_gain_roll; //Gain setting for the pitch D-controller.
int pid_max_pitch = pid_max_roll; //Maximum output of the PID-controller (+/-)

float pid_p_gain_yaw = 4.0; //Gain setting for the pitch P-controller. //4.0
float pid_i_gain_yaw = 0.02; //Gain setting for the pitch I-controller. //0.02
float pid_d_gain_yaw = 0.0; //Gain setting for the pitch D-controller.
int pid_max_yaw = 400; //Maximum output of the PID-controller (+/-)

boolean auto_level = true; //Auto level on (true) or off (false)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Declaring global variables
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
byte last_channel_1, last_channel_2, last_channel_3, last_channel_4;
byte eeprom_data[36];
byte highByte, lowByte;
volatile int receiver_input_channel_1, receiver_input_channel_2, receiver_input_channel_3,
receiver_input_channel_4;
int counter_channel_1, counter_channel_2, counter_channel_3, counter_channel_4, loop_counter;
int esc_1, esc_2, esc_3, esc_4;
int throttle, battery_voltage;
int cal_int, start, gyro_address;
int receiver_input[5];
int temperature;
int acc_axis[4], gyro_axis[4];
float roll_level_adjust, pitch_level_adjust;

long acc_x, acc_y, acc_z, acc_total_vector;


unsigned long timer_channel_1, timer_channel_2, timer_channel_3, timer_channel_4, esc_timer,
esc_loop_timer;
unsigned long timer_1, timer_2, timer_3, timer_4, current_time;
unsigned long loop_timer;
double gyro_pitch, gyro_roll, gyro_yaw;
double gyro_axis_cal[4];
float pid_error_temp;
float pid_i_mem_roll, pid_roll_setpoint, gyro_roll_input, pid_output_roll, pid_last_roll_d_error;
float pid_i_mem_pitch, pid_pitch_setpoint, gyro_pitch_input, pid_output_pitch,
pid_last_pitch_d_error;
float pid_i_mem_yaw, pid_yaw_setpoint, gyro_yaw_input, pid_output_yaw, pid_last_yaw_d_error;
float angle_roll_acc, angle_pitch_acc, angle_pitch, angle_roll;
boolean gyro_angles_set;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Setup routine
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup(){
//Serial.begin(57600);
//Copy the EEPROM data for fast access data.
for(start = 0; start <= 35; start++)eeprom_data[start] = EEPROM.read(start);
start = 0; //Set start back to zero.
gyro_address = eeprom_data[32]; //Store the gyro address in the variable.
Wire.begin(); //Start the I2C as master.

TWBR = 12; //Set the I2C clock speed to 400kHz.

//Arduino (Atmega) pins default to inputs, so they don't need to be explicitly declared as inputs.
DDRD |= B11110000; //Configure digital poort 4, 5, 6 and 7 as
output.
DDRB |= B00110000; //Configure digital poort 12 and 13 as output.

//Use the led on the Arduino for startup indication.


digitalWrite(12,HIGH); //Turn on the warning led.

//Check the EEPROM signature to make sure that the setup program is executed.
while(eeprom_data[33] != 'J' || eeprom_data[34] != 'M' || eeprom_data[35] != 'B')delay(10);

//The flight controller needs the MPU-6050 with gyro and accelerometer
//If setup is completed without MPU-6050 stop the flight controller program
if(eeprom_data[31] == 2 || eeprom_data[31] == 3)delay(10);

set_gyro_registers(); //Set the specific gyro registers.

for (cal_int = 0; cal_int < 1250 ; cal_int ++){ //Wait 5 seconds before continuing.
PORTD |= B11110000; //Set digital poort 4, 5, 6 and 7 high.
delayMicroseconds(1000); //Wait 1000us.
PORTD &= B00001111; //Set digital poort 4, 5, 6 and 7 low.
delayMicroseconds(3000); //Wait 3000us.
}

//Let's take multiple gyro data samples so we can determine the average gyro offset (calibration).
for (cal_int = 0; cal_int < 2000 ; cal_int ++){ //Take 2000 readings for calibration.
if(cal_int % 15 == 0)digitalWrite(12, !digitalRead(12)); //Change the led status to indicate
calibration.
gyro_signalen(); //Read the gyro output.
gyro_axis_cal[1] += gyro_axis[1]; //Ad roll value to gyro_roll_cal.
gyro_axis_cal[2] += gyro_axis[2]; //Ad pitch value to gyro_pitch_cal.
gyro_axis_cal[3] += gyro_axis[3]; //Ad yaw value to gyro_yaw_cal.
//We don't want the esc's to be beeping annoyingly. So let's give them a 1000us puls while calibrating
the gyro.
PORTD |= B11110000; //Set digital poort 4, 5, 6 and 7 high.
delayMicroseconds(1000); //Wait 1000us.
PORTD &= B00001111; //Set digital poort 4, 5, 6 and 7 low.
delay(3); //Wait 3 milliseconds before the next loop.
}
//Now that we have 2000 measures, we need to devide by 2000 to get the average gyro offset.
gyro_axis_cal[1] /= 2000; //Divide the roll total by 2000.
gyro_axis_cal[2] /= 2000; //Divide the pitch total by 2000.
gyro_axis_cal[3] /= 2000; //Divide the yaw total by 2000.

PCICR |= (1 << PCIE0); //Set PCIE0 to enable PCMSK0 scan.


PCMSK0 |= (1 << PCINT0); //Set PCINT0 (digital input 8) to trigger an
interrupt on state change.
PCMSK0 |= (1 << PCINT1); //Set PCINT1 (digital input 9)to trigger an
interrupt on state change.
PCMSK0 |= (1 << PCINT2); //Set PCINT2 (digital input 10)to trigger
an interrupt on state change.
PCMSK0 |= (1 << PCINT3); //Set PCINT3 (digital input 11)to trigger
an interrupt on state change.

//Wait until the receiver is active and the throtle is set to the lower position.
while(receiver_input_channel_3 < 990 || receiver_input_channel_3 > 1020 || receiver_input_channel_4
< 1400){
receiver_input_channel_3 = convert_receiver_channel(3); //Convert the actual receiver signals
for throttle to the standard 1000 - 2000us
receiver_input_channel_4 = convert_receiver_channel(4); //Convert the actual receiver signals
for yaw to the standard 1000 - 2000us
start ++; //While waiting increment start whith every loop.
//We don't want the esc's to be beeping annoyingly. So let's give them a 1000us puls while waiting for
the receiver inputs.
PORTD |= B11110000; //Set digital poort 4, 5, 6 and 7 high.
delayMicroseconds(1000); //Wait 1000us.
PORTD &= B00001111; //Set digital poort 4, 5, 6 and 7 low.
delay(3); //Wait 3 milliseconds before the next loop.
if(start == 125){ //Every 125 loops (500ms).
digitalWrite(12, !digitalRead(12)); //Change the led status.
start = 0; //Start again at 0.
}
}
start = 0; //Set start back to 0.

//Load the battery voltage to the battery_voltage variable.


//65 is the voltage compensation for the diode.
//12.6V equals ~5V @ Analog 0.
//12.6V equals 1023 analogRead(0).
//1260 / 1023 = 1.2317.
//The variable battery_voltage holds 1050 if the battery voltage is 10.5V.
battery_voltage = (analogRead(0) + 65) * 1.2317;

loop_timer = micros(); //Set the timer for the next loop.

//When everything is done, turn off the led.


digitalWrite(12,LOW); //Turn off the warning led.
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Main program loop
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop(){

//65.5 = 1 deg/sec (check the datasheet of the MPU-6050 for more information).
gyro_roll_input = (gyro_roll_input * 0.7) + ((gyro_roll / 65.5) * 0.3); //Gyro pid input is deg/sec.
gyro_pitch_input = (gyro_pitch_input * 0.7) + ((gyro_pitch / 65.5) * 0.3);//Gyro pid input is deg/sec.
gyro_yaw_input = (gyro_yaw_input * 0.7) + ((gyro_yaw / 65.5) * 0.3); //Gyro pid input is deg/sec.

//Gyro angle calculations


//0.0000611 = 1 / (250Hz / 65.5)
angle_pitch += gyro_pitch * 0.0000611; //Calculate the traveled pitch angle and
add this to the angle_pitch variable.
angle_roll += gyro_roll * 0.0000611; //Calculate the traveled roll angle and add
this to the angle_roll variable.

//0.000001066 = 0.0000611 * (3.142(PI) / 180degr) The Arduino sin function is in radians


angle_pitch -= angle_roll * sin(gyro_yaw * 0.000001066); //If the IMU has yawed transfer
the roll angle to the pitch angel.
angle_roll += angle_pitch * sin(gyro_yaw * 0.000001066); //If the IMU has yawed transfer
the pitch angle to the roll angel.

//Accelerometer angle calculations


acc_total_vector = sqrt((acc_x*acc_x)+(acc_y*acc_y)+(acc_z*acc_z)); //Calculate the total
accelerometer vector.

if(abs(acc_y) < acc_total_vector){ //Prevent the asin function to produce a NaN


angle_pitch_acc = asin((float)acc_y/acc_total_vector)* 57.296; //Calculate the pitch angle.
}
if(abs(acc_x) < acc_total_vector){ //Prevent the asin function to produce a NaN
angle_roll_acc = asin((float)acc_x/acc_total_vector)* -57.296; //Calculate the roll angle.
}

//Place the MPU-6050 spirit level and note the values in the following two lines for calibration.
angle_pitch_acc -= 0.0; //Accelerometer calibration value for pitch.
angle_roll_acc -= 0.0; //Accelerometer calibration value for roll.

angle_pitch = angle_pitch * 0.9996 + angle_pitch_acc * 0.0004; //Correct the drift of the gyro
pitch angle with the accelerometer pitch angle.
angle_roll = angle_roll * 0.9996 + angle_roll_acc * 0.0004; //Correct the drift of the gyro roll
angle with the accelerometer roll angle.

pitch_level_adjust = angle_pitch * 15; //Calculate the pitch angle correction


roll_level_adjust = angle_roll * 15; //Calculate the roll angle correction

if(!auto_level){ //If the quadcopter is not in auto-level mode


pitch_level_adjust = 0; //Set the pitch angle correction to zero.
roll_level_adjust = 0; //Set the roll angle correcion to zero.
}

//For starting the motors: throttle low and yaw left (step 1).
if(receiver_input_channel_3 < 1050 && receiver_input_channel_4 < 1050)start = 1;
//When yaw stick is back in the center position start the motors (step 2).
if(start == 1 && receiver_input_channel_3 < 1050 && receiver_input_channel_4 > 1450){
start = 2;

angle_pitch = angle_pitch_acc; //Set the gyro pitch angle equal to the


accelerometer pitch angle when the quadcopter is started.
angle_roll = angle_roll_acc; //Set the gyro roll angle equal to the accelerometer
roll angle when the quadcopter is started.
gyro_angles_set = true; //Set the IMU started flag.

//Reset the PID controllers for a bumpless start.


pid_i_mem_roll = 0;
pid_last_roll_d_error = 0;
pid_i_mem_pitch = 0;
pid_last_pitch_d_error = 0;
pid_i_mem_yaw = 0;
pid_last_yaw_d_error = 0;
}
//Stopping the motors: throttle low and yaw right.
if(start == 2 && receiver_input_channel_3 < 1050 && receiver_input_channel_4 > 1950)start = 0;

//The PID set point in degrees per second is determined by the roll receiver input.
//In the case of deviding by 3 the max roll rate is aprox 164 degrees per second ( (500-8)/3 = 164d/s ).
pid_roll_setpoint = 0;
//We need a little dead band of 16us for better results.
if(receiver_input_channel_1 > 1508)pid_roll_setpoint = receiver_input_channel_1 - 1508;
else if(receiver_input_channel_1 < 1492)pid_roll_setpoint = receiver_input_channel_1 - 1492;

pid_roll_setpoint -= roll_level_adjust; //Subtract the angle correction from the


standardized receiver roll input value.
pid_roll_setpoint /= 3.0; //Divide the setpoint for the PID roll controller
by 3 to get angles in degrees.

//The PID set point in degrees per second is determined by the pitch receiver input.
//In the case of deviding by 3 the max pitch rate is aprox 164 degrees per second ( (500-8)/3 = 164d/s
).
pid_pitch_setpoint = 0;
//We need a little dead band of 16us for better results.
if(receiver_input_channel_2 > 1508)pid_pitch_setpoint = receiver_input_channel_2 - 1508;
else if(receiver_input_channel_2 < 1492)pid_pitch_setpoint = receiver_input_channel_2 - 1492;

pid_pitch_setpoint -= pitch_level_adjust; //Subtract the angle correction from the


standardized receiver pitch input value.
pid_pitch_setpoint /= 3.0; //Divide the setpoint for the PID pitch controller
by 3 to get angles in degrees.

//The PID set point in degrees per second is determined by the yaw receiver input.
//In the case of deviding by 3 the max yaw rate is aprox 164 degrees per second ( (500-8)/3 = 164d/s ).
pid_yaw_setpoint = 0;
//We need a little dead band of 16us for better results.
if(receiver_input_channel_3 > 1050){ //Do not yaw when turning off the motors.
if(receiver_input_channel_4 > 1508)pid_yaw_setpoint = (receiver_input_channel_4 - 1508)/3.0;
else if(receiver_input_channel_4 < 1492)pid_yaw_setpoint = (receiver_input_channel_4 - 1492)/3.0;
}

calculate_pid(); //PID inputs are known. So we can calculate the


pid output.

//The battery voltage is needed for compensation.


//A complementary filter is used to reduce noise.
//0.09853 = 0.08 * 1.2317.
battery_voltage = battery_voltage * 0.92 + (analogRead(0) + 65) * 0.09853;

//Turn on the led if battery voltage is to low.


if(battery_voltage < 1000 && battery_voltage > 600)digitalWrite(12, HIGH);

throttle = receiver_input_channel_3; //We need the throttle signal as a base


signal.
if (start == 2){ //The motors are started.
if (throttle > 1800) throttle = 1800; //We need some room to keep full control at
full throttle.
esc_1 = throttle - pid_output_pitch + pid_output_roll - pid_output_yaw; //Calculate the pulse for esc 1
(front-right - CCW)
esc_2 = throttle + pid_output_pitch + pid_output_roll + pid_output_yaw; //Calculate the pulse for esc
2 (rear-right - CW)
esc_3 = throttle + pid_output_pitch - pid_output_roll - pid_output_yaw; //Calculate the pulse for esc 3
(rear-left - CCW)
esc_4 = throttle - pid_output_pitch - pid_output_roll + pid_output_yaw; //Calculate the pulse for esc 4
(front-left - CW)

if (battery_voltage < 1240 && battery_voltage > 800){ //Is the battery connected?
esc_1 += esc_1 * ((1240 - battery_voltage)/(float)3500); //Compensate the esc-1 pulse for
voltage drop.
esc_2 += esc_2 * ((1240 - battery_voltage)/(float)3500); //Compensate the esc-2 pulse for
voltage drop.
esc_3 += esc_3 * ((1240 - battery_voltage)/(float)3500); //Compensate the esc-3 pulse for
voltage drop.
esc_4 += esc_4 * ((1240 - battery_voltage)/(float)3500); //Compensate the esc-4 pulse for
voltage drop.
}

if (esc_1 < 1100) esc_1 = 1100; //Keep the motors running.


if (esc_2 < 1100) esc_2 = 1100; //Keep the motors running.
if (esc_3 < 1100) esc_3 = 1100; //Keep the motors running.
if (esc_4 < 1100) esc_4 = 1100; //Keep the motors running.

if(esc_1 > 2000)esc_1 = 2000; //Limit the esc-1 pulse to 2000us.


if(esc_2 > 2000)esc_2 = 2000; //Limit the esc-2 pulse to 2000us.
if(esc_3 > 2000)esc_3 = 2000; //Limit the esc-3 pulse to 2000us.
if(esc_4 > 2000)esc_4 = 2000; //Limit the esc-4 pulse to 2000us.
}

else{
esc_1 = 1000; //If start is not 2 keep a 1000us pulse for ess-1.
esc_2 = 1000; //If start is not 2 keep a 1000us pulse for ess-2.
esc_3 = 1000; //If start is not 2 keep a 1000us pulse for ess-3.
esc_4 = 1000; //If start is not 2 keep a 1000us pulse for ess-4.
}

//! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
//Because of the angle calculation the loop time is getting very important. If the loop time is
//longer or shorter than 4000us the angle calculation is off. If you modify the code make sure
//that the loop time is still 4000us and no longer! More information can be found on
//the Q&A page:
//! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !

if(micros() - loop_timer > 4050)digitalWrite(12, HIGH); //Turn on the LED if the loop time
exceeds 4050us.

//All the information for controlling the motor's is available.


//The refresh rate is 250Hz. That means the esc's need there pulse every 4ms.
while(micros() - loop_timer < 4000); //We wait until 4000us are passed.
loop_timer = micros(); //Set the timer for the next loop.

PORTD |= B11110000; //Set digital outputs 4,5,6 and 7 high.


timer_channel_1 = esc_1 + loop_timer; //Calculate the time of the faling edge of
the esc-1 pulse.
timer_channel_2 = esc_2 + loop_timer; //Calculate the time of the faling edge of
the esc-2 pulse.
timer_channel_3 = esc_3 + loop_timer; //Calculate the time of the faling edge of
the esc-3 pulse.
timer_channel_4 = esc_4 + loop_timer; //Calculate the time of the faling edge of
the esc-4 pulse.

//There is always 1000us of spare time. So let's do something usefull that is very time consuming.
//Get the current gyro and receiver data and scale it to degrees per second for the pid calculations.
gyro_signalen();

while(PORTD >= 16){ //Stay in this loop until output 4,5,6 and 7 are
low.
esc_loop_timer = micros(); //Read the current time.
if(timer_channel_1 <= esc_loop_timer)PORTD &= B11101111; //Set digital output 4 to low
if the time is expired.
if(timer_channel_2 <= esc_loop_timer)PORTD &= B11011111; //Set digital output 5 to low
if the time is expired.
if(timer_channel_3 <= esc_loop_timer)PORTD &= B10111111; //Set digital output 6 to low
if the time is expired.
if(timer_channel_4 <= esc_loop_timer)PORTD &= B01111111; //Set digital output 7 to low
if the time is expired.
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//This routine is called every time input 8, 9, 10 or 11 changed state. This is used to read the receiver
signals.
//More information about this subroutine can be found in this video:
//https://youtu.be/bENjl1KQbvo
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ISR(PCINT0_vect){
current_time = micros();
//Channel 1=========================================
if(PINB & B00000001){ //Is input 8 high?
if(last_channel_1 == 0){ //Input 8 changed from 0 to 1.
last_channel_1 = 1; //Remember current input state.
timer_1 = current_time; //Set timer_1 to current_time.
}
}
else if(last_channel_1 == 1){ //Input 8 is not high and changed from 1 to 0.
last_channel_1 = 0; //Remember current input state.
receiver_input[1] = current_time - timer_1; //Channel 1 is current_time - timer_1.
}
//Channel 2=========================================
if(PINB & B00000010 ){ //Is input 9 high?
if(last_channel_2 == 0){ //Input 9 changed from 0 to 1.
last_channel_2 = 1; //Remember current input state.
timer_2 = current_time; //Set timer_2 to current_time.
}
}
else if(last_channel_2 == 1){ //Input 9 is not high and changed from 1 to 0.
last_channel_2 = 0; //Remember current input state.
receiver_input[2] = current_time - timer_2; //Channel 2 is current_time - timer_2.
}
//Channel 3=========================================
if(PINB & B00000100 ){ //Is input 10 high?
if(last_channel_3 == 0){ //Input 10 changed from 0 to 1.
last_channel_3 = 1; //Remember current input state.
timer_3 = current_time; //Set timer_3 to current_time.
}
}
else if(last_channel_3 == 1){ //Input 10 is not high and changed from 1 to
0.
last_channel_3 = 0; //Remember current input state.
receiver_input[3] = current_time - timer_3; //Channel 3 is current_time - timer_3.

}
//Channel 4=========================================
if(PINB & B00001000 ){ //Is input 11 high?
if(last_channel_4 == 0){ //Input 11 changed from 0 to 1.
last_channel_4 = 1; //Remember current input state.
timer_4 = current_time; //Set timer_4 to current_time.
}
}
else if(last_channel_4 == 1){ //Input 11 is not high and changed from 1 to
0.
last_channel_4 = 0; //Remember current input state.
receiver_input[4] = current_time - timer_4; //Channel 4 is current_time - timer_4.
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Subroutine for reading the gyro
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void gyro_signalen(){
//Read the MPU-6050
if(eeprom_data[31] == 1){
Wire.beginTransmission(gyro_address); //Start communication with the gyro.
Wire.write(0x3B); //Start reading @ register 43h and auto increment
with every read.
Wire.endTransmission(); //End the transmission.
Wire.requestFrom(gyro_address,14); //Request 14 bytes from the gyro.

receiver_input_channel_1 = convert_receiver_channel(1); //Convert the actual receiver signals


for pitch to the standard 1000 - 2000us.
receiver_input_channel_2 = convert_receiver_channel(2); //Convert the actual receiver signals
for roll to the standard 1000 - 2000us.
receiver_input_channel_3 = convert_receiver_channel(3); //Convert the actual receiver signals
for throttle to the standard 1000 - 2000us.
receiver_input_channel_4 = convert_receiver_channel(4); //Convert the actual receiver signals
for yaw to the standard 1000 - 2000us.

while(Wire.available() < 14); //Wait until the 14 bytes are received.


acc_axis[1] = Wire.read()<<8|Wire.read(); //Add the low and high byte to the acc_x
variable.
acc_axis[2] = Wire.read()<<8|Wire.read(); //Add the low and high byte to the acc_y
variable.
acc_axis[3] = Wire.read()<<8|Wire.read(); //Add the low and high byte to the acc_z
variable.
temperature = Wire.read()<<8|Wire.read(); //Add the low and high byte to the
temperature variable.
gyro_axis[1] = Wire.read()<<8|Wire.read(); //Read high and low part of the angular
data.
gyro_axis[2] = Wire.read()<<8|Wire.read(); //Read high and low part of the angular
data.
gyro_axis[3] = Wire.read()<<8|Wire.read(); //Read high and low part of the angular
data.
}

if(cal_int == 2000){
gyro_axis[1] -= gyro_axis_cal[1]; //Only compensate after the calibration.
gyro_axis[2] -= gyro_axis_cal[2]; //Only compensate after the calibration.
gyro_axis[3] -= gyro_axis_cal[3]; //Only compensate after the calibration.
}
gyro_roll = gyro_axis[eeprom_data[28] & 0b00000011]; //Set gyro_roll to the correct axis
that was stored in the EEPROM.
if(eeprom_data[28] & 0b10000000)gyro_roll *= -1; //Invert gyro_roll if the MSB of
EEPROM bit 28 is set.
gyro_pitch = gyro_axis[eeprom_data[29] & 0b00000011]; //Set gyro_pitch to the correct
axis that was stored in the EEPROM.
if(eeprom_data[29] & 0b10000000)gyro_pitch *= -1; //Invert gyro_pitch if the MSB of
EEPROM bit 29 is set.
gyro_yaw = gyro_axis[eeprom_data[30] & 0b00000011]; //Set gyro_yaw to the correct
axis that was stored in the EEPROM.
if(eeprom_data[30] & 0b10000000)gyro_yaw *= -1; //Invert gyro_yaw if the MSB of
EEPROM bit 30 is set.

acc_x = acc_axis[eeprom_data[29] & 0b00000011]; //Set acc_x to the correct axis that
was stored in the EEPROM.
if(eeprom_data[29] & 0b10000000)acc_x *= -1; //Invert acc_x if the MSB of
EEPROM bit 29 is set.
acc_y = acc_axis[eeprom_data[28] & 0b00000011]; //Set acc_y to the correct axis that
was stored in the EEPROM.
if(eeprom_data[28] & 0b10000000)acc_y *= -1; //Invert acc_y if the MSB of
EEPROM bit 28 is set.
acc_z = acc_axis[eeprom_data[30] & 0b00000011]; //Set acc_z to the correct axis that
was stored in the EEPROM.
if(eeprom_data[30] & 0b10000000)acc_z *= -1; //Invert acc_z if the MSB of EEPROM
bit 30 is set.
}

void calculate_pid(){
//Roll calculations
pid_error_temp = gyro_roll_input - pid_roll_setpoint;
pid_i_mem_roll += pid_i_gain_roll * pid_error_temp;
if(pid_i_mem_roll > pid_max_roll)pid_i_mem_roll = pid_max_roll;
else if(pid_i_mem_roll < pid_max_roll * -1)pid_i_mem_roll = pid_max_roll * -1;

pid_output_roll = pid_p_gain_roll * pid_error_temp + pid_i_mem_roll + pid_d_gain_roll *


(pid_error_temp - pid_last_roll_d_error);
if(pid_output_roll > pid_max_roll)pid_output_roll = pid_max_roll;
else if(pid_output_roll < pid_max_roll * -1)pid_output_roll = pid_max_roll * -1;

pid_last_roll_d_error = pid_error_temp;

//Pitch calculations
pid_error_temp = gyro_pitch_input - pid_pitch_setpoint;
pid_i_mem_pitch += pid_i_gain_pitch * pid_error_temp;
if(pid_i_mem_pitch > pid_max_pitch)pid_i_mem_pitch = pid_max_pitch;
else if(pid_i_mem_pitch < pid_max_pitch * -1)pid_i_mem_pitch = pid_max_pitch * -1;

pid_output_pitch = pid_p_gain_pitch * pid_error_temp + pid_i_mem_pitch + pid_d_gain_pitch *


(pid_error_temp - pid_last_pitch_d_error);
if(pid_output_pitch > pid_max_pitch)pid_output_pitch = pid_max_pitch;
else if(pid_output_pitch < pid_max_pitch * -1)pid_output_pitch = pid_max_pitch * -1;

pid_last_pitch_d_error = pid_error_temp;

//Yaw calculations
pid_error_temp = gyro_yaw_input - pid_yaw_setpoint;
pid_i_mem_yaw += pid_i_gain_yaw * pid_error_temp;
if(pid_i_mem_yaw > pid_max_yaw)pid_i_mem_yaw = pid_max_yaw;
else if(pid_i_mem_yaw < pid_max_yaw * -1)pid_i_mem_yaw = pid_max_yaw * -1;

pid_output_yaw = pid_p_gain_yaw * pid_error_temp + pid_i_mem_yaw + pid_d_gain_yaw *


(pid_error_temp - pid_last_yaw_d_error);
if(pid_output_yaw > pid_max_yaw)pid_output_yaw = pid_max_yaw;
else if(pid_output_yaw < pid_max_yaw * -1)pid_output_yaw = pid_max_yaw * -1;

pid_last_yaw_d_error = pid_error_temp;
}

//This part converts the actual receiver signals to a standardized 1000 – 1500 – 2000 microsecond value.
//The stored data in the EEPROM is used.
int convert_receiver_channel(byte function){
byte channel, reverse; //First we declare some local variables
int low, center, high, actual;
int difference;

channel = eeprom_data[function + 23] & 0b00000111; //What channel corresponds


with the specific function
if(eeprom_data[function + 23] & 0b10000000)reverse = 1; //Reverse channel when most
significant bit is set
else reverse = 0; //If the most significant is not set there is no
reverse

actual = receiver_input[channel]; //Read the actual receiver value for the


corresponding function
low = (eeprom_data[channel * 2 + 15] << 8) | eeprom_data[channel * 2 + 14]; //Store the low value
for the specific receiver input channel
center = (eeprom_data[channel * 2 - 1] << 8) | eeprom_data[channel * 2 - 2]; //Store the center value
for the specific receiver input channel
high = (eeprom_data[channel * 2 + 7] << 8) | eeprom_data[channel * 2 + 6]; //Store the high value for
the specific receiver input channel
if(actual < center){ //The actual receiver value is lower than the
center value
if(actual < low)actual = low; //Limit the lowest value to the value that was
detected during setup
difference = ((long)(center - actual) * (long)500) / (center - low); //Calculate and scale the actual
value to a 1000 - 2000us value
if(reverse == 1)return 1500 + difference; //If the channel is reversed
else return 1500 - difference; //If the channel is not reversed
}
else if(actual > center){ //The actual receiver value is higher
than the center value
if(actual > high)actual = high; //Limit the lowest value to the value that was
detected during setup
difference = ((long)(actual - center) * (long)500) / (high - center); //Calculate and scale the actual
value to a 1000 - 2000us value
if(reverse == 1)return 1500 - difference; //If the channel is reversed
else return 1500 + difference; //If the channel is not reversed
}
else return 1500;
}

void set_gyro_registers(){
//Setup the MPU-6050
if(eeprom_data[31] == 1){
Wire.beginTransmission(gyro_address); //Start communication with the address
found during search.
Wire.write(0x6B); //We want to write to the PWR_MGMT_1
register (6B hex)
Wire.write(0x00); //Set the register bits as 00000000 to activate the
gyro
Wire.endTransmission(); //End the transmission with the gyro.

Wire.beginTransmission(gyro_address); //Start communication with the address


found during search.
Wire.write(0x1B); //We want to write to the GYRO_CONFIG
register (1B hex)
Wire.write(0x08); //Set the register bits as 00001000 (500dps full
scale)
Wire.endTransmission(); //End the transmission with the gyro

Wire.beginTransmission(gyro_address); //Start communication with the address


found during search.
Wire.write(0x1C); //We want to write to the ACCEL_CONFIG
register (1A hex)
Wire.write(0x10); //Set the register bits as 00010000 (+/- 8g full
scale range)
Wire.endTransmission(); //End the transmission with the gyro

//Let's perform a random register check to see if the values are written correct
Wire.beginTransmission(gyro_address); //Start communication with the address
found during search
Wire.write(0x1B); //Start reading @ register 0x1B
Wire.endTransmission(); //End the transmission
Wire.requestFrom(gyro_address, 1); //Request 1 bytes from the gyro
while(Wire.available() < 1); //Wait until the 6 bytes are received
if(Wire.read() != 0x08){ //Check if the value is 0x08
digitalWrite(12,HIGH); //Turn on the warning led
while(1)delay(10); //Stay in this loop for ever
}

Wire.beginTransmission(gyro_address); //Start communication with the address


found during search
Wire.write(0x1A); //We want to write to the CONFIG register (1A
hex)
Wire.write(0x03); //Set the register bits as 00000011 (Set Digital
Low Pass Filter to ~43Hz)
Wire.endTransmission(); //End the transmission with the gyro

}
}
4.2 Testing Approach

4.2.1 Unit Testing


Our project required significant testing at several stages throughout the project’s duration. It was
important that we tested the performance of critical systems as we developed them. This section covers
the main tests that we performed during the development of the quadcopter.

Sr.No. Test Cases Conditions Expected Output Pass or


fail
1 Arduino Compile and upload the Uploading done and PASS
code Successfully compiled
2 ESC’s and Check the signals and Beep sound to be heard and the PASS
Motors power flow motors are armed

3 MPU 6050 Find in I2C address of Found the address PASS


Arduino

4 Receiver Check if its receives the Red Led lights when the PASS
signal from transmitter transmitter is turned on

5 Camera Captures Images Successfully captured images PASS


Module

6 SD card Stores the images Successfully stores the images PASS


Module

Table 3 Unit Testing


1. Motor Thrust Testing and ESC Signal Testing:
As we developed the quadcopter considered PID implementation, concerns were raised that the
motor thrust may not vary linearly with ESC command signal pulse length. If that were the case, the
PID controllers would behave differently at different throttle settings unless some form of
compensation was implemented, such as a motor speed mapping function.
To test the motor thrust and determine its relationship to ESC command signal, one of the motors
was mounted to a load cell. The load cell had a 5 kg maximum load, more than adequate for the less
than 1 kg maximum that we expected from the motor.
After the load cell readings were calibrated using proof masses, an Arduino program was written
that would increase the motor speed by steps of 100 μs and take 100 measurements from the load cell
at each speed. The measurements were then averaged to obtain a thrust reading at that speed setting.
2. Stabilization Testing :
Once the quadcopter had been assembled, we needed to test its stabilization capabilities. To do
this safely, we constructed a test stand that would restrain the quad while allowing it to rotate about
one axis, either pitch or roll.
The test stand was used to find balancing of the quadcopter or we can say the centre of gravity to
make the drone more stable.

3. PID Testing:
Once the stability of the drone was maintained the next phase was to apply the PID to the
quadcopter. The PID is used to make the drone move freely while flying and maintaining the stability
as it was stored while stabilization testing.
The PID consist of two factors
1. Gyroscope:
The gyroscope measures angular velocity, 𝜃̇, in radians per second or degrees per second.
Intuitively, by integrating the angular velocity the tilt angle can be calculated. Since the gyroscope
readings are taken at discrete time intervals, 𝑑𝑡, numerical integration is performed using the Euler
method.

The equation above assumes the sensor initial position is 0 degrees. If the starting position of
the sensor is non-zero, the angle has to be initialized to the value, possibly from another sensor i.e.
accelerometer.
A key element to observe was that the measurements were taken while the quadcopter was at
rest. So that if the quadcopter tilts to desire directions its can send signals to accelerometer.
2. Accelerometer:
The accelerometer measures the acceleration relative to free fall. The acceleration is often
measured in gs, which is based on earth’s gravitational pull (9.81m/s). To determine the orientation of
the accelerometer, it is assumed that the only force acting on the object is earth’s gravitation pull.
Gravity always acts ‘down’, thus when the object is tilted, the force is divided into components in the
x, y, and z directions of the object. Since the axes are orthogonal to one another, Pythagoras theorem
can be used to show the relationship between the forces.
The following equation is more useful as it calculates the angle relative to the z-axis (Roll)
𝑅𝑜𝑙𝑙 = 𝜙𝑥𝑦𝑧 = tan−1 ( 𝑦 /𝑧 )
With a similar approach, Pitch can also be calculated. However, Yaw cannot be determined
accurately, especially when the force in the z direction = 1g (assuming the accelerometer can only rotate
and not translate in any direction). In this case changing the yaw, will have impact on the components
of x and y, making yaw not constant.

4.2.2 Integrate Testing


The integrated testing, we see the start to end process the with detail study
 First the Setup code is uploaded to the Arduino to check the connection of the receiver to the
board using the 4 channels of the transmitter. In this step the measurements of the transmitter
levers or the channels are stored.
 After the measurements are taken the next step is to search for MPU 6050 module in I2C address
and the PID Measurements are taken for x, y and z axis.
 Second, we upload ESC calibration code to Arduino to calibrate the esc of the drone to
transmitter. After uploading the code, we put the transmitter thrust lever to the highest position
and then connect battery to the drone and wait for the beep sound of the esc’s and then lower the
lever to lowest position and a long beep is heard and then the esc is calibrated.
 Third we upload the flight controller code to Arduino to arm and disarm the motors. In this the
throttle lever is used to arm and disarm the drone.
 The code for the camera are uploaded to Arduino as its captures the images and is stored to the
SD card using the SD card module.
CHAPTER 5
RESULTS AND DISCUSSIONS
3.6 Test Report
The Test Report we will see the set of components will be work as per selection, all the components
will work fine and accurate other test will be work fine as see in units testing and integrated testing

3.7 User Documentation


1. Before the flight all the propellers are to be attached to the drone. According to the rotation of
motors clockwise and anti-clockwise.
2. Turn on the transmitter first and then connect the battery to the drone. This is done to prevent
the signals of the receiver and transmitter.
3. When the battery is connected to the drone arm the motors using transmitter, and the rotation
of the motors starts.
4. Now the drone is ready to fly.
5.2.1 Screen Shots

Fig 26 Drone Setup coding and uploading

Fig. 27 Calibration of Setup code with transmitter


Fig. 28 Searching for MPU-6050

Fig. 29 Calibrating PID with the help of Gyroscope


Fig. 30 ESC Calibrating Coding and uploading

Fig. 31 Flight Controller code for drone.


Fig 33 Connection to Arduino

Fig 34. Drone


CHAPTER 6

CONCLUSIONS AND FUTURE WORK

6.1 CONCLUSION
Our research work yielded a successful development of Arduino Uno based Quadcopter at a
cheaper and affordable amount. Quadcopter which can be easily made from shelf components. It can
be used as a low-cost alternative to various applications which includes pesticide sprinkling, end to end
delivery within the transmitter’s RF range, surveillance in defence and other sensitive places like nation
border, mapping through remote sensing, and record the picture of places.

6.2 LIMITATION
The battery in the drone has a limited power of 10-15min of flight. The Camera used has a
very low resolution and the images are of low quality.

6.3 FUTURE WORK


Our team goals were to design, test, and build a quadcopter kit. There are various possible up-
gradation in future based on its application which includes:
 Adding a sonic sensor module to controller board for more accurate altitude determination.
 Implementing a GPS module on kit for tracking & spy based applications.  This design can
employ Motor Driver of high rating or Relay driver can be used for its commercial
applications.
 Can be used for real estate photography by employing camera on it. Other applications
includes inspection, surveillance and monitoring a wide area by camera equipped quadcopter.
 Pesticides sprinkling
Based on the weight lifting calculations we can use our single economical Quadcopter to lift
these different modules satisfying the weight lifting criteria.

CHAPTER 7
BIBLIOGRAPHY
http://www.brokking.net/ymfc-al_main.html
(this link is used to make the quadcopter.)
https://www.instructables.com/id/How-to-Make-a-Drone-Using-Arduino-Make-a-Quadcopte/
(this link is used to build the quadcopter frame and other parts)
https://hkalasua.wordpress.com/2017/09/11/ov7670-arduino-sd/
(used to connect the camera module and sd card module to Arduino)
https://www.youtube.com/watch?v=2pHdO8m6T7c
(this video is used for the connection of the drone)
https://www.youtube.com/watch?v=DYpHB-LfloI&t=132s
(this video is used to write code to make quadcopter move according to the transmitter movement)
https://www.youtube.com/watch?v=nCPEJTUYch8&list=PL0K4VDicBzsibZqfa42DVxC8CGCMB7
G2G&index=5&t=377s
(used to set the gyro and accelerometer to the drone)

You might also like