String troppo "lunga"

nid69ita: Si, mi riferisco a quello. Parliamo di Arduino Uno o Mega? La Uno ha poca SRAM, solo 2K

Si sto utilizzando arduino uno

uwefed: ma quanto lunga é lunga?

E' lunga lunga un po' :) ... invia ip di arduino + più un codice di controllo + un id arduino + i 6 valori degli ingressi analogici + 8 valori di i/o digitali. Per intenderci è di questo tipo:

CtrlCod=#####&ArduId=##&ArduIP=###.###.###.###&a0=###&a1=###&a2=###...d7=#&d8=#&d9=#

Dove # è un numero intero. Idee da suggerirmi?

Un oggetto String è limitato dalla RAM libera. Ma tramite quale canale spedisci la String? Forse il limite è nel buffer del canale che usi e/o nella velocità di invio che può essere inferiore a quella con cui mandi in trasmissione i dati.

leo72:
Ma tramite quale canale spedisci la String?

Tramite EthernetClient

leo72:
Forse il limite è nel buffer del canale che usi e/o nella velocità di invio che può essere inferiore a quella con cui mandi in trasmissione i dati.

Non dipende della velocità del canale dato che con GET funziona e che i dati vengono inviati ogni 5 minuti utilizzando un while (millis() < 300000) e un delay di un paio di secondi dopo l’invio del POST. Mi sbaglio?

Non so come lavora la libreria Ethernet, non so dirti molto di più... mi spiace.

Mi spiace di che?!

leo72: Un oggetto String è limitato dalla RAM libera.

Da quello che dici il limite di String è limitato solamente dalla RAM libera. Ho fatto delle prove aggiungendo a monte della stringa altri parametri e valori e difatti sulla webapp arrivano solo i primi valori mentre gli ultimi si vanno a perdere. Sono convinto che è un problema di RAM perchè i valori di POST scritti arrivano fino a un certo punto mentre le variabili cdei sensori che Arduino deve elaborare si perdono per strada. Questo è il parsing dei valori che arrivano sulla webapp e come si può notare dopo a2 le variabili si perdono per strada e dopo a4 non viene scritto altro.

CtrlCod=#####
ArduId=###
ArduIP=###.###.###.###
a0=###
a1=###
a2=
a3=
a4=

Mentre se non invio l'ip di arduino i valori arrivano oltre ma dopo d6 si perdono per strada.

CtrlCod=#####
ArduId=###
a0=###
a1=###
a2=###
a3=###
a4=###
a5=###
d2=#
d3=#
d5=#
d6=
d7=
d8=
d9=

# numeri interi C'è la maniera di calcolare la dimensione di byte di una stringa? Devo dare una spolverata a "Calcolatori Elettronici"? :)

L'oggetto String è costruito dinamicamente a runtime ed occupa tante celle quanti sono i caratteri che deve contenere. Il problema è che se crei un oggetto String, poi altre variabili, poi lo distruggi, se vai a crearne un altro e questo non entra nello spazio lasciato libero dal primo, esso verrà messo dopo tutto il resto. E spesso la RAM si satura.

Una soluzione per evitare questo è di dichiarare un oggetto String assegnandogli già una stringa predefinita con una dimensione più grande del massimo che andrà poi a contenere. E dopo svuotarla quando serve.

Esempio:

String miaStringa = "kjdsgfwurgvuiwrghcukwerhctnerihchcgkwejhcgwkehgvkwerhcg";
...
...
miaStringa = "";

oppure

miaStringa ="mia stringa";

In questo modo non frammenti la memoria.

Ci faresti vedere come prepari quella stringa prima di inviarla?

La ethernet è come la seriale, il send invia un char alla volta in modo seriale, il problema è che prepari una stringa troppo lunga ma dipende anche da quanto spazio hai in memoria in quel momento, magari ottimizzando lo sketch nelle varie parti recuperi spazio.

Nessuno ti impedisce di spezzare la stringona in 2 parti o 3-4... tipo:

aggiungo un indice identificativo es CtrlCod+="A&"; Preparo la stringa aggiungendo gli altri dati invio CtrlCod+=#####&ArduId=##&ArduIP=###.###.###.### svuoto la stringa CtrlCod

aggiungo un indice identificativo es CtrlCod+="B&"; Preparo la stringa aggiungendo gli altri dati invio CtrlCod+=a0=###&a1=###&a2=###...d7=#&d8=#&d9=# svuoto la stringa CtrlCod

A& e B& identificano il tipo di dati che stai mandando così il tuo programma che riceve può fare un salto ad un altra funzione il programma che riceve dovrà essere un po' modificato, ma non è difficile, però dirti dove è il problema senza vedere niente resta sempre un incognita.

ciao

pablos:
Ci faresti vedere come prepari quella stringa prima di inviarla?

La stringa la preparo così all’interno di una funzione

  // preparo l'ip di arduino perchè se uso direttamente Ethernet.localIP() l'ip inviato via post arriva a destinazione in formato numerico intero
  uint32_t ipAddress = Ethernet.localIP();
  uint8_t *ipAddr = (uint8_t*) &ipAddress;
  char ArduIP[16];
  sprintf(ArduIP, "%d.%d.%d.%d\0", ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]);

  PostDataSend = "";
  PostDataSend += "CtrlCod=";
  PostDataSend += (postCod);
  PostDataSend += "&";
  PostDataSend += "ArduID=";
  PostDataSend += (ArduID);
  PostDataSend += "&";
  PostDataSend += "ArduIP=";
  PostDataSend += (ArduIP);
  PostDataSend += "&";
  PostDataSend += "a0=";
  PostDataSend += (a0);
  PostDataSend += "&";
  PostDataSend += "a1=";
  PostDataSend += (a1);
  PostDataSend += "&";
  PostDataSend += "a2=";
  PostDataSend += (a2);
  PostDataSend += "&";
  PostDataSend += "a3=";
  PostDataSend += (a3);
  PostDataSend += "&";
  PostDataSend += "a4=";
  PostDataSend += (a4);
  PostDataSend += "&";
  PostDataSend += "a5=";
  PostDataSend += (a5);
  PostDataSend += "&";
  PostDataSend += "d2=";
  PostDataSend += (d2);
  PostDataSend += "&";
  PostDataSend += "d3=";
  PostDataSend += (d3);
  PostDataSend += "&";
  PostDataSend += "d4=";
  PostDataSend += (d4);
  PostDataSend += "&";
  PostDataSend += "d5=";
  PostDataSend += (d5);
  PostDataSend += "&";
  PostDataSend += "d6=";
  PostDataSend += (d6);
  PostDataSend += "&";
  PostDataSend += "d7=";
  PostDataSend += (d7);
  PostDataSend += "&";
  PostDataSend += "d8=";
  PostDataSend += (d8);
  PostDataSend += "&";
  PostDataSend += "d9=";
  PostDataSend += (d9);
  PostDataSend += "";

leo72:
Una soluzione per evitare questo è di dichiarare un oggetto String assegnandogli già una stringa predefinita con una dimensione più grande del massimo che andrà poi a contenere. E dopo svuotarla quando serve.

Ho provato anche come suggeriva leo72 di dichiarare una stringa “più lunga” di quella utilizzata nell’inizializzazione dello sketch e svuotandola alla fine di ogni loop ma Arduino fa un paio di loop e poi si blocca. Il log di caricamento mi dice che sto utilizzando 25.048 bytes (su un massimo di 32.256 bytes).

Praticamente @Pablos ti suggerisce di spedire tutto quel casino di roba un pezzo alla volta. Che sò, dopo le prime 10 PostDataSend += spedisci e fai PostDataSend = ""; In questo modo non hai una String enorme ma la spedisci a pezzi. Naturalmente meglio ancora sarebbe usare array di char (come fai per ArduIP usando la sprintf, meglio la snprintf) perchè con la PostDataSend = ""; l'oggetto String rischia di esaurire la memoria limitata del Arduino Uno (vedi quel che dice prima @Leo72 qui)

P.S. ma a che ti servono le due tonde in tutte queste istruzioni? PostDataSend += (ArduID);

… per questo ti è stato suggerito di usare gli array di char e non la classe String !

Arduino è una piccola MCU che ha solo 2 KBytes di SRAM e … l’uso della classe String è praticamente un suicidio, specie come la stai usando tu, ovvero assegnando e riassegnando pezzi alla stessa stringa.

Come dicevo sei su una piccola MCU, non su un PC dove c’è un “garbage collector” … le String sono allocate dinamicamente con delle malloc() e realloc() … peccato che ogni volta che si cambia la loro lunghezza, il sistema deve cercare uno spazio dove riallocare, distruggere la precedente e crearne una nuova … la cui cosa spesso porta alla frammentazione della memoria e all’andare “fuori” con lo spazio … con conseguente perdita di controllo da parte del programma …

Se vuoi uscirne … impara ad usare gli array di char … :roll_eyes:

Guglielmo

Edit : gli array di char li manipoli con tutte le funzioni che trovi in string.h che fa parte di AVR libc e che è automaticamente inclusa dal compilatore :wink:

fvlgnn: Il log di caricamento mi dice che sto utilizzando 25.048 bytes (su un massimo di 32.256 bytes).

Pensiero errato :grin: Questo è la dimensione del programma (ovvero del solo codice) che viene caricato nella memoria Flash (max 32Kb). La memoria per le variabili è la SRAM ed è di soli 2Kb su Arduino Uno Nel programma puoi usare la freeRam() per vedere quanta ne è rimasta libera, magari al fondo della loop:

Serial.print("sram="); Serial.println(freeRam);
int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

nid69ita: P.S. ma a che ti servono le due tonde in tutte queste istruzioni? PostDataSend += (ArduID);

Ho sempre pensato che le tonde servissero al compilatore per dichiarare il valore di una variabile, in molti esempi vengono utilizzate.

nid69ita: Naturalmente meglio ancora sarebbe usare array di char (come fai per ArduIP usando la sprintf, meglio la snprintf)

gpb01: Se vuoi uscirne ... impara ad usare gli array di char ... :roll_eyes: Edit : gli array di char li manipoli con tutte le funzioni che trovi in string.h che fa parte di AVR libc e che è automaticamente inclusa dal compilatore smiley-wink

Ci provo, nonostante la mia conclamata antipatia per gli Array :roll_eyes:

nid69ita: La memoria per le variabili è la SRAM ed è di soli 2Kb su Arduino Uno Nel programma puoi usare la freeRam() per vedere quanta ne è rimasta libera, magari al fondo della loop.

Grazie della precisazione!

Spererei di riuscire a risolvere in maniera tale da lasciare un pezzo di codice funzionante ai posteri.

XD Gianni

Un esempio, OCCHIO suppongo che tutte le variabili siano intere, ma non credo, di sicuro ArduIP è testo:

char buf[50];
// primo pezzo
snprintf(buf, 50, "CtrlCod=%d&ArduID=%d&ArduIP=%s", postCod, ArduID, ArduIP);
client.print(buf);           // spedisco primo pezzo
// secondo pezzo che riusa buf
snprintf(buf, 50, "&a0=%d&a1=%d&a2=%d", a0, a1, a2);
client.print(buf);     // spedisco secondo pezzo
// terzo pezzo
snprintf(buf, 50, "&a3=%d&a4=%d&a5=%d", a3, a4, a5);
client.print(buf);     // spedisco terzo pezzo

fvlgnn: Ci provo, nonostante la mia conclamata antipatia per gli Array ....

Male, sono fondamentali e quindi ... prima impari a padroneggiarli e meglio è !!!

Comunque guarda che basta ragionarci ... sono banali ... immagina uno schedario (l'array) con tante schede (gli elementi) ... e il gioco è fatto ;)

Guglielmo

gpb01: Comunque guarda che basta ragionarci ... sono banali ... immagina uno schedario (l'array) con tante schede (gli elementi) ... e il gioco è fatto ;)

Faccio prima a vederli come lo schema di battaglia navale ... dove perdo sempre perchè non becco mai quello che mi serve XD

Oppure molto più semplicemente SENZA ARRAY (dovrebbe funzionare):

client.print("CtrlCod="); client.print(CtrlCod); 
client.print("&ArduID="); client.print(ArduID);
client.print("&ArduIP="); client.print(ArduIP);
client.print("&a0=");     client.print(a0);
...
client.print("&d9=");     client.println(d9);

Sull’ultima uso println
Con questo ultimo pezzo puoi ulteriormente liberare Sram usando F()

client.print( F("&ArduIP=") );

nid69ita: Oppure molto più semplicemente SENZA ARRAY (dovrebbe funzionare):

client.print("CtrlCod="); client.print(CtrlCod); 
client.print("&ArduID="); client.print(ArduID);
client.print("&ArduIP="); client.print(ArduIP);
client.print("&a0=");     client.print(a0);
...
client.print("&d9=");     client.println(d9);

Sull'ultima uso println Con questo ultimo pezzo puoi ulteriormente liberare Sram usando F()

client.print( F("&ArduIP=") );

Questa è la maniera con cui costruisco l'invio via GET e funziona, purtroppo non lo posso utilizzare con POST, dato che questo, per essere accettato lato server ha bisogno di portare con se l'informazione Content-Length che serve all'interprete per sapere la lunghezza precisa del POST da elaborare.

Non me ne intendo ma mica avrai il problema che ad un certo punto devi fare questo:

 client.println("Content-Lenght: "+PostDataSend.length());

E quindi usavi la String per avere il giusto numero di caratteri? Se è così, ti picchio :grin:

Leggi la reference della print() nella sezione return http://arduino.cc/en/Reference/ClientPrint.

int lun=0;
lun+=n.print("CtrlCod="); lun+=n.print(CtrlCod); 
lun+=n.print("&ArduID="); lun+=n.print(ArduID);
lun+=n.print("&ArduIP="); lun+=n.print(ArduIP);
lun+=n.print("&a0=");     lun+=n.print(a0);
...
lun+=n.print("&d9=");     lun+=n.println(d9);

alla fine lun contiene la lunghezza

@ fvlgnn ... prova a non metterlo proprio il Content-Length ... c'è un'alta possibilità che funzioni ugualmente (... c'ho scritto, in un'altro ambiente, una libreria per DropBox e ... dato che quel parametro dell'HTTP POST mi incasinava sempre la vita ... l'ho eliminato e viaggia che è una meraviglia :grin: :grin: :grin:)

Guglielmo

Edit : Con HTTP 1.1 non sono neanche sicuro che sia obbligatorio ... RFC2616 Sez. 4.4

gpb01: @ fvlgnn ... prova a non metterlo proprio il Content-Length ... c'è un'alta possibilità che funzioni ugualmente (... c'ho scritto, in un'altro ambiente, una libreria per DropBox e ... dato che quel parametro dell'HTTP POST mi incasinava sempre la vita ... l'ho eliminato e viaggia che è una meraviglia :grin: :grin: :grin:) Guglielmo

Ehhh, fatta la legge... trovato l'inganno :grin: