FAB ACADEMY 2026

WEEK 12

Mechanical Design, Machine Design

Group assignment:

VIDEO

SLIDE

SLIDE

Individual Contribution:

My contribution started with searching for materials to develop the mechanical design. On my side, I first reviewed the base structure, which I developed in a 2D plan using the 3D reference modeled by my teammate Jennifer. For this, I used Corel Draw to break down the base and prepare the file for laser cutting. I also designed the string art canvases and finally carried out several tests to identify what needed to be modified.

Work in progress Week 11
  • Electronic & Programming

I first needed to understand the electronics of a stepper motor, so I researched its electronics and programming. Key parameters were good torque (1.2Nm), step angle (1.8°), and current (2A). Then I looked for a compatible driver, and the DM556 was the most recommended for these motors. I reviewed its datasheet and, with AI support, configured and made the connections on my shield for my ESP32. Once finished, I asked AI for a test code to rotate our stepper motor.

Work in progress Week 11
Work in progress Week 11

DIGITAL STEPPER DRIVER DM566

Work in progress Week 11

This test code was generated in Gemini according to the connections I made on my shield, and it allowed us to test the back-and-forth movement as an initial validation.

Code: STEPPER MOTOR TEST  Source: GEMINI AI

// Pins
#define STEP_PIN 2   // D2
#define DIR_PIN  4   // D4

void setup() {
  pinMode(STEP_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);

  digitalWrite(DIR_PIN, HIGH); // Initial direction
}

void loop() {

  // Rotate in one direction
  digitalWrite(DIR_PIN, HIGH);

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

  delay(1000);

  // Change direction
  digitalWrite(DIR_PIN, LOW);

  for (int i = 0; i < 2000; i++) {
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(500);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(500);
  }

  delay(2000);
}
    

In the video you can see the connections and the movement of the stepper motor. On the group page you can find the rest of the electronics and the final programming.

  • Testing & Assembly

I was able to assemble everything once each part was cut and 3D printed. A press-fit design and screw anchors were used on the weaving head, as well as for securing the motor and other required components. It was necessary to run several tests on the needle and thread tension; a lollipop stick worked better as a needle guide, along with using a 100-nail test for the initial canvas.


The final assembly included adding more elements such as an LCD screen, some clamps, and different supports for the canvas. In the end, we achieved a final AI-generated code to create a geometric canvas.

Code: STRING ART  Source: Gemini AI

#include
#include
#include

// ---------------- LCD ----------------
LiquidCrystal_I2C lcd(0x27, 16, 2);

// ---------------- PINS ----------------
const int stepPin = 14;
const int dirPin  = 27;
const int servoPin = 13;

// ---------------- SERVO ----------------
Servo aguja;

const int ANGULO_ARRIBA = 40;
const int ANGULO_ABAJO  = 5;

// ---------------- CONFIG ----------------
const float pasosPorVuelta = 9600.0;
const int totalClavos = 100;

const int offsetAngular = 3;
const int pasosOffset = 85;

// SECUENCIA OPTIMIZADA
int secuencia[] = {
  0,17,34,51,68,85,2,19,36,53,70,87,4,21,38,55,72,89,6,23,
  40,57,74,91,8,25,42,59,76,93,10,27,44,61,78,95,12,29,46,63,
  80,97,14,31,48,65,82,99,16,33,50,67,84,1,18,35,52,69,86,3,
  20,37,54,71,88,5,22,39,56,73,90,7,24,41,58,75,92,9,26,43,
  60,77,94,11,28,45,62,79,96,13,30,47,64,81,98,15,32,49,66,83
};

const int totalPasos = sizeof(secuencia) / sizeof(secuencia[0]);

// ---------------- VARIABLES ----------------
float posicionActual = 0.0;

unsigned long tiempoInicio = 0;
unsigned long ultimoLCD = 0;
unsigned long ultimoSerial = 0;

int progresoGlobal = 0;

// ---------------- LCD ----------------

void actualizarLCD() {

  if (millis() - ultimoLCD < 200) return;
  ultimoLCD = millis();

  unsigned long tiempoActual = millis() - tiempoInicio;

  int segundos = tiempoActual / 1000;
  int minutos = segundos / 60;
  segundos = segundos % 60;

  lcd.setCursor(0, 0);
  lcd.print("String Art 1.0");

  lcd.setCursor(0, 1);
  lcd.print("Prog: ");

  if (minutos < 10) lcd.print("0");
  lcd.print(minutos);
  lcd.print(":");
  if (segundos < 10) lcd.print("0");
  lcd.print(segundos);

  lcd.setCursor(12, 1);

  if (progresoGlobal < 100) lcd.print(" ");
  if (progresoGlobal < 10) lcd.print(" ");

  lcd.print(progresoGlobal);
  lcd.print("%");
}

// ---------------- SERIAL ----------------

void actualizarSerial(int i, int origen, int destino) {

  if (millis() - ultimoSerial < 300) return;
  ultimoSerial = millis();

  unsigned long tiempoActual = millis() - tiempoInicio;

  int segundos = tiempoActual / 1000;
  int minutos = segundos / 60;
  segundos = segundos % 60;

  Serial.print("[");
  if (minutos < 10) Serial.print("0");
  Serial.print(minutos);
  Serial.print(":");
  if (segundos < 10) Serial.print("0");
  Serial.print(segundos);
  Serial.print("] ");

  Serial.print("Paso ");
  Serial.print(i);
  Serial.print(" | ");

  Serial.print(origen);
  Serial.print(" -> ");
  Serial.print(destino);

  Serial.print(" | ");
  Serial.print(progresoGlobal);
  Serial.println("%");
}

// ---------------- MOTOR ----------------

void stepMotor(long pasos, bool dir) {
  digitalWrite(dirPin, dir);

  for (long i = 0; i < pasos; i++) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(500);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(500);
  }
}

void irAClavo(int clavo) {

  float pasosObjetivo = (pasosPorVuelta * clavo) / totalClavos;

  float delta = pasosObjetivo - posicionActual;
  bool sentido = (delta >= 0);

  if (sentido) pasosObjetivo += offsetAngular;
  else pasosObjetivo -= offsetAngular;

  delta = pasosObjetivo - posicionActual;

  stepMotor(abs((long)delta), sentido);

  posicionActual += (long)delta;
}

// ---------------- SERVO ----------------

void bajarAguja() {
  aguja.write(ANGULO_ABAJO);
  delay(200);
}

void subirAguja() {
  aguja.write(ANGULO_ARRIBA);
  delay(200);
}

void engancharHilo() {

  bajarAguja();
  stepMotor(pasosOffset, HIGH);
  subirAguja();
}

// ---------------- SETUP ----------------

void setup() {
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);

  aguja.attach(servoPin);
  subirAguja();

  lcd.init();
  lcd.backlight();

  Serial.begin(115200);
  Serial.println("=== INICIO STRING ART ===");

  lcd.setCursor(0, 0);
  lcd.print("String Art 1.0");
  lcd.setCursor(0, 1);
  lcd.print("Starting...");
  delay(2000);

  lcd.clear();

  tiempoInicio = millis();
}

// ---------------- LOOP ----------------

void loop() {

  for (int i = 0; i < totalPasos - 1; i++) {

    progresoGlobal = (i * 100) / totalPasos;

    int origen = secuencia[i];
    int destino = secuencia[i + 1];

    actualizarLCD();
    actualizarSerial(i, origen, destino);

    irAClavo(origen);
    engancharHilo();
  }

  // -------- FIN --------

  unsigned long tiempoTotal = millis() - tiempoInicio;

  int segundos = tiempoTotal / 1000;
  int minutos = segundos / 60;
  segundos = segundos % 60;

  lcd.clear();

  lcd.setCursor(0, 0);
  lcd.print("String Art 1.0");

  lcd.setCursor(0, 1);
  lcd.print("Finish! ");

  if (minutos < 10) lcd.print("0");
  lcd.print(minutos);
  lcd.print(":");
  if (segundos < 10) lcd.print("0");
  lcd.print(segundos);

  Serial.println("=== FINISHED ===");

  while (true) {
  }
}

    

For more detail about my individual process, you can review my group page where all the documentation of my mechanical part, electronics, and programming is available, as well as test videos and the final AI-generated code.

Conclusions: