Problemi con MariaDB o MySQL

Questa è la configurazione di massima del sistema:
Ho un Arduino Nano con una serie di sensori per una stazione meteo.
Collegato a questo Arduino ho un modulo RTX HC-12 che trasmette i dati.
Questo è il codice usato:

    char buf[200];
    String postStr = "";
    postStr+=String(C_temp);   // Temperature
    postStr+=",";
    postStr+=String(humidity); // pressure
    postStr+=",";
    postStr+=String(pressure); // pressureUVIndex
    postStr+=",";
    postStr+=String(F_temp);   // UVIndex
    postStr+=",";
    postStr+=String("field5"); // Wind Speed
    postStr+=",";
    postStr+=String("field6"); // Wind Direction
    postStr+=",";
    postStr+=String("field7"); // rainTicks
    postStr+=",";
    postStr+=String("field8"); // batteryVolt

    postStr.toCharArray(buf,postStr.length()+1);

    HC12.println(buf);
    delay(5000); 

La trasmissione funziona senza alcun problema oramai da giorni e senza errori, e nel frattempo monitoravo i consumi coasì da calibrare la cella fotovoltaica e la durata/ricarica della batteria.

Questi dati vengono ricevuti sempre con lo stesso modulo HC-12 da un ESP32 C3-F12, il quale ha il compito di ricevere le informazioni e di scriverle in un database MySQL in realtà un MariaDB presente nel NAS della rete LAN domestica.
I dati vengono ricevuti sempre senza errori e li posso visualizzare sul monitor seriale.
I test di collegamento al database funzionano senza problemi e funziona anche l'inserimento in un database di test (provato con due dati di esempio, impostati a mano).
Il problema attuale è che non riesco a costruire una query in grado di inserire i dati che ricevo dalla seriale.
Prima inviavo delle Stringhe ora sto provando con dei char, ma siccome sono una foca in C non sono in grado di convertire questi dati cosi da creare l'istruzione e poterli registrare nel database.
Qualche anima gentile è in grado di darmi una mano ?
Questo è il codice usato nel ESP32 (mi scuso per il pasticcio di rem presenti nel codice):

#include <MySQL_Generic.h>
#include "Credentials.h"

#define MYSQL_DEBUG_PORT Serial

// Debug Level from 0 to 4
#define _MYSQL_LOGLEVEL_      1

IPAddress server(10,20,1,21);
uint16_t server_port = 3306;    //3306;

char default_database[] = "meteo";           //"test_arduino";
char default_table[]    = "meteo";          //"test_arduino";
const char* ntpServer = "it.pool.ntp.org";
const long  gmtOffset_sec = 0;
const int   daylightOffset_sec = 3600;
const long  Latitude = 41.353333;
const long  Longitude = 19.456667;
const long  elevation = 10;
char created_at,field1,field2,field3,field4,field5,field6,field7,field8;

// Sample query
const char INSERT_SQL[] = "INSERT INTO meteo (created_at,field1,field2,field3,field4,field5,field6,field7,field8,latitude,longitude,elevation) VALUES (";
//('$created_at','$field1','$field2','$field3','$field4','$field5','$field6','$field7','$field8','$Latitude','$Longitude','$elevation') ");

MySQL_Connection conn((Client *)&client);

MySQL_Query *query_mem;

// Replace with your network credentials
const char* ssid = "MYHOME2"; // Input your wifi network name
const char* pass = "password"; // Input your wifi password

void runInsert() {
  
  // Initiate the query class instance
  MySQL_Query query_mem = MySQL_Query(&conn);

  if (conn.connected())  {
    //String INSERT_DATA = INSERT_SQL = INSERT_SQL + " (" + created_at + Data + Latitude + Longitude + elevation + ") ";
    //MYSQL_DISPLAY(INSERT_DATA);
    //Serial.println(INSERT_DATA);
    // Execute the query
    // KH, check if valid before fetching
/*
if ( !query_mem.execute(INSERT_DATA.c_str()) )  {
      
   MYSQL_DISPLAY("Insert error");
    }
    else
    {
      MYSQL_DISPLAY("Data Inserted.");
    }
 */   
  }
  else
  {
    MYSQL_DISPLAY("Disconnected from Server. Can't insert.");
  }
}

