Problème de lecture de bits

Bonjour à tous,

je suis un débutant sur Arduino, j'ai quelques bases apprises en cours mais rien de très élaboré pour le moment. Je suis un passionné de vol à voile et de simulation, c'est pourquoi j'ai comme projet actuel de réaliser un anémomètre contrôlé par mon simulateur de vol : Condor 2. Pour cela, j'ai imprimé en 3D mon anémomètre et j'ai relié l'aiguille à un stepper. J'ai commencé par la partie simple qui consistait à convertir une vitesse en entrée en une position du stepper, pas de problèmes de ce côté là tout fonctionne.

Le hic concerne la récupération et le traitement des données. Pour récupérer les données ( en l'occurence ici, la vitesse du planeur), j'utilise un petit logiciel créé par un amateur : CondorUDP2COM. Ce logiciel récupère les données, les encode de float en bits et les envoie sur le port COM. Jusque là tout va bien, les données encodées arrivent bien jusqu'à mon Arduino. Le problème est que j'ai beau me torturer l'esprit je ne comprend pas comment décoder ces bits en float. Je vous mets en dessous le lien où est disponible le code source du logiciel, ainsi qu'un exemple Arduino (stepper_motor_gauge) qui ne fonctionne pas. J'ai essayé de comprendre comment marchait cet exemple Arduino mais je n'y suis pas parvenu.

Lien : GitHub - kbobrowski/CondorUDP2COM: Application to parse Condor UDP stream and send it formatted over COM

Je vous met également une image qui montre un preview de ce qui est envoyé sur le port COM et qui est reçu par l'Arduino. Sur la capture on voit que ce qui est envoyé à l'arduino est :

7878787878787a0112A71F42

qui doit se décoder en :

z 1 39.913....

J'espère avoir été assez clair dans l'expression du problème, et j'espère que qqun pourra m'aider.

Bonne soirée :slight_smile:

Je pense que tout est expliqué la dedans (j'adore les programmes sans commentaires!):

def formatFrame(self):
"""
Gathers data and encodes them in hex format

Returns:
bytes: Frame sutable to be sent over COM port.
"""
start = int('78', 16)
packedStart = 6struct.pack('B', start)
packedZ = bytes('z'.encode('UTF-8'))
frame = packedStart
for i,check in enumerate(self.model.checkList):
if check:
frame += packedZ
packedInt = struct.pack('B', i)
frame += packedInt
packedFloat = struct.pack('f', self.model.valList
)*

  • frame += packedFloat*
  • hexFrame = binascii.hexlify(frame).decode('UTF-8')*
  • strBytes = [hexFrame[i:i+2] for i in range(0, len(hexFrame), 2)]*
  • printHexFrame = ''*
  • while len(strBytes):*
  • for i in range(6): printHexFrame += ' '+strBytes.pop(0)*
  • printHexFrame += '\n'*
  • self.serialPreviewText.setPlainText(printHexFrame.upper())*
  • self.decodeFrame(frame)*
    return frame[/quote]
    Il doit bien avoir quelqu'un qui lit le pyton mieux que moi.
    Ce que j'ai vu ou cru voir (qui est vérifiable en essayant d'autres valeurs)
    - il y a d'abord une série fixe de 6 octets 78
    - puis le code ascii de z (c'est 7A en hexa); Serial.println(char(0x7A)); affiche z
    - puis vient un octet 1 pour le chiffre 1
    - puis heu...je vais me coucher...
    Je ne sais pas en C copier les 4 octets qui sont dans un float vers un long (memcpy?)... Je suppose que vu qu'il y a 4 octets pour un float et qu'il reste 4 octets à décoder, il y a simplement à faire une copie.
 char* adresse = adresse_du_1er_octets à décoder;
float valeur = *(float*)adresse;

à condition que le codage binaire des floats soit le même sur les 2 machines (endianness en particulier)

Merci @biggil.

Si je regarde le codage sous Arduino de 39.9132
en faisant le programme:

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

  float* adresse;
  *adresse = 39.9132;
  long valeur = *(long*)adresse;
  Serial.println(valeur, HEX);
  Serial.println();
}

void loop()
{
}

J'obtiens:

421FA71E

c'est la valeur des 4 octets dans l'autre sens, à un arrondi près. Les octets sont en sens inverse

Pour transformer les 4 octets restants 12A71F42 il faut prendre les octets dans le sens inverse et les mettre dans un float:

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

  // pour transformer 12A71F42 en float
  long* adresse;
  *adresse = 0x421FA712;
  float valeur = *(float*)adresse;
  Serial.println(valeur);
  Serial.println();
}

