En anteriores post he mencionado el espacio que Python se ha ido ganando a pulso en el mundo de la ingeniería y las matemáticas, ya sea por su sencillez de sintaxis donde más se preocupa en el fondo que en la forma, es así que si pretendes realizar algún tipo de aplicación relacionada a la ingeniería, python es tu lenguaje ideal (siempre y cuando la aplicación sea para lenguajes de alto nivel). Paralelo a ello vemos el avance de las aplicaciones en IoT que es uno de los corolarios de la tercera revolución industrial y que está recibiendo un gran empuje, entusiastas (como yo) intentamos de entender y comprender el paradigma de su funcionamiento y de sus posibilidades, y como se dice en algunas películas de peleas:
“Los hombres se entienden en la pelea“
Pues los desarrolladores se entienden cuando desarrollan, entonces que nos trae en esta ocasión, algo relativamente simple, vamos a recibir datos desde un arduino a través de la red de internet y estos datos seamos capaces de enviarlo a una web o al menos poderlos sensar, simple el enunciarlo pero que guarda cierta complejidad en su implementación; para ello necesitamos de:
- Arduino UNO.
- Placa Ethernet para Arduino.
- La base shield de Arduino Grove (esto es opcional, pues podrías usar un protoboard y cables hembra – macho).
- IDE de programación (Arduino IDE y VSCODE), también son opcionales, ya que al ser Ethernet una librería oficial de Arduino también se encuentra disponible en Arduino Web Editor y en lugar de VSCODE puedes usar un IDE más especializado como PyCharm.
- Un sensor cualquiera (entre más simple mejor) de temperatura, pero verás, cualquier sensor será útil.

EL DILEMA DEL PROTOCOLO
¿Qué protocolo usar? de los muchos que existen y que se especializan en estas lides, podemos quizá para este experimento establecer una discusión entre TCP y UDP, el primero ampliamente usado por todos pues es el que da soporte a las principales aplicaciones de internet como HTTP, FTP, SSH y SMTP, y el segundo empleado para transmisiones de audio o vídeo, leamos un poco de su funcionamiento:
Primero TCP
“Las aplicaciones envían flujos de bytes a la capa TCP para ser enviados a la red. TCP divide el flujo de bytes llegado de la aplicación en segmentos de tamaño apropiado (normalmente esta limitación viene impuesta por la unidad máxima de transferencia (MTU) del nivel de enlace de datos de la red a la que la entidad está asociada) y le añade sus cabeceras. Entonces, TCP pasa el segmento resultante a la capa IP, donde a través de la red, llega a la capa TCP de la entidad destino. TCP comprueba que ningún segmento se ha perdido dando a cada uno un número de secuencia, que es también usado para asegurarse de que los paquetes han llegado a la entidad destino en el orden correcto. TCP devuelve un reconocimiento por bytes que han sido recibidos correctamente; un temporizador en la entidad origen del envío causará un timeout si el asentimiento no es recibido en un tiempo razonable, y el (presuntamente desaparecido) paquete será entonces retransmitido. TCP revisa que no haya bytes dañados durante el envío usando un checksum; es calculado por el emisor en cada paquete antes de ser enviado, y comprobado por el receptor“, Fuente.
Ahora vamos con UDP:
“User Datagram Protocol (UDP) es un protocolo del nivel de transporte basado en el intercambio de datagramas. Permite el envío de datagramas a través de la red sin que se haya establecido previamente una conexión, ya que el propio datagrama incorpora suficiente información de direccionamiento en su cabecera. Tampoco tiene confirmación ni control de flujo, por lo que los paquetes pueden adelantarse unos a otros; y tampoco se sabe si ha llegado correctamente, ya que no hay confirmación de entrega o recepción. Su uso principal es para protocolos como DHCP, BOOTP, DNS y demás protocolos en los que el intercambio de paquetes de la conexión/desconexión son mayores, o no son rentables con respecto a la información transmitida, así como para la transmisión de audio y vídeo en tiempo real, donde no es posible realizar retransmisiones por los estrictos requisitos de retardo que se tiene en estos casos“, fuente: la misma de la anterior.
Comparativa TCP y UDP
UDP
Proporciona un nivel de transporte no fiable de datagramas, ya que apenas añade la información necesaria para la comunicación extremo a extremo al paquete que envía al nivel inferior. Lo utilizan aplicaciones como NFS (Network File System) y RCP (comando para copiar ficheros entre ordenadores remotos), pero sobre todo se emplea en tareas de control y en la transmisión de audio y vídeo a través de una red. No introduce retardos para establecer una conexión, no mantiene estado de conexión alguno y no realiza seguimiento de estos parámetros. Así, un servidor dedicado a una aplicación particular puede soportar más clientes activos cuando la aplicación corre sobre UDP en lugar de sobre TCP.
TCP
Es el protocolo que proporciona un transporte fiable de flujo de bits entre aplicaciones. Está pensado para poder enviar grandes cantidades de información de forma fiable, liberando al programador de la dificultad de gestionar la fiabilidad de la conexión (retransmisiones, pérdida de paquetes, orden en el que llegan los paquetes, duplicados de paquetes…) que gestiona el propio protocolo. Pero la complejidad de la gestión de la fiabilidad tiene un coste en eficiencia, ya que para llevar a cabo las gestiones anteriores se tiene que añadir bastante información a los paquetes que enviar. Debido a que los paquetes para enviar tienen un tamaño máximo, cuanta más información añada el protocolo para su gestión, menos información que proviene de la aplicación podrá contener ese paquete (el segmento TCP tiene una sobrecarga de 20 bytes en cada segmento, mientras que UDP solo añade 8 bytes).
Por eso, cuando es más importante la velocidad que la fiabilidad, se utiliza UDP. En cambio, TCP asegura la recepción en destino de la información para transmitir, fuente.
Finalmente podemos hacer esta analogía con algo de humor:

POR CUAL NOS DECANTAMOS
Actualmente existe una total controversia en el aspecto de la seguridad en el IoT (otro ejemplo), es cierto que los protocolos actuales o el como se implementan les falta aún camino para recorrer en términos de seguridad; sin embargo, ello no puede limitar acercarnos a entender el paradigma de lo que significa el IoT, así que haciendo dicha observación de obviar en sentido estricto la seguridad, estableceremos el esquema de lo que deseamos implementar.

Cuando nuestro Arduino ha de enviar datos de lo que ha censado, estos datos son muy grandes, y por lo general no muy diferenciados (esto es porque la diferenciación se da con la variable tiempo), imaginemos pues que estamos censando la temperatura a lo largo del día, decir que a una hora específica se está a determinada temperatura es por la resultante de la media (teniendo en cuenta la problemática de los valores extremos); podemos decir que no nos importa la secuencia de los datos ni necesitamos asegurarnos de que todos los datos lleguen, sino la gran mayoría de datos independientemente de su secuencia, entonces queda casi por obviedad que apostaremos por el protocolo UDP que se adapta mejor a nuestro requerimiento.
Decidido el protocolo ahora pasemos a la parte de implementación, la implementación tiene dos componentes en el campo del desarrollo; el primero será la programación de nuestra placa arduino montada conjuntamente con la placa ethernet, y el segundo del lado del computador (servidor más adelante) que se encargará de recepcionar los datos y de ahí si es posible enviarlos a una web o leerlos en una consola.
PROGRAMACIÓN EN ARDUINO
Intentemos bosquejar todo lo que hemos de realizar:

