Aller au contenu


Photo

Glenn Robot Humanoide


  • Veuillez vous connecter pour répondre
989 réponses à ce sujet

#981 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 8 020 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 01 septembre 2019 - 08:13

Ok, bon ben suis pas rendu avec tout ça   :dash2:

Essaye déjà ce que t'ont proposé sandro et thot ! =)


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !

 

Les réalisations de Mike118  

 

 

 


#982 Oliver17

Oliver17

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 2 676 messages
  • Gender:Male
  • Interests:Glenn

Posté 02 septembre 2019 - 08:49

Oui, va falloir bien réfléchir sur le code pour faire tout ça propre et surtout faire des recherches sur comment faire :)

 

En tout cas merci les gars ;)


signature_01.png -->

 

Mon Tipeee
 


#983 Oliver17

Oliver17

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 2 676 messages
  • Gender:Male
  • Interests:Glenn

Posté 02 septembre 2019 - 02:24

@Sandro : juste pour te faire un retour, j'ai testé à 90ms ça a l'air de fonctionner carrément mieux niveau latence, par contre j'ai des pertes d'octets comme tu le précisais si je ne dis pas de bêtises.

 

Bon je continue mes recherches avec vos conseils ;)

 

Faut que j'arrive à mélanger ces deux idées de Thot et Sandro, tintin tiiiinnnn gros challenge, ça sent la galère, zou à moi google ^^

 

EDIT : Si vous avez des liens qui explique le pourquoi tu comment avec surtout du code d'exemple que je puisse avoir une idée, welcome.


signature_01.png -->

 

Mon Tipeee
 


#984 Sandro

Sandro

    Membre occasionnel

  • Membres
  • Pip
  • 109 messages
  • Gender:Male

Posté 06 septembre 2019 - 12:30

Avec le 90ms (sans utiliser de start byte), il y a aura en effet des pertes des pertes de paquets si tu tu lis pendant qu'un paquet est en train d'arriver.

 

 

Je t'ai écris un code d'exemple qui fonctionne avec un Arduino (j'ai pas d'ESP) et un Raspberry en utilisant un start byte et une checksum :

 

Arduino :

#define SIZE_OFF_DONNEES 3  //nbr d'octets sans start byte et checksum

struct Donnees {  //les données que tu veux envoyer (SIZE_OFF_DONNEES octets)
  uint8_t toto;
  int16_t tata;
};

Donnees donnees;

void envoyer_donnees()
{
  uint8_t donnees_a_envoyer[SIZE_OFF_DONNEES+2];
  donnees_a_envoyer[0]=42; //start byte
  donnees_a_envoyer[1]=donnees.toto;
  donnees_a_envoyer[2]=donnees.tata>>8;  //octet de poids fort de tata (on pourrait écrire donnees.tata/256, mais c'est plus lent à calculer)
  donnees_a_envoyer[3]=donnees.tata&0xFF; //octet de poids faible de tata (on pourrait écrire donnees.tata%256, mais c'est plus lent à calculer)

  uint8_t checksum=0;
  for(int i=0; i<SIZE_OFF_DONNEES+1; i++)  //calcul du xor entre tous les bytes (sauf la checksum) pour calculer la checksum
  {
    checksum=checksum^donnees_a_envoyer[i];
  }
  donnees_a_envoyer[SIZE_OFF_DONNEES+1]=checksum; //on rajoute la checksum à la fin
  Serial.write(donnees_a_envoyer, SIZE_OFF_DONNEES+2);
}

void setup() {
  Serial.begin(115200);
  donnees.toto=0;
  donnees.tata=1000;
}

void loop() {
  donnees.toto=donnees.toto+1;
  donnees.tata=donnees.toto+1000;
  envoyer_donnees();
  delay(100); //tu mets ce que tu veux, ça ne doit même pas être constant. Alternativement, tu peux envoyer tes données depuis un timer
}

Raspberry Pi:

#include <iostream>
#include "rs232.h"

using namespace std;

#define SIZE_CHARGE_UTILE 3	//nombre d'octets utiles à envoyer (on envois 2 octets en plus pour le début de paquet et la checksum)
#define SIZE_PAQUET SIZE_CHARGE_UTILE+2	//nombre d'octets d'un paquet (y compris start byte et checksum)

int cport_nr(24);               //24=ttyACM0 // 0 = ttyS0  ls /dev/tty*
int bdrate(115200);             // Baud
char mode []={'8','N','1',0};   // 8 data bits, no parity, 1 stop bit

struct Donnees {  //les données que tu veux recevoir
  uint8_t toto;
  int16_t tata;
};


