[Résolu] Traiter données port série

Bonsoir à tous !

Je souhaites pour un projet communiquer des donnés à mon arduino via le port série, données provenant d'un shield wifi.

Les exemple fournis sur internet ne montre que des commandes à 1 caractère :

void setup()
{
Serial.begin(9600);
pinMode(13,OUTPUT);
digitalWrite(13,LOW);
}

void loop()
{
while (Serial.available() != 0)
{
switch (Serial.read())
{
case 'H':
digitalWrite(13,HIGH);
break;
case 'L':
digitalWrite(13,LOW);
break;
}
}
}

Cet exemple fonctionne mais est très restreint car je souhaiterais envoyer à l'arduino des commandes beaucoup plus longues contenant entre autre des int et des float.

Voila un petit script que j'ai réalisé mais je bloque car travailler avec char ne me permet pas d'utiliser switch et un ifelse ne fonctionne pas non plus...

while (Serial.available() == 12){

for(i=0;i<12;i++){
data = Serial.read();

  • }*
  • char para1[5] = {data[0],data[1],data[2],data[3]}; //PARAMÈTRE 1*
  • char para2[5] = {data[4],data[5],data[6],data[7]}; //PARAMÈTRE 2*
  • char para3[5] = {data[8],data[9],data[10],data[11]}; //PARAMÈTRE 3*
    }
    [/quote]
    N'y a-t-il pas une façon plus simple de recevoir des données ?
    J'avoue que la je bloque sur un truc qui parait simple...

bonsoir,
je pense à un truc genre :

String readString; 
void setup() 
{
  Serial.begin(9600);

}
void loop() 
{
readString="";
      do                                 
      {
        char c = serial.read(); 
        readString += c;
 
        if(!Serial.available())
        {
          if(readString.indexOf("L") >0)
            {
            digitalWrite(13, LOW);            
            }
          else if(readString.indexOf("H") >0)
            {
            digitalWrite(13, HIGH);          
            }   

        }         }
      while (Serial.available() != 0);


}

:slight_smile:

Bonsoir

peut être que mon tuto sur les protocoles de communication peux t'aider ?
http://arduino.cc/forum/index.php/topic,102540.0.html

Merci !

Je vais jeter un oeil à tous ça je constate que faire communiquer l'aduino vers l’extérieur reste relativement complexe...

elriri:
Merci !

Je vais jeter un oeil à tous ça je constate que faire communiquer l'aduino vers l’extérieur reste relativement complexe...

Dans la pratique, c'est assez simple:

  1. Tant qu'il y a quelque chose dans le buffer du port série
  2. Lire 1 caractère dans le buffer série (le buffer 'avance' tout seul d'un cran. C'est à dire qu'il 'éjecte' le caractère lu)
  3. Si ce caractère lu n'est pas le caractère choisi de fin de séquence, le concaténer à la chaine
  4. sinon, on boucle
  5. Traiter la chaine (sscanf par exemple)

exemple (moche)

char Buf;   // 1 caractère
String Ligne;  // La ligne de stockage

void setup()
{
  Serial.begin(9600);
  Serial.println("Debut transmission");
  // ... etc
}

void loop()
{
  while (Serial.available() > 0)   // tant qu'il y a à manger sur le port série
  {
    Buf = Serial.read();  // lire 1 caractère dans le buffer serie
    Serial.print(Buf);   // afficher le caractère (sert à rien, juste à contrôler sur le moniteur série)
    Ligne = Ligne + Buf;  // concaténer le caractère dans la chaine (peut aussi s'écrire:  Ligne += Buf c'est plus lisible)
    if (Buf == '*')   // --- Ici, j'utilise l'* comme caractère de fin de séquence.
    { 
      Serial.print("Sequence: "); 
      Serial.println(Ligne);
      // Traitement avec sscanf pour extraire les données de la ligne
    }
}
Serial.end();
}

À adapter XD

Merci Marc ! En faite c'est surtout la fonction sscanf que je ne connaissais pas qui va m'être utile !

Edit :

Serial.end(); empêche l'affichage dans le terminal.

sscanf(); me fait plein de conflit entre String Char et Int

char Buf; // 1 caractère
String Ligne; // La ligne de stockage
float val1;
float val2;
float val3;
void setup()
{
Serial.begin(9600);

}

void loop()
{
while (Serial.available() > 0) // tant qu'il y a à manger sur le port série
{
Buf = Serial.read(); // lire 1 caractère dans le buffer serie
if (Buf == '') // --- Ici, j'utilise l' comme caractère de fin de séquence.
{
Serial.print("Sequence: ");
Serial.println(Ligne);

sscanf(Ligne, "%d, %d, %d",&val1, &val2, &val3);

}else{
Ligne = Ligne + Buf;
}
}
//Serial.end();
}

Ce code me retourne ce message d'erreur à chaque compilation :

sketch_jul09a:21: error: cannot convert 'String' to 'const char*' for argument '1' to 'int sscanf(const char*, const char*, ...)'

Pourtant à aucun moment sscanf utilise de char.... je capte rien

sscanf(Ligne, "%d, %d, %d",&val1, &val2, &val3);

Il y a 2 problèmes dans cette ligne :

val1, val2, val3 sont déclarés en float (nombres à virgules) alors que %d fait reference a des entiers.

De plus sscanf prend en premier parametre un pointeur sur char.

Donc tu peux modifier ton code par exemple de cette façon :

char Buf;   // 1 caractère
char Ligne[80];  // La ligne de stockage 79 caractères au maximum
int val1, val2, val3;  // trois valeurs entieres

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

}

