Extraire des caractères d'une trame RS232

Bonjour,

Un système de mesure m'envoi une trame RS232 avec 40 caractères.
Je souhaite mettre en evidence sur un afficheur les caractères 10 à 16 (par exemple).

Je sais gérer toute la partie afficheur, je ne sais pas comment faire pour extraire ces caractères.

Je pense que ca ne doit pas être très compliqué pour des experts.

J'espère recevoir un peu d'aide.

Merci

Bonsoir,
Regarde du côté de la librairie string.h
Un exemple parmi d'autres:

Très bien.

Effectivement, en combinant ces informations avec la gestion du RS232, je pense arriver au but.

Merci pour ton aide !!

Bonjour manu-428

Avec la fonction chaine40caracteres. substring(from) ou chaine40caracteres. substring(from, to)

Cordialement
jpbbricole

J'aimerais attirer l'attention de manu-428 sur le fait que l'utilisation de String peut potentiellement provoquer de la fragmentation mémoire sur un ARDUINO, y compris MEGA, on ne le répétera jamais assez.
S'il s'agit d'un ESP8266 ou ESP32 c'est différent.

D'autre part "une trame RS232 avec 40 caractères" ne veut strictement rien dire. Il peut s'agir de binaire, d'hexa, d'ASCII.
Un exemple serait le bienvenu.

Si la trame fait exactement 40 caractères de longueur, la lire avec :
int n = Serial.readBytes(buffer, 40);
buffer est une C string : char buffer[40];
n indiquera la longueur effectivement reçue.

Ensuite il suffit d'extraire les 7 chars qui t'intéressent :

char display[10];
strncpy(display, buffer, 7);
display[7] = '\0';
lcd.print(display);

Cela sous-entend que ces caractères soient imprimables.

L'autre moyen est de pré-dimensionner les Strings :

String buffer;
buffer.reserve(40);      // pour la trame
String subString;          
subString.reserve(10); // pour la donnée extraite

Placer buffer et subString en données globales (pas dans les fonctions).
Cela évitera les allocations mémoire à répétition.

Avant de parler de la réception de la trame, il conviendrait d'avoir une idée précise de son format :

  • présence d'un caractère de début, et / ou d'un caractère de fin
  • ou simplement 40 données brutes sans délimiteur

Dans le premier cas, s'il y a un caractère de fin, la lecture peut être effectivement réalisée dans une String (pré-dimensionnée) à l'aide de Serial.readStringUntil() ou dans une C string à l'aide de Serial.readBytesUntil()
Dans le deuxième cas, Serial.readBytes() devra être utilisé, car il n'existe pas d'équivalent Serial.readString() permettant de lire N caractères.

Mais pour en dire plus il faudrait connaître le format de la trame, et donc préciser la demande.

La trame est de ce type :

<01>: 103.1 mbar:(PB): 13.2cm3/mn

Je souhaite récupérer la valeur 13.2 cm3/mn.

Cette trame est récupérée via le moniteur série en 9600

Bonjour manu-428

ainsi:

	String valeurEntree = "<01>: 103.1 mbar:(PB): 13.2cm3/mn";
	String valeurExtraite = valeurEntree.substring(22);
	Serial.println(valeurExtraite);

Comme le texte recherché est en fin de chaîne, seul le début (22) est nécessaire.
On pourrait "affiner" en positionnant le texte recherché par un

valeurEntree.indexOf("(PB):") + 5

Cordialement
jpbbricole

Il faudrait en premier lieu s'assurer de la présence du début de ligne, parce que si la trame est incomplète, aller y rechercher une information ne rime à rien.
Les caractères "<01>" sont-ils toujours présents ?
Le numéro 01 est-il fixe ou évolue t-il ?

D'autre part, je suppose que la valeur 103.1 mbar n'a pas forcément une longueur fixe.
Aller chercher une sub-string à une position X n'est pas la solution.

Bonjour hbachetti

C'est bien pourquoi j'ai proposé:

En général, les appareils de mesures envoient des chaînes avec des valeurs positionnées toujours à la même place, ce qui en facilite l'extraction.

Cordialement
jpbbricole.

Pour ma part j'attends les réponses à #10.

En attendant, petit exercice avec la librairie REGEXP de Nick Gammon (installable depuis le gestionnaire de bibliothèques) :


#include <Regexp.h>

char trame[] = "<01>: 103.1 mbar:(PB): 13.2cm3/mn";

void match_callback  (const char * match,          // matching string (not null-terminated)
                      const unsigned int length,   // length of matching string
                      const MatchState & ms)      // MatchState in use (to get captures)
{
  char cap [10];   // must be large enough to hold captures

  Serial.print ("Matched: ");
  Serial.write ((byte *) match, length);
  Serial.println ();
  for (byte i = 0; i < ms.level; i++)
  {
    Serial.print ("Capture ");
    Serial.print (i, DEC);
    Serial.print (" = ");
    ms.GetCapture (cap, i);
    Serial.println (cap);
  }
}

void setup() {
  MatchState ms (trame);
  unsigned long count;
  
  Serial.begin(115200);
  count = ms.GlobalMatch("<([0-9]+)>: ([0-9.]+) ([a-z]*):.([A-Z]*).: ([0-9.]+)([a-z0-9.\/]*)", match_callback);
  Serial.print("Found ");
  Serial.print(count);
  Serial.println(" matches.");
}

void loop() {
}

Exécution :

Matched: <01>: 103.1 mbar:(PB): 13.2cm3/mn
Capture 0 = 01
Capture 1 = 103.1
Capture 2 = mbar
Capture 3 = PB
Capture 4 = 13.2
Capture 5 = cm3/mn
Found 1 matches.

L'avantage avec ce genre de solution est que la trame est vérifiée. Si quelque chose ne colle pas,
si par exemple elle ne trouve pas un des éléments recherchés elle retournera ZERO correspondance (0 match).

Si seule la valeur 13.2 est à prendre en compte, on enlève toutes les parenthèses sauf celles qui nous intéressent : ([0-9.]+) en avant dernière position :

// l'expression complète  : 
count = ms.GlobalMatch("<([0-9]+)>: ([0-9.]+) ([a-z]*):.([A-Z]*).: ([0-9.]+)([a-z0-9.\/]*)", match_callback);`
// l'expression devient : 
count = ms.GlobalMatch("<[0-9]+>: [0-9.]+ [a-z]*:.[A-Z]*.: ([0-9.]+)[a-z0-9.\/]*", match_callback);`

Matched: <01>: 103.1 mbar:(PB): 13.2cm3/mn
Capture 0 = 13.2
Found 1 matches.

Merci pour tout vos conseils.
J'ai de quoi m'occuper pour tester tout cela.

Si tu as besoin d'explications sur les REGEXP n'hésite pas.
Petite question subsidiaire, J'aimerais savoir : quel est ce système de mesure ?

C'est un mesureur de fuite : ATEQ

J'essai de récupérer la trame et ensuite, je me penche sur l'extraction.

Si tu comptes recevoir la trame, le plus simple est Serial.readBytes().

char buffer[40];  // longueur maximale de la trame
if (Serial.available() > 0) {
  int n = Serial.readBytes(buffer, 40);
// n est la longueur reçue
}

Si par bonheur le système de mesure envoie un retour chariot à la fin de la trame, ce qui ne serait pas étonnant :

char buffer[40];  // longueur maximale de la trame
if (Serial.available() > 0) {
  int n = Serial.readBytesUntil('\r', buffer, 40);
// n est la longueur reçue
}

S'il envoie un caractère retour ligne remplace '\r' par '\n'.