Top Banner
EECS Senior Design RDRIV: Rapidly Developed Reconnaissance Inspection Vehicle Final Design Report Date: 14/04/2015 University of Cincinnati, Ohio
106

RDRIV Final Report

Apr 07, 2017

Download

Documents

Alex Jones
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: RDRIV Final Report

EECS Senior Design RDRIV: Rapidly Developed Reconnaissance Inspection

Vehicle Final Design Report

Date: 14/04/2015

University of Cincinnati, Ohio

Page 2: RDRIV Final Report

UNIVERSITY OF CINCINNATI SCHOOL OF ELECTRONICS & COMPUTING SYSTEMS SENIOR DESIGN FINAL TECHNICAL DESIGN REPORT

Prepared By:

_____________________________________ Thomas M. Braum Electrical Engineering

_____________________________________ Alex M. Jones Electrical Engineering

_____________________________________ Meagan E. Lesher Electrical Engineering

_____________________________________ Nathanial W. Thomas Electrical Engineering

Page 3: RDRIV Final Report

Executive Summary

This project was proposed by the Air Force Research Lab to fulfill a need for a cost-

effective alternative for first response inspection robots that are currently on the market. The

intention was to create a robot that was low cost, using a single-board computer and inexpensive

electronics and motors for object detection and movement with a modular 3D printed chassis.

Their intention was to have a robust robot that could be controlled by an operator to roam in and

around a dangerous environment by using sensors and a camera to observe its surroundings,

allowing the operator to identify any potential threats before humans enter the area.

We accomplished building a fully functional robot. A user can control the robot’s motion

through a G.U.I. on a computer while streaming live video via Wi-Fi. The computer can

communicate with the robot up to 120 feet away using two nRF24l01+ chips which are

connected to the computer using an Arduino Nano, and directly to a Raspberry Pi on the robot.

The Raspberry Pi is used to control all the peripheral electronics on the robot to include four

motors, two infrared sensors, two sonar sensors, and a camera. The robot itself is almost entirely

3D printed; each part takes between twenty five minutes and three and a half hours to print,

allowing for relatively rapid recreation of damaged parts when used in the field. The robot

utilizes treads as its mode of transportation, allowing for use in diverse environments. The

operator can control the motion of the robot while viewing the environment through a camera

mounted on the front of the robot. The sensors are placed on the front and back of the robot to

augment the user’s situational awareness and to determine distances of obstacles from the robot.

This is useful in situations where obstacles are visibly indistinguishable from the background, or

are just outside the camera’s field of view.

Page 4: RDRIV Final Report

Background

First response robots can be used to protect human life by entering a dangerous

environment and alerting the user of potential hazards, allowing a human to enter the

environment prepared to deal with any hazards. They can also be used to enter areas inaccessible

to humans; such as cramped spaces, contaminated areas, and unstable terrain. These robots are

used by several agencies including the military, police, and S.W.A.T teams. First response robots

are able to save lives, but unfortunately, current market models are extremely robust and offer

high functionality, but their cost (starting at $50,000) puts them well out of the reach of small

scale first response units.[1] Repairs, if any, only compound on to that initial cost. The Air Force

Research Lab saw the importance of these robots and set out to create a low cost version of their

own with a given list of priorities and a budget of $300.

The Air Force Research Lab described the features they felt were necessary for the robot

to be competitive. The most important was movement; including rolling forwards and

backwards, turning, traversing obstacles, and determining if a new path is needed using an array

of sensors. The robot needed to stream live video and receive remote commands from a range of

30 feet. The chassis, wheels, and treads needed to be 3D printed, with individual modules to be

printed in under 3 hours.

[1] http://www.homelandsecuritynewswire.com/first-response-law-enforcement-ground-robot-

market-grow

Page 5: RDRIV Final Report

Design Process

The two constraints given by the Air Force Research Lab were that the robot cost less

than $300 and run for at least 15 minutes. The other priorities given to us were:

1. Movement

a. Forward, backward, and turning

b. Able to climb over objects

c. Semiautonomous motion - programed to move freely with a manual override

option

2. Video Feed

3. Small size to fit into tight spaces but large enough to see over a two foot object

4. Operation Range minimum of 30 ft from user

5. Printing time below two hours per component

6. Able to flip over if inverted

Our first task was deciding what requirements from the AFRL we wanted to focus on.

Their idea for this project was ambitious and it became apparent that we needed to narrow down

our priority list to finish before the end of the school year. We decided our number one priority

was user controlled movement. Our next top priority was being able to control the robot

wirelessly. Finally, we wanted to ensure that a video feed could be wirelessly sent from the robot

to the user.

With this in mind we created numerical evaluation matrixes to analyze what components

we needed.

Table 1 - Controller Evaluation Matrix

When choosing a microcontroller we took several factors into consideration. We looked

at cost in order to stay under budget, programmability in order to maximize our limited timeline

for software development, and compatibility with peripheral systems in order to fulfill the

movement, sensory, and video design objectives. The Raspberry Pi B+ became our

Page 6: RDRIV Final Report

microcontroller due to its simple and versatile programmability, with it able to be programmed in

C, C++, and Python, and its compatibility with the camera module, motor shields, and wireless

communication chip. The PIC Microcontroller fell to the wayside mainly due to its poor

compatibility with a camera, or motors, and its programmability requiring us to use assembly

code. The Beaglebone Black was not chosen, despite its impressive number of IO pins and easy

programmability, due to its cost and incompatibility with peripheral systems. The model of

Arduino with a comparable cost to the Raspberry Pi B+ did not have enough IO pins to support

the number of peripheral systems that the Raspberry Pi could and did not have an integrated

camera module which would have further depleted the number of available IO pins.

Movement Priority and Numerical Evaluation Matrix (1 being the worst & 4 Being the

Best)

Design Constraints and

Objectives Priority Wheels Legs Treads Ball

(C) All Terrain

2 2 4 1

(C) 3D printable

4 3 3 1

(O) high Durability 4 1 4 2

(O) high maneuverability 2 3 4 4

Total 12 9 15 8

Table 2 - Movement Evaluation Matrix

In order to fulfill our requirement for the robot to be able to traverse difficult terrain, we

had to choose between wheels, legs, treads, and an encapsulating sphere. The mode of

transportation for the robot needed to be 3D printable, drivable on almost any terrain, durable,

and allow the robot to be highly maneuverable. We chose the Tread design for its high

maneuverability, ability to drive all terrain, and durability. While the Ball design allows for the

maximum amount of maneuverability possible, the design would fail in rough terrain and 3D

printing the design would interfere with streaming video. The Leg design was projected to be

too fragile if 3D printed. The wheel design was not chosen because it lacked in all terrain and

maneuverability despite being easy to 3D print and durable.

Page 7: RDRIV Final Report

Table 3 - User Interface Evaluation Matrix

For the User Interface we required a way to display the camera feed from the robot while

simultaneously allowing for the user to control the robot via directional controls. Our UI consisted of

a console window on a web enabled tablet or laptop computer with 5 buttons commanding the robot

to move forward, backward, left, right, or to stop. Around the buttons were virtual LCD screens

which displayed the values returned from the sensors. The video feed is displayed via a separate web

browser devoted to the video display and settings.

Table 4 - Wireless Data Evaluation Matrix

For the wireless data device we needed it to be low cost with a large signal distance. Since

the robot needed to move in a different room than the operator we knew the signal strength had to be

more than a hundred feet. This eliminated Wi-Fi and Bluetooth devices. Another critical objective

was that the wireless device has to be camera compatible. This left us with the nRF24l01+. After

testing and research we found out that the RF is unable to send the payload of data we would end up

needing. That is why we decided to use Wi-Fi for the video feed. The nRF remains the most cost

efficient solution, and it is capable of transmitting and receiving data at a 2mbps v.s. the other

contender the Xbee which only transmits and receives data at 250kbps.

We also considered the following:

System Software:

Python was chosen because it is Raspberry Pi’s native language and there are extensive

online tutorials and resources

Motors:

DC brushed gear motors were chosen to keep weight and cost down while

maintaining torque strength

Page 8: RDRIV Final Report

Camera:

The Raspberry Pi camera was chosen because of its low cost and simple integration with the

board

The Air Force Research Lab made it clear to us that the end of our project would not be the

end of the development for this robot. We made sure that even though we did not accomplish all of

the goals requested for this robot that it would be easy for someone else to pick up the project and

add to it.

We spent time considering how to create a small robot with a camera being held up to 2ft off

the ground. The robot could not be 2ft tall, so we decided that a retractable arm would be the best

way to accomplish this task. We did not have time or the mechanical knowledge to develop the

retractable arm, but designed the body to be large enough to accommodate the addition of a

retractable arm in the future. This resulted in a body that was larger than what was needed to hold all

of the components we used.

Table 5 - Sensor Evaluation Matrix

For the sensor array the sensors needed to be low cost, sensitive to varying distances, and

able to detect both soft and solid objects. The final design contains both IR and Sonar sensors which

complement each other when dealing with a variety of heat and acoustic insulating surfaces. Both

types are low cost and are easily integrated into the design. Both Radar and LIDAR sensors, while

being extremely accurate, are extremely expensive and would immediately put our design outside of

our budget range.

Page 9: RDRIV Final Report

Requirements/ Assessment Metrics

The requirements we used for are project stemmed from the requirements given to us

from the Air Force Research Lab. These requirements, listed below, ranged from the movement

the robot would be able to achieve, the range it would need to be able to perform in, the time for

printing of pieces, the overall cost of the robot, the run time minimum, and the feedback to the

users.

Able to move forward, backwards, and turn

Able to navigate around objects in a room

Able to navigate over small objects in a room (2” tall object)

Video feed sent from the robot to the operator

Able to navigate the terrain autonomously with the user able to override control at

any time

Small enough to fit into small spaces while still able to see over a 2 foot object

Operating Range of 30 feet from the user

Printing time around or below 2 hours per module

Battery life longer than 15 minutes

Overall cost less than $300

Able to flip over if inverted

With these requirements we were able to start the assessment process to decide on what

route we would like to take for our project. By using the Morph chart shown below we were able

to list all of the plausible possibilities for our project and start a comparison process for each

Means Function. Once we had all the possibilities listed we moved to researching and comparing

the possibilities with each other through the Numerical Evaluation Matrixes, seen above in the

Design Process section. In the morph chart the values in blue are the winning means and show

our choices from the Numerical Evaluation Matrixes. This allows for an easy view of the

options we chose such as IR and Sonar sensors, Raspberry Pi and Arduino processing, Graphical

User Interface for the user, the nRF24L01 chips for wireless, C C++ and Python for

programming languages, treads for movement, and reversible brushed motors.

Page 10: RDRIV Final Report

The boxes that are highlighted in blue in the morph chart are the design, devices, or software that

were a best fit for the RDRIV.

Means

Functions 1 2 3 4 5

Object sensor IR Sensor Sonar Sensor Radar Sensor …. ….

Digitize Sensor

Signal

Integrated w/

sensor

Integrated w/

controller Separate A/D Analog ….

Process Sensor

Data

PIC micro

controller Raspberry Pi Arduino FPGA

Beagle Bone

Black

User Interface Button/LED Character

Display

Graphic

Display

Touch

screen

Joystick w/

screen

Wireless Data Cellular WiFi Bluetooth nRF24l01 Zigbee

System

Software C C++ C# Python Java

Motor Type Brushless Brushed …. …. ….

Motor Direction Continuous Reversable …. …. ….

Movement Wheels Legs Treads Ball ….

Page 11: RDRIV Final Report

Use of Standards

The “IEEE Standard for Safety Levels with Respect to Human Exposure to

Radiofrequency Electromagnetic Fields, 3kHz to 300 GHz” is a standard that defines the

acceptable levels of radio frequencies. The standard rates the levels that are acceptable for

human exposure and is broken down into ranges of electrical field strength, magnetic field

strength, power density, and average time of exposure. The standard specifies how the different

strengths affect the amount of damage that can be done to people.

This standard influenced our design by limiting the types and strengths of signals that

would be acceptable for our control. By limiting us to a small power level of frequencies we

needed to decide on a device that allowed for the 30 ft. minimum range and that didn’t require

licensing. With choosing the nRF24L01+ we were able to get both the overall range we needed

and the ability to have a license free device in an acceptable ranges.These chips are designed to

be ultra-low power which keeps the band well below the acceptable power density of 10

mW/cm2. The nRF24l01+ chip also uses the ISM (Industrial, Scientific and Medical) band

which allows for worldwide license-free band operation.

Link to standard documentation:

http://www.etsist.upm.es/estaticos/catedra-

coitt/web_salud_medioamb/normativas/ieee/C95.1.pdf

Page 12: RDRIV Final Report

Design Overview

Parts List:

The parts list above shows the parts used, the cost of the parts, and links to sites for

purchase. The discrete parts consisted primarily of the 16 gauge connector wires and screws used

to wire and screw the robot together.

Page 13: RDRIV Final Report

Transparent Box Diagram

Page 14: RDRIV Final Report

Transparent Box description:

The transparent box on the previous page shows the input and output of the robot that

was used to clearly see on a higher level what the functionality of the robot was from step to

step. This allows for a clear path to be seen on what should be happening inside the code and

what actions should be performed based on what input.

Circuit Diagram description:

The circuit diagrams on the next page show how the components on the robot are

powered and the pin layouts for the Raspberry Pi. It is very important to get the pinouts on the

Raspberry Pi correct as the code for the Raspberry Pi is GPIO pin specific and will not work

properly if not wired as seen in the diagram. It is also important to point out that the nRF24L01

chips do not run on 5V, but on 3V supplied from the Raspberry Pi and Arduino. If these chips

are hooked up to 5V it may damage chips and cause unpredictable results.

Page 15: RDRIV Final Report

Robot Circuit Diagram:

Page 16: RDRIV Final Report

Laptop Communication Circuit Diagram:

Page 17: RDRIV Final Report

The 3D printed components were designed used Solid Edge. There were

twenty-three unique parts designed. Each part was created to be easily printed and

assembled. Multiples of multiple components were needed, resulting to a total of

126 printed components in the final build. The picture below shows how some of

the components from the body fit together. You can see that each part is designed

to slide into the other components. All of the parts were designed for easy

assembly in this way

Refer to the appendix for the mechanical drawings of all 3D printed components use, as

well as the final assembly.

Page 18: RDRIV Final Report

Test Plan, Cascade Matrix, and Project Limitation

Test Protocols

I/O Protocol 1.0

Structural Stability 2.0

RF Communication 3.0

Graphical User Interface (G.U.I.) 4.0

1. I/O Protocol

1.1Successful communication with sonar sensor

a. Equipment Needed

Sonar Sensor

Raspberry Pi B+

Power supply

Monitor

Measuring tape

Moveable object

b. Setup

1. Wire sonar sensors GPIO pins Trigger and Echo to the designated GPIO

positions on the Raspberry Pi.

2. Create voltage divider between Echo pin and GND pin on the sonar sensor in

order to achieve 3.3V

3. Set object in front of sonar sensor

4. Run code to activate solely the sonar sensor.

c. Procedure

1. Start with object in front of the sonar sensor

2. Run code

3. Record the calculated distance data compared to the measured distance

a. If calculated distance is more than 5 cm off (test fails)

4. Move object the correct incremental distance away from the sonar sensor seen in the table

below.

d. Table

Measured distance Calculated distance from sensor

2 cm

10 cm

20 cm

50 cm

100 cm

200 cm

400 cm

e. Success Criteria

1. The calculated distance for the sensor should be within about 2-5 (cm) of the

actual distance.

2. Should accurately measure up to 4 m

Page 19: RDRIV Final Report

1.2 Successful communication with the IR sensors

a. Equipment Needed

IR Sensor

Raspberry Pi B+

Power supply

Monitor

Measuring tape

Moveable object

b. Setup

1. Wire the IR sensor to the ADC correctly, diagram given in schematic

2. Wire ADC properly to the Raspberry Pi

3. Set object in front of the IR sensor.

4. Run code solely for the IR sensor

c. Procedure

1. Start with object in front of the left IR sensor

2. Run code

3. Record the calculated distance data compared to the measured distance

a. If calculated distance is more than 5 cm off (test fails)

4. Move object the correct incremental distance away from the IR sensor seen in

the table below.

5. Repeat steps 1-4 for the right IR sensor

d. Table

Measured distance Calculated distance from sensor

0 cm

10 cm

20 cm

40 cm

80 cm

e. Success Criteria 1. The calculated distance for the sensor should be within about 2-5 (cm) of the

actual distance.

2. Should accurately measure up to 80 cm

Page 20: RDRIV Final Report

1.3 Successful communication with the motors

a. Equipment Needed

Stepper motors

Raspberry Pi B+

Power supply

Motor shield L298N

Monitor

b. Setup

1. Properly setup the motor shield

2. Wire motors GPIO to Raspberry Pi and power to the motor shield

3. Run code solely for the motors

c. Procedure

1. Run code for the right motor

a. Run motor forwards for 5 seconds

b. Run motor backwards for 5 seconds

2. Run code for right motor and slowly increase speed

a. Slowly increase speed from 0% 100% in 5 seconds

b. Slowly decrease speed from 100% 0% in 5 seconds

3. Repeat steps 1 & 2 for the left motor

d. Success Criteria

1. Motors show difference in full on or full stop verse slowly increasing or

decreasing speed over 5 seconds.

Page 21: RDRIV Final Report

1.4 Successful communication between motors, IR sensors, and sonar sensor

a. Equipment Needed

IR sensors

Sonar sensor

Movable object

Measuring tape

Stepper motors

Raspberry Pi B+

Power supply

Motor shield L298N

Monitor

b. Setup (Via electrical diagram)

1. Properly setup the motor shield

2. Wire motors GPIO to Raspberry Pi and power to the motor shield

3. Wire the IR sensor to the ADC correctly, diagram given in schematic

4. Wire ADC properly to the Raspberry Pi

5. Set object in front of the IR sensor.

6. Wire sonar sensors GPIO pins Trigger and Echo to the designated GPIO

positions on the Raspberry Pi.

7. Create voltage divider between Echo pin and GND pin on the sonar sensor in

order to achieve 3.3V

8. Set a different object a in front of sonar sensor at a different distance than the

IR

9. Load code to run all devices at the same time.

c. Procedure

1. Once code is initialized let all of the motors run forward for three seconds

2. Then the motors will begin to roll backwards for 3 seconds.

3. Let the right side of motors roll forward and the left side motors roll

backwards for three seconds indicating a right turn

4. Let the left side of motors roll forward and the right side motors roll

backwards for three seconds indicating a left turn.

5. Once the motors are running the left sonar sensor will then start collecting

data all within a one second interval, it will take about ten different readings

in an array, and then perform an average of the sum to give you the most

accurate reading.

a. Then move the object further away to the distances specified in the table

below

6. Repeat step 5 for the right sonar sensor.

7. While the motors are running, move an object in front of the IR sensor and

move it between the different zones.

a. Move object between zero to twenty (cm) away. This should all be within

the first zone

b. The second zone will consist of twenty to forty (cm) away

c. The third zone will be from forty (cm) and on

Page 22: RDRIV Final Report

d. Table

