# Tema 0 · Entorno ESP32-C3 con VS Code y PlatformIO

Programación Dirigida por Eventos · Grado en Ingeniería Informática · 2026/27

Prepara y verifica Git, VS Code y PlatformIO en Windows o macOS, compila firmware real para la ESP32-C3 sin tener la placa y sigue el recorrido de un evento desde el pin GPIO hasta loop().

## Prepara tu entorno: Git, VS Code y PlatformIO

_Comprueba tu equipo, instala las herramientas en Windows (Git Bash) o macOS (zsh), entiende platformio.ini y compila la plantilla hasta obtener firmware.elf y firmware.bin. Termina diagnosticando los fallos típicos._

### Qué vas a montar hoy (y qué no) · Teoría

En esta asignatura vas a perseguir eventos a dos alturas muy distintas. En Android el sistema te entrega un `onClick` ya masticado; en un sistema empotrado puedes seguir el evento **desde el cambio eléctrico en un pin** hasta la interrupción, la rutina que la atiende y el código que acaba usando ese dato. Esta unidad deja listo el entorno para la parte empotrada.

La placa de referencia es la **ESP32-C3-DevKitM-1**, que PlatformIO identifica como `esp32-c3-devkitm-1`. Su microcontrolador tiene un núcleo **RISC-V de 32 bits**, Wi-Fi y Bluetooth Low Energy.

<div class="flow">
  <div><b>Git</b><small>y Git Bash en Windows</small></div>
  <div><b>VS Code</b><small>el editor</small></div>
  <div><b>PlatformIO</b><small>extensión + comando pio</small></div>
  <div><b>Plantilla</b><small>pde-esp32c3-eventos</small></div>
  <div><b>pio run</b><small>SUCCESS + firmware</small></div>
</div>

> [!IMPORTANTE] Primera sesión: solo el ordenador
> No necesitas la ESP32-C3, ni cable USB, ni componentes. La carga por USB, el puerto serie y el botón BOOT se prueban más adelante, con la placa delante.

### Anfitrión frente a objetivo

La idea clave del día es separar dos máquinas:

- **Anfitrión (host)**: tu Windows o tu Mac, donde escribes y compilas.
- **Objetivo (target)**: el núcleo RISC-V de la ESP32-C3, donde se ejecutará el firmware.

Compilas en un sitio para ejecutar en otro. Por eso hace falta una **toolchain** específica (compilador, ensamblador, enlazador y utilidades para RISC-V), y por eso **compilar sin placa es una prueba real**: valida el código, el framework, el compilador y el enlazado. Lo único que queda pendiente es la interacción con el hardware.

![En el anfitrión (tu Windows o tu Mac), tu código src/main.cpp pasa por pio run, que usa la toolchain RISC-V (compilador, ensamblador, enlazador y utilidades) para producir firmware.elf, con símbolos para depurar, y firmware.bin, la imagen para la flash. Una flecha discontinua lleva firmware.bin hasta el objetivo, la ESP32-C3 con núcleo RISC-V de 32 bits, donde se cargará por USB en otra sesión.](/img/pde/tema-0/anfitrion-objetivo.svg "Compilas en el anfitrión para ejecutar en el objetivo: hoy llegas hasta firmware.bin")

> [!ANALOGIA]
> Es como imprimir un libro en una imprenta de Madrid para venderlo en Buenos Aires: puedes comprobar que el libro está bien impreso sin haber viajado. Lo que no sabrás hasta llegar es cómo lo reciben los lectores.

**Preguntas**

**1.** Compilas el proyecto en tu portátil con Windows. ¿Para qué arquitectura genera código PlatformIO?

   a) Para x86-64, la de tu portátil, y luego la placa lo traduce
   b) Para el núcleo RISC-V de 32 bits de la ESP32-C3
   c) Para ARM, porque todos los microcontroladores son ARM
   d) Para ninguna concreta: el firmware es bytecode portable

**2.** ¿Qué **sí** queda comprobado cuando la compilación termina en SUCCESS sin placa conectada?

   a) Que el compilador y el enlazado funcionan
   b) Que el botón BOOT genera interrupciones
   c) Que el framework Arduino-ESP32 está resuelto
   d) Que el puerto serie de la placa responde
   e) Que el código fuente no tiene errores de compilación

### Comprueba tu equipo y aprende las reglas · Práctica

Compilar para una ESP32-C3 pide pocos recursos. Lo esencial: que VS Code sea compatible con tu sistema, espacio para los paquetes de PlatformIO e **Internet durante la primera instalación**.

| Componente | Mínimo | Recomendado |
| --- | --- | --- |
| Windows | Windows 10/11 de 64 bits | Windows 11 o 10 actualizado |
| macOS | Versión compatible con VS Code actual | macOS 13 o posterior, Apple Silicon o Intel |
| Memoria / disco | 4 GB de RAM y 5 GB libres | 8 GB de RAM y unos 10 GB libres |
| Red | HTTPS durante la instalación | Buena conexión la primera vez; luego se usa la caché |

**Autodiagnóstico en dos minutos.** En Windows: `Win + R` → `msinfo32` → «Tipo de sistema», y el espacio libre en *Este equipo*. En macOS: menú Apple → «Acerca de este Mac» y *Ajustes del Sistema → General → Almacenamiento*. **No necesitas virtualización** para compilar.

| Caso | Tu equipo | Qué haces |
| --- | --- | --- |
| A | Windows x64 o Mac reciente, ≥ 8 GB, SSD | Instalación estándar (ruta recomendada) |
| B | Windows ARM64 o arquitectura poco habitual | Instaladores de **tu** arquitectura; PlatformIO resuelve luego la toolchain del objetivo |
| C | Poco disco o red muy limitada | Git y VS Code primero; PlatformIO y la 1.ª compilación desde una red mejor |

### Reglas que evitan casi todos los problemas

1. Misma plantilla y mismo `platformio.ini` que el resto de la clase: **no cambies** `platform`, `board` ni `framework`.
2. En Windows, usa **Git Bash** para los comandos: así la sintaxis es casi la de macOS.
3. Rutas simples, sin tildes: `C:\dev\pde` en Windows, `~/dev/pde` en macOS.
4. **No instales a mano** GCC para RISC-V, esptool ni Arduino-ESP32: PlatformIO gestiona versiones compatibles en `~/.platformio`.
5. No borres `~/.platformio` para «arreglar» algo sin saber la causa: tendrías que volver a descargarlo todo.
6. Hoy **no** lances `pio run -t upload` ni `pio device monitor`: sin placa no se pueden verificar.
7. Distingue **compilación** de **ejecución**.

**Preguntas**

**3.** ¿Qué carpeta de trabajo recomienda la guía en Windows?

   a) `C:\Users\<tú>\OneDrive\Documentos\Prácticas PDE`
   b) `C:\Program Files\pde`
   c) `C:\dev\pde`
   d) El Escritorio, para tenerla a mano

**4.** Marca las acciones que **van contra** las reglas de esta unidad.

   a) Instalar a mano el compilador GCC para RISC-V
   b) Usar Git Bash para los comandos en Windows
   c) Cambiar `board` en `platformio.ini` porque «suena mejor otra placa»
   d) Ejecutar `pio run -t upload` hoy para ver qué pasa
   e) Dejar que PlatformIO gestione sus paquetes en `~/.platformio`

### Instalación en Windows con Git Bash · Práctica

En Windows usamos **Git for Windows** porque, además de Git, trae **Git Bash**, una terminal con sintaxis tipo Unix. Luego instalamos VS Code y, desde el propio editor, la extensión PlatformIO IDE.

1. **Git.** Descarga el instalador para tu arquitectura desde `https://git-scm.com/download/win` y deja las opciones por defecto (el editor predeterminado da igual).
2. Abre *Inicio → Git Bash* y comprueba:

```bash
git --version
```

3. **VS Code** desde `https://code.visualstudio.com` («User Installer» basta). Marca **Add to PATH** si aparece. Cierra y vuelve a abrir Git Bash.
4. Comprueba:

```bash
code --version
```

5. **PlatformIO IDE.** En VS Code, `Ctrl + Shift + X`, busca «PlatformIO IDE», verifica que el publicador es **PlatformIO** y pulsa *Install*. Espera a que se pueda abrir **PIO Home**.
6. La extensión instala su *Core* en `~/.platformio`. Para usar `pio` fuera de VS Code, añade su carpeta **Scripts** al PATH de Bash:

```bash
echo 'export PATH="$HOME/.platformio/penv/Scripts:$PATH"' >> ~/.bashrc
source ~/.bashrc
pio --version
```

7. Si `pio` sigue sin existir, abre una vez PIO Home, espera a que termine y abre una Git Bash **nueva**.

> [!TRUCO] La regla del «cierra y abre»
> Una terminal lee el PATH al arrancar. Si instalas algo con la terminal abierta, esa terminal no se entera. Ante un `command not found` justo después de instalar, lo primero es abrir una terminal nueva.

La instalación está lista cuando `git --version`, `code --version` y `pio --version` responden. En Windows ARM64 usa instaladores ARM64; no hace falta activar Hyper-V, WHPX ni virtualización.

**Preguntas**

**5.** En Windows, ¿qué carpeta de PlatformIO se añade al PATH en `~/.bashrc`?

   a) `$HOME/.platformio/penv/bin`
   b) `$HOME/.platformio/packages`
   c) `$HOME/.vscode/extensions/platformio`
   d) `$HOME/.platformio/penv/Scripts`

**6.** ¿Qué comando escribes en Git Bash para comprobar que PlatformIO Core es accesible?

### Instalación en macOS con zsh · Práctica

En macOS Git llega con las **Command Line Tools** de Apple y la terminal por defecto es **zsh**.

1. **Git.** En Terminal:

```bash
xcode-select --install
```

Pulsa «Instalar», acepta la licencia y espera. Si dice que ya están instaladas, sigue. Después: `git --version`.

2. **VS Code** desde `https://code.visualstudio.com`, versión **Apple Silicon** o **Intel** según tu equipo. Arrástralo a *Aplicaciones*.
3. Para tener el comando `code`: en VS Code, `Cmd + Shift + P` → escribe «shell command» → **Shell Command: Install 'code' command in PATH**. Cierra y abre Terminal y prueba `code --version`.
4. **PlatformIO IDE.** `Cmd + Shift + X`, busca «PlatformIO IDE» del publicador PlatformIO, *Install*, y espera a que termine PlatformIO Core.
5. Añade PlatformIO al PATH de zsh:

```bash
echo 'export PATH="$HOME/.platformio/penv/bin:$PATH"' >> ~/.zprofile
source ~/.zprofile
pio --version
```

6. Si `pio` no aparece, abre PIO Home, espera a que se inicialice, abre una Terminal nueva y repite.

### Windows frente a macOS, de un vistazo

| | Windows | macOS |
| --- | --- | --- |
| Terminal | Git Bash | Terminal (zsh) |
| Git viene de | Git for Windows | Command Line Tools (`xcode-select --install`) |
| Comando `code` | Opción *Add to PATH* del instalador | *Shell Command: Install 'code' command in PATH* |
| Fichero de perfil | `~/.bashrc` | `~/.zprofile` |
| Carpeta de `pio` | `~/.platformio/penv/Scripts` | `~/.platformio/penv/bin` |

> [!NOTA]
> Hoy no conectas ninguna placa, así que tampoco tienes que autorizar dispositivos USB en macOS.

**Preguntas**

**7.** ¿Qué comando instala Git en macOS mediante las Command Line Tools de Apple?

**8.** Una compañera en macOS añade la línea del PATH de PlatformIO a `~/.bashrc` y `pio` sigue sin aparecer en Terminal. ¿Qué ha pasado?

   a) macOS usa zsh por defecto, que lee `~/.zprofile`, no `~/.bashrc`
   b) En macOS PlatformIO no se puede usar fuera de VS Code
   c) Tenía que usar `penv/Scripts` en lugar de `penv/bin`
   d) Le falta instalar Git Bash para macOS

### platformio.ini: la fuente de verdad · Teoría

PlatformIO separa tres conceptos que conviene no mezclar:

<div class="cards">
  <div class="card"><h4>platform</h4><p>El ecosistema de soporte del fabricante. Aquí, <code>espressif32</code>, fijado a una versión.</p></div>
  <div class="card"><h4>board</h4><p>Una placa concreta: su microcontrolador y parámetros habituales. Aquí, <code>esp32-c3-devkitm-1</code>.</p></div>
  <div class="card"><h4>framework</h4><p>La capa de software con la que programas. Aquí, <code>arduino</code> (Arduino-ESP32).</p></div>
</div>

La plantilla de la asignatura trae exactamente esto:

```ini
[env:esp32-c3-devkitm-1]
platform = espressif32@7.0.1
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
```

Léelo línea a línea como **decisiones reproducibles**:

- `[env:...]` da **nombre al entorno**. Ese nombre aparece luego en la salida de `pio run` y en la carpeta `.pio/build/esp32-c3-devkitm-1/`.
- `platform = espressif32@7.0.1` fija la **versión** de la plataforma, para que toda la clase resuelva el mismo conjunto de paquetes.
- `board` selecciona la ESP32-C3-DevKitM-1.
- `framework = arduino` activa la **API** Arduino-ESP32. **No** significa que uses el IDE de Arduino.
- `monitor_speed = 115200` deja preparado el monitor serie para cuando haya placa.

> [!EXAMEN]
> Distinguir *platform*, *board* y *framework* es pregunta de manual: plataforma = soporte del fabricante, placa = hardware concreto, framework = API con la que escribes.

### Cómo llega la toolchain

No descargas ningún compilador RISC-V. Al procesar el proyecto, el **gestor de paquetes** de PlatformIO resuelve plataforma, toolchain, framework y utilidades, y lo guarda en `~/.platformio`:

```bash
pio pkg install   # instala o completa los paquetes del proyecto
pio pkg list      # muestra lo que ha quedado resuelto
pio project config  # cómo interpreta PlatformIO tu platformio.ini
pio boards esp32-c3 # placas cuyo identificador contiene esp32-c3
```

La primera vez tarda varios minutos y descarga cientos de megabytes; después se reutiliza la caché.

**Preguntas**