void printLocalTime(){
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    return;
  }
  //Serial.println(&timeinfo,"%Y%m%d-%H%M%S");
  char created_at[17];
  strftime(created_at, 17, "%Y%m%d-%H%M%S", &timeinfo);
  Serial.println(created_at);
}

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 5000); // wait for serial port to connect
  Serial1.begin(115200);               // Serial port to HC12
  while (!Serial1 && millis() < 5000); // wait for serial port to connect

  // Connect to Wi-Fi network with SSID and password
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    MYSQL_DISPLAY0(".");
  }
  
  // Init and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  
  printLocalTime();
  
  MYSQL_DISPLAY1("\nStarting Basic_Insert_ESP on", ARDUINO_BOARD);
  MYSQL_DISPLAY(MYSQL_MARIADB_GENERIC_VERSION);

 // Begin WiFi section
  MYSQL_DISPLAY1("Connecting to", ssid);

  MYSQL_DISPLAY3("Connecting to SQL Server @", server, ", Port =", server_port);
  MYSQL_DISPLAY5("User =", user, ", PW =", password, ", DB =", default_database);

}

//COM7
void loop() {

  if (Serial1.available()) {           // If HC-12 has data
      //char Data[200];
     char Data[200] = {(char)Serial1.read()};  
     //char X = INSERT_SQL + " (" + Data + ") ";
     char INSERT_DATA[200];
     strcpy(INSERT_DATA, INSERT_SQL);
     strcat(INSERT_DATA, "20230805-121510,");
     strcat(INSERT_DATA, Data);
     strcat(INSERT_DATA, ") ");
     
     Serial.write(INSERT_DATA);              // Send the data to Serial monitor
     Serial.println("");

     /*
     // Serial.write(Serial1.read());
      Serial.println("");      
      printLocalTime();                // Print Time
      Serial.println("");
      MYSQL_DISPLAY("Connecting...");
  
      if ( conn.connect(server, server_port, user, password) )
         if ( conn.connectNonBlocking(server, server_port, user, password) != RESULT_FAIL) {
            delay(500);
            runInsert();
            conn.close();                     // close the connection
         } 
      else 
         {
            MYSQL_DISPLAY("\nConnect failed. Trying again on next iteration.");
         }
  */
  }
}

Cosa dovrebbe fare questa istruzione ??

Comunque: String alloca da solo la dimensione del testo mentre se fai con stringhe classiche del C DEVI fare attenzione a quanto occupi. Quindi: da solo la tua INSERT_SQL sono 124 caratteri
Dichiari una stringa Data da 200 che puoi cerchi di concatenare dentro ad una stringa da dove già ci hai ficcato dentro la INSERT_SQL da 124. Mi pare che qualcosa non torna nei tuoi calcoli.

Per copiare o accodare stringhe usa strncpy e strncat (con una enne in più) che ti chiedono quanto è grande la stringa che riceve i dati e quindi controlla che non vai oltre la sua dimensione.

Per convertire da non testo a testo puoi usare itoa() per interi e ltoa() per long. Per i float/long usa dtostrf()

In alternativa puoi usare snprintf (che su Esp32 dovrebbe funzionare con i float/double). In pratica non fai print a seriale/video ma dentro ad una stringa. Per formato dei risultati vedi la printf del c.
Esempio:

char buf[200];
snprintf(buf,200,"La %s di %d is %f", "meta", 61, 61.0/2);

%d stampa numero come è, %03d invece stampa sempre solo 3 cifre riempite di 0

Si funziona e, con una piccola modifica ed un po' più d'uso di memoria, funziona anche sulle classiche AVR (vd. mio post QUI). :grin:

Guglielmo

Come ti ha già detto @nid69ita questa istruzione non ha alcun senso.

Devi dividere il tuo problema in due blocchi distinti da eseguire in sequenza:

  1. completi la ricezione di tutti i dati dalla seriale
  2. quando sei sicuro che la stringa è completa, esegui la query SQL.

Per quanto riguarda il punto 1, puoi ottenerlo usando diversi approcci.
Innanzi tutto nella tua stringa deve esserci un terminatore di riga e questa condizione dovrebbe essere soddisfatta perché stai usando una println() quindi alla fine della tua stringa viene inserito il carattere new line '\n' che corrisponde al codice numerico ASCII decimale 10 o esadecimale 0xA.