Entonces, abrimos el Arduino IDE y creamos un nuevo proyecto. Lo que necesitamos es incluir las librerías necesarias:
#include <Ethernet.h> //librería de ethernet
#include <EthernetUdp.h> //librería de UDP
#include <SPI.h>
La librería Ethernet es la utilizada para el manejo de la placa ethernet y la conexión a internet, Ethernet.Udp es el encargado de manejar el protocolo UDP y SPI es la librería que se encarga de la gestión de transmisión de datos entre los circuitos, la placa Ethernet en su misma descripción hace referencia a que la comunicación se realiza a través del bus SPI en los pines 11, 12 y 13 (si quieres saber más).
Ya que hemos de registrar la temperatura del ambiente, debemos de crear una variable de tipo entera y el pin donde se ha de conectar el sensor y una variable donde se almacenará los valores que se obtengan del medio, entonces:
int sensor_pin=A0;
double valor_sensor=0
Para que nuestro Arduino pueda conectarse a internet debe poder ser identificado y que se le asigne una IP, la identificación se realiza a través de la dirección MAC y es un arreglo de tipo byte:
byte mac[]={0xDE,0xAD,0xBE,0xEF,0xFE,0xEE};
El ip le asignamos a través de la clase IPAddress, este ip que le asignamos debe tener la misma configuración que el que tiene nuestra red de internet, de esa configuración debemos de coger uno tal que no lo esté usando otro dispositivo que esté conectado a nuestra red para que no existan conflictos, ¿Cómo saber la configuración de nuestra IP?, en windows es a través del CMD y digitando la instrucción ipconfig.
IPAddress ip(192,168,1,48);
Cuando enviamos un dato a través de internet nosotros debemos de indicar tanto el ip de destino como el puerto, el puerto definitivamente es un valor positivo y para no cometer errores vamos a restringirlo a ello usando la palabra reservada unsigned:
unsigned int puerto=3000;
CUESTIÓN OPCIONAL
Supongamos que tengamos cuando hagamos un requerimiento de datos este sea específico, es decir, sea que tengamos temperatura y presión, el cliente debe tener la facultad de elegir que tipo de dato desea observar, es así que debemos de almacenar dichos solicitudes en un arreglo y esto lo podemos hacer con lo que se describe a continuación.
Cuando Arduino tenga que leer los paquetes de datos y que esté sobre UDP debe ser almacenado en un arreglo de caracteres, la librería ethernet define una constante denominada UDP_TX_PACKET_MAX_SIZE equivalente a 24 y que asignaremos como tamaño del arreglo, entonces (más aquí):
char paqueteBuffer[UDP_TX_PACKET_MAX_SIZE];
Recordemos que la data que hemos recibido es de tipo Char, entonces necesitamos convertirlo a una cadena a través de un String, para ello creamos una variable que ha de recepcionar esta conversión ya que las cadenas son más sencillas de trabajar que un char o un arreglo:
String dataReq;
Así también debemos de crear la dimensión de ese paquete que se está leyendo:
int paquete_size;
FIN DE LA CUESTIÓN OPCIONAL
Finalmente creamos una instancia de ethernet UDP:
EthernetUDP udp;
Hechos todos los preparativos debemos de proceder a inicializar en la función setup.
#include <Ethernet.h> //librería de ethernet
#include <EthernetUdp.h> //librería udp
#include <SPI.h>
int sensor_pin=A0;
double valor_sensor=0;
byte mac[]={0xDE,0xAD,0xBE,0xEF,0xFE,0xEE}; //dirección MAC
IPAddress ip(192,168,1,48);//ip que asignamos a nuestro arduino
unsigned int puerto=3000;//puerto que se le asigna
char paqueteBuffer[UDP_TX_PACKET_MAX_SIZE];//dimension del char
String dataReq; //cadena para nuestro string
int paquete_size;//tamaño del paquete
EthernetUDP udp; //creamos un objeto UDP
Función Setup
Debemos de iniciar el puerto serial:
Serial.begin(9600);
Ahora debemos de inicializar la librería con las configuraciones que hemos asignado con el método begin de la clase Ethernet, ahora este método tiene sobrecarga, tal como sabemos los métodos solo se diferencian por la cantidad de parámetros cuando son sobre escritos, nosotros solo poseemos o hemos configurado una ip (el creado a través de la clase IPAddress y una dirección mac (que la tenemos en el arreglo de tipo byte), entonces usaremos el método que solo requiera de ellos:
Ethernet.begin(mac,ip);
De igual forma y con el mismo fin sucede con la librería UDP;sin embargo, esta librería requiere que como parámetro el paso de un puerto, que es el puerto por el cual se ha de comunicar (recordar que hemos asignado un puerto, el puerto 3000:
udp.begin(puerto);
Establecemos una pausa de 1.5 segundos en su equivalente 1500 mili segundos con el método delay:
delay(1500);
Con todo ello todos los elementos que necesitamos ya se encuentran inicializados, ahora debemos de observar que es lo que sucede en la siguiente función:
void setup() {
Serial.begin(9600);
Ethernet.begin(mac,ip);//inicializa el ethernet
udp.begin(puerto);//inicializar el udp
delay(1500);
}
Function Loop
¿Cómo ha de funcionar nuestra comunicación?, bueno nosotros debemos de enviar datos siempre que existan datos, es decir, debemos de asegurar que se hayan recibido correctamente los datos del sensor para que puedan ser enviados, la forma en la que se capturan estos datos es a través del método analogRead() el cual recibe como parámetro el pin donde se encuentra conectado el sensor:
valor_sensor=analogRead(sensor_pin);
Muy bien, recordemos que nuestra máquina donde configuraremos nuestro fichero en python estará fungiendo como un cliente que solicitará constantemente los datos que arduino tenga en relación a la temperatura, esto se hará a través del protocolo UDP, entonces nosotros debemos de estar atentos a la solicitud de datos y cuando este se reciba proceder al envío de los mismos, para ello solo debemos hacer la siguiente inferencia: “Si el paquete de datos es mayor que cero, entonces existe una solicitud“, el paquete de datos en UDP se realiza a través del método parsePacket():
if(udp.parsePacket()>0){}
Ahora sabemos que existe una solicitud; sin embargo, no creo que sea tan apropiado leer que es lo que se está pidiendo, ya que lo único que haremos es despachar temperatura, es por ello que solo aseguramos que exista una solicitud sin saber que es lo que solicita porque lo único que obtendrá será temperatura, ¿En qué situación si nos interesa saber el contenido?, cuando por ejemplo tengamos más de un sensor, si te encuentras en dicha situación deberías primero leer el paquete con:
udp.read(paqueteBuffer,UDP_TX_PACKET_MAX_SIZE);
String datReq(paqueteBuffer); //convierte el array en un string
Y luego de ello reemplazar:
if(udp.parsePacket()>0){}
por:
if(datReq=="Condición 1"){}
Tal que condición 1 sea quizás la solicitud de temperatura o presión
Sin embargo, para este ejemplo no nos decantamos por la lectura, sino que nos basta con saber que existe una solicitud, bueno, entonces debemos de enviar este dato a través del protocolo udp y esto lo hacemos a través del método beginPacket(), este método recibe como parámetros tanto el ip como el puerto al que vamos a conectarnos, el ip y el puerto lo obtenemos dado que un cliente ya estableció un requerimiento a través de los métodos remoteIP() y remotePort(), entonces dentro del if:
udp.beginPacket(udp.remoteIP(),udp.remotePort());
Hecho eso, capturamos la data del sensor y la enviamos, esto lo hacemos con el método print() de la clase Server que tiene como parámetro la data que se desea enviar, en nuestro caso deseamos que lo que se envíe sea el valor del sensor, se debe acotar que cuando se envía una cadena, un número o lo que fuera este será enviado como un conjunto de caracteres (Si se envía 123 este será ‘1’ ‘2’ ‘3’.
udp.print(valor_sensor);
Ahora debemos de finalizar la escritura de los datos a través del método endPacket():
udp.endPacket();
Queda de la siguiente forma:
void loop() {
valor_sensor=analogRead(sensor_pin);
if(udp.parsePacket()>0){
udp.beginPacket(udp.remoteIP(),udp.remotePort());
udp.print(valor_sensor);
udp.endPacket();
}
}
Bien, hasta el momento hemos asegurado que nuestro servidor esté en funcionamiento, esto lo podemos comprobar haciendo un pin al ip asignado a nuestro arduino, el resultado debería ser el siguiente:
