Python et Ethernet/UDP

Python est le langage de prédilection du Raspberry Pi

Modérateurs : Francois, Manfraid

Répondre
Pinhapple
Raspinaute
Messages : 125
Enregistré le : jeu. 23 févr. 2017 15:53
Localisation : Rouen

Python et Ethernet/UDP

Message par Pinhapple » mar. 20 juin 2017 09:47

Bonjour à tous,

Suite à mes précédents posts (cf. le dernier en date), je me lance dans une ultime tentative d'obtenir ce que je souhaite.

Le contexte : je tente de lire un signal radar à 4 kHz avec une carte Arduino Uno équipée d'un shield Ethernet, puis d'envoyer le tout à un RPi via Ethernet/UDP, avec du Python côté RPi et évidemment du C côté Arduino. L'idée est de lire et de stocker des valeurs avec l'Arduino, puis de balancer le tout régulièrement au RPi avec un câble RJ45.

J'essaie dans un premier temps de communiquer de manière basique entre mes cartes, mais j'ai un souci de réception des données sur le RPi : en me basant sur ce tuto que j'ai adapté pour avancer pas à pas, j'arrive à faire communiquer mes deux cartes pendant quelques secondes, puis tout s'arrête, jusqu'à ce que je reset mon Arduino, auquel cas le même problème survient de nouveau au bout de quelques secondes.

Voici mon code Arduino pour référence :

Code : Tout sélectionner

#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SPI.h>

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(169, 254, 43, 243);
unsigned int localPort = 5000;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE];
String datReq;
int packetSize;
EthernetUDP Udp;

void setup()
{

  Serial.begin(230400);
  Ethernet.begin(mac, ip);
  Udp.begin(localPort);
  delay(1500);

}

void loop()
{
 
  packetSize = Udp.parsePacket();

  if (packetSize > 0)
  {

    Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
   
    String datReq(packetBuffer);

    if (datReq == "red")
    {

      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
      Udp.print("RED");
      Udp.endPacket();

      return;
     
    }

    else if (datReq == "green")
    {

      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
      Udp.print("GREEN");
      Udp.endPacket();

      return;
     
    }

    else if (datReq == "blue")
    {

      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
      Udp.print("BLUE");
      Udp.endPacket();

      return;
     
    }
   
  }

  memset(packetBuffer, 0, UDP_TX_PACKET_MAX_SIZE);

}
Et pour compléter, le code Python (2.7.9) côté RPi :

Code : Tout sélectionner

from socket import *
import time

address = ("169.254.43.243", 5000)
client_socket = socket(AF_INET, SOCK_DGRAM)
client_socket.settimeout(0.1)

while (1):

    # --- red ---

    data = "red"
    client_socket.sendto(data, address)

    try:
       
        rec_data, addr = client_socket.recvfrom(2048)
        print(rec_data)

    except:

        print("except red")
        pass

    # --- green ---

    data = "green"
    client_socket.sendto(data, address)

    try:
       
        rec_data, addr = client_socket.recvfrom(2048)
        print(rec_data)

    except:

        print("except green")
        pass

    # --- blue ---

    data = "blue"
    client_socket.sendto(data, address)

    try:
       
        rec_data, addr = client_socket.recvfrom(2048)
        print(rec_data)

    except:

        print("except blue")
        pass
L'ensemble n'est pas trop compliqué à comprendre : le RPi envoie un mot et reçoit en retour la réponse adaptée (en l'occurrence, le même mot mais en majuscules), puis passe au mot suivant, et boucle indéfiniment.

Le code Arduino compile et s'uploade sur la carte sans souci, et le code Python s'exécute sans problème non plus. Je devrais alors avoir dans ma console Python une succession de RED, GREEN, BLUE, RED, GREEN, etc., ce qui est le cas pendant quelques secondes, puis les except se succèdent...

Je pensais que le problème venait du code Arduino, car j'ai remarqué que c'est GREEN qui me renvoie les premiers except, puis BLUE, et enfin RED: c'est dans l'ordre décroissant de longueur de caractères que les except liés surviennent, ce qui m'amène à penser qu'il y a une dépassement de mémoire quelque part (c'est ce que ça m'évoque ; en toute honnêteté, je n'en sais rien).

Finalement, j'arrive à faire communiquer deux cartes Arduino avec le même code, donc c'est probablement plus côté Python que ça déconne, et c'est la fonction recvfrom() qui a l'air de poser problème (erreur timeout: timed out).

Auriez-vous une idée de ce qui ne va pas ?

Merci d'avance pour vos remarques ! :)

PS : petit aparté ; ça vous paraît faisable ce que je tente ? :?:
  • RPi 3 + LibreELEC / RPi 3 + RetroPie / RPi B+ + Sense HAT ou Framboisedorf ou module caméra
  • Arduino Mega, Uno, Nano
  • Freescale FRDM KL25Z

destroyedlolo
Raspinaute
Messages : 1160
Enregistré le : dim. 10 mai 2015 18:44
Localisation : Dans la campagne à côté d'Annecy
Contact :

Re: Python et Ethernet/UDP

Message par destroyedlolo » mar. 20 juin 2017 10:59

Salut,

Je pense que tu trouveras plus simplement d'ou vient l'erreur en ... affichant l'erreur générée par tes clientsockets.
En C, ca se fait par la variable globale errno et strerror() (tu devrais le faire aussi coté Arduino), je te laisse voir pour ton code Python.
Merci d'avance pour vos remarques ! :)
Perso, hormis gros pb de perf, je ne serai pas passé par l'UDP qui n'est pas "fiable" : généralement l'UDP ne sert qu'a envoyer des broadcast où on assume avoir des manques (hormis a rajouter tes propres vérifications, détection de trames manquantes comme le fait NFS).
Si j'avais a faire ce genre de chose, j'utiliserai MQTT qui te permet de t'affranchir de toute la communication de bas niveau et prend en charge la qualité de service que tu souhaites, du "j'envoie et vogue la galère" au "j'envoie, mais je veux être sur que la donnée est recue, et n'est recue qu'une fois".

A+
  • BananaPI : Gentoo, disque SATA de 2 To
  • Domotique : 1-wire, TéléInfo, Tablette passée sous Gentoo, ESP8266
  • Multimedia par DNLA
  • Et pleins d'idées ... et bien sûr, pas assez de temps.
Un descriptif de ma domotique 100% fait maison.

romaxx
Messages : 78
Enregistré le : lun. 24 oct. 2016 10:59

Re: Python et Ethernet/UDP

Message par romaxx » mar. 20 juin 2017 11:24

destroyedlolo a écrit :je ne serai pas passé par l'UDP qui n'est pas "fiable"
hummm.. sur le même segment réseau l'UDP est super fiable.. c'est lorsque ça commence à être routé qu'il peut y avoir des pertes (volontaires) de paquets.
--
Adhérent à l'A.F.S.T.L

Pinhapple
Raspinaute
Messages : 125
Enregistré le : jeu. 23 févr. 2017 15:53
Localisation : Rouen

Re: Python et Ethernet/UDP

Message par Pinhapple » mar. 20 juin 2017 15:41

destroyedlolo a écrit :Je pense que tu trouveras plus simplement d'ou vient l'erreur en ... affichant l'erreur générée par tes clientsockets.
En C, ca se fait par la variable globale errno et strerror() (tu devrais le faire aussi coté Arduino), je te laisse voir pour ton code Python.
En farfouillant dans la doc Arduino, j'ai vu que UDP.endPacket() retournait 0 en cas d'échec et 1 en cas de succès. J'ai modifié mon code Arduino pour afficher cette valeur de retour, qui est à 0 à chaque fois.
Ce qui finalement ne m'avance pas plus : est-ce l'Arduino qui n'arrive pas à envoyer le paquet, ou est-ce le RPi qui n'arrive pas à le recevoir ? (comme réponse à sa requête)

Concernant errno et strerror(), l'IDE Arduino n'a pas l'air de connaître la fonction, alors que je peux importer la bibliothèque et utiliser la constante sans souci. :(

Une piste côté Python ? Il y en a dans tous les sens dans la doc, je ne vois pas ce qui pourrait me servir à afficher d'éventuelles erreur.
destroyedlolo a écrit :Si j'avais a faire ce genre de chose, j'utiliserai MQTT qui te permet de t'affranchir de toute la communication de bas niveau et prend en charge la qualité de service que tu souhaites, du "j'envoie et vogue la galère" au "j'envoie, mais je veux être sur que la donnée est recue, et n'est recue qu'une fois".
Je n'ai jamais utilisé MQTT, donc je garde ça dans un coin si j'ai besoin, merci pour l'info ! ;)
  • RPi 3 + LibreELEC / RPi 3 + RetroPie / RPi B+ + Sense HAT ou Framboisedorf ou module caméra
  • Arduino Mega, Uno, Nano
  • Freescale FRDM KL25Z

destroyedlolo
Raspinaute
Messages : 1160
Enregistré le : dim. 10 mai 2015 18:44
Localisation : Dans la campagne à côté d'Annecy
Contact :

Re: Python et Ethernet/UDP

Message par destroyedlolo » mar. 20 juin 2017 15:54

Pinhapple a écrit :J'ai modifié mon code Arduino pour afficher cette valeur de retour, qui est à 0 à chaque fois.
Ce qui finalement ne m'avance pas plus : est-ce l'Arduino qui n'arrive pas à envoyer le paquet, ou est-ce le RPi qui n'arrive pas à le recevoir ? (comme réponse à sa requête)
Il n'y a aucun acquittement explicite en UDP ... ce qui laisserait penser que l'erreur serait de ce coté (a prendre avec des pincettes, je n'ai jamais utilisé cette librairie, mais elle te renvoie forcement un code qq part pour te dire ce qui ne va pas, sinon ... comment débuger ?).
Pinhapple a écrit :Une piste côté Python ? Il y en a dans tous les sens dans la doc, je ne vois pas ce qui pourrait me servir à afficher d'éventuelles erreur.
Ben si
exception socket.error

This exception is raised for socket-related errors. The accompanying value is either a string telling what went wrong or a pair (errno, string) representing an error returned by a system call, similar to the value accompanying os.error. See the module errno, which contains names for the error codes defined by the underlying operating system.
  • BananaPI : Gentoo, disque SATA de 2 To
  • Domotique : 1-wire, TéléInfo, Tablette passée sous Gentoo, ESP8266
  • Multimedia par DNLA
  • Et pleins d'idées ... et bien sûr, pas assez de temps.
Un descriptif de ma domotique 100% fait maison.

romaxx
Messages : 78
Enregistré le : lun. 24 oct. 2016 10:59

Re: Python et Ethernet/UDP

Message par romaxx » mar. 20 juin 2017 16:18

destroyedlolo a écrit :Il n'y a aucun acquittement explicite en UDP ... ce qui laisserait penser que l'erreur serait de ce coté (a prendre avec des pincettes, je n'ai jamais utilisé cette librairie, mais elle te renvoie forcement un code qq part pour te dire ce qui ne va pas, sinon ... comment débuger ?)
Alors il faut faire attention à ce qui circule sur le net, certes UDP n'a pas d'acquittement explicite, c'est à dire que si votre paquet passe par un routeur, il n'y aura pas de paquet de retour.
Cependant, lorsque l'ip de destination se trouve sur le même réseau (hors broadcast), ce qui est le cas ici, certaines librairies vérifient que l'hôte est accessible (et renvoyer une erreur si c'est impossible), à partir de là, on peux supposer que si l'hôte existe (et qu'un socket UDP est ouvert sur l'hôte de destination), que celui-ci va bien recevoir le message.

Image
En fait tout se joue dans les niveaux plus bas du modèle OSI: Niveau 3 et 2.

Le plus simple est d'utiliser un analyseur réseau pour bien comprendre ce qu'il se passe (genre wireshark).
Si vous voulez espionner le traffic entre deux hôtes de façon simple, soit vous utilisez un HUB Ethernet (qui balance le traffic de tout le monde à tout le monde), soit vous utilisez un Switch Ethernet digne de ce nom avec une option port mirroring, dans tous les cas votre carte réseau doit être configurée en mode promiscuous.
--
Adhérent à l'A.F.S.T.L

Bud Spencer
Raspinaute
Messages : 377
Enregistré le : lun. 15 août 2016 21:38

Re: Python et Ethernet/UDP

Message par Bud Spencer » mar. 20 juin 2017 23:40

Pinhapple a écrit :...c'est probablement plus côté Python que ça déconne, et c'est la fonction recvfrom() qui a l'air de poser problème (erreur timeout: timed out).
La, je doute ...

Les except signifient tout simplement que ton client n’a pas reçu de réponse dans le temps imparti. Quand tu définis le timeout d’un client sur un socket udp, ce dernier attend des données dans le temps defini du timeout. Si le délai est dépassé, il lève une exception attendue (et non pas une erreur). Comme je n’ai pas d’arduino pour tester ton serveur udp donc j’en ai écrit un similaire vite fait en c# sur un pc et j’ai testé avec ton client python sur un pi (juste adapté en python 3) et cela fonctionne sans problème aussi bien avec le serveur sur un réseaux local que sur un réseaux distant (J’ai laissé tourner une dizaine de minutes sur chaque).

Petite expérience :
- Demarre ton client python sur le pi (tu ne vas voir que des except)
-Puis allume l’arduitruc, tu devrais voir passer tes couleurs qualques secondes sur le pi
- quand ça va commencer à merdouiller (except,except .. sur le pi) , éteint juste l’arduino et rallume le sans toucher au pi.
Vois tu de nouveau passer tes couleurs quelques secondes ? si oui, le problème est forcement coté Arduino

Tout ça pour te dire que ton code python envois bien ses datagrammes et qu’il reçoit bien les réponses du point de terminaison distant … si ce dernier est capable de faire l’inverse et de lui répondre dans les delais. Bon, après tu risques d'avoir d'autres soucis parce que coté codage et gestion de tes sockets udp, t'es pas encore au top, mais je t'expliquerais peut être ca quand tu en seras la ;)

Ps : Si il te vient a l’idée d’utiliser du python 3 plutôt que du 2.x, il faudra penser à remplacer les client_socket.sendto(data,address) par des client_socket.sendto(data.encode() ,address). C’est juste dû au fait qu’en python 3, la fonction attend les data sous forme de byte array et non pas sous forme de string. Code C# serveur et code client python 3 à dispo si ca t'intéresse
Le premier ennemi de la connaissance n’est pas l’ignorance, c’est l’illusion de la connaissance (S. Hawking).

Pinhapple
Raspinaute
Messages : 125
Enregistré le : jeu. 23 févr. 2017 15:53
Localisation : Rouen

Re: Python et Ethernet/UDP

Message par Pinhapple » mer. 21 juin 2017 09:52

destroyedlolo a écrit :mais elle te renvoie forcement un code qq part pour te dire ce qui ne va pas, sinon ... comment débuger ?).
exception socket.error
Tout ce que j'ai comme message d'erreur, c'est :

Code : Tout sélectionner

Traceback (most recent call last):
  File "C:\User\...\ReceiveUdp.py", line 22, in <module>
    rec_data, addr = client_socket.recvfrom(2048)
timeout: timed out
(ligne 22 car j'ai sorti mon recvfrom du try pour déclencher l'erreur rapidement)

Donc si je me réfère à la doc : "This exception is raised when a timeout occurs on a socket which has had timeouts enabled via a prior call to settimeout(). The accompanying value is a string whose value is currently always “timed out”." ; j'ai bien un settimeout() utilisé précédemment sur ce socket, ainsi que la chaîne "timed out", donc ça a l'air de correspondre. Si je comprends bien, cette erreur apparaît lorsque je n'ai pas reçu de réponse à ma requête en moins de temps que celui spécifié par le settimeout(), j'ai bon ?
romaxx a écrit :Le plus simple est d'utiliser un analyseur réseau pour bien comprendre ce qu'il se passe (genre wireshark).
Si vous voulez espionner le traffic entre deux hôtes de façon simple, soit vous utilisez un HUB Ethernet (qui balance le traffic de tout le monde à tout le monde), soit vous utilisez un Switch Ethernet digne de ce nom avec une option port mirroring, dans tous les cas votre carte réseau doit être configurée en mode promiscuous.
N'ayant pas ce matériel sous la main (mais moyen d'en demander), je garde aussi ça sous le coude si vraiment je ne m'en sors pas côté logiciel. Et merci pour le schéma du modèle OSI, il est bien fichu ! ;)
Bud Spencer a écrit :Petite expérience :
- Demarre ton client python sur le pi (tu ne vas voir que des except)
-Puis allume l’arduitruc, tu devrais voir passer tes couleurs qualques secondes sur le pi
- quand ça va commencer à merdouiller (except,except .. sur le pi) , éteint juste l’arduino et rallume le sans toucher au pi.
Vois tu de nouveau passer tes couleurs quelques secondes ? si oui, le problème est forcement coté Arduino
Le sketch : sans toucher à rien par rapport à hier, je tente ton expérience, et mon code passe et se comporte comme attendu... :(
Tout ce que j'ai fait, c'est remplacer mes print par des write côté Arduino, constater que ça ne fonctionne pas, et remettre des print. J'ai relancé mon script plusieurs fois, ça fonctionne à chaque fois, et aussi longtemps que je le laisse tourner, sans aucune erreur levée...

Par curiosité, j'ai testé sur Windows au lieu du RPi, avec le même code Python, la même version : que des except, c'est curieux, sûrement une subtilité Windows/Linux qui m'échappe.
Bud Spencer a écrit :Ps : Si il te vient a l’idée d’utiliser du python 3 plutôt que du 2.x, il faudra penser à remplacer les client_socket.sendto(data,address) par des client_socket.sendto(data.encode() ,address). C’est juste dû au fait qu’en python 3, la fonction attend les data sous forme de byte array et non pas sous forme de string. Code C# serveur et code client python 3 à dispo si ca t'intéresse
Le portage en Python 3 était prévu, histoire d'éviter d'avoir du legacy code à l'avenir ; mais, comme tu me l'as déjà dit avant, je serais aussi bien passé sur du compilé pour obtenir de meilleures performances.

Pour rappel de ce que je souhaite faire : j'ai un radar qui envoie 16 384 impulsions A par tour, et une impulsion B à chaque nouveau tour ; je souhaite compter le nombre d'impulsions A entre deux impulsions B, mesurer la durée des impulsions A, et mesurer la durée entre deux impulsions A. Avec mon programme, l'idée est d'utiliser une Arduino Uno pour faire tout le traitement précédemment expliqué (déjà fait précédemment), puis d'envoyer tout ça au RPi via Ethernet.

Du coup dernières questions : si j'enregistre tout ça en local sur l'Arduino à chaque tour (nombre d'impulsions A + durée des impulsions A * 16 384 + durée entre deux impulsions A * 16 384), et que j'envoie ça au RPi dès que j'ai toutes les données du tour, pour ensuite recommencer à chaque tour, est-ce que ce sera assez rapide en Ethernet ? Je suis limité à l'envoi réception de chaînes de caractères uniquement ?
Si l'envoi est trop lent et que je loupe des impulsions, je dois trouver autre chose... MQTT peut-être ? :D

Si je ne suis pas assez clair, n'hésitez pas à demander des précisions !

Merci à tous pour vos réponses. :)
  • RPi 3 + LibreELEC / RPi 3 + RetroPie / RPi B+ + Sense HAT ou Framboisedorf ou module caméra
  • Arduino Mega, Uno, Nano
  • Freescale FRDM KL25Z

romaxx
Messages : 78
Enregistré le : lun. 24 oct. 2016 10:59

Re: Python et Ethernet/UDP

Message par romaxx » mer. 21 juin 2017 10:09

Pinhapple a écrit :est-ce que ce sera assez rapide en Ethernet
ça c'est à tester.. quitte à bufferiser coté arduino.

Après je ne connais pas les performances d'un langage interprété (même si certaines parties sont wrappées) plutôt que d'un bon vieux code compilé en ce qui concerne les performances réseaux.
En général dès qu'on veut un minimum de temps réel ou de fiabilité => Python out..
--
Adhérent à l'A.F.S.T.L

destroyedlolo
Raspinaute
Messages : 1160
Enregistré le : dim. 10 mai 2015 18:44
Localisation : Dans la campagne à côté d'Annecy
Contact :

Re: Python et Ethernet/UDP

Message par destroyedlolo » mer. 21 juin 2017 10:57

Pinhapple a écrit : Pour rappel de ce que je souhaite faire : j'ai un radar qui envoie 16 384 impulsions A par tour, et une impulsion B à chaque nouveau tour ; je souhaite compter le nombre d'impulsions A entre deux impulsions B, mesurer la durée des impulsions A, et mesurer la durée entre deux impulsions A. Avec mon programme, l'idée est d'utiliser une Arduino Uno pour faire tout le traitement précédemment expliqué (déjà fait précédemment), puis d'envoyer tout ça au RPi via Ethernet.

Du coup dernières questions : si j'enregistre tout ça en local sur l'Arduino à chaque tour (nombre d'impulsions A + durée des impulsions A * 16 384 + durée entre deux impulsions A * 16 384), et que j'envoie ça au RPi dès que j'ai toutes les données du tour, pour ensuite recommencer à chaque tour, est-ce que ce sera assez rapide en Ethernet ? Je suis limité à l'envoi réception de chaînes de caractères uniquement ?
Quelle en serait la volumétrie a transférer par seconde ?
Il faut savoir que de part son architecture, la frambroise est loin d'être un foudre de guerre niveau réseau (parce hub USB interne, et s'est encore pire si tu utilise le disque en même temps).
Cependant, il y a quand même de la marge avant de le saturer.
Pinhapple a écrit :Si l'envoi est trop lent et que je loupe des impulsions, je dois trouver autre chose... MQTT peut-être ? :D
Il n'accelèrera pas le transfère. Mais par contre, il pourra t'éviter de perdre des trames.
Des tests sont a faire ... est-ce qu'un broker tournant sur un PI peut suivre ?
Est-ce que les envoies sont en continus ou as-tu des "pause" entre les batches ?
romaxx a écrit :En général dès qu'on veut un minimum de temps réel ou de fiabilité => Python out..
On est bien d'accord :)
  • BananaPI : Gentoo, disque SATA de 2 To
  • Domotique : 1-wire, TéléInfo, Tablette passée sous Gentoo, ESP8266
  • Multimedia par DNLA
  • Et pleins d'idées ... et bien sûr, pas assez de temps.
Un descriptif de ma domotique 100% fait maison.

Répondre

Retourner vers « Python »