Comandar el brazo a distancia con seis potenciómetros
Recorrido del nivel
Esa barra de arriba te dice en qué paso estás. Tocala para ver el índice completo y saltar a cualquier paso.
Armar el circuito con los potenciómetros
Si queremos controlar el brazo a distancia, podemos usar un potenciómetroPotenciómetroResistor cuyo valor de resistencia se ajusta de forma manual con una perilla. Conectando sus extremos a VCC y GND, la pata central entrega un nivel de tensión que representa la posición de la perilla y se lee por una entrada analógica del Arduino. para fijar la posición de cada servomotor: girás la perilla y el servo sigue ese ángulo. Vamos a montarlos sobre una protoboardProtoboardPlaca experimental para el prototipado rápido de circuitos: sus orificios permiten insertar y conectar componentes sin soldadura. Las líneas superior e inferior recorren la placa de punta a punta y suelen usarse para la alimentación.. 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.
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 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 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);
} 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.
Verificá
Con el programa cargado, girá cada potenciómetro de a uno: el servo de esa articulación tiene que seguir la perilla, de 0° a 180°. Si un servo no recorre todo el rango, revisá el mapeo (el divisor del ADC de tu placa); si tiembla o salta, revisá que los extremos del potenciómetro estén firmes en VCC y GND. Despejá la zona antes de mover: con seis potes activos el brazo puede barrer un área amplia.
Que recuerde una pose
Hoy el brazo hace lo que dicen las perillas en tiempo real. ¿Cómo harían para que, apretando un botón, el brazo recuerde la pose actual (los seis ángulos) y pueda volver a ella más tarde? Pista: hace falta guardar seis valores en variables cuando se aprieta el botón. Es el germen de lo que viene en el nivel avanzado: una secuencia de poses guardadas que el brazo reproduce solo.
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.