bclose

Controlando un LED por Ethernet

Control de pines digitales desde Internet

Objetivos

 

 
    • Montar un circuito con un LED y un Shield Ethernet.
    • Crear un servidor Web.
    • Mostrar un código básico HTML para activar odesactivar el LED, que nos sirva como armazón para nuestros proyectos.
    • Presentar el Port Forwarding y su razón de ser, cuando queremos acceder desde internet al servidor de nuestro Arduino.
 

Material requerido.

Imagen de Arduino UNO   Arduino UNO o equivalente.
Protoboardconexiones Una Protoboard más cables.
componente  Una resistencia de 330Ω.
componente  Un LED
Shield Ethernet  Un Shield Ethernet
tienda online prometec

A vueltas con el HTML

 

El HTML es un lenguaje de descripción de páginas que ha evolucionado en sus últimas versiones, (HTML5, a la hora de escribir esto) en algo muy sofisticado que sin llegar a ser un lenguaje de programación, es capaz de ofrecer una potencia espectacular.

Un curso medianamente organizado de HTML escapa con mucho la capacidad y el objetivo de estas humildes sesiones, pero para continuar presentando como hacer ciertas cosas con Arduino y la conexión a Ethernet, necesitamos presentar algunas ideas al respecto, porque sin ellas cualquier intento de avanzar seria vano.

Por ejemplo, es muy útil disponer de un programa tipo, que nos permita manipular un LED conectado a nuestro Arduino mediante una página Web, y este es el objetivo de la sesión actual.

Para ello, vamos a presentar pequeños snippets (fragmentos) de HTML que nos permitan hacerlo, sin entrar en grandes detalles del lenguaje, pero que vamos a emplear en nuestros programas.

EL primero, es un snippet que nos permite mostrar un checkbox en nuestra página y reconocer si se ha pinchado o no:

<!DOCTYPE html>
<html>
     <head>
          <title> Control de LEDs en Arduino</title>
     </head>
     <body>
           <h1>LED remoto</h1>
           <p>Haz click para conmutar el LED.</p>
           <form method="get">
                <input type="checkbox" name="LED2" value="2" onclick="submit();">LED2
           </form>
      </body>
</html>
 
  • Como norma general, lo que se presenta entre < > se llaman tags y describen algo en concreto, títulos, cabeceras, negritas….
  • Por ejemplo para expresar que es código HTML, debemos empezar con <html> y finalizar con </html>, lo mismo ocurre con los demás tags, como < head> cabecera, <body> cuerpo, o <p> párrafo, que cuando finalizan incorporan un tag con el símbolo “/” para indicar el fin del tag.
 

En el snippet de arriba, hemos usado un método HTML para mostrar un checkbox y enviar un texto cuando se pulse.

Si copiáis ese código con un editor de textos y lo guardáis con extensión htm, cuando lo abras con el navegador veras:

Status OFF
Status ON

 

Fijate en la imagen de la derecha, que cuando pulsemos el checkbox, el navegador añadirá al final de la dirección IP, el texto ?LED2. Utilizaremos este texto de respuesta para decidir si encendemos o apagamos el LED. Para ello usaremos la función  indexOf() que nos devuelve si un string está incluido o no en otro.

 

Diagrama del circuito

 

Vamos a usar un circuito con Arduino y un LED conectado alD2 con sus resistencia, en conjunción con la página web que hemos descrito en el apartado anterior. En función del status del checkbox encenderemos o apagaremos los LED.

Control remoto por ethernet de un LED

 

El programa de control

 

La mayor parte del programa va a ser similar al programa de la sesión anterior, con algunas modificaciones (Pocas). Principalmente, serán crear un variable para almacenar el status del LED, y un String para registrar la petición que nos hace el cliente Web:

String HTTP_req;          // stores the HTTP request
boolean LED_status = 0;   // state of LED, off by default

