Controlar una salida de tu Raspberry Pi Pico desde el navegador del celular, sin apps ni servicios en la nube, es más fácil de lo que parece: basta con levantar un pequeño servidor web dentro de la propia Pico. En este tutorial vas a programar una Raspberry Pi Pico W (o Pico 2W) con el IDE de Arduino para servir una página con dos botones que encienden y apagan una salida, y. dándole una vuelta al proyecto original. vamos a controlar un relé en lugar de un simple LED, para que puedas manejar algo real: una ampolleta, un ventilador o una bomba. Al final vas a saber cómo funciona un servidor web asíncrono en la Pico y cómo reflejar el estado del GPIO en la página.

Importante: este proyecto solo sirve con Raspberry Pi Pico W y Raspberry Pi Pico 2W, que son las que tienen WiFi. Una Pico común (sin W) no puede levantar el servidor.

Qué vamos a construir

Cuando escribes la dirección IP de la Pico en el navegador, haces una petición al servidor (la Pico) en la ruta raíz /. La Pico responde con el HTML de una página simple con dos botones. Al tocar cada botón se dispara una nueva petición:

  • Botón encender: petición a /lighton?
  • Botón apagar: petición a /lightoff?

La página usa una plantilla HTML que inserta el estado actual de la salida justo después de cada petición. Ojo con un detalle: no confirma el estado real del pin, solo lo actualiza según la petición que se hizo.

Página web servida por la Raspberry Pi Pico con botones de control de salida

Instalar las librerías

Para crear el servidor web asíncrono usamos la librería ESPAsyncWebServer, la misma que se ocupa en ESP32 y ESP8266; también es compatible con la RP2040/RP2350 de la Pico. Para la Pico necesitas además la librería RPAsyncTCP.

En el IDE de Arduino ve a Sketch > Include Library > Manage Libraries (o el ícono del Library Manager en la barra lateral) e instala:

  1. ESPAsyncWebServer por ESP32Async
  2. RPAsyncTCP por Ayush Sharma

Instalar la librería ESPAsyncWebServer en el IDE de Arduino

Hardware y conexiones

El código controla la salida en GPIO 2. Puedes usar cualquier otro GPIO si ajustas la línea correspondiente del código. El tutorial original enciende un LED con su resistencia de 220Ω; nosotros vamos a conectar en ese mismo pin un módulo relé de 1 canal, que trae optoacoplador y se comanda igual con un nivel alto/bajo. Así el mismo sketch pasa de prender un LED de prueba a conmutar una carga de verdad.

Diagrama de conexión de la Raspberry Pi Pico con LED en GPIO 2

Si quieres empezar con el LED simple para probar (recomendado antes de meter voltaje de red), lo conectas al GPIO 2 con una resistencia de 220Ω a GND, sobre una protoboard y con cables dupont. Cuando ya funcione, reemplazas el LED por la entrada de señal del relé.

El código completo

Este es el sketch que levanta el servidor y sirve la página. Pega tu SSID y contraseña en las líneas correspondientes antes de subirlo:

C++
/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-web-server-outputs-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
// Import required libraries
#include <WiFi.h>
#include <RPAsyncTCP.h>
#include <ESPAsyncWebServer.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// LED to control
#define LED_PIN 2

// Current LED state (to display on the web page)
String ledState = "OFF";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <title>Pico Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    html {
      font-family: Arial;
      display: inline-block;
      margin: 0px auto;
      text-align: center;
    }
    body {
      max-width: 600px;
      margin: 0 auto;
      padding: 20px;
    }
    h1 {
      color: #333;
    }
    h2 {
      color: #666;
    }
    form {
      margin: 10px 0;
    }
    .button {
      background-color: #4CAF50;
      border: none;
      color: white;
      padding: 10px 20px;
      text-decoration: none;
      font-size: 20px;
      margin: 2px;
      cursor: pointer;
      border-radius: 5px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.2);
      transition: background-color 0.3s, box-shadow 0.3s;
    }
    .button:hover {
      background-color: #45a049;
      box-shadow: 0 4px 8px rgba(0,0,0,0.3);
    }
    .button2 {
      background-color: #f44336;
    }
    .button2:hover {
      background-color: #da190b;
    }
    .state {
      font-size: 1.4em;
      margin: 20px 0;
      color: #444;
    }
  </style>
