Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Gestire un modem tramite comandi AT

281 views
Skip to first unread message

pozz

unread,
May 31, 2014, 9:49:58 AM5/31/14
to
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?

pozz

unread,
May 31, 2014, 9:57:27 AM5/31/14
to
Il 31/05/2014 15:49, pozz ha scritto:
> [...]
> 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.


Archaeopteryx

unread,
Jun 2, 2014, 5:33:44 PM6/2/14
to
Il 31/05/2014 15:57, pozz ha scritto:
>> 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?

PaoloC

unread,
Jun 5, 2014, 10:04:03 AM6/5/14
to
On 31/05/2014 15.49, pozz wrote:

Ciao.
Puo' essere che l'implementazione varii con il modulo GSM a
disposizione. Io l'ho fatto con un oggetto Nokia 30 e ATmega diretto (no
Arduino).

La mia applicazione ᅵ probabilmente piᅵ semplice della tua: riceve una
chiamata, hang-up, manda un SMS.

Io ho implementato solo i comandi che mi servono, non gli invio altro:
inizializzazione di visualizzazione del CLIP, lettura del livello di
segnale e invio dell'SMS. Non essendo una applicazione mission-critical,
ignoro le risposte tranne per il livello di segnale.

> - 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.

L'unsolicited lo gestisco usando un HW interrupt (linea RI) e a quel
punto leggo il buffer con il numero chiamante. Forse al secondo squillo,
avendo liberato il buffer di ingresso.

> 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?

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


pozz

unread,
Jun 5, 2014, 6:01:31 PM6/5/14
to
Il 05/06/2014 16:04, PaoloC ha scritto:
> Puo' essere che l'implementazione varii con il modulo GSM a
> disposizione. Io l'ho fatto con un oggetto Nokia 30 e ATmega diretto (no
> Arduino).

Diciamo che la maggior parte dei comandi AT per un modem GSM sono
standardizzati, quindi l'implementazione ᅵ generalmente modem-indipendente.
Ovviamente lato micro la questione ᅵ diversa. Anch'io sto lavorando con
un ATMega "diretto".

> La mia applicazione ᅵ probabilmente piᅵ semplice della tua: riceve una
> chiamata, hang-up, manda un SMS.
>
> Io ho implementato solo i comandi che mi servono, non gli invio altro:
> inizializzazione di visualizzazione del CLIP, lettura del livello di
> segnale e invio dell'SMS. Non essendo una applicazione mission-critical,
> ignoro le risposte tranne per il livello di segnale.

Cosa vuoi dire che ignori le risposte? La comunicazione ᅵ bloccante? Tipo:
invia_comando("AT+CLIP=1\r");
aspetta_ok();
Cosa succede se non ricevi l'OK? Ti fermi lᅵ a vita?

Purtroppo, anche per applicazioni non mission-critical, non me la sento
di ignorare queste, seppur rare, situazioni.


>> - 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.
>
> L'unsolicited lo gestisco usando un HW interrupt (linea RI) e a quel
> punto leggo il buffer con il numero chiamante. Forse al secondo squillo,
> avendo liberato il buffer di ingresso.

Se quando arriva il RING (il tuo interrupt hw) stai inviando un comando
al modem? Hai implementato un meccanismo per interrompere quel task che
impegna il modem?


>> 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?
>
> La gestione del modem in ambiente embedded ᅵ complessa, essendo
> simpaticamente asincrona da entrambe le parti.

Me ne sono accorto.


> Sei sicuro che non ci sia una libreria preconfezionata per la tua
> 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).

Daniele Orlandi

unread,
Jun 5, 2014, 8:14:26 PM6/5/14
to
pozz wrote:
>
> - 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.

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,

news.solani.org

unread,
Jun 7, 2014, 5:47:16 AM6/7/14
to
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.


pozz

unread,
Jun 11, 2014, 5:18:03 PM6/11/14
to
Il 06/06/2014 02:14, Daniele Orlandi ha scritto:
> [...]
> 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.

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.


0 new messages