Publié par
Il y a 2 années · 9 minutes · IoT

Atelier – Collecte 2 : Envoyer / Recevoir des données depuis le backend Sigfox

recette-IoT-4

Plus d’info sur les ingrédients

Nous avons maintenant réalisé un objet communicant complexe. Mais, car il y a un mais, notre réseau de communication possède ses propres contraintes : la charge utile (payload) des messages est limité à 12 octets, en montant, et 8 en descendant. Difficile de faire passer par ce canal autant d’informations que nous le souhaiterions. Il est donc temps de revenir aux fondamentaux, et de compresser un maximum de données en un minimum d’octets. Bienvenue dans l’informatique des années 80.

Un peu de code pour envoyer vers le Cloud

En reprenant le code source du précédent article, nous pouvons obtenir des float pour nos variables environnementales. Nous allons maintenant limiter leur précision, pour respecter les contraintes de taille de message.

Pour cela, nous allons utiliser une structure de type union : nous allons stocker 2 variables de type différents (float et tableau de byte) sur la même adresse mémoire. On peut ainsi facilement coder la température sur une taille de 4 octets et la luminosité sur 2 octets. Pour l’humidité, étant donné qu’il s’agit d’un pourcentage, on peut se limiter à un octet.

Nous sommes même finalement assez tranquille, puisque nous n’utilisons que 7 octets. Il reste de la place pour ajouter des capteurs !

// Sigfox
uint8_t dataSigfox[12];
uint8_t size;
union
{
  uint8_t  value1[4];
  float    value2;
}t_union;
union
{
  uint8_t  value1[2];
  uint16_t value2;
}l_union;
uint8_t status;
 
float h = dht.TemperatureHumidityRead(dhtPin, 'H'); 
float t = dht.TemperatureHumidityRead(dhtPin, 'T'); 
float l = readLuminance(lumPin);    

printf("***  Measures   ***\n"); 
printf("Luminance : %f\n", l); 
printf("Humidity : %f\n", h); 
printf("Temperature : %f\n", t); 
printf("Send to Sigfox\n");

// fill data array
t_union.value2 = t; 
dataSigfox[0] = t_union.value1[3];
dataSigfox[1] = t_union.value1[2];
dataSigfox[2] = t_union.value1[1];
dataSigfox[3] = t_union.value1[0];
 
dataSigfox[4] = (uint8_t) h;

l_union.value2 = (uint16_t) l; 
dataSigfox[5] = l_union.value1[1];
dataSigfox[6] = l_union.value1[0];
size = 7;

// Final Frame to send in "data"
printf("Final Frame to send: 0x%X\n", dataSigfox);
 
// Sending packet to Sigfox
status = Sigfox.send(dataSigfox,size);

Nous pouvons envoyer ces octets à Sigfox en utilisant la méthode Sigfox.send.

Comme pour les articles précédents de cette série, vous trouverez les scripts complets sur notre dépot GitHub.

Pour lancer un envoi simple vers Sigfox, il vous suffit de deux lignes :

pi@raspberrypi:~/sigfox $ make TARGET=06-send-sigfox-uplink
pi@raspberrypi:~/sigfox $ sudo ./06-send-sigfox-uplink
***  Switch Sigfox ON   ***
*** Waiting for user action (push button) ***
***  Measures   ***
Luminance : 20 lux
Humidify : 75 %
Temperature : 22°C
*** Sending data to Sigfox ***
*** Sigfox packet sent ***
*** Waiting for user action (push button) ***

Le traitement de l’information côté Sigfox

Nous n’allons pas tout de suite nous lancer dans un backend compliqué. Mais nous aimerions quand même décoder les octets envoyés à Sigfox pour les faire apparaitre « en clair » et être capables de retourner une réponse. Il est donc l’heure de dégainer Mockbin, l’outil parfait pour simuler un backend REST. En effet, le but de cette série d’article est de créer une véritable application autour de nos objets connectés. Le backend Sigfox n’est donc qu’une étape, et nous allons au fur et à mesure rerouter les valeurs reçues dans Sigfox vers leur destination finale. Mockbin est un premier pas.

Première étape, créer notre callback dans le backend Sigfox.

sigfox-donnees

Nous sommes sur un callback de type DATA, UPLINK (soit une communication unidirectionnelle).

Nous allons traiter une charge utile de type custom. Ce champ va nous aider à décoder notre tableau d’octets. La documentation est relativement claire et notre charge utile est donc du type temperature::float:32 humidity::uint:8 luminance::uint:16

Screen-Shot-2016-02-28-at-15.02.28.png

 

En laissant cet onglet ouvert, passons maintenant côté Mockbin, pour créer une terminaison à notre callback. Dans un premier temps, un backend tout simple suffira à notre bonheur.

callback-Mockbin

Notre terminaison REST est maintenant prête. Entrons son adresse dans notre callback. Comme nous connaissons les règles et normes du web, nous utiliserons le verbe HTTP POST pour pousser nos données (depuis peu, il est même possible d’utiliser le verbe PUT). Le corps de notre requête sera composé d’un mélange de données issues de Sigfox et de données issues de notre charge utile Custom. Dans le corps de notre JSON, les variables Sigfox sont à mettre entre { }. Il est aussi possible d’utiliser ces variables dans les URLs et donc de les construire selon une véritable grammaire REST.

grammaire-rest

Le grand moment est arrivé. Il est temps de poster votre premier vrai message à Sigfox. Démarrez votre Pi, lancez le programme ad-hoc, et pressez le bouton. Vous devriez, dans l’ordre, constater dans votre console RPi que le message a bien été envoyé, puis voir apparaitre le message et son indicateur Callback au vert dans Sigfox, et enfin voir vos données en clair dans Mockbin !

sigfox-device
sigfox-device2

Les données descendantes

Utilisons maintenant notre sapin de Noël. Les DEL de notre plaque d’expérimentation vont être pilotées par des données descendantes en provenance de notre backend, à savoir MockBin. Malheureusement, il n’est pas possible d’éditer une une terminaison Mockbin existante. Nous allons donc en recréer une à l’usage de notre application bidirectionnelle. Comme le spécifie la norme, l’appel descendant doit comporter exactement 8 octets pour être valide. Nous le verrons plus tard, mais nous n’avons besoin de que d’un octet pour piloter nos 3 DEL. En respectant la spécification, notre réponse doit être de la forme :

{ "<deviceId>" : "downlinkData" : "<8 octets>" }}

Malheureusement, Mockbin ne permet pas de générer des réponses dynamique. Il va donc falloir créer une terminaison, ou bin, par objet, qui contiendra son identifiant dans le corps de la réponse. Concernant les données en downlink,  nous allons réserver 2 octets à l’état de nos 3 DEL. Nous verrons plus tard comment interpréter cette réponse, mais dans un premier temps, nous allons renvoyer les 8 octets suivants : 0700000000000000.

Coté Sigfox, nous allons modifier notre callback, pour le rendre BIDIRectionnel, et activer le downlink sur l’écran principal. Notez que vous pouvez maintenant utiliser le flag {ack} dans votre charge utile. Notez aussi que si vous renvoyez une réponse ne respectant pas ce format, ou ayant moins de 8 octets, Sigfox vous gratifiera d’un obscur message, INVALID PAYLOAD, assez frustrant.

Si tout se passe bien, vous devriez voir apparaitre deux magnifiques flèches vertes.

sigfox-bidirectionnel

Passons enfin au dernier étage de la fusée, le code sur votre Raspberry Pi. Peu de changement sont à prévoir du coté de l’envoi des données. Nous allons simplement remplacer la méthode d’envoi Sigfox.send par Sigfox.sendACKqui attend une réponse du portail Sigfox avant de déconnecter le modem. Dans le cadre d’un échange bidirectionnel Sigfox, cette réponse peut être assez longue à venir, de l’ordre de 30 secondes. Prenez donc votre mal en patience.

Si vous utilisez les bibliothèques Cooking Hacks telles quelles, vous aurez la chance de voir que votre modem reçoit une réponse, mais pas de pouvoir en exploiter le contenu.

Pour pouvoir exploiter cette réponse, il nous a fallu modifier le programme C fourni par Cooking Hacks. Vous trouverez l’historique de ce pacth sur leur forum. Notre Github contient bien sûr cette bibliothèque modifiée.

Nous sommes donc en mesure de récupérer les données du downlink, et de les transformer en un integer.

int led_state = (int)strtol(reinterpret_cast<const char*>(Sigfox._ackData), NULL, 16);

Dans un souci d’économie, nous allons réduire l’empreinte de l’information et notamment la représentation de nos DEL. Nous réserverons 2 octets, dont chaque bit représentera l’état d’une DEL (0 : éteinte, 1 : allumée). À cet effet , nous utiliserons des masques de bit et les opérateurs de décalage afin de manipuler individuellement ces bits. Le lecteur réveillé notera que 2 octets = 16 bits, ce qui est supérieur aux 3 bits nécessaires à la représentation de l’état de nos 3 DEL, mais à ce stade, nous souhaitons également garder de la place pour les futures évolutions.

Si vous voulez en savoir plus, vous pouvez lire cet article pour débuter dans la représentation binaire des données et les opérateurs qu’elle offre.

// LED
#define RED_LED 1
#define GREEN_LED (1 << 1)
#define BLUE_LED (1 << 2)
#define LED_IS_ON(led_id,state) (state & (1 << (led_id-1)))
 
i = 0;
for(;i <= 3; i++) {
  if(LED_IS_ON(i, led_state)) {
    printf("led %d is on\n", i);
    digitalWrite(ledsPin[i], HIGH);
  } else {
    printf("%d pin is off\n", ledsPin[i]);
    digitalWrite(ledsPin[i], LOW);
    }
}

Vos DEL devraient, en retour de l’appel Sigfox, s’allumer. Félicitations, vous avez réalisé un aller retour complet. Pour modifier l’état de vos DEL, vous pouvez modifier votre mockbin pour qu’il retourne une autre représentation binaire, en vous basant sur ce tableau :

Décimal Binaire DEL
0 0 Tout éteint
1 1 Rouge
2 10 Vert
3 11 Rouge Vert
4 100 Bleu
5 101 Rouge Bleu
6 110 Vert Bleu
7 111 Rouge Vert Bleu

Pour tester directement le programme :

pi@raspberrypi:~/sigfox $ make TARGET=07-send-sigfox-bidir
pi@raspberrypi:~/sigfox $ sudo ./07-send-sigfox-bidir
***  Switch Sigfox ON   ***
*** Waiting for user action (push button) ***
***  Measures   ***
Luminance : 20 lux
Humidity : 75 %
Temperature : 22°C
*** Sending data to Sigfox ***
*** Sigfox packet sent ***
*** Sigfox downlink packet received ***
Led wired on pin 2 is on
Led wired on pin 3 is on
Led wired on pin 4 is on
*** Waiting for user action (push button) ***

La suite

Notre objet est maintenant complet : il est communicant (en Sigfox), il est capable de mesurer son environnement (via ses capteurs) et capable de recevoir des instructions (via l’éclairage de ses DEL). Nous pouvons donc nous attaquer à la suite de notre apprentissage, les plate-formes Cloud.

Comme aux Césars, il faut rendre grâce à ceux qui ont participé au montage de cet objet. Merci à Qian Jin pour son enthousiasme et avoir supporté mes (très nombreuses) râleries dans l’Atelier. Merci à Paali Tandia pour m’avoir fait (re)découvrir que je n’aime pas le C++. Et encore merci à Aurélien Maury de WeScale pour ses conseils avinsés sur Ansible.

Pablo Lopez

Pablo est directeur technique chez Xebia et formateur Hadoop certifié par Cloudera au sein de Xebia Training .

5 thoughts on “Atelier – Collecte 2 : Envoyer / Recevoir des données depuis le backend Sigfox”

  1. Publié par Noury, Il y a 1 année

    Bonjour,

    Excellent article.
    Je m’en suis largement inspiré.

    J’ai toutefois bloqué sur les données descendantes.
    J’ai créé un callback en BIDIR, mais je n’arrive pas à activer le downlink. J’ai juste un petit cercle (donc intérieur vide), je n’arrive ppas à avoir un cercle plein. Comment faire?

    Par ailleurs, j’utilise un Arduino, pas un Pi. La bibliothèque que j’ai trouvée ne contenait pas le patch. J’ai également compris qu’il fallait patcher le .h, ce qui n’était pas mentionné dans les échanges sur le forum dont vous avez parlés. Il s’agit de « _ackData ».
    La grosse difficulté que j’aie, est comment utiliser cette variable pour récupérer les données dans le code Arduino.

    Si vous pouviez m’aider, ce serait formidable.
    Je n’utilse plus Mockbin maintenant, je passe par mon serveur, je peux donc faire ce que je veux.

    Merci d’avance

    Noury

  2. Publié par Noury, Il y a 1 année

    Bonsoir,

    Finalement tout est ok. Tout fonctionne correctement.
    J’ai fini par trouver comment on passe du DIRECT au CALLBACK.
    C’était un peu caché.

    Merci en tout cas pour ce magnifique et très complet document.

  3. Publié par Pablo Lopez, Il y a 1 année

    Merci pour votre retour et pour vos encouragements ! Et bon courage pour vos prochains prototypes, n’hésitez pas à nous en parler, nous sommes toujours curieux de voir ce que nos lecteurs vont inventer !

  4. Publié par Jérôme, Il y a 3 mois

    Bonjour à tous,

    En premier lieu je tiens à vous remercier pour le tuto qui m’a jusque là été très utile.
    En suite j’ai une petit demande d’aide, dans le cadre d’un projet je souhaiterai envoyer les datas transmises par un arduino+sigfox vers une db firebase via je suppose un callback. Et c’est là que ça coince car malgré plusieurs essais je n’y arrive pas.
    Auriez-vous déjà tenté cette opération et si oui pourriez-vous me transmettre les configuration des différentes étapes.

    D’avance merci.

    Jérôme

  5. Publié par Godefroy NSIMBA, Il y a 2 mois

    Bonjour,
    je suis entrain de pose les sondes SIGFOX pour les compteur d’eau lors de mise en service elle m’envoie 9 à 11 trames ca depend mon probleme je cherche à comprendre pour coptabilise mes index à partir de la date de pose.
    merci pour votre aide

Laisser un commentaire

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