**9.** ¿Qué significa `framework = arduino` en nuestra plantilla?

   a) Que hay que compilar desde el IDE de Arduino en vez de VS Code
   b) Que la placa objetivo es una Arduino Uno
   c) Que el programa se escribe con la API Arduino-ESP32
   d) Que se usa la toolchain AVR de Arduino

**10.** ¿Por qué la plantilla escribe `espressif32@7.0.1` y no simplemente `espressif32`?

   a) Porque sin versión PlatformIO no sabe que es una placa Espressif
   b) Para que toda la clase resuelva el mismo conjunto de paquetes y los errores sean reproducibles
   c) Porque la versión 7.0.1 es la única que soporta RISC-V
   d) Para que la compilación sea más rápida

**11.** Escribe el identificador de `board` que usa la plantilla.

### Carpeta de trabajo y proyecto plantilla · Práctica

Partimos de una **plantilla común** en lugar de crear el proyecto desde cero: así todos compilamos para la misma placa, plataforma y framework, y nadie obtiene resultados distintos por haber creado el proyecto otro día.

La plantilla es `pde-esp32c3-eventos.zip` y contiene `platformio.ini`, `src/main.cpp` y un `README.md`. **No** trae la toolchain ni binarios: eso lo resuelve PlatformIO en tu equipo.

### 1. Crea la carpeta

```bash
# Windows · Git Bash
mkdir -p /c/dev/pde
cd /c/dev/pde
pwd

# macOS · Terminal
mkdir -p ~/dev/pde
cd ~/dev/pde
pwd
```

> [!CUIDADO]
> No uses OneDrive, iCloud Drive ni otra carpeta sincronizada: los sincronizadores e indexadores pueden bloquear los ficheros temporales de compilación.

### 2. Descomprime y abre

Copia el ZIP a esa carpeta y extráelo (Windows: clic derecho → «Extraer todo…»; macOS: doble clic). Luego:

```bash
cd /c/dev/pde/pde-esp32c3-eventos   # en macOS: cd ~/dev/pde/pde-esp32c3-eventos
code .
```

Si PlatformIO reconoce `platformio.ini`, la barra inferior muestra **Build**, **Upload** y **Serial Monitor**. Hoy solo usas Build.

### 3. Reconoce la estructura

```text
pde-esp32c3-eventos/
├── platformio.ini
├── README.md
└── src/
    └── main.cpp
```

Al compilar aparecerá además **`.pio`**: paquetes y resultados generados. **No la edites ni la entregues.** Antes de compilar, confirma que estás en la raíz:

```bash
pwd
ls      # debe aparecer platformio.ini
```

Si `ls` no muestra `platformio.ini`, estás un nivel por encima o por debajo y `pio run` no sabrá qué proyecto construir.

**Preguntas**

**12.** Estás en la raíz del proyecto en la terminal. ¿Qué comando abre VS Code en la carpeta actual?

**13.** Tras compilar aparece la carpeta `.pio`. ¿Qué haces con ella al entregar la práctica?

   a) La entregas: contiene el firmware y es lo que se evalúa
   b) No la entregas ni la editas: es contenido generado automáticamente
   c) La renombras a `build` para que sea más claro
   d) La copias dentro de `src/` para que el proyecto sea autocontenido

### Primera compilación sin placa · Práctica

La compilación **no necesita dispositivo**: el objetivo ya está descrito en `platformio.ini`. Desde la raíz del proyecto:

```bash
pio pkg install
pio run
```

La primera vez descargará paquetes y luego compilará y enlazará. Al final debe aparecer **SUCCESS** para el entorno `esp32-c3-devkitm-1`.

### Los artefactos

```bash
ls -lh .pio/build/esp32-c3-devkitm-1/
```

| Fichero | Qué es |
| --- | --- |
| `firmware.elf` | Conserva información útil para el **enlazado y la depuración** (símbolos, secciones). |
| `firmware.bin` | La **imagen binaria** que, en otra sesión, se cargará en la memoria flash de la placa. |

De forma simplificada, `pio run` coordina: fuente C/C++ → preprocesado → compilación → objetos → enlazado → firmware. Tú no invocas `gcc`, el enlazador ni `esptool` a mano.

### Comandos útiles

```bash
pio run -t clean   # limpia la compilación sin borrar las dependencias descargadas
pio run            # vuelve a compilar desde cero
pio run -v         # verbose: compilador, flags, cabeceras y paquetes reales
```

La salida `-v` es enorme: úsala solo cuando necesites saber qué está usando PlatformIO de verdad. Desde la interfaz, lo mismo es *PlatformIO (barra lateral) → Project Tasks → esp32-c3-devkitm-1 → General → Build*.

> [!IMPORTANTE]
> No ejecutes `pio run -t upload` ni `pio device monitor`. Es **normal** que no haya puerto serie ni aparezca ninguna ESP32-C3 en `pio device list`. El objetivo de hoy termina en **SUCCESS** y en `firmware.elf` / `firmware.bin`.

### Verificación completa

```bash
git --version && code --version && pio --version && pio system info
pio project config
pio pkg install
pio pkg list
pio run
ls -lh .pio/build/esp32-c3-devkitm-1/
```

**Preguntas**

**14.** ¿Cuál de los dos artefactos es la imagen que se cargará en la flash de la placa?

   a) `firmware.elf`, porque incluye los símbolos de depuración
   b) `platformio.ini`, que describe el entorno
   c) `firmware.bin`, la imagen binaria
   d) `main.cpp`, que se compila en la propia placa

**15.** Ordena la secuencia de verificación dentro de la raíz del proyecto.

   Ordena: `ls -lh .pio/build/esp32-c3-devkitm-1/` · `pio pkg install` · `pio pkg list` · `pio project config` · `pio run`

**16.** ¿Qué comando limpia la compilación **sin** borrar las dependencias descargadas?

### Diagnóstico I: «command not found» y carpetas · Ejercicio

Cuando algo falla, **busca el primer error significativo**: los mensajes posteriores suelen ser consecuencia de él. Esta es la parte de la tabla de problemas que tiene que ver con la terminal, el PATH y la carpeta en la que estás.

| Síntoma | Causa probable | Solución |
| --- | --- | --- |
| `git: command not found` | Git no instalado o terminal abierta antes de instalarlo | Windows: Git for Windows + Git Bash nueva. macOS: `xcode-select --install` |
| `code: command not found` | VS Code no está en el PATH | Windows: reinstalar con *Add to PATH* o terminal nueva. macOS: *Shell Command: Install 'code' command in PATH* |
| `pio: command not found` | PlatformIO Core no está en el PATH o la extensión no terminó | Abrir PIO Home, esperar y aplicar el PATH del apartado de tu sistema |
| `Not a PlatformIO project` / no `platformio.ini` | Ejecutas `pio` en otra carpeta | `pwd` y `ls`; entrar en `pde-esp32c3-eventos` |

> [!TRUCO] Método en tres preguntas
> 1. ¿El programa está **instalado**?
> 2. ¿La terminal **lo encuentra** (PATH, terminal nueva)?
> 3. ¿Estoy en la **carpeta** correcta?

Ahora tú. Lee cada caso, decide la causa y la primera acción.

