Envoyer/recevoir messages TCP

Bonjour,

Pour un projet j'aurais besoin de contrôler des réglages de plusieurs vidéoprojecteurs via le protocole TCP, et gérer le retour d'info.

Pour un vidéoprojecteur, envoyer (PWR 1) à 192.168.0.13 par exemple.
Dans la doc, si on envoie (PWR ?), le projecteur renvoie l'info (Power On). (à partir de ça j'allumerai une LED)

Malheureusement je suis encore débutant et je n'ai pas trouvé d'exemple ni de tuto pour envoyer/recevoir simplement des messages TCP.

Pouvez-vous m'aiguiller svp?

Merci

Il te faut utiliser un WifiClient (ou un EthernetClient si tu es en filaire)

Voir cet exemple.

Merci beaucoup. J'ai choisi EthernetClient et suis en train de le préparer.

J'arrive à ping mon arduino (uno), mais quand je veux envoyer un message, le PC ne répond pas.
Existe-t-il un soft "server" simple qui permet de configurer une réponse personnalisée à un message TCP reçu?
Wireshark ne le permet pas.

Cela me permettra de créer mon code complet, car les vidéoprojecteurs ne sont pas chez moi.

Je précise que je n'ai pas encore de 2e arduino. Là, ce serait bien pratique..

Merci

robinmensch:
Bonjour,

Pour un projet j'aurais besoin de contrôler des réglages de plusieurs vidéoprojecteurs via le protocole TCP, et gérer le retour d'info.

Pour un vidéoprojecteur, envoyer (PWR 1) à 192.168.0.13 par exemple.
Dans la doc, si on envoie (PWR ?), le projecteur renvoie l'info (Power On). (à partir de ça j'allumerai une LED)

Malheureusement je suis encore débutant et je n'ai pas trouvé d'exemple ni de tuto pour envoyer/recevoir simplement des messages TCP.

Pouvez-vous m'aiguiller svp?

Merci

Bonjour
TCP implique une notion de port
lien vers la doc ?

J'arrive à ping mon arduino (uno), mais quand je veux envoyer un message, le PC ne répond pas.

Il est clair que le PC ne va pas répondre tout seul comme un grand sans un logiciel serveur accroché à un port TCP.
Un moyen simple pour coder un serveur TCP : PYTHON.

Artouste:
Bonjour
TCP implique une notion de port
lien vers la doc ?

Oui c'est le port 3002.
La doc ici, page 7 pour la connexion ethernet.
Page 46 pour la commande Power
https://www.christiedigital.com/globalassets/resources/public/020-102660-04-Christie-LIT-TECH-REF-Crimson-API.pdf

hbachetti:
Il est clair que le PC ne va pas répondre tout seul comme un grand sans un logiciel serveur accroché à un port TCP.
Un moyen simple pour coder un serveur TCP : PYTHON.

Merci je vais lire ça.

Bonjour,

Pour bidouiller avec TCP, il y a un outil (gratuit) : Hercules Utility. Il fonctionne sous Windows sans installation et sur Linux avec Wine. On peut simplement créer des clients et serveurs TCP pour le test, envoyer et recevoir des messages. Rien à coder...
Bonne bidouille
MicroQuettas

MicroQuettas:
Bonjour,

Pour bidouiller avec TCP, il y a un outil (gratuit) : Hercules Utility. Il fonctionne sous Windows sans installation et sur Linux avec Wine. On peut simplement créer des clients et serveurs TCP pour le test, envoyer et recevoir des messages. Rien à coder...
Bonne bidouille
MicroQuettas

Bonjour
connaissais pas
Pour faire des petits tests/controles/scenario comme exposé là , perso j'utilise node-red , c'est rapide à faire

MicroQuettas:
Bonjour,

Pour bidouiller avec TCP, il y a un outil (gratuit) : Hercules Utility. Il fonctionne sous Windows sans installation et sur Linux avec Wine. On peut simplement créer des clients et serveurs TCP pour le test, envoyer et recevoir des messages. Rien à coder...
Bonne bidouille
MicroQuettas

J'ai essayé Hercules utility et il est super, merci. J'arrive à simuler le retour d'info à une question "état shutter?" et à allumer ma led en fonction.

J'ai bien avancé grâce à vos conseils, merci. Mais une question:
Actuellement la demande d'état est intégrée à la commande du switch 1, ou manuelle via le switch 2.
Il faut que la demande soit automatique et incluse dans la loop pour que ma LED s'allume même si un autre périphérique commande le shutter, et que mon bouton réagisse dans le bon sens.

