Communication chronomètre Tag Heuer via Arduino Uno et ESP32

Bonjour,

Je souhaite récupérer des données (temps) sur un chronomètre TAG HEUER CP520 en passant par une arduino Uno. Le chronomètre CP520 dispose d'une prise RJ12 RS232 selon la documentation.

J'ai le branchement suivant : Prise RJ12 --> module MAX3232 2313 --> Arduino Uno.

Les valeurs envoyées par le CP520 suivent ce format :

J'ai testé avec le code suivant :

#include <SoftwareSerial.h>
SoftwareSerial CP520 (10, 11); // RX, TX

void setup() {

  Serial.begin(9600);
  while (!Serial) {
    ;}
  Serial.println("Pret");
  CP520.begin(9600);
}

void loop() {
  if (CP520.available() > 0) {
    Serial.print(CP520.read());
    Serial.print(' ');
  }
}

Et j'obtient ceci :

Pret
86 251 159 151 191 191 191 191 191 191 191 191 191 191 99 117 87 191 87 109 101 117 191 191 191 191 191 191 191 229 87 191 191 191 191 191 191 191 191 191 191 191 101 155 191 191 191 191 191 191 191 155 143 163 155 145 151 159 159 159 229 87 191 191 191 191 191 191 191 191 191 191 191 101 155 191 191 191 191 191 157 139 155 153 163 155 143 147 159 159 159 229 87 191 191 191 191 191 191 191 191 191 191 191 101 155 191 191 157 163 159 87 191 191 101 191 251 235 139 151 141 159 191 191 191 101 191 139 155 159 191 191 191 157 191 149 159 159 191 191 155 191 191 145 151 229 191 191 191 191 155 163 159 91 157 191 191 191 191 163 159 0 

Cela ne correspond pas au format que je suis censer recevoir...

En cherchant sur les différents forum j'ai trouvé une piste avec un code dans ce style :

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

Mais j'ai que des 0 en retour ..

Je pense que mon branchement est correct mais que mon code est mauvais ... Je ne sais pas comment faire pour recevoir la trame complete selon le format du CP520.
Pouvez-vous m'aider ?

Merci d'avance

Bonjour @vl1998 et Bienvenue sur ce Forum !

As-tu testé au multimètre les valeurs de la tension sur la sortie TX du chronomètre ? (ou sur l'entrée RIN du MAX3232)
(ceci pour s'assurer qu'il s'agit bien de niveaux logiques RS232 sur la sortie RJ12 et que donc le MAX3232 est nécessaire avec l'adaptation de tensions et l'inversion logique qu'il apporte)

le terme 'protocole RS232' est parfois utilisé en présence de signaux aux 'niveaux TTL'

Bonjour @al1fch,
Je viens de tester .... J'ai du 5V (5,5V) en continu sans transmission. Et du 0V à 5V en transmission.

Sans le MAX3232, j'ai les valeurs suivantes :

Pret
83 32 48 48 52 32 32 32 32 32 32 32 32 32 32 78 69 84 32 84 73 77 69 32 32 32 32 32 32 32 13 84 32 32 32 32 32 32 32 32 32 32 32 77 50 32 32 32 32 32 32 32 50 56 46 50 55 52 48 48 48 13 84 32 32 32 32 32 32 32 32 32 32 32 77 50 32 32 32 32 32 49 58 50 51 46 50 56 54 48 48 48 13 84 32 32 32 32 32 32 32 32 32 32 32 77 50 32 32 32 32 32 49 58 51 50 46 48 48 50 48 48 48 13 84 32 32 32 32 77 32 32 32 32 32 49 58 52 57 48 13 32 32 32 32 32 32 58 46 51 48 32 32 32 49 49 32 50 50 48 48 84 32 32 32 77 32 32 53 57 48 13 32 32 32 32 32 32 58 46 50 48 32 49 32 49 32 32 32 55 57 48 

ça va dans le bon sens, ton chronometre ne sort donc pas des 'niveaux RS232', mais des 'niveaux TTL' , MAX3232 inutile

fais une conversion Décimal -> ASCII et ta trame reçue :

83 32 48 48 52 32 32 32 32 32 32 32 32 32 32 78 69 84 32 84 73 77 69 32 32 32 32 32 32 32 13 84 32 32 32 32 32 32 32 32 32 32 32 77 50 32 32 32 32 32 32 32 50 56 46 50 55 52 48 48 48 13 84 32 32 32 32 32 32 32 32 32 32 32 77 50 32 32 32 32 32 49 58 50 51 46 50 56 54 48 48 48 13 84 32 32 32 32 32 32 32 32 32 32 32 77 50 32 32 32 32 32 49 58 51 50 46 48 48 50 48 48 48 13 84 32 32 32 32 77 32 32 32 32 32 49 58 52 57 48 13 32 32 32 32 32 32 58 46 51 48 32 32 32 49 49 32 50 50 48 48 84 32 32 32 77 32 32 53 57 48 13 32 32 32 32 32 32 58 46 50 48 32 49 32 49 32 32 32 55 57 48

devient :

S 004          NET TIME       
T           M2       28.274000
T           M2     1:23.286000
T           M2     1:32.002000
T    M     1:490
      :.30   11 2200T   M  590
      :.20 1 1   790

(fait avec ce convertisseur en ligne)

83 -> S
32 -> Espace
48 -> 0
52 -> 4
etc....

1 Like

Bonjour,

Au lieu d'afficher la valeur reçue sous forme de nombre, affiche la sous forme de caractère.
Serial.print((char)CP520.read());

Merci pour le conseil de supprimer le MAX3232 :+1:

Oui j'ai aussi ces valeurs avec un convertisseur en ligne. Mais il me manque des données ... Je perds une partie de la transmission ?

Le CP520 dispose d'une imprimante (je peux envoyer les données sur l'imprimante ou sur un PC) : Normalement je dois recevoir ceci :

Les trois premiers temps sont corrects mais pas le reste. La transmission a lieu trop vite et l'Arduino ne suit pas avec ce code et le port serie virtuel ?

Edit :
Avec la proposition de @kamill c'est ok j'ai bien tout

Il me reste maintenant à gérer ces valeurs pour les enregistrer sous en .txt ou .csv sur une carte SD ... Je vais regarder comment faire ça. J'ai un module micro SD en I²C.

Merci !

Bonsoir vl1998

Avec Serial.print(CP520.read()); on des chiffres alors que la doc parle de caractères:
image

Si tu le désires, je te fais un exemple comment traiter ça.

A+
jpbbricole

Il faut remplacer ça

Par ça

Serial.begin(115200);

softwareSerial monopolise pas mal de ressources lorsque le débit est relativement bas car il y a des attentes assez longues. De fait, le temps pour sortir sur Serial doit se trouver limité, en augmentant le débit sur cette sortie on maximise la possibilité d'afficher toutes le données reçues..

Bonjour,

Je suis passé sur un ESP32 pour un gain de place (je n'ai pas d'Arduino nano en stock).

Je souhaite enregistrer les données sur une carte micro SD.
Le code suivant est-il la meilleure solution pour ne pas perdre de données (avec un enregistrement qui arriverait trop tôt) ? Je laisse 3s et si je reçoit rien, alors je lance l'enregistrement ?

@fdufnews : j'ai fait la modif que tu proposes, merci. Et cela devrait aller mieux avec l'ESP32.

unsigned long Timer;
char c;

void setup() {

  Serial.begin(115200);
    delay(100);
  Serial.println("PC OK");
  
  Serial2.begin(9600, SERIAL_8N1, 16, 17); //RX:16 / TX:17
    delay(100);
  Serial.println("ESP32 OK");
}

void loop() {
  if (Serial2.available() > 0) {
    RecepetionCp520();
  }
}

void RecepetionCp520()
{
  Timmer = millis();

  while (millis() - Timer <= 3000)
  {
    while(Serial2.available())
    {
      char c = Serial2.read();
      Timer = millis();
      Serial.print(c);
    }
  }
}