uint8_t received_data[4096+SIZE_CHARGE_UTILE+2];	//taille du buffer d'entrée (4096) + taille pour un paquet
unsigned int nbr_bytes_in_buffer=0;

void process_received_package(Donnees &donnees)
{
	cout<<(unsigned int)donnees.toto<<endl;
	cout<<(int)donnees.tata<<endl;
	cout<<endl;
}

void get_received_data_and_process()
{
	unsigned int n = RS232_PollComport(cport_nr, received_data+nbr_bytes_in_buffer, 4096);	//on écrit les nouvelles données à l'adresse received_data+nbr_bytes_in_buffer de manière à ce qu'ils soient après ceux déjà présents
	nbr_bytes_in_buffer+=n;	//on a maintenant n octets de plus dans notre buffer (ie les n octets qu'on vient de lire)
	
	unsigned int start_index=0;	//premier octet du buffer pas encore lu
	while(start_index+SIZE_PAQUET<=nbr_bytes_in_buffer)	//tant qu'il y a assez de données dans le buffer pour potentiellement faire un paquet complet
	{
		//vérifions que le (potentiel) paquet commence bien par 42
		if(received_data[start_index]!=42)	//si le premier octet n'est pas 42, alors il ne s'agit pas du début du paquet
		{
			cerr<<"invalide byte:"<<(unsigned int)received_data[start_index]<<endl;
			start_index++;	//on ignore le premier octet
			continue;	//on arrête ce tour du while, et on repart pour un nouveau tour avec l'octet suivant (si la condition du while est toujours respectée)
		}

		//vérifions que le potentiel paquet est valide (checksum correcte)
		uint8_t checksum=0;
		for(unsigned int i=0; i< SIZE_PAQUET ; i++)
		{
			checksum=checksum^received_data[start_index+i];
		}
		if(checksum!=0)	//normalement on doit obtenir 0, car le dernier octet est sensé être le xor des précédents, et n xor n=0
		{
			cerr<<"invalide checksum"<<endl;
			start_index++;	//on ignore le premier octet
			continue;	//on arrête ce tour du while, et on repart pour un nouveau tour avec l'octet suivant (si la condition du while est toujours respectée)
		}
		
		//on sait maintenant qu'on a un paquet valide, qu'on peut donc traiter
		uint8_t paquet[SIZE_PAQUET];
		memcpy(paquet,received_data+start_index,SIZE_PAQUET);	//on récupère le paquet (nb : il serait plus optimisé de lire directement dans received_data, mais ça rendrait le code moins clair)
		struct Donnees donnees;
		donnees.toto=paquet[1];
		donnees.tata=256*paquet[2]+paquet[3];
		process_received_package(donnees);	//on traite les donnees utiles
		
		//on considère tout un paquet comme traité
		start_index+=SIZE_PAQUET;
	}
	
	//il reste maintenant moins de sizeof(UnionDonneesARecevoir) octets. Pour préparer l'appel suivant à la fonction, on actualise le nombre restant d'octets et on met tous les octets restant au début du buffer
	nbr_bytes_in_buffer=nbr_bytes_in_buffer-start_index;	//nombre d'octets restant
	memmove(received_data,received_data+start_index,nbr_bytes_in_buffer);	//on déplace les octets restants vers le début du buffer (nb : il faut utilise memmove au lieu de memcpy car il y a potentiellement chevauchement des zones d'origine et de destination)
}



int main(int argc, char **argv)
{

	if (RS232_OpenComport(cport_nr, bdrate, mode,0))
    {
        cout <<"-----> Ne peut pas ouvrir le port com.\n \n";
        exit(-1);
    }
    cout << "-----> Port de communication ouvert PI. \n \n";

	while(1)
	{
		get_received_data_and_process();
		usleep(100000);
	}

	return 0;
}


Quelques remarques :

