What is this?

This is, basically, a 2 axis (azimuth/altitude) Wi-Fi remotely controlled robot. Completely OPEN SOURCE (all the design files and control code are fully available for everyone to modify or adapt it to its needs) and with a 3D printed frame. It can hold: small cameras, laser pointers, flashlights, toy guns, a stick…. everything you want to move or point to. It has been designed with these premises in mind:

  1. Have to be easy to print and set-up
  2. Arduino compatible / Python code controllable
  3. With a smooth but fast movement
  4. Accurate
  5. Robust but portable
  6. Adaptable and easy to be modified
The laser pointer robot can rotate on its azimuth (horizontal) plane up to 700º, and on its altitude axis, up to 360º but this last value will depend on the laser/camera/flashlight you are mounting on it (if it is too large it may collide with the laser pointer frame)

This laser pointer robot is a remotely controlled arduino robot created with 3D printed parts. With only two NEMA17 motors (popular and inexpensive), this robot is able to point any object in the X and Y axis. The amplitude, speed and acceleration of its movements can be configured in the CODE. The maximum WI-FI signal range is 60 meters (direct view).

Laser pointer robot Specifications

  • Total height: 200 mm.
  • Nº of axis: 2 (altitude and azimuth)
  • Freedom of movement: -60° to +240° altitude, 0° to ±180° azimuth
  • Max rotational speed: 360°/second 
  • Total Weight: 920 grams
  • Repeatability (degrees): 0.1
  • Maximum Payload Capacity without the need of weight balancing (laser/torch):120 grams
  • Driving motors: 2x NEMA17 1.8° stepper motor
  • Can control a 12V up to 0.5A light/laser via software when connected to the AUX port on the DEVIA control board.
  • Firmware: Arduino code. Control software: Python. Both are freely available.
  • Xbox /PS4 controller compatible
  • Portable (you can use any 9 to 15V DC battery pack or power source)

Interactive 3D model

ABOVE: Version 10 of the camera /laser pointer. Why so many iterations? You may ask. The reason: Minimum quality standards, repeatability and robustness. It is “easy” to develop a robot which just “works”, but making it: easy to be printed, assembled, controlled and replicated is not easy. You can get to a working prototype after 2-4 iterations. Achieving what we are looking for, takes no less than 8-10 additional revisions!


Daniel, using the laser pointer to entertain its cats 😀

Assembly Guide

BEFORE STARTING: Most of this robot elements have been “3D printed”. The “official” KIT comes with PLA Ingeo 870 printed parts, much more durable and with higher resistance to impact than the regular PLA. Of course, you can print the parts by yourself in ABS or PLA but keeping this in mind: You can break it if you apply too much force or tight a screw more that you should. We will let you know, during this assembly guide, when you can tighten the screws as much as you can or where you should just fix a part to another not forcing it at all.

If you choose to print the parts by yourself: every filament (PLA, PLA+, ABS, PETG…) has its own set of “perfect printing” parameters in order to achieve the flawless 3D-printed parts, if you overheat the filament, the layer thickness will be increased and the different part´s tolerances may be compromised. This should not be a problem but using a knife to clean the parts a bit may be mandatory.

During the design process, we have tested many 3D printers and a bunch of filament brands and if you print the parts carefully, you will not find problems at all.

Laser Pointer robot elements (BOM):

Below, the items you need to create this robot. The list includes some more bolts than the quantity really needed, but in this case “better to have too much, than too little”

Laser pointer 3D printed parts 1
NEMA 17 stepper motor (MT-1703HS168A or equivalent) 2
MOTOR CABLES (45 cms) 1
MOTOR CABLES (14 cms) 1
Mouse projector LED torch OPTIONAL
20 teeth GT2 pulley 2
Circular Ball bearing 6002RS or 6002ZZ 2
zip ties (100mm long, 5mm wide) 6-10
200mm Timing belt (200 GT2) 3
Cable wrap 50 cms (not mandatory but recommended) 1
Silicone “feet” (to improve its “ground anchoring“) 1
DEVIA control Board (or equivalent Arduino M0, ESP8266) 1
Motor drivers A4988 heatsinks 2
USB cable 1m (micro USB connector) 1
12V/2A Power supply with 2.1mm POWER JACK OPTIONAL
Car “Lighter socket” power cord with 2.1 POWER JACK OPTIONAL
M3 6mm bolt 6
M3 10mm bolt 14
M3 15mm bolt 10
M3 45mm bolt 4
M3 nuts 6
Camera NUT (tripod type, if you are planning to attach it to a tripod) 4
First, you need the 3D printed parts. You can print them by yourself. Download all of them from here. Check the above image if you do not know which part is which. Consult the interactive 3D model if you need a good point of view of the robot: rotate/tilt it, see where everything goes.
Direct link to the 3D parts repository: here
Let’s start with the easy step: fix one of the 20 teeth pulleys to one of the NEMA17 motors.
Now, take the LOWER ARM and the LOWER ARM PULLEY SUPPORT parts and, using 4x6mm bolts, put both parts together. Use 1x15mm bolt to attach the very end of LOWER ARM (botton right part of the photo). Do not fully tighten the bolts yet, as we still need to attach the NEMA17 motor.
This step in IMPORTANT: insert the 6002 ball bearing (red arrow) in the PULLEY 80 as indicated above. The protruding ring of the PULLEY 80 has to be on the side touching the LOWER ARM PULLEY SUPPORT. Then firmly push the 6002 ball bearing + PULLEY 80 into the “knob” of the LOWER ARM PULLEY SUPPORT. If you feel there is clearance and the ball bearing is not firmly fixed, do as the image above: use adhesive tape (any kind) to increase the width of the knob. It is very important for the laser pointer to have a firm fixation here, otherwise it will vibrate with high speed movements. You can use glue as alternative. Prepare the CAP with a 10 mm (red circle) bolt. This bolt will keep the pulley in place.
Blue: the CAP in place and the 4x15mm bolts already set. Leave this component aside, we will come back to it later.
Now, insert the other 6002 ball bearing inside the socket of the UPPER ARM SUPPORT part. Again, it should fit firmly. Then flip the part and fix the motor you already prepared with the 20 teeth pulley using 2X6mm bolts for the upper holes and 2X 10mm bolts to attach the UPPER ARM MOTOR HOLDER RIGHT part. Check the motor orientation in the image below: where should the motor connector be pointing to?.
Note: the 2x6mm + 2x10mm bolts will firmly “capture” the motor to the plastic part, but do not completely tighten these bolts yet. There is a belt we will need to place and stretch. In red circles: 2x10mm bolts ready to be screwed.
Check the orientation of the motor as indicated above. The motor´s cable connector has to be orientated like that. Remove the two bolts in the red circles. We will replace them with longer ones that will keep the motor firmly fixed to the frame.
Take the PULLEY 64 part and one of the 200 GT2 belts. Insert the PULLEY 64 into the 6002 ball bearing hole and run the belt around the metal pulley. Then, you can now completely tighten the bolts that will keep the motor in place. NOTE: if the 4x bolts of the motor are not fully tighten, you will be able to move it up and down a little, helping you to stretch the belt until you fell it tense enough: you should be able to bend the belt under the force applied by your pinkie finger, so “tense” means, do not over tight it.
This is a photo of the other side of the LOWER ARM PULLEY SUPPORT. Spinning the PULLEY 64 when you are trying to place the belt in place will help you to pass the belt around the pulleys.
Once the timing belt is tense, fully tigthen the M3 bolts.
At this point, if you have printed several “holders”, pick one and using 2x M15 bolts place it as shown below. You may need to slightly unscrew both bolts if you feel the pulley can not rotate freely.
Detail of one holder (15mm diameter) in place.
There are available, several “holders” for different items (laser pointers, led torch, cameras, flashlights…). If you want to attach your own gadget, feel free to design it: download the 3D file and modify it as your requirements. There is a diagram below indicating the dimensions any “holder” should have on its base to perfectly fit on the laser pointer.
Above: Holder’s BOTTOM VIEW. Any holder designed to support any custom gadget should have the dimensions indicated above.
If you get the PULLEY 80 you have already assembled, you will notice 4 holes on it. Take the UPPER ARM part and, using 4x 10mm bolts, put both parts together.
Now, get the UPPER ARM MOTOR HOLDER LEFT and using just one M3 6mm bolt, fix it (green circle). Then insert 2x40mm as shown. They will capture a NEMA17 motor.
Remember when you removed (with some effort) two motor´s base bolts? Now it is time to place the motor inserting the new bolts replacements and tighten those bolts.
Side view of the same procedure.
Now, tighten the other 2x 6mm bolts you already set on the UPPER ARM MOTOR HOLDER RIGHT, so the UPPER ARM SUPPORT (so does the motor) stay firmly in place attached to the UPPER ARM
Above: another different support placed after removing its two fixing bolts. It is quite simple to change holders.
Another image of the two bolts you had to screw (6mm)
Check the orientation of the remaining motor and using the 4x 15mm bolts, fix it. Do not fully tighten them, there is a timing belt we need to place. The cable connector has to be pointing “OUT” (green circle)
Now, run the belt around the motor. As before, spinning the pulleys will help you to set the belt properly. Once set, stretch the belt until it is tense and fully tighten the bolts.
Take the BASE and, using 3x 10mm bolts, fix the 3x rubber feet as above. Be gentle! Use 1x6mm or 10mm bolt to attach the back of the DEVIA control board to its case (bolt location: upper-left in the photo)
Then, insert 2x40mm bolts (green circles). They will keep in place the BASE MOTOR (azimuth motor). Use a 15mm and 6mm bolt to fix the Control board DEVIA to the tripod´s leg. Do not worry about placing the A4988 modules yet. You can do that later. Check the power barrel socket of the DEVIA in the image above to know the orientation of the DEVIA control board.
Remove the motor´s bolts indicated in the green circles. They will be replaced with the 40mm ones.
OPTIONAL: planning to attach the robot to a tripod? Do this: heat up with a solder iron or any other heat source the camera´s nut. Do it until you can barely touch it, then, using a screwdriver, screw it in the hole as you can see above. The PLA plastic will melt around the screw’s thread, making it solid with the BASE. This is a popular technic used to embed screws, nuts and similar elements into PLA printed parts (valid for ABS and PETG materials too)
And… done! You still have to connect the motors to the control board. For that, move to the next steps.

How to upload the firmware to the DEVIA control board (or compatible Arduino board)

(Skip this step if you got the Plug & Play Laser pointer robot kit version. The Arduino is already programmed)

a) Install the Arduino IDE on your PC from here (skip this step if you have the Arduino IDE already installed) This Laser pointer code has been tested and developed on IDE version 1.8.11 and later versions. If you have a problem compiling the code, let us know

b) Download all the arduino files from here. Copy the files inside the Laser_pointer_VX folder in your hard drive  

c) Compile and send the code to the DEVIA control board:

  1. Open your Arduino IDE
  2. Open the main code in /Laser_pointer_VX/Laser_pointer_VX.ino
  3. Connect your DEVIA board with the USB cable to the PC
  4. Note: If this is the first time you connect an Arduino board to your PC maybe you might need to install the driver.
  5. Select the board Arduino/Genuino ZERO (native USB port). In the TOOLS menu->board
  6. Select the serial port that appears on the tools->Serial port
  7. Send the code to the board (UPLOAD button: Arrow pointing to the RIGHT)
Selecting the right board
Selecting the right board before uploading the code

d) Done!

Electronics. How to connect everything

Check the image above to know how to connect everything properly. Double-check the polarity of the motor cables connected to the control board. If you connect it backwards, the motor will spin in the opposite direction.


  • Never connect a cable or A4988 module when the power supply is ON/ plugged.
  • When placing the heat sinks on top of the A4988 modules, do not let them touch the metal headers protruding from the modules. That may cause a short-circuit and possibly damaging the modules and/or control board.
  • Cables slack: Keep in mind that the cables must let the robot move freely. If they are too tight, they will limit the robot movement (and possibly they will get broken). So, run the cables from the motors to the control board and move the laser pointer to its physical limits. Once you are sure the cables are not interfering with the movements, fix them using zip ties. There are holes in the laser pointer robot where you can fix the zipties. You can use the cable wrap to tidy everything up a bit.

Python and Arduino CODE. Controlling the robot

Download the Python code from here.

NOTE:You need to have already installed the Python 3.8 in your computer. And the libraries:

  • pygame
  • serial
  • tkinter
  • time

LINK: how to install Python libraries

Basically, the python code will send the Laser pointer two angles: Azimuth and altitude.

You can send these two parameters via a SERIAL port (Usb cable connected to the DEVIA control board) or via WI-FI.

SERIAL: you need to know which COM PORT has been assigned by your computer to the DEVIA control board. It will vary from computer to computer. You can know it, opening the Arduino IDE and checking the TOOLS-> PORT menu

In this case, the computer assigned the COM3 to the DEVIA

Once you know it, you need to let the Python code where is the Laser pointer robot in this code line:

COM_PORT = 'COM3' # You could check the Serial port number on Arduino

Just modify the COM3 port for any other it has been assigned. Then, RUN (pressing F5) the code after saving changes.

Alternatively, if you are planning to control the robot via WI-FI just connect your computer to the NETWORK the DEVIA control board will create after some seconds once powered up. Its name should be something like JJROBOTS_XX. Use this password to get access to the network: 87654321

By default, the connection will be established over WI-FI. If you want to control the laser pointer robot with a USB cable modify this code line, and set it as TRUE:


CONTROL COMMAND from Python: syntax

The Arduino firmware is in charge of understanding the control command send by the Python code (or any lenguage), actuating the motors, calculate how to move both at the same time…etc. The syntax is of the command the DEVIA control board understand is like this:


Where every value means:

sendAngles2(Azimuth angle, Altitude angle, time to get to position)

For this case, 130 is the AZIMUTH angle the laser pointer will move to, 10, the ALTITUDE and 1, the time, in seconds we are giving the robot to get there. Check image below.

Azimuth can go from 0° (starting position of the laser pointer) to 360°. Positive values will move the robot as the image indicates. You can get to 120° both using the command sendAngles2(120,0,1) or sendAngles2(-240,0,1)

Altitude can go from 0° (starting position of the laser pointer) to 360°. Positive values will move the robot as the image indicates with the movement arrows. Example: You can get to 120° using the command sendAngles2(0,120,1)

NOTE: Altitude values are limited by the gadget’s dimensions you have attached to the laser pointer robot. It may collide with the frame, limiting its movement to less than 90° or not restricting the freedom of movement at all.

There is another command you can send to configure the laser pointer behavior. This one will set the maximum speed and acceleration of the robot:


Where b’JJAS’ is the command header identifier of the message to be sent. And the following values are:

sendCommand(b’JJAS’,50,0,70,0), where 50 indicates that we are moving the laser pointer MOTOR1 at 50% of its maximum speed and 70% of its maximum acceleration.


But… where are those MAX SPEED and MAX ACCEL parameters defined? Well, in the Arduino (firmware) code:

If you open the configuration.h file in the Arduino IDE, you will find all the parameters that defines the laser pointer behavior: movement limits for both motors, reductions (pulleys), and maximum speeds and accelerations. They have been set, so the robot will behave “properly”, but feel free to play with the values if you want to.

So, for example: If in the Arduino CODE it is defined the max speed and acceleration as this: #define MAX_SPEED_M1 16000, #define MAX_ACCEL_M1 30. And we send, this command: sendCommand(b’JJAS’,50,0,70,0), we are telling the laser pointer to move the MOTOR1 at 50% of 16000 steps per second (8000) and get up to 70% of its maximum acceleration (21).

