Home > Implementation

Implementation

LaserBots: User Manual

Friday, 18 January 2013 Category : , , , 1

The year is 2067. Health and Safety regulations have gone too far. Sport has been outlawed . To satisfy the entertainment needs of the people, robots have been created to fight one another. They have been built with family-friendly lasers. They hunt out the opponent and blast them away with their laser. Skill is everything. Failure is not an option. LaserBots - be entertained. 

(Rated PG. Ages 8+). 

OBJECTIVE OF THE GAME

The objective of LaserBots is to shoot the opponent robot and be the first to get to a score of 50. 

PLAYERS

Currently, LaserBots is for two players. However, in the future, targets could be made and sold with the game so that the game could be for one player. To increase the number of players in the future, more XBees will need to be setup and added to the circle network. 

Future Work

Category : , 0

A specific Raspberry Pi camera has been developed which interfaces directly with the GPU, which would improve it's performance, however these have not been put into production yet.  It has been suggested that using the VoIP service Skype for the video streaming would work as a simple solution.  Each robot would have a mobile device attached to it, and the user would run a video call from it.

To fix the motors causing interrupts issue, it has been suggested to use smoothing capacitors to reduce the current spike and resulting voltage dips.  Another solution would be to disable interrupts from being triggered while the robot is driving, or changing the interrupts from falling to rising edge could also solve the problem.

At the moment the PCB has to be removed to change the batteries (which is fairly often with cheap batteries) , this is not an ideal solution.  Large PCB spacers could be mounted in the chassis and drilled through the PCB. so that it is mounted high enough that the batteries can be removed without removing the PCB.  On a similar note, the PCB should be redesigned with the correct size of Molex connectors, so that the connections can be neater and more robust.

As discussed earlier, more work will be done on accessing the serial port through Processing on the Raspberry Pi, this will allow the games master GUI to be run on a much cheaper machine.

Problems with Design

Category : , 0

Motor Current Spikes Triggering Interrupts


It was discovered during extensive testing that when motors were turned on i.e. the robot driving, that the interrupts for laser detection were being called.  This was only found when status LEDs were implemented, as otherwise there was no other indication of the laser detection other than on the GUIs.  It is suggested that this is the reason why both robots cannot be controlled simultaneously through the network, as the interrupt is flooding the network with 'games master' packets, and causing the actual control signals to robot 2 to be lost.

The reason for the interrupts being called is that they are triggered on a falling edge, i.e. when the laser is detected, the interrupt pin goes low.  When the motors are started, a high current draw is required, this causes the voltage across the whole robot to dip. This voltage dip is low enough to trigger the interrupt. 

This is undesirable, as it causes the score to increment when the player drives the robot.  It also stops both robots from being simultaneously driven.  A work around for this was achieved by implementing turn by turn play - making the game more strategic.  It also makes the game slightly easier - as hitting a tiny moving target has proven to be difficult through the robot.  

If this problem is fixed in the future, it is suggested to keep the turn by turn play, with the scores staying the same when robots are driven.

On-board Video on Raspberry Pi


After attempting to get Processing to run on the Raspberry Pi, and observing the speed at which that runs, concerns were raised about the ability of the Pi to process two video streams simultaneously.  In addition, other groups findings were that the Pi could only achieve an approximate refresh rate of 1 frame per second at a very low resolution from a wired webcam.  Our project would require this stream to be wireless, and at a high enough resolution to see the laser.

It was decided to move this objective into the future, as time constraints and hardware availability would restrict the development of this further.

Power Consumption


Other findings from testing were that each robot had a very short battery life, with the 6V power gradually running out.  In addition the 9V batteries that were powering the Arduino and Xbee were very cheap batteries from a pound shop, and did not provide enough juice to run both.  Higher quality batteries were purchased and this solved the issue, although it is unknown as to how long for.

Final Demonstration Code

Category : , 0

In this post is all the Arduino source code for each robot, and the games master Processing code.  See the individual posts for a walk-through of each section and it's function.

Final Arduino Code for Robot 1

#include <Servo.h>
#include "avr/interrupt.h"

char instruction [10]; //instruction sent to arduino
int i = 0; //index for instruction array
int j = 0; //index for for loop

String stringXCoord = "";
int intXCoord; //x coordinate for servo
String stringYCoord = "";
int intYCoord; //y coordinate for servo

Servo servoX; //servo x
Servo servoY; //servo y

Final Build Pictures

Thursday, 17 January 2013 Category : , 0

The PCBs were mounted using double sided tape on top of cardboard to protect connections on the bottom.  This was mounted on top of the 6V battery pack, which was in turn stuck to the chassis.  This type of mounting is not ideal, as while it is secure, it makes changing the batteries underneath awkward, as the robot essentially has to be dismantled to do so.  This can be improved in future iterations of the design.


Bullseye targets were created and mounted on the LDRs, during testing it was found that these were particularly useful when the player has just missed the target, and can adjust accordingly. 

Games Master GUI

Wednesday, 16 January 2013 Category : , , , 0

The Games Master GUI is the graphical user interface that is displayed on a laptop that is connected to the Games Master Arduino. Its purpose is to display the scores of each LaserBot for everyone to see.

DESIGN

The Games Master GUI was designed using Processing. In Processing, there are quite a few examples on how to draw different things. Firstly,it was figured out how to draw text by looking at the Processing example "Words". The screen was to be split into two halves, one the left hand side would be LaserBot one's score, and on the right LaserBot two's score. 

Testing Multiple Controllers

Sunday, 13 January 2013 Category : , , 0

In order for the two robots to be controlled effectively, it is necessary to receive and differentiate between messages sent from two controllers.  The TouchOSC app allows for the IP address of the host to be configured, along with the port of the application to send messages to.  The port of received messages on the iPad can also be configured, this is generally left as default.  At the bottom is the iPads IP address, which is required to send messages back to the controller to control LEDs and labels on screen. The configuration screen is shown below.
TouchOSC configuration screen

Sending Messages

Using some of the examples on the OscP5 library's website it was possible to create some prototype code to send OSC messages back to the controller, to update labels and turn on LEDs etc. The processing code below updates the player's score label.

PCB Design and Manufacture

Friday, 11 January 2013 Category : , 1

In order to make the robot implementation more robust and neater, a PCB was designed.  The software used was the EAGLE design suite.  The tested prototype circuits were analysed and transferred to a schematic with connectors to interface the PCB with the Arduino and the rest of the robot.  This schematic can be seen below.
PCB Schematic
Each subsystem can be seen; the connector for the Arduino control signals is on the left, the motor chip middle centre, four LEDs for robot status to the right of the motor chip, three LDR detector transistor circuits, and then finally the Molex connectors on the far right.  At the bottom are two connectors to allow the two power supplies to be connected to the board.

Building a Full Robot

Tuesday, 8 January 2013 Category : , 0

Before designing a PCB for the robot, it was necessary to test all the prototype circuits when put together.  This was important, as if the circuits are not correct, it is much easier to fix at design stage than after it is set in copper on a PCB.  The completed prototype robot can be seen below.
Completed Prototype
Each group member constructed their own subsystem on a breadboard that fit the size of the robot.  The Arduino and Xbee was mounted towards the rear of the robot.  The complete robot was then tested with communications through a USB cable, and no issues were encountered.  The hit notification was tested by using another laser to trigger the sensor, and then Processing was monitored to see the notification packet come through.  These tests can be seen in the video below.

Circle Network: Controlling each robot

Monday, 7 January 2013 Category : , , 0

In this post, it was explained that the LaserBots are now connected in a circle network as shown below. The Games Master talks to LaserBot 1, LaserBot 1 talks to LaserBot 2 and LaserBot 2 talks back to the Games Master. 
Circle Network Setup

NEW PACKET STRUCTURE

To implement this correctly, the packet that is being sent round needs to be modified so that the instruction is sent to the correct LaserBot. Therefore, the new packet format is as follows:
< - character to signify start of message
1 or 2 - robot ID 
F or f - turn laser on or off
XX - numbers to control servo x
XX - numbers to control servo y
F, B or S - left motor controls (forward, back or stop)
F, B or S - right motor controls (forward, back or stop)
> - character to signify end of message

Also, to send the scores back to the Games Master, another packet needs to be introduced:
[ - character to signify start of message
G - intended for Games Master
1 or 2 - robot ID
XX - number representing how much score should be incremented
] - character to signify end of message


LASERBOT ONE CODE 

INSTRUCTION PACKET CODE

The code in each robot needs to be modified to accept this new packet structure. Firstly, the array used to store the instruction needs to be made bigger to accommodate the extra characters being sent. 
char instruction [10]; //instruction sent to arduino 
Next, a String needs to be introduced so that a message can be resent on to robot 2.

