Project tutorial
Laser Pointer Panther Tank

Laser Pointer Panther Tank © GPL3+

Build a robotic Panther tank that can use a laser pointer to play with a cat! It also uses a custom controller with Bluetooth.

  • 1 comment
  • 13 respects

Components and supplies

DFRobot MiniQ Chassis
Laser Diode
Adafruit PCA 9685
Texas instruments l293dne image
Texas Instruments Dual H-Bridge motor drivers L293D
5V Regulator
475267 240424 01 front zoom kankcmvqdh
Raspberry Pi Zero Wireless
9g Micro Servo
Ard nano
Arduino Nano R3
61pby065esl  sx679  tnr8syww5d
HC-05 Bluetooth Module
09590 01
LED (generic)
Omron b3f 1000 image 75px
SparkFun Pushbutton switch 12mm
Actually only 6mm
1800 mAh LiPo Battery 11.1V 30C
1300 mAh LiPo Battery 7.4V 30C
Transistor NPN

Necessary tools and machines

3D Printer
I used the XYZ DaVinci Pro
09507 01
Soldering iron (generic)

Apps and online services

About this project

Recently I created a Laser Shootin' Robot that was able to drive around and had a laser that was fixed in place. This is part 2 of my Tanks in Real Life project series. This time, I took the chassis and added a hull and turret that could both move.

The Hull

I began by 3D printing the necessary parts: 4 hull supports, 4 hull mounting components, the upper turret, the lower turret, and the cupola. I used 4- 3mm x 12mm machine screws to attach the hull supports to the chassis. A brass rod goes across each pair of supports. I then glued the 4 hull mounting components into 2 pairs that align with the hull supports. These attach onto the hull I CNC routed. This will allow for the hull to drop and attach into place on the tank, enabling greater modularity.

The Turret

The barrel on the tank is a 3/4" dowel. It is quite heavy for a micro servo to handle, so I decided to use a lever mechanism to move it. For elevation, the servo moves the opposite way, and vice-versa. I put a hole about 1" deep on the end of the barrel to accommodate the laser diode. The wires for the laser run underneath as to not hinder any movements. I glued the servo onto the lower turret plate facing the cutout. A cotter pin was added between the barrel and servo horn to create the hinge point.

My friend AceofAces16 helped to design the turret and body. Find him at this link:

Tank Electronics

In my previous project Laser Shootin' Tank project, I detailed how to make the chassis move and the laser fire. Those electronics remain the same, except for one part. The Raspberry Pi isn't a microcontroller, but instead a microprocessor. This means it can't time pulses accurately, which causes servos to jitter about and draw large amounts of current. In order to save GPIO pins, I opted to use the I2C PCA 9685, which can output up to 16 channels of PWM pulses, all with just 2 wires! The turret drive servo connects to channel 0, and the barrel servo connects to channel 1. I then connected the PCA 9685 to the Pi by connecting SDA to SDA and SCL to SCL, along with GND to GND and VCC to 3.3V. DO NOT CONNECT 3.3V TO V+, as V+ is for SERVO power, not IC power.

Controller Body

A controller can have the best electronics, but without good ergonomics and looks it is nothing. I designed the controller body to look like a tank, with curves on the back and a frontal slope. It has a separate compartment for a 7.4V 1300 mAh LiPo battery. I 3D printed the back and front, along with the battery compartment. I CNC routed the lower plate and added a hinge so it can open, exposing the battery compartment.

Controller Electronics

I began by taking a 6x4cm perf board and soldering two female headers the size of an Arduino Nano to the board. Next, I soldered the two joysticks to +5V, GND, A0, A1, A2, and A3. Joystick X axis pins go to even-numbered analog pins, and the Y axis go to odd-numbered analog pins. I decided on using an I2C LCD to reduce the number of pins needed. The LCD connects via +5V, GND, A4 for data, and A5 for clock. Finally, a simple tact switch attaches to D2 and GND. This will be a "fire" button that controls the laser.

Using the Panther

