Calcolare lunghezza stringa... ottimizzazione?

Ciao cari!
Qualche giorno fa in un topic abbiamo visto come due codici che fanno la stessa cosa, possono arrivare ad utilizzare quantità di risorse molto diverse.
Mi sono quindi chiesta se qualcuno di voi cervelloni (non sono sarcastica) ha in mente un modo più ottimizzato per fare quello che sto facendo ora.

Premessa ed obiettivi.
Ho alcune varabili: delle float ed un unsigned long.
Invio queste variabili a pachube, singolarmente, tramite dei client.print.
Pachube però mi richiede anche di comunicare il content-length.

Per calcolare quindi la lunghezza in caratteri di tutte le variabili inviate faccio così:
converto le float con dtostrf e l'unsigned long con ltoa.
Unisco tutte le variabili così convertite in un unica stringa.
Calcolo la lunghezza della stringa con strlen.

Questo è il mio content-length.

Il codice funziona, però... probabilmente è uno spreco di risorse.
Creo una stringa e dei buffer che utilizzo in pratica solo per contare i caratteri, considerato che le variabili le invio (e preferisco fare così per questioni di formattazione della PUT) singolarmente con dei client.print.

La mia domanda è, vi viene in mente un modo diverso e migliore per ricavare la somma totale dei caratteri che compongono le mie variabili?
Al momento ho 2k liberi e 200 di FREERAM, tutto funziona alla perfezione ma se trovo il modo di liberare un po' di ram posso mettere altre cose nello sketch.

Qui sotto trovate il codice che uso ora, tutte le variabili sono locali.

/*----- Pachube -----*/

    // buffer per la conversione delle variabili
    char charbuffertout[6]; // t_out
    char charbufferhout[6]; // h_out
    char charbuffertin[6]; // t_in
    char charbufferhin[6]; // h_in
    char charbufferT1[6]; // T1 h20
    char charbufferT2[6]; // T2 Heat
    char charbufferluce[10]; //luce

    // conversione delle variabili in ASCII
    // conversione float dtostrf(floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, charBuf);
    dtostrf(mydata.toutES,4,1,charbuffertout); 
    dtostrf(mydata.houtES,4,1,charbufferhout);
    dtostrf(mydata.tinES,4,1,charbuffertin);
    dtostrf(mydata.hinES,4,1,charbufferhin);
    dtostrf(mydata.T2ES,4,1,charbufferT2);
    dtostrf(mydata.T1ES,4,1,charbufferT1);

    ltoa(mydata.mediaHzES,charbufferluce,10); // conversione long

    //composizione della stringa per il calcolo del Content-Length:
    char stringaTs[60] = "";
    strcat(stringaTs, charbuffertout);  // Field 1 t_out
    strcat(stringaTs, charbufferhout); // Field 2 h_out
    strcat(stringaTs, charbuffertin); // Field 3 t_in
    strcat(stringaTs, charbufferhin); // Field 4 h_in
    strcat(stringaTs, charbufferT1); // Field 5 T1 H20
    strcat(stringaTs, charbufferT2); // Field 6 T2 Heat
    strcat(stringaTs, charbufferluce); // Field 7 MediaLuce

    PgmPrint("Stringa: ");
    Serial.println(stringaTs);  
    PgmPrint("StringaTs lunghezza: ");
    Serial.println( strlen(stringaTs) ); // Lunghezza della stringa originale

Mi viene in mente un'idea al volo (occhio: potrebbe essere una minchiata :wink:

Invece di convertire in stringa i numeri, unirli e misurare la lunghezza della stringa risultante potresti usare un unico buffer di lunghezza tale da contenere il valore convertito più lungo, e riutilizzarlo per ogni conversione, in base a questo procedimento:

  • lunghezza totale = 0;
  • converti il valore in stringa usando buffer
  • somma la lunghezza del buffer alla lunghezza totale
  • ripeti il dal punto due per ogni valore da convertire

