You are on page 1of 97

PSZ 19:16 (Pind.

1/07)

UNIVERSITI TEKNOLOGI MALAYSIA


DECLARATION OF THESIS / UNDERGRADUATE PROJECT PAPER AND COPYRIGHT

LAW SEI CUI


Authors full name :
1 SEPTEMBER 1988
Date of birth :
MAZE SOLVING ROBOT
Title :

2010/2011
Academic Session:

I declare that this thesis is classified as :

CONFIDENTIAL (Contains confidential information under the Official Secret


Act 1972)*

RESTRICTED (Contains restricted information as specified by the


organisation where research was done)*

 OPEN ACCESS I agree that my thesis to be published as online open access


(full text)

I acknowledged that Universiti Teknologi Malaysia reserves the right as follows :

1. The thesis is the property of Universiti Teknologi Malaysia.


2. The Library of Universiti Teknologi Malaysia has the right to make copies for the purpose
of research only.
3. The Library has the right to make copies of the thesis for academic exchange.

Certified by :

SIGNATURE SIGNATURE OF SUPERVISOR

880901-05-5104 MOHD ARIFFANAN BIN MOHD BASRI


(NEW IC NO. /PASSPORT NO.) NAME OF SUPERVISOR

Date : 16 MAY 2011 Date : 16 MAY 2011

NOTES : * If the thesis is CONFIDENTIAL or RESTRICTED, please attach with the letter from
the organisation with period and reasons for confidentiality or restriction.
DECLARATION

I hereby declare that I have read this thesis and in my opinion this thesis is
sufficient in terms of scope and quality for the award of the degree of
Bachelor of Engineering (Electrical Mechatronics)

Signature : .
Name of Supervisor : MOHD ARIFFANAN BIN MOHD BASRI
Date : 16 MAY 2011
MAZE SOLVING ROBOT

LAW SEI CUI

A thesis submitted in partial fulfillment of the


requirements for the award of the degree of
Bachelor of Engineering (Electrical - Mechatronics)

Faculty of Electrical Engineering,


Universiti Teknologi Malaysia

MAY 2011
ii

DECLARATION

I declare that this thesis entitled Maze


Solving Robot is the result of my own
research except as cited in the references. The thesis has not been accepted for any
degree and is not concurrently submitted in candidature of any other degree.

Signature :
Name : LAW SEI CUI
Date : 16 MAY 2011
iii

DEDICATION

To Sam Men Wee for his unconditional support


iv

ACKNOWLEDGEMENT

First and foremost, I would like to express my deepest gratitude to my project


supervisor, Mr. Mohd Ariffanan bin Mohd Basri who had gave me guidance and
support throughout the entire project. The project will become harder if without his
help.

My sincere appreciation also goes to my parents and siblings for their


understanding and support. Lastly, thanks are giving to all my friends that supported
me during the development. I may not able to complete this project without all of
your support. Thank you all.
v

ABSTRACT

A maze solving robot is an autonomous robot that is able to negotiate a maze


in shortest possible time. The maze solving robot can finds out the shortest path of
the maze from a starting point to a specified end point. It follows black colored line
with many junctions, and six analogue IR sensors are employed for line sensing. An
encoder with Quadrature Encoder Interface (QEI) is then used to measure the travel
distance of autonomous robot and the data is send to a LCD for display. All of the
I/O interfaces are controlled by using microchip PIC30F4011 programmed in C
language. In order to calculate the shortest path from all possible pairs of starting
point and end point, Dijkstras algorithm is implemented in the robot, which is
popular among Path Finding Algorithms known for its fast calculation and low
memory consumption.
vi

ABSTRAK

Maze solving robot adalah sebuah autonomi robot yang dapat menerokai
maze yang diberikan dalam masa sesingkat mungkin. Maze solving robot itu boleh
mengetahui laluan terpendek dari titik awal ke titik akhir dalam maze tersebut. Robot
tersebut bergerak mengikut garis berwarna hitam dengan banyak persimpangan dan
enam IR sensor analog digunakan sebagai penderian dalam pergerakan. Sebuah
encoder digunakan untuk mengukur jarak perjalanan maze solving robot. Maklumat
yang diperolehi akan dipaparkan kepada LCD. Segala perantaramukaan dikawal
oleh microchip PIC30F4011. Di samping itu, algoritma Dijkstra diimplementasikan
untuk mencari laluan terpendek dari titik awal ke semua titik lain.
vii

TABLE OF CONTENTS

CHAPTER TITLE PAGE


DECLARATION ii
DEDICATION iii
ACKNOWLEDGEMENT iv
ABSTRACT v
ABSTRAK vi
TABLE OF CONTENTS vii
LIST OF TABLES x
LIST OF FIGURES xi
LIST OF ABBREVIATIONS xiii
LIST OF APPENDICES xiv

1. INTRODUCTION
1.1. Project Background 1
1.2. Objective 1
1.3. Scope 2
1.4. Problem Statement 3

2. LITERATURE REVIEW
2.1. Introduction 4
2.2. University of East London 4
2.2.1. 1999 Micromouse 4
2.3. Previous Final Year Projects in UTM 6
2.3.1. 2006 Micromouse I 6
2.3.2. 2006 Micromouse II 7
2.3.3. 2008 Micromouse Maze Solving Robot 8
viii

2.3.4. 2009 Explorer Robot 9


2.4. Summary 10

3. METHODOLOGY AND APPROACH


3.1. Introduction 11
3.2. Project Overview 11
3.2.1. Project Flow 13
3.3. Mechanical Hardware 14
3.3.1. Tamiya Twin Motor Gearbox 15
3.3.2. Mini Wheels 16
3.3.3. Castor 16
3.3.4. Encoder Wheel 17
3.4. Electronic Circuitry 17
3.4.1. Power Supply Circuit 18
3.4.2. Microcontroller 19
3.4.2.1. I/O Pin Assignation 20
3.4.3. LCD Display 21
3.4.4. Push Button 22
3.4.5. Motor Driver L293D 23
3.4.6. Incremental Encoder 24
3.4.7. IR Sensor with Comparator LM324 25
3.5. Software 28
3.5.1. Line Following 29
3.5.2. Distance Measurement using QEI 30
3.5.3. Path Planning 31
3.5.4. Dijkstras Algorithm 32
3.5.4.1. Example of Dijkstras Algorithm 34
3.6. Maze Mechanical Drawing 37

4. RESULT AND DISCUSSION


4.1. Introduction 39
4.2. Hardware Design 39
4.3. Circuitry Design 41
ix

4.4. Maze Field Design 42


4.5. Line following Feature 42
4.6. Distance Measurement Feature 43
4.7. Maze Solving Test 45

5. CONCLUSION AND RECOMMENDATION


5.1. Conclusion 48
5.2. Recommendation 49

REFERENCES 50

APPENDICES 51
x

LIST OF TABLES

TABLE NO. TITLE PAGE

3.1 Step 2 of Dijkstras algorithm 34


3.2 Step 3 of Dijkstras algorithm 35
3.3 Step 5 of Dijkstras algorithm 35
3.4 Step 6 of Dijkstras algorithm 36
3.5 Step 7 of Dijkstras algorithm 37
4.1 The maze solving robot specification 39
4.2 The result of distance measurement 45
6.1 FYP 1 Gantt chart 51
6.2 FYP 2 Gantt chart 51
xi

LIST OF FIGURES

FIGURE NO. TITLE PAGE

1.1 A 3X3 Maze in Matrix Form 2


2.1 Micromouse UEL 1999 by Michael Gims, Sonja Lenz and Dirk Becker 5
2.2 Micromouse UTM FYP 2006 by Louis Wong Siang San 6
2.3 Micromouse UTM FYP 2006 by Indran A/L Thandavarian 7
2.4 Micromouse Maze Solving Robot UTM FYP 2008 by Chang Yuen 8
Chung
2.5 Explorer Robot UTM FYP 2009 by Lew Chong Hao 9
3.1 Project components 12
3.2 Project flow 13
3.3 Tamiya Motor 15
3.4 Mini wheel 16
3.5 Screw typed castor 16
3.6 Encoder wheel in computer mouse 17
3.7 Power supply circuit 18
3.8 Microchip dsPIC30F4011 19
3.9 Pin diagram of Microchip dsPIC30F4011 19
3.10 Pin assignation of Microchip dsPIC30F4011 20
3.11 LCD HD44780U 21
3.12 Schematic of LCD HD44780U 21
3.13 Push button 22
3.14 Schematic of push button 22
3.15 Motor Driver L293D 22
3.16 Motor Driver L293D interface 23
3.17 Schematic of motor driver L293D 23
xii

3.18 Incremental encoder 24


3.19 Incremental encoder disk track patterns 24
3.20 Schematic of incremental encoder 25
3.21 Basic design of the Infra Red sensor 25
3.22 Comparator LM324 26
3.23 Schematic of LM324 26
3.24 Schematic of IR sensors 27
3.25 Schematic of comparator LM324 27
3.26 Flow chart of maze solving robot 28
3.27 Flow chart of line following features 29
3.28 Quadrature Encoder Interface Signals 31
3.29 Flow chart of maze exploration 31
3.30 Flow chart of shortest path execution 32
3.31 Flow chart of Dijkstras algorithm 33
3.32 Step 1 of Dijkstras algorithm 34
3.33 Step 3 of Dijkstras algorithm 35
3.34 Step 5 of Dijkstras algorithm 35
3.35 Step 6 of Dijkstras algorithm 36
3.36 Step 7 of Dijkstras algorithm 36
3.37 Step 8 of Dijkstras algorithm 37
3.38 Patterns of card boards 38
3.39 Example of maze 1 38
3.40 Example of maze 2 38
4.1 Views of the maze solving robot 40
4.2 Embedded microcontroller board 41
4.3 Sensor board with motor control board 41
4.4 Maze field 42
4.5 Line following of maze solving robot 43
4.6 Distance measurement 43
4.7 Results of distance measurement 44
4.8 Test maze 46
4.9 The exploration of maze solving robot 46
4.10 The shortest path execution by maze solving robot 47
xiii

LIST OF ABBREVIATIONS

A - Ampere
cm - Centi-meter
DC - Direct current
FYP - Final year project
GPIO - General purpose input and output pins
IC - Integrated circuit
IDE - Integrated Development Environment
IR - Infra red
I/O - Input or output
kg - Kilogram
LCD - Liquid crystal display
LED - Light emitter diode
MHz - Mega Hertz
mm - Milimeter
PCB - Printed circuit board
pF - Pico-farad
PWM - Pulse-width modulation
QEI - Quadrature Encoders Interface
rpm - Revolution per minute
R/W - Read or write
uF - Micro-farad
UTM - Universit Teknologi Malaysia
V - Volt
xiv

LIST OF APPENDICES

APPENDIX TITLE PAGE

A Gantt Chart for Semester 1 and Semester 2 51


B Header File Source Code 52
C Embedded Microcontroller Source Code 57
CHAPTER 1

INTRODUCTION

1.1. Project Background

Maze solving robot is one of the autonomous robots which aim to negotiate a
maze. The idea of the autonomous Micro-mouse robot as a maze solving device was
firstly introduced by the IEEE Spectrum Magazine in 1977. Many such competitions
were then held all around the world. In fact, this robot is an advanced tool for
military uses. For example, in 1944, robot bombs have killed many people and
destroyed houses in six weeks of bombardment in London. Fewer of these bombs
had reached this land through the maze of defense device. This is a conspicuous case
of advantage to use such robots. By the way, this technology can also be used for
many other applications such as car park systems, object searching and so on.

1.2. Objective

The main objective of this project is to design and build an autonomous robot
that is able to negotiate a maze in the shortest possible time. Besides, Dijkstras
algorithm is implemented in the robot to calculate the shortest path from all possible
pairs of starting point and end point.
2

1.3. Scope

