Go Down

Topic: serialEvent() (Read 6613 times) previous topic - next topic

manuo1

Bonsoir,
j'ai du mal à trouvé des tuto sur l'utilisation de serialEvent (ceux que je trouve sont pour processing) donc je commence par la base de base donc savoir quand se déclenche serialEvent.

Sur arduino.cc

Quote
serialEvent()
Description
Called when data is available. Use Serial.read() to capture this data.
j'ai une trame télé info EDF qui se déroule sur le port RX3 de ma Mega donc normalement il y a des données disponibles donc "déclenchement" de serialevent non ?

Je voulais tester si serialEvent "se déclenche" avec ce sketch :

Code: [Select]
void setup() {
  Serial.begin(9600);  // Port série pour liasion Arduino <-> PC
  Serial3.begin(1200); // Port série pour liasion Arduino <-> Teleinfo
}

void loop() {
}

void serialEvent3() {

  Serial.print("SerialEvent OK");
 
}


cela compile mais pas de "SerialEvent OK" dans la console  :'(

Ou est-ce que je me trompe ?

infobarquee

bonjour,
tu peux t'inspirer de ceci pour mieux comprendre
http://arduino.cc/en/Tutorial/SerialEvent
AUCUNE AIDE PAR MP

fdufnews

Le tutorial proposé par infobarquee présente exactement le principe de ce que tu cherches à faire.
A savoir accumuler les caractères reçus jusqu'à trouver un code marquant la fin de ligne et signaler cette fin de ligne en positionnant un drapeau.
Il ne faut pas oublier de prévoir une phase de synchronisation qui recherche le début de trame à l'initialisation (ou après reprise suite à une perte de liaison)

manuo1

Bonsoir,

Merci pour le lien infobarquee j'avais justement commencé par cette exemple en le modifiant pour mon cas :

Code: [Select]

String inputString = "";         // string pour stocker la suite de caractere recus
boolean stringComplete = false;  // pour savoir si la ligne et complette (si inChar=0x0D  / 0x0D = retour à la ligne)

void setup() {
  // Port série pour liasion Arduino <-> PC
  Serial.begin(9600); 
  // Port série pour liasion Arduino <-> Teleinfo
  Serial3.begin(1200);
  // reserve 200 bytes pour inputString:
  inputString.reserve(200);
}

void loop() {
  // si la ligne est complette
  if (stringComplete) {
    //imprime la ligne
    Serial.println(inputString);
    // RAZ pour recevoir nouvelle ligne
    inputString = "";
    stringComplete = false;
  }
}

void serialEvent3() {
  while (Serial3.available()) {
    // stock le nouveau caratere recu
    char inChar = (char)Serial3.read() & 0x7F; //'& 0x7F' => trame teleinfo en 7bit
    // concatenne les carateres recu dans inputString:
    inputString += inChar;
    // si le caratere recu est un retour à la ligne
    // previent que la ligne est complette
    if (inChar == 0x0D) {
      stringComplete = true;
    }
  }
}


Mais comme je ne sortais rien sur la console je voulais partir de la base c'est a dire déjà savoir quand et comment se déclenche serialevent.

Par contre ce que je ne saisi pas c'est le rôle de la phase de synchronisation que tu me suggère fdufnews.

Si la synchro permet d'être sur d'avoir une trame complète je peux ignorer les trames incomplète en vérifiant avec le checksum de fin de ligne mais si elle a un autre rôle je ne saisi pas.

infobarquee

si tu fais pas un serial.println(quelque chose)
tu ne sauras pas si ca arrive ou non ;)

ce que veux dire fdufnews, c'est que tu dois comparer si le début de la trame correspond ou non à ce qui doit être le début et vérifier aussi pour la fin.
un checksum est faisable.

je n'ais jamais utilié cette fonction, donc je ne la connais que par les lignes.
AUCUNE AIDE PAR MP

manuo1

ben j'ai bien un serial.print dans les deux sketch ?
je n'ai même que ça dans mon premier post pour voir si serialevent déclenche quelque chose  :-\

et donc la synchro c'est juste pour avoirdes trames complettes ? si je zap cette partie cela n'emperchera pas mon sketch d'afficher au moins une partie de la trame ?

fdufnews

