Arduino Web Server Course

Sigue este tutorial para saber implementar tu propio Servidor Web Arduino.[IS.AC003.GC]

#0

Chapter #0 

La primera pregunta que debemos hacernos es: "Qué es un Servidor Web?". Un Servidor Web es una aplicación diseñada para recibir y procesar solicitudes HTTP de un cliente, para posteriormente enviar una respuesta a dicho cliente. Las comunicaciones se llevan a cabo mediante el protocolo HTTP, el cual, por definición, es un protocolo de transferencia de hipertexto. Generalmente, las respuestas que ejecuta el Servidor Web són páginas HTML, contenido XML, objetos JSON, hojas de estilo CSS, etc.
En este curso serviremos una página HTML con botones para controlar salidas, cuadros de texto para enviar datos a través de RS-485,textos con valores de entrada y contenido dinámico, esto último ayudandonos de la plataforma Javascript.


Capítulo #3

Este capítulo contiene un par de enlaces para establecer y borrar una salida.

      1. Añadir las acciones de establecer y borrar

La forma más sencilla de agregar acciones simples en la página web es insertar elementos HTML de enlace(<a>) con rutas de acción asociadas. La respuesta HTTP del servidor debe contener estos enlaces:

client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); client.println( "<!doctype html>" "<html>" "<head>" "<title>Hello PLC!</title>" "</head>" "<body>" "<h1>Hello PLC!</h1>" "<a href='/setOutput'>Set output</a><br>""<a href='/clearOutput'>Clear output</a><br>" "</body>" "</html>" ); client.flush(); 
      1. Interpretar la solicitud

Ahora el sketch debe analizar la solicitud para establecer o borrar la salida dependiendo de la ruta. Para hacer esto, obtiene la ruta desde la primera línea de la solicitud usando un objeto String llamado ruta y algunas de sus funciones.

Algunas variables son necesarias:

String header; bool firstLine = true; 

Y el carácter recibido del cliente se agrega al header

if (firstLine) { header += c; } 

Cuando se completa la solicitud, el sketch analiza la línea de encabezado para obtener la ruta.

int pos = header.indexOf(' '); String path = header.substring(pos + 1, header.indexOf(' ', pos + 1)); 

Recuerde actualizar la variable firstLine cuando el cliente envíe un carácter 'nueva línea' (\ n).

if (c == '\n') { emptyLine = true; firstLine = false; } else if (c != '\r') { emptyLine = false; } 
      1. Establecer o borrar la salida dependiendo de la ruta

El servidor debe establecer la salida en HIGH cuando la ruta sea igual a '/ setOutput' y LOW cuando la ruta sea igual a '/ clearOutput'.

if (path.compareTo("/setOutput") == 0) { // Set output digitalWrite(Q0_0, HIGH); } else if (path.compareTo("/clearOutput") == 0) { // Clear output digitalWrite(Q0_0, LOW); } 
      1. Revisión final del código

Aquí está el código completo con algunas mejoras: envía un error 404 para solicitudes desconocidas, envía una respuesta de redirección a la página principal en la solicitud de comando establecer/borrar y agrega la función sendMainPage() para facilitar la lectura y reutilizarla.
 

#include <Ethernet2.h> // Server creation EthernetServer server(80); void setup() { Serial.begin(9600UL); Serial.println("WebServer started"); // Ethernet initialization byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; byte ip[] = {10, 10, 10, 25}; Ethernet.begin(mac, ip); // Server initialization server.begin(); } void loop() { // Wait for clients EthernetClient client = server.available(); if (client) { // Parse request bool emptyLine = true; String header; bool firstLine = true; while (client.connected()) { if (client.available()) { char c = client.read(); if (firstLine) { header += c; } // The request is finished when an empty line is received if (emptyLine && (c == '\n')) { // Get path from the header line int pos = header.indexOf(' '); String path = header.substring(pos + 1, header.indexOf(' ', pos + 1)); // Act depending on path if (path.compareTo("/") == 0) { sendMainPage(client); } else if (path.compareTo("/setOutput") == 0) { // Set output digitalWrite(Q0_0, HIGH); // Redirect response redirectToMainPage(client); } else if (path.compareTo("/clearOutput") == 0) { // Clear output digitalWrite(Q0_0, LOW); // Redirect response redirectToMainPage(client); } else { // Unrecognized path sendNotFound(client); } client.flush(); // Close connection client.stop(); break; } // The request is finished when a blank line is received if (c == '\n') { emptyLine = true; firstLine = false; } else if (c != '\r') { emptyLine = false; } } } } } void sendNotFound(EthernetClient &client) { // Path not found client.println("HTTP/1.1 404 Not Found"); client.println("Content-Type: text/plain"); client.println(); client.println("404 Not Found"); } void redirectToMainPage(EthernetClient &client) { // Redirect to main page client.println("HTTP/1.1 303 See Other"); client.println("Location: /"); client.println(); } void sendMainPage(EthernetClient &client) { // Send the response client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); client.println( "<!doctype html>" "<html>" "<head>" "<title>Hello PLC!</title>" "</head>" "<body>" "<h1>Hello PLC!</h1>" "<a href='/setOutput'>Set output</a><br>" "<a href='/clearOutput'>Clear output</a><br>" "</body>" "</html>" ); }

Capítulo #4

Este capítulo explica cómo mostrar un valor de entrada actualizado dinámicamente en la página web. Obviamente, también es posible mostrar otros valores dinámicos de variables o cualquier otra cosa.

      1. Mostrar el valor de entrada

Se agrega un elemento de texto HTML a la página web para poner el valor de entrada. Para actualizarlo dinámicamente, es necesario agregar un poco de Javascript en la página web. La función Javascript updateInputValue () envía una solicitud al servidor para obtener el valor de entrada y, cuando se recibe la respuesta, inserta el texto recibido en el elemento HTML de valor de entrada utilizando la función getElementById () y la propiedad innerHTML.

client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); client.println( "<!doctype html>" "<html>" "<head>" "<title>Hello PLC!</title>" "</head>" "<body>" "<h1>Hello PLC!</h1>" "<a href='/setOutput'>Set output</a><br>" "<a href='/clearOutput'>Clear output</a><br>" "<p>Input value: <span id='input-value'></span></p>""<script>""function updateInputValue() {"" var xhttp = new XMLHttpRequest();"" xhttp.onreadystatechange = function() {"" if (this.readyState == 4 && this.status == 200) {"" document.getElementById('input-value').innerHTML = this.responseText;"" }"" };"" xhttp.open('GET', 'inputValue', true);"" xhttp.send();""}""setInterval(updateInputValue, 1000);""</script>" "</body>" "</html>" ); 
      1. Procesar la solicitud de valor de entrada

El sketch debería reconocer la solicitud /inputValue, y obtener el valor de entrada usando la función digitalRead () y enviar la respuesta al cliente.

// ...
} else if (path.compareTo("/inputValue") == 0) {
  // Get the input value
  int inputValue = digitalRead(I0_1);

  // Envía el valor de entrada
  sendText(client, inputValue == HIGH ? "HIGH" : "LOW");
} else {
//... 

Sería muy bueno enviar un texto JSON o cualquiera de estos sofisticados protocolos de texto estructurado, sobretodo para aplicaciones más complejas, pero en este caso es suficiente enviar una respuesta de texto sin formato.

void sendText(EthernetClient &client, const char *text) { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/plain"); client.println(); client.println(text); } 
      1. Revisión final del código

#include <Ethernet2.h> // Server creation EthernetServer server(80); void setup() { Serial.begin(9600UL); Serial.println("WebServer started"); // Ethernet initialization byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; byte ip[] = {10, 10, 10, 25}; Ethernet.begin(mac, ip); // Server initialization server.begin(); } void loop() { // Wait for clients EthernetClient client = server.available(); if (client) { // Parse request bool emptyLine = true; String header; bool firstLine = true; while (client.connected()) { if (client.available()) { char c = client.read(); if (firstLine) { header += c; } // The request is finished when an empty line is received if (emptyLine && (c == '\n')) { // Get path from the header line int pos = header.indexOf(' '); String path = header.substring(pos + 1, header.indexOf(' ', pos + 1)); // Act depending on path if (path.compareTo("/") == 0) { sendMainPage(client); } else if (path.compareTo("/setOutput") == 0) { // Set output digitalWrite(Q0_0, HIGH); // Redirect response redirectToMainPage(client); } else if (path.compareTo("/clearOutput") == 0) { // Clear output digitalWrite(Q0_0, LOW); // Redirect response redirectToMainPage(client); } else if (path.compareTo("/inputValue") == 0) { // Get the input value int inputValue = digitalRead(I0_1); // Send input value sendText(client, inputValue == HIGH ? "HIGH" : "LOW"); } else { // Unrecognized path sendNotFound(client); } client.flush(); // Close connection client.stop(); break; } // The request is finished when a blank line is received if (c == '\n') { emptyLine = true; firstLine = false; } else if (c != '\r') { emptyLine = false; } } } } } void sendNotFound(EthernetClient &client) { // Path not found client.println("HTTP/1.1 404 Not Found"); client.println("Content-Type: text/plain"); client.println(); client.println("404 Not Found"); } void redirectToMainPage(EthernetClient &client) { // Redirect to main page client.println("HTTP/1.1 303 See Other"); client.println("Location: /"); client.println(); } void sendMainPage(EthernetClient &client) { // Send the response client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); client.println( "<!doctype html>" "<html>" "<head>" "<title>Hello PLC!</title>" "</head>" "<body>" "<h1>Hello PLC!</h1>" "<a href='/setOutput'>Set output</a><br>" "<a href='/clearOutput'>Clear output</a><br>" "<p>Input value: <span id='input-value'></span></p>" "<script>" "function updateInputValue() {" " var xhttp = new XMLHttpRequest();" " xhttp.onreadystatechange = function() {" " if (this.readyState == 4 && this.status == 200) {" " document.getElementById('input-value').innerHTML = this.responseText;" " }" " };" " xhttp.open('GET', 'inputValue', true);" " xhttp.send();" "}" "setInterval(updateInputValue, 1000);" "</script>" "</body>" "</html>" ); } void sendText(EthernetClient &client, const char *text) { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/plain"); client.println(); client.println(text); }

CAPÍTULO #5

Una vez que se implementan las acciones básicas, también es posible crear otras más complejas, como las comunicaciones RS-232 / RS-485 / RS-422. Este capítulo le explicará cómo enviar datos RS-485 desde la aplicación web, pero también es posible mostrar los datos recibidos desde el RS-485.

      1. Agregue el campo RS-485

El primer paso podría ser agregar un formulario HTML para enviar los datos RS-485 al PLC. En este caso, se agrega un campo de entrada HTML para escribir datos alfanuméricos. Cuando se hace clic en el botón de enviar, se llama a la función sendRS485 (). ¡Esta función todavía no existe! Se creará en el siguiente paso.

<form action='javascript:void(0)' onsubmit='sendRS485()'> Data: <input type='text' pattern='[a-zA-Z0-9]+' title='Alphanumeric string' value='' id='rs485Data'> <input type='submit' value='Send'> </form> 
      1. Función de envío en Javascript

La función de Javascript sendRS485() obtiene los datos del campo de entrada HTML y los envía integrados en la URL de la solicitud HTTP: / rs485 / <data>.

function sendRS485() { var el = document.getElementById('rs485Data'); if (el) { var xhttp = new XMLHttpRequest(); xhttp.open('GET', '/rs485/' + el.value, true); xhttp.send(); } } 
      1. Interpretar la solicitud

Los datos de RS-485 están contenidos en la URL de la solicitud. El sketch debe analizarlo para obtener los datos para enviar. Cuando la URL comienza con la cadena '/ rs485 /', se considera una solicitud de datos RS-485 que se enviará a través de RS-485, por lo que el sketch extrae el comienzo de la URL para obtener los datos RS-485. A medida que el cliente ingiere la respuesta, solo se envía un mensaje de texto OK.

 // ...
} else if (path.startsWith("/rs485/")) {
  // Coje los datos de la solicitud
  String data = path.substring(7);

  // Envía una respuesta
  sendText(client, "OK");
  // ... 
      1. Configurar RS-485

Antes de enviar datos a través de RS-485, es obligatorio iniciar el controlador utilizando la función RS485.begin () en la configuración (). Esta función está disponible después de incluir la biblioteca RS485.h.

#include <RS485.h> 

Para inicializar el RS485 DEBE establecer la velocidad en serie. En este caso, la tasa se establece en 9600 bps.

// ... RS485.begin(9600); // ... 
      1. Enviar datos a través de RS-485

Se llama a la función RS485.write () para enviar datos a través del RS-485.

 // ...
} else if (path.startsWith("/rs485/")) {
  // Get data from the request
  String data = path.substring(7);

  // Envía datos a través de RS-485
  RS485.write(data.c_str());

  // Envía una respuesta
  sendText(client, "OK");
} else {
  // ... 
      1. Revisión final del código

#include <RS485.h> #include <Ethernet2.h> // Server creation EthernetServer server(80); void setup() { Serial.begin(9600UL); Serial.println("WebServer started"); // RS-485 initialization RS485.begin(9600); // Ethernet initialization byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; byte ip[] = {10, 10, 10, 25}; Ethernet.begin(mac, ip); // Server initialization server.begin(); } void loop() { // Wait for clients EthernetClient client = server.available(); if (client) { // Parse request bool emptyLine = true; String header; bool firstLine = true; while (client.connected()) { if (client.available()) { char c = client.read(); if (firstLine) { header += c; } // The request is finished when an empty line is received if (emptyLine && (c == '\n')) { // Get path from the header line int pos = header.indexOf(' '); String path = header.substring(pos + 1, header.indexOf(' ', pos + 1)); // Act depending on path if (path.compareTo("/") == 0) { sendMainPage(client); } else if (path.compareTo("/setOutput") == 0) { // Set output digitalWrite(Q0_0, HIGH); // Redirect response redirectToMainPage(client); } else if (path.compareTo("/clearOutput") == 0) { // Clear output digitalWrite(Q0_0, LOW); // Redirect response redirectToMainPage(client); } else if (path.compareTo("/inputValue") == 0) { // Get the input value int inputValue = digitalRead(I0_1); // Send input value sendText(client, inputValue == HIGH ? "HIGH" : "LOW"); } else if (path.startsWith("/rs485/")) { // Get data from the request String data = path.substring(7); // Send data through RS-485 RS485.write(data.c_str()); // Send a response sendText(client, "OK"); } else { // Unrecognized path sendNotFound(client); } client.flush(); // Close connection client.stop(); break; } // The request is finished when a blank line is received if (c == '\n') { emptyLine = true; firstLine = false; } else if (c != '\r') { emptyLine = false; } } } } } void sendNotFound(EthernetClient &client) { // Path not found client.println("HTTP/1.1 404 Not Found"); client.println("Content-Type: text/plain"); client.println(); client.println("404 Not Found"); } void redirectToMainPage(EthernetClient &client) { // Redirect to main page client.println("HTTP/1.1 303 See Other"); client.println("Location: /"); client.println(); } void sendMainPage(EthernetClient &client) { // Send the response client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); client.println( "<!doctype html>" "<html>" "<head>" "<title>Hello PLC!</title>" "</head>" "<body>" "<h1>Hello PLC!</h1>" "<a href='/setOutput'>Set output</a><br>" "<a href='/clearOutput'>Clear output</a><br>" "<p>Input value: <span id='input-value'></span></p>" "<form action='javascript:void(0)' onsubmit='sendRS485()'>" "Data: <input type='text' pattern='[a-zA-Z0-9]+' title='Alphanumeric string' value='' id='rs485Data'>" "<input type='submit' value='Send'>" "</form>" "<script>" "function updateInputValue() {" " var xhttp = new XMLHttpRequest();" " xhttp.onreadystatechange = function() {" " if (this.readyState == 4 && this.status == 200) {" " document.getElementById('input-value').innerHTML = this.responseText;" " }" " };" " xhttp.open('GET', 'inputValue', true);" " xhttp.send();" "}" "setInterval(updateInputValue, 1000);" "function sendRS485() {" " var el = document.getElementById('rs485Data');" " if (el) {" " var xhttp = new XMLHttpRequest();" " xhttp.open('GET', '/rs485/' + el.value, true);" " xhttp.send();" " }" "}" "</script>" "</body>" "</html>" ); } void sendText(EthernetClient &client, const char *text) { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/plain"); client.println(); client.println(text); }

Fill in the form and stay up to date of our courses and products

We create contents in order to help people interested in our products. From webinars to courses, case studies and product promotions,
increase your knowledge and start using open source based PLC's for industrial applications.

Send