Skip to content

Final Design and Manufacturing

PCB Design, Milling, and Soldering

PCB Design:

The first step involved planning the connections and pin assignments by hand. This planning phase was crucial to justify all design decisions and ensure a well-organized layout. Previous knowledge gained during the electronics design and electronics production weeks were crucial for this step. The design considerations included:

  • Component Placement: Ensuring optimal placement for ease of soldering and functionality.
  • Power Distribution: Efficient power supply to all components.

I used headers for both the microcontroller and the driver, allowing easy removal and replacement if needed. I also chose to use two main headers for ribbon cables: one for the arc (sensor, lights, and phone) and another for the interface (OLED, potentiometer, and buttons). Additionally, a 3-pin header was added for a relay to control the shutter, a power jack connector to provide 12V for the driver, and a 4-pin header to connect the driver output to the stepper motor. Ribbon cables with ribbon cable connectors were selected for these connections.

Using KiCad, I created the schematic and then the PCB layout. Below are images of the schematic, the PCB layout, and a 3D render of the PCB.

PCB Milling:

To mill the PCB, I used Mods to create the toolpath from the SVG files. The Roland SRM-20 milling machine was used for this process. The setup involved:

  • Material Preparation: Securing the PCB material on the milling bed.
  • Toolpath Configuration: Setting the correct depth and speed for milling.
  • Milling Execution: Running the machine to precisely cut the traces and holes as per the design.

PCB Soldering:

Once the PCB was milled, I soldered all the components and headers. This involved:

  • Component Placement: Carefully placing each component according to the layout.
  • Soldering Technique: Using proper soldering techniques to ensure strong and reliable connections.
  • Testing: After soldering, I tested the PCB to ensure all connections were correct and the board functioned as expected.

This meticulous process of planning, designing, milling, and soldering ensured a robust and functional PCB for the 3D hand scanner project.


Final Design

Starting the Design Process:

The design process began in SketchUp, where I laid out the initial concept of the 3D hand scanner. This phase focused on visualizing the overall structure and basic component placement.

Refining in Fusion 360:

The design underwent refinement in Fusion 360, incorporating essential improvements based on prior testing and prototyping. Key enhancements included:

  1. Increased Distance Between Bearings: Strengthened shaft support to enhance stability.
  2. Enclosed Scanner Design: Controlled hand lighting and improved overall aesthetics.
  3. User Interface Addition: Integrated OLED screen, potentiometer, and buttons for user interaction.
  4. Easy Access to Phone: Ensured convenient access for phone adjustments and maintenance.
  5. Adjustable Phone Position: Enabled fine-tuning of phone position for optimal scanning.
  6. Sturdy Base and Structure: Engineered a robust base for comprehensive scanner support.
  7. Enclosed Electronics Area: Provided a dedicated, enclosed space for electronics to enhance reliability.

Moreover, the plywood frame serves as the structural foundation for the machine, facilitating secure attachment of all components. Each frame octagon (front and back) is crafted from two plywood layers, each divided into four sections. This design optimizes material utilization by minimizing waste through nesting similar octagonal sections.


CNC Machining the Frame

Machine Setup:

To begin, I set up the ShopBot CNC machine by securing the plywood boards onto the machine bed. Ensuring the material was firmly fixed was crucial to achieving precise cuts.

Manual Nesting and Material Optimization:

The parts were manually nested to ensure optimal material usage. This process involved strategically placing the pieces to be cut from one and a half boards of 18mm plywood, minimizing waste and maximizing efficiency.

Toolpath Generation:

Using VCarve, I created multiple toolpaths for the cutting process. The first toolpath was designed to cut the pockets for some joints, allowing for precise and secure assembly. The second toolpath was created to cut out the individual pieces from the plywood boards.

Cutting Process:

With the toolpaths generated, I proceeded with the cutting process. The CNC machine executed the toolpaths accurately, first creating the pockets and then cutting out the frame components. This method ensured all parts were cut to the exact specifications needed for the final assembly.

This meticulous approach to CNC machining ensured that the frame components were fabricated with precision and efficiency, laying a solid foundation for the assembly of the complete 3D hand scanner system.

Image Description
Image Description

Previous knowledge gained during the computer controlled machining week was crucial for this step.

Laser Cutting

Acrylic Front Covers:

The front cover was designed in two halves using Fusion 360. The bottom cover, which houses the interface components, is fixed using screws and screw caps. The top cover is removable, featuring two slots and magnets that attach to the screws and screw caps on the frame. This design allows easy access to the inside of the machine to connect and remove the phone. Additionally, two handles on the top half facilitate easy removal and placement.

The design was exported as a DXF file from Fusion 360 and imported into Rhino for laser cutting. Using the Trotec laser cutter, the front covers were cut from 3mm black acrylic.

Foam Board Side Covers:

Rectangles of 5mm foam board were also cut on the laser cutter. This ensured consistent and accurate side panels for the enclosure.

This process resulted in precisely cut front and side covers, enhancing the aesthetics and functionality of the 3D hand scanner.


3D Printing

Designing the Motor Mount and Phone Mount:

The motor mount and phone mount were designed in Fusion 360, ensuring precise dimensions and features to accommodate the components securely.

Design decisions for motor mount:

  • The motor mount includes slots for the screws instead of holes to allow for sliding of the mount to tension the belt easily.
  • The motor mount has a larger center hole to remove and insert the motor without the need of removing the pulley.
  • The motor mount has thicker and wider sides and edges to increase the strength, and allow for the screws to apply a significant clamping force.

Design decisions for phone mount:

  • The phone mount is designed to be smaller than the phone so that it can flex and clamp the phone securely.
  • the phone mount is designed with a cented hole to allow for a bolt and a fly nut to clamp the mount to the arc.

Slicing in Cura:

The designed models were then exported as STL files and imported into Cura for slicing. In Cura, the models were prepared with appropriate settings for layer height, infill density, and supports to ensure optimal print quality and strength.

Printing on Ultimaker S5:

The sliced models were printed on the Ultimaker S5 3D printer. The Ultimaker S5 provided high precision and reliability, resulting in sturdy and accurate parts essential for the final assembly of the 3D hand scanner.

The 3D-printed parts were crucial for the functionality and stability of the motor and phone mounts, enhancing the overall performance of the scanner.

Previous knowledge gained during the 3d printing week was crucial for this step.


Final Assembly and Integration

Component Assembly:

All the plywood pieces were assembled and connected using a friction fit of the finger joints, supplemented by wood screws for added stability. The bearings, shaft, pulleys, and motor mount were then installed.

Integration:

Next, the power supply and PCB were attached to the back of the frame in their designated locations. Cables were mounted and secured using cable clips to ensure neatness and organization. The electrical components were connected to their respective cables and wires.

Once the internal components were secured, the front and side panels were attached. Finally, the phone mount was added to complete the assembly.

This process resulted in a fully assembled and integrated 3D hand scanner, ready for final testing and programming.


Programming and Coding

Microcontroller Programming:

During the testing and prototyping stages, different codes were used to test specific components or aspects of the project. These smaller codes helped ensure each part functioned correctly before being integrated into the final system. The final code was created by combining and refining these smaller codes.

Arduino IDE:

The Arduino IDE was used to code and program the Xiao RP2040 microcontroller. This environment provided the tools needed for writing, compiling, and uploading the code to the controller.

Functional Testing:

Extensive testing and debugging were performed on the integrated system to ensure all components worked seamlessly together. Each functionality was verified, and adjustments were made as needed to optimize performance and reliability.

CODE

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_NeoPixel.h>

// Pin definitions
#define TRIG_PIN 4        // Ultrasonic sensor trigger pin
#define ECHO_PIN 1        // Ultrasonic sensor echo pin
#define LED_PIN 2         // Pin connected to the DIN of the WS2812B LED strip
#define DIR_PIN 27        // Stepper motor direction pin
#define STEP_PIN 26       // Stepper motor step pin
#define BUTTON_PIN 29     // Button pin to start scanning
#define POT_PIN 28        // Potentiometer pin
#define RELAY_PIN 3       // Relay pin for triggering external device
#define OLED_RESET -1     // OLED reset pin (-1 if not used)
#define SCREEN_WIDTH 128  // OLED screen width
#define SCREEN_HEIGHT 64  // OLED screen height

// LED strip configuration
#define NUMPIXELS 28      // Number of LEDs in the strip