La synchro c'est pour avoir des groupes d'informations complèts.
Si tu regardes le protocole décrit dans le document d'EDF, les trames commencent par STX (0x02) puis ensuite les groupes d'informations qui commencent par LF (0x0A) et se termine par CR (0x0D) à la fin d'une trame tu reçois un ETX (0x03).
La synchro dont je parlais c'est la détection du STX afin d'être certain par la suite de recevoir des groupes d'informations cohérents.
Cela répond à 2 besoins:
   - la facilité d'interprétation, il est plus simple d'interpréter les groupes si on est certain que le début de celui-ci est bien situé au début du buffer de réception
   - cela permet d'optimiser la taille du buffer de réception en le dimensionnant de telle manière qu'il soit capable de stocker le plus long message et pas plus (l'arduino a relativement peu de mémoire RAM)

Pour que ton code fonctionne, il faut déclarer stringComplete  comme volatil car sinon l'optimisation fait que le contenu de la variable n'est pas systématiquement lu dans loop puisque la boucle ne modifie pas l'état de la variable hors du if.

ATTENTION, pour que Serial.event fonctionne loop() ne doit pas être bloquante car en fait, Serial.event() n'est pas appelée à chaque réception de caractère mais à chaque itération de loop()
Le code du main() généré par l'IDE
Code: [Select]
int main(void)
{
init();

#if defined(USBCON)
USBDevice.attach();
#endif

setup();
   
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
       
return 0;
}

manuo1

OK la synchro c'est bien ce que je pensais et on va dire que le traitement de stringComplete viendra après j'aimerais déja juste sortir quelque chose sur la console en utilisant serialevent.

pour en revenir au dernier exemple j'ai tester :

Code: [Select]
volatile boolean stringComplete = false;

mais cela ne change rien
Dans le même raisonnement je pense, j'ai ajouter aussi

Code: [Select]

if (inChar == 0x0D) {
      stringComplete = true;
      return;
    }


mais pas plus d'effet.

j'en reviens donc à mon idée première : essayer juste de déclencher serialevent avec ca :

Code: [Select]

void setup() {
  Serial.begin(9600);  // Port série pour liasion Arduino <-> PC
  Serial3.begin(1200); // Port série pour liasion Arduino <-> Teleinfo
}

void loop() {
}

void serialEvent3() {

  Serial.print("SerialEvent OK");
 
}


pour déjà être sur que cette fonction "s'active bien" mais je pense avoir simplifier le code à son minimum (trop peut-être) mais si cela ne déclenche pas serialevent cela ne peux faire fonctionner les autres codes.

Est-ce que dans l'absolue le code juste au dessus est correcte ?

fdufnews

Je pense que cela devrait fonctionner.
Maintenant il faut bien voir que ce code risque de saturer l'émission sur Serial car la vitesse sur Serial n'étant que 8x supérieure à celle de Serial3 si ton message fait plus de 8 caractères tu vas saturer le buffer d'émission de Serial. Et lorsque le buffer d'émission est saturé le code de Serial devient bloquant.
Donc je suggérerais de n'envoyer qu'un ou deux caractères au maximum dans serialEvent3()

manuo1

 :(  bon ben toujours pas moyen cela ne fonctionne pas.
Pas de "SerialEvent OK" sur la console.
Soit il me manque quelque chose soit il y a un souci sur ma méga...


Et d'ailleurs même si cela fonctionnait je me demande si cela va améliorer mes temps de fonctionnement ?

se qui m'intrigue c'est le fait que serialevent soit appelé à chaque loop(), donc aucun rapport avec le fait qu'il y ai ou non de données sur le port série.
Donc en gros à chaque loop le sketch exécute serialevent(), lit toute la trame (donc bloque le sketch) jusqu'au caractère de fin de trame et sort de serialevent().
Pas de différence de fonctionnement avec fonction sans serialevent :

Code: [Select]

inChar = Serial3.read() & 0x7F ;
while (inChar != 0x0A){
   if (Serial3.available()){
           inChar = Serial3.read() & 0x7F ;
   }
}

inChar = Serial3.read() & 0x7F ;
while (inChar != 0x0D){
    if (Serial3.available()){
            inChar = Serial3.read() & 0x7F ;
            inputString  += inChar;
    }                     
}


Moi je cherche si c'est possible, à remplir le buffer du port série sans bloquer mon sketch et quand il est plein je le récupère et l'analyse.

C'est possible ça ?

infobarquee

tu n'as pas trop compris le principe apparemment
il faut un déclencheur dans le event pour exécuter ce qu'il doit faire.
ca revient au même que si tu rentre dans ta voiture, tant que tu n'as pas mis la clef et tourné, elle ne démarre pas.

je viens de faire le test et ca fonctionne très bien, par contre dans la console, il faut mettre en bas à coté de la vitesse, les deux NL et CR ou retour chariot
j'ai remplacé le \n par un caractère | dans l'exemple
si le caractère est présent après avoir appuyé sur entrée, ca affiche la ligne entrée et un message

Code: [Select]
String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete

void setup() {
  // initialize serial:
  Serial.begin(9600);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
}

void loop() {
  // print the string when a newline arrives:
  if (stringComplete) {
    Serial.println(inputString);
    // clear the string:
    inputString = "";
    stringComplete = false;
  }
}

/*
  SerialEvent occurs whenever a new data comes in the
 hardware serial RX.  This routine is run between each
 time loop() runs, so using delay inside loop can delay
 response.  Multiple bytes of data may be available.
 */
void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '|') {
      Serial.println("caracter | trouve en fin de phrase");
      stringComplete = true;
    }
  }
}


