Home


Fab academy

Week 10: Machine Design



Week 10: Machine Design

This week's assignment began with a collaborative brainstorming session to decide on the machine we would design and build, integrating mechanisms, actuation, automation, and application.

MachineIdeas

Among the ideas deliberated were CNC machines, each equipped with different toolheads. While initially drawn to the concept of a CNC with a spindle, we also considered alternative types of toolheads. Subsequently, the laser module emerged as a viable alternative. However, concerns regarding our constrained timeline prompted a shift in focus towards a CNC pyrographer.

The rationale behind selecting a pyrographer stemmed from its perceived manageability within the given timeframe. Moreover, we discerned a promising niche for such a device within the startups, particularly within the realm of small-scale ice cream and pastry enterprises. Envisioned as a tool to automate the branding process, our pyrographer would facilitate the efficient engraving of company logos onto spoons and popsicles, thereby alleviating the manual burden associated with this task.

Collaborative Braintorming:

BrainStorming
BrainStorming


First Iteration (Testing and Fitting Purposes):

V1
V1
V1
PyroV1



Final Iteration (Final Design for the X and Z Axis):

Final
Final
Final
Final
Final



Electronics:


For this step we decided to use a Arduino Uno Board with a CNC Shield V3. We used Drv8825 silent drivers for this task running at 32 microsteps with 12V supply. Also, we used a 12V Power Supply and a DC-DC Buck Converter to step down the voltage from 12V to 5V for the Arduino Uno. We used an SSR Relay to PWM the 220V Pyrographer for enhanced temperature control. All the electronics where place in a control box and wired properly.

For wiring the control box, pinout for the Arduino Uno R3 and the Cnc Shield V3 where needed.

CNC Shield V3 Pinout:

Electronics

Arduino Uno R3 CNC Shield V3 Pinout:

Electronics

Hall Sensor:

Electronics

RepRap Limit Switch:

Electronics

Solid State Relay:

Electronics

DC-DC Step Down Buck Converter:

Electronics

12V PSU:

Electronics



Machine Final Iteration Assembly Process:

1. Assembly of the Z-Axis

2. Manual X-Axis and Y-Axis

3. Rotational Axis and Z-Axis Integration

4. Tool Selector Integration

5. Cable Management

Assembly

Assembly

Assembly


Programming (Arduino IDE):


For this step, I decided to utilize Arduino Libraries to make the motor control much smoother. I used the AccelStepper library for smooth acceleration and deceleration of the stepper motor by sending pulses to the Step, Dir, and Enable pins of the stepper driver. I needed to read the AccelStepper documentation to know how to input the correct values for achieving the perfect speed and acceleration for this kind of machine.The final code was a blend of examples generated by ChatGPT and my own adjustments. ChatGPT proved to be very useful in offering examples and code for a variety of applications.

Funtionalities:

Stepper Motor Control: The code employs the AccelStepper library to manage two stepper motors: one for the R-axis (rotational movement) and one for the Z-axis (linear movement). This library facilitates seamless acceleration and deceleration, thereby refining the precision of motor control.

G-code Interpretation: The code processes G-code commands received via the serial monitor ('G1' for linear movement, 'G28' for homing, etc.), making the machine compatible with standard CNC control commands. This functionality expands the versatility of the machine, allowing users to execute complex engraving tasks with ease.

Stock Positioning: The code enables the selection of different stock positions for the rotary axis, represented by positions 1 to 6, using the 'T' command. This functionality allows users to conveniently switch between various engraving stages.

Current Position Monitoring: The code includes a command ('P') to provide real-time feedback on the Z-axis's current position in millimeters, enabling users to monitor the machine's state.

Run Command: The 'Run' command automates a sequence of engraving operations for six different positions using a predefined Z-axis position ('ENGRAVE_Z') and hop distance ('ZHOP'). This automation enhances the efficiency of the engraving process, streamlining workflow.

Safety Measures:

Limit Switches: Incorporating a limit switch (Z_LIMIT_SWITCH_PIN) on the Z-axis prevents the axis from surpassing its maximum travel limits. During the Z-axis homing procedure, this limit switch accurately determines the zero position.

Homing Functionality: Both the R-axis and the Z-axis feature homing procedures to ensure the machine initiates from a known position, thereby augmenting accuracy and repeatability.

Motor Disabling: Activating the 'M18' command disables the stepper motors by setting the ENABLE_PIN high, enhancing safety during periods of inactivity.

Error Handling: The code features error messages to manage scenarios such as unrecognized commands ('Error: Unrecognized command.') and attempts to execute actions without homing the respective axes ('Error: Z-axis not homed.', 'Error: R-axis not homed.'), promoting operational efficiency and user-friendliness.

Safety against Exceeding Maximum Axis Length: A safety feature has been implemented to prevent crashes caused by movement commands that exceed the maximum length of the axis. If a movement command sent via serial input exceeds the maximum length of the axis, the movement will not be executed, ensuring the safety of the machine and preventing potential crashes.



    //PyroEngraver Code//

    //Written by: Jorge Suarez de Freitas + ChatGPT//



    #include <AccelStepper.h>
    //R-AXIS PINS
    #define DIR_R_PIN 6
    #define STEP_R_PIN 3
    //Z-AXIS PINS
    #define DIR_Z_PIN 5
    #define STEP_Z_PIN 2

    //ENABLE STEPPERS PIN
    #define ENABLE_PIN 8

    //R-AXIS HALL MAGNETIC SENSOR PIN
    #define HALL_PIN 10

    //Z-AXIS LIMIT SWITCH
    #define Z_LIMIT_SWITCH_PIN 9 

    //STEPSxREV/(R-AXIS) & STEPSxMM/(Z-AXIS)
    #define STEPS_PER_REVOLUTION 6400 // 200 STEPS *32 MICROSTEPS
    #define STEPS_PER_MM_Z 800 // ( 200STEPS * 32 MICROSTEPS ) / 8MM PITCH T8 LEADSCREW

    //DEFINE ACCELSTEPPER OBJECTS
    AccelStepper stepperR(AccelStepper::DRIVER, STEP_R_PIN, DIR_R_PIN);
    AccelStepper stepperZ(AccelStepper::DRIVER, STEP_Z_PIN, DIR_Z_PIN);

    //HOMING SAFETY CHECK
    bool RAxisHomed = false; // FLAG TO INDICATE IF THE R AXIS IS HOMED
    bool ZAxisHomed = false; // FLAG TO INDICATE IF THE Z AXIS IS HOMED

    // SET LINEAR MOVE SPEED AND ACCELERATION FOR THE Z-AXIS & SET ROTATING SPEED AND ACCELERATION FOR THE R-AXIS
    int stepperZsetSpeed = 5000; // MAXIMUM SPEED IN STEPS PER SECOND
    int stepperZsetAcceleration = 10000; // ACCELERATION IN STEPS PER SECOND SQUARED
    int stepperRsetSpeed = 5000; 
    int stepperRsetAcceleration = 10000; 

    // SET HOMING SPEED AND ACCELERATION FOR THE Z-AXIS AND R-AXIS
    int stepperZsetHSpeed = 1700; // MAXIMUM SPEED IN STEPS PER SECOND
    int stepperZsetHAcceleration = 8000; // ACCELERATION IN STEPS PER SECOND SQUARED
    int stepperRsetHSpeed = 500; 
    int stepperRsetHAcceleration = 600; 

    //CURRENT R-AXIS POSITION NUMBER 
    int positionNumber = 0;

    //Z ENGRAVING POSITION
    int ENGRAVE_Z= 14;
    int ZHOP= 8;

    // MAXIMUN LIMITS FOR Z-AXIS SAFETY CHECK
    #define Z_MAX_LIMIT 35 // MAXIMUM LIMIT FOR THE Z-AXIS MOVEMENT


    void setup() {
      // SET CONTROL PINS AS OUTPUTS
      pinMode(DIR_Z_PIN, OUTPUT);
      pinMode(STEP_Z_PIN, OUTPUT);
      pinMode(ENABLE_PIN, OUTPUT);

      // SET HALL SENSOR DIGITAL PIN AS INPUT
      pinMode(HALL_PIN, INPUT);

      // SET LIMIT SWITCH PIN AS INPUT WITH PULL-UP
      pinMode(Z_LIMIT_SWITCH_PIN, INPUT_PULLUP);

      // DISABLE THE MOTORS AT STARTUP
      digitalWrite(ENABLE_PIN, HIGH);

      // SET MAXIMUM SPEED AND ACCELERATION FOR THE Z-AXIS AND R-AXIS
      stepperZ.setMaxSpeed(3000); // MAXIMUM SPEED IN STEPS PER SECOND
      stepperZ.setAcceleration(5000); // ACCELERATION IN STEPS PER SECOND SQUARED
      stepperR.setMaxSpeed(2000); 
      stepperR.setAcceleration(500); 

      //INITIALIZE SERIAL COMUNICATION
      Serial.begin(115200);
      while (!Serial) {} // Espera a que se conecte el puerto serie
    }


    //FUNTIONS

    void homeR() {
      // HOMING SPEED AND ACCELERATION
      stepperR.setMaxSpeed(stepperRsetHSpeed); 
      stepperR.setAcceleration(stepperRsetHAcceleration); 

      // CHECK IF THE SENSOR IS ALREADY ACTIVATED
      if (digitalRead(HALL_PIN) == LOW) {
        // MOVE AWAY FROM THE SENSOR UNTIL DEACTIVATES
        stepperR.moveTo(1000); 
        while (digitalRead(HALL_PIN) == LOW) {
          stepperR.run();
          delay(0.05); // SMALL DELAY TO ALLOW THE MOTOR TO MOVE
        }
      }

      // MOVE MOTOR TWARDS THE HOME POSITION (CLOCKWISE)
      stepperR.moveTo(1000); 

      // CONTINUE MOVING UNTIL THE MAGNETIC SENSOR IS TRIGGERED
      while (stepperR.distanceToGo() != 0) {
        stepperR.run();
        delay(0.05); // SMALL DELAY TO ALLOW THE MOTOR TO MOVE
        if (digitalRead(HALL_PIN) == LOW) {
          stepperR.setCurrentPosition(0); // SET CURRENT POSITION AS 0(HOME)
          stepperR.stop(); // STOP THE MOTOR
          break;
        }
      }

      // MOVE THE MOTOR FOR THE HALL SENSOR TO BE CENTERED WITH THE MAGNET
      stepperR.setMaxSpeed(stepperRsetHSpeed); 
      stepperR.setAcceleration(stepperRsetHAcceleration);
      stepperR.moveTo(140); 
      delay(100);
      stepperR.runToPosition(); // WAIT UNTIL THE MOTOR REACHES THE TARGET POSITION
      stepperR.setCurrentPosition(0); // SET THE CURRENT POSITION AS THE HOME POSITION

      // SET THE FLAG TO INDICATE THAT THE R AXIS IS HOMED
      RAxisHomed = true;
    }


    void selectTool(String input) {
      positionNumber = input.substring(1).toInt(); // EXTRACT THE POSITION NUMBER FROM THE INPUT

      // CALCULATE THE MOTOR STEP POSITION FOR THE SELECTED TOOL
      int toolPosition = map(positionNumber, 1, 7, 0, STEPS_PER_REVOLUTION);

      // CALCULATE THE CURRENT MOTOR POSITION
      int currentPosition = stepperR.currentPosition();

      // CALCULATE THE DISTANCE TO MOVE CLOCKWISE AND COUNTERCLOCKWISE
      int distanceClockwise = (toolPosition - currentPosition + STEPS_PER_REVOLUTION) % STEPS_PER_REVOLUTION;
      int distanceCounterclockwise = (currentPosition - toolPosition + STEPS_PER_REVOLUTION) % STEPS_PER_REVOLUTION;

      // DETERMINE THE DIRECTION TO MOVE THE MOTOR
      if (distanceClockwise <= distanceCounterclockwise) {
        stepperR.move(distanceClockwise);
      } else {
        stepperR.move(-distanceCounterclockwise);
      }
      stepperR.setMaxSpeed(stepperRsetSpeed); 
      stepperR.setAcceleration(stepperRsetAcceleration);

      // RUN THE MOTOR TO THE SELECTED POSITION
      stepperR.runToPosition();
    }


    void executeLinearMove(String input) {
      // PARSE THE G1 COMMAND TO EXTRACT THE TARGET POSITION FOR THE Z-AXIS MOVE
      double zPosition = 0.0;

      int index = input.indexOf('Z');
      if (index != -1) {
        String zValueString = input.substring(index + 1);
        zPosition = zValueString.toDouble();
      }

      // CHECK IF THE TARGET POSITION EXCEEDS THE LIMITS
      if (zPosition < 0 || zPosition > Z_MAX_LIMIT) {
        Serial.println("Error: Movement exceeds limits.");
        return;
      }

      // MOVE THE Z-AXIS TO THE SPECIFIED POSITION
      moveAxis('Z', zPosition, stepperZsetSpeed, stepperZsetAcceleration);
    }


    void moveAxis(char axis, double position_mm, float speed, float accel) {
      AccelStepper* stepper;
      double steps_per_mm;
      delay(100); // SMALL DELAY TO AVOID SERIAL COMMUNICATION ISSUES

      // DETERMINE WHICH MOTOR TO CONTROL AND ITS STEPS PER MILLIMETER
      if (axis == 'Z') {
        stepper = &stepperZ;
        steps_per_mm = STEPS_PER_MM_Z;
      } else {
        return; // EXIT THE METHOD IF THE AXIS LETTER IS INVALID
      }

      // Convert position from millimeters to steps
      double target_steps = position_mm * steps_per_mm;

      // Move the motor to the target position
      digitalWrite(ENABLE_PIN, LOW); // ENABLE STEPPERS
      delay(100); // SHORT DELAY FOR STABILITY

      // Set maximum speed and acceleration
      if (axis == 'Z') {
        stepper->setMaxSpeed(speed); 
        stepper->setAcceleration(accel);
      }

      // Move the motor to the target position
      stepper->moveTo(target_steps);

      // Wait for the motor to reach the target position
      while (stepper->distanceToGo() != 0) {
        stepper->run();
      }
    }


    void homeZ() {

        // Set acceleration for homing
      stepperZ.setAcceleration(stepperZsetAcceleration); // ACCELERATION IN STEPS PER SECOND SQUARED

      digitalWrite(ENABLE_PIN, LOW); // Enable the motor
      delay(100); // Short delay for stability

      // PERFORM HOMING TOWARDS THE MINIMUM LIMIT UNTIL THE LIMIT SWITCH IS ACTIVATED
      while (digitalRead(Z_LIMIT_SWITCH_PIN)) {
        stepperZ.move(-100); // MOVE TOWARDS THE MINIMUM LIMIT
        stepperZ.setSpeed(-stepperZsetSpeed); // MAX SPEED IN STEPS PER SECOND
        stepperZ.setAcceleration(-stepperZsetAcceleration); // ACCELERATION IN STEPS PER SECOND SQUARED
        stepperZ.runSpeed(); // EXECUTE THE MOVEMENT
        delay(0.05); // SMALL DELAY TO ALLOW THE MOTOR TO MOVE
      }

      // STOP THE MOTOR AND SET IT TO THE MINIMUM LIMIT POSITION
      stepperZ.stop();

    // MOVE AWAY FROM THE LIMITSWITCH
      stepperZ.move(800); // Move an additional 20 steps
      stepperZ.runToPosition(); // Wait until the motor reaches the target position

      stepperZ.setCurrentPosition(0);

      // UPDATE HOMING STATUS
      ZAxisHomed = true;
    }


    void loop() {
      // INPUT G-CODE WITH SERIAL MONITOR FOR COMMUNICATION
      if (Serial.available() > 0) {
        String input = Serial.readStringUntil('\n');
        input.trim(); // REMOVE LEADING AND TRAILING WHITESPACE
        processCommand(input);


      }
    }

    void processCommand(String input) {
      // Print the received command for debugging
      Serial.print("Received command: ");
      Serial.println(input);

      // Execute corresponding G-code command
      if (input.startsWith("G28")) {
        // HOME Z-AXIS
        homeZ();
        homeR();
        Serial.println("ok"); // Acknowledge successful execution
      } else if (input.startsWith("G1")) {
        // Execute linear movement on the Z-axis if Z-axis is homed
        if (ZAxisHomed) {
          executeLinearMove(input);
          Serial.println("ok"); // Acknowledge successful execution
        } else {
          Serial.println("Error: Z-axis not homed.");
        }
      } else if (input.startsWith("T")) {
        // Select a tool if the R axis is homed
        int toolPosition = input.substring(1).toInt();
        if (toolPosition >= 1 && toolPosition <= 6) {
          if (RAxisHomed) {
            selectTool(input);
            Serial.println("ok"); // Acknowledge successful execution
          } else {
            Serial.println("Error: R-axis not homed.");
          }
        } else {
          Serial.println("Error: Choose an existing position (1-6)");
        }
      } else if (input.startsWith("M18")) {
        // Disable steppers
        digitalWrite(ENABLE_PIN, HIGH);
        ZAxisHomed = false;
        RAxisHomed = false;
        Serial.println("ok"); // Acknowledge successful execution
      } else if (input.startsWith("P")) {
        // Output current position in millimeters
        double position_mm = stepperZ.currentPosition() / (double)STEPS_PER_MM_Z;
        Serial.print("Current position Z-Axis (mm): ");
        Serial.println(position_mm, 2);
        Serial.print("Current stock position R-Axis: ");
        Serial.println(positionNumber);
        Serial.println("ok"); // Acknowledge successful execution
      } else if (input.startsWith("Run")) {
        // Make 1 Run for Engrave in 6 positions
        homeZ();
        homeR();
        selectTool("T1");
        moveAxis('Z', ENGRAVE_Z, stepperZsetSpeed, stepperZsetAcceleration);
        moveAxis('Z', (ENGRAVE_Z-ZHOP), stepperZsetSpeed, stepperZsetAcceleration);
        selectTool("T2");
        moveAxis('Z', ENGRAVE_Z, stepperZsetSpeed, stepperZsetAcceleration);
        moveAxis('Z', (ENGRAVE_Z-ZHOP), stepperZsetSpeed, stepperZsetAcceleration);
        selectTool("T3");
        moveAxis('Z', ENGRAVE_Z, stepperZsetSpeed, stepperZsetAcceleration);
        moveAxis('Z', (ENGRAVE_Z-ZHOP), stepperZsetSpeed, stepperZsetAcceleration);
        selectTool("T4");
        moveAxis('Z', ENGRAVE_Z, stepperZsetSpeed, stepperZsetAcceleration);
        moveAxis('Z', (ENGRAVE_Z-ZHOP), stepperZsetSpeed, stepperZsetAcceleration);
        selectTool("T5");
        moveAxis('Z', ENGRAVE_Z, stepperZsetSpeed, stepperZsetAcceleration);
        moveAxis('Z', (ENGRAVE_Z-ZHOP), stepperZsetSpeed, stepperZsetAcceleration);
        selectTool("T6");
        moveAxis('Z', ENGRAVE_Z, stepperZsetSpeed, stepperZsetAcceleration);
        moveAxis('Z', (2), stepperZsetSpeed, stepperZsetAcceleration);
        digitalWrite(ENABLE_PIN, HIGH);


      } else {
        // Handle unrecognized commands
        Serial.println("Error: Unrecognized command.");
      }
    }


        
Pyro



Group Assigment Page!!