Guía OLED de Luka

Guía para la IA tutora · ESP32 + OLED en Wokwi · edición Halloween

Detener Python remoto y ver la OLED en el mismo instante

El ESP32 y la pantalla OLED ya están listos en el simulador. Aquí aprendes a usarlos: encender Wokwi, pausar tu programa dentro del ESP32 y ver a la vez las variables y la pantalla. Después, practica con retos de Halloween.

Demo OLED
Sergio: ESP32 OK
vuelta 1
GNDVCCSCLSDA
SSD1306 128×64 · lo que se ve al pausar en la línea 29

Contrato del bot tutor

Un paso a la vez

Explica en español, da un solo paso, pide el resultado y espera antes del siguiente. Ante un fallo, pide el mensaje completo y cambia una sola cosa por vez.

Enseña con pistas

Preguntas y pistas, no soluciones. No resuelve ejercicios ni exámenes, ni escribe lógica de robots sin permiso docente explícito. Esta demo diagnostica el entorno; no autoriza resolver el trabajo de Luka.

Nunca hardware real

No flashear placas físicas, no tocar puertos USB, no instalar machine con pip. Para APIs nuevas verificar en Context7 /micropython/micropython o declarar la limitación.

Antes de empezar

Pines y conexiones, explicado fácil

Un pin es una patita metálica de la placa. Cada una tiene una etiqueta impresa (3V3, GND, 21…) y un trabajo. Conectar es unir con un cable el pin de un aparato con el pin correcto del otro. En nuestro montaje bastan 4 cables entre el ESP32 y la pantalla OLED.

Cableado ESP32 DevKit C V4 con OLED SSD1306 VCC de la OLED al pin 3V3, GND al GND del lado izquierdo, SCL al pin 22 y SDA al pin 21. Demo OLED vuelta 1 SSD1306 · 0x3C GNDVCCSCLSDA ESP32 DevKit C V4 USB 3V3ENVPVN343532332526271412GNDGND2322TXRX21GND19185171640
El mismo montaje de diagram.json. Los colores de los cables son los que usa Wokwi. Los puntitos que se mueven muestran hacia dónde viajan la energía y los datos.

VCC → 3V3 energía · rojo

Le da corriente a la pantalla. El ESP32 trabaja a 3,3 voltios, por eso usamos 3V3. Es como el enchufe.

GND → GND tierra · negro

El camino de regreso de la corriente. Sin él no hay circuito: la energía necesita ir y volver. Los dos aparatos deben compartir GND.

SDA → 21 datos · verde

Por aquí viajan los mensajes: qué letras dibujar y dónde.

SCL → 22 reloj · azul

Marca el ritmo, como un metrónomo: en cada «tic» se lee un trocito del mensaje que va por SDA.

SDA y SCL juntos forman I2C

I2C es una «calle» de dos cables donde pueden vivir varios aparatos. Cada uno tiene su número de casa, la dirección. Nuestra OLED vive en la 0x3C (está en diagram.json). El ESP32 escribe a esa dirección y solo la pantalla responde.

El número del código es la etiqueta

El 21 no es «la patita 21 contando desde arriba»: es el pin que tiene 21 impreso al lado (GPIO 21). Busca la etiqueta, no cuentes.

¿Y el print()?

Los pines TX (enviar) y RX (recibir) van al monitor serie de Wokwi. Por ahí llega a tu pantalla el texto de print("vuelta", contador, …).

Cables y código tienen que decir lo mismo

Así lo dice oled_demo.py. Si el cable está en el 21 pero el código dice otro número, el ESP32 habla por una patita donde no hay nadie escuchando.

python · oled_demo.py
from machine import Pin, I2C
import ssd1306

i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
display = ssd1306.SSD1306_I2C(128, 64, i2c)
I2C(0, …)
Usa el primer controlador I2C del ESP32.
scl=Pin(22)
El reloj sale por el pin 22, donde está el cable azul.
sda=Pin(21)
Los datos salen por el pin 21, donde está el cable verde.
freq=400000
Velocidad del reloj: 400 000 «tics» por segundo.
128, 64
Tamaño de la pantalla en puntitos (píxeles): 128 de ancho por 64 de alto.

Piensa antes de mirar

Si cambias sda=Pin(21) por Pin(19) sin mover el cable, ¿qué verás?

La pantalla se queda negra: el ESP32 manda los datos por el 19, pero la OLED escucha en el 21. Código y cables ya no coinciden.

¿Qué pasa si quitas solo el cable negro (GND)?

La pantalla no enciende. La corriente entra por VCC pero no tiene por dónde volver: el circuito queda abierto.

Si intercambias los cables SDA y SCL en la placa, ¿funcionará?

No. El ESP32 enviaría el ritmo por el cable de datos y los datos por el del ritmo. Hay que devolver los cables a su sitio o cambiar también el código.

¿Por qué en el breakpoint de la línea 29 ya se ve «vuelta 1»?

Porque la línea 27 llama a mostrar_contador, que termina con show(): en ese momento los datos viajan por SDA y SCL y la pantalla se actualiza.

En Wokwi todo es simulado: puedes equivocarte con los cables sin romper nada. Con una placa real, desconecta la energía antes de mover cables. ¿Quieres saber qué hacen los otros 34 pines? Mira el mapa de pines al final.

Cada vez que vayas a programar

Tutorial de uso

Todo ya está instalado. Este es el recorrido de cada sesión: abrir, encender el simulador, lanzar el programa, pausarlo y mirar qué pasa por dentro.

  1. 1

    Abrir el proyecto

    En VS Code: Archivo → Abrir carpeta y elige la carpeta luka-wokwi-oled-debug entera, no solo MicroPython-debug/.

    Si hay novedades del profe, tráelas antes de empezar, desde la terminal de VS Code:

    sh
    git pull
  2. 2

    Encender el simulador

    1. Haz clic en el icono de Wokwi de la barra lateral.
    2. Select Config File → MicroPython-debug/wokwi.toml.
    3. Pulsa Start Simulation. No uses Start with Debugger.

    Verás el ESP32 y la pantalla. Que esté negra es normal: todavía no se ha dibujado nada. Deja el simulador a la vista; si lo ocultas, Wokwi se puede pausar.

  3. 3

    Lanzar tu programa

    1. Abre el panel Ejecutar y depurar (Ctrl+Shift+D).
    2. En el selector de arriba elige qué correr:
      • Práctica OLED — REMOTA GHI: tu archivo practica.py.
      • OLED — depuración REMOTA GHI (no Python del PC): la demo de las tres vueltas.
    3. Pulsa el triángulo verde de ese panel. No uses el botón de «ejecutar archivo» del editor.
    4. Se abre una terminal y aparece Puente activo. Déjala abierta: es la conexión con el ESP32 simulado.
    5. Si se detiene en Paused on entry, pulsa F5 para seguir.

    Un mensaje de reintento «(1/3)» al arrancar es normal. Espera el resultado final.

  4. 4

    Pausar con un breakpoint

    Un breakpoint es una señal de «para aquí». Haz clic a la izquierda del número de línea: aparece un punto rojo. Cuando el programa llega a esa línea, se congela antes de ejecutarla.

    Mientras está en pausa puedes mirar tres cosas a la vez:

    Variables
    Cuánto vale cada variable ahora mismo.
    Pila de llamadas
    En qué función estás y quién la llamó.
    Simulador
    Lo que la pantalla muestra hasta ese momento.

    Pon los breakpoints dentro de funciones, como practica(). En líneas sueltas fuera de una función, a veces no se detiene.

  5. 5

    Avanzar paso a paso

    F5
    Continuar hasta el siguiente breakpoint.
    F10
    Ejecutar la línea actual y parar en la siguiente.
    F11
    Entrar dentro de la función de esta línea.
    Shift+F5
    Detener la depuración.

    Pruébalo aquí con la demo. Está pausada en la línea 29 de oled_demo.py, justo después de dibujar:

    ⏸ Paused on breakpoint · oled_demo.py:29

    Variables locales

    contador = 1 _ = 0 display = <SSD1306_I2C>

    Pila de llamadas

    ejecutar_demo · línea 29 <module> · línea 33

    OLED

    Demo OLED
    Sergio: ESP32 OK
    vuelta 1

    Antes de pulsar F5, predice el siguiente valor de contador.

    Antes de cada F5, di en voz alta qué esperas ver. Luego compara con Variables y con la pantalla.

  6. 6

    Terminar la sesión

    1. Detén la depuración con Shift+F5.
    2. Cierra la terminal del puente (icono de papelera de la terminal).
    3. Detén Wokwi.

    El ESP32 simulado no guarda nada al apagarse: tu código vive en practica.py y se vuelve a enviar cada vez que lanzas.

Si algo falla

Lo que vesQué hacer
Error de machine o la palabra debugpySe lanzó Python del PC, no el del ESP32. Detén y elige un perfil REMOTA GHI en el panel Ejecutar y depurar.
Falla la tarea antes de lanzarNo elijas Debug Anyway. Lee la terminal de la tarea y comprueba que Wokwi está encendido con MicroPython-debug/wokwi.toml.
Lost connection to the boardComprueba que el simulador está visible y sin pausar. Detén todo (paso 6) y vuelve a empezar desde el paso 2.
Pantalla negra o dibujo viejoSi la pausa está antes de show(), es normal. Avanza con F10 hasta pasar show().
Recargaste VS Code o reiniciaste WokwiLa conexión anterior ya no sirve. Detén la depuración y lanza el perfil otra vez.
Nada de lo anteriorCopia el mensaje completo del error y enséñaselo a tu tutor.

Después de la demo

Ruta de práctica embrujada

Un poco de teoría, practicar, una idea nueva, practicar otra vez. Cada práctica usa lo de antes y le suma algo. Se trabaja en MicroPython-debug/practica.py.

Cómo se practica

  1. Enciende Wokwi como en el tutorial de uso (pasos 1 y 2). Si es la primera vez, haz git pull para traer practica.py.
  2. Abre MicroPython-debug/practica.py.
  3. Reemplaza lo que está dentro de def practica(display): por el código de la práctica.
  4. Completa los huecos ___.
  5. En Ejecutar y depurar elige Práctica OLED — REMOTA GHI y pulsa el triángulo.
  6. Si aparece Paused on entry, pulsa F5. Al terminar, detén la depuración antes de cambiar de práctica.

Para la IA tutora

