Arduino como servidor Web

Objetivos

    • Montar un circuito con un DHT11 (sensor de temperatura) y un Shield Ethernet.
    • Crear un servidor Web.
    • Mostrar un código básico HTML para publicar los valores del sensor.

Material requerido

 arduino
  • Arduino Uno o similar. Esta sesión acepta cualquier otro modelo de Arduino.
 Img_3_4
  •  Una Protoboard.
 Img_3_6-300x185
  • Algunos cables de Protoboard.
EtherShield-150x150
  • Un Un Shield Ethernet (W5100).
RJ45_Cable-300x192
  • Un cable rj45.
Img_3_5
  • Una resistencia de 330Ω.
DHT11-150x150
  • Un sensor de temperatura DHT11
concentrador
  • Acceso a la red local.

Circuito de prueba

En la entrada anterior, vimos como conectarnos a la red local y salir a consultar una página Web externa. Es decir ser clientes de internet para conseguir recoger alguna información de la red.

Pero en muchas ocasiones, es muy práctico publicar el resultado de unos sensores en la web, para poder consultarlos en remoto. En esta sesión sesión veremos cómo publicar estos valores con nuestro Arduino y el Shield Ethernet mediante un servidor web.

Para ello montaremos un pequeño circuito similar al visto en la entrada Sensores de temperatura DHT11 y publicaremos los valores el sensor para poderlos consultar inicialmente desde la red local doméstica.

Diagramas del circuito

El esquema de conexión es el siguiente:

sesion-23_esquema-600x382

Simplemente montad el Shield Ethernet sobre vuestro Arduino y después conectad normalmente el sensor al pin D2, junto con tensión y la tierra.

  • En el caso del Shield Ethernet, utiliza los pines SPI para comunicarse con Arduino y por ello no podemos usar los pines D11, D12, y D13 en Arduino UNO y los pines D50, D51, D52 en un MEGA, más que para la gestión del bus.
  • Además, la mayor parte de las Shields Ethernet incluyen un lector de tarjetas SD o micro SD por lo que otro pin adicional queda reservado. En el UNO suele ser el pin D10 y en el Mega el pin D53 ( Aunque parece que puede modificarse el número de pin al configurar la librería SD).

El esquema de protoboard es el siguiente:

sesion-23_bb_-600x668

El programa de control

Vamos a empezar comprobando que todo está en su sitio cargando uno de los ejemplos que viene con la librería Ethernet. Se llama WebServer y podéis cargarlo desde:

\\Archivos\Ejemplos\Ethernet\WebServer

Lo único que hace es crear una página muy sencilla y ponerla en el servidor web para comprobar que las conexiones a la red son correctas.

Por ahora solo queremos comprobar que estamos en la red. Ese programa incluye al principio una línea así:

IPAddress ip(192, 168, 1, 177);

Que es la dirección IP que asignamos al Shield Ethernet. Si esta dirección os sirve, porque estáis en la misma subred, no tenéis que modificarla, pero en caso contrario aseguraros de ponerle una dirección IP válida para vuestra red como vimos en la entrada anterior.

Lo que hace este programa, es mostrar los valores de los pin analógicos en una página web cuya dirección IP es la que le hemos asignado en el paso anterior.  Naturalmente como no tenemos nada enchufado a estos pin, el resultado que leamos será aleatorio, pero por ahora esto no nos importa.

Vamos a hora a comprobar que podemos leer estos valores. Si usáis un PC, abrid vuestro navegador preferido y escribir en la página de direcciones la IP de vuestro Shield Ethernet. En mi caso es la 192.168.1.177. Deberíais ver algo parecido a esto:

IMg_62_1

Y cada vez que le deis a refrescar la pantalla los valores cambiaran de modo impredecible por su naturaleza aleatoria. (Normalmente se refresca con F5 en un PC). Si veis algo así, entonces ya podemos pasar a cosas más interesantes porque tenemos acceso a Arduino a través de Ethernet.

Vamos con el programa. Algunas inclusiones y datos previos:

#include <SPI.h>   // El shield Etherent usa SPI
#include <Ethernet.h>
#include "DHT.h"

byte mac[]={0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192,168,1,177); // vuestra IP  
EthernetServer server(80);  
// Arrancamos el servidor en el puerto 80
DHT dht(2,DHT11);

Nada nuevo aquí. Fijamos la dirección MAC y la IP, e inicializamos el servidor Web en el puerto 80 (el habitual) y creamos una instancia de DHT, en el pin 2 con modelo DHT11.

  • Podríamos cambiar la dirección de escucha del servidor asignando aquí un puerto diferente, pero entonces solo quien supiera el nuevo puerto podría acceder al servidor web.
  • Si el nuevo puerto fuera el 1256, por ejemplo, para acceder al servidor, la dirección seria: 192.168.1.177: 1256

El setup, tampoco tiene ninguna novedad, con excepción de server.begin() que inicia el servidor web en nuestro Arduino.

void setup()
{
   dht.begin();
   Serial.begin(9600);
   while (!Serial) ; // Retraso para el Leonardo
   Ethernet.begin(mac, ip);
   server.begin();  // Inicia el servidor web
   Serial.print("Serv Web en la direccion: ");
   Serial.println(Ethernet.localIP());
}

Para entrar con la función loop, tenemos antes que entender que los códigos HTML, son un lenguaje de descripción de páginas que consisten en directivas de texto que indican a tu navegador como mostrar la información de la página concreta que visitas.

Así que lo que vamos a hacer es construir una página Web (muy, muy sencilla) que entrega valores a quien lo solicite. Ese texto que conforma la página Web estará formado por una cabecera HTML y unos textos y valores numéricos que representan la humedad y la temperatura leída en el sensor.

La cabecera será:

HTTP/1.1 200 OK
Content-Type: text/html
Connection: close
Refresh: 30
<!DOCTYPE HTML>
<html>

Y después enviar los valores leídos del sensor junto con algún texto explicativo de lo que son esos números, con formato HTML.

Como ya hemos iniciado el servidor en el setup, ahora tenemos que comprobar periódicamente si hemos recibido peticiones de un cliente web, en caso afirmativo la propiedad available de server devolverá una entrada a la petición de un cliente, y en caso negativo será 0:

EthernetClient client = server.available()

Por eso, si client es diferente de 0, significa que tenemos una petición de cliente y deberemos procesarla.

El programa que usaremos para procesar esto será más o menos así:

void loop()
{
  EthernetClient client = server.available();   
  if (client){ 
      Serial.println("nuevo cliente");
      boolean currentLineIsBlank = true;  
      while (client.connected()){ 
       if (client.available()){  
          char c = client.read();
          Serial.write(c); 
          if(c=='\n' && currentLineIsBlank) 
             {   
             //Enviar una respuesta típica
            client.println("HTTP/1.1 200 OK");                            client.println("Content-Type: text/html");
client.println("Connection: close");
            client.println("Refresh: 15");            // Actualizar cada 15 segs
           client.println();
           client.println("<!DOCTYPE HTML>");
           client.println("<html>");
           float h=dht.readHumidity();
           // Leer el sensor
           float t = dht.readTemperature();
           Serial.println(t);
           Serial.println(h);
           // Desde aquí código HTML
           client.print("<head><title>Situacion del lugar</title></head>”);
           client.print(“<body><h1> Situacion Ambiente</h1><p>Temperatura - ");
           client.print(t);
           client.print(" grados Celsius</p>");
           client.print("<p>Humedad:  ");
           client.print(h); // Aqui va la humedad
           client.print(" porciento</p>");
           client.print("<p><em> La página se actualiza cada 15 segundos.</em></p></body></html>");
           break;
           }
           if (c=='\n')
              currentLineIsBlank = true;       
               else if (c != '\r')
                currentLineIsBlank = false;
          }
        }
     delay(10); 
     client.stop();  
     Serial.println("cliente desconectado");
   }
}

Algunos comentarios respecto del programa. Client es un puntero a un objeto que desciende de la clase Stream de Arduino, lo mismo que Serial y también Ethernet entre otros. Esto quiere decir que tienen propiedades y métodos comunes.

Por eso, lo mismo que podemos hacer Serial.print(), podemos hacer client.print y cumple con exactamente las mismas reglas.  Usamos client.print, para enviar un String de texto al navegador que hay al otro extremo de la conexión abierta.

Igualmente hemos usado client.connected(), para comprobar si alguien ha conectado con el servidor y después usamos client.available(), para determinar si nos ha enviado algún mensaje. Cuando client.available() toma un valor no nulo, sabemos que hay que leer ese mensaje y usamos client.read().

Si os dais cuenta, los conceptos que usamos para leer de un cliente web, son los mismos que usamos para leer de una consola serie y se llaman igual. Es una de las grandes ventajas de la programación orientada a objetos, porque ambos descienden de una clase similar, Stream (corriente, flujo), porque conceptualmente, aunque el origen y la tecnología de soporte es muy distinta, la idea de un flujo continuo bit a bit por algo parecido a un hilo lógico es común a ambos casos.