How to take data from Api ans watch it in monitor serial in Arduino IDE?

Hi!
I show reading the sensors in the dashboard of Thinger.io, now I would like to recover that reading on a different NodeMCU.
I’ve see the Json file in

https://api.thinger.io/v1/users/EstacionMeteo/buckets/Prueba_bucket/data?items=1&max_ts=0&sort=desc&authorization=([TOKENDEVICE])

[{"ts":1607192420867.0,"val":{"Humedad_DHT11":null,"Humedad_DHT22":null,"Luminosidad":80.0,"Temperatura_DHT11":null,"Temperatura_DHT22":null}}]

(I’ve desconected DHT11 and DHT22 sensor)

I’ve used ArduinoJson assistant

Expression

SON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(5)

and Postman for see “pretty” code:

[

{

    "ts": 1607193260952.0,

    "val": {

        "Humedad_DHT11": null,

        "Humedad_DHT22": null,

        "Luminosidad": 72.0,

        "Temperatura_DHT11": null,

        "Temperatura_DHT22": null

    }

}

]

In Arduino IDE I wrote this code

//Dirección de la API de ThingerIO
clienteHttp.begin(“https://api.thinger.io/v1/users/EstacionMeteo/buckets/Prueba_bucket/data?items=1&max_ts=0&sort=desc&authorization=([TOKENDEVICE])”); //Specify request destination

int codigoHttp = clienteHttp.GET(); //Envío y petición HTTP al servidor

#ifdef DEBUG
Serial.print ("Código HTTP: ");
Serial.println (codigoHttp);
#endif

//Si todo ha ido bien, devolverá un número positivo mayor que cero
if (codigoHttp > 0) {
//Si ha encontrado el recurso en el servidor
if (codigoHttp == HTTP_CODE_OK) {
#ifdef DEBUG
Serial.print ("Archivo JSON: ");
Serial.println (clienteHttp.getString());
#endif

//Parsear archivo Json
//Para obtener el tamaño del buffer visitar https://arduinojson.org/v6/assistant/
const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(5) + 100;
DynamicJsonDocument doc(capacity);

//Parsear objeto JSON
DeserializationError error = deserializeJson(doc, clienteHttp.getString());
if (error) {
//Si hay error no continua
#ifdef DEBUG
Serial.print ("Fallo al parsear JSON. Error: ");
Serial.println (error.c_str());
#endif
return;
}
//Luminosidad
float lumi = doc[“val”][“Luminosidad”];
char Luminosidad [7];
Serial.println (Luminosidad);

}
}

In monitor serial I can see

([codigoHttp]) is = a -1

I don’t know why the answer is -1 if I can see the Json with Postman and web navigator.

Can anyone helpme?

Sorry for writting mistakes, I don’t write very well in english.

Hi, check this post

Hope this helps, any doubt let me know.

1 Like

Gracias Ega. Voy a darle un vistazo.

Corrige la forma como posteas el código, selecciona todo el código y presiona el botón “</>” en el menú para que se vea bien, es incómodo leer un código así.

El enlace http cuando lo pegas en el navegador te trae la respuesta del bucket? el código -1 creo que es un error, o sea no esta obteniendo nada.

Otra cosa, en el loop únicamente tienes la solicitud http, esto quiere decir que el microcontrolador va a hacerlo tan rápido como pueda, esto puede causar problemas porque en un segundo podría ejecutar miles de veces una instrucción, tienes que controlar la frecuencia con la que se hace la consulta.

Aquí hay otro hilo, en el cual tuvo origen este how to:

Respondo aquí para seguir un orden con tu caso particular.

Saludos,

1 Like

Bueno, gracias a vuestro conocimiento he conseguido obtener los datos de los sensores. He usado el siguiente método, que Julian_Farchi explica perfectamente.

Consulta lectura datos desde data bucket - Reading data from data bucket - General - The Internet of Thinger

De esta forma, en la muestra del sensor Temperatura_DHT22 me salen los caracteres finales del archivo Json:

}}]

Con más tiempo miraré de pulirlo y de intentar que solo salgan dos decimales después del “.” (Ahora la temperatura la marca 17.23442957397590, y lo bonito sería 17.2 o 17,2)

De la forma más correcta que expones tú en el siguiente enlace:

Data bucket query, from a device - How-To - The Internet of Thinger

usando dos funciones para obtener tener la lectura de los sensores, no lo he conseguido hacer funcionar. Como dije, soy bastante nuevo y muchas cosas no entiendo.

float luminosidadExtraida (String Luminosidad, String payload) {
  int Start = payload.indexOf(Luminosidad) + Luminosidad.length() + 2;
  int End = payload.indexOf(",", Start);
  if (End < 0) // Last variable doesnt have ',' at the end, have a ']'
  {
    End = payload.indexOf("]");
  }
  String stringVar = payload.substring(Start, End);
  float var = stringVar.toFloat();
  Serial.print ("El valor de luminosidad es ");
  Serial.println (var);
  delay(5000);
  return var;
}

Al hacer la llamada a la función:

float luminosidadExtraida(String Luminosidad, String payload);

No muestra el valor de “Luminosidad”

Hola, yo te sugiero que el decimal lo corrijas en otra función, una vez ya extraída la variable, te queda mejor y más ordenado el código, después podemos ver eso, vamos a enfocarnos en extraer correctamente el dato.

Muestrame el mensaje completo, en el que se lee el bucket, y también el mensaje que enseña la función al intentar extraer el valor de luminosidad, qué respuesta te arroja esta función en la consola serial?

Entiendo que eres novato, si hay algo en particular que no entiendas pregunta explícitamente, no tengo problema en explicar cosas, por más básicas que sean, siempre y cuando haya esfuerzo y entusiasmo de tu parte :wink:

Quedo atento,

1 Like

Gracias Ega.
Para sacar el valor de una variable de dentro de una función, se haría declarando dicha variable como una variable global?

Te adjunto lo que se ve en el bucket.


Y por el monitor serie

Por entusiasmo y esfuerzo no tengo problema, antes de preguntar por aquí, llevaba 3 días buscando la solución, leyendo los mensajes de otras personas con problemas similares…me decidí a preguntar por desesperación, jajaja.

Un saudo!

Creo se ve pequeño, te lo paso de otra manera
Bucket

Date                          Humedad DHT22    Luminosidad  TemperaturaDHT22
    2020-12-08T15:57:09.696Z  57.29999923706055  51         17.100000381469727

(He suprimido HumedadDHT11 y TemperaturaDHT11 porque no me cabía todo en una misma línea)

Monitor serie.

Código HTTP: 200
[{"ts":1607439790414.0,"val":{"Humedad_DHT11":null,"Humedad_DHT22":57.099998474121094,"Luminosidad":51.0,"Temperatura_DHT11":null,"Temperatura_DHT22":17.299999237060547}}]
51.0
17.299999237060547}}] ºC
57.099998474121094%

Es una forma, pero muy ineficiente porque básicamente tienes que hacer una variable global para cada caso diferente que uses la función (y definir una función aparte, claro), la forma de hacerlo es definiendo la función e invocándola cada vez que necesitas, en este caso al momento de definirla, establecemos qué tipo de dato va a regresarnos

float luminosidadExtraida (String Luminosidad, String payload)

Esta función, por ejemplo, va a regresarnos un valor de tipo “float” y requiere dos argumentos de tipo “String”, de esta forma podemos usar cualquier payload para extraer cualquier variable de tipo float, no necesitamos hacer una función aparte para cada valor que necesitamos, me explico?

En el loop la invocas así

void loop(){
float var;
var = luminosidadExtraida (Luminosidad, payload);
}

Luminosidad y payload deben estar definidas previamente (como String, si no te da error el compilador), o puedes escribir un string allí directamente.

Hagamos algo, pega todo el código tal cual, porque creo que estas bastante novato programando arduino jejeje, así puedo ver el código completo y encontrar el error porque de verdad que al menos esto de recuperar las variables desde el api es bastante sencillo.

1 Like
void loop(){
float lux;
float hum;
lux = luminosidadExtraida ("Luminosidad", payload);
hum = luminosidadExtraida ("Humedad", payload);
}

En este ejemplo, con la función ya definida, estaríamos asignando el valor de “Luminosidad” y “Humedad” en el string “payload”, a variable lux y hum respectivamente, “Luminosidad” y “Humedad” es textual como debe estar escrito en el json que estamos recuperando, si no lo consigue regresa un -1 en la posición de la cadena de caracteres donde debe extraer, evidentemente nos va a arrojar un error o, en el mejor caso, algo totalmente alejado de la realidad.

1 Like

:smile: si, soy bastante novato, jajajja.
Te paso el código.

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ThingerESP8266.h>

//Datos Thinger.io
#define USERNAME "EstacionMeteo"
#define DEVICE_ID "NodeMCU_Light"
#define DEVICE_CREDENTIAL "DEVICE_CREDENCIAL"
#define _DEBUG_

// Temporizador
unsigned long ultimaConsulta = 0;
unsigned long tiempoConsulta = 10000;

/*---------IMPORTANTE no poner la "s" en "http" ------------------*/
String link = "http://api.thinger.io/v1/users/EstacionMeteo/buckets/Prueba_bucket/data?items=1&max_ts=0&sort=desc&authorization=[{MI_TOKEN}]";
const char* ssid = "MI_SSID";
const char* ssid_PASSWORD = "MI_PASS";

HTTPClient clienteHttp; //Cliente web

ThingerESP8266 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);

void configWifi () {
#ifdef _DEBUG_
  //Conexión con la red wifi
  Serial.print ("Conectando con ");
  Serial.println (ssid);
#endif

  //Configuración en modo cliente
  WiFi.mode (WIFI_STA);
  //Iniciar conexión con la red wifi
  WiFi.begin (ssid, ssid_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
#ifdef _DEBUG_
    Serial.print (".");
#endif
  }

#ifdef _DEBUG_
  Serial.println ("");
  Serial.print ("Conectado a la red ");
  Serial.println (ssid);
#endif
}

String peticionHTTP(String link) {
  String payload;
  if (WiFi.status() == WL_CONNECTED) { //Comprueba el estado de la conexión wifi
    clienteHttp.begin(link);  //Especifica el destino de la petición
    int codigoHttp = clienteHttp.GET();    //Envía la petición

#ifdef _DEBUG_
    Serial.print ("Código HTTP: ");
    Serial.println (codigoHttp);
#endif

    if (codigoHttp > 0) { //Commprobamos el código obtenido. Códigos Thinger.io = 200 - Correcto, 401 - No autorizado, 404 - No encontrado
      payload = clienteHttp.getString();   //Obtener la respuesta de solicitud en payload
      Serial.println(payload);                     //Mostrar el resultado de payload
    } else if (codigoHttp < 0) {  //Si la respuesta es menor que 0 es que hay un error. Entonces
      payload = clienteHttp.getString();
      Serial.println ("ERROR payload"); //Mostrar error por el monitor serie.
    }
    /*Busca en que n° de caracter (o indice) de la cadena comienza el “nombre” del sensor del que se busca obtener el valor. En este caso “Luminosidad”.
       Esto lo hace la función indexOf() con la cadena buscada como argumento.
       Tomando como base el índice obtenido en el punto anterior, le suma 13 (que es la longitud de la cadena “Luminosidad” incluidas las comillas, y
       desde esa posición busca el indice de la primera “,” (coma) que aparezca. Nuevamente la funcion indexOf() esta vez con dos argumentos, el primero
       es desde que índice empezar a buscar, y el segundo el caracter buscado.
       Con estos 2 datos ya sabemos la posición dentro de la cadena en la que se encuentra nuestra variable, y usamos la función substring(a+13,b) para
      recuperar el valor. a y b son los indices obtenidos en los pasos 1 y 2.*/

    /*FUENTE
      https://community.thinger.io/t/consulta-lectura-datos-desde-data-bucket-reading-data-from-data-bucket/1482/12/ */

    Serial.print (payload.substring(((payload.indexOf("Luminosidad", ((payload.indexOf("Luminosidad"))))) + 13), (payload.indexOf(",", ((payload.indexOf("Luminosidad"))))))); //Muestra el valor de Luminosidad.
    Serial.println ();
    Serial.print (payload.substring(((payload.indexOf("Temperatura_DHT22", ((payload.indexOf("Temperatura_DHT22"))))) + 19), (payload.indexOf(",", ((payload.indexOf("Temperatura_DHT22"))))))); //Muestra el valor de Temperatura_DHT22.
    Serial.println (" ºC");
    Serial.print (payload.substring(((payload.indexOf("Humedad_DHT22", ((payload.indexOf("Humedad_DHT22"))))) + 15), (payload.indexOf(",", ((payload.indexOf("Humedad_DHT22"))))))); //Muestra el valor de Humedad_DHT22.
    Serial.println ("%");
    clienteHttp.end();   //Cerramos la conexión
  }
  return payload;
}

/*float luminosidadExtraida (String Luminosidad, String payload) {
  int Start = payload.indexOf(Luminosidad) + Luminosidad.length() + 2;
  int End = payload.indexOf(",", Start);
  if (End < 0) // Last variable doesnt have ',' at the end, have a ']'
  {
   End = payload.indexOf("]");
  }
  String stringVar = payload.substring(Start, End);
  float var = stringVar.toFloat();
  Serial.print ("El valor de luminosidad es ");
  Serial.println (var);
  delay(5000);
  return var;
  }*/

void temporizador () {
  if (millis() < ultimaConsulta) {  //Comprobar si se ha dado la vuelta
    ultimaConsulta = millis();    //Asignar un nuevo valor
  }
  if ((millis() - ultimaConsulta) > tiempoConsulta) {
    ultimaConsulta = millis();  //Marca de tiempo
    peticionHTTP (link);
  }
}

void setup() {
  Serial.begin (9600);
  configWifi();
  thing.add_wifi(ssid, ssid_PASSWORD);
}

void loop() {
  temporizador();

}

Vale, entiendo lo de la variable dentro de la función.
Muchas gracias por tu tiempo.

Ahora releyendo tu contestación veo que en mi código en donde declaro la variable
String payload

Podría hacerlo dentro de los paréntesis en la función
luminosidadExtraida ()

Lo que no “veo” es porque debería meter también la variable Luminosidad cuando en esta función en concreto no la tengo declarada para obtener su valor. :sweat_smile:

jejejee efectivamente hiciste un desastre con el código del tutorial, ya te comento como yo pienso que se deben hacer las cosas, esto mas que error de concepto tiene que ver con buenas prácticas, no siempre cuando funciona un código es que está de lo mejor, me explico?

Tienes una única función que presumo quieres que haga la solicitud http y recupere el valor de cada variable, yo sugiero en esta situación usar 2 funciones, una para hacer la solicitud http y recuperar el ultimo valor del bucket y con otra función recuperar el valor de cada variable que se necesite, con esto hacemos una unica solicitud y podemos invocar la función para extraer cada valor cuantas veces necesitemos, sin necesidad de hacer muchas modificaciones.

Mira lo que vas a hacer, vas a comentar (o eliminar) todo tu código que tenga q ver con la consulta y extracción de variables

Vas a pegar esta función fuera del loop (creo que tienes claro como se definen las funciones, cualquier duda, you know what to do)

String httpRequest(String link)
{

  String payload;
  if (WiFi.status() == WL_CONNECTED) { //Check WiFi connection status
 
    HTTPClient http;  //Declare an object of class HTTPClient
 
    http.begin(link);  //Specify request destination
    int httpCode = http.GET();                                                                  //Send the request
 
    if (httpCode > 0) { //Check the returning code
 
       payload = http.getString();   //Get the request response payload
  //    Serial.println(payload);                     //Print the response payload
 
    }
 
    http.end();   //Close connection
  }

return payload;
}

Vas a definir una variable global, de tipo string, que se va a llamar lastBucketValue.

En la función “temporizador” vas a agregar este código:

lastBucketValue = httpRequest(link);

Con esto vamos a tener lo que arroja la consulta a ese link en lastBucketValue

Para extraer las variables, debes definir la siguiente función:

float varExtract(String varToExtract, String payload)
{
  int Start = payload.indexOf(varToExtract) + varToExtract.length() + 2; 
  int End = payload.indexOf(",", Start);
    if (End<0) // Last variable doesnt have ',' at the end, have a ']'
    {
      End = payload.indexOf("]");
    }
  String stringVar = payload.substring(Start, End);
  float var = stringVar.toFloat();
  return var;
}

