[résolu] 2 libraries utilisées: Conflit PSTR ?

Bonjour / Bonsoir

Je suis en train de travailler sur mon premier vrai projet Arduino depuis maintenant quelques mois.
J'ai vraiment TRÈS bien progressé mais là, depuis 2 jours, je suis bloqué. Habituellement, je cherche jusqu'a ce que je trouve la solution par moi même (généralement en me documentant) car c'est comme ca que j'apprend le mieux. Mais ce soir, j'avoue, je suis à court d'idée. Je me permet donc de vous soumettre mon problème.

J'utilise dans mon projet principalement 2 libraries: ethercard (module ENC28j60) et RF24 ( nRF24L01)
J'ai deja fais pas mal de tests avec ces deux libraries ensemble et je n'ai jamais eu de problème auparavant. Elles fonctionnent très bien toutes les deux.

Voici ce que je fais mon projet en mode simplifié:
J'ai un serveur web qui tourne sur l'arduino, il recoit une requete, il recupère une valeur passé dans la querystring et il envoit cette valeur par onde radio via le RF24 à un autre arduino.
Jusque la cela fonctionnait bien.

Je voudrais maintenant que ma value recu soit aussi en même temps envoyé a PACHUBE (ou devrais je dire COSM maintenant)

Avec la librarie Ethercard, il y a un exemple pour envoyer des data sur PACHUBE. Je l'ai testé, ca fonctionne super. J'ai donc repris ce bout de code et l'ai ajouté a mon code principal. Mais depuis que j'ai fais ca, je suis impossible de compiler mon code, j'ai toujours 4 erreurs:

error: __c causes a section type conflict

qui apparait vis à vis de ce morceau de code:

    Stash::prepare(PSTR("PUT http://$F/v2/feeds/$F.csv HTTP/1.0" "\r\n"
                        "Host: $F" "\r\n"
                        "X-PachubeApiKey: $F" "\r\n"
                        "Content-Length: $D" "\r\n"
                        "\r\n"
                        "$H"),
            website, PSTR(FEED), website, PSTR(APIKEY), stash.size(), sd);

C'est le PSTR qui pose problème.

J'ai essayé d'enlever le plus d'info dans mon code pour essayer de trouver l'endroit qui cause problème et je me suis rendu compte que les deux libraries déclarent PSTR

RF24Config.h pour la lib RF24

// Avoid spurious warnings
#if 1
#if ! defined( NATIVE ) && defined( ARDUINO )
#undef PROGMEM
#define PROGMEM __attribute__(( section(".progmem.data") ))
#undef PSTR
#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
#endif
#endif

// Progmem is Arduino-specific
#ifdef ARDUINO
#include <avr/pgmspace.h>
#define PRIPSTR "%S"
#else
typedef char const char;
typedef uint16_t prog_uint16_t;
#define PSTR(x) (x)
#define printf_P printf
#define strlen_P strlen
#define PROGMEM
#define pgm_read_word(p) (*(p)) 
#define PRIPSTR "%s"
#endif

Et dans tcpip.cpp pour la lib Ethercard

// Avoid spurious pgmspace warnings - http://forum.jeelabs.net/node/327
// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
#undef PROGMEM 
#define PROGMEM __attribute__(( section(".progmem.data") )) 
#undef PSTR 
#define PSTR(s) (__extension__({static prog_char c[] PROGMEM = (s); &c[0];}))

J'ai essayé de me documenter sur cela mais cela depasse mes compétences malheuresement.
Je comprend l'utilité de PROGMEM (après mettre documenté)... J'imagine que PSTR c'est pour dire "print string" et que par conséquent, j'imagine que ca sert à "printer" une valeur qui etait setté avec progmem...
Mais à part cela...

J'ai fais pas mal de test:
-Supprimer ces defines
-Laisser seulement ceux de RF24
-Laisser seulement ceux de Ethercard
-Supprimer les deux et inserer juste ceux de ethercard à meme mon fichier ino
-Remplacer celui de RF24 par exactement le même que celui de ethercard.

Je suis à court d'idée. Si jamais quelqu'un a une piste, je suis preneur!

