Query InfluxDB w/ ESP8266

Salve a tutti.

Ho da poco realizzato un server con database InfluxDB, tale database viene popolato da un sistema separato che ciclicamente pubblica i dati verso questo database.
Affiancato a InfluxDB viene hostato anche una istanza di Grafana per la visualizzazione di tutti i dati e la creazione di grafici.
Fino a qui tutto funziona correttamente e sono molto soddisfatto di tale sistema.

Ora però mi sorge l’esigenza di mostrare tali dati (o meglio l’ultimo valore) su di un display LCD 20x4. A tal problema sono arrivato alla conclusione di utilizzare una scheda ESP8266 che si occupi di svolgere la query al database per poi, come scritto in precedenza visualizzare i dati sul display.

Purtroppo sorge un problema nella definizione del codice che dovrà eseguire tale ESP8266 ovvero:
la definizione della stringa di Flux per la definizione della query non capisco come funzioni e come dovrei modificare per ottenere l’ultimo valore dal server per ogni campo del database.
Ho provato molte volte a vedere come si potesse fare dalla documentazione di InfluxDB ma non ne sono venuto a capo.
Ho successivamente effettuato molte ricerche sul Web ma non ho trovato nulla di simile a ciò che volessi fare io.

Per documentazione allego la pagina GitHub della libreria che sto tentando di utilizzare e la stringa di Flux proveniente dall’esempio correlato alla libreria “QueryAggregated.ino“.

Libreria per ESP8266 InfluxDB : GitHub - tobiasschuerg/InfluxDB-Client-for-Arduino: Simple library for sending measurements to an InfluxDB with a single network request. Supports ESP8266 and ESP32.

Stringa Query di Flux : “String query = "from(bucket: \"" INFLUXDB_BUCKET "\") |> range(start: -1h) |> filter(fn: (r) => r._measurement == \"wifi_status\" and r._field == \"rssi\""; ”

Spero, grazie all'aiuto del forum, di risolvere questo problema di natura software e mi scuso in anticipo se tale argomento non fosse in linea con il forum

Ringrazio in anticipo per le risposte fornite

Saluti

Ciao Wally_rc3, premetto di non conoscere minimamente InfluxDB ma cercando ho trovato che anche su questo DB esistono funzioni simili a quelle che solitamente vengono utilizzate negli altri DB per ottenere quanto da te voluto.

La prima soluzione può essere: Ordinare i campi della tabella in ordine decrescente e poi limitare la visualizzazione al primo record con le seguenti funzioni:

sort(columns: ["ColonnaOrdinamento"], desc: true)

limit(n:1)

Ho trovato scritto che i dati vengono salvati sul DB in ordine crescente di tempo e quindi altra soluzione, che è la composizione delle 2 precedenti, può essere:

bottom(n:1)

Ciao Diego67,
Innanzitutto ti ringrazio per la rapidissima risposta che mi hai fornito.

Tornando al progetto, precedentemente avevo intenzione(seguendo la documentazione di InfluxDB) di utilizzare la funzione “last()” poiché forniva l'ultimo valore registrato nel database e credevo che questo bastasse per lo scopo che volevo raggiungere, ma il metodo che mi hai suggerito tu Diego con l’utilizzo della funzione “bottom()” mi sembra di gran lunga migliore rispetto alla funzione “last()”quindi a mio parare continuerei con l’utilizzo della funzione da te suggerita.

Una possibile ulteriore espansione potrebbe essere quella di far ottenere, ad esempio, 10 valori per poi fare la media localmente dalla scheda ESP8266 per ottenere dei risultati filtrati.
Ma questo potrei pensarci in futuro se si dovessero verificare dei problemi.

Comunque, cambiando un po' di parti della stringa di origine che è:

String query = "from(bucket: \"" INFLUXDB_BUCKET "\") |> range(start: -1h) |> filter(fn: (r) => r._measurement == \"wifi_status\" and r._field == \"rssi\"";

E aggiungendo le parti necessarie consultando la documentazione di influx la stringa cambia in:

String query = "from(bucket: "NOME_DATABASE") |> range(start: -1h) |> filter(fn: (r) => r._measurement == "NOME_MEASUREMENT" and r._field == "NOME_CAMPO) |> bottom(n:1)";

Dove:

NOME_DATABASE : E’ il nome del database poiché la versione di InfluxDB che utilizzo (v1.8.X) e la libreria che sto utilizzando mi implica tale accorgimento
NOME_MEASUREMENT : Tale nome se non sbaglio nel mio caso coincide con il nome del database e quindi rimane lo stesso del punto precedente
NOME_CAMPO : E’ il nome della “colonna” dove viene inserito il valore

In questo caso comunque definisco che debba tener conto solo dei valori registrati nell’ultima ora

Compilando però tale codice mi si presentano errori di compilazione, in particolare viene generato l’errore:
“expected ',' or ';' before 'NOME_DATABASE'”.

Questo errore sta a significare solamente che per arrivare a qualcosa di funzionante devo ancora sbatterci un po' la testa :smiley: .

Per il resto ora mi è molto più chiaro il funzionamento di queste funzioni.

Grazie per la risposta Saluti

>Wally_rc3: Quando si quota un post, NON è necessario riportarlo (inutilmente) tutto; bastano poche righe per far capire di cosa si parla ed a cosa ci si riferisce, inoltre, se si risponde al post immediatamente precedente, normalmente NON è necessario alcun "quote" dato che è sottinteso. :slight_smile:

Gli utenti da device "mobile" (piccoli schermi) ringrazieranno per la cortesia :wink:

Guglielmo

P.S.: Ho eliminato io il "quote" dal tuo post qui sopra :wink:

Ciao Wally_rc3, in "C", "C++", "C#" se devi inserire un doppio apice all'interno di una stringa devi farlo precedere dal carattere "" (sequenze di escape). Quindi per scrivere:
String query = "from(bucket: "NOME_DATABASE") ...... dovrai codificarlo con:

String query = "from(bucket: \"NOME_DATABASE\") ......

Se non fai questo il doppio apice viene interpretato come inizio di una nuova stringa o termine della precedente con conseguenti errori da parte del compilatore.
Oltre a questo, senza analizzare la correttezza della sintassi, ho notato che hai scritto:

and r._field == "NOME_CAMPO)

Qui, oltre alla barra "", manca il doppio apice dopo NOME_CAMPO.

Innanzitutto mi scuso con i moderatori se ho quotato dove non non serviva :sweat_smile: .

Ciao Diego67,

questa è una cosa che non sapevo e che prima ingnoravo totalmente che sicuramente mi tornerà utile nei futuri progetti che coinvolgeranno stringhe con doppi apici, detto ciò dopo il tuo consiglio il codice compila e dopo un po’ di lavoro (seguendo principalmente la documentazione di InfluxDB e della libreria) sono giunto ad una primitiva versione del codice che purtroppo non ho ancora avuto il tempo di testare tramite l’ESP8266.

Tornando al progetto tu mi dissi che:

Diego67:
Oltre a questo, senza analizzare la correttezza della sintassi, ho notato che hai scritto:

and r._field == "NOME_CAMPO)

Qui, oltre alla barra “”, manca il doppio apice dopo NOME_CAMPO.

Molto probabilmente durante la scrittura del post ho commensso un errore di batittura ed ho levato un doppio apice dalla stringa ma nel codice tale doppio apice era presente

Detto ciò ho corretto gli errori che impedivano il funzionamento del codice ed ecco il risultato.

/**
 * QueryAggregated Example code for InfluxDBClient library for Arduino.
 * 
 * This example demonstrates querying basic aggreagated statistic parameters of WiFi signal level measured and stored in BasicWrite and SecureWrite examples.
 * 
 * Demonstrates connection to any InfluxDB instance accesible via:
 *  - unsecured http://...
 *  - secure https://... (appropriate certificate is required)
 *  - InfluxDB 2 Cloud at https://cloud2.influxdata.com/ (certificate is preconfigured)
 * 
 *  Enter WiFi and InfluxDB parameters below
 **/

#if defined(ESP32)
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
#define DEVICE "ESP32"
#elif defined(ESP8266)
#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti wifiMulti;
#define DEVICE "ESP8266"
#endif

#include <InfluxDbClient.h>
#include <InfluxDbCloud.h>

// WiFi AP SSID
#define WIFI_SSID "SSID"
// WiFi password
#define WIFI_PASSWORD "PASSWORD"
// InfluxDB v2 server url, e.g. https://eu-central-1-1.aws.cloud2.influxdata.com (Use: InfluxDB UI -> Load Data -> Client Libraries)
// InfluxDB 1.8+  (v2 compatibility API) server url, e.g. http://192.168.1.48:8086
#define INFLUXDB_URL "server-url"
// InfluxDB v2 server or cloud API authentication token (Use: InfluxDB UI -> Load Data -> Tokens -> <select token>)
// InfluxDB 1.8+ (v2 compatibility API) use form user:password, eg. admin:adminpass
#define INFLUXDB_TOKEN "server token"
// InfluxDB v2 organization name or id (Use: InfluxDB UI -> Settings -> Profile -> <name under tile> )
// InfluxDB 1.8+ (v2 compatibility API) leave empty 
#define INFLUXDB_ORG "org name/id"
// InfluxDB v2 bucket name (Use: InfluxDB UI -> Load Data -> Buckets)
// InfluxDB 1.8+ (v2 compatibility API) use database name
#define INFLUXDB_BUCKET "bucket name"

// Set timezone string according to https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
// Examples:
//  Pacific Time: "PST8PDT"
//  Eastern: "EST5EDT"
//  Japanesse: "JST-9"
//  Central Europe: "CET-1CEST,M3.5.0,M10.5.0/3"
#define TZ_INFO "CET-1CEST,M3.5.0,M10.5.0/3"

// InfluxDB client instance with preconfigured InfluxCloud certificate
InfluxDBClient client(INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN, InfluxDbCloud2CACert);



