Introduction: Bluetooth-Controlled 360° Motorized Turntable for Product Photography

About: A maker and designer passionate about 3D printing, electronics, and innovation.

This project is a Bluetooth-controlled 360° motorized turntable, operated via a mobile app, designed to automate product photography by providing precise and consistent image capture. It allows users to trigger a camera at specific rotational angles or divide a full rotation into selectable segments (e.g., 1/64, 1/48, or 1/32 of a full turn), ensuring uniform shots from multiple perspectives.

At the core of this system is an Arduino Nano, which receives user commands from the mobile application via the Bluetooth module HC-05 and controls the turntable’s motion. The system is designed to wirelessly trigger either a DSLR via a triggering port or a Smartphone camera using the same triggering port, with an Arduino Pro Micro simulating the volume-up function to capture images when the camera app is opened on most smartphones.

The mobile app was programmed using the MIT App Inventor Android app builder tool. The app has a main screen that controls all of the turntable's functions, including Bluetooth connectivity, basic rotation controls (start/stop, direction, and speed), manual snapping, and an auto-snapping feature that triggers the camera at selectable segments of a full turn.

This makes it an efficient and versatile tool for e-commerce, 3D scanning, and creative photography, ensuring high-quality, repeatable images with minimal effort. Additionally, this turntable can also be used as a regular turntable for showcasing items or products. By controlling the speed and direction of rotation, it offers flexible options for display and presentation.

Supplies

Step 1: CAD Designing

The turntable was designed using Fusion 360, ensuring precise dimensions and smooth operation. The turntable consists of three main components: the main body, the top base with the primary gear, and a smaller gear mounted on the stepper motor.

To achieve optimal performance, multiple design iterations were made, refining the structure for better stability and smooth rotation. To enhance accuracy and save time, 3D CAD models of the electronic components were imported directly into the design, ensuring precise alignment and optimal component placement.

The turntable has a 30 cm diameter, which is the largest I can print on my Creality K1 MAX. This size is also a common standard for 3D printers, making it easier for others to replicate. The total printing time is approximately13 hours, though this may vary depending on printer settings and speed.

Additionally, there is a small 3D-printed support piece recommended for the HC-05 module. Since the module's pins are located on one side only, and to maintain a compact electronic circuit layout, the HC-05 is mounted in an overhanging position. This 3D-printed support ensures the module stays securely in place.


Step 2: Circuit Connection

With all 3D-printed parts ready, it's time to move on to wiring the electronics. Although the number of components might seem a bit overwhelming at first, organizing your layout on a PCB makes the process much easier to follow and assemble.

For this project, I used a 4x6 cm double-sided PCB board to mount and solder all the components.

PCB Assembly and Wiring

I soldered each component directly to the PCB (The relay module was the only exception—it was mounted separately on the base of the turntable), planning the layout to minimize wire clutter and ensure solid connections. Below is a summary of the key connections:

Stepper Driver (TMC2209):

  1. EN (Enable) → Arduino D6 — Enables/disables the stepper motor.
  2. STEP → Arduino D3 — Sends step pulses to the driver.
  3. DIR (Direction) → Arduino D4 — Controls motor rotation direction.
  4. VIO, VM, GND — Connected to appropriate power and ground lines.

Bluetooth Module (HC-05):

  1. RX → Arduino D11 — Connected through a voltage divider using 1kΩ and 2kΩ resistors to step down 5V to 3.3V.
  2. TX → Arduino D10 — Sends data to Arduino directly.
  3. VCC → 5V
  4. GND → Common ground.

Push Button:

  1. One leg → Arduino D5
  2. Other leg → GND
  3. (Use a pull-down resistor if needed to ensure reliable input reading.)

Relay Module:

  1. IN → Arduino D8 — Used to trigger external devices (such as a camera).
  2. VCC and GND → Connected to power and ground.

Stepper Motor (connected to TMC2209):

  1. Coil A1 → Driver OUT1
  2. Coil A2 → Driver OUT2
  3. Coil B1 → Driver OUT3
  4. Coil B2 → Driver OUT4

General Notes:

  1. All components share a common ground, which must be properly connected to avoid malfunction.
  2. Double-check the orientation and pinout of each module before soldering to avoid damage or incorrect behavior.
  3. After completing the wiring, inspect all joints for cold soldering or shorts and make sure every connection is secure.

Arduino Pro Micro (Optional)

This optional setup allows you to use your smartphone as a camera instead of a DSLR. When connected, the Arduino Pro Micro board will simulate the "Volume Up" button when the camera app is opened on your smartphone. This will trigger the camera to snap a photo.

Connections:

  1. Shutter Jack +D2 (Arduino Pro Micro): This simulates the Volume Up button on the smartphone.
  2. Shutter Jack -GND (Arduino Pro Micro): Ground connection.
  3. Smartphone USBMicro Pro USB Port: To connect the smartphone for triggering the camera.

This feature is entirely optional, but it offers an easy alternative if you prefer using a smartphone for photography instead of a DSLR camera.

Step 3: Hardware Assembly

The assembly process brings together all the mechanical and electronic parts into one integrated unit.

First, I inserted six thread inserts into the designated holes of the 3D-printed Main Body. These inserts allow secure fastening of various parts during final assembly.

Next, I mounted the four POM pulley wheels into their positions. Each wheel was secured using an M5x20mm hex socket head screw and an M5 locknut. These wheels are crucial for keeping the top platform flat and ensuring smooth, stable rotation.

Then, I installed the power jack, main switch, camera trigger jack, and reverse direction push button into their designated slots.

Following that, I positioned the main PCB and the LM2596 DC-DC converter module into their spots inside the main body. They were secured into the thread inserts using small M3 screws.

The stepper motor was then placed into its mount, snapping securely into position without the need for screws.

The relay module was also slid into its designed slot at the base, again without requiring any screws.

Once all modules were fixed in place, I completed the wiring between the components, as shown in the connection diagram and photos.

Note: I added some connectors between the component wiring to make assembly and future maintenance easier.

The small printed gear was mounted onto the stepper motor shaft, and the 625ZZ bearing was inserted into the center pole of the body.

Finally, I placed the top platform onto the main body. The central bearing ensures the top part stays perfectly aligned while rotating smoothly during operation.

An opening at the bottom of the main body allows easy access to the Arduino Nano's USB port for programming and updates.

Step 4: The Code

I programmed the Arduino Nano to control the stepper motor and functions of a motorized turntable via Bluetooth commands. With limited programming skills, I used ChatGPT to improve the code.

The Arduino code handles several important features:

  1. Start and stop the motor.
  2. Change rotation direction (clockwise/counterclockwise).
  3. Adjust the motor speed dynamically.
  4. Rotate a specific fraction of a full turn (e.g., 1/64, 1/48, 1/32, etc.).
  5. Manually trigger the relay at any time via a button in the app.
  6. Rotate in increments while automatically triggering the relay (used for controlling a DSLR or mobile camera).

The Bluetooth communication is managed using a SoftwareSerial connection between the Arduino Nano and the HC-05 Bluetooth module.

Incoming commands from the mobile app are interpreted by the Arduino, which then executes the corresponding motor or relay actions.

The stepper motor is controlled via a TMC2209 stepper driver, ensuring smooth and quiet motion even at different speeds.

For Smartphone camera triggering, the Arduino Pro Micro requires a small code that simulates the volume-up button while the camera app is running, as pressing the volume-up button on most smartphones triggers the camera to take a photo.