<details><summary>Caso de ejemplo resuelto</summary>
<p>Javier instala VS Code con Git Bash ya abierta y escribe <code>code --version</code>: <code>command not found</code>. Causa: la terminal leyó el PATH antes de la instalación. Acción: cerrar Git Bash y abrir una nueva. Si persiste, reinstalar marcando <i>Add to PATH</i>.</p>
</details>

**Preguntas**

**17.** Lucía instaló la extensión PlatformIO IDE hace un minuto. `git --version` y `code --version` responden, pero `pio --version` da `command not found`. ¿Qué haces primero?

   a) Borrar `~/.platformio` y reinstalar la extensión
   b) Instalar Python y PlatformIO Core a mano con pip
   c) Abrir PIO Home, esperar a que termine, aplicar el PATH de PlatformIO y abrir una terminal nueva
   d) Cambiar `platform` en `platformio.ini`

**18.** Marcos ejecuta `pio run` en `/c/dev/pde` y obtiene `Not a PlatformIO project`. ¿Cuál es la causa?

   a) Está una carpeta por encima: `platformio.ini` está en `pde-esp32c3-eventos`
   b) Le falta ejecutar `pio pkg install`
   c) El `board` está mal escrito
   d) No tiene la placa conectada

**19.** ¿Qué comando muestra la carpeta en la que está tu terminal ahora mismo?

### Diagnóstico II: red, disco, permisos y falsas alarmas · Ejercicio

La segunda mitad de la tabla trata de lo que pasa **dentro** del proyecto: paquetes, red, disco y hardware.

| Síntoma | Causa probable | Solución |
| --- | --- | --- |
| `Unknown board ID` | `board` incorrecto o plataforma no resuelta | `board = esp32-c3-devkitm-1` y `pio pkg install` |
| `Could not install package` / HTTP error | Red, proxy, DNS o firewall | Otra red; revisar proxy; repetir `pio pkg install` |
| `No space left on device` | Sin espacio para paquetes o temporales | Liberar varios GB y recompilar |
| `Permission denied` en `~/.platformio` | Carpeta creada con otro usuario | **No** ejecutar PlatformIO como administrador/root; corregir permisos |
| Error de compilación en `main.cpp` | Código modificado o incompleto | Restaurar `src/main.cpp` del ZIP y `pio run` |
| No aparece ningún puerto serie | No hay placa | **Normal hoy**: no es un fallo |
| `Upload failed` / device not found | Se intentó cargar sin hardware | No usar `upload` hasta tener placa |

### Red del campus y proxy

Si la descarga va en casa pero falla en el campus, **no toques `platformio.ini`**: el problema es de conectividad, no de la placa. Si el centro usa un proxy, se configura de forma temporal en la terminal con los datos reales que te den:

```bash
export HTTP_PROXY=http://proxy.ejemplo:8080
export HTTPS_PROXY=http://proxy.ejemplo:8080
pio pkg install
```

No dejes contraseñas en documentos compartidos ni en el historial. Si una descarga queda a medias, vuelve a lanzar `pio pkg install`: PlatformIO detecta lo que falta y continúa.

### Si tu equipo no puede

PlatformIO Core desde Python (avanzado, no recomendado para la clase), **Wokwi** (simulador web con ESP32, GPIO, botones e interrupciones), un equipo del aula, o hacer la primera descarga en otra red y conservar el perfil de usuario.

**Preguntas**

**20.** En casa `pio pkg install` funciona; en la Wi-Fi del campus falla con `HTTP error`. Un compañero sugiere cambiar `platform` a otra versión. ¿Qué respondes?

   a) Buena idea: otra versión puede estar en un servidor distinto
   b) Mejor cambiar `board`, que es lo que descarga los paquetes
   c) Hay que borrar `~/.platformio` y empezar de cero
   d) Es un problema de conectividad: revisar proxy u otra red y repetir `pio pkg install`, sin tocar `platformio.ini`

**21.** En esta primera sesión sin placa, ¿qué situaciones son **normales** y no indican fallo del entorno?

   a) `pio device list` no muestra ninguna ESP32-C3
   b) `pio run` termina con `No space left on device`
   c) No aparece ningún puerto serie
   d) `pio run -t upload` falla con *device not found*
   e) `Unknown board ID` al compilar

**22.** Te sale `Permission denied` en `~/.platformio`. ¿Qué **no** debes hacer?

   a) Ejecutar PlatformIO como administrador o con `sudo`
   b) Revisar los permisos de tu carpeta de usuario
   c) Comprobar con qué usuario se creó la carpeta
   d) Leer el primer error significativo del registro

### Repaso: chuleta del entorno · Repaso

> [!RESUMEN] El entorno en cinco pasos
> 1. Instala **Git** y **VS Code**.
> 2. Añade **PlatformIO IDE**.
> 3. Haz accesible **pio** en el PATH.
> 4. Abre la plantilla con su `platformio.ini`.
> 5. `pio pkg install` + `pio run` → **SUCCESS**, `firmware.elf` y `firmware.bin`.

| Concepto | Lo que debes recordar |
| --- | --- |
| Anfitrión / objetivo | Compilas en Windows/macOS; el código es para el RISC-V de la ESP32-C3 |
| `platformio.ini` | Fuente de verdad: `env`, `platform`, `board`, `framework`, `monitor_speed` |
| Toolchain | La resuelve PlatformIO en `~/.platformio`; nunca a mano |
| PATH de `pio` | Windows: `penv/Scripts` en `~/.bashrc`. macOS: `penv/bin` en `~/.zprofile` |
| Artefactos | `.pio/build/esp32-c3-devkitm-1/firmware.elf` y `firmware.bin` |
| Hoy no | `upload`, `device monitor`, puerto serie, BOOT |

**Lista final:** `git`, `code` y `pio` responden · ruta simple (`C:\dev\pde` o `~/dev/pde`) · VS Code reconoce `platformio.ini` · `pio project config` muestra el entorno · `pio pkg install` y `pio pkg list` correctos · `pio run` en SUCCESS · existen los dos artefactos.

**Preguntas**

**23.** «Si compila, el botón funciona.» ¿Es correcta la afirmación?

   a) Sí: si el código compila, el comportamiento eléctrico está garantizado
   b) No: compilar valida la cadena de herramientas y el código, pero el comportamiento eléctrico solo se verifica con la placa
   c) Sí, siempre que `monitor_speed` sea 115200
   d) No: compilar sin placa no demuestra nada

**24.** ¿Qué comando instala o completa los paquetes del proyecto (y retoma una descarga interrumpida)?

**25.** ¿Qué claves de `platformio.ini` **no** debes cambiar salvo indicación expresa del profesor?

   a) `platform`
   b) `board`
   c) `framework`
   d) Comentarios que añadas tú

### Solucionario

1. b) Para el núcleo RISC-V de 32 bits de la ESP32-C3  
   El anfitrión (Windows/macOS) solo compila. El código generado es para el **objetivo**: el núcleo RISC-V de la ESP32-C3. Por eso PlatformIO descarga una toolchain RISC-V.
