Publicando datos en Thingspeak. Parte 4: Usando POST

En un artículo anterior de esta serie te mostré como enviar datos de sensores al servidor de Thingspeak con un programa escrito en Micropython. En esa ocasión utilicé el método GET del protocolo HTTP, pero este método no ha sido originalmente diseñado para enviar datos sino que se lo pensó como un mecanismo para leer datos desde el servidor (como su nombre lo indica).

Para enviar datos el método mas apropiado es POST y en este artículo te voy a enseñar a utilizarlo y a programarlo en Micropython para que puedas utilizarlo para enviar la información que quieras al servidor de Thingspeak.

El protocolo HTTP y los métodos GET y POST

A modo de repaso, recordemos los fundamentos del protocolo de comunicaciones HTTP, uno de los mas usados en Internet y dos de sus métodos mas importantes.

El protocolo HTTP

El protocolo HTTP fue creado a comienzos de la década de 1990 para definir la forma en que viajaría la información entre los servidores Web y los usuarios.

El protocolo HTTP reconoce dos partes que tienen un rol bien diferenciado: El servidor, que contiene la información y el cliente, que pretende acceder a dicha información. Por esta razón se dice que tiene o responde a una estructura cliente-servidor.

Cuando accedemos a cualquier página en la Web lo hacemos empleando el protocolo HTTP. En ese caso nosotros (o el navegador que usemos para acceder) es el cliente, que le solicita información al servidor, la cual generalmente es una página, que puede contener muchos elementos diferentes. Esto se llama solicitud o petición (request). Cuando el servidor recibe la solicitud la analiza y si puede, responde enviando la información solicitada. Eso se denomina respuesta (response). Si algo falló o el servidor no tiene la información solicitada, responderá con un mensaje de error.

Fig. 1. Estructura cliente-servidor de HTTP

Métodos GET y POST

HTTP tiene varios métodos, destinados a realizar distintas acciones. Para enviar y recibir datos en una petición los mas comunes son GET y POST. GET y POST nos permiten enviar información desde el cliente al servidor dentro de la solicitud. Sirven al mismo propósito, pero tienen características diferentes:

GET: En el método GET, la información enviada se incluye junto a la dirección o URL luego de un símbolo ? Si se envían varios datos, se deben separar con el símbolo &.

El método GET es muy simple y sencillo de implementar, pero tiene varias desventajas. La fundamental es la falta de seguridad: los datos enviados están a la vista de cualquiera. Además, los valores están limitados a letras y números y la extensión total de la URL más los datos no puede superar los 2000 caracteres.

POST: El método POST no incluye los datos con la dirección sino que los envía aparte, de manera no visible. Esto es ideal si queremos enviar información sensible o contraseñas. De esta forma tampoco hay limitación en el formato de los datos (podemos enviar imágenes o videos por ejemplo) ni en su longitud. Como contraparte, es más complicado de implementar.

Enviando datos a Thingspeak con POST

En el artículo anterior, terminamos con este programa que enviaba los datos del sensor DHT11 al servidor cada un minuto:

#Envia temperatura y humedad a Thingspeak

import network, time, urequests
from dht import DHT11
from machine import Pin

def conectaWifi (red, password):
      global miRed
      miRed = network.WLAN(network.STA_IF)     
      if not miRed.isconnected():              #Si no está conectado…
          miRed.active(True)                   #activa la interface
          miRed.connect(red, password)         #Intenta conectar con la red
          print('Conectando a la red', red +"…")
          timeout = time.time ()
          while not miRed.isconnected():           #Mientras no se conecte..
              if (time.ticks_diff (time.time (), timeout) > 10):
                  return False
      return True

sensorDHT = DHT11 (Pin(5))

if conectaWifi ("nombre", "clave"):

    print ("Conexión exitosa!")
    print('Datos de la red (IP/netmask/gw/DNS):', miRed.ifconfig())
      
    url = "https://api.thingspeak.com/update?api_key=*************"  
    
    while (True):
        time.sleep (60)
        sensorDHT.measure ()

        temp=sensorDHT.temperature ()
        hum=sensorDHT.humidity()

        print ("T={:02d} ºC, H={:02d} %".format (temp,hum))

        respuesta = urequests.get(url+"&field1="+str(temp)+"&field2="+str(hum))
        print(respuesta.text)
        print (respuesta.status_code)
        respuesta.close ()
 
else:
       print ("Imposible conectar")
       miRed.active (False)

Para enviar los datos usando POST en lugar de GET debemos hacer algunas modificaciones porque el método POST si bien nos ofrece mas funcionalidades, también es un poco mas complejo de utilizar.

Para empezar, vamos a enviar los datos en formato JSON (lease “jota-son” o también “yeison”).

JSON (JavaScript Object Notation) es un formato para la transmisión de datos en Internet que se utiliza en muchísimas aplicaciones (aunque no estén hechas en JavaScript como lo indica su nombre). Para los que usamos Python es muy familiar porque es prácticamente igual a un diccionario, es decir, un conjunto de claves con sus respectivos valores.

En Micropyrhon, la librería ujson tiene métodos para lidiar con este formato y empaquetar varias variables en un string en formato JSON y viceversa.

El código usando POST quedaría entonces como sigue:

#Envia temperatura y humedad a Thingspeak usando POST

import network, time, urequests, ujson,gc
from dht import DHT11
from machine import Pin

def conectaWifi (red, password):
      global miRed
      miRed = network.WLAN(network.STA_IF)     
      if not miRed.isconnected():              #Si no está conectado…
          miRed.active(True)                   #activa la interface
          miRed.connect(red, password)         #Intenta conectar con la red
          print('Conectando a la red', red +"…")
          timeout = time.time ()
          while not miRed.isconnected():           #Mientras no se conecte..
              if (time.ticks_diff (time.time (), timeout) > 10):
                  return False
      return True

sensorDHT = DHT11 (Pin(5))

if conectaWifi ("red", "contraseña"):

    print ("Conexión exitosa!")
    print('Datos de la red (IP/netmask/gw/DNS):', miRed.ifconfig())
      
    url = "https://api.thingspeak.com/update"
    
    while (True):
        time.sleep (60)
        sensorDHT.measure ()

        temp=sensorDHT.temperature ()
        hum=sensorDHT.humidity()
        
        print ("T={:02d} ºC, H={:02d} %".format (temp,hum))
        
        datos = {
            "api_key": "***********",
            "field1": temp,
            "field2": hum
        }
        
        respuesta = urequests.post(url, headers = {'content-type': 'application/json'}, data=ujson.dumps(datos))
        
        print(respuesta.text)
        print (respuesta.status_code)
        respuesta.close ()
        
        gc.collect ()
 
else:
       print ("Imposible conectar")
       miRed.active (False)

En este programa seguramente apreciarás un par de cosas nuevas, que analizaremos a continuación.

En la línea 3 encontramos la primer diferencia: incluimos el módulo “ujson”, que nos permitirá preparar los datos del sensor en formato JSON para subirlos al servidor, como vimos antes. También se importa el módulo “gc” que incluye métodos relacionados con el uso de la memoria RAM.

El resto se mantiene sin cambios hasta la línea 27 donde se define la url del servidor, pero ahora no incluimos la API Key como antes, ya que la suministraremos luego de otra manera.

En la línea 38 preparamos los datos a enviar en un diccionario. Se incluyen tres variables. Además de la temperatura y humedad agregamos aquí la “api_key” que nos pide Thingspeak para poder escribir en el canal.

En la línea 44 se hace la petición HTTP al servidor utilizando el método urequests.post

Como se puede apreciar, los parámetros son diferentes a cuando empleamos GET. Al utilizar POST vamos a tener que suministrar la siguiente información:

url: La dirección del servidor (en este caso de Thingspeak)

headers: Un header o cabecera añade información en el momento de hacer la petición HTTP. Existen distintos headers. En este caso en particular enviamos uno con el valor “content-type” que sirve para indicar el formato de los datos a intercambiar. En esta aplicación, como vamos a enviar los datos en formato JSON, el valor del header será “application/json”.

data: Los datos propiamente dichos. En este caso se emplea el método dumps para pasar el diccionario datos a formato JSON.

Finalmente, en la línea 50, el método g.collect hace una “limpieza” en la memoria, eliminando objetos que ya no se usen. En teoría esto se hace automáticamente pero en mi experiencia incluirlo aquí mejora la estabilidad del programa evitando algunos errores.

Como vemos, POST es algo mas complejo que GET y esto se debe a que tiene la capacidad de enviar datos de formatos muy variados. Por ejemplo, podríamos enviar una imagen o un archivo de gran tamaño, algo que no podemos hacer con GET.

Fig. 2. Datos subidos al canal con POST

Errores

La librería urequests en Micropython consume mucha memoria y puede fallar en ocasiones en dispositivos que no tengan mucha RAM, como los ESP32. Eso se ve agravado si empleamos HTTPS, como en este ejemplo. Una búsqueda en Google en los foros de Micropython arrojará numerosas consultas por este mismo tema. Por tanto no es raro que el programa anterior se detenga en algunas ocasiones con mensajes de error de lo mas variado, pero que en general tienen todos el mismo origen: la escasez de recursos. Desde luego el programa se puede mejorar incluyendo la petición dentro de una estructura try – except que nos permita manejar el error que se pueda producir, pero esto no resuelve el problema.

Afortunadamente, tenemos a nuestra disposición otras formas de enviar datos a un servidor que están mas adaptadas a dispositivos de capacidades modestas, como es MQTT, que veremos en un próximo artículo.

Conclusiones

Vimos como utilizar el método POST del protocolo HTTP para enviar datos a Thingspeak (aunque el procedimiento es extensivo a cualquier otra plataforma). También vimos en que consiste el formato JSON y de que manera lo debemos utilizar para enviar datos. El método POST tiene mas posibilidades que GET, pero también tiene complicaciones adicionales.

Navegación de la serie<< Publicando datos en Thingspeak. Parte 3: EjemploPublicando datos en Thingspeak. Parte 5: Usando MQTT >>

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Habilitar notificaciones OK No, gracias