Continuación de la entrada anterior: #6 Curso IoT con Arduino y ESP8266 WiFi: Ejemplo Arduino como puente
5.8.2) Cliente: Envió de datos a Thingspeak
De entre todos los ejemplos este posiblemente sea el más usado ya que por lo general en la mayoría de proyectos nos es suficiente con enviar los datos que recoge el Arduino a un servidor para posteriormente consultarlos, por ello en este ejemplo enviaremos datos y nada más, no recogeremos la respuesta que pueda devolvernos el servidor así que tampoco sabremos si los datos llegaron bien o no… abstraernos de eso en este ejemplo hará que sea más sencillo para que comprendamos cómo funciona, en siguientes ejemplos sí que veremos la forma de recoger esos datos y utilizarlos en nuestro beneficio.
De nuevo usaremos la página de Thingspeak para recoger los datos que enviemos, así que lo que debemos de hacer es ejecutar los mismos comandos que usamos manualmente en el punto anterior pero esta vez ya con la ayuda del Arduino.
Debido a la complejidad y extensión creciente de los ejemplos se partirá el código por funciones, de este modo cada parte incluirá una explicación justo debajo de cada bloque. En el primer bloque de todos se incluye un enlace a Gist de Github por lo que no es necesario copiar el código trozo a trozo, en el enlace se encuentra todo junto y pulsando en el botón Raw puede copiarse para posteriormente pegarlo en el IDE de Arduino.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
// Código completo 4: https://goo.gl/2WXj3Y HardwareSerial &pc = Serial; #include <SoftwareSerial.h> #define SSID "CasaWiFi" #define PASS "cVYJ81664XB3gMW" #define IP "184.106.153.149" // thingspeak.com #define PORT "80" #define SERVICE "/update" #define KEY "6YB60SDFJX4RLWBW" #define SENDTIME 30000 SoftwareSerial wifi(2, 3); // RX | TX unsigned long time, previousTime=0; String parameters = ""; String cmd; void setup() { pc.begin(9600); wifi.begin(9600); delay(1000); pc.println("1.Programa iniciado"); if( !connect() ) { pc.println("2.Error conexion"); while(true); } else { pc.println("2.Conectado"); if( !configure(SSID, PASS) ) { pc.println("3.Error configuracion WiFi"); while(true); } else { pc.println("3.WiFi configurado"); pc.println("4.Inicio envio de datos:"); } } } |
En este primer bloque se definen todas las costantes, variables y la función setup() de configuración. Dentro se llama a las diferentes funciones que se han usado para partir el programa en trozos. Paso por paso se realiza una comunicación con el ESP8266, si en alguno de los pasos la comunicación da error se para el programa en un bucle infinito con la ayuda de while(true); Primero se realiza la conexión con el módulo con connect() si devuelve false es que ha dado error, lo mismo para configure(SSID, PASS), si no se consigue configurar el WiFi y conectarse al router se devolverá false y el programa terminara, en caso contrario seguirá en el loop().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void loop() { time = millis(); if( time - previousTime > SENDTIME ) { previousTime = time; addParameter("key" , KEY ); // String addParameter("field1", time / 1000 ); // int // addParameter("field2", time ); // unsigned long // addParameter("field3", time / 0.288 ); // float pc.println( send(IP, PORT, SERVICE) ? " Envio OK" : " Envio Error" ); } } |
Para preparar los datos se usa la función addParameter que nos permite pasar dos parámetros, el primero de ello es el nombre de la variable y el segundo el valor de esa variable, la función se encarga de concatenar los datos de forma adecuada para que la URL de conexión sea correcta.
Por último se llama a la función send(IP, PORT, SERVICE) que se conecta con el servidor indicado y envía los datos. Dependiendo del resultado obtendremos un true o un false. Si nos fijamos en el código no hay el típico if(/*condición*/){/*ejecución true*/}else{/*ejecución false*/}, si no que se ha hecho uso del operador ternario, este operador es la operación if else de forma abreviada, su estructura es la siguiente: boolean resultado = ( 5+2==7 ? true : false ), en el caso del ejemplo se devuelve uno de los dos texto posibles.
1 2 3 4 5 6 7 8 9 10 |
boolean connect() { wifi.println("ATE0"); delay(1000); cleanWiFiBuffer(); wifi.println("AT"); delay(1000); return wifi.find("OK") ? true : false; } |
Es la primera función que se conectara al ESP8266 mediante comandos AT, por lo que primero se comprueba si el módulo está conectado y al hacerle un AT nos responde un OK. El método find de la librería SoftwareSerial permite buscar un texto concreto entre la respuesta obtenida.
Después de ejecutar cualquier comando el ESP8266 puede tardar un tiempo en responder, por ello es necesario añadir delays después de cada comando, el tiempo de pausa variara dependiendo del comando, con algunos podemos apurar más y con otros será necesario mas tiempo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
boolean configure( String ssid, String pass ) { wifi.println("AT+CWMODE=3"); delay(1000); cmd = "AT+CWJAP=\""; cmd += ssid; cmd += "\",\""; cmd += pass; cmd += "\""; wifi.println(cmd); delay(5000); if( !wifi.find("OK") ) return false; else { cmd = "AT+CIPSTA?"; wifi.println(cmd); delay(1000); return wifi.find("OK") ? true : false; } } |
En realidad la configuración del WiFi no es obligatoria hacerla cada vez pues el ESP8266 la guarda en su memoria interna aun desconectándolo de la corriente, sin embargo para evitarnos problemas realizaremos la configuración igualmente.
Después con el comando AT+CIPSTA? Consultaremos la IP actual del módulo, si tiene una IP configurada significara que se ha conectado al router correctamente. Además de la IP también nos responde con un OK así que solo se ha de comprobar eso para cerciorarnos de que la conexión es correcta.
1 2 3 4 |
void cleanWiFiBuffer() { while( wifi.available() > 0 && wifi.read() != -1 ); } |
Cada vez que el ESP8266 nos responde la respuesta obtenida se guarda en el buffer (memoria) del Arduino, este buffer es de una capacidad limitada y si lo dejamos llenar perderemos información.
La función cleanWiFiBuffer se encarga de leer todos los bytes que queden en el buffer hasta dejarlo vacío.
En este código realmente no es imprescindible ya que cuando buscamos el OK en las respuestas con el método find se va vaciando el buffer carácter a carácter hasta que el OK se encuentra y como el OK siempre es el final de la respuesta pues no quedan bytes por leer. Otro cantar seria si después del OK hubiera información, en ese caso sí que se quedaría en el buffer y en la próxima lectura quizás creara un comportamiento inadecuado en la ejecución del programa al no encontrar la información esperada
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void addParameter( String name, int value ){ addParameter(name, String(value)); } void addParameter( String name, unsigned long value ){ addParameter(name, String(value)); } void addParameter( String name, float value ){ addParameter(name, String(value)); } void addParameter( String name, double value ){ addParameter(name, String(value)); } void addParameter( String name, String value ){ parameters += parameters.length() == 0 ? "?" : "&"; parameters += name; parameters += "="; parameters += value; } |
Si mientras leíais el párrafo anterior habéis estado viendo la función addParameter os habrá sorprendido que esta repetida varias veces, sin embargo cada una de estas repeticiones tiene una diferencia y es que su segundo parámetro recibido es de tipo distinto. Esto se llama sobrecargar la función y nos permite recuperar datos muy distintos para luego llamar a la función addParameter principal que se encarga de hacer la verdadera concatenación.
A la hora de usar el código es transparente, solo tenemos que pasarle un nombre y un valor de cualquier tipo de los soportados: int, unsigned long, float, double, o String.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
boolean send(String ip, String port, String service) { cmd = "AT+CIPSTART=\"TCP\",\""; cmd += ip; cmd += "\","; cmd += port; wifi.println(cmd); delay(3000); if( wifi.find("ERROR") ) return false; cmd = "GET "; cmd += service; cmd += parameters; cmd += "\r\n"; wifi.print("AT+CIPSEND="); wifi.println(cmd.length()); wifi.print(wifi.find(">") ? cmd : "AT+CIPCLOSE\r\n"); delay(3000); parameters = ""; return wifi.find("OK") ? true : false; } |
Tras concatenar todos los datos con addParameter solo quedara enviar la información al servidor con la funcion send.
Para ello se realiza la conexión al servidor con el comando AT+CIPSTART que incluye el comando, el tipo de conexión, la ip y el puerto.
Si no recibimos un ERROR se pasa a la concatenación del servicio que está a la espera de recibir los datos, de los datos a enviar y de los caracteres especiales \r\n para incluir un retorno de carro y un salto de línea. Con la cadena resultante preparada se obtiene su tamaño y se ejecuta el comando AT+CIPSEND= junto al tamaño resultante. Tras ese comando deberíamos recibir un > que indica que ya se puede enviar la cadena de texto, nuestros datos. Si no recibiéramos > se cerrara automáticamente la conexión con el servidor.
Tras esto todos los datos que se concatenan automáticamente en parameters con la función addParameter se borraran para el próximo envió. Como ultima línea de la función se comprueba si los datos se enviaron correctamente o no, es decir, si recibimos un OK o no.
* Un apunte extra sobre los caracteres \r\n. Estos caracteres cada vez que usamos println se envían automáticamente junto al texto que pasemos al método, sin embargo cuando usamos print esos caracteres no se envían, si queremos enviarlos igualmente se deberán de incluir a mano en el texto.
Es decir, estas dos líneas son lo mismo:
wifi.print("AT\r\n");
wifi.println("AT");
Se incluyen a mano en el comando AT+CIPCLOSE\r\n ya que el String contenido en la variable cmd ya los tiene concatenados con anterioridad!
Como puede verse en la siguiente captura, tras un rato ya se habían enviado unos cuantos datos correctamente a Thingspeak.
Tengo una duda cuando quiero enviar varios datos no aparecen en mi canal (ya me asegure que la key sea la correcta para el canal) específicamente quiero enviar datos de presión y temperatura del BMP180 y me muestra que se envían pero no se muestran en la pagina
Hola,
Hazlo funcionar primero desde el navegador de Internet y luego desde el Arduino.
Saludos
A lo mejor estoy haciendo una pregunta muy burda pero como se hace en el navegador
Aquí está explicado:
https://giltesa.com/2016/04/05/5-curso-iot-con-arduino-y-esp8266-wifi-modulo-wifi-esp8266-2
Saludos.
Una consulta, cuando se mandan los datos y me sale «envío OK», estos aparecen con cierto retraso en thingpeak , además que no me salen todos los datos correspondientes a cada vez que se imprime un «Envío ok», alguna ayuda para solucionar esto? :c
No recuerdo que los datos se mostrarán instantáneamente en la web, ¿introduciendolos desde el navegador de Internet se muestran al instante?
Prueba a enviar los datos a un servidor web para comprobar que todos los llegan o no, tal vez thingpeak tenga alguna limitación de tiempo entre cada envío.
Saludos.
falto codigo update.php que recibe
Se lo tendrás que pedir a
Saludos.
Hola, queria saber como enviar muchos parametros del mismo tipo, en mi caso float. Muchas gracias
Hola, así:
addParameter(«FLOAT_1», 1.1);
addParameter(«FLOAT_2», 1.2);
addParameter(«FLOAT_3», 1.3);
addParameter(«FLOAT_4», 1.4);
addParameter(«FLOAT_5», 1.5);
addParameter(«FLOAT_6», 1.6);
addParameter(«FLOAT_7», 1.7);
addParameter(«FLOAT_8», 1.8);
addParameter(«FLOAT_9», 1.9);
addParameter(«FLOAT_10», 1.10);
addParameter(«FLOAT_11», 1.11);
addParameter(«FLOAT_12», 1.12);
addParameter(«FLOAT_13», 1.13);
addParameter(«FLOAT_14», 1.14);
addParameter(«FLOAT_15», 1.15);
addParameter(«FLOAT_16», 1.16);
addParameter(«FLOAT_17», 1.17);
addParameter(«FLOAT_18», 1.18);
addParameter(«FLOAT_19», 1.19);
addParameter(«FLOAT_20», 1.20);
addParameter(«FLOAT_21», 1.21);
addParameter(«FLOAT_22», 1.22);
addParameter(«FLOAT_23», 1.23);
addParameter(«FLOAT_24», 1.24);
addParameter(«FLOAT_25», 1.25);
addParameter(«FLOAT_26», 1.26);
addParameter(«FLOAT_27», 1.27);
addParameter(«FLOAT_28», 1.28);
addParameter(«FLOAT_29», 1.29);
addParameter(«FLOAT_30», 1.30);
addParameter(«FLOAT_31», 1.31);
addParameter(«FLOAT_32», 1.32);
addParameter(«FLOAT_33», 1.33);
addParameter(«FLOAT_34», 1.34);
Saludos.
En donde puedo comenzar a hacer el curso de arduino desde cero debido a que me gusto bastante tu modo de explicar las cosas.
Muchas felicitaciones
Hola Mauricio,
No hay tal curso para Arduino, este fue una excepción ya que iba a ser para una plataforma online de aprendizaje pero al final lo dejé y termine publicando aquí para compartir lo que ya tenía escrito.
Saludos
hola, perdonar mi ingnorancia, estoy intentando aprender pero se me esta haciendo complicado, segun este codigo, que lineas debo modificar o que incluir para enviar datos de un sensor ultrasonico que estoy probando, no se si me explico, gracias de antemano
Hola Jairo,
Tienes que modificar el código del loop, concretamente las llamadas a la función addParameter para enviar los datos del sensor de ultrasonidos.
Saludos.
Hola, lo primero agradecer su tiempo y su trabajo. Tengo una duda, no me deja subir a Thingspeak más de dos señales, cuando añado una tercera se cuelga y no sube nada, que habría que añadir o cambiar?.
Otra pregunta sería, se pueden añadir varias señales a un mismo field?
Saludos
Hola Faraday,
Pues debería de permitirtelo, si usas los nombres de los parámetros que te indica Thingspeak en tu código deberían de llegar todos los parámetros, de hecho ahora envía la key y el valor 1
Prueba a hacer la llamada desde el navegador de Internet para saber si es problema de la web o de Arduino.
Saludos.
Lo siento, soy un poco novato, como podría hacer una llamada desde el navegador de internet?
Aquí te lo explican:
https://community.thingspeak.com/documentation%20…/api/
Básicamente es coger el dominio de ThingSpeak, el puerto, el fichero que recibe los datos, y los parámetros:
http://thingspeak.com:80/update?key=1111&field1=2222&field2=3333
Gracias¡¡¡ Thingspeak me actualiza los valores de las ocho gráficas, ahora he cogido sólo tú codigo y con un arduino pro mini y me actualiza hasta seis gráficas, cuando le pido ocho se cuelga, no se porqué.
Hola,
En la función de send() antes de la linea: wifi.print(«AT+CIPSEND=»); puedes imprimir por Serial el valor de parameters, así sabrás si están bien concatenados todos los valores o se ha roto la cadena por alguno de los valores de tus sensores, también puedes usar ese valor para pegarlo en el navegador junto al resto de la URL y probar si funciona.
Saludos,
Hola, he probado lo que me has comentado y tanto cuando da el envio OK como con error, me sale
?key=RURTFDEHANUTFCRN&field1=30&field2=30001&field3=104170.14&field4=30&field5=30001&field6=104170.14&field7=30001&field8=104170.14
He probado a duplicar la linea de la funcion send wifi.println(cmd.length()); y con esto empieza a enviar OK, he probado a quitarla y poner algun delay y no me ha funcionado.
Una vez me funciona OK duplicando wifi.println(cmd.length()); he ido añadiendo cosas de mi proyecto y he visto como al incluir la librería Wire.h empieza a enviar el ESP8266 como error.
Saludos
Hola buenos días , he montado con dos arduinos y dos módulos ESP8266 ESP01 un cliente y un servidor , todo funciona bien , el cliente tiene un pulsador y el servidor un led , el cual se enciende al pulsar el servidor , solo hay un pequeño problema , y es que cuando lel servidor esta sin recibir nada durante un tiempo determinado , cierra la conexión , de momento para que no ocurra , le envio un «saludo» cada minuto , me interesa saber si hay algún parámetro a modificar para que el servidor no se desconecte en modo automático, porque me interesa que el tiempo entre pulsación y activación del led sea constante , y si la pulsación coincide con el envío de refresco puede retardar el envío.
<<<<<<<<<<gracias
Hola José,
Y en vez de intentar mantener la conexión abierta, ¿No sería mejor que la abrieras, hicieras lo que tuvieras que hacer y la volvieras a cerrar?
Es lo que hacen todos los ejemplos que publique, crean una conexión al servidor, envían los datos, recuperan datos y cierra la conexión.
Saludos.
Edit:
¿Tanto les cuesta a los ESP8266 conectarse entre sí abriendo una nueva conexión con cada comunicación? Estando en una comunicación directa da a pensar que el proceso sería rápido… pero ni idea, esas cosas no las he probado.
Hola Muchas gracias , por contestar , esta aplicación que monte está pensada para medir tiempos , entonces el delay que existe entre pulsar en el cliente y reflejar la llegada en el servidor tiene que ser lo mas rápido posible , por eso utilizé esa opción.
Saludos
Hola qué tal mi duda es, como se realiza la conexión entre el modulo y el arduino
Hola Daniel,
Se hace así:
https://giltesa.com/2016/04/05/5-curso-iot-con-arduino-y-esp8266-wifi-modulo-wifi-esp8266-2
Saludos.