my 2 cents :slight_smile:

confermo l'idea di riutilizzare lo stesso buffer e usare una variabile contatore gloable, così risparmi un bel pò di array.
se poi vuoi evitare la conversione (e quindi l'uso dei buffer), se il numero è intero lo dividi per 10 fino ad ottenere 0, il numero di volte che dividi per 0 va sommato al contatore globale.
Per i float si complica un pò, a priori moltilplichi per 10 fino ad ottenre il modulo della divisione per 10 = 0 oppure raggiungi il numero di cifre dopo la virgola che ti servono (metti questo controllo o rischi cicli infiniti per via dell'infinitesimo del calcolatore con i numeri float)
poi a questo punto li elabori per ottenere la lunghezza come se fossero INT +1 (il +1 è la virgola)

-pensieri a caso-
però è strano, i numeri li stai inviado come stringa no? altrimenti se li invii in formato byte sai a priori che gli int sono 2 e i float 4 + la dimensione di cioò che indica che formato sono quei byte (ma dubito che usino questo tipo di formato/ottimizzazione quelli di pachube, se non altro perchè un numero in formato byte potrebbe essere uguale a \n o a \0 creando qualche casino all'interprete dell'HTTP che usa qusti valori per capire quando termina la pagina... anche se potresti incapsularli tra " ", perdendo però molto nell'ottimizzazione di questo metodo
-fine pensieri a caso-

legacy:
len = 0
char stringaTs[60] = "";
char buffergarbage[???]="";

/* /
dtostrf(mydata.toutES,4,1,buffergarbage);
strcat(stringaTs, buffergarbage); // Field 1
len+=strlen(buffergarbage);
/
/
dtostrf(mydata.toutES,4,1,buffergarbage);
strcat(stringaTs, buffergarbage); // Field 2
len+=strlen(buffergarbage);
...
/
*/
dtostrf(mydata.xxxxx,x,x,buffergarbage);
strcat(stringaTs, buffergarbage); // Field x
len+=strlen(buffergarbage);
....

Ma in questo modo stringaTs cresce ad ogni conversione, e ben presto supererà i 60 caratteri. In pratica hai seguito il procedimento dell'OP.

Quello che intendevo io era invece:

unsigned int totalLen;
char buf[60];    // 60 = numero a caso, calibrare in base al numero convertito più lungo + 1

totalLen = 0;

ltoa(long_value_1, buf, 10);
totalLen += strlen(buf);

ltoa(long_value_2, buf, 10);
totalLen += strlen(buf);

// ecc.

(ovviamente l'uso di variabili long è solo un esempio per illustrare il meccanismo).

guarda mamma, senza buffer :grin:

totlaLen=0;
a=datoInt;
if (a==0)
  totlaLen+=1; //nel caso sia già 0, bisogna dimensionare per contenere lo 0
while (a > 0){
 a/=10;
 totlaLen+=1;
}

DanielaES:
Invio queste variabili a pachube, singolarmente, tramite dei client.print.
Pachube però mi richiede anche di comunicare il content-length.

[...]

Creo una stringa e dei buffer che utilizzo in pratica solo per contare i caratteri, considerato che le variabili le invio (e preferisco fare così per questioni di formattazione della PUT) singolarmente con dei client.print.

la richiesta è chiara, non sono necessari i buffer :grin:

Scrivo al volo dall'iphone...
Lesto ma quel codice funziona anche per le float?
Ad occhio mi sembra possa funzionare solo per le int.
Ma Io ho prevalentemente float!

EDIT:
E poi mi sembra che ci sia anche un altro problema...
Oltre al calcolo del punto decimale, il codice non riuscirebbe a lavorare con il segno meno di una temperatura negativa.
-12.4 sono cinque caratteri...

DanielaES:
Scrivo al volo dall'iphone...
Lesto ma quel codice funziona anche per le float?
Ad occhio mi sembra possa funzionare solo per le int.
Ma Io ho prevalentemente float!

quanti numeri vuoi dopo la virgola? se per esempio 3, basta moltiplicare il tuo float per 1000 e poi esegui lo stesso identico codice, aggiungendo 1 per la virgola.

legacy:
@lesto
pero' se metti totlaLen=1
salti quell' if (a==0) totlaLen+=1; //nel caso sia già 0, bisogna dimensionare per contenere lo 0

ahaha tie' beccati questa semplificazione (e cosi' mi vendico) :grin:

hai ragione!

totlaLen =0 è corretto

Eccoci qua per l'aggiornamento :slight_smile:
Diamo prima i giusti meriti.
L'idea di tuxino era ottima!
A fronte di un aumento di soli 40byte nella dimensione dello sketch... si guadagnavano quasi 100byte di ram!
Difficile fare di meglio :slight_smile:
Eppure... lesto parrebbe avercela fatta :stuck_out_tongue:
Rimaneggiando un po' il suo codice, la FREERAM aumenta di ben 140(!!) e le dimensioni dello sketch diminuiscono di ben 2k!!

Il codice, che sto testando ora è questo:

/*----- Pachube -----*/
    // PACHUBE OTTIMIZZATO LESTO
    ContentLenghtPachubeVAR = 0;
    ContentLenghtFloat(mydata.toutES);
    ContentLenghtFloat(mydata.houtES);
    ContentLenghtFloat(mydata.tinES);
    ContentLenghtFloat(mydata.hinES);
    ContentLenghtFloat(mydata.T1ES);                
    ContentLenghtFloat(mydata.T2ES);
    ContentLenghtUnsLong(mydata.mediaHzES);

    Serial.print(F("ContentLenghtPachubeVAR: "));
    Serial.println(ContentLenghtPachubeVAR);

e poi queste due funzioni fuori dal loop:

/*--------- Calcolo ContentLenght Float ---------*/
void ContentLenghtFloat (float contenitoreFloat) {
  ContentLenghtPachubeVAR+=1;     // aggiungo il punto decimale
  int operatoreVoidPachube = contenitoreFloat * 10; // trasformo la float in un numero intero. 23.4 diventa 234 considerando un solo decimale
  if ( contenitoreFloat == 0 ){   // se la temperatura è 0
    ContentLenghtPachubeVAR+=2;   // bisogna dimensionare il content lenght per contenere 0.0
  }
  if (contenitoreFloat < 0) {     // se la temperatura è negativa
    ContentLenghtPachubeVAR+=1;   // aggiungo il segno meno al calcolo del ContentLenght
    while ( operatoreVoidPachube < 0 ) {  // fin tanto che è < di 0
      operatoreVoidPachube /= 10; // divido per dieci
      ContentLenghtPachubeVAR+=1; // ed incremento il content lenght ad ogni divisione
    }
  }
  else {                          // altrimenti la temperatura è positiva
    while ( operatoreVoidPachube > 0 ) {  // fin tanto che è > di 0
      operatoreVoidPachube /= 10;         // divido per dieci
      ContentLenghtPachubeVAR+=1; // ed incremento il content lenght ad ogni divisione
    }
  }
  Serial.println(contenitoreFloat,1);
  Serial.println (operatoreVoidPachube);
} 

/*--------- Calcolo ContentLenght UnsignedLong ---------*/
void ContentLenghtUnsLong (unsigned long contenitoreLong) {
  Serial.println(contenitoreLong);
  if ( contenitoreLong == 0 ){  
    ContentLenghtPachubeVAR+=1;   // se il valore è 0 bisogna dimensionare per contenere 0
  }
  while ( contenitoreLong > 0 ) {
    contenitoreLong /= 10;
    ContentLenghtPachubeVAR+=1;
  }
  Serial.println(contenitoreLong);

}

Le sto testando ora e sembra funzionino. Gestiscono i valori negativi ecc.
L'unico mio dubbio.
Se una float con un decimale è zero e faccio un print ottengo
0.0 ?
Oppure semplicemente 0?

Eppure... lesto parrebbe avercela fatta smiley-razz
Rimaneggiando un po' il suo codice, la FREERAM aumenta di ben 140(!!) e le dimensioni dello sketch diminuiscono di ben 2k!!

Se una float con un decimale è zero e faccio un print ottengo
0.0 ?
Oppure semplicemente 0?

sono abbastanza sicuro che sia 0

piccola otimizzazione:

if (contenitoreFloat < 0) {     // se la temperatura è negativa
    ContentLenghtPachubeVAR+=1;   // aggiungo il segno meno al calcolo del ContentLenght
    while ( operatoreVoidPachube < 0 ) {  // fin tanto che è < di 0
      operatoreVoidPachube /= 10; // divido per dieci
      ContentLenghtPachubeVAR+=1; // ed incremento il content lenght ad ogni divisione
    }
  }
  else {                          // altrimenti la temperatura è positiva
    while ( operatoreVoidPachube > 0 ) {  // fin tanto che è > di 0
      operatoreVoidPachube /= 10;         // divido per dieci
      ContentLenghtPachubeVAR+=1; // ed incremento il content lenght ad ogni divisione
    }
  }

diventa:

if (contenitoreFloat < 0) {     // se la temperatura è negativa
    ContentLenghtPachubeVAR+=1;   // aggiungo il segno meno al calcolo del ContentLenght
    contenitoreFloat*=-1; //lo trasformo in numero positivo
    }
    while ( operatoreVoidPachube > 0 ) {  // fin tanto che è < di 0
      operatoreVoidPachube /= 10; // divido per dieci
      ContentLenghtPachubeVAR+=1; // ed incremento il content lenght ad ogni divisione
    }

lesto:

Se una float con un decimale è zero e faccio un print ottengo
0.0 ?
Oppure semplicemente 0?

sono abbastanza sicuro che sia 0

E' che, ovviamente, non ho modo di riprodurre la condizione con i sensori...
c'è qualche posto dove poter aver la certezza di come viene rappresentata una float = 0 con un decimale ?
Tendenzialmente quando il decimale è zero, questo viene cmq stampato.
24.0 °C, 33.0% di umidità ecc

piccola otimizzazione:

contenitoreFloat*=-1; //lo trasformo in numero positivo

E questa roba cos'è?
Io stavo cercando ora come fare il modulo di un valore...

contenitoreFloat*=-1;

significa

contenitoreFloat=contenitoreFloat*(-1);

se moltiplichi un numero per -1... inverti il suo segno :slight_smile:

esiste il metodo abs() per avere il modulo, ma sicuramente occupa più byte di una soluzione "ad-hoc"

ps. preceduto da legacy

@legacy: la signorina ha iniziato da un paio di mesi, e partendo da 0, legge vari sensori, utilizza vari attuatori e butta tutto su internet, tra l'altro creandosi PCB home-made.. direi che un errorino se lo può permettere :slight_smile:

lesto:

contenitoreFloat=contenitoreFloat*(-1);

se moltiplichi un numero per -1... inverti il suo segno :slight_smile:

Chissa perché quando scrivo codice mi dimentico della matematica...
Faccio ancora fatica ad usare le due cose contemporaneamente!
BTW l'ottimizzazione fa risparmiare 14 byte di sketch... ma nn credo la userò :slight_smile:
Modificando il contenitoreFloat, quando vado a fare il serial print vedo solo valori positivi (ovviamente).
Mentre mantenendo il codice precedente ho un debug seriale dei valori corretti :slight_smile:
Ed i debug servono sempre!

Ora mi rimane da capire se la float 0 è 0.0 o 0 (LOL) ma in ogni caso...
La palma di cervellone d'oro va a lesto!