[RESOLU] Améliorer l'implantation de requete udp Arduino wifi.

Bonjour,

Dans mon petit projet de gestion d'aquarium, j'utilise les requêtes udp pour modifier des variables eeprom de l'arduino mega 2560, depuis une interface web sous wamp.

L'arduino fait principalement des mesures de temperature, ph de l'eau, etcµ...

Mon programmes est de plus en plus complexe et je deviens confronté au probleme de reception des requete udp non prit en compte.

J'aurais aimé savoir comment cela fonctionnait.

lorsque j'envois une requete udp, la carte wifi stock la requete et lorsque l'on verifie la presence de données, recupere les données sur l'arduino. (je dois pas etre trop mauvais jusque la)

Petite question:

  1. combien de temps la requete est stocké dans la carte wifi?
  2. les interruptions pourrait peut etre m'aidé a ne plus raté les requetes udp reçu?
  3. Quelqu'un pourrait me detaillé le fonctionnement pour que je comprenne ou est mon problème?

D'avance merci!

Salut camarade

Je pense qu'on est en train de faire un peu la même chose, chacun de son côté.

A première vue j'aurais tendance à dire qu'il y a tout ce qu'il faut sur ta carte wifi pour ne pas avoir à se préoccuper de gérer la réception de trames udp via des interruptions de l'arduino, si tant est que cela soit possible.
Je pencherais plutôt pour un problème de structure globale de ton programme, ou bien un bug sournois dans un coin.

Qu'est-ce qui te fait dire que la trame n'est pas reçue ?
Ce serait possible de voir ton code ?

Salut,

logiquement, tant que tu ne vide pas le buffer il n'y a pas de raison qu'il se vide tout seul ... L'erreur vient à mon avis tout simplement de l'UDP : la base de l'UDP c'est justement de n'implémenter aucun contrôle. J'envois et je ne sais pas si c'est arrivé ou pas. Contrairement au TCP qui lui établi un vrai protocole de dialogue : établissement d'une connexion envoi de donnée, accusé de réception, si pas d'accusé je renvois ... C'est bien pour ça que mis à part des applications bien précises (jeu en ligne notamment) c'est le TCP qui prévaut.

Salut,

Tu pourrais mettre ton code et un détail tu la trame udp reçue ?

Ça pourrais aider.

Merci pour vos réponses!

Je poste demain soir mon code, car je suis en déplacement et je n'est pas pris mon code avec moi.

bricoleau:
Salut camarade
Je pense qu'on est en train de faire un peu la même chose, chacun de son côté.
Qu'est-ce qui te fait dire que la trame n'est pas reçue ?
Ce serait possible de voir ton code ?

Pour moi la trame n'ai pas reçu car la led bleu ne clignote pas et je ne reçois pas ma commande dans le debug de l'ide.
Ton projet est a quel statut? Si le mien te plait je suis pret a bossé a deux sans problème et de se repartir les taches.
Demain je t’envoie un lien de la partie web.

B@tto:
Salut,

logiquement, tant que tu ne vide pas le buffer il n'y a pas de raison qu'il se vide tout seul ...

L'erreur vient à mon avis tout simplement de l'UDP : la base de l'UDP c'est justement de

n'implémenter aucun contrôle. J'envois et je ne sais pas si c'est arrivé ou pas.

Contrairement au TCP qui lui établi un vrai protocole de dialogue : établissement d'une connexion envoi de donnée,

accusé de réception, si pas d'accusé je renvois ... C'est bien pour ça que mis à part des

applications bien précises (jeu en ligne notamment) c'est le TCP qui prévaut.

C'est bien ce qu'il me semblait.

TribesTom:
Salut,

Tu pourrais mettre ton code et un détail tu la trame udp reçue ?

Ça pourrais aider.

Voila le principe de fonctionnement de mon code:

  1. J'instancie
  • WifiClient
  • WifiUdp
  1. dans le setup
  • je connecte la carte wifi au reseau local
  • je stop la connexion udp Udp.stop();
  • j'initialise Udp.begin(localPort);
  1. dans le loop
  • je controle si il y a des paquets reçu (si ma trame a une correspondance => action)
  • [...Ici des mesures de sondes a intervalle regulier...]
  • connexion client pour envois de données dans une bd MYSQL (traitement via php) et stop de la connexion client.
  • // Fin du loop

Il y a peut être besoin d'un flush pour l'udp a un endroit ou un autre?

Pour ce qui ai de la trame voila ce que j'utilise comme code: (un bout de code qui traine dans le pc)

 // read packet
  int packetSize = Udp.parsePacket();

  if(packetSize)
  {
    // subtract the 8 byte header
    packetSize = packetSize - 8;
    memset(packetBuffer, 0, UDP_TX_PACKET_MAX_SIZE);

    // read packet into packetBufffer and get senders IP addr and port
    Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE);

    //parse packetBuffer
    char *record = packetBuffer;
    char *l, *i, *v, *f, *ss, *mm, *hh, *w, *d, *m, *y; 

    //  First strtok iteration find function to switch
    l = strtok_r(record,",",&i);
    to = atoi(l);

    //  strtok iteration 2
    v = strtok_r(NULL,",",&i);
    val = atoi(v);

    // strtok iteration 3
    f = strtok_r(NULL,",",&i);
    func = atoi(f);  

    ss = strtok_r(NULL,",",&i);
    sec = atoi(ss);

    mm = strtok_r(NULL,",",&i);
    min = atoi(mm);

    hh = strtok_r(NULL,",",&i);
    hour = atoi(hh);

    w = strtok_r(NULL,",",&i);
    date = atoi(w);

    m = strtok_r(NULL,",",&i);
    month = atoi(m);

    d = strtok_r(NULL,",",&i);
    day = atoi(d);

    Serial.print("function:");
    Serial.println(func);
    Serial.print("to:");
    Serial.println(to);
    Serial.print("Value:");
    Serial.println(val);

    //Switch function
    switch (func) { 
    //LED CHANNEL VALUES
    case 1: 
      if ( to == 1 )
      {
        EEPROM.write(eled1,val);
        c1 = val;
      }
      else if ( to == 2 )
      {
        EEPROM.write(eled2,val);
        c2 = val;        
      }
      else if ( to == 3 )
      {
        EEPROM.write(eled3,val);
        c3 = val;
      }
      restoreLed();
      break;

Sinon juste pour info:

  • carte wifi a jour.
  • carte arduino mis a jour il y a 1 an environ.
  • ide 1.0.5

D'ailleurs j'ai testé la version de l'ide 1.0.6 et l'udp ne fonctionnait plus du tout, meme en utilisant l'une de mes premiere source qui fonctionnait tres bien sous ide 1.0.5

J'ai pourtant basculé tout mes Serial.println(""); par Serial.println(F(""));

D'ailleurs je comprend pas que dans les librairies "Serial.println(F(""));" ne soit pas utilisé d'office pour libéré la flash.

Merci pour la proposition mais pour ce qui est de bosser ensemble sur le même projet, désolé je suis trop chiant, y compris avec moi-même. :stuck_out_tongue_closed_eyes:

Je suis du genre à coder toutes mes librairies, en les ré-écrivant intégralement plusieurs fois jusqu'à ce que je n'y trouve plus rien à y redire, au caractère espace près.

Et puis je ne suis pas pressé, et savoure le plaisir du DIY.

Pour ton problème j'ai une hypothèse à te soumettre :

Tu dis que ton loop est organisé ainsi :
a) je controle si il y a des paquets reçu (si ma trame a une correspondance => action)
b) [...Ici des mesures de sondes a intervalle regulier...]
c) connexion client pour envois de données dans une bd MYSQL (traitement via php) et stop de la connexion client.

Question : combien de temps dure ton étape b), et que se passe t'il si la trame udp est reçue pendant ce temps là ?
Je veux dire : peut-être que l'info stockée temporairement quelque part, en attente de récupération par ton programme principal, est lessivée par ton émission dans l'autre sens vers ta bd MYSQL

Ca doit pouvoir se tester
Par exemple, ajoute un gros delay, genre 10 secondes, avant l'étape c), et un autre juste après.
Vois ce qui se passe quand tu envoies ta trame udp pendant le premier delay ou pendant le second.

Oui c'est exactement ce qu'il se passe! Cela correspond a ce que j'ai pu remarqué.

Pour ce qui est de la durée de l'etape b, on va dire de l'ordre de la seconde. Mais c'est pas elle qui me bloque la connexion car dans mon deboggueur, la commande arrive après que l'action soit terminé.

Je test en profondeur ce week end.

Mais par contre je pense que l'envois de données a ma base Mysql lessive en effet mon emission udp.

A part envoyé mes données dans la bd MYSQL moins souvent pour réduire le pourcentage de perte de réception udp, tu vois une autre solution.

Comment procède tu pour envoyer tes données a la base de données MYSQL et utilise tu l'udp pour faire ta demande d'envois de données au micro pour tes valeurs de réglage sur le site web.

Tu partagerais tes sources pour mes cours particulier? ;D

Je procède de manière différente :

Tous les événements liés à l'aquarium (relevés de température, etc.) sont stockés sur une carte SD dans un fichier log. Une ligne horodatée par événement.

Pour la température, c'est un relevé toutes les 5 min (en réalité un relevé toutes les 30 secondes et calcul de moyenne sur 5 minutes en enlevant le min et le max).

L'arduino est complètement autonome, et peut fonctionner coupé de tout réseau pendant des jours sans perdre de données.

En parallèle, il fonctionne en serveur udp, et attend de recevoir des demandes sous forme de trame entrante.
Lorsqu'il y en a une, il la lit et la traite.

Une demande udp peut être de plusieurs natures. En gros :
a) demande d'info
a1) lecture de l'état général de l'aquarium (états des système, température, ...)
a2) lecture de la valeurs de paramètres
a3) lecture de log indexée
b) modification d'un paramètre
c) forçage de l'état des systèmes (pompe, chauffage, éclairage)

Une fois toutes ces primitives développées et disponibles au niveau de l'arduino, il y a plusieurs façons de les exploiter.

Par exemple, j'ai développé uen console en mode texte sur mon PC, qui permet de tout piloter à distance, et en premier lieu de tester toutes les primitives.

Ma prochaine étape, c'est d'installer une base MYSQL sur un raspberry pi, avec un script qui interroge régulièrement l'arduino pour récupérer sa log et l'insérer en table.
Après y aura la transformation du raspberry en serveur web : exploitation du contenu des tables

et en parallèle, pour le fun, développement d'une petite appli android dialoguant avec l'arduino pour récupérer et afficher l'état général de l'aquarium, voire éventuellement passer quelques commandes de base.

Mais avant tout ça, il me reste à finir l'automate arduino en lui-même. Il me reste quelques soucis à régler sur la partie électrique avant de le mettre véritablement en service.

Très bien merci pour ces info.

Sinon pour rester dans la logique de ton programme : déjà vois si ta connexion client pour uploader tes mesures n'est pas optimisable pour durer moins longtemps.

En particulier, si tu utilises un client.print, remplace le par un client.write(buf, len);
Ajoute un mouchard pour voir en combien de temps la fonction s'exécute, cela te permettra de vérifier le résultat.

Après, dans tous les cas, pour tout ce qui est communication par udp, il est nécessaire de prendre en compte le fait que certaines trames peuvent ne jamais arriver à bon port.

Par exemple, au niveau de l'arduino, lorsque tu traites un message reçu par udp, il faudrait que tu retournes une réponse udp à l'émetteur (un acquittement). Et côté émetteur, prévoir le cas où la réponse n'est pas reçue (réémission, etc.)

Mon bug, c'est cette fameuse tempo, dont voici le code:

      function loadData()
       {
                //Are we connected with arduino ? 
                sleep ( 9 );

                $this->checkconnect();

                //Load data file. 

                if ( !$handle = fopen(CONFIG_FILE_DATA, 'r') ) 
                {
                     print "false|".LANG_dontopen." (".CONFIG_FILE_DATA.")";
                     exit;
                }

                if ( !$contents = fread($handle, filesize(CONFIG_FILE_DATA)) ) 
                {
                     print "false|".LANG_nodata." (".CONFIG_FILE_DATA.")";
                     exit;
                }

                fclose($handle);

                if ( $contents )
                {
                     $contents = explode ( ',', $contents );
                     $contents = implode ( '|', $contents );
                     print ( "true|".LANG_dataok."|".$contents );
                } 
                else
                {
                     print "false|".LANG_datanotok."(".CONFIG_FILE_DATA.")";
                }
                exit;
       }

