MonteSTEAM AIDARAC IES Monterroso

Chisme 183 del Lince: la MPU-6050 en tu móvil

Conecta el sensor a la NodeMCU y mira su inclinación en vivo en el móvil. Cuatro pasos y a medir.

1 Monta el sensor

Material: NodeMCU ESP8266, módulo MPU-6050 (GY-521), 4 cables dupont, ordenador, móvil y WiFi de 2,4 GHz.

NodeMCU MPU-6050 3VGD1D2G VCCGNDSCLSDAAD0 INT, XDA y XCL: sin conectar
MPU-6050NodeMCUCable
VCC3V (no VIN)rojo
GNDGnegro
SCLD1naranja
SDAD2verde
AD0Gnegro
⚠ Todo a 3,3 V. Aprieta bien los cables: un contacto flojo da el error «MPU no responde».

2 Comprueba que el sensor responde

Arduino IDE, placa NodeMCU 1.0 (ESP-12E Module). Copia, sube y abre el monitor serie a 115200.

// MPU-6050 en NodeMCU ESP8266
// Sin libreria, solo Wire (I2C)
#include <Wire.h>

const int SDA_PIN = D2;
const int SCL_PIN = D1;
// Con AD0 a GND la direccion es 0x68
const uint8_t MPU = 0x68;

void setup() {
  Serial.begin(115200);
  delay(500);
  Serial.println();

  Wire.begin(SDA_PIN, SCL_PIN);

  // Comprueba que el sensor responde
  Wire.beginTransmission(MPU);
  byte error = Wire.endTransmission();
  if (error != 0) {
    Serial.println("MPU no responde.");
    Serial.println("Revisa el cableado.");
    while (true) {
      delay(1000);
    }
  }

  // Despierta el sensor (sale de reposo)
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission();
  Serial.println("MPU-6050 listo");
}

void loop() {
  // Pide 14 bytes desde el registro 0x3B
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom((uint8_t)MPU,
                   (size_t)14, true);

  int16_t ax = Wire.read() << 8
             | Wire.read();
  int16_t ay = Wire.read() << 8
             | Wire.read();
  int16_t az = Wire.read() << 8
             | Wire.read();
  int16_t t  = Wire.read() << 8
             | Wire.read();
  int16_t gx = Wire.read() << 8
             | Wire.read();
  int16_t gy = Wire.read() << 8
             | Wire.read();
  int16_t gz = Wire.read() << 8
             | Wire.read();

  // Conversion a unidades reales
  // Acel: 16384 por g (rango +-2 g)
  // Giro: 131 por grado/s (+-250)
  Serial.print("Acel g: ");
  Serial.print(ax / 16384.0);
  Serial.print("  ");
  Serial.print(ay / 16384.0);
  Serial.print("  ");
  Serial.print(az / 16384.0);

  Serial.print(" | Giro grad/s: ");
  Serial.print(gx / 131.0);
  Serial.print("  ");
  Serial.print(gy / 131.0);
  Serial.print("  ");
  Serial.print(gz / 131.0);

  Serial.print(" | Temp C: ");
  Serial.println(t / 340.0 + 36.53);

  delay(500);
}
¿Qué debería salir?

Con el sensor quieto y plano: Acel Z ≈ 1 g, X e Y ≈ 0, giro ≈ 1 °/s.

Si sale «MPU no responde», revisa el montaje del paso 1.

3 Sube el servidor web

Cambia TU_WIFI y TU_CLAVE por los de tu red (2,4 GHz). Sube el código y mira el monitor serie: saldrá la IP.

// Chisme: MPU-6050 en el movil
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <Wire.h>

// Pon aqui tus datos (mayusculas exactas)
const char* ssid =
  "TU_WIFI";
const char* password =
  "TU_CLAVE";

const int SDA_PIN = D2;
const int SCL_PIN = D1;
// Con AD0 a GND la direccion es 0x68
const uint8_t MPU = 0x68;

ESP8266WebServer server(80);