Arduino Nano Code

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Show hidden characters
#include
#define EN_PIN 6 // Enable pin
#define STEP_PIN 3 // Step pin
#define DIR_PIN 4 // Direction pin
#define BUTTON_PIN 5 // Push button
#define RELAY_PIN 8 // Relay module pin (NEW FEATURE)
SoftwareSerial BT(10, 11); // HC-05 (RX = D10, TX = D11)
bool motorEnabled = false;
bool direction = true; // true = Clockwise, false = Counterclockwise
int stepDelay = 1500; // Default speed (Normal)
const int stepsPerRevolution = 9600; // Adjust for your stepper
// Debounce variables
bool lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const int debounceDelay = 50; // 50ms debounce time
void setup() {
pinMode(EN_PIN, OUTPUT);
pinMode(STEP_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP); // Internal pull-up resistor
pinMode(RELAY_PIN, OUTPUT); // Set relay pin as output
digitalWrite(EN_PIN, HIGH); // Motor initially disabled
digitalWrite(DIR_PIN, HIGH); // Default direction
digitalWrite(RELAY_PIN, LOW); // Ensure relay is off initially
BT.begin(9600); // Bluetooth baud rate
}
// Function to rotate the stepper motor a given number of steps
void rotateSteps(int steps) {
digitalWrite(EN_PIN, LOW); // Enable motor
for (int i = 0; i < steps; i++) {
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(stepDelay);
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(stepDelay);
}
digitalWrite(EN_PIN, HIGH); // Disable motor after movement
}
// NEW FUNCTION: Rotate in increments and trigger relay at each stop
void rotateWithRelay(int stepsPerStop) {
int totalStops = stepsPerRevolution / stepsPerStop;
digitalWrite(EN_PIN, LOW); // Enable motor
for (int i = 0; i < totalStops; i++) {
rotateSteps(stepsPerStop); // Rotate step size
delay(1000); // Pause for 1 seconds
digitalWrite(RELAY_PIN, HIGH); // Activate relay
delay(500); // Keep relay on for 0.5 second
digitalWrite(RELAY_PIN, LOW); // Turn off relay
delay(1000); // Pause for 1 second before next movement
}
digitalWrite(EN_PIN, HIGH); // Disable motor after full cycle
}
void loop() {
if (BT.available()) {
String command = BT.readStringUntil('\n');
command.trim(); // Remove spaces or newlines
if (command.startsWith("SPD")) {
int newSpeed = command.substring(3).toInt();
stepDelay = map(newSpeed, 1, 100, 5000, 200);
BT.print("Speed Set to: ");
BT.println(newSpeed);
}
else if (command == "S") {
motorEnabled = !motorEnabled;
digitalWrite(EN_PIN, motorEnabled ? LOW : HIGH);
BT.println(motorEnabled ? "Motor Started" : "Motor Stopped");
}
else if (command == "C1") {
direction = true;
digitalWrite(DIR_PIN, HIGH);
BT.println("Direction: Clockwise");
}
else if (command == "C2") {
direction = false;
digitalWrite(DIR_PIN, LOW);
BT.println("Direction: Counterclockwise");
}
else if (command == "S1") { stepDelay = 5000; BT.println("Speed: Slow"); }
else if (command == "S2") { stepDelay = 1500; BT.println("Speed: Normal"); }
else if (command == "S3") { stepDelay = 200; BT.println("Speed: Fast"); }
// Rotation commands (T1 - T8)
else if (command == "T1") { rotateSteps(stepsPerRevolution / 64); BT.println("Rotated 1/64 Turn"); }
else if (command == "T2") { rotateSteps(stepsPerRevolution / 48); BT.println("Rotated 1/48 Turn"); }
else if (command == "T3") { rotateSteps(stepsPerRevolution / 32); BT.println("Rotated 1/32 Turn"); }
else if (command == "T4") { rotateSteps(stepsPerRevolution / 16); BT.println("Rotated 1/16 Turn"); }
else if (command == "T5") { rotateSteps(stepsPerRevolution / 8); BT.println("Rotated 1/8 Turn"); }
else if (command == "T6") { rotateSteps(stepsPerRevolution / 4); BT.println("Rotated 1/4 Turn"); }
else if (command == "T7") { rotateSteps(stepsPerRevolution / 2); BT.println("Rotated 1/2 Turn"); }
else if (command == "T8") { rotateSteps(stepsPerRevolution); BT.println("Rotated 1 Full Turn"); }
// NEW Rotation commands with relay (R1 - R6)
else if (command == "R1") { rotateWithRelay(stepsPerRevolution / 64); BT.println("Rotating 1/64 with Relay"); }
else if (command == "R2") { rotateWithRelay(stepsPerRevolution / 48); BT.println("Rotating 1/48 with Relay"); }
else if (command == "R3") { rotateWithRelay(stepsPerRevolution / 32); BT.println("Rotating 1/32 with Relay"); }
else if (command == "R4") { rotateWithRelay(stepsPerRevolution / 16); BT.println("Rotating 1/16 with Relay"); }
else if (command == "R5") { rotateWithRelay(stepsPerRevolution / 8); BT.println("Rotating 1/8 with Relay"); }
else if (command == "R6") { rotateWithRelay(stepsPerRevolution / 4); BT.println("Rotating 1/4 with Relay"); }
else if (command == "SN") {
digitalWrite(RELAY_PIN, HIGH); // Activate relay
delay(500); // Keep relay on for 0.5 second
digitalWrite(RELAY_PIN, LOW); // Turn off relay
}
}
// Push button toggles direction with debounce
bool buttonState = digitalRead(BUTTON_PIN);
if (buttonState == LOW && lastButtonState == HIGH) {
if (millis() - lastDebounceTime > debounceDelay) {
direction = !direction;
digitalWrite(DIR_PIN, direction ? HIGH : LOW);
BT.println(direction ? "Button: Clockwise" : "Button: Counterclockwise");
lastDebounceTime = millis();
}
}
lastButtonState = buttonState;
// Run motor if enabled
if (motorEnabled) {
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(stepDelay);
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(stepDelay);
}
}