Lato ricezione quindi dovrai "accumulare" uno per uno tutti i caratteri ricevuti fino a quando non ti arriva il new line '\n'. A quel punto sai che la stringa è completa e puoi eseguire la query.

Francamente, considerata la tua poca esperienza in merito, non capisco perché ti stai facendo del male con le C string lato ESP32, mentre sul Nano hai usato serenamente la classe String... semmai sarebbe stato più sensato il contrario.

Io farei uso dei metodi messi a disposizione da questa classe anche con ESP32.

Grazie a tutti per il vostro più che rapido intervento.
I vostri consigli sono decisamente molto apprezzati.
Allora rispondo in ordine:
(L'ignoranza alla grande...) Ho messo le graffe semplicemente perchè il compilatore me le ha consigliate, a volte ci azzecca ed io lo seguo...
L'intenzione è di trasformare tutto il ricevuto in char.
Questo perchè ho letto credo su questo forum (o da qualche parte) che è meglio trattare le Stringhe come la peste bubbonica. Ma quello che non capisco è che invio un char via seriale e ricevo delle stringhe.

In tutta sincerità non mi sono molto applicato nell'apprendere il C, ed immagino vi sia chiaro. Ma ci sono elementi che mi confondono e poco chiare per la mia conoscenza. Io provengo da un fortran HP per macchine di processo usate nelle piattaforme petrolifere. Poi ho scoperto l'assembler 8080 e di seguito il BASIC in quasi tutte le sue versioni.

Per quello che riguarda la dimensione del buffer l'ho scelta abbondante non era quello il momento di essere precisi (per me) ma quello di vedere il tutto funzionare in qualche modo, poi tutto si aggiusta...

Mi ero riproposto di conoscere meglio la funzione "snprintf", ma non l'ho mai affrontata :frowning:

Giusta osservazione, ma dopo aver letto quanto già espresso prima sulle stringhe, volevo evitare di accumulare ulteriori problemi.

In ultima analisi è inutile che invii un char ma come era prima dell'ultima modifica all'Arduino gli faccio inviare una stringa, tanto poi ricevo comunque una stringa (almeno così mi è parso).
In ricezione farò il controllo del carattere terminatore "/n" cosi sono sicuro di aver ricevuto tutta la stringa intera e dopo userò quelle funzioni che mi avete suggerito ed andrò a leggermi anche i link che mi avete consigliato.
Mille grazie per la vostra cortesia.
Uei, spero di farcela, nel caso sarò di nuovo qui a fare due chiacchere con voi, sempre se non disturbo...

Metà dei problemi con le stringhe è dovuta ad una confusione tra quello che il K&Rano chiama stringhe (ovvero array di caratteri terminati da un carattere valorizzato a zero binario) e quello che in C++ viene chiamato String, ovvero una struttura ( classe ) che rappresenta quello che in Basic è una variabile stringa

Inizialmente è normale avere un po' di vonfusione in testa, poi ci fai la mano

L'altra metà dei problemi con le stringhe viene dal fatto che in C non è possibile valorizzare (=) interi array, ne confrontarli (==) tra loro ne conoscerne la dimensione (salvo casi particolari e quasi inutili) e nemmeno concatenarli (+=)
Serve imparare a farlo con funzioni apposite (string.h)

Un'altra metà dei problemi con le stringhe (sì è vero: con le stringhe i problemi sono talmente tanti che solo due metà non bastano) è capire di volta in volta la differenza tra carattere, array di caratteri e puntatore a carattere
Se poi si passa ad avere array di stringhe (o puntatori a puntatori a carattere) la confusione cerebrale iniziale è assicurata

Ma se non si perde la trebisonda all'inizio stai tranquillo che ne esci

Il problema con la classe String è che usa l'allocazione dinamica per riservare lo spazio nella RAM del microcontrollore necessario per memorizzare i caratteri che compongono la stringa.

Questo meccanismo di allocazione, se usato senza criterio, può comportare che la memoria libera a disposizione risulti frammentata.
Ad esempio, se ad un certo punto hai bisogno di un blocco di RAM contiguo lungo mettiamo 100 byte, ma il blocco più grande disponibile è di 99 byte il risultato è un bel patatrac.

Questo però NON significa che sarà sempre cosi e che quindi devi evitare l'utilissima classe String a prescindere.
Se usi con cognizione di causa la classe, anche con MCU dotate di pochissima RAM come quella del Nano, non avrai mai problemi.

Ciò detto, ritengo che conoscere (e magari padroneggiare) le C string, ovvero in altre parole gli array di char sia un qualcosa di cui un programmatore C/C++ non può fare a meno, ma la ragione non può essere "perché ho letto sul forum che devo evitare String"

Per avere una spiegazione un po' più dettagliata, leggi il post iniziale di QUESTA discussione :wink:

Guglielmo

Ciao Guglielmo ed a tutti voi,
grazie per i suggerimenti che mi avete fornito, sono stati molto più che graditi.
Ho terminato il programma che riceve i dati, direi che dopo i vostri suggerimenti (anche se non tutti sono stati presi alla lettera) direi che il progrmma funziona bene e sembra anche stabile nel funzionamento. Mi piacerebbe tanto che possiate dargli un'occhiata così da renderlo il più stabile possibile, sempre se questo sia possibile per i vostri impegni.
Posto la parte ricevente che alimenta il database con i dati ricevuti dai sensori.
In seguito posterò anche la parte software di riveamento esterna che include sia i sensori utilizzati (o che si potranno utlizzare), quindi lo schematico al termine.
Vi ringrazio anticipatamente per i vostri consigli.

/*
ESP32 Internal meteo data recorder (ESP32_IMDR).
This program is designed to populate a MySQL database within the local network with data 
collected from meteorological sensors. The collected data includes:

- External temperature in degrees Celsius;
- Atmospheric pressure in hPa;
- Humidity percentage;
- Wind speed in km/h;
- Wind direction in degrees;
- Amount of precipitation in mm/h;
- Quantity of UV irradiation in W.
- Location using Latitude, Longitude, Elevation (a.s.l.)

Based on these inputs, it is possible to calculate the perceived temperature in degrees Celsius. 
These data are collected using high-precision sensors with an error rate of approximately 0.5%. 
Temperature readings are obtained from two different sensors and compensated by averaging them.

The program has been authored by Ivano Giacchi (ZA/IK2RLM), with the invaluable assistance of 
various experts whose names are not mentioned here but are wholeheartedly appreciated for their 
dedication and contributions.

I am open to implementing any suggestions aimed at improving energy efficiency and system stability. 
If you have any advice, please do not hesitate to contact me at zaik2rlm@gmail.com.

The program receives data from an RTX HC-12 module tuned to the 433MHz frequency, which can be 
adjusted as needed, up to 470MHz. I would like to emphasize the importance of carefully selecting 
the channel to avoid any interference with other users operating within this band, such as 
official services and amateur radio operators.
This program can be used by anyone who needs it without any restrictions.
*/

#include <MySQL_Generic.h>
#include "Credentials.h"

#define MYSQL_DEBUG_PORT Serial

// Debug Level from 0 to 4
#define _MYSQL_LOGLEVEL_      0

IPAddress server(10,20,1,21);                  // Server database (this is my setup you use your database server IP)
uint16_t server_port = 3306;                   // is the 3306 standard port (idem)

char default_database[] = "meteo";             // my setup
char default_table[]    = "meteo";             // my setup

const char* ntpServer = "it.pool.ntp.org";     // my setup
const long  gmtOffset_sec = 0;                 // my setup
const int   daylightOffset_sec = 3600;         // my setup

String      Latitude = "41.353333";            // my setup
String      Longitude = "19.456667";           // my setup
String      elevation = "10";
String      Data,field1,field2,field3,field4,field5,field6,field7,field8,INSERT_DATA;

char created_at[28];

// Sample query
String INSERT_SQL = "INSERT INTO meteo.meteo (created_at,field1,field2,field3,field4,field5,field6,field7,field8,latitude,longitude,elevation) VALUES (";
//                   ('$created_at','$field1','$field2','$field3','$field4','$field5','$field6','$field7','$field8','$Latitude','$Longitude','$elevation') ");


MySQL_Connection conn((Client *)&client);

MySQL_Query *query_mem;

// Replace with your network credentials
const char* ssid = "MYHOME2";       // Input your wifi network name
const char* pass = "chiavedirete."; // Input your wifi password

void runInsert() {
  
  // Initiate the query class instance
  MySQL_Query query_mem = MySQL_Query(&conn);

  if (conn.connected())  {
     if ( !query_mem.execute(INSERT_DATA.c_str()) )  {
         MYSQL_DISPLAY("Insert error");
     }
     else {
       MYSQL_DISPLAY("Data Inserted.");
     }
  }
  else {
    MYSQL_DISPLAY("Disconnected from Server. Can't insert.");
  }
}

void printLocalTime(){ 
  
  struct tm timeinfo;
  if ( !getLocalTime(&timeinfo )) {
    Serial.println("Failed to obtain time");
    return;
  }
 
  strftime( created_at, 28, "'%Y-%m-%dT%H:%M:%S+00:00'", &timeinfo ); //+ or - then 00:00 indicate delta time from UCT
}

void setup() {
  
  Serial.begin(115200);
  while (!Serial && millis() < 5000);  // wait for serial port to connect
  Serial1.begin(115200);               // Serial port to HC12
  while (!Serial1 && millis() < 5000); // wait for serial port to connect

  // Connect to Wi-Fi network with SSID and password
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    MYSQL_DISPLAY0(".");
  }
  
  // Init and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  
  MYSQL_DISPLAY1("\nStarting Basic_Insert_ESP on", ARDUINO_BOARD);
  MYSQL_DISPLAY(MYSQL_MARIADB_GENERIC_VERSION);

 // Begin WiFi section
  MYSQL_DISPLAY1("Connecting to", ssid);

  MYSQL_DISPLAY3("Connecting to SQL Server @", server, ", Port =", server_port);
  MYSQL_DISPLAY5("User =", user, ", PW =", password, ", DB =", default_database);

}

//COM7
void loop() {

  printLocalTime();                    // Print Time
  if (Serial1.available()) {           // If HC-12 has data
          Data = Serial1.readStringUntil('\n');
          INSERT_DATA = INSERT_SQL;    // query construction
          INSERT_DATA += created_at;
          INSERT_DATA += ",";
          INSERT_DATA += Data;
          INSERT_DATA += ",";
          INSERT_DATA += Latitude;
          INSERT_DATA += ",";
          INSERT_DATA += Longitude;
          INSERT_DATA += ",";
          INSERT_DATA += elevation;
          INSERT_DATA += ")";

   MYSQL_DISPLAY("Connecting...");
  
      if ( conn.connect(server, server_port, user, password) )
         if ( conn.connectNonBlocking(server, server_port, user, password) != RESULT_FAIL) {
            delay(500);
            runInsert();
            conn.close();   // close the connection
         } 
      else {
            MYSQL_DISPLAY("\nConnect failed. Trying again on next iteration.");
      }
   delay(1000); // Data are received every minute, this is a delay, let's say for security reasons but I think it is not necessary
  }
}

Personalmente non vedo alcun problema "di stabilità".

C'è qualche variabile String di troppo e qualcuna che io avrei usato a livello locale, ma sono dettagli:

  • E' prassi comune definire le variabili usando il tutto maiuscolo solo quando si tratta di costanti.
  • Le variabili fieldx sono tutte inutilizzate.
  • La variabile INSERT_SQL dovrebbe essere una costante

Ciao contestatnt,
si hai ragione vedrò di sistemare anche questi dettagli, per il momento le variabili le mantengo per ricordarmi cosa devo inserire al loro posto, sistemerò quelle che diventeranno delle costanti.
Una volta che tutto il progetto sarà completato apporterò anche queste modifiche.
Ma era tanta la fretta di avere una vostra opinione che mi sono lasciato prendere la mano.
Adesso mi dedicherò alla parte più critica che dovrà consumare meno energia possibile ed avere la massima stabilità dal momento che l'unità è remota e funzionerà con una cella solare che ricaricherà una batteria LiPo.
Grazie ancora per tutto il vostro aiuto.