The main task the maze solving robot must do is to explore the maze given
and then finds out the shortest route from starting point to a specified end point.
During process of explore maze given, the robot will obtain the distance that read
through encoder (QEI). Once finished explore, the robot will analyze all the data
stored using Dijkstras algorithm to find out shortest route.

The maze solving robot is designed to explore in a 3x3 maze in matrix form
as shown as Figure 1.1. There can be many paths from a fixed starting point to a
fixed end point. Each bullet shown is actually a junction in real maze and it can be
moved to any position as long as the arrangement of nodes is in 3x3 matrix order.
The maze solving robot stores the distance from one junction to another junction
during line following. The distance is then sending to LCD for displaying purpose so
that we can know whether the robot counts distance correctly. The movement of
robot during meet junction is programmed and it can memorize all the direction it
passed through. Once the maze solving robot returned to starting point, it will
process the data using Dijkstras algorithm and then heading to shortest route.

END

START

Figure 1.1: A 3X3 Maze in Matrix Form


3

1.4. Problem Statement

In order to negotiate the maze in shortest time, an efficient algorithm is


needed to process the data. In addition, a smooth and fast movement is required for
the autonomous robot to travel the maze. Path planning is the main element to
process all route passed by maze solving robot. A good environment is required for
the exploration of maze solving robot. The maze field should be flexible and easy to
construct.
CHAPTER 2

LITERATURE REVIEW

2.1. Introduction

This chapter describes a few project related to maze solving robot that have
been done by previous students and researchers. The specification and design of
previous robot can be served as a guideline to this project.

2.2. University of East London

The first literature review documentation is referring to the Micromouse


project by Michael Gims, Sonja Lenz and Dirk Becker from University of East
London.

2.2.1. 1999 Micromouse

The Figure 2.1 shows the Micromouse by Michael Gims, Sonja Lenz and
Dirk Becker [1]. The PCB circuit design make the circuit look tidy but the
arrangement of cables and connectors are still needed some improvement. The big
size of wheels increased the stability of robot. This is a good consideration when
constructing a robot.
5

Components used:

i) PIC 16F84 RISC Microcontroller


ii) PCB circuit design
iii) Focused IR-Sensors (Optoelectronics OPB704W)
iv) 2x 16-bit PWM motor-control.

Algorithm: Wall following

Figure 2.1: Micromouse UEL 1999 by Michael Gims, Sonja Lenz and Dirk Becker
6

2.3. Previous Final Year Projects in UTM

There are totally four previous final year projects in UTM which are related
to maze solving robot. The projects are listed as below:

i. Micromouse FYP 2006 by Louis Wong Siang San


ii. Micromouse FYP 2006 by Indran A/L Thandavarian
iii. Micromouse Maze Solving Robot FYP 2008 by Chang Yuen Chung
iv. Explorer Robot FYP 2009 by Lew Chong Hao

2.3.1. 2006 Micromouse I

The Figure 2.2 shows the Micromouse UTM FYP 2006 by Louis Wong Siang
San [2]. The wooden plank base is not suitable for robot because it is heavy and this
will affect the robot performance since the stepper motor may not has sufficient
torque to function effectively. Besides, there are many cables that out of robot. This
condition may cause the robot fault during performance. Therefore, a good circuit
design and arrangement is needed in the process of developing a robot.

Figure 2.2: Micromouse UTM FYP 2006 by Louis Wong Siang San
7

Components used:

i) PIC16F627.
ii) PCB circuit design
iii) IR sensors
iv) 2 stepper motors drive with darlington array IC
v) Wooden plank base with castor

2.3.2. 2006 Micromouse II

The Figure 2.3 shows the Micromouse UTM FYP 2006 by Indran A/L
Thandavarian [3]. This robot is bad in circuit arrangement. As we can see from
Figure 2.3, the outlook of micromouse robot is very messy. This is not a good robot
design. Hence, the circuit arrangement is a very critical issue during circuit design.

Figure 2.3: Micromouse UTM FYP 2006 by Indran A/L Thandavarian

Components used:

i) MC68HC11E
ii) Two stepper motors
iii) Whisker sensor
iv) IR sensors
v) Aluminum chassis base
8

2.3.3. 2008 Micromouse Maze Solving Robot

The Figure 2.4 shows the Micromouse Maze Solving Robot UTM FYP 2008
by Chang Yuen Chung [4]. The circuitry with different layer is making the robot
look nice. But there are many connectors from board to board. This is also not a good
way to link circuits. Thus, all the inputs and outputs are recommended to place
together so that only one cable with a number of ways is needed for the circuit.

Figure 2.4: Micromouse Maze Solving Robot UTM FYP 2008 by Chang Yuen
Chung

Components used:

i) PIC18F452
ii) 2 stepper motor
iii) 3 analogue distance sensors
iv) Wheel mounted with rubber
v) Acrylic casing
vi) Wheel chair design supporting with castor

Algorithm: Flood fill


9

2.3.4. 2009 Explorer Robot

The Figure 2.5 shows Explorer Robot UTM FYP 2009 by Lew Chong Hao
[5]. This robot design is similar to Micromouse Maze Solving Robot UTM FYP
2008 by Chang Yuen Chung, which are circuitry with different layer and many
connectors from board to board. Three different layers may make the robot unstable
due to the gravity of robot. Hence, the number of layers should reduce to increase the
stability of robot.

Figure 2.5: Explorer Robot UTM FYP 2009 by Lew Chong Hao

Components used:

i) PIC18F452
ii) 2 stepper motor
iii) Analogue distance sensors
iv) O-ring and wheels
v) Acrylic casing

Algorithm: A*
10

2.4. Summary

The literature review done in this chapter has exposed the advantages and
disadvantages of each previous project. As a summary, the following are list out as
my major preferences.

1. Low cost material


a. Motor -- Tamiya motor.
b. Platform black colored line pasted on card board.
c. Sensor Six analogue IR sensors with two units of comparators.
d. Encoder Incremental encoder that dissected from computer mouse.
e. Microchip dsPIC30F4011

My target for this project is to construct a maze solving robot using lowest
possible cost. Thus, reliable devices with reasonable cost such as Tamiya motor, card
board platform, analogue IR sensor, mouse modified encoder and dsPIC30F4011 are
chosen.

2. Algorithm -- Dijkstras algorithm

There are several path finding algorithm such as A*, B*, flood fill, Floyd-
Warshall algorithm, Johnsons algorithm, Dijkstras algorithm, etc. Each algorithm
has its own features and advantages. A* and Dijkstras algorithm are widely used
and most popular among those path finding algorithms. In this project, Dijkstras
algorithm is implemented in the robot. This is because Dijkstras algorithm is easier
to implement compare to A*. A* is a modification upon Dijkstra's basic algorithm
[6], so we have to understand Dijkstra's first in order to use A*. A* is faster than
Dijkstras, but not always as good. It uses heuristics, and tries to go towards the
target and around obstacles [7]. This can lead to it finding a more direct but possibly
more expensive path. For a small program such as 100 nodes and below, the
difference of speed between A* and Dijkstras would be negligible [8]. There are 9
nodes in my project, so Dijkstras is the best choice to achieve objectives stated.
CHAPTER 3

METHODOLOGY AND APPROACH

3.1. Introduction

This chapter describes the methods implemented in designing maze solving


robot. The main topics discussed in this chapter are project flow, mechanical
hardware design, electronic circuitry, software, maze mechanical drawing, etc.

3.2. Project Overview

The maze solving robot basically consists of four primary components, which
are maze mechanical drawing, mechanical hardware, software programming and
electronic circuitry. There are some considerations to be taken in account when
designing every single part for a better stability and reliability. Each part is essential
to the completion of the project.

Mechanical hardware is used to form a solid structure of robot. Thus, stability


is the main issue during process to construct robot mechanism. Electronic circuitry
consists of embedded system that interfaces with environment and executes
instructions. Software system plays role as brain for planning and controlling the
robot.
12

Software programming is the last stage after mechanical hardware,


hardware maze
mechanical platform and electronic circuitry have been completed, so that the
performance of maze solving robot can be tested and analyzed smoothly. Figure 3.1
shows the primary components in designing the maze solving robot.

Software Programming Maze Mechanical


Dijkstra's
Dijkstra's algorithm Black colored tape
Path
Path Planning Maze Platform

Mechanical Electronic Circuitry


Aluminum joint dsPIC30F4011
dsPIC30F4011
Castor LCD
LCD display
Mini wheels IR
IR sensor
Encoder wheel Motor
Motor driver
Integration Encoder
Encoder

Testing and Fine Tuning

Figure 3.1: Project components


13

3.2.1. Project Flow

Figure 3.2 shows the flow of this project. Every single step was carried out
according to schedule planned.

Idea and Concept


1

Literature Review
2

Mechanical Design and Construction


3

Circuit Design and Construction


4

Maze Design and Construction


5

Software development
6

Software and Hardware Integration


7

Implementation
8

Testing and Fine Tuning


9

Documentation
10

Figure 3.2: Project flow

The foremost step is getting idea and concept for final year project where my
project title is maze solving robot. To gain the knowledge and idea for this project,
literature review of previous project or other related articles has been done.
14

Next, the mechanism of maze solving robot was designed. Since a light and
small in size of robot is required to perform a smooth movement, the electronic
circuitry boards are designed to install directly to the Tamiya motor as the robots
body. Thus, a complete set of electronic circuitry is needed to accomplish the robot
mechanism.

The electronic circuitries were then designed by using gEDA Schematic


Editor. The circuitries designed consist of sensor, motor, main controller, etc. After
acquired all components, a whole set of circuits were constructed according to the
schematics drawn. Ultimately, the circuitry boards and hardware stuff were
combined together to come out a complete hardware mechanism. Besides, a maze
with 9 junctions were designed and constructed.

Then, the software algorithm and flow chart were developed and analyzed
using Microchips MPLAB IDE in C programming language. The Dijkstras
algorithm and path planning are implemented to the robot. Lastly, the system was
tested and fine tuned to get a better performance.

3.3. Mechanical Hardware

The concept in hardware design is based on the autonomous car. The


components used in the hardware structure construction are cheap and affordable.
The primary design goal of this project is to keep the overall cost the lowest possible.
Thus, a simple and reliable robot design is made with major components such as
castor and Tamiya motor with mini wheels.
15

3.3.1. Tamiya Twin Motor Gearbox

There are many motor such as stepper motor, servo motor, linear motor,
tamiya motor and so on. Each motor has its own features and characteristics. Servo
motor is widely used in educational robotic project. It can be used to control rotation
angle well compare to other motors. Linear motor is the most expensive among
several types of motor due to its specifications. It provides linear actuation in normal
electric motor. Stepper motor has an excellent low speed torque and good
repeatability. It can drive many loads without gearing and returns to the same
position accurately.

Somehow, those motors are quite expensive. As mentioned before, in order to


keep cost as minimum as possible, Tamiya Twin Motor Gearbox is chose in this
project.

The Tamiya twin-motor gearbox is a small plastic gearbox [9]. It contains


two small DC motors that drive separate 3mm hexagonal output shafts. There are
two ways to put the kit together: with a high-speed 58:1 gear ratio or with a slower
203:1 gear ratio. Either way, the motors provide plenty of power to drive any small
robot.

Figure 3.3: Tamiya Motor


16

3.3.2. Mini Wheels

A pair of custom-designed
custom designed plastic wheel as shown in Figure 3.4 is used
together with Tamiya motor. It has a rubber tire measuring 54mm in diameter and fit
with the output shafts on Tamiya twin motor.

Figure 3.4: Mini wheel

3.3.3. Castor

Figure 3.5 shows a screw typed castor that used to support the robot structure.
This type of castor is chosen due to its economically cost and light weight. It is 7mm
in diameter and 30mm in length.

Figure 3.5: Screw typed castor


17

3.3.4. Encoder Wheel