Je n'arrive pas à intégrer une demande dans mon loop, ça coupe la connexion client sur Hercules.
Comment faire pour que l'état soit demandé en permanence, y a-t-il un risque de saturation de la machine ou du réseau surtout si je surveille plusieurs paramètres?

Ou alors, mais c'est plus hasardeux... peut-être écouter la commande qu'une autre machine donnerait au vidéoprojecteur, pour la récupérer et actualiser l'état de la LED concernée?

Je ne vois pas la stratégie à adopter.

Pour mieux comprendre il faut que je précise la finalité de ce projet: dans une installation vidéo de théâtre, il y a plusieurs paramètres à commander sur plusieurs vidéoprojecteurs (power, shutter, input, sur les projecteurs 1, 2, 3...). Tout ceci est en IP fixe.
Un ordinateur principal envoie habituellement les commandes pendant les spectacles, mais il faut revoir la programmation à chaque création de nouveau show car nous n'avons jamais les mêmes besoins.
Le arduino servira de télécommande pendant les créations, mais aussi de "surveillant" d'état des machines, et de secours en cas de plantage de l'ordi.

Voilà pourquoi il faut un retour d'info "permanent".

Voici mon loop en chantier, j'espère que je n'ai pas fait trop d'aberrations:

Merci

void loop()  // comment demander l'état du système à chaque loop?
{
  EthernetClient client = server.available();

  SW1.update();
  SW2.update();


  if (SW1.risingEdge())
  { if (etat_led_shutter == 1)
    {
      client.connect(VP1, 3002);
      client.println("SHU0");
      client.stop();
      client.connect(VP1, 3002);
      client.println("SHU?");
      client.stop();
      Serial.println("envoi SHU0 (ouvre le shutter) et demande l'état");
      analogWrite(led_CMD, 100);
      delay(75);
      analogWrite(led_CMD, LOW);
    }
    else if (etat_led_shutter == 0) {
      client.connect(VP1, 3002);
      client.println("SHU1");
      client.stop();
      client.connect(VP1, 3002);
      client.println("SHU?");
      client.stop();
      Serial.println("envoi SHU1 (ferme le shutter) et demande l'état");
      analogWrite(led_CMD, 100);
      delay(75);
      analogWrite(led_CMD, LOW);
    }
  }



  if (SW2.risingEdge())  // demande l'état du shutter
  {
    client.connect(VP1, 3002);
    client.println("SHU?");
    client.stop();
    Serial.println("envoi SHU?");
    analogWrite(led_CMD, 100);
    delay(75);
    analogWrite(led_CMD, LOW);
  }



  // lecture + affichage de la commande retour

  if (client.available()) {
    char c = client.read();
    if (c != '\n') {
      // Add received char to string variable
      commandStrVP1 += c;
    } else {
      // Print received command to serial monitor
      Serial.print("recu du VP1: " + commandStrVP1);

      // Process the received command
      processCommand(commandStrVP1);

      // Clear variable for receive next command
      commandStrVP1 = "";
    }
  }
}


// action du retour d'info

void processCommand(String cmd) {
  if (cmd == "SHU!0") {  // SHU0 = shutter ouvert
    Serial.println (" = shutter ouvert");
    analogWrite(led_shutter, 255);
    etat_led_shutter = 0;
  }
  else if (cmd == "SHU!1") {   //SHUT1 = shutter fermé
    analogWrite (led_shutter, LOW);
    Serial.println (" = shutter fermé");
    etat_led_shutter = 1;
  }
}

Bonjour,
Je n'ai pas regardé votre programme en détail, mais j'ai vu que vous gériez les communications au coup par coup :

client.connect(VP1, 3002);

client.println("SHU0");
      client.stop();

En faisant ainsi, vous ne tirez pas profit du TCP qui permet de gérer tout un dialogue à l'intérieur d'une même connexion. Vous fermez la connexion juste après l'écriture. Le rétro n'a peut-être pas le temps de vous répondre ?
En plus, c'est très inefficace car les connexions déconnexions sont gourmandes en ressources.

Après un client.connect(), vous pouvez lire et écrire tant que dure la connexion.
C'est normalement vous qui fermez la connexion avec un client.close(), quand vous avez terminé le dialogue. Le serveur (votre rétro) peut aussi la fermer, par ex. s'il y a une erreur ou une trop longue attente.

