Normal view of the turn table when it is set up
This project aims to help miniature painters with their paint jobs. They can use the joystick to turn the table to their desired angle to work with, without touching the model itself, which often times will result in damage to the paint that is yet to fully dry. There is also lighting to make sure you are seeing clearly.
Clean, interior view of the gears
Compact view when folded for storage
Detailed look on the wiring and the gears
This video shows the operation of the turn table. Being able to turn in both direction. However, an issue I was not able to figure out is even at 12V there isn't enough power to turn it fully.
The video to the left shows the interior moving in more detail, while the video to the right shows the operation of the turn table in different direction depending on the joystick input. Still, the actual table was not used as there isn't enough power to move it fully.
This is an initial prototype made with a worm gear device to make sure I can translate horizontal rotation to vertical rotation.
These are testings I did after scratching the worm gear idea. As it is much easier and more reliable to use a bevel gear. I used CAD to make sure the measurements are exact and find the best placement for the components.
This is the finalized product with all the components I plan on having on my device.
These are the 3D printed parts that I was able to attach and bolt onto the motor. Then, I attached them onto the laser cut board. The tolerance was low, so it closed very tightly.
This is the wired up board.
This is the soldered light with the current power supply.
I was unpleasantly surprised by how complicated simople geara systems could be. I had gotten through at least 5 different interations of gear designs to finalize my bevel gear. Only to spend another few hours adjusting the position of these gears so it will be in contact and move the way they are intended to. I learned a lot about the maths behind gears and more CAD functionalities in this project, and I was able to 3D print and alser cut the final design, and have them fit perfeclty as simulated in CAD. However, the power issue was very frustrating, with me failing to get the lights to turn on for the majority of the time, as well as the weak motor movements.
Andy mentioned that “creating some sort of way to raise the miniatures could be super cool,” and I think that is an excellent idea. Painting different areas often requires viewing the model from multiple angles, including from below. However, implementing that feature would likely make the project significantly more complex. Leighton said that “the gears are very cool and hard to implement,” and I completely agree, as that part of the project took the most time, and I appreciated that he noticed it.
Overall, I am not entirely satisfied with how the project turned out. Although I was able to overcome challenges related to the complexity of the gears, I was not able to fully resolve issues involving power delivery and LED consistency. I successfully achieved the mechanical goals of the project, but I fell short on the electronic side of the device. Given more time, I would likely simplify the mechanical aspects and focus more on improving the technical reliability, since that is ultimately the more important part of the project.
Throughout this process, I learned that I am stronger at planning complicated CAD designs in advance and measuring precise component placement. However, I also realized that I need to improve my technical skills, especially in wiring correctly and understanding the power requirements of each component. If I were to do this project again, I would begin by determining the power limitations of the device and focus on the electronics first. That would allow me to design the mechanical components around those electronic constraints.
My advice to future self would be to plan ahead using the methods I mentioned earlier. I think I spent too much time focused on the gears and mechanical design when I could have chosen a simpler solution, even if it meant the table would spin at a less ideal speed. Given the opportunity, I would like to revisit this project. This time, I would make sure the housing is properly designed to accommodate all components, including external power requirements, while also ensuring that the device works reliably. I believe that, with enough time, I could refine this project successfully because I already have the fundamental skills needed to solve the problems I encountered, and I also have ideas for how to improve the design. For example, I would probably use a different LED that draws less power, since I do not need it to be extremely bright like a flashlight (which the ones I used did and needed 9-12V), and I would redesign the gear ratio so the motor experiences less stress.
power
/*
Turn Table joystick operations
Reads a joystick and pushbutton as input, drives a DC motor through
an L293 motor driver, and controls warm/cool LED drivers as output.
Motor behavior:
- Joystick controls motor direction and speed in normal mode
- Double-click button toggles auto-rotate mode
- In auto-rotate mode, motor spins at a constant speed
LED behavior:
- Single-click button cycles through light modes:
0 = all off
1 = warm only
2 = cool only
3 = both on
Serial Monitor behavior:
- Prints light state changes
- Prints auto-rotate mode changes
Some code taken from the official arduino websites, as well as chat-gbt for help with the double click input code
pin mapping:
Arduino pin | role | description
___________________________________________________________________
A0 input joystick analog output
2 input joystick pushbutton switch
9 output L293 enable pin (PWM motor speed control)
7 output L293 input 1 (motor direction)
8 output L293 input 2 (motor direction)
5 output warm LED driver control
6 output cool LED driver control
*/
const int joyPin = A0,
swPin = 2,
enPin = 9,
in1Pin = 7,
in2Pin = 8,
warmPin = 5,
coolPin = 6;
const int center = 512,
deadZone = 80;
// auto motor mode
bool autoMode = false;
bool lastPrintedAutoMode = true; // opposite of initial value
const int autoSpeed = 40; // raise if motor doesn't actually spin
// LED modes
// 0 = all off
// 1 = warm only
// 2 = cool only
// 3 = both on
int ledMode = 0;
int lastPrintedLedMode = -1;
// button click handling
bool lastReading = HIGH;
bool buttonStableState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 30;
unsigned long firstClickTime = 0;
bool waitingForSecondClick = false;
const unsigned long doubleClickWindow = 300;
void setup() {
pinMode(swPin, INPUT_PULLUP);
pinMode(enPin, OUTPUT);
pinMode(in1Pin, OUTPUT);
pinMode(in2Pin, OUTPUT);
pinMode(warmPin, OUTPUT);
pinMode(coolPin, OUTPUT);
// initialize motor off
analogWrite(enPin, 0);
digitalWrite(in1Pin, LOW);
digitalWrite(in2Pin, LOW);
// initialize LEDs off
digitalWrite(warmPin, LOW);
digitalWrite(coolPin, LOW);
Serial.begin(9600);
// print initial states
printLightState();
printAutoState();
lastPrintedLedMode = ledMode;
lastPrintedAutoMode = autoMode;
}
void loop() {
// handle button presses
handleButtonClicks();
// run motor in selected mode
if (autoMode) {
runAutoMode();
} else {
runJoystickMode();
}
// update outputs
updateLEDs();
updateSerialStates();
}
void handleButtonClicks() {
bool reading = digitalRead(swPin);
if (reading != lastReading) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != buttonStableState) {
buttonStableState = reading;
if (buttonStableState == LOW) {
processClick();
}
}
}
lastReading = reading;
if (waitingForSecondClick && (millis() - firstClickTime > doubleClickWindow)) {
waitingForSecondClick = false;
handleSingleClick();
}
}
void processClick() {
if (!waitingForSecondClick) {
waitingForSecondClick = true;
firstClickTime = millis();
} else {
if (millis() - firstClickTime <= doubleClickWindow) {
waitingForSecondClick = false;
handleDoubleClick();
} else {
firstClickTime = millis();
}
}
}
void handleSingleClick() {
ledMode++;
if (ledMode > 3) {
ledMode = 0;
}
}
void handleDoubleClick() {
autoMode = !autoMode;
}
void updateLEDs() {
switch (ledMode) {
case 0:
digitalWrite(warmPin, LOW);
digitalWrite(coolPin, LOW);
break;
case 1:
digitalWrite(warmPin, HIGH);
digitalWrite(coolPin, LOW);
break;
case 2:
digitalWrite(warmPin, LOW);
digitalWrite(coolPin, HIGH);
break;
case 3:
digitalWrite(warmPin, HIGH);
digitalWrite(coolPin, HIGH);
break;
}
}
void updateSerialStates() {
if (ledMode != lastPrintedLedMode) {
printLightState();
lastPrintedLedMode = ledMode;
}
if (autoMode != lastPrintedAutoMode) {
printAutoState();
lastPrintedAutoMode = autoMode;
}
}
void runAutoMode() {
digitalWrite(in1Pin, HIGH);
digitalWrite(in2Pin, LOW);
analogWrite(enPin, autoSpeed);
}
void runJoystickMode() {
// read joystick value
int joy = analogRead(joyPin);
int offset = joy - center;
// stop motor inside dead zone
if (abs(offset) < deadZone) {
digitalWrite(in1Pin, LOW);
digitalWrite(in2Pin, LOW);
analogWrite(enPin, 0);
return;
}
// map joystick distance from center to motor speed
int speedVal = map(abs(offset), deadZone, 512, 0, 255);
// ensure motor speed stays in valid PWM range
speedVal = constrain(speedVal, 0, 255);
// set motor direction
if (offset > 0) {
digitalWrite(in1Pin, HIGH);
digitalWrite(in2Pin, LOW);
} else {
digitalWrite(in1Pin, LOW);
digitalWrite(in2Pin, HIGH);
}
// drive motor
analogWrite(enPin, speedVal);
}