magnify
Home Contribution Communiquer avec un GPS Bluetooth sous Windows Phone
formats

Communiquer avec un GPS Bluetooth sous Windows Phone

A l’époque de Windows Mobile, communiquer avec un périphérique en utilisant une liaison série était quelques chose de très simple à mettre en œuvre. Le système d’exploitation intégrait tout ce qu’il fallait pour ouvrir un port série et permettre l’envoi et la réception de données par ce canal. Simple, ouvert, pratique.

Dans les 1ères versions de l’OS mobile de Microsoft, du temps de Pocket PC 2000 sous Windows CE 3 par exemple, la liaison série était physiquement réelle. On branchait un câble qui reliait le PDA avec le périphérique, comme on peut le voir dans cet exemple de borne interactive animée par un Pocket PC que j’ai mis en ligne en 2001. Le programme était réalisé en eVB (ça ne me rajeunit pas cette histoire).

Ensuite, avec la démocratisation du Bluetooth, les machines ont été en mesure de créer des liaisons séries virtuelles, le câble ayant été remplacé par les ondes radio. Classiquement, à cette époque, on se connectait avec des GPS Bluetooth car les machines sous Windows Mobile n’intégraient pas encore de puce GPS, il fallait donc passer par des appareils externes pour être en mesure de proposer une géolocalisation et de la carto.

A l’époque j’avais d’ailleurs écrit un article sur la manière de se connecter à un GPS et être en mesure de décoder les trames NMEA qu’il crachait périodiquement et pour le TechDays 2008 j’avais présenté une application permettant de prendre des photos géolocalisées avec la possibilité de les afficher sur une carte. Et oui à l’époque ça en a impressionné plus d’un, maintenant c’est d’une grande banalité je vous l’accorde.

Pour moi la sortie de Windows Phone fût un grand choc. Belle plateforme, belles et puissantes machines, fluidité, design moderne, en gros que du bonheur par rapport à la plateforme Windows Mobile. Mais en creusant un peu, quel retour en arrière sur certains points ! Certes les machines intégraient nativement une puce GPS et le SDK permettait d’en tirer partie, mais impossible par exemple d’ouvrir une liaison série (réelle ou virtuelle), impossible de créer un socket serveur, impossible de faire plein de choses qui étaient devenues si naturelles avec l’ancienne plateforme… Allez expliquer ça à vos clients qui vous demandent de porter leurs anciennes applications Windows Mobile vers Windows Phone. Dans certains cas impossible de le faire, et on passe donc à Android où là il n’y a rien qui s’y oppose.

Avec le recul je comprends très bien pourquoi les choses ont été faites comme ça, mais je dois bien avouer qu’à l’époque MS a pourri une partie importante de mon business avec ces conneries !

Dans certains cas il est indispensable d’avoir plusieurs GPS en même temps. Par exemple, si vous faites du GPS différentiel, vous ne pouvez vous contenter d’utiliser celui inclus dans le device, vous devez collecter les données de plusieurs GPS et effectuer des traitements en temps réel pour arriver à une précision de positionnement de l’ordre du cm par exemple. De plus vous ne pouvez vous contenter de récupérer simplement la position (latitude, longitude), vous devez aussi récupérer des données très importantes sur le niveau de précision, le nombre de satellites en vue et j’en passe. Je ne vais pas entrer dans le détail du GPS différentiel ce n’est pas le but de cet article.

Donc avec Windows Phone, si vous voulez faire du GPS différentiel vous êtes cuit. Sauf depuis la sortie de Windows Phone 8 qui a ouvert encore un peu plus le périmètre des libertés anciennes (Windows Mobile) retrouvées.

Avec Windows Phone 8 vous avez la possibilité de créer un socket entre votre application et un périphérique Bluetooth. Donc pour faire simple, on peut communiquer très facilement en Bluetooth avec par exemple… un GPS, et même plusieurs GPS en même temps ! Ca y est, 3 ans après la sortie de la plateforme Windows Phone, je peux dire à mes clients qu’il peuvent abandonner Android et revenir dans le droit chemin sous Windows Phone ! Sauf que pour eux maintenant c’est trop tard, ils sont passés à Android et ne reviendront pas en arrière. En effet il n’ont pas la capacité financière de faire redévelopper toutes les 5 minutes leurs applications mobiles.

Je vais donc dans cet article vous montrer comment dialoguer avec un GPS en Bluetooth et vous allez voir que c’est assez simple à faire.

Le petit programme GpsBT que j’ai réalisé pour l’occasion permet de faire 3 choses :

  1. Lister les appareils liés en Bluetooth
  2. Sélectionner un appareil et s’y connecter
  3. Afficher les trames NMEA envoyées par le GPS

Les sources de GpsBT sont disponibles ici.

1/ Les droits

La 1ère chose à faire, c’est de donner à votre application les droits pour utiliser les fonctions Bluetooth :

ID_CAP_PROXIMITY

ID_CAP_NETWORKING

2/ Lister les appareils liés

Il faut partir à la découverte des appareils liées. Ces appareils sont ceux qui sont listés dans l’écran des settings du Bluetooth et qui ont été liés à votre téléphone par une opération d’appairage. On ne peut se connecter qu’avec un appareil qui a été au préalable lié et impossible dans l’état actuel du SDK de provoquer par programme ce lien.

        private async void BouPeerDiscovery_Click(object sender, RoutedEventArgs e)
        {
            BouPeerDiscovery.IsEnabled = false;

            PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";

            try
            {
                var wPairedDevices = await PeerFinder.FindAllPeersAsync();
                this.ViewModel = new MainPageViewModel(wPairedDevices.ToList());
                BouPeerDiscovery.Visibility = System.Windows.Visibility.Collapsed;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Peer Discovery", MessageBoxButton.OK);
                BouPeerDiscovery.IsEnabled = true;
            }
        }

La liste des appareils liés est retournée par la fonction PeerFinder.FindAllPeerAsync. Dans mon exemple, la liste est fournie au ViewModel de la page pour un affichage dans une liste comme dans l’écran suivant :

Le GPS que j’utilise est le Holux GR-230, c’est donc à lui que je vais essayer de me connecter. Il ne faut pas oublier au préalable de le mettre en route, bien sûr.

3/ Connexion à un appareil

La fonction permettant de lister les appareils disponibles retourne pour chacun d’entre eux un objet de type PeerInformation. Parmi les informations disponibles, on a le DisplayName qui est le nom en clair (celui que j’utilise dans la liste), et le HostName qui sera utilisé pour effectuer la connexion.

La connexion est réalisée via un objet de type « StreamSocket » en utilisant sa méthode asynchrone « ConnectAsync » avec en premier paramètres le HostName provenant du PeerInformation de l’appareil auquel on veut se connecter et la chaîne 1 en second paramètre (je ne sais pas vraiment à quoi correspond ce second paramètre, dans tous les exemples de la documentation la valeur utilisée est la chaine 1 alors va pour ça).

        public async Task ConnectAsync(PeerInformation wPeer)
        {
            // Si un précédent device était déjà connecté, on débranche

            if (_StreamSocket != null)
            {
                _ScanGpsTimer.Stop();

                _StreamSocket.Dispose();
                _StreamSocket = null;
            }

            // On essaye de se connecter au device

            _StreamSocket = new StreamSocket();

            try
            {
                await _StreamSocket.ConnectAsync(wPeer.HostName, "1");

                // Pas d'erreur ?
                // Alors c'est qu'on est connecté
                // On peut mettre en place le scan des données qui arrivent du device

                _CurrentGpsInput = "";
                this.DeviceName = wPeer.DisplayName;
                _ScanGpsTimer.Start();
            }
            catch (Exception ex)
            {
                // Erreur pendant la connexion

                _StreamSocket = null;
                return ex;
            }

            return null;
        }

Ma petite fonction de connexion, placée dans le ViewModel de la page, retourne l’erreur rencontrée en cas de problème ou null si tout c’est bien passé.
Si la connexion se passe bien, on passe en mode pooling dans lequel on va lire à intervalle régulier le contenu du buffer d’entrée du StreamSocket. Si des données sont disponibles, elles sont lues et partant du principe que ce sont des trames NMEA, elles sont « découpées » pour être affichées dans une liste.

C’est un DispatcherTimer qui s’occupe d’effectuer les lectures à intervalles régulier :

        private async void OnScanGpsTimerTick(object sender, EventArgs e)
        {
            if (_StreamSocket != null)
            {
                _ScanGpsTimer.Stop();

                // Lecture des données

                var wBuffer = new Windows.Storage.Streams.Buffer(100);
                await _StreamSocket.InputStream.ReadAsync(wBuffer, 100, Windows.Storage.Streams.InputStreamOptions.None);

                if (wBuffer.Length > 0)
                {
                    // On a bien des données

                    using (var wReader = DataReader.FromBuffer(wBuffer))
                    {
                        // On prépare le buffer de réception 

                        byte[] wBuffer2 = new byte[wBuffer.Length];

                        // On récupère les données 
                        wReader.ReadBytes(wBuffer2);

                        // On transforme ces données en string

                        string wString = System.Text.Encoding.UTF8.GetString(wBuffer2, 0, wBuffer2.Length);

                        // On ajoute ces données aux trames générales reçues du GPS

                        _CurrentGpsInput += wString;

                        // On essaye de découper la trame générale en lignes NMEA

                        int p;

                        do
                        {
                            // On recherche les caractères CRLF

                            p = _CurrentGpsInput.IndexOf("\r\n");

                            if (p > -1)
                            {
                                // On récupère le début (avant les caratères CRLF)

                                string wDebut = _CurrentGpsInput.Substring(0, p);

                                // On enlève cette partie de la trame générale

                                _CurrentGpsInput = _CurrentGpsInput.Substring(p + 2);

                                // Généralement une trame NMEA valide commence par $GP

                                if (wDebut.StartsWith("$GP"))
                                {
                                    // On ajoute la partie à la liste des trames NMEA

                                    this.GpsNmea.Add(wDebut);

                                    // On ne garde que les 25 dernières trames NMEA dans la liste

                                    while (GpsNmea.Count > 25) { GpsNmea.RemoveAt(0); }
                                }
                            }

                        } while (p > -1);
                    }
                }
                else
                {
                    // Aucune donnée n'a été lue, si ça dure trop longtemps cela voudra dire
                    // Que le device ne communique plus ou qu'il n'est plus connecté
                    // Dans la vraie vie, c'est un cas qu'il faut traiter
                }

                _ScanGpsTimer.Start();
            }
        }

Voilà, la boucle est bouclée, nous avons réalisé toutes les opérations pour se connecter à un GPS Bluetooth depuis un device Windows Phone 8.

Comme vous pouvez le constater, c’est très simple à mettre en œuvre. Mon seul regret étant de devoir effectuer du pooling pour récupérer les données.

Il y a peut-être le moyen de se « brancher » sur un événement du StreamSocket ou du InputStream sous-jacent, mais je dois vous avouer que je n’ai pas encore pris le temps de creuser cette piste pour le moment.

 
 Share on Facebook Share on Twitter Share on Reddit Share on LinkedIn
3 Comments  comments 

3 réponses

  1. […] Dans un précédent article j’ai abordé la communication avec un périphérique Bluetooth en utilisant comme exemple la communication avec un GPS. […]

  2. Pradon

    Bonjour,
    Je tombe sur votre article d’octobre 2013, et je suis content de vous retrouver sur le web.
    Je fais parti des « bidouilleurs » sur Pocket- pc écœurés par WP7, WP8, pleurant toujours la mort de PPC.
    Client depuis toujours chez MS, programmant en VB, je n’ai pu rejoindre Androïd, l’effort était trop grand.
    Votre article donne un peu d’espoir…
    J’ai écrit sous VB2005 un programme de GPS différentiel en faisant communiquer 2 ordinateurs munis de clé GPS en wifi. Je m’apprêtai à transposer ce programme sur pocket-pc en utilisant votre code, lorsque la « catastrophe » est arrivée.
    Mais ma question est tout autre.
    Avez –vous vous-même écris un programme de GPS référentiel ?
    Car en fait mon programme ne marchait pas, pour la simple raison que les deux clés situées au même endroit ne donnent pas le même résultat : elles ne peuvent être interrogées exactement au même moment, et n’ont le plus souvent pas capté le même nombre de satellites.
    L’écart entre elles est du même ordre de grandeur que deux informations successives sur la même clé, ainsi le gain obtenu est nul(1).
    Les vrais GPS différentiels «  se calent sur la phase », une technologie qui ne se contente pas le lire la trame…
    Y-a-t’il quelque chose qui m’échappe ?
    Mon rêve, placer un Windows phone sur un point connu et lire les coordonnées de l’autre au cm, est il réalisable ?
    Merci pour les informations que vous pourriez m’apporter, et bonne continuation.

    (1) Toutefois, en pondérant avec le GDOP , ( deux clés sur le même ordi), les résultats sont sensiblement améliorés.

  3. Markiki

    Bonjour,
    Je suis pilote amateur et j’aimerais utiliser une application type gps lap timer et la couplée à un gps externe 10 Hz type Qstarz.
    Malgré mes maigres connaissances en informatique je n’ai pas compris comment appairer un gps externe sur un Windows phone 8.1.
    Pouvez vous m’aider svp?

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *