Arduino Laser Turret

Introduction: Arduino Laser Turret

About: I like to build random things, some of which are presented here on Instructables.

This is a servo laser turret controlled by two potentiometers for x and y planes. It has two playing modes. Manual mode lets you control the laser and hit the targets within a time limit. When you hit the target, a photoresistor, a small video game controller vibrator vibrates the box and an LED simulates an explosion by flashing a couple times. Automatic mode lets you move the laser to the targets and save their locations, and afterwards, moves independently to the different targets and hits them with the laser.

Step 1: Hardware/Wiring

Hardware included:

Arduino Uno

2 180 degree servos

2 potentiometers

lcd display with i2c backbpack

7 segment display

laser diode (removed from laser pointer)

video game controller vibrator

3 photoresistors

3 LEDs

2 NO buttons

2 npn transistors

2 capacitors

The jank is strong with the wire management. This is the first project I've wired from scratch so I just added wires as I went instead of making a schematic before I started. I didn't have any plugs to use for connecting wires to the shield or the back of the box which made it all a real bird's nest.

The box works to keep the turret stable so the automatic mode can remain accurate when you press the button to save the locations. The targets are simple and just have the wiring exposed on the back. All of the wiring, inside the box and on the targets, are hot glued into place for premium strength and stability. The servos and laser are attached using double-sided foam tape that I found, which is less permanent than hot glue.

The lcd display tells you which mode you are in and the steps you are taking in those modes. The 7 segment display works as a timer during manual mode and a counter for both modes. Both are mounted through holes on the lid with hot glue.

The project is powered by a 15v power adapter I can plug into the wall. Inside, I use a dc-to-dc step-down converter, stepping it down to 5v for the Arduino and the servos. I added two capacitors to prevent voltage drops from the servos when both run at full speed, which pulls a total of ~600 mA. The laser and the motor are controlled through transistors so they can run off of the 5v rails instead of the digital pins, which have an amperage limit lower than the two require.

Step 2: Code

#include <LiquidCrystal_I2C.h>
#include <Servo.h>

#include "SevenSegmentTM1637.h"
// 0x3f
const int button = 2;
const int laser = 3;
const int sensor3 = 4;
const int modeButton = 5;
const byte PIN_CLK = 7;   // define CLK pin (any digital pin)
const byte PIN_DIO = 6;   // define DIO pin (any digital pin)
const int LED1 = 8;
const int servo1 = 9;
const int servo2 = 10;
const int LED2 = 11;
const int LED3 = 12;
const int motor = 13;
const int sensor1 = A0 ;
const int sxpot = A1;
const int sypot = A2;
const int sensor2 = A3;
int sensors[] = {sensor1, sensor2, sensor3};
int LEDs[] = {LED1, LED2, LED3};
SevenSegmentTM1637    display(PIN_CLK, PIN_DIO);
Servo servox;
Servo servoy;
LiquidCrystal_I2C lcd(0x3f, 16, 2);
int sensorVal = 0;
int targetCount = 0;
boolean manual = true;
boolean automatic = false;
boolean hit = false;
// ------SETUP------------SETUP--------------------SETUP----------- //
void setup() {
  pinMode(motor, OUTPUT);
  pinMode(laser, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  digitalWrite(motor, LOW);
  digitalWrite(laser, LOW);
  digitalWrite(LED1, HIGH);
  digitalWrite(LED2, HIGH);
  digitalWrite(LED3, HIGH);
  Serial.begin(9600);
  lcd.begin();
  lcd.backlight();
  lcd.clear();
  servox.attach(servo1);
  servoy.attach(servo2);
  display.begin();
  display.setBacklight(100);
}
// -------LOOP--------------LOOP----------------LOOP----------------- //
void loop() {
  if (manual) {
    lcd.setCursor(0, 1);
    lcd.print("Manual        ");
    manualMode();
  }
  if (automatic) {
    lcd.setCursor(0, 1);
    lcd.print("Automatic");
    AutoMode();
  }
  if (digitalRead(modeButton) == HIGH) {
    if (manual) {
      manual = false;
      automatic = true;
    } else {
      manual = true;
      automatic = false;
    }
    delay(200);
  }
  display.print(targetCount);
  hit = false;
  for (int x : sensors) {
    checkTarget(x);
  }
}
// --------CHECKTARGET--------------CHECKTARGET-------------CHECKTARGET-------- //
void checkTarget(int x) {
  if (digitalRead(x) == HIGH) {
    digitalWrite(laser, LOW);
    digitalWrite(motor, HIGH);
    hit = true;
    lcd.setCursor(0, 0);
    lcd.print("Target Destroyed");
    targetCount ++;
    if (x == A0) {
      flash(LED2);
    }
    if (x == A3) {
      flash(LED3);
    }
    if (x == 4) {
      flash(LED1);
    }
    digitalWrite(motor, LOW);
  }
}
// -------FLASH--------------FLASH----------------------FLASH------------------ //
void flash(int pin) {
  int d = 90;
  for (int i = 0; i < 3; i ++) {
    digitalWrite(pin, LOW);
    delay(d);
    digitalWrite(pin, HIGH);
    delay(d);
  }
}
// ------MANUAL--------------MANUAL--------------------MANUAL--------- //
void manualMode() {
  int sxVal = map(analogRead(sxpot), 0, 1023, 0, 180);
  int syVal = map(analogRead(sypot), 0, 1023, 0, 180);
  servox.write(sxVal);
  servoy.write(syVal);
  int seconds = 0;
  if (digitalRead(button) == HIGH) {
    lcd.setCursor(0, 0);
    lcd.print("Firing!                       ");
    digitalWrite(laser, HIGH);
    for (seconds = 2000 ; seconds >= 0; seconds -= 1) {
      display.clear();
      display.print(seconds);
      int sxVal = map(analogRead(sxpot), 0, 1023, 0, 180);
      int syVal = map(analogRead(sypot), 0, 1023, 0, 180);
      servox.write(sxVal);
      servoy.write(syVal);
      for (int x : sensors) {
        checkTarget(x);
      }
      if (hit) {
        display.clear();
        break;
      }
      if (seconds == 0) {
        digitalWrite(laser, LOW);
        lcd.setCursor(0, 0);
        lcd.print("Missed           ");
        targetCount = 0;
      }
    }
  }
}
// --------AUTOMODE--------------AUTOMODE--------------AUTOMAODE---------- //
void AutoMode() {
  lcd.setCursor(0, 0);
  lcd.print("Aquire Targets        ");
  digitalWrite(laser, HIGH);
  targetCount = 0;
  display.print(0);
  int x1 = 0;
  int y1 = 0;
  int x2 = 0;
  int y2 = 0;
  int x3 = 0;
  int y3 = 0;
  int xtemp = 0;
  int ytemp = 0;
  while (digitalRead(button) == LOW) {
    xtemp = map(analogRead(sxpot), 0, 1023, 0, 180);
    ytemp = map(analogRead(sypot), 0, 1023, 0, 180);
    servox.write(xtemp);
    servoy.write(ytemp);
  }
  x1 = xtemp;
  y1 = ytemp;
  delay(400);
  while (digitalRead(button) == LOW) {
    xtemp = map(analogRead(sxpot), 0, 1023, 0, 180);
    ytemp = map(analogRead(sypot), 0, 1023, 0, 180);
    servox.write(xtemp);
    servoy.write(ytemp);
  }
  x2 = xtemp;
  y2 = ytemp;
  delay(400);
  while (digitalRead(button) == LOW) {
    xtemp = map(analogRead(sxpot), 0, 1023, 0, 180);
    ytemp = map(analogRead(sypot), 0, 1023, 0, 180);
    servox.write(xtemp);
    servoy.write(ytemp);
  }
  x3 = xtemp;
  y3 = ytemp;
  delay(400);
  servox.write(90);
  servoy.write(90);
  lcd.setCursor(0, 0);
  lcd.print("Ready to Fire    ");
  digitalWrite(laser, LOW);
  delay(1000);
  // -----start auto-------------------//
  while (digitalRead(button) == LOW) {
  }
  // first target -----
  servox.write(x1);
  servoy.write(y1);
  delay(1000);
  digitalWrite(laser, HIGH);
  sweep(servox, servoy, x1, y1);
  delay(500);
  digitalWrite(laser, LOW);
  delay(1000);
  // second target -----
  servox.write(x2);
  servoy.write(y2);
  delay(1000);
  digitalWrite(laser, HIGH);
  sweep(servox, servoy, x2, y2);
  delay(500);
  digitalWrite(laser, LOW);
  delay(1000);
  // third target ----
  servox.write(x3);
  servoy.write(y3);
  delay(1000);
  digitalWrite(laser, HIGH);
  sweep(servox, servoy, x3, y3);
  delay(500);
  digitalWrite(laser, LOW);
  delay(1000);
  servox.write(90);
  servoy.write(90);
  delay(500);
  automatic = false;
  manual = true;
  lcd.setCursor(0, 0);
  lcd.print("Destroyed ->              ");
}
// -----------SWEEP------------SWEEP----------------SWEEP------//
void sweep(Servo x, Servo y, int xpos, int ypos) {
  for (int i = -3; i < 3; i += 1) {
    x.write(xpos + i);
    delay(50);
    for (int s = -6; s < 6; s += 1) {
      y.write(ypos + s);
      delay(20);
      display.print(targetCount);
      for (int x : sensors) {
        checkTarget(x);
        if (hit) {
          break;
        }
      }
    }
    if (hit) {
      break;
    }
  }
  hit = false;
}

Step 3: Final Product

Here's a short video showing the various features of the Arduino Laser Turret.

Step 4: Issues With Project

For some reason, in automatic mode, the servos refused to go to the same location that was saved previously. I'm not sure what is causing this, a lack of resolution in the servo potentiometers or an issue with my code. I'm pretty sure the latter is not the case. Usually, it's close so I created a sweep method that lets the servo sweep around the target photoresistor to try to hit it. Most of the time this works fine but sometimes it just gets so far off that doesn't even work. It feels like this is a hardware issue rather than a software issue.

The potentiometers used to control the turret are extremely finicky. When you try to get the laser right on the target it usually comes down to fighting what seems to be a faceted potentiometer in your hand and a faceted potentiometer on the servo. I toyed with the idea to decrease the sensitivity of the controls, so more movement in the hand is less movement on the servo, but I couldn't figure out a way to get that to work. It works as is but it has a learning curve.

Share

    Recommendations

    • Creative Misuse Contest

      Creative Misuse Contest
    • Metalworking Contest

      Metalworking Contest
    • Fix It! Contest

      Fix It! Contest

    Discussions

    The issue with the potentiometers feeling "faceted" is an issue with your map function:

    xtemp = map(analogRead(sxpot), 0, 1023, 0, 180);

    This is taking the relatively large precision of the potentiometers and shrinking it. You could use the "writeMicroseconds()" function built into the servo library to get a greater range. Here is the info: https://www.arduino.cc/en/Reference/ServoWriteMicr...

    This gives you a range of about 1000 values. When you write an angle to the servo from the library it is mapping that value of 0-180 to a value between 1000 and 2000. That range is much closer to that of your potentiometer so greater precision could be achieved.