To save the project cost, an encoder set is dissected from an unused computer
mouse. One of the important elements that can be applied in my project is the rubber
encoder wheel. It is 22mm in diameter and placed beside of the right mini wheel. The
encoder wheel is used to count distance that the robot travels.

Figure 3.6: Encoder wheel in computer mouse

3.4. Electronic Circuitry

The main function of circuitry is to interface with environment that the robot
situated in. Then the data received will be analyzed and the shortest path from start
point to end point will then be calculated. The circuitry consists of embedded system
to execute algorithm and control the movement of robot.

The circuit design divided into 3 boards. Every board is arranged according to
its functionality. The top layer is main board, which consists of power supply circuit,
push button, led as indicator, LCD display, switches and microchip dsPIC30F4011.
The middle layer is motor driver and encoder circuitry board while bottom layer is
sensor board which used for line sensing. The schematics are firstly designed and
sketched using gEDA Schematic Editor. All of the circuit boards are then constructed
and tested.
18

3.4.1. Power Supply Circuit

The power supply circuit is used to provide voltage to other components such
as microchip, comparator, motor driver, sensor, etc. There are three voltage
regulators in my circuit design as shown in Figure 3.7. Two units of 7805 and one
unit of 7809 are used for different purposes. The output voltage from 7805 circuit set
is +5V while 7809 circuit set generates +9V. Both of 7805 voltage regulator circuits
are used as power supply for microcontroller and comparator. The main function of
7809 circuit set is to supply power to motor driver.

A switch is connected series with power supply to on and off power of whole
circuitry. A diode IN4007 is also connected series with switch so that it will act as an
open circuit when the input voltage is inversed. This is a safety precaution to prevent
voltage regulator from damage or burning. 1000uF and 0.1uF capacitors are used to
stabilize the output voltage. LED indicators are used to indicate the proper function
of the circuit.

Figure 3.7: Power supply circuit


19

3.4.2. Microcontroller

The maze solving robot has an embedded microcontroller responsible for


movement and path searching. The microcontroller from Arizona Microchip as
shown in Figure 3.8 called dsPIC30F4011 is chosen.

Figure 3.8: Microchip dsPIC30F4011

Microchip dsPIC30F4011 is a high performance with 16 bit digital signal


controller [10]. It features a high speed core optimized to perform complex
calculations quickly. The dsPIC30F4011 includes a wide range of timers together
with a number of PWM modules for adjustable motor speed control. It is selected
due to its specialties. There are a large number of general I/O ports available for easy
interfacing to external devices. The operating speed of dsPIC30F4011 is at
29.4912MHz which considered as a high speed performance. Therefore, the
dsPIC30F4011 is an ideal microcontroller for Motor Control Applications.

Figure 3.9: Pin diagram of Microchip dsPIC30F4011


20

3.4.2.1. I/O Pin Assignation

Microchip dsPIC30F4011 is the most important element for the intact


embedded system. Nothing can be function without it because it processes the entire
signal input or output for the system. Microcontroller contains several of general
purpose input and output pins (GPIO). GPIO pins are software configurable to either
an input or an output state. When GPIO pins are configured to an input state, they are
often used to read sensors or external signals. Configured to the output state, GPIO
pins can drive external devices such as LEDs or motors.

Therefore, there is very important to assign input or output pins in order to


make use of the microcontrollers features. The pin configuration is made according
to the functionalities of devices as shown in Figure 3.10. Positive 5V and 0V are
connected as a power supply to the microcontroller. Besides, a crystal is also
connected to the OSC1 and OSC2 pins in order to generate pulse for microcontroller.
Two 33pF capacitors are connected to both of crystal pins to provide stabilization to
oscillator.

Figure 3.10: Pin assignation of Microchip dsPIC30F4011


21

3.4.3. LCD Display

A LCD HD44780U as shown in Figure 3.11 is used to display data during


development process. It is an important device to show the coding error.

Figure 3.11: LCD HD44780U

The Figure 3.12 shows a schematic of LCD that constructed a complete LCD
device. This LCD is used as an output device to the microcontroller. The 5th pin of
LCD which is R/W enables the LCD to read or write data.

Figure 3.12: Schematic of LCD HD44780U


22

3.4.4. Push Button

There are two units of push button used for mode execution to the robot.
Figure 3.13 shows the push button image. The push buttons are inputs to the
microcontroller. The circuit connection of push button is shown as Figure 3.14.

Figure 3.13: Push button

Figure 3.14: Schematic of push button

3.4.5. Motor Driver L293D

A motor driver L293D as shown in Figure 3.15 is used as output from


microcontroller to control the Tamiya motor.

Figure 3.15: Motor Driver L293D


23

The Figure 3.15 shows a simple schematic for interfacing a DC motor using
L293D [11]. L293 H-Bridge IC is a dual H-Bridge motor driver. It is used to
interface two DC motors which can be controlled in both clockwise and counter
clockwise direction. L293D has output current of 600mA and peak output current of
1.2A per channel. The output supply has a wide range from 4.5V to 36V, which has
made L293D a best choice for DC motor driver. There are two PWM pin that used to
control rpm of motor.

Figure 3.16: Motor Driver L293D interface

The schematic of motor driver schematic is designed based on the interface


above. Power supply of +9V provided from power supply set using voltage regulator
of 7809. The circuit connection of motor driver L293D is shown as Figure 3.17.

Figure 3.17: Schematic of motor driver L293D


24

3.4.6. Incremental Encoder

In order to find out the shortest path from a point to another point, the
distance between the two points must be known. The best way to save cost and
calculate distance accurately is to use the incremental encoder of an unused mouse.
The encoder wheel as shown in Figure 3.18 has been extracted from a mouse.

Figure 3.18: Incremental encoder

An encoder is a device that converts information from one format to another,


for the purposes of standardization, speed, etc. The incremental encoder is the most
widely used of all rotary encoders due to its low cost. Thus, an incremental encoder
is applied in this project. Basically it includes two tracks whose outputs care called
channels A and B. Pulse trains occur on these channels at a frequency proportional to
the shaft speed whenever the shaft rotates. The phase relationship between the
signals yields the direction of rotation. Figure 3.19 shows the code disk pattern and
output signals channels A and B. The angular motion can be measured by counting
the number of pulses and knowing the resolution of the disk [12].

Figure 3.19: Incremental encoder disk track patterns


25

The increment encoder is used as input to the microcontroller. Figure 3.20


shows the schematic of incremental encoder.

Figure 3.20: Schematic of incremental encoder

3.4.7. IR Sensor with Comparator LM324

There are six Infra Red (IR) sensors used for purpose of line sensing. The
basic idea is to send infra red light through IR-LED, which is then reflected by the
black colored tape in front of the sensor. There is another IR-LED used for detecting
the reflected IR light. Figure 3.21 illustrates the basic design of the Infra Red sensor.

Figure 3.21: Basic design of the Infra Red sensor


26

The sensitivity of sensors is controlled by a potentiometer which acts as a


voltage divider. The potentiometer provides a voltage reference to comparator
LM324. Figure 3.22 shows a comparator LM324. The different of voltage between
potentiometer and IR sensor can be read as either logic 0 or 1. Figure 3.23 illustrates
the schematic of a comparator LM324.

Figure 3.22: Comparator LM324

Figure 3.23: Schematic of LM324

The combination of comparator and IR sensors circuit is designed. This


circuitry is divided into two parts. Figure 3.24 shows the schematic of first part
which consists of IR sensors.
27

Figure 3.24: Schematic of IR sensors

Since there are only four sensors available for one comparator LM324, two
units of LM324 are required in order to connect six sensors in this project. Therefore,
the circuit connection of comparator is assigned as second part. Figure 3.25 shows
the schematic of comparator LM324.

Figure 3.25: Schematic of comparator LM324


28

3.5. Software

Some theories and design techniques will be implemented into the maze
solving robot. The programming part includes path planning, line following, distance
measurement and Dijkstras algorithm. The sections below give a more detailed
overview into each of them.

Figure 3.26 illustrates the overall concept of algorithm for maze solving robot.
The combination of all algorithms yields a functional maze solving robot that able to
move following line, measured distance accurately, memorized maze and finds out a
shortest path. These steps are shown below as a flowchart.

Start

Line following

Distance Measurement

Maze exploration

No
Complete path?

Yes

Dijkstras algorithm

Shortest path execution

End

Figure 3.26: Flow chart of maze solving robot


29

3.5.1. Line Following

The maze solving robot is moved following black colored tape. The six units
of IR sensors used to read analogue value from reflection of those black colored tape.
The analogue values of IR sensors are then digitalized by two comparators as
mentioned in Section 3.4.7. The flowchart of line following features is shown in
Figure 3.27.

Start

Input from IR sensors

Speed adjustment

Yes
Out of line? Direction adjustment

No
No
Need to stop?

Yes
Stop

End

Figure 3.27: Flow chart of line following features


30

3.5.2. Distance Measurement using QEI

The Quadrature Encoders Interface (QEI) is one of the main features of


dsPIC30F4011 [12]. The quadrature encoders are used in position and speed
detection of rotating motion systems. It is a suitable device to measure distance
displaced by a rotating motor. Thus, an encoder dissected from mouse is used to
calculate distance that maze solving robot travelled. There are four unique states
produced by the quadrature signals in one count cycle. Figure 3.28 shows the
Quadrature Encoder Interface signals.

By using Quadrature Encoder, the direction of robot can be easily known.


There is a certain count sequence for moving forward or backward. To implement
QEI, there are four user-accessible control and status registers that need to be
configured. The registers are accessible in either byte or word mode. The registers
are listed below:

i. Control/Status Register (QEICON) To control the QEI operation and status


flags indicating the module state.

ii. Digital Filter Control Register (DFLTCON) To control the digital input
filter operation.

iii. Position Count Register (POSCNT) To read and write of the 16-bit position
counter.

iv. Maximum Count Register (MAXCNT) To hold a value that will be


compared to the POSCNT counter in some operations.
31

Figure 3.28: Quadrature Encoder Interface Signals

3.5.3. Path Planning

The path planning is very crucial for the maze solving robot to identify every
single point passed through. There is a 3x3 in matrix form of maze. Basically the
starting point and ending point are fixed as shown as Figure 1.1 before maze solving
robot start to explore the maze. There are two sets of path planning. One is for maze
exploration and another one for shortest path execution. The path planning is based
on maze of Figure 1.1. The Coordinate of starting point as (2, 1) and ending point is
(0, 1). The flowchart of maze exploration is shown in Figure 3.29.

Figure 3.29: Flow chart of maze exploration


32

After the maze exploration done, the maze solving robot will then analyses
the data obtained through Dijkstras algorithm. At last, the maze solving robot will
direct move to the destination in shortest route. The flowchart of shortest path
execution is shown in Figure 3.30.

Figure 3.30: Flow chart of shortest path execution

3.5.4. Dijkstras Algorithm

The Dijkstras algorithm is an algorithm used to calculate the shortest path


from a starting node to all other nodes whether directed or undirected. To do this, the
direct path from a starting nod to the separate nodes is noted as the shortest way.
33

These steps are shown below as a flowchart.

Start

Assign to every node a distance value, Y:


0 for initial node
for all other nodes.

Mark all nodes as unvisited.


Set initial node as current.

Calculate tentative distance, Y from the


current node to unvisited neighbors

Yes
Current Y < Previous Y?

Overwrite previous Y
No

No Done considering
all neighbors?

Set the unvisited Yes


node with
smallest Y Mark current node as visited

No All nodes have


been visited?

Yes

End

Figure 3.31: Flow chart of Dijkstras algorithm


34

3.5.4.1. Example of Dijkstras Algorithm

In the following steps, the lowest distance not yet visited node is chosen and
it is checked if there is a node that can be reached from there with lower distance
than before. Dijkstra's algorithm will assign some initial distance values and will try
to improve them step by step. Let node A to be initial node.

Step 1: Set 0 to node A to and infinity to all other nodes.

Figure 3.32: Step 1 of Dijkstras algorithm

Step 2: Mark all nodes as unvisited. Set initial node as current.

Table 3.1: Step 2 of Dijkstras algorithm


Step Tentative distance Visited?
B C D E
Initial -

Step 3: For current node A, consider all its unvisited neighbors that are node B and C.
Tentative distance from the initial node to node B and node C will be calculated.

Since the distance B and C are less than the previously recorded distance which is
infinity in the beginning, the distances have been overwritten.
35

Step 4: Since all neighbors of the current node A have been done considering, node
A is marked as visited.

Figure 3.33: Step 3 of Dijkstras algorithm

Table 3.2: Step 3 of Dijkstras algorithm


Step Tentative distance Visited?
B C D E
Initial 3 1 A

Step 5: The visited node A will not be checked ever again. Thus, its distance
recorded now is final and minimal. The unvisited node C with the smallest distance
from the initial node is set as the next current node and continues from step 3.

Figure 3.34: Step 5 of Dijkstras algorithm

Table 3.3: Step 5 of Dijkstras algorithm


Step Tentative distance Visited?
B C D E
Initial 3 1 A
1 2 1 5 C
36

Step 6: The node C is marked as visited and will not be checked anymore. The
unvisited node B with the smallest distance from the initial node is set as the next
current node and continues from step 3.

Figure 3.35: Step 6 of Dijkstras algorithm

Table 3.4: Step 6 of Dijkstras algorithm


Step Tentative distance Visited?
B C D E
Initial 3 1 A
1 2 1 5 C
2 2 1 3 6 B

Step 7: The node B is marked as visited. The unvisited node D with the smallest
distance from the initial node is set as the next current node and continues from
step 3.

Figure 3.36: Step 7 of Dijkstras algorithm


37

Table 3.5: Step 7 of Dijkstras algorithm


Step Tentative distance Visited?
B C D E
Initial 3 1 A
1 2 1 5 C
2 2 1 3 6 B
3 2 1 3 5 D

Step 8: All of the nodes have been visited and a shortest path from initial node A to
node E is found.

Figure 3.37: Step 8 of Dijkstras algorithm

Figure 3.30 and Table 3.5 above illustrate the shortest path from node A to node E
and distance accumulated for each node. The result is: A  C  B  D  E.

3.6. Maze Mechanical Drawing

A 3x3 in matrix form of maze is designed as the platform for exploration of


maze solving robot. There are totally nine nodes in the maze as shown in Figure 1.1.
Several card boards are constructed with different pattern of line following as shown
in Figure 3.38. Combination of those different patterns of card boards brings out a
complete maze. This method is flexible to make various pattern of maze. To reduce
the friction force between card board and motor of robot, a piece of mahjong paper is
pasted on the card board. Multipurpose adhesive is used to stick the cardboards
together.
38

The different patterns of card boards that used to construct maze is shown as
figure below.

Figure 3.38: Patterns of card boards

Here some examples for the maze that combined with several patterns of card
board.

Junction
Destination
Starting point

Figure 3.39: Example of maze 1

Junction
Destination
Starting point

Figure 3.40: Example of maze 2


CHAPTER 4

RESULT AND DISCUSSION

4.1. Introduction

The results obtained are discussed in this chapter. This section involves
design of hardware, circuitry and maze field, and some features such as line-
following and distance measurement. Maze solving test is discussed in the last
section.

4.2. Hardware Design

The hardware of maze solving robot is designed as mentioned before in


methodology. The maze solving robot with following specification has been
completely constructed.

Table 4.1: The maze solving robot specification


Specification Description
Length 14 cm
Height 9 cm
Width 11.5 cm
Weight 0.45 kg
Input 6 units of IR sensors, 1 unit of incremental rotary encoder
Output Tamiya motor, LCD display
40

The Figure 4.1 below shows the different view of maze solving robot.

Front View Rear View

Left View Right View

Top View 450 of angle

Figure 4.1: Views of the maze solving robot


41

4.3. Circuitry Design

The electronic circuitry consists of three boards, namely the main embedded
microcontroller board, motor control board and sensor board. The main controller
board consists of microcontroller dsPIC30F4011 from the Arizona Microchip and
other integrated circuit such as push button, LCD and the like. The motor control
board is used to control the direction of robot while the sensor board provides the
feedback of the current position of robot.

Voltage
regulator

dsPIC30F4011 LCD

Comparator
LM324

Figure 4.2: Embedded microcontroller board

Encoder

IR Sensors
Motor Driver L293D

Figure 4.3: Sensor board with motor control board


42

4.4. Maze Field Design

The overall concept of a maze for the robot is described in Section 3.6 Maze
Mechanical Drawing. Figure 4.4 shows the complete maze field that using for the
exploration of maze solving robot. The maze field constructed is light in weight, easy
to build up and flexible to construct any path.

Figure 4.4: Maze field

4.5. Line following Feature

The line-following feature is very crucial for the maze solving robot to find a
correct path from a point to another point. The maze solving robot is able to move
according to the input from IR sensors that used for black colored line sensing as
shown in Figure 4.5.
43

Figure 4.5: Line following of maze solving robot

4.6. Distance Measurement Feature

After the robot succeeds to move with line-following, distance measurement


is the next essence feature for the maze solving robot to calculate the shortest path
from starting point to destination. The maze solving robot measures distance by
using incremental rotary encoders with QEI. The data obtained is displayed to the
LCD screen as reference. Figure 4.6 shows the distance measurement by maze
solving robot. The actual length of line is 523mm.

Figure 4.6: Distance measurement


44

The distance measured by maze solving robot is shown in LCD screen.


Figure 4.7 reveals the final result at the end of line.

0mm

Initial

523mm 523mm 521mm

Result 1 Result 2 Result 3

523mm 523mm

Result 4 Result 5

Figure 4.7: Results of distance measurement


45

The data obtained was analyzed. The average error and percentage error of
distance measurement using rotary encoder were then calculated.

Table 4.2: The result of distance measurement


Test Distance measured, mm Error, |523mm Distance measured|
1 523 0
2 523 0
3 521 2
4 523 0
5 523 0
Sum of errors 2

Average error = Sum of errors / n


= 2/5
= 0.4mm

% Error = Average error / Actual Distance X 100%


= 0.4 / 523 X 100 %
= 0.076%

4.7. Maze Solving Test

The robot arm is being tested in the maze shown in Figure 4.8. The shortest
path is shown in that figure.

The robot will firstly start its exploration to the maze. Then, Dijkstras
algorithm is implemented to analyze the shortest path. The next route of robot will be
executed by the result calculated using Dijkstras algorithm.

Ultimately, the maze solving robot was succeed to explore the test maze and
direct go the destination at second route. The main objective of this project is finally
achieved.
46

Figure 4.8 shows the test maze that being used for maze solving robot.

END

START

Figure 4.8: Test maze

Figure 4.9: The exploration of maze solving robot.


47

After the maze solving robot done the exploration as shown in Figure 4.9, the
shortest path found. The maze solving robot was heading to the destination through
shortest path as shown in Figure 4.10.

Figure 4.10: The shortest path execution by maze solving robot


CHAPTER 5

CONCLUSION

5.1. Conclusion

In the nutshell, the objectives of the project have been achieved. A maze
solving robot that able to negotiate a maze has been constructed. It can finds out the
shortest path in a maze by using Dijkstras algorithm. The maze solving robot is also
able to perform some features such as line following and distance measurement.
Several tests have been done to determine the proficiency of maze solving robot.

The comprehensive planning and design of maze solving robot at earlier stage
made the construction process carry out easy. Even using low cost material, the maze
solving robot is still able to perform well. The Dijkstras algorithm is implemented
and shown a good result in path finding case.

Besides, this project provides a good platform for academic learning. We can
apply some of the theories learnt during class and practice in this project. During
practical time, we are able to understand the actual condition to carry out a project
and trying to solve all the problems faced. Hence, critical thinking and problem
solving are the major issues for us to bring out a final year project succeedfully.
49

5.2. Recommendation

There are still some limitations of the maze solving robot that can be further
improved to achieve goal. The possible improvements to be carried out if the project
is undertaken by somebody else in the future are listed below:

1. Actuator In order to save cost, Tamiya motor is selected as the actuator for
maze solving robot. To get a faster and stable response, there are some other
commercially available advanced motors might be to use such as stepper, DC
motor, etc. But this will increases the cost of the project dramatically.

2. Encoder The economically rotary encoder that dissected from mouse is


actually easy to break. It is not suitable to install at the bottom side of robot.
However, the most ideal location for distance measurement is at the center of
robot. Perhaps this problem can be fixed in mechanism or replaced by a better
encoder.

3. PCB boards for circuitry All of the embedded systems are constructed and
soldered using hand. Sometimes, it is take time to trouble shoot and check the
circuit. This condition leads to instability of maze solving robot during
performance. A PCB layout will better improve the system circuitry.

4. Maze field This kind of maze field is easy to build up and flexible. At the
same time, the platforms are easy to move once it is not stick strongly. This
caused the robot can not perform well. Hence, a good maze field is required to
enhance the performance of maze solving robot.
50

REFERENCES

[1] Michael Gims.. Micromouse.University of East London; 1999

[2] S. S Wong.. Micromouse The Maze Solving Robot. Bachelor Thesis.


University Technology Malaysia; 2006

[3] Indran A/L Thandavarian. Micromouse The Maze Solving Robot. Bachelor
Thesis. University Technology Malaysia; 2006.

[4] Y. C. Chang.. Micromouse Maze Solving Robot. Bachelor Thesis. Universiti


Teknologi Malaysia; 2009

[5] C. H. Lew.. Explorer Robot. Bachelor Thesis. Universiti Teknologi Malaysia;


2010

[6] http://en.wikipedia.org/wiki/A*_search_algorithm

[7] http://allmybrain.com/2008/06/02/the-difference-between-dijkstras-
algorithm-and-a/

[8] http://www.xtremevbtalk.com/showthread.php?t=86509

[9] http://www.cytron.com.my/

[10] http://www.futurlec.com/dsPIC30F4011_Controller.shtml

[11] L298 datasheet, 2000.

[12] dsPIC30F Family Reference Manual, 2003.


51

APPENDIX A Gantt Chart for Semester 1 and Semester 2

Table 6.1: FYP 1 Gantt chart


Week 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Project
Proposal
Literature
Review
Software
Design
Hardware
Design
Components
Purchasing
Main Board
Circuitry
Report
Writing
Presentation

Table 6.2: FYP 2 Gantt chart


Week 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Complete
Hardware
Complete
Plateform
Encoder
Measurement
Coding
Report
Writing
Presentation
Thesis
Compilation
52

APPENDIX B Header File Source Code

Dijkstra.h
#ifndef DIJKSTRA_H
#define DIJKSTRA_H

void Dijkstra_send_data(unsigned char cEdge1, unsigned char cEdge2, unsigned long lDistance);
void Dijkstra_analyse_data(void);
void Dijkstra_go_path(int dest);
#endif

Encoder.h
#ifndef ENCODER_H
#define ENCODER_H

extern unsigned int encoder_iPosWrapped;


extern unsigned long encoder_lCurrentDistance;
extern unsigned long encoder_lPosElapsed;

enum Encoder_instruction {junction_turn, out_of_line_turn, _90turn};

void Encoder_distance_measurement(void);
void Encoder_set_distance(unsigned char Encoder_instruction);

#endif

LCD_Commands.h
#ifndef LCD_COMMANDS_H
#define LCD_COMMANDS_H

#include <p30fxxxx.h>

#define RS _LATC13
#define ENA _LATC14

enum LCD_instruction {clear_display, return_home, cursor_increment, cursor_decrement,


display_shift_on, display_shift_off, display_on, display_off, cursor_on, cursor_off, blink_on,
blink_off, shift_cursor_left, shift_cursor_right, shift_display_left, shift_display_right,
four_bits_data_length, eight_bits_data_length, one_display_line, two_display_lines,
five_times_eight_font, five_times_ten_font, write_IR, write_DR};