2. a) Que el compilador y el enlazado funcionan; c) Que el framework Arduino-ESP32 está resuelto; e) Que el código fuente no tiene errores de compilación  
   Compilar valida la cadena de herramientas: código, framework, compilador y enlazado. El comportamiento eléctrico (botón, interrupciones reales) y el puerto serie solo se verifican con la placa.
3. c) `C:\dev\pde`  
   `C:\dev\pde` es corta, sin tildes ni espacios y no está sincronizada. OneDrive o iCloud pueden bloquear ficheros temporales de compilación.
4. a) Instalar a mano el compilador GCC para RISC-V; c) Cambiar `board` en `platformio.ini` porque «suena mejor otra placa»; d) Ejecutar `pio run -t upload` hoy para ver qué pasa  
   La toolchain la gestiona PlatformIO, el `platformio.ini` es común a toda la clase y hoy no se carga nada en la placa. Git Bash y dejar a PlatformIO gestionar `~/.platformio` son justo lo recomendado.
5. d) `$HOME/.platformio/penv/Scripts`  
   En Windows el entorno virtual de PlatformIO deja los ejecutables en `penv/Scripts`. En macOS la carpeta equivalente es `penv/bin`.
6. `pio --version`  
   `pio --version` debe mostrar una versión. Junto con `git --version` y `code --version`, es el criterio de que la instalación está lista.
7. `xcode-select --install`  
   `xcode-select --install` abre la ventana del sistema que instala las Command Line Tools, que incluyen Git.
8. a) macOS usa zsh por defecto, que lee `~/.zprofile`, no `~/.bashrc`  
   zsh no lee `~/.bashrc`. La guía usa `~/.zprofile` con `penv/bin`. `penv/Scripts` es la carpeta de Windows y Git Bash solo existe allí.
9. c) Que el programa se escribe con la API Arduino-ESP32  
   `framework = arduino` selecciona Arduino-ESP32 como API de programación. Seguimos en VS Code + PlatformIO y la placa sigue siendo la ESP32-C3.
10. b) Para que toda la clase resuelva el mismo conjunto de paquetes y los errores sean reproducibles  
   Fijar la versión hace que todos obtengan las mismas versiones de plataforma y paquetes, así un error es reproducible en cualquier equipo de la clase.
11. `esp32-c3-devkitm-1`  
   `board = esp32-c3-devkitm-1`, el identificador de PlatformIO para la ESP32-C3-DevKitM-1.
12. `code .`  
   `code .` abre VS Code con la carpeta actual como proyecto (el punto es «aquí»).
13. b) No la entregas ni la editas: es contenido generado automáticamente  
   `.pio` contiene paquetes y resultados generados. Se regenera con `pio run`; no forma parte del código fuente.
14. c) `firmware.bin`, la imagen binaria  
   `firmware.bin` es la imagen binaria para la flash. `firmware.elf` conserva información para enlazado y depuración.
15. 1. `pio project config` → 2. `pio pkg install` → 3. `pio pkg list` → 4. `pio run` → 5. `ls -lh .pio/build/esp32-c3-devkitm-1/`  
   Primero ves cómo interpreta PlatformIO la configuración, luego resuelves y listas paquetes, compilas y, por último, compruebas que existen los artefactos.
16. `pio run -t clean`  
   `pio run -t clean` borra los resultados de compilación; los paquetes de `~/.platformio` se conservan.
17. c) Abrir PIO Home, esperar a que termine, aplicar el PATH de PlatformIO y abrir una terminal nueva  
   La causa probable es que el Core no está en el PATH o no terminó de instalarse. Borrar `~/.platformio` sin diagnóstico va contra las reglas, y `platformio.ini` no tiene nada que ver con que la terminal encuentre `pio`.
18. a) Está una carpeta por encima: `platformio.ini` está en `pde-esp32c3-eventos`  
   En `/c/dev/pde` solo está la carpeta del proyecto, no `platformio.ini`. Hay que hacer `cd pde-esp32c3-eventos` y repetir.
19. `pwd`  
   `pwd` (*print working directory*) muestra la ruta actual; `ls` lista lo que hay dentro.
20. d) Es un problema de conectividad: revisar proxy u otra red y repetir `pio pkg install`, sin tocar `platformio.ini`  
   La guía insiste: un fallo de red no se arregla editando `platformio.ini`. Se revisa proxy/red y se repite `pio pkg install`, que reanuda lo que falte.
21. a) `pio device list` no muestra ninguna ESP32-C3; c) No aparece ningún puerto serie; d) `pio run -t upload` falla con *device not found*  
   Sin placa es normal que no haya puerto ni dispositivo, y que `upload` falle (por eso no se lanza hoy). Falta de espacio y `Unknown board ID` sí son problemas reales del entorno.
22. a) Ejecutar PlatformIO como administrador o con `sudo`  
   Ejecutar como administrador/root suele ser la causa del problema (carpetas creadas con otro propietario). La solución es corregir los permisos de tu carpeta de usuario.
23. b) No: compilar valida la cadena de herramientas y el código, pero el comportamiento eléctrico solo se verifica con la placa  
   Hay que distinguir compilación de ejecución. Compilar sin placa es una comprobación real (código, framework, compilador, enlazado), pero no prueba el hardware.
24. `pio pkg install`  
   `pio pkg install` resuelve plataforma, toolchain y framework; si algo quedó a medias, descarga solo lo que falta.
25. a) `platform`; b) `board`; c) `framework`  
   `platform`, `board` y `framework` se fijan para toda la clase y garantizan una construcción reproducible.

## Del pin al loop(): el evento sin hardware

_Sigue en el código de la plantilla el recorrido señal → GPIO → interrupción → ISR → estado compartido → loop(): GPIO9 y BOOT, INPUT_PULLUP, flanco FALLING, attachInterrupt, volatile y antirrebote, y compáralo con un onClick de Android._

### Un evento empieza mucho antes del callback · Teoría

Cuando en Android se ejecuta tu `onClick`, el sistema ya ha hecho muchísimo trabajo por ti: el táctil detectó el dedo, el sistema decidió a qué vista pertenece, la gestionó el hilo principal... Tú solo ves el final.

En un microcontrolador ese trabajo **no está escondido**. Puedes seguir el evento desde que cambia la tensión de un pin hasta que tu código decide qué hacer. Y aparece el mismo esquema que en cualquier sistema dirigido por eventos:

<div class="flow">
  <div><b>Suceso</b><small>algo cambia fuera</small></div>
  <div><b>Detección</b><small>alguien lo nota</small></div>
  <div><b>Manejador</b><small>código que reacciona</small></div>
  <div><b>Estado</b><small>queda constancia</small></div>
  <div><b>Consumo</b><small>se interpreta y actúa</small></div>
</div>

| | Android (`onClick`) | ESP32-C3 (GPIO) |
| --- | --- | --- |
| Nivel | Alto: muchas capas ya han procesado la entrada | Bajo: ves el pin, la interrupción y la rutina |
| Manejador | Un método normal en el hilo principal | Una **ISR**, que interrumpe lo que se estuviera ejecutando |
| Duración permitida | Corta, para no congelar la interfaz | **Extremadamente** corta |

> [!NOTA]
> La relación con Android es **conceptual**: comparten el patrón, no el mecanismo. Una ISR trabaja muy por debajo de un listener y tiene reglas mucho más estrictas.

