Week 12: Mechanical Design & Machine Design

Week 12 focused on machine design and, unlike previous assignments, this was a group project where multiple systems had to work together as a single machine. The project required coordination between mechanical design, electronics, firmware, fabrication, and user interaction.

To understand the scope of the assignment and what could realistically be achieved within the available time, we began by reviewing documentation from previous Fab Academy machine projects.


Project Concept

During discussions around shared tabletop systems, I was reminded of the rotating Lazy Susan commonly used in Chinese dining settings. The idea evolved from a simple rotating tray into an interactive system that could deliver specific items to specific users.

An automated rotary table that brings selected items to selected users through voice commands using Alexa.

The concept combined mechanical movement, position control, and voice interaction into a single system.


Team Roles

Area Team Member
Mechanical Design Abhishek Shah
Electronics Ardradevi
Fabrication & Assembly Ali Abdul Gafoor
Firmware Mishael
Alexa Integration Ashtami P. S.

Although responsibilities were distributed among team members, very few decisions could be made in isolation. The project was highly interconnected, and progress in one area often depended on decisions made in another.

Mechanical design and electronics were closely linked from the beginning. The positioning system relied on a Hall sensor and magnet, which meant the sensor location, magnet placement, mounting strategy, and alignment tolerances all had to be considered during the mechanical design process. Similarly, the chosen gear ratio directly influenced the step calculations, positioning resolution, and motion planning implemented in firmware.

Mechanical design and fabrication were equally dependent on one another. Features such as gear geometry, motor mounts, bearing placement, clearances, assembly methods, and material selection had to be designed with the available fabrication processes in mind. At the same time, fabrication constraints often required design modifications to ensure parts could be manufactured, assembled, and operated reliably.

Electronics also depended on the mechanical structure. The placement of the Hall sensor, routing of wires, mounting of the stepper motor, PCB positioning, and accessibility for assembly all had to be coordinated with the physical layout of the machine. Small changes in mechanical design could affect sensor alignment and ultimately influence positioning accuracy.

Firmware development depended on all of these decisions. The gear ratio determined the effective step resolution of the system, compartment spacing influenced the lookup table calculations, and the Hall sensor placement affected the homing strategy. Even seemingly simple choices such as the number of user positions or compartment locations changed how positions were mapped and addressed in software.

The Alexa integration added another layer of dependency. Voice commands needed to correspond to physical compartments and user locations, which meant the interaction logic had to align with both the firmware coordinate system and the final mechanical arrangement. A request such as "Bring Cookies to Mishael" only works because the physical layout, lookup table, positioning logic, and communication system all agree on what those locations represent.

As the project evolved, it became clear that the machine was not a collection of separate parts but a system of interconnected decisions. Mechanical design, electronics, fabrication, firmware, and interaction design continuously influenced one another, requiring frequent communication and coordination throughout the development process.


My Contribution

I was responsible for developing the firmware for the rotary table. My work focused on stepper motor control, homing, position tracking, lookup-table based positioning, shortest-path motion planning, and integration with the Alexa workflow.

In addition to firmware development, I coordinated project planning and documentation efforts, and prepared the final presentation slides for the team.


Click here to view the group documentation of Voice Controlled Rotary Table

For Machine Week, our team built a voice-controlled rotary table inspired by the traditional Lazy Susan. The idea was simple: instead of manually rotating the table to reach an item, a user could request it through Alexa and have it delivered automatically.

The table is divided into six compartments containing different snacks, while users are assigned fixed positions around the table. When a command is given, Alexa sends the request through MQTT to an ESP32. The firmware then determines the required destination using a lookup table, calculates the shortest path to get there, and rotates the table using a stepper motor.

To make positioning reliable, the system establishes a reference position every time it starts. A Hall effect sensor and magnet are used to perform a homing sequence, allowing the firmware to define a known zero position before any movement takes place.

The project brought together mechanical design, electronics, fabrication, firmware, and voice interaction into a single system. While we demonstrated it using snacks and users around a table, the same approach could be applied to organizing tools, materials, or shared resources in other environments.

At its core, the project explores how a simple rotary mechanism can become a more interactive and intelligent system when combined with sensing, positioning, and automation.




Collaborative Workspace Setup and Project Management

Our instructors, Jogin and Saheen helped us set up a collaborative workspace on Miro and guided us on project management tools. Later, all members contributed their research and work updates to this space and it was easier for us to keep track and refer details.

I was in charge of project management using Gantt Chart


System Definition

The system was defined as:

  • Circular table (~55 cm diameter)
  • 6 compartments (60° separation)
  • Multiple fixed user positions (homes)
  • Central stepper-driven mechanism

Design Thinking Before Implementation

A significant portion of my time was spent imagining system behavior before testing.

Key Questions

  • How will a user call a compartment?
  • Should the table always rotate in one direction?
  • Or should it choose the shortest path?
  • What kind of motion feels natural to a user?
  • How do we ensure repeatability every time?

This early reasoning helped identify core issues before hardware testing:

  • Direction ambiguity (CW vs CCW)
  • Position drift over time
  • Need for a consistent reference

Hardware Overview: Stepper Motor Fundamentals

What is a Stepper Motor?

A stepper motor rotates in discrete angular steps, enabling precise position control.

Typical NEMA 17

Image source: Mechtex, MOONS Industries

Bipolar Stepper (Used in this System)

How does a Stepper Motor work?

Why a Driver is Required

What is a Motor Driver?

Microcontrollers cannot directly drive motors. The DRV8825 stepper motor driver:



Control Signals

Signal Function
STEP Each pulse = one step
DIR Controls direction
ENABLE Enables/disables motor

Hall Sensor – AH49E

Processing Approach



From Geometry to Step Counts

Before writing any motion code, I first needed a way to describe positions around the table.

The table was designed with six compartments spaced equally around the circumference. At the same time, users would be seated at fixed positions around the table. The challenge was not simply rotating the motor, but determining exactly how many motor steps were required to align any compartment with any user position.

I began by treating the system as a circular coordinate problem.

To convert these angular positions into motor movement, I first calculated the effective step resolution of the system.

Step Resolution

Motor Resolution      = 200 steps/rev
Microstepping         = 1/16
Gear Ratio            = 5:1

Effective Resolution
= 200 × 16 × 5
= 16000 steps per table revolution

This means the table requires 16,000 microsteps to complete one full rotation.

Steps per Degree

16000 / 360
≈ 44.44 steps per degree

Using this value, any angular position on the table could be converted into a step position.

Compartment Spacing

Since the six compartments are separated by 60°:

60 × 44.44
≈ 2666.67 steps

At first glance this seems straightforward. However, stepper motors can only move in whole-number steps.

The value 2666.67 cannot be executed directly and must be rounded.

This created an important problem.



The Rounding Error Problem

If every movement was calculated relative to the previous compartment, the system would repeatedly round 2666.67 to a nearby integer value.

For example:

C1 → C2 = 2665
C2 → C3 = 2665
C3 → C4 = 2665
...

Each movement introduces a small error.

Individually the error is tiny, but after many rotations those errors accumulate, causing the table to drift away from its intended alignment.

This meant that relative positioning was not reliable enough for repeated operation.

The solution was to stop thinking about movement and start thinking about addresses.

Instead of asking:

How far should I move from where I am?

I started asking:

Where should this compartment be located in the global coordinate system?

This led to the use of absolute positioning and eventually the lookup table.



From Compartments and People to Step Addresses

Once the mechanical system was working, the next challenge was determining how many motor steps were required to bring any compartment in front of any user.

I started by thinking about the table as two independent coordinate systems:

The homes never move. The compartments rotate with the table.


Step Resolution

The final motion resolution of the table was calculated from the motor, microstepping configuration, and gear reduction.

Motor Resolution = 200 steps/revolution

Microstepping = 1/16

Gear Ratio = 5:1

Total Table Resolution

= 200 × 16 × 5

= 16000 steps/revolution

This means one complete rotation of the table corresponds to 16,000 microsteps.

Therefore:

1° ≈ 16000 / 360

≈ 44.44 steps

Defining the Homes

The first positions I defined were the homes. These represent fixed user locations around the table.

Home Angle Step Address
H1 0
H2 90° 4000
H3 180° 8000
H4 270° 12000

These values remain constant because the users do not move.


Two Different Coordinate Systems

At first glance it might seem unusual that the system contains six compartments but only four homes. This is because the two represent different things.

The compartments rotate with the table, while the homes remain stationary.

During the initial design phase, the system was intended to serve four users seated around the table at approximately 90° intervals.

Element Quantity Description
Compartments 6 Storage locations on the rotating table
Homes 4 Fixed user positions around the table

This means the controller must support every possible combination of compartment and user position.

6 Compartments × 4 Homes

= 24 Possible Alignments

Each of these alignments corresponds to a unique destination in the lookup table.


Defining the Compartments

The table contains six equally spaced compartments.

360° / 6

= 60° between compartments

Converting this angular spacing into steps gives:

60 × 44.44

≈ 2666.67 steps

Since the motor can only move in whole-number steps, the values were rounded and assigned as compartment offsets.

Compartment Angle Offset
C1 0
C2 60° 2665
C3 120° 5332
C4 180° 7999
C5 240° 10666
C6 300° 13333

These values represent the angular relationship between compartments, not actual motor movements.


The Key Question

At this point I had:

The remaining question was:

What absolute table position places a selected compartment in front of a selected user?

For example:

C2 → H2

Compartment Offset = 2665

Home Offset = 4000

Final Position = 2665 + 4000

= 6665

This means that when the table reaches step position 6665, compartment C2 will be aligned with Home H2.


Wrap-Around Behaviour

Some combinations exceed one complete revolution.

For example:

C5 → H3

10666 + 8000

= 18666

Since the table only contains positions from 0 to 15999, the value must wrap around.

18666 % 16000

= 2666

This keeps every valid position inside a single revolution.


Building the Lookup Table

The lookup table contains every valid alignment between a compartment and a home.

Each row represents a compartment attached to the rotating table.

Each column represents a fixed home position.

Since there are six compartments and four homes, the table contains:

6 × 4 = 24 possible states

Each cell stores the absolute step address required to align that compartment with that home.


The Formula Behind the Lookup Table

After calculating several combinations, a pattern became obvious.

Position

=

(Compartment Offset + Home Offset)

% 16000

This single equation generates every destination in the system.

Rather than calculating these values repeatedly during operation, I precomputed them and stored them in a lookup table.


Why a Lookup Table?

The lookup table does not store movements.

It stores destinations.

Each cell answers a single question:

At what absolute step position should the table be located so that a specific compartment aligns with a specific home?

This approach simplified the firmware, eliminated repeated calculations, and provided a fixed set of repeatable target positions for the motion controller.

Visualizing the Lookup Table

Using the relationship:

Position = (Compartment Offset + Home Offset) % 16000

I generated a table containing every valid destination in the system.

Rows represent compartments attached to the rotating table, while columns represent fixed user positions around the table.

H1
0
H2
4000
H3
8000
H4
12000
C1
0
0 4000 8000 12000
C2
2665
2665 6665 10665 14665
C3
5332
5332 9332 13332 1332
C4
7999
7999 11999 15999 3999
C5
10666
10666 14666 2666 6666
C6
13333
13333 1333 5333 9333

Each value represents an absolute step address within a single revolution (0–15999).

For example:

Rather than storing how far the motor should move, the table stores where the system should end up.

This distinction became important later when implementing shortest-path motion planning and homing-based absolute positioning.

Observation:
Moving horizontally across a row adds 4000 steps each time because the homes are separated by 90°. Moving vertically down a column adds approximately 2666 steps because the compartments are separated by 60°. The values that suddenly become small again (1332, 2666, 3999, etc.) are positions that wrapped around after exceeding 16000 steps.
                 Homes
            H1   H2   H3   H4
          ---------------------
C1  --->    •    •    •    •
C2  --->    •    •    •    •
C3  --->    •    •    •    •
C4  --->    •    •    •    •
C5  --->    •    •    •    •
C6  --->    •    •    •    •

24 valid alignments

Motion Control Fundamentals


Motion Logic Evolution

Once absolute target positions were established using the lookup table, the next challenge was deciding how the table should move between positions.

The question was no longer:

Where should the table go?

Instead, it became:

What is the best path to get there?

Stage 1: Clockwise-Only Motion

The first implementation used a simple clockwise-only strategy.

delta = target - current

if (delta < 0)
    delta += 16000

If the target position was behind the current position, the system wrapped around and continued moving clockwise until it reached the destination.

For example:

Current = 14000
Target  = 2000

delta = 2000 - 14000

delta = -12000

delta += 16000

delta = 4000

The table therefore rotates 4000 steps clockwise.

However, this approach was often inefficient.

A destination that was only a short distance behind the current position could require almost a full revolution to reach.


Stage 2: Shortest Path Motion

After validating the positioning system, I changed the motion planner to choose the shortest rotational path.

delta = target - current

if (delta > 8000)
    delta -= 16000

if (delta < -8000)
    delta += 16000

The logic is based on the fact that:

Full Revolution = 16000 steps

Half Revolution = 8000 steps

Any movement greater than half a revolution is no longer the shortest route.

In those cases, the controller reverses direction and approaches the target from the opposite side.


Example 1

Current = 2000
Target  = 14000

delta = 12000

Moving 12000 steps clockwise would reach the target, but this is longer than half a revolution.

The controller therefore converts the movement:

delta = 12000 - 16000

delta = -4000

The motor moves 4000 steps counter-clockwise instead of 12000 steps clockwise.


Example 2

Current = 12000
Target  = 14000

delta = 2000

Since the movement is already less than half a revolution, no correction is required.

The motor simply moves 2000 steps clockwise.


Visual Interpretation

The shortest-path algorithm treats the turntable as a circular coordinate system.

Any destination can be reached in two directions:

The controller compares both possibilities and selects whichever requires fewer steps.

This reduces travel distance, improves response time, and creates motion that feels more natural to the user.


Design Outcome

The final system combines:

The lookup table determines where the table should go.

The shortest-path algorithm determines how it should get there.


Final Mapping After Alexa Integration

During early development, the system used generic labels such as C1–C6 and H1–H4 to simplify testing and motion control.

Once Alexa integration was introduced, the command structure became user-facing. Generic labels were no longer meaningful, so the homes and compartments were mapped to real people and real items.

This allowed commands to be interpreted in a more natural way.

For example:

"Bring Cookies to Mishael"

instead of:

"C6 → H5"

Homes → People

Home Person
H1Abhishek
H2Ardra
H3Ali
H4Ashtami
H5Mishael

Compartments → Items

Compartment Item
C1Nachos
C2Juice
C3Lays
C4Snickers
C5Popcorn
C6Cookies

Expanding from 4 Homes to 5 Homes

The original system assumed four user positions spaced 90° apart.

After the complete team was included in the Alexa workflow, a fifth home position was added.

This changed the angular spacing between homes:

360° / 5

= 72°

Using the table resolution of 16000 steps per revolution:

16000 / 5

= 3200 steps

Therefore each home is separated by 3200 steps.


Home Offsets

Home Angle Step Offset
H10
H272°3200
H3144°6400
H4216°9600
H5288°12800

Updated Lookup Table

The same lookup-table logic was retained.

Only the home offsets changed.

Each cell still represents the absolute position required to align a selected compartment with a selected user.

Item Abhishek Ardra Ali Ashtami Mishael
Nachos 0 3200 6400 9600 12800
Juice 2667 5867 9067 12267 15467
Lays 5333 8533 11733 14933 2133
Snickers 8000 11200 14400 1600 4800
Popcorn 10667 13867 1067 4267 7467
Cookies 13333 533 3733 6933 10133

The resulting lookup table contains:

6 Compartments × 5 Homes

= 30 possible alignments

Each alignment corresponds to a unique absolute step address that can be recalled directly by the controller.


Firmware Architecture

The final firmware combines several independent subsystems that work together to achieve reliable positioning.

Module Purpose
Stepper Control Generates motor motion using the DRV8825 driver and AccelStepper library.
Hall Sensor Processing Converts noisy analog Hall sensor readings into a stable digital signal.
Homing System Establishes a repeatable reference position during startup.
Lookup Table (LUT) Stores all valid compartment-home alignments as absolute step positions.
Motion Planner Calculates the shortest route to a target position.
Position Tracking Maintains the current absolute position of the turntable.
MQTT / Alexa Interface Receives user commands and converts them into motion requests.

The following sections explain the most important parts of the firmware in detail.


Homing Logic

The positioning system relies on absolute step addresses stored in the lookup table. For these addresses to have meaning, the controller must first establish a known physical reference.

This is the purpose of the homing sequence.

During homing, a Hall effect sensor detects a magnet mounted on the turntable. The detected position becomes the system's zero reference.

After homing:

Current Position = 0

C1 aligned with H1

Every subsequent movement is calculated relative to this reference.

Why Not Stop on First Detection?

A Hall sensor does not detect a single point. Instead, it detects a magnetic field over a small region.

If the motor stopped immediately on first detection, the final position would vary slightly depending on speed, inertia, and sensor response.

To improve repeatability, a multi-stage homing sequence was implemented.

Homing State Machine

SEARCH
   ↓
BACKOFF
   ↓
APPROACH
   ↓
DONE

SEARCH

The motor rotates clockwise at a relatively high speed until the Hall sensor first detects the magnet.

SEARCH_SPEED = 800

This stage prioritizes speed over precision.

BACKOFF

Once the magnet is detected, the motor reverses direction and moves away until the magnetic field is completely cleared.

This ensures the system starts the final approach from outside the detection zone.

APPROACH

The motor then approaches the magnet again at a much slower speed.

APPROACH_SPEED = 200

The second detection event occurs under controlled conditions and provides a significantly more repeatable reference point.

DONE

When the magnet is detected during the slow approach:

stepper.setCurrentPosition(0);

The controller assigns:

Position = 0

From this point onward, every lookup-table address corresponds to a known physical location on the turntable.

Result

The homing procedure transforms an unknown startup position into a repeatable global reference frame.

Without homing, the lookup table, shortest-path calculations, and absolute positioning system would all become invalid.

Firmware Implementation

The final firmware can be understood as four cooperating subsystems:

  1. Hall Sensor Processing
  2. Homing State Machine
  3. Motion Planner
  4. System State Machine

Each subsystem solves a different problem and builds upon the previous one.


1. Hall Sensor Processing

The AH49E Hall sensor provides an analog signal that changes when a magnet passes nearby.

Raw readings contain noise and small fluctuations, making direct detection unreliable.

Low-Pass Filtering

filtered = 0.8f * filtered + 0.2f * raw;

This acts as a simple low-pass filter.

Hysteresis

if (!hallState && filtered > ON_THRESHOLD)
    hallState = true;

if (hallState && filtered < OFF_THRESHOLD)
    hallState = false;

Two different thresholds are used.

This prevents rapid switching when the signal hovers near a threshold.


2. Homing State Machine

Once reliable magnet detection was available, it could be used to establish a physical reference.

Because the turntable uses absolute positioning, the controller must first determine where position zero is located.

State Diagram

SEARCH
  ↓
BACKOFF
  ↓
APPROACH
  ↓
DONE

Implementation

switch (state) {

case SEARCH:
  ...
  break;

case BACKOFF:
  ...
  break;

case APPROACH:
  ...
  break;
}

SEARCH

if (currentHall && !lastHall)

The sensor has just entered the magnetic field.

BACKOFF

if (!currentHall && lastHall)

The sensor has fully exited the magnetic field.

APPROACH

stepper.setCurrentPosition(0);

This defines the global coordinate origin used by the lookup table.

After homing:

Position 0

C1 aligned with H1

3. Motion Planning

Once a target position is selected from the lookup table, the controller must decide how to reach it.

Lookup Table Selection

long target = LUT[c][h];

This converts:

Compartment + Home

↓

Absolute Step Address

No geometric calculations are required during operation.

Shortest Path Calculation

delta = target - turntableStepPos;

This calculates the distance between the current position and the target.

The movement is then normalized to the shortest rotational path.

if (delta > 8000)
    delta -= 16000;

if (delta < -8000)
    delta += 16000;

Because:

Full Revolution = 16000

Half Revolution = 8000

Any movement larger than half a revolution is replaced with the shorter path in the opposite direction.

Movement Execution

stepper.moveTo(
  stepper.currentPosition() + delta
);

The lookup table provides the destination.

The shortest-path planner determines the route.


4. System State Machine

The overall firmware is controlled using a higher-level state machine.

HOMING
  ↓
IDLE
  ↓
POSITIONING
  ↓
IDLE

HOMING

IDLE

POSITIONING

if (stepper.distanceToGo() != 0)

The motor continues moving until the target is reached.


Final Refinement: Crawl Speed

if (abs(stepper.distanceToGo()) < CRAWL_THRESHOLD)
{
  stepper.setMaxSpeed(CRAWL_SPEED);
}

As the target approaches, the motor automatically slows down.


Complete System Flow

Power On

↓

HOMING

↓

Position = 0

↓

Wait for Command

↓

Lookup Table

↓

Shortest Path

↓

Motor Motion

↓

IDLE

This sequence allows a user command to be translated into a repeatable physical position on the turntable.


Firmware Overview

Intent

The goal of the firmware was to transform a stepper motor-driven turntable into a system capable of delivering a selected compartment to a selected user.

To achieve this, the system needed to know its position, establish a repeatable reference at startup, interpret user requests, and move efficiently between destinations.

Rather than treating the turntable as a motor that rotates by a certain number of steps, the firmware treats it as a circular coordinate system where every compartment-home combination corresponds to a predefined address.


Requirements

ID Requirement
R1 Establish a repeatable reference position at startup.
R2 Track the turntable position within a full revolution.
R3 Move any compartment to any user position.
R4 Minimize travel distance during movement.
R5 Accept commands from Serial and Alexa/MQTT.
R6 Maintain repeatable positioning over repeated movements.

How Each Requirement Was Addressed

Requirement Implementation
R1 Hall sensor, magnet, filtering, hysteresis, and a homing state machine.
R2 Absolute position tracking using a 0–15999 step coordinate system.
R3 Lookup table containing all valid compartment-home alignments.
R4 Shortest-path motion planning using circular distance calculations.
R5 Serial command parser and MQTT subscriber.
R6 Absolute positioning and repeatable homing strategy.

Firmware Architecture

The firmware is organized into four major subsystems.

Subsystem Purpose
Hall Sensor Processing Converts noisy analog readings into reliable digital events.
Homing System Establishes a repeatable reference position.
Motion Planner Determines where to move and how to get there.
Communication Interface Receives commands from Serial and MQTT.

High-Level Firmware Flow

Power On

↓

Initialize Hardware

↓

Homing

↓

Position = 0

↓

Wait for Command

↓

Lookup Table

↓

Shortest Path Calculation

↓

Move Motor

↓

Target Reached

↓

Wait for Command

System Architecture

User Command
(Serial / Alexa)

        ↓

Command Parser

        ↓

Lookup Table

        ↓

Motion Planner

        ↓

Stepper Motor

        ↓

Turntable Motion

        ↑

Hall Sensor
(Homing Reference)

The Hall sensor establishes the reference position, the lookup table determines the destination, and the motion planner determines the shortest route between them.


Development Process

The firmware was developed in parallel with the mechanical design. Since the physical system was not immediately available, a significant amount of time was spent modelling the behaviour of the turntable before full integration.

Rather than starting with a complete system, each subsystem was developed and validated independently.

Key Areas of Investigation

This process ultimately led to the adoption of absolute positioning and lookup-table based control.


Incremental Testing Strategy

To simplify debugging, the system was developed in stages.

Stage Objective
1 Verify basic stepper motion and direction control
2 Configure microstepping and confirm system resolution
3 Read and characterize Hall sensor behaviour
4 Implement and validate homing
5 Test absolute positioning using predefined targets
6 Implement shortest-path motion planning
7 Integrate MQTT communication and Alexa commands
8 Validate complete end-to-end operation

Each stage was tested independently before moving to the next, making it easier to isolate and troubleshoot problems.


Key Firmware Structures

The final firmware is built around a small number of core structures.

Component Purpose
System Constants Define mechanical resolution, speeds and operating limits.
Lookup Table (LUT) Stores all valid compartment-home alignments.
Hall Sensor Processing Provides stable magnetic detection using filtering and hysteresis.
Homing FSM Establishes a repeatable reference position.
Motion Planner Selects the shortest path to a target.
System FSM Coordinates homing, positioning and idle operation.

Final Firmware Flow

Power On

↓

Hall Sensor Active

↓

Homing Sequence

↓

Position = 0

↓

Wait for Command

↓

Lookup Table Lookup

↓

Shortest Path Calculation

↓

Motor Motion

↓

Target Reached

↓

Idle

The result is a repeatable positioning system in which every user request is translated into a predefined absolute destination and executed through the shortest available route.



Test 1: Hall Effect Sensor Magnet Detection

Before implementing homing, I needed to verify that the AH49E Hall effect sensor could reliably detect the magnet mounted on the turntable.

The goal of this test was to determine whether the sensor output changed consistently when a magnet approached and moved away.

Reference: AH49E Hall Effect Sensor Datasheet

https://www.diodes.com/assets/Datasheets/AH49E.pdf

Test Objective

Code

#define HALL_PIN D8

bool magnetDetected = false;

void setup() {
Serial.begin(115200);
}

void loop() {

int val = analogRead(HALL_PIN);

// Detect magnet (only when it first appears)
if (!magnetDetected && val > 3800) {
magnetDetected = true;
Serial.println("MAGNET CLOSE");
}

// Reset state when magnet is removed
if (magnetDetected && val < 3400) {
magnetDetected = false;
}

delay(20);
} 

How It Works

Using separate ON and OFF thresholds introduces a simple form of hysteresis, preventing rapid toggling near the detection boundary.

Result

When the magnet was brought close to the sensor, the Serial Monitor printed:

MAGNET CLOSE

The message was printed only once per approach and did not continuously repeat while the magnet remained in place.

Outcome



Test 2: Converting the Hall Sensor into a Reliable Digital Switch

The previous test confirmed that the AH49E Hall effect sensor could detect a nearby magnet. However, the raw analog readings were noisy and unsuitable for direct use in the homing algorithm.

Since the final system only needed to know whether the magnet was present or not, the next step was to convert the analog sensor into a stable digital signal.

This required three improvements:

Problem Observed

While monitoring the raw analog values, occasional spikes and fluctuations appeared even when the magnet position remained unchanged.

Without additional processing, these fluctuations could cause false triggering and unstable homing behaviour.

Solution

The sensor reading was processed in three stages:

  1. Apply a low-pass filter to smooth the signal.
  2. Use separate ON and OFF thresholds.
  3. Trigger events only when the state changes.

Code

#define HALL_PIN D8

// --- Thresholds (tuned from testing) ---
#define ON_THRESHOLD 3800
#define OFF_THRESHOLD 3400

// --- Filtering factor ---
float filtered = 0;

// --- Digital state ---
bool hallState = false;

void setup() {
Serial.begin(115200);

filtered = analogRead(HALL_PIN);
}

bool readHallDigital() {

int raw = analogRead(HALL_PIN);

// Low-pass filter
filtered = 0.8 * filtered + 0.2 * raw;

// Hysteresis
if (!hallState && filtered > ON_THRESHOLD) {
hallState = true;
}

if (hallState && filtered < OFF_THRESHOLD) {
hallState = false;
}

return hallState;
}

void loop() {

static bool lastState = false;

bool currentState = readHallDigital();

if (currentState && !lastState) {
Serial.println("MAGNET DETECTED");
}

if (!currentState && lastState) {
Serial.println("MAGNET REMOVED");
}

lastState = currentState;

delay(20);
} 

Understanding the Logic

1. Low-Pass Filtering

filtered = 0.8 * filtered + 0.2 * raw;

This smooths sudden spikes in the sensor signal by combining the current reading with previous readings.

2. Hysteresis

ON_THRESHOLD  = 3800
OFF_THRESHOLD = 3400

Instead of using a single threshold, two different thresholds are used.

This prevents rapid switching when the signal hovers near the detection boundary.

3. Edge Detection

currentState && !lastState

Detects the exact moment the magnet appears.

!currentState && lastState

Detects the exact moment the magnet disappears.

This behaviour mimics a digital interrupt and produces a single event for each transition.

Result

The Serial Monitor consistently produced:

MAGNET DETECTED
MAGNET REMOVED

Only one message was generated per transition, even when the magnet remained stationary near the sensor.

What I Learned

This implementation became the foundation of the final homing algorithm used in the turntable.



Test 3: Basic Stepper Motor Control

Before implementing any positioning logic, I first verified that the ESP32 could correctly drive the NEMA 17 stepper motor through the DRV8825 driver.

The objective of this test was to:

Test Setup

Code

// ---- Pin Definitions ----
#define STEP_PIN    D1
#define DIR_PIN     D0
#define EN_PIN      D3

#define M0_PIN      D2
#define M1_PIN      D4
#define M2_PIN      D5

// ---- Motor Parameters ----
#define STEPS_PER_REV 200
#define MICROSTEPPING 16

int totalSteps = STEPS_PER_REV * MICROSTEPPING;

// ---- Timing ----
int stepDelay = 800;

void stepMotor(int steps, bool direction) {

digitalWrite(DIR_PIN, direction);

for (int i = 0; i < steps; i++) {

```
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(stepDelay);

digitalWrite(STEP_PIN, LOW);
delayMicroseconds(stepDelay);
```

}
}

void setup() {

pinMode(STEP_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
pinMode(EN_PIN, OUTPUT);

pinMode(M0_PIN, OUTPUT);
pinMode(M1_PIN, OUTPUT);
pinMode(M2_PIN, OUTPUT);

// Enable Driver
digitalWrite(EN_PIN, LOW);

// Set 1/16 Microstepping
digitalWrite(M0_PIN, HIGH);
digitalWrite(M1_PIN, HIGH);
digitalWrite(M2_PIN, LOW);

delay(1000);
}

void loop() {

// Clockwise
stepMotor(totalSteps, HIGH);
delay(1000);

// Counter-clockwise
stepMotor(totalSteps, LOW);
delay(1000);
} 

Understanding the Logic

Driver Enable

digitalWrite(EN_PIN, LOW);

The DRV8825 enable pin is active low, so setting the pin LOW allows the driver to energize the motor.

Microstepping Configuration

M0 = HIGH
M1 = HIGH
M2 = LOW

This configuration sets the DRV8825 to 1/16 microstepping mode.

For a standard 200-step motor:

200 × 16

= 3200 microsteps per revolution

Generating Motion

digitalWrite(STEP_PIN, HIGH);
digitalWrite(STEP_PIN, LOW);

Each pulse on the STEP pin advances the motor by one microstep.

The DIR pin determines the rotation direction.

One Revolution Test

totalSteps = 200 × 16

= 3200

The motor is commanded to move 3200 microsteps clockwise, pause, and then return 3200 microsteps counter-clockwise.

Result

What I Learned

This test established the foundation for all subsequent motion-control experiments.



Test 4: Homing Using Hall Sensor Feedback

After independently testing the stepper motor and Hall effect sensor, the next step was to combine them into a complete homing routine.

The objective was to establish a repeatable physical reference position every time the system powered on.

This was necessary because the positioning system relied on absolute step addresses. Without a known starting point, a value such as 6400 steps would have no physical meaning.

Test Objective

Homing Strategy

Rather than stopping on the first magnet detection, a multi-stage process was used.

SEARCH
   ↓
BACKOFF
   ↓
APPROACH
   ↓
DONE

The purpose of this sequence was to reduce uncertainty caused by the width of the magnetic field.

The first detection identifies the approximate location of the magnet, while the second detection is used to establish a precise and repeatable reference.

Code

#include <AccelStepper.h> 

// ================== PIN DEFINITIONS ==================
#define STEP_PIN D1      // STEP signal to driver
#define DIR_PIN  D0      // DIR signal to driver
#define EN_PIN   D3      // ENABLE (LOW = enabled)

#define M0_PIN D2        // Microstepping pins
#define M1_PIN D4
#define M2_PIN D5

#define HALL_PIN D8      // Hall sensor (analog input)

// ================== HALL THRESHOLDS ==================
// Tune these based on your sensor readings
#define ON_THRESHOLD  3800   // Enter magnet zone above this value
#define OFF_THRESHOLD 3400   // Exit magnet zone below this value

// ================== SPEED SETTINGS ==================
// Steps per second (sign defines direction)
#define SEARCH_SPEED    800   // Fast CW search
#define BACKOFF_SPEED   400   // CCW backoff
#define APPROACH_SPEED  200   // Slow CW final approach (high accuracy)

// ================== STEPPER OBJECT ==================
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);

// ================== HALL FILTER STATE ==================
float filtered = 0.0f;  // Low-pass filtered sensor value
bool  hallState = false; // Debounced digital state (true = magnet present)

// ================== HOMING STATE MACHINE ==================
enum HomingState {
  SEARCH,    // Rotate CW until magnet is first detected (edge)
  BACKOFF,   // Rotate CCW until magnet is fully released
  APPROACH,  // Rotate CW slowly to detect edge again (precise)
  DONE       // Homing complete
};

HomingState state = SEARCH;

// ================== SETUP ==================
void setup() {
  Serial.begin(115200);

  // ---- Enable driver ----
  pinMode(EN_PIN, OUTPUT);
  digitalWrite(EN_PIN, LOW);   // LOW = driver enabled

  // ---- Set microstepping (1/16 for DRV8825) ----
  pinMode(M0_PIN, OUTPUT);
  pinMode(M1_PIN, OUTPUT);
  pinMode(M2_PIN, OUTPUT);

  digitalWrite(M0_PIN, HIGH);
  digitalWrite(M1_PIN, HIGH);
  digitalWrite(M2_PIN, LOW);

  // ---- Initialize Hall filter baseline ----
  filtered = analogRead(HALL_PIN);

  // ---- Configure stepper limits ----
  stepper.setMaxSpeed(1200);      // Upper limit (must exceed SEARCH_SPEED)
  stepper.setAcceleration(800);   // Not used by runSpeed(), but kept for later use

  Serial.println("Homing start...");
}

// ================== HALL SENSOR (FILTER + HYSTERESIS) ==================
bool readHallDigital() {
  int raw = analogRead(HALL_PIN);     // Read analog value

  // ---- Low-pass filter (smooth noise) ----
  filtered = 0.8f * filtered + 0.2f * raw;

  // ---- Hysteresis (prevents flicker near threshold) ----
  if (!hallState && filtered > ON_THRESHOLD)  hallState = true;   // enter zone
  if (hallState && filtered < OFF_THRESHOLD)  hallState = false;  // exit zone

  return hallState;
}

// ================== LOOP ==================
void loop() {

  // Track previous Hall state to detect edges (transitions)
  static bool lastHall = false;
  bool currentHall = readHallDigital();

  switch (state) {

    // -------- STAGE 1: FAST SEARCH (CW) --------
    case SEARCH:
      // Positive speed = CW
      stepper.setSpeed(SEARCH_SPEED);
      stepper.runSpeed();   // Non-blocking continuous stepping

      // Detect rising edge: entering magnet zone
      if (currentHall && !lastHall) {
        Serial.println("SEARCH: magnet detected (edge)");
        state = BACKOFF;    // Move to backoff stage
      }
      break;

    // -------- STAGE 2: BACKOFF (CCW) --------
    case BACKOFF:
      // Negative speed = CCW
      stepper.setSpeed(-BACKOFF_SPEED);
      stepper.runSpeed();

      // Detect falling edge: fully exited magnet zone
      if (!currentHall && lastHall) {
        Serial.println("BACKOFF: exited magnet zone");
        state = APPROACH;   // Proceed to precise approach
      }
      break;

    // -------- STAGE 3: SLOW APPROACH (CW) --------
    case APPROACH:
      // Slow CW motion for precise edge detection
      stepper.setSpeed(APPROACH_SPEED);
      stepper.runSpeed();

      // Detect rising edge again (high repeatability)
      if (currentHall && !lastHall) {
        Serial.println("APPROACH: magnet detected (precise edge)");

        // Stop motion
        stepper.setSpeed(0);

        // Define HOME position (c0) as 0 steps
        stepper.setCurrentPosition(0);

        Serial.println("HOMING COMPLETE → c0 = 0 steps");

        state = DONE;
      }
      break;

    // -------- STAGE 4: DONE --------
    case DONE:
      // Motor is stopped; ready for positioning logic
      // (No further action here)
      break;
  }

  // Update last state for edge detection in next loop
  lastHall = currentHall;
}

How the State Machine Works

SEARCH

The motor rotates clockwise at high speed.

stepper.setSpeed(SEARCH_SPEED);

During this stage the controller waits for a rising edge from the Hall sensor.

currentHall && !lastHall

This indicates that the sensor has just entered the magnetic field.

Once detected, the state machine transitions to BACKOFF.

BACKOFF

The motor reverses direction and moves away from the magnet.

stepper.setSpeed(-BACKOFF_SPEED);

The controller waits for a falling edge:

!currentHall && lastHall

This confirms that the sensor has completely exited the magnetic field.

Once the field is cleared, the system transitions to APPROACH.

APPROACH

The motor moves clockwise again, but at a much slower speed.

stepper.setSpeed(APPROACH_SPEED);

This slow movement improves repeatability by reducing overshoot and increasing edge-detection accuracy.

When the rising edge is detected for a second time:

stepper.setCurrentPosition(0);

The current position is defined as the global reference position.

In the final system this corresponds to:

C1 aligned with H1
Position = 0

DONE

Once the reference has been established, the motor stops and the system is ready for positioning commands.

Result

The Serial Monitor produced:

Homing start...

SEARCH: magnet detected (edge)

BACKOFF: exited magnet zone

APPROACH: magnet detected (precise edge)

HOMING COMPLETE → c0 = 0 steps

The expected sequence occurred consistently across repeated tests.

Why the Backoff Step Matters

The Hall sensor does not detect a single point. It detects a magnetic region.

If the motor stopped immediately on first detection, the final position could vary depending on speed, inertia, and the exact shape of the magnetic field.

The backoff-and-approach strategy ensures that the final reference is always established from the same direction and under the same conditions.

This significantly improves repeatability.

What I Learned

This test completed the foundation required for absolute positioning and lookup-table based motion control.



Test 5: Clockwise Rotation to Fixed Targets

After establishing a repeatable home position, the next step was to verify that the turntable could move to predefined target positions.

At this stage, the controller always rotated in the clockwise direction. The goal was not efficiency, but validating that absolute target positions could be reached consistently.

Test Objective

Control Strategy

The initial implementation only allowed clockwise rotation.

delta = target - current;

if (delta < 0)
    delta += 16000;

If the target position was behind the current position, the controller wrapped around and continued rotating clockwise until the destination was reached.

Example

Current Position = 14000
Target Position  = 2000

delta = 2000 - 14000

delta = -12000

delta += 16000

delta = 4000

The motor therefore rotates 4000 steps clockwise to reach the target.

Result

Observation

Although the system reached the correct destination, some movements were unnecessarily long because the motor always rotated clockwise.

This led to the next improvement: implementing shortest-path motion planning so the controller could choose between clockwise and counter-clockwise movement.



Test 6: Absolute Positioning with Shortest-Path Motion

After validating homing and fixed-position movement, the next objective was to make the system behave like a real turntable rather than a simple motorized platform.

Until this stage, movements were performed using a clockwise-only strategy. While functional, it often resulted in unnecessarily long rotations.

This test introduced three major improvements:

Test Objective

System Architecture

User Command

↓

Lookup Table

↓

Target Position

↓

Shortest Path Calculation

↓

Motor Motion

↓

Target Reached

Code

#include <AccelStepper.h> 

// ================== PIN DEFINITIONS ==================
#define STEP_PIN D1
#define DIR_PIN  D0
#define EN_PIN   D3

#define M0_PIN   D2
#define M1_PIN   D4
#define M2_PIN   D5

#define HALL_PIN D8

// ================== HALL THRESHOLDS ==================
#define ON_THRESHOLD  3800
#define OFF_THRESHOLD 3400

// ================== TURNTABLE CONSTANTS ==================
#define MOTOR_STEPS   200
#define MICROSTEPS    16
#define GEAR_RATIO    5
#define STEPS_PER_REV (MOTOR_STEPS * MICROSTEPS * GEAR_RATIO)  // 16000

// ================== SPEED SETTINGS ==================
#define SEARCH_SPEED    800
#define BACKOFF_SPEED   400
#define APPROACH_SPEED  200

#define MOVE_MAX_SPEED  1200
#define MOVE_ACCEL      600

// ================== LOOKUP TABLE ==================
// Absolute positions (pre-calibrated, drift-free)
const long LUT[7][5] = {
  {0,      0,      0,      0,      0     },

  {0,      0,   4000,   8000,  12000    }, // C1
  {0,   2665,   6665,  10665,  14665    }, // C2
  {0,   5332,   9332,  13332,   1332    }, // C3
  {0,   7999,  11999,  15999,   3999    }, // C4
  {0,  10666,  14666,   2666,   6666    }, // C5
  {0,  13333,   1333,   5333,   9333    }, // C6
};

const char* COMP_LABEL[7] = {"", "C1","C2","C3","C4","C5","C6"};
const char* HOME_LABEL[5] = {"", "H1","H2","H3","H4"};

// ================== STEPPER ==================
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);

// ================== HALL FILTER ==================
float filtered  = 0.0f;
bool  hallState = false;

// ================== STATE MACHINES ==================
enum HomingState { SEARCH, BACKOFF, APPROACH, HOMING_DONE };
enum SystemState { HOMING, POSITIONING, IDLE };

HomingState homingState = SEARCH;
SystemState sysState    = HOMING;

