Introduzione
Un messaggio SMS, così come specificato dall'organizzazione
Etsi ( www.etsi.org ) nei documenti GSM 03.40 e GSM
03.38, può essere lungo sino a 160 caratteri.
Ogni carattere è codificato con 7 bit e fa parte
di un set ristretto di 127 caratteri detto alfabeto
di default a 7 bit. I messaggi ad otto bit (per un massimo
di 140 caratteri) generalmente non sono visualizzabili
dai telefoni come messaggi di testo, ma sono usati per
la trasmissione di dati quali immagini o suonerie.
I messaggi a 16 bit (per un massimo di 70 caratteri)
sono invece utilizzati per la trasmissione di messaggi
di testo in formato Unicode (UCS2).
Il
formato PDU
Esistono principalmente due modi per ricevere ed inviare
messaggi SMS: il formato testo (non disponibile su alcuni
telefoni) ed il formato PDU (protocol description unit).
In definitiva, una applicazione in grado di leggere
dei messaggi SMS può usare sia la modalità
testo sia la modalità PDU, tuttavia utilizzando
la prima modalità, l'applicazione è limitata
ad un set di opzioni preimpostate mentre utilizzando
il formato PDU si ha una maggiore flessibilità.
Formato
di un messaggio PDU in ricezione
La stringa PDU di un messaggio in ricezione contiene
non soltanto il testo del messaggio vero e proprio,
ma anche un insieme di meta-informazioni riguardo il
mittente, il centro servizi SMS, il time stamp d'invio,
ecc. Tutte queste informazioni sono in forma di ottetti
esa-decimali o semi-ottetti decimali.
Un ottetto esadecimale è la rappresentazione,
sotto forma di stringa esadecimale, del valore di un
byte, ottenuto invertendo l'ordine dei nibble o più
semplicemente l'ordine delle due cifre esadecimali.
Ad esempio il valore decimale 207 (esadecimale CF) è
rappresentato dalla stringa 'FC'.
I semi ottetti decimali rappresentano un numero in formato
decimale ottenuto invertendo a due a due l'ordine di
apparizione delle cifre. Ad esempio il numero del centro
servizi 39 34 92 00 02 00 è codificato in semi-ottetti
decimali come 93 43 29 00 20 00. Se il numero di cifre
non è pari viene aggiunta una F in coda al numero
prima dell'inversione.
Il
seguente messaggio, in formato PDU, è quanto
ricevuto con il messaggio 'Ciao'
0791934329006060040C9193434774422600002050922242300804C374F80D
infatti
07
- la lunghezza del numero del centro servizi (SMSC)
espressa in numero di ottetti
91 - tipo del numero del centro servizi SMSC (91 indica
il formato internazionale dei numeri di telefono)
93 43 29 00 60 60 - il numero del centro servizi (espresso
in semi ottetti decimali)
04 - il primo ottetto del messaggio SMS-DELIVER
0C - lunghezza del numero del mittente espressa in numero
di caratteri
91 - tipo del numero del mittente
93 43 47 74 42 26 - il numero del mittente (espresso
in semi ottetti decimali)
00 - TP-PID
00 - TP-DCS
20 50 92 22 42 30 08 - TP-SCTS Time Stamp
04 - TP-UDL Lunghezza del messaggio. Se il TP_DCS specifica
il formato a 7-bit, la lunghezza è il numero
di blocchi di 7 bit.
C3 74 F8 0D - il messaggio 'Ciao' codificato
Il
numero del centro servizi e il numero del mittente sono
espressi in semi ottetti-decimali.
Il
TP-Data Coding Scheme fornisce informazioni riguardo
la codifica, la compressione e l'alfabeto utilizzato.
Il valore 00 specifica un testo non compresso, che utilizza
l'alfabeto di default , di classe 0 (Immediate display).
Il
TP-Service Center Time Stamp specifica l'istante di
invio del messaggio. I primi sei caratteri (3 ottetti)
rappresentano la data nel formato anno-mese-giorno,
i successivi sei l'orario nel formato ore-minuti-secondi.
L'ultimo ottetto indica, invece, la time zone riferita
a GMT. In particolare ogni unità rappresenta
15 minuti e se il bit più significativo è
a 1 il valore indicato è negativo.
Anche il time stamp è espresso in semi ottetti
decimali, quindi la data
0x20
0x50 0x92 0x22 0x42 0x30 0x08
indica
il 20 Maggio 2002 22:24:03 GMT+2
Formato
di un messaggio PDU in trasmissione
Al messaggio precedentemente analizzato in ricezione
corrisponde il seguente in trasmissione:
079193432900200011000C919343477442260000AA04C374F80D
07
- La lunghezza del numero del centro servizi. Se la
lunghezza è 0, viene utilizzato il numero memorizzato
nel telefono. Questo valore è opzionale, e su
alcuni telefoni l'utilizzo del numero interno è
implicito.
91 - Tipo del numero del centro servizi
93 43 29 00 20 00 - il numero del centro servizi
11 - Il primo ottetto del messaggio SMS-SUBMIT
00 - TP - Message-Reference. Il valore 0 fa si che il
telefono imposti da solo questo numero
0C - Lunghezza del numero di telefono del destinatario
91 -Tipo del numero di telefono del destinatario
93 43 47 74 42 26 - il numero di telefono del destinatario
codificato in semi ottetti decimali
00 - il TP-PID Protocol Identifier
00 - il TP-DCS
AA - il TP-VP Validity Period
04 - la lunghezza del messaggio
C3 74 F8 0D - il messaggio codificato
Il
periodo di validità specifica entro quanto il
messaggio può essere consegnato. Superato questo
periodo il messaggio viene scartato dal centro servizi.
Il periodo può essere espresso in forma relativa,
assoluta e accresciuta.
Nella forma relativa la validità è espressa
da un solo ottetto che viene interpretato in base allo
schema di figura 1.
Figura 1 - Significato del TP-VP
Codifica
a 7 bit
Questo tipo di codifica consiste nel trasformare una
sequenza di caratteri a 7 bit (septet), appartenenti
all'alfabeto GSM di default, in una sequenza di byte
(octet).
L'algoritmo compone i singoli byte del messaggio operando
nel seguente modo:
il carattere di sette bit è trasformato in un
byte aggiungendo come bit più significativo il
bit più a destra del secondo carattere. Il bit
più a destra del secondo carattere viene così
consumato. Per otterenere un nuovo byte necessitano,
quindi, i due bit meno significativi del terzo carattere.
Iterando il procedimento otteniamo una codifica come
quella dell'esempio in figura 2
Figura 2 - Esempio di codifica a 7 bit
Gli
algoritmi per la codifica e la decodifica, possono essere
implementati in Java nel modo seguente:
/**
* Questo metodo ritorna una stringa che rappresenta
* in forma esadecimale
* il messaggio passato come parametro codificato nel
formato 7-bit
* encoded data
*
* @param message il messaggio da codificare
* @return il messaggio codificato
*/
public static String encode(String message)
{
StringBuffer encodedMessage = new StringBuffer();
// Conversione dei caratteri ISO8859-1 nei corrispettivi
// caratteri dell'alfabeto di default
//
message = isoTogsm(message) ;
int bits = 0 ;
int bitsLength = 0 ;
for(int i=0; i < message.length() ; i++)
{
char c = message.charAt(i);
bits |= (c << bitsLength);
bitsLength += 7;
while (bitsLength >= 8)
{
char octet = (char) (bits & 255);
encodedMessage.append(JSmsUtils.toHexString(octet));
bits >>>= 8;
bitsLength -= 8;
}
}
if( (bitsLength > 0) )
encodedMessage.append(JSmsUtils.toHexString(bits));
return encodedMessage.toString();
}
/**
* Questo metodo decodifica un messaggio codificato nel
* formato 7-bit encoded data
*
* @param message la stringa esadecimale del messaggio
codificato
* @return il messaggio decodificato
*/
public static String decode(String message)
throws NumberFormatException {
StringBuffer decodedMessage = new StringBuffer() ;
int bits = 0 ;
int bitsLength = 0 ;
for(int i=0; i < message.length() ; i+=2) {
char c = (char)Integer.parseInt(message.substring(i,i+2),16);
bits = (c << bitsLength) | bits ;
bitsLength += 8 ;
while (bitsLength >= 7){
char septet = (char) (bits &
127);
decodedMessage.append(septet);
bits >>>= 7;
bitsLength -= 7;
}
}
// Conversione dei caratteri dell'alfabeto
di default
// nei corrispettivi caratteri ISO8859-
1
//
return gsmToIso(decodedMessage.toString())
;
}
Le due funzioni isoToGsm e gsmToIso si occupano di trasformare
una stringa di caratteri ISO8859 in una stringa di caratteri
dell'alfabeto Gsm di default, in base alla seguente
tabella di conversione:
Hex |
Dec |
Character name |
Character |
ISO-8859-1 |
0x00 |
0 |
COMMERCIAL AT |
@ |
64 |
0x01 |
1 |
POUND SIGN |
£ |
163 |
0x02 |
2 |
DOLLAR SIGN |
$ |
36 |
0x03 |
3 |
YEN SIGN |
¥ |
165 |
0x04 |
4 |
LATIN SMALL LETTER E WITH GRAVE |
È |
232 |
0x05 |
5 |
LATIN SMALL LETTER E WITH ACUTE |
È |
233 |
0x06 |
6 |
LATIN SMALL LETTER U WITH GRAVE |
Ú |
250 |
0x07 |
7 |
LATIN SMALL LETTER I WITH GRAVE |
Ì |
236 |
0x08 |
8 |
LATIN SMALL LETTER O WITH GRAVE |
Ò |
242 |
0x09 |
9 |
LATIN CAPITAL LETTER C WITH CEDILLA |
Ç |
199 |
0x0A |
10 |
LINE FEED |
|
10 |
0x0B |
11 |
LATIN CAPITAL LETTER O WITH STROKE |
Ø |
216 |
0x0C |
12 |
LATIN SMALL LETTER O WITH STROKE |
Ø |
248 |
0x0D |
13 |
CARRIAGE RETURN |
|
13 |
0x0E |
14 |
LATIN CAPITAL LETTER A WITH RING
ABOVE |
Å |
197 |
0x0F |
15 |
LATIN SMALL LETTER A WITH RING
ABOVE |
Å |
229 |
0x10 |
16 |
GREEK CAPITAL LETTER DELTA |
|
|
0x11 |
17 |
LOW LINE |
_ |
95 |
0x12 |
18 |
GREEK CAPITAL LETTER PHI |
|
|
0x13 |
19 |
GREEK CAPITAL LETTER GAMMA |
|
|
0x14 |
20 |
GREEK CAPITAL LETTER LAMBDA |
|
|
0x15 |
21 |
GREEK CAPITAL LETTER OMEGA |
|
|
0x16 |
22 |
GREEK CAPITAL LETTER PI |
|
|
0x17 |
23 |
GREEK CAPITAL LETTER PSI |
|
|
0x18 |
24 |
GREEK CAPITAL LETTER SIGMA |
|
|
0x19 |
25 |
GREEK CAPITAL LETTER THETA |
|
|
0x1A |
26 |
GREEK CAPITAL LETTER XI |
|
|
0x1B |
27 |
ESCAPE TO EXTENSION TABLE |
|
|
0x1B0A |
27 10 |
FORM FEED |
|
12 |
0x1B14 |
27 20 |
CIRCUMFLEX ACCENT |
^ |
94 |
0x1B28 |
27 40 |
LEFT CURLY BRACKET |
{ |
123 |
0x1B29 |
27 41 |
RIGHT CURLY BRACKET |
} |
125 |
0x1B2F |
27 47 |
REVERSE SOLIDUS (BACKSLASH) |
\ |
92 |
0x1B3C |
27 60 |
LEFT SQUARE BRACKET |
[ |
91 |
0x1B3D |
27 61 |
TILDE |
~ |
126 |
0x1B3E |
27 62 |
RIGHT SQUARE BRACKET |
] |
93 |
0x1B40 |
27 64 |
VERTICAL BAR |
| |
124 |
0x1B65 |
27 101 |
EURO SIGN |
€ |
164 (ISO-8859-15) |
0x1C |
28 |
LATIN CAPITAL LETTER AE |
Æ |
198 |
0x1D |
29 |
LATIN SMALL LETTER AE |
Æ |
230 |
0x1E |
30 |
LATIN SMALL LETTER SHARP S (German) |
ß |
223 |
0x1F |
31 |
LATIN CAPITAL LETTER E WITH ACUTE |
Ê |
202 |
0x20 |
32 |
SPACE |
|
32 |
0x21 |
33 |
EXCLAMATION MARK |
! |
33 |
0x22 |
34 |
QUOTATION MARK |
" |
34 |
0x23 |
35 |
NUMBER SIGN |
# |
35 |
0x24 |
36 |
CURRENCY SIGN |
¤ |
164 (ISO-8859-1) |
0x25 |
37 |
PERCENT SIGN |
% |
37 |
0x26 |
38 |
AMPERSAND |
& |
38 |
0x27 |
39 |
APOSTROPHE |
' |
39 |
0x28 |
40 |
LEFT PARENTHESIS |
( |
40 |
0x29 |
41 |
RIGHT PARENTHESIS |
) |
41 |
0x2A |
42 |
ASTERISK |
* |
42 |
0x2B |
43 |
PLUS SIGN |
+ |
43 |
0x2C |
44 |
COMMA |
, |
44 |
0x2D |
45 |
HYPHEN-MINUS |
- |
45 |
0x2E |
46 |
FULL STOP |
. |
46 |
0x2F |
47 |
SOLIDUS (SLASH) |
/ |
47 |
0x30 |
48 |
DIGIT ZERO |
0 |
48 |
0x31 |
49 |
DIGIT ONE |
1 |
49 |
0x32 |
50 |
DIGIT TWO |
2 |
50 |
0x33 |
51 |
DIGIT THREE |
3 |
51 |
0x34 |
52 |
DIGIT FOUR |
4 |
52 |
0x35 |
53 |
DIGIT FIVE |
5 |
53 |
0x36 |
54 |
DIGIT SIX |
6 |
54 |
0x37 |
55 |
DIGIT SEVEN |
7 |
55 |
0x38 |
56 |
DIGIT EIGHT |
8 |
56 |
0x39 |
57 |
DIGIT NINE |
9 |
57 |
0x3A |
58 |
COLON |
: |
58 |
0x3B |
59 |
SEMICOLON |
; |
59 |
0x3C |
60 |
LESS-THAN SIGN |
< |
60 |
0x3D |
61 |
EQUALS SIGN |
= |
61 |
0x3E |
62 |
GREATER-THAN SIGN |
> |
62 |
0x3F |
63 |
QUESTION MARK |
? |
63 |
0x40 |
64 |
INVERTED EXCLAMATION MARK |
¡ |
161 |
0x41 |
65 |
LATIN CAPITAL LETTER A |
A |
65 |
….. |
…. |
|
…. |
….. |
0x5A |
90 |
LATIN CAPITAL LETTER Z |
Z |
90 |
0x5B |
91 |
LATIN CAPITAL LETTER A WITH DIAERESIS |
Ä |
196 |
0x5C |
92 |
LATIN CAPITAL LETTER O WITH DIAERESIS |
Ö |
214 |
0x5D |
93 |
LATIN CAPITAL LETTER N WITH TILDE |
Ñ |
209 |
0x5E |
94 |
LATIN CAPITAL LETTER U WITH DIAERESIS |
Ü |
220 |
0x5F |
95 |
SECTION SIGN |
§ |
167 |
0x60 |
96 |
INVERTED QUESTION MARK |
¿ |
191 |
0x61 |
97 |
LATIN SMALL LETTER A |
A |
97 |
….. |
…. |
|
…. |
…. |
0x7A |
122 |
LATIN SMALL LETTER Z |
Z |
122 |
0x7B |
123 |
LATIN SMALL LETTER A WITH DIAERESIS |
Ä |
228 |
0x7C |
124 |
LATIN SMALL LETTER O WITH DIAERESIS |
Ö |
246 |
0x7D |
125 |
LATIN SMALL LETTER N WITH TILDE |
Ñ |
241 |
0x7E |
126 |
LATIN SMALL LETTER U WITH DIAERESIS |
Ü |
252 |
0x7F |
127 |
LATIN SMALL LETTER A WITH GRAVE |
À |
224 |
Tabella
1 - conversione GSM - ISO8859/1
Esecuzione
di comandi AT per l'invio di un messaggio
La lettura o la spedizione di un messaggio SMS avviene
inviando una serie di comanti AT, attraverso la porta
seriale, dal computer al telefonino.
L'utilizzo
delle porte seriali e parallele in Java è reso
possibile mediante l'utilizzo delle API Java Communication.
Queste API possono essere utilizzate per scrivere delle
applicazioni, indipendenti dalla piattaforma, che accedono
alle porte di comunicazione disponibili. Le funzionalità
messe a disposizione dalle javacomm possono essere così
riassunte:
-
enumerazione delle porte disponibili su un sistema
- apertura
e blocco di una porta
- comunicazione
sincrona e asincrona
- gestione
degli eventi di cambio stato
Di
seguito è riportato il codice per l'apertura
di una porta seriale
CommPortIdentifier
portId = null;
Enumeration portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
portId = (CommPortIdentifier) portList.nextElement();
if (portId.getPortType() ==
CommPortIdentifier.PORT_SERIAL){
if (portId.getName().equals(
portName )) {
serialPort
= (SerialPort)portId.open(JSms.class.getName(),
2000);
break
;
}
}
}
if (serialPort==null) throw new Exception("Serial
port " +
portName
+ " not found.") ;
outStream = serialPort.getOutputStream();
inStream = serialPort.getInputStream();
serialPort.setSerialPortParams(bitRate,SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
Il ciclo while scorre le porte disponibili sulla macchina
sino ad individuare quella con il nome indicato. Se
la porta non viene trovata, viene rilanciata una eccezione,
altrimenti vengono creati gli stream per l'input e l'output.
L'ultima istruzione, infine, imposta i parametri di
comunicazione della porta seriale.
Una
volta ottenuti gli stream di input ed output, possiamo
inviare al telefonino i comandi AT necessari per gestire
i messaggi SMS. I comandi AT di nostro interesse sono:
Inizializzazione:
ATE0
- disabilita l'eco dei comandi
ATQ0
- abilita le risposte del DTE
Invio:
AT+CMGF=0
- imposta il formato PDF
AT+CMGS=lunghezza_messaggio
- invia un messaggio
Ricezione:
AT+CMGL
- Legge tutti i messaggi
AT+CMGR=numero_messaggio
- Legge un messaggio
Il comando AT+CMGS deve essere seguito dal messaggio
in formato PDF terminato dal caratte '\032'
Volendo
inviare il messaggio PDU codificato in precedenza, dobbiamo
quindi inviare, in successione, le seguenti stringhe:
AT+CMGF=0\015
AT+CMGS=52\015
079193432900200011000C919343477442260000AA04C374F80D\032
Ogni
stringa AT per poter essere eseguita deve essere terminata
dal carattere '\015', quindi, occorre aggiungerlo in
coda ad ogni comando inviato al telefonino. Se l'esecuzione
di un comando AT fallisce, il telefono risponde con
un codice di errore del tipo:
+CMS
ERROR: <err>
I
codice numerici di errore principali hanno il seguente
significato:
300
Phone failure
301 SMS service of phone reserved
302 Operation not allowed
303 Operation not supported
304 Invalid PDU mode parameter
305 Invalid text mode parameter
310 SIM not inserted
311 SIM PIN necessary
312 PH-SIM PIN necessary
313 SIM failure
314 SIM busy
315 SIM wrong
320 Memory failure
321 Invalid memory index
322 Memory full
330 SMSC (message service center) address unknown
331 No network service
332 Network timeout
500 Unknown error
Per
verificare se l'esecuzione di ogni comando è
andata a buon fine, occorre leggere i messaggi di risposta
del telefonino. Per leggere occorre registrare un listener
degli eventi generati dalla porta seriale. Questo viene
effettuato con le istruzioni
serialPort.addEventListener(
serialPortEventListener );
serialPort.notifyOnDataAvailable(true);
La classe listener deve implementare l'interfaccia SerialPortEventListener
definendone il metodo serialEvent. Il codice seguente
mostra una implementazione del metodo serialEvent che
legge i dati dalla porta e li elabora, una riga per
volta, ogni volta che incontra la sequenza di caratteri
cr e lf.
public
void serialEvent(SerialPortEvent event) {
if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE){
int n;
try{
while ( (n = inStream.available()) >
0) {
n = inStream.read(readBuffer,
bufferOffset, n);
bufferOffset += n;
//
//
if((readBuffer[bufferOffset-1]
== 10)
&&
(readBuffer[bufferOffset-2] == 13)) {
String newLine =
new String(readBuffer,0,bufferOffset-2);
// Elaborazione
della riga ricevuta
//
lineRecived( newLine
);
bufferOffset = 0;
}
}
}
catch (IOException e) {
.
}
}
}
Conclusioni
Gli SMS sono ormai un nuovo modo di comunicare che si
è diffuso di pari passo con i telefonini, grazie
alla immediatezza e alla economicità. Una applicazione
può sfruttare gli SMS tutte le volte che la criticità
dei messaggi non consente l'invio di una semplice mail
(ad esempio la segnalazione di malfunzioni di sistemi
che richiedono un intervento umano tempestivo).
Java, con le API Javacomm, consente di sfruttare le
potenzialità degli SMS dimostrando, ancora una
volta, di essere un linguaggio flessibile e completo.
Bibliografia
[1]
Javacomm - http://java.sun.com/products/javacomm
[2] Etsi - http://www-etsi.org
|