Hoy no hay placa, así que **no** podemos medir la parte eléctrica real: el nivel de tensión, el rebote del pulsador o el instante exacto en que el periférico pide la interrupción. Lo que sí podemos es leer el código y saber qué configura cada etapa cuando el firmware se ejecute.

**Preguntas**

**1.** Ordena el patrón general que comparten la ESP32 y Android.

   Ordena: Consumo · Detección · Estado · Manejador · Suceso

**2.** Sin placa, ¿qué **no** podemos comprobar hoy?

   a) El nivel de tensión real del pin
   b) Que el código de la ISR compila
   c) El rebote mecánico del pulsador
   d) El instante en que el periférico GPIO solicita la interrupción
   e) Qué variable usa la ISR para dejar constancia del evento

### GPIO9, botón BOOT, pull-up y flanco FALLING · Teoría

El programa de la plantilla está escrito para observar un pulsador en el **GPIO9** de la placa de referencia: en la ESP32-C3-DevKitM-1, el botón **BOOT** lleva el GPIO9 a nivel bajo al pulsarlo.

Un **GPIO** (*General-Purpose Input/Output*) es un pin digital configurable como entrada o salida, y puede ser **fuente de interrupciones**.

```cpp
pinMode(PIN_BOTON, INPUT_PULLUP);
attachInterrupt(PIN_BOTON, alPulsarBoton, FALLING);
```

### INPUT_PULLUP: un nivel de reposo conocido

Una entrada «al aire» no tiene un valor fiable. Una **resistencia pull-up** la mantiene en **nivel alto** mientras nadie la toque. `INPUT_PULLUP` activa una resistencia pull-up **interna** del chip, así que no hace falta añadirla por fuera.

> [!ANALOGIA]
> El pull-up es un muelle que mantiene una puerta abierta (nivel alto). Pulsar el botón es empujar la puerta y cerrarla (nivel bajo). Al soltar, el muelle la devuelve a su sitio.

### FALLING: el momento exacto que nos interesa

Al pulsar, el pin pasa de **alto a bajo**: es un **flanco descendente**. Justo eso es lo que pide `FALLING`.

```text
nivel   alto ───────┐           ┌──────── alto
                    │  pulsado  │
        bajo        └───────────┘
                    ↑ FALLING   ↑ (RISING, no nos interesa)
```

Fíjate en la combinación: el pull-up deja el pin **alto en reposo**, y el botón lo baja. Por eso el evento natural «se ha pulsado» es el flanco **descendente**. Si el circuito estuviera al revés (reposo bajo, pulsado alto), el flanco de pulsación sería el ascendente.

> [!EXAMEN]
> Pregunta típica: «¿por qué `FALLING` y no `RISING`?». Respuesta: porque con `INPUT_PULLUP` el reposo es nivel alto y la pulsación lleva el pin a bajo; esa transición alto → bajo es el flanco descendente.

**Preguntas**

**3.** Con `INPUT_PULLUP` y el botón sin pulsar, ¿en qué nivel está el GPIO9?

   a) Alto, gracias a la resistencia pull-up interna
   b) Bajo, porque no hay corriente
   c) Indefinido, oscila entre alto y bajo
   d) Alterna a 115200 baudios

**4.** ¿Qué constante de `attachInterrupt` indica una transición de nivel alto a nivel bajo?

### attachInterrupt y la ISR mínima · Teoría

Este es el código esencial de `src/main.cpp` de la plantilla:

```cpp
constexpr uint8_t PIN_BOTON = 9;
volatile uint32_t eventosHardware = 0;
volatile bool eventoPendiente = false;

void ARDUINO_ISR_ATTR alPulsarBoton() {
  ++eventosHardware;
  eventoPendiente = true;
}

void setup() {
  pinMode(PIN_BOTON, INPUT_PULLUP);
  attachInterrupt(PIN_BOTON, alPulsarBoton, FALLING);
}

void loop() {
  // Copia el estado compartido y procesa el evento fuera de la ISR.
}
```

### Qué hace cada pieza

- **`attachInterrupt(pin, función, modo)`** registra el manejador: «cuando en este pin ocurra este flanco, ejecuta esta función». Es el equivalente de bajo nivel a `setOnClickListener`.
- **Interrupción**: el periférico GPIO solicita atención al procesador, que **desvía temporalmente** la ejecución hacia la rutina asociada y luego vuelve a donde estaba.
- **ISR** (*Interrupt Service Routine*): esa rutina, aquí `alPulsarBoton()`. `ARDUINO_ISR_ATTR` es la marca que Arduino-ESP32 usa para las funciones de interrupción.

### La regla de oro de la ISR: lo mínimo imprescindible

Una ISR puede ejecutarse en **cualquier momento** respecto al flujo normal. Por eso no conviene meter en ella:

- operaciones lentas o esperas (`delay`),
- lógica de aplicación compleja,
- accesos que dependan de bloqueos.

La de la plantilla solo hace dos cosas: **incrementa un contador** y **marca un indicador**. Detectar y registrar; nada más.

> [!ANALOGIA]
> La ISR es el conserje que oye el timbre: apunta «ha llamado alguien» en una libreta y vuelve a su puesto. No sube a atender la visita; eso lo hará quien lea la libreta (`loop()`).

**Preguntas**

**5.** ¿Qué línea **registra** el manejador del evento?

   a) `pinMode(PIN_BOTON, INPUT_PULLUP);`
   b) `volatile bool eventoPendiente = false;`
   c) `++eventosHardware;`
   d) `attachInterrupt(PIN_BOTON, alPulsarBoton, FALLING);`

**6.** ¿Qué **no** debería hacer una ISR?

   a) Esperar con `delay(500)`
   b) Incrementar un contador
   c) Ejecutar la lógica completa de la aplicación
   d) Marcar un indicador booleano
   e) Esperar a que se libere un bloqueo

### Ejercicio: recorre el evento etapa a etapa · Ejercicio

Juntemos todo en una tabla. Cada fila es una etapa del recorrido y, a la derecha, **dónde la vemos hoy**, sin placa.

| Etapa | Qué ocurre | Dónde lo vemos hoy |
| --- | --- | --- |
| 1. Señal física | La tensión del GPIO pasa de alto a bajo | Solo la razonamos |
| 2. GPIO | La lógica de entrada detecta el nivel y el flanco configurado | `pinMode` y `FALLING` |
| 3. Interrupción | El periférico solicita atención al procesador | `attachInterrupt` registra el manejador |
| 4. ISR | El procesador desvía la ejecución a `alPulsarBoton()` | Podemos leer y compilar la función |
| 5. Estado | La ISR deja constancia para el resto del programa | `eventoPendiente` y `eventosHardware` |
| 6. Consumo | `loop()` procesa el evento en contexto normal | Seguimos la lógica y verificamos que compila |

![Arriba, la forma de onda del GPIO9 con INPUT_PULLUP: en reposo está en alto gracias al pull-up, al pulsar BOOT baja (ese flanco descendente es FALLING) y al soltar vuelve a subir (RISING, que no nos interesa). Debajo, el recorrido en dos filas. Hardware: 1 Señal, la tensión pasa a bajo; 2 GPIO, detecta el flanco; 3 Interrupción, avisa al procesador. Software: 4 ISR alPulsarBoton(), detecta y registra; 5 Estado, eventoPendiente y eventosHardware; 6 loop(), interpreta y actúa en contexto normal.](/img/pde/tema-0/recorrido-evento.svg "El recorrido del evento: del flanco FALLING en el pin hasta loop()")