</head>
<body>
  <h1>Raspberry Pi Pico Web Server</h1>
  <h2>Led Control</h2>
  <p class="state">LED State: %STATE%</p>
  <form action="./lighton">
    <input type="submit" value="LED on" class="button" />
  </form>
  <form action="./lightoff">
    <input type="submit" value="LED off" class="button button2" />
  </form>
</body>
</html>
)rawliteral";

// Processor function
String processor(const String& var){
  if(var == "STATE"){
    return ledState;
  }
  return String();
}

void setup(){
  Serial.begin(115200);

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print Pico Local IP Address
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/html", index_html, processor);
  });

  // Route to turn LED ON
  server.on("/lighton", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(LED_PIN, HIGH);
    ledState = "ON";
    request->send(200, "text/html", index_html, processor);
  });

  // Route to turn LED OFF
  server.on("/lightoff", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(LED_PIN, LOW);
    ledState = "OFF";
    request->send(200, "text/html", index_html, processor);
  });

  // Start server
  server.begin();
}

void loop() {

}

Cómo funciona, en breve

  • Librerías: WiFi para conectar a la red, más RPAsyncTCP y ESPAsyncWebServer para el servidor.
  • index_html: guarda todo el HTML como raw string literal (entre R"rawliteral( y )rawliteral"), lo que permite escribir el HTML tal cual sin escapar comillas ni saltos de línea. Se almacena en flash con PROGMEM.
  • %STATE%: es un marcador de posición. Antes de enviar la página, la función processor() lo reemplaza por el valor real de ledState.
  • Rutas: server.on("/", ...) sirve la página; /lighton y /lightoff cambian el pin con digitalWrite() y actualizan el estado.
  • loop() vacío: como el servidor es asíncrono, no hace falta poner nada en el loop; las rutas se ejecutan solas cuando llega una petición.

Subir el código y probar

Para subir código a la Pico W tiene que estar en modo bootloader. Si viene con MicroPython, conéctala al computador manteniendo apretado el botón BOOTSEL. Se abrirá una unidad de almacenamiento; ciérrala. En próximas subidas desde el IDE de Arduino, la placa entra sola en bootloader.

Raspberry Pi Pico en modo bootloader conectada por USB

En el menú desplegable superior elige Select other board and port…, selecciona Raspberry Pi Pico W o Raspberry Pi Pico 2W según tu placa, marca Show all ports si no aparece el COM, y sube el código. Luego abre el Monitor Serial a 115200 baudios: al reconectar la Pico, imprimirá su dirección IP. Escribe esa IP en el navegador y ya puedes controlar la salida.

Raspberry Pi Pico controlando la salida encendida desde el servidor web

Variantes y mejoras

  • Leer el estado real del pin: como notamos arriba, la página muestra el estado "supuesto". Puedes cambiar processor() para que lea digitalRead(LED_PIN) y devuelva el estado real del GPIO, más fiable si algo externo cambia la salida.
  • Varias salidas: duplica las rutas y los botones para controlar 2 o 4 relés desde la misma página (riego por zonas, luces por ambiente).
  • Protección de la carga: si vas a conmutar 220V con el relé, aísla bien la etapa de potencia y nunca manipules la protoboard con la carga energizada.

Personalización para Chile

En Chile consigues todo en MechatronicStore:

  • Raspberry Pi Pico 2 W (SKU GS3-4). $16.990 CLP
  • Módulo relé 1 canal 5V (SKU D-205). $1.690 CLP
  • LED 5mm rojo (SKU GA1-5). $100 CLP
  • Resistencia 220Ω 1/4W (SKU GK1-18). $100 CLP
  • Protoboard 400 puntos (SKU C-301). $2.690 CLP
  • Cables macho macho 30 cm (SKU C-417). $2.990 CLP

Si en algún tutorial ves una "Pico W" a secas, la Pico 2 W (RP2350) del catálogo cumple la misma función con más rendimiento y también trae WiFi.

Recursos

Versión chilena con componentes en stock local en MechatronicStore, inspirada en el tutorial de Random Nerd Tutorials y re enfocada para controlar una carga real con relé.