Newsgroup: it.hobby.elettronica.digitale
Titolo: Gestire un modem tramite comandi AT

Visto che il gruppo ï¿œ un po' morto ultimamente, non mi sento in colpo se 
lo imbratto con una mia domanda :-)
 
Come strutturereste un modem GMS comandato via seriale con i comandi AT? 
Considerando che:
 
- ad ogni comando AT...<CR> bisogna aspettare la risposta prima di 
inviarne un altro (sembra un protocollo half-duplex);
 
- la risposta puï¿œ essere di vario tipo (OK, NO CARRIER, +CPIN: READY), 
racchiusa tra <CR><LF>;
 
- alcuni comandi hanno risposte su due righe (tipo AT+CPIN che risponde 
con lo stato della SIM e l'OK);
 
- alcuni comandi hanno una risposta che non termina con <CR><LF> (tipo 
il prompt dell'AT+CMGS);
 
- il modem puï¿œ inviare, in qualsiasi momento, un comando asincrono 
unsolicited (tipo il RING, oppure NO CARRIER se cade la connessione in 
corso, ...)... quindi il protocollo ï¿œ definitivamente full-duplex.
 
 
Il primo requisito mi fa pensare di demandare ad un livello inferiore 
(una sorta di driver) la gestione dell'attesa dell'ultimo comando, prima 
di inviare l'altro. In questo modo l'applicazione puï¿œ fregarsene e 
reagire velocemente ad impulsi asincroni dell'utente.
Tipo, sono connesso ma l'utente schiaccia il tasto di disconnessione 
subito dopo aver inviato un comando AT, prima di aver ricevuto la 
risposta. Anzichï¿œ bufferizzare questa richiesta asincrona a livello 
applicativo, in modo che il comando sia veramente inviato dopo la 
risposta al comando precedente, posso fare il buffering nel driver.
Una sorta di coda dei comandi che l'applicazione riempie e il driver 
consuma. Le risposte, cosï¿œ come arrivano, vengono passate 
all'applicazione e, se ci sono altri comandi in coda, si trasmette il 
successivo e si attende la nuova risposta.
 
Ma c'ï¿œ un problema con questo approccio. L'applicazione manda AT1 
(esempio) e subito dopo manda ATH perchï¿œ l'utente vuole disconnettersi. 
Il driver bufferizza ATH e lo manda effettivamente dopo l'OK di risposta 
ad AT1. Nel frattempo l'applicazione si ï¿œ spostata nello stato in cui sa 
di aver inviato ATH e aspetta la risposta di ATH. Ma il driver deve 
ricevere l'OK di AT1 e quando la riceve la passa all'applicazione che 
penserï¿œ di aver ricevuto la risposta di ATH.
 
Altro esempio ï¿œ la riproduzione di un messaggio vocale (il modem fa 
anche questo). L'utente, spingendo i tastini, puï¿œ cambiare il messaggio 
in corso in modo asincrono. Ma anche qui non posso inviare il comando AT 
per mettere in play un nuovo messaggio se non ho ricevuto l'OK del 
precedente comando. Ed anche qui l'applicazione potrebbe confondere l'OK 
di risposta di un comando con quella di un altro.
 
Insomma, un casotto, sperando di essermi spiegato bene.
 
Soluzioni a cui ho pensato.
Torno indietro e gestisco nell'applicazione la complicazione di 
bufferizzare le "richieste asincrone" e gestirle subito dopo la risposta 
dell'ultimo comando. Ma non ï¿œ per niente elegante e tutto il codice si 
sporcherebbe per dover gestione questi "comandi differiti".
 
Oppure ho pensato di associare un ID ad ogni comando inviato (magari 
l'ID torna dal driver quando si accoda un nuovo comando). L'applicazione 
dovrï¿œ fare un controllo di coerenza tra ID del comando e ID della 
risposta per accettare la risposta.
 
Senza parlare degli unsolicited che possono arrivare quando vogliono 
(anche prima della risposta di un comando AT appena inviato) e che il 
comando CMGS risponde con un prompt che non termina con <CR><LF> (che mi 
obbliga a rinunciare alla semplice implementazione della ricezione in 
modalitï¿œ line-oriented).
 
Siccome sono sempre molto bravo a far diventare complicate cose 
semplici, ho la sensazione che stia sbagliando strada e ci sia un modo 
migliore... me ne consigliate uno?

 Soluzioni a cui ho pensato.
 > [...]
 
Dimenticavo ad un'altra soluzione a cui avevo pensato. La coda dei 
comandi nel driver ï¿œ in realta ad un unico elemento: l'applicazione ï¿œ 
sempre responsabile di aspettare la risposta dell'ultimo comando se non 
avvengono eventi asincroni.
 
Se dovessero succedere eventi asincroni e l'applicazione dovesse pushare 
un nuovo comando, il driver si accorgerebbe di ciï¿œ perchï¿œ dovrebbe 
realmente utilizzare l'unico elemento della coda. In questo caso, 
ipotizza che ï¿œ successo qualcosa e l'applicazione ha "cambiato corso" e 
non si aspetta piï¿œ la risposta del comando precedente.
 
In altre parole, non inoltra all'applicazione la risposta di un comando 
se la coda non ï¿œ vuota.
 
Cosï¿œ evito la questione degli ID.
 
 

> Soluzioni a cui ho pensato.
 
io ci sto impazzendo solo che vedo dal livello delle tue
domande che ne sai dieci volte più di me, sicché spero che
il thread si sviluppi e possa pure io comandare lo
smartphone con arduino (tanto per esplicitare a cosa miro).
 
ciao!
 
Apx.
 
-- 
- mi dai il tuo numero?
- sono fidanzata
- Mi serve il numero
- Sono fidanzata, ti ho detto
- Aho, ma le vuoi provare 'ste scarpe o no?
 

 migliore... me ne consigliate uno?
 
La gestione del modem in ambiente embedded ï¿œ complessa, essendo 
simpaticamente asincrona da entrambe le parti.
 
Sei sicuro che non ci sia una libreria preconfezionata per la tua 
piattaforma HW+SW?
 
Buona fortuna e, se vuoi, aggiornaci.
 
PaoloC
 
 

 piattaforma HW+SW?
 
Arduino ha qualche libreria, ma sono troppo semplici per il grado di 
affidabilitï¿œ che vorrei raggiungere. Sono routine tutte bloccanti che 
non mi piacciono affatto (per esempio, mentre compongo un numero non 
posso fare altro).
 
Non dico di aver risolto, ma ho raggiunto un livello decente nel 
seguente modo.
 
Ho scritto un driver di "basso livello" che gestisce il modem GSM. 
Questo driver *non* ï¿œ "comando agnostico", nel senso che l'applicazione 
dice di voler inviare il comando ATH, piuttosto che AT+CMGR, mediante 
degli identificativi dei comandi (ed eventuali parametri associati, se 
servono).
 
Il driver sa che all'ATH il modem risponde normalmente con OK, mentre 
all'AT+CMGR con un messaggio +CMGR su una riga e poi OK su un'altra 
riga. Quindi, in base al comando che l'applicazione vuole inviare, il 
driver si sposta su uno stato apposito.
La stessa risposta viene decodificata nel driver e tornata indietro 
all'applicazione, mediante un messaggio comprendente i vari parametri 
presenti nella risposta del modem (per esempio, il numero mittente di un 
SMS e il testo).
Naturalmente il driver gestisce, sempre in base al comando, risposte 
errate, inattese o timeout di non risposta, inviando all'applicazione il 
relativo messaggio.
 
Per fare in modo che piï¿œ task possano accedere "contemporaneamente" al 
modem in modo indipendente, la funzione di invio comando in realtï¿œ si 
limita a riempire una FIFO di comandi che il driver, con calma, 
consumerï¿œ. Ad ogni comando ï¿œ legato il task generatore (nel mio caso, 
una state-machine). La risposta verrï¿œ inviata solo a quel task.
 
Per quanto riguarda gli URC, ho proceduto in questo modo. Il driver 
legge byte a byte dalla seriale e si ferma al <CR><LF>. Controlla subito 
se si tratta di un URC (RING, NO CARRIER, ...) e, se sï¿œ, lo invia ad un 
gestore di URC generico per tutta l'applicazione. Altrimenti lo passa 
allo stato che si occupa della ricezione (sempre se stiamo aspettando 
una risposta ad un comando, altrimenti ignora la riga).
Lo stato interpreta i messaggi ricevuti in base al comando inviato. In 
alcuni casi puï¿œ essere settato un flag urc_notadmitted per evitare di 
controllare gli URC quando abbiamo iniziato a ricevere una risposta 
complessa di un comando (tipo la lettura di un SMS che ï¿œ formata da 3 
messaggi su righe diverse, cosï¿œ un SMS con il testo NO CARRIER non viene 
interpretato come URC).
 

 corso, ...)... quindi il protocollo è definitivamente full-duplex.
 
Ahaha... quanto tempo ci ho smadonnato dietro :)
 
Beh, funziona così... che il protocollo AT non specifica un dettaglio 
fondamentale e la maggior parte delle implementazioni sono bacate.
 
L'unica implementazione che tiene in considerazione questo fatto di cui sono 
a conoscenza sono i moduli ex-Siemens.
 
Come fanno?
 
Ti assicuri di avere l'echo abilitato.
 
Tu mandi il tuo bel ATPIPPO<cr><lf>
 
Il modem inizia a rispondere con A, da quel momento il modem GARANTISCE che 
non invierà nessun URC fino a che non hai completato l'invio del comando e 
lui ti ha mandato tutta la risposta.
 
C'è un timeout di 30 secondi per l'invio del comando per evitare che un baco 
o un carattere spurio possa lockare la seriale indefinitamente.
 
Ciao,

Fai una macchina a stati.
0. Nop (niente da fare)
 
1. Invio comando
 
2. Attesa risposta comando
    2.a eventuale gestione evento asincrono
 
    2.b eventuale gestione timeout
        se timeout, invio eventualmente sequenza escape +++
        e stato=3
 
3. gestione errore
      torna a stato=0
 
 
La macchina a stati non e' bloccante per il resto dell' applicazione :-)
 
Brix.
 
 

 o un carattere spurio possa lockare la seriale indefinitamente.
 
Sicuro? Forse dici bene quando affermi che praticamente nessun modulo 
tiene in considerazione questa regoletta.
 
Il mio modem Simcom non funziona per niente così. Dopo l'echo del primo 
carattere 'A' può tranquillamente sparare un RING prima che il comando 
sia terminato.