


| Item | Quantity | Comments |
|---|---|---|
| Movement platform | 1 | 2024: Created a new platform using Coreldraw, added holes to slot in the timing belt. The design was made in coreldraw but my computer only have inkscape so I showing the picture here.![]() |
| Phone holder | 1 | 2024: Created a holder for my iphone 13 pro max using Onshape. I used the set screw concept which Steven showed me. and the parts was two different print so that I can print faster and assemble them.![]() ![]() |
| Stepper holder | 1 | Used the same holder as what I designed in 2022, it was crude but it was my work when i was starting out.
I still remember going to select the 3D parts from McMaster.![]() ![]() |
| Ball bearing holder | 1 | Used the holder which Bryan designed in 2022. |
| Axle clamp | 1 | Used the holder which Bryan designed in 2022. |
| Step | Description |
|---|---|
| 2022-1 | Inspiration taken from Ender 3 V2's x-axis linear motion for replicability. |
| 2024-1B | Added rotation for the camera holder |
| 2022-2 | Designed a moving platform for the camera using 3D printed M4 spacers and rollers. |
| 2024-2B | Changed moving platform using laser cut parts to put a stepper on top of it. ![]() |
| 2022-3 | Attached the platform to a 3mm acrylic piece. |
| 2024-3B | Used book screws to support the stand to allow 2nd stepper to rotate the camera holder![]() |
| 2022-4 | Created a camera (phone) holder to sit on a 3mm acrylic piece. |
| 2024-4B | Remove old camera holder, create new camera holder to be screw mounted to 2nd stepper so that it can be rotated and secured usign set screw concept. ![]() ![]() |
| 2022-5 | Decided against separate platforms for camera and movement, combining both functions. |
| 2022-6 | Assembled the moving platform first, then attached the camera mount to it.![]() |
| 2022-7 | Conducted a manual test by zip-tying a timing belt to the platform and checking motion.![]() ![]() |
| 2024-7B | Added one limit switch to stop Moving Platform when it touches end.![]() |

