Getting the joystick controlled servo maze built and coded

The November 2020 Alien3D UFO subscription box included the electronics to make the maze many of us have seen on Thingiverse. It is a project I’ve meant to do, and to have it part of this month’s box pushed me over the edge actually to complete the project. I wanted the finished product to have a joystick instead of being controlled through a phone app for multiple reasons. This post will highlight the differences between my project, the original project, and the Alien3D take on the project.

3D printing the pieces

First, I 3D printed the pieces. I used the files from Thingiverse without modifications. I printed all the parts using the GreenGate3D recycled PETG filament samples from the Alien3D November UFO box.

I used a joystick module from one of my Arduino sensor kits. To go with that I printed out this great case made just for this module, Thing 1162200.

Here is a picture that includes the maze printed parts, plus some other things I printed with the GreenGate3D samples.

Prints from the Alien3d November 2020 UFO subscription box.

Testing the servos

Before actually doing anything, I tested the servos. I’ve had bad SG-90 servos before and wanted to make sure they were right before I hot glued them into place. Plus, I wanted to make sure the servos were centered before assembly.

One difference between the original design and the Alien3D design is the use of an Arduino Nano instead of the RoboRed. Most of my projects are done using the Nano, so I was happy with that change. To test the servos and the Nano, I wrote this simple sketch.

include <Servo.h>
 const int ServoXPin = 9;
 const int ServoYPin = 10;

 Servo XServo;
 Servo YServo;

 int ServoPos;

 void setup() {
   // put your setup code here, to run once:
   pinMode(ServoXPin, OUTPUT);
   pinMode(ServoYPin, OUTPUT);

   XServo.attach(ServoXPin);
   YServo.attach(ServoYPin);

   XServo.write(90);
   YServo.write(90);
 }
 void loop() {

    for (ServoPos = 45; ServoPos <= 135; ServoPos += 1) { //      XServo.write(ServoPos); 
      YServo.write(ServoPos); 
      delay(15); 
    } 
   
    for (ServoPos = 135; ServoPos >= 45; ServoPos -= 1) {
      XServo.write(ServoPos);
      YServo.write(ServoPos);
      delay(15);
    }
 }

The above code will home the servos to 90 degrees each. That is the best place to have them when attaching the gimbal. I then ran the code in the void loop to test the basic functionality of the servos. I went from 45 degrees to 135 degrees because I figured that going the full 180 degrees in this project would be way too far, especially since I plan to let kids play with the maze.

After testing the servos, I commented everything in the void loop and saved it to the Nano. That set the servos at 90 degrees.

Assembling the maze

The maze was pretty simple to assemble. I skimmed the original instructable to know how to build the maze. The only deviation from the instruction I did was the screws. To connect the two gimbal centers, I used five M3*8 screws with nuts.

Instead of a rivet to secure each gimbal side, I used two screws I found in my collection of odd screws. I’m not sure of the size, but it was big enough to screw into the gimbal and barely poke into the gimbal bottom and gimbal top.

I did once again test the servos with my above script after assembling the maze. My shaky hands caused issues, and I knew I had not gotten the gimbals installed on the servos even close to the center. After running the script, I had to use the below angle settings to get my base level. Luckily I don’t need the full 180 degrees, so this change is no big deal.

XServo.write(95);
YServo.write(75);

The other change I did was to power my servos via my bench power supply. I know I’ve had problems powering servos through my computer’s USB in the past. If you use an external power supply (which you should), just make sure to share the ground between the servos and the Nano. To make this easy, I mounted the Nano on a mini breadboard. These mini breadboards are great for Nano projects where soldering isn’t required. Plus, it will allow me to change the configuration between different hardware easily.

Testing the maze via Processing

Alien3D created a Processing app to control the maze via a computer. Even though this was not my intended way to use the maze, I did want to try out the Processing app. I’ve heard other makers talk about making Processing interfaces for Arduino projects. This was a chance to see it in operation.

I followed the instructions from Alien3d for getting the Nano setup and the Processing app installed. Below is a short video of me testing out the app. The only change I had made when I took the video was to change the orientation of the DOWN and UP buttons in the app to align with my build.

I then spent a while playing with the Arduino code to get a smoother operation. The above video is the unaltered code. With a little tweaking, I had the maze working pretty smooth. Unfortunately, I forgot to take a video at that point.

And then, of course, I was sidetracked for a few hours playing with Processing and going through tutorials. Even though I didn’t plan on using Processing for this project, I can see where it would be beneficial to use in other projects I’ve been contemplating. A massive shout out to Alien3D for getting me to finally check Processing out.

Opted out of using Bluetooth

The original instructable has the project set up to use a Bluetooth module to control the maze via a cell phone app. Even Alien3D had intended to use a Bluetooth connection in this means. Due to a mixup, the Alien3D kit did not include the Bluetooth module, but instead just a card to hold a Bluetooth module. Personally, I am glad for the mixup. It gave me the chance to play with Processing. Plus, I want little kids to be able to use this maze. I would rather have those kids using a small joystick than my cell phone.

I could have borrowed a Bluetooth module from one of my other robotics projects. But honestly, I find those to be a pain in the rear. Having to disconnect those modules every time I want to make code changes can get annoying. If I do anything Bluetooth-related I am looking at microcontrollers with Bluetooth already built into the design. In particular, I’m looking at using the Adafruit Feather nRF52840 Express for Bluetooth projects.

Hooking up the Joystick

I then hooked up the joystick to the Nano. At this point, I have the Nano and mini breadboard outside of the maze case so I can change configurations quickly. I also have the servos and joystick being powered by 5VDC from my benchtop power supply. The Arduino is being powered via the USB cord to my computer. Here is a look at my testing configuration.

Maze Prototype

I then started from scratch to code the Arduino. To me, this was an exercise to test my ability to get this working without using outside code. So I did not look at how anyone else coded this and built the code one part at a time.

My requirements for the code were:

  • The servo motors should level the maze upon startup.
  • The joystick should move the maze.
  • When the user lets go of the joystick, the maze should stay in its current location.
  • When the button on the joystick is pressed, the maze should gracefully go to the level position.

Here is the code I wrote. I added comments here and there to follow my logic later when I look at the script again. If someone wants, I can create another post walking through the code step by step.

#include <Servo.h&gt;
const int ServoXPin = 10;  // X Servo digital pin
const int ServoYPin = 9; // Y Servo digital pin

const int JXpin = A0; //Joystick X pwm pin
const int JYpin = A1; //Joystick Y pwm pin
const int JSpin = 7; //Joystick Select button digital pin

const int Xdiff = -10; // home position for X servo in relation to 90
const int Ydiff = 5; // home position for Y servo in relation to 90

Servo XServo;
Servo YServo;

int Xval;
int Yval;
int JSval;
int Xang; // x servo angle
int Yang; // y servo angle

void setup() {
  // put your setup code here, to run once:

  pinMode(ServoXPin, OUTPUT);
  pinMode(ServoYPin, OUTPUT);
  pinMode(JXpin, INPUT);
  pinMode(JYpin, INPUT);
  pinMode(JSpin, INPUT);

  XServo.attach(ServoXPin);
  YServo.attach(ServoYPin);

  Xang = 90 + Xdiff; // setting a start place for X angle
  Yang = 90 + Ydiff; // setting a start place for Y angle
  XServo.write(Xang);
  YServo.write(Yang);

  digitalWrite(JSpin, HIGH); //Software solution for pullup resistor

}

void loop() {
  //   put your main code here, to run repeatedly:

  Xval = analogRead(JXpin);
  Yval = analogRead(JYpin);

  if (Xval < 500) {
    Xang = Xang - 1;
    if (Xang <= 45 + Xdiff) {
      Xang = 45 + Xdiff;
    }
    XServo.write(Xang);
  }

  if (Xval &gt; 600) {
    Xang = Xang + 1;
    if (Xang &gt;= 135 + Xdiff) {
      Xang = 135 + Xdiff;
    }
    XServo.write(Xang);
  }

  if (Yval < 500) {
    Yang = Yang + 1;
    if (Yang &gt;= (135 + Ydiff)) {
      Yang = (135 + Ydiff);
    }
    YServo.write(Yang);
  }

  if (Yval &gt; 600) {
    Yang = Yang - 1;
    if (Yang <= (45 + Ydiff)) {
      Yang = (45 + Ydiff);
    }
    YServo.write(Yang);
  }

  JSval = digitalRead(JSpin);


  if (JSval == 0) {
    if (Xang < 90 + Xdiff) {
      for (Xang; Xang < (91 + Xdiff); Xang ++) {
        XServo.write(Xang);
        delay(50);
      }
    }
    if (Xang &gt; 90 + Xdiff) {
      for (Xang; Xang &gt; (89 + Xdiff); Xang --) {
        XServo.write(Xang);
        delay(50);
      }
    }
    if (Yang < 90 + Ydiff) {
      for (Yang; Yang < (91 + Ydiff); Yang ++) {
        YServo.write(Yang);
        delay(50);
      }
    }
    if (Yang &gt; 90 + Ydiff) {
      for (Yang; Yang &gt; (89 + Ydiff); Yang --) {
        YServo.write(Yang);
        delay(50);
      }
    }
  }

  delay(50);
}

Putting it all together

It was now time to get the maze in the final form I envisioned with the code ready.

My requirements for the final form of the maze were:

  • The maze will be controlled by a joystick attached to the Nano.
  • The Nano will be housed on a mini breadboard in the base.
  • The servo motors and joystick need male DuPont connectors.
  • The whole thing will work off a 9V battery.
  • A switch needs to be added so the battery won’t wear down.

Here is a Fritzing drawing of how I hooked all the electronics up.

Maze electronics.

Everything that hooks up to the Arduino Nano is done via a male Dupont connector into the mini breadboard. The only soldering I did in this project was to connect the two leads to the switch.

I clipped the female connectors from the servos and installed male Dupont connectors.
Positive wire from the 9V battery clip soldered to a switch. A wire soldered to the switch. Both the negative and positive ends have male Dupont connectors added.

Once everything had Dupont connectors added, I hooked it all up to the breadboard. To do this, I put the wires down through the bottom of the base. The mini breadboard is just small enough to fit through the bottom of the case.

I had fed the servo wires through wrong in the picture below and had to refeed them from the other side of the base. Feeding through the other side allowed the wires to come into the proper side of the breadboard. The Arduino has to be positioned so the USB port can be accessed.

All of the electronics hooked up to the breadboard.

Then I put the breadboard carefully up into the base. The Dupont connectors used more room than I had planned. So I just aligned the bottom of the breadboard with the base’s bottom and used electrical tape to hold it in place. It does slightly protrude under the case. That is no problem, however, since I put silicone feet on each of the three legs.

I used one of the wire keepers to neatly tuck the servo wires down into the base, leaving enough play in the wiring for servo movements.

I secured the power switch with a zip tie. As the last step, I attached the battery to the case with a piece of padded double-sided tape.

Electronics all connected and secured to the base.

The final product

Here is a look at the completed maze with the joystick.

Completed servo maze with joystick.

And here is a short video I made showing it works. It shows the basic functionality, and at the end, I hit the switch show it rehomes nice and slowly.

Project completed! For now…

Another project was done. Well, no, not really. I’ll leave it configured how it is for now. In the future, I probably will hook up the Bluetooth module and play with that a bit. Plus, it just occurred to me that it might be fun to set the maze up with an IR remote. That is part of why I chose to mount the Nano on a mini breadboard. Being connected this way makes it easier to change out connected devices reasonably quickly.

Song of the day: New Math

I wasted more time than I should have coding this project due to a simple math error. Which brings this classic Tom Lehrer song to mind.

Bonus song: Elements

I would be remiss in sharing Lehrer’s math song without also sharing this classic Elements song.

Leave a Reply