void loop()
{
}

ce qui affiche

39.91

Salut à tous,

Premièrement merci pour vos réponses, c'est vrai que je n'avais pas pensé à faire le chemin inverse pour essayer de trouver ce qui n'allait pas.

Cependant, novice que je suis toujours, je n'arrive pas à voir comment à partir du bit de 24 octets du départ arrivant du port série, ne garder que les 4 derniers octets, les inverser et en lire les données. Cela vous paraît certainement simple mais j'ai beau chercher je m'embrouille avec les types de variables à utiliser (float, char, long...).

Il faut d'abord que l'on en sache un peu plus, sur ce que tu récupères: dans quoi?
Je suppose que c'est un tableau d'octets, mais cela pourrait être un enregistrement, ou encore un tableau de booléens....

Bonjour,
Désolé pour le temps de réponse j'ai pas eu le temps de me remettre à ça avec les examens...
J'ai rebossé dessus aujourd'hui, j'ai essayé de modifier l'exemple qui était fourni avec le logiciel UDP2COM pour l'adapter à ma situation. Voici ce que ca donne :

#include <Stepper.h>

const int stepsPerRevolution = 2048;
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);

float data[40] = {0};
int desStepperPos;
int actualPosition = 0; // On initialise la valeur de position 

void setup() {
    Serial.begin(9600);
    myStepper.setSpeed(10);
}


String vitesseVoulue;
void loop() {
    read_frame(data); // On lit les données qui arrivent et on les envoie dans data[p]
    desStepperPos = getStepperPos(1); // On convertit ces données en nombre de pas pour le moteur
    if(stepperPosition < desStepperPos) // Si la vitesse voulue est supérieure à la vitesse actuelle, on fait avancer le stepper
    {
        myStepper.step(1);
        actualPosition ++;
    }
    else if(stepperPosition > desStepperPos) // Si la vitesse voulue est inférieure à la vitesse actuelle, on fait reculer le stepper
    {
        myStepper.step(-1);
        actualPosition --;
    }
      
      }



// Conversion de la vitesse en un nombre de pas pour le stepper (p=1 nous permet de donner la vitesse, j'ai fait cela pour pouvoir ajouter par la suite d'autres instruments (altimètre, variomètre...)
int getStepperPos (int p) {
  float vitesse = data[p];
  float stepperPos;
  switch (p){
    case 1 : //Vitesse 
      if (vitesse <= 0){
        stepperPos = 0;
      }
      else if (vitesse >0 && vitesse<=40){
        stepperPos = map(vitesse,0,40,0,130);
      }
      else if (vitesse >40 && vitesse<=50){
        stepperPos = map(vitesse,40,50,131,296);
      }
      else if (vitesse >50 && vitesse<=60){
        stepperPos = map(vitesse,50,60,297,455);
      }
      else if (vitesse >60 && vitesse<=70){
        stepperPos = map(vitesse,60,70,456,643);
      }
      else if (vitesse >70 && vitesse<=80){
        stepperPos = map(vitesse,70,80,644,796);
      }
      else if (vitesse >80 && vitesse<=90){
        stepperPos = map(vitesse,80,90,797,933);
      }
      else if (vitesse >90 && vitesse<=100){
        stepperPos = map(vitesse,90,100,934,1063);
      }
      else if (vitesse >100 && vitesse<=110){
        stepperPos = map(vitesse,100,110,1064,1189);
      }
      else if (vitesse >110 && vitesse<=120){
        stepperPos = map(vitesse,110,120,1190,1297);
      }
      else if (vitesse >120 && vitesse<=130){
        stepperPos = map(vitesse,120,130,1298,1410);
      }
      else if (vitesse >130 && vitesse<=140){
        stepperPos = map(vitesse,130,140,1411,1513);
      }
      else if (vitesse >140 && vitesse<=150){
        stepperPos = map(vitesse,140,150,1514,1621);
      }
      else if (vitesse >150 && vitesse<=160){
        stepperPos = map(vitesse,150,160,1622,1718);
      }
      else if (vitesse >160 && vitesse<=170){
        stepperPos = map(vitesse,160,170,1719,1803);
      }
      else if (vitesse >170 && vitesse<=180){
        stepperPos = map(vitesse,170,180,1804,1894);
      }
      else if (vitesse >180 && vitesse<=190){
        stepperPos = map(vitesse,180,190,1895,1985);
      }
      else if (vitesse >190 && vitesse<=200){
        stepperPos = map(vitesse,190,200,1986,2082);
      }
      else if (vitesse >200 && vitesse<=210){
        stepperPos = map(vitesse,200,210,2083,2161);
      }
      else if (vitesse >210 && vitesse<=220){
        stepperPos = map(vitesse,210,220,2162,2252);
      }
      else if (vitesse >220 && vitesse<=230){
        stepperPos = map(vitesse,220,230,2253,2338);
      }
      else if (vitesse >230 && vitesse<=240){
        stepperPos = map(vitesse,230,240,2339,2412);
      }
      else if (vitesse >240 && vitesse<=250){
        stepperPos = map(vitesse,240,250,2413,2491);
      }
      else if (vitesse >250 && vitesse<=260){
        stepperPos = map(vitesse,250,260,2492,2548);
      }
      else if (vitesse >260 && vitesse<=270){
        stepperPos = map(vitesse,260,270,2549,2651);
      }
      else if (vitesse >270 && vitesse<=280){
        stepperPos = map(vitesse,270,280,2652,2730);
      }
      else if (vitesse >280 && vitesse<=290){
        stepperPos = map(vitesse,280,290,2731,2793);
      }
      else if (vitesse >290 && vitesse<=300){
        stepperPos = map(vitesse,290,300,2794,2838);
      }
      else{
        stepperPos = 2838;
      }
  }
  return (int)(stepperPos);
}

// Lis les bytes qui arrivent et les convertit en float
float read_from_bytes(void)
{
    union u_tag {
        byte b[11];
        float ulval;
    } u;
    u.b[10] = Serial.read();
    u.b[11] = Serial.read();
    u.b[8] = Serial.read();
    u.b[9] = Serial.read();
    u.b[6] = Serial.read();
    u.b[7] = Serial.read();
    u.b[4] = Serial.read();
    u.b[5] = Serial.read();
    u.b[2] = Serial.read();
    u.b[3] = Serial.read();
    u.b[0] = Serial.read();
    u.b[1] = Serial.read();
    return u.ulval;
}

//Remplis le tableau de données avec les données reçues
void read_frame(float *data)
{
    int n;
    float w;
    if (Serial.available())
    {
        while(Serial.available() < 12);
        n = (int)Serial.read();
        w = read_from_bytes();
        Serial.println(w);
        data[n] = w;
    }
}

Evidemment ce code ne fonctionne pas, et je ne vois pas comment le corriger étant donné que je n'ai aucune vue sur ce qui se passe pendant l'exécution du code puisque lorsque je lance la communication entre l'arduino et le simulateur, le port est occupé. Je ne peux donc pas mettre de Serial.print() pour voir ce qui se passe.

Si vous avez une idée de ce qui coince je suis preneur.

Merci encore :slight_smile:

Evidemment ce code ne fonctionne pas

Cela ne fonctionne pas comme tu le voudrait plutôt? Tu ne décris pas ce que tu veux, tu ne décrit pas ce qui se passe et qui ne te plaît pas.
Il y a plusieurs choses, la gestion du moteur, le décodage.... Ok si toutes les briques fonctionnent séparément. Sinon il vaut mieux commencer par faire marcher uniquement un objet seul, le deuxième, le troisièmes... puis tout ensemble. En mettant tout au début, on ne peut plus savoir ce qui est en cause.

Cela ne fonctionne pas comme je veux non. Ce que je veux c'est que les bits entrant soient décodés en une valeur (qui correspond à la vitesse du planeur), ensuite cette valeur est convertie en position du stepper grâce à la fonction getStepperPos() (cette fonction marche, j'ai pu la tester). Enfin, je veux que dans le loop, la valeur de vitesse venant du simulateur soit actualisée constamment, puis convertie en position du stepper et qu'ensuite en comparant les vitesses à t et t-1, le stepper tourne dans un sens ou dans l'autre pour s'en rapprocher.

Mon principal problème est la fonction permettant de lire les bits entrant, je n'arrive pas à traduire en code ce que je veux. Le bits entrant doit être modifié afin de ne garder que les 4 derniers octets et ces 4 derniers octets doivent être inversés (le premier devient le dernier, le deuxième devient le troisième, etc.) et ensuite une fois modifié, ce bit doit être converti en float pour retourner la valeur numérique de la vitesse. Et je ne vois pas comment faire cela.

Je suis désolé du manque de précision, je n'ai pas l'habitude des forums et j'ai souvent du mal à expliquer en général.

Si tu donnes un code d'un km, il a y aura moins de réponses que si il n'y a que quelques lignes. Isole jute les lignes qui te pose problème.

C'est juste cette partie qui pose un problème?

// Lis les bytes qui arrivent et les convertit en float
float read_from_bytes(void)
{
    union u_tag {
        byte b[11];
        float ulval;
    } u;
    u.b[10] = Serial.read();
    u.b[11] = Serial.read();
    u.b[8] = Serial.read();
    u.b[9] = Serial.read();
    u.b[6] = Serial.read();
    u.b[7] = Serial.read();
    u.b[4] = Serial.read();
    u.b[5] = Serial.read();
    u.b[2] = Serial.read();
    u.b[3] = Serial.read();
    u.b[0] = Serial.read();
    u.b[1] = Serial.read();
    return u.ulval;
}

Il n'y a que 4 valeurs à garder, les 8 autres sont à ignorer. On peut les lire dans...rien.

for (byte huitOctets=0; huitOctets<8; huitOctets++) Serial.read();
Puis 4 octets à lire et à traduire. On peut donc faire une union qui ne comprenne que ces 4 octets:
union u_tag {
byte b[4];
float ulval;
} u;
Pour les avoir dans l'autre sens, il faut enregistrer l'octet 3, puis le 2...:
u.b[3] = Serial.read();
u.b[2] = Serial.read();
u.b[1] = Serial.read();
u.b[0] = Serial.read();
Le code devient

// Lis les bytes qui arrivent et les convertit en float
float read_from_bytes(void)
{
    union u_tag {
        byte b[4];
        float ulval;
    } u;
    for (byte huitOctets=0; huitOctets<8; huitOctets++) Serial.read();
    u.b[3] = Serial.read();
    u.b[2] = Serial.read();
    u.b[1] = Serial.read();
    u.b[0] = Serial.read();
    return u.ulval;
}

Je ne l'ai pas testé, il faut voir si il fait son travail. Pour cela on rajoute des infos pour le débuggage. Il va falloir trouver des astuces. La première chose à faire est de tester le coeur de la transformation. On peu supposer qu'on ait une lecture des codes qui soit bonne. Faire alors
union u_tag {
byte b[4];
float ulval;
} u;
void setup()
{
Serial.begin(115200);
u.b[3] = Serial.read();
u.b[2] = Serial.read();
u.b[1] = Serial.read();
u.b[0] = Serial.read();
Serial.println(u.ulval);
}
Si cela fonctionne on peut aller plus loin.

Pour voir si le float est bon ou pas dans le programme, il faut pouvoir l'afficher ou en afficher un bout. Vu que l'on ne peut pas utiliser println, on peut essayer de le faire avec une led. Si la LED_BUILTIN est libre on peut l'utiliser (pas de câblage à faire).
Dans le genre j'envoie une valeur fixe et si le float devrait valoir par exemple 3.125 on peut faire un programme qui teste si il est plus grand que 3.1 et affiche le résultat de la comparaison sur la led, puis faire un programme qui teste si c'est plus petit que 3.2.

Autre solution on fait flasher la led, un flash par unité -> elle devrait flasher 3 fois
Si elle ne flash pas, le nombre est trop petit et pour le voir, on fait un flash par 1/00 par exemle

Mieux encore, 1 flash si supérieur à 0.0001, 1 flash en plus si supérieur à 0.001... on a ainsi avec le nb de flash l'ordre de grandeur. Avec un doigt, il y en a qui ont écrit un livre. Avec une led, on peut avoir une approximation du float.

Il faut redoubler d'immagination.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.