Zone(#) IR Confirmed

Zone

Readings

Sonar

Measured

Distance

(L) Sonar Calculated

Distance

(R) Sonar Calculated

Distance

Zone #1

0-20 cm

20 cm

Zone #2

20-40 cm

50 cm

Zone #1

40+ cm

100 cm

200 cm

400 cm

e. Success Criteria

1. Motors run along with sensor data displaying simultaneously.

a. Sensor data comes out accurate from these readings

Page 23: RDRIV Final Report

1.5 Successful communication between Raspberry Pi and Camera

a. Equipment Needed

Raspberry Pi Camera

Raspberry Pi B+

Power supply

Monitor

Wi-Pi Dongle

b. Setup 1. Plug Raspberry Pi Camera into the Raspberry Pi camera slot

2. Install latest software for the camera

3. Stream to web browser

c. Procedure 1. Once the Raspberry Pi is activated the Raspberry Pi camera stream should run

in the background.

2. On the device controlling the robot, type in the IP of the Raspberry Pi.

3. Once connected to the live video stream of the robot, adjust the video

accordingly.

d. Success Criteria 1. The video stream is connected correctly through the web browser and has low

latency.

2. Structural Stability

Page 24: RDRIV Final Report

2.1 3D Printing Accuracy

a. Equipment Needed

Printrbot 3D printer

PLA printing filament

STL files of components

b. Setup

1. Load filament into printer

2. Connect the printer to the computer

3. Open Repetier Host

4. Load STL Program into Repetier Host

5. Set splicer settings to print components with a 3mm brim, 3 layer shell, 0.4

layer height, and 15% rectilinear infill

a. For the cross bars and boxes use a 10% rectilinear infill to reduce weight

c. Procedure

1. Instruct program to begin print

2. Observe the part to ensure there is no warping, broken layers, or any physical

flaws within the part that could damage the structural stability of the part.

3. If the part has holes for hardware, test if the hardware fits properly into the

holes

4. If the part needs to fit with another piece ensure the pieces have enough

tolerance to fit together.

5. If there is an error in the print repeat steps 1 through 4. If the top faces of the

print are not staple increase the infill level. If issues arise from the lack of

preciseness of the component decrease the layer height.

6. If the part is not sized correctly to fit with other components or accommodate

the hardware modify the source file accordingly and re-save as an STL file and

begin again at the Setup.

d. Success Criteria

1. Printed part has little to no errors in print

2. Component did not warp during printing

3. Components fit together where required and hardware fits into proper holes

3. RF Communication

Page 25: RDRIV Final Report

3.1 nRF Communication Between Devices

a. Equipment Needed

Raspberry Pi B+

Arduino Nano

nRF24L01+ wireless chip

Computer Monitor

Computer Mouse

Computer Keyboard

b. Setup

1. Turn on computer

2. Load Arduino compiler program

3. Load program into Arduino

4. Wire nRF24L01+ to Arduino

5. Hook computer keyboard and mouse to Raspberry Pi B+

6. Wire nRF24L01+ to Raspberry Pi B+

7. Turn on Raspberry Pi B+

8. Load program on Raspberry Pi B+

9. Run program on Raspberry Pi B+ in receiving mode

10. Run program on Arduino for transmitting mode

c. Procedure

1. Run program on Raspberry Pi B+ in receiving mode

2. Run program on Arduino for transmitting mode

3. Send command signal through Arduino

4. Watch for signal on Raspberry Pi B+

5. Watch for returned signal from Raspberry Pi B+ on Arduino

d. Success Criteria

1. Signal sent from Arduino received by the Raspberry Pi B+

2. Signal sent from Raspberry Pi B+ received by the Arduino

3. Signals received in same format as sent

Page 26: RDRIV Final Report

4. Graphical User Interface (G.U.I.)

4.1 G.U.I. Operation

a. Software Needed 1. Qt (Software installed on the user’s device of choice)

b. Setup 1. Make sure everything is wired according to the schematic.

2. Run and build code within Qt to execute GUI to control the robot.

3. Make sure Raspberry Pi code is running in the background.

c. Procedure 1. Press the Forward key on the GUI and make sure all the motors run forwards.

2. Press the Backward key on the GUI and make sure all the motors run

backwards.

3. Press the Left key on the GUI and make sure the left motors roll forward and

the right motors roll backwards.

4. Press the Right key on the GUI and make sure the right motors roll forward

and the left motors roll backwards.

5. Make sure that every time a directional key is pressed all of the sensors are

updated with the latest distance or zone measurements.

d. Success Criteria 1. Motors run in the correct direction and all of the sensor data is updated

correctly.

Page 27: RDRIV Final Report

5. Inversion Test

5.1 Successful inversion of the RDRIV

a. Equipment Needed

RDRIV

Arduino Nano

nRF chip

User’s device with G.U.I.

b. Setup

1. Turn on RDRIV

2. Load all code into the Raspberry Pi

3. Load GUI onto user’s device

c. Procedure

1. Once the RDRIV is operational drive it towards a wall.

2. Approach wall at a slow pace and attempt to climb wall and flip over.

3. Approach wall at a moderate pace and attempt to climb wall and flip over.

4. Approach wall at a fast pace and attempt to climb wall and flip over.

d. Table

Speed approaching wall Did it successfully flip over

Slow

Medium

Fast

e. Success Criteria 1. The RDRIV successfully flips over and nothing is damaged in the process.

Page 28: RDRIV Final Report

Cascade Matrix

FUNCTION Requirement

Specification

Design

Verified

Test

Protocol

Device

Validated? Signature

I/O Functionality

Test Motors with

sensors on the

Raspberry Pi side of

the device

Yes

NT 3/21/2015

Document

1.1-1.5 Yes

3D printing

&

Structure stability

Test to make sure

parts are properly

printed and designed

to fit together

correctly

Yes

ML 3/27/2015

Document

2.1 Yes

RF Communication

Integrate

communication

between Arduino and

Raspberry Pi over the

nRF24l01+ chip

Yes

TB 3/15/2015

Document

3.1 Yes

G.U.I.

Creating a G.U.I. to

provide functional

control over the

RDRIV while

enabling live camera

feed

Yes

AJ 4/3/2015

Document

4.1 Yes

Inversion Test

Run the RDRIV into

a wall and see if it is

able to flip over No

Document

5.1 No

Project Limitations

Page 29: RDRIV Final Report

Video for the robot is limited to having Wi-Fi access as the nRF24L01+ wireless chips do

not allow for adequate bandwidth to transmit video. This stipulation could be worked around in

future renditions to enable a RF or direct signal feed for the video that would not require Wi-Fi

access but rather have its own receiver for the video. This option was not a plausible solution for

our rendition as we were trying to keep it as low cost as possible.

The sensors play a limited role in the function of the RDRIV due to the fact that they can

only sense the proximity of a single point and provide an extremely limited view of the RDRIV’s

surroundings. In addition to this, the sonar sensors have a limitation to their accuracy since the

Raspberry Pi does not have a real time operating system. The absence of a real time operating

system allows the Raspberry Pi to place high priority functions ahead of lesser processes. While

in general this may not be an issue, when the sonar sensor is waiting for an echo, an interruption

can cause the sensor to miss the echo and start an infinite loop of waiting, or return a value much

smaller than what would be accurate. In order to combat this, multiple readings from each

sensor are taken and the maximum returned distance is sent back to the user. Taking several

readings requires placing a cap on the maximum distance the sensor can see before sending

another pulse. In future renditions of the RDRIV, small microprocessors devoted to a single

sensor system could be added to reduce any peripheral processes that would corrupt the returned

data.

When designing the body and treads of the robot we did not account for the weight of the

hardware needed to assemble the treads. These added a lot of unwanted weight and made it

necessary for us to use four motors, instead of our original plan to use two. Even with the two

additional motors we did not have enough torque to achieve the full performance that we desired.

The motors were not strong enough to move over objects and we were not able to test if we

could flip the robot over using a wall, like we originally designed our body to do. Fortunately, all

that is needed to solve these limitations is larger motors.

Page 30: RDRIV Final Report

Social Responsibility

This project was created with the intention of helping preserve human life. The intention

of our robot was to allow humans to avoid potentially harmful environments. The importance of

this was a constant consideration for every member of this group. However, the fear that our

robot could be used to create a false sense of security for first response teams by poorly

analyzing a situation was a larger motivator. We ensured that our robot was robust, accurate, and

easy to use to ensure that first responders entering an environment could be sure that they are

safe. We were able to ensure this by using various types of sensors to allow for accurate

feedback of objects in the robots path. The robot is easy to use, with all of the information for the

user right next to the buttons, allowing the operator to see the feedback clearly before making an

decisions. A strong type of 3D printer material was used to allow for a robust design that will not

fall apart during application.

A major criterion for our project was to design a robot that could be built for a reasonable

cost and could be easily maintained. By creating a first response robot that costs 0.5% of the cost

of other first response robots currently on the market we are making this important technology

more easily accessible to those who need it. Our modular design and low cost components also

allows the user to replace individual components on the robot, allowing for low cost

maintenance.

Page 31: RDRIV Final Report

Conclusion

The RDRIV was a success once the requirements were scaled down to more reasonable

and achievable goals. All of the top priority goals were achieved within the given budget

constraint of $300. The final project was an almost completely 3D printed robot capable of

moving using wireless communication. The robot incorporated a sensor array that could be used

for object detection in the path of the robot in conjunction with a camera that streams wirelessly

over Wi-Fi.

A major issue encountered was the wireless transmission over the nRF chips. A lot of

research had been put into learning how RF communications work and how to use the SPI bus

from the Raspberry Pi to the Arduino Nano. For the coding it was not initially understand why

packages had to be sent as a character array of 32-bytes. After overcoming this obstacle the rest

of the project was easy and more necessary tests could be performed.

One of the most critical tests performed was getting the robot to move forwards,

backwards, left, and right. The first attempt at powering the robot was successful in moving it

forwards and backwards, but not at making it turn. Initially two motors were used and did not

have the necessary torque to rotate the robot in different directions. After a critical design

overhaul, an additional two motors were added to the design. It took away from the overall

amperage being distributed across the motor, making the robot slower, yet powerful enough to

make the RDRIV able to rotate left or right.

Once mobility was accomplished it was time for the next testing phase: having the sensor

data come over and be displayed through the G.U.I. Knowing that the sensor data would come

over in a string and each value was separated by a period, from this a function was found within

Qt that lets the data to be parsed by a certain character, such as a period. Once this was

incorporated each value of the string was able to be pushed to its proper place within the G.U.I.

The video feed was the only problem that could not be overcome. A temporary solution

was created by sending the video feed of the Raspberry Pi Camera over a Wi-Fi connection, then

having it run on a web browser using its own IP address. The original goal was to send the video

over the nRF chip, but the bandwidth of the chip was much lower than expected. In hindsight,

creating the code for formatting the video and receiving and organizing the array of pixels would

be a monumental task alone, one that most likely could not have been accomplished in the time

available.

Over all the RDRIV was a success. It is able to move omni-directional while maintaining

a constant rate of speed. The goal was to meet a fifteen minute window of operational time. Not

only was this requirement met, it was surpassed with over 3 hours of functionality observed. In

addition to exceeding the battery expectations, the RDRIV has an operational range of over

100ft, 70ft longer than what was requested by the Air Force Research Lab. A cheaper and more

modular competitor to the reconnaissance vehicles currently on the market was successfully

created.

Page 32: RDRIV Final Report

Appendix:

Design History File Review:

Page 33: RDRIV Final Report
Page 34: RDRIV Final Report

Arduino Code for transmitting to Raspberry Pi B + #include <SPI.h>

#include "nRF24L01.h"

#include "RF24.h"

#include "printf.h"

//

// Hardware configuration

//

// Set up nRF24L01 radio on SPI bus plus pins 9 & 10

RF24 radio(9,10);

byte addresses[][6] = {"1Node","2Node"};

//

// Setup

//

void setup()

{

Serial.begin(115200);

printf_begin();

//printf("\n\rRF24/examples/led_remote/\n\r");

// Setup and configure rf radio

radio.begin(); // Start up the radio

radio.setAutoAck(1); // Ensure autoACK is enabled

radio.setRetries(15,15); // Max delay between retries & number of retries

radio.openWritingPipe(addresses[0]); //[0] makes it the transmitter 1 the reciever

radio.openReadingPipe(1,addresses[1]); //[1] makes it the transmitter 1 the reciever

radio.startListening(); // Start listening

//radio.printDetails(); // Dump the configuration of the rf unit for debugging

}

char inData[32]; // Allocate some space for the string

char inChar; // Where to store the character read

byte index = 0; // Index into array; where to store the character

void loop()

{

while(Serial.available() > 0) // Don't read unless

// there you know there is data

{ //if statement to populate string to be fed through RF

if(index < 31) // One less than the size of the array

Page 35: RDRIV Final Report

{

inChar = Serial.read(); // Read a character

inData[index] = inChar; // Store it

index++; // Increment where to write next

inData[index] = '\0'; // Null terminate the string

//if statement to send the string through RF chip to PI once a period is seen at end of string

if(inChar == '.')

{

radio.stopListening(); // First, stop listening so we can talk.

//printf("Now sending \n\r");

//fail loop if the radio doesn't write to RF

if (!radio.write( &inData, index )){ printf("failed.\n\r"); }

radio.startListening(); // Now, continue listening

unsigned long started_waiting_at = micros(); // Set up a timeout period, get the current

microseconds

boolean timeout = false; // Set up a variable to indicate if a response was received

or not

while ( ! radio.available() ){ // While nothing is received

if (micros() - started_waiting_at > 2000000 ){ // If waited longer than 2s, indicate timeout and

exit while loop

timeout = true;

break;

}

}

if ( timeout ){ // Describe the results

//printf("Failed, response timed out.\n\r");

}else{

char sensor_data[20]={0}; // Grab the response, compare, and send to debugging

spew

radio.read(sensor_data, 20);

// Spew it

printf("%s, \n",sensor_data);

}

// Try again 1s later

index = 0; //clear index so you can start the process fresh with the string other wise it adds to the

end of the current string

delay(1000);

Page 36: RDRIV Final Report

}}}}

QT Header File: “dialog.h” #ifndef DIALOG_H

#define DIALOG_H

#include <QSerialPort>

#include <QDialog>

namespace Ui {

class Dialog;

}

class Dialog : public QDialog

{

Q_OBJECT

public:

explicit Dialog(QWidget *parent = 0);

~Dialog();

private slots:

void on_Forwards_pressed();

void on_Backwards_pressed();

void on_Left_pressed();

void on_Right_pressed();

void on_Stop_pressed();

void updateRDRIV(QString);

void readSerial();

void updateRightSonarSensorLCD(const QString);

void updateLeftSonarSensorLCD(const QString);

void updateFrontIRSensorLCD(const QString);

void updateBackIRSensorLCD(const QString);

private:

Ui::Dialog *ui;

QSerialPort *arduino;

Page 37: RDRIV Final Report

static const quint16 arduino_nano_vendor_id = 1027;

static const quint16 arduino_nano_product_id = 24577;

QString arduino_port_name;

bool arduino_is_available;

QByteArray serialData;

QString serialBuffer;

QString serialBuff1;

QString serialBuff2;

QString serialBuff3;

QString serialBuff4;

};

#endif // DIALOG_H

QT Function File: “dialog.cpp” #include "dialog.h"

#include "ui_dialog.h"

#include <QSerialPort>

#include <QSerialPortInfo>

#include <string>

#include <QCoreApplication>

#include <QThread>

#include <QDebug>

#include <QtWidgets>

#include <QTimer>

#include <QObject>

Dialog::Dialog(QWidget *parent) :

QDialog(parent),

ui(new Ui::Dialog)

{

ui->setupUi(this);

ui->tempRightSonarSensorLcdNumber->display("---");

ui->tempLeftSonarSensorLcdNumber->display("---");

ui->tempFrontIRSensorLcdNumber->display("-");

ui->tempBackIRSensorLcdNumber_2->display("-");

arduino = new QSerialPort(this);

serialBuffer = "";

bool arduino_is_available = false;

QString arduino_port_name;

/***********DEBUG QSerialPortINFO for serial port info, available ports,

vendor ID, and Product ID******/

/*

qDebug() << "Number of availabl e ports: " <<

QSerialPortInfo::availablePorts().length();

Page 38: RDRIV Final Report

foreach(const QSerialPortInfo &serialPortInfo,

QSerialPortInfo::availablePorts() ){

qDebug() << "Has vendor ID: " <<

serialPortInfo.hasVendorIdentifier();

if(serialPortInfo.hasVendorIdentifier()){

qDebug() << "Vendor ID: " << serialPortInfo.vendorIdentifier();

}

qDebug() << "Has product ID: " <<

serialPortInfo.hasProductIdentifier();

if(serialPortInfo.hasProductIdentifier()){

qDebug() << "Product ID: " << serialPortInfo.productIdentifier();

}

}*/

/****************************************************************************

*****************************/

//arduino_port_name = "";

foreach(const QSerialPortInfo &serialPortInfo,

QSerialPortInfo::availablePorts()){

if(serialPortInfo.hasVendorIdentifier()

&&serialPortInfo.hasProductIdentifier()){

if(serialPortInfo.vendorIdentifier() == arduino_nano_vendor_id){

if(serialPortInfo.productIdentifier() ==

arduino_nano_product_id){

arduino_port_name = serialPortInfo.portName();

arduino_is_available = true;

}

}

}

}

if(arduino_is_available){

//open and configure the serial port

arduino->setPortName(arduino_port_name);

arduino->open(QSerialPort::ReadWrite);

arduino->setBaudRate(QSerialPort::Baud115200);

arduino->setDataBits(QSerialPort::Data8);

arduino->setParity(QSerialPort::NoParity);

arduino->setStopBits(QSerialPort::OneAndHalfStop);

arduino->setFlowControl(QSerialPort::NoFlowControl);

QObject::connect(arduino, SIGNAL(readyRead()), this,

SLOT(readSerial())); //

}else{

//give error message if not available

qDebug() << "Couldnt find the correct por for the arduino. \n";

Page 39: RDRIV Final Report

QMessageBox::information(this, "Serial Port Error", "Couldn't find

the arduino!");

}

}

Dialog::~Dialog()

{

if(arduino->isOpen()){

arduino->close(); //Close serial port if it is open

}

delete ui;

}

void Dialog::updateRightSonarSensorLCD(const QString sensor_reading)

{

ui->tempRightSonarSensorLcdNumber->display(sensor_reading);

}

void Dialog::updateLeftSonarSensorLCD(const QString sensor_reading)

{

ui->tempLeftSonarSensorLcdNumber->display(sensor_reading);

}

void Dialog::updateFrontIRSensorLCD(const QString sensor_reading)

{

ui->tempFrontIRSensorLcdNumber->display(sensor_reading);

}

void Dialog::updateBackIRSensorLCD(const QString sensor_reading)

{

ui->tempBackIRSensorLcdNumber_2->display(sensor_reading);

}

/******************************DEBUG read

serial***********************************/

void Dialog::readSerial()

{

//qDebug() << "serialport works";

serialData = arduino->readAll();

serialBuffer = QString::fromStdString(serialData.toStdString());

qDebug() << serialBuffer;

QStringList list1 = serialBuffer.split(".", QString::SkipEmptyParts);

QString serialBuff1 = list1[0];

QString serialBuff2 = list1[1];

QString serialBuff3 = list1[2];

QString serialBuff4 = list1[3];

Page 40: RDRIV Final Report

Dialog::updateRightSonarSensorLCD(serialBuff1);

Dialog::updateLeftSonarSensorLCD(serialBuff2);

Dialog::updateFrontIRSensorLCD(serialBuff3);

Dialog::updateBackIRSensorLCD(serialBuff4);

}

/****************************************************************************

******/

void Dialog::on_Forwards_pressed()

{

QString forward = "forward.";

Dialog::updateRDRIV(QString(forward));

}

void Dialog::on_Backwards_pressed()

{

QString back = "back.";

Dialog::updateRDRIV(QString(back));

}

void Dialog::on_Left_pressed()

{

QString left = "left.";

Dialog::updateRDRIV(QString(left));

}

void Dialog::on_Right_pressed()

{

QString right = "right.";

Dialog::updateRDRIV(QString(right));

}

void Dialog::on_Stop_pressed()

{

QString stop = "stop.";

Dialog::updateRDRIV(QString(stop));

}

void Dialog::updateRDRIV(QString command)

{

if(arduino->isWritable()){

arduino->write(command.toStdString().c_str());

qDebug()<< "Sending";

}else{

qDebug()<< "Couldn't write to serial.";

}

}

QT GUI code: “main.cpp” #include "dialog.h"

Page 41: RDRIV Final Report

#include <QApplication>

int main(int argc, char *argv[])

{

QApplication a(argc, argv);

Dialog w;

w.show();

w.setFixedSize(670,270);

w.setWindowTitle("RDRIV GUI");

return a.exec();

}

Raspberry Pi Code Main C++ file handles NRF communication and UDP server

#include <cstdlib>

#include <iostream>

#include <sstream>

#include <string>

#include <RF24/RF24.h>

#include <stdlib.h>

#include <stdio.h>

#include <boost/array.hpp>

#include <boost/asio.hpp>

using namespace std;

using boost::asio::ip::udp;

//

// Hardware configuration

//

// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz

RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ);

// Radio pipe addresses for the 2 nodes to communicate.

const uint8_t pipes[][6] = {"1Node","2Node"};

//const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL };

int main(int argc, char** argv){

//bool role_ping_out = true, role_pong_back = false;

Page 42: RDRIV Final Report

//bool role = role_pong_back;

// Print preamble:

//printf("RF24/examples/pingtest/\n");

// Setup and configure rf radio

radio.begin();

// optionally, increase the delay between retries & # of retries

radio.setRetries(15,15);

// Dump the configuration of the rf unit for debugging

radio.printDetails();

/***********************************/

// This simple sketch opens two pipes for these two nodes to communicate

// back and forth.

radio.openWritingPipe(pipes[1]);

radio.openReadingPipe(1,pipes[0]);

radio.startListening();

//UDP initializations

boost::asio::io_service io_service;

udp::socket socket(io_service, udp::endpoint(udp::v4(), 5005));

boost::array<char, 128> sensor_buf;

udp::endpoint remote_endpoint;

// forever loop

while (1)

{

//

// Pong back role. Receive each packet, dump it out, and send it back

//

// if there is data ready

//printf("Check available...\n");

if ( radio.available() )

{

Page 43: RDRIV Final Report

char instruct[12]= {0}; // ------------------------------------- (EDITED LINE)

// Fetch the payload, and see if this was the last one.

radio.read( instruct, 12 );

radio.stopListening();

// UDP Server communication with pMain.py

// try

// {

remote_endpoint.port(5006);

boost::system::error_code error;

std::string instructs = std::string(instruct, 12);

std::cout << instructs << std::endl;

if(error && error != boost::asio::error::message_size)

throw boost::system::system_error(error);

boost::system::error_code ignored_error;

socket.send_to(boost::asio::buffer(instructs), remote_endpoint, 0, ignored_error);

//send instruct to pMain.py

remote_endpoint.port(5005);

size_t len = socket.receive_from(boost::asio::buffer(sensor_buf),

remote_endpoint, 0, error); //recieve sensor packet from pMain.py

std::string sensor = std::string(sensor_buf.data(), len);

std::cout << sensor << std::endl; // alternate-->

std::cout.write(instruct_buf.data(), len);

// write back sensor packet

radio.write(sensor_buf.data(), len );

// }

// catch (std::exception& e)

// {

// std::cerr << e.what() << std::endl;

// }

// Now, resume listening so we catch the next packets.

Page 44: RDRIV Final Report

radio.startListening();

// Spew it

printf("%s \n", instruct);

delay(925); //Delay after payload responded to, minimize RPi CPU time

}// if radio available

} // forever loop

return 0;

}

Main Python code handles GPIO manipulation and UDP client

import RPi.GPIO as GPIO

import time

import motor

import sensor

import socket

#UDP Setup

UDP_IP = "127.0.0.1"

UDP_PORTIN = 5006

UDP_PORTOUT = 5005

#GPIO Setup

GPIO.setmode(GPIO.BOARD)

Min1 = 35 #Motor Enable Pins

Min2 = 37

Min3 = 32

Min4 = 36

TRIG1 = 38 #Sonar Trigger Pin

ECHO1 = 40 #Sonar Echo Pin

TRIG2 = 16 #Sonar Trigger Pin

ECHO2 = 18 #Sonar Echo Pin

SPICLK = 7 #ADC_CLK

SPIMISO = 29 #ADC_DOUT

SPIMOSI = 31 #ADC_DIN

SPICS = 33 #ADC_Chip_Select

Page 45: RDRIV Final Report

IRS0 = 0

IRS1 = 1

GPIO.setup(Min1, GPIO.OUT)

GPIO.setup(Min2, GPIO.OUT)

GPIO.setup(Min3, GPIO.OUT)

GPIO.setup(Min4, GPIO.OUT)

GPIO.setup(TRIG1, GPIO.OUT)

GPIO.setup(TRIG2, GPIO.OUT)

GPIO.output(TRIG1, 0)

GPIO.output(TRIG2, 0)

GPIO.setup(ECHO1, GPIO.IN)

GPIO.setup(ECHO2, GPIO.IN)

GPIO.setup(SPIMOSI, GPIO.OUT)

GPIO.setup(SPIMISO, GPIO.IN)

GPIO.setup(SPICLK, GPIO.OUT)

GPIO.setup(SPICS, GPIO.OUT)

time.sleep(0.1)

#Motor function definitions

#(Min1,Min2,Min3,Min4)

Forward = motor.DriveForward

Reverse = motor.DriveBackward

Right = motor.TurnRight

Left = motor.TurnLeft

SRight = motor.SoftRight

SLeft = motor.SoftLeft

Stop = motor.StopMotor

#Sensor function definitions

#(TRIG,ECHO)

#(IRS0, SPICLK, SPIMOSI, SPIMISO, SPICS)

SonarSense = sensor.SonarSense

IRSense = sensor.IRSense

#UDP initialization

sock = socket.socket(socket.AF_INET, #Internet

socket.SOCK_DGRAM) #UDP

sock.bind((UDP_IP, UDP_PORTIN))

#Forever loop

dataprev = 'stop'

Page 46: RDRIV Final Report

idist1 = '0'

idist2 = '0'

sensor = "0000"

moving = False

while True:

data, addr = sock.recvfrom(1024) #buffer size is 1024 bytes

print "received message: ", data #print recieved data package

sdist1 = SonarSense(TRIG1,ECHO1) #read sensor data

sdist2 = SonarSense(TRIG2,ECHO2) #read sensor data

#if (moving == True): #read IR sensor if RDRIV is moving

idist1 = IRSense(IRS0, SPICLK, SPIMOSI, SPIMISO, SPICS) #read front IR sensor

#idist2 = IRSense(IRS1, SPICLK, SPIMOSI, SPIMISO, SPICS) #read rear IR sensor

sensor = str(sdist1) + ',' + str(sdist2) + ',' + str(idist1) #+ str(idist2)

sock.sendto(sensor, (UDP_IP, UDP_PORTOUT)) #send sensor string

if (data != dataprev): #---------------- #if new instruction is different from previous

dataprev = data

if (data == "forward."):

Forward(Min1,Min2,Min3,Min4)

print 'moving Forward'

moving = True

elif (data == "back."):

Reverse(Min1,Min2,Min3,Min4)

print 'moving Backwards'

moving = True

elif (data == "right."):

Right(Min1,Min2,Min3,Min4)

print 'moving Right'

moving = True

elif (data == "left."):

Left(Min1,Min2,Min3,Min4)

print 'moving Left'

moving = True

else:

Stop(Min1,Min2,Min3,Min4)

print 'stopped'

moving = False

print sensor

Page 47: RDRIV Final Report

GPIO.cleanup()

RF24 library

#include "./RF24_config.h"

#include "./RF24.h"

#include "./nRF24L01.h"

/****************************************************************************/

uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len)

