char array o String ?

buon giorno, premetto che piu o meno so la differenza tra i due e quanto sia pesante la libreria String, ma non dovrei avere problemi di memoria. devo inviare via seriale per mezzo di moduli HC12 dei valori letti da alcuni sensori da un arduino micro a un arduino mega ma con un unico "pacchetto". avevo pensato di convertire i valori dei sensori ed unirli in un unica String e che una volta ricevuta dall'altro arduino avrei fatto il procedimento inverso. la lunghezza della variabile dovrebbe essere di circa 16 caratteri cosi composta:

primo valore : un carattere per il controllo di inizio (inizialmente char)
dal 2° al 3° : temperatura (inizialmente float)
dal 4° al 7° : umidità (inizialmente float)
dal 8° al 12° : pressione (inizialmente float)
dal 13° al 15° :controlli vari (inizialmente char)
16° : carattere di controllo finale (inizialmente char)

non volevo il codice ma chiarimenti idee e procedura piu corretta dato che non sono molto esperto.
grazie

Mai usare la classe String, usa sempre gli array di char, la classe String allocando e deallocando memoria porta alla saturazione della stessa con errori imprevedibili.
Per formattare il testo guarda questa funzione qui snprintf

ciao,

se vuoi avere un ottimo compromesso da un'occhiata alla libreria PString (suggerita dal buon SukkoPera).
questa gestisce un'array di char, di lunghezza definita, "come" fosse un Oggetto String...nel senso che mette a disposizione alcuni metodi per trattare l'array come fosse una String...non sono tutti ovviamente ma, dal mio punto di vista, nella maggior parte dei casi, più che sufficienti.

Parliamo di un pacchetto di caratteri di dimensione fissa pari a 16 byte, non ha nessun senso usare la String (ma neanche la PString...).

Per formattare il pacchetto (dò per scontato che debba per forza essere fatto in quel modo, ma ci sono anche altre soluzioni che per ora tralascio) puoi usare la sprintf() occhio che però su Arduino non prevede i float ("%f") ma puoi convertire i valori in intero, se per te basta (dipende da quale debba essere il reale formato, ad esempio per indicare 15 gradi i due caratteri della struttura sono proprio "15" ossia '1' e '5' o sono in binario quindi 0x00 0x0F?).

Io ci aggiungerei anche dei separatori tra un campo e il successivo, e manderei in radio un campo alla volta, tanto in print, non in println, si accodano da soli...

Standardoil:
Io ci aggiungerei anche dei separatori tra un campo e il successivo, e manderei in radio un campo alla volta

Dipende comunque da cosa deve farci, teoricamente non è necessario neanche usare stringhe, se deve mandare valori interi bastano i byte (2 per gli int o unsigned, 4 per i float, ecc.), cosa che io farei.
Per i separatori non c'è bisogno, a patto che la sincronizzazione il byte di start sia realmente riconoscibile ossia un valore che non compare mai all'interno dei dati (es. 0xFF), ma questo problema lo delego a chi ha progettato il pacchetto... :wink:

Verissimo, ma se fa come dico io ha alcuni vantaggi:
Parte soft, senza stringhe, pstring e simili
Per sapere se va gli basta "analizzare" il traffico guardandolo con un terminale seriale
Per provare il ricevitore gli basta "scrivere" da tastiera del pc
In sostanza ogni dubbio possibile se lo fuga con un semplice terminale
Dopo che ha fatto esperienza passa a metodi più efficiente, se gli servono, che' magari gli basta questo

Per conservare i decimali, basta moltiplicare il valore per una potenza di 10: ad esempio, per inviare 15,35 invii 15 e poi 35. In ricezione, poi, sommi 15 e 35/100.

buon giorno e grazie a tutti per le risposte.
ho studiato un pochino snprintf, vediamo se ho capito qualcosa.
cosi creo la mia stringa da inviare:

char carattereIniziale = 'A';
float temperatura = 27.2;
float umidita = 48.1;
float pressione = 999.0; 
char carattereFinale = 'Z';
int temperaturaInt;
int umiditaInt;
int pressioneInt;
char totale[15];



void setup() {
  Serial.begin(9600);
  
  temperaturaInt = temperatura * 10;
  Serial.println(temperaturaInt);
  
  umiditaInt = umidita * 10;
  Serial.println(umiditaInt);
  
  pressioneInt = pressione * 10;
  Serial.println(pressioneInt);

  snprintf(totale, 15, "%c%04d%03d%05d%c" , carattereIniziale, temperaturaInt, umiditaInt, pressioneInt, carattereFinale);
  Serial.println(totale);

}

void loop() {

}

