[Résolu]Comment bien comprendre/utiliser les bytes, les doubles et les char

Bonjour à tous,

Je dois envoyer des données avec LoRa. Les données à envoyer doivent etre des bytes

To send data back and forth over The Things Network you’ll need to use bytes

J'utilise une librairie qui m'aide à faire ceci.

Ceci dit, il y a un truc que je n'arrive pas comprendre conecrnant le byte et la conversion des char et des double en byte.

Dans mon cas, j'ai une varaible qui est déclarée ainsi

uint8_t mydata[64];

C'est le contenu de cette varaiable qui sera envoyer

Plus bas, on du code comme ceci:

strcpy((char *) mydata,"{\"Hello\":\"Word\"");

Hello Word est bien reçu.

Je n'arrive pas à expliquer comment est compiler les texte dans mydata.

Si je remprend ce code

strcpy((char *) mydata,"{\"Hello\":\"Word\"");

Que contiendra exectement mydata? Est-ce qu'elle contiendra "Hello Word" ou Hello Word est converti en bytes? Est-ce que le fait myddata est un uint8_t, le texte, comme Hello Word, est converti en bytes dans mydata? Et que fait exactement (char *) avant mydata

Ce que j'aimerais arriver à comprendre et à expliquer à un autre, est comment convertir donc du texte en bytes, et comment vérifier la taille et le contenu de la variable qui le contiendra, comme dans mon cas, mydata.

Comment puis imprimer mydata pour vérifier que le contenu correspond bien au double ou au char que je veux envoyer

Merci pour vos lumières

bonjour

je suppose que c’est la suite de la discussion des structure :slight_smile:

Si je remprend ce code

strcpy((char *) mydata,"{\"Hello\":\"Word\"");

Que contiendra exectement mydata?

myData comprendra la suite de représentation ASCII des caractères