La communication en TCP, c'est comme avec un téléphone : on établit la liaison, on dialogue et on raccroche quand c'est fini.

Avec Hercules Utility, partie client, vous pouvez vous connecter manuellement sur vos rétros et tester les dialogues que vous automatiserez ensuite avec l'Arduino.
Bonne bidouille,
MicroQuettas

Merci.
Je ferai des essais demain en condition, je verrai comment réagit le système.
Donc en théorie je peux laisser la partie client ouverte. Mais en pratique, est-ce que sur une journée de travail de 8/12h, une déconnexion est possible?
Est-ce que ça vaut le coup de faire un "client check" pour relancer la connexion au cas ou?

Bonjour,

Le premier test a bien fonctionné, la connexion TCP ouverte aussi
Bon, à la question " (SHU?) " , le serveur me répondait " (SHU!1 "Fermé") " ou autres réponses pleines d'accent.
J'ai fini par trouver le très pratique StartsWith pour prendre uniquement le début du code " (SHU!000 ".

if (cmd.startsWith ("(SHU!001")) { // = shutter fermé
 analogWrite (led_shutter, LOW);
 etat_led_shutter = 0;

Par contre, je ne trouve pas de solution pour déclencher une commande après 5 secondes d'appui sur un bouton. J'ai cru comprendre que ce n'était pas très compatible avec la fonction Bounce qui ne prend en compte que les changements d'état?
Et je bloque toujours sur l'actualisation "en temps réel" de mes led...
Merci

Bonjour,

Je me suis trompé dans mon code. En réalité le programme interprétait les commandes reçues en mode serveur, alors que je dois interpréter les réponses reçues à mes commandes en tant que client. D'où la non-réponse du VP lors des essais.

Je n'arrive pas à faire allumer une led.
Est-ce possible (et la bonne solution) de convertir un char en string?

Commande:

  if (SW1.risingEdge())
  { if (etat_led_shutter == 0) { // si le shutter est fermé
      client1.connect(serveur, 3002);
      client1.println("(SHU0)(SHU?)");
      Serial.println("SW1 shutter open");
      analogWrite(led_CMD, 100);
      delay(75);
      analogWrite(led_CMD, LOW);
      client.stop();
    }
    else if (etat_led_shutter == 1) { // si le shutter est ouvert
      client1.connect(serveur, 3002);
      client1.println("(SHU1)(SHU?)");
      Serial.println("SW1 shutter close");
      analogWrite(led_CMD, 100);
      delay(75);
      analogWrite(led_CMD, LOW);
      client.stop();
    }
  }

Monitoring de la réponse:

  char reponse;
  if (client1.available()) {
    reponse = client1.read();
    Serial.print(reponse);
  }

Pour utiliser ceci (et surtout le startsWith)

 if (reponse.startsWith ("(SHU!001")) {     // message complet: (SHU!001 "Shutter Fermé")
    analogWrite (led_shutter, LOW);
    etat_led_shutter = 0;
  }
  if (reponse.startsWith ("(SHU!000")) {     // message complet: (SHU!000 "Shutter Ouvert")
    analogWrite(led_shutter, 255);
    etat_led_shutter = 1;
  }

Merci

Bonjour
J'ai un peu de mal à comprendre comment fonctionnent vos video projecteurs. Tel que vous écrivez votre code, ils fonctionnent en serveur tcp-ip. Donc vous cherchez à faire un client tcp-ip qui se connecte à ce serveur. Si ça fonctionne comme ça, il est probable que le fabricant a prévu cette liaison tcp-ip pour qu'on puisse se connecter au projecteur avec un simple navigateur sur le pc, en entrant par exemple l'adresse IP fixe du projecteur dans le champ d'adresse serveur du navigateur.
Si ça fonctionne, le projecteur va vous afficher une page avec les divers réglages possibles, et en cliquant ici ou là, réglant des potentiomètres graphiques, etc. vous pouvez régler le projecteur.
Donc commencez par vérifier ça, et si c'est OK, alors il vous faut réaliser un client TCP qui se connecte sur ce "serveur" et émule le navigateur et sa page. Cela nécessite un peu de connaissance des techniques de création de page sur le web et de leur interaction avec le serveur. La première étape sera d'examiner le code source de la dite page, dans le navigateur, et de comprendre comment il interagit avec votre équipement. Cela nécessite, suivant la complexité de le la page en question et la technique de dialogue impliquée, un peu de connaissance des technos du web ou beaucoup...

Oui, les vidéoprojecteur ont une page.
Mais il est possible de les faire réagir en envoyant une simple commande texte.
Ils réagissent à l’envoi de mon message mais je n’arrive pas à faire interpréter le retour de message par le Arduino.
J’arrive à afficher la réponse avec un char mais je voudrais utiliser le début de ce message pour allumer mes leds, ce que je n’arrive pas.

Cela vous fera gagner du temps d'examiner le code source de cette page dans votre navigateur, et vous verrez exactement ce qu'il faut envoyer au projecteur pour les diverses fonctions de réglage. Vous pouvez faire du "copier coller" de ce code source de la page dans un message ici et quelqu'un qui connait les techniques du web pourra vous aiguiller. Maintenant, si vous disposez d'une notice du fabricant qui vous explique quels codes envoyer en texte, pourquoi pas, mais attention à bien programmer votre code en mode "client" et pas en mode "serveur", comme ce que vous avez montré. Le protocole http est asymétrique.

Bonsoir,
Indépendamment de l'approche proposée par JiPe38, voici quelques infos sur votre question au sujet du traitement de la réponse du rétro :

  • read() retourne un seul char. Pour traiter toute la réponse, il faut concaténer le dernier caractère reçu à ceux reçus précédemment pour former une chaîne de caractères,
  • il y a deux types de chaînes de caractères :
    1- les C-strings, autrement dit un tableau de char dont le dernier est un zéro '\0' pour marquer la fin de chaîne,
    2- une instance de la classe String (avec une majuscule), plutôt déconseillé sur Arduino (voir nombreux posts sur ce sujet), mais qui fonctionne...

Pour les deux, il existe un ensemble de fonctions toutes faites permettant de traiter les chaînes et d'en extraire toute l'info possible.
Dans votre post du Feb 15, 2021, 01:48 pm, vous avez un bout de code qui fait exactement cela avec un objet String. C'est une base pour recevoir et décortiquer la réponse du rétro. Une question sera à régler : comment le rétro termine-t'il sa réponse ? Avec un '\n' comme supposé dans votre code ? Hercules peut vous aider à décortiquer les réponses du rétro.
Bonne bidouille,
MicroQuettas
Votre bout de code qui reçoit la réponse dans une String commandStrVP1

// lecture + affichage de la commande retour

  if (client.available()) {
    char c = client.read();
    if (c != '\n') {
      // Add received char to string variable
      commandStrVP1 += c;
    } else {
      // Print received command to serial monitor
      Serial.print("recu du VP1: " + commandStrVP1);

      // Process the received command
      processCommand(commandStrVP1);

      // Clear variable for receive next command
      commandStrVP1 = "";
    }
  }
}

Merci pour vos réponses.

Pendant mes simulations j’utilisais encore le Arduino en mode serveur ce qui est une erreur à la base mais qui m’a permis d’obtenir le code qui fait réagir l’arduino a un message, comme MicroQuettas m’a cité au dessus. Cette partie là est fonctionnelle.

Lors des essais sur le terrain, grâce à Hercules j’ai obtenu le code exact de la réponse, qui -forcément- n’était pas écrit dans la doc: C’est, parenthèses comprises, (SHU!001 "Shutter Fermé"). Le projecteur n’ajoute rien, pas de zéro, ni de LF, ni finalement de \n. Tout s’ajoute à la suite.

Toutes les réponses sont au même format, (XXX!000 "texte") et je n’ai besoin que des 8 premiers caractères donc pour mes simulations j’ai utilisé la commande if...startsWith.

Jusqu’ici tout va bien mais c’était une simulation et Arduino en serveur.

SAUF qu’en mode client, je n’arrive pas à convertir la réponse char que j’obtiens, en string pour garder le startsWith qui est quand même pratique pour l’interprétation des réponses. Et qui m’évite d’avoir à traiter les accents..

Est-ce que je suis obligé de concaténer mon char pour le convertir en string, pour ensuite le traiter?

Bonjour,
Oui, l'envoi et la réception des messages sont identiques en client ou en serveur.
La différence, c'est la connexion :

  • Le serveur attend une connexion,
  • Le client initie la connexion,
  • L'un ou l'autre peuvent fermer la connexion.

Bonne bidouille
MicroQuettas