¿Te imaginas un robot que quepa en la palma de tu mano y aun así sea capaz de trepar una pared vertical y caminar de cabeza por el techo? La mayoría de los robots escaladores que ves en internet dependen de motores de dron enormes, hélices de 5" y baterías LiPo pesadas para generar suficiente fuerza de agarre. En este proyecto le damos la vuelta al problema: en lugar de fuerza bruta, buscamos el equilibrio perfecto entre peso, aerodinámica y diseño mecánico para que un solo motor brushless mantenga pegado al robot.
Al terminar este tutorial vas a entender la física detrás de la succión por presión negativa, vas a saber cómo dimensionar el motor midiendo su fuerza de agarre real, y vas a tener un robot con forma de escarabajo. carcasa impresa en 3D incluida. controlado desde tu celular vía WiFi con un ESP32. Lo mejor: es un proyecto reproducible con una lista de materiales concreta.

Cómo se pega el robot a la pared (la física de la succión)
Acá no hay ventosas ni pegamento. El truco es puramente aerodinámico. Un motor brushless de alta velocidad hace girar una hélice montada bajo el chasis. Al girar, la hélice succiona el aire que queda atrapado debajo del robot y lo expulsa hacia arriba, creando una región de baja presión entre el chasis y la superficie.
Como la presión atmosférica exterior es mayor que la interior, esa diferencia de presión empuja al robot con firmeza contra la pared o el techo. En otras palabras: no es el robot el que "se agarra", es la atmósfera la que lo aprieta contra la superficie. Mientras mayor sea la diferencia de presión, mayor es la fuerza de agarre.
Este principio es el mismo que usan los sistemas de vacío industriales, pero miniaturizado. La clave del proyecto es que la hélice mueva suficiente caudal de aire sin agregar peso.
Midiendo la fuerza de succión (no adivines, mide)
Antes de armar el robot completo conviene verificar que el motor elegido genera succión suficiente. Para eso se arma un banco de pruebas simple: una pequeña carcasa impresa en 3D que sostiene el motor brushless con su hélice, apoyada sobre una pesa digital SF-400.
El detalle interesante es cómo se interpreta la medición: la balanza muestra valores positivos cuando algo la presiona hacia abajo, y valores negativos cuando algo tira del plato hacia arriba. Ese valor negativo es exactamente la fuerza de succión que genera el motor.
La regla de dimensionamiento es directa. Suma el peso total de la electrónica. batería, ESP32, motor brushless, ESC y motores de tracción. que en este caso ronda los 137 gramos. Para trepar de forma confiable, la fuerza de succión debe ser bastante mayor que el peso propio del robot: un objetivo seguro y óptimo es el doble del peso total. Es decir, apuntamos a unos 300 gramos de agarre, cifra que efectivamente se logró.
Este cálculo es la parte más importante del proyecto. Si el motor no genera al menos 2× el peso, el robot vibra y se despega; si genera de más, malgastas batería y peso.
Hardware y conexiones
El cerebro del robot es un ESP32, que controla dos subsistemas independientes: la succión (motor brushless) y la tracción (dos motores N20).
- Los motores N20 de tracción se conectan a las salidas del driver MX1508. Las 4 entradas del driver van a los GPIO 16, 17, 18 y 19 del ESP32.
- El ESC que maneja el motor brushless se conecta al GPIO 4.
- El convertidor buck MP1584 se conecta directo a la batería LiPo de 7.4V y entrega 5V estables para alimentar el ESP32 y el driver.
- El ESC también va conectado directo a la batería, para aprovechar toda la potencia disponible.

Un detalle de armado que marca la diferencia: monta el ESP32 y la batería lo más al centro posible para reducir el torque de vuelco, y reemplaza los cables jumper por cables normales bien ordenados. En un robot que trabaja invertido, cada gramo mal distribuido cuenta.
Diseño e impresión 3D del caparazón
Con la mecánica resuelta, el siguiente objetivo es que el robot se vea como un escarabajo real y no como "otro robot más". El diseño parte de patrones tipo panal, muy parecidos a un patrón de Voronoi. ese que usan muchos diseñadores y arquitectos. .

El flujo de modelado es: se genera el patrón de Voronoi con un complemento de Autodesk Fusion, se exporta como SVG, se importa al sketch de Fusion y se extruye sobre una cáscara hemisférica.
Imprimir la curva directamente es complicado: el caparazón tiene apenas 0.5 mm de espesor y necesita muchos soportes, con alto riesgo de romper la pieza al retirarlos. La solución ingeniosa es aplanar el diseño para imprimirlo y después reformarlo a su curvatura real aplicando calor. Para aplanar la superficie 3D a un patrón 2D se usa Meshmixer (de la familia Autodesk). Como el patrón plano no cabía en la cama de impresión, se dividió en dos mitades que luego se pegaron.

Ensamblaje
En el corazón del robot va el motor brushless con su hélice. Alrededor se montan el ESP32 y la batería al centro, y el convertidor buck junto con el driver de motor se ubican sobre las abrazaderas de los motores N20.

Una vez ensamblado, el robot se parece bastante a un escarabajo real y sigue siendo lo suficientemente liviano para trepar. El resultado final sube paredes verticales y camina por el techo, demostrando cómo la optimización cuidadosa de la distribución del peso, el diseño estructural y el flujo de aire producen un escalador miniatura efectivo.
Software: control WiFi desde el celular
El ESP32 levanta un servidor web propio. Después de cargar el código con el IDE de Arduino, el monitor serial te muestra la dirección IP del ESP32. Escribe esa IP en el navegador de tu celular y accedes a una interfaz simple: control del motor brushless de succión (ON/OFF + slider de velocidad) y control de movimiento (adelante, atrás, izquierda, derecha, stop).

La lógica de movimiento es directa: para avanzar, ambos motores giran en sentido antihorario (CCW); para retroceder, ambos en sentido horario (CW); para girar, un motor gira en un sentido y el otro en el contrario.
Dos funciones merecen atención. usToDuty() convierte el ancho de pulso en microsegundos al ciclo de trabajo PWM que espera el ESC. Y setThrottle() controla la velocidad del motor brushless de 0 a 100%, pero solo si bldcEnabled está activo. una protección para que el motor no arranque solo. .
Este es el código completo del proyecto, listo para cargar (recuerda cambiar ssid y password por los de tu red):
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "your_ssid";
const char* password = "your-password";
WebServer server(80);
// ESC
const int escPin = 4;
const int pwmFreq = 50;
const int pwmResolution = 16;
// MX1508
const int L1 = 16;
const int L2 = 17;
const int R1 = 18;
const int R2 = 19;
bool bldcEnabled = false;
int throttle = 0;
uint32_t usToDuty(uint16_t us)
{
uint32_t maxDuty = (1UL << pwmResolution) - 1;
return (uint32_t)((us / 20000.0) * maxDuty);
}
void setThrottle(int percent)
{
if (!bldcEnabled)
{
ledcWrite(escPin, usToDuty(1000));
return;
}
percent = constrain(percent, 0, 100);
uint16_t pulse =
map(percent, 0, 100, 1000, 2000);
ledcWrite(escPin, usToDuty(pulse));
}
// --------------------
// Robot Movement
// --------------------
void stopRobot()
{
digitalWrite(L1, LOW);
digitalWrite(L2, LOW);
digitalWrite(R1, LOW);
digitalWrite(R2, LOW);
}
void forward()
{
digitalWrite(L1, HIGH);
digitalWrite(L2, LOW);
digitalWrite(R1, HIGH);
digitalWrite(R2, LOW);
}
void backward()
{
digitalWrite(L1, LOW);
digitalWrite(L2, HIGH);
digitalWrite(R1, LOW);
digitalWrite(R2, HIGH);
}
void left()
{
digitalWrite(L1, LOW);
digitalWrite(L2, HIGH);
digitalWrite(R1, HIGH);
digitalWrite(R2, LOW);
}
void right()
{
digitalWrite(L1, HIGH);
digitalWrite(L2, LOW);
digitalWrite(R1, LOW);
digitalWrite(R2, HIGH);
}
void handleRoot()
{
String page = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body{
font-family:Arial;
background:#f4f4f4;
text-align:center;
}
h2{
margin-top:20px;
}
.blueBtn{
background:#007BFF;
color:white;
border:none;
padding:15px 28px;
font-size:18px;
border-radius:8px;
margin:8px;
cursor:pointer;
}
.blueBtn:hover{
background:#0056b3;
}
.slider{
width:85%;
margin-top:10px;
}
#val{
color:#007BFF;
}
.controller{
width:260px;
margin:25px auto;
}
.row{
display:flex;
justify-content:center;
margin:8px 0;
}
.ctrlBtn{
width:80px;
height:80px;
font-size:18px;
border:none;
border-radius:12px;
background:#4CAF50;
color:white;
cursor:pointer;
margin:5px;
}
.stopBtn{
background:#E53935;
}
.ctrlBtn:hover{
opacity:0.85;
}
</style>
</head>
<body>
<h2>Wall Climbing Robot</h2>
<h3>BLDC Suction Control</h3>
<button class="blueBtn"
onclick="fetch('/bldcOn')">
ON
</button>
<button class="blueBtn"
onclick="fetch('/bldcOff')">
OFF
</button>
<br><br>
<input
type="range"
min="0"
max="100"
value="0"
class="slider"
id="speed">
<h2 id="val">0%</h2>
<hr>
<h3>Movement Control</h3>
<div class="controller">
<div class="row">
<button class="ctrlBtn"
onclick="fetch('/forward')">
FWD
</button>
</div>
<div class="row">
<button class="ctrlBtn"
onclick="fetch('/left')">
LEFT
</button>
<button class="ctrlBtn stopBtn"
onclick="fetch('/stop')">
STOP
</button>
<button class="ctrlBtn"
onclick="fetch('/right')">
RIGHT
</button>
</div>
<div class="row">
<button class="ctrlBtn"
onclick="fetch('/backward')">
BKWD
</button>
</div>
</div>
<script>
var slider=document.getElementById("speed");
var val=document.getElementById("val");
slider.oninput=function(){
val.innerHTML=this.value+" %";
fetch("/speed?value="+this.value);
}
</script>
</body>
</html>
)rawliteral";
server.send(200, "text/html", page);
}
// --------------------
// Web Requests
// --------------------
void handleSpeed()
{
throttle =
server.arg("value").toInt();
setThrottle(throttle);
server.send(200, "text/plain", "OK");
}
void handleBLDCOn()
{
bldcEnabled = true;
setThrottle(throttle);
server.send(200, "text/plain", "ON");
}
void handleBLDCOff()
{
bldcEnabled = false;
setThrottle(0);
server.send(200, "text/plain", "OFF");
}
// --------------------
// Setup
// --------------------
void setup()
{
Serial.begin(115200);
Serial.println("Serial connected successfully!");
pinMode(L1, OUTPUT);
pinMode(L2, OUTPUT);
pinMode(R1, OUTPUT);
pinMode(R2, OUTPUT);
stopRobot();
ledcAttach(
escPin,
pwmFreq,
pwmResolution);
ledcWrite(
escPin,
usToDuty(1000));
delay(5000); // arm ESC
WiFi.begin(ssid, password);
while(WiFi.status()!=WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/speed", handleSpeed);
server.on("/bldcOn", handleBLDCOn);
server.on("/bldcOff", handleBLDCOff);
server.on("/forward",
[](){ forward(); server.send(200); });
server.on("/backward",
[](){ backward(); server.send(200); });
server.on("/left",
[](){ left(); server.send(200); });
server.on("/right",
[](){ right(); server.send(200); });
server.on("/stop",
[](){ stopRobot(); server.send(200); });
server.begin();
}
void loop()
{
server.handleClient();
}
Pruebas y solución de problemas
Cuando pruebes el robot, estos son los tres problemas más comunes y su causa real:
- El robot vibra demasiado en el techo: la succión no alcanza. Sube el valor del slider en la app web para aumentar la potencia del motor y mantenerlo firme.
- El motor brushless se reinicia y hace pitidos de arranque en loop: la batería está baja y no entrega corriente suficiente. Recárgala.
- El robot no se pega a la pared ni al techo: revisa la separación de las ruedas. Mantén el diámetro de las ruedas al mínimo para que la distancia entre el cuerpo y la superficie sea la menor posible (menos separación = más succión efectiva).
En la versión final, pese a su tamaño compacto, el robot genera más de 360 gramos de fuerza de agarre, pesa apenas unos 104 gramos, y trepa paredes y techos usando un solo motor brushless para la succión.

Variantes y mejoras
Una vez que tengas el escarabajo funcionando, puedes llevarlo más lejos con estas ideas que no están en el proyecto original:
- Robot limpiador de techos o paredes: agrega un pequeño paño de microfibra o un cepillo bajo el chasis. Como el robot ya mantiene contacto y presión contra la superficie, aprovechar ese contacto para limpieza es una extensión natural del diseño.
- Telemetría en vivo: suma un sensor de corriente (por ejemplo un INA219 por I2C) para medir en tiempo real cuánta potencia consume el motor de succión y mostrarlo en la misma app web. Así sabes cuándo la batería está por agotarse antes de que el robot se despegue.
- Autonomía con sensores: incorpora un sensor de distancia (VL53L0X o HC SR04) para que el robot detecte bordes y esquinas, y evolucione de teleoperado a semiautónomo.
Personalización para Chile
En Chile puedes conseguir buena parte de la electrónica en MechatronicStore. Ojo: este es un proyecto con piezas de dron/micro robótica, así que algunos componentes muy específicos (el motor brushless de 4300KV, la hélice de 3", los micromotores N20 y la batería LiPo de 300mAh) son especialidad y conviene buscarlos aparte. Estos son los equivalentes locales en stock para el resto del BOM:
- Placa ESP32 (ESP WROOM-32) (SKU X2-10V2). $7.990 CLP. Es exactamente el controlador del proyecto.
- Convertidor Buck LM2596 (SKU B-207). $1.990 CLP. Equivalente funcional al MP1584: es un reductor ajustable, solo déjalo calibrado en 5V antes de conectar el ESP32.
- Driver de motor dual L9110S (SKU GQ2-9). $1.990 CLP. Cumple el mismo rol que el MX1508 para los dos motores N20 (control por 4 pines lógicos).
- Controlador ESC brushless bidireccional 30A (SKU GS2-9). $10.990 CLP. Es un ESC de mayor amperaje del estrictamente necesario, pero compatible con 2S y con el motor brushless de succión.
Para el motor brushless, la hélice, los micromotores N20 y la batería LiPo, revisa el catálogo de motores y baterías de la tienda o consúltalos por soporte, ya que las especificaciones exactas dependen del peso final que logres en tu impresión.
Recursos
- Tutorial original (inglés): DIY ESP32 Wall Climbing Robot Using BLDC Suction Power
- Repositorio GitHub: el proyecto original publica el código completo y la documentación en su repositorio "ESP32 Wall Climbing Robot" (búscalo desde el artículo original enlazado arriba).
Versión chilena, re angulada e inspirada en el proyecto de Circuit Digest, con componentes en stock local en MechatronicStore.




