Saltar al contenido
Volver a la ficha del proyecto

Manual del proyecto · Proyecto 11

Brazo Robótico

Diseño, construcción y programación de un brazo robótico de 6 grados de libertad (seis servomotores) que simula operaciones de procesos productivos: primero se conectan y calibran los seis servos y se ensambla el brazo del kit para automatizar un movimiento simple; luego se suman seis potenciómetros que funcionan como un mando a distancia para controlar cada articulación; por último se programa una secuencia automática de pick-and-place que traslada objetos de una cinta transportadora a otra.

Arduino UNO R3 ESP32 DevKit
Dificultad:
Nivel educativo:
Secundario. Ciclo Básico.
Mecánica Electrónica Programación Fabricación digital

Saberes Digitales

INET / EDUCAR — Ministerio de Educación, Cultura, Ciencia y Tecnología de la Nación Argentina

2019

Contenido del manual

  1. 01 Materiales y equipamiento
  2. 02 Conectar, calibrar y armar el brazo de 6 servomotores Nivel inicial
  3. 03 Comandar el brazo a distancia con seis potenciómetros Nivel intermedio
  4. 04 Automatizar una secuencia de pick-and-place Nivel avanzado

Materiales y equipamiento

Insumos

Imagen Insumo Cantidad Plataforma
Foto de Servomotor Servomotores estándar (de 3 cables) Son las seis articulaciones del brazo (base, hombro, codo, muñeca, rotación de la pinza y apertura/cierre de la pinza). Vienen incluidos en el kit del brazo. Cada uno tiene tres cables: señal (naranja/amarillo), VCC (rojo) y GND (marrón/negro). En el UNO se controlan con la librería Servo; en el ESP32, con ESP32Servo. 6 Todas
IO Expansion Shield DFRobot (V7.1) Se apila sobre el Arduino UNO y agrupa, para cada pin, una fila de señal + VCC + GND: así los seis servos se enchufan sin cablear la alimentación uno por uno. Está pensado para el formato del UNO: NO encaja en un ESP32, donde los servos se cablean directo a los GPIO. 1 Arduino UNO R3
Foto de Potenciómetro Potenciómetros Desde el nivel intermedio: cada uno comanda una articulación, como un mando a distancia. Se conectan sus extremos a VCC y GND y la pata central (cursor) a una entrada analógica de la placa. 6 Todas
Foto de Fuente de alimentación Fuente de alimentación externa de 5V 2A Obligatoria. Seis servos consumen demasiada corriente para tomarla del puerto USB: se alimentan desde esta fuente, con GND común a la placa. Alimentar por USB puede resetear la placa o dañar el regulador. 1 Todas
Foto de Protoboard Protoboard de 830 puntos Desde el nivel intermedio: sobre ella se montan y cablean los seis potenciómetros del mando. 1 Todas
Foto de Cables dupont macho-macho Cable dupont macho-hembra 20 Todas
Filamento para impresora 3D (PLA) Para imprimir las piezas de soporte que estabilizan la base del brazo y la carcasa donde se montan los potenciómetros del mando. 1 Todas
Foto de Arduino UNO R3 Arduino UNO R3 Placa de control primaria del proyecto, junto con el IO Shield de DFRobot. 1 Arduino UNO R3
Foto de Cable USB tipo B Cable USB tipo B 1 Arduino UNO R3
Foto de Placa ESP32 DevKit Placa ESP32 DevKit v1 Variante sin shield: los seis servos se cablean directo a los GPIO y se manejan con ESP32Servo. Su conversor analógico (ADC) es de 12 bits (0 a 4095), no de 10 bits (0 a 1023) como el UNO, lo que cambia la cuenta del mapeo de los potenciómetros. 1 ESP32 DevKit
Foto de Cable micro USB Cable micro USB (con línea de datos) 1 ESP32 DevKit

Equipamiento del taller

  • Computadora
  • Kit Brazo Robótico (DFRobot)
  • Destornilladores Phillips
  • Llave francesa
  • Soldador
  • Estaño
  • Alicate
  • Pinza de punta
  • Pinza brusela

Plano de conexionado en protoboard

Arduino UNO Conexionado en protoboard · Brazo robótico: 6 servos (uno por articulación) + 6 potenciómetros de control.

Deslizá horizontalmente para ver el plano completo

Arduino UNO
Servo 1 (base)
SG90
VCCseñalGND
Servo 2 (hombro)
SG90
VCCseñalGND
Servo 3 (codo)
SG90
VCCseñalGND
Servo 4 (muñeca)
SG90
VCCseñalGND
Servo 5 (rotación pinza)
SG90
VCCseñalGND
Servo 6 (pinza)
SG90
VCCseñalGND
Pot. 1
AWB
Pot. 2
AWB
Pot. 3
AWB
Pot. 4
AWB
Pot. 5
AWB
Pot. 6
AWB
ESP32 DevKit v1 Conexionado en protoboard · Brazo robótico: 6 servos (uno por articulación) + 6 potenciómetros de control.

Deslizá horizontalmente para ver el plano completo

ESP32 DevKit v1
Servo 1 (base)
SG90
VCCseñalGND
Servo 2 (hombro)
SG90
VCCseñalGND
Servo 3 (codo)
SG90
VCCseñalGND
Servo 4 (muñeca)
SG90
VCCseñalGND
Servo 5 (rotación pinza)
SG90
VCCseñalGND
Servo 6 (pinza)
SG90
VCCseñalGND
Pot. 1
AWB
Pot. 2
AWB
Pot. 3
AWB
Pot. 4
AWB
Pot. 5
AWB
Pot. 6
AWB
Arduino UNOConexionado en protoboard
Conexiones · Arduino UNO. Convención de colores: alimentación de los 6 servos: fuente externa 5 V / 2 A = rojo, GND común con la placa = negro; señal de cada servo = azul; potenciómetros (extremos a los buses de la protoboard) = rojo/negro, cursor de cada uno = amarillo.
Desde Hacia Color de cable
Arduino UNO · 5VBus + de la protoboardrojo
Arduino UNO · GNDBus − de la protoboardnegro
Fuente externa 5V/2A (+)Bus + de servos (independiente)rojo
Fuente externa 5V/2A (−)Bus − de la protoboardnegro
Servo 1 · VCCBus + de servos (independiente)rojo
Servo 1 · GNDBus − de la protoboardnegro
Servo 1 · señalArduino UNO · 2azul
Servo 2 · VCCBus + de servos (independiente)rojo
Servo 2 · GNDBus − de la protoboardnegro
Servo 2 · señalArduino UNO · 3azul
Servo 3 · VCCBus + de servos (independiente)rojo
Servo 3 · GNDBus − de la protoboardnegro
Servo 3 · señalArduino UNO · 4azul
Servo 4 · VCCBus + de servos (independiente)rojo
Servo 4 · GNDBus − de la protoboardnegro
Servo 4 · señalArduino UNO · 5azul
Servo 5 · VCCBus + de servos (independiente)rojo
Servo 5 · GNDBus − de la protoboardnegro
Servo 5 · señalArduino UNO · 6azul
Servo 6 · VCCBus + de servos (independiente)rojo
Servo 6 · GNDBus − de la protoboardnegro
Servo 6 · señalArduino UNO · 7azul
Potenciómetro 1 · extremo ABus + de la protoboardrojo
Potenciómetro 1 · extremo BBus − de la protoboardnegro
Potenciómetro 1 · cursorArduino UNO · A0amarillo
Potenciómetro 2 · extremo ABus + de la protoboardrojo
Potenciómetro 2 · extremo BBus − de la protoboardnegro
Potenciómetro 2 · cursorArduino UNO · A1amarillo
Potenciómetro 3 · extremo ABus + de la protoboardrojo
Potenciómetro 3 · extremo BBus − de la protoboardnegro
Potenciómetro 3 · cursorArduino UNO · A2amarillo
Potenciómetro 4 · extremo ABus + de la protoboardrojo
Potenciómetro 4 · extremo BBus − de la protoboardnegro
Potenciómetro 4 · cursorArduino UNO · A3amarillo
Potenciómetro 5 · extremo ABus + de la protoboardrojo
Potenciómetro 5 · extremo BBus − de la protoboardnegro
Potenciómetro 5 · cursorArduino UNO · A4amarillo
Potenciómetro 6 · extremo ABus + de la protoboardrojo
Potenciómetro 6 · extremo BBus − de la protoboardnegro
Potenciómetro 6 · cursorArduino UNO · A5amarillo
ESP32 DevKit v1Conexionado en protoboard
Conexiones · ESP32 DevKit v1. Convención de colores: alimentación de los 6 servos: fuente externa 5 V / 2 A = rojo, GND común con la placa = negro; señal de cada servo = azul; potenciómetros (extremos a los buses de la protoboard) = rojo/negro, cursor de cada uno = amarillo.
Desde Hacia Color de cable
ESP32 DevKit v1 · 3V3Bus + de la protoboardrojo
ESP32 DevKit v1 · GNDBus − de la protoboardnegro
Fuente externa 5V/2A (+)Bus + de servos (independiente)rojo
Fuente externa 5V/2A (−)Bus − de la protoboardnegro
Servo 1 · VCCBus + de servos (independiente)rojo
Servo 1 · GNDBus − de la protoboardnegro
Servo 1 · señalESP32 DevKit v1 · GPIO13azul
Servo 2 · VCCBus + de servos (independiente)rojo
Servo 2 · GNDBus − de la protoboardnegro
Servo 2 · señalESP32 DevKit v1 · GPIO14azul
Servo 3 · VCCBus + de servos (independiente)rojo
Servo 3 · GNDBus − de la protoboardnegro
Servo 3 · señalESP32 DevKit v1 · GPIO25azul
Servo 4 · VCCBus + de servos (independiente)rojo
Servo 4 · GNDBus − de la protoboardnegro
Servo 4 · señalESP32 DevKit v1 · GPIO26azul
Servo 5 · VCCBus + de servos (independiente)rojo
Servo 5 · GNDBus − de la protoboardnegro
Servo 5 · señalESP32 DevKit v1 · GPIO27azul
Servo 6 · VCCBus + de servos (independiente)rojo
Servo 6 · GNDBus − de la protoboardnegro
Servo 6 · señalESP32 DevKit v1 · GPIO32azul
Potenciómetro 1 · extremo ABus + de la protoboardrojo
Potenciómetro 1 · extremo BBus − de la protoboardnegro
Potenciómetro 1 · cursorESP32 DevKit v1 · GPIO36amarillo
Potenciómetro 2 · extremo ABus + de la protoboardrojo
Potenciómetro 2 · extremo BBus − de la protoboardnegro
Potenciómetro 2 · cursorESP32 DevKit v1 · GPIO39amarillo
Potenciómetro 3 · extremo ABus + de la protoboardrojo
Potenciómetro 3 · extremo BBus − de la protoboardnegro
Potenciómetro 3 · cursorESP32 DevKit v1 · GPIO34amarillo
Potenciómetro 4 · extremo ABus + de la protoboardrojo
Potenciómetro 4 · extremo BBus − de la protoboardnegro
Potenciómetro 4 · cursorESP32 DevKit v1 · GPIO35amarillo
Potenciómetro 5 · extremo ABus + de la protoboardrojo
Potenciómetro 5 · extremo BBus − de la protoboardnegro
Potenciómetro 5 · cursorESP32 DevKit v1 · GPIO33amarillo
Potenciómetro 6 · extremo ABus + de la protoboardrojo
Potenciómetro 6 · extremo BBus − de la protoboardnegro
Potenciómetro 6 · cursorESP32 DevKit v1 · GPIO4amarillo
Nivel inicial

Conectar, calibrar y armar el brazo de 6 servomotores

Conectar los seis servomotores

El brazo tiene 6 motores (servomotores) que le permiten rotar y trasladarse en el espacio imitando el movimiento de un brazo humano. Cada servo mueve una articulación —base, hombro, codo, muñeca, rotación de la pinza y apertura/cierre de la pinza—, y juntos le dan al brazo sus 6 grados de libertad. Cada servo tiene tres cables: el naranja o amarillo es la señal (va al pin de datos), el rojo es el positivo (VCC) y el marrón o negro es el negativo (GND).

Esquema de conexión: seis servomotores rotulados SERVO en fila, cada uno con tres cables, conectados a una placa Arduino con el IO Expansion Shield de DFRobot. A la izquierda, una fuente externa rotulada Fuente externa 5 Volt 2 Amp se conecta a la entrada de alimentación de servos del shield.
Conexión de los seis servomotores al IO Shield de DFRobot con la fuente externa de 5 V 2 A (figura original del proyecto). El shield agrupa, para cada pin, la señal (D), el positivo (VCC) y el negativo (GND).

En el Arduino UNO usamos el IO Expansion Shield de DFRobot, que se apila sobre la placa. Para cada pin ofrece una fila de tres contactos —señal, VCC y GND—, así que el servo se enchufa completo sin cablear la alimentación aparte. Conectamos los seis servos a los pines 2, 3, 4, 5, 6 y 7 (servo 1 = pin 2 … servo 6 = pin 7). El shield tiene dos grupos de 5 pines de un lado y dos grupos de 6 del otro: hay un solo modo de encastrarlo sin forzar las piezas.

El IO Shield de DFRobot está hecho para el formato del Arduino UNO y no encaja en un ESP32. Por eso, en esta placa no se usa shield: cada servo se cablea directo, con la señal a un GPIO y la alimentación a la fuente externa (con GND común). Conectamos los seis servos a los GPIO 13, 14, 25, 26, 27 y 32 (servo 1 = GPIO 13 … servo 6 = GPIO 32). En el ESP32 los servos se manejan con la librería ESP32Servo (la Servo.h clásica del UNO no funciona en esta placa).

Como el consumo de corriente de seis servos es alto, el sistema necesita una fuente de alimentación externa de 5V 2A. No la tomamos del USB: seis servos moviéndose a la vez piden más corriente de la que da el puerto, y podrían resetear la placa o dañar su regulador. La fuente comparte GND con la placa.

Programar los ángulos de los servomotores (calibración)

Antes de armar el brazo hay que calibrar los servos: llevarlos a todos a 90°, el centro de su recorrido (que va de 0° a 180°). Con los seis en 90°, el brazo queda en una posición neutra desde la que puede girar por igual hacia ambos lados; si armáramos el brazo con los servos en una posición cualquiera, quedaría torcido y con menos margen de movimiento.

La programación la hacemos directamente en el lenguaje de Arduino (C++), con el Arduino IDE 2.x, que se descarga gratis de arduino.cc/en/software. Un programa de Arduino se llama sketch y tiene dos bloques: setup(), que se ejecuta una sola vez al encender, y loop(), que se repite después. La calibración vive en setup(): el programa engancha cada servo a su pin y le ordena write(90). Para no repetir seis veces lo mismo, guardamos los pines en un arreglo y recorremos los seis con un for. En este nivel el loop() queda vacío a propósito: la calibración es estática, los servos solo tienen que sostener los 90°.

Subir el código a la placa

Para subir el programa, conectá la placa a la computadora, elegí en el Arduino IDE 2.x la placa y el puerto (en el IDE 2.x se eligen juntos en el desplegable de la barra superior) y apretá el botón Subir (la flecha →). Al terminar, aparece el mensaje de carga completada. Con la fuente externa conectada, los seis servomotores deberían moverse a su nueva posición de 90°.

Para el Arduino UNO: usá el cable USB tipo B, elegí la placa Arduino UNO y el puerto que se agregó al conectarla. La librería Servo ya viene incluida con el IDE. Si tu placa DFRobot no es reconocida, quizás necesites el driver del conversor CH340 (era un requisito del material original).

Para el ESP32 hay dos pasos que se hacen una sola vez: instalar el soporte de placas ESP32 desde el gestor de placas (buscá “esp32” e instalá el paquete de Espressif) y, desde el Gestor de bibliotecas, la librería ESP32Servo. Si el puerto no aparece, instalá el driver del conversor USB-serie (CP210x o CH340). Elegí la placa ESP32 Dev Module y el puerto. Si al subir queda en “Connecting…”, mantené apretado el botón BOOT.

Armar el brazo robótico

Con los servos calibrados a 90°, recién ahora ensamblamos el brazo del kit sobre ellos: cada servo queda en el centro de su recorrido, así que las piezas mecánicas se montan derechas y el brazo puede moverse el mismo ángulo hacia un lado y hacia el otro. El material original acompaña el armado con un video paso a paso.

Si no contás con una mesa o superficie donde fijar la base del brazo, conviene imprimir en 3D unas piezas de soporte y lastrarlas con material de descarte (por ejemplo, metal) para darle estabilidad: un brazo que se mueve con fuerza necesita una base firme para no volcarse. Estas piezas forman parte de los paquetes de fabricación del proyecto, que se descargan desde su ficha.

Programar un movimiento sincronizado de los servomotores

Con el brazo armado, vemos su primer movimiento sincronizado: el mismo programa de calibración que subimos es el que produce ese movimiento. Cuando la placa arranca o se resetea, los seis servos se mueven a 90° al mismo tiempo, desde donde estuvieran hasta el centro. Ese salto coordinado de las seis articulaciones es la primera vez que el brazo se mueve como un conjunto: no seis motores sueltos, sino un cuerpo articulado que adopta una pose común.

Ver el código completo · brazo-calibracion.ino
brazo-calibracion.ino Arduino UNO
// Brazo robotico — Nivel Inicial (Arduino UNO).
//
// CALIBRACION: los 6 servomotores se llevan a 90 grados (posicion central).
// Es el punto de partida obligatorio del armado: con todos los servos a 90 el
// brazo puede girar en ambos sentidos por igual, tal como pide el Paso 1 de la
// guia ("asegurarnos de que la posicion de los servomotores sea de 90").
//
// Plataforma: Arduino UNO con IO Shield de DFRobot (el shield expone los pines
// de servo con alimentacion; no cambia el numero de pin). Proyecto de BAJA
// TENSION (servos 5 V): no hay 220 V ni agua.
//
// ATENCION — ALIMENTACION: seis servos consumen demasiada corriente para el
// puerto USB. Es OBLIGATORIO usar la FUENTE EXTERNA 5V 2A con GND comun a la
// placa. Alimentar por USB puede resetear el Arduino o danar el regulador.
//
// ATENCION — MECANICA: el brazo tiene 6 grados de libertad y al energizarse
// salta a 90 grados de golpe. Despejar la zona de barrido antes de conectar.
//
// Nivel inicial: se permite delay() (fiel a la progresion original).
//
// Basado en: Fragmento de codigo del Nivel Inicial, Paso 2 — Brazo Robotico,
// Saberes Digitales (INET/EDUCAR 2019).
// Desvios respecto del original (ver README):
//  (1) mBlock3 -> C++ limpio: se elimina el cruft del generador de bloques
//      (angle_rad/angle_deg, _delay/_loop) y los nombres servo_2..servo_7.
//  (2) Los seis servos se manejan con un arreglo indexado por constante, sin
//      numeros magicos en el cuerpo del programa.

#if defined(ESP32)
  #include <ESP32Servo.h>
#else
  #include <Servo.h>
#endif

// --- Servomotores (contrato UNO: pines digitales 2..7) ---
// Indice 0 = servo 1 (base) ... indice 5 = servo 6 (pinza).
const int CANT_SERVOS = 6;
const int PINES_SERVO[CANT_SERVOS] = {2, 3, 4, 5, 6, 7};

// Angulo de calibracion: centro del recorrido 0..180.
const int ANGULO_CENTRO = 90;

Servo servos[CANT_SERVOS];

void setup() {
  // Enganchar cada servo a su pin y llevarlo al centro. Al terminar, el brazo
  // queda calibrado y listo para el armado mecanico.
  for (int i = 0; i < CANT_SERVOS; i++) {
    servos[i].attach(PINES_SERVO[i]);
    servos[i].write(ANGULO_CENTRO);
  }
}

void loop() {
  // Calibracion estatica: no hay movimiento. Los seis servos sostienen los 90
  // grados; el loop queda vacio a proposito.
}
brazo-calibracion.ino ESP32
// Brazo robotico — Nivel Inicial (ESP32 DevKit v1).
//
// CALIBRACION: los 6 servomotores se llevan a 90 grados (posicion central),
// punto de partida del armado (Paso 1: "asegurarnos de que la posicion de los
// servomotores sea de 90"), para que el brazo pueda girar por igual en ambos
// sentidos.
//
// Plataforma: ESP32 (DevKit v1). Se usa la libreria ESP32Servo (la Servo.h
// clasica NO funciona en ESP32). El IO Shield de DFRobot es para UNO: en ESP32
// NO se usa shield, los servos se cablean directo a los GPIO (senal al GPIO,
// alimentacion a la fuente externa, GND comun).
//
// ATENCION — ALIMENTACION: seis servos NO se alimentan por USB. Es OBLIGATORIA
// la FUENTE EXTERNA 5V 2A con GND comun. El ESP32 trabaja a 3,3 V pero el pin
// de senal del servo tolera ese nivel; la potencia sale de la fuente de 5 V.
//
// ATENCION — MECANICA: 6 grados de libertad; al energizar, los servos saltan a
// 90 grados. Despejar la zona de barrido.
//
// Nivel inicial: se permite delay() (fiel a la progresion original).
//
// Basado en: Fragmento de codigo del Nivel Inicial, Paso 2 — Brazo Robotico,
// Saberes Digitales (INET/EDUCAR 2019).
// Desvios (ver README): mBlock3 -> C++ limpio (sin angle_rad/deg, sin
// _delay/_loop, sin servo_2..servo_7); GPIO seguros del contrato; sin shield.

#if defined(ESP32)
  #include <ESP32Servo.h>
#else
  #include <Servo.h>
#endif

// --- Servomotores (contrato ESP32: GPIO 13,14,25,26,27,32 con ESP32Servo) ---
// Indice 0 = servo 1 (base) ... indice 5 = servo 6 (pinza).
const int CANT_SERVOS = 6;
const int PINES_SERVO[CANT_SERVOS] = {13, 14, 25, 26, 27, 32};

const int ANGULO_CENTRO = 90;

Servo servos[CANT_SERVOS];

void setup() {
  for (int i = 0; i < CANT_SERVOS; i++) {
    servos[i].attach(PINES_SERVO[i]);
    servos[i].write(ANGULO_CENTRO);
  }
}

void loop() {
  // Calibracion estatica: los seis servos sostienen los 90 grados.
}
Calibración del brazo: en setup() los seis servos se enganchan a su pin y se llevan a 90° con un bucle. Lo único que cambia entre plataformas son los pines (2..7 en el UNO; GPIO 13..32 en el ESP32) y la librería del servo (Servo / ESP32Servo).

Podés ver cómo las seis articulaciones se acomodan a 90° en el simulador, antes de energizar el brazo real:

Simulación del comportamiento

La misma máquina de estados del firmware, portada a TypeScript, maneja el comportamiento del dispositivo. No se ejecuta el binario real: la ejecución del .hex con avr8js está en el roadmap.

Simulación lista. Presioná «Iniciar» para arrancar la secuencia.

Cierre del nivel

Al terminar este nivel, tu brazo robótico ya existe físicamente: los seis servos están conectados y alimentados con la fuente externa, calibrados a 90° y con las piezas del kit ensambladas sobre ellos. Subiste el programa desde el Arduino IDE 2.x y viste a las seis articulaciones moverse de forma sincronizada. Pero por ahora el brazo hace un único gesto fijo. En el nivel intermedio vamos a darle un mando a distancia: seis potenciómetros con los que vas a mover cada articulación con la mano, como los controles de una grúa.

Evidencia de cierre

Al terminar este nivel, esto tiene que estar funcionando:

  • Los seis servomotores están conectados al IO Shield de DFRobot (o directo a los GPIO en el ESP32) y alimentados con la fuente externa de 5V 2A, con GND común a la placa.
  • Subiste desde el Arduino IDE 2.x el programa de calibración: al energizar, los seis servos se mueven a 90° de forma sincronizada y sostienen esa posición.
  • El brazo del kit está ensamblado sobre los servos ya calibrados a 90°, de modo que puede girar por igual hacia ambos lados desde el centro.
  • Entendés que seis servos necesitan la fuente externa (no alcanza el USB) y que, antes de energizar, hay que despejar la zona de movimiento del brazo.
Nivel intermedio

Comandar el brazo a distancia con seis potenciómetros

Armar el circuito con los potenciómetros

Si queremos controlar el brazo a distancia, podemos usar un potenciómetro para fijar la posición de cada servomotor: girás la perilla y el servo sigue ese ángulo. Vamos a montarlos sobre una protoboard. Empezamos por tres potenciómetros, para los tres servos con los que ya venimos trabajando (base, codo y pinza); más adelante sumamos los otros tres.

Cada potenciómetro se conecta con sus dos extremos a VCC y GND y la pata central (el cursor) a una entrada analógica de la placa. En esa pata central aparece una tensión que representa la posición de la perilla, y eso es lo que la placa lee.

Esquema de conexión: una placa Arduino con el IO Shield de DFRobot y seis servomotores arriba; abajo, una protoboard con tres potenciómetros cuyos cursores se conectan a las entradas analógicas de la placa, y una fuente externa de 5 V 2 A.
Los tres primeros potenciómetros montados en la protoboard y cableados a las entradas analógicas (figura original del proyecto). Cada cursor lleva la posición de su perilla a un pin analógico.

Las entradas analógicas de la placa se diferencian de las digitales en cuánto pueden leer. Una entrada digital solo distingue dos estados —alto (1) o bajo (0)— según si la tensión está más cerca de 5 V o de 0 V. Una entrada analógica, en cambio, mide la tensión con una escala de muchos valores intermedios: por eso sirve para leer la posición de un potenciómetro, que no es “sí o no” sino “cuánto”.

En el Arduino UNO las entradas analógicas son A0 a A5 (seis en total, justo las que necesitamos). El primer potenciómetro va a A0, y así hasta A5. Cada uno controla el servo del mismo índice: A0 → servo del pin 2, A1 → servo del pin 3, y así. El UNO lee estas entradas con una resolución de 10 bits: un número de 0 a 1023.

En el ESP32 las entradas analógicas son varios GPIO con ADC. Acá hay un detalle: el ESP32 tiene seis entradas cómodas en su ADC1 (GPIO 32, 33, 34, 35, 36 y 39), pero el GPIO 32 ya lo usamos como salida de un servo. Como este proyecto no usa WiFi, podemos leer el sexto potenciómetro por el ADC2 (GPIO 4), que queda libre. Así, los seis potes van a GPIO 36, 39, 34, 35, 33 y 4. Además, los potenciómetros del ESP32 se alimentan a 3,3 V (no a 5 V): su ADC mide de 0 a 3,3 V, y con 5 V se satura la lectura y se puede dañar el pin.

Crear las variables

Hasta ahora el ángulo de cada servo era un valor fijo (90°). A partir de acá, cada ángulo depende de la posición de su potenciómetro. Para trabajar con ese dato que cambia, lo guardamos en una variable: un espacio de memoria con un nombre, donde el programa anota un valor para usarlo más adelante.

En el código vas a ver un arreglo (PINES_POT) con los seis pines de los potenciómetros y, dentro del loop(), una variable lectura donde se guarda lo que devuelve cada uno. Un detalle de los nombres en código: no pueden empezar con un número, ni tener espacios, ni llevar tilde o Ñ (por eso escribimos angulo, no ángulo).

Adaptar los valores (regla de tres)

Hay un desajuste de escalas: el servo se mueve entre 0° y 180°, pero la lectura del potenciómetro entrega otra escala. Necesitamos convertir una en otra con una regla de tres: multiplicar la lectura por 180 y dividir por el máximo que puede leer el ADC.

En el UNO el ADC lee de 0 a 1023, así que la cuenta es ángulo = lectura × 180 ÷ 1023. Es exactamente la fórmula del material original. En el código se hace en la función lecturaAAngulo(), que usa un tipo long para que el producto lectura × 180 no se desborde antes de dividir.

Acá aparece un desvío importante respecto del original. El ESP32 lee con 12 bits: de 0 a 4095, no de 0 a 1023 como el UNO. Si dividiéramos por 1023 (como el original, pensado solo para UNO), el potenciómetro recorrería apenas un cuarto del ángulo. Por eso la cuenta es ángulo = lectura × 180 ÷ 4095: usamos el máximo real del ADC de esta placa para que la perilla cubra los 180° completos.

Programar los servomotores restantes

Con un potenciómetro funcionando, el resto es repetir el mismo patrón para los demás: leer su pin, convertir con la regla de tres y escribir el ángulo en su servo. En lugar de copiar y pegar seis bloques casi iguales, el programa recorre los seis con un for: en cada vuelta lee PINES_POT[i], lo convierte y lo escribe en servos[i]. Menos repetición, menos errores.

Algo importante del arranque: en setup() todos los servos se centran a 90° antes de tomar el mando de su potenciómetro, para que el brazo parta de una pose conocida y no de un ángulo cualquiera.

Controlar los seis servomotores

Para comandar el brazo completo sumamos los otros tres potenciómetros, hasta llegar a seis: uno por articulación. El programa final es el mismo patrón para los seis: en cada vuelta del loop() lee los seis potenciómetros y refleja cada uno en su servo. Una pequeña espera de refresco suaviza el movimiento.

Ver el código completo · brazo-potenciometros.ino
brazo-potenciometros.ino Arduino UNO
// Brazo robotico — Nivel Intermedio (Arduino UNO).
//
// MANDO A DISTANCIA: seis potenciometros comandan los seis servomotores, uno
// por articulacion. Cada potenciometro actua como perilla de posicion: al
// girarlo, el servo correspondiente sigue su angulo. Es el "control remoto"
// que pide la situacion-problema (manipular sustancias peligrosas sin contacto).
//
// Plataforma: Arduino UNO con IO Shield de DFRobot. Las entradas analogicas
// A0..A5 leen los potenciometros; los pines 2..7 mandan los servos.
//
// ATENCION — ALIMENTACION: FUENTE EXTERNA 5V 2A obligatoria (6 servos no van
// por USB), GND comun. ATENCION — MECANICA: 6 grados de libertad; al mover los
// potenciometros el brazo puede golpear. Despejar la zona.
//
// Adaptacion de escala (regla de tres del Paso 3): el ADC del UNO entrega
//   0..1023 (10 bits) y el servo acepta 0..180 grados. Angulo = lectura*180/1023.
//
// Nivel intermedio: se permite delay() para dar una cadencia de refresco suave.
//
// Basado en: Fragmentos de codigo del Nivel Intermedio, Pasos 3 a 5 — Brazo
// Robotico, Saberes Digitales (INET/EDUCAR 2019).
// Desvios (ver README):
//  (1) mBlock3 -> C++ limpio (sin angle_rad/deg, sin _delay/_loop, sin
//      servo_2..servo_7): arreglos indexados y una funcion de mapeo reutilizable.
//  (2) El original arrancaba tres potenciometros y despues sumaba tres mas; aca
//      se entregan los SEIS desde el inicio (es el estado final del Paso 5).

#if defined(ESP32)
  #include <ESP32Servo.h>
#else
  #include <Servo.h>
#endif

// --- Servomotores (contrato UNO: pines digitales 2..7) ---
const int CANT_SERVOS = 6;
const int PINES_SERVO[CANT_SERVOS] = {2, 3, 4, 5, 6, 7};

// --- Potenciometros (contrato UNO: entradas analogicas A0..A5) ---
// PIN_POT[i] controla PINES_SERVO[i] (misma articulacion).
const int PINES_POT[CANT_SERVOS] = {A0, A1, A2, A3, A4, A5};

// Escala del ADC del UNO: 10 bits -> valor maximo 1023.
const long ADC_MAX = 1023;
const int ANGULO_MAX = 180;

// Cadencia de refresco (ms). Suaviza la lectura sin bloquear percepcion.
const unsigned long REFRESCO_MS = 15;

Servo servos[CANT_SERVOS];

// Convierte una lectura del ADC (0..ADC_MAX) al angulo del servo (0..180)
// con la regla de tres del original. Se usa long para no desbordar en el
// producto lectura*180 antes de dividir.
int lecturaAAngulo(int lectura) {
  return (int)(((long)lectura * ANGULO_MAX) / ADC_MAX);
}

void setup() {
  for (int i = 0; i < CANT_SERVOS; i++) {
    pinMode(PINES_POT[i], INPUT);
    servos[i].attach(PINES_SERVO[i]);
    // Arranque seguro: centrar cada servo antes de tomar el mando del pote.
    servos[i].write(90);
  }
}

void loop() {
  // Leer los seis potenciometros y reflejar cada uno en su servo.
  for (int i = 0; i < CANT_SERVOS; i++) {
    int lectura = analogRead(PINES_POT[i]);
    servos[i].write(lecturaAAngulo(lectura));
  }
  delay(REFRESCO_MS);
}
brazo-potenciometros.ino ESP32
// Brazo robotico — Nivel Intermedio (ESP32 DevKit v1).
//
// MANDO A DISTANCIA: seis potenciometros comandan los seis servomotores, uno
// por articulacion (el "control remoto" de la situacion-problema).
//
// Plataforma: ESP32 (DevKit v1), servos con ESP32Servo, SIN shield (cableado
// directo). El mapa de pines del ESP32 es AJUSTADO: 6 servos + 6 entradas
// analogicas es lo maximo que entra sin colisiones (ver README, tabla ESP32).
//
// Mapa resuelto SIN COLISIONES:
//   Servos  -> GPIO 13, 14, 25, 26, 27, 32
//   Pots    -> GPIO 36, 39, 34, 35 (ADC1 solo-entrada) + GPIO 33 (ADC1) + GPIO 4
//   * GPIO 32 es SERVO, no pote: no puede ser las dos cosas. Por eso el pote que
//     el contrato ubicaba en 32 se reubica a GPIO 4.
//   * GPIO 4 es ADC2. El ADC2 solo entra en conflicto cuando el WiFi esta
//     activo; este proyecto NO tiene IoT/WiFi, asi que GPIO 4 lee sin problemas.
//
// ATENCION — ALIMENTACION: FUENTE EXTERNA 5V 2A obligatoria (6 servos), GND
// comun. Los potenciometros se alimentan a 3,3 V (no a 5 V): el ADC del ESP32
// mide 0..3,3 V; con 5 V se satura la lectura y se dana el pin.
// ATENCION — MECANICA: 6 grados de libertad; al mover los potes el brazo puede
// golpear. Despejar la zona.
//
// Adaptacion de escala: el ADC del ESP32 entrega 0..4095 (12 bits, contra los
// 10 bits / 0..1023 del UNO). Angulo = lectura*180/4095.
//
// Nivel intermedio: se permite delay() para una cadencia de refresco suave.
//
// Basado en: Fragmentos de codigo del Nivel Intermedio, Pasos 3 a 5 — Brazo
// Robotico, Saberes Digitales (INET/EDUCAR 2019).
// Desvios (ver README): mBlock3 -> C++ limpio; GPIO seguros; ADC de 12 bits
// (÷4095 en vez de ÷1023); reubicacion del pote 6 a GPIO 4 para evitar colision
// con el servo 6 (GPIO 32); los seis potes desde el inicio (estado final Paso 5).

#if defined(ESP32)
  #include <ESP32Servo.h>
#else
  #include <Servo.h>
#endif

// --- Servomotores (contrato ESP32) ---
const int CANT_SERVOS = 6;
const int PINES_SERVO[CANT_SERVOS] = {13, 14, 25, 26, 27, 32};

// --- Potenciometros (mapa ESP32 resuelto sin colisiones) ---
// PINES_POT[i] controla PINES_SERVO[i].
//   36,39,34,35 = ADC1 solo-entrada | 33 = ADC1 | 4 = ADC2 (libre sin WiFi)
const int PINES_POT[CANT_SERVOS] = {36, 39, 34, 35, 33, 4};

// Escala del ADC del ESP32: 12 bits -> valor maximo 4095.
const long ADC_MAX = 4095;
const int ANGULO_MAX = 180;

const unsigned long REFRESCO_MS = 15;

Servo servos[CANT_SERVOS];

// Regla de tres: lectura del ADC (0..ADC_MAX) -> angulo del servo (0..180).
// long evita el desborde del producto lectura*180 antes de dividir.
int lecturaAAngulo(int lectura) {
  return (int)(((long)lectura * ANGULO_MAX) / ADC_MAX);
}

void setup() {
  for (int i = 0; i < CANT_SERVOS; i++) {
    pinMode(PINES_POT[i], INPUT);
    servos[i].attach(PINES_SERVO[i]);
    servos[i].write(90);  // arranque seguro: centrar antes de dar el mando
  }
}

void loop() {
  for (int i = 0; i < CANT_SERVOS; i++) {
    int lectura = analogRead(PINES_POT[i]);
    servos[i].write(lecturaAAngulo(lectura));
  }
  delay(REFRESCO_MS);
}
Mando a distancia: los seis potenciómetros comandan los seis servos con un bucle (leer, convertir con la regla de tres, escribir). Entre plataformas cambian los pines y el máximo del ADC del mapeo (÷1023 en el UNO, ÷4095 en el ESP32).

Podés probar cómo cada potenciómetro mueve su articulación en el simulador:

Simulación del comportamiento

La misma máquina de estados del firmware, portada a TypeScript, maneja el comportamiento del dispositivo. No se ejecuta el binario real: la ejecución del .hex con avr8js está en el roadmap.

Simulación lista. Presioná «Iniciar» para arrancar la secuencia.

Para operar el mando con comodidad conviene pasar los potenciómetros de la protoboard a una carcasa definitiva, donde queden montados y firmes. Con un cableado más largo, incluso, se puede comandar el brazo desde lejos. La carcasa se imprime en 3D y sus archivos forman parte de los paquetes de fabricación del proyecto.

Render 3D de una carcasa roja en forma de L con seis potenciómetros montados (tres en la cara vertical y tres en la horizontal) y, a la derecha, una placa Arduino con su shield apoyada sobre la misma pieza.
Carcasa impresa en 3D para el mando a distancia (figura original del proyecto): los seis potenciómetros quedan montados y ordenados, uno por articulación del brazo.

Cierre del nivel

Al terminar este nivel, el brazo dejó de tener una pose fija: lo comandás a voluntad con seis potenciómetros, uno por articulación, como un mando a distancia. En el camino aprendiste la diferencia entre entradas digitales y analógicas, a guardar lecturas en variables y a mapear la escala del ADC a los grados del servo (atendiendo a que el UNO y el ESP32 leen con distinta resolución). Pero mover el brazo con las manos no automatiza nada: cada movimiento lo hacés vos. En el nivel avanzado vamos a quitar los potenciómetros y programar al brazo para que ejecute solo una secuencia completa de tomar y colocar objetos.

Evidencia de cierre

Al terminar este nivel, esto tiene que estar funcionando:

  • Sobre la protoboard están cableados los seis potenciómetros (extremos a VCC y GND, cursor a una entrada analógica) y cada uno comanda una articulación del brazo.
  • El programa lee cada potenciómetro con analogRead() y convierte esa lectura al ángulo del servo con una regla de tres, contemplando la resolución del ADC de la placa (÷1023 en el UNO, ÷4095 en el ESP32).
  • Al girar cada potenciómetro, el servo correspondiente sigue su posición: el brazo se mueve como con un mando a distancia.
  • Entendés la diferencia entre una entrada digital (dos estados) y una analógica (una escala de valores) y por qué el potenciómetro necesita una entrada analógica.
Nivel avanzado

Automatizar una secuencia de pick-and-place

Armar el circuito (sin potenciómetros)

En este nivel el brazo se mueve de forma automática, así que no usamos los potenciómetros: el movimiento ya no lo decide una persona girando perillas, sino el programa. El circuito vuelve a ser el del nivel inicial: los seis servomotores conectados y alimentados con la fuente externa de 5V 2A, con GND común.

En el Arduino UNO, los seis servos siguen en los pines 2 a 7 a través del IO Shield de DFRobot. Simplemente desconectamos los potenciómetros de las entradas analógicas: ya no hacen falta.

En el ESP32, los seis servos siguen cableados directo a los GPIO 13, 14, 25, 26, 27 y 32 y se manejan con ESP32Servo. Al no usar potenciómetros, los GPIO analógicos del nivel intermedio quedan libres.

Programar la secuencia de movimiento

Vamos a programar al brazo para que traslade objetos de una cinta a otra, ubicada a menor altura y a 90° de la primera. Es una operación de pick-and-place (tomar y colocar): la base de la automatización industrial.

Ilustración de un brazo robótico de seis articulaciones montado entre dos cintas transportadoras: una superior de la que la pinza toma un objeto y otra inferior, dispuesta a 90°, donde lo deposita.
El brazo trasladando objetos entre dos cintas transportadoras (figura original del proyecto): toma de la cinta superior y deposita en la inferior, ubicada a 90° y a menor altura.

La secuencia, paso a paso: partir con todo a 90°; girar la base hacia la cinta de origen y bajar la muñeca con la pinza abierta; cerrar la pinza para tomar el objeto; levantar; girar 90° hacia la cinta de destino; acomodar el codo y el hombro sobre esa cinta y descender; abrir la pinza para soltar el objeto; y volver todo a 90° para reiniciar el ciclo.

En vez de escribir una a una las órdenes a cada servo (como el original), esta reedición declara la coreografía como una tabla de poses. Una pose es el estado completo del brazo: los seis ángulos de las articulaciones más el tiempo que se sostiene antes de pasar a la siguiente. El programa recorre la tabla pose por pose. Así, cambiar el movimiento es editar una fila de números, no reescribir el código.

El avance entre poses no es bloqueante: en lugar de delay(), que congela todo el programa mientras cuenta (código bloqueante), usamos millis() —un cronómetro que se consulta sin frenar nada— dentro de una máquina de estados. En cada vuelta del loop(), el programa se pregunta si ya pasó el tiempo de la pose actual; si sí, avanza a la siguiente. El loop() nunca se cuelga.

Ver el código completo · brazo-secuencia.ino
brazo-secuencia.ino Arduino UNO
// Brazo robotico — Nivel Avanzado (Arduino UNO).
//
// SECUENCIA PICK-AND-PLACE: el brazo repite un ciclo automatico que simula
// tomar un objeto de una cinta transportadora y depositarlo en otra ubicada a
// 90 grados y a menor altura (control de calidad de la situacion-problema).
// Sin potenciometros: el movimiento es programado.
//
// Articulaciones (indice del arreglo):
//   0 = servo 1 (base, pin 2)     3 = servo 4 (pin 5)
//   1 = servo 2 (pin 3)           4 = servo 5 (pin 6)
//   2 = servo 3 (pin 4)           5 = servo 6 (PINZA, pin 7)
//
// Plataforma: Arduino UNO con IO Shield DFRobot.
// ATENCION — ALIMENTACION: FUENTE EXTERNA 5V 2A obligatoria, GND comun.
// ATENCION — MECANICA: 6 grados de libertad en movimiento AUTONOMO y continuo;
// el brazo barre un area amplia y puede golpear. Despejar la zona y tener a
// mano el corte de alimentacion antes de energizar.
//
// Nivel avanzado: NO-BLOQUEANTE. En vez de encadenar delay() (como el original
// mBlock3, que usaba _delay), la secuencia avanza con una maquina de estados
// basada en millis(). Asi el loop() nunca se congela y el patron queda listo
// para sumarle, en el futuro, sensores o paradas de emergencia sin reescribir.
//
// Basado en: Fragmento de codigo del Nivel Avanzado, Paso 2 — Brazo Robotico,
// Saberes Digitales (INET/EDUCAR 2019). Se conservan los angulos y el orden de
// movimientos del original.
// Desvios (ver README):
//  (1) mBlock3 -> C++ limpio (sin angle_rad/deg, sin _delay/_loop, sin
//      servo_2..servo_7).
//  (2) delay()/_delay() bloqueante -> maquina de estados con millis() (estandar
//      del nivel avanzado de la coleccion).
//  (3) La secuencia se declara como tabla de POSES (angulo de las 6
//      articulaciones + tiempo de espera), en vez de escrituras sueltas: mismo
//      resultado, mucho mas legible y facil de reordenar (extension sugerida en
//      el cierre del original).

#if defined(ESP32)
  #include <ESP32Servo.h>
#else
  #include <Servo.h>
#endif

// --- Servomotores (contrato UNO: pines digitales 2..7) ---
const int CANT_SERVOS = 6;
const int PINES_SERVO[CANT_SERVOS] = {2, 3, 4, 5, 6, 7};

Servo servos[CANT_SERVOS];

// Una POSE es el estado completo del brazo: el angulo de cada una de las 6
// articulaciones mas cuanto tiempo se sostiene antes de pasar a la siguiente.
struct Pose {
  int angulos[CANT_SERVOS];
  unsigned long espera_ms;
};

// Secuencia pick-and-place. La pose 0 es el HOME de arranque (todos a 90) y se
// ejecuta una sola vez; el ciclo productivo son las poses 1..9, que se repiten
// para siempre. Los angulos y tiempos son los del listado original del Paso 2.
const Pose SECUENCIA[] = {
  // {base, s2,  s3,  s4,  s5,  pinza}          espera
  {{ 90,  90,  90,  90,  90,  90}, 2000},  // 0 HOME: todos a 90, arranque estable
  {{  0,  90,  90, 130,  90,  90}, 1000},  // 1 girar la base hacia la cinta de origen
  {{  0,  90,  90,  90,  90,   0}, 1000},  // 2 bajar la muneca y abrir la pinza
  {{  0,  90,  90,  90,  90, 180}, 1000},  // 3 cerrar la pinza: toma el objeto
  {{  0,  90,  90, 130,  90, 180}, 1000},  // 4 levantar la muneca con el objeto
  {{180,  90,  90, 130, 180, 180}, 1000},  // 5 girar 180 hacia la cinta de destino
  {{180,  50,  40, 130, 180, 180}, 1000},  // 6 acomodar codo/hombro sobre el destino
  {{180,  50,  40,  90, 180, 180}, 1000},  // 7 descender a la cinta de destino
  {{180,  50,  40,  90, 180,   0}, 1000},  // 8 abrir la pinza: suelta el objeto
  {{ 90,  90,  90,  90,  90,   0}, 1000},  // 9 volver el cuerpo a 90 y reiniciar ciclo
};
const int CANT_POSES = sizeof(SECUENCIA) / sizeof(SECUENCIA[0]);

int poseActual = 0;
unsigned long marcaTiempo = 0;

// Escribe en los seis servos los angulos de la pose indicada.
void aplicarPose(int idx) {
  for (int i = 0; i < CANT_SERVOS; i++) {
    servos[i].write(SECUENCIA[idx].angulos[i]);
  }
}

void setup() {
  for (int i = 0; i < CANT_SERVOS; i++) {
    servos[i].attach(PINES_SERVO[i]);
  }
  // Arrancar en HOME y anotar el instante de inicio.
  poseActual = 0;
  aplicarPose(poseActual);
  marcaTiempo = millis();
}

void loop() {
  unsigned long ahora = millis();
  // Cuando se cumplio la espera de la pose actual, avanzar a la siguiente.
  if (ahora - marcaTiempo >= SECUENCIA[poseActual].espera_ms) {
    marcaTiempo = ahora;
    poseActual++;
    // Al terminar el ciclo, volver a la pose 1 (no se repite el HOME de arranque).
    if (poseActual >= CANT_POSES) {
      poseActual = 1;
    }
    aplicarPose(poseActual);
  }
}
brazo-secuencia.ino ESP32
// Brazo robotico — Nivel Avanzado (ESP32 DevKit v1).
//
// SECUENCIA PICK-AND-PLACE: el brazo repite un ciclo automatico que simula
// tomar un objeto de una cinta y depositarlo en otra a 90 grados y a menor
// altura (control de calidad de la situacion-problema). Sin potenciometros.
//
// Articulaciones (indice del arreglo):
//   0 = servo 1 (base, GPIO 13)   3 = servo 4 (GPIO 26)
//   1 = servo 2 (GPIO 14)         4 = servo 5 (GPIO 27)
//   2 = servo 3 (GPIO 25)         5 = servo 6 (PINZA, GPIO 32)
//
// Plataforma: ESP32 (DevKit v1), servos con ESP32Servo, SIN shield (cableado
// directo).
// ATENCION — ALIMENTACION: FUENTE EXTERNA 5V 2A obligatoria, GND comun.
// ATENCION — MECANICA: 6 grados de libertad en movimiento AUTONOMO y continuo;
// el brazo barre un area amplia. Despejar la zona y tener a mano el corte de
// alimentacion antes de energizar.
//
// Nivel avanzado: NO-BLOQUEANTE. La secuencia avanza con una maquina de estados
// basada en millis() (no con delay()/_delay encadenados como el original
// mBlock3), asi el loop() nunca se congela.
//
// Basado en: Fragmento de codigo del Nivel Avanzado, Paso 2 — Brazo Robotico,
// Saberes Digitales (INET/EDUCAR 2019). Se conservan los angulos y el orden de
// movimientos del original.
// Desvios (ver README): mBlock3 -> C++ limpio; GPIO seguros; delay bloqueante
// -> maquina de estados con millis(); secuencia declarada como tabla de POSES.

#if defined(ESP32)
  #include <ESP32Servo.h>
#else
  #include <Servo.h>
#endif

// --- Servomotores (contrato ESP32) ---
const int CANT_SERVOS = 6;
const int PINES_SERVO[CANT_SERVOS] = {13, 14, 25, 26, 27, 32};

Servo servos[CANT_SERVOS];

// Una POSE es el estado completo del brazo: angulo de las 6 articulaciones mas
// el tiempo que se sostiene antes de pasar a la siguiente.
struct Pose {
  int angulos[CANT_SERVOS];
  unsigned long espera_ms;
};

// Secuencia pick-and-place. Pose 0 = HOME de arranque (una sola vez); las poses
// 1..9 son el ciclo productivo que se repite. Angulos y tiempos del original.
const Pose SECUENCIA[] = {
  // {base, s2,  s3,  s4,  s5,  pinza}          espera
  {{ 90,  90,  90,  90,  90,  90}, 2000},  // 0 HOME: todos a 90, arranque estable
  {{  0,  90,  90, 130,  90,  90}, 1000},  // 1 girar la base hacia la cinta de origen
  {{  0,  90,  90,  90,  90,   0}, 1000},  // 2 bajar la muneca y abrir la pinza
  {{  0,  90,  90,  90,  90, 180}, 1000},  // 3 cerrar la pinza: toma el objeto
  {{  0,  90,  90, 130,  90, 180}, 1000},  // 4 levantar la muneca con el objeto
  {{180,  90,  90, 130, 180, 180}, 1000},  // 5 girar 180 hacia la cinta de destino
  {{180,  50,  40, 130, 180, 180}, 1000},  // 6 acomodar codo/hombro sobre el destino
  {{180,  50,  40,  90, 180, 180}, 1000},  // 7 descender a la cinta de destino
  {{180,  50,  40,  90, 180,   0}, 1000},  // 8 abrir la pinza: suelta el objeto
  {{ 90,  90,  90,  90,  90,   0}, 1000},  // 9 volver el cuerpo a 90 y reiniciar ciclo
};
const int CANT_POSES = sizeof(SECUENCIA) / sizeof(SECUENCIA[0]);

int poseActual = 0;
unsigned long marcaTiempo = 0;

void aplicarPose(int idx) {
  for (int i = 0; i < CANT_SERVOS; i++) {
    servos[i].write(SECUENCIA[idx].angulos[i]);
  }
}

void setup() {
  for (int i = 0; i < CANT_SERVOS; i++) {
    servos[i].attach(PINES_SERVO[i]);
  }
  poseActual = 0;
  aplicarPose(poseActual);
  marcaTiempo = millis();
}

void loop() {
  unsigned long ahora = millis();
  if (ahora - marcaTiempo >= SECUENCIA[poseActual].espera_ms) {
    marcaTiempo = ahora;
    poseActual++;
    if (poseActual >= CANT_POSES) {
      poseActual = 1;  // no se repite el HOME de arranque
    }
    aplicarPose(poseActual);
  }
}
Secuencia pick-and-place: la coreografía es una tabla de poses (seis ángulos + tiempo) que una máquina de estados con millis() recorre sin bloquear el loop(). Entre plataformas solo cambian los pines y la librería del servo.

Podés ver el ciclo completo —tomar, girar, colocar y volver— en el simulador:

Simulación del comportamiento

La misma máquina de estados del firmware, portada a TypeScript, maneja el comportamiento del dispositivo. No se ejecuta el binario real: la ejecución del .hex con avr8js está en el roadmap.

Simulación lista. Presioná «Iniciar» para arrancar la secuencia.

Cierre del nivel

Al terminar este nivel, el brazo robótico quedó automatizado: sin nadie que lo comande, repite una secuencia completa de tomar un objeto de una cinta y depositarlo en otra. Programaste esa coreografía como una tabla de poses y la recorriste con una máquina de estados basada en millis(), sin código bloqueante, de modo que el loop() queda libre para futuras mejoras. Con esto cerrás el recorrido del proyecto: de calibrar seis servos y armar el brazo, a comandarlo a distancia, a que trabaje solo. Si el grupo quiere seguir, en la ficha están las extensiones sugeridas: cambiar la trayectoria, sumar un sensor de color para seleccionar objetos, o comandar el brazo de forma inalámbrica por bluetooth o WiFi.

Evidencia de cierre

Al terminar este nivel, esto tiene que estar funcionando:

  • El circuito quedó sin potenciómetros: los seis servomotores están conectados y alimentados con la fuente externa, listos para un movimiento programado.
  • El brazo repite solo una secuencia completa: toma un objeto de una cinta, gira 90° y lo deposita en otra cinta más baja, y vuelve a empezar.
  • La secuencia está programada con una máquina de estados basada en millis() (sin código bloqueante), como una tabla de poses que el programa recorre una a una.
  • Podés explicar por qué conviene declarar la coreografía como una tabla de poses y por qué el avance con millis() deja el loop() libre para futuras extensiones.