// ================== POSITION TRACKING ==================
long turntableStepPos = 0;  // 0 → 15999 absolute
int  lastComp = 0;
int  lastHome = 0;

// ================== HALL SENSOR ==================
bool readHallDigital() {
  int raw = analogRead(HALL_PIN);

  // Low-pass filter
  filtered = 0.8f * filtered + 0.2f * raw;

  // Hysteresis
  if (!hallState && filtered > ON_THRESHOLD) hallState = true;
  if ( hallState && filtered < OFF_THRESHOLD) hallState = false;

  return hallState;
}

// ================== HOMING ==================
void runHoming() {

  static bool lastHall = false;
  bool currentHall = readHallDigital();

  switch (homingState) {

    case SEARCH:
      stepper.setSpeed(SEARCH_SPEED);
      stepper.runSpeed();

      if (currentHall && !lastHall) {
        Serial.println("[HOMING] Magnet detected → BACKOFF");
        homingState = BACKOFF;
      }
      break;

    case BACKOFF:
      stepper.setSpeed(-BACKOFF_SPEED);
      stepper.runSpeed();

      if (!currentHall && lastHall) {
        Serial.println("[HOMING] Magnet cleared → APPROACH");
        homingState = APPROACH;
      }
      break;

    case APPROACH:
      stepper.setSpeed(APPROACH_SPEED);
      stepper.runSpeed();

      if (currentHall && !lastHall) {

        stepper.setSpeed(0);

        // Define absolute zero
        // Guarantees: C1 = H1
        stepper.setCurrentPosition(0);
        turntableStepPos = 0;

        lastComp = 1;
        lastHome = 1;

        // Switch to motion mode
        stepper.setMaxSpeed(MOVE_MAX_SPEED);
        stepper.setAcceleration(MOVE_ACCEL);

        homingState = HOMING_DONE;
        sysState    = IDLE;

        Serial.println("HOMING COMPLETE → C1 aligned with H1");
      }
      break;

    case HOMING_DONE:
      break;
  }

  lastHall = currentHall;
}

// ================== MOVE (SHORTEST PATH) ==================
void moveCompartmentToHome(int c, int h) {

  if (c < 1 || c > 6) { Serial.println("[ERR] C must be 1–6"); return; }
  if (h < 1 || h > 4) { Serial.println("[ERR] H must be 1–4"); return; }

  long target = LUT[c][h];

  // -------- SHORTEST PATH CALCULATION --------

  // Raw difference
  long delta = target - turntableStepPos;

  // Normalize to shortest path range [-8000, +8000]
  if (delta >  STEPS_PER_REV / 2)  delta -= STEPS_PER_REV;
  if (delta < -STEPS_PER_REV / 2)  delta += STEPS_PER_REV;

  if (delta == 0) {
    Serial.println("[INFO] Already at target");
    return;
  }

  // Command relative move
  stepper.moveTo(stepper.currentPosition() + delta);
  sysState = POSITIONING;

  // Update logical state
  turntableStepPos = target;
  lastComp = c;
  lastHome = h;

  // Debug info
  Serial.print("Moving ");
  Serial.print(COMP_LABEL[c]);
  Serial.print(" → ");
  Serial.println(HOME_LABEL[h]);

  Serial.print("Steps: ");
  Serial.println(delta);

  Serial.print("Direction: ");
  Serial.println(delta > 0 ? "CW" : "CCW");
}

// ================== SERIAL ==================
void parseCommand(String cmd) {

  cmd.trim();
  cmd.toUpperCase();

  // Format: CnHm
  if (cmd.length() == 4 && cmd[0]=='C' && cmd[2]=='H') {
    int c = cmd[1] - '0';
    int h = cmd[3] - '0';
    moveCompartmentToHome(c, h);
    return;
  }

  if (cmd == "HOME") {
    homingState = SEARCH;
    sysState    = HOMING;
    Serial.println("[HOMING] Restarting...");
    return;
  }

  if (cmd == "POS") {
    Serial.print("Steps: ");
    Serial.println(turntableStepPos);
    return;
  }

  Serial.println("[ERR] Unknown command");
}

// ================== SETUP ==================
void setup() {

  Serial.begin(115200);

  pinMode(EN_PIN, OUTPUT);
  digitalWrite(EN_PIN, LOW);

  // DRV8825 → 1/16 microstepping
  pinMode(M0_PIN, OUTPUT); digitalWrite(M0_PIN, LOW);
  pinMode(M1_PIN, OUTPUT); digitalWrite(M1_PIN, LOW);
  pinMode(M2_PIN, OUTPUT); digitalWrite(M2_PIN, HIGH);

  filtered = analogRead(HALL_PIN);

  stepper.setMaxSpeed(SEARCH_SPEED);

  Serial.println("Starting homing...");
}

// ================== LOOP ==================
void loop() {

  switch (sysState) {

    case HOMING:
      runHoming();
      break;

    case POSITIONING:
      if (stepper.distanceToGo() != 0) {
        stepper.run();
      } else {
        Serial.println("Move complete");
        sysState = IDLE;
      }
      break;

    case IDLE:
      if (Serial.available()) {
        String cmd = Serial.readStringUntil('\n');
        parseCommand(cmd);
      }
      break;
  }
} 

New Feature 1: Serial Commands

The system now accepts commands in the format:

CnHm

Examples:

C2H3
C5H1
C6H4

Each command specifies:

The parser extracts these values and passes them to the motion planner.


New Feature 2: Lookup Table Positioning

long target = LUT[c][h];

The lookup table converts a compartment-home request directly into an absolute step address.

For example:

C2H3

↓

LUT[2][3]

↓

10665

This means:

Move the turntable until step position 10665 is reached.

No angular calculations are required during operation.


New Feature 3: Shortest-Path Motion Planning

The raw movement is calculated as:

delta = target - turntableStepPos;

However, the direct path is not always the shortest path.

Since one complete revolution equals:

16000 steps

Half a revolution equals:

8000 steps

If the required movement exceeds half a revolution, the controller reverses direction.

if (delta > 8000)
    delta -= 16000;

if (delta < -8000)
    delta += 16000;

Example

Current Position = 2000
Target Position  = 14000

delta = 12000

Moving 12000 steps clockwise would work, but it is inefficient.

The controller therefore converts the movement:

12000 - 16000

= -4000

The motor now rotates 4000 steps counter-clockwise instead of 12000 steps clockwise.


Position Tracking

The system maintains a logical representation of the turntable position.

turntableStepPos

This variable stores the current absolute address within the 0–15999 coordinate system.

Every successful movement updates this value, ensuring future shortest-path calculations are based on the correct position.


Debug Feedback

During testing, the Serial Monitor reported:

Moving C3 → H2

Steps: 5332

Direction: CW

This made it easy to verify:


Result

What I Learned

This test marked the transition from a positioning experiment to a usable turntable control system and formed the basis for the later Alexa integration.



Test 7: Alexa Integration via MQTT

At this stage, the motion control system was already capable of homing, absolute positioning, and shortest-path movement.

The next objective was to connect the turntable to the team's Alexa interface so that voice commands could trigger movement automatically.