Je met le code source de mon programme qui ne compile pas en l'état des lieux:

https://dl.dropbox.com/u/138037/web/code.ino

Et voici les liens vers les deux libs en question:

Merci beaucoup

Bonjour

PSTR ne sert pas à faire un print mais c'est une macro qui simplifie la déclaration d'une chaine de caractère qui devra rester en mémoire Flash.

En effet, par défaut, les mémoires programmes (flash) et données (ram) d'un ATmega sont différents et ne peuvent pas être accédés de la même façon (cherche la différence entre model VonNeuman et modèle Harvard pour en savoir plus. l'atmega suit le modèle Harvard).
Ce qui veut dire qu'au début du programme (avant le setup) il existe un code qui recopie les chaines de caractères de la flash dans la RAM.
C'est evidemment idiot car cela consomme de la RAM pour des informations qui sont constantes.

Pour cela, tu peux forcer le compilateur a laisser des chaines de caractères en Flash mais il faut que derrière les fonctions sachent aller chercher la chaine de caractère en flash plutot qu'en RAM. Ainsi par exemple la fonction sprintf( destination, format, ....) se sait pas aller chercher la chaine de format ailleurs qu'en RAM. Si tu utilises sprintf( result, PSTR("%u"), 5 ) ca ne marcheras pas. Il existe pour cela une fonction spéciale sprintf_P qui sait aller chercher la chaine de format en Flash (mais pas en RAM...)
Serial.print() au contraire sait faire la différence et aller chercher la chaine de caractère aussi bien en RAM qu'en flash.

Voila pour le principe.

Maintenant pour en revenir à ton problème...
Ces lib redéfinissent la macro PSTR() d'une façon légèrement différent de la façon standard (qui est dans avr/pgmspace.h) afin d'éviter des warning que la version standard laisse passer à la compilation.

Mais je pense que le problème ne vient pas de là mais de ta façon de coder :

Stash::prepare(PSTR("PUT http://$F/v2/feeds/$F.csv HTTP/1.0" "\r\n"
                        "Host: $F" "\r\n"
                        "X-PachubeApiKey: $F" "\r\n"
                        "Content-Length: $D" "\r\n"
                        "\r\n"
                        "$H"),
            website, PSTR(FEED), website, PSTR(APIKEY), stash.size(), sd);

Le problème devrait apparaitre aussi bien avec 1 ou 2 lib car la syntaxe utilisée est incomplète/erronée.
En effet d'un point de vue C, PSTR étant une macro doit tenir sur une seule ligne !
Hors ta façon de coder la chaine de caractères sur plusieurs lignes est compatible d'un appel de fonction mais pas de l'utilisation d'une macro.
Il existe en C un moyen d'indiquer qu'une même ligne de code se poursuit sur plusieurs lignes de l'éditeur de texte. Pour cela il faut ajouter un "backslash" \ sans autre caractères après à la fin de la ligne :

Stash::prepare(PSTR("PUT http://$F/v2/feeds/$F.csv HTTP/1.0" "\r\n" \
                        "Host: $F" "\r\n"  \
                        "X-PachubeApiKey: $F" "\r\n"  \
                        "Content-Length: $D" "\r\n"  \
                        "\r\n"  \
                        "$H"),
            website, PSTR(FEED), website, PSTR(APIKEY), stash.size(), sd);

devrait marcher

Bonjour

Merci pour tes explications. Ca m'aide beaucoup.
Cependant le script fonctionne si j'enleve tout ce qui est relatif aux RF24.
J'ai quand même essayé ce que tu m'a donné, mais malheuresement cela fait la même erreur.

Bonsoir

Personne n'aurait une 2eme idée? J'ai encore fais plein de nouvelle recherche, sur les macro, mais rien de concret pour le moment :frowning:

Merci

Je suis en déplacement professionnel cette semaine donc pas trop le temps mais j'essayerais ce week-end si personne d'autre ne trouve la solution d'ici là.

Pourrais tu poster le INO minimum qui permet de reproduire le problème ainsi que le lien vers les site où tu as téléchargé les lib utilisée ?
Ceci pour être sur de tester dans les mêmes conditions que toi.
Ton IDE Arduino est bien la dernière v1.0.1 ?

Il y a un autre moyen à mon avis : définis 2 PSTR : tu changes les définitions par exemple en PSTRE pour EtherCard et PSTRR pour RF24. Puis dans le sketch tout ce qui a trait à EtherCard tu utilises PSTRE et pour RF24 PSTRR. Comme ce sont des macros, de toute façon à la compilation c'est remplacé par autre chose.

Pour les chaines de caractère, je crois qu'on de doit pas forcément mettre les "" de continuation. Ce n'est nécessaire que à l'intérieur de #define etc. Sinon, les retours lignes sont traités comme du "whitespace" normal.

Bonsoir

Merci pour vos réponses.
J'ai essayé dans la library RF24 de remplacer tous les PSTR par PSTR2 et laisser tel quel dans la librarie ethercard mais ca ne fonctionne pas. Ca me fait plein d'erreur comme par exemple l'objet SPI qui est indéfini (??)
Mais j'ai l'impression que ce doit forcement être PSTR car cela decoule du fichier "avr/pgmspace.h"

Je vais faire le prototype minimum.
Merci beaucoup!

Ok, pour faire un prototype le plus simple qui ne fonctionne pas, c'est très simple

Il faut installer les deux librairies:
RF24
https://github.com/maniacbug/RF24/archive/master.zip
Et Ethercard
https://github.com/jcw/ethercard/archive/master.zip

Ensuite, dans les exemple fournit avec Ethercard, il y a pachube.ino

Si on compile comme cela, ça fonctionne, mais dès qu'on ajoute l'inclusion de RF24 (#include <RF24.h>), ca ne fonctionne plus

L'exemple complet AVEC l'inclusion de rf24:

// Simple demo for feeding some random data to Pachube.
// 2011-07-08 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php

#include <EtherCard.h>
#include <RF24.h>   // L'INCLUSION QUI PAUSE PROBLEME !!!!!!!!!!!!

// change these settings to match your own setup
#define FEED    "5942"
#define APIKEY  "bXkPFCiYm57f7flLyD86bm0HK3TXsfuQF-Jeyh3HeMg"

// ethernet interface mac address, must be unique on the LAN
byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };

char website[] PROGMEM = "api.pachube.com";

byte Ethernet::buffer[700];
uint32_t timer;
Stash stash;

void setup () {
  Serial.begin(57600);
  Serial.println("\n[webClient]");

  if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) 
    Serial.println( "Failed to access Ethernet controller");
  if (!ether.dhcpSetup())
    Serial.println("DHCP failed");

  ether.printIp("IP:  ", ether.myip);
  ether.printIp("GW:  ", ether.gwip);  
  ether.printIp("DNS: ", ether.dnsip);  

  if (!ether.dnsLookup(website))
    Serial.println("DNS failed");
    
  ether.printIp("SRV: ", ether.hisip);
}

void loop () {
  ether.packetLoop(ether.packetReceive());
  
  if (millis() > timer) {
    timer = millis() + 10000;
    
    // generate two fake values as payload - by using a separate stash,
    // we can determine the size of the generated message ahead of time
    byte sd = stash.create();
    stash.print("0,");
    stash.println((word) millis() / 123);
    stash.print("1,");
    stash.println((word) micros() / 456);
    stash.save();
    
    // generate the header with payload - note that the stash size is used,
    // and that a "stash descriptor" is passed in as argument using "$H"
    Stash::prepare(PSTR("PUT http://$F/v2/feeds/$F.csv HTTP/1.0" "\r\n"
                        "Host: $F" "\r\n"
                        "X-PachubeApiKey: $F" "\r\n"
                        "Content-Length: $D" "\r\n"
                        "\r\n"
                        "$H"),
            website, PSTR(FEED), website, PSTR(APIKEY), stash.size(), sd);

    // send the packet - this also releases all stash buffers once done
    ether.tcpSend();
  }
}

Et ce que cela renvoit:

pachube:62: error: __c causes a section type conflict
pachube:62: error: __c causes a section type conflict
pachube:56: error: __c causes a section type conflict