Il faut a tout pris que supprime cette tempo car si elle est trop longue, les données précédemment sauvegardées dans le fichier texte sont écrasées par les nouvelles données envoyées par l'arduino pour la base de donnée.

Je vais donc attendre de recevoir la confirmation udp pour lancer cette fonction. D'ailleurs tu fait comment pour définir une tempo en php au cas ou la connexion serait HS, pour renvoyer la requette udp au bout d'un certain temps. (je devrais faire un compteur pour limiter le nombre de tentative...

Je suppose qu'en contrôlant la taille du fichier avec filesize, pour contrôler la bonne écriture des données et relancer la demande udp si le fichier n'est pas assez lourd.

Je vais testé ta méthode client.write(buf, len); et je te donne le résultat, la différence devrait être énorme!

Bravo, ma bêtise, tu la senti venir... (pour le reply de l'udp)

Merci!

Arf ça fume! :cold_sweat:

C'est pas si simple à mon niveau.

Mes données actuellement son de se format:

data = "do=data&data="+dateTimeForWeb()+","
+EEPROM.readBit(EEautoUpdateTimeStatus,0)+","+"0"+","  
+EEPROM.readBit(EElanguage,0)+","+EEPROM.readBit(EEdisplayError,0)+","   +EEPROM.readByte(EEipArduinoOctet1)+","+EEPROM.readByte(EEipArduinoOctet2)+","+EEPROM.readByte(EEipArduinoOctet3)+","+EEPROM.readByte(EEipArduinoOctet4)+","
+EEPROM.readInt(EEportUdpArduino)+","
[...]

Voila ce que j'ai fait:

#include <avr/pgmspace.h>       // for progmem

#define P(name)   static const prog_uchar name[] PROGMEM  // declare a static string


P(data) = "do=data&func=3&date="+dateTime()
      +"&temp1="+tempAquaTankStr+"&temp2="+tempAquaTankSoilStr+"&temp3="+tempFillingTankStr+"&temp4="+tempAmbientStr+"&hygro1="+hygroAmbientStr
      +"&light1="+light1PowerStr+"&light2="+light2PowerStr+"&light3="+light3PowerStr+"&light4="+light4PowerStr
      //+"&food1="+EEPROM.readBit(EEstatusGraphFood1,0)+"&food2="+EEPROM.readBit(EEstatusGraphFood2,0)
      +"&co2="+co2NumbBubble+"&ph="+phStr;

[...]

//client.print( "Content-Length: " ); // ici j'ai mi en commentaire j'ai pas la solution
//client.println( data.length() );      // juste pour voir si ça compile
printP( data );

[...]


void printP(const prog_uchar *str)
{

  uint8_t buffer[32]; 
  size_t bufferEnd = 0;

  while (buffer[bufferEnd++] = pgm_read_byte(str++))
  {
    if (bufferEnd == 32)
    {
      client.write(buffer, 32);
      bufferEnd = 0;
    }
  }

  // write out everything left but trailing NUL
  if (bufferEnd > 1)
    client.write(buffer, bufferEnd - 1);
}

Je sèche sur plusieurs problème rencontré.

  1. Dans mes données data je doit convertir en string toute les valeurs?
  2. il n'arrive pas a determiné la taille de data, le signe + il aime pas?
  3. calculer client.print( "Content-Length: " ); me semble pas evidant?!
  4. uint8_t buffer[32]; je vois pas trop commant definir la grosseur du buffeur en automatique?!

Bon sur ce, la nuit porte conseil, donc bonne nuit! :wink:

Salut,

J'ai implémenté la récupération de la réponse UDP pour remplacer ma tempo, ça tourne du tonnerre. :grin:

Je t'avoue que j'y pensais même pas. MERCI

Par contre pour ce qui est de remplacer le client.prin par un client.write(buf, len); eh bien je suis à la rue.