Lire un fichier XML local et extraire des données

Bonjour à tous,
Je sollicite la communauté car je bute sur le problème suivant :
J'ai un fichier XML contenant des données (fichier stocké sur mon PC) et je souhaite pouvoir récupérer l'une de ces données avec un arduino.
Je suis un débutant en programmation et donc je me base énormément sur des tutos et vidéo que je glane sur internet.
En l’occurrence j'ai trouvé un code qui pourrait me permettre de tester la librairie tinyxml2 qui apparemment est "l'outil" nécessaire, et ainsi comprendre son fonctionnement et l'adapter ensuite à mes besoins.

Voici donc mon code :

#include <tinyxml2.h>
 
using namespace tinyxml2;

char * testDocument = "<root><element>7</element></root>";
 
void setup() {
     
  Serial.begin(9600);
   
  XMLDocument xmlDocument;
 
  if(xmlDocument.Parse(testDocument)!= XML_SUCCESS){
    Serial.println("Error parsing"); 
    return; 
  };
 
  XMLNode * root = xmlDocument.FirstChild();
  XMLElement * element = root->FirstChildElement("fuelUsage");
 
  int val;
  element->QueryIntText(&val);
   
  Serial.println(val);
}
 
void loop() {}

Cependant après avoir testé ce code j'ai une erreur lors de la compilation. La voici :

In file included from D:*****\Parse_XML.ino:1:0:

D:*******\Arduino\libraries\tinyxml2/tinyxml2.h:37:13: fatal error: cctype: No such file or directory

include

^~~~~~~~

compilation terminated.

exit status 1
Erreur de compilation pour la carte Arduino Uno

J'ai testé avec une Leonardo mais le problème est le même.

A la lecture du code d'erreur il semble que le programme trouve une erreur avec la librairie à la ligne 37.
J'ai bien trouvé cette ligne dans le fichier tinyxml2.h, mais là je bloque complètement.
Cela dépasse mes capacité pour l'instant.

Peut-être pouvez-vous m'orienter vers un autre tuto ou exemple afin que je puisse comprendre/apprendre de mon erreur.
Je n'ai pas trouvé d'autre exemple (que je suis capable de comprendre) qui me permet de trouver la solution à mon problème.
Merci par avance pour vos contributions futures. :slight_smile:

Cette librairie n'est pas adaptée pour l'Arduino.
Peut-être regarder de ce coté là

En fait elle est adaptée si vous utilisez un ESP32 par exemple, tinyxml2

Dans un petit arduino il n'y a pas la place mémoire pour construire le 'DOM' (modèle objet du document en mémoire) correspondant

fdufnews:
Cette librairie n'est pas adaptée pour l'Arduino.
Peut-être regarder de ce coté là

je l'avais regardée il y a un moment et je pense que c'est buggy...

Par exemple ils définissent leur structure de parsing comme étant

const parseTable stateTable[] = {
/* 00 Init                */  {'<',           incLTcount,        starttagname,    TagStart},
/* 01 2                   */  {whiteSpace,    donothing,         donothing,       Init},
/* 02 3                   */  {anychar,       cleardata,         storeifneeded,   Init1},

/* 03 Init1               */  {'<',           incLTcount,        starttagname,    TagStart},
/* 04 2                   */  {anychar,       storeifneeded,     donothing,       Init1},
...

et dans l'analyseur il font comme si c'était en PROGMEM (ce qui n'est pas le cas) chToParse = pgm_read_word(&(pTable[currentState].charToParse));

--> à mon avis ça ne fonctionne pas.

L'approche est intéressante car il travaille sur un flux et ça extrait les données pertinentes au vol mais il faudrait se pencher sérieusement sur l'implémentation pour optimiser la mémoire mais si vous virez tous ces pgm_read_word() alors on peut en tirer quelque chose sur un UNO pour des petits XML

Tenez j'ai retrouvé mon test et j'ai adapté le code de test à votre XML

Je mets en PJ la bibliothèque modifiée - les modifications sont décrites dans le LisezMoi (mal écrit d'ailleurs dans le ZIP :slight_smile: )

Librairie venant de GitHub - adafruit/TinyXML: Fork of Adam Rudd's (adamvr) TinyXML library

Modifiée par &J-M-L https://forum.arduino.cc/index.php?action=profile;u=438300
car buggy


Dans le .cpp
J'ai viré tous les pgm_read_word

Dans le .h
j'ai changé les types qui étaient uint8_t en int16_t pour

int16_t LTCount;
int16_t tagCount;

(Car ils sont testés pour voir s'ils deviennent négatifs ce qui est impossible en non signé)

le code pour tester:

/*
    Copyright (c) 2020 J-M-L  https://forum.arduino.cc/index.php?action=profile;u=438300
    Author        :   J-M-L
    Create Time   :   May 2020
    Change Log    :

    The MIT License (MIT)
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.

    USING LIBRARIES (c) their own authors. See their terms and conditions
    TinyXML.h                 https://github.com/adafruit/TinyXML

*/


#include <TinyXML.h> // https://github.com/adafruit/TinyXML
TinyXML    xml;
uint8_t    xmlBuffer[150]; // For XML decoding

const char testDocument[] = "<root><element>7</element></root>";


void XMLParser(uint8_t statusflag, char* nameBuffer,  uint16_t namebuflen, char* dataBuffer,  uint16_t databuflen)
{
  Serial.print(F("XMLParser Status= "));
  switch (statusflag) {
    case STATUS_START_TAG:  Serial.print(F("STATUS_START_TAG")); break;
    case STATUS_TAG_TEXT:   Serial.print(F("STATUS_TAG_TEXT")); break;
    case STATUS_ATTR_TEXT:  Serial.print(F("STATUS_ATTR_TEXT")); break;
    case STATUS_END_TAG:    Serial.print(F("STATUS_END_TAG")); break;
    case STATUS_ERROR:      Serial.print(F("STATUS_ERROR")); break;
  }
  if (namebuflen) {
    Serial.print(F(" Name= "));
    Serial.print(nameBuffer);
  }
  if (databuflen) {
    Serial.print(F(" data = "));
    Serial.print(dataBuffer);
  }
  Serial.println();

  const char* etiquetteInteressante = "/root/element";
  if ((statusflag == STATUS_TAG_TEXT) && !strcasecmp(nameBuffer, etiquetteInteressante)) {
    Serial.print(F("J'ai trouvé ")); Serial.print(etiquetteInteressante);
    Serial.print(F(" qui vaut '")); Serial.print(dataBuffer); Serial.println(F("'"));
  }

}

void setup()
{
  Serial.begin(115200);
  xml.init(xmlBuffer, sizeof(xmlBuffer), &XMLParser);

  Serial.println(F("\n*** DEBUT XML Parser ***"));
  for (const char* p = testDocument; *p; p++) {
    xml.processChar(*p); // feed the beast
  }
  xml.reset(); // for next time
  Serial.println(F("\n*** FIN XML Parser ***\n"));
}

void loop() {}

le moniteur série (à 115200 bauds) affichera

[color=purple]

*** DEBUT XML Parser ***
XMLParser Status= STATUS_START_TAG Name= /root
XMLParser Status= STATUS_START_TAG Name= /root/element
XMLParser Status= STATUS_TAG_TEXT Name= /root/element data = 7
[color=red]J'ai trouvé /root/element qui vaut '7'
[/color]XMLParser Status= STATUS_END_TAG Name= /root/element
XMLParser Status= STATUS_END_TAG Name= /root

*** FIN XML Parser ***
[/color]

---> on voit bien donc que le call back est appelé à des moment opportuns et que j'ai pu trouver (en rouge) la valeur 7.

Pour extraire vos données (7 ici) il faut attendre un statusflag de type STATUS_TAG_TEXT, que le tag corresponde à celui recherché (ici '/root/element') et extraire avec un atoi() par exemple la valeur dans dataBuffer

je n'ai pas poussé très loin les tests pour voir la robustesse au dépassement mémoire etc....

TinyXML.zip (6.66 KB)

Tout d'abord merci à vous deux pour vos réponses rapides.
J'avais tenté tinyXML mais cela ne fonctionnait pas non plus, maintenant je comprend pourquoi.

En ce qui me concerne je ne suis pas figé sur une arduino UNO.
J'avais trouvé des références à l'ESP32 sur des forums Anglais mais c'était trop technique pour moi et je n'avais pas saisi qu'il s'agissait tout simplement d'un type de carte.

En ce qui concerne ta réponse J-M-L, elle est très complète et je te remercie pour ton investissement !
Il va me falloir un peu de temps pour comprendre et assimiler ton explication car je n'ai que peu de connaissances mais c'est super intéressant et en plus ça fonctionne, donc bref, c'est parfait.
Je m'en vais donc tester et étudier tout cela.
Je vous remercie pour le temps que vous m'avez accordé.

Pour votre information, mon objectif final est simplement de faire afficher sur un Ecran LCD relier à l'arduino (par exemple) le contenu d'une balise précise stockée dans un fichier XML d'un PC.
Lors de l'émergence de mon idée pour mon projet, cela semblait simple et accessible mais en fait ce n'est pas si évident que cela vu mon niveau. Quoi qu'il en soit c'est cool j'apprends toujours des trucs en bidouillant avec Arduino.
Bonne continuation à vous. :smiley:

Avec cette bibliothèque, vous pouvez balancer le flux du fichier XML sur le port série et au lieu d'envoyer les octets d'une chaîne à l'analyseur, vous envoyez les octets reçus sur le port série.

L'analyseur va simplement essayer de trouver votre balise. c'est jouable si le XML n'est pas trop compliqué avec plein de grandes données (car la mémoire est très limitée sur le UNO)

Bonsoir,
Merci pour vos conseils, mais là du coup, je suis complètement largué !
Envoyer un fichier sur le port série : je comprends l'idée mais dans la pratique pour moi, c'est le flou le plus total. Désolé.
Après je ne veux pas abuser et vous monopoliser ; donc vous n'êtes pas obliger de passer trop de temps sur mon "cas". Etant débutant, j'ai conscience de mes limites et du fait que vous ne pouvez pas me faire tout un cour sur la programmation en C ou C++.

Sinon, pour ce qui est du fichier XML il n'est pas très volumineux selon moi, 10 Ko environ pour 170 balises qui ne dépassent pas 15 caractères. Mais est-ce déjà trop pour un Arduino ou dois-je prévoir de changer de matériel selon vous?

Quoi qu'il en soit merci pour tout.

Il vient d’où ce fichier xml ? Internet ?

C'est un fichier provenant d'un jeu vidéo Farming Simulator.
Je fabrique une manette de commande pour mon fils qui joue à ce jeu avec des joystick et des boutons.
J'aimerai en plus des commandes ajouter un affichage LCD permettant d'afficher des informations provenant du jeu.
Par exemple le niveau de chargement d'une remorque où le niveau de carburant du véhicule utilisé.

Ces données sont stockées en local dans un fichier XML mis à jour en temps réel par le jeu.
Il est d'ailleurs aisé également de les modifier du coup.

Mais le but est de rendre le panneau de commande plus réaliste, un peu dans l'esprit de ce qui se fait pour les cockpits flight simulator. Sans avoir la prétention de lui refaire une cabine de tracteur !

Lui joue avec son jeu et sa manette, et moi je m'amuse en bidouillant l'arduino et un peu de code.

Le challenge sera de décider comment envoyer régulièrement le fichier a l’arduino.

Votre arduino ne peut pas lire le disque dur du PC

Il faudra donc une commande qui prend Régulièrement le fichier et l’envoie vers l’arduino. Cette commande pourrait extraire tant qu’elle y est les données importantes de façon à n’envoyer que ce qui est pertinent pour affichage et dans un format simple a gérer pour l’arduino tant que vous y êtes.

Ah oui, excellente idée.
Si je comprends bien, vous proposez de faire travailler l'ordinateur qui va extraire les données qui m'intéressent et n'envoyer donc à l'Arduino que ce qu'il a besoin d'afficher. Cela pour "épargner" l'analyse complète du fichier à l'Arduino et le faire faire à l'ordinateur qui, pour ça, n'aura pas de difficulté en terme de mémoire et puissance.

D'accord. Je vais m'orienter mes recherches vers ce point.

Et selon-vous qu'est-ce qui est le plus simple à lire pour lui, un simple fichier txt/csv? Vu que je n'ai, au final, qu'une dizaine de données différentes qui s'afficheront en fonction du bouton pressé.

Merci beaucoup pour vos contributions et ce petit coup de pouce !

exactement. Votre ordinateur a plein de mémoire et de puissance de calcul.... comme de toutes façons quelque chose doit s'executer sur le PC, autant faire un max de boulot là bas...

pour envoyer les données le plus simple c'est de les mettre dans un format super simple à décoder avec un caractère de début de trame, un caractère de fin de trame et entre les valeurs à afficher dans un ordre bien connu. par exemple

[color=red]>[/color]10,carburant bas,50,42[color=red]\n[/color]

le [color=red]>[/color] et le retour chariot à la fin [color=red]\n[/color] sont les marqueurs de début et de fin de message. entre les deux vous séparez les valeurs par des virgules ou un caractère qui n'apparaît pas dans les valeurs à transmettre.

ensuite à vous de savoir que 10 c'est le nombre de poules ,"carburant bas" un message à afficher, 50 le nombre de jour avant la moisson et 42 la réponse à la grande question sur la vie, l'univers et le reste (à adapter à vos besoins :slight_smile: )

L'arduino saurait exactement où afficher quel champ dans l'ordre de lecture

Sur Unix ce serait pas compliqué, sur un PC je n'utilise pas, mais j'imagine que c'est faisable en powershell ou en Python assez simplement.

Bon ben c'est parfait tout ça, ça me donne de quoi m'amuser.
J'adore la référence mais je suis loin d'être un voyageur galactique ! :grinning:
Je vais maintenant mettre tout cela en pratique.
En tout cas merci beaucoup pour votre aide et vos conseils précieux.
Bonne continuation.
Je reviendrai ultérieurement pour faire un point de situation et préciser la méthode utilisée pour arriver à mes fins.

Ok bons bidouillages !