Problemi vari di invio di SMS

Ciao a tutti. Sto preparando un progetto con arduino per l'esame di maturità. In pratica è una stazione meteo (con sensore di temperatura, umidità e pressione) che comunica i dati via SMS (ho una GSM SHIELD ufficiale e utilizzo la libreria ufficiale), ma ho molti problemi. Ho un po' di esperienza con arduino, ma ho zero esperienza con la manipolazione di stringhe, array di caratteri e con la shield GSM. Iniziamo con ordine.

Non so come inserire nel messaggio da inviare le variabili di temperatura, pressione e umidità, vedendo gli esempi della libreria GSM vedo come, per inviare gli sms, quello che arduino legge dalla porta seriale lo salva in un array di caratteri (char txtMsg[200]), quando invece riceve gli sms salva ogni carattere in una variabile char (char c;) e la mostra sulla porta seriale ( while(c=sms.read())
Serial.print(c);
un carattere alla volta da quello che ho capito). Tenendo conto che il messaggio da inviare vorrei farlo pre-strutturato in questo modo:

T=[valoreTemperatura]°C
U=[valoreUmidità]%
P=[valorePressione]mbar

e tenendo conto che arduino invia, da quello che ho capito, un array di caratteri, io non so come inserire queste variabili nei rispettivi spazi dell'array. Mi sembra di aver letto da qualche parte che potrei usare sempre delle variabili char per salvare il valori di temperatura, umidità e pressione, però, mentre per l'umidità è una cosa fattibile (dato che varia tra 0 e 100), per la pressione no (varia da 950 a 1050 circa) e riguardo la temperatura vorrei far si che mi mostri il primo decimale (ad esempio 24,2 e non 24), qui penso sia obbligatoria la variabile float. Ma, come ho detto, non so come inserirle nell'array che sarà inviato.