//message to send on to Robot 2
String resendMessage = "";
Inspecting the previous code for the robot, the first part of void loop() stays the same - while there is serial data available, read the data and store in the instruction array. Next, check to see if the instruction starts and ends with the characters '<' and '>' (remembering to change the index by one for the ending character). 

Next, another if statement needs to be added to check whether or not the instruction received is for robot 1. 
//check that message is for robot 1 <1....>
        if (instruction[j+1] == '1')
        {
If the message is for the robot, the normal parsing takes place (parse the rest of the message to deal with lasers, servos and motors). Else, if the message is for robot 2, robot 1 needs to resend the message. The message is concatenated together and then printed out. Before being printed, however, the serial port is flushed. 
//else check is message is for robot 2 <2...>
        else if (instruction[j+1] == '2')
        {
          //reset resend message to blank
          resendMessage = "";
          int k = 0;
          //loop around the instruction message
          for (k = j; k<(j+10); k++)
          {
            //concatenate all characters of message
            resendMessage += instruction[k];
          }
          Serial.print(0); //flush the serial port
          Serial.print(resendMessage); //send the message onto Robot 2
        }
That is all the code that needs to be added/changed for robot 1 to parse the instruction message.

SCORE PACKET CODE

To update the score, a score packet needs to be sent on to robot 2, who will then send it on to the Games Master, which keeps track of all scores. Therefore, when the robot is hit, it needs to send a score packet. Firstly, the variables need to be stated.
//string to print to contact games master - in the following form:
//G (games master) 2 (robot 2's score) 10 (how much to increment score)
String GMscoreLR = "[G210]";
String GMscoreB = "[G215]";

As can be seen, the robot ID has been stated has 2, even though this code is on robot 1. This is because robot 1 has been hit, but it has been hit by robot 2 and so it is robot 2's score that needs to be updated. 

The reason for two different score packets is for the two interrupts that are used to detect when the robot is hit. This allows for two different scores to be implemented. Therefore, in interrupt one the following code needs to be added
    Serial.print(0);//flush the serial port
    //print message to GM to increment Robot 2's score
    Serial.println(GMscoreLR);
In the other interrupt, this code needs to be added:
    Serial.print(0);//flush the serial port
    //print message to GM to increment Robot 2's score
    Serial.println(GMscoreB);
These interrupts print the correct string which will then be sent on to robot 2. 

LASERBOT TWO CODE

INSTRUCTION PACKET CODE

Again, the array used to store the instruction needs to be made bigger to accommodate the extra characters being sent. 
char instruction [10]; //instruction sent to arduino 
Next, a String needs to be introduced so that a message can be resent on to the Games Master.
//message to send on to Games Master
String resendMessage = "";
Again, inspecting the previous code for the robot, the first part of void loop() stays the same - while there is serial data available, read the data and store in the instruction array. Next, check to see if the instruction starts and ends with the characters '<' and '>' (remembering to change the index by one for the ending character). 

Next, another if statement needs to be added to check whether or not the instruction received is for robot 2. This is not really needed as anything that is sent to robot 2 will be for it, but it is extra error checking.
//check the message is for  robot 2 <2...>
        if (instruction[j+1] == '2')
        {
If the message is for robot 2, the normal parsing takes place (parse the rest of the message to deal with lasers, servos and motors). That is all the code needed for the instruction packet. 

SCORE PACKET CODE

For the score packet, as well as sending its own score packet, robot 2 needs to process robot 1's score packet. Let's deal with that first. 

In void loop(), there is an if statement to check for the characters '<' and '>.' After this statement and else if statement needs to be included to check for the characters '[' and ']'. 
//if message is score packet for the Games Master
      else if ((instruction[j] == '[') &&  (instruction[j+5] == ']'))
      {
        //reset resend message to blank
          resendMessage = "";
          int k = 0;
          //loop around the instruction message
          for (k = j; k<(j+6); k++)
          {
            //concatenate all characters of message
            resendMessage += instruction[k];
          }
          Serial.print(0); //flush the serial port
          Serial.print(resendMessage); //send the message onto games master
      }
In this else-if statement, the message is concatenated together and then sent on to the Games Master. 

For robot 2's own score, the same code as robot 1 needs to be included in the interrupts.
    Serial.print(0);//flush the serial port
    //print message to GM to increment Robot 2's score
    Serial.println(GMscoreLR);
And in the other interrupt, this code needs to be added:
    Serial.print(0);//flush the serial port
    //print message to GM to increment Robot 2's score
    Serial.println(GMscoreB);
That concludes all the code that needs to be updated on the robots. Please see post BLAH for the parsing of the Games Master packets in Processing.

XBee Configuration: Coordinator and Router (Circle Network)

Category : , , 1

After trying a point to point network, and a point to multipoint network, it was decided that a circle network would be implemented. 

Circle network
For this, one XBee needed to be set up as a coordinator and two XBees needed to be set up as routers. 

WHICH ONE TO BE THE COORDINATOR?

For this circle network, the XBees were configured several times until the network worked. Trial and error was used, changing which robot or games master should be a coordinator or router. In order to test the system, a simple test was done by sending integers around the circle, which were read and retransmitted by the correct XBees. Eventually, the setup that worked was:
Coordinater - Robot 2
Router 1 - Games Master
Router 2 - Robot 1
Coordinator and Router Setup for Circle Network

Running Processing on the Raspberry Pi

Category : , 0

Having encountered difficulties installing processing on the raspberry pi it was discovered that the problem was due to the default Java JDK'Sun 6 JDK'  not being able to run on ARM. In order to overcome this an alternative ARM compatible JDK was downloaded and installed.
sudo apt-get install openjdk-6-jdk librxtx-java

Processing linux 32 ( in this case 1.5.1) was then downloaded and unzipped.
http://processing.org/download/

This linux verion of Processing assumes a Intel architecture so we need to remove the link to java directory from within the unzipped files. To do this the command rf is used. a side effect of this is that the director will most likely be deleted.  This is described in more detail here. http://www.linuxjournal.com/article/50
rm -rf java

Point to Multipoint - Broadcast Network Design

Category : , , 0

This method for configuring Xbees ended up being too slow for this projects requirements, due to the Zigbee protocol's requirement for delays to be in place to allow packets to propogate through the whole network.

A simple solution could be to simply use another Xbee, and have a separate channel for each robot, however this would require another shield and Arduino as an interface.  The budget and hardware is not available for this.  Instead a 'circle' network configuration was devised to allow all devices to communicate at rate that was suitable.

In a broadcast network, one module is set as the 'master'.  This sends out all it's messages to all devices on the network.  The devices can then check an ID that is sent as part of the message, and then decide whether they need to act upon it or not.  This sounded suitable for this project, as both robots could then receive all messages, and only carry out the instructions if the ID matched the number of the robot.  They can then reply to the master if they are hit, or as required through a uni-cast connection which is configured in their modules. This configuration is illustrated below.
Broadcast configuration for an Xbee network
This is not as simple as the standard uni-cast point to point network with two modules that is often employed with Xbees so direct configuration of the Xbee's firmware was required.

Point to Point Xbee Network Testing

Friday, 4 January 2013 Category : , , 2

Xbees are wireless modules which interface radio transmissions with standard serial input output.  All transmission issues are taken care of by the Zigbee protocol (which is the protocol that Xbee Series 2 modules use).  This ease of use means Xbees are often used as drop in replacements for a serial cable as can be seen in the diagram below.  In this configuration the module's default settings mean that a link can be set up quickly and easily.
Xbees in Unicast mode
This is called 'uni-cast', when a module knows the address of it's recipient, and sends data directly to it.  In this example, the second module also knows the address of the first Xbee, and therefore can reply back to it.

Xbee Shields

In this project, shields are used to interface the Xbee module with the Arduino Uno on the robots.  The main component on these shields is a voltage regulator to convert the Arduino's 5V logic to the Xbee's 3.3V. Without this the Xbee module could be damaged.

Raspberry Pi Setup

Wednesday, 12 December 2012 Category : , , 0


The offboard system uses the raspberry pi as the base station to send and receive signals from the iPads over the network interface and process the messages and send control packets to the game master Xbee.


This Xbee is connected to the raspberry pi using a slice of pi interface board which sends and receives serial signals from the serial GPIO TX and RX pins on the raspberry pi to the UART data in and UART data out pins on the Xbee.

Laser Detection: Handling Multiple Detector Circuits

Category : , , , 0

In the post Laser Detection it was discussed how to set up a detector circuit and how the Arduino would recognise when the LaserBot was hit through the use of an interrupt. That was only for one detector circuit. However, three detector circuits are wanted on the LaserBot to make the game more interesting – more targets to shoot at, different points for the different targets etc. 

PROBLEM ENCOUNTERED WITH ONE INTERRUPT

At first, only one interrupt was going to be used for all three detector circuits. This caused a problem in detecting which target had been hit as the interrupt only notifies the Arduino that it has been hit but not which target has been hit. The idea was then to have an output coming off each circuit which would then have its own individual port on the Arduino. When the interrupt said that the Arduino had been hit, the Arduino could then check the three different ports and see which ports state had changed. This, however, did not work as the interrupt joined all three circuits together, causing all three circuits to change state when they were hit.

Therefore, it was decided to use the two interrupts available on the Arduino. Two detector circuits would be connected to one interrupt, and the other circuit would be connected to the other interrupt. This meant that the Arduino could differentiate between two circuits, but not three. So, the user could get two different scores for hitting targets, rather than three different scores, which is not a big loss in terms of flexibility for the game. 

Platform Construction

Monday, 10 December 2012 Category : , 0

The Tamiya tank systems, gearbox, tracks and platforms all came with instructions and although they had many small parts and were time consuming to assemble were otherwise relatively straightforward to assemble and test.

Wheel and Track Parts 
Gearbox Parts 
Gearbox Assembly 
Platform, wheel and track assembly 
Full Assembly
Battery and motor chip installation for testing

Motor chip connected to gearbox
Ready for Arduino 
Testing
For testing code see   

iPad to Computer Comms

Wednesday, 28 November 2012 Category : , , 1

Further research into the game's communication systems led to the discovery of the OSC (Open Sound Control) protocol. This is often used instead of MIDI in electronic musical instruments to allow communication and control between different devices.

Much of the inspiration for this section of the project came from Pete-O's excellent work on OSC and the Arduino which can be found here.

For this project we used TouchOSC on the iPad, as there are two iPads available for use within the group (freeing up iPhones and iPod touches for possible camera use).  This app costs £3, and includes a free interface designer for Windows. Using this designer, an application was developed to send robot control signals from the iPad, this interface can be seen below.
iPad interface for control - note space at the top for future game messages
Once run, the app was configured with host IP address and port of the Processing application which is discussed later.

Processing is an IDE and language that builds on Java, but with a simpler graphics programming model.  This is ideal for us, as it works very well alongside the Arduino development environment, which was developed on the same interface.  This makes it relatively easy, and well documented online to get an Arduino talking to the Processing sketch.  In addition, there is an OSC library available, allowing us to parse and process OSC messages received over a local network.  The OSC library has been developed by Andreas Schlegel, and is available here.

Understanding Control Signals on the Arduino

Category : , , 0

An Arduino is used to control each robot. Through the use of XBees, a signal will be sent from Processing, which the robot Arduino will pick up. The packet that is sent from Processing is in the following format:
< - indicates the start of the packet
F or f - sets whether the laser is on or off
XX - two numbers that determine the position of servo x
XX - two numbers that determine the position of servo y
F, B or S - determines left motor state
F, B or S - determines right motor state
> - indicates the end of the packet

For the Arduino to receive the packets, the Arduino reads its serial port. While there is information available on the serial port, it stores the incoming bytes into an array. The code for this can be seen below.


while(Serial.available() > 0){//look for Serial data 
    int incoming = Serial.read();//read and store the value
    
    instruction[i] = incoming; //store the instruction in the array

There are then checks put into place to see whether or not what was sent to the Arduino is in the correct packet format. If the Arduino cannot find the start and end delimiters of the packet (< and >, respectively) it ignores the received information. 

//loop round the instruction array (expecting 9 characters)
    for (j = 0; j<9; j++)
    {
      //get the coordinates for the motor  
      if ((instruction[j] == '<') &&  (instruction[j+8] == '>'))
      {

If the incoming packet does have the start and ending characters, the Arduino can parse the packet. The string format is known so it is as simple as comparing each character to what you expect. 


Firstly, the servo instructions are dealt with. There should be 4 numbers in the packet - two for servo x and two for servo y. Since these numbers arrive as characters, they need to be concatenated into a String and then converted to an Integer. The code for this can be seen below. Once the Integer has been retrieved, the number is written to the correct servo.
//concatenate the two strings for x coordinates together
stringXCoord += instruction[j+2];
stringXCoord += instruction[j+3];
//make the string an integer
intXCoord = stringXCoord.toInt();
servoX.write(intXCoord); //write to the servo
  
//concatenate the two strings for y coordinates together
stringYCoord += instruction[j+4];
stringYCoord += instruction[j+5];
//make the string an integer
intYCoord = stringYCoord.toInt(); 
servoY.write(intYCoord); //write to the servo 

Next, the laser is dealt with. If the laser character is 'F', then the laser should be turn on else if it is 'f' then it should be turned off. 
          //turn laser on
          if (instruction[j+1] == 'F')
          {
            digitalWrite(laser, LOW);
          }
          //turn laser off
          if (instruction[j+1] == 'f')
          {
            digitalWrite(laser, HIGH);
          }

The motors on the robot are next to be parsed. The expected characters are 'F', 'B' or 'S'. 'F' means that the motor should move forward, 'B' means that the motor should go backwards and 'S' means the motor should stop moving. There is one character for the left motor and one for the right. The code for the motors can be seen below.
//control motors on robot - left motor forward
        if (instruction[j+6] == 'F')
        {
          digitalWrite(leftMotor1, LOW);
          digitalWrite(leftMotor2, HIGH);
        }
        //control motors on robot - left motor stop
        if (instruction[j+6] == 'S')
        {
          digitalWrite(leftMotor1, LOW);
          digitalWrite(leftMotor2, LOW);
        }          
        //control motors on robot - left motor backward
        if (instruction[j+6] == 'B')
        {
          digitalWrite(leftMotor1, HIGH);
          digitalWrite(leftMotor2, LOW);
        }
        //control motors on robot - right motor forward
        if (instruction[j+7] == 'F')
        {
          digitalWrite(rightMotor1, HIGH);
          digitalWrite(rightMotor2, LOW);
        }
        //control motors on robot - right motor stop
        if (instruction[j+7] == 'S')
        {
          digitalWrite(rightMotor1, LOW);
          digitalWrite(rightMotor2, LOW);
        }
        //control motors on robot - right motor backward
        if (instruction[j+7] == 'B')
        {
          digitalWrite(rightMotor1, LOW);
          digitalWrite(rightMotor2, HIGH);
        }
Finally, variables need to be reset. The variable i used for storing the incoming serial into an array is set to -1 and then incremented to get back to 0, as resetting back to 0 would not work. 
        //reset variable for instruction array 
        i = -1;
      }
    }
    //increment counter for instruction array
    i++;
    //reset variables used
    stringXCoord = "";
    stringYCoord = "";
    intXCoord = 0;
    intYCoord = 0;
  }
}
All of the code given was in the void loop() method. For the code to work, all variables were stated at the start of the sketch and any libraries needed were included.
#include <Servo.h>
char instruction [9]; //instruction sent to arduino
int i = 0; //index for instruction array
int j = 0; //index for for loop

String stringXCoord = "";
int intXCoord; //x coordinate for motor
String stringYCoord = "";
int intYCoord; //y coordinate for motor

Servo servoX; //servo x
Servo servoY; //servo y

//pin for laser and LDR
int laser = 13;

//pin numbers for the motor on the robot
int leftMotor1 = 11;
int leftMotor2 = 12;
int rightMotor1 = 9;
int rightMotor2 = 10;

In void setup(), pins have to be set as either an input or output, and servos need to be attached.
void setup(){
  //init Serial library (make sure Processing is sending data at the same baud rate)
  Serial.begin(9600);
  servoX.attach(5);
  servoY.attach(6);
  
  pinMode(sensor1, INPUT);
  pinMode(laser, OUTPUT);
  pinMode(leftMotor1, OUTPUT);
  pinMode(leftMotor2, OUTPUT);
  pinMode(rightMotor1, OUTPUT);
  pinMode(rightMotor2, OUTPUT);
}

Any extensions to the packet sent between the robots can easily be integrated. The indices of any arrays will need to be changed, and if statements will need to be added, which is relatively easy to do, showing that the code is very flexible in terms of future work. 

Laser Setup and Detection

Wednesday, 21 November 2012 Category : , , , 3

Laser Setup

TESTING LASER SWITCHES ON

Firstly, the laser needed to be tested to see if it would work. On the TTL laser, there is a polarized 4-pin connector with the following connections:

  • Red wire:5-12 V DC input
  • Black wire: GND
  • Green wire: 10k pull-down (aka GND)
  • White wire: active low TTL input (3.3 V or 5 V logic)

The laser was tested by firstly just turning the laser ON and leaving it on. This was done by connecting the red wire to a power source, the black wire to GND, the white wire to GND and leaving the green wire unconnected. This successfully turned the laser on. 


TESTING LASER WORKS WITH ARDUINO

The laser was then tested with the Arduino. The white wire was connected to a TTL output on the Arduino (Port 13), the red wire was connected to 5V and the black wire was connected to Gnd as shown in the diagram below. Again, the green wire was left unconnected. 
Arduino Laser Connections

The Arduino was given power and the basic sketch "Blink" was downloaded to the board. Once downloaded, the laser successfully started blinking in accordance with the sketch. This shows that the laser can easily work with the Arduino. 

The testing for this laser was found at this site.

Laser Detection

LDR

To detect a laser, a light dependent resistor (LDR) is needed. An LDR is a device which has a resistance that varies according to the amount of light falling on its surface. A typical LDR can be seen pictured below, along with its circuit diagram symbol.
LDR and circuit symbol
The LDR that was chosen to go with the TTL Laser Module was the Mini Photocell, as this was suggested from the manufacturer. The photocell can be seen below. 
Mini Photocell

DETECTION CIRCUIT

An LDR can be set up in two ways:
  1. The LDR is activated by darkness
  2. The LDR is activated by light
For LaserBots, a laser is fired and the LDR is meant to detect when the laser hits it. Therefore, the LDR needs to be activated by light. The circuit for this can be seen below:
LDR circuit
When light hits the LDR, the LED will light up. The variable resistor is used to fine tune the amount of light needed to activate the LDR. 

A similar circuit to the one pictured above (resistor values were different) was built on a breadboard and the variable resistor was used to tune the LDR so that only the high intensity laser beam activated the LED and not the background light from the room. The circuit was powered by connecting it to an Arduino, along with a laser. A video of this circuit working can be seen below. 


As can be seen, when the laser moves over and hits the LDR, it switches the transistor and turns the LED on. 

ARDUINO CODE

SIMPLE LASER DETECTION

Now that the laser and detection circuit has been tested to see that both work, the next stage is to get the Arduino involved. The Arduino should be able to turn the laser on and off. Firstly, the pin that the laser is connected to can be given a name, for example, laser. This pin can then be set as an output, which will tell the laser to go either high or low depending on which command the Arduino writes to it. For this first simple set up of code, the laser is set to be low. The pin that the LDR is connected to is set up in the same way. It is given a name (LDR) and is assigned a pin mode (this time it is set to input, as data will be received from the LDR). For this circuit, the laser is connected to pin 8 and the LDR is connected to pin 7, but any pin could have been chosen. The code can be seen below. 

/*
int LDR = 7; //LDR attached to pin 7
int laser = 8; //Laser attached to pin 8

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  // make the LDR's pin an input:
  pinMode(LDR, INPUT);
  //make the laser's pin an output:
  pinMode(laser, OUTPUT);
  
  digitalWrite(laser, LOW);//turn laser on 
}
Now that the pins have been set up, code can be written to interact with the detector circuit that has been built. When the LDR is hit with the laser, the output changes from high to low. This change can be picked up by the Arduino by doing a digital read of the pin. A digital read reads the value from a specified digital pin, either HIGH or LOW. If the value read is LOW, the Arduino can state that it has been hit, else if it is HIGH it won't do anything, as can be seen in the following code.
// the loop routine runs over and over again forever:
void loop() {
  // read the input pin:
  int hitState = digitalRead(LDR);

  //LDR goes low when it has been hit
  if (hitState == 0)
  {
    Serial.println("HIT");
    delay(2000);        // delay in between reads for stability
  }
This code can now be put together and uploaded onto the Arduino. The Arduino and detector circuit should be set up as follows.
INSERT PIC
Once everything is set up, the Arduino was kept connected to the computer and a Serial monitor was opened. When the laser hit the LDR, on the Serial monitor "HIT" was printed. The Arduino successfully works with the laser detection circuit.


USING INTERRUPTS

If the robot was only detecting whether or not it had been hit, the code shown above would be fine. However, the robot is going to be processing lots of other code (controlling servos, motors, lasers etc) and so the above code is not sufficient, as it only checks if it has been hit once every turn of the loop. This could cause the robot to miss detecting that it has been hit.

To overcome this problem, interrupts can be used. An interrupt is a signal to the processor emitted by hardware or software indicating an event that needs immediate attention, which in this case is the LDR stating that it has been hit. There are only two external interrupt pins on the Arduino Uno, INT0 and INT1, and they are mapped to Arduino pins 2 and 3. The interrupts can be set to trigger on RISING or FALLING signal edges, or on low level. It was decided that the interrupt should occur on the FALLING signal edge rather than on low level. This is because it means that the user will have to move the laser off the LDR before they can hit the robot again, making the game slightly harder. Otherwise, the user could just leave the laser pointing on the LDR and get a huge score. 

The code was changed to use interrupts instead of a digital read. For interrupts to work, the interrupt library needs to be included in the code, therefore at the top of the program before anything else this line of code was added:
 #include "avr/interrupt.h"
Firstly, the pin that the LDR was on was changed from 7 to 2 (as pin 2 has an interrupt attached to it). In the setup method, the following code was added to enable the interrupt and set the interrupt to trigger on a falling edge. 
  sei(); //Enable global interrupts
  EIMSK |= (1 << INT0); //Enable external interrupt INT0
  EICRA |= (1 << ISC01); //Trigger INT0 on falling edge
An interrupt service routine (ISR) then needs to be written. An ISR is the code that is run when the interrupt is triggered. For the moment, the Arduino will just print out that it has been hit.
//interrupt service routine for when the LDR is hit
ISR(INT0_vect)
{
    Serial.println("HIT INTERRUPT");
}
In the main loop of the program, the digital read is removed, as that is no longer needed. To get the program to still do something for the interrupt to interrupt, the Arduino will just print out that it is waiting for the interrupt. The whole code now looks like this:
int LDR = 2; //LDR attached to pin 2 (as pin 2 has an interrupt on it)
int laser = 8; //Laser attached to pin 8

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  // make the LDR's pin an input:
  pinMode(LDR, INPUT);
  //make the laser's pin an output:
  pinMode(laser, OUTPUT);
  
  sei(); //Enable global interrupts
  EIMSK |= (1 << INT0); //Enable external interrupt INT0
  EICRA |= (1 << ISC01); //Trigger INT0 on falling edge
  
  digitalWrite(laser, HIGH);//turn laser on 
}

// the loop routine runs over and over again forever:
void loop() {
  //print something so program has something to do
  Serial.println("Waiting for interrupt");
  delay(1000); //wait one second
}

//interrupt service routine for when the LDR is hit
ISR(INT0_vect)
{
    Serial.println("HIT INTERRUPT");
}
Running the code and watching on a serial monitor, the program outputs "Waiting for interrupt" every second until the interrupt is called, in which case it prints "HIT INTERRUPT".


UPDATING SCORES AND USING MILLIS

Now that the code works with interrupts, more functionality can be added. A score can be added into the code, so that every time the robot is hit, a score is incremented. To stop a user from continually hitting the robot and gaining a large score very quickly, an interval can be included to stop incrementing the score until 5 seconds has passed between hits. Finally, rather than using delays in the code which hold the whole program up, a function called millis() can be used. It is a command that returns the number of milliseconds since the Arduino board started running its current program. An example of using millis() is given in the Arduino examples, callled "Blink without Delay". 

Combining all of the above, the final code looks like the following:
 #include "avr/interrupt.h"

int LDR = 2; //LDR attached to pin 2 (as pin 2 has an interrupt on it)
int laser = 8; //Laser attached to pin 8
int score = 0; //score of the robot
int scoreFront = 10; //score of hitting the LDR
//introduce 5 second interval after hit before another hit can occur
int interval = 5000; 
long lastBlinkTime = 0; //variable used with millis

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  // make the LDR's pin an input:
  pinMode(LDR, INPUT);
  //make the laser's pin an output:
  pinMode(laser, OUTPUT);
  
  sei(); //Enable global interrupts
  EIMSK |= (1 << INT0); //Enable external interrupt INT0
  EICRA |= (1 << ISC01); //Trigger INT0 on falling edge
  
  digitalWrite(laser, HIGH); 
}

// the loop routine runs over and over again forever:
void loop() {
  //make program do something
  digitalWrite(laser, HIGH);  
}

//interrupt service routine for when the LDR is hit
ISR(INT0_vect)
{
  unsigned long hitMillis = millis();
  
  //if there has been more than 5 seconds since the last hit
  if ((hitMillis - lastBlinkTime) > interval)
  {
    lastBlinkTime = hitMillis;
    
    //increment score
    score = score + scoreFront;
    
    Serial.println("Hit Interrupt");
    Serial.println(score);
  }
}
Running this code, when the LDR is hit, the ISR is called and "Hit Interrupt" along with a score is printed, for example 10 for the first hit. When the user tries to hit the LDR again, nothing happens until 5 seconds has passed. The user hits the LDR again and now the Arduino prints out "Hit Interrupt" with a score, now equal to 20. This concludes the code that is needed for laser detection.

Theme MXS. Powered by Blogger.