@jpbbricole : En copiant collant les données 'brutes' du port serie dans un fichier .txt j'ai bien des sauts de lignes qui se font (donc ok pour une exploitation Excel derrière). Cela sera aussi le cas si j'enregistre les données brutes (cad c dans mon code) ? Ou je doit faire un code plus propre qui sépare bien les chaines ?

Merci d'avance

Bonjour vl1998

Quel est la finalité de tout ça ?
Traiter les données reçues du chrono dans Excel ?
Quel serait le rôle de l'Arduino ?

Cordialement
jpbbricole

Bonjour,
Le but est d'avoir une solution autonome (sans PC) pour récupérer les temps dans un chronomètre sans avoir a saisir les temps manuellement.
Ensuite, les temps sont classés dans un fichier Excel (avec un code VBA pour les différentes catégories et autres selon le n° de dossard).

Bonjour vl1998

M5stack a des solutions ESP32 avec carte SD

Cordialement
jpbbricole

Comme tu n'utilises pas de println(print + retour à la ligne) sur Serial.
C'est que ton CP520 t'envoi le retour à la ligne, tu n'a donc pas besoin de le rajouter

Mais je ne comprends pas trop ce que tu veux faire.
ton programme appel RecepetionCp520 des qu'il y a quelque chose à lire.
RecepetionCp520 transférant tout ce qu'il lit sur Serial2 sur Serial.

pourquoi ne pas remplacer tout ça par

if (Serial2.available() > 0) {
    char c = Serial2.read();
    Serial.print(c);
}

Après c'est à toi de décider si tu veux traiter les données sur le microcontrôleur pour une lecture directe dans Excel ou via du code VBA.

Bonjour vl1998

Voici une façon de faire, tout se passe dans cette ligne:
String chronoTrame = CP520.readStringUntil('\r'); // Lecture jusqu'au caractère CR
où l'on reçoit la trame en une passe jusqu'à \r ou CR.

Cet exemple de programme reçoit via le port CP520 (SoftwareSerial) à 9600 ou depuis la ligne de commande la la console (Serial) à 9600, il y a les 2 réceptions.

Il y a un exemple de trame en entête du programme:
R0000100002000000000011.669000
concurrent 2 classé 1 à 11.669
que tu peux copier/coller dans la ligne de commande.

Attention la console doit être paramétrée ainsi:
image

Cr qu'il y a dans la console est directement exploitable, via cpier/coller, dans Excel.

Classement:	Concurrent:	Imp. Chrono:	Temps:
001	012	4	000000011.42500
001	002	0	000000011.66900
001	012	4	000000011.42500
001	002	0	000000011.66900
001	012	4	000000011.42500
001	002	0	000000011.66900

Le programme:

/*
    Name:       ARDFR_ChronoVersArduino.ino
    Created:	11.12.2023
    Author:     jpbbricole/vl1998
	https://forum.arduino.cc/t/communication-chronometre-via-arduino-uno-rs232-ttl/1199275

	R0000100002000000000011.669000 concurent 2 classé 1 à 11.669
*/
#include <SoftwareSerial.h>
SoftwareSerial CP520 (10, 11); // RX, TX
//SoftwareSerial CP520 (A2, A3); // RX, TX jpbbricole

boolean firstPrint = true;     // Pour première impression

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

void loop()
{
	if (CP520.available())
	{
		String chronoTrame = CP520.readStringUntil('\r');     // Lecture jusqu'au caractère CR
		chronoTrame.trim();     // Pour nettoyer la commande reçue

		//Serial.println("\nCP520");
		chronoDecode(chronoTrame);
	}

	if (Serial.available())
	{
		String chronoTrame = Serial.readStringUntil('\r');     // Lecture jusqu'au caractère CR
		chronoTrame.trim();     // Pour nettoyer la commande reçue
		
		//Serial.println("\nSerial");
		chronoDecode(chronoTrame);
	}
}

void chronoDecode(String trame)
{
	if (firstPrint)     // Si première impression
	{
		Serial.print("Classement:");
		Serial.print("\tConcurrent:");
		Serial.print("\tImp. Chrono:");
		Serial.print("\tTemps:");
		Serial.println("");
		firstPrint = false;
	}
	Serial.print(trame.substring(3, 6));     // Classement
	Serial.print("\t" + trame.substring(8, 11));     // Concurrent
	Serial.print("\t" + trame.substring(12, 13));     // No. entrée impulsion chrono
	Serial.print("\t" + trame.substring(14, 29));     // Temps
	Serial.println("");
}

A+
Cordialement
jpbbricole

Merci beaucoup @jpbbricole, j'avais peur qu'un traitement des données de cette façon soit trop long trop et je que je loupe des données reçues sur le port série ... Mais non visiblement après essai.

J'ai modifié un peu le code pour le passer sur un ESP32 et cela fonctionne parfaitement.
Penses-tu que j'ai le temps d'enregistrer en direct les données sur une carte micro SD dans le void chronoDecode() à la place des Serial.print sans perdre de données ? Je ne peux pas faire l'essai rapidement ... mon module micro SD est cramé (j'ai commandé un nouveau).

Je fonctionne de cette façon pour ce test pour avoir un void lisible. J'ai d'autre lignes de codes dans le void loop non partagées ici car elles fonctionne correctement.

Merci pour l'aide

Bonoir vl1998

Je peux essayer, demain, avec un M5 Stack.
Sais tu combien de temps se passe entre 2 trames?
Est ce que ces trames arrivent par salves?

Peux tu mettre ta version ESP32 en ligne.

Cordialement
jpbbricole

je suppose que c'est très tributaire de la prise du temps.

c'est quoi que tu appel un void, la fonction loop ?
En fait cela ne change pas réellement le fond de ma remarque, sauf si tu as des cas ou tu as des lectures de données pendant plus de 3s
Sauf cas particulier, si tu as des données tu les lis, puis tu passe au autre chose à faire dans ta loop.
Une fois fini, la loop étant rappelé immédiatement, elle va vérifier si il y des données qui sont arrivée entre temps, sinon le code restant est exécuter directement.
Je ne sais pas si je suis très claire ?

Aucune idée ... J'ai peu d'info dans la docs.

@terwal : oui oui c'est clair et je suis d'accord. Je voulais bloqué le loop sur la lecture et ne pas passer à la suite du code mais c'est pas la meilleure méthode surement.
Avec le code de @jpbbricole c'est plus simple que ma version.

#include "FS.h"
#include "SD.h"
#include "SPI.h"

unsigned long Timer;
String chronoTrame;
boolean firstPrint = true; 
File Fichier;

void setup() {
  Serial.begin(115200);  
  Serial2.begin(9600, SERIAL_8N1, 16, 17); //RX:16 / TX:17
  delay(100);

void loop() {
  if (Serial2.available()){
    String chronoTrame = Serial2.readStringUntil('\r'); // Lecture jusqu'au caractère CR
    chronoTrame.trim(); // Pour nettoyer la commande reçue
    chronoDecode(chronoTrame);
  }

void chronoDecode(String trame){
  if (firstPrint) {    // Si première impression
    Serial.print("Classement:");
    Serial.print("\tConcurrent:");
    Serial.print("\tImp. Chrono:");
    Serial.print("\tTemps:");
    Serial.println("");
    firstPrint = false;
  }
  Serial.print(trame.substring(3, 6)); // Classement
  Serial.print("\t" + trame.substring(8, 11)); // Concurrent
  Serial.print("\t" + trame.substring(12, 13)); // No. entrée impulsion chrono
  Serial.print("\t" + trame.substring(14, 29)); // Temps
  Serial.println("");
}

Bonsoir vl1998

Je fais des essais demain.

Bonne soirée.
jpbbricole

Tant que ça marche et que tu t'y retrouve, c'est le principal.