// Initialize OLED and LED objects
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_NeoPixel strip(NUMPIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);

// Variables
bool scanning = false;
bool handDetected = false;
int stepsPerRevolution = 750;  // Number of steps for one full rotation
int rpm = 10;                  // Default RPM

// Function prototype for moveStepper
void moveStepper(int steps, int rpm, bool direction = HIGH);

void setup() {
  // Initialize serial communication
  Serial.begin(115200);

  // Initialize OLED display
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Loop forever if OLED initialization fails
  }
  display.display();
  delay(2000);
  display.clearDisplay();

  // Initialize pins
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLDOWN);
  pinMode(POT_PIN, INPUT);
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, HIGH); // Ensure relay is off initially (high for low-level trigger)

  // Initialize LED strip
  strip.begin();
  strip.show();  // Initialize all pixels to 'off'

  // Set initial LED color to red
  for (int i = 0; i < NUMPIXELS; i++) {
    strip.setPixelColor(i, strip.Color(150, 0, 0));  // Red
  }
  strip.show();
}

void loop() {
  // Read potentiometer value and map to RPM
  int potValue = analogRead(POT_PIN);
  rpm = map(potValue, 0, 1023, 2, 6); // Map to RPM range 2-6

  // Display RPM on OLED
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Speed: ");
  display.print(rpm);
  display.println(" RPM");
  display.display();

  // Measure distance with ultrasonic sensor
  long duration, distance;
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  duration = pulseIn(ECHO_PIN, HIGH);
  distance = duration * 0.034 / 2; // Convert to centimeters

  // Check if hand is detected
  if (distance >= 20 && distance <= 35) {
    if (!handDetected) {
      handDetected = true;

      // Change LED color to green
      for (int i = 0; i < NUMPIXELS; i++) {
        strip.setPixelColor(i, strip.Color(0, 150, 0));  // Green
      }
      strip.show();

      // Display message on OLED
      display.setCursor(0, 30);
      display.println("Ready to Scan");
      display.display();
    }
  } else {
    handDetected = false;
    // Change LED color back to red
    for (int i = 0; i < NUMPIXELS; i++) {
      strip.setPixelColor(i, strip.Color(150, 0, 0));  // Red
    }
    strip.show();

    // Clear OLED display
    display.clearDisplay();
    display.display();
  }

  // Read the state of the button
  int buttonState = digitalRead(BUTTON_PIN);

  // Check if the button is pressed to start scanning
  if (buttonState == HIGH && !scanning && handDetected) {
    scanning = true;

    // Trigger relay to start recording (low for low-level trigger)
    digitalWrite(RELAY_PIN, LOW);
    delay(200); // Relay on for 200ms
    digitalWrite(RELAY_PIN, HIGH);

    // Change LED color to white
    for (int i = 0; i < NUMPIXELS; i++) {
      strip.setPixelColor(i, strip.Color(255, 255, 255));  // White
    }
    strip.show();

    // Display scanning message on OLED
    display.setCursor(0, 30);
    display.println("Scanning...");
    display.display();

    // Move stepper motor 750 steps forward
    moveStepper(750, rpm, HIGH);

    delay(1000);

    // Move stepper motor 750 steps backward to return to starting position
    moveStepper(750, rpm, LOW);

    // Trigger relay to stop recording (low for low-level trigger)
    digitalWrite(RELAY_PIN, LOW);
    delay(200); // Relay on for 200ms
    digitalWrite(RELAY_PIN, HIGH);

    // Change LED color back to red
    for (int i = 0; i < NUMPIXELS; i++) {
      strip.setPixelColor(i, strip.Color(150, 0, 0));  // Red
    }
    strip.show();

    // Display ready message on OLED
    display.clearDisplay();
    display.setCursor(0, 30);
    display.println("Ready to Scan");
    display.display();

    scanning = false;
  }

  // Delay before the next loop
  delay(100);
}

// Function to move stepper motor
void moveStepper(int steps, int rpm, bool direction) {
  int stepDelay = 60000000L / (stepsPerRevolution * rpm); // Calculate delay based on RPM
  digitalWrite(DIR_PIN, direction); // Set direction

  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(stepDelay / 2);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(stepDelay / 2);
  }
}

Explanation:

Libraries and Pin Definitions:
  • Libraries: Includes necessary libraries (Wire.h, Adafruit_GFX.h, Adafruit_SSD1306.h, Adafruit_NeoPixel.h) for OLED display, NeoPixel LED strip, and communication protocols (I2C).
  • Pin Definitions: Defines pin assignments for various components:
    • Ultrasonic sensor (TRIG_PIN for trigger, ECHO_PIN for echo)
    • NeoPixel LED strip (LED_PIN)
    • Stepper motor (DIR_PIN for direction, STEP_PIN for step)
    • Button (BUTTON_PIN) for scanning initiation
    • Potentiometer (POT_PIN) for adjusting RPM
    • Relay (RELAY_PIN) for triggering external devices
    • OLED display reset (OLED_RESET)
Global Objects and Variables:
  • Global Objects:

    • display: Initializes an object for the SSD1306 OLED display.
    • strip: Initializes an object for the NeoPixel LED strip (NUMPIXELS LEDs).
  • Global Variables:

    • scanning: Boolean flag to track scanning state.
    • handDetected: Boolean flag indicating hand proximity detected by the ultrasonic sensor.
    • stepsPerRevolution: Defines the number of steps for a full revolution of the stepper motor.
    • rpm: Holds the current speed setting in revolutions per minute.
Setup Function:
  • Serial and Display Initialization:

    • Initializes serial communication at 115200 baud rate.
    • Initializes the OLED display (SSD1306_SWITCHCAPVCC mode, I2C address 0x3C). Displays a message if initialization fails and loops indefinitely.
    • Delays for 2 seconds to allow the display to stabilize and then clears it.
  • Pin Configuration:

    • Sets pin modes for:
    • Ultrasonic sensor (trigger as output, echo as input)
    • Stepper motor (direction and step pins as outputs)
    • Button (as input with internal pull-down resistor)
    • Potentiometer (as input)
    • Relay (as output, initially high to ensure it’s off)
  • LED Strip Initialization:

    • Begins the NeoPixel strip and turns off all LEDs (strip.show()). Initializes LEDs to red (150, 0, 0 RGB color).
Loop Function:
  • Potentiometer Reading and OLED Display:

    • Reads analog value from the potentiometer (POT_PIN) and maps it to adjust rpm (2-6 RPM). Displays the current RPM on the OLED screen with large font size.
  • Ultrasonic Sensor Measurement:

    • Measures distance using the ultrasonic sensor (TRIG_PIN and ECHO_PIN). Calculates distance in centimeters based on the duration of the echo pulse.
  • Hand Detection:

    • Updates handDetected based on the detected distance (20-35 cm). Changes LED strip color to green (0, 150, 0) and displays “Ready to Scan” on the OLED when a hand is detected.
  • Button State Check and Scanning Initiation:

    • Checks the state of the button (BUTTON_PIN). If pressed (HIGH), initiates scanning (scanning).
    • Triggers the relay (RELAY_PIN) to signal the start of recording (low-level trigger). Changes LED strip color to white (255, 255, 255) and displays “Scanning…” on the OLED.
  • Stepper Motor Control During Scanning:

    • Moves the stepper motor (DIR_PIN and STEP_PIN) 750 steps forward at the specified RPM (rpm).
    • After a delay, moves the stepper motor 750 steps backward to return to the starting position.
  • Relay Control to End Scanning:

    • Stops the relay to signal the end of recording. Changes LED strip color back to red (150, 0, 0) and updates OLED display to “Ready to Scan”.
  • Delay Between Loops:

    • Includes a delay of 100 milliseconds (delay(100)) to control the loop execution rate.
Utility Function:
  • moveStepper(int steps, int rpm, bool direction):
    • Function to control the stepper motor movement. Calculates step delay based on RPM and direction, then moves the motor accordingly.
Display Management:
  • OLED Display Updates:
    • Manages OLED display updates for different states (ready to scan, scanning). Clears the display as needed to update information.
Overall Functionality:
  • Integrates various components (OLED display, NeoPixel LEDs, ultrasonic sensor, stepper motor, button, and relay) to create a system:
  • Displays information on the OLED screen.
  • Detects hand presence using an ultrasonic sensor.
  • Controls a stepper motor based on user input and sensor feedback.
  • Triggers external devices via a relay for recording or signaling purposes.

Files: