February 13, 2026
Description
Brazo robótico minimalista con micro servos MG90, fácil de imprimir y de ensamblar, cuenta con 3 grados de libertad sin contar el servo de la pinza de agarre.
Diseño Físico Circuito PCB Brazo Robótico Racbots
Código Arduino Brazo Robótico Racbots
#include <ESP32Servo.h>
#include <Wire.h> // Para comunicación I2C con la OLED
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// --- Definiciones de Pines ---
// Pines de los Servos
#define SERVO_BASE_PIN 16
#define SERVO_BRAZO1_PIN 17
#define SERVO_BRAZO2_PIN 18
#define SERVO_PINZA_PIN 19
// Pines de los Potenciómetros
#define POT_BASE_PIN 33
#define POT_BRAZO1_PIN 25
#define POT_BRAZO2_PIN 26
#define POT_PINZA_PIN 27
// Pines para la OLED (I2C)
#define OLED_SDA_PIN 21
#define OLED_SCL_PIN 22
#define SCREEN_WIDTH 128 // Ancho en pixeles de la OLED
#define SCREEN_HEIGHT 32 // Alto en pixeles de la OLED
#define OLED_RESET -1 // Pin de reset (o -1 si está en el mismo VCC)
// --- Objetos Servo ---
Servo servoBase;
Servo servoBrazo1;
Servo servoBrazo2;
Servo servoPinza;
// --- Objeto OLED ---
// La dirección I2C común es 0x3C, pero algunos modelos pueden usar 0x3D
// Si no funciona, intenta display.begin(SSD1306_SWITCHCAPVCC, 0x3D)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// --- Variables para los valores de los servos ---
int anguloBase = 90;
int anguloBrazo1 = 90;
int anguloBrazo2 = 90;
int anguloPinza = 90;
// Variables para almacenar el ángulo objetivo (leído del potenciómetro)
int anguloObjetivoBase = 90;
int anguloObjetivoBrazo1 = 90;
int anguloObjetivoBrazo2 = 90;
int anguloObjetivoPinza = 90;
// --- Parámetros de Estabilidad y Velocidad ---
const int THRESHOLD = 2; // Umbral de cambio: el servo solo se mueve si el ángulo objetivo cambia más de este valor (en grados).
const int STEP_SIZE = 3; // Tamaño del paso para un movimiento más rápido.
const int UPDATE_DELAY_MS = 5; // Menor retraso entre actualizaciones para mayor reactividad.
void setup() {
Serial.begin(115200); // Inicializa la comunicación serial para depuración
// --- Inicialización de la OLED (debe ser lo primero para mostrar el mensaje) ---
Wire.begin(OLED_SDA_PIN, OLED_SCL_PIN); // Inicializa I2C con los pines definidos
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Dirección I2C común 0x3C
Serial.println(F("SSD1306 allocation failed"));
for (;;); // No continuar si la inicialización falla
}
display.display(); // Muestra el buffer de inicio (logo de Adafruit)
// No delay aquí para que el mensaje aparezca de inmediato sobre el logo
display.clearDisplay(); // Limpia el buffer
// --- Mensaje de Bienvenida "RACBOTS INDUSTRIES" ---
display.setTextSize(2); // Tamaño del texto más grande para "RACBOTS"
display.setTextColor(SSD1306_WHITE); // Color del texto
display.setCursor(0, 0); // Posición para "RACBOTS"
display.println("RACBOTS");
display.setTextSize(1); // Tamaño del texto más pequeño para "INDUSTRIES"
display.setCursor(0, 16); // Posición para "INDUSTRIES" (debajo de "RACBOTS")
display.println("INDUSTRIES");
display.display(); // Muestra el mensaje en la pantalla
delay(2500); // Espera 2.5 segundos (2500 milisegundos)
display.clearDisplay(); // Limpia la pantalla después del mensaje
// --- Inicialización de Servos ---
servoBase.attach(SERVO_BASE_PIN);
servoBrazo1.attach(SERVO_BRAZO1_PIN);
servoBrazo2.attach(SERVO_BRAZO2_PIN);
servoPinza.attach(SERVO_PINZA_PIN);
// Mueve los servos a una posición inicial (dentro de sus nuevos rangos)
anguloBase = 90;
anguloBrazo1 = 60;
anguloBrazo2 = 90;
anguloPinza = 130;
servoBase.write(anguloBase);
servoBrazo1.write(anguloBrazo1);
servoBrazo2.write(anguloBrazo2);
servoPinza.write(anguloPinza);
// Inicializa los ángulos objetivo con la posición inicial
anguloObjetivoBase = anguloBase;
anguloObjetivoBrazo1 = anguloBrazo1;
anguloObjetivoBrazo2 = anguloBrazo2;
anguloObjetivoPinza = anguloPinza;
}
void loop() {
// --- Lectura de Potenciómetros y Mapeo a Ángulos Objetivo ---
// Los ADC del ESP32 tienen una resolución de 12 bits (0-4095)
int valPotBase = analogRead(POT_BASE_PIN);
// Base ahora vuelve al rango completo de 0 a 180 grados
anguloObjetivoBase = map(valPotBase, 0, 4095, 0, 180);
int valPotBrazo1 = analogRead(POT_BRAZO1_PIN);
// Limita el brazo 1 de 0 a 120
anguloObjetivoBrazo1 = map(valPotBrazo1, 0, 4095, 0, 120);
int valPotBrazo2 = analogRead(POT_BRAZO2_PIN);
// Brazo 2 se mantiene en el rango completo de 0 a 180
anguloObjetivoBrazo2 = map(valPotBrazo2, 0, 4095, 0, 180);
int valPotPinza = analogRead(POT_PINZA_PIN);
// Limita la pinza de 80 a 180 (extremo inferior 80, extremo superior 180)
anguloObjetivoPinza = map(valPotPinza, 0, 4095, 80, 180);
// --- Mover Servos Suavemente con Umbral ---
// Servo Base
if (abs(anguloObjetivoBase - anguloBase) > THRESHOLD) {
if (anguloObjetivoBase > anguloBase) {
anguloBase += STEP_SIZE;
} else {
anguloBase -= STEP_SIZE;
}
// Asegurarse de no sobrepasar el objetivo final
if ((anguloBase > anguloObjetivoBase && STEP_SIZE > 0) || (anguloBase < anguloObjetivoBase && STEP_SIZE < 0)) {
anguloBase = anguloObjetivoBase;
}
servoBase.write(anguloBase);
}
// Servo Brazo 1
if (abs(anguloObjetivoBrazo1 - anguloBrazo1) > THRESHOLD) {
if (anguloObjetivoBrazo1 > anguloBrazo1) {
anguloBrazo1 += STEP_SIZE;
} else {
anguloBrazo1 -= STEP_SIZE;
}
if ((anguloBrazo1 > anguloObjetivoBrazo1 && STEP_SIZE > 0) || (anguloBrazo1 < anguloObjetivoBrazo1 && STEP_SIZE < 0)) {
anguloBrazo1 = anguloObjetivoBrazo1;
}
servoBrazo1.write(anguloBrazo1);
}
// Servo Brazo 2
if (abs(anguloObjetivoBrazo2 - anguloBrazo2) > THRESHOLD) {
if (anguloObjetivoBrazo2 > anguloBrazo2) {
anguloBrazo2 += STEP_SIZE;
} else {
anguloBrazo2 -= STEP_SIZE;
}
if ((anguloBrazo2 > anguloObjetivoBrazo2 && STEP_SIZE > 0) || (anguloBrazo2 < anguloObjetivoBrazo2 && STEP_SIZE < 0)) {
anguloBrazo2 = anguloObjetivoBrazo2;
}
servoBrazo2.write(anguloBrazo2);
}
// Servo Pinza
if (abs(anguloObjetivoPinza - anguloPinza) > THRESHOLD) {
if (anguloObjetivoPinza > anguloPinza) {
anguloPinza += STEP_SIZE;
} else {
anguloPinza -= STEP_SIZE;
}
if ((anguloPinza > anguloObjetivoPinza && STEP_SIZE > 0) || (anguloPinza < anguloObjetivoPinza && STEP_SIZE < 0)) {
anguloPinza = anguloObjetivoPinza;
}
servoPinza.write(anguloPinza);
}
// --- Mostrar Valores en la OLED ---
display.clearDisplay(); // Limpia el buffer para dibujar de nuevo
display.setCursor(0, 0); // Posición inicial del cursor (x, y)
display.print("Base: ");
display.print(anguloBase);
display.println(" deg");
display.print("Brazo1: ");
display.print(anguloBrazo1);
display.println(" deg");
display.print("Brazo2: ");
display.print(anguloBrazo2);
display.println(" deg");
display.print("Pinza: ");
display.print(anguloPinza);
display.println(" deg");
display.display(); // Actualiza el display con lo que se ha dibujado en el buffer
// Pequeña pausa para evitar lecturas y actualizaciones demasiado rápidas
delay(UPDATE_DELAY_MS);
}
License:
Creative Commons — Attribution
6