Arduino Pro Micro code

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Show hidden characters
#include // Required for HID-Project
#include // HID functions
const int buttonPin = 2; // Connect a push button to pin 2
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
Consumer.begin(); // Start media control HID
}
void loop() {
if (digitalRead(buttonPin) == LOW) { // When button is pressed
Consumer.write(MEDIA_VOLUME_UP); // Send Volume Up command
delay(500); // Debounce delay
}
}

Step 5: Mobile App Making

For this project, I used MIT App Inventor, a user-friendly platform that allows you to build Android apps using a drag-and-drop interface. The great thing about App Inventor is that it doesn't require advanced programming skills, like mine.

So yes, the app works only on Android devices.

I designed the app to control the turntable, as explained in the previous steps.

The app’s interface is simple. It includes buttons for each command (start/stop, speed control, direction change) and a slider for adjusting motor speed. Additionally, I incorporated Bluetooth connection functionality to allow communication between the mobile device and the Arduino Nano via the HC-05 Bluetooth module.

The (Home & Set Home) buttons are inactive at the moment and will be activated in future updates

You can download the app from this link

I have also included the MIT App Inventor project as an .aia file in case you want to make upgrades yourself.

Step 6: Operation

Now that everything is set up and ready, the first thing you want to do is connect the turntable to a 12V DC power adapter (1.5 amps or higher is recommended).

Next, run the app (assuming it has already been installed on your Android device). Press the Bluetooth button at the top and select the HC-05 Bluetooth connection.

You can now connect the turntable to a DSLR camera or use any smartphone to start shooting. Even if your camera isn’t ready yet, you can simply press the buttons in the app and start rotating the table.

Step 7: Final Thoughts

This project was a rewarding journey that combined 3D design, electronics, coding, and mobile app development into a fully functional and customizable system. The final result is a Bluetooth-controlled 360° motorized turntable that enables users to capture precise, high-quality product photos or create dynamic 3D scans with ease.

Using simple and affordable components and 3D-printed parts, I was able to build a system that is versatile, efficient, and accessible to hobbyists and makers of all skill levels.

The ability to control rotation speed, direction, angle divisions, and trigger a camera remotely - all from a custom mobile app - greatly simplifies the process of professional-grade photography and object scanning, making this turntable a valuable tool for e-commerce, creative studios, and DIY enthusiasts.

Throughout the process, I learned a lot - not just about hardware and software, but also about problem-solving, design optimization, and integrating different technologies into one seamless project.

I hope this guide inspires you to build your own version, modify it to fit your needs, and most importantly - to keep creating and innovating!

Thank you for following along, and happy building! 🚀