Pourquoi ma conversion double => byte[] => double ne peut-elle pas fonctionner

Bonjour,

Afin d'envoyer des valeur via un module sans fil (NRF24L01 ou module LoRa) je dois rassembler et convertir toutes les variables à envoyer dans un tableau de bytes. J'ai trouvé beaucoup de solutions différentes mais aucune suffisamment simple pour mon petit niveau. J'avais imaginé la méthode suivante facilement lisible:

Il me semble que chaque variable est quoi qu'il arrive une suites de bits dont la longueur peut varier en fonction du type de la variable (et du type de processeur) pour un double sur mon arduino mini pro:

Avant envoi:

TabDeBytes[0]=(MonDouble & 0b1111000000000000) >> 12;
TabDeBytes[1]=(MonDouble & 0b0000111100000000) >> 8;
TabDeBytes[2]=(MonDouble & 0b0000000011110000) >> 4;
TabDeBytes[3]=(MonDouble & 0b0000000000001111);

Après réception:

MonDouble=TabDeBytes[0] << 12 | TabDeBytes[1] << 8 | TabDeBytes[2] << 4 | TabDeBytes[3];

Le compilateur n'en veux pas :

error: invalid operands of types 'float' and 'unsigned int' to binary 'operator&'

Je suppose qu'il faut par un moyen lui indiquer qu'on veux utiliser la représentation binaire de la variable?

L'avantage de cette méthode, c'est que comme j'ai certaines variable qui ne seront utilisées qu'en partie, par exemple la mesure de la pression atmosphérique m'est retournée par la librairie en Pa sous forme d'un double. La modification de la pression atmosphérique ne varie que sur une petite plage par rapport à la valeur complète retournée. Dans mon exemple je peux sans problème retirer 87 000 Pa avant envoi et du coup je n'ai plus besoin des 4 octets et peux me contenter de 3. Côté réception j'ai juste à ajouter à nouveau ces 87 000 Pa après avoir recombiné ma variable. Hop j'ai gagné un byte sur mon envoi / temps d’exécution et donc sur la batterie.

-Edit-
En continuant de chercher je me rend compte que les pointeurs sont sûrement une solution?

Avant envoi:

TabDeBytes[0]=valeur retournée par Pointeur positionné sur 1er octet de MonDouble;
TabDeBytes[1]=valeur retournée par Pointeur positionné sur 2eme octet de MonDouble;
TabDeBytes[2]=valeur retournée par Pointeur positionné sur 3eme octet de MonDouble;
TabDeBytes[3]=valeur retournée par Pointeur positionné sur 4eme octet de MonDouble;

Après réception:

valeur par Pointeur positionné sur 1er octet de MonDouble=TabDeBytes[0];
valeur par Pointeur positionné sur 2eme octet de MonDouble=TabDeBytes[1]
valeur par Pointeur positionné sur 3eme octet de MonDouble=TabDeBytes[2]
valeur par Pointeur positionné sur 4eme octet de MonDouble=TabDeBytes[3]

Est-ce la bonne méthode? Est-ce dangereux (je ne contrôle pas la taille de MonDouble je peux écraser d'autres données mémoires?) Comment faire si je passe mon programme d'un atmega 328p à un ESP8266 ou un ESP32, la taille d'un double n'est peut-être pas la même?

Bonjour,

Ta méthode ne fonctionne qu'avec des long ou unsigned long, mais pas avec des flot ou double (voir la structure des float).
Mais tu n'en as pas besoin. Il suffit de caster l'adresse de ta variable en byte *
(byte *)&MonDouble

AVR est little endian (octet de poids faible en premier). ESP8266 peut faire les deux, mais la chaîne de compilation choisit little endian.
Sur AVR un double = 32 bits.
Sur ESP un double = 64 bits.
Sur ESP un float = 32 bits.

Donc transmets ton double naturellement, avec une taille de 4, sans le couper en morceaux et récupère-le côté ESP dans un float.

Merci pour vos réponses :slight_smile:

kamill:
Ta méthode ne fonctionne qu'avec des long ou unsigned long, mais pas avec des flot ou double (voir la structure des float).

La première ou la deuxième (celle de l'edit)? Toutes les variables sont bien des suites de bits dans la mémoire? donc pourquoi la structure de la variable peut-elle avoir une incidence?

kamill:
Mais tu n'en as pas besoin. Il suffit de caster l'adresse de ta variable en byte *
(byte *)&MonDouble

Dans mon cas je vais envoyer plein de variables en une fois avec un tableau de bytes.
Est-ce que je peux faire par exemple:

TabDeBytes[0] = (byte*)&MonDouble1
TabDeBytes[4] = (byte*)&MonDouble2
TabDeBytes[8] = (byte*)&MonFloat1
TabDeBytes[12] = (byte*)&MonFloat2
TabDeBytes[16] = (byte*)&MonInt1
TabDeBytes[18] = (byte*)&MonInt2

Et comment économiser les Bytes inutiles (exemple de la pression dans mon post initial)
Je dois passer par un tableau intermédiaire?

TabDeBytes[0] = (byte*)&MonDouble1

Tampon = (byte*)&MonDouble2
TabDeBytes[4]=Tampon[0]
TabDeBytes[5]=Tampon[1]
TabDeBytes[6]=Tampon[2]

TabDeBytes[7] = (byte*)&MonFloat1
TabDeBytes[11] = (byte*)&MonFloat2
TabDeBytes[15] = (byte*)&MonInt1
TabDeBytes[17] = (byte*)&MonInt2

Et comment recombiner (re-caster?) dans l'autre sens?

hbachetti:
AVR est little endian (octet de poids faible en premier). ESP8266 peut faire les deux, mais la chaîne de compilation choisit little endian.

J'avoue que j'essaye dans un sens et si ça ne marche pas j'inverse :-[

hbachetti:
Sur AVR un double = 32 bits.
Sur ESP un double = 64 bits.
Sur ESP un float = 32 bits.

S'aurait été plus simple si les tailles étaient les mêmes... Dans les utilisations prévues:

  • Programme 1 "mesure" [Arduino mini Pro] ou [ESP8266] ou [ESP32] -> Émet vers -> Programme 2 "réception/émission" [ESP8266] ou [ESP32]
  • Programme 3 "mesure/réception" [Arduino Nano] -> Émet vers -> Programme 2 "réception/émission" [ESP8266] ou [ESP32]
  • Programme 3 "mesure/réception" [Arduino Nano] <- Reçois de <- Programme 2 "réception/émission" [ESP8266] ou [ESP32]
  • Programme 4 "réception/mesure" [ESP32] <- Reçois de <- Programme 1 "mesure" [Arduino mini Pro] ou [ESP8266] ou [ESP32]

Avec en plus l'objectif qu'un même programme soit adapté pour les différents processeurs là où il y a un "ou"... Bonne migraine en perspective! :cry:
Peut-être en gérant l'exemple de kamill avec des "size_off" je pourrais rendre ça automatiquement adapté à chaque processeur?

hbachetti:
Donc transmets ton double naturellement, avec une taille de 4, sans le couper en morceaux et récupère-le côté ESP dans un float.

Comment? Et comment optimiser les Bytes inutiles (exemple de la pression dans mon post initial)

Après moult essais voici ce qui compile enfin:

byte BytesDepuisFloat[sizeof(MaVariableFloat)];
*((float*)BytesDepuisFloat)=MaVariableFloat;

TabDeBytes[5]=BytesDepuisFloat[0];
TabDeBytes[6]=BytesDepuisFloat[1];
TabDeBytes[7]=BytesDepuisFloat[2];


byte BytesDepuisDouble[sizeof(MaVariableDouble)];
*((double*)BytesDepuisDouble)=MaVariableDouble;

TabDeBytes[8]=BytesDepuisDouble[0];
TabDeBytes[9]=BytesDepuisDouble[1];
TabDeBytes[10]=BytesDepuisDouble[2];

Pas sûr que ce soit la meilleure méthode ni que ça fonctionne, je vais créer la partie réceptrice pour voir.
Mais gros inconvénient: je ne comprends rien à ce qui se passe. :confused:

Pas moyen de juste récupérer les bits par morceaux de 8 des variables et de les ré-assembler derrière?

Tu les mets dans une structure. Tu envoies et reçois la structure entière et ça se fait tout seul.
Il faut bien sur que les double aient la même taille sur les deux systèmes, sinon il faut utiliser des float.

kamill:
Tu les mets dans une structure. Tu envoies et reçois la structure entière et ça se fait tout seul.
Il faut bien sur que les double aient la même taille sur les deux systèmes, sinon il faut utiliser des float.

Je me documente et cherche depuis l'ouverture de mon post, voilà où j'en suis:

   //METHODE 0  : avec les décalages ! NE FONCTIONNE PAS
       /*TableauBytes[5+0]=(MonDouble & 0b1111000000000000) >> 12;
       TableauBytes[5+1]=(MonDouble & 0b0000111100000000) >> 8;
       TableauBytes[5+2]=(MonDouble & 0b0000000011110000) >> 4;
       TableauBytes[5+3]=(MonDouble & 0b0000000000001111);*/
   //*****
   //METHODE 1  : avec les changement de type 1/2 ! NON TESTE + Voir si possible economiser Bytes inutiles
       float MonFloat=97667.31;
       double MonDouble=97667.31;
       int MonInt=35001;
       unsigned int MonUInt=65001;

       TableauBytes[5+4] = (byte*)&MonFloat;
       TableauBytes[5+8] = (byte*)&MonDouble;
       TableauBytes[5+12] = (byte*)&MonInt;
       TableauBytes[5+14] = (byte*)&MonUInt;
       //data_1[Derdata_1+7]=BytesDepuisFloat[3];
   //*****
   //METHODE 2  : avec les changement de type 2/2 ! ERREUR COMPILATION: invalid conversion from 'byte* {aka unsigned char*}' to 'byte {aka unsigned char}' [-fpermissive]
       //float MonFloat=97667.31;
       //double MonDouble=97667.31;
       //int MonInt=35001;
       //unsigned int MonUInt=65001;

       byte BytesDepuisFloat[sizeof(MonFloat)];
       byte BytesDepuisDouble[sizeof(MonDouble)];
       byte BytesDepuisInt[sizeof(MonInt)];
       byte BytesDepuisUInt[sizeof(MonUInt)];

       *((float*)BytesDepuisFloat)=MonFloat;
       *((double*)BytesDepuisDouble)=MonDouble;
       *((int*)BytesDepuisInt)=MonInt;
       *((unsigned int*)BytesDepuisUInt)=MonUInt;

       TableauBytes[5+16]=BytesDepuisFloat[0];
       TableauBytes[5+17]=BytesDepuisFloat[1];
       TableauBytes[5+18]=BytesDepuisFloat[2];
       //TableauBytes[Derdata_1+19]=BytesDepuisFloat[3];

       TableauBytes[5+20]=BytesDepuisDouble[0];
       TableauBytes[5+21]=BytesDepuisDouble[1];
       TableauBytes[5+22]=BytesDepuisDouble[2];
       //TableauBytes[Derdata_1+23]=BytesDepuisDouble[3];

       TableauBytes[5+24]=BytesDepuisInt[0];
       TableauBytes[5+25]=BytesDepuisInt[1];

       TableauBytes[5+26]=BytesDepuisUInt[0];
       TableauBytes[5+27]=BytesDepuisUInt[1];

   //*****
   //METHODE 3  : avec les unions

       //Float
       typedef union {
                       float f;                // Assigning fVal.f will also populate fVal.bytes;
                       char b[sizeof(f)]; // Both fVal.f and fVal.bshare the same 4 bytes of memory.
                       } TypeUnionFloatBytes;

       TypeUnionFloatBytes MonFloatUnion;

       MonFloatUnion.f = 97667.01;
   
       Serial.print("le float avant envoi");
       Serial.println(String(MonFloatUnion.f,8));

       TableauBytes[5+28]=MonFloatUnion.b[0];
       TableauBytes[5+29]=MonFloatUnion.b[1];
       TableauBytes[5+30]=MonFloatUnion.b[2];
       TableauBytes[5+31]=MonFloatUnion.b[3];


       //Double -PROBLEME SI RECEPTION SUR ESP32
       typedef union {
                       double d;                // Assigning fVal.f will also populate fVal.bytes;
                       char b[sizeof(d)]; // Both fVal.f and fVal.bshare the same 4 bytes of memory.
                       } TypeUnionDoubleBytes;

       TypeUnionDoubleBytes MonDoubleUnion;

       MonDoubleUnion.d = 97667.31;
   
       Serial.print("le Double dvant envoi");
       Serial.println(String(MonDoubleUnion.d,8));

       TableauBytes[5+32]=MonDoubleUnion.b[0];
       TableauBytes[5+33]=MonDoubleUnion.b[1];
       TableauBytes[5+34]=MonDoubleUnion.b[2];
       TableauBytes[5+35]=MonDoubleUnion.b[3];

           //test recompo avec donnees envoyees
           MonDoubleUnion.b[0]=TableauBytes[5+32];
           MonDoubleUnion.b[1]=TableauBytes[5+33];
           MonDoubleUnion.b[2]=TableauBytes[5+34];
           MonDoubleUnion.b[3]=TableauBytes[5+35];

           Serial.print("le Double envoye");
           Serial.println(String(MonDoubleUnion.d,8));
   //*****
   //METHODE 4  : avec divisions
       //Float
       //float MonFloat=97667.31;

       TableauBytes[5+36]=(MonFloat*100UL)/(256UL*256UL);
       TableauBytes[5+37]=((MonFloat*100UL)-(TableauBytes[5+36]*256UL*256UL))/256UL;
       TableauBytes[5+38]=((MonFloat*100UL)-(TableauBytes[5+36]*256UL*256UL)-(TableauBytes[5+37]*256UL));
   //*****

Pour l'instant la seule méthode que je comprends à 100% et que je suis arrivé à faire fonctionner c'est la n°4 ( en plus je l'ai faite tout seul :slight_smile: )
Par contre je perds parfois xxxx.01 de précision mais si j'ai compris que le nombre de chiffres significatifs est limité en virgule flottante je ne comprends pas trop pourquoi j'obtiens cela.
Après je ne sais pas ce que ça vaut par rapport aux autres en terme de temps d’exécution.
Mais au moins pas de problème par rapport aux différentes longueurs des variables des différents processeurs.

La numero 3 fonctionne aussi sauf que je suis obligé de stocker le double dans un float coté réception avec un ESP32. Merci d'ailleurs hbachetti sans ton explication sur les écarts de tailles de variables entres les processeurs je n'aurais pas compris qu'il pourrait y avoir un soucis ici

Tu te fais vraiment des nœuds au cerveau pour quelque-chose de très simple.
Tu utilises une structure contenant toutes les valeurs que tu veux envoyer, tu envoies la structure entière et tu utilises la même structure pour la réception.

  struct SMesFloat
  {
    float float1;
    float float2;
    float float3;
  } mesFloat;

  mesFloat.float1=1.23;
  mesFloat.float2=5.64;
  mesFloat.float3=759.02;
  
  radio.write((byte *)&mesFloat, sizeof mesFloat);

ROUGEXIII:
par exemple la mesure de la pression atmosphérique m'est retournée par la librairie en Pa sous forme d'un double.

Ca c'est n'importe quoi : la pression mesurée avec 16 chiffres significatifs ! Ca n'a aucun sens.
Il faut se dépêcher de tronquer cette valeur dans un float (qui lui-même est déjà bien trop précis par rapport à la précision du capteur).

kamill:
Tu utilises une structure contenant toutes les valeurs que tu veux envoyer, tu envoies la structure entière et tu utilises la même structure pour la réception.

Merci pour ton aide précieuse,
L'exercice que je souhaite faire c'est justement d'adapter le tableau d'octets envoyé aux stricts octets nécessaires, pas un de plus! Au lieu d'avoir une taille de tableau d'octets fixe avec le maximum de cas je passe une taille d'une longueur calculée après un scan des capteurs dans le setup() (d'où l'autre post où tu m'as aidé :wink: )
Un des premiers octets sert à dire à quoi correspondent les suivants pour qu'à la réception le découpage soit possible.

Est ce que la structure que tu proposes pourrait être déclarée après le scan pour l'émetteur, et après la réception pour le récepteur ?

Autre enjeu (toujours dans l'optique de l'économie d'octets) c'est que par exemple pour certaines variables (ex: la pression) je peux sans problème transmettre la variable dans 3 octets sans rien perdre du fait de l'amplitude maximale réelle mesurée.

Est ce que cette structure pourrait également permettre de gagner les octets inutilisés?

biggil:
Ca c'est n'importe quoi : la pression mesurée avec 16 chiffres significatifs ! Ca n'a aucun sens.
Il faut se dépêcher de tronquer cette valeur dans un float (qui lui-même est déjà bien trop précis par rapport à la précision du capteur).

Sur atmega328p un double fait la même taille qu'un float (4 octets):

Double precision floating point number. On the Uno and other ATMEGA based boards, this occupies 4 bytes. That is, the double implementation is exactly the same as the float, with no gain in precision.

Par contre, comme le conseillait hbachetti ce n'est plus le cas sur un ESP, il vaut donc mieux suivre ton conseil de le passer en float.
J'avoue que les librairies toutes faites (adafruit / sparkfun / ...) ont souvent pas mal d'inconvénients (Par exemple certaines ont des delay() ce qui n'est pas du tout optimisé, ou par exemple attendent la l'adresse I2C dès la déclaration au lieu de la laisser configurer à la connexion) et le choix d'un stockage en virgule flottante au lieu de virgule fixe me surprend sur ce type de mesures (VEML6070/75, DHT22, BMP/E280, TSL2561, CCS811 ...)

Npn, la définition de la structure est fixe et ne peut donc s'adapter dynamiquement.
Si le nombre et le type de capteurs peut varier, il faut que tu prévois dans ta transmission non seulement les valeurs, mais aussi leur type et leur nombre.

Correction + ajout resultats:

float MonFloat = 97667.41-87000;
    float MonNFloat = -67.41;
    double MonDouble = 97667.41-87000;
    double MonNDouble = -67.41;
    int MonInt = 32701;
    int MonNInt = -121;
    unsigned int MonUInt = 65001;

Methode 2

//METHODE 2  : avec les changement de type 2/2 !Voir si possible economiser Bytes inutiles
        //float MonFloat = 97667.31-87000;
        //float MonNFloat = -67.31;
        //double MonDouble = 97667.31-87000;
        //double MonNDouble = -67.31;
        //int MonInt = 35001;
        //int MonNInt = -121;
        //unsigned int MonUInt = 65001;
        
        Serial.println("\nMETHODE 2 : avec les changement de type 2/2\n");
     


        //Float
        byte BytesDepuisFloat[sizeof(MonFloat)];
        *((float*)BytesDepuisFloat)=MonFloat;

        Serial.print("le Float avant envoi:\t");
        Serial.println(MonFloat);

        TableauBytes[5+26]=0;//BytesDepuisFloat[0];
        TableauBytes[5+27]=BytesDepuisFloat[1];
        TableauBytes[5+28]=BytesDepuisFloat[2];
        TableauBytes[5+29]=BytesDepuisFloat[3];

            //test recompo avec donnees envoyees
            byte TabTamponBytesFloat[sizeof(MonFloat)];

            TabTamponBytesFloat[0]=0;//TableauBytes[5+26];
            TabTamponBytesFloat[1]=TableauBytes[5+27];
            TabTamponBytesFloat[2]=TableauBytes[5+28];
            TabTamponBytesFloat[3]=TableauBytes[5+29];

            Serial.print("le Float apres envoi:\t");
            Serial.println(*((float*)TabTamponBytesFloat));


        //Float par long
        byte BytesDepuisLong[sizeof(MonFloat*100L)];
        *((long*)BytesDepuisLong)=MonFloat*100L;

        Serial.print("le Float par long avant envoi:\t");
        Serial.println(MonFloat*100L/100.0);

        TableauBytes[5+26]=0;//BytesDepuisLong[0];
        TableauBytes[5+27]=BytesDepuisLong[1];
        TableauBytes[5+28]=BytesDepuisLong[2];
        TableauBytes[5+29]=BytesDepuisLong[3];

            //test recompo avec donnees envoyees
            byte TabTamponBytesLong[sizeof(MonFloat*100L)];

            TabTamponBytesLong[0]=0;//TableauBytes[5+26];
            TabTamponBytesLong[1]=TableauBytes[5+27];
            TabTamponBytesLong[2]=TableauBytes[5+28];
            TabTamponBytesLong[3]=TableauBytes[5+29];

            Serial.print("le Float par long apres envoi:\t");
            Serial.println(*((long*)TabTamponBytesLong)/100.0);


        //Nfloat
        byte BytesDepuisNFloat[sizeof(MonNFloat)];
        *((float*)BytesDepuisNFloat)=MonNFloat;
        
        Serial.print("le NFloat avant envoi:\t");
        Serial.println(MonNFloat);

        TableauBytes[5+30]=0;//BytesDepuisNFloat[0];
        TableauBytes[5+31]=BytesDepuisNFloat[1];
        TableauBytes[5+32]=BytesDepuisNFloat[2];
        TableauBytes[5+33]=BytesDepuisNFloat[3];

            //test recompo avec donnees envoyees
            byte TabTamponBytesNFloat[sizeof(MonNFloat)];

            TabTamponBytesNFloat[0]=0;//TableauBytes[5+30];
            TabTamponBytesNFloat[1]=TableauBytes[5+31];
            TabTamponBytesNFloat[2]=TableauBytes[5+32];
            TabTamponBytesNFloat[3]=TableauBytes[5+33];

            Serial.print("le NFloat apres envoi:\t");
            Serial.println(*((float*)TabTamponBytesNFloat));


        //Double
        byte BytesDepuisDouble[sizeof(MonDouble)];
        *((double*)BytesDepuisDouble)=MonDouble;

        Serial.print("le Double avant envoi:\t");
        Serial.println(MonDouble);

        TableauBytes[5+34]=0;//BytesDepuisDouble[0];
        TableauBytes[5+35]=BytesDepuisDouble[1];
        TableauBytes[5+36]=BytesDepuisDouble[2];
        TableauBytes[5+37]=BytesDepuisDouble[3];

            //test recompo avec donnees envoyees
            byte TabTamponBytesDouble[sizeof(MonDouble)];

            TabTamponBytesDouble[0]=0;//TableauBytes[5+34];
            TabTamponBytesDouble[1]=TableauBytes[5+35];
            TabTamponBytesDouble[2]=TableauBytes[5+36];
            TabTamponBytesDouble[3]=TableauBytes[5+37];

            Serial.print("le Double apres envoi:\t");
            Serial.println(*((double*)TabTamponBytesDouble));


        //NDouble
        byte BytesDepuisNDouble[sizeof(MonNDouble)];
        *((double*)BytesDepuisNDouble)=MonNDouble;

        Serial.print("le NDouble avant envoi:\t");
        Serial.println(MonNDouble);

        TableauBytes[5+38]=0;//BytesDepuisNDouble[0];
        TableauBytes[5+39]=BytesDepuisNDouble[1];
        TableauBytes[5+40]=BytesDepuisNDouble[2];
        TableauBytes[5+41]=BytesDepuisNDouble[3];

            //test recompo avec donnees envoyees
            byte TabTamponBytesNDouble[sizeof(MonNDouble)];

            TabTamponBytesNDouble[0]=0;//TableauBytes[5+38];
            TabTamponBytesNDouble[1]=TableauBytes[5+39];
            TabTamponBytesNDouble[2]=TableauBytes[5+40];
            TabTamponBytesNDouble[3]=TableauBytes[5+41];

            Serial.print("le NDouble apres envoi:\t");
            Serial.println(*((double*)TabTamponBytesNDouble));


        //Int
        byte BytesDepuisInt[sizeof(MonInt)];
        *((int*)BytesDepuisInt)=MonInt;

        Serial.print("le Int avant envoi:\t");
        Serial.println(MonInt);

        TableauBytes[5+42]=BytesDepuisInt[0];
        TableauBytes[5+43]=BytesDepuisInt[1];

            //test recompo avec donnees envoyees
            byte TabTamponBytesInt[sizeof(MonInt)];

            TabTamponBytesInt[0]=TableauBytes[5+42];
            TabTamponBytesInt[1]=TableauBytes[5+43];

            Serial.print("le Int apres envoi:\t");
            Serial.println(*((int*)TabTamponBytesInt));


        //NInt
        byte BytesDepuisNInt[sizeof(MonNInt)];
        *((int*)BytesDepuisNInt)=MonNInt;
        
        Serial.print("le NInt avant envoi:\t");
        Serial.println(MonNInt);

        TableauBytes[5+44]=BytesDepuisNInt[0];
        TableauBytes[5+45]=BytesDepuisNInt[1];

            //test recompo avec donnees envoyees
            byte TabTamponBytesNInt[sizeof(MonNInt)];

            TabTamponBytesNInt[0]=TableauBytes[5+44];
            TabTamponBytesNInt[1]=TableauBytes[5+45];

            Serial.print("le NInt apres envoi:\t");
            Serial.println(*((int*)TabTamponBytesNInt));



        //UInt
        byte BytesDepuisUInt[sizeof(MonUInt)];
        *((unsigned int*)BytesDepuisUInt)=MonUInt;
        
        Serial.print("le UInt avant envoi:\t");
        Serial.println(MonUInt);

        TableauBytes[5+46]=BytesDepuisUInt[0];
        TableauBytes[5+47]=BytesDepuisUInt[1];

            //test recompo avec donnees envoyees
            byte TabTamponBytesUInt[sizeof(MonUInt)];

            TabTamponBytesUInt[0]=TableauBytes[5+46];
            TabTamponBytesUInt[1]=TableauBytes[5+47];

            Serial.print("le UInt apres envoi:\t");
            Serial.println(*((unsigned int*)TabTamponBytesUInt));

Resultat:

METHODE 2 : avec les changement de type 2/2

le Float avant envoi:	10667.41
le Float apres envoi:	10667.25
le Float par long avant envoi:	10667.41
le Float par long apres envoi:	10664.96
le NFloat avant envoi:	-67.41
le NFloat apres envoi:	-67.41
le Double avant envoi:	10667.41
le Double apres envoi:	10667.25
le NDouble avant envoi:	-67.41
le NDouble apres envoi:	-67.41
le Int avant envoi:	32701
le Int apres envoi:	32701
le NInt avant envoi:	-121
le NInt apres envoi:	-121
le UInt avant envoi:	65001
le UInt apres envoi:	65001

Methode 3

//METHODE 3  : avec les unions !Voir si possible economiser Bytes inutiles
        Serial.println("\nMETHODE 3 : avec les unions\n");

        //Float
        typedef union {
                        float f;                // Assigning fVal.f will also populate fVal.bytes;
                        char b[sizeof(f)]; // Both fVal.f and fVal.bshare the same 4 bytes of memory.
                        } TypeUnionFloatBytes;

        TypeUnionFloatBytes MonFloatUnion;

        MonFloatUnion.f = MonFloat;
            
        Serial.print("le float avant envoi:\t");
        Serial.println(MonFloatUnion.f);

        TableauBytes[5+48]=0;//MonFloatUnion.b[0];
        TableauBytes[5+49]=MonFloatUnion.b[1];
        TableauBytes[5+50]=MonFloatUnion.b[2];
        TableauBytes[5+51]=MonFloatUnion.b[3];

            //test recompo avec donnees envoyees
            MonFloatUnion.b[0]=0;//TableauBytes[5+48];
            MonFloatUnion.b[1]=TableauBytes[5+49];
            MonFloatUnion.b[2]=TableauBytes[5+50];
            MonFloatUnion.b[3]=TableauBytes[5+51];

            Serial.print("le Float apres envoi:\t");
            Serial.println(MonFloatUnion.f);


        //Float par long
        typedef union {
                        long l;                // Assigning fVal.f will also populate fVal.bytes;
                        char b[sizeof(l)]; // Both fVal.f and fVal.bshare the same 4 bytes of memory.
                        } TypeUnionLongBytes;

        TypeUnionLongBytes MonLongUnion;
        TypeUnionLongBytes MonNLongUnion;

        MonLongUnion.l = MonFloat*100L;
    
        Serial.print("le Float par long avant envoi:\t");
        Serial.println(MonLongUnion.l/100.0);

        TableauBytes[5+48]=0;//MonLongUnion.b[0];
        TableauBytes[5+49]=MonLongUnion.b[1];
        TableauBytes[5+50]=MonLongUnion.b[2];
        TableauBytes[5+51]=MonLongUnion.b[3];

            //test recompo avec donnees envoyees
            MonLongUnion.b[0]=0;//TableauBytes[5+48];
            MonLongUnion.b[1]=TableauBytes[5+49];
            MonLongUnion.b[2]=TableauBytes[5+50];
            MonLongUnion.b[3]=TableauBytes[5+51];

            Serial.print("le Float par long apres envoi:\t");
            Serial.println(MonLongUnion.l/100.0);


        //NFloat

        TypeUnionFloatBytes MonNFloatUnion;

        MonNFloatUnion.f = MonNFloat;

        Serial.print("le Nfloat avant envoi:\t");
        Serial.println(String(MonNFloatUnion.f));

        TableauBytes[5+52]=0;//MonNFloatUnion.b[0];
        TableauBytes[5+53]=MonNFloatUnion.b[1];
        TableauBytes[5+54]=MonNFloatUnion.b[2];
        TableauBytes[5+55]=MonNFloatUnion.b[3];

            //test recompo avec donnees envoyees
            MonNFloatUnion.b[0]=0;//TableauBytes[5+52];
            MonNFloatUnion.b[1]=TableauBytes[5+53];
            MonNFloatUnion.b[2]=TableauBytes[5+54];
            MonNFloatUnion.b[3]=TableauBytes[5+55];

            Serial.print("le NFloat apres envoi:\t");
            Serial.println(String(MonNFloatUnion.f));


        //Double -PROBLEME SI RECEPTION SUR ESP32
        typedef union {
                        double d;                // Assigning fVal.f will also populate fVal.bytes;
                        char b[sizeof(d)]; // Both fVal.f and fVal.bshare the same 4 bytes of memory.
                        } TypeUnionDoubleBytes;

        TypeUnionDoubleBytes MonDoubleUnion;
        TypeUnionDoubleBytes MonNDoubleUnion;

        MonDoubleUnion.d = MonFloat;
        MonNDoubleUnion.d = MonNFloat;
    
        Serial.print("le Double avant envoi:\t");
        Serial.println(MonDoubleUnion.d);

        TableauBytes[5+56]=0;//MonDoubleUnion.b[0];
        TableauBytes[5+57]=MonDoubleUnion.b[1];
        TableauBytes[5+58]=MonDoubleUnion.b[2];
        TableauBytes[5+59]=MonDoubleUnion.b[3];

            //test recompo avec donnees envoyees
            MonDoubleUnion.b[0]=0;//TableauBytes[5+56];
            MonDoubleUnion.b[1]=TableauBytes[5+57];
            MonDoubleUnion.b[2]=TableauBytes[5+58];
            MonDoubleUnion.b[3]=TableauBytes[5+59];

            Serial.print("le Double apres envoi:\t");
            Serial.println(MonDoubleUnion.d);


        Serial.print("le NDouble avant envoi:\t");
        Serial.println(MonNDoubleUnion.d);

        TableauBytes[5+60]=0;//MonNDoubleUnion.b[0];
        TableauBytes[5+61]=MonNDoubleUnion.b[1];
        TableauBytes[5+62]=MonNDoubleUnion.b[2];
        TableauBytes[5+63]=MonNDoubleUnion.b[3];

            //test recompo avec donnees envoyees
            MonNDoubleUnion.b[0]=0;//TableauBytes[5+60];
            MonNDoubleUnion.b[1]=TableauBytes[5+61];
            MonNDoubleUnion.b[2]=TableauBytes[5+62];
            MonNDoubleUnion.b[3]=TableauBytes[5+63];

            Serial.print("le NDouble apres envoi:\t");
            Serial.println(MonNDoubleUnion.d);

RESULTAT:

METHODE 3 : avec les unions

le float avant envoi:	10667.41
le Float apres envoi:	10667.25
le Float par long avant envoi:	10667.40
le Float par long apres envoi:	10664.96
le Nfloat avant envoi:	-67.41
le NFloat apres envoi:	-67.41
le Double avant envoi:	10667.41
le Double apres envoi:	10667.25
le NDouble avant envoi:	-67.41
le NDouble apres envoi:	-67.41

Methode 4

//METHODE 4  : avec divisions
        Serial.println("\nMETHODE 4 : avec divisions\n");

        //Float
        //float MonFloat=97667.31-87000;
        MonFloat=MonFloat;
        TableauBytes[5+64]=(MonFloat*100UL)/(256UL*256UL);
        TableauBytes[5+65]=((MonFloat*100UL)-(TableauBytes[5+64]*256UL*256UL))/256UL;
        TableauBytes[5+66]=((MonFloat*100UL)-(TableauBytes[5+64]*256UL*256UL)-(TableauBytes[5+65]*256UL));

            //test recompo avec donnees envoyees
            Serial.print("le float avant envoi:\t");
            Serial.println(MonFloat);
            Serial.print("le Float apres envoi:\t");
            Serial.println((TableauBytes[5+64]*256UL*256UL+TableauBytes[5+65]*256UL+TableauBytes[5+66])/100.00);

        //NFloat //Oblige de passer par 4 bytes
        //float MonNFloat=-67.31;

        /*
        TableauBytes[5+67]=(MonNFloat*100UL)/(256UL*256UL);
        TableauBytes[5+68]=((MonNFloat*100UL)-(TableauBytes[5+67]*256UL*256UL))/256UL;
        TableauBytes[5+69]=((MonNFloat*100UL)-(TableauBytes[5+67]*256UL*256UL)-(TableauBytes[5+68]*256UL));
        */
        TableauBytes[5+67]=(MonNFloat*100UL)/(256UL*256UL*256UL);
        TableauBytes[5+68]=((MonNFloat*100UL)-(TableauBytes[5+67]*256UL*256UL*256UL))/(256UL*256UL);
        TableauBytes[5+69]=((MonNFloat*100UL)-(TableauBytes[5+67]*256UL*256UL*256UL)-(TableauBytes[5+68]*256UL*256UL))/256UL;
        TableauBytes[5+70]=((MonNFloat*100UL)-(TableauBytes[5+67]*256UL*256UL*256UL)-(TableauBytes[5+68]*256UL*256UL)-(TableauBytes[5+69]*256UL));
        
            //test recompo avec donnees envoyees
            Serial.print("le Nfloat avant envoi:\t");
            Serial.println(MonNFloat);
            Serial.print("le NFloat apres envoi:\t");
            Serial.println((TableauBytes[5+67]*256UL*256UL*256UL+TableauBytes[5+68]*256UL*256UL+TableauBytes[5+69]*256UL+TableauBytes[5+70])/100.00);

RESULTAT:

METHODE 4 : avec divisions

le float avant envoi:	10667.41
le Float apres envoi:	10665.32
le Nfloat avant envoi:	-67.41
le NFloat apres envoi:	0.00

kamill:
Npn, la définition de la structure est fixe et ne peut donc s'adapter dynamiquement.
Si le nombre et le type de capteurs peut varier, il faut que tu prévois dans ta transmission non seulement les valeurs, mais aussi leur type et leur nombre.

Ça c'est pas un problème l'adaptation automatique (assemblage / découpage) est codée et fonctionne (côté émetteur comme côté récepteur), ce qu'il me manque c'est le passage optimisé en octets au milieu pour la transmission.

Autant le float vu la structure des données binaires je me doute que supprimer un octet peut poser problème,
Autant pour le long je ne comprends pas:

1 066 741 =>0000 0000 0001 0000 0100 0110 1111 0101
Ça devrait passer sur 3 octets non?

Je me demande si toute cette gymnastique est bien nécessaire.
Le plus sûr pour échanger entre plateformes hétérogènes c'est de transmettre les informations sous forme de chaines de caractères. Les chaines 46.5 ou 12345 sont indépendantes du mode de codage utilisé sur les plateforme. Elles seront comprises de la même manière quelque soit la plateforme. Tu économisera de la mémoire car tu n'auras pas besoin de passer par des tableaux intermédiaires.
Éventuellement, tu peux réduire le nombre de caractères transmis en limitant le nombre de décimales des chaines mais je ne crois franchement pas qu'un ou deux octets de plus aient une grosse influence sur l'autonomie du système.

fdufnews:
Tu économisera de la mémoire car tu n'auras pas besoin de passer par des tableaux intermédiaires.

fdufnews:
je ne crois franchement pas qu'un ou deux octets de plus aient une grosse influence sur l'autonomie du système.

C'est justement la prochaine étape, mesurer le temps d’éveil nécessaire (et donc la perte d'énergie) pour cette gymnastique et le comparer à l’énergie nécessaire pour émettre les octets en plus.

Je suis curieux de savoir ce que représente l'énergie nécessaire à l'envoi d'une dizaine d'octets en plus à puissance maximale du NRF24L01 ou du module LoRa multiplié par le nombre d'envois périodiques.

L'émetteur doit tenir des mois et des mois sur une batterie ridicule, par exemple au repos je vise une conso de l'ordre du uA voire inférieure, même inférieure à l’auto-décharge de la batterie. (pour l'instant je n'y suis pas du tout...)