esiste un qualcosa di analogo per tornare indietro?

Vuoi dire lato ricevitore per ricostruire il numero?
La atoi(), ma devi separare la stringa ricevuta nelle sue sottostrighe

una cosa del genere:

char carattereIniziale = 'A';
float temperatura = -27.2;
float umidita = 48.1;
float pressione = 999.0; 
char carattereFinale = 'Z';
int temperaturaInt;
int umiditaInt;
int pressioneInt;
char totale[15];

char carattereInizialeRicevuto;
float temperaturaRicevuta;
float umiditaRicevuta;
float pressioneRicevuta;
char temp[4];
char umid[3];
char pres[5];
char carattereFinaleRicevuto;
void setup() {
  Serial.begin(9600);
///parte di codice della trasmittente

  Serial.println(carattereIniziale);
  
  temperaturaInt = temperatura * 10;
  Serial.println(temperaturaInt);
  
  umiditaInt = umidita * 10;
  Serial.println(umiditaInt);
  
  pressioneInt = pressione * 10;
  Serial.println(pressioneInt);

  Serial.println(carattereFinale);

  snprintf(totale, 15, "%c%04d%03d%05d%c" , carattereIniziale, temperaturaInt, umiditaInt, pressioneInt, carattereFinale);
  Serial.println(totale);
  
///parte di codice della ricevente
carattereInizialeRicevuto=totale[0];
Serial.println(carattereInizialeRicevuto);

for (int x=0;x<4;x++){
  temp[x]=totale[x+1];//x+1 dal carattere 2
}
temperaturaRicevuta=atoi(temp);
temperaturaRicevuta=temperaturaRicevuta/10;
Serial.println(temperaturaRicevuta,1);

for (int x=0;x<3;x++){
  umid[x]=totale[x+5];
}
umiditaRicevuta=atoi(umid);
umiditaRicevuta=umiditaRicevuta/10;
Serial.println(umiditaRicevuta,1);

for (int x=0;x<5;x++){
  pres[x]=totale[x+8];
}
pressioneRicevuta=atoi(pres);
pressioneRicevuta=pressioneRicevuta/10;
Serial.println(pressioneRicevuta,1);

carattereFinaleRicevuto=totale[13];
Serial.println(carattereFinaleRicevuto);

}

void loop() {

}

puo andare? mi garantisce una stabilita nel tempo?
accetto qualsiasi suggerimento (che sia alla mia portata) per migliorare il codice
grazie

Mi viene in mente subito una cosa, chiramente il tuo esempio è in linea e non prevede la vera ricezione però ti direi nella parte ricevente di controllare se esistono dati nella seriale, finché ci sono caratteri e quello corrente è il carattere d'inizio trasmissione inizierei a scriverli nell'array di destinazione, quando arrivi alla lunghezza massima dell'array esci comunque dal ciclo e poi immediatamente dopo verifichi che il primo e l'ultimo carattere corrispondano rispettivamente ai caratteri d'inizio e fine trasmissione, se è così allora la trasmissione è "valida" e puoi iniziare a separare le varie parti, altrimenti devi scartare la trasmissione.
Come otimizzazione potresti poi usare la funzione strncpy per copiare le parti di stringa dall'array di tutti i valori negli array che poi vai a usare per riconvertirli in float, non è che migliori molto ma il codice è più pulito e lineare e ti eviti tutti queri for, non indispensabile chiaramente.

Aggiungo prima d'iniziare a scrivere nell'array della ricezione gli farei un bel

memset(arrayDestinazione,0,15)

in modo che in caso di trasmissione incomplete le varie funzioni sugli array di char troverebbero sempre e comunque il terminatore

nella parte ricevente ho impostato dei controlli sulla lunghezza, sul primo carattere e ultimo carattere dei dati ricevuti. se passa il controllo la stringa viene inviata alla ricevente per un ulteriore controllo dei dati facendo un confronto con quelli inviati se è ok invia un ultima conferma (e solo a questo punto lavoro sulla stringa estrapolando come da codice i dati) altrimenti dopo tot secondi si azzera tutto e si ricomincia da capo.
non riesco a capire il funzionamento di strncpy. puoi consigliarmi una guida un sito dove leggere qualcosa.
grazie

UCAS
ufficio complicazioni affari semplici
io la mia la ho detta, e adesso la ripeto:
il programma postato occupa:

Lo sketch usa 5298 byte (16%) dello spazio disponibile per i programmi. Il massimo è 32256 byte.
Le variabili globali usano 263 byte (12%) di memoria dinamica, lasciando altri 1785 byte liberi per le variabili locali. Il massimo è 2048 byte.