Un altro problema (ma questo è un po' meno importante) è che non so come andare a capo nei messaggi. Come potete vedere dalla struttura del messaggio ho messo i tre valori su tre righe diverse, ma non so come far farlo ad arduino. Ho provato a mettere un "\n" nell'array e l'ho inviato al mio cellulare, ma il cellulare mi ha letto "Ön". Invece, inviando un messaggio dal cellulare ad arduino con dei ritorni a capo sulla porta seriale mi appare il messaggio ben impostato (con i ritorni a capo). Questo significa che arduino gli riesce a interpretare.

Mi spiace se ho detto delle stupidaggini nel messaggio (come ho detto su questa parte non so niente) e spero mi possiate aiutare, perché questo progetto lo devo far funzionare (soprattutto davanti la commissione!!!) e non posso più cambiarlo.
Grazie a tutti

prima di tutto campiamo cos'è un charset.
un PC può contenere solo numeri, tra l'altro in base 2,dunque come si può rppresentare un simbolo con i numeri? semplice, creando una tabella che dice: per il numero tot, il simbolo corrispondente è tat. Esistono molto charset, il più famoso è l'ascii (usato da arduino) e la famiglia unicode (in particolare UTF-8, che è un'estensione della ascii usata ormai nel 99% dei casi, e UTF-16, estensione della UTF8 con un byte in più, e quindi in grado di rappresentare tutti gli alfabeti del mondo, se non erro, ma profondamente incompatibile con le versioni precedenti)

su google trovi le tabelle, dove vedrai che anche I NUMERI sono dei caratteri.

se vuoi rappresentare il numero 100, ad esempio, puoi usare 2 modi:

  1. metodo "grezzo", potenze di 2: questo è quello che fai usando una variabile int, byte, e "più o meno" anche con float e double. La variabile può essere usata per fare operazioni matematiche, ma se provi ad osservarla come essere umano non capisci nulla, devi convertira in base 10 e forse ci capisci qualcosa.
  2. metodo "umano", con caratteri: se devi salvare 100, lo salvi come carattere "1", carattere "0", carattere "0", e il carattere di fine stringa "\0"(è un carattere speciale, vuol dire "elemento 0 della tabella ascii", ovver "NULL". Quindi ti serve un array di 4 caratteri, (in generale, lunghezza stringa + 1)

perchè si usa il metodo 1?
.perchè il computer pensa in potenze di due, e quindi egli può fare i conti solo in questo formato. Certo, di nascosto puoi continuare a trasformare da formato 2 a 1 e viceversa, ma perdi tempo macchina inutile.
.perchè occupa meno spazio, e lo psazio occupato è "fisso": una variabile "byte" usa un solo byte, eppure può contenere valori da 0 a 512, o da -256 a 255 se signed. per registrare -256 ti servono 5 caratteri: -, 2, 5, 6, \0
ATTENZIONE! dato che il formato 1 ha lunghezza fissa, hai dei vlori minimi e massimi che puoi far assumere alle variabili. Se "vai oltre" (overflow) sono cavoli tuoi.

Tu mi dirai: ma io quando scrivo via seriale, vedo i numeri nel modo 2: parzialmente vero;
Serial.print() trasfrorma le variabili NON char nella rappresentazione in char (quindi converte in automatico da modo 1 a modo 2), ma NON FUNZIONA CON I FLOAT.
se vuoi vedere il valore "grezzo", devi sare la Serial.write()

Esistono delle funzioni già fatte per passare da modo 1 a modo 2, ma la cosa più semplice è usare la classe String:

String stringOne = "A long integer: "; //creo l'oggetto String e gli do il valore A long integer: 
stringOne += 123456789; //aggiungo in coda la rappresentazione ascii del numero 123456789

stringOne.toCharArray(charBuf, 50); //metto l'array di char dell'oggetto String nell'array di char charBuf, limitandomi a 50 caratteri al massimo (per evitare di scrivere più dati di quanti l'array ne possa contenere)

non so come andare a capo nei messaggi

Abbiamo parlato di charset. Ma chi ci dice che gli SMS usino lo stesso charset di arduino (ascii?) nessuno! infatti se cerchi su google "charset sms" , come primo risultato hai: GSM 03.38 - Wikipedia come puoi notare la tabella base varia leggermente in base alla lingua a cui è impostato il cellulare ricevente. Se guardi il carattere GSM 'A' (0x41, dove 0x vuol dire numero esadecimale, che in decimale è 65), corrisponde alla ASCII 'A' ! fico! lo stesso vale ler le lettere minuscole e per i numeri. Ma andiamo a cercare il carattere "\n", ovver il Carriage Return (CR): tabella ASCII: 0x0D, che nella GSM è il CR. Quindi corrisponde. (non conoscevo il charset, quindi quello che leggi è il procedimendo come lo sto seguendo io)

Bene, allora il problema è da cercare da un'altra parte. Tu hai detto di aver inserito "\n", ma attenzione, tu avresti dovuto inserire il carattere speciale che \n rappresenta! Secondo me tu invece hai spedito le due lettere \ e n seprate: infatti la n l'hai ricevuta ok, la \ la hai ricevuta come Ö... quindi verifichiamo la tabella ascii al carattere \ e il valore 0x5C, che sulla GSM è il carattere.... (rullo di tamburi) Ö!! tutto torna

nota bene: se mettevi il codice, mi sarei risparmiato tutta questo rigiro mentale.
nota bene2: non ci sono risposte, ma devi fare veramente un passettino minuscolo per ottenerle, in entrambi i casi (mica credevi di ottenere la papa pronta, vero?). D'altronde è la TUA maturità :smiley:

Ciao,
sto navigando anche io in problemi molto simili a quelli di hispanico.
Io concateno tutto il testo e il messaggio (ho provato sia con una stringa che con un char) ma capita spesso che mandi un sms vuoto, e magari la volta successiva manda il messaggio corretto.
E' da impazzire perchè a volte sono convinto di aver risolto e poi alla fine torna a mandarmi messaggi vuoti..
questo è il pezzo incriminato (dove mcc è un valore che devo comunicare nel messaggio):
double mcc=0.0;
char message[100];
String dato=String(String((int)mcc)+"!");
String txta =String("Possibile problema! (dato: " + dato + ")");
txta.toCharArray(message,100);
sms.beginSMS(Numriceve);
sms.print(message);
sms.endSMS();

...ogni aiuto è ben accetto! :astonished:

char message[100];

è il problema. non hai abbastanza ram.riduci la dimensione il più possibile!
secondo me con 20 o 30 caratteri hai risolto

Già, risolto! XD
Come si fa quindi a sapere l'occupazione ram durante l'esecuzione del programma? almeno evito di incappare ancora nello stesso problema!
grazie per la risposta!

tanto per cominciare sappi che hai 2Kbyte di ram.. ben pochi. poi considera che un array di 100 caratteri vuol dire 100 byte CONSECUTIVI... se quell'array lo crei all'inizio, magari globale quando la ram è tutta vuota ok, ma se lo crei a runtime è facile che per colpa della frammentazione delle variabili in ram non hai 100 byte di fila liberi...

se cerchi nel playgound c'è una piccola libreria fatta da un tizio per misurare EMPIRICAMENTE l'uso della ram. Funziona, ma non ti puoi fidare (per esempio, non tiene conto della frammentazione, ti dice che hai 600byte liberi ma in realtà ne hai al massimo 10byte di fila, per dire)

Grazie per le risposte! In ogni caso ho risolto in maniera molto semplice usando una interessantissima (e utilissima) libreria: PString. PString serve a scrivere in char array di qualsiasi dimensione, e funziona in maniera molto simile a scrivere sulla porta seriale. lo consiglio a tutti quelli che devono creare dei char array che contengono variabili o che comunque possono variare.

uhmm... guarda che anche la normale String "ufficiale" permette di farlo...

Ciao, torno nuovamente a richiedere l'aiuto della comunity!
sto cercando di impostare tramite pulsanti il numero di telefono al quale poi la scheda deve mandare i messaggi (piuttosto che avere il valore prestabilito tramite sketch)

non riesco però a capire bene il passaggio da un array di numeri ad un array di numeri ma di tipo char.
ho provato diverse soluzioni, di cui questa che immaginavo fosse la meno elegante ma la più banale, ma niente. in pratica mi memorizza un valore che non centra nulla con quello che ho impostato!

char NUM[11]={0}; //questo il numero a cui inviare sms scelto da utente
char Numriceve[11]="328XXXXXXX"; //questo il numero a cui inviare sms preimpostato da sketch

do{
delay(80);
nb=readButtons(5); // è una funzione per leggere i pulsanti
delay(80);
if(nb==1){NUM[p]=n; p=p+1;a++;n=0;} //ok (a e p partono da 0.)
if(nb==2){p=p-1; a=a-1;if(p==-1){p=12;} n=0;} //back
if(nb==3){n=n+1;if(n==10){n=0;}} //+1
if(nb==4){n=n-1;if(n==-1){n=9;}} //-1
lcd.setCursor(a-1,1);
lcd.print(n);
lcd.setCursor(a-1,1);
lcd.blink();
}while(p<10);
lcd.noBlink();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("OK hai inserito:");
lcd.setCursor(0,1);
for(int i=0; i<10;i++)
{lcd.print(NUM*);}*
delay(5000); //fin qui tutto bene!
//for(int i=0; i<10;i++) // provato anche questo ma senza esito!
//{Numriceve_=NUM*;}*
String myString = String(NUM[0]+NUM[1]+NUM[2]+NUM[3]+NUM[4]+NUM[5]+NUM[6]+NUM[7]+NUM[8]+NUM[9]+"\0");
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Ho memorizzato:");
lcd.setCursor(0,1);
lcd.print(myString); // e qui esce di tutto!!:slight_smile:
delay(5000);
poi dovrei associare all'array Numriceve[] la stringa myString oppure i vari valori dell'array NUM ma in modo che poi sia quello il nuovo numero a cui mandare l'sms...
diciamo che ho un po di confusione lo ammetto...!_

fedecatalogors:
...
non riesco però a capire bene il passaggio da un array di numeri ad un array di numeri ma di tipo char.
...

:astonished: :astonished: :astonished:

Un tipo char è un'area di memoria di un byte ... STOP ! Un array di char E' un array di NUMERI !

All'interno tu puoi metterci un numero 1, 2, 3, ... che in esadecimale possiamo descrivere come 0x01, 0x02, 0x03 ... oppure puoi metterci dei caratteri secondo la codifica ASCII dove il carattere 1 è identificato dal numero 0x31, il carattere 2 è rappresentato dal numero 0x32, il carattere 3 è rappresentato dal numero 0x33 e così via ... ma una variabile di tipo char ... sempre un NUMERO al suo interno ha, numero da 0 a 255.

Ora, detto questo, ricorda che quando scrivi '1' (singolo apice) intendi 0x31, quando scrivi "1" (doppio apice) intendi DUE char e, per l'esattezza 0x31 0x00 dove, il primo rappresenta in ASCII il numero 1 ed il secondo termina la stringa (visto che hai usato il doppio apice).

Ampliando il concetto, quando scrivi "123" intendi 0x31 0x32 0x33 0x00 ... quindi ... se usi i doppi apici, ricordati sempre di prevedere un carattere in più per la terminazione di stringa.

Fatto tutto questo cappello ... cerca un po' di vedere perché ti si incasinano le cose ... :wink:

Per finire posso dirti che scrivere :

if(nb==2){p=p-1; a=a-1;if(p==-1){p=12;} n=0;}

è un'emerita porcheria e crea facilmente degli errori ... MI RIFIUTO di leggere codice così scritto !

Guglielmo

Edit : Corretto errore segnalato da Lesto ...

gpb01:
Ora, detto questo, ricorda che quando scrivi '1' (singolo apice) intendi 0x01, quando scrivi "1" (doppio apice) intendi DUE char e, per l'esattezza 0x31 0x00 dove, il primo rappresenta in ASCII il numero 1 ed il secondo termina la stringa (visto che hai usato il doppio apice).

mi permetto di dissentire, 1 è 0x01, '1' è 0x31 (carattere 1), e "1" è un puntatore ad un array di char il cui primo elemento è il carattere 1 (quindi '1') e il secondo il carattere di fine stringa ('\0', ovvero 0x00)

BTW l'errore è quì:

String myString = String(NUM[0]+NUM[1]+NUM[2]+NUM[3]+NUM[4]+NUM[5]+NUM[6]+NUM[7]+NUM[8]+NUM[9]+"\0");

come detto anche i char soono numeri in realtà, e il compilatore non è abbastanza intelligente per capire che il + serve per CONCATENARE (puoi farlo tra String, ma sono oggetti e staimo cadendo nel C++ che per ora non ci serve) e quindi lui SOMMA i valori.

Senza passare da String, potevi fare l'array NUM di un carattere più grande:

char NUM[12]={0};

così da poter mettere lo '\n' nell'array di char rendendolo una string (notare la lettera MINUSCOLA, in C una string è un array di char con uno \n finale), dopodichè lo puoi stampare esattamente come hai fatto per la String;

NUM[11]='\0';
Serial.println(NUM);

è un'emerita porcheria e crea facilmente degli errori ... MI RIFIUTO di leggere codice così scritto !

non sono così cattivo ma approvo :grin:

esistono delle convenzioni:
dopo il { e } si va sempre a capo, e si aggiunge una identazione, usa le {} anche se non necessario (con leparentesi: meglio abbindare che deficere, sempre!)
il nome delle variabili è sempre in minuscolo, al posto degli spazi _
il nome delle COSTANTI va in maiuscolo, al posto degli spazi _

il nome delle CLASSI va scritto con la prima lettera maiuscola, poi CamelCase (maiuscola al posto degli spazi) (es String)
il nome degli OGGETTI va scritto in camel case con la prima lettera minuscola
(es: String nomeUtente)

lesto:
...
mi permetto di dissentire, 1 è 0x01, '1' è 0x31 (carattere 1), e "1" è un puntatore ad un array di char il cui primo elemento è il carattere 1 (quindi '1') e il secondo il carattere di fine stringa ('\0', ovvero 0x00)
...

Hai perfettamente ragione, grazie ! Ho corretto l'errore di battitura sul singolo apice, ma ho lasciato la parte riguardante il doppio apice perché lo scopo era far capire la differenza (... ovvero che in memoria non hai più un solo char, ma N+1 char, causa l'aggiunta del terminatore) e ribadire il concetto che il tipo char è un normal tipo numerico di un byte, per cui ... conta quello che ci metti dentro e come lo interpreti :wink: (... mi preoccupava difatti questa richiesta : "il passaggio da un array di numeri ad un array di numeri ma di tipo char").
In ogni caso è correttissima l'indicazione che quell'insieme di caratteri sia un array e che il compilatore lo tratti tramite il suo indirizzo ... ma non volevo mettergli "troppa carne al fuoco" :wink:

Guglielmo

P.S. : Non si tratta di essere cattivo ... è che è proprio un modo ignobile di scrivere il codice, che può facilmente dare adito a svariati errori e nel quale mi rifiuto di mettere le mani ... :grin:

A proposito di "puntatori", piccolo [Off-Topic] che magari interessa ...

... è da poco uscito un bel libro dedicato all'argomento : "Understanding and Using C Pointers", edito da O'Reilly, ISBN : 978-1-449-34418-4, interamente dedicato ai puntatori del C ed alla relativa gestione della memoria !

Ne consiglio la lettura ...

Fine dell [Off-Topic] XD

Guglielmo

OK scusate, avete ragione.. ma sono abbastanza nuovo sull'argomento ed i difetti si correggono via via insieme all'apprendimento!.
il fatto di scrivere quel pezzo di codice su una sola riga mi pareva comodo per caricare in poche righe 4 comandi simili e ripetitivi. se è un problema di sola indentazione ok. se invece c'è un modo piu elegante di scrivere quel tipo di comando (a livello di esecuzione intendo, non di sola grafica) è altra cosa.
immaginavo che quindi lo string di NUM fosse quella la vera schifezza...!
intanto cerco di capire le vostre risposte...
grz

fedecatalogors:
...
mi viene il dubbio che io assegno anche NUM[p]=n; con n int e che possa perdersi nelle assegnazioni. considera che NUM[p] in pratica deve contenere il numero di telefono impostato tramite tastierino (quindi un int in origine) al quale mandare l'sms tramite funzione sms.beginSms(NUM) che richiede però NUM di tipo char. dovrei convertire i valori di n nei corrispettivi char (ovvero numeri ma in formato char) oppure il problema è a livello di visualizzazione succesiva sul comando lcd.print? grz

Se hai dei numeri (solo singole cifre da 0 a 9) e li devi trasformare, uno ad uno, (quindi non un numero di più cifre) nel loro equivalente ASCII (ovvero il carattere stampabile) hai varie strade ...
... la più semplice è :

char numASCII = (char)(((int)'0')+num);

... che in pratica non fa altro che aggiungere 0x30 al tuo valore numerico. Dato che i numeri da 0 a 9 in ASCII vanno da 0x30 a 0x39 ... il giochino funziona.

Guglielmo

Edit: Se invece hai un numero di più cifre (es un intero) e lo vuoi trasformare in una stringa di N caratteri, allora devi usare le funzioni di conversione, es. itoa, ltoa, ecc.

char NUM[12]; e poi NUM[12]='\0';

stai scrivendo fuiri dall'array! al massimo puoi usare fino alla cella 11!

Normalmente il numero di telefono è una stringa contenente una sequenza di caratteri ASCII lunga al massimo 16 caratteri.

Il numero telefonico (a parte il prefisso) non può essere più lungo di 10 caratteri (11 per zone ad alta concentrazioni di telefoni).
Quindi 11 caratteri più 5 di prefisso sono appunto 16 caratteri.

La stringa deve perciò essere un vettore char di 16 + 1 elementi.

Ad esempio "+393401234567890" conterrà in esadecimale i seguenti elementi:
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7

  • 3 9 3 4 0 1 2 3 4 5 6 7 8 9 0
    2B 33 39 33 34 30 31 32 33 34 35 36 37 38 39 30 00

Non puoi inserire, dunque, la cifra come numero ("1" ad esempio vale 1 come numero, ma vale 31 esa = 49 dec come carattere)