void loop()
{
  while (Serial.available() > 0)   // tant qu'il y a à manger sur le port série
  {
    Buf = Serial.read();  // lire 1 caractère dans le buffer serie
    if (Buf == '*')   // --- Ici, j'utilise l'* comme caractère de fin de séquence.
    { 
      Serial.print("Sequence: "); 
      Serial.println(Ligne);
      
      sscanf(Ligne, "%d, %d, %d",&val1, &val2, &val3);
      
    }else{
     strcat(ligne, buf);  // strcat ajoute à ligne la chaine buf
    }
}
//Serial.end();
}

Il y a peut-être encore des erreurs, je n'ai pas testé ce code

De plus, d'après ce que je googlise depuis 1/4 d'heure, sscanf version Arduino a des problèmes pour traiter le type float (qui s'utilise d'habitude avec un simple %f (et pas %d qui est pour un entier, comme indiqué))

Il semble que certains traitent le problème en deux fois

  • extration en format chaine (%s)
  • conversion en float (atof)

un lien http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1164927646
Un café et j'y retourne :smiley:
Tu l'auras ton float XD (enfin si c'est bien un décimal que tu voulais ?)

Café bu, code testé :open_mouth:

char Buf;   // 1 caractère
char Ligne[80];          // La ligne de stockage 79 caractères au maximum
float val1, val2, val3;  // trois valeurs décimales
char valtxt1[10], valtxt2[10], valtxt3[10];
int i;

void setup()
{
  Serial.begin(9600);
}



void loop()
{
  while (Serial.available())   
  {
    Buf = Serial.read(); 
    Ligne[i] = Buf; 
    i++;
    
      if (Buf == '*')   
      { 
        Ligne[i] = '\0';
        Serial.print("Sequence: "); Serial.println(Ligne);
        
        sscanf(Ligne, "%s %s %s", &valtxt1, &valtxt2, &valtxt3);
        
        val1 = atof(valtxt1);
        val2 = atof(valtxt2);
        val3 = atof(valtxt3);
        
        Serial.print("Val1: ");   Serial.println(val1);    
        Serial.print("Val2: ");   Serial.println(val2); 
        Serial.print("Val3: ");   Serial.println(val3);  
        i = 0;
      }
}
}

Pas bien beau, mais j'espère didactique :grin:
(à améliorer)

Résultat

Sequence: 1.23 4.56 7.89*
Val1: 1.23
Val2: 4.56
Val3: 7.89

Merci c'est exactement ce dont j'ai besoin !

Je découvre le C alors que je programme uniquement en PHP où pour la même chose un simple explode(' ',$var) aurait suffit !

Merci encore

Bonjour,

elriri:
Je vais jeter un oeil à tous ça je constate que faire communiquer l'aduino vers l’extérieur reste relativement complexe...

Tout dépend du protocole utilisé, textuelle ou binaire.
Tu travail en textuelle ici (avec du texte) ce qui est le plus compliqué, avec du binaire tu aurais juste eu quelques lignes de code.
Mais côté PC cela aurait etait plus compliqué pour transmettre des commandes binaire (pas de serial monitor, obligation d'utiliser un terminal série ou un prog maison).

elriri:
Serial.end(); empêche l'affichage dans le terminal.

NON !
Serial.end() désactive le port série, qu'il faut ensuite réactiver par un nouveau Serial.begin().
En aucun cas cette fonction ne permet "d'empécher" l'affichage dans le terminal.

Disables serial communication, allowing the RX and TX pins to be used for general input and output. To re-enable serial communication, call Serial.begin().

elriri:
Pourtant à aucun moment sscanf utilise de char.... je capte rien

"Ligne" est un objet de type String, pas une chaine de caractére.
Il faut d'abord utiliser l'opérateur String.toCharArray() pour copier les caractéres dans un vrai tableau de char :

Exemple :

char buf[64];
Ligne.toCharArray(buf, 64);
sscanf(buf, "%d, %d, %d",&val1, &val2, &val3);

Marc56:
De plus, d'après ce que je googlise depuis 1/4 d'heure, sscanf version Arduino a des problèmes pour traiter le type float (qui s'utilise d'habitude avec un simple %f (et pas %d qui est pour un entier, comme indiqué))

Pas seulement la "version arduino", la version de base de scanf, sprintf, etc ne gére pas les float car cela augmente considérablement la taille du programme.
Pour activer la version avec support des nombres flottants il faut normalement ajouter les options compilateurs suivantes :

-Wl,-u,vfprintf -lprintf_flt -lm

Par défaut sans support des flottants les options sont :

-Wl,-u,vfprintf -lprintf_min

Comme il est impossible d'ajouter / supprimer des options via l'ide arduino tu n'as pas le choix de la version de sprintf, scanf, etc ...

Marc56:
Il semble que certains traitent le problème en deux fois

  • extration en format chaine (%s)
  • conversion en float (atof)

Un float n'est rien d'autre que deux int séparé par un point !

Beaucoup de monde ce casse la tête pour rien :

int entier, flottant;
scanf(buf, "%d.%d", &entier, &flottant);

Aprés tu rassemble la partie entiére et la partie flottante dans un int (et t'oublie pas de diviser la partie flottante par une puissance de 10 flottante)

elriri:
Je découvre le C alors que je programme uniquement en PHP où pour la même chose un simple explode(' ',$var) aurait suffit !

PHP gère les tableaux de manière trés particulière et sans typage ce qui facilite beaucoup de chose.

Merci pour le complément d'infos skywodd. Je prends note !

elriri:
Merci pour le complément d'infos skywodd. Je prends note !

Pas de quoi, si tu as d'autre chose sur lesquelles tu veut des info n'hésite pas :wink:

Bonjour,

Ce code :

char Buf;   // 1 caractère
char Ligne[80];          // La ligne de stockage 79 caractères au maximum
float val1, val2, val3;  // trois valeurs décimales
char valtxt1[10], valtxt2[10], valtxt3[10];
int i;

void setup()
{
  Serial.begin(9600);
}



void loop()
{
  while (Serial.available())   
  {
    Buf = Serial.read(); 
    Ligne[i] = Buf; 
    i++;
    
      if (Buf == '*')   
      { 
        Ligne[i] = '\0';
        Serial.print("Sequence: "); Serial.println(Ligne);
        
        sscanf(Ligne, "%s %s %s", &valtxt1, &valtxt2, &valtxt3);
        
        val1 = atof(valtxt1);
        val2 = atof(valtxt2);
        val3 = atof(valtxt3);
        
        Serial.print("Val1: ");   Serial.println(val1);    
        Serial.print("Val2: ");   Serial.println(val2); 
        Serial.print("Val3: ");   Serial.println(val3);  
        i = 0;
      }
}
}

est très bien, meric pour ce code, ça fait longtemps que je veux manipuler les chaînes de caractères avec arduino, mais pour mon projet il faudrait que la séquence contiennent aussi du texte ou du moins des lettres genre a b c d.... ce qui correspondrait à ue instruction puis les flottants derrières constituraits la valeur, par exemple si mon code python envoi arduino.write('C 24.25*') l'arduino doit extraire val1 et val2 et voir que val1=C et val2=24.25 puis après une comparaison if(val1==B) arduino comprend qu'il faut mettre la consigne de la chaudière à 24.25°C, comment prendre en charge du texte dans la séquence ?

un grand merci d'avance

au lieu de t'embeter avec des lettres tu choisit des valeurs style 1;2;3 ou si ca te dérange car c'est trop près des températures voulu 55;56;57 etc comme ça tu peux réutiliser le code direct

J'ai essayé mais quand j'envoi 1 arduino affiche 1.00 (surement à ause du float) et les conditions ne marche pas, par exmeple

char Buf;   // 1 caractère
char Ligne[80];          // La ligne de stockage 79 caractères au maximum
float val1, val2, val3;  // trois valeurs décimales
char valtxt1[10], valtxt2[10], valtxt3[10];
int i;

void setup()
{
  Serial.begin(9600);
}



void loop()
{
  while (Serial.available())   
  {
    Buf = Serial.read(); 
    Ligne[i] = Buf; 
    i++;
    
      if (Buf == '*')   
      { 
        Ligne[i] = '\0';
        Serial.print("Sequence: "); Serial.println(Ligne);
        
        sscanf(Ligne, "%s %s %s", &valtxt1, &valtxt2, &valtxt3);
        
        val1 = atof(valtxt1);
        val2 = atof(valtxt2);
        val3 = atof(valtxt3);
        
        Serial.print("Val1: ");   Serial.println(val1);    
        Serial.print("Val2: ");   Serial.println(val2); 
        Serial.print("Val3: ");   Serial.println(val3);  
        i = 0;
	if(val1 == '1' && val2 == '2')
	{
	Serial.print("Condition reconnu !");
	}
      }
}
}

"Condition reconnu !"' ne s'affiche jamais...