// Ultimos datos leidos
float aX, aY, aZ;
float gX, gY, gZ;
float tempC;
float roll, pitch;

// Pagina web con actualizacion en vivo
const char PAGINA[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
 content="width=device-width,
 initial-scale=1">
<title>MPU-6050</title>
<style>
body{text-align:center;
 font-family:sans-serif}
table{margin:10px auto;
 font-size:1.3em}
td{padding:4px 12px}
.caja{height:140px;
 padding-top:50px}
#barra{width:220px;
 height:22px;
 margin:0 auto;
 background:#2e7de0;
 border-radius:11px;
 transition:transform .2s}
#estado{color:#c0392b}
</style>
</head>
<body>
<h1>MPU-6050</h1>
<div class="caja">
 <div id="barra"></div>
</div>
<p>Inclinacion (roll):
 <b id="roll">-</b> grados</p>
<p>Inclinacion (pitch):
 <b id="pitch">-</b> grados</p>
<table>
<tr><td>Acel X</td>
 <td id="ax">-</td><td>g</td></tr>
<tr><td>Acel Y</td>
 <td id="ay">-</td><td>g</td></tr>
<tr><td>Acel Z</td>
 <td id="az">-</td><td>g</td></tr>
<tr><td>Giro X</td>
 <td id="gx">-</td><td>grad/s</td></tr>
<tr><td>Giro Y</td>
 <td id="gy">-</td><td>grad/s</td></tr>
<tr><td>Giro Z</td>
 <td id="gz">-</td><td>grad/s</td></tr>
<tr><td>Temp chip</td>
 <td id="temp">-</td><td>C</td></tr>
</table>
<p id="estado"></p>
<script>
function poner(id, v) {
  document.getElementById(id)
    .textContent = v.toFixed(2);
}
function actualizar() {
  fetch("/datos")
    .then(function (r) {
      return r.json();
    })
    .then(function (d) {
      poner("roll", d.roll);
      poner("pitch", d.pitch);
      poner("ax", d.ax);
      poner("ay", d.ay);
      poner("az", d.az);
      poner("gx", d.gx);
      poner("gy", d.gy);
      poner("gz", d.gz);
      poner("temp", d.temp);
      document.getElementById("barra")
        .style.transform =
        "rotate(" + d.roll + "deg)";
      document.getElementById("estado")
        .textContent = "";
    })
    .catch(function () {
      document.getElementById("estado")
        .textContent = "Sin datos";
    });
}
setInterval(actualizar, 300);
actualizar();
</script>
</body>
</html>
)rawliteral";

// Lee el sensor (igual que el base)
bool leerMPU() {
  // Pide 14 bytes desde el registro 0x3B
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  size_t n = Wire.requestFrom(
    (uint8_t)MPU, (size_t)14, true);
  if (n < 14) {
    return false;
  }

  int16_t ax = Wire.read() << 8
             | Wire.read();
  int16_t ay = Wire.read() << 8
             | Wire.read();
  int16_t az = Wire.read() << 8
             | Wire.read();
  int16_t t  = Wire.read() << 8
             | Wire.read();
  int16_t gx = Wire.read() << 8
             | Wire.read();
  int16_t gy = Wire.read() << 8
             | Wire.read();
  int16_t gz = Wire.read() << 8
             | Wire.read();

  // Conversion a unidades reales
  aX = ax / 16384.0;
  aY = ay / 16384.0;
  aZ = az / 16384.0;
  gX = gx / 131.0;
  gY = gy / 131.0;
  gZ = gz / 131.0;
  tempC = t / 340.0 + 36.53;

  // Inclinacion a partir de la gravedad
  roll = atan2(aY, aZ) * 180.0 / PI;
  pitch = atan2(-aX, sqrt(aY * aY
          + aZ * aZ)) * 180.0 / PI;
  return true;
}

void paginaInicio() {
  Serial.println("Peticion: /");
  server.sendHeader("Cache-Control",
                    "no-store");
  server.send_P(200, "text/html",
                PAGINA);
}

// Devuelve los datos en formato JSON
void datos() {
  if (!leerMPU()) {
    server.send(503, "text/plain",
                "sin sensor");
    return;
  }
  String j = "{";
  j += "\"ax\":" + String(aX, 2) + ",";
  j += "\"ay\":" + String(aY, 2) + ",";
  j += "\"az\":" + String(aZ, 2) + ",";
  j += "\"gx\":" + String(gX, 2) + ",";
  j += "\"gy\":" + String(gY, 2) + ",";
  j += "\"gz\":" + String(gZ, 2) + ",";
  j += "\"temp\":" + String(tempC, 2);
  j += ",\"roll\":" + String(roll, 1);
  j += ",\"pitch\":" + String(pitch, 1);
  j += "}";
  server.sendHeader("Cache-Control",
                    "no-store");
  server.send(200, "application/json", j);
}

// Lista las redes que ve la placa
void mostrarRedes() {
  Serial.println("Buscando redes...");
  int n = WiFi.scanNetworks();
  Serial.print("Redes vistas: ");
  Serial.println(n);
  for (int i = 0; i < n; i++) {
    Serial.print(i + 1);
    Serial.print(") ");
    Serial.print(WiFi.SSID(i));
    Serial.print("  senal ");
    Serial.print(WiFi.RSSI(i));
    Serial.print(" dBm  canal ");
    Serial.print(WiFi.channel(i));
    if (WiFi.SSID(i) == String(ssid)) {
      Serial.print("  <-- TU RED");
    }
    Serial.println();
  }
}

// Intenta conectar durante 20 s
bool conectarWiFi() {
  Serial.print("Conectando a: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  for (int i = 0; i < 40; i++) {
    if (WiFi.status() == WL_CONNECTED) {
      return true;
    }
    Serial.print("estado ");
    Serial.println(WiFi.status());
    delay(500);
  }
  return false;
}

void setup() {
  Serial.begin(115200);
  delay(500);
  Serial.println();

  Wire.begin(SDA_PIN, SCL_PIN);

  // Comprueba que el sensor responde
  Wire.beginTransmission(MPU);
  byte error = Wire.endTransmission();
  if (error != 0) {
    Serial.println("MPU no responde.");
    Serial.println("Revisa el cableado.");
    while (true) {
      delay(1000);
    }
  }

  // Despierta el sensor (sale de reposo)
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission();
  Serial.println("MPU-6050 listo");

  WiFi.persistent(false);
  WiFi.disconnect(true);
  delay(200);
  WiFi.mode(WIFI_STA);
  mostrarRedes();

  if (!conectarWiFi()) {
    Serial.println("NO CONECTA.");
    Serial.println("Reinicio en 10 s");
    delay(10000);
    ESP.restart();
  }

  Serial.print("Conectado. IP: ");
  Serial.println(WiFi.localIP());

  server.on("/", paginaInicio);
  server.on("/datos", datos);
  server.begin();
  Serial.println("Servidor listo");
}

void loop() {
  server.handleClient();
}

En el móvil (misma WiFi), abre el navegador y escribe la IP, por ejemplo http://192.168.1.45. Verás los datos moverse cada 0,3 s.

4 Predice y mide

Antes de inclinar el sensor, escribe qué eje crees que marcará ≈ 1 g en cada posición. Luego míralo en el móvil.

Ver la pista

La gravedad tira siempre hacia abajo: el eje que queda apuntando hacia arriba o hacia abajo marca ≈ 1 g (en valor absoluto). Los otros dos marcan ≈ 0.

Mide el giroscopio en reposo: con el sensor quieto debería marcar 0 °/s, pero marca ≈ 1 °/s. Es un desfase normal de fábrica.

Mira la temperatura: marca unos 39 °C. Es la del chip, no la de la habitación.

★ Si algo falla

🏁 Reto final

Calibra el giroscopio: mide el valor medio con el sensor quieto y réstalo a cada lectura. ¿Marca ahora ≈ 0 °/s?

Solo funciona en tu red local y sin contraseña: cualquiera conectado a tu WiFi puede ver la página. Prototipo educativo.