Antes de ejecutar, pide a Luka que prediga qué verá. Da las pistas de una en una y solo cuando las pida o se atasque. No completes los huecos por Luka: si se equivoca, pregúntale qué esperaba ver y qué vio.

  1. Teoría

    La pantalla es una cuadrícula de puntitos

    La OLED tiene 128 puntitos de ancho y 64 de alto. Cada puntito (píxel) tiene una dirección: x cuenta de izquierda a derecha y y de arriba hacia abajo. El (0, 0) es la esquina de arriba a la izquierda.

    Tres órdenes bastan para empezar:

    fill(0)
    Borra todo: pinta la hoja de negro.
    text("Hola", x, y, 1)
    Escribe empezando en el punto (x, y). El 1 es «encendido».
    show()
    Envía el dibujo a la pantalla. Antes de esto no se ve nada.

    Piensa en una hoja: fill y text dibujan en la hoja; show() la pega en la ventana.

    (0, 0) (127, 63) x → y ↓ 128 × 64
  2. Práctica 1

    Tu nombre en la cripta

    Usa: fill · text · show

    python · practica.py
    def practica(display):
        display.fill(0)
        display.text("Feliz Halloween", 0, 0, 1)
        display.text(___, 0, 20, 1)   # escribe tu nombre
        display.show()
    Pista 1

    El texto va entre comillas, igual que "Feliz Halloween".

    Pista 2

    Si no aparece nada, revisa que show() siga al final.

    Comprueba: arriba «Feliz Halloween» y debajo tu nombre. ¿Qué número cambiarías para bajar tu nombre hasta la última línea?

  3. Concepto nuevo

    Dibujar formas

    Además de letras, la pantalla puede dibujar puntos, líneas y rectángulos. Todos empiezan por la esquina de arriba a la izquierda (x, y).

    pixel(x, y, 1)
    Enciende un solo puntito.
    hline(x, y, largo, 1)
    Línea horizontal de largo puntitos.
    rect(x, y, ancho, alto, 1)
    Rectángulo hueco, solo el borde.
    fill_rect(x, y, ancho, alto, 1)
    Rectángulo relleno.

    Igual que con el texto: se dibuja en la hoja y se ve al llamar a show().

  4. Práctica 2

    Calabaza de cuadrados

    Usa: rect · fill_rect

    python · practica.py
    def practica(display):
        display.fill(0)
        display.rect(44, 20, 40, 36, 1)          # cuerpo: empieza en y = 20
        display.fill_rect(60, 12, 8, ___, 1)   # tallo: empieza en y = 12
        display.show()
    Pista 1

    El hueco es el alto del tallo.

    Pista 2

    El tallo empieza en y = 12 y debe tocar el cuerpo, que empieza en y = 20. ¿Cuántos puntitos hay entre los dos?

    Comprueba: un cuadrado hueco con un tallo encima, sin espacio entre los dos. ¿Qué pasa si el alto es muy grande?

  5. Profundización

    Calcular posiciones: centrar y simetría

    Cada letra mide 8 × 8 puntitos: caben 16 letras por línea (128 ÷ 8) y 8 líneas (64 ÷ 8). Para centrar un texto se resta lo que ocupa del ancho total y se reparte a los dos lados:

    x = (128 − letras × 8) ÷ 2

    Ejemplo: «Hola» tiene 4 letras → (128 − 32) ÷ 2 = 48. Los espacios y signos también cuentan.

    Con las formas pasa igual. El cuerpo de la calabaza empieza en x = 44 y mide 40: ocupa de 44 a 83. El ojo izquierdo empieza en x = 52, o sea 8 puntitos dentro del borde. El derecho debe terminar 8 antes del borde derecho; como mide 6 de ancho, también hay que restar esos 6:

    inicio del ojo derecho = 84 − 8 − 6
  6. Práctica 3

    Dale cara a la calabaza

    Usa: lo de la práctica 2 + centrar + simetría + hline

    python · practica.py
    def practica(display):
        display.fill(0)
        display.rect(44, 20, 40, 36, 1)        # cuerpo
        display.fill_rect(60, 12, 8, 8, 1)     # tallo
        display.fill_rect(52, 28, 6, 6, 1)     # ojo izquierdo
        display.fill_rect(___, 28, 6, 6, 1)    # ojo derecho
        display.hline(52, 46, ___, 1)       # boca centrada
        display.text("BOO!", ___, 0, 1)      # texto centrado arriba
        display.show()
    Pista 1

    Para el ojo derecho, usa la cuenta de la profundización.

    Pista 2

    La boca empieza 8 puntitos dentro del borde izquierdo (x = 52). Debe terminar 8 antes del borde derecho, en x = 75. ¿Cuántos puntitos hay de 52 a 75, contando los dos?

    Pista 3

    Para «BOO!», cuenta sus letras (el signo también) y usa la fórmula de centrar.

    Comprueba: los dos ojos a la misma distancia de los lados, la boca centrada y «BOO!» centrado arriba. Reto: hazle dientes con pixel.

  7. Concepto nuevo

    Repetir sin copiar: for

    Cuando algo se repite, no hace falta escribirlo muchas veces. for repite las líneas que tiene debajo con sangría (4 espacios a la derecha).

    python · ejemplo
    for i in range(3):
        display.pixel(i * 10, 0, 1)   # se repite 3 veces

    range(3) le da a i los valores 0, 1 y 2: empieza en 0 y no llega al 3. En cada vuelta i vale distinto, así que i * 10 pone cada punto en otro sitio: x = 0, 10, 20.

  8. Práctica 4

    Murciélagos en fila

    Usa: for · range · fill_rect

    python · practica.py
    def practica(display):
        display.fill(0)
        for i in range(___):           # queremos 4 murciélagos
            display.fill_rect(i * 30 + 4, 20, 20, 10, 1)
        display.show()
    Pista 1

    range(n) repite n veces.

    Pista 2

    Pon un breakpoint en la línea de fill_rect y mira cuánto vale i en cada parada con F5.

    Comprueba: 4 rectángulos en fila. Antes de ejecutar, predice: ¿en qué x empieza el tercero? ¿Por qué show() está fuera del for?

  9. Profundización

    range con inicio, fin y paso, y esperar

    range(1, 4)
    1, 2, 3. Empieza en 1, no llega al 4.
    range(0, 10, 2)
    0, 2, 4, 6, 8. Va de 2 en 2.
    range(3, 0, -1)
    3, 2, 1. Paso negativo: cuenta hacia atrás.
    time.sleep(1)
    Espera 1 segundo antes de seguir.
    str(5)
    Convierte el número 5 en el texto «5». text() solo acepta texto.
  10. Práctica 5

    Cuenta regresiva para Halloween

    Usa: range hacia atrás · str · time.sleep

    python · practica.py
    def practica(display):
        for numero in range(___, ___, -1):   # 5, 4, 3, 2, 1
            display.fill(0)
            display.text("Halloween en", 16, 10, 1)
            display.text(str(numero), 60, 30, 1)
            display.show()
            time.sleep(1)
        display.fill(0)
        display.text("BOO!", 48, 28, 1)
        display.show()
    Pista 1

    El primer hueco es donde empieza la cuenta.

    Pista 2

    El fin no se incluye. Si quieres que el último sea 1, ¿dónde tienes que parar?

    Comprueba: 5, 4, 3, 2, 1 y luego «BOO!». ¿Por qué el «BOO!» está fuera del for?

  11. Concepto nuevo

    Variables que van sumando

    Una variable es una caja con nombre que guarda un valor. Puede cambiar: dulces = dulces + 2 significa «lo que había en la caja, más 2, y lo guardo otra vez en la caja».

    El depurador te deja ver la caja por dentro: en cada pausa, el panel Variables muestra cuánto vale ahora. Así compruebas si lo que pensabas es lo que pasa de verdad.

  12. Práctica 6

    La bolsa de dulces

    Usa: variables · for · el depurador

    python · practica.py
    def practica(display):
        dulces = 0
        for casa in range(1, 5):
            dulces = dulces + casa     # cada casa da tantos dulces como su número
            display.fill(0)
            display.text("Casa " + str(casa), 0, 0, 1)
            display.text("Dulces: " + str(dulces), 0, 20, 1)
            display.show()
            time.sleep(1)

    Esta vez no hay huecos: el reto es predecir. Antes de ejecutar, haz en papel una tabla con dos columnas, casa y dulces, y llénala vuelta por vuelta.

    Pista 1

    Primera vuelta: casa vale 1 y dulces valía 0. ¿Cuánto queda?

    Pista 2

    Pon un breakpoint en la línea de display.show() y compara tu tabla con el panel Variables en cada F5.

    Comprueba: tu tabla coincide con Variables y con la pantalla en las 4 casas. Reto: ¿qué cambiarías para que cada casa dé siempre 2 dulces?

  13. Profundización

    Dibujar en la hoja o mandar una orden

    Hasta ahora todo se dibujaba en la hoja y se veía con show(). Algunas funciones son distintas: son órdenes directas a la pantalla y actúan al instante, sin show().

    invert(1)
    Cambia negro por blanco en toda la pantalla.
    invert(0)
    La deja como estaba.

    Por eso invertir es instantáneo: no cambia el dibujo de la hoja, cambia cómo la pantalla lo muestra.

  14. Práctica 7

    El fantasma que parpadea

    Usa: invert · for · time.sleep

    python · practica.py
    def practica(display):
        display.fill(0)
        display.text("BOO!", 48, 28, 1)
        display.show()
        for vez in range(___):        # parpadea 3 veces
            display.invert(1)
            time.sleep(0.5)
            display.invert(___)
            time.sleep(0.5)
    Pista 1

    Mira la profundización: un valor invierte, el otro devuelve la pantalla a la normalidad.

    Pista 2

    Pon un breakpoint en time.sleep(0.5) y avanza con F5: ¿cambia la pantalla aunque no haya show()?

    Comprueba: «BOO!» parpadea 3 veces y termina con fondo negro. ¿Qué pasa si quitas el segundo time.sleep?

  15. Reto final

    Tu escena de Halloween

    Junta lo aprendido en una sola escena: una cuenta regresiva, después aparece la calabaza con cara y, al final, la pantalla parpadea. Antes de escribir código, dibuja en papel la cuadrícula y apunta las coordenadas.

    Comprueba: explica a tu tutor en qué orden pasan las cosas y por qué, usando un breakpoint para enseñarlo.

Para consultar

Mapa de pines del ESP32

La placa del ejemplo es una ESP32 DevKit C V4 y tiene 38 pines, 19 por lado. Aquí está qué hace cada uno. Los colores dicen si puedes usarlo tranquilo en tus proyectos.

Los 38 pines del ESP32 DevKit C V4, coloreados por tipoESP32USB3V3OLEDENVPVN343532332526271412GNDOLED13D2D3CMD5VGND2322OLEDTXRX21OLEDGND19185171640215D1D0CLK
Vista desde arriba, con el USB abajo. Los nombres son los que trae impresos la placa y los que usa Wokwi. «OLED» marca los pines de nuestro montaje.
  • EnergíaDan corriente a otros aparatos.
  • TierraEl camino de regreso de la corriente.
  • LibresBuenos para tus proyectos: entrada o salida.
  • Solo entradaLeen, pero no pueden encender nada.
  • Cuidado al arrancarFuncionan, pero influyen en cómo enciende la placa.
  • Conexión con el PCPor aquí viajan print() y el código.
  • No tocarEstán conectados a la memoria interna.

Palabras que vas a ver

GPIO
Pin de uso general: tú decides si lee (entrada) o enciende cosas (salida). El número del código es el número GPIO.
Analógico
Lee voltajes intermedios, no solo encendido o apagado. Los pines analógicos del grupo «ADC2» no leen si usas WiFi.
Táctil
Nota cuando lo tocas con el dedo, sin botón.
PWM
Encender y apagar muy rápido. Sirve para regular el brillo de un LED o mover un servo. Casi todos los pines de salida lo pueden hacer.
I2C, SPI, UART
Formas de hablar con otros aparatos por cables. Nuestra OLED usa I2C.
Pin de arranque
La placa lo mira al encender para decidir cómo arrancar. Por eso hay que tener cuidado con lo que se le conecta.

Todos los pines de datos trabajan a 3,3 V. Con una placa real, nunca les metas 5 V. En Wokwi puedes probar sin miedo: no se rompe nada.

Pin por pin

PinLadoQué haceTipo
3V3izq.Saca 3,3 voltios para alimentar sensores y pantallas.VCC de la OLEDEnergía
ENizq.Reinicio. Si se une a GND, la placa se reinicia; es lo que hace el botón EN. No le conectes nada.Cuidado al arrancar
VPizq.GPIO 36. Solo entrada: lee voltajes (analógico), por ejemplo un potenciómetro.Solo entrada
VNizq.GPIO 39. Solo entrada analógica, igual que VP.Solo entrada
34izq.Solo entrada, puede leer voltajes (analógico). No tiene resistencia interna de pull-up.Solo entrada
35izq.Solo entrada analógica, igual que el 34.Solo entrada
32izq.Libre. Además lee voltajes (analógico) y detecta el toque de un dedo (táctil).Libres
33izq.Libre. Analógico y táctil, como el 32.Libres
25izq.Libre. Tiene DAC: puede sacar voltajes intermedios, útil para sonidos sencillos.Libres
26izq.Libre. También tiene DAC, como el 25.Libres
27izq.Libre. Analógico y táctil.Libres
14izq.Al encender manda una señal rápida (PWM) un momento. Después se puede usar normal.Cuidado al arrancar
12izq.Pin de arranque: si está en alto al encender, la placa puede no arrancar.Cuidado al arrancar
GNDizq.Tierra. Todos los GND están unidos entre sí.GND de la OLEDTierra
13izq.Libre. Analógico y táctil.Libres
D2izq.GPIO 9. Conectado a la memoria flash interna. No usar.No tocar
D3izq.GPIO 10. Memoria flash. No usar.No tocar
CMDizq.GPIO 11. Memoria flash. No usar.No tocar
5Vizq.5 voltios que llegan del USB. Para aparatos de 5 V; ojo, los pines de datos solo aguantan 3,3 V.Energía
GNDder.Tierra, igual que los demás GND.Tierra
23der.Libre. En el bus SPI hace de MOSI (datos que salen hacia el aparato).Libres
22der.Libre. Muy usado como SCL (reloj) de I2C.SCL de la OLEDLibres
TXder.GPIO 1. Envía datos al PC: por aquí sale lo que escribes con print(). Mejor no usarlo.Conexión con el PC
RXder.GPIO 3. Recibe datos del PC. Mejor no usarlo.Conexión con el PC
21der.Libre. Muy usado como SDA (datos) de I2C.SDA de la OLEDLibres
GNDder.Tierra.Tierra
19der.Libre. En SPI hace de MISO (datos que llegan del aparato).Libres
18der.Libre. En SPI hace de SCK (reloj).Libres
5der.Pin de arranque. Se puede usar, pero sin forzarlo a alto o bajo al encender. En SPI suele ser CS.Cuidado al arrancar
17der.Libre. Puede ser TX de un segundo puerto serie (UART2).Libres
16der.Libre. Puede ser RX del segundo puerto serie (UART2).Libres
4der.Libre. Analógico y táctil.Libres
0der.Es el botón BOOT: si está en bajo al encender, la placa entra en modo de cargar firmware.Cuidado al arrancar
2der.Pin de arranque: debe estar en bajo o libre para poder cargar firmware.Cuidado al arrancar
15der.Pin de arranque: cambia los mensajes que la placa escribe al encender.Cuidado al arrancar
D1der.GPIO 8. Memoria flash. No usar.No tocar
D0der.GPIO 7. Memoria flash. No usar.No tocar
CLKder.GPIO 6. Memoria flash. No usar.No tocar

Siguiente nivel

Hablar con otros chips

El ESP32 puede conversar con sensores, pantallas y otras placas. Cada uno habla un «idioma» (protocolo). Aquí verás cuatro, cada uno con su circuito en Wokwi, su código completo y una práctica. Todo está en la carpeta chips/ del repositorio (haz git pull si no la tienes).

  1. Teoría

    Los cuatro idiomas de los chips

    ProtocoloCables de datos¿Cómo sabe a quién le habla?Chip de ejemplo
    Un cable1: datosSolo hay un chip en ese cableDHT22 (temperatura)
    I2C2: SDA (datos) y SCL (reloj)Cada chip tiene una dirección, como un número de casaMPU6050 (movimiento) y la OLED
    SPI3: MOSI (datos), SCK (reloj) y CSUn cable CS por chip: «te hablo a ti»MAX7219 (matriz de LEDs)
    UART2: TX (envía) y RX (recibe), cruzadosSolo hay dos: uno en cada puntaOtra placa, un GPS, el PC

    Para cualquier chip, la receta siempre es la misma:

    1. Energía: VCC a 3V3 (o 5V si el chip lo pide) y GND a GND.
    2. Cables de datos: a los pines que elijas, según el protocolo.
    3. Crear el bus en el código diciendo qué pines usaste.
    4. Comprobar que el chip responde antes de pedirle nada.
    5. Leer o escribir datos y mostrarlos.
  2. Concepto nuevo · Un cable

    Chip 1 · DHT22, el termómetro embrujado

    El DHT22 mide temperatura y humedad. Usa un solo cable de datos: el ESP32 le pide una medida y el sensor responde por el mismo cable, por turnos.

    ESP32DHT223V3VCCGNDGND4SDA (datos)
    • VCC → 3V3
    • GND → GND
    • SDA (datos) → 4

    También lleva la OLED conectada como siempre (21, 22, 3V3 y GND) para mostrar los valores.

    Configurar la comunicación, paso a paso

    1. 1

      Importar el módulo

      MicroPython ya trae el idioma del DHT22: se llama dht.

      python
      import dht
      from machine import Pin
    2. 2

      Crear el sensor con su pin

      Solo hay que decirle en qué pin está el cable de datos. Si el cable va al 4, el código dice 4.

      python
      sensor = dht.DHT22(Pin(4))
    3. 3

      Pedir una medida

      measure() hace la conversación por el cable: el ESP32 pregunta y el sensor contesta.

      python
      sensor.measure()
    4. 4

      Leer los valores

      Después de medir, se leen los dos números. Entre medidas hay que esperar al menos 2 segundos.

      python
      temperatura = sensor.temperature()   # grados Celsius
      humedad = sensor.humidity()         # porcentaje
    5. 5

      Comprobar

      Pon un breakpoint en mostrar(...) y mira temperatura y humedad en Variables. Mientras corre la simulación, haz clic en el DHT22 de Wokwi: aparecen controles para cambiar la temperatura.

    Ver el archivo completo: chips/1-dht22/termometro.py
    python · termometro.py
    # Chip 1 · DHT22: el termómetro embrujado.
    # Protocolo de UN cable: el sensor y el ESP32 se turnan para hablar por el pin 4.
    import time
    import dht
    from machine import Pin, I2C
    import ssd1306
    
    
    def preparar_pantalla():
        i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
        return ssd1306.SSD1306_I2C(128, 64, i2c)
    
    
    def preparar_sensor():
        # El DHT22 solo necesita saber en qué pin está su cable de datos.
        return dht.DHT22(Pin(4))
    
    
    def leer(sensor):
        sensor.measure()  # pide una medida nueva al sensor
        return sensor.temperature(), sensor.humidity()
    
    
    def mostrar(display, temperatura, humedad):
        display.fill(0)
        display.text("Termometro", 24, 0, 1)
        display.text("Temp: %.1f C" % temperatura, 0, 20, 1)
        display.text("Humedad: %d %%" % humedad, 0, 32, 1)
        if temperatura > 30:
            display.text("Calor de caldero", 0, 52, 1)
        display.show()
    
    
    def main():
        display = preparar_pantalla()
        sensor = preparar_sensor()
        for vuelta in range(30):
            temperatura, humedad = leer(sensor)
            mostrar(display, temperatura, humedad)  # breakpoint aquí para ver los valores
            time.sleep(2)  # el DHT22 necesita 2 segundos entre medidas
    
    
    if __name__ == "__main__":
        main()
    Para probarlo: en Wokwi, Select Config File → chips/1-dht22/wokwi.toml y Start Simulation. Luego, en Ejecutar y depurar, elige Chip 1 · DHT22 termómetro — REMOTA GHI.
  3. Práctica · Chip 1

    Alarma de frío de tumba

    Usa: if · elif · el DHT22

    En termometro.py, dentro de mostrar, añade un aviso cuando haga frío:

    python · termometro.py
        if temperatura > 30:
            display.text("Calor de caldero", 0, 52, 1)
        elif temperatura < ___:
            display.text(___, 0, 52, 1)
    Pista 1

    elif significa «si no pasó lo de arriba, pero pasa esto otro».

    Pista 2

    Elige un número de grados que te parezca frío, por ejemplo menos de 5.

    Pista 3

    El texto va entre comillas y no puede pasar de 16 letras. Sin tildes: la OLED no las tiene.

    Comprueba: haz clic en el DHT22 en Wokwi y baja la temperatura. ¿En qué valor exacto cambia el mensaje? ¿Qué pasa justo en ese número?

  4. Profundización · I2C

    Chip 2 · MPU6050, la varita mágica

    El MPU6050 mide inclinación y movimiento. Habla I2C, el mismo idioma que la OLED, así que comparte los mismos dos cables: SDA en el 21 y SCL en el 22. ¿Cómo sabe cada chip que le hablan a él? Por su dirección: la OLED es la 0x3C y el MPU6050 la 0x68.

    ESP32 OLED0x3C MPU60500x68 21 SDA 22 SCL SDASCL SDASCL Energía y GND como siempre:3V3 y GND a los dos chips

    Configurar la comunicación, paso a paso

    1. 1

      Crear el bus I2C una sola vez

      Un bus para los dos chips, con los pines del reloj y los datos.

      python
      i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
    2. 2

      Buscar quién está conectado

      scan() pregunta en todas las direcciones y devuelve las que contestan. Las da en decimal: 60 es 0x3C y 104 es 0x68.

      python
      print(i2c.scan())   # [60, 104]
    3. 3

      Despertar el MPU6050

      Los chips I2C guardan su configuración en registros, cajitas numeradas. El MPU6050 arranca dormido: se despierta escribiendo 0 en su registro 0x6B.

      python
      i2c.writeto_mem(0x68, 0x6B, b"\x00")   # (dirección, registro, dato)
    4. 4

      Leer los datos

      La aceleración está en 6 bytes a partir del registro 0x3B: dos por cada eje X, Y, Z.

      python
      datos = i2c.readfrom_mem(0x68, 0x3B, 6)
    5. 5

      Convertir los bytes en números

      Cada eje viene en dos bytes que se juntan en un número con signo. Dividido entre 16384 da la aceleración en «g» (1 g es la gravedad).

      python
      ax = con_signo(datos[0], datos[1]) / 16384
    6. 6

      Comprobar

      Mira la consola: debe imprimir [60, 104]. Pon un breakpoint en mostrar(...) y mira ax y ay. Para «inclinar» la varita, cambia accelX y accelY en chips/2-mpu6050/diagram.json y vuelve a iniciar la simulación.

    Ver el archivo completo: chips/2-mpu6050/varita.py
    python · varita.py
    # Chip 2 · MPU6050: la varita mágica.
    # Protocolo I2C: comparte los cables SDA (21) y SCL (22) con la OLED.
    # Cada chip tiene su dirección: OLED = 0x3C (60), MPU6050 = 0x68 (104).
    import time
    from machine import Pin, I2C
    import ssd1306
    
    MPU = 0x68          # dirección del MPU6050 en el bus I2C
    REG_ENCENDIDO = 0x6B  # registro PWR_MGMT_1: 0 = despierto
    REG_ACELERACION = 0x3B  # aquí empiezan los 6 bytes de aceleración X, Y, Z
    
    
    def preparar_bus():
        return I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
    
    
    def despertar_mpu(i2c):
        # Al encender, el MPU6050 está dormido: se despierta escribiendo 0 en su registro.
        i2c.writeto_mem(MPU, REG_ENCENDIDO, b"\x00")
    
    
    def con_signo(alto, bajo):
        # Junta dos bytes en un número que puede ser negativo (−32768 a 32767).
        valor = (alto << 8) | bajo
        if valor > 32767:
            valor -= 65536
        return valor
    
    
    def leer_aceleracion(i2c):
        datos = i2c.readfrom_mem(MPU, REG_ACELERACION, 6)
        ax = con_signo(datos[0], datos[1]) / 16384  # 16384 = 1 g en el rango de ±2 g
        ay = con_signo(datos[2], datos[3]) / 16384
        az = con_signo(datos[4], datos[5]) / 16384
        return ax, ay, az
    
    
    def mostrar(display, ax, ay):
        display.fill(0)
        display.text("Varita magica", 12, 0, 1)
        x = 60 + int(ax * 50)
        y = 34 + int(ay * 20)
        x = max(0, min(120, x))
        y = max(12, min(56, y))
        display.fill_rect(x, y, 8, 8, 1)  # el fantasma se mueve al inclinar
        display.show()
    
    
    def main():
        i2c = preparar_bus()
        print("Chips encontrados:", i2c.scan())  # debería mostrar [60, 104]
        display = ssd1306.SSD1306_I2C(128, 64, i2c)
        despertar_mpu(i2c)
        for vuelta in range(100):
            ax, ay, az = leer_aceleracion(i2c)
            mostrar(display, ax, ay)  # breakpoint aquí para ver ax, ay
            time.sleep(0.2)
    
    
    if __name__ == "__main__":
        main()
    Para probarlo: en Wokwi, Select Config File → chips/2-mpu6050/wokwi.toml y Start Simulation. Luego, en Ejecutar y depurar, elige Chip 2 · MPU6050 varita — REMOTA GHI.
  5. Práctica · Chip 2

    ¿Dónde está la varita?

    Usa: i2c.scan() · direcciones · in

    En varita.py, dentro de main, cambia la línea del print por esta comprobación:

    python · varita.py
        chips = i2c.scan()
        print("Chips encontrados:", chips)
        if ___ not in chips:
            print("No encuentro la varita: revisa SDA y SCL")
    Pista 1

    Busca la dirección del MPU6050: está guardada en una variable al principio del archivo.

    Pista 2

    x in lista pregunta si x está en la lista; not in, si NO está.

    Pista 3

    Para probar que funciona, en diagram.json borra la conexión de imu1:SDA y vuelve a iniciar.

    Comprueba: con todo conectado no aparece el aviso; sin el cable SDA del MPU6050, sí. Reto: haz que el fantasma se mueva al revés cuando inclinas.

  6. Concepto nuevo · SPI

    Chip 3 · MAX7219, la calabaza de LEDs

    El MAX7219 controla una matriz de 8 × 8 LEDs. Habla SPI: un cable de reloj, uno de datos y uno llamado CS que funciona como levantar la mano: mientras está en 0, el chip escucha.

    ESP32MAX72195VVCCGNDGND23DIN18CLK5CS
    • VCC → 5V
    • GND → GND
    • DIN → 23
    • CLK → 18
    • CS → 5

    Configurar la comunicación, paso a paso

    1. 1

      Crear el bus SPI

      Se indica el reloj (sck) y los datos de salida (mosi). La velocidad es cuántos bits por segundo se envían.

      python
      spi = SPI(2, baudrate=1000000, sck=Pin(18), mosi=Pin(23))
    2. 2

      Preparar el pin CS

      CS empieza en 1: «no te estoy hablando».

      python
      cs = Pin(5, Pin.OUT, value=1)
    3. 3

      Hablar con el chip

      Cada orden son dos bytes: el número del registro y su valor, siempre entre bajar y subir CS.

      python
      cs.value(0)                          # levantar la mano
      spi.write(bytes([registro, valor]))  # enviar la orden
      cs.value(1)                          # el chip la guarda
    4. 4

      Encender y configurar

      Al arrancar se le dice cómo trabajar: usar las 8 filas, dibujar punto por punto, el brillo, y encender.

      python
      escribir(spi, cs, 0x0B, 7)   # usar las 8 filas
      escribir(spi, cs, 0x09, 0)   # punto por punto
      escribir(spi, cs, 0x0A, 3)   # brillo (0 a 15)
      escribir(spi, cs, 0x0C, 1)   # encender
    5. 5

      Dibujar

      Las filas son los registros 1 a 8. Cada fila es un número binario: cada 1 es un LED encendido.

      python
      escribir(spi, cs, 1, 0b00011000)   # fila 1: dos LEDs en el centro
    6. 6

      Comprobar

      Pon un breakpoint en dibujar(...) y avanza con F5: la matriz cambia entre calabaza y fantasma. Si el dibujo sale girado o como en un espejo, es por cómo está montada la matriz: prueba "layout": "fc16" en los attrs de la matriz en diagram.json.

    Ver el archivo completo: chips/3-max7219/calabaza_leds.py
    python · calabaza_leds.py
    # Chip 3 · MAX7219: la calabaza de LEDs (matriz de 8 × 8).
    # Protocolo SPI: reloj (18), datos (23) y un pin de "te hablo a ti" llamado CS (5).
    import time
    from machine import Pin, SPI
    
    REG_DECODIFICAR = 0x09  # 0 = dibujamos punto por punto, sin números automáticos
    REG_BRILLO = 0x0A       # de 0 (tenue) a 15 (máximo)
    REG_FILAS = 0x0B        # cuántas filas usar: 7 = las 8 (de 0 a 7)
    REG_ENCENDIDO = 0x0C    # 1 = encendido
    REG_PRUEBA = 0x0F       # 1 = enciende todo para probar
    
    CALABAZA = [
        0b00011000,
        0b01111110,
        0b11111111,
        0b10111101,
        0b11111111,
        0b10100101,
        0b11011011,
        0b01111110,
    ]
    
    FANTASMA = [
        0b00111100,
        0b01111110,
        0b11011011,
        0b11111111,
        0b11111111,
        0b11111111,
        0b11111111,
        0b10101010,
    ]
    
    
    def preparar_spi():
        spi = SPI(2, baudrate=1000000, sck=Pin(18), mosi=Pin(23))
        cs = Pin(5, Pin.OUT, value=1)  # CS en 1 = "no te estoy hablando"
        return spi, cs
    
    
    def escribir(spi, cs, registro, valor):
        cs.value(0)                       # 1. levantar la mano: "te hablo a ti"
        spi.write(bytes([registro, valor]))  # 2. enviar dirección del registro y valor
        cs.value(1)                       # 3. bajar la mano: el chip guarda el dato
    
    
    def iniciar(spi, cs):
        escribir(spi, cs, REG_PRUEBA, 0)
        escribir(spi, cs, REG_FILAS, 7)
        escribir(spi, cs, REG_DECODIFICAR, 0)
        escribir(spi, cs, REG_BRILLO, 3)
        escribir(spi, cs, REG_ENCENDIDO, 1)
    
    
    def dibujar(spi, cs, filas):
        for numero in range(8):
            escribir(spi, cs, numero + 1, filas[numero])  # las filas son los registros 1 a 8
    
    
    def main():
        spi, cs = preparar_spi()
        iniciar(spi, cs)
        for vez in range(6):
            dibujar(spi, cs, CALABAZA)  # breakpoint aquí
            time.sleep(1)
            dibujar(spi, cs, FANTASMA)
            time.sleep(1)
        print("Fin del espectáculo")
    
    
    if __name__ == "__main__":
        main()
    Para probarlo: en Wokwi, Select Config File → chips/3-max7219/wokwi.toml y Start Simulation. Luego, en Ejecutar y depurar, elige Chip 3 · MAX7219 calabaza — REMOTA GHI.
  7. Práctica · Chip 3

    Dibuja un murciélago

    Usa: números binarios · dibujar · brillo

    Añade esta figura debajo de FANTASMA y complétala. Dibújala antes en papel en una cuadrícula de 8 × 8.

    python · calabaza_leds.py
    MURCIELAGO = [
        0b00000000,
        0b10000001,
        0b11000011,
        0b11111111,
        0b___,      # cuerpo: 8 cifras, 1 = encendido
        0b00100100,
        0b00000000,
        0b00000000,
    ]

    Luego, en main, añade dibujar(spi, cs, MURCIELAGO) y un time.sleep(1) dentro del for, y cambia el brillo:

    python · calabaza_leds.py
        escribir(spi, cs, REG_BRILLO, ___)   # de 0 a 15
    Pista 1

    Cada fila tiene exactamente 8 cifras, solo ceros y unos.

    Pista 2

    El cuerpo del murciélago va en el centro: los LEDs de los extremos de esa fila, apagados.

    Pista 3

    Para el brillo, cualquier número de 0 a 15. ¿Se nota la diferencia entre 1 y 15?

    Comprueba: aparecen calabaza, fantasma y murciélago. ¿Qué pasa si una fila tiene 9 cifras?

  8. Concepto nuevo · UART

    Chip 4 · UART, el mensaje secreto

    UART (puerto serie) es como un teléfono: TX habla y RX escucha. Entre dos placas se cruzan: el TX de una va al RX de la otra, y comparten GND. Es el mismo idioma con el que el ESP32 te manda los print(). En este circuito hay un solo ESP32, así que el cable sale por el 17 y vuelve a entrar por el 16: el ESP32 se habla a sí mismo.

    ESP32ESP3217 TX16 RX
    • 16 RX → 17 TX

    Configurar la comunicación, paso a paso

    1. 1

      Crear el puerto serie

      Se elige el UART 2 (el 0 lo usa el PC), la velocidad y los pines. Los dos lados tienen que usar la misma velocidad; si no, llegan letras sin sentido.

      python
      uart = UART(2, baudrate=9600, tx=17, rx=16)
    2. 2

      Enviar

      write manda el texto letra por letra por el cable TX.

      python
      uart.write("BOO")
    3. 3

      Esperar y comprobar si llegó algo

      any() dice cuántos bytes están esperando en RX.

      python
      time.sleep(0.1)
      if uart.any():
          mensaje = uart.read().decode()   # bytes → texto
    4. 4

      Comprobar

      Pon un breakpoint en mostrar(...) y compara palabra con llegada en Variables. La OLED muestra lo enviado y lo recibido.

    Ver el archivo completo: chips/4-uart/mensaje_secreto.py
    python · mensaje_secreto.py
    # Chip 4 · UART: el mensaje secreto.
    # Protocolo UART (puerto serie): un cable para enviar (TX, 17) y otro para recibir (RX, 16).
    # En este circuito el cable sale del 17 y vuelve a entrar por el 16: el ESP32 se habla a sí mismo.
    # Con dos placas reales, el TX de una va al RX de la otra (y comparten GND).
    import time
    from machine import Pin, I2C, UART
    import ssd1306
    
    
    def preparar_pantalla():
        i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
        return ssd1306.SSD1306_I2C(128, 64, i2c)
    
    
    def preparar_uart():
        # UART 2, velocidad 9600 bits por segundo. Los dos lados deben usar la MISMA velocidad.
        return UART(2, baudrate=9600, tx=17, rx=16)
    
    
    def enviar(uart, texto):
        uart.write(texto)
    
    
    def recibir(uart):
        time.sleep(0.1)  # dar tiempo a que lleguen las letras
        if uart.any():
            return uart.read().decode()
        return ""
    
    
    def mostrar(display, enviado, recibido):
        display.fill(0)
        display.text("Mensaje secreto", 4, 0, 1)
        display.text("Envie: " + enviado, 0, 24, 1)
        display.text("Llego: " + recibido, 0, 40, 1)
        display.show()
    
    
    def main():
        display = preparar_pantalla()
        uart = preparar_uart()
        for palabra in ["BOO", "Dulce", "Truco"]:
            enviar(uart, palabra)
            llegada = recibir(uart)
            mostrar(display, palabra, llegada)  # breakpoint aquí
            time.sleep(1)
    
    
    if __name__ == "__main__":
        main()
    Para probarlo: en Wokwi, Select Config File → chips/4-uart/wokwi.toml y Start Simulation. Luego, en Ejecutar y depurar, elige Chip 4 · UART mensaje secreto — REMOTA GHI.
  9. Práctica · Chip 4

    Corta la línea

    Usa: uart.write · listas · el cable TX → RX

    En mensaje_secreto.py, añade tu propia palabra a la lista:

    python · mensaje_secreto.py
        for palabra in ["BOO", "Dulce", "Truco", ___]:
    Pista 1

    La palabra va entre comillas. Recuerda el límite: «Envie: » ya ocupa 7 letras de las 16.

    Pista 2

    Después, abre chips/4-uart/diagram.json, borra la conexión esp:17 → esp:16 y vuelve a iniciar. Predice antes qué mostrará «Llego:».

    Comprueba: tu palabra aparece en «Envie» y en «Llego». Sin el cable, ¿qué llega? ¿Por qué el programa no se queda esperando para siempre?

  10. Reto final de chips

    La casa embrujada

    Con tu tutor, diseña un circuito con DHT22 y MAX7219 a la vez: si hace calor, la matriz muestra la calabaza; si hace frío, el fantasma. Antes de programar, haz la tabla de pines: ¿alguno se repite? Revisa el mapa de pines para elegir.

Estos circuitos y programas se prepararon con la misma configuración de la demo, pero aún no se han probado en tu Wokwi. Si algo no responde, revisa primero los cables con la receta de 5 pasos y avisa a tu tutor con el mensaje completo.