Bare bones head tracking shoulder cannon circuitry.


I like to do everything on a cheap budget with things I can find in bulk or find easily. I tried for years to get this right and think I have a pretty stable version of a Shoulder cannon that tracks head movement in both Y and Z axis, and for cheap as I think I can get it with COTS items (Common Off The Shelf).
Here's the video:

It's not your typical caster cannon, but I feel the shoulder cannon is like the Jedi knight's light saber. It is built individually....and I liked the look of the 80/20 Aluminum bracketing.

I used an Arduino Uno, 2ea - 5vdc servos, an MPU6050 Arduino module (6 axis), a project box from Amazon, RJ45 (Ethernet) connectors with panel mounts, a panel mounted audio jack, a few things I had around my shop, and a 2000 maH rechargeable battery bank (the ones used for charging your phones...I will upgrade to 4000+ maH for more hours of use). I'll put the code I used in my next comment.


Here is the Arduino Code: MUST install both the "I2CDEV" and "MPU6050" libraries into the Arduino IDE. Ensure you do the Zero Sketch from the MPU6050 examples first and input those zeroing numbers (look for gyro offset comment) into the Arduino code below. Each MPU6050 will be slightly different.

It is set up to scan the servos when walking around. Pressing the fire laser button will lock the servos on the last known position, make an LED increase in intensity over a short duration, fire the laser pointer for 0.5s, then dim the LED down fast to off. I did this to simulate it charging up, firing, then powering down. It will only fire once per button push. You could code in a small recoil of the Y Axis servo if you wanted when fired. If you have any questions...please ask. the servos will automatically go to whatever position the MPU sensor is at once the firing function is complete. I can try to add any additional functions that could make it even more awesome. Just need some time to code it in and make sure it works.

   Predator Shoulder Ca
   nnon v2
   by: Raymond Willis 8/1/17

   I edited serveral different pre-existing programs to
   create this one. Too many to list all sources. If you
   see your code, thank you.
   This program uses 2 servos, 1 MPU6050 3 AXIS Module, 1 LED,
   and 1 Laser Diode to have the servo mounted shoulder cannon
   track your head position like in the Predator movies and
   when a button is pressed the LED birghtens, fires the laser
   diode for 0.5s, and then dims the LED (simulate a build up
   of energy, firing, and then energy dissipation).
   The I2CDev and MPU6050 libraries are required for this program.

   The Servos follow the Y and Z axis of the MPU6050. The
   module should be placed at the top-center of your head
   or mask/helmet. When walking around, the Z axis will drift.
   So at certain degrees for an extended period I assume the user
   is walking around and go into scan mode, then when not walking
   around I reset the MPU to re-zero the Z Axis.

   I commented as much as I can to help anyone else use this.

#include <Servo.h>
#include <string.h>
#include <stdio.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"

Servo myservoY; // Roll
Servo myservoX; // Pitch
Servo myservoZ; // Yaw
int countA = 0;
int countB = 0;
int posY;
int posZ;
int rstZ;
int buttonState = LOW;
int oneFlash = 1; // variable to flash the LED Laser once per each button press.
const int buttonPin = 7; // Mom switch pin, fires laser diode
const int ledPin = 3;  // LED
const int laserPin = 4; // Laser Diode

#include "Wire.h"
MPU6050 mpu;

#define OUTPUT_READABLE_YAWPITCHROLL    //Comment or uncomment to output data

#define LED_PIN 13
bool blinkState = false;

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
  mpuInterrupt = true;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(laserPin, OUTPUT);
  pinMode(buttonPin, INPUT);

  // join I2C bus (I2Cdev library doesn't do this automatically)
  TWBR = 24;
