Bluetooth Mecanum Robot

by Blobfish7blob in Circuits > Robots

51 Views, 0 Favorites, 0 Comments

Bluetooth Mecanum Robot

20260125_143449.jpg

Mecanum wheels are revolutionary wheels that work using rollers along the wheels, all angled at a 45° angle. These rollers allow for omnidirectional movement. This means the robot can not only move forward and backward, but it can also strafe left to right, travel on diagonals, and rotate along multiple axes. I am going to teach each step to make, CAD, and program your own mecanum robot.

Supplies

Screenshot 2026-01-02 at 1.53.48 PM.png
module-bluetooth-hc05.jpg
Screenshot 2026-01-02 at 1.54.11 PM.png

To create a mecanum robot, you will need a few things:

  1. 1x Chassis kit (1 frame, 4 TT motors, 4 mecanum wheels)
  2. 1x Bluetooth module
  3. 1x Arduino microcontroller (I used the Arduino Mega)
  4. 1x Motor driver Arduino shield
  5. Batteries and cases (1x 9v and 4x AA)

For my project, I used a chassis kit, which included the chassis, wheels, and motors. However, you can also make the chassis and purchase wheels and motors separately.

Wiring

Note The motor shield is on the arduino.png
20260125_143700.jpg

When wiring the wheels, it is crucial to connect the correct motors, as the robot won't move properly otherwise. Return to this step if the directions are incorrect later on.

Front Left --> M1 on shield

Front Right --> M2 on shield

Back Left --> M4 on shield

Back Right --> M3 on shield

Bluetooth module:

RXD --> D2

TXD --> A8 (If you don't use a mega, you will have to change this number in the code to another analog pin)

GND --> GND

VCC --> 5V

Power:

6V (4 AA batteries) --> +M and GND terminal on shield

**Remove the yellow jumper pin from the shield. It connects power from the board and Arduino, which causes voltage drops when the motors turn on, shutting down the robot.

9v --> Vin and ground on Arduino

**Don't attach the 9v to any pin but the Vin ( Voltage input ) pin; it could fry the board.

CAD

Screenshot 2026-01-25 at 2.56.28 PM.png
Screenshot 2026-01-12 at 1.16.56 PM.png
Screenshot 2026-01-25 at 2.55.13 PM.png

I've used Onshape to design the top battery holder and modified open-source designs to mount the Arduino, 9v battery, and breadboard.

I've also included the step file for a servo-operated claw. The code to run this claw is included in both the app and the program. Feel free to 3d print it for yourself!

These robots can do more than drive, so be creative and have it do whatever you want!

Building the Robot

Untitled design (4).png
mecanum-orientation.png
MecanumDiagram.png

You should have 2 types of wheels, a left and a right. Like the diagrams, make sure the rollers create an X. This X must be on the top, not the bottom. You can get/make the chassis either a rectangle or a square; both are good for different uses. A square chassis rotates better and is slightly more ideal, while a rectangular chassis can improve straight-line stability and speed in some cases.

Simply attach the wheels to the motors and the motors to the chassis. Different robots will mount these parts in different ways. For me, a hub attached the wheels to the motors, and the motors got screwed into the chassis. If you bought a chassis, there will likely be a video specific to the one you have.

Programming

mecanum directions.jpg

Mecanum wheels have tons of directions and ways to move, and it all comes down to which wheels move when and how. When all wheels go either forward or backward, the robot will go in that direction. The robot can also move right to left, called strafing, by having the diagonal wheel pairs go in opposite directions. This is just the simplified movement; all of the possible directions and rotations are in the image above

The code can quickly know which wheels to move by using functions that define the proper motor directions for each movement. Each button on the app sends the Arduino a separate letter, which corresponds to each of the movements.

The code has two modes: Turning and strafing. These modes can be switched by clicking the toggle button on the app. The code changes the mode and will either strafe side to side or rotate. This implementation was crucial as the controller would feel cluttered without it. Instead of rotating with another whole set of buttons, a single button allows you to switch between the two.

The Program

#include <AFMotor.h>
#include <SoftwareSerial.h>
#include <Servo.h>
int VEL = 255;
int serv = 0;

Servo myservo;
Servo myservo2;

SoftwareSerial HC05(A8, 2); // RX, TX

AF_DCMotor motor1(1);
AF_DCMotor motor2(2);
AF_DCMotor motor3(3);
AF_DCMotor motor4(4);

char command;
boolean state = 0;
void setup()
{
HC05.begin(9600);
myservo.attach(9);
myservo.write(0);
myservo2.attach(53);
myservo2.write(0);
Serial.begin(9600);
}

void loop() {
if (HC05.available() > 0) {
command = HC05.read();
Serial.println(command);
Stop();
if (command == 'X') {
state = 1;
} else if (command == 'x') {
state = 0;
}
if (command == 'W') {
myservo.write(25);
} else if (command == 'w') {
myservo.write(0);
}

if (command == 'U') {
myservo2.write(25);
} else if (command == 'u') {
myservo2.write(0);
}




/*
S : Stop
FF : Forward
B : Backward
RR : Rotate Right
RL : Rotate left
R : Right
L : Left
FR : Forward Right
FL : Forward Left
BR : Backward Right
BL : Backward Left
RRF: Rotate Right, Front Pivot
RLF: Rotate Left, Front Pivot
RRB: Rotate Right, Back Pivot
RLB: Rotate Left, Back Pivot
*/
if (command == 'F') {
FF();
} else if (command == 'B') {
B();
} else if (command == 'R' && state == 1) {
RR();
} else if (command == 'L' && state == 1) {
RL();
} else if (command == 'R' && state == 0) {
R();
} else if (command == 'L' && state == 0) {
L();
} else if (command == 'G' && state == 0) {
FL();
} else if (command == 'I' && state == 0) {
FR();
} else if (command == 'H' && state == 0) {
BL();
} else if (command == 'J' && state == 0) {
BR();
}
else if (command == 'G' && state == 1) {
RLF();
} else if (command == 'I' && state == 1) {
RRF();
} else if (command == 'H' && state == 1) {
RLB();
} else if (command == 'J' && state == 1) {
RRB();
}
else {
Stop();
}
}
}
void FF()
{
motor1.setSpeed(VEL);
motor1.run(FORWARD);
motor2.setSpeed(VEL);
motor2.run(FORWARD);
motor3.setSpeed(VEL);
motor3.run(FORWARD);
motor4.setSpeed(VEL);
motor4.run(FORWARD);
}

void B()
{
motor1.setSpeed(VEL);
motor1.run(BACKWARD);
motor2.setSpeed(VEL);
motor2.run(BACKWARD);
motor3.setSpeed(VEL);
motor3.run(BACKWARD);
motor4.setSpeed(VEL);
motor4.run(BACKWARD);
}

void R()
{
motor1.setSpeed(VEL);
motor1.run(FORWARD);
motor2.setSpeed(VEL);
motor2.run(BACKWARD);
motor3.setSpeed(VEL);
motor3.run(FORWARD);
motor4.setSpeed(VEL);
motor4.run(BACKWARD);
}

void L()
{
motor1.setSpeed(VEL);
motor1.run(BACKWARD);
motor2.setSpeed(VEL);
motor2.run(FORWARD);
motor3.setSpeed(VEL);
motor3.run(BACKWARD);
motor4.setSpeed(VEL);
motor4.run(FORWARD);
}
void FL()
{
motor1.setSpeed(0);
motor1.run(RELEASE);
motor2.setSpeed(VEL);
motor2.run(FORWARD);
motor3.setSpeed(0);
motor3.run(RELEASE);
motor4.setSpeed(VEL);
motor4.run(FORWARD);
}
void FR()
{
motor1.setSpeed(VEL);
motor1.run(FORWARD);
motor2.setSpeed(0);
motor2.run(RELEASE);
motor3.setSpeed(VEL);
motor3.run(FORWARD);
motor4.setSpeed(0);
motor4.run(RELEASE);
}
void BL()
{
motor1.setSpeed(VEL);
motor1.run(BACKWARD);
motor2.setSpeed(0);
motor2.run(RELEASE);
motor3.setSpeed(VEL);
motor3.run(BACKWARD);
motor4.setSpeed(0);
motor4.run(RELEASE);
}
void BR()
{
motor1.setSpeed(0);
motor1.run(RELEASE);
motor2.setSpeed(VEL);
motor2.run(BACKWARD);
motor3.setSpeed(0);
motor3.run(RELEASE);
motor4.setSpeed(VEL);
motor4.run(BACKWARD);
}
void RR()
{
motor1.setSpeed(VEL);
motor1.run(FORWARD);
motor2.setSpeed(VEL);
motor2.run(BACKWARD);
motor3.setSpeed(VEL);
motor3.run(BACKWARD);
motor4.setSpeed(VEL);
motor4.run(FORWARD);
}
void RL()
{
motor1.setSpeed(VEL);
motor1.run(BACKWARD);
motor2.setSpeed(VEL);
motor2.run(FORWARD);
motor3.setSpeed(VEL);
motor3.run(FORWARD);
motor4.setSpeed(VEL);
motor4.run(BACKWARD);
}

void RLF()
{
motor1.setSpeed(VEL);
motor1.run(BACKWARD);
motor2.setSpeed(VEL);
motor2.run(FORWARD);
motor3.setSpeed(VEL);
motor3.run(RELEASE);
motor4.setSpeed(VEL);
motor4.run(RELEASE);
}
void RRF()
{
motor1.setSpeed(VEL);
motor1.run(FORWARD);
motor2.setSpeed(VEL);
motor2.run(BACKWARD);
motor3.setSpeed(VEL);
motor3.run(RELEASE);
motor4.setSpeed(VEL);
motor4.run(RELEASE);
}
void RRB()
{
motor1.setSpeed(VEL);
motor1.run(RELEASE);
motor2.setSpeed(VEL);
motor2.run(RELEASE);
motor3.setSpeed(VEL);
motor3.run(BACKWARD);
motor4.setSpeed(VEL);
motor4.run(FORWARD);
}
void RLB()
{
motor1.setSpeed(VEL);
motor1.run(RELEASE);
motor2.setSpeed(VEL);
motor2.run(RELEASE);
motor3.setSpeed(VEL);
motor3.run(FORWARD);
motor4.setSpeed(VEL);
motor4.run(BACKWARD);
}
void Stop()
{
motor1.setSpeed(0);
motor1.run(RELEASE);
motor2.setSpeed(0);
motor2.run(RELEASE);
motor3.setSpeed(0);
motor3.run(RELEASE);
motor4.setSpeed(0);
motor4.run(RELEASE);
}

Connecting the App

app screenshot.jpg

The app for this robot was created on the MIT App Inventor. The app is what sends the commands to the robot. For each button press, the program checks if any other buttons were previously pressed. If, for example, the front and left buttons are pressed, it will move diagonally to the left.


Logic for each button press

  1. While the button is pressed, set a boolean value to be on
  2. Check if any other buttons are also pressed
  3. If none are, go the direction pressed; if 2 are, go the combination direction.

For example, the forward button checks if either the left or right are pressed

  1. If none are, it goes forward
  2. If the left button is also pressed, move forward to the left
  3. If the right button is also pressed, move forward to the right.


You can connect the robot with Bluetooth, and you should be done!

The .apk file can be used to download it onto an Android device.

Finishing and Troubleshooting

Now that you are done, drive it around with the app and have fun!

If you have any issues, there are a few things you should check.

  1. Do the wheels make an X from the top?
  2. Are the motors plugged in the correct order?
  3. Is the motor polarity the same?
  4. Is the yellow jumper on the motor shield removed?

If these are all correct and the problem persists, feel free to leave a comment!