//--SETUP--
void setup() {
  Serial.begin(9600);

  // Setup wifi
  WiFi.mode(WIFI_STA);
  wifiMulti.addAP(WIFI_SSID, WIFI_PASSWORD);

  Serial.print("Connecting to wifi");
  while (wifiMulti.run() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();

  
  timeSync(TZ_INFO, "pool.ntp.org", "time.nis.gov");

  // Check server connection
  if (client.validateConnection()) {
    Serial.print("Connected to InfluxDB: ");
    Serial.println(client.getServerUrl());
  } else {
    Serial.print("InfluxDB connection failed: ");
    Serial.println(client.getLastErrorMessage());
  }
}



//--LOOP--
void loop() {

  VALUE1();
  
  Serial.println("Wait 10s");
  delay(10000);
}



//--VALUE1--
void VALUE1() {
  
  String query1 = "from(bucket: \"NOME_DATABASE\") |> range(start: -1h) |> filter(fn: (r) => r._measurement == \"NOME_MEASUREMENT\" and r._field == \"NOME_CAMPO\") |> bottom(n:1)";

  // Print composed query
  Serial.print("Querying with: ");
  Serial.println(query1);

  // Send query to the server and get result
  FluxQueryResult result1 = client.query(query1);


  while (result1.next()) {
    long value1 = result1.getValueByName("NOME_CAMPO").getLong();
    Serial.print(value1);
  
    Serial.println();
    }

  if(result1.getError() != "") {
    Serial.print("Query result error: ");
    Serial.println(result1.getError());
    }
}

Ovviamente sempre prendendo molto dall’esempio fornito nella libreria come si può vedere dalla prima parte del codice stesso.

Come detto poco fa devo ancora testarlo ma sarà il passo successivo.

Nel frattempo Grazie per il grosso aiuto fornito
Saluti

Salve a tutti,

mi rispondo al mio precedente post per condividere i risultati dei test che ho condotto riguardo al progetto riguardante la query di InfluxDB tramite ESP8266.
Premetto di aver cambiato alcune cose nella stringa di query, infatti ho cambiato il campo "bucket" in NOME_DATABASE/autogen poiché dalla documentazione di InfluxDB ho visto che nella versione 1.XX il bucket è definito come: NOME_DATABASE/RETENTION_POLICY in particolare nel mio caso le retention policy non sono attive quindi ho riempito tale campo con il termine “autogen” poiché io non utilizzo nessuna retention policy.

Tornando però ai risultati del test ho concluso con una montagna di errori e un nulla di fatto.

Infatti l’errore che rimanda la scheda è il seguente:

Query result error: Unconfigured instance

non riesco a capire sinceramente dove stia sbagliando poiché i parametri del server sono corretti infatti utilizzando Grafana per visualizzare i dati ci riesco con successo.

In secondo luogo ho provato ad effettuare la query attraverso la CLI di Flux direttamente dal server, copiando ed incollando la stringa del codice, il risultato è lo stesso ma viene generato un errore diverso da quello della scheda.
Infatti l’errore che mi si presenta è il seguente:

Error: 404 page not found : 404 Not Found

Ho contrllato più volte che Flux sia abilitato dal file di configurazione di InfluxDB.
In conclusione non mi sembra un probelma del server ma piuttosto della stringa che sto inviando al server.

In attesa di capire un modo per risolvere questo problema ringrazio in anticipo per l’aiuto fornito.
Saluti

Rispondo nuovamente al mio precedente post per aggiornare questo topic riguardo ai progressi con questo progetto.

Ho risolto (tramite l’aiuto degli sviluppatori della libreria) in maniera definitiva i problemi che legavano la configurazione e l’autenticazione dell’istanza client nel codice dell’ESP arrivando ad una versione definitiva del codice che condivido qui sotto

#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti wifiMulti;



#include <InfluxDbClient.h>
#include <InfluxDbCloud.h>

// WiFi AP SSID
#define WIFI_SSID             "WIFI_SSID"
// WiFi password
#define WIFI_PASSWORD     "WIFI_PSW"

#define INFLUXDB_URL      	"INFLUXDB_URL"
#define INFLUXDB_DB_NAME      "INFLUXDB_DB_NAME"
#define INFLUXDB_TOKEN          "INFLUXDB_USER:INFLUXDB_PSW"


#define TZ_INFO "CET-1CEST,M3.5.0,M10.5.0/3"

InfluxDBClient client(INFLUXDB_URL, INFLUXDB_DB_NAME);



//--SETUP--
void setup() {
  Serial.begin(9600);

  // Setup wifi
  WiFi.mode(WIFI_STA);
  wifiMulti.addAP(WIFI_SSID, WIFI_PASSWORD);

  Serial.print("Connecting to wifi");
  while (wifiMulti.run() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }

  client.setConnectionParamsV1(INFLUXDB_URL, INFLUXDB_DB_NAME,  INFLUXDB_TOKEN);
  
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.print(WiFi.localIP());

  Serial.println();
  
  Serial.print("Singnal strength:");
  Serial.print(WiFi.RSSI());
  Serial.print("dBm");
  
  Serial.println();

  
  timeSync(TZ_INFO, "pool.ntp.org", "time.nis.gov");

  // Check server connection
  if (client.validateConnection()) {
    Serial.print("Connected to InfluxDB: ");
    Serial.println(client.getServerUrl());
  } 
  else {
    Serial.print("InfluxDB connection failed: ");
    Serial.println(client.getLastErrorMessage());
  }
}



//--LOOP--
void loop() {

  VALUE();

  Serial.println("Wait 10s");
  delay(10000);
}



//--VALUE1--
void VALUE() {
  
  String query = "from(bucket: \"INFLUXDB_DB_NAME/autogen\") |> range(start: -1h) |> filter(fn: (r) => r._measurement == \"INFLUXDB_DB_MEASUREMNET\" and (r._field == \"INFLUXDB_FIELD\")) |> bottom(n:1)";

 
  // Print composed query
  Serial.print("Querying with: ");
  Serial.println(query);

  // Send query to the server and get result
  FluxQueryResult result = client.query(query);

  while (result.next()) {
  double value = result.getValueByName("_value").getDouble();
  Serial.print(value);
  Serial.println();
  }    

  if(result.getError() != "") {
    Serial.print("Query result error: ");
    Serial.println(result.getError());
    }
    
  result.close();
}

Questo codice però è solo la funzione che le gestisce la query, mentre per la gestione del display LCD e di altre funzioni ausiliare l’implementazione è facile e comunque già molto trattata su internet.
Detto ciò spero che questo pezzo di codice possa essere utile anche ad altri che come me si trovano in difficoltà nel raggiungimento del mio stesso obbiettivo.
Ringrazio Diego67 che mi ha messo nella “buona strada” suggerendomi alcune correzioni riguardo a miei errori su cui dopo ho potuto approfondire da solo.

Grazie
Saluti

Karma+ per aver condiviso la soluzione.