El resto es casi igual, excepto porque cambiamos el código HTML que enviamos en la respuesta, para presentar el snippet del que hablamos más arriba, y porque hemos añadido una función ProcessCheckbox() que hará el procesamiento del click (incluyendo enviar el HTML correspondiente a pulsado o no).

 
  • Fijaros que enviamos los comandos HTML, mediante client.print() a quien le pasamos los strings limitados por comillas. Pero como los comandos HTML también incluyen comillas, si los copiamos por las buenas tendríamos un lio servido.
  • La solución de C++ para esto es preceder el carácter a imprimir por un carácter de escape para que el compilador sepa que no es el fin del string, si no parte de el.
  • Por eso, si queremos mandar <form method=”get”> lo que tenemos que enviar con print es <form method=\”get\”>.
 

Aquí tenéis el programa   Prog_63_1 

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1,177);
EthernetServer server(80);

String HTTP_req;          // Para guardar la peticion del cliente
boolean LED2_status = 0;

void setup()
{   Ethernet.begin(mac, ip);  
    server.begin();           
    Serial.begin(9600);       
    pinMode(2, OUTPUT);       
}
void loop()
{  EthernetClient client = server.available();  // Comprobamos si hay peticiones
   if (client)                                  // En caso afirmativo
      {  boolean currentLineIsBlank = true;
         while (client.connected()) 
           { if (client.available())           // Hay algo pendiente de leer
               { char c = client.read(); // Leemos los caracteres de uno en uno
                 HTTP_req += c;          // Los añadimos al String
                 if (c == '\n' && currentLineIsBlank)
                    { client.println("HTTP/1.1 200 OK");
                      client.println("Content-Type: text/html");
                      client.println("Connection: close");
                      client.println();
                      client.println("<!DOCTYPE html>");          // Envia la pagina Web
                      client.println("<html>");
                      client.println("<head>");
                      client.println("<title>Control de LEDs en Arduino</title>");
                      client.println("</head>");
                      client.println("<body>");
                      client.println("<h1>LED</h1>");
                      client.println("<p>Haz click para conmutar el LED.</p>");
                      client.println("<form method=\"get\">");
                      ProcessCheckbox(client);
                      client.println("</form>");
                      client.println("</body>");
                      client.println("</html>");
                      Serial.print(HTTP_req);
                      HTTP_req = "";    // Una vez procesador, limpiar el string
                      break;
                    }
                 if (c == '\n')
                      currentLineIsBlank = true;
                 else if (c != '\r')
                      currentLineIsBlank = false;
              } // if (client.available
           }   // WHile
        delay(10);      // dar tiempo
        client.stop(); // Cerra conexion
   }  // If client
}
void ProcessCheckbox(EthernetClient cl)
  {  if (HTTP_req.indexOf("LED2=2") > -1)    // LED2 pinchado?
          LED2_status = !LED2_status ;         // Si pichado invertimos el valor

     digitalWrite(2, LED2_status);
     if (LED2_status)
             cl.println("<input type=\"checkbox\" name=\"LED2\" value=\"2\" \\ onclick=\"submit();\" checked>LED2");
     else
             cl.println("<input type=\"checkbox\" name=\"LED2\" value=\"2\" \\  onclick=\"submit();\">LED2");
   }

Aquí os dejo un minivideo con el resultado

 

Accediendo desde internet a nuestro Arduino

 

Hasta ahora, hemos accedido a nuestro servidor Arduino, desde la red interna, a través de un cable Ethernet. No es distinto acceder desde el exterior de la red local, pero para hablar de ello necesitamos presentar algunas ideas adicionales, así que poneros cómodos.

El esquema de una red local LAN, puede ser más o menos este:

Elementos interconectados

Cada una de los equipos internos dispone de una dirección IP única, válida para la red interna. Normalmente son IPs en uno de dos rangos:

192.168.xx.xx
172.26.xx.xx

Esta dos rangos están reservadas a direcciones internas y no son enrutables, es decir el Router rechazará sacarlas a internet. A este tipo de direcciones que usamos internas se les llama direcciones IP privadas, porque en principio no se puede acceder desde internet a ellas.

Por el contrario, las direcciones a las que accedemos desde Internet, las llamamos direcciones públicas y no pueden coincidir con otra IP pública bajo ninguna circunstancia.

Cuando desde dentro de la LAN, queremos acceder a una dirección pública, el Router utiliza el NAT (Que vimos en la sesión anterior), para enmascarar mi dirección interna privada y utiliza su propia dirección pública para hacer la petición.  Cuando recibe la respuesta, nos la reenvía como si no hubiera pasado nada.

Pero es importante entender aquí que:

 
  • Siempre que tu Router se enciende, recibe una dirección pública que le asigna tu operadora telefónica, y que es la que usará para acceder a internet. Necesita ser una dirección pública para poder navegar en el exterior.
  • Si quieres saber con qué IP publica estas navegando (Acuérdate del NAT, quien navega es el Router con su IP, no tú) usa cualquiera de los servicio para ello, por ejemplo http://cual-es-mi-ip-publica.com/, y veras que tu IP privada es distinta de la que te dice esta página, pero es correcto.
 

Algunas veces me preguntan que tienen que hacer para conseguir  una dirección IP pública para un servidor doméstico. Yo siempre respondo que dirección pública, ya tienen, la que habéis visto antes. Lo que no tienen es una dirección IP fija o estática, porque esta cambia cada vez que apagas y enciendes el Router, pero es una IP publica de pleno derecho.

Si necesitas acceder a tu Arduino desde fuera puedes utilizar esa IP, por lo menos hasta que reinicies el Router (Y es algo que hacemos habitualmente con los servidores domésticos).

Cuando contratas a tu proveedor una IP fija, lo que haces es pagar por una que no cambie cuando reinicias y por regla general también te permite registrar en sus DNS públicos un nombre que apunte a la IP que compras.

 
  • Si queréis hacer pruebas a coste mínimo, podéis usar alguna página como noip, que te ofrecen soluciones gratuitas para encontrar tu dirección pública incluso cuando apagues tu Router. No es una solución definitiva, pero tiene la virtud de tener un precio imbatible: 0.
 

Supongamos ahora que ya sabemos que IP pública  nos corresponde. Lo siguiente es acceder desde internet, pero surge un nuevo problema (Como no).

Cuando salimos a través del Router este sabe nuestra dirección interna (Va en los paquetes TCPIP) y cuando recibe la respuesta, no tiene problema en saber a quién corresponde. Pero cuando alguien del exterior trata de entrar en nuestra red sin una petición registrada por nuestra parte, tiene dos problemas:

 
  • Permitimos por seguridad que se entre desde el exterior a nuestra red interna? Normalmente hay que revisar la política de seguridad del Router para garantizar que se pueda.
  • ¿Cómo sabe el Router a que dirección interna va la petición? Dentro de la red interna hay muchos puestos. ¿A cuál de ellos debe enviar la petición?
 

No existe manera de dirigir correctamente estas peticiones de fuera, sin informar previamente al Router de algún criterio para que tome una decisión. Para esto se inventó el Port Forwarding o redirección de puertos IP.

¿Cómo diferenciamos una petición del exterior hacia el interior?

Tenemos la clave en que cada petición IP lleva anexo un puerto. O lo que es lo mismo, todas las peticiones que vengan por el puerto 80 podremos asumir, razonablemente, que son para el servidor Web interno.

Así, si vienen por el puerto 21, es razonable enviárselo a un servidor de FTP y si viene por el 25 lo lógico es que sea un tema de SMTP y se lo envíe al servidor interno de correo.

 
  • Además, siempre que en una red con administrador profesional se permite la entrada del exterior (O zona desmilitarizada, en la jerga) al interior de la red local, se suele instalar un FireWall que controla, limita e inspecciona y eventualmente bloquea, todas las transacciones, para evitar problemas de seguridad.
 

Así pues, bastaría con informar al Router, mediante una tabla de redirección de puertos, de que cualquier intento de llegar al interior de la red local por el puerto 80, HTTP, se desvíen, hacia la dirección interna del servidor Web. Y lo mismo con el FTP y SMTP a su propia IP privada.

Redirecciones de puertos

 

Así con una única IP publica, podemos recibir y servir peticiones, de múltiples servidores internos, sin más condición de que se pueda discriminar entre ellos por el puerto característico  (Lo habitual).

 
  • No está de más destacar, que este sistema implica, necesariamente, programar las reglas de port forwarding en el Router. No funcionará en absoluto si se obvia este paso.
  • La limitación de este sistema es, que podemos poner tantos servidores como queramos detrás del Router o firewall, a condición de que solo haya uno de cada. No podemos poner dos servidores web en el puerto 80, porque todo lo que venga por el puerto 80, el Router hará un port forwarding a una única dirección.
  • Si hemos puesto un servidor estándar en un puerto inusual, o si hemos montado un servicio propio en un puerto dado, la dirección completa con puerto se puede especificar como por ejemplo en 192.168.1.175: 4217
 

Por ultimo para programar el port forwandig en tu Router, debes entrar en él. Actualmente todos los routers domésticos pueden administrarse desde una página web que sirve el propio Router en su dirección IP.

Si por ejemplo tu Router tiene la dirección 192.168.1.1, puedes ir a esa dirección con tu navegador. Te pedirá un nombre de usuario y clave de acceso para validar tu autorización.

Es imposible describir la configuración de los routers en detalle porque hay cientos de modelos, pero en todos ellos hay que buscar la definición de port forwandig.

Voy a incluir capturas de mi Router, por si hay suerte y coincide con el vuestro. Si no goglead el tema con el modelo especifico de vuestro Router.

En mi caso concreto, os anexo un pantallazo de mis opciones, donde veis en la columna de la izquierda, el menú del Router con la selección de Port Forwarding, y a la derecha la tabla de redirecciones de mi Router, donde redirecciono varios puertos hacia dos IPs internas diferentes

Redireccion de puertos
 
  • Aunque hasta ahora hemos hablado exclusivamente como si el Transporte fuera siempre TCP, lo cierto es que hay otro protocolo que se utiliza habitualmente, que es el UDP.
  • No he querido complicar innecesariamente las explicaciones hablando de él, pero hay que tenerlo en cuenta. Algunos servicios usan UDP y no TCP y cuando hacemos port Forwarding, se debe especificar (Aunque casi todos los routers modernos aceptan especificar ambos como arriba).
 

Un último comentario, Cuando accedes desde el exterior a tu servidor Arduino, vas a usar en tu navegador, la dirección pública de tu Router. Pero si intentas usar esa dirección pública exterior para entrar desde el interior de tu red local (por ejemplo cuando usas tu teléfono WIFI) te llevaras un chasco.

Algunos routers lo detectan como un intento de violar la seguridad y te bloquearan sin contemplaciones (A esto se le llama Spoofing en la jerga y suele suponer malas intenciones de todas, todas… menos en el caso del que hablamos).

Y otros te pedirán una clave para pasar, que será la del Router, en cuyo caso te pondrá con la página Web de administración y no con el servidor interno en Arduino.

Cuando estés conectado por Ethernet o WIFI, tienes que utilizar la IP privada interna. Cuando estés fuera de tu LAN, utilizarás la dirección pública, y si, esto te va a dar guerra para hacer pruebas.

 

Resumen de la sesión

 

 

    • Hemos visto cómo usar el servidor web de Arduino con un código HTML especial, para controlar el status de un LED conectado a un pin digital.
    • Veréis que es muy sencillo a partir del programa que ya vimos en la sesión anterior, y debería ser fácil ampliar el programa a múltiples LEDs.
    • Hemos presentado el Port Fowarding y porque es necesario, para acceder desde el exterior.
    • Hemos apuntado a como configurar la tabla de redirección de tu Router.
 

 

 

 

 

(69) Comments

  • Buenas.

    En primer lugar agradecer los tutoriales porque me ayudan mucho.

    HE corrido el programa y va perfecto, enciendo y apago el led…pero me gustaria modificarlo para abrir y subir persiana con un rele…¿que modificación habría que hacer, para que pasados, por ejemplo, 20 segundos, la cajita de LED2 volviera aparecer desmarcada?

    Gracias

    saludos

    • Juan

    Buen día, como interpreto y corrijo este error que me devuelve al compilar mi arduino uno:

    collect2.exe: error: ld returned 5 exit status

    exit status 1
    Error compilación en tarjeta Arduino/Genuino Uno.

    saludos

    • Hola Juan, te lo devuelve sólo con este programa o con cualquiera?

    • Jmdev

    Solo para aclarar, el navegador no interpreta PHP, es un lenguaje del lado del servidor.

    Un saludo.

    • Mohamed

    cual podría ser el código para ponerle un usuario y una contraseña a una pagina creada en arduino ?
    por ejemplo, la página que usted ha creado, si quisiera ponérselo y en caso de introducir datos erróneos que de un mensaje de error “404 NOT FOUND”. y en caso contrario me muestre esa pagina de arriba, la del control del LED.

    • Veo por ahí que hay una librería llamada webduino que trae un ejemplo precisamente con el tema que proponers. No la he probado, pero puedes echarle un ojo a ver. Un saludo.

    • Julio cesar

    que ocurre si le coloco otro puerto que no sea el 80.para que si accedo desde el exterior me muestre la pagina html, pero por otro puerto ej. puerto 2345. como hago ya que ponele el puerto 80 lo usa una computadora conectada a ese router.

    • Sin problemas julio, basta con que pongas el servidor arduino en el puerto 2345 y listo te funcionara perfecto

  • hola, me tira error en el checkbox (exit status 1 ‘Process’ was not declared in this scope) ademas no entiendo como hacer para que mi html creado aparezca en192.168.1.177
    muchas gracias

    • Diego Santos

    Hola, Php no lo interpreta el navegador, así que no se podría hacer.

    • Asier

    Buenas, ¿como podría hacer para tener alojada la pagina web en un servidor externo y desde este controlar los leds del arduino?

    Por ejemplo una pagina web con un botón, no se que código meterle a ese botón para que envíe una cadena y el arduino la lea.

    La idea es poder controlar con una web varios arduinos, no se cual es el método mas eficaz para hacer esto, pero creo que funcionaria mejor que alojando la pagina en el propio arduino.

    Un saludo.

    • Hola Asier, lo que planteas se acerca mucho a la idea de la IOT y estamos estudiando plantear una serie de tutoriales al respecto

        • Asier

        Estaré atento a las novedades, no he encontrado demasiada info en castellano pero he leído por ahí que se puede hacer con el método (get /?……) que no se bien como funciona, y después de hacerlo funcionar me gustaría meterle también algo de seguridad, tanto a la web como al arduino.

        Un saludo.

  • Saludos, una consulta ¿Cómo podría encender y apagar un led desde una página web y con una palicación que se comunique de forma serial al arduino?
    De antemano gracias.

    • Hola Daniel, No estoy seguro de cual es tu pregunta porque la forma de encender el led desde internet dispone de un tuto especifico que puedes revisar y para hacerlo dese ula puerta serie es aun mas facil

    • Carla valenzuela

    hola sabes probe el codigo tal cual como esta mas arriba pero el led no hace nada o sea se mantiene siempre encendido y veo en el serial y manda las señales correspondientes he probado con varios ejemplos y ninguno me funciona que podra ser…

    • Hola Carla, siento oir tu problema, pero te aseguro que pruebo todos los ejemplos antes de publicarlos y si no funciona deberia ser a causa de algun problema hardware

    • Milka

    Otra duda, también puedo escribir en PHP??? Este lenguaje también lo interpreta el navegador???

    • Claro Milka, Ten en cuenta que el codigo que escribas es solo texto. Es el navegador quien lo interpreta

  • Muchísimas Gracias!!!

Give a Reply

WordPress Anti-Spam by WP-SpamShield