Leggere risposte da modulo gsm

Salve a tutti, vorrei riuscire a trovare il modo migliore per leggere le risposte che mi ritornano da un modulo gsm collegato in seriale, non so esattamente come procedere, so che ci sono varie librerie specifiche ma vorrei evitare, per poter gestire il tutto autonomamente.
Quello che vorrei fare e catturare le risposte che mi arrivano dalla scheda, per ora sono riuscito a popolare un array, con questo codice.

#include <SoftwareSerial.h>   
SoftwareSerial mySerial(7, 8); // RX, TX
char inSerial[50];
int i=0;
 
void setup() {
  
  Serial.begin(9600);
 
  while (!Serial) {
   
  }

  mySerial.begin(9600);
  
}

void loop() { 
  

  
  if (mySerial.available()){
    
      PopolaArry();
     
    }
  
  if (Serial.available()) {
    
    mySerial.write(Serial.read()); //Invio comandi a modem
  
}
 } 

void PopolaArry(){
  i=0;
  while (mySerial.available() > 0) {
      inSerial[i]=(mySerial.read());
      delay(10);
     // Debug
      Serial.write(inSerial[i]);
      Serial.print(" = Pos = ");
      Serial.println(i);
      i++;
      
   } 
   // Debug
   Serial.print("Popolato");
}

Ora quello che vi chiedo e se secondo voi usare gli arrey sia giusto o se voi usereste altri metodi, e finche sto scrivendo vorrei sapere come faccio a sapere quanta parte del mio array e occupata?

Grazie

Il metodo è quello giusto, in pratica altro non stai facendo che costruire una classica stringa del 'C', ovvero un char array, però:

  1. devi verificare di non superare i limiti dell'array
  2. devi verificare che arrivi il terminatore del messaggio che, normalmente, per questi oggetti è \r\n
  3. ricevuto il terminatore, devi eliminare i due caratteri ed inserire al loro posto 0x00 che, per definizione, in 'C' indica la fine della stringa di caratteri.

In questo modo sarai completamente compatibile con tutte le funzioni che ti mette a disposizione la AVRlibc ed in particolare il modulo <string.h>

Guglielmo

Considerando che devo poter gestire anche i messaggi ricevuti, e la memoria del arduino non esagera che dimensione di array mi consigli, non vorrei ne essere troppo piccolo ne troppo grande...

Dato che le dimensioni dell'array sono fisse, devi scegliere la dimensione massima del messaggio che immagini di poter ricevere ...
... se ben ricordo il singolo SMS non può superare i 160 char e SMS più lunghi vengono comunque inviati con più SMS (fino a 4) ... poi si passa agli MMS, ma li il discorso è diverso.

Insomma, tra header, messaggio e chisura ... 200 bytes dovrebbero bastare, però verifica.

Questo comunque NON ESCLUDE che in fase di ricezione si DEVE sempre controllare di NON superare la dimensione massima, eltrimenti si deve dare un messaggio di errore.

Guglielmo

Grazie delle dritte, faro dei controlli sulla lunghezza massima.

Vi faro sapere

Adesso pero iniziano i problemi, e non riesco a capire se sbaglio qualcosa oppure no..
In sostanza mi sono accorto che se popolo l'array i messaggi che mi arrivano non sono completi, mentre se uso semplicemente un lettore scrittore seriale i messaggi arrivano completi, per essere più chiaro faccio un esempio pratico....
usando questo codice ..

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  mySerial.begin(9600);
  
}

void loop() { // run over and over
  if (mySerial.available()) {
    
    Serial.write(mySerial.read());
   
  }
  if (Serial.available()) {
    
    mySerial.write(Serial.read());
  }
}

e chiedendo la lista degli sms ricevuti la risposta che ottengo e...

AT+CMGL="ALL"

+CMGL: 1,"REC READ","+393096","EM","16/11/17,08:56:09+04"
TESTO DEL MESSAGGIO
SMS PROVA

OK

Se invece uso il codice postato inizialmente ne ottengo solo una parte.
Questo problema da cosa e dovuto, dalla dimensione del buffer seriale ??, o devo elaborare i dati prima di inserirli nel array?
o provato a stampare a video il valore char e la posizione nel array, ma non va mai oltre i 70

qualcuno sa spiegarmi il motivo, penso sia banale ma non arrivo a capirlo, credo di sbagliare nel popolare l'array ma in cosa sbaglio non lo capisco.

... capitava anche a me ... fai una prova, apri il file /arduino/avr/libraries/SoftwareSerial/src/SoftwareSerial.h e modifica così la 43 riga:

#define _SS_MAX_RX_BUFF 128 // RX buffer size

Riprova e fammi sapere.

Guglielmo

Intanto grazie del aiuto...

Ho fatto la prova, ma il risultato non cambia, quindi ho iniziato a fare altre prove per capire meglio, e mi sono accorto di una "anomalia" che non capisco, in sostanza se scrivo il codice in questo modo :

 #include <SoftwareSerial.h>   
SoftwareSerial mySerial(7, 8); // RX, TX
String inSerial[128];

char c;
int i=0;
 
void setup() {
 
  Serial.begin(9600);
 
  while (!Serial) {
   
  }

  mySerial.begin(9600);
 
}

void loop() {
 
  if (mySerial.available()){
   
      PopolaArry();
      
    }
 
  if (Serial.available()) {
   
    mySerial.write(Serial.read()); //Invio comandi a modem
 
}
 }

void PopolaArry(){
  
  while (mySerial.available() > 0) {
      
       Serial.write(mySerial.read());
       
      delay(1); // SERVE PER FARE IL CICLO CORRETTO OGNI SINGOLO CARATTERE
     
    // char c = mySerial.read();
      i++;
      
     
   }
   // Debug

   Serial.print(" (*) "); // Separatore 
   Serial.print(i);
  
  
}

a monitor ottengo e:
ATI

SIM900 R11.0

OK
27

Dove 27 e il numero di caratteri stampati a video compresi terminato ecc ed è corretto.

Se tolgo il delay(1); o inserisco char c = mySerial.read(); per poter poi scrivere il carattere di c nell'array si incasina tutto ed esce dal ciclo while (mySerial.available() > 0) { di PopolaArry() scrivendo il valore di i quando vuole lui.

Spero di essere stato chiaro, visto che io non riesco a capire come catturare il carattere proveniente dalla seriale per inserirlo nell'array

Quando fai la read() il carattere viene tolto dal buffer, quindi se metti la seconda devi togliere la prima. Stampa direttamente c, se proprio lo vuoi vedere.

Scusa ma non ho capito cosa intendi se metti la seconda devi togliere la prima ??

Se fai il ciclo in quel modo, la delay(1) è praticamente obbligatoria ...
... la MCU fa molto prima ad eseguire il ciclo while() che NON il carattere ad essere disponibile e quindi ... quella while (mySerial.available() > 0) esce.

E' un vecchio problema noto ...

Guglielmo

Se vuoi evitarlo puoi fare in questo modo ...

idxBuffer = 0;
do {
      while (!Serial.available()) {}
      inpBuffer[idxBuffer] = Serial.read();        
   } while ( (inpBuffer[idxBuffer++] != TERCHAR) && (idxBuffer < MAXCHAR + 1) );

... dove TERCHAR è il carattere terminatore che ti aspetti (... io lo metto in una #define), MAXCHAR è la dimensione massima dell'array meno uno, quindi, l'array lo devi dimensionare MAXCHAR+1, inpBuffer è l'array, idxBuffer è l'indice.

Usando questo sistema, in realtà, per essere certi di non rimanere bloccati nel "do/while", sarebbe bene implementare anche un meccanismo di timeout ... ma lascio a te il compito di farlo.

Guglielmo

Grazie a entrambi per l'aiuto, non e facile ma a forza di sbatterci la testa contro otterrò quello che mi seve, vi tengo informati.

Guglelmo il terminatore usato dal modulo gsm e #CR#LF ovvero \r\n , ma come li si puo combinare in un unico define come terminatore ?

ho fatto varie prove ma per stampare a video il comando corretto devo fare due due Serial.write distinti.

Non puoi combinarli ... decidi quale è per te la fine del messaggio, quello che segue poi lo scarti quando pulisci il buffer prima di una nuova ricezione. Personalmente uso il CR come terminatore :wink:

Per inviarli ovvio che devi inviare due carrateri \r e \n

Guglielmo

Grazie l'avevo pensato anche io ma visto il modo in cui riceo i dati dal gsm pensavo di non poterlo fare, non so perche ma mi complico sempre la vita da solo.

grazie

Guglielmo mi trovo veramente in crisi! ho provato per ore ad ottenere qualcosa di decente dall codice che mi hai postato ma faccio fatica a capirlo e non mi entra in testa, un minimo risultato l'ho ottenuto in questo modo:

 if (mySerial.available()){
    
     //Serial.write(mySerial.read()); //Invio comandi a modem
      int  idxBuffer = 0;
    do {
      while (!Serial.available()) {}
      inpBuffer[idxBuffer] = mySerial.read();        
   } while ( (inpBuffer[idxBuffer++] != TERCHAR) && (idxBuffer < MAXCHAR + 1) ); 
     Serial.print("A ");
     Serial.print(idxBuffer);
      for (int i=0; i <= idxBuffer; i++){
      Serial.println(inpBuffer[i], DEC);
      delay(10);
   }

E sbagliato usalro in questo modo ma non ci arrivo, allora di base il codice va in loop qui while (!Serial.available()) {} e ci rimane.
Se avvio il codice come l'ho postato mi compare la scritta di risposta con il buffer che arriva a 200 con una marea di -1 quindi la seriale e vuota, almeno mi sembra di averlo letto da qualche parte nel forum
il carattere terminatore l'ho scritto #define TERCHAR '\r' //è il carattere terminatore e giusto ?
Quello che voglio fare non dovrebbe essere cosi difficile ma proprio non ci arrivo, ho cercato anche varie librerie ma o risultano incompatibili con la versione di arduino, oppure usano dei pin rx e tx che non si riesce a cambiare.
Spero che tu mi possa consigliare qualcosa da studiare per capire il ciclo do while..

No, in quel modo proprio NON va bene ... ed ovviamente ti da problemi ... hai fatto un mix di Serial e mySerial ...

Io avevo postato un esempio generico fatto sulla Serial ... ma ovvimente dovevi ADATTARLO alla tua SoftwareSerial di nome mySerial ... ::slight_smile:

Allora .... devi iniziare dichiarando delle define/costanti ...

#define MAXCHAR 200     // Max string lenght
#define TERCHAR 0x0D    // Termination character (0x0D is CR)
#define TIMEOUT 5000    // Max waiting time (0 = unlimited)

...dove MAXCHAR intendiamo il numero massimo di caratteri utili, TERCHAR quale terminatori ti aspetti, e ... già che ci siamo implementiamo anche un TIMEOUT (espresso in millisecondi) entro il quale ti aspetti la stringa completa ... altrimenti c'è un errore.

Ti serve poi il buffer dove mettere i caratteri ricevuti, un indice, una variabile per indicare che c'è stato un errore e una variabile per il timeout ...

char inpBuffer[MAXCHAR + 1];
int idxBuffer;
byte tErrore;
unsigned long curtime;

... il buffer deve essere un carattere più lungo perché, oltre ai caratteri utili ci interessa anche il terminatore della stringa.

A questo punto ... STUDIATI come funziona la while() e la do ... while()

Dopo aver studiato e capito come funzionano, il codice che segue ti dovrebbe essere piuttosto chiaro (... è lo stesso che abbiamo già visto con in più il timeout ed una fase conclusiva) ...

tErrore   = 0;
idxBuffer = 0;
curtime   = millis();
//
do  {
        while (!mySerial.available()) {
            if ( (TIMEOUT > 0) && (millis() - curTime > TIMEOUT) ) {
                tErrore = 1;   // tErrore = 1 significa che c'è stato un timeout
                break;   // esco da questo while()
            }
        }
        if (tErrore != 0) break;   // esco dal do ... while()
        //
        inpBuffer[idxBuffer] = mySerial.read();
        curTime = millis();        
} while ( (inpBuffer[idxBuffer++] != TERCHAR) && (idxBuffer < MAXCHAR + 1) );
//
if (tErrore == 0) {
    // non c'è stato errore di timeout, si può passare ad effettuare le altre verifiche ...
    idxBuffer--;
    if (inpBuffer[idxBuffer] != TERCHAR) {
        // se l'ultimo carattere NON è il terminatore, la stringa era troppo lunga, quindi errore
        tErrore = 2;   // tErrore = 2 significa stringa troppo lunga
        // bisogna comunque svuotare il buffer
        delay(5);
        while (mySerial.available()) {
            mySerial.read();
            delay(5);
        }
    }
    else {
        // tutto ok, si inserisce il terminatore di stringa (0x00) al posto di TERCHAR
        inpBuffer[idxBuffer] = 0x00;
    }
}
// a questo punto, se tErrore == 0, inpBuffer contiene la stringa, 
// se tErrore != 0 allora c'è stato un errore e, in particolare :
// tErrore == 1 c'è stato un timeout
// tErrore == 2 troppi caratteri ricevuti e non ricevuto il terminatore TERCHAR

... studia il codice, cerca di capirlo e dimmi cosa non ti è chiaro.

Guglielmo

P.S.: Il codice l'ho modificato al volo e non escluso qualche errore di battitura :wink:

Guglielmo intanto grazie, adeso studio un po il ciclo e il codice che hai postato, e vedo di capire al meglio come muovermi.

Grazie