Le GPRS (General Packet Radio Service) est une norme (protocole réseau) pour la téléphonie mobile dérivée du GSM et complémentaire de celui-ci, permettant un débit de données plus élevé.
Le GPRS est une extension du protocole GSM : il ajoute par rapport à ce dernier la transmission par paquets. Cette méthode est plus adaptée à la transmission des données. En effet, les ressources ne sont allouées que lorsque des données sont échangées, contrairement au mode « circuit » en GSM où un circuit est établi pour toute la durée de la communication. Le GPRS a ensuite évolué au début des années 2000 vers la norme Edge également optimisée pour transférer des données et qui utilise les mêmes antennes et les mêmes fréquences radio.
Le GPRS permet de fournir une connectivité IP constamment disponible à une station mobile (MS), mais les ressources radio sont allouées uniquement quand des données doivent être transférées, ce qui permet une économie de la ressource radio. Les utilisateurs ont donc un accès bon marché, et les opérateurs économisent la ressource radio. De plus, aucun délai de numérotation n’est nécessaire.
Ce moyen de transmission nécessite un terminal disposant d’un modem GSM/GPRS ou 3G/UMTS, ainsi que d’une carte SIM de n’importe quel opérateur avec un forfait “données” (Data) adapté. Le terminal nécessite d’être sous couverture GSM/GPRS pour pouvoir envoyer les données vers la plateforme de traitement. Ce type de terminal est utilisé lorsque l’objet ou la personne à géolocaliser reste dans une zone bien couverte par les réseaux GSM/GPRS.
Remarque : Les forfaits GSM/GPRS sont économiquement plus avantageux que les forfaits satellite lorsque l’on souhaite remonter les positions à une fréquence élevée. Ils sont donc à privilégier si les zones où l’équipement se déplace restent bien couvertes par les réseaux GSM/GPRS.
La firme Hayes, fabricant de modems, a développé un protocole pour la commande d’un modem externe à partir d’un ordinateur. Le protocole définit diverses commandes permettant par exemple :
Les commandes AT (ou Commandes Hayes) sont des commandes que l’on peut directement envoyer au modem, lorsque celui-ci est en mode Command.
Chaque commande est envoyée sous la forme d’une ligne de texte encodée en ASCII, terminée par le caractère ‘\r’ seul (code ASCII 0x13). Le modem retourne une réponse sous la forme d’une ou plusieurs lignes selon la commande envoyée, chaque ligne se terminant par les caractères ‘\r’ suivi de ‘\n’ (codes ASCII 0x13 et 0x10).
La série SIM5218 est un module GSM / GPRS / EDGE quadri-bande supportant les protocoles HSPA et la technologie WCDMA. Il autorise un débit descendant (download) de 7.2Mbps et un débit montant (upload) de 5,76Mbps.
Lien : www.cooking-hacks.com
Le module SIM5218 sera détecté comme un port série virtuel [PDF] lorsqu’il est relié par une liaison USB [PDF]. On le programmera à partir de commandes AT.
Lire : SIM5218_AT_command_manual.pdf
Être capable de communiquer avec un modem GPRS/GSM.
On peut tester les commandes AT avec la commande screen
(Ctrl-a k
pour sortir), picocom
(Ctrl-a Ctrl-x
pour sortir) ou cutecom
.
La déclaration de la classe ModemGPRS
:
#ifndef MODEMGPRS_H
#define MODEMGPRS_H
#include "qextserialenumerator.h"
#include "qextserialport.h"
#include <QStringList>
#include <QMap>
#include <QTime>
#include <unistd.h>
#define PORT_DEFAUT "/dev/ttyUSB2"
//#define DEBUG_MODEMGPRS
class ModemGPRS
{
public:
ModemGPRS();
ModemGPRS(QString peripherique);
~ModemGPRS();
QString authentifier(const QString &pin);
bool demarrerGPS();
bool arreterGPS();
QStringList getGPS();
QString getInformations();
QString getNomFabricant();
QString getNomModel();
QString getNumeroSerie();
QString getJeuDeCaracteres();
QString getIMSI();
QString getCapacitesGlobales();
QString getIMEI();
QString getRevision();
QString getOperateur();
QString getListeOperateurs();
QString getNumero();
QString getNumero(int index);
QString getListeNumeros();
QString getQualiteSignal();
QString getNomFournisseur();
QStringList getListeAppelsEmis();
QStringList getListeAppelsManques();
QStringList getListeAppelsRecus();
QStringList getListeRepertoire();
bool setModeTexteSMS();
bool setStockageSMS(const QString &mem1);
bool setStockageSMS(const QString &mem1, const QString &mem2, const QString &mem3);
int getNbSMS();
int getNbSMS(const QString &mem1);
QString getSMS(int index);
QString getSMS(int index, const QString &mem1);
QStringList getListeSMS();
QStringList getListeSMS(const QString &mem1);
QStringList getListeSMSNonLus();
QStringList getListeSMSNonLus(const QString &mem1);
bool envoyerSMS(const QString &numero, const QString &message);
bool envoyerEmail(const QString &adresse, const QString &nom, const QString &sujet, const QString &message);
QString getURL(const QString &url, const QString &requete, unsigned int timeout=20000);
void testerSocketTCP(const QString &serveur, const int port, const QString &message);
bool envoyerCommandeAT(const QString &message, const QString &reponseAttendue, unsigned int timeout=100);
int transmettre(const QString &message);
QString recevoir(unsigned int timeout=100);
private:
QextSerialPort *port;
QString peripherique;
bool gps;
bool modeTexte;
QMap<QString, int> stockageSMS;
QMap<QString, int> totalSMS;
bool ouvrir();
void fermer();
};
#endif // MODEMGPRS_H
La définition partielle de la classe ModemGPRS
:
#include "modemgprs.h"
#include <QtCore/QDebug>
ModemGPRS::ModemGPRS() : port(NULL), peripherique(""), gps(false), modeTexte(false)
{
// mode asynchrone : QextSerialPort::Polling
port = new QextSerialPort(QLatin1String(PORT_DEFAUT), QextSerialPort::Polling);
ouvrir();
}
ModemGPRS::ModemGPRS(QString peripherique) : port(NULL), peripherique(peripherique), gps(false), modeTexte(false)
{
port = new QextSerialPort(peripherique, QextSerialPort::Polling);
ouvrir();
}
ModemGPRS::~ModemGPRS()
{
fermer();
}
bool ModemGPRS::ouvrir()
{
port->setBaudRate(BAUD115200);
port->setParity(PAR_NONE);
port->setDataBits(DATA_8);
port->setStopBits(STOP_1);
port->setTimeout(100);
port->open(QIODevice::ReadWrite | QIODevice::Unbuffered);
#ifdef DEBUG_MODEMGPRS
qDebug("%s ouvert : %d", peripherique.toAscii().data(), port->isOpen());
#endif
if (!port->isOpen()) return false;
return true;
}
void ModemGPRS::fermer()
{
if (port->isOpen())
port->close();
#ifdef DEBUG_MODEMGPRS
qDebug("%s ferme : %d", peripherique.toAscii().data(), !port->isOpen());
#endif
}
QString ModemGPRS::authentifier(const QString &pin)
{
QString reponse;
QString statut;
// déjà autjentifié ?
if(transmettre("AT+CPIN?"))
{
reponse = recevoir();
if(reponse.length() == 0)
return "";
if(!reponse.contains("OK"))
return "";
if(!reponse.startsWith("\r\n+CPIN:"))
return "";
reponse.remove(0, 9);
while(reponse[0] != '\r')
{
statut.append(reponse[0]);
reponse.remove(0, 1);
}
#ifdef DEBUG_MODEMGPRS
if(statut.contains("READY"))
qDebug() << "READY : ME is not pending for any password";
else if(statut.contains("SIM PIN"))
qDebug() << "SIM PIN : ME is waiting SIM PIN to be given";
else
qDebug() << "Statut : " << statut;
/*
SIM PUK : ME is waiting SIM PUK to be given
PH-SIM PIN : ME is waiting phone-to-SIM card password to be given
SIM PIN2 : ME is waiting SIM PIN2 to be given
SIM PUK2 : ME is waiting SIM PUK2 to be given
PH-NET PIN : ME is waiting network personalization password to be give
*/
#endif
}
if(!statut.contains("READY"))
{
if(transmettre("AT+CPIN=\""+pin+"\""))
{
reponse = recevoir();
if(reponse.length() == 0)
return "";
if(!reponse.contains("OK"))
return "";
usleep(5000*1000);
return reponse;
}
}
return "";
}
QString ModemGPRS::getURL(const QString &url, const QString &requete, unsigned int timeout/*=20000*/)
{
QString commandeAT;
bool res;
commandeAT = "AT+CREG?";
res = envoyerCommandeAT(commandeAT, "+CREG: 0,5", 500);
if(res == false)
return "";
// sets APN, user name and password
/*
* APN (Access Point Name) est un identifiant qui permet à un utilisateur de téléphonie mobile d'un réseau 2G ou 3G
* de se connecter à Internet en identifiant le Gateway GPRS Support Node (GGSN) qu'il veut utiliser.
* L'APN est généralement constitué d'un code identifiant le GGSN (et le réseau IP derrière lui) et
* des codes MCC et MNC identifiant l'opérateur de réseau mobile.
*/
res = envoyerCommandeAT("AT+CGSOCKCONT?", "+CGSOCKCONT: 1,\"IP\",\"Free\"", 200);
if(res == false)
{
// sets APN, user name and password
res = envoyerCommandeAT("AT+CGSOCKCONT=1,\"IP\",\"Free\"", "OK", 2000);
if(res == false)
return "";
res = envoyerCommandeAT("AT+CSOCKAUTH=1,1,\"\",\"\"", "OK", 2000);
if(res == false)
return "";
}
res = envoyerCommandeAT("AT+CHTTPACT=\""+url+"\",80", "+CHTTPACT: REQUEST", 10000);
if(res == true)
{
qDebug() << "envoie requete : " << "GET /"+requete+" HTTP/1.1\r\nHost: "+url+"\r\nContent-Length: 0\r\n";
// Ctrl-Z -> 0x1A
if(transmettre("GET /"+requete+" HTTP/1.1\r\nHost: "+url+"\r\nContent-Length: 0\r\n\r\n\x1a"))
{
QString reponse = recevoir(timeout);
qDebug() << "reponse : " << reponse;
if(reponse.length() == 0)
return "";
if(!reponse.contains("+CHTTPACT: DATA"))
return "";
reponse.remove(0, 2); // enleve \r\n au début
return reponse;
}
}
return "";
}
/*
* client TCP
*
* pour les tests, il faut lancer un serveur avec netcat :
* $ nc.traditional -l -p 64465
*
*/
void ModemGPRS::testerSocketTCP(const QString &serveur, const int port, const QString &message)
{
QString commandeAT;
bool res;
commandeAT = "AT+CREG?";
res = envoyerCommandeAT(commandeAT, "+CREG: 0,5", 500);
if(res == false)
return;
// sets APN, user name and password
/*
* APN (Access Point Name) est un identifiant qui permet à un utilisateur de téléphonie mobile d'un réseau 2G ou 3G
* de se connecter à Internet en identifiant le Gateway GPRS Support Node (GGSN) qu'il veut utiliser.
* L'APN est généralement constitué d'un code identifiant le GGSN (et le réseau IP derrière lui) et
* des codes MCC et MNC identifiant l'opérateur de réseau mobile.
*/
res = envoyerCommandeAT("AT+CGSOCKCONT?", "+CGSOCKCONT: 1,\"IP\",\"Free\"", 200);
if(res == false)
{
// sets APN, user name and password
res = envoyerCommandeAT("AT+CGSOCKCONT=1,\"IP\",\"Free\"", "OK", 2000);
if(res == false)
return;
res = envoyerCommandeAT("AT+CSOCKAUTH=1,1,\"\",\"\"", "OK", 2000);
if(res == false)
return;
}
commandeAT = "AT+NETOPEN=\"TCP\","+QString::number(port);
res = envoyerCommandeAT(commandeAT, "Network opened", 5000);
if(res == false)
{
res = envoyerCommandeAT("AT+NETCLOSE", "OK", 5000);
return;
}
commandeAT = "AT+TCPCONNECT=\""+serveur+"\","+QString::number(port);
res = envoyerCommandeAT(commandeAT, "Connect ok", 5000);
if(res == false)
{
res = envoyerCommandeAT("AT+NETCLOSE", "OK", 5000);
return;
}
commandeAT = "AT+TCPWRITE="+QString::number(message.length());
res = envoyerCommandeAT(commandeAT, ">", 100);
if(res == false)
{
res = envoyerCommandeAT("AT+NETCLOSE", "OK", 5000);
return;
}
res = envoyerCommandeAT(message, "Send ok", 10000);
if(res == false)
{
res = envoyerCommandeAT("AT+NETCLOSE", "OK", 5000);
return;
}
res = envoyerCommandeAT("AT+NETCLOSE", "OK", 5000);
}
bool ModemGPRS::envoyerCommandeAT(const QString &message, const QString &reponseAttendue, unsigned int timeout/*=100*/)
{
if (!port->isOpen()) return false;
if(transmettre(message))
{
//usleep(timeout*1000);
QString reponse = recevoir(timeout);
if(reponse.length() == 0)
return false;
if(reponse.indexOf(reponseAttendue) == -1)
return false;
}
else return false;
return true;
}
int ModemGPRS::transmettre(const QString &message)
{
QString delimiteur = "\r\n";
if (!port->isOpen()) return 0;
int i = port->write(message.toLatin1() + delimiteur.toLatin1());
#ifdef DEBUG_MODEMGPRS
qDebug("-> send (%d) : %s", i, message.toAscii().data());
#endif
return i;
}
QString ModemGPRS::recevoir(unsigned int timeout/*=100*/)
{
QString message = "";
char buffer[1024];
int nbOctets = 0;
QTime t;
int debut = 0;
t.start();
if (!port->isOpen()) return "";
if(timeout != 0)
{
debut = t.elapsed();
while((nbOctets == 0) && (t.elapsed() < (int)(debut + timeout)))
{
nbOctets = port->bytesAvailable();
if(nbOctets > 1024)
nbOctets = 1024;
if(nbOctets > 0)
{
int i = port->read(buffer, nbOctets);
if (i != -1)
{
if (buffer[i-1] == '\n' && buffer[i-2] == '\r')
buffer[i-2] = '\0';
else
buffer[i] = '\0';
}
else
buffer[0] = '\0';
message += QLatin1String(buffer);
#ifdef DEBUG_MODEMGPRS
qDebug("-> receive (%d/%d) : %d ms (%d ms)", i, nbOctets, t.elapsed(), (debut + timeout));
#endif
nbOctets = 0;
}
}
#ifdef DEBUG_MODEMGPRS
qDebug("-> receive (%d) : %s", message.length(), message.toAscii().data());
//qDebug("en %d ms", t.elapsed());
#endif
return message;
}
else
{
while(nbOctets == 0)
{
nbOctets = port->bytesAvailable();
if(nbOctets > 1024)
nbOctets = 1024;
}
}
int i = port->read(buffer, nbOctets);
if (i != -1)
{
if (buffer[i-1] == '\n' && buffer[i-2] == '\r')
buffer[i-2] = '\0';
else
buffer[i] = '\0';
}
else
buffer[0] = '\0';
message = QLatin1String(buffer);
#ifdef DEBUG_MODEMGPRS
qDebug("-> receive (%d/%d) : %s", i, nbOctets, message.toAscii().data());
qDebug("en %d ms", t.elapsed());
#endif
return message;
}
Un programme de test de la classe ModemGPRS
:
#include "modemgprs.h"
#include <QStringList>
#include <QtCore/QDebug>
void afficherDetails(ModemGPRS *modemGPRS);
int main()
{
ModemGPRS *modemGPRS = new ModemGPRS("/dev/ttyUSB2");
QString commandeAT;
QString resultat;
bool res;
// Test
commandeAT = "AT";
res = modemGPRS->envoyerCommandeAT(commandeAT, "OK");
if(res == false)
{
qDebug() << "Pas de reponse du modem !";
delete modemGPRS;
return 0;
}
modemGPRS->authentifier(QString("1234"));
afficherDetails(modemGPRS);
// ...
qDebug() << "Test client socket TCP";
/*
-> send (24) : AT+NETOPEN="TCP",64465
-> receive (20) :
Network opened
OK
-> send (37) : AT+TCPCONNECT="92.153.12.130",64465
-> receive (16) :
Connect ok
OK
-> send (16) : AT+TCPWRITE=13
-> receive (3) :
>
-> send (15) : hello world !
-> receive (32) :
OK
+TCPWRITE: 13, 13
Send ok
-> send (13) : AT+NETCLOSE
Network closed
OK
*/
modemGPRS->testerSocketTCP("92.153.12.130", 64465, "hello world !");
delete modemGPRS;
return 0;
}
void afficherDetails(ModemGPRS *modemGPRS)
{
QString resultat;
//resultat = modemGPRS->getInformations();
//qDebug() << resultat;
resultat = modemGPRS->getNomFabricant();
qDebug() << "Fabricant : " << resultat;
resultat = modemGPRS->getNomModel();
qDebug() << "Model : " << resultat;
resultat = modemGPRS->getNumeroSerie();
qDebug() << "Numero de serie : " << resultat;
resultat = modemGPRS->getNomFournisseur();
qDebug() << "Fournisseur : " << resultat;
resultat = modemGPRS->getJeuDeCaracteres();
if(resultat.contains("IRA"))
qDebug() << "Jeu de caracteres : IRA (International Reference Alphabet)";
else if(resultat.contains("GSM"))
qDebug() << "Jeu de caracteres : GSM (GSM default alphabet); this setting causes easily software flow control (XON/XOFF) problems";
else if(resultat.contains("UCS2"))
qDebug() << "Jeu de caracteres : UCS2 (16-bit Universal multiple-octet coded Character Set); UCS2 character strings are converted to hexadecimal numbers from 0000 to FFFF";
else
qDebug() << "Jeu de caracteres : " << resultat;
resultat = modemGPRS->getIMSI();
qDebug() << "IMSI : " << resultat;
resultat = modemGPRS->getCapacitesGlobales();
qDebug() << "Capacites : " << resultat;
QStringList liste = resultat.split(",");
for (int i = 0; i < liste.size(); ++i)
{
if(liste.at(i).contains("CGSM"))
qDebug() << "CGSM : GSM function is supported";
else if(liste.at(i).contains("FCLASS"))
qDebug() << "FCLASS : FAX function is supported";
else if(liste.at(i).contains("DS"))
qDebug() << "DS : Data compression is supported";
else if(liste.at(i).contains("ES"))
qDebug() << "ES : Synchronous data mode is supported";
}
resultat = modemGPRS->getIMEI();
qDebug() << "IMEI : " << resultat;
resultat = modemGPRS->getRevision();
qDebug() << "Revision : " << resultat;
}
Code source : test-mo-sim5218.zip