Python Controlled , Arduino Laser Turret

by mejdchennoufi06 in Circuits > Arduino

1388 Views, 2 Favorites, 0 Comments

Python Controlled , Arduino Laser Turret

mouse execution.png

In this project, we'll be seeing the steps, supplies and code needed to make your own Arduino laser turret, controlled with a mouse or even a game controller, with the help of a python.

Supplies

arduino nano.jpg
Servo.jpg
laser diode.jpg

we'll be needing just a couple of components :

* An Arduino Nano.

* Two servo motors

Note: you can compromise by getting cheaper servo motors with plastic gears , but I recommend the metal geared and for the higher torch and the nicer build quality.

* A laser diode.

* Along with a soldering iron and some solder, a mini USB cable for the Arduino nano, a computer with the Arduino IDE software , and optionally Python 3 installed and of course a mouse or game controller.

Note : I will be providing a .exe file, so the computer won't need Python to run the application, it's only an option if you want to tinker with the code on your own.

Wiring

wiring diagram.png

* The wiring job here is as easy as it gets, you don't even necessarily need a soldering iron, but it's recommended for good looks' sake, just follow the above wiring diagram, and you'll be good.

* And as for the physical construction, you just fixate one servo to another with hot glue or zip ties at a 90 degree angle.

The Code

markus-spiske-cvbbo4pzwpg-unsplash.jpg

The code in this project consists of many parts, just pay good attention , and read the explanation in the code , and you'll be good to go.

The Arduino Code

Arduino firmata steps.png

Since the control is managed by python , we won't need to write any Arduino code , we just have to upload the "standard Firmata" code , found in the Arduino IDE examples like shown in the above image.

The Python Code

Python logo.png

The python code is differnt for each the controller and the mouse , so just follow along.

The Python Code for the Mouse

mouse execution.png
iconfinder_Mouse_605503.png

As mentioned before, the code is explained thoroughly with comments, so it shouldn't be hard to understand :

#importing the necessary libraries:
import pygame as pg        #importing the pygame library, for the UI and naming it "pg" for simplicity.
import pyfirmata        #importing the pyfirmata library for the python-Arduino communication.

board = pyfirmata.Arduino('your port')		#initializing the Arduino board at the specified port. Note that you will find this port when you upload the Firmata code to your Arduino , named as COM + a number , so just paste it here in all caps.
print("Arduino connected successflly")		#print a message to inform the user that the connection has been successfully established.

pg.init()       #initiating the pygame library, s owe can have access to all its components.
screen = pg.display.set_mode((500,500))     #setting the window parameters (width / height) and naming the window element "screen".
pg.display.set_caption("turret control")		#setting the pygame window label to "turret control".

font = pg.font.Font('freesansbold.ttf',15)      #setting a font element with a size of 15 and a "sans bold" style and giving it the name "font".

size = 0         #making a variable for the circle size / the laser intensity.
done = True     #making a variable for the loop , to stop the main loop when we stop the pygame application.

#Assigning names to each pin connected to the sevos and the laser diode , we chose those pins in articular because they are PWM pin , which we need in this application.
servo1 = board.get_pin('d:5:s')
servo2 = board.get_pin('d:6:s')
laser = board.get_pin('d:9:s')

while done :        #only execute the pygame application if the loop variable is True. 
    for event in pg.event.get():        #for every event happening do the following. 
        if event.type == pg.QUIT:        #check if it's a pygame quitting event (check if the closing button is being pressed).
            done = False        #end the loop by changing the loop variable into false. 
            
            
    pg.display.flip()       #function necessary for the pygame window to function.
    screen.fill((0,0,0))        #refresh the screen by filling it with the color black, erasing the previous overlay in the process.
    
    (mouse_x,mouse_y) = pg.mouse.get_pos()      #assigning the mouse x axis and y axis position to two variable.

    #keep in mind that the "pg. mouse. get_pos ()" function contains both the x AND y information of the mouse, therefore, it has to be stored in two variable, like you see above.
    #also the mouse position will go from 0 to 500 in each axis, since those are the window parameters we set previously.

    #converting the mouse info , to go from 0 to 180 in each axis , rather than from 0 to 500 , for future use with the servos.
    x = int(mouse_x * (180/500))
    y = int(mouse_y * (180/500))

    #converting the position info of the mouse from 0 to 180 into info from 0 to 255 , because it's needed for the write function.
    servo_x = x * (255/180)
    servo_y = y * (255/180)

    #writing the final values to the servos.
    servo1.write(servo_x)
    servo2.write(servo_y)

    #storing the x and y information (in degrees) in two text variables to be displayed later.
    text1 = font.render("x position :  " + str(x) + "°",1,(255,255,255))
    text2 = font.render("y position :  " + str(y) + "°",1,(255,255,255))

    #writing the text to the screen with the specified coordinates.
    screen.blit(text1,(10,10))
    screen.blit(text2,(10,25))

    pg.draw.line(screen , (0,0,255),(mouse_x,0),(mouse_x,500),3)        #drawing a vertical line following the x position in the window.
    pg.draw.line(screen , (255,0,0),(0,mouse_y),(500,mouse_y),3)         #drawing a horizontal line following the y position in the window.
    pg.draw.circle(screen,(0,255,0),(mouse_x,mouse_y),int(size/50))         #drawing a circle at the intersection of the two lines.

    #keep in mind that the size variable is really big, which is decreased by dividing it by 50 in the drawing function, AND that is in order to decrease the time it takes to transition from one size to another, since if we don't do this, the transition will be practically instantaneous, and we won't get the transition effect between the sizes.
    right_button = pg.mouse.get_pressed()[0]        #assigning the value of the right button to a variable.
    left_button = pg.mouse.get_pressed()[2]     #assigning the value of the left button to a variable.

    if right_button :       #if the first button was pressed, increase the circle size, therefore the laser intensity.
        size += 1

    if left_button :         #if the second button was pressed decrease the circle size and the laser intensity.
        size = size - 1

    laser.write(size*(255/600))		#writing the laser intensity ,  proportional to the size of the circle , to the laser pin.

    #limiting function, to stop the circle size from increasing/decreasing beyond the desired limit.

    if size > 600:      #if the size variable gets too big , stop it from increasing.
        size = 600

    if size < 200:        #if the size variable gets too small , stop it from decreasing.
        size = 200

The Python Code for the Controller

controller execution.png
1024px-Ps-controller-icon.svg.png

Same thing for the controller code here :

#importing the necessary libraries:
import pygame as pg 		#importing the pygame library, for the UI and naming it "pg" for simplicity.
import pyfirmata		#importing the pyfirmata library for the python-Arduino communication.

board = pyfirmata.Arduino('COM9')		#initializing the Arduino board at the specified port. Note that you will find this port when you upload the Firmata code to your Arduino , named as COM + a number , so just paste it here in all caps.
print("Arduino connected successflly")		#print a message to inform the user that the connection has been successfully established.

pg.init()		#initiating the pygame library, s owe can have access to all its components.
screen = pg.display.set_mode((500,500))		#setting the window parameters (width / height) and naming the window element "screen".
pg.display.set_caption("turret control")		#setting the pygame window label to "turret control".

font = pg.font.Font('freesansbold.ttf',15)		#setting a font element with a size of 15 and a "sans bold" style and giving it the name "font".

controller = pg.joystick.Joystick(0)		#making a joystick element off the first found joystick and calling it "controller".
controller.init()		#initializing the controller element set before.

x = 250		#making a variable for the x position in the pygame window.
y = 250		 #making a variable for the y position in the pygame window.
size = 0		#making a variable for the circle size / the laser intensity.
done = True     #making a variable for the loop , to stop the main loop when we stop the pygame application.

#Assigning names to each pin connected to the sevos and the laser diode , we chose those pins in articular because they are PWM pin , which we need in this application.
servo1 = board.get_pin('d:5:s')
servo2 = board.get_pin('d:6:s')
laser = board.get_pin('d:9:s')

while done  :		#only execute the pygame application if the loop variable is True. 
    for event in pg.event.get():		#for every event happening do the following. 
        if event.type == pg.QUIT:		#check if it's a pygame quitting event (check if the closing button is being pressed).
        	done = False		#end the loop by changing the loop variable into false.	
            
    pg.display.flip()		#function necessary for the pygame window to function.
    screen.fill((0,0,0))		#refresh the screen by filling it with the color black, erasing the previous overlay in the process.
    
    (x_pos,y_pos) = (controller.get_axis(0),controller.get_axis(1))		#assigning the controller joystick coordinates to two variables.
    
    #something to keep in mind is that the axis used are the horizontal and the vertical one of the same stick and that the joystick value go from -1 to 1 and everything in between is a positive or negative float under the value of 1.

    if x_pos > 0 :		#do the following if the x axis was moved right.
        x = x + 1		  #increase the x position, moving the cursor right.

    if x_pos < 0 :		#do the following if the x axis was moved left.
        x = x - 1		  #decrease the x position, therefore moving the cursor on the screen left.

    if y_pos > 0 :		#do the following if the y axis was moved down.
        y = y + 1		  #increase the y position , and in doing so , moving the cursor in the window down.

    if y_pos < 0 :		#do the following if the y axis was moved up.
        y = y - 1		  #decrease the y position, and with that, moving the cursor up.

    #converting the coordinate info into angle info from 0 to 180.
    angle_x = int(x*(180/500))
    angle_y = int(y*(180/500))

    #converting the coordinate info to go from 0 to 180 into 0 to 255 , to work with the write function.
    servo_x = angle_x*(255/180)
    servo_y = angle_y*(255/180)

    #writing the final values to the servos.
    servo1.write(servo_x)
    servo2.write(servo_y)
    
    #storing both the x and y information of the axees in two text variables to be displayed later.
    text1 = font.render("x position :  " + str(angle_x) + "°",1,(255,255,255))		
    text2 = font.render("y position :  " + str(angle_y) + "°",1,(255,255,255))	

    #writing the text to the screen with the specified coordinates.
    screen.blit(text1,(10,10))		
    screen.blit(text2,(10,25))

    pg.draw.line(screen , (0,0,255),(x,0),(x,500),3)		#drawing a vertical line following the x position in the window.
    pg.draw.line(screen , (255,0,0),(0,y),(500,y),3)		 #drawing a horizontal line following the y position in the window.
    pg.draw.circle(screen,(0,255,0),(x,y),int(size/50))			#drawing a circle at the intersection of the two lines .

    #keep in mind that the size variable is really big, which is decreased by dividing it by 50 in the drawing function, AND that is in order to decrease the time it takes to transition from one size to another, since if we don't do this, the transition will be practically instantaneous, and we won't get the transition effect between the sizes.
    right_trigger = controller.get_button(9)		#assigning the value of the right trigger to a variable.
    left_trigger = controller.get_button(8)		 #assigning the value of the left trigger to a variable.

    if right_trigger :		#if the first button was pressed, increase the circle size, therefore the laser intensity.
    	size += 1

    if left_trigger :		 #if the second button was pressed decrease the circle size and the laser intensity.
        size = size - 1

    laser.write(size*(255/600))		#writing the laser intensity ,  proportional to the size of the circle , to the laser pin.

    #limiting function, to stop the graphic related variables from exceeding their visible limit.

    if size > 600:		#if the size variable gets too big , stop it from increasing.
    	size = 600

    if size < 200:		 #if the size variable gets too small , stop it from decreasing.
    	size = 200

    if x > 500 :			#if the x position gets too big, stop it from getting even bigger, for it not to get the cursor out of the screen.
        x = 500

    if x < 0 :			#same just the way around.	
        x = 0

    if y > 500 :		#And same with the y coordinates.
        y = 500

    if y < 0 :		#same just flipped around.
        y = 0

The End Result : Wiring

With a bit of care and wire management your turret should look a little something like the above image

Note: the construction should be fixed to a heave object at the bottom so the movement doesn't cause the thing to collapse.

The End Result : Code

The below files are the raw python ones , that need python insalled to be able to run.

The Executables links:

Mouse code / Controller code

- As I said before , I'm providing a .exe file for each of the mouse and the controller code.

- something to note is , since every Arduino is different when it comes to Its serial port , I decided to put a text file along with the .exe file where the user puts the name of their COM port , note that :

First , no other information should be inside the text file , and you should pay close attention to any spaces or line jumps , the file has to contain the COM port and NOTHING ELSE , for example : COM9 or COM5 .

Second , this file should be in the same directory as the .exe file from before and It's name shouldn't be changed at all.

Third Note that you may have to extract the app for it to work and that it's gonna ask you if you really want to execute the file , since it doesn't have any company name associated with it , you should click execute , and rest assured that it won't harm your computer in anyway shape or form.

Fourth, some error messages may pop up before the actual execution of the script, don't worry, just close them all up and wait a bit for the app to run, since it could take up to 20 seconds to load because the use of two python packages.

And fifth, you should have already edited the .txt file to your COM port, connected your Arduino with the "standard format" uploaded onto it, and connected your game controller in case you chose the controller script, BEFORE you run the executable, or your script WILL NOT RUN.

Note: if more than two error messages pop up, something is not connected correctly, or you didn't select the right COM port, or the Firmata code isn't uploaded onto your Arduino.