// 400kHz I2C clock (200kHz if CPU is 8MHz)
  Fastwire::setup(400, true);

  myservoY.attach(9); // Attach Y servo to pin 9
  //myservoX.attach(10);// Attach X servo to pin 10, not used
  myservoZ.attach(11); // Attach Z servo to pin 11

  while (!Serial); // wait for enumeration, others continue immediately

  // initialize device
  Serial.println(F("Initializing I2C devices..."));

  // verify connection
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // load and configure the DMP
  Serial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(2); //was 49
  mpu.setYGyroOffset(-29); //was -26
  mpu.setZGyroOffset(36);  // was 13
  mpu.setZAccelOffset(923); // 1688 factory default for my test chip 1459

  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
    // turn on the DMP, now that it's ready
    Serial.println(F("Enabling DMP..."));

    // enable Arduino interrupt detection
    Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
    attachInterrupt(0, dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();

    // set our DMP Ready flag so the main loop() function knows it's okay to use it
    Serial.println(F("DMP ready! Waiting for first interrupt..."));
    dmpReady = true;

    // get expected DMP packet size for later comparison
    packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
    // ERROR!
    // 1 = initial memory load failed
    // 2 = DMP configuration updates failed
    // (if it's going to break, usually the code will be 1)
    Serial.print(F("DMP Initialization failed (code "));

  // configure LED for output
  pinMode(LED_PIN, OUTPUT);

// Main Program Loop
void loop() {
  // if programming failed, don't try to do anything
  if (!dmpReady) return;

  // wait for MPU interrupt or extra packet(s) available
  while (!mpuInterrupt && fifoCount < packetSize) {
    // other program behavior stuff here if oyu want


  // reset interrupt flag and get INT_STATUS byte
  mpuInterrupt = false;
  mpuIntStatus = mpu.getIntStatus();

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();

  // check for overflow (this should never happen unless our code is too inefficient)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
    // reset so we can continue cleanly
    Serial.println(F("FIFO overflow!"));

    // otherwise, check for DMP data ready interrupt (this should happen frequently)
  } else if (mpuIntStatus & 0x02) {
    // wait for correct available data length, should be a VERY short wait
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);

    // track FIFO count here in case there is > 1 packet available
    // (this lets us immediately read more without waiting for an interrupt)
    fifoCount -= packetSize;

    // display Euler angles in degrees
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    Serial.print(ypr[0] * 180 / M_PI);
    myservoZ.write(int(ypr[0] * 180 / M_PI) + 90); // Rotation around Z
    rstZ = (ypr[0] * 180 / M_PI); // Z axis into a variable to test
    Serial.print(ypr[1] * 180 / M_PI);
    myservoY.write(int(ypr[1] * 180 / M_PI) + 90); // Rotation around Y
    Serial.println(ypr[2] * 180 / M_PI);
    myservoX.write(int(ypr[2] * 180 / M_PI) + 90); // Rotation around X

    /* If the Z axis servo is max left or right for so many seconds,
       you are probably walking around and we need to re-orient the
       Z Axis gyro's earth reference point to maintain correct orientation
       for tracking head movement.
    if (rstZ > 100 || rstZ < (-100)) { // test if Z Axis is at max L or R
      countA++; // count each time too far L or R
      if (countA >= 3) { //if max L or R, 3 consecutive times..reset MPU

    if (rstZ < 99 && rstZ > (-99)) { // when outside L or R max
      countA = 0; // reset count to 0, you are likely stationary

    buttonState = digitalRead(buttonPin); //read the firing button state
    if (buttonState == HIGH) { // if the button reads High, the button is pressed
      posY = (ypr[1] * 180 / M_PI); //read Y position
      posZ = (ypr[0] * 180 / M_PI); //read Z position
      myservoY.write(posY + 90); //stop moving and stay at last know positions
      myservoZ.write(posZ + 90);
      laserFire(); //perform the laser firing function

    else if (buttonState == LOW) {
      // If Im not holding the button, turn everything off:
      analogWrite(ledPin, 0);
      digitalWrite(laserPin, LOW);
      oneFlash = 1;
  Serial.print("Reset Count: ");
  Serial.print("Laser Fire Button: ");

void resetMode() { // reset function
  Serial.println("You must be walking around so let's reset Z Axis reference point!");
  countA = 0;
  mpu.reset();        // reset the Gyros to reset Z axis reference point
  setup();            // re-initialize everything for new gyro data

void scanMode() { // scan mode function
  Serial.println("We are in Auto Scan Mode.");
  posY = 90;                                      // set Y servo to 90 degrees
  for (posZ = 45; posZ <= 115; posZ += 1)         // start scanning Z servo L to R
  for (posZ = 115; posZ >= 45; posZ -= 1)

void laserFire() { //Led & laser diode firing function
  if (oneFlash >= 1) {

    for (int fadeValue = 10 ; fadeValue <= 255; fadeValue += 5) {
      analogWrite(ledPin, fadeValue); // LED glows bright, charging up
    analogWrite(ledPin, 255); // after ramp up, keep on
     //recreate mini recoil
    digitalWrite(laserPin, HIGH); //energize laser diode
    delay (500); // Laser Pointer ON time in millisecs after LED ramp up is done.
    digitalWrite(laserPin, LOW); //laser diode off
    for (int fadeValue = 255 ; fadeValue >= 0; fadeValue -= 10) {
      analogWrite(ledPin, fadeValue); // LED dims down.
    analogWrite(ledPin, 0); //led off
    oneFlash = 0; //set button count to ensure it only fires once per button press
Last edited:


What a great work, my friend, that is incredible.I am learning to program with arduino to also mount the computer bomb


I cannot express enough how incorporating Arduino Uno, Nano, or any other pre-built microcontroller is cheap and easy. Arduino IDE is free and provides a huge list of examples to get you going in no time.... and an quick Google search can yield even more examples and pre-built libraries to code from. You can get Arduino Nanos or Minis for like 5ea for $15 on Amazon (I even bought the Arduino rip offs and they are great for prop building). Arduino Nano, a few switches, flat LEDs, and you can code some cool wrist gauntlets in no time.
I've been toying with the idea of using Arduino to power a linear actuator to have real in/out gauntlet blades, but even the small linear actuators (or solenoids) are over $100. I would think a movement of 6" or more would be enough for the effect. I'll keep searching.
I have made several things with Arduino and it inspires me to make more. I created a wizard staff that lights up depending on how to tap it on the ground and has a secret reed switch to activate a LED sparkle mode. I have another idea (which I have already made schematics for) in which a real temperature sensor will make the 3 LED lights on Vader's chest box vary in pattern and pattern speed based on actual outside temperature.


What a great work, my friend, that is incredible.I am learning to program with arduino to also mount the computer bomb
An Arduino Nano, some switches, and flat LEDS on a breadboard. Just randomize the LED sequencing using random function and a switch case for ON/OFF (HIGH/LOW) state.

Random Function:
Arduino - Random
Last edited:


Estaba pensando en usar una matriz led 8x8 para cada pantalla y activarla individualmente
Sorry, I don't understand Spanish very well... I wish I did.

I think I got you are using an LED 8x8 matrix for the display. Me personally I would just use the flat LEDs in the pattern used for gauntlets. Many people in the forums have them in the right pattern. From my understanding, in the movie they did not just use the old LED 7 segment displays...they made their own display pattern in LEDs not manufactured normally like this: 10 pcs Plastic Metal Common Anode 1 Bit 7 Segment Red LED Display Digital Tube: Home Audio & Theater

Maybe a 16 segmented display would be close:
0.39" (9.9mm) Single Digit Alpha-Numeric Blue LED Display, Black Face, Common Anode (Pack of 3) - -


Wow, what a great strategy. The movement seems to be very smooth. Definitely going to look into this as a future upgrade! Great Work!!:D


As long as it uses the MPU6050, any add on module should work.
But, there is still the issue of mapping rotation around the z axis to properly rotate the servo when turning your head. Reason is it defines a center point when first powered up. You have to redefine a center point after walking around. You can't track while walking around. What I did in the code was knowing I can only rotate my head, in degrees, so far with a mask on. When I go over those points, I assume I must be walking around because when you do the z servo will begin to turn thinking you are rotating on the z centerline but you are actually walking away from it, so I tell the servos to default to make the cannon go straight ahead and check that reading every loop. When it gets back under the limits, I must be stationary so reset the center point and start tracking head movement again. When stationary, this works great.
I may be updating the code. The nice thing about the MPU 6050 is it has 2 accelerometers also incorporated. What I may do is read those to see of I'm walking around to default the servos....not using the limits of my head rotation.


holy crap.....great work that is amazing you guys on here are incredible with the practical work effects you create...bravo!


Wow. I hope one day to achieve something like this but I am new to electronics and programming but am eager to learn. Hopefully your guides will help me achieve my goals or a working wrist computer and plasma caster


Young Blood
I did something similar of Honus original Build:
wii nunchuck 4 servo control

I may have to switch to undercode's implementation:
Arduino + Wii Nunchuck [Servo Motores] - Underc0de Blog

I will try adding Honus' transister board and see if that helps, but his setup is one of the best ones I have seen.

The undercode setup saves on motor drive times and only runs the motors when you press the c or z buttons.

I was thinking with either code base you could run dual canons, however, you would need about 6 servos to do that properly. Using the third to lift the main arm to 45 degrees and then use the pan/tilt unit servos to control up/down and left/right on the cannon.


Young Blood
I got the setup working and have posted the code in one of my other threads here.

Geez posting from a mobile sucks.
Last edited:


Young Blood
I am still working out the bugs on mine. I have several write ups over at aliens legacy and a couple here. Anywho. What holds you up from doing it yourself?