io propongo una cosa del genere:

#define START '#'
#define STOP '*'
#define PRIMOCAMPO 'A'
#define ULTIMOCAMPO 'C'

float valori[ULTIMOCAMPO - PRIMOCAMPO + 1];

#define temperatura valori[0]
#define umidita valori[1]
#define pressione valori[2]
// e volendo si prosegue


void setup()
{
    // definisco i valori da trasmettere
    temperatura = -27.2;
    umidita = 48.1;
    pressione = 999.0;
    Serial.begin(9600);
    ///parte di codice della trasmittente
    Serial.println(START);

    for (byte i = PRIMOCAMPO - 1; i < ULTIMOCAMPO; i++)
    {
        Serial.print((char)(i + 'A')); // separatore
        Serial.print((int)(valori[i] * 10)); // il valore
    }

    Serial.print(STOP);
}

void loop()
{
    static char ricevuta[50]; //o qualunque altro valore di dimensione sufficente
    static byte indice = 0;

    //parte di codice della ricevente
    // posto di aspettare la stringa ricevuta nella sua totalità
    // io preferisco sempre invece trattare durante la ricezione
    // ma questo lo si trova in praticamente in tutti i miei post
    if (Serial.available())
    {
        char cx = Serial.read();

        if (cx == START)
        {
            // start ricevuto
            indice = 0;
            ricevuta[indice + 1] = 0; // termino subito la stringa
            return;// non serve proseguire
        }

        if (cx == STOP)
        {
            // terminatore arrivato
            // mando a interpretare la stringa ricevuta
            interpreta(ricevuta);
        }

        ricevuta[indice++] = cx;
        ricevuta[indice] = 0; // tengo terminata la stringa
    }
}

void interpreta(char ricevuta[])
{
    byte indice = 0;

    // legge e distribuisce i valori dalla stringa ricevuta
    while (ricevuta[indice]) // fino alla fine della stringa
    {
        if (ricevuta[indice] >= PRIMOCAMPO && ricevuta[indice] <= ULTIMOCAMPO)
        {
            // se si tratta di un separatore di campo
            // leggo il valore
            valori[ricevuta[indice] - 'A'] = (float)(atoi(&ricevuta[indice + 1]));
            // avanti un passo
            indice++;
        }
    }
}

che occupa:

Lo sketch usa 2702 byte (8%) dello spazio disponibile per i programmi. Il massimo è 32256 byte.
Le variabili globali usano 251 byte (12%) di memoria dinamica, lasciando altri 1797 byte liberi per le variabili locali. Il massimo è 2048 byte.