Load up the code onto both the Arduino Nano and the Raspberry Pi Zero W. Make sure to include the needed libraries, such as "Timer" for the Nano which can be found here. You also need to put into the same folder as Adafruit provides a PCA 9685 library that is used to control the servos. Additionally, you must pair and bind the HC-05 to the Pi. The HC-05 must be on in order to pair and scan. Using the terminal via SSH, you can do all of this. First, type sudo bluetoothctl, then agent on, then default-agent, then scan on, then scan off, after HC-05 appears and finally pair MAC_OF_HC05 1234. Type "exit" and then sudo rfcomm bind /dev/rfcomm3 MAC_OF_HC05. If you ever forget the MAC address, just type hcitool scan and then copy/paste the address. Now, type sudo python2 path/to/folder/ to run the program.

That's it! Now you can fire a laser from a replica Panther. You can let your cat play around with it by driving it and moving the turret.


Controller CodeC/C++
#include "Timer.h"

int JS_VALS[2] = {};
int JS_VALS_R[2] = {};

const int JS_PINS[4] = {A0, A1, A2, A3}; //L_X, L_Y, R_X, R_Y

const int button_pin = 2;
const int deadzone = 8;

const int servo_limits[5] = {0, 180, 50, 95, 75}; //Left, Right, Lower, Upper, Middle limits for the turret
int turret_positions[2] = {90, servo_limits[4]}; //Initial positions

unsigned long last_time = 0;
long motor_delay = 30; //Delay 30 ms between motor control polls
long turret_delay = 150; //Delay 150 ms between turret control polls

Timer t;

void setup() {
  // put your setup code here, to run once:
pinMode(button_pin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(button_pin), fire_laser, FALLING);

t.every(motor_delay, control_motors);
t.every(turret_delay, control_turret);


void loop() {

void fire_laser(){
  String json_string = "{\"shoot\":\"1\"}";

void control_motors(){
  JS_VALS[0] = analogRead(JS_PINS[0]);
  JS_VALS[1] = analogRead(JS_PINS[1]);
  int x = map(JS_VALS[0], 0, 1023, -50, 50);
  x = constrain(x, -100, 100);
  int y = map(JS_VALS[1], 0, 1023, -50, 50);
  y = constrain(y, -100, 100);
  int motor1 = x+y;
  int motor2 = y-x;
  motor1 = constrain(motor1, -100, 100);
  motor2 = constrain(motor2, -100, 100);
  if(abs(motor1) <= deadzone){
    motor1 = 0;
  if(abs(motor2) <= deadzone){
    motor2 = 0;
  String json_string = "{\"motor\":[" + String(motor1)+","+String(motor2)+"]}";

void control_turret(){
  JS_VALS_R[0] = analogRead(JS_PINS[2]); //Get X values
  JS_VALS_R[1] = analogRead(JS_PINS[3]); //Get Y values
  int x = map(JS_VALS_R[0], 0, 1023, 5, -5);
  x = constrain(x, -5, 5); //Catch outliers
  int y = map(JS_VALS_R[1], 0, 1023, 2, -2);
  y = constrain(y, -2, 2);

  turret_positions[0] += x;
  turret_positions[1] += y;

  if(turret_positions[0] <= servo_limits[0]){
    turret_positions[0] = servo_limits[0];
  else if(turret_positions[0] >= servo_limits[1]){
    turret_positions[0] = servo_limits[1];
  if(turret_positions[1] <= servo_limits[2]){
    turret_positions[1] = servo_limits[2];
  else if(turret_positions[1] >= servo_limits[3]){
    turret_positions[1] = servo_limits[3];
  String json_string = "{\"turret\":[" + String(turret_positions[0])+","+String(turret_positions[1])+"]}";
Robot ClassPython
Put this in the same folder as ""
from __future__ import division
import RPi.GPIO as GPIO
from time import sleep
from serial import Serial
import json

import Adafruit_PCA9685

class Car(object):

	def __init__(self, BT_Port, laser_pin):
		self.ser = Serial(BT_Port, baudrate=9600)
                self.laser_pin = laser_pin
		self.laser_state = False
                GPIO.setup(self.laser_pin, GPIO.OUT)
	def configMotors(self, mL1, mL2, mR1, mR2, invertLR=False):
	    self.motor_array = [mL1, mL2, mR1, mR2]
	    self.invert = invertLR
	    for pin in self.motor_array:
		GPIO.setup(pin, GPIO.OUT)
		#self.motor_pwm.append(GPIO.PWM(pin, 490))
	    self.motorL1_pwm = GPIO.PWM(mL1, 100)
	    self.motorL2_pwm = GPIO.PWM(mL2, 100)
	    self.motorR1_pwm = GPIO.PWM(mR1, 100)
	    self.motorR2_pwm = GPIO.PWM(mR2, 100)
	def configServos(self):
		self.pwm = Adafruit_PCA9685.PCA9685()
		self.servo_min = 150
		self.servo_max = 600
	def deg_to_pulse(self, degree):
		pulse = (degree - 0) * (self.servo_max - self.servo_min) / (180 - 0) + 150 
		print('Pulse: '+str(pulse))
		return int(pulse)
	def receive_command(self):
            self.cmd = ""
            if self.ser.inWaiting():
                self.cmd = self.ser.readline()
                if self.cmd != None:
                        data = self.cmd.decode('utf-8')
                        data = json.loads(data.strip())
                        print data
                        if data != "":
                            return data
                            return ""
                    except ValueError:
	def turn_wheel(self, motor1, motor2):
            if self.invert:
                temp = motor1
                motor1 = motor2
                motor2 = temp
            motor1 = (motor1 / 2)
            motor2 = (motor2 / 2)
            if motor1<0:
		self.motorL1_pwm.ChangeDutyCycle(0)#make positive if negative
            if motor2<0:
		self.motorR1_pwm.ChangeDutyCycle(0)#make positive if negative
	def move_turret(self, turret_deg, barrel_deg):
            self.pwm.set_pwm(0, 0, self.deg_to_pulse(turret_deg))
	    self.pwm.set_pwm(1, 0, self.deg_to_pulse(barrel_deg))
        def fire_laser(self):
	    self.laser_state =  not self.laser_state
            GPIO.output(self.laser_pin, self.laser_state)

	def update(self): #Run to make the robot work
            self.json_obj = self.receive_command()
	    print self.json_obj
                if "motor" in self.cmd:
                    if self.json_obj["motor"] != None:
                   	 val1 = self.json_obj["motor"][0]
			# if val1 > 0:
      			 #    val1 += 10
                   	 val2 = self.json_obj["motor"][1]
			# if val2 <0:
			#	val2 += 4
                   	 self.turn_wheel(val1, val2)
		elif "shoot" in self.cmd:
		elif "turret" in self.cmd:
		    print('Turret found!!!!!!!!!!')
                    val1 = self.json_obj["turret"][0]
                    val2 = self.json_obj["turret"][1]
                    self.move_turret(val1, val2)
            except TypeError:
Robot MainPython
Run this with sudo python2 <filename>
import car

bt_port = "/dev/rfcomm3"
laser_pin = 20
rc_car = car.Car(bt_port, laser_pin)

rc_car.configMotors(6,5,19,13, invertLR=True)
rc_car.configServos() #Turret drive servo, barrel servo

while 1:

Custom parts and enclosures

Controller Back
Controller Front
Controller Battery Compartment
Panther Turret Upper
Panther Capola
Panther Turret Lower
Chassis Support
Hull Connector


Robot Schematic
Schematic1 bb bd1xm8j17q
Controller Schematic
Controller schematic2 bb 87idaaf1ik


Similar projects you might like

WW2 Tank Laser Tag Sherman & Panther

Project tutorial by Arduino “having11” Guy

  • 1 comment
  • 60 respects

Laser Shootin' Robot

Project tutorial by Arduino “having11” Guy

  • 12 respects

Raspberry Pi - Powered Candy Dispenser

Project tutorial by Arduino “having11” Guy

  • 1 comment
  • 13 respects


Project tutorial by Amit Nandi (BigWiz)

  • 81 respects

WalaBeer Tank

Project tutorial by Balázs Simon

  • 177 respects

How to Make a Cat Laser Pointer

Project tutorial by team

  • 8 respects
Add projectSign up / Login