Je vous remercis vraiment beaucoup. Si j'arrive a être "decoincé", ça va être la fête :slight_smile:

Problème reproduit, simulé mais pas entièrement compris.

  1. Dans ton INO, ajouter #include <SPI.h> avant Ethercard et RF24
#include <SPI.h>
#include <EtherCard.h>
#include <RF24.h>
  1. Dans RF24_config.h remplacer #if 1 par #if 0
// Avoid spurious warnings
#if 0
#if ! defined( NATIVE ) && defined( ARDUINO )
#undef PROGMEM
#define PROGMEM __attribute__(( section(".progmem.data") ))
#undef PSTR
#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
#endif
#endif

Mais je ne comprend pas à la base le conflit créé

Ok

Est ce que tu veux dire qu'en faisant cela, tu as résolu le conflit? Je ne suis pas sur de comprendre.
De mon coté je viens d'essayer et malheureusement ça me créer d'autre erreur que voici:

/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘void RF24::csn(int)’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:22: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:23: error: ‘SPI_MODE0’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:24: error: ‘SPI_CLOCK_DIV4’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘uint8_t RF24::read_register(uint8_t, uint8_t*, uint8_t)’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:43: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘uint8_t RF24::read_register(uint8_t)’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:57: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘uint8_t RF24::write_register(uint8_t, const uint8_t*, uint8_t)’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:71: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘uint8_t RF24::write_register(uint8_t, uint8_t)’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:89: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘uint8_t RF24::write_payload(const void*, uint8_t)’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:110: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘uint8_t RF24::read_payload(void*, uint8_t)’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:133: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘uint8_t RF24::flush_rx()’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:150: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘uint8_t RF24::flush_tx()’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:163: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘uint8_t RF24::get_status()’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:176: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘void RF24::begin()’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:340: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘uint8_t RF24::getDynamicPayloadSize()’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:529: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘void RF24::toggle_features()’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:661: error: ‘SPI’ was not declared in this scope
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp: In member function ‘void RF24::writeAckPayload(uint8_t, const void*, uint8_t)’:
/home/nicolas/arduino-1.0.2/libraries/RF24/RF24.cpp:726: error: ‘SPI’ was not declared in this scope

Je ne comprend vraiment pas. Surtout que j'ai bien ajouté le include spi au tout debut

Je confirme qu'avec juste ces modifs, l'IDE 1.0.1 sous Windows, je n'ai aucun pb de compilation

Il faut que je récupère l'IDE 1.0.2 pour comparer, je n'ai pas encore eu le temps de le faire

J'essayais sour la version 1.02
je viens d'essayer avec la 1.01 mais j'ai la même erreur.
Mais je suis sous linux par contre.

=(

je n'ai meme pas de windows pour tester

Bonsoir

J'ai tout repris du debut et cela fonctionne!

Barbudor avait effectivement raison: avec ta modification, cela fonctionne.

C'était moi le problème: À force de faire des tests, j'avais ( je ne sais pas comment ) supprimé la librarie SPI tout simplement...
Ce qui m'étonne c'est que je n'ai pas d'erreur lors de la compilation si je met un mauvais nom de lib, ou une lib qui n'existe pas.
Je n'avais pas pensé a voir si cela était relié à la lib SPI car l'exemple "pachube" fonctionnait. J'en conclus que la lib ethercard n'utilise pas la lib SPI (alors que dans ma tête, c'était le cas)

Merci à tous! Je vais pouvoir ENFIN continuer mon projet

Bonjour,

atlas2003:
Ce qui m'étonne c'est que je n'ai pas d'erreur lors de la compilation si je met un mauvais nom de lib, ou une lib qui n'existe pas.
Je n'avais pas pensé a voir si cela était relié à la lib SPI car l'exemple "pachube" fonctionnait. J'en conclus que la lib ethercard n'utilise pas la lib SPI (alors que dans ma tête, c'était le cas)

La librairie Ethercard utilise en interne sa propre version de la librairie SPI.
Le code de gestion du port SPI est dans "enc28j60.cpp" dans les alentours des lignes 256 ~ 314.
A mon avis le développeur de Ethercard ne voulais pas s'embêter avec la librairie SPI officielle.