| CNC Shield Pin | Function | Arduino Pin | Notes |
|---|---|---|---|
| X-Step | Controls step for X-axis | 2 | Connected to stepper driver for X-axis movement ![]() |
| X-Dir | Controls direction for X-axis | 5 | Specifies the direction of X-axis movement |
| Y-Step | Controls step for Y-axis | 3 | Connected to stepper driver for rotation |
| Y-Dir | Controls direction for Y-axis | 6 | Specifies the direction of rotation. |
| Limit Switches X | Input for X-axis limit switch | 9 | Used for X-axis homing and preventing overtravel. Previously, I do not know how to use it well, lucky Steven gave me tips to better use it. Multimeter is awesome. |
| Function | Parameter(s) | Description |
|---|---|---|
AccelStepper |
AccelStepper::DRIVER, stepPinX, dirPinXAccelStepper::DRIVER, stepPinRot, dirPinRot |
Constructor for creating a stepper object. Specifies the interface type (DRIVER) and pins for step and direction control. |
setMaxSpeed |
(speed) |
Sets the maximum speed in steps per second. For stepperX, it's set to 1000. For stepperRot, it's set to 3000. |
setAcceleration |
(acceleration) |
Sets the acceleration in steps per second squared. Both stepperX and stepperRot are set to 1000. |
pinMode |
limitSwitchPinX, INPUT_PULLUP |
Sets the pin mode for the limit switch pin to input with an internal pull-up resistor so that it works with CNC shield. |
digitalRead |
limitSwitchPinX |
Reads the value from the limit switch pin. Used to determine if the limit switch is pressed (HIGH). |
move |
(steps) |
Enqueues a move of a certain number of steps. Positive to move forward, negative for backward. |
stop |
N/A | Stops the motor immediately without deceleration. |
run |
N/A | Continuously moves the stepper motor. |
We took a look at this design. Making the dolly motorized at the stand however was too big of an undertaking.
Some of the things we identified that needed to be designed are:
1. Linear Motion
2. Camera Mount
3. Left and Right Stands
Next we did a task allocation among ourselves. Jon will be designing most of the mechanical aspects of the Dolly, while Bryan will focus on automating the entire Camera Dolly procedure.
# Mechanical Design
_Goal: machine that has mechanism + actuation + automation, manually operate it._
### List of Materials
These were the items that we acquired for the project.
|Item|Quantity|
|----|--------|
|Timing belt + bore|~1meter|
|Ball Bearing (diameter 8mm)|1|
|Stepper motor|1|
|Stepper driver (TB6600)|1|
|20mmx20mm Aluminium profile 600mm|1|
|M5 screws|4|
|M5 nuts|8|
|M5 washers|4|
|M3 screws|2|
|M3 nuts|2|
|Breadboard|1|
These were the items that needed to be fabricated:
|Item|Quantity|
|----|--------|
|[Rollers]()|4|
|[Movement platform]()|1|
|[Phone holder]()|1|
|[Stepper holder]()|1|
|[Ball bearing holder](./roller_holder.f3d)|1|
|[Axle clamp](./axle.stl)|1|
|[M8 spacers]()|1|
|[M4 spacers]()|1|
|Micro-Controller [traces](./hello_world.png) [outline](./hello_world.outline.png)|1|
## Mechanism
The main mechanism is a single axis linear motion. Since the example above motorizes near the camera mount and seemed a bit complicated, we decided to take inspiration from a more replicable design.
Looking at the various Ender 3 V2 in our lab, we noticed that there was potential in reproducing the x-axis linear motion.
To start, we designed a move platform for the camera. This is done by 3D printing M4 spaces and Rollers, and screwed on a piece of 3mm acrylic.
Next, we designed a holder for the camera (in this case it is our phone). It sits on a piece of 3mm acrylic.
We were debating over to use a singular platform or just have one single platform has both functionalities. To save some time and prototype more efficiently, we decided to go with the latter, where the moving platform is constructed first, then the camera mount is screwed on the moving platform.
This is a test of moving the platform manually. We zip tied the timing belt on the moving platform and tried to pull it to check for smoothness of motion.
## Actuation
The actuation is our stepper motor, where we convert the rotation motion to linear motion, similar to how Ender 3 oeprates.
To realize this, we need to construct the following:
1. Stepper holder
2. Ball bearing holder
### Right hand side holder
Designed a clamp to hold the stepper, and have space to fit the timing belt. This part was 3D printed.
### Ball Bearing holder + Dolly Left Side Support
The dolly is supported on the left hand side by a stand. We measured the height from the stepper and the bottom support, and placed the ball bearing axis
at the same height as the right hand side. This component was 3D printed.
The construction was an 8mm diameter axis through the 3D print. spacers were used to offset the ball bearing to the center, and its position fixed.
### Driving the Stepper Motor
For the microcontroller, we decided to use [Bryan's ATTiny1614 Microcontroller](https://fabacademy.org/2022/labs/singapore/students/bryan-tee/assignments/week06/) as he has pulled out all of the pins for his hello world board.
- [.sch](https://fabacademy.org/2022/labs/singapore/students/bryan-tee/files/week06/hello_world.sch)
- [.brd](https://fabacademy.org/2022/labs/singapore/students/bryan-tee/files/week06/hello_world.brd)
We decided to drive the stepper at 12Vs, and power the Mircontroller via USB.
### Testing
[Code to control stepper](./test-stepper.ino)
We have decided to use just Bang Bang motion, as the Dolly's speed should be constant to take a perfect Panorama Shot.
```
#define EN 0 // PA4
#define PUL 1 // PA5
#define DIR 2 // PA6
#define REV 800 // steps/rev => 38mm
#define STEP_DIST 38 // mm
void rotate_CW(float distance) {
Serial.print("Rotate CW by "); Serial.println(distance);
int steps = (distance / STEP_DIST) * REV;
digitalWrite(EN, LOW);
digitalWrite(DIR, LOW);
for(int i = 0; i < steps; i++) {
digitalWrite(PUL, LOW);
delay(1);
digitalWrite(PUL, HIGH);
}
digitalWrite(EN, HIGH);
}
void rotate_CCW(float distance) {
Serial.print("Rotate CCW by "); Serial.println(distance);
int steps = (distance / STEP_DIST) * REV;
digitalWrite(EN, LOW);
digitalWrite(DIR, HIGH);
for(int i = 0; i < steps; i++) {
digitalWrite(PUL, LOW);
delay(1);
digitalWrite(PUL, HIGH);
}
digitalWrite(EN, HIGH);
}
void rotate(float distance) {
if (distance > 0) {
rotate_CW(distance);
} else {
rotate_CCW(-distance);
}
}
void setup() {
// put your setup code here, to run once:
pinMode(EN, OUTPUT);
pinMode(PUL, OUTPUT);
pinMode(DIR, OUTPUT);
// initialize stepper
digitalWrite(PUL, HIGH);
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
if (Serial.available()) {
float input_distance = Serial.parseFloat();
if (input_distance > 0 || input_distance < 0) { // filter out 0 values
rotate(input_distance);
}
delay(1000);
}
}
```
We tested to see that the stepper moves in our intended distance, which means the abstraction would start from moving the platform by `x` distance.
## Automation
This is the panorama code logic flowchart:
[Code for Panorama view](./panorama.ino)
```
#define EN 0 // PA4
#define PUL 1 // PA5
#define DIR 2 // PA6
#define LIMIT_SW 8 // PA3
#define GO_SW 9 //PA2
#define REV 800 // steps/rev => 38mm
#define STEP_DIST 38 // mm / rev
#define MAX_DIST 550 // 550mm
// Global variables
bool is_zeroed = false;
float curr_dist = 0;
float delta_dist = (STEP_DIST * 1.0) / REV;
// Note: define CCW as negative distance
void rotate_CW(float distance) {
Serial.print("Rotate CW by "); Serial.println(distance);
int steps = (distance / STEP_DIST) * REV;
digitalWrite(EN, HIGH);
digitalWrite(DIR, LOW);
for (int i = 0; i < steps; i++) {
digitalWrite(PUL, LOW);
delay(1);
digitalWrite(PUL, HIGH);
curr_dist += delta_dist;
if (curr_dist + delta_dist > MAX_DIST) break;
}
digitalWrite(EN, LOW);
Serial.println("Done CW");
}
void rotate_CCW(float distance) {
Serial.print("Rotate CCW by "); Serial.println(distance);
int steps = (distance / STEP_DIST) * REV;
digitalWrite(EN, HIGH);
digitalWrite(DIR, HIGH);
for (int i = 0; i < steps; i++) {
digitalWrite(PUL, LOW);
delay(1);
digitalWrite(PUL, HIGH);
curr_dist -= delta_dist;
if (curr_dist - delta_dist < 0) break;
}
digitalWrite(EN, LOW);
Serial.println("Done CCW");
}
void rotate(float distance) {
if (distance > 0) {
rotate_CW(distance);
} else {
rotate_CCW(-distance);
}
}
void zeroPan() {
digitalWrite(EN, HIGH);
digitalWrite(DIR, HIGH);
while (!is_zeroed) {
digitalWrite(PUL, LOW);
delay(1);
digitalWrite(PUL, HIGH);
is_zeroed = !digitalRead(LIMIT_SW);
}
digitalWrite(EN, LOW);
rotate_CW(10);
is_zeroed = false;
Serial.println("Done zero!");
curr_dist = 0;
}
void runPan() {
rotate_CW(MAX_DIST);
}
void setup() {
// put your setup code here, to run once:
pinMode(EN, OUTPUT);
pinMode(PUL, OUTPUT);
pinMode(DIR, OUTPUT);
pinMode(LIMIT_SW, INPUT);
pinMode(GO_SW, INPUT);
// initialize stepper
// digitalWrite(EN, HIGH);
digitalWrite(PUL, HIGH);
Serial.begin(9600);
zeroPan();
}
void loop() {
// put your main code here, to run repeatedly:
if (Serial.available()) {
float input_distance = Serial.parseFloat();
if (input_distance > 0 || input_distance < 0) {
rotate(input_distance);
}
delay(1000);
}
bool go = !digitalRead(GO_SW);
if (go) {
runPan();
delay(2000);
zeroPan();
}
}
```
Results are as per video. Here is a demo of the logic:
The circuit itself was quite messy, we wired it according to the circuit diagram above, and powered the system with a lab bench power supply, as shown in the video at the top of the page at timestamp 0:45.
Our teammate Jon had lost his phone, which contained better images. Nonetheless, I think the "better images" are still as messy as the one shown above.
# Future improvements
1. Make Microcontroller specific for Dolly
2. Better Roller designs
3. Increase length of Dolly for more effective Panorama shots
## Some fun works that were done but not included:
Stepper clip that worked really well. This was intended to be used with the custom microcontroller, and pasted at the back of the stepper.