BELOW: This is where, in the configuration.h file everything has been defined. These are the “firmware” parameters that the Arduino will use to move the laser pointer.

#define ROBOT_ABSOLUTE_MAX_M1 121 //121 //114 //121   // max degrees
#define ROBOT_ABSOLUTE_MAX_M2 142 //146 //146 //142   // max degrees

#define MOTOR_STEPS 200 //1.8 degree motor = 360/1.8=200

#define M1_REDUCTION 4
#define M2_REDUCTION 3.2

// This depends on your motor type and reduction

// Maximun motor acceleration in (steps/seg2)/1000 [acceleration in miliseconds] 30 => 30.000 steps/sec2
#define MAX_ACCEL_M1 30  //50
#define MAX_ACCEL_M2 30  //50

#define MIN_ACCEL_M1 50
#define MIN_ACCEL_M2 50

// Maximun speed in steps/seg (max 32765)
#define MAX_SPEED_M1 16000 //16000
#define MAX_SPEED_M2 16000 //16000

#define MIN_SPEED_M1 200  //1000
#define MIN_SPEED_M2 200  //1000

Python: If you run the file, (the control APP) you will find a popup menu like this:

There are 3 fields. You can send azimuth and altitude angles just copying the syntax created by the Stellarium software (we did it like this for a new feature we are working on)

121°13’32.2″   +21°53’33.7 (you can copy and paste these coordinates to the field “AZ/ALT” and after pressing “Go!” the laser pointer will move and point at that point.

Or you can just populate the Angle1 (Azimuth) and Angle2 (Altitude) files with the angles you want the laser pointer to point to.

NOTE: you can, additionally, move the laser pointer using the computer’s keyboard and or Xbox /PS4 controller connected to your PC.

For the Keyboard: use the arrow keys.

Arrow keys: normal movement

CONTROL + Arrow keys: more precision

SHIFT + Arrow keys: faster movement

To control the laser pointer with the controller, just connect it to your computer and check if it has been recognized. Then, launch the python control APP and move the robot using the right thumb stick. Easy!

Video: the Xbox controller is connected to the computer (Bluetooth), which is already running the Python control code. The computer is connected via Wi-Fi to the laser pointer robot.

Python control: CODE EXAMPLE

Download the example code from here (laserpointer_control EXAMPLE

This code has additional lines (see below) that will move the laser pointer to certain positions. In this case, the laser will draw a kind of “cross”, 10 times: defined in the line ( for i in range(0,10): )

The robot movement is defined by the command sendAngles2 (azimuth, altitude, time). We are giving the robot 0.2 seconds to complete the movement (from the code t=0.2). If the “goal” point is not reached within those 0.2 seconds, the robot will continue to the next point and try to get there within the time assigned to this new movement (again, 0.2 seconds). The coordinates the robot will point to are 8,0 -> 0,0 -> -8,0 -> 0,0 -> 0,8

for i in range(0,10):
    t=0.2 #Time we are giving the robot to get there. If the movement takes too long to reproduce,
          #the robot will move to the next one even when the previous has not been completed.
Get the laser pointer robot



Laser Pointer robot is not responding to the command sent

Check you are connected to the JJROBOTS_XX network using the correct password (by default: 87654321) and your device has not blocked the data traffic to the B-robot (stay always connected to the robot)


Confirm you are running the Python code with the WIFI option enabled. Check guide above to know how to solve this. If you are running the code with the SERIAL option enabled, check the USB cable: use another one, check if you are selecting the right COM port and SPEED (115200)

My robot lacks of power or fall without reason

Adjust the current delivered by the stepper motors drivers. Use a screwdriver and gently rotate the screws indicated on the photo below. Rotating 10º-30º is more than enough.

A good explanation about how to do it here.

A4988 adjusment1
Clockwise rotation: increase the power delivered to the motors