This transformed the project from a standalone positioning system into an interactive tool-delivery platform.

Test Objective


System Architecture

User Voice Command

↓

Alexa

↓

MQTT Message

↓

ESP32

↓

Command Parser

↓

Lookup Table

↓

Shortest Path Motion

↓

Turntable Movement

The motion-control firmware remained largely unchanged. The main difference was the source of the command.

Previously commands were entered manually through the Serial Monitor.

C2H3
C5H1
C6H4

Now those commands arrived remotely through MQTT.


Human-Friendly Naming

To make voice commands more meaningful, the generic compartment and home labels were replaced with real people and real items.

Homes → Team Members

HomePerson
H1Abhishek
H2Ardra
H3Ali
H4Ashtami
H5Mishael

Compartments → Items

CompartmentItem
C1Nachos
C2Juice
C3Lays
C4Snickers
C5Popcorn
C6Cookies

This mapping made it possible for the user interface to work with meaningful names while the firmware continued using lookup-table indices internally.


Expanding from 4 Homes to 5 Homes

Earlier tests assumed four user positions spaced 90° apart.

For the final team setup, a fifth home position was added.

360° / 5

= 72°

Using the system resolution of 16000 steps per revolution:

16000 / 5

= 3200 steps

The new home offsets became:

HomeOffset
H10
H23200
H36400
H49600
H512800

The lookup table was regenerated using the same formula:

Position =
(Compartment Offset + Home Offset)
% 16000

MQTT Communication

The ESP32 connected to the team's MQTT broker and subscribed to a predefined topic.

fabacademy/ashtami/tool

Whenever a message arrived, the callback function extracted the payload and passed it to the command parser.

MQTT Message

↓

callback()

↓

parseCommand()

↓

moveCompartmentToHome()

This allowed MQTT commands to reuse exactly the same motion-planning logic that had already been tested through the Serial interface.


Additional Motion Refinement

During integration, a crawl-speed mode was added.

if (abs(stepper.distanceToGo()) < CRAWL_THRESHOLD)
{
    stepper.setMaxSpeed(CRAWL_SPEED);
}

As the turntable approached its target, the speed automatically reduced.

This improved stopping accuracy and reduced overshoot.


Result

What I Learned

This test completed the transition from a standalone motion-control prototype to the final interactive turntable system demonstrated during Machine Week.


AI-Assisted Development

AI was used throughout the development process as a thinking partner rather than a code generator. Most of the interactions revolved around understanding the problem, exploring different approaches, validating calculations, and refining ideas before implementation.

Much of the firmware architecture emerged from a series of discussions around how a rotary system should behave before writing the final code.

Example Prompts

Understanding the Rotary System

I am trying to figure out the working of a rotary table that has 4 homes at equal angles and 6 compartments at equal angles. I am planning to position people at the homes and snacks in each compartment. I want to rotate a selected compartment to a selected home using a serial command.

Motion Planning and Coordinate System

We are planning to use a 5:1 gear ratio and a NEMA 17 stepper motor. Help me understand how to calculate positions, steps per revolution, and define a coordinate system for the table.

Homing Strategy

I want to use a Hall effect sensor and magnet to establish a repeatable reference position and define that position as Home 0. What are different approaches to homing and what are their trade-offs?

Stepper Motor Control

My first priority is finding home. After that I want to test basic motor rotation using microstepping for smoother movement. Help me understand how to structure the firmware development process.

Shortest Path Logic

Once the basic rotation is working, I want the table to choose the shortest rotational path between two positions instead of always rotating in one direction.

Position Accuracy and Lookup Tables

I am concerned about rounding errors and positional drift in a circular system. Can a lookup table be used to store absolute positions for every compartment-home combination?

Role of AI

AI was primarily used to explore ideas, challenge assumptions, verify calculations, and discuss implementation strategies. The final firmware architecture, lookup table generation, Hall sensor calibration, threshold tuning, testing, debugging, and validation were developed through iterative experimentation on the physical prototype.

In many cases, the most useful outcome was not code, but a clearer understanding of the problem itself and the different ways it could be approached.


Observations


Limitations


Future Improvements


Reflection

When I started, I thought the hard part would be getting a stepper motor to rotate a table. That part turned out to be relatively straightforward.

The real challenge was figuring out how the system should think.

Before I could move a compartment to a person, I first had to define what a compartment meant, what a home position meant, where zero was, how positions should be represented, and how the system could always find its way back to a known reference.

A lot of the development happened before the final hardware was even ready. I spent time sketching layouts, calculating positions, mapping relationships between users and compartments, and thinking through edge cases such as drift, repeatability, rounding errors, and direction changes.

What started as a motor-control task slowly became an exercise in building a coordinate system for a rotating object. Once that framework was in place, the code became much easier to write because every movement could be described as a relationship between a compartment, a user, and a known reference point.

Looking back, the most valuable part of this project was not making the table move. It was learning how much thought goes into making a system move predictably.

The final implementation is not simply a table that rotates to a location. It is a system that knows where it is, knows where it needs to go, and determines the most efficient way to get there.


Acknowledgements

Machine Week would not have been possible without the collective effort of the entire team and the continuous support of our instructors.

Unlike many of the individual assignments throughout Fab Academy, this project depended heavily on coordination, communication, and trust. Every major decision involved multiple disciplines, and the final outcome is a result of everyone continuously adapting their work to support one another.

Machine Week Team
The team behind the rotating table, countless discussions, troubleshooting sessions, and a significant percentage of the week's snack consumption.

My sincere thanks to Abhishek Shah for leading the mechanical design, Ardradevi for developing the electronics, Ali Abdul Gafoor for fabrication and assembly, and Ashtami P. S. for the Alexa integration and interaction workflow.

Thank you for the coordination, cooperation, hard work, patience, troubleshooting sessions, last-minute fixes, and countless discussions that helped bring this project together. Every subsystem depended on another, and the project succeeded because everyone remained willing to communicate, iterate, and support one another throughout the process.

I would also like to express my gratitude to our instructors and mentors: Jogin Francis, Sreyas George, Ashish Joy, and Saheen Palayi.

Thank you for patiently clearing our doubts, challenging our assumptions, helping us troubleshoot problems, reviewing our ideas, and standing beside us through the stressful moments when things were not working as expected. Machine Week can be overwhelming because every subsystem depends on another, and your guidance helped us stay focused, keep moving forward, and find solutions when we felt stuck.

While our responsibilities were different, the machine only worked when all of the pieces came together. This project was a reminder that building a successful machine is as much about teamwork, collaboration, and mutual support as it is about engineering.

Finally, a special thanks to all the snacks that participated in this project. Some were carefully positioned for testing, some were repeatedly relocated for calibration, and some unfortunately never made it past the experimentation phase. Their contribution to research, development, debugging, and team morale is deeply appreciated.



Source Files

  • Nema17 Test Code
  • Hall Sensor & Neopixel Test
  • Homing Logic
  • Clockwise Rotation Test
  • Clockwise Serial Debugging
  • Shortest Path Final Code
  • Alexa Integrated Version
  • Renamed Compartments Final Code
  • OTA Version