void LCD_printscr_string(unsigned char y, const char *buffer);


void LCD_menu(unsigned char position, unsigned char blink_no, const char *,
unsigned long display_delay);
void LCD_printxy_string_delay(unsigned char x, unsigned char y,
const char *buffer, unsigned long delay);
53

void LCD_printxy_string(unsigned char x, unsigned char y, const char *);


void LCD_printxy(unsigned char x, unsigned char y, const char character);
void LCD_erase(unsigned char x, unsigned char y, unsigned char no_blank);
void LCD_char_to_bits(unsigned char x, unsigned char y, unsigned char character);
void LCD_char_to_no(unsigned char x, unsigned char y, unsigned char digit_no, unsigned char
character);
void LCD_int_to_no(unsigned char x, unsigned char y, unsigned char digit_no, unsigned int integer);
void LCD_print_string(const char *);
void LCD_print(const char character);
void LCD_command(unsigned char LCD_instruction);
void LCD_init(void);
void LCD(unsigned char data, unsigned char LCD_instruction);
void LCD_set_cursor(unsigned char address);
void LCD_write_data(unsigned char addr);

// Virtual function, must be implemented in other source file


void Delay(unsigned long interval);

#endif

Motor_Control.h
#ifndef MOTOR_CONTROL_H
#define MOTOR_CONTROL_H

#include "MSR_Config.h"

extern flag_t motor_control_dijkstra;


extern unsigned long temp_encoder_lCurrentDistance;

void Motor_Control_line_following(void);
unsigned char Motor_Control_update_line(void);
void Motor_Control_Send_Data(void);
void Motor_Control_Run_Dijkstra(void);
void Motor_Control_forward(void);
void Motor_Control_backward(void);
void Motor_Control_turn_left(void);
void Motor_Control_turn_right(void);
void Motor_Control_stop(void);
void Motor_Control_break(void);
void Motor_Control_rotate_left(void);
void Motor_Control_rotate_right(void);
void Motor_Control_junc_rotate_right(void);
void Motor_Control_junc_rotate_left(void);

#endif

MSR_Config.h
#ifndef MSR_CONFIG_H
#define MSR_CONFIG_H

//Input
#define Button1 _RF4
54

#define Button2 _RF5


#define Sensor_Left_Most _RB8
#define Sensor_Left _RD3
#define Sensor_Middle_Left _RD1
#define Sensor_Middle_Right _RD2
#define Sensor_Right _RD0
#define Sensor_Right_Most _RF6

//Output
#define led _LATE8
#define Motor_Left_1 _LATF0
#define Motor_Left_2 _LATE2
#define Motor_Right_1 _LATF1
#define Motor_Right_2 _LATE0
#define pwm_Left PDC1
#define pwm_Right PDC2

//Constant
#define _1s 1999998UL
#define _100ms (_1s / 10)
#define _10ms (_100ms / 10)
#define _1ms (_10ms / 10)

//in unit of mm
#define config_encoder_perimeter 78
#define config_encoder_pulse 12
#define config_total_encoder_pulse (4 * config_encoder_pulse)
#define config_min_poscnt 2000

//Line following constant


#define config_pwm_max 1100
#define config_pwm_left 755
#define config_pwm_right 770
#define config_pwm_junction 700
#define config_pwm_junction_path 670

#define config_right_0001 160


#define config_right_0X11 90
#define config_right_0010 60
#define config_left_1000 160
#define config_left_11x0 90
#define config_left_0100 60

//Add task size


#define config_number_of_task 10

//Dijkstra's Algorithm
//Number of nodes
#define config_nodes 3
#define config_no_of_nodes (config_nodes * config_nodes)
#define config_no_of_edges (config_nodes * (config_nodes - 1) * 2)
#define config_INFINITY -1

//Structure
typedef union {
struct {
//0: Out of line during line following
unsigned out_of_line : 1;
//0: No corner found
//1: Turn left
55

//2: Turn right


unsigned corner_turn : 2;
unsigned : 5;
};
unsigned char byte;
} status_t;

typedef union {
struct {
//1: Valid edge, proceed to path planned
unsigned send_dijkstra : 1;
//1: Done analyzed, proceed to shortest path
unsigned run_dijkstra : 1;
//1: Destination reached, the end of path
unsigned end_path : 1;
//1: Meet junction during running dijstra algorithm
unsigned junction_dijkstra : 1;
//1: Non-valid edge
unsigned detect_edge : 1;
unsigned end_dijkstra : 1;
unsigned : 2;
};
unsigned char byte;
} flag_t;

typedef union {
struct {
unsigned node_3 : 1;
unsigned node_4 : 2;
unsigned node_5 : 1;
unsigned node_6 : 2;
unsigned node_8 : 2;
unsigned node_9 : 1;
unsigned node_10 : 2;
unsigned node_13 : 2;
unsigned node_15 : 1;
unsigned count_valid : 1;
unsigned : 1;
};
unsigned int word;
} direction_t;

//*****************************Function Prototype*************************
void Delay(unsigned long lInterval);
void init(void);
void __attribute__((__interrupt__,auto_psv)) _QEIInterrupt(void);
void __attribute__((__interrupt__,auto_psv)) _T3Interrupt(void);
void __attribute__((__interrupt__,auto_psv)) _T4Interrupt(void);
void __attribute__((__interrupt__,auto_psv)) _OscillatorFail(void);
void __attribute__((__interrupt__,auto_psv)) _AddressError(void);
void __attribute__((__interrupt__,auto_psv)) _StackError(void);
void __attribute__((__interrupt__,auto_psv)) _MathError(void);

//Variable Declaration
extern unsigned int pwm_Left_acc, pwm_Right_acc;

#endif
56

Path.h
#ifndef PATH_H
#define PATH_H

void Path_fixed_point_new(void);

#endif

Task.h
#ifndef TASK_H
#define TASK_H

#include "MSR_Config.h"

typedef void (*ptrTaskFunc)(void);

extern unsigned char task_cUnit;


extern ptrTaskFunc task_cTask[config_number_of_task];
extern unsigned long task_cTiming[config_number_of_task];

void Task_add(void (*cFunc)(), unsigned long cDuration);

#endif
57

APPENDIX C Embedded Microcontroller Source Code

Dijkstra.c
#include "Dijkstra.h"
#include "MSR_Config.h"
#include "LCD_Commands.h"

unsigned long Dijkstra_lEdge_Dist[config_no_of_nodes][config_no_of_nodes] = {{0}};


unsigned long Dijkstra_lShortest_Dist[config_no_of_nodes] = {0};
unsigned long Dijkstra_lPrev[config_no_of_edges] = {0};
unsigned int Dijstra_Dest[config_no_of_nodes + 2] = {0};

//Recieve data of distance from node to node


void Dijkstra_send_data(unsigned char cEdge1,unsigned char cEdge2,unsigned long lDistance){
static unsigned char count_edges = 0;

Dijkstra_lEdge_Dist[cEdge1][cEdge2] = lDistance;
Dijkstra_lEdge_Dist[cEdge2][cEdge1] = lDistance;

if(++count_edges == config_no_of_edges) {
count_edges = 0;
Dijkstra_analyse_data();
}
}

//Analyze the shortest path from starting point to ending point


void Dijkstra_analyse_data(void) {
int i, k, minimum;
int visited[config_no_of_nodes];

for (i = 1; i <= config_no_of_nodes; ++i) {


//Step 1 : Set infinity for all other nodes
Dijkstra_lShortest_Dist[i] = config_INFINITY;
//No path has yet been found to i
Dijkstra_lPrev[i] = -1;
//Step 2 : Mark all nodes as unvisited
visited[i] = 0;
}

//Step 1 : Set 0 for initial node


Dijkstra_lShortest_Dist[1] = 0;

for (k = 1; k <= config_no_of_nodes; ++k) {


minimum = -1;

//Step 2 : Set initial node as current


//Step 3 : Set another current node
for (i = 1; i <= config_no_of_nodes; ++i) {
if (!visited[i] && ((minimum == -1) || (Dijkstra_lShortest_Dist[i]
< Dijkstra_lShortest_Dist[minimum]))) {

minimum = i;
}
}
58

//Step 4 : Mark it as visited


visited[minimum] = 1;

//Step 3 : Calculate the tentative distance of unvisited neighbors


for (i = 1; i <= config_no_of_nodes; ++i) {
if (Dijkstra_lEdge_Dist[minimum][i]) {
//Updated Dijkstra_lShortest_Dist[i] at first
If ((Dijkstra_lShortest_Dist[minimum] +
Dijkstra_lEdge_Dist[minimum][i]) <
Dijkstra_lShortest_Dist[i]) {

Dijkstra_lShortest_Dist[i] =
Dijkstra_lShortest_Dist[minimum] +
Dijkstra_lEdge_Dist[minimum][i];

Dijkstra_lPrev[i] = minimum;
}
}
}
}
}

//Show the shortest path from starting point to ending point


void Dijkstra_go_path(int dest) {
static int i = 0;

//Recursive function
if (Dijkstra_lPrev[dest] != -1)
Dijkstra_go_path(Dijkstra_lPrev[dest]);

Dijstra_Dest[i++] = dest;
}

Encoder.c
#include <p30fxxxx.h>
#include "Encoder.h"
#include "MSR_Config.h"
#include "Motor_Control.h"
#include "LCD_Commands.h"

extern flag_t motor_control_dijkstra;

unsigned int encoder_iPosWrapped = 0;


unsigned long encoder_lCurrentDistance = 0;
unsigned long encoder_lPosElapsed = 0;

//Count the current distance travelled along line following


void Encoder_distance_measurement(void) {
encoder_lPosElapsed = encoder_iPosWrapped*(0xffff-config_min_poscnt) + POSCNT;
encoder_lCurrentDistance = (encoder_lPosElapsed - 2000) * config_encoder_perimeter
/ config_total_encoder_pulse;

if(motor_control_dijkstra.junction_dijkstra == 0) {
//Display the current distance travelled
LCD_printxy_string(0, 0, "Dist:");
LCD_int_to_no(6, 0, 6, encoder_lCurrentDistance);
59

}
}

void Encoder_set_distance(unsigned char Encoder_instruction) {


unsigned int temp_lCurrentDistance = 0;
unsigned int temp_lPosElapsed = 0;
unsigned long lDistance = 0;
unsigned long lDesiredPos = 0;
unsigned long lDesiredDisplacement = 0;

temp_lPosElapsed = encoder_lPosElapsed;
temp_lCurrentDistance = encoder_lCurrentDistance;

switch(Encoder_instruction) {
case junction_turn : lDesiredDisplacement = 43; break;
case out_of_line_turn : lDesiredDisplacement = 50; break;
case _90turn : lDesiredDisplacement = 80; break;
default : break;
}

lDesiredPos = config_total_encoder_pulse * lDesiredDisplacement


/ config_encoder_perimeter;

do {
Encoder_distance_measurement();
lDistance = encoder_lCurrentDistance - temp_lCurrentDistance;
} while (encoder_lPosElapsed <= (temp_lPosElapsed + lDesiredPos));
}

LCD_Commands.c

#include "LCD_Commands.h"

unsigned char entry_mode_set, display_control, function_set;

void LCD_printscr_string(unsigned char y, const char *buffer) {


unsigned char string_size = 0, buffer2;

while(*buffer++) {
string_size++; }

buffer -= (string_size + 1);

if(string_size < 17){


buffer2 = 16 - string_size;
} else if(string_size > 16 && string_size < 33) {
buffer += 16;
buffer2 = 16;

//0b00100000 represent a blank space on ASCII code


while(*buffer != 0b00100000) {
buffer--;
buffer2--;
}

buffer -= buffer2;
}
60

if(string_size < 17) {


if(y == 1){
LCD_set_cursor(buffer2/2);
} else if(y == 0){
LCD_set_cursor(0x40+(buffer2/2));
} else if(y == 3){
LCD_set_cursor(0x10+(buffer2/2));
} else if(y == 2){
LCD_set_cursor(0x50+(buffer2/2));
}

LCD_print_string(buffer);
}
else if(string_size > 16 && string_size < 33) {
if(y == 1){
//actually it is (15 - buffer2 + 1), 1 is the blank space
LCD_set_cursor((16-buffer2)/2);
} else if(y == 3){
LCD_set_cursor(0x10+(16-buffer2)/2);
}

//second line string size, not includes the blank space


string_size -= (buffer2 + 1);

while(buffer2--){
LCD_print(*buffer++);
}

buffer++;

if(y == 1){
LCD_set_cursor(0x40+(16-string_size)/2);
} else if(y == 3){
LCD_set_cursor(0x50+(16-string_size)/2);
}

LCD_print_string(buffer);
}
}

void LCD_menu(unsigned char position, unsigned char blink_no,


const char *buffer, unsigned long display_delay) {

unsigned char string_size = 0;

while(*buffer++){
string_size++;
}

buffer -= (string_size + 1);

for(blink_no++ ; blink_no > 0; blink_no--) {


if(position == 0){
//page 1 lower left
LCD_set_cursor(0x40);
} else if(position == 1){
//page 1 lower right
LCD_set_cursor(0x50-string_size);
} else if(position == 2){
61

//page 2 lower left


LCD_set_cursor(0x50);
} else if(position == 3){
//page 2 lower right
LCD_set_cursor(0x60-string_size);
}

LCD_print_string(buffer);

if(blink_no > 1){


Delay(display_delay);

if(position == 0){
LCD_erase(0,0,4);
} else if(position == 1){
LCD_erase(12,0,4);
} else if(position == 2){
LCD_erase(16,0,4);
} else if(position == 3){
LCD_erase(28,0,4);
}

Delay(display_delay);
}
}
}

void LCD_printxy_string_delay(unsigned char x, unsigned char y, const char *buffer,


unsigned long display_delay) {

if(y == 1){
LCD_set_cursor(x);
} else if(y == 0){
LCD_set_cursor(0x40+x);
} else if(y == 3){
LCD_set_cursor(0x10+x);
} else if(y == 2){
LCD_set_cursor(0x50+x);
}

while(*buffer) {
LCD_print(*buffer++);
Delay(display_delay);
}
}

void LCD_printxy_string(unsigned char x, unsigned char y, const char *buffer) {


if(y == 1){
LCD_set_cursor(x);
} else if(y == 0){
LCD_set_cursor(0x40+x);
} else if(y == 3){
LCD_set_cursor(0x10+x);
} else if(y == 2){
LCD_set_cursor(0x50+x);
}

LCD_print_string(buffer);
}
62

void LCD_printxy(unsigned char x, unsigned char y, char character) {


if(y == 1){
//y = 1 --> page 1 upper row
LCD_set_cursor(x);
} else if(y == 0){
//y = 0 --> page 1 lower row
LCD_set_cursor(0x40+x);
} else if(y == 3){
//y = 3 --> page 2 upper row
LCD_set_cursor(0x10+x);
} else if(y == 2){
//y = 2 --> page 2 lower row
LCD_set_cursor(0x50+x);
}

LCD_print(character);
}

void LCD_erase(unsigned char x, unsigned char y, unsigned char no_blank){


if(y == 1){
LCD_set_cursor(x);
} else if(y == 0){
LCD_set_cursor(0x40+x);
} else if(y == 3){
LCD_set_cursor(0x10+x);
} else if(y == 2){
LCD_set_cursor(0x50+x);
}

for( ; no_blank; no_blank--){


LCD_print(' ');
}
}

void LCD_char_to_bits(unsigned char x, unsigned char y, unsigned char character) {


unsigned char i1, i2 = 0b10000000;

if(y == 1){
LCD_set_cursor(x);
} else if(y == 0){
LCD_set_cursor(0x40+x);
}

for(i1 = 0; i1 < 8; i1++) {


(character & i2) ? LCD_print('1') : LCD_print('0');
i2 >>= 1;
}
}

void LCD_char_to_no(unsigned char x, unsigned char y,


unsigned char digit_no, unsigned char character) {

unsigned char ten = 0, hundred = 0;

if(y == 1){
LCD_set_cursor(x);
} else if(y == 0){
LCD_set_cursor(0x40+x);
}
63

while(character > 99){


character -= 100;
hundred++;
}

while(character > 9){


character -= 10;
ten++;
}

//convert to string binary


hundred += 0b00110000;
ten += 0b00110000;
character += 0b00110000;

if(digit_no > 2){


LCD_print(hundred);
}
if(digit_no > 1){
LCD_print(ten);
}
if(digit_no > 0){
LCD_print(character);
}
}

void LCD_int_to_no(unsigned char x, unsigned char y, unsigned char digit_no, unsigned int integer) {
unsigned char ten = 0, hundred = 0, thousand = 0, ten_thousand = 0;

if(y == 1){
LCD_set_cursor(x);
} else if(y == 0){
LCD_set_cursor(0x40+x);
}

while(integer > 9999){


integer -= 10000;
ten_thousand++;
}
while(integer > 999){
integer -= 1000;
thousand++;
}
while(integer > 99){
integer -= 100;
hundred++;
}
while(integer > 9){
integer -= 10;
ten++;
}

//convert to string binary


ten_thousand += 0b00110000;
thousand += 0b00110000;
hundred += 0b00110000;
ten += 0b00110000;
integer += 0b00110000;

if(digit_no > 4){


64

LCD_print(ten_thousand);
}
if(digit_no > 3){
LCD_print(thousand);
}
if(digit_no > 2){
LCD_print(hundred);
}
if(digit_no > 1){
LCD_print(ten);
}
if(digit_no > 0){
LCD_print(integer);
}
}

void LCD_print_string(const char *buffer) {


while(*buffer) {
LCD_print(*buffer++);
}
}

void LCD_print(const char character) {


ENA=1;
LCD_write_data(character);
//write data register
RS=1;
ENA=1;
Delay(1);
ENA=0;
Delay(2000);
}

void LCD_command(unsigned char LCD_instruction) {


unsigned char a;

ENA=1;

switch(LCD_instruction) {
case clear_display : a = 0b00000001; break;
case return_home : a = 0b00000010; break;
case cursor_increment : a = 0b00000110 + (entry_mode_set &0b00000001);
entry_mode_set |= 0b00000010; break;
case cursor_decrement : a = 0b00000100 + (entry_mode_set &0b00000001);
entry_mode_set &= 0b11111101; break;
case display_shift_on : a = 0b00000101 + (entry_mode_set &0b00000010);
entry_mode_set |= 0b00000001; break;
case display_shift_off : a = 0b00000100 + (entry_mode_set &0b00000010);
entry_mode_set &= 0b11111110; break;
case display_on : a = 0b00001100 + (display_control&0b00000011);
display_control |= 0b00000100; break;
case display_off : a = 0b00001000 + (display_control&0b00000011);
display_control &= 0b11111011; break;
case cursor_on : a = 0b00001010 + (display_control&0b00000101);
display_control |= 0b00000010; break;
case cursor_off : a = 0b00001000 + (display_control&0b00000101);
display_control &= 0b11111101; break;
case blink_on : a = 0b00001001 + (display_control&0b00000110);
display_control |= 0b00000001; break;
case blink_off : a = 0b00001000 + (display_control&0b00000110);
65

display_control &= 0b11111110; break;


case shift_cursor_left : a = 0b00010000; break;
case shift_cursor_right : a = 0b00010100; break;
case shift_display_left : a = 0b00011000; break;
case shift_display_right : a = 0b00011100; break;
case four_bits_data_length : a = 0b00100000 + (function_set & 0b00001100);
function_set &= 0b11101111; break;
case eight_bits_data_length : a = 0b00110000 + (function_set & 0b00001100);
function_set |= 0b00010000; break;
case one_display_line : a = 0b00100000 + (function_set & 0b00010100);
function_set &= 0b11110111; break;
case two_display_lines : a = 0b00101000 + (function_set & 0b00010100);
function_set |= 0b00001000; break;
case five_times_eight_font : a = 0b00100000 + (function_set & 0b00011000);
function_set &= 0b11111011; break;
case five_times_ten_font : a = 0b00100100 + (function_set & 0b00011000);
function_set |= 0b00000100; break;
}

LCD_write_data(a);
ENA=1;
RS=0;
Delay(1);
ENA=0;

if(a == 0b00000001)
Delay(5454);
else if(a == 0b00000010)
Delay(5454);
else
Delay(1080);
}

void LCD_init(void) {
//LCD initialization
Delay(40905);
LCD(0b00110000, write_IR);
Delay(11454);
LCD(0b00110000, write_IR);
Delay(300);
LCD(0b00110000, write_IR);
Delay(300);
//8 bits data,2 line display, 5x8 font
LCD(0b00111000, write_IR);
//turn off display, cursor
LCD(0b00001000, write_IR);
//clear display
LCD(0b00000001, write_IR);
//auto-increment, end of LCD initialization
LCD(0b00000110, write_IR);
//turn on display, cursor, blinking
LCD(0b00001100, write_IR);
entry_mode_set = 0b00000010;
display_control = 0b00000100;
function_set = 0b00011000;
}

void LCD(unsigned char data, unsigned char LCD_instruction) {


LCD_write_data(data);
ENA=1;
66

switch (LCD_instruction) {
case 22 : // write_IR
RS=0;
//1 Tcy=100ns / Delay1TCY();
ENA=1;
Delay(1);
ENA=0;
if(data == 0b00000001)
Delay(5454);
else if(data == 0b00000010)
Delay(5454);
else
Delay(1080);
break;

case 23 : //write_DR
RS=1;
ENA=1;
Delay(1);
ENA=0;
Delay(2000);
break;
}
}

void LCD_set_cursor(unsigned char address) {


//0x00 <= address <= 0x27
//0x40 <= address <= 0x67
address += 0b10000000;
LCD_write_data(address);
RS=0;
//write instruction register
ENA=1;
Delay(1);
ENA=0;
Delay(1080);
}

void LCD_write_data(unsigned char data){


unsigned char temp_LATB, temp_LATE;

temp_LATB = (data & 0xcf) | (PORTB & 0x30);


temp_LATE = (data & 0x30) | (PORTE & 0xcf);
//Extract data of RB4 and RB5 to data LCD, then write to LCD
LATB = temp_LATB;
//Extract data out of RE4 and Re5 to data LCD,then write to data LCD
LATE = temp_LATE;
}

Motor_Control.c
#include <p30fxxxx.h>
#include "Motor_Control.h"
#include "MSR_Config.h"
#include "Dijkstra.h"
#include "Encoder.h"
67

#include "LCD_Commands.h"

extern unsigned int encoder_iPosWrapped;


extern unsigned long encoder_lCurrentDistance;
extern unsigned long encoder_lPosElapsed;

flag_t motor_control_dijkstra = {{0}};


unsigned long temp_encoder_lCurrentDistance;

void Motor_Control_line_following(void) {
unsigned char temp_QEIM = 0;
unsigned char cLine = Motor_Control_update_line();
static status_t status;

if(!(motor_control_dijkstra.end_path)) {
Motor_Control_forward();

switch(cLine & 0b00111111) {


case 0b00011111 : //Turn_Right();
status.corner_turn = 2; status.out_of_line = 1;
break;
case 0b00000001 :
case 0b00000011 : status.corner_turn = 2;
case 0b00000010:
status.out_of_line = 1;
pwm_Left = config_pwm_left;
pwm_Right = config_pwm_right - config_right_0001;
break;
case 0b00000100 :
status.out_of_line = 1;
pwm_Left = config_pwm_left;
pwm_Right = config_pwm_right - config_right_0010;
break;
case 0b00000111 :
case 0b00001111 : status.corner_turn = 2;
case 0b00000110 :
case 0b00001110 : status.out_of_line = 1;
pwm_Left = config_pwm_left;
pwm_Right = config_pwm_right - config_right_0X11;
break;
case 0b00111110 : //Turn_Left();
status.corner_turn = 1; status.out_of_line = 0;
break;
case 0b00100000 :
case 0b00110000 : status.corner_turn = 1;
case 0b00010000 : status.out_of_line = 0;
pwm_Left = config_pwm_left - config_left_1000;
pwm_Right = config_pwm_right;
break;
case 0b00001000 : status.out_of_line = 0;
pwm_Left = config_pwm_left - config_left_0100;
pwm_Right = config_pwm_right;
break;
case 0b00111000 :
case 0b00111100 : status.corner_turn = 1;
case 0b00011000 :
case 0b00011100 : status.out_of_line = 0;
pwm_Left = config_pwm_left - config_left_11x0;
pwm_Right = config_pwm_right;
break;
68

case 0b00001100 : //Forward();


pwm_Left = config_pwm_left;
pwm_Right = config_pwm_right;
break;
case 0b00000000 : //Out of line
case 0b00111111 : //Junction
break;
default : pwm_Left = config_pwm_left - 30;
pwm_Right = config_pwm_right - 30;
break;
}

if(motor_control_dijkstra.junction_dijkstra == 0) {
LCD_printscr_string(1, "Maze Exploration");
} else {
if(motor_control_dijkstra.end_dijkstra == 0)
LCD_printscr_string(1, "Heading to destination...");
}

if(((cLine & 0b00111111) == 0b00111111)


|| ((cLine & 0b00111111) == 0b00111110)
|| ((cLine & 0b00111111) == 0b00011111)
|| ((cLine & 0b00111111) == 0b00111100)
|| ((cLine & 0b00111111) == 0b00001111)
|| ((cLine & 0b00111111) == 0b00111000)
|| ((cLine & 0b00111111) == 0b00000111)
|| ((cLine & 0b00111111) == 0b00110000)
|| ((cLine & 0b00111111) == 0b00000011)
|| ((cLine & 0b00111111) == 0b00100000)
|| ((cLine & 0b00111111) == 0b00000001)){

while((cLine & 0b00110011) == 0b00110011) {


cLine = Motor_Control_update_line();
}

pwm_Left = pwm_Right = config_pwm_junction_path;


Motor_Control_break();
Delay(_100ms);
Motor_Control_forward();
Encoder_set_distance(junction_turn);
Motor_Control_break();
Delay(_100ms);
cLine = Motor_Control_update_line();

if((cLine & 0b00111111) == 0) {


//Out of line at T-junction
if((status.corner_turn == 1) || (status.corner_turn == 2)) {
//Disable QEI to ignore the distance travel
temp_QEIM = QEICONbits.QEIM;
QEICONbits.QEIM = 0;

(status.corner_turn == 2) ?
Motor_Control_rotate_right():
Motor_Control_rotate_left();

status.corner_turn = 0;
} //Out of line during line following
else {
temp_QEIM = QEICONbits.QEIM;
QEICONbits.QEIM = 0;
69

(status.out_of_line == 1) ?
Motor_Control_rotate_right() :
Motor_Control_rotate_left();
}

while(Sensor_Middle_Left && Sensor_Middle_Right);


Motor_Control_break();
Delay(_100ms);
//Enable QEI to continue count distance after out of line
QEICONbits.QEIM = temp_QEIM;
LCD_int_to_no(6, 1, 6, encoder_lCurrentDistance);
Motor_Control_forward();
} else {
//Valid edge, proceed to path planned
motor_control_dijkstra.send_dijkstra = 1;
Delay(_100ms * 5);

//Meet junction during running dijstra algorithm


if(motor_control_dijkstra.junction_dijkstra == 1) {
//Proceed to shortest path
motor_control_dijkstra.run_dijkstra = 1;
} else {
temp_encoder_lCurrentDistance=
encoder_lCurrentDistance;

//Store distance and proceed to next path


motor_control_dijkstra.run_dijkstra = 0;
}
}
} else if((cLine & 0b00111111) == 0) {
Motor_Control_break();
Delay(_100ms);
pwm_Left = pwm_Right = 650;

if(encoder_lCurrentDistance < 110) {


motor_control_dijkstra.detect_edge = 1;
Motor_Control_forward();
Encoder_set_distance(out_of_line_turn);
Motor_Control_break();
Delay(_100ms);
//Disable QEI to ignore the distance travel after out of line
temp_QEIM = QEICONbits.QEIM;
QEICONbits.QEIM = 0;

(status.out_of_line == 1) ?
Motor_Control_rotate_right() :
Motor_Control_rotate_left();
} else {
//Disable QEI to ignore the distance travel after out of line
temp_QEIM = QEICONbits.QEIM;
QEICONbits.QEIM = 0;

(status.out_of_line == 1) ?
Motor_Control_rotate_right() :
Motor_Control_rotate_left();
}

while(Sensor_Middle_Left && Sensor_Middle_Right);


Motor_Control_break();
70

if((Motor_Control_update_line() != 0b00001100)
|| (Motor_Control_update_line() != 0b00000100)
|| (Motor_Control_update_line() != 0b00001000)) {

pwm_Left = pwm_Right = 620;

if((Motor_Control_update_line() == 0b00010000)
|| (Motor_Control_update_line() == 0b00011000)
|| (Motor_Control_update_line() == 0b00001000)) {

Motor_Control_turn_left();
} else if((Motor_Control_update_line() == 0b00000001)
|| (Motor_Control_update_line() == 0b00000011)
|| (Motor_Control_update_line() == 0b00000010)) {

Motor_Control_turn_right();
}

while(Sensor_Middle_Left && Sensor_Middle_Right);


Motor_Control_break();
}

Delay(_100ms * 5);
//Enable QEI to continue count distance after out of line
QEICONbits.QEIM = temp_QEIM;
LCD_int_to_no(6, 1, 6, encoder_lCurrentDistance);
Motor_Control_forward();
}
}
}

unsigned char Motor_Control_update_line(void) {


unsigned char cLine = 0;

if(!Sensor_Left_Most){
cLine |= 0b00100000; }
if(!Sensor_Left){
cLine |= 0b00010000; }
if(!Sensor_Middle_Left){
cLine |= 0b00001000; }
if(!Sensor_Middle_Right){
cLine |= 0b00000100; }
if(!Sensor_Right){
cLine |= 0b00000010; }
if(!Sensor_Right_Most){
cLine |= 0b00000001; }

return cLine;
}

void Motor_Control_forward(void){
Motor_Left_1 = Motor_Right_1 = 0;
Motor_Left_2 = Motor_Right_2 = 1;
}

void Motor_Control_backward(void){
Motor_Left_1 = Motor_Right_1 = 1;
Motor_Left_2 = Motor_Right_2 = 0;
}
71

void Motor_Control_turn_left(void){
Motor_Left_1 = Motor_Right_1 = Motor_Left_2 = 0;
Motor_Right_2 = 1;
}

void Motor_Control_turn_right(void){
Motor_Left_1 = Motor_Right_1 = Motor_Right_2 = 0;
Motor_Left_2 = 1;
}

void Motor_Control_stop(void){
pwm_Left = pwm_Right = 0;
}

void Motor_Control_break(void){
Motor_Left_1 = Motor_Right_1 = Motor_Right_2 = Motor_Left_2 = 0;
}

void Motor_Control_rotate_left(void){
Motor_Left_2 = Motor_Right_1 = 0;
Motor_Left_1 = Motor_Right_2 = 1;
}

void Motor_Control_rotate_right(void){
Motor_Left_1 = Motor_Right_2 = 0;
Motor_Left_2 = Motor_Right_1 = 1;
}

void Motor_Control_junc_rotate_right(void) {
pwm_Left = pwm_Right = 0;
Motor_Control_rotate_right();
pwm_Left = pwm_Right = config_pwm_junction_path;

while((Motor_Control_update_line() & 0b00001100) != 0);


Delay(_100ms);
while(Sensor_Middle_Left && Sensor_Middle_Right);
Motor_Control_break();
}

void Motor_Control_junc_rotate_left(void) {
pwm_Left = pwm_Right = 0;
Motor_Control_rotate_left();
pwm_Left = pwm_Right = config_pwm_junction_path;

while((Motor_Control_update_line() & 0b00001100) != 0);


Delay(_100ms * 3);
while(Sensor_Middle_Left && Sensor_Middle_Right);
Motor_Control_break();
}

MSR.c
#include <p30fxxxx.h>
#include "MSR_Config.h"
#include "Dijkstra.h"
#include "Encoder.h"
72

#include "Task.h"
#include "Motor_Control.h"
#include "Path.h"
#include "LCD_Commands.h"

_FOSC(CSW_FSCM_OFF & XT_PLL8);


_FWDT(WDT_OFF);

#if __DEBUG
_FBORPOR(PBOR_ON & BORV_27 & PWRT_64 & MCLR_DIS);
#else
_FBORPOR(PBOR_OFF & PWRT_64 & MCLR_DIS);
#endif

extern unsigned int msr_iTMR4Wrapped;

//*************************************Mode**********************************
void Mode(unsigned char action) {
switch(action) {
case 0 : {
LCD_command(clear_display);
LCD_printscr_string(1, "MODE 23");
LCD_printxy_string(1, 0, "Line Following");
Delay(_1s);
LCD_command(clear_display);
Motor_Control_forward();

Task_add(Motor_Control_line_following, 1);
Task_add(Encoder_distance_measurement, 10);
Task_add(Path_fixed_point_new, 10);
while(1) { }
break;
}

default : {
unsigned char i;

LCD_printscr_string(3, "No Coding Inside!");


Delay(_1s );

for(i = 0; i < 16; i++) {


LCD_command(shift_display_left);
Delay(_100ms);
}

Delay(_1s * 2);

for(i = 0; i < 16; i++) {


LCD_command(shift_display_right);
Delay(_100ms);
}
break;
}
}
}

//*********************************Main Function*************************
int main(void) {
//For 2nd digit, _x
unsigned char mode_scroll_2 = 1;
73

//For 1st digit, x_


unsigned char mode_scroll_1 = 0;
unsigned long mode_delay = 0;

init();
Delay(_1ms * 5);

if(!(Button1 && Button2)) {


LCD_printscr_string(1, "Select MODE");

while(!(Button1 && Button2));


Delay(_100ms);

while(1){
LCD_int_to_no(7, 0, 1, mode_scroll_1);
LCD_int_to_no(8, 0, 1, mode_scroll_2);

if(!(Button1)){
Delay(_100ms);

if(++mode_scroll_2 == 6){
mode_scroll_2 = 0;
}
} else if(!(Button2)){
while(!(Button2)){
if(++mode_delay > 500000L){
LCD_command(clear_display);
LCD_printscr_string(1, "Mode Selected");
while(!Button2);
Delay(_100ms);
Mode((mode_scroll_1*10) + mode_scroll_2);
}
}

if(++mode_scroll_1 == 6){
mode_scroll_1 = 0;
}
}

Delay(_100ms);
mode_delay = 0;
}
}

Mode(0);

return 0;
}

MSR_Config.c
#include <p30fxxxx.h>
#include "MSR_Config.h"
#include "LCD_Commands.h"
#include "Motor_Control.h"
#include "Task.h"
74

extern unsigned int encoder_iPosWrapped;


extern unsigned char task_cUnit;
extern ptrTaskFunc task_cTask[config_number_of_task];
extern unsigned long task_cTiming[config_number_of_task];

unsigned int msr_iTMR4Wrapped = 0;


unsigned int pwm_Left_acc = 0, pwm_Right_acc = 0;

void Delay(unsigned long lInterval) {


while(lInterval--);
}

void init(void){
//**1 as input; 0 as output
TRISB = 0x0130;
TRISC = 0x8000;
TRISD = 0x000F;
TRISE = 0x0000;
TRISF = 0x0070;

//**Clear all port latch


LATB = LATC = LATD = LATE =LATF = 0;

//**PORTB all used as digital I/O


ADPCFG = 0xffff;

//**PWM
PWMCON1 = 0x0f30;
PWMCON2 = 0x0004;
PTCON = 0x2003;
PTPER = 511;
PTMR = 0x0000;
SEVTCMP = 0x0000;
FLTACON = 0x0000;
OVDCON = 0xFF00;
PDC1 = PDC2 = 0;

//**Type B Timer as check and execute task at certain time


T4CON = 0x2000;
TMR4 = 0;
PR4 = 2000;

//**QEI
QEICON = 0x2780;
DFLTCON = 0x01a0;
POSCNT = config_min_poscnt;
MAXCNT = 0xffff;

//**Interrupt
//Disable all user's interrupt
SRbits.IPL = 7;
INTCON1 = 0;
INTCON2 = 0;
//Clear all interrupt flag
IFS0 = IFS1 = IFS2 = 0;
IEC0 = IEC1 = IEC2 = 0;
_T3IP = 4;
_T4IP = 5;
_T3IE = 1;
_T4IE = 1;
75

//Enable interrupt
SRbits.IPL = 0;

//**Enable rigisters
PTCONbits.PTEN = 1;
T3CONbits.TON = 1;
T4CONbits.TON = 1;

//**Initialize output state


LCD_init();

//**"Reset" detection
#if __DEBUG
if(!(RCONbits.BOR && RCONbits.POR)){
LCD_printscr_string(1, "Reset");

while(1){
led =~ led;
Delay(_100ms * 5);
}
}
RCON = 0;
#endif
}

//*********************************Interrupt*****************************
void __attribute__((__interrupt__,auto_psv)) _QEIInterrupt(void) {
_QEIIF=0;

encoder_iPosWrapped++;
POSCNT = config_min_poscnt;

led = 1;
}

void __attribute__((__interrupt__,auto_psv)) _T4Interrupt(void) {


unsigned char i = 0;
static unsigned long iTimes = 0;

_T4IF=0;
msr_iTMR4Wrapped++;
iTimes++;

for(i = 0; i < task_cUnit; i++) {


if(!(iTimes % task_cTiming[i])) {
(*task_cTask[i])();
}
}
}

void __attribute__((__interrupt__,auto_psv)) _OscillatorFail(void) {


INTCON1bits.OSCFAIL = 0;

#ifdef __DEBUG
while(1) {
LCD_printscr_string(1, "Oscillator Fail");
}
#endif
}
76

void __attribute__((__interrupt__,auto_psv)) _AddressError(void) {


INTCON1bits.ADDRERR = 0;

#ifdef __DEBUG
while(1) {
LCD_printscr_string(1, "Address Error");
}
#endif
}

void __attribute__((__interrupt__,auto_psv)) _StackError(void) {


INTCON1bits.STKERR = 0;

#ifdef __DEBUG
while(1) {
LCD_printscr_string(1, "Stack Error");
}
#endif
}

void __attribute__((__interrupt__,auto_psv)) _MathError(void) {


INTCON1bits.MATHERR = 0;

#ifdef __DEBUG
while(1) {
LCD_printscr_string(1, "Math Error");
}
#endif
}

Path.c
#include "Path.h"
#include "Motor_Control.h"
#include "MSR_Config.h"
#include "Dijkstra.h"
#include "Encoder.h"
#include "LCD_Commands.h"

extern flag_t motor_control_dijkstra;


extern unsigned long temp_encoder_lCurrentDistance;
extern unsigned int Dijstra_Dest[config_no_of_nodes + 2];

unsigned long Path_Dist[config_no_of_edges + 1];


direction_t direction = {{0}};

void Path_fixed_point_new(void) {
static signed char current_point = 0;
static unsigned char motor_control_count_nodes = 0;

if(motor_control_dijkstra.send_dijkstra) {
motor_control_dijkstra.send_dijkstra = 0;

if(!motor_control_dijkstra.run_dijkstra) {
if(!(motor_control_dijkstra.detect_edge)) {
++motor_control_count_nodes;
77

} else {
motor_control_dijkstra.detect_edge = 0;
direction.count_valid = 1;
}

switch(motor_control_count_nodes) {
case 2 : Path_Dist[1] = temp_encoder_lCurrentDistance;
break;
case 3 : if(direction.count_valid == 0)
Path_Dist[2] = temp_encoder_lCurrentDistance;
else
++direction.node_3;

Motor_Control_junc_rotate_left();
break;
case 4 : if(direction.count_valid == 0)
Path_Dist[3] = temp_encoder_lCurrentDistance;
else
++direction.node_4;}

Motor_Control_junc_rotate_left();
break;
case 5 : {
if(direction.count_valid == 0)
Path_Dist[4] = temp_encoder_lCurrentDistance;
else
++direction.node_5;

Motor_Control_junc_rotate_right();
break;
case 6 : if(direction.count_valid == 0)
Path_Dist[5] = temp_encoder_lCurrentDistance;
else
++direction.node_6;

Motor_Control_junc_rotate_left();
break;
case 7 : Path_Dist[6] = temp_encoder_lCurrentDistance;
break;
case 8 : if(direction.count_valid == 0)
Path_Dist[7] = temp_encoder_lCurrentDistance;
else
++direction.node_8;

Motor_Control_junc_rotate_left();
break;
case 9 : if(direction.count_valid == 0) {
Path_Dist[8] = temp_encoder_lCurrentDistance;
else
++direction.node_9;

Motor_Control_junc_rotate_right();
break;
case 10 : if(direction.count_valid == 0)
Path_Dist[9] = temp_encoder_lCurrentDistance;
else
++direction.node_10;

Motor_Control_junc_rotate_left();
break;
78

case 11 : Path_Dist[10] = temp_encoder_lCurrentDistance;


if(direction.node_3 == 1)
Motor_Control_junc_rotate_right();
break;
case 12 : if((direction.node_4 == 0) || (direction.node_4 == 2)) {
if(!(direction.node_4)) {
Motor_Control_junc_rotate_left();
} else {
Motor_Control_junc_rotate_right();
}
}
break;
case 13 : if(direction.node_5)
Motor_Control_junc_rotate_left();
else
if(++direction.node_13 == 2)
Motor_Control_junc_rotate_right();
break;
case 14: Path_Dist[11] =temp_encoder_lCurrentDistance; break;
case 15 : if(direction.count_valid == 0)
Path_Dist[12]=temp_encoder_lCurrentDistance;
else
++direction.node_15;

if(direction.node_9)
Motor_Control_junc_rotate_right();
else
Motor_Control_junc_rotate_right();

break;
case 16 : if((direction.node_8 == 0) || (direction.node_8 == 2)) {
if(direction.node_8 == 0)
Motor_Control_junc_rotate_right();
else
Motor_Control_junc_rotate_left();
}
break;
case 17 :
unsigned char i = 0;

for(i = 1; i <= config_no_of_edges; ++i) {


if(i == 1) {
Dijkstra_send_data(1, 5, Path_Dist[1]);
} else if(i == 2) {
Dijkstra_send_data(1, 2, Path_Dist[6]);
} else if(i == 3) {
Dijkstra_send_data(1, 3, Path_Dist[7]);
} else if(i == 4) {
Dijkstra_send_data(2, 4, Path_Dist[5]);
} else if(i == 5) {
Dijkstra_send_data(3, 6, Path_Dist[8]);
} else if(i == 6) {
Dijkstra_send_data(4, 7, Path_Dist[4]);
} else if(i == 7) {
Dijkstra_send_data(4,5, Path_Dist[11]);
} else if(i == 8) {
Dijkstra_send_data(5,6, Path_Dist[12]);
} else if(i == 9) {
Dijkstra_send_data(5, 9, Path_Dist[2]);
} else if(i == 10) {
79

Dijkstra_send_data(6, 8, Path_Dist[9]);
} else if(i == 11) {
Dijkstra_send_data(7, 9, Path_Dist[3]);
} else if(i == 12) {
Dijkstra_send_data(8,9, Path_Dist[10]);
}
}

Dijkstra_analyse_data();
Dijkstra_go_path(config_no_of_nodes);

motor_control_dijkstra.junction_dijkstra = 1;
Motor_Control_junc_rotate_left();
Delay(_100ms);
LCD_command(clear_display);
break;
default : break;
}

led = 0;
direction.count_valid = 0;
} else {
switch(Dijstra_Dest[current_point]) {
case 1 : if(Dijstra_Dest[current_point + 1] == 3) {
Motor_Control_junc_rotate_right();
} else if(Dijstra_Dest[current_point + 1] == 2) {
Motor_Control_junc_rotate_left();
} else { }
break;
case 2 : if(direction.node_6 == 0) {
Motor_Control_junc_rotate_right();
} else if(direction.node_6 == 2) {
Motor_Control_junc_rotate_left();
} else { }
break;
case 3 : if(direction.node_6 == 0) {
Motor_Control_junc_rotate_left();
} else if(direction.node_6 == 2) {
Motor_Control_junc_rotate_right();
} else { }
break;
case 4 : if(Dijstra_Dest[current_point + 1] == 7) {
if(Dijstra_Dest[current_point - 1] == 2) {
if(direction.node_5 == 0)
Motor_Control_junc_rotate_left();
} else {
if(direction.node_13 != 1)
Motor_Control_junc_rotate_right();
}
} else {
if(direction.node_13 != 0)
Motor_Control_junc_rotate_right();
}
break;
case 5 : if(((Dijstra_Dest[current_point - 1] == 1) &&
(Dijstra_Dest[current_point + 1] == 4)) ||
((Dijstra_Dest[current_point - 1] == 4) &&
Dijstra_Dest[current_point + 1] == 9))) {

Motor_Control_junc_rotate_left();
80

} else if(((Dijstra_Dest[current_point - 1] == 1) &&


(Dijstra_Dest[current_point + 1] == 6)) ||
((Dijstra_Dest[current_point - 1] == 6) &&
(Dijstra_Dest[current_point + 1] == 9))) {

Motor_Control_junc_rotate_right();
}
break;
case 6 : if(Dijstra_Dest[current_point + 1] == 8) {
if(Dijstra_Dest[current_point - 1] == 3) {
if(direction.node_9 == 0)
Motor_Control_junc_rotate_left();
} else {
if((direction.node_9 == 1)
|| (direction.node_15 == 1)) {
Motor_Control_junc_rotate_left();
}
}
} else {
if(direction.node_15 != 1)
Motor_Control_junc_rotate_left();
}
break;
case 7 : if(direction.node_4 == 0) {
Motor_Control_junc_rotate_right();
} else if(direction.node_4 == 2) {
Motor_Control_junc_rotate_left();
} else { }
break;
case 8 : if(direction.node_10 == 0) {
Motor_Control_junc_rotate_left();
} else if(direction.node_10 == 2) {
Motor_Control_junc_rotate_right();
} else { }
motor_control_dijkstra.end_dijkstra = 1;
LCD_command(clear_display);
LCD_printscr_string(1, "THE END");
break;
default : break;
}

if(Dijstra_Dest[current_point++] == config_no_of_nodes) {
motor_control_dijkstra.end_path = 1;
Motor_Control_break();
}
}

//Reset Poscount and distance


encoder_iPosWrapped = 0;
POSCNT = config_min_poscnt;
Delay(_100ms);
}
}

Task.c
#include "Task.h"
81

#include "MSR_Config.h"

unsigned char task_cUnit = 0;


ptrTaskFunc task_cTask[config_number_of_task] = {0};
unsigned long task_cTiming[config_number_of_task] = {0};

void Task_add(ptrTaskFunc cFunc, unsigned long cDuration) {


task_cTask[task_cUnit] = cFunc;
task_cTiming[task_cUnit++] = cDuration;
}

You might also like