{"Hello":"World"

avec les guillemets et un caractère nul à la fin

uint8_t ou un char c’est une zone mémoire de 8 bit, un octet. l’interprétation de ce qui est dans ces 8 bits dépend du type de données. uint8_t ça veut dire que vous indiquez votre intention d’avoir un un entier non signé sur un octet, donc un chiffre en 0 et 255 et si vous faites print par exemple vous verrez ce chiffre alors que si vous l’avez déclaré en char vous dites que c’est plutôt un caractère, généralement ASCII. le type des variable devient important quand vous appelez des fonctions parce que suivant le type passé ce n’est pas la même fonction qui est appelée. un Serial.print( <int>) n’est pas la même chose que Serial.print (<char>) (dans le premier cas la fonction va penser que vous voulez la valeur de l’entier en base 10, dans le second cas que c’est un caractère ASCII à imprimer)

comment convertir donc du texte en bytes

le compilateur le fait pour vous suivant le type de données

char x = 'a'; // l'octet associée à x est initialisé à la valeur numérique ASCII qui représente la lettre a (97)
char x = 97; // fait la même chose
char message[] = "bonjour";

crée un tableau de char qui vont successivement être initialisés avec les valeurs ASCII des lettres du mot bonjour, suivi d’un caractère de fin de chaine nul ‘\0’

Si vous exécutez ce code

char message[] = "Bonjour";
byte tailleMessage = sizeof(message); // taille du tableau, donc va comprendre le 0 à la fin

void setup() {
  Serial.begin(115200);

  for (byte i = 0; i < tailleMessage; i++) {
    Serial.print("message["); Serial.print(i); Serial.print("] contient la valeur ");
    Serial.print((uint8_t) message[i]); // on cast en type entier pour que la bonne fonction print soit appelée
    Serial.print(" caractere ["); Serial.print(message[i]); // ici le type est char, donc la fonction print imprime le caractere ASCII
    Serial.println("]");
  }
}

void loop() {}

vous verrez cela dans la console:

[sub][color=blue]
[nobbc]message[0] contient la valeur 66 caractere [B]
message[1] contient la valeur 111 caractere [o]
message[2] contient la valeur 110 caractere [n]
message[3] contient la valeur 106 caractere [j]
message[4] contient la valeur 111 caractere [o]
message[5] contient la valeur 117 caractere [u]
message[6] contient la valeur 114 caractere [r]
message[7] contient la valeur 0 caractere []
[/nobbc]
[/color][/sub]

un double sur un UNO c’est 4 octets. Si vous faites

double x = 123.45;

alors les 4 cases mémoires pointées associées à la variable x vont stocker une représentation binaire du nombre 123.45 suivant une norme internationale (IEEE 754)

par exemple pour voir ce qui est en mémoire vous pouvez faire ce petit programme

double x = 123.45;

void setup() {
  Serial.begin(115200);

  Serial.print("x="); Serial.println(x); // imprimé sous forme de nombre à virgule car le compilateur a vu qu'on passait un double et donc appelle la bonne fonction print
  
  uint8_t *ptr = (uint8_t *) &x;  // on prend maintenant un pointeur sur la zone de stockage de la variable x et on lui dit que dedans ce sont des uint8_t
  Serial.print("les 4 octets en memoire sont 0x");
  Serial.print(*(ptr), HEX); // ici on dit qu'on veut la fonction print qui imprime en hexadécimal
  Serial.print(" 0x");
  Serial.print(*(ptr + 1), HEX);
  Serial.print(" 0x");
  Serial.print(*(ptr + 2), HEX);
  Serial.print(" 0x");
  Serial.print(*(ptr + 3), HEX);
}

void loop() {}

qui va afficher sur la console

[sub][color=blue]x=123.45
les 4 octets en memoire sont 0x66 0xE6 0xF6 0x42[/color][/sub]

Votre arduino étant “little endian” les octets de poids faibles sont rangés en premier dans la représentation IEEE donc la représentation sur 4 octets associée dans l’ordre est 0x[color=blue]42F6E666[/color] (on lit depuis la fin de l’impression)

si vous allez sur un site web de conversion en ligne et tapez la valeur 0x[color=blue]42F6E666[/color], magique on trouve la valeur d’origine
double.png

le site vous montre en plus comment le signe, la mantisse et l’exposant sont représentés en binaire, c’est une partie des bits qui est affectée à chacun des morceaux

au final vous avez toujours des octets et des bits, le reste est juste affaire d’interprétation pour y associer une signification.

Milles mercis pour ces grandes exlications
Oui c’est la suite des structures :slight_smile: . J’ai réussi à faire ce que je voulais, merci!!!

Je dois avouer que je nage en ce qui concerne les bits et les caracteres >:(

Le problème que j’ai, c’est si j’active trop de capteur, les données ne sont pas envoyées. Si j’en active que 3, ca va…

Je me demande donc si je ne surchage pas mydata, qui va contenir ma chaine à envoyer.

Selon l’excercice précédent :slight_smile: , je construis ma chaine ainsi

Note: le sprintln(val,2) est une fonction qui va soit affiché dans le terminal (Serial.pritnln) ou/et das la carte SD. (Pour le moment, il n’y a pas de carte SD…)
J’ai modifié votre exemple, pour avoir ceci à l’afficahe

Valeurs à envoyer: ba:4.31,te:2068,pr:-12896,al:42400,hu:3260,lu:1570

sprint(F("Valeurs à envoyer:\t"),2);
for (byte i = 0; i < nbMaxCapteurs; i++) {
      if (mesCapteur[i].actif) { // si le capteur n'est pas actif on n'imprime pas
        
        if(i==0)
        {
          strcpy((char *) mydata,mesCapteur[i].nomDuCapteur);
          strcat((char *) mydata,":");
          strcat((char *) mydata,mesMesures[i][mesCapteur[i].idDuCapteur].valeur);
        }
        else
        {
          strcat((char *) mydata,mesCapteur[i].nomDuCapteur);
          strcat((char *) mydata,":");
          strcat((char *) mydata,mesMesures[i][mesCapteur[i].idDuCapteur].valeur);
        }
        
        if (i < nbCapteurActif)
        {
          strcat((char *) mydata,",");
        }

// AFFICHE DANS LE TERMINAL LA CHAINE A ENVOYER
        sprint(mesCapteur[i].nomDuCapteur,2);
        sprint(F(":"),2);
        sprint(mesMesures[i][mesCapteur[i].idDuCapteur].valeur,2);
        if (i < nbCapteurActif) sprint(F(","),2);
      }
    }
    sprintln(F("\n"),2);

Si vais sur un site pour convertir ceci

ba:4.31,te:2068,pr:-12896,al:42400,hu:3260,lu:1570

Ma variable mydata doit avoir cette valeur

62613a342e33312c74653a323036382c70723a2d31323839362c616c3a34323430302c68753a333236302c6c753a31353730

Je suppose donc que (entre autre)

strcpy((char *) mydata,mesCapteur[i].nomDuCapteur);

Va donc copier le nom du capteur dans mydata et que ceci ne sera plus du texte (ou des caracteres) mes des bytes.

C’est cette conversion que je n’arrive pas à expliquer.

Si je reprends vos explications.

le compilateur le fait pour vous suivant le type de données

Mais que signifie alors (char *)? Ca indique le type de la variable source?

Aussi, est-ce que strlen(mydata) va m’indiquer la taille des bitsde mydata sur les 64

uint8_t mydata[64];

Ce que j’aimerais faire, c’est d’afficher la progression de l’empilage du contenu de madata dans la boucle

for (byte i = 0; i < nbMaxCapteurs; i++) {...

Ainsi, je pourrai voir si, en activant des capteurs, mydata vient à être surchargée

Votre bug il est là:

          strcpy((char *) mydata,mesCapteur[i].nomDuCapteur);
          strcat((char *) mydata,":");
          strcat((char *) mydata,mesMesures[i][mesCapteur[i].idDuCapteur].valeur);

vous ne pouvez pas utiliser strcat() pour mettre en binaire la valeur de vos octets au milieu du buffer, ça ne fonctionne qu'avec des chaînes de caractères terminées par un '\0'...

pour ça il faut gruger un peu "à la main"... je vous ponds un petit exemple

Bon alors voilà un bout de code qui remplit un buffer avec une chaîne et ensuite rajoute une valeur de 4 octets derrière.

uint32_t valeur = 0xDEADBEEF; // une valeur sur 4 octets

const uint8_t tailleMax = 20;
uint8_t data[tailleMax]; // un buffer assez grand

void printData()
{
  Serial.print("data = { ");
  for (byte i = 0; i < tailleMax; i++) {
    Serial.print("0x");
    if (data[i] <= 0xF) Serial.print("0");
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.println("}");
}

void setup() {
  Serial.begin(115200);

  memset(data, 0xFF, tailleMax); // on met le buffer à 0xFF partout (pas nécessaire juste pour montrer, une variable globale sera à 0 la première fois)
  printData();

  strcpy((char *) data, "valeur="); // ici le buffer contient {'v','a','l','e','u','r','=','\0', ....}
  printData();

  uint32_t * ptr = (uint32_t*) (data + strlen((const char*) data)); // on caclule la où mettre les données
  *ptr = valeur; // on met les 4 octets au bon endroit.
  printData();

  uint8_t * ptr1 = ((uint8_t *) ptr) + sizeof(valeur) ; // on cherche la fin
  *ptr1 = 0; // on remet un zéro à la fin
  printData();
}

void loop() {}

ça affiche cela

[sub][color=blue]data = { 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF }
data = { 0x76 0x61 0x6C 0x65 0x75 0x72 0x3D 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF }
data = { 0x76 0x61 0x6C 0x65 0x75 0x72 0x3D 0xEF 0xBE 0xAD 0xDE 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF }
data = { 0x76 0x61 0x6C 0x65 0x75 0x72 0x3D 0xEF 0xBE 0xAD 0xDE 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF }
[/color][/sub]

et en image ça donne ça

explication1.png

explication2.png


mais cela dit il faudrait savoir exactement ce que vous devez envoyer… (attention j’ai écrasé le ‘\0’ de fin de chaîne dans mon exemple, peut-être il faut le conserver pour décoder correctement de l’autre côté)


Ouhaaa!!!! Je suis impressionnée par votre réponse!!!!

mais cela dit il faudrait savoir exactement ce que vous devez envoyer

Je dois envoyer des bytes

Ce qui me pertube, et rien à voir avec votre exemple, c'est pourquoi l'exemple sur lequel je me base, ne fait pas comme ca.

Dans arduino, j'ai téléchargé la librairie pour envoyer les paquets, et je suis parti sur un exemple de la librairie, Pourquoi il fait ainsi

strcpy((char *) mydata,"hello");
strcat((char *) mydata,":");
...

Mais qu'importe, je vais suivre votre exemple pour encapsuler mes valeurs des capteurs à envoyer en bytes.

Merci beaucoup pour ce travail, je vais faire ceci plus tard ou demain dans le train...

Votre librairie envoie peut être les données sous forme de chaîne ASCII

Comme dit plus haut, ce sont toujours des octets (ou des bits même) qui circulent. tout dépend donc du protocole de communication… qu’attendez-vous côté serveur ?

Bonjour J-M-L,
(Je répond à votre dernier message avant même d’avoir étudier les dernieres propositions. Je fais ca de suite après)

Selon la documentation de The Thing Network, les données doivent être envoyées en bytes

To send data back and forth over The Things Network you’ll need to use bytes.

Du côté serveeur, les données envoyées sont visible depuis la console. Mais c’est inlisible:

62613A342E33312C74653A323035382C70723A2D31323830312C616C3A34323430302C68753A333430302C6C753A20373836

Pour rendre ceci lisible, et surtout pour pouvoir m’assurer que les données sont corectement envoyé, dans la console, on peut ajouter du cote qui va traiter les données reçues. J’ai donc ajouté ce code qui ca me permettre d’avoir (et de voir) des noms de variables et des valeurs.

function Decoder(bytes, port) {

  // if (port === 1) decoded.led = bytes[0];

  var str=String.fromCharCode.apply(null,bytes);
  var a = JSON.parse(str);
  var name;
  var value;
  for (var attribute in a) 
  {
    name  = attribute;
    value = a[attribute];
  }
  
  return{
    sensor:name,
    val:value
  };
  
  //var val1=(bytes[0] << 8) | bytes[1];
  //var val2=(bytes[2] << 8) | bytes[3];
  
  //return{
  //  val1:val1 / 100,
  //  val2:val2 / 100
  //}
  
}

Le truc, c’est que j’envoie mes données sous le format json, qui en soit n’est pas une bonne idée, car les { et les " prennent de bits. Vu qu’il faut que le paquet à envoyer, je vais envoyer mes données sous ce format

ba:4.30,te:2075,pr:-12958,al:42400,hu:3380,lu:1009

Je modifierai mon code, dans la console, plus tard, pour qu’il traite les donnée reçues. Mais pour le moment, ceci n’est pas un souci…

Le challenge, ce que je souhaiterais faire comme il le faut, et surtout comprendre, c’est d’empiler les valeurs de mes capteurs, comme indiqué plus haut, dans la variable

uint8_t mydata[64];

C’est le contenu de cette variable qui va être envoyé, si je suis mon exemple
LMIC_setTxData2
Dans l’exemple, lui il se content juste d’envoyer Hello Word:

//Serial.print("ready to send: ");
	  strcpy((char *) mydata,"{\"Hello\":\"World\"}");
LMIC_setTxData2(1, mydata, strlen((char *)mydata), 0);

Moi je dois envoyer des valeurs…

Alors pour cela, j’ai cette boucle (et merci J-M-L) qui vient de l’exercice sur des structures

Je peux très très bien afficher (Serial.println) les résultat sous cette forme

ba:4.30,te:2075,pr:-12958,al:42400,hu:3380,lu:1009

Je modifie

Mon challenge, et mes lacunes me font défaut, je dois empiler des char dans ‘mydata’ et tout doit être des bytes.

J’ai fait ceci, mais je vais modifier ceci en fonction de votre dernières proposition. J’attaque ca maintenant…

sprintln(F("\nValeurs des capteurs actifs"),2);
    sprintln(F("---------------------------------------"),2);
    sprint(F("Nombre de capteurs: "),2);
    sprintln(nbMaxCapteurs,2);
    sprint(F("Nombre de capteurs actifs: "),2);
    sprintln(nbCapteurActif,2);
    
    sprint(F("Valeurs:\t"),2);
    //Serial.print(F("{"));

// EMPILE LES VALEURS MESUREES DE MES CAPTEURS DANS mydata

    for (byte i = 0; i < nbMaxCapteurs; i++) {
      if (mesCapteur[i].actif) { // si le capteur n'est pas actif on n'imprime pas
        
        if(i==0)
        {
          strcpy((char *) mydata,mesCapteur[i].nomDuCapteur);
          strcat((char *) mydata,":");
          strcat((char *) mydata,mesMesures[i][mesCapteur[i].idDuCapteur].valeur);
        }
        else
        {
          strcat((char *) mydata,mesCapteur[i].nomDuCapteur);
          strcat((char *) mydata,":");
          strcat((char *) mydata,mesMesures[i][mesCapteur[i].idDuCapteur].valeur);
        }
        
        if (i < nbCapteurActif)
        {
          strcat((char *) mydata,",");
        }

        Serial.print(F("Taille de mydata: ")); Serial.println(sizeof(mydata));

// AFFICHE ICI LES VALEURS DES CAPTEURS SEPARE PAR DES VIRGULES SAUF POUR LE DERNIER
        sprint(mesCapteur[i].nomDuCapteur,2);
        sprint(F(":"),2);
        sprint(mesMesures[i][mesCapteur[i].idDuCapteur].valeur,2);
        if (i < nbCapteurActif) sprint(F(","),2);
      }
    }
    sprintln(F("\n"),2);

Voilà, j’espère avoir bien répondu à la question :slight_smile:

To send data back and forth over The Things Network you’ll need to use bytes.

Oui on échange toujours des octets… un char, une String, un entier, tout au final est représenté sous forme de bits et d’octets en mémoire

→ ça ne dit rien de l’organisation de ces octets, quel est le protocole ? qui écoute côté serveur et quel format (organisation des octets) est attendu ?

My gateway utilise le proprocole LoRaWAN pour envoyer les donnée

We use a long range and low power radio frequency protocol called LoRaWAN.

(J'ai commencé à travailler sur l'exemple, mais pas encore terminé :) )

Bonjour J-M-L
J’ai essayé de reproduire votre, mais malheureusement, je vole pas très haut :frowning:
Je ne sais pas si vous auriez encore un petit moment pour me metter dans la bonne voie.

Voici ce que j’ai fait…

const uint8_t tailleMax = 64;
uint8_t mydata[tailleMax];

// On met le buffer à zero partout
    // (pas nécessaire juste pour montrer, une variable globale sera à 0 la première fois)
    memset(mydata, 0xFF, tailleMax); 
    // DEBUG
    printData();

/*
Ceci imrpime
mydata = { FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF }
*/

    // Loop la strcture des capteurs
    for (byte i = 0; i < nbMaxCapteurs; i++) {

      if (mesCapteur[i].actif) { // si le capteur n'est pas actif on n'imprime pas
      /*
        // PRINT LES MESURES MESUREES
        sprint(mesCapteur[i].nomDuCapteur,2);
        sprint(F(":"),2);
        sprint(mesMesures[i][mesCapteur[i].idDuCapteur].valeur,2);
        if (i < nbCapteurActif) sprint(F(","),2);
        */

        // --------------------------------------------------------
        // EMPILE LES VALERUS MESUREE DANS mydata POUR
        // LES ENVOYER A TTN 
        
        // Empile le nom du capteur
        // const uint8_t tailleMax = 64; 
        // uint8_t mydata[tailleMax];

        strcpy((char *) mydata, mesCapteur[i].nomDuCapteur); // Ici de toute manière, ca coincera, car au deuxième passe du second capteur, il écrasera la premiere valeur...
        // DEBUG
        printData();
/* Ceci impirme
mydata = { 62 61 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF }
*/
 
     /* ICi i y a une erreu, mais je ne maitrise pas trop les pointeurs...
   // on caclule la où mettre les données suivantes
        uint32_t * ptr = (uint32_t*) (mydata+ strlen((const char*) mydata)); 
        // on met les octets au bon endroit en fonction du contenu de nomDuCapteur.
        *ptr = 0x3A; // Ajoute une les deux point (:)
        // DEBUG
        printData();
/*
Ceci n'imprime rien. Mon code n'imprime plus comme s'il était bloqué...
*/

        // On positionne le point et ajoute les valeur
        uint8_t * ptr1 = ((uint8_t *) ptr) + sizeof(mydata) ; // on cherche la fin
        //*ptr1 = (char *)mesMesures[i][mesCapteur[i].idDuCapteur].valeur; // Ceci ne fnctionne pas et c'est bien ceci quue je dois ajouté, pas 0x40..
        *ptr1 = 0x40; // J'ajoute un @ en bits pour comparer le résultat
        // DEBUG
        printData(); 

        ptr2 = (uint32_t*) (mydata + strlen((const char*) mydata));
        
        if (i < nbCapteurActif)
        {
          *ptr2 = 0x2C; // On ajoute une virgule
        }
        else
        {
          *ptr2 = 0; // On ajoute un 0 pour terminer la chaine
        }
        
        // DEBUG
        printData();
        
       
      }
    }
    sprintln(F("\n"),2);

Pourriez-vous encore me mettre sur la bonne voie?

Merci beaucoup!

Bonsoir vous n'avez pas répondu à ma question:

Comment doivent être structurées les données du côté serveur? Qui va les lire et quel format est attendu?

Lorawan c'est un protocole de communication, ça transporte des octets, mais suivant qui est au bout de la communication et ce qu'il attend, la façon d'agencer les octets sera différente

Dans votre lien ils parlent d'un "Uplink Message - a message from a Device to an Application", ce qui vous faut c'est décrire le message.

Bonsoir,

Ben sincèrement, je ne sais pas trop quoi répondre.
Aussi, je ne me suis pas concentré la dessus à100% (hélas)

Ce que je peux vous dire, c’est que les données sont envoyée et je peux les consultées comme ceci


Aussi, je peux modifier le fomat pour que je puisse rendre la vu plus lisible.

Selon cette vue


Si j’ajoute ce code (ou remplace…)

function Decoder(bytes, port) {

  // if (port === 1) decoded.led = bytes[0];

  var str=String.fromCharCode.apply(null,bytes);
  var a = JSON.parse(str);
  var name;
  var value;
  for (var attribute in a)
  {
    name  = attribute;
    value = a[attribute];
  }
 
  return{
    sensor:name,
    val:value
  };
 
  //var val1=(bytes[0] << 8) | bytes[1];
  //var val2=(bytes[2] << 8) | bytes[3];
 
  //return{
  //  val1:val1 / 100,
  //  val2:val2 / 100
  //}
 
}

je peux visualiser le nom des variables et les valeurs. Je n’ai malheureusement pas de photo-exemple, mais ca fonctionne bien et je peux vérifier ce qui a été envoyé et reçu

Ceci dit, ce code n’est en réalité plus valable, car à ce moment là, j’envoyais les valeurs des capteurs une par une. C’est pourquoi il n’est plus.

Maitenant j’aimerais envoyer tout d’un coup

ba:4.30,te:2075,pr:-12958,al:42400,hu:3380,lu:1009

mais ne bytes :slight_smile:

TTN me permet aussi de stocker pendant une semaine, les valeurs dans une base de donnée, et j’utilise curl pour “rapatrier” ces données sur mon propre serveur/base de donnée/fichier json.

Pour le moment je ne me suis pas encore concentré la dessus. Je me concentre sur comment les envoyer et de vérifier que les données sont bien arrivées dans ma console, comme je l’ai décris plus haut. Dès que j’arriverai à bien les envoyer, j’utiliserai l’option de la base de donnée et curl.

J’espère que malgré tout, j’ai pu répondre à votre question? :slight_smile:

OK donc votre back-end est adaptatif - vous pouvez décrire comment relire les octets envoyés, il vous suffit donc de définir ce que vous voulez envoyer et comment.

si vous avez un tableau statique avec toutes les mesures (avec ces cellules vides pour les capteurs inactifs) vous pouvez ne pas vous ennuyer et envoyer d’un seul coup tout le tableau et côté serveur relire tout cela.

Mais ce n’est pas très efficace si de nombreux capteurs ne sont pas utilisés.

On pourrait essayer de définir une mesure à envoyer et dire qu’un payload c’est une suite de mesures

vous avez peu de capteurs, chaque capteur pourrait être décrit avec une lettre ‘P’ pour pression, ‘T’ pour température etc…

vous avez ensuite une valeur à envoyer, il faut définir si vous envoyez une valeur convertie dans l’unité associée (°Celsius, Pascal, etc) ou si vous envoyez la lecture digitale échantillonnée et vous faites les maths côté serveur.

Comme votre serveur est plus performant faire le calcul côté serveur et envoyer uniquement 2 octets pour les data (échantillonnage jusqu’à 16 bits), 4 octets pour l’heure de la mesure ça devrait déjà être pas mal et ça simplifie la communication.

un payload aurait alors cette tête (octet par octet)

LETTRE DATA1 DATA2 TIME1 TIME2 TIME3 TIME4
LETTRE DATA1 DATA2 TIME1 TIME2 TIME3 TIME4
LETTRE DATA1 DATA2 TIME1 TIME2 TIME3 TIME4
LETTRE DATA1 DATA2 TIME1 TIME2 TIME3 TIME4

et côté serveur en fonction de la lettre reçue, avec un switch/case vous appliquez la bonne formule de calcul.

Voici un exemple de code

struct  sample {
  uint16_t mesure;
  uint32_t chrono;
};

struct sensor {
  char identifier;
  boolean isActive;
};

sensor sensorList[] = {
  {'T', true}, // temperature
  {'P', false}, // Pression, capteur inactif
  {'B', true}, // Battery
};

enum : byte {temperature, pression, batterie}; // on définit des mots clés pour la lisibilité

const byte maxNbSensors = sizeof(sensorList) / sizeof(sensorList[0]);
const byte nbMaxMesures = 5;

sample allMesures[maxNbSensors][nbMaxMesures];

void printHex(uint8_t v)
{
  if (v < 0xF) Serial.print("0");
  Serial.print(v, HEX);
}

void setup() {
  Serial.begin(115200);

  // on en remplit quelques une avec des valeurs reconnaissables

  allMesures[temperature][0].mesure = 0xAA11;
  allMesures[temperature][0].chrono = 0x1122AABBul;
  allMesures[temperature][1].mesure = 0xBB11;
  allMesures[temperature][1].chrono = 0x2233AABBul;
  allMesures[temperature][4].mesure = 0xCC11;
  allMesures[temperature][4].chrono = 0x3344AABBul;

  allMesures[batterie][0].mesure = 0xDD33;
  allMesures[batterie][0].chrono = 0x4455AABBul;
  allMesures[batterie][1].mesure = 0xEE33;
  allMesures[batterie][1].chrono = 0x5566AABBul;
  allMesures[batterie][4].mesure = 0xFF33;
  allMesures[batterie][4].chrono = 0x6677AABBul;


  // si vous êtes "lazy" vous pouvez directement envoyer allMesures comme payload.

  // sinon on bâtit un message avec les infos pertinentes des capteurs actifs
  // on va calculer la taille du buffer data à envoyer (le payload)
  byte nbActiveSensors = 0;
  for (byte i = 0; i < maxNbSensors; i++)
    if (sensorList[i].isActive) nbActiveSensors++;

  // un buffer assez grand pour toutes nos données
  uint8_t payload[nbActiveSensors * nbMaxMesures * (sizeof(char) + sizeof(sample))];

  uint8_t * ptr = payload;
  for (byte s = 0; s < maxNbSensors; s++) {

    if (sensorList[s].isActive) {
      for (byte m = 0; m < nbMaxMesures; m++) {
        *ptr = sensorList[s].identifier; // on écrit le char
        ptr += sizeof(char); // on passe à l'octet suivant

        * ((uint16_t *) ptr) = allMesures[s][m].mesure; // on est obligé de faire un cast pour bien copier tous les octets
        ptr += sizeof(uint16_t); // on avance du bon nombre d'octets

        * ((uint32_t *) ptr) = allMesures[s][m].chrono; // on est obligé de faire un cast pour bien copier tous les octets
        ptr += sizeof(uint32_t); // on avance du bon nombre d'octets
      }
    }
  }

  // ici notre buffer est complet, on l'affiche
  Serial.println("---- le payload ----");
  // en mémoire ce serait cette suite d'octets
  for (uint16_t index = 0; index < sizeof(payload); index ++) {
    Serial.print("0x");
    printHex(payload[index]);
    Serial.println("");
  }

  Serial.println("----- le payload avec structure ----");
  for (uint16_t index = 0; index < sizeof(payload); index += sizeof(char) + sizeof(uint16_t) + sizeof(uint32_t)) {
    Serial.print("{ 0x");
    printHex(payload[index]);
    Serial.print(" (lettre '");
    Serial.print((char) payload[index]);
    Serial.print("'), 0x");
    printHex(payload[index + 1]);
    printHex(payload[index + 2]);
    Serial.print(", 0x");
    printHex(payload[index + 3]);
    printHex(payload[index + 4]);
    printHex(payload[index + 5]);
    printHex(payload[index + 6]);
    Serial.println("}");
  }
}

void loop() {}

si vous l’exécutez vous verrez ça sur la sortie

[sub][color=blue]---- le payload ----
0x54
0x11
0xAA
0xBB
0xAA
0x22
0x11
0x54
0x11
0xBB
0xBB
0xAA
0x33
0x22
0x54
0x00
0x00
0x00
0x00
0x00
0x00
0x54
0x00
0x00
0x00
0x00
0x00
0x00
0x54
0x11
0xCC
0xBB
0xAA
0x44
0x33
0x42
0x33
0xDD
0xBB
0xAA
0x55
0x44
0x42
0x33
0xEE
0xBB
0xAA
0x66
0x55
0x42
0x00
0x00
0x00
0x00
0x00
0x00
0x42
0x00
0x00
0x00
0x00
0x00
0x00
0x42
0x33
0xFF
0xBB
0xAA
0x77
0x66
----- le payload avec structure ----
{ 0x54 (lettre 'T'), 0x11AA, 0xBBAA2211}
{ 0x54 (lettre 'T'), 0x11BB, 0xBBAA3322}
{ 0x54 (lettre 'T'), 0x0000, 0x00000000}
{ 0x54 (lettre 'T'), 0x0000, 0x00000000}
{ 0x54 (lettre 'T'), 0x11CC, 0xBBAA4433}
{ 0x42 (lettre 'B'), 0x33DD, 0xBBAA5544}
{ 0x42 (lettre 'B'), 0x33EE, 0xBBAA6655}
{ 0x42 (lettre 'B'), 0x0000, 0x00000000}
{ 0x42 (lettre 'B'), 0x0000, 0x00000000}
{ 0x42 (lettre 'B'), 0x33FF, 0xBBAA7766}
[/color][/sub]

on voit dans l’impression que l’architecture est little endian, les octets de poids faible sont écrits en premier en mémoire. Il faudra en tenir compte du côté de la lecture côté serveur.

ça vous aide?

Bonjour,

Vous êtes extra! Je ne pourrai pas rearder ceci cet après-midi, mais je le ferai aussi tot rentré ce soir.
Dans l’alternative j’ai trouvé une solution mais je n’ai pas pu encore la tester et la publier.
Mais j’aimerais “honorer” vos propositions et les appliquer.

Je regarderai ceci ce soir.

Milles mercis et toute bonne journée

Bonjour,
Je suis dans le train et je viens d’essayer la solution que j’ai faite ce matin.
En fait, je pense que je me SUIS COMPLIQUER la vie.

Au lieux d’empiler en byte, j’empile en char et dès que tous mes variable sont dans mon buffer, je converti.

Ca fonctionne

    sprintln(F("\nValeurs des capteurs actifs"),2);
    sprintln(F("---------------------------------------"),2);
    sprint(F("Nombre de capteurs: "),2);
    sprintln(nbMaxCapteurs,2);
    sprint(F("Nombre de capteurs actifs: "),2);
    sprintln(nbCapteurActif,2);
    
    sprint(F("Valeurs:\t"),2);

    // Clean char_buffer
    memset(char_buffer,'\0',BUFFERSIZE);
    // Loop la strcture des capteurs
    for (byte i = 0; i < nbMaxCapteurs; i++) {
      if (mesCapteur[i].actif) { // si le capteur n'est pas actif on n'imprime pas
      
        // PRINT LES MESURES MESUREES
        sprint(mesCapteur[i].nomDuCapteur,2);
        sprint(F(":"),2);
        sprint(mesMesures[i][mesCapteur[i].idDuCapteur].valeur,2);
        if (i < nbCapteurActif+1) sprint(F(","),2);
        

        // --------------------------------------------------------
        // EMPILE LES VALERUS MESUREE DANS mydata POUR LES ENVOYER A TTN 

        if(i==0)
        {
          //strcpy(char_buffer, mesCapteur[i].nomDuCapteur);
          // Ajoute 0 à la fin du text
          char_buffer[strlen(mesCapteur[i].nomDuCapteur)]='\0';
          //strcat(char_buffer, ":");
          strcpy(char_buffer, mesMesures[i][mesCapteur[i].idDuCapteur].valeur);
        }
        else
        {
          //strcat(char_buffer, mesCapteur[i].nomDuCapteur);
          //strcat(char_buffer, ":");
          strcat(char_buffer, mesMesures[i][mesCapteur[i].idDuCapteur].valeur);
        }
        
        if (i < nbCapteurActif+1)
        {
          strcat(char_buffer, ",");
        }
        char_buffer[strlen(char_buffer)]='\0';


      }
    }
    sprintln(F("\n"),2);
        
  
    // JE CONVERTI EN BYTE DANS MYDATA
    memset(mydata, 0xFF, tailleMax); 
    // DEBUG
    printData();
    
    strcpy((char *) mydata, char_buffer);
    printData(); 
     

// J ENVOIE
    sprint(F("\nOpmode check: "),2);
    LMIC_setTxData2(1, mydata, strlen((char *)mydata), 0);

Le premier printData affiche

mydata = { FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF }

Et le deuxieme

mydata = { 34 2E 33 31 2C 32 32 34 37 2C 2D 31 34 33 31 33 2C 34 32 34 30 30 2C 33 34 35 30 2C 31 34 30 31 34 35 2C 2C 2C 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF }

Par contre, j’ai aussi remarqué que mydata est trop petit pour tout avoir, dans le cas ou j’active tous mes capteur. Donc j’ai supprimer le nom et les :. Il faudra encore que je réfléchisse comment mieux faire, pour la leture des données reçues au serveur.

Par contre, je ne vais pas rester là, ce soir j’appliquerai votre exemple

  1. par respect à votre travail et aide
  2. pour mieux me familiarisé avec les pointeurs (c’est sujet que je ne maitrise vraiment pas)
  3. et surement parce que c’est meiux :slight_smile:

Aussi la fonction printData m’a bien aider de voir le contenu de mydata et réaliser que j’avais trop de caracteres pour mydata.

… (je dois sortir du train…)
Bon finaprès-midi

// JE CONVERTI EN BYTE DANS MYDATA strcpy((char *) mydata, char_buffer);

ça ne sert à rien.

Vous avez déjà des BYTEs dans char_buffer, il n'y a rien à convertir... vous dupliquez simplement les données avec strcpy (et je n'ai pas regardé si vous aviez bien mis un '\0' à la fin)

je vous laisse lire mon exemple

Bonjour, Je pense que j'arrive au bout. Le problème qu'il y avec ton exemple c'st vous utilisez des byte pour les valeur.

Par ecemple ici:

allMesures[batterie][0].mesure = 0xDD33;

Vous définissez une valeur et ici vous l'exploiter

* ((uint16_t *) ptr) = allMesures[s][m].mesure;
// Dans mon casallMesures[s][m].mesure est un char

Quand je compile, j'ai ce message

invalid conversion from 'char*' to 'uint16_t {aka short unsigned int}' [-fpermissive]

Le problème est que ce vous utilisez des bit, dans votre exemple. Mes fonctions prendre les valeurs des capteurs me retourne un char

void get_luminosity(char * luminosity)

{

[...code...]
dtostrf(lux,4,0,luminosity); // lux est un double
}

Donc je me retrouve toujours bloqué avec ce passage....

Pourquoi ne pas retourner lux ou mieux encore la lecture de l'échantillon ?

Dans votre cas vous n'avez pas besoin d'une représentation ASCII des données

Ben chais pas. Je me prends le chou :) La réflection est bonne Toute mes fonctions mesures de valerus en double.

C'est moi qui ai ajouté un bout de code pour 1. garder deux decimal 2. retoruner le tout en char

dtostrf(lux, 4, 0, luminosity);

Pour répondre à votre question lux est un double et dans votre exemple vous utiliser des uint8_t et unit16_t

Ce que j'essaye de faire maintenant, c'est que ma fonction me retourne la valeur hexa. Par exemple si lux est egal à 24.65, ma fonction me retorunerai un uint8_t avec la valeur de

32 34 2e 36 35

ou encore mieux, pour supprimer la virgule, je multiplie par cent et me fonction me retorunerai

32 34 36 35

Je ne sais pas si je pense juste, mais ainsi, je pourrai reprendre votre exemple

* ((uint16_t *) ptr) = allMesures[s][m].mesure

Est-ce que je ferai bien de me préoccuper sur le type de la valeur retournée? Au fond, "tout raisonne" en bit, finallement... :o :o