Objetivos
Material requerido.
Arduino Uno o similar. | |
Un Ethernet shield R3 | |
Un potenciómetro de 10K |
La ventaja de disponer de una SD Card
Disponer de un lector de tarjetas SD nos abre una cantidad de posibilidades inmensas, en cuanto a las cosas que podemos hacer con nuestros Arduinos, porque nos abre la puerta a utilizar código externo, tanto HTML como ya vimos en alguna sesión anterior, como a utilizar javascript. Y esto merece una pequeña explicación.
Hasta ahora, todo el código HTML que hemos ido viendo lo generaba nuestro Arduino y a través del web server interno se lo mostraba al cliente web que se conectase a él.
Al tener que generar el código con print (), suponía que todo el código que escribiéramos tenía que ir dentro del sketch de Arduino, lo que en la práctica eliminaba la posibilidad de usar un UNO y teníamos que usar automáticamente un MEGA porque los modelos sencillos de Arduino carecen de la capacidad de memoria necesaria para esto.
Pero al disponer de la tarjeta SD, en principio, podemos pasar buena parte del código HTML a ficheros en la SD, lo que nos permite descargar parte del programa, y además por otra parte nos podemos plantear usar código JavaScript para hacer cosas adicionales.
Yo no soy la persona adecuada para hablar de JavaScript porque no lo he usado nunca, pero por lo que he visto la sintaxis es muy próxima a la de C++, que ya conocemos, lo que parece indicar que la curva de aprendizaje no sería muy empinada si decidimos meternos con él.
Para los que podáis seguir unos tutoriales en ingles sobre Arduino, HTML y java, un buen sitio para empezar es la página de starting electronics, que tiene unos excelentes tutoriales (de donde he sacado el ejemplo que vamos a ver hoy), sobre estos temas y basados en el shield Ethernet que os recomiendo. La dirección es:
http://startingelectronics.com/tutorials/arduino/ethernet-shield-web-server-tutorial/
La ventaja del JavaScript es que se ejecuta en el navegador que nos consulta, con lo que nuestro Arduino queda libre para corretear por ahí haciendo sus cosas, sin preocuparse del mundo exterior y nos esto nos permite hacer algunas cosas sorprendentes.
En esta sesión, vamos a generar con JavaScript un reloj analógico (Gauge) que muestre el valor de una lectura analógica que leemos del pin A2. El resultado es una página web que muestra las variaciones en tiempo real del valor que leamos en A2, con el reloj que veis a continuación.
Esquema para protoboard
El circuito va a ser muy sencillo. Simplemente montar un potenciómetro conectado al pin A2 para leer su valor.
El programa de control
Vamos a utilizar un reloj analógico o galga (gauge) para representar el valor que leemos en cada instante en la puerta analógica A2.
El reloj está escrito en JavaScript con HTML5, y lo utilizamos simplemente como un componente estándar que puedes usar en tus propios proyectos. El valor del reloj se actualiza cada 200 ms.
Destacar aquí que no pretendemos tener el menor mérito en este programa y componente. EL desarrollo original es de Mykhailo Stadnyk y aquí vamos a utilizar la versión sin modificar, de starting electronics, que os mencionamos en el apartado anterior
Cualquier defecto es nuestro, pero los méritos son sin duda de ellos.
El programa tiene dos partes diferenciadas. Un sketch para Arduino y un par de ficheros para que copies en la tarjeta SD, con el programa JavaScript. Descomprime este fichero y copia sus dos archivos (gauge_license.txt e index.htm) al directorio raíz de una tarjeta SD limpia sin nada más:
Arduino_web_gauge.zip (6.3 kB)
Después, necesitas cargar este programa en tu Arduino Prog_66_1. Cambia simplemente la dirección IP si no se acomoda a tu rango.
Aquí os pongo un listado pulido del programa. Veréis que utiliza el mismo esquema básico de control de peticiones HTTP que utilizamos en los programas anteriores.
#include <SPI.h> #include <Ethernet.h> #include <SD.h> #define REQ_BUF_SZ 50 // Buffer para las peticiones HTTP byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192, 168, 1,177); // Pon aqui una IP de tu rango EthernetServer server(80); File webFile; // El fichero de la pagina web en la SD card char HTTP_req[REQ_BUF_SZ] = {0}; char req_index = 0; void setup() { pinMode(10, OUTPUT); digitalWrite(10, HIGH); Serial.begin(9600); // for debugging Serial.println("Initializing SD card..."); if (!SD.begin(4)) { Serial.println("ERROR - Fallo al inicar SD card!"); return; // init failed } Serial.println("SUCCESS - SD card initialized."); if (!SD.exists("index.htm")) { Serial.println("ERROR - No encuentro index.htm file!"); return; // can't find index file } Serial.println("SUCCESS - Found index.htm file."); Ethernet.begin(mac, ip); // initialize Ethernet device server.begin(); // start to listen for clients } void loop() { EthernetClient client = server.available(); if (client) { // Si tenemos cliente boolean currentLineIsBlank = true; while (client.connected()) { if (client.available()) // Hay algo pendiente de leer { char c = client.read(); if (req_index < (REQ_BUF_SZ - 1)) { HTTP_req[req_index] = c; // Montar la peticion HTTP req_index++; } if (c == '\n' && currentLineIsBlank) { client.println("HTTP/1.1 200 OK"); if (StrContains(HTTP_req, "ajax_inputs")) { client.println("Content-Type: text/xml"); client.println("Connection: keep-alive"); client.println(); // send XML file containing input states XML_response(client); } else { client.println("Content-Type: text/html"); client.println("Connection: keep-alive"); client.println(); webFile = SD.open("index.htm"); // open web page file if (webFile) { while(webFile.available()) client.write(webFile.read()); // send web page to client webFile.close(); } } Serial.print(HTTP_req); req_index = 0; StrClear(HTTP_req, REQ_BUF_SZ); break; } // every line of text received from the client ends with \r\n if (c == '\n') currentLineIsBlank = true; else if (c != '\r') currentLineIsBlank = false; } // end if (client.available()) } // end while (client.connected()) delay(1); // give the web browser time to receive the data client.stop(); // close the connection } // end if (client) } // send the XML file containing analog value
void XML_response(EthernetClient cl) { int analog_val; cl.print("<?xml version = \"1.0\" ?>"); cl.print("<inputs>"); // read analog pin A2 analog_val = analogRead(2); cl.print("<analog>"); cl.print(analog_val); cl.print("</analog>"); cl.print("</inputs>"); } // sets every element of str to 0 (clears array) void StrClear(char *str, char length) { for (int i = 0; i < length; i++) str[i] = 0; } // searches for the string sfind in the string str // returns 1 if string found // returns 0 if string not found char StrContains(char *str, char *sfind) { char found = 0; char index = 0; char len; len = strlen(str); if (strlen(sfind) > len) return 0; while (index < len) { if (str[index] == sfind[found]) { found++; if (strlen(sfind) == found) return 1; } else found = 0; index++; } return 0; }
El resultado es espectacular. Aquí os dejo un minivideo conel montaje y a ver que os parece:
Y por si hablaís JavaScript, aquí teneis el programa que hay en index.htm
<!DOCTYPE html> <html> <head> <title>Arduino Ajax Dial</title> <script> var data_val = 0; eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseIn….. function GetArduinoInputs() { nocache = "&nocache=" + Math.random() * 1000000; var request = new XMLHttpRequest(); request.onreadystatechange = function() { if (this.readyState == 4) { if (this.status == 200) { if (this.responseXML != null) { document.getElementById("input3").innerHTML = this.responseXML.getElementsByTagName('analog')[0].childNodes[0].nodeValue; data_val = this.responseXML.getElementsByTagName('analog')[0].childNodes[0].nodeValue; } } } } request.open("GET", "ajax_inputs" + nocache, true); request.send(null); setTimeout('GetArduinoInputs()', 200); } </script> </head> <body onload="GetArduinoInputs()"> <h1>Arduino Ajax Dial</h1> <p>Analog (A2): <span id="input3">...</span></p> <canvas id="an_gauge_1" data-major-ticks="0 100 200 300 400 500 600 700 800 900 1000 1023" data-type="canv-gauge" data-min-value="0" data-max-value="1023" data-onready="setInterval( function() { Gauge.Collection.get('an_gauge_1').setValue(data_val);}, 200);"> </canvas> </body> </html>
Resumen de la sesión