Y después de que hayas asignado el valor en lastBucketValue vas a asignar el valor a cada variable (que no veo que hayas definido en ningun lado) de humedad y luminosidad

Las defines como variables globales tipo float
float lum, hum; (por ejemplo)

Y asignas los valores en la misma función temporizador

lum = varExtract("Luminosidad", lastBucketValue);
hum = varExtract("Humedad_DHT22", lastBucketValue);

Y eso debería funcionar sin problema…

Puedes agregar una linea de codigo a ver que es lo que hay en cada variable al momento de ejecutar el código, por ejemplo a ver que hay en lastBucketValue y en las otras lum y hum, para que veas la evolución.

Pienso que más importante que hacer funcionar tu código, es entender que es lo que hace cada paso, eso te va a dar la capacidad de modificarlo a tu antojo sin pedir apoyo, preocúpate por entender que estamos haciendo y pregunta si tienes alguna otra duda.

Otra buena práctica es programar y comentar el código en inglés, básicamente se ha vuelto un estándar no escrito jejeje… So get used to.

1 Like

WOW!! ahora creo que lo tengo bastante más claro. Gracias a tu explicación por supuesto.
Me ha servido de mucha ayuda añadir

Serial.println

para ver como iban cambiando los valores de la diferentes variables que me indicabas en la función

varExtract

Una cosa no entiendo, al buscar la posición en el string de la variable deseada con

int Start = payload.indexOf (varToExtract) + varToExtract.length() + 2;

obtenemos un valor de 46 que corresponde para lo siguiente:

[{"ts":1607587015939.0,"val":{"Humedad_DHT11":

los carácteres [{ no cuentan? por eso añadimos " +2" al final de la línea, para “saltar” las comillas finales ’ " ’ y los dos puntos ’ : ’ ?

No se si me explico correctamente.

He estado leyendo en la referencia de Arduino sobre el objeto

toFloat()

para entender como teniendo un valor así:

16.899999618530273

nos quedábamos con un valor así:

16.89

No he sido capaz de entenderlo.

De nuevo muchas gracias por mostrarme de una manera tan didáctica el código.

Exacto, eso yo recomiendo hacerlo así porque con eso puedes usar esa función para consultar cualquier link que asignes como argumento, en el caso que tuvieras que consultar multiples links.

1 Like

Exacto (menos lo de los [{ jeje)… Te explico que es lo que hace esa función, para que veas la dinámica:
int Start = payload.indexOf (varToExtract) + varToExtract.length() + 2;
En esta linea se determina el inicio de donde empieza el dato que vamos a extraer, la instrucción payload.indexOf(varToExtract) nos indica la posición donde inicia la cadena varToExtract que es el tag de la variable que necesitamos, a esto le sumamos la longitud del tag con varToExtrac.lenght() y le sumamos 2 caracteres por el cierre de comillas y los 2 puntos como habías comentado.

Con esto tenemos en donde inicia, nos falta donde termina lo que queremos extraer.

La función indexOf() acepta 2 argumentos, puedes indicarle solo lo que quieres ubicar, y puedes o no indicarle a partir de cuál posición buscar ese caracter o cadena de caracteres.

Para la posición final usamos la instrucción:
int End = payload.indexOf(",", Start);
Para que busque la siguiente , desde la posición inicial de lo que necesitamos, esto es por la estructura particular del JSON que esta dividido por comas, si estamos en la ultima variable, no tiene coma al final, en consecuencia, la instrucción va a arrojarnos un -1, por ello el if porque la ultima variable termina en ]

Con ambas posiciones, extraemos el string entre ellas con:
String stringVar = payload.substring(Start, End);

Es importante destacar que esta variable es un string, no podemos hacer ningun calculo con ella, por eso la convertimos en float con:
float var = stringVar.toFloat();

Esto lo ves porque el Serial.print() trunca los decimales, si invocas por ejemplo Serial.print(var,6); creo que te muestra 6 posiciones decimales.

Espero haberme explicado.

1 Like

Te has explicado perfectamente.
Mañana probaré el Serial.print (var, 6) y te digo.

Muchas gracias.

Ya probé a añadir el 6 dentro de Serial.print
Gracias por tu ayuda.
Saludos.

Vale, perfecto, éxito en tu proyecto.

1 Like