Go Down

Topic: Transferts de données entre arduino par la liaison série (Read 19876 times) previous topic - next topic

skywodd


Bon, j'ai testé rapidement hier soir. Ça a l'air de fonctionner  XD  :D. La déclaration (unsigned long) semble être la parade. Le '|' semble également pouvoir être remplacé par le '+'. Je testerais avec plus de valeurs lorsque j'aurais un peu plus de disponibilité également.

Le + et le | (ou bits à bits) ont le même effet dans le cas d'un calcul "bitwise", mais dans le doute comme je pouvais tester pas mon code j'ai utilisé le | qui marche dans tout les cas.


C'est quand même cool d'avoir des dieux de la programmation…  :smiley-mr-green:  ;)(Je n'aurais jamais trouvé  :smiley-eek-blue:).

"Dieux de la programmation" :smiley-mr-green:


Les étapes suivantes :
-   Transfert de plusieurs valeurs par le biais de tableaux.
-   Passage du port Uart 0/1 aux ports 'softserial'
-   Déclenchement de l'envoi sur quel critère ? (variation d'une variable du tableau, top horloge, réponse à une interruption, …)
-   Envoi de toutes les données du tableau, ou juste celle modifiée ?
-   Augmentation de la vitesse de transfert.

- boucle for, tableaux
- lib NewSoftSerial
- (for/if, lib MsTimer, attachInterrupt())
- transfert complet -> simple et rapide, transfert partiel -> envoi d'une adresse, longueur du paquet, ... -> compliqué, mais pratique lors de l'envoi de grosse quantité de données.
Des news, des tutos et plein de bonnes choses sur http://skyduino.wordpress.com !

Bubule


- lib NewSoftSerial


Il me semble avoir lu que la version V1.0 intègre maintenant les fonctionnalités de la NewSoftSerial sous la dénomination de la Softserial.

skywodd


Il me semble avoir lu que la version V1.0 intègre maintenant les fonctionnalités de la NewSoftSerial sous la dénomination de la Softserial.

Ouaip en faite ils ont juste renommé NewSoftSerial en SoftSerial ^^
Des news, des tutos et plein de bonnes choses sur http://skyduino.wordpress.com !

Bubule

#18
Jan 13, 2012, 11:03 pm Last Edit: Jan 13, 2012, 11:14 pm by Bubule Reason: 1
Me revoici  :)

Ca avance !
Mais il y a au moins un hic :

Je n'arrive pas à chopper la première valeur (tableauDonnée[0]).
Sinon, toutes les valeurs (tableauDonnée[1 à 4]) sont quant à elles correctes.


A l'émission j'ai mis dans le loop :
Code: [Select]

// Renseignement Tableau
 tableauDonnees[0] = 999999999;    // valeur arbitraire signalant le début de la transmission
 tableauDonnees[1] = pos_servo_1;  // Variable oscillant entre 10 et 130
 tableauDonnees[2] = millis();        
 tableauDonnees[3] = pH*100;       // Variable (double) multipliée par 100 pour chopper 2 décimales
 tableauDonnees[4] = 111111111;    // valeur arbitraire signalant la fin de la transmission

// Toutes les secondes :
int e;
   for (e = 0; e < 5 ; e = e + 1) {
     envoiInt(tableauDonnees[e]);
     }

 Fin de loop


void envoiInt (long valeur) {
 
     Serial.write(valeur & 0xFF);          // Envoi l'octet de poids faible
     Serial.write((valeur >> 8) & 0xFF);      
     Serial.write((valeur >> 16) & 0xFF);
     Serial.write((valeur >> 24) & 0xFF);  // puis l'octet de poids fort



Dans la carte réceptrice :
Code: [Select]

 
// Déclaration des variables et constantes horloge
 unsigned long refTemps;      // Mémoire de l'horloge millis pour horloge
 int topsec = 0;              // Impulsion topsec toutes les secondes
 int topmin = 0;              // Impulsion topmin toutes les minutes

// Permet de remettre l'horloge à l'heure sur init
 int heure = 14;              // Heures de l'horloge >>>> Permet de remettre l'horloge à l'heure sur init
 int minute = 35;             // Minutes de l'horloge >>> Permet de remettre l'horloge à l'heure sur init
 int seconde = 0;             // Secondes de l'horloge

 //Tableau de variable
 long tableauDonnees[5] = {0};         //Déclare un tableau à 5 lignes
 
   // Valeurs du tableau de variable qui doivent être reçues de la carte emettrice
 long var0;          // Doit recevoir la valeur 999999999 (valeur fixe)
 long milli;         // Doit recevoir la valeur millis() de la carte emettrice (variable)
 long posServo1;     // Doit recevoir la position du servo 1 (10 à 130) (variable)
 double pH = 0.00;   // Doit recevoir la mesure de pH (variable)
 long var4;          // Doit recevoir la valeur 111111111 (valeur fixe)
   
 
void setup() {
 Serial.begin(19200);
 Serial1.begin(19200);
 
// Pull-up de la liaison série
 digitalWrite(18, HIGH);  
 digitalWrite(19, HIGH);
 
} //FIN de SETUP


 long recevLong() {    
   while (Serial1.available() < 4 );  
     long recu = ((unsigned long)Serial1.read() | ((unsigned long)Serial1.read() << 8 ) | ((unsigned long)Serial1.read() << 16) | ((unsigned long)Serial1.read() << 24));
    return (recu);
   }


void loop() {              
 
   if (recevLong()) {  
      int r;      
      for (r = 0; r < 4; r = r + 1){       // Passer à  'r < 5' ne permet pas de renseigner le champ [0]. Et les valeurs suivantes sont erronées.
        tableauDonnees[r + 1] = recevLong(); // Et du coup, afin de pouvoir renseigner les bonnes cases du tableau, je dois passer à r + 1
         }
     }
   
    var0 =      tableauDonnees[0] ;
    posServo1 = tableauDonnees[1] ;
    milli  =    tableauDonnees[2] ;
    pH =        tableauDonnees[3] / 100.00;
    var4 =      tableauDonnees[4];
   
         // LIAISON SERIE POUR TESTS
    if (topsec) {  
       Serial.println(var0);
       Serial.println(posServo1);
       Serial.println(milli);
       Serial.println(pH);
       Serial.println(var4);
       Serial.println("fin");
       Serial.println();    
   }
     
 
  topsec = 0;
  topmin = 0;

     // HORLOGE
     if (millis() >= refTemps){
        refTemps += 1000;
        seconde++;           // Incrémentation des secondes
        topsec = 1;          // Monte le bit topsec toutes les secondes
       if (seconde >= 60) {  // à chaque 60 secondes
           minute++ ;        // Incrémentation des minutes
           seconde = 0;
           topmin = 1;       // Monte le bit topmin toutes les minutes
         if (minute >= 60) {
             heure++ ;       // Incrémentation des heures
             minute = 0 ;
           if (heure >=24) {
                 heure = 0 ; // réinitialise l'horloge toutes les 24h
              }
             }
            }
           }
               
/*    
    // POUR TEST réception Port Serial1 et transfert sur port Uart          
   if (Serial1.available()) {
      long ValEntree = Serial1.read();
      Serial.print(ValEntree);
     }

*/
     
} // FIN de LOOP


Puis-je mieux faire ?

skywodd

Tu as fait une erreur dans la partie réception :
Code: [Select]
if (recevLong()) { 
  int r;       
  for (r = 0; r < 4; r = r + 1){       // Passer à  'r < 5' ne permet pas de renseigner le champ [0]. Et les valeurs suivantes sont erronées.
    tableauDonnees[r + 1] = recevLong(); // Et du coup, afin de pouvoir renseigner les bonnes cases du tableau, je dois passer à r + 1
  }
}

Le if(recevLong()) te "mange" le 1er long reçu donc par la suite tout ta réception est faussé ;)

Code: [Select]
if (tableauDonnees[0] = recevLong()) {     
  for (int r = 1; r < 5; r++){
    tableauDonnees[r] = recevLong();
  }
}
Des news, des tutos et plein de bonnes choses sur http://skyduino.wordpress.com !

Bubule

:smiley-eek: :smiley-eek: :smiley-eek:

Bonjour

Je ne pensais pas que l'on pouvait écrire un truc pareil dans une condition
if(tableauDonnees[0] = recevLong()) {     
 

Et çà marche en plus !!!

Merci dieux

skywodd


Je ne pensais pas que l'on pouvait écrire un truc pareil dans une condition
if(tableauDonnees[0] = recevLong()) {     
 
Et çà marche en plus !!!

C'est une assignation conditionné ;) 
Ya plein de petites astuces dans ce genre pour réduire la taille d'un programme tout en gardant un maximum de lisibilité ;)
(Pour dire vrai ya tellement d'astuces dans ce genre que ce serais impossible de toute les lister :smiley-sweat:)


Merci dieux

heu ... pas de quoi :smiley-mr-green:
Des news, des tutos et plein de bonnes choses sur http://skyduino.wordpress.com !

Bubule

Bonjour

Bon, maintenant que les transferts sont correctement envoyés, reçus, et répartis dans les bonnes variables, je suis en train de libérer les ports Uart (Serial), au profit des ports Softserial.

J'ai donc inclus la librairie SoftSerial qui correspondrait à la librairie NewSoftSerial depuis l'IDE V0.1.

La liaison fonctionne sur ma Duemilanove, mais j'ai une interaction avec la librairie « servo ».  Enfin c'est-ce que je suppose car, à chaque envoi de données sur le port (RX pin 4 / Tx pin 5), les servos raccordés sur les pin 10 et 11, sont parasités. C'est-à-dire qu'ils effectuent un saut de quelques degrés (avec retour à la position) en une fraction de seconde. Cela est suffisamment bruyant pour être gênant.

Ce parasite intervient à chaque envoi des données. Soit toutes les secondes.

Avez-vous déjà remarqué ce phénomène ?

skywodd


J'ai donc inclus la librairie SoftSerial qui correspondrait à la librairie NewSoftSerial depuis l'IDE V0.1.
(...)
Ce parasite intervient à chaque envoi des données. Soit toutes les secondes.
Avez-vous déjà remarqué ce phénomène ?

SoftSerial (et par extension NewSoftSerial) utilise l'interruption d'un timer pour fonctionner, de même pour Servo, par conséquent quand SoftSerial transmet des données l'interruption de Servo est mis en pause ... essaye de mettre sei(); avant l'envoi des données, ça va relancer l'interruption de Servo le probléme c'est que ya de forte chance que ça fasse planter les timings de SoftSerial du coup ...
Des news, des tutos et plein de bonnes choses sur http://skyduino.wordpress.com !

Bubule

Ça ne change rien. J'ai tenté de mettre l'instruction sei(); à différents endroits (dans le loop, juste avant l'appel de la fonction d'envoi, dans la fonction avant les 4 port45.write(....) ). J'ai toujours le même parasite.

Ça confirme un peu ce que je pensais. Bien que je pensais plus à un pb d'interruption.

Je me dis maintenant que la solution serait peut-être de travailler sur d'autres timer, mais comme j'ai modifié les prescaler des autres timer pour augmenter la fréquence des PWM afin de limiter le bruit des moteurs de pompes. C'est sans doute impossible de s'en sortir.

Je dois avouer que je suis un peu déçu par le procédé µC (très certainement parce que je ne maitrise pas tout). Une application fonctionne bien, et puis on y ajoute d'autres applications, puis encore d'autres. Au final, on se retrouve avec des interactions gênantes.

J'espère que la solution de remplacement de la Duemilanove par la mega sera la solution à tous ces problèmes. Mais l'utilisation des ports Serial 1, 2 et 3 ne posera-t-il pas le même soucis ?

skywodd


Ça confirme un peu ce que je pensais. Bien que je pensais plus à un pb d'interruption.

C'est un probléme de conflit entre l'interruption du timer utilisé par la lib Servo et l'interruption du timer utilisé par la lib SoftSerial.


Je me dis maintenant que la solution serait peut-être de travailler sur d'autres timer, mais comme j'ai modifié les prescaler des autres timer pour augmenter la fréquence des PWM afin de limiter le bruit des moteurs de pompes. C'est sans doute impossible de s'en sortir.

Le probléme c'est qu'il n'est pas possible d'avoir deux taches simultanément, donc impossible d'avoir deux interruptions simultanément ...
Lorsque SoftSerial bloque le µc pour envoyer les données l'interruption de Servo passe à la trappe ...


Je dois avouer que je suis un peu déçu par le procédé µC (très certainement parce que je ne maitrise pas tout). Une application fonctionne bien, et puis on y ajoute d'autres applications, puis encore d'autres. Au final, on se retrouve avec des interactions gênantes.

Faut pas être déçu, c'est ça la programmation embarqué, ya pas de multitâche sur les micro-contrôleur faut faire avec ;)
L'utilisation de SoftSerial est vraiment obligatoire ? Tu ne peut pas rester en Serial hardware ?
Sinon tu peut laisser de coté la lib Servo est utilisé les broches PWM hardware ce qui réglerai le probléme :
http://www.societyofrobots.com/member_tutorials/node/231


J'espère que la solution de remplacement de la Duemilanove par la mega sera la solution à tous ces problèmes. Mais l'utilisation des ports Serial 1, 2 et 3 ne posera-t-il pas le même soucis ?

Serial, Serial1, Serial2, Serial3 sont des ports série hardware qui fonctionne sans avoir besoin d'interruption, pas d'incompatibilité donc.
Des news, des tutos et plein de bonnes choses sur http://skyduino.wordpress.com !

zeric

Bonjour,
J'en rajoute un peu, comme ça concerne du transfert de données par la laison série:
Perso, je fais que du bricolage (débutant), j'aimerais donc bien avoir l'avis de qqun qui connait le "C" pour améliorer mon prog de transfert de String (propre  :smiley-mr-green:)

Code: [Select]
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //init lib (keypad shield)
String hein = ""; //you talking to me?

void setup(){
Serial.begin(9600); //régler SerialMonitor sur [NewLine]
lcd.begin(16, 2);
lcd.print("Test RX");
}

void decode_rx(String quoi){ //anlyse fct
while (Serial.available() > 0){ //data on RX
  char lu = Serial.read(); //read 1° byte & put in variable
if (lu == 10){ //char newline?
hein = ""; //RAZ
break; //get out of while
}
else hein += char(lu); //char not a newline, add char to String
}
}

void loop(){
decode_rx(hein); //ma, que volio? (humour)
if (hein == "Oups"){
Serial.print("mince\n");
lcd.setCursor(0,1);
lcd.print("mince alors    ");
}
if (hein == "ben_alors"){
Serial.print("ben vrai\n");
lcd.setCursor(0,1);
lcd.print("mon pov' msieur");
}
}


Si je veux que ça fonctionne avec un programme plus long (qqKO), je suis obligé de rajouter une nouvelle variable "String"
que je "reset" au moment voulu dans le prog, car, comme je la "RAZ" dès le code "\n" reçu, elle part vite au paradis des données ! :smiley-red:


skywodd

@zeric :
Code: [Select]
// Exemple d'appel :
// String str;
// decode_rx(&str);

void decode_rx(String *str){
  *str = ""; // Init String
  char c; // Declare temp char
  while(Serial.available() < 1); // Block until Serial data available
  c = Serial.read(); // First read
  while(Serial.available() > 0 && c != '\0'){ // Serial test && char test
if(c == 10)
  *str = ""; // Reset string
else
  *str = *str + c; // Append char to string
c = Serial.read(); // Next read
  }
}


Version plus propre sans String utilisant un tableau de char :
Code: [Select]
void loop() { // Exemple d'appel
  char str[10];
  if(Serial.available() > 0)
    decode_rx(str, 10);
}

void decode_rx(char str[], int max){
  memset(str, 0, max);
  int i = 0;
  while(Serial.available() > 0 && i < (max - 1)){
    if(str[i] = Serial.read())
      i++;
    else
      break;
  }
}

Des news, des tutos et plein de bonnes choses sur http://skyduino.wordpress.com !

zeric

Merci beaucoup  :smiley-eek-blue: , mais je dois apprendre les pointeurs :
& reference operator
* dereference operator
et voir l'instruction "memset" avant d'assimiler tout ça ...
Je reviens quand j'ai lu les explications !

zeric


@zeric :
Code: [Select]
// Exemple d'appel :
// String str;
// decode_rx(&str);

void decode_rx(String *str){
  *str = ""; // Init String
  char c; // Declare temp char
  while(Serial.available() < 1); // Block until Serial data available
  c = Serial.read(); // First read
  while(Serial.available() > 0 && c != '\0'){ // Serial test && char test
if(c == 10)
  *str = ""; // Reset string
else
  *str = *str + c; // Append char to string
c = Serial.read(); // Next read
  }
}


Bonjour, pour l'instant je me suis attaqué à la 1° solution:
Pourquoi bloquer le programme tant qu'on ne reçoit pas de caractère ?   =>while(Serial.available() < 1); // Block until Serial data available
Pourquoi tester le caractère '\0' qui correspond au code NULL?
Pourquoi initialiser la valeur de str dans la fonction alors que l'on gagne 36 octets de prg en le faisant dans les définitions =>   *str = ""; // Init String 1°ligne de la fct
La fonction n'est pas linéaire, on ne fait rien d'autre tant que l'on n'est pas sorti de la 2° boucle !
Ne peut-on pas faire une addition composée sur une valeur pointée et gagner 126 octets de prg ?  =>*str = *str + c; // Append char to string  =>(*str += c;)
Euh, une dernière remarque  :smiley-roll-sweat: ça ne fonctionne pas :smiley-zipper:

Go Up