> [!EXAMEN]
> El recorrido **GPIO → interrupción → ISR → estado compartido → loop()** aparece literalmente en los objetivos de la unidad. Tienes que saber ordenarlo y señalar en el código qué línea corresponde a cada etapa.

Haz el ejercicio sin mirar la tabla: tápala con la mano.

**Preguntas**

**7.** Ordena el recorrido del evento desde el botón hasta tu código.

   Ordena: Consumo: `loop()` procesa el evento · Estado: se actualizan `eventoPendiente` y `eventosHardware` · GPIO: la lógica de entrada detecta el flanco FALLING · ISR: se ejecuta `alPulsarBoton()` · Interrupción: el periférico pide atención al procesador · Señal física: la tensión del pin baja

**8.** ¿En qué etapa del recorrido encaja la línea `eventoPendiente = true;`?

   a) GPIO
   b) Interrupción
   c) Estado
   d) Consumo

### volatile, estado compartido y antirrebote · Teoría

### ¿Por qué `volatile`?

`eventosHardware` y `eventoPendiente` se modifican **desde la ISR** y se leen **desde `loop()`**. Para el compilador, `loop()` es un bucle normal y podría «optimizar» la lectura guardando el valor en un registro y no volviendo a mirar la memoria. `volatile` le dice: *esta variable puede cambiar por fuera del flujo que ves; léela de memoria cada vez*.

### Detectar y registrar ≠ interpretar y actuar

La plantilla separa dos responsabilidades:

<div class="cards">
  <div class="card"><h4>ISR</h4><p>Detecta y registra. Brevísima. Contador + indicador.</p></div>
  <div class="card"><h4>loop()</h4><p>Copia el estado compartido e interpreta y actúa en contexto normal, donde sí puede tardar.</p></div>
</div>

Esta separación volverá con temporizadores, comunicaciones, sensores y otros periféricos.

### Antirrebote (*debounce*)

Un pulsador mecánico no pasa limpiamente de alto a bajo: sus contactos **rebotan** y pueden producir **varios flancos en una sola pulsación**. Cada uno dispara la ISR. Por eso el ejemplo añade un **antirrebote temporal** en el consumo: se ignoran los avisos que llegan demasiado pronto tras el último aceptado.

Un boceto orientativo de cómo puede quedar `loop()` (la plantilla trae su propia versión):

```cpp
void loop() {
  static uint32_t ultimaAceptada = 0;
  if (eventoPendiente) {
    eventoPendiente = false;               // consumimos el aviso
    uint32_t total = eventosHardware;      // copia del estado compartido
    uint32_t ahora = millis();
    if (ahora - ultimaAceptada >= 50) {    // antirrebote: p. ej. 50 ms
      ultimaAceptada = ahora;
      Serial.printf("Pulsación aceptada (flancos vistos por la ISR: %lu)\n",
                    (unsigned long) total);
    }
  }
}
```

Observa que el contador `eventosHardware` puede crecer más que las pulsaciones aceptadas: la ISR cuenta **flancos**, `loop()` decide cuáles son **pulsaciones**.

![Línea temporal de una pulsación con rebote. Arriba, el GPIO9 baja tres veces seguidas: tres flancos FALLING. En cada flanco se ejecuta una ISR brevísima que suma uno, y eventosHardware pasa de 1 a 2 y a 3. Debajo, loop() queda partido en trozos porque la CPU lo desvía a la ISR en cada flanco y luego vuelve. Al final, loop() lee el aviso, interpreta y actúa: total igual a 3 flancos, 1 pulsación.](/img/pde/tema-0/isr-vs-loop.svg "La ISR detecta y registra cada flanco; loop() interpreta y decide cuántas pulsaciones hubo")

**Preguntas**

**9.** ¿Por qué `eventoPendiente` se declara `volatile`?

   a) Para que se guarde en la flash y sobreviva a un reinicio
   b) Porque cambia desde la ISR y el compilador no debe suponer que su valor sigue igual entre lecturas
   c) Para que la ISR se ejecute más rápido
   d) Porque es obligatorio en todas las variables globales de Arduino

**10.** Pulsas BOOT **una** vez y `eventosHardware` pasa de 0 a 3. ¿Qué ha ocurrido?

   a) La ISR tiene un bucle que suma tres veces
   b) El pull-up interno está mal configurado
   c) `FALLING` dispara en los dos flancos y además en el nivel
   d) El rebote mecánico ha producido varios flancos y cada uno ha disparado la ISR

### Ejercicio: identifica las piezas en main.cpp · Ejercicio

La guía propone este ejercicio para hacer **sin placa**: localiza en `src/main.cpp` cada pieza del evento y compáralo después con un `onClick`, donde las capas inferiores ya están ocultas.

```cpp
constexpr uint8_t PIN_BOTON = 9;                       // (A)
volatile uint32_t eventosHardware = 0;                 // (B)
volatile bool eventoPendiente = false;                 // (B)

void ARDUINO_ISR_ATTR alPulsarBoton() {                // (C)
  ++eventosHardware;
  eventoPendiente = true;
}

void setup() {
  pinMode(PIN_BOTON, INPUT_PULLUP);                    // (D)
  attachInterrupt(PIN_BOTON, alPulsarBoton, FALLING);  // (E)
}

void loop() {                                          // (F)
  // Copia el estado compartido y procesa el evento fuera de la ISR.
}
```

Piensa en el equivalente Android de cada letra:

```kotlin
boton.setOnClickListener {      // registro del manejador
    pulsaciones++               // manejador que cambia el estado
    actualizarVista()           // consumo: refleja el estado en la UI
}
```

<details><summary>Ver solución completa</summary>
<p>(A) identifica el origen; (D) configura el origen (entrada con pull-up); (E) registra el manejador y el tipo de evento; (C) es la ISR; (B) es el estado compartido; (F) es el consumo. En Android, el «origen» (el táctil, la vista) y la detección ya vienen resueltos por el sistema: solo registras y escribes el manejador.</p>
</details>

**Preguntas**

**11.** ¿Qué papel juega la línea (D), `pinMode(PIN_BOTON, INPUT_PULLUP);`?

   a) Registra la ISR
   b) Configura el origen del evento: el pin como entrada con pull-up
   c) Consume el evento
   d) Declara el estado compartido

**12.** ¿Qué línea de la ESP32 hace el papel de `boton.setOnClickListener { ... }`?

   a) (A) `constexpr uint8_t PIN_BOTON = 9;`
   b) (F) `void loop()`
   c) (E) `attachInterrupt(PIN_BOTON, alPulsarBoton, FALLING);`
   d) (B) `volatile bool eventoPendiente = false;`

### Repaso: glosario y preguntas de examen · Repaso