- j'ai décidé de ne pas utiliser d'unions, car selon les cas (par exemple si elle contient un char suivit d'un int), il n'est pas spécifié si des octets "vides" doivent être insérés ou pas. Il y a donc un risque qu'en utilisant des unions le code devienne faux si on change de compilateur ou de plateforme

- tu remarquera que je découpe explicitement les données en octets (en particulier toto, qui est sur 16 bits) : là encore, il s'agit de garantir que ça marchera sur n'importe quel appareil (certains stockent l'octet de poids fort d'abord, d'autre celui de poids faible, du coup si tu as la mal chance de tomber sur un de chaque, le résultat est faux)

- pour transmettre des floats, il y a plusieurs possibilités : juste envoyer les octets correspondants (mais ça donnera des résultats faux si de l'autre coté c'est pas interprété pareil), ou envoyer les informations dans un format que tu décide (par exemple 1 octet pour la partie entière suivit d'un entier entre 0 et 99 pour la partie décimale)

 

D'après le (petit) test que j'ai fait, je n'ai pas de pertes de données (tant que la communication électrique vas bien). Il est "normal" que tu voies des messages "invalide byte" au tout début : celà correspond simplement aux données déjà présentes dans le buffer quand ton programme se lance et est prêt à lire. Dans la version sans vérification, tu aurais probablement des valeurs absurdes à la place (sans que le programme détecte que c'est du n'importe quoi, alors qu'ici les données corrompues sont juste ignorées)

 

Si tu as des questions, n'hésite pas (je pourrais probablement te répondre jusqu'à demain (vendredi) début d'aprèm, et ensuite qu'à partir de samedi soir ou dimanche)



#985 Oliver17

Oliver17

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 2 676 messages
  • Gender:Male
  • Interests:Glenn

Posté 06 septembre 2019 - 08:09

Ok merci beaucoup, je regarde cela de plus prés plus tard.


signature_01.png -->

 

Mon Tipeee
 


#986 Forthman

Forthman

    Membre chevronné

  • Membres
  • PipPipPipPip
  • 956 messages
  • Gender:Not Telling
  • Location:Montauban (82)

Posté 06 septembre 2019 - 10:53

Je suis ce post et je me pose une question : il n'y a pas d'UART sur une carte arduino ? c'est le MC qui doit tout gérer ?



#987 Sandro

Sandro

    Membre occasionnel

  • Membres
  • Pip
  • 109 messages
  • Gender:Male

Posté 06 septembre 2019 - 12:18

Je viens de vérifier dans la datasheet de l'ATMEGA328P (utilisé dans l'arduino Uno) : il comporte un circuit USART, qui est une extension de l'UART (et qui peut fonctionner en mode UART) : il s'agit donc bien d'une implémentation hardware (ouf!). Pour y accéder directement, c'est les pins 0 et 1 (RX et TX). En plus de ça, tu as, si mes souvenirs sont bons, une petite puce dédiée qui te transforme l'UART en UART via USB (c'est ainsi que tu peux envoyer des messages à l'ordi en utilisant Serial.print())



#988 Forthman

Forthman

    Membre chevronné

  • Membres
  • PipPipPipPip
  • 956 messages
  • Gender:Not Telling
  • Location:Montauban (82)

Posté 06 septembre 2019 - 01:45

ça faisait un petit moment que je ne suivais que de loin, et je n'avais pas vu que c'était un ESP32 et pas une Arduino,

mais bon, le résultat est le même puise que l'ESP32 a aussi un UART...

 

Tout ça pour dire que je ne comprends pas cette histoire de temporisation.

en liaison full-duplex, ce sont les UART de chaque carte qui devraient s'occuper de la synchro non ?

J'ai jamais joué avec du microcontroleur, mais uniquement sur PC (UART 8250)

et une fois l'UART correctement paramétrée, elle déclenche une interruption à chaque réception d'octet.

il est aussi possible de stocker temporairement ces octets dans une pile FIFO, ce qui permet de ne pas mobiliser

toutes les ressources à chaque interruption.

Idem pour l'envoi, on peut consulter l'état de l'UART pour savoir si la place est libre ou non.



#989 Oliver17

Oliver17

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 2 676 messages
  • Gender:Male
  • Interests:Glenn

Posté 06 septembre 2019 - 02:42

Moi non plus je ne comprend pas ^^ lol


signature_01.png -->

 

Mon Tipeee
 


#990 Sandro

Sandro

    Membre occasionnel

  • Membres
  • Pip
  • 109 messages
  • Gender:Male

Posté 06 septembre 2019 - 02:48

En théorie, il devrait être possible d'avoir une interruption à chaque octet reçu, mais ce n'est pas implémenté dans la bibliothèque qu'utilise Olivier17. En revanche, il y a bien une FIFO (buffer) de 4096 octets coté Raspi, ce qui rends possible le code que j'ai posté (ie de lire plus d'un paquet si on a du "retard").

 

D'ailleurs, je viens de me rendre compte que j'ai oublié de préciser quelque chose : dans le code que j'ai posté, rien n'impose de mettre des delais de 100ms : tu peux sans problème mettre un délai différent d'un coté ou de l'autre (si la bibliothèque raspi ne disait pas explicitement de ne pas l'appeler trop souvent, tu pourrais très bien ne pas mettre de delai du tout coté raspi. En gros, tant que tu appelle la fonction de lecture du raspi avant que tu n'ait accumulé 4096 octets non lus, tout ira bien (au sens que tu ne perdra pas de données)






0 utilisateur(s) li(sen)t ce sujet

0 members, 0 guests, 0 anonymous users