🌐 Servidor Web amb ESP32: Temperatura i Humitat al Mòbil pas a pas

Transforma el teu ESP32 en un servidor web que mostra les dades del DHT22 (o DHT11) al navegador de qualsevol dispositiu, inclòs el telèfon mòbil.

📑 Continguts

1. Per què un servidor web a l’ESP32?

Un ESP32 pot funcionar com un petit servidor web que entrega pàgines HTML al navegador. Així, sense necessitat de pantalles ni cables, pots consultar la temperatura i humitat des de qualsevol dispositiu connectat a la mateixa xarxa Wi‑Fi (ordinador, tauleta, mòbil).

En aquesta guia aprendràs a:

2. Material i connexió ràpida del DHT

🛠️ Material
⚡ Connexions bàsiques
DHTESP32
VCC3.3 V
DATAGPIO 15 (o un altre GPIO lliure)
GNDGND

Resistència pull‑up entre DATA i VCC si el mòdul no la té.

Si necessites recordar tots els detalls, consulta la nostra guia completa dels sensors DHT.

3. Connectar l’ESP32 a la xarxa Wi‑Fi

Perquè el navegador del mòbil pugui veure la pàgina, l’ESP32 ha d’estar connectat a la mateixa xarxa que el telèfon (o bé crear la seva pròpia xarxa, veure Mode AP).


#include <WiFi.h>

const char* ssid     = "el_teu_ssid";
const char* password = "la_teva_contrasenya";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  
  Serial.print("Connectant a Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("\nConnectat!");
  Serial.print("Adreça IP: ");
  Serial.println(WiFi.localIP());   // Apunta aquesta IP
}
ℹ️ Com trobar la IP? Obre el monitor sèrie de l’Arduino IDE (115200 bauds) i veuràs l’adreça IP assignada (ex: 192.168.1.42). Aquesta serà l’adreça que escriuràs al navegador del mòbil.

4. Crear un servidor web bàsic

Fem servir la llibreria WebServer (inclosa en el paquet ESP32 per Arduino). El servidor escoltarà al port 80 i respondrà a les peticions HTTP.


#include <WebServer.h>

WebServer server(80);   // Servidor al port HTTP estàndard

void setup() {
  // ... connexió Wi-Fi ...
  
  // Indiquem quina funció s'executa quan algú demana la pàgina principal "/"
  server.on("/", handleRoot);
  
  server.begin();
  Serial.println("Servidor web iniciat");
}

void loop() {
  server.handleClient();   // Escolta i respon les peticions entrants
}

void handleRoot() {
  // Enviar una pàgina HTML simple
  String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>";
  html += "<title>ESP32 Web Server</title></head><body>";
  html += "<h1>Hola des de l'ESP32!</h1>";
  html += "</body></html>";
  
  server.send(200, "text/html", html);
}

5. Generar una pàgina web amb dades del sensor

Ara combinem el DHT i el servidor. La funció handleRoot() llegeix el sensor i insereix els valors dins de l'HTML. Per evitar recàrregues manuals, afegim un petit script JavaScript que demana les dades actualitzades cada 2 segons (AJAX). Així el mòbil només ha de mostrar la pàgina un cop i les xifres es refresquen soles.

Crearem dues rutes:


// dins de setup():
server.on("/", handleRoot);
server.on("/data", handleData);

// ...

void handleRoot() {
  String html = R"rawliteral(
    <!DOCTYPE html>
    <html lang="ca">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>DHT22 – ESP32</title>
      <style>
        body { font-family: Arial; text-align: center; margin-top: 2rem; }
        .dada { font-size: 3rem; font-weight: bold; }
        .unit { font-size: 1.5rem; color: #555; }
      </style>
    </head>
    <body>
      <h1>🌡️ Temperatura i Humitat</h1>
      <div>
        <span id="temp" class="dada">--</span>
        <span class="unit">°C</span>
      </div>
      <div style="margin-top:1rem;">
        <span id="hum" class="dada">--</span>
        <span class="unit">% HR</span>
      </div>
      <script>
        function actualitza() {
          fetch('/data')
            .then(r => r.json())
            .then(d => {
              document.getElementById('temp').innerText = d.temperatura;
              document.getElementById('hum').innerText  = d.humitat;
            })
            .catch(e => console.error(e));
        }
        setInterval(actualitza, 2500);  // cada 2,5 segons
        actualitza();                    // primera càrrega immediata
      </script>
    </body>
    </html>
  )rawliteral";
  
  server.send(200, "text/html", html);
}

void handleData() {
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  
  // Protecció si falla la lectura
  if (isnan(t) || isnan(h)) {
    server.send(500, "text/plain", "Error de lectura");
    return;
  }
  
  String json = "{";
  json += "\"temperatura\": " + String(t, 1) + ",";
  json += "\"humitat\": "     + String(h, 1);
  json += "}";
  
  server.send(200, "application/json", json);
}
Per què JSON i AJAX? El mòbil només descarrega la pàgina HTML una vegada. Després, cada 2.5 s, demana un petit objecte JSON amb les dades actuals i actualitza els números al vol. Així estalviem dades i la pàgina no fa “parpellejos”.

6. Codi complet i explicació línia a línia

A continuació tens el programa complet per copiar i enganxar a l’Arduino IDE. Inclou tot: Wi‑Fi, DHT, servidor web amb ruta JSON.


//  Incloure llibreries necessàries
#include <WiFi.h>
#include <WebServer.h>
#include "DHT.h"

//  Configuració Wi‑Fi – canvia pel teu SSID i contrasenya
const char* ssid     = "EL_TEU_SSID";
const char* password = "LA_TEVA_CONTRASENYA";

//  Configuració del DHT
#define DHTPIN 15       // GPIO on connectes el DATA
#define DHTTYPE DHT22   // O DHT11
DHT dht(DHTPIN, DHTTYPE);

//  Servidor web al port 80
WebServer server(80);

//  --------------------------------------------------
void setup() {
  Serial.begin(115200);
  delay(1000);
  
  // Inicia el sensor
  dht.begin();
  
  // Connexió Wi‑Fi
  WiFi.begin(ssid, password);
  Serial.print("Connectant a Wi‑Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnectat!");
  Serial.print("Adreça IP: ");
  Serial.println(WiFi.localIP());
  
  // Configura les rutes del servidor
  server.on("/", handleRoot);      // Pàgina principal
  server.on("/data", handleData);  // Dades en JSON
  
  // Inicia el servidor
  server.begin();
  Serial.println("Servidor web en marxa!");
}

//  --------------------------------------------------
void loop() {
  server.handleClient();   // Atén les peticions dels navegadors
}

//  --------------------------------------------------
//  Pàgina HTML amb JavaScript per al refresc automàtic
void handleRoot() {
  String html = R"rawliteral(
    <!DOCTYPE html>
    <html lang="ca">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>DHT22 – ESP32</title>
      <style>
        body { font-family: Arial; text-align: center; margin-top: 2rem; }
        .dada { font-size: 3rem; font-weight: bold; }
        .unit { font-size: 1.5rem; color: #555; }
        .error { color: red; }
      </style>
    </head>
    <body>
      <h1>🌡️ Temperatura i Humitat</h1>
      <div>
        <span id="temp" class="dada">--</span>
        <span class="unit">°C</span>
      </div>
      <div style="margin-top:1rem;">
        <span id="hum" class="dada">--</span>
        <span class="unit">% HR</span>
      </div>
      <script>
        function actualitza() {
          fetch('/data')
            .then(r => r.json())
            .then(d => {
              document.getElementById('temp').innerText = d.temperatura;
              document.getElementById('hum').innerText  = d.humitat;
            })
            .catch(e => {
              document.getElementById('temp').innerText = 'Error';
              document.getElementById('hum').innerText  = '';
            });
        }
        setInterval(actualitza, 2500);
        actualitza();
      </script>
    </body>
    </html>
  )rawliteral";
  
  server.send(200, "text/html", html);
}

//  --------------------------------------------------
//  Retorna les dades del sensor en format JSON
void handleData() {
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  
  if (isnan(t) || isnan(h)) {
    server.send(500, "text/plain", "Error de lectura");
    return;
  }
  
  String json = "{";
  json += "\"temperatura\":" + String(t, 1) + ",";
  json += "\"humitat\":"     + String(h, 1);
  json += "}";
  
  server.send(200, "application/json", json);
}
⚠️ Abans de pujar el codi: canvia EL_TEU_SSID i LA_TEVA_CONTRASENYA pels de la teva xarxa. Assegura't que el pin DHTPIN coincideix amb la teva connexió. Si fas servir DHT11, canvia DHTTYPE a DHT11.

7. Accedir des del mòbil (pas a pas)

  1. Connecta el telèfon a la mateixa xarxa Wi‑Fi que l’ESP32 (no dades mòbils).
  2. Obre el monitor sèrie de l’Arduino IDE i busca la línia que diu Adreça IP: 192.168.x.x.
  3. Al navegador del mòbil (Chrome, Safari…), escriu exactament la IP que has vist al monitor, incloent els punts. Exemple: http://192.168.1.42 (sense “http://” també funciona la majoria de navegadors).
  4. Prem Enter. Després d’un instant veuràs la pàgina amb la temperatura i humitat actuals, i les dades s’aniran refrescant automàticament.
❗ Consell per a IPs canviants: Si el router reassigna IPs, pots configurar una IP fixa a l’ESP32 (amb WiFi.config()) o reservar-la al router. En cas contrari, hauràs de consultar el monitor sèrie cada cop que es reiniciï l’ESP32.

8. Mode punt d’accés (AP) – sense router

Si no tens un router a prop, l’ESP32 pot crear la seva pròpia xarxa Wi‑Fi. El mòbil es connecta a aquesta xarxa i accedeix al servidor igualment. Només cal canviar unes línies al codi:


// Substitueix la part de connexió Wi‑Fi per això:
const char* ap_ssid     = "ESP32_Sensor";     // Nom de la xarxa que veuràs al mòbil
const char* ap_password = "12345678";         // Almenys 8 caràcters

void setup() {
  // ... inicialitza DHT ...
  
  WiFi.softAP(ap_ssid, ap_password);
  Serial.print("Punt d'accés creat. IP: ");
  Serial.println(WiFi.softAPIP());
  
  // ... resta del codi (server.on, server.begin) ...
}

Des del mòbil, busca la xarxa “ESP32_Sensor”, introdueix la contrasenya i després obre la IP que veus al monitor sèrie (normalment 192.168.4.1).

9. Problemes habituals i solucions