un pochino in meno, a parte una variabile char * locale di dimensione sufficente (ecco la ragione per cui io preferisco sempre trattare byte a byte mano a mano che arrivano
come funziona:
primo: un array di valori (perchè array? è facilmente estendibile a nuove grandezze, chesso temperatura intera ed esterna…)
secondo: una serie di define per usare dei nomi mnemonici invece che degli indici, quando servono
terzo: con 4 define definisco: primo e ultimo separatore, carattere di start e carattere di stop
creo lo array globale calcolando a compile-time la dimensione che serve
mando in trasmissione direttamente il valore che serve senza passare per variabili di appoggio che non aggiungono nulla e occupano spazio
trasmetto in un ciclo (facilmente espandibile cambiando DUE sole define
ricevo e riconosco lo start, e creo una stringa temporanea
alla ricezione dello stop mando in trattamente la stringa locale
il trattamento NON presume nulla sulla lunghezza dei campi, interpreta al volo il separatore e lo usa come indice del campo ricevuto
questo permetterebbe all’occorrenza di compattare la trasmissione senza trasmettere i valori che non sono variati, e/o trasmettere i valori nell’ordine preferito a run-time e/o usare un differente numero di cifre significative

baadaa:
nella parte ricevente ho impostato dei controlli sulla lunghezza, sul primo carattere e ultimo carattere dei dati ricevuti. se passa il controllo la stringa viene inviata alla ricevente per un ulteriore controllo dei dati facendo un confronto con quelli inviati se è ok invia un ultima conferma (e solo a questo punto lavoro sulla stringa estrapolando come da codice i dati) altrimenti dopo tot secondi si azzera tutto e si ricomincia da capo.
non riesco a capire il funzionamento di strncpy. puoi consigliarmi una guida un sito dove leggere qualcosa.
grazie

Eeeelamadonna, cos'èil lancio di un missile? :slight_smile: A parte gli scherzi se già hai implementato tutti quei controlli allora sei a posto, per la funzione strncpy puoi guardare qui, è molto semplice comunque, il primo parametro è l'array di destinazione, ovvero dove vuoi mettere la parte di stringa da convertire, il secondo paramatero è l'array contenente tutto il tuo messaggio da scomporre e il terzo sono i caratteri da copiare. Occhio che il terzo parametro NON deve essere di un valore che superi la lunghezza dell'array indicato come primo parametro.
In realtà sia il primo che il secondo parametro non sono "gli array" ma il puntatore al primo elemento dell'array. Quindi per copiare dall'array con tutto il messaggio all'altro non partendo dal primo elemento di basta indicare nomeArray+N

ciao Standardoil, ti ringrazio tanto dei tuoi consigli ed addirittura del tuo codice postato!!
ma forse non è alla mia portata ...sto cercando di capirlo da ieri ma faccio molta fatica , mi diletto un pochino in elettronica ma purtroppo non capisco molto di programmazione mi arrangio perdendoci quel poco di tempo che ho a disposizione durante la giornata e cercando di studiare un pò , ma quelle variabili definite e trattate cosi non riesco a capirle... non mi piace neanche copiare solo il codice. cercherò di perderci un po di tempo per studiare il tuo suggerimento.

Anche perché non andrebbe
Non avertene a male, ma non metto più codici completi, troppi ne approfittavano
c'è una imprecisione sul calcolo degli indici, ma è facile da capire
Il mio prendilo solo come un codice di ispirazione

ho fatto una funzione che utilizza il comando strncpy , gentilmente consigliatomi da fabpolli.

char carattereIniziale = 'A';
float temperatura = -10.8;
float umidita = 48.2;
float pressione = 901.0;
char carattereFinale = 'Z';
int temperaturaInt;
int umiditaInt;
int pressioneInt;
char totale[15];

char carattereInizialeRicevuto;
float temperaturaRicevuta;
float umiditaRicevuta;
float pressioneRicevuta;
char carattereFinaleRicevuto;

void setup() {
  Serial.begin(9600);
  ///parte di codice della trasmittente

  Serial.println(carattereIniziale);

  temperaturaInt = temperatura * 10;
  Serial.println(temperaturaInt);

  umiditaInt = umidita * 10;
  Serial.println(umiditaInt);

  pressioneInt = pressione * 10;
  Serial.println(pressioneInt);

  Serial.println(carattereFinale);

  snprintf(totale, 15, "%c%04d%03d%05d%c" , carattereIniziale, temperaturaInt, umiditaInt, pressioneInt, carattereFinale);
  Serial.println(totale);

  ///parte di codice della ricevente

  carattereInizialeRicevuto = totale[0];
  Serial.println(carattereInizialeRicevuto);

  /*for (int x=0;x<4;x++){
    temp[x]=totale[x+1];//x+1 dal carattere 2
    }
    temperaturaRicevuta=atoi(temp);
    temperaturaRicevuta=temperaturaRicevuta/10;
    Serial.println(temperaturaRicevuta,1);

    for (int x=0;x<3;x++){
    umid[x]=totale[x+5];
    }
    umiditaRicevuta=atoi(umid);
    umiditaRicevuta=umiditaRicevuta/10;
    Serial.println(umiditaRicevuta,1);

    for (int x=0;x<5;x++){
    pres[x]=totale[x+8];
    }
    pressioneRicevuta=atoi(pres);
    pressioneRicevuta=pressioneRicevuta/10;
    Serial.println(pressioneRicevuta,1);


  */


  temperaturaRicevuta = assegnoValore(1, 4) / 10; //dal carattere 1 per 4 caratteri
  Serial.println(temperaturaRicevuta, 1);

  umiditaRicevuta = assegnoValore(5, 3) / 10; //dal carattere 5 per 3 caratteri
  Serial.println(umiditaRicevuta, 1);

  pressioneRicevuta = assegnoValore(8, 5) / 10; //dal carattere 8 per 5 caratteri
  Serial.println(pressioneRicevuta, 1);

  carattereFinaleRicevuto = totale[13];
  Serial.println(carattereFinaleRicevuto);
}



float assegnoValore(byte puntatoreArray, byte lunghezzaArray) {

  char valore[lunghezzaArray];
  strncpy ( valore, totale + puntatoreArray, lunghezzaArray );
  valore[lunghezzaArray] = '\0';
  float returnedVar;
  returnedVar = atoi(valore);
  return returnedVar;

}

void loop() {

}

cosi è meglio?
grazie

Non ho provato ma credo che tu debba aumentare di un carattere la definizione dell'array valore

char valore[lunghezzaArray+1];

e manca anche la divisione per tornare al valore float corretto