ce qui donne
Quote
caracter | trouve en fin de phrase
fgfdgfdgfgfdgfdg|  =====> la phrase que j'ai tapé
AUCUNE AIDE PAR MP

fdufnews

Et d'ailleurs même si cela fonctionnait je me demande si cela va améliorer mes temps de fonctionnement ?

se qui m'intrigue c'est le fait que serialevent soit appelé à chaque loop(), donc aucun rapport avec le fait qu'il y ai ou non de données sur le port série.
Donc en gros à chaque loop le sketch exécute serialevent(), lit toute la trame (donc bloque le sketch) jusqu'au caractère de fin de trame et sort de serialevent().
Pas de différence de fonctionnement avec fonction sans serialevent :
Si tu regardes le code de l'exemple du site arduino, Serial.event() regardes s'il y a quelque chose dans le buffer de réception. Si celui-ci est vide il revient immédiatement. S'il y a des caractères il les lit, les place dans un buffer et retourne à moins qu'il ait trouvé un caractère de fin de ligne auquel cas avant de retourner il positionne un drapeau qui sera testé dans loop().
Donc si Serial.event() est bien écrit, il est non bloquant.
Serial.event() n'est pas magique, c'est juste un outil qui incite à écrire proprement du code de gestion de communication en sortant la gestion de la réception de la boucle principal.

manuo1

J'ai testé l'exemple qu'infobarquee a modifié et cela fonctionne bien aussi chez moi.


Effectivement j'avais mal compris le principe du déclenchement avec loop puis si il y a des caractères fais quelquechose ::)

Merci c'était vraiment à cause de ça que je n'y arrivait pas.


Meme si Serialevent doit etre appelé automatiquement sans l'inscrire dans loop() J'ai bêtement testé en ajoutant "serialEvent3();" dans le loop() et mon code fonctionne    ;D   

Mais dans ce cas quelle différence si j'appelle ma fonction autrement que Serialevent ?

J'ai fait le test cela fonctionne de la même façon ...






Code: [Select]

String inputString = "";         // string pour stocker la suite de caractere recus
boolean stringComplete = false;  // pour savoir si la ligne et complette (si inChar=0x0D  / 0x0D = retour à la ligne)

void setup() {
  // Port série pour liasion Arduino <-> PC
  Serial.begin(9600); 
  // Port série pour liasion Arduino <-> Teleinfo
  Serial3.begin(1200);
  // reserve 200 bytes pour inputString:
  inputString.reserve(200);
}

void loop() {
  // si la ligne est complette
  if (stringComplete) {
    //imprime la ligne
    Serial.println(inputString);
    // RAZ pour recevoir nouvelle ligne
    inputString = "";
    stringComplete = false;
  }
serialEvent3();
}

void serialEvent3() {
  while (Serial3.available()) {
    // stock le nouveau caratere recu
    char inChar = (char)Serial3.read() & 0x7F; //'& 0x7F' => trame teleinfo en 7bit
    // concatenne les carateres recu dans inputString:
    inputString += inChar;
    // si le caratere recu est un retour à la ligne
    // previent que la ligne est complette
    if (inChar == 0x0D) {
      stringComplete = true;
    }
  }
}




Go Up