> [!RESUMEN] Del pin al loop()
> El botón BOOT baja el **GPIO9**, que en reposo está alto por `INPUT_PULLUP`. Esa transición es un flanco **FALLING**. `attachInterrupt` registra la **ISR** `alPulsarBoton()`, que solo incrementa un contador y marca un indicador `volatile`. `loop()` copia ese estado, aplica **antirrebote** y actúa. **La ISR detecta y registra; el resto del programa interpreta.**

| Término | Definición breve |
| --- | --- |
| **ESP32-C3** | Microcontrolador de Espressif con CPU RISC-V de 32 bits, Wi-Fi y Bluetooth Low Energy |
| **RISC-V** | Arquitectura abierta de conjunto de instrucciones |
| **Toolchain** | Compilador, ensamblador, enlazador y utilidades que generan firmware para una arquitectura |
| **Firmware** | Programa compilado para ejecutarse en un sistema empotrado |
| **Build** | Transformar el código fuente en firmware (compilación + enlazado) |
| **Artefacto** | Fichero generado por la construcción: `firmware.elf`, `firmware.bin` |
| **Board** | Identificador de una placa concreta en PlatformIO |
| **Arduino-ESP32** | API de estilo Arduino sobre los ESP32; se descarga con `framework = arduino` |
| **GPIO** | Pin digital de entrada/salida, posible fuente de interrupciones |
| **Pull-up** | Resistencia que mantiene una entrada en nivel alto |
| **FALLING** | Disparo en transición de alto a bajo |
| **Interrupción** | Un periférico desvía temporalmente la ejecución a una rutina |
| **ISR** | Rutina de interrupción; corta y sin trabajo bloqueante |
| **Debounce** | Filtrar las transiciones rápidas de un pulsador mecánico |
| **Serial Monitor** | Leer y enviar datos por puerto serie (con placa conectada) |
| **Git Bash** | Terminal de Git for Windows con entorno tipo Unix |

**Preguntas**

**13.** ¿Cómo se llama el conjunto de compilador, ensamblador, enlazador y utilidades que transforma el código fuente en firmware para una arquitectura concreta?

**14.** ¿Cuál es la definición correcta de ISR?

   a) Rutina que se ejecuta al desviar el procesador la ejecución por una interrupción; debe ser corta y no bloqueante
   b) Bucle principal del firmware que se repite indefinidamente
   c) Resistencia interna que fija el nivel de un pin
   d) Herramienta para leer el puerto serie

**15.** Marca las afirmaciones **correctas**.

   a) El núcleo de la ESP32-C3 implementa una variante RISC-V de 32 bits
   b) `INPUT_PULLUP` deja el pin en nivel bajo en reposo
   c) `firmware.elf` y `firmware.bin` son artefactos de compilación
   d) El antirrebote se hace dentro de la ISR con `delay`
   e) `board` identifica una placa concreta dentro de PlatformIO

**16.** Pregunta de examen: ¿qué reparto de trabajo propone la plantilla entre la ISR y `loop()`?

   a) La ISR interpreta y actúa; `loop()` solo duerme
   b) Ambas hacen lo mismo para tener redundancia
   c) La ISR detecta y registra; `loop()` copia el estado, interpreta y actúa
   d) `loop()` detecta el flanco por sondeo y la ISR imprime por serie

### Solucionario

1. 1. Suceso → 2. Detección → 3. Manejador → 4. Estado → 5. Consumo  
   Suceso → detección → manejador → estado → consumo. En Android las primeras etapas las hacen capas que no ves; en la ESP32 las configuras tú.
2. a) El nivel de tensión real del pin; c) El rebote mecánico del pulsador; d) El instante en que el periférico GPIO solicita la interrupción  
   Lo eléctrico y temporal (tensión, rebote, instante de la petición) necesita hardware. Leer y compilar el código, incluida la ISR y sus variables, sí se puede hacer hoy.
3. a) Alto, gracias a la resistencia pull-up interna  
   La pull-up interna mantiene la entrada en nivel alto en reposo. Al pulsar BOOT, el pin baja.
4. `FALLING`  
   `FALLING` es la condición de disparo para el flanco descendente (alto → bajo).
5. d) `attachInterrupt(PIN_BOTON, alPulsarBoton, FALLING);`  
   `attachInterrupt` asocia el pin, la función `alPulsarBoton` y el modo `FALLING`. `pinMode` configura el origen, no registra nada.
6. a) Esperar con `delay(500)`; c) Ejecutar la lógica completa de la aplicación; e) Esperar a que se libere un bloqueo  
   La ISR debe ser brevísima: registrar (contador, indicador) y salir. Esperas, lógica compleja y bloqueos van fuera, en el contexto normal.
7. 1. Señal física: la tensión del pin baja → 2. GPIO: la lógica de entrada detecta el flanco FALLING → 3. Interrupción: el periférico pide atención al procesador → 4. ISR: se ejecuta `alPulsarBoton()` → 5. Estado: se actualizan `eventoPendiente` y `eventosHardware` → 6. Consumo: `loop()` procesa el evento  
   Señal → GPIO → interrupción → ISR → estado → consumo. La ISR solo llega hasta el estado; el consumo ocurre después, en contexto normal.
8. c) Estado  
   Se ejecuta dentro de la ISR, pero su efecto es dejar constancia del suceso: es la etapa de **estado compartido** que luego leerá `loop()`.
9. b) Porque cambia desde la ISR y el compilador no debe suponer que su valor sigue igual entre lecturas  
   La escribe la ISR y la lee `loop()`. `volatile` obliga a leerla de memoria cada vez, en lugar de reutilizar un valor que el compilador creía constante.
10. d) El rebote mecánico ha producido varios flancos y cada uno ha disparado la ISR  
   Los contactos rebotan y generan varios flancos descendentes. Por eso el consumo en `loop()` aplica un antirrebote temporal.
11. b) Configura el origen del evento: el pin como entrada con pull-up  
   `pinMode` prepara el origen: el GPIO9 como entrada con resistencia pull-up interna. El registro lo hace (E).
12. c) (E) `attachInterrupt(PIN_BOTON, alPulsarBoton, FALLING);`  
   Ambas registran un manejador para un tipo de evento de una fuente. La diferencia es el nivel: `attachInterrupt` trabaja directamente con el pin y la interrupción.
13. `toolchain`  
   Es la **toolchain**. PlatformIO descarga la de RISC-V automáticamente.
14. a) Rutina que se ejecuta al desviar el procesador la ejecución por una interrupción; debe ser corta y no bloqueante  
   ISR = *Interrupt Service Routine*. El bucle principal es `loop()`, la resistencia es el pull-up y la herramienta es el Serial Monitor.
15. a) El núcleo de la ESP32-C3 implementa una variante RISC-V de 32 bits; c) `firmware.elf` y `firmware.bin` son artefactos de compilación; e) `board` identifica una placa concreta dentro de PlatformIO  
   `INPUT_PULLUP` deja el pin **alto** en reposo, y la ISR nunca debe esperar con `delay`: el antirrebote se aplica al consumir el evento en `loop()`.
16. c) La ISR detecta y registra; `loop()` copia el estado, interpreta y actúa  
   Separar «detectar y registrar» (ISR) de «interpretar y actuar» (contexto normal) mantiene la ISR mínima y es la idea que se repetirá con temporizadores y comunicaciones.