{

uint8_t status;

uint8_t * prx = spi_rxbuff;

uint8_t * ptx = spi_txbuff;

uint8_t size = len + 1; // Add register value to transmit buffer

*ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) );

while (len--){

*ptx++ = NOP ; // Dummy operation, just for reading

}

bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size);

status = *prx++; // status is 1st byte of receive buffer

// decrement before to skip status byte

while ( --size ){

*buf++ = *prx++;

}

return status;

}

/****************************************************************************/

uint8_t RF24::read_register(uint8_t reg)

{

uint8_t result;

uint8_t * prx = spi_rxbuff;

uint8_t * ptx = spi_txbuff;

*ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) );

Page 48: RDRIV Final Report

*ptx++ = NOP ; // Dummy operation, just for reading

bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2);

result = *++prx; // result is 2nd byte of receive buffer

return result;

}

/****************************************************************************/

uint8_t RF24::write_register(uint8_t reg, uint8_t value)

{

uint8_t status;

uint8_t * prx = spi_rxbuff;

uint8_t * ptx = spi_txbuff;

*ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) );

*ptx = value ;

bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2);

status = *prx++; // status is 1st byte of receive buffer

if (debug)

printf("write_register(%02x,%02x)\r\n",reg,value);

return status;

}

/****************************************************************************/

uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len)

{

uint8_t status;

uint8_t * prx = spi_rxbuff;

uint8_t * ptx = spi_txbuff;

uint8_t size = len + 1; // Add register value to transmit buffer

*ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) );

while ( len-- )

Page 49: RDRIV Final Report

*ptx++ = *buf++;

bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size);

status = *prx; // status is 1st byte of receive buffer

return status;

}

/****************************************************************************/

uint8_t RF24::write_payload(const void* buf, uint8_t len, const uint8_t writeType)

{

uint8_t status;

uint8_t * prx = spi_rxbuff;

uint8_t * ptx = spi_txbuff;

uint8_t size ;

const uint8_t* current = reinterpret_cast<const uint8_t*>(buf);

uint8_t data_len = min(len,payload_size);

uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len;

size = data_len + blank_len + 1 ; // Add register value to transmit buffer

if (debug)

printf("[Writing %u bytes %u blanks]",data_len,blank_len);

*ptx++ = W_TX_PAYLOAD;

while ( data_len-- )

*ptx++ = *current++;

while ( blank_len-- )

*ptx++ = 0;

bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size);

status = *prx; // status is 1st byte of receive buffer

return status;

}

Page 50: RDRIV Final Report

/****************************************************************************/

uint8_t RF24::read_payload(void* buf, uint8_t len)

{

uint8_t status;

uint8_t * prx = spi_rxbuff;

uint8_t * ptx = spi_txbuff;

uint8_t size ;

uint8_t* current = reinterpret_cast<uint8_t*>(buf);

uint8_t data_len = min(len,payload_size);

uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len;

size = data_len + blank_len + 1; // Add register value to transmit buffer

if (debug)

printf("[Reading %u bytes %u blanks]",data_len,blank_len);

*ptx++ = R_RX_PAYLOAD;

while(size--)

*ptx++ = NOP;

// Size has been lost during while, re affect

size = data_len + blank_len + 1; // Add register value to transmit buffer

bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size);

// 1st byte is status

status = *prx++;

// Decrement before to skip 1st status byte

while ( --size )

*current++ = *prx++;

return status;

}

/****************************************************************************/

uint8_t RF24::flush_rx(void)

Page 51: RDRIV Final Report

{

uint8_t status;

status = bcm2835_spi_transfer( FLUSH_RX );

return status;

}

/****************************************************************************/

uint8_t RF24::flush_tx(void)

{

uint8_t status;

status = bcm2835_spi_transfer( FLUSH_TX );

return status;

}

/****************************************************************************/

uint8_t RF24::get_status(void)

{

return bcm2835_spi_transfer( NOP );

}

/****************************************************************************/

void RF24::print_status(uint8_t status)

{

printf("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n",

status,

(status & _BV(RX_DR))?1:0,

(status & _BV(TX_DS))?1:0,

(status & _BV(MAX_RT))?1:0,

((status >> RX_P_NO) & 0b111),

(status & _BV(TX_FULL))?1:0

);

}

/****************************************************************************/

Page 52: RDRIV Final Report

void RF24::print_observe_tx(uint8_t value)

{

printf("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n",

value,

(value >> PLOS_CNT) & 0b1111,

(value >> ARC_CNT) & 0b1111

);

}

/****************************************************************************/

void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty)

{

char extra_tab = strlen(name) < 8 ? '\t' : 0;

printf("%s\t%c =", name, extra_tab);

while (qty--)

printf(" 0x%02x",read_register(reg++));

printf("\n");

}

/****************************************************************************/

void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty)

{

char extra_tab = strlen(name) < 8 ? '\t' : 0;

printf("%s\t%c =",name,extra_tab);

while (qty--)

{

uint8_t buffer[addr_width];

read_register(reg++,buffer,sizeof buffer);

printf(" 0x");

uint8_t* bufptr = buffer + sizeof buffer;

while( --bufptr >= buffer )

printf("%02x",*bufptr);

}

printf("\r\n");

}

Page 53: RDRIV Final Report

/****************************************************************************/

RF24::RF24(uint8_t _cepin, uint8_t _cspin, uint32_t _spi_speed):

ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed),p_variant(false),

payload_size(32), dynamic_payloads_enabled(false),addr_width(5)//,pipe0_reading_address(0)

{

}

/****************************************************************************/

void RF24::setChannel(uint8_t channel)

{

const uint8_t max_channel = 127;

write_register(RF_CH,min(channel,max_channel));

}

/****************************************************************************/

void RF24::setPayloadSize(uint8_t size)

{

const uint8_t max_payload_size = 32;

payload_size = min(size,max_payload_size);

}

/****************************************************************************/

uint8_t RF24::getPayloadSize(void)

{

return payload_size;

}

/****************************************************************************/

static const char rf24_datarate_e_str_0[] = "1MBPS";

static const char rf24_datarate_e_str_1[] = "2MBPS";

static const char rf24_datarate_e_str_2[] = "250KBPS";

static const char * const rf24_datarate_e_str_P[] = {

rf24_datarate_e_str_0,

rf24_datarate_e_str_1,

rf24_datarate_e_str_2,

};

static const char rf24_model_e_str_0[] = "nRF24L01";

Page 54: RDRIV Final Report

static const char rf24_model_e_str_1[] = "nRF24L01+";

static const char * const rf24_model_e_str_P[] = {

rf24_model_e_str_0,

rf24_model_e_str_1,

};

static const char rf24_crclength_e_str_0[] = "Disabled";

static const char rf24_crclength_e_str_1[] = "8 bits";

static const char rf24_crclength_e_str_2[] = "16 bits" ;

static const char * const rf24_crclength_e_str_P[] = {

rf24_crclength_e_str_0,

rf24_crclength_e_str_1,

rf24_crclength_e_str_2,

};

static const char rf24_pa_dbm_e_str_0[] = "PA_MIN";

static const char rf24_pa_dbm_e_str_1[] = "PA_LOW";

static const char rf24_pa_dbm_e_str_2[] = "PA_HIGH";

static const char rf24_pa_dbm_e_str_3[] = "PA_MAX";

static const char * const rf24_pa_dbm_e_str_P[] = {

rf24_pa_dbm_e_str_0,

rf24_pa_dbm_e_str_1,

rf24_pa_dbm_e_str_2,

rf24_pa_dbm_e_str_3,

};

static const char rf24_csn_e_str_0[] = "CE0 (PI Hardware Driven)";

static const char rf24_csn_e_str_1[] = "CE1 (PI Hardware Driven)";

static const char rf24_csn_e_str_2[] = "CE2 (PI Hardware Driven)";

static const char rf24_csn_e_str_3[] = "Custom GPIO Software Driven";

static const char * const rf24_csn_e_str_P[] = {

rf24_csn_e_str_0,

rf24_csn_e_str_1,

rf24_csn_e_str_2,

rf24_csn_e_str_3,

};

// Display NRF24L01 details

void RF24::printDetails(void)

{

printf("================ SPI Configuration ================\n" );

if (csn_pin < BCM2835_SPI_CS_NONE )

{

Page 55: RDRIV Final Report

printf("CSN Pin \t = %s\n",rf24_csn_e_str_P[csn_pin]);

}

else

{

printf("CSN Pin \t = Custom GPIO%d%s\n", csn_pin,

csn_pin==RPI_V2_GPIO_P1_26 ? " (CE1) Software Driven" : "" );

}

printf("CE Pin \t = Custom GPIO%d\n", ce_pin );

// SPI Bus Speed

printf("Clock Speed\t = " );

switch (spi_speed)

{

case BCM2835_SPI_SPEED_64MHZ : printf("64 Mhz"); break ;

case BCM2835_SPI_SPEED_32MHZ : printf("32 Mhz"); break ;

case BCM2835_SPI_SPEED_16MHZ : printf("16 Mhz"); break ;

case BCM2835_SPI_SPEED_8MHZ : printf("8 Mhz"); break ;

case BCM2835_SPI_SPEED_4MHZ : printf("4 Mhz"); break ;

case BCM2835_SPI_SPEED_2MHZ : printf("2 Mhz"); break ;

case BCM2835_SPI_SPEED_1MHZ : printf("1 Mhz"); break ;

case BCM2835_SPI_SPEED_512KHZ: printf("512 KHz"); break ;

case BCM2835_SPI_SPEED_256KHZ: printf("256 KHz"); break ;

case BCM2835_SPI_SPEED_128KHZ: printf("128 KHz"); break ;

case BCM2835_SPI_SPEED_64KHZ : printf("64 KHz"); break ;

case BCM2835_SPI_SPEED_32KHZ : printf("32 KHz"); break ;

case BCM2835_SPI_SPEED_16KHZ : printf("16 KHz"); break ;

case BCM2835_SPI_SPEED_8KHZ : printf("8 KHz"); break ;

default : printf("Probably Bad !!!"); break ;

}

printf("\n");

printf("================ NRF Configuration ================\n" );

print_status(get_status());

print_address_register("RX_ADDR_P0-1",RX_ADDR_P0,2);

print_byte_register("RX_ADDR_P2-5",RX_ADDR_P2,4);

print_address_register("TX_ADDR",TX_ADDR);

print_byte_register("RX_PW_P0-6",RX_PW_P0,6);

print_byte_register("EN_AA",EN_AA);

Page 56: RDRIV Final Report

print_byte_register("EN_RXADDR",EN_RXADDR);

print_byte_register("RF_CH",RF_CH);

print_byte_register("RF_SETUP",RF_SETUP);

print_byte_register("CONFIG",CONFIG);

print_byte_register("DYNPD/FEATURE",DYNPD,2);

printf("Data Rate\t = %s\r\n",rf24_datarate_e_str_P[getDataRate()]);

printf("Model\t\t = %s\r\n",rf24_model_e_str_P[isPVariant()]);

printf("CRC Length\t = %s\r\n",rf24_crclength_e_str_P[getCRCLength()]);

printf("PA Power\t = %s\r\n",rf24_pa_dbm_e_str_P[getPALevel()]);

}

/****************************************************************************/

bool RF24::begin(void)

{

debug = false;

#if defined(DEBUG)

debug = true;

#endif

// This initialize the SPI bus with

// csn pin as chip select (custom or not)

// Init BCM2835 chipset for talking with us

if (!bcm2835_init()){

return false;

}

switch(csn_pin){ //Ensure valid hardware CS pin

case 0: break;

case 1: break;

case 8: csn_pin = 0; break;

case 7: csn_pin = 1; break;

default: csn_pin = 0; break;

}

bcm2835_spi_begin();

// used to drive custom I/O to trigger my logic analyser

// bcm2835_gpio_fsel(GPIO_CTRL_PIN , BCM2835_GPIO_FSEL_OUTP);

Page 57: RDRIV Final Report

// start the SPI library:

// Note the NRF24 wants mode 0, MSB first and default to 1 Mbps

bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);

bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);

// Set SPI bus Speed

bcm2835_spi_setClockDivider(spi_speed);

// Choose hardware CSN pin

bcm2835_spi_chipSelect(csn_pin);

// Initialise the CE pin of NRF24 (chip enable) after the CSN pin, so that

// The input mode is not changed if using one of the hardware CE pins

bcm2835_gpio_fsel(ce_pin, BCM2835_GPIO_FSEL_OUTP);

bcm2835_gpio_write(ce_pin, LOW);

// wait 100ms

delay(100);

// Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier

// WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet

// sizes must never be used. See documentation for a more complete explanation.

//printf("write_register(%02X, %02X)\n", SETUP_RETR, (0b0100 << ARD) | (0b1111 << ARC));

setRetries(5,15);

// Determine if this is a p or non-p RF24 module and then

// reset our data rate back to default value. This works

// because a non-P variant won't allow the data rate to

// be set to 250Kbps.

if( setDataRate( RF24_250KBPS ) )

{

p_variant = true ;

}

// Then set the data rate to the slowest (and most reliable) speed supported by all

// hardware.

setDataRate( RF24_1MBPS ) ;

// Initialize CRC and request 2-byte (16bit) CRC

setCRCLength( RF24_CRC_16 ) ;

Page 58: RDRIV Final Report

toggle_features();

write_register(FEATURE,0 );

write_register(DYNPD,0);

// Reset current status

// Notice reset and flush is the last thing we do

write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );

// Set up default configuration. Callers can always change it later.

// This channel should be universally safe and not bleed over into adjacent

// spectrum.

setChannel(76);

// Flush buffers

//flush_rx();

flush_tx();

powerUp();

// Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to

RX or TX instead of 1500us from powerUp )

// PTX should use only 22uA of power

write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) );

return true;

}

/****************************************************************************/

void RF24::startListening(void)

{

powerUp();

write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX));

write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );

// Restore the pipe0 adddress, if exists

if (pipe0_reading_address[0] > 0){

write_register(RX_ADDR_P0, pipe0_reading_address,addr_width);

}

// Flush buffers

//flush_rx();

Page 59: RDRIV Final Report

flush_tx();

// Go!

bcm2835_gpio_write(ce_pin, HIGH);

// wait for the radio to come up (130us actually only needed)

delayMicroseconds(130);

}

/****************************************************************************/

void RF24::stopListening(void)

{

bcm2835_gpio_write(ce_pin, LOW);

flush_tx();

flush_rx();

delayMicroseconds(150);

write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) );

delayMicroseconds(150);

}

/****************************************************************************/

void RF24::powerDown(void)

{

bcm2835_gpio_write(ce_pin, LOW);

write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP));

}

/****************************************************************************/

void RF24::powerUp(void)

{

bool up = read_register(CONFIG) & _BV(PWR_UP);

if(! up ){

write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ));

delay(5);

}

}

Page 60: RDRIV Final Report

/******************************************************************/

#if defined (FAILURE_HANDLING)

void RF24::errNotify(){

if(debug){ printf("HARDWARE FAIL\n\r"); }

failureDetect = true;

}

#endif

/******************************************************************/

bool RF24::write( const void* buf, uint8_t len, const bool multicast ){

// Begin the write

startFastWrite(buf,len, multicast);

//Wait until complete or failed

#if defined (FAILURE_HANDLING)

uint32_t timer = millis();

#endif

// If this hangs, it ain't coming back, no sense in timing out

while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) {

#if defined (FAILURE_HANDLING)

if(millis() - timer > 175){

errNotify();

return 0;

}

#endif

}

bcm2835_gpio_write(ce_pin, LOW);

uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );

//Max retries exceeded

if( status & _BV(MAX_RT)){

flush_tx(); //Only going to be 1 packet int the FIFO at a time using this method, so just flush

return 0;

}

//TX OK 1 or 0

return 1;

}

Page 61: RDRIV Final Report

bool RF24::write( const void* buf, uint8_t len ){

return write(buf,len,0);

}

/****************************************************************************/

//For general use, the interrupt flags are not important to clear

bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout )

{

//Block until the FIFO is NOT full.

//Keep track of the MAX retries and set auto-retry if seeing failures

//This way the FIFO will fill up and allow blocking until packets go through

//The radio will auto-clear everything in the FIFO as long as CE remains high

uint32_t timer = millis(); //Get the time

that the payload transmission started

while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will

loop and block until TX is successful or timeout

if( get_status() & _BV(MAX_RT)){ //If MAX

Retries have been reached

reUseTX();

//Set re-transmit and clear the MAX_RT interrupt flag

if(millis() - timer > timeout){ return 0; } //If this payload has exceeded

the user-defined timeout, exit and return 0

}

#if defined (FAILURE_HANDLING)

if(millis() - timer > (timeout+75) ){

errNotify();

return 0;

}

#endif

}

//Start Writing

startFastWrite(buf,len,0);

//Write the payload if a buffer is clear

return 1;

//Return 1 to indicate successful transmission

Page 62: RDRIV Final Report

}

/****************************************************************************/

void RF24::reUseTX(){

write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag

//spiTrans( REUSE_TX_PL );

bcm2835_spi_transfer( REUSE_TX_PL);

bcm2835_gpio_write(ce_pin, LOW);

//Re-Transfer packet

bcm2835_gpio_write(ce_pin, HIGH);

}

/****************************************************************************/

bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast )

{

//Block until the FIFO is NOT full.

//Keep track of the MAX retries and set auto-retry if seeing failures

//Return 0 so the user can control the retrys and set a timer or failure counter if required

//The radio will auto-clear everything in the FIFO as long as CE remains high

#if defined (FAILURE_HANDLING)

uint32_t timer = millis();

#endif

while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full.

This will loop and block until TX is successful or fail

if( get_status() & _BV(MAX_RT)){

//reUseTX();

//Set re-transmit

write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag

return 0;

//Return 0. The previous payload has been retransmitted

//From the user perspective, if you get a 0, just keep trying to send the same payload

}

#if defined (FAILURE_HANDLING)

if(millis() - timer > 75 ){

errNotify();

return 0;

Page 63: RDRIV Final Report

}

#endif

}

//Start Writing

startFastWrite(buf,len,multicast);

return 1;

}

bool RF24::writeFast( const void* buf, uint8_t len ){

return writeFast(buf,len,0);

}

/****************************************************************************/

//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and

set CE high

//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us

delay)

//Otherwise we enter Standby-II mode, which is still faster than standby mode

//Also, we remove the need to keep writing the config register over and over and delaying for 150 us

each time if sending a stream of data

void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //TMRh20

//write_payload( buf,len);

write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ;

bcm2835_gpio_write(ce_pin, HIGH);

}

/****************************************************************************/

void RF24::startWrite( const void* buf, uint8_t len, const bool multicast )

{

// Send the payload

write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ;

bcm2835_gpio_write(ce_pin, HIGH);

delayMicroseconds(10);

bcm2835_gpio_write(ce_pin, LOW);

Page 64: RDRIV Final Report

}

/****************************************************************************/

bool RF24::txStandBy(){

#if defined (FAILURE_HANDLING)

uint32_t timer = millis();

#endif

while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){

if( get_status() & _BV(MAX_RT)){

write_register(STATUS,_BV(MAX_RT) );

bcm2835_gpio_write(ce_pin, LOW);

flush_tx(); //Non blocking, flush the data

return 0;

}

#if defined (FAILURE_HANDLING)

if( millis() - timer > 75){

errNotify();

return 0;

}

#endif

}

bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode

return 1;

}

/****************************************************************************/

bool RF24::txStandBy(uint32_t timeout){

uint32_t start = millis();

while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){

if( get_status() & _BV(MAX_RT)){

write_register(STATUS,_BV(MAX_RT) );

bcm2835_gpio_write(ce_pin, LOW);

//Set re-transmit

bcm2835_gpio_write(ce_pin, HIGH);

if(millis() - start >= timeout){

bcm2835_gpio_write(ce_pin, LOW);

flush_tx();

Page 65: RDRIV Final Report

return 0;

}

}

#if defined (FAILURE_HANDLING)

if( millis() - start > (timeout+75)){

errNotify();

return 0;

}

#endif

}

bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode

return 1;

}

/****************************************************************************/

void RF24::maskIRQ(bool tx, bool fail, bool rx){

write_register(CONFIG, ( read_register(CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS

| rx << MASK_RX_DR );

}

/****************************************************************************/

uint8_t RF24::getDynamicPayloadSize(void)

{

spi_txbuff[0] = R_RX_PL_WID;

spi_rxbuff[1] = 0xff;

bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2);

if(spi_rxbuff[1] > 32) { flush_rx(); return 0; }

return spi_rxbuff[1];

}

/****************************************************************************/

bool RF24::available(void)

{

return available(NULL);

}

Page 66: RDRIV Final Report

/****************************************************************************/

bool RF24::available(uint8_t* pipe_num)

{

//Check the FIFO buffer to see if data is waitng to be read

if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){

// If the caller wants the pipe number, include that

if ( pipe_num ){

uint8_t status = get_status();

*pipe_num = ( status >> RX_P_NO ) & 0b111;

}

return 1;

}

return 0;

}

/****************************************************************************/

void RF24::read( void* buf, uint8_t len )

{

// Fetch the payload

read_payload( buf, len );

//Clear the two possible interrupt flags with one command

write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) );

}

/****************************************************************************/

void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready)

{

// Read the status & reset the status in one easy call

// Or is that such a good idea?

uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );

// Report to the user what happened

tx_ok = status & _BV(TX_DS);

tx_fail = status & _BV(MAX_RT);

Page 67: RDRIV Final Report

rx_ready = status & _BV(RX_DR);

}

/****************************************************************************/

void RF24::openWritingPipe(uint64_t value)

{

// Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+)

// expects it LSB first too, so we're good.

write_register(RX_ADDR_P0, reinterpret_cast<uint8_t*>(&value), addr_width);

write_register(TX_ADDR, reinterpret_cast<uint8_t*>(&value), addr_width);

write_register(RX_PW_P0,payload_size);

}

/****************************************************************************/

void RF24::openWritingPipe(const uint8_t *address)

{

// Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+)

// expects it LSB first too, so we're good.

write_register(RX_ADDR_P0,address, addr_width);

write_register(TX_ADDR, address, addr_width);

//const uint8_t max_payload_size = 32;

//write_register(RX_PW_P0,min(payload_size,max_payload_size));

write_register(RX_PW_P0,payload_size);

}

/****************************************************************************/

static const uint8_t child_pipe[] =

{

RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5

};

static const uint8_t child_payload_size[] =

{

RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5

};

static const uint8_t child_pipe_enable[] =

Page 68: RDRIV Final Report

{

ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5

};

void RF24::openReadingPipe(uint8_t child, uint64_t address)

{

// If this is pipe 0, cache the address. This is needed because

// openWritingPipe() will overwrite the pipe 0 address, so

// startListening() will have to restore it.

if (child == 0){

memcpy(pipe0_reading_address,&address,addr_width);

}

if (child <= 6)

{

// For pipes 2-5, only write the LSB

if ( child < 2 )

write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast<const uint8_t*>(&address),

addr_width);

else

write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast<const uint8_t*>(&address), 1);

write_register(pgm_read_byte(&child_payload_size[child]),payload_size);

// Note it would be more efficient to set all of the bits for all open

// pipes at once. However, I thought it would make the calling code

// more simple to do it this way.

write_register(EN_RXADDR,read_register(EN_RXADDR) |

_BV(pgm_read_byte(&child_pipe_enable[child])));

}

}

/****************************************************************************/

void RF24::setAddressWidth(uint8_t a_width){

if(a_width -= 2){

write_register(SETUP_AW,a_width%4);

addr_width = (a_width%4) + 2;

}

}

Page 69: RDRIV Final Report

/****************************************************************************/

void RF24::openReadingPipe(uint8_t child, const uint8_t *address)

{

// If this is pipe 0, cache the address. This is needed because

// openWritingPipe() will overwrite the pipe 0 address, so

// startListening() will have to restore it.

if (child == 0){

memcpy(pipe0_reading_address,address,addr_width);

}

if (child <= 6)

{

// For pipes 2-5, only write the LSB

if ( child < 2 ){

write_register(pgm_read_byte(&child_pipe[child]), address, addr_width);

}else{

write_register(pgm_read_byte(&child_pipe[child]), address, 1);

}

write_register(pgm_read_byte(&child_payload_size[child]),payload_size);

// Note it would be more efficient to set all of the bits for all open

// pipes at once. However, I thought it would make the calling code

// more simple to do it this way.

write_register(EN_RXADDR,read_register(EN_RXADDR) |

_BV(pgm_read_byte(&child_pipe_enable[child])));

}

}

/****************************************************************************/

void RF24::toggle_features(void)

{

bcm2835_spi_transfer( ACTIVATE );

bcm2835_spi_transfer( 0x73 );

}

/****************************************************************************/

void RF24::enableDynamicPayloads(void)

{

Page 70: RDRIV Final Report

// So enable them and try again

toggle_features();

write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) );

if (debug)

printf("FEATURE=%i\r\n",read_register(FEATURE));

// Enable dynamic payload on all pipes

//

// Not sure the use case of only having dynamic payload on certain

// pipes, so the library does not support it.

write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) |

_BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0));

dynamic_payloads_enabled = true;

}

/****************************************************************************/

void RF24::enableAckPayload(void)

{

//

// enable ack payload and dynamic payload features

//

// So enable them and try again

toggle_features();

write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) );

if (debug)

printf("FEATURE=%i\r\n",read_register(FEATURE));

//

// Enable dynamic payload on pipes 0 & 1

//

dynamic_payloads_enabled = true;

write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0));

}

/****************************************************************************/

Page 71: RDRIV Final Report

void RF24::enableDynamicAck(void){

//

// enable dynamic ack features

//

toggle_features();

write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) );

if(debug){printf("FEATURE=%i\r\n",read_register(FEATURE));}

}

/****************************************************************************/

void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len)

{

uint8_t * ptx = spi_txbuff;

uint8_t size ;

const uint8_t* current = reinterpret_cast<const uint8_t*>(buf);

uint8_t data_len = min(len,payload_size);

size = data_len + 1 ; // Add register value to transmit buffer

if (debug){

printf("[Writing %u bytes]",data_len);

}

*ptx++ = W_ACK_PAYLOAD | ( pipe & 0b111 );

while ( data_len-- ){

*ptx++ = *current++;

}

bcm2835_spi_transfern( (char *) spi_txbuff, size);

}

/****************************************************************************/

bool RF24::isAckPayloadAvailable(void)

Page 72: RDRIV Final Report

{

return ! read_register(FIFO_STATUS) & _BV(RX_EMPTY);

}

/****************************************************************************/

bool RF24::isPVariant(void)

{

return p_variant ;

}

/****************************************************************************/

void RF24::setAutoAck(bool enable)

{

if ( enable )

write_register(EN_AA, 0b111111);

else

write_register(EN_AA, 0);

}

/****************************************************************************/

void RF24::setAutoAck( uint8_t pipe, bool enable )

{

if ( pipe <= 6 )

{

uint8_t en_aa = read_register( EN_AA ) ;

if( enable )

{

en_aa |= _BV(pipe) ;

}

else

{

en_aa &= ~_BV(pipe) ;

}

write_register( EN_AA, en_aa ) ;

}

}

/****************************************************************************/

Page 73: RDRIV Final Report

bool RF24::testCarrier(void)

{

return ( read_register(CD) & 1 );

}

/****************************************************************************/

bool RF24::testRPD(void)

{

return ( read_register(RPD) & 1 ) ;

}

/****************************************************************************/

void RF24::setPALevel(uint8_t level)

{

uint8_t setup = read_register(RF_SETUP) & 0b11111000;

if(level > 3){ // If invalid level, go to max PA

level = (RF24_PA_MAX << 1) + 1; // +1 to support the SI24R1 chip extra bit

}else{

level = (level << 1) + 1; // Else set level as requested

}

write_register( RF_SETUP, setup |= level ) ; // Write it to the chip

}

/****************************************************************************/

uint8_t RF24::getPALevel(void)

{

return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1 ;

}

/****************************************************************************/

bool RF24::setDataRate(rf24_datarate_e speed)

{

bool result = false;

uint8_t setup = read_register(RF_SETUP) ;

Page 74: RDRIV Final Report

// HIGH and LOW '00' is 1Mbs - our default

setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ;

if( speed == RF24_250KBPS )

{

// Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0

// Making it '10'.

setup |= _BV( RF_DR_LOW ) ;

}

else

{

// Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1

// Making it '01'

if ( speed == RF24_2MBPS )

{

setup |= _BV(RF_DR_HIGH);

}

}

write_register(RF_SETUP,setup);

// Verify our result

if ( read_register(RF_SETUP) == setup )

{

result = true;

}

return result;

}

/****************************************************************************/

rf24_datarate_e RF24::getDataRate( void )

{

rf24_datarate_e result ;

uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH));

// switch uses RAM (evil!)

// Order matters in our case below

if ( dr == _BV(RF_DR_LOW) )

{

// '10' = 250KBPS

result = RF24_250KBPS ;

}

Page 75: RDRIV Final Report

else if ( dr == _BV(RF_DR_HIGH) )

{

// '01' = 2MBPS

result = RF24_2MBPS ;

}

else

{

// '00' = 1MBPS

result = RF24_1MBPS ;

}

return result ;

}

/****************************************************************************/

void RF24::setCRCLength(rf24_crclength_e length)

{

uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ;

// switch uses RAM (evil!)

if ( length == RF24_CRC_DISABLED )

{

// Do nothing, we turned it off above.

}

else if ( length == RF24_CRC_8 )

{

config |= _BV(EN_CRC);

}

else

{

config |= _BV(EN_CRC);

config |= _BV( CRCO );

}

write_register( CONFIG, config ) ;

}

/****************************************************************************/

rf24_crclength_e RF24::getCRCLength(void)

{

rf24_crclength_e result = RF24_CRC_DISABLED;

uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ;

Page 76: RDRIV Final Report

if ( config & _BV(EN_CRC ) )

{

if ( config & _BV(CRCO) )

result = RF24_CRC_16;

else

result = RF24_CRC_8;

}

return result;

}

/****************************************************************************/

void RF24::disableCRC( void )

{

uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ;

write_register( CONFIG, disable ) ;

}

/****************************************************************************/

void RF24::setRetries(uint8_t delay, uint8_t count)

{

write_register(SETUP_RETR,(delay&0xf)<<ARD | (count&0xf)<<ARC);

}

Python Motor Library

import RPi.GPIO as GPIO

## Motor Control Functions

#Forward

#Reverse

#Turn Right

#Turn Left

#Soft Right

#Soft Left

#All Stop

def DriveForward(Min1,Min2,Min3,Min4):

GPIO.output(Min1, 1)

GPIO.output(Min2, 0)

GPIO.output(Min3, 0)

Page 77: RDRIV Final Report

GPIO.output(Min4, 1)

def DriveBackward(Min1,Min2,Min3,Min4):

GPIO.output(Min1, 0)

GPIO.output(Min2, 1)

GPIO.output(Min3, 1)

GPIO.output(Min4, 0)

def TurnRight(Min1,Min2,Min3,Min4):

GPIO.output(Min1, 0)

GPIO.output(Min2, 1)

GPIO.output(Min3, 0)

GPIO.output(Min4, 1)

def SoftRight(Min1,Min2,Min3,Min4):

GPIO.output(Min1, 0)

GPIO.output(Min2, 1)

GPIO.output(Min3, 0)

GPIO.output(Min4, 0)

def TurnLeft(Min1,Min2,Min3,Min4):

GPIO.output(Min1, 1)

GPIO.output(Min2, 0)

GPIO.output(Min3, 1)

GPIO.output(Min4, 0)

def SoftLeft(Min1,Min2,Min3,Min4):

GPIO.output(Min1, 0)

GPIO.output(Min2, 0)

GPIO.output(Min3, 1)

GPIO.output(Min4, 0)

def StopMotor(Min1,Min2,Min3,Min4):

GPIO.output(Min1, 0)

GPIO.output(Min2, 0)

GPIO.output(Min3, 0)

GPIO.output(Min4, 0)

Python Sensor Library

import RPi.GPIO as GPIO

import time

Page 78: RDRIV Final Report

#Reads the Sonar Sensor

def readsonar(TRIG,ECHO):

GPIO.output(TRIG,1) #begin Trigger pulse

time.sleep(0.00001)

GPIO.output(TRIG,0) #end Trigger pulse

begin = time.time()

while GPIO.input(ECHO) == 0: #wait for Trigger pulse

if ((time.time() - begin) * 1700) >= 30.0: # if no object is encountered.. stop looking

return 30

start = time.time()

while GPIO.input(ECHO) == 1:

pass

stop = time.time()

dist = (stop - start) * 17000

time.sleep(0.05)

return int(dist)

#SonarSense averages ten measurements

def SonarSense(TRIG,ECHO):

L = [0,0,0,0,0,0,0,0,0,0,0]

for x in range(11):

dist_new = readsonar(TRIG,ECHO)

L[x] = dist_new

L = sorted(L)

dist = L[10]

return dist

# read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7)

def readadc(adcnum, clockpin, mosipin, misopin, cspin):

if ((adcnum > 7) or (adcnum < 0)):

return -1

GPIO.output(cspin, True)

GPIO.output(clockpin, False) # start clock low

GPIO.output(cspin, False) # bring CS low

commandout = adcnum

commandout |= 0x18 # start bit + single-ended bit

commandout <<= 3 # we only need to send 5 bits here

for i in range(5):

if (commandout & 0x80):

GPIO.output(mosipin, True)

else:

Page 79: RDRIV Final Report

GPIO.output(mosipin, False)

commandout <<= 1

GPIO.output(clockpin, True)

GPIO.output(clockpin, False)

adcout = 0

# read in one empty bit, one null bit and 10 ADC bits

for i in range(12):

GPIO.output(clockpin, True)

GPIO.output(clockpin, False)

adcout <<= 1

if (GPIO.input(misopin)):

adcout |= 0x1

GPIO.output(cspin, True)

adcout >>= 1 # first bit is 'null' so drop it

return adcout

def IRSense(IRS0, SPICLK, SPIMOSI, SPIMISO, SPICS):

measure_tot = 0

for x in range(30):

measure_new = readadc(IRS0, SPICLK, SPIMOSI, SPIMISO, SPICS)

measure_tot = measure_tot + measure_new

measure_avg = (measure_tot / 30)

if (measure_avg < 200):

zone = 3

elif ((measure_avg < 350) & (measure_avg > 200)):

zone = 2

elif ((measure_avg < 1024) & (measure_avg > 350)):

zone = 1

else:

return -1

return zone

Page 80: RDRIV Final Report

Users Guide for product:

Step 1.) Turn on Raspberry Pi and connect it to a keyboard, mouse, and monitor.

Step 2.) Load the cMain file and the pMain.py file.

Page 81: RDRIV Final Report

Step 3.) Find the IP address of the Raspberry Pi at the given time by typing in “hostname –I”.

Remember the IP address.

Step 4.) Once the Raspberry Pi code is loaded up, disconnect the keyboard, mouse, and monitor.

Place the RDRIV on the ground for it is ready to be activated.

Step 5.) Attach Arduino to USB port and upload ArduinoRDRIVTwo.ino

Figure 1-Open Arduino compiler program

Page 82: RDRIV Final Report

Figure 2- Program will look like this when opened

Page 83: RDRIV Final Report

Figure 3- Open the code for the RDRIV

Figure 4- Select the ArduinoRDRIVTwo program or most up to date version

Page 84: RDRIV Final Report

Figure 5- Code will open in a new window that looks like this

Page 85: RDRIV Final Report

Figure 6- Make sure the port the Arduino s connected to is the same port the program is talking to

Figure 7- Upload code to Arduino

Step 6.) Load the Qt program up on the user’s device of choice

Page 86: RDRIV Final Report

Figure 8- Open project in QT

Page 87: RDRIV Final Report

Figure 9- Choose QT Project file named RDRIV_4_5_15 or latest version

Page 88: RDRIV Final Report

Figure 10- QT should look like this when loaded

Step 7.) Build and Run the GUI

Figure 11-Click the run arrow in lower left of the screen

Figure 12- Project will build with progress bar in the lower right hand corner

Figure 13- Progress bar will become green when done building

Page 89: RDRIV Final Report

Figure 14- The GUI will appear when built sucessfully and can be used from there

Step 8.) Once connected to the same Wi-Fi network, enter in the same IP address the Raspberry

Pi has, into the user’s url window.

Step 9.) Since the Raspberry Pi Camera is placed upside down you will need to click on settings

once connected to the Raspberry Pi Camera.

Step 10.) Go down to where it says “Rotate”, rotate the camera by 180 degrees. Now the

Raspberry Pi Camera video feed will come in at the right perspective.

Step 11.) Navigate to your destination with the G.U.I.

Page 90: RDRIV Final Report

Robot Circuit Diagram

Page 91: RDRIV Final Report

Laptop Communication Circuit Diagram

Page 92: RDRIV Final Report

Mechanical Drawings

Page 93: RDRIV Final Report
Page 94: RDRIV Final Report
Page 95: RDRIV Final Report
Page 96: RDRIV Final Report
Page 97: RDRIV Final Report
Page 98: RDRIV Final Report
Page 99: RDRIV Final Report
Page 100: RDRIV Final Report
Page 101: RDRIV Final Report
Page 102: RDRIV Final Report
Page 103: RDRIV Final Report
Page 104: RDRIV Final Report
Page 105: RDRIV Final Report
Page 106: RDRIV Final Report