Esp32 problème de compilation: collect2.exe: error: ld returned 1 exit status exit status 1 Erreur de compilation pour la carte ESP32-WROOM-DA Module

Bonjour,
je ne suis pas un expert en c, et après avoir consulté nombre de tutos (et l'anglais n'est pas mon fort, google que moyennement!) je viens vers vous tous pour résoudre mon problème.
je vous remercie par avance pour votre aide.

J'essaie de modifier un exemple posté par f-leb:
voici ou on peut le trouver.

  Serveur Web sur DevKit ESP32 - V0.1

   Voir la discussion forum sur Developpez.com :

[ESP32] Créer un serveur Web - Contribuez

   Le principe et les codes sont (fortement) inspirés par le tutoriel 
    de naute (https://www.developpez.net/forums/u292302/naute/) en trois parties :
    
    Le navigateur aux commandes de l’Arduino 
      - https://herve-troadec.developpez.com/tutoriels/arduino/le-navigateur-aux-commandes-de-l-arduino-partie-1/
      - https://herve-troadec.developpez.com/tutoriels/arduino/le-navigateur-aux-commandes-de-l-arduino-partie-2/
      - https://herve-troadec.developpez.com/tutoriels/arduino/le-navigateur-aux-commandes-de-l-arduino-partie-3/"

et le code que j'essaie de modifier: fichier .ino

/*************************************************************************************************************************************
         Serveur Web sur DevKit ESP32 - V0.1
     Voir la discussion forum sur Developpez.com :

[ESP32] Créer un serveur Web - Contribuez

     Le principe et les codes sont (fortement) inspirés par le tutoriel
     de naute (https://www.developpez.net/forums/u292302/naute/) en trois parties :

     Le navigateur aux commandes de l’Arduino
       - https://herve-troadec.developpez.com/tutoriels/arduino/le-navigateur-aux-commandes-de-l-arduino-partie-1/
       - https://herve-troadec.developpez.com/tutoriels/arduino/le-navigateur-aux-commandes-de-l-arduino-partie-2/
       - https://herve-troadec.developpez.com/tutoriels/arduino/le-navigateur-aux-commandes-de-l-arduino-partie-3/
         Licence CC-BY-NC-SA 2020

                                                           Juillet 2020
                                                           f-leb (https://www.developpez.net/forums/u283256/f-leb/)

 **************************************************************************************************************************************/

#include "HandleWebServer.h"
#include "WifiConfig.h"
//#include "hardware.h"



// Initialisation d'une instance de bus 1-Wire
//OneWire oneWire(oneWireBusPin);

// Initialisation de capteurs Dallas DS18b20 sur le bus 1-Wire
//DallasTemperature temperatureSensors(&oneWire);

AsyncWebServer myServer(80);      // instance du serveur web

void setup()
{
  //temperatureSensors.begin();    // Démarrage capteurs ds18b20
  // pinMode(ledPin, OUTPUT);       // ledPin en sortie

  Serial.begin(115200);          // Port série pour debug
  delay(100);
  SPIFFS.begin();                // Démarrage système de fichiers

  WIFISetUp();                   // Configuration WiFi

  configServer();                // Configuration serveur Web
  myServer.begin();              // Démarrage serveur Web
  Serial.println("Démarrage du serveur Web HTTP");
}

void loop()
{

  // Serial.print(getTemperature(), 1);
  // Serial.println("ºC");
  // delay(1000);

}

puis fichier HandleWebServer.ccp" et HandleWebServer.h"

#include "HandleWebServer.h"
#include "hardware.h"
#include "AsyncJson.h"
#include "ArduinoJson.h"


extern AsyncWebServer myServer;


void configServer(void) {
  myServer.on("/", HTTP_GET, [](AsyncWebServerRequest * request)
  {
    request->send(SPIFFS, "/telecommande.html");                                 //  envoi du fichier HTML
  });

  myServer.on("/icone.png", HTTP_GET, [](AsyncWebServerRequest * request)
  {
    request->send(SPIFFS, "/icone.png", "image/png");                           //  envoi de l'icone
  });

  myServer.on("/telecommande.js", HTTP_GET, [](AsyncWebServerRequest * request)
  {
    request->send(SPIFFS, "/telecommande.js", "application/javascript");        // envoi du javascript
  });

  myServer.on("/telecommande.css", HTTP_GET, [](AsyncWebServerRequest * request)
  {
    request->send(SPIFFS, "/telecommande.css", "text/css");                     // envoi de la feuille de style
  });
  //  reception d'une chaine de caractères
  myServer.on("/ajax", HTTP_GET, [](AsyncWebServerRequest * request)
  {
    
    if (request->hasParam("json")) //
    {
      AsyncWebParameter* p = request->getParam("json");                         // le js possede ce param
      String val = p->value();                                                  //  lecture du json reçu
      //     // Stream& input;

      StaticJsonDocument<200> doc;

      DeserializationError error = deserializeJson(doc, val);                   // le json est fourni à la désériialisation
      //                                                                                 qui délivre ce qui suit
      if (error) {
        Serial.print("deserializeJson() failed: ");
        Serial.println(error.c_str());
        return;
      }

      int  r1 = doc["r1"]; // 0
      int  r2 = doc["r2"]; // 0
      int  r3 = doc["r3"]; // 0
      int  r4 = doc["r4"]; // 0
      int  r5 = doc["r5"]; // 0
      int  r6 = doc["r6"]; // 0
      float t = doc["t"]; // 19
      /*     qui doit être distribué  */

      sendMaj(request);                                                             // renvoi d'une mise à jour
    }
    else if (request->hasParam("maj"))                                              // LE JS possède ce param
    {
      Serial.println("MAJ");
      sendMaj(request);
    }
    
  });

  myServer.onNotFound([](AsyncWebServerRequest * request) {
    request->send(404, "text/plain", "Erreur 404");
  });
}

void sendMaj(AsyncWebServerRequest *request) {
  AsyncResponseStream *response = request->beginResponseStream("application/json"); // initialise le flux de data
  DynamicJsonDocument doc(500);

  //  doc["bouton"] = getLedState() == HIGH ? "ledOn" : "ledOff";
  //  doc["temperature"] = getTemperature();

  serializeJson(doc, *response);                                                    // fabrique  la chaine json
  request->send(response);                                                          // envoi du json reformulé en réponse
}

[/code]

puis
 et c'est ici que réside le problème
J'essaie de récupérer les données reçues depuis un json vers un tableau de 6 int +un float.
Ce qui est fait dans la fonction my_serveur.on(.....
Si j'essaie de poser ce  tableau quelque part --> erreur de compilation.

* List item

Dans le .ino--> erreur
dans un nouveau fichier .h avec #ifndef.... -->erreur
dans la fonction elle même --> erreur

* List item
Comment faire?

Dans le script ci dessous en commentaire l'endroit ou se produit l'erreur

#ifndef HANDLE_WEB_SERVER_H
#define HANDLE_WEB_SERVER_H

#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>

/*
   MON PROBLEME EST ICI:
      si je commente ces 2 lignes alors la compilation réussit
      si non j'obtiens:
          collect2.exe: error: ld returned 1 exit status
          exit status 1
          Erreur de compilation pour la carte ESP32-WROOM-DA Module
      idem pour la carte doit esp32 devkit v1
*/

float consigneTemp = 0;
int consigne[6];

/*
   et fini là */
* /
void configServer(void);
void sendMaj(AsyncWebServerRequest *request);


#endif

puis les 3 fichiers "WifiConfig.ccp" "WifiConfig.h" et "credentials.h"

#include "WifiConfig.h"
#include "credentials.h"

void WIFISetUp(void) {
  if (WiFi.isConnected())
    WiFi.disconnect(true);  // Eventuellement, se déconnecter d'une connexion précédente à un point d'accès

  delay(1000);
  WiFi.mode(WIFI_STA);         // configuration en mode station STA
  WiFi.begin(ssid, password);  // SSID + mot de passe
  delay(100);
  // connection au réseau 15 essais
  byte count = 0;
  Serial.print("Connexion en cours...");
  while (WiFi.status() != WL_CONNECTED && count < 15) {
    count++;
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  // si connection
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("Connexion OK");

    Serial.print("Adresse IP : ");
    Serial.println(WiFi.localIP());

    Serial.print("Masque de sous-réseau :");
    Serial.println(WiFi.subnetMask());

    Serial.print("Paserelle :");
    Serial.println(WiFi.gatewayIP());

    Serial.print("Adresse MAC :");
    Serial.println(WiFi.macAddress());
  } else {
    Serial.println("Echec connexion");
    while (1)
      ;
  }

  Serial.println("Configuration WiFi terminée");
  delay(500);

  WIFIScan();
  delay(500);
}

void WIFIScan(void) {
  Serial.println();
  Serial.println("Début scan réseau WiFi...");

  int n = WiFi.scanNetworks();  // Nombre de réseaux trouvés
  delay(500);
  Serial.println("Fin du scan réseau");
  if (n == 0) {
    Serial.println("Aucun réseau trouvé");
    while (1) ;                             // on reste bloqué ici
  } else {
    Serial.print(n);
    Serial.println(" réseau(x) trouvé(s) :");
    delay(500);
    // pour tous les réseaux trouvés
    for (int i = 0; i < n; ++i) {
      // Afficher SSID et RSSI pour chaque réseau trouvé
      Serial.print(i + 1);
      Serial.print(":");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));  // Received Signal Strength, force du signal en dBm
      Serial.println(")");
      //  Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
      delay(10);
    }
    Serial.println();
  }
  delay(100);
}

et enfin

#ifndef WIFI_CONFIG_H
#define WIFI_CONFIG_H

#include <WiFi.h>

void WIFISetUp(void);
void WIFIScan(void);

#endif
[/code]
et
```[code]
#ifndef CREDENTIALS_H
#define CREDENTIALS_H

const char* ssid = "SSID à renseigner";
const char* password = "mot de passe à renseigner";

#endif

Je ne sais pas si je suis clair dans ma demande.
En gros comment récupérer ceci pour s'en servir ailleurs.
merci.

Bonsoir

fil de discussion déplacé vers la partie principale du Forum Francophone, en conformité avec les "Bonnes Pratiques du Forum Francophone"

Salut

problème de compilation: collect2.exe: error: ld

Cette erreur signale un problème au niveau édition de liens.
Il y a forcément un message qui la précède. Difficile d'aider sans ce message.

Dans l'application que j'essaie de modifier, le esp 32 reçoit un json de ce type:
{r1:0,r2:0,r3:0,r4:0,r5:0,r6:0,t:19.0}.
Celui-ci est fourni je crois par la fonction:

myServer.on("/ajax", HTTP_GET, [](AsyncWebServerRequest * request)
  {
  if (request->hasParam("json")) //
    {
      AsyncWebParameter* p = request->getParam("json");                         // le js possede ce param
      String val = p->value();                                                  //  lecture du json reçu
      //     // Stream& input;

      StaticJsonDocument<200> doc;

      DeserializationError error = deserializeJson(doc, val);                   // le json est fourni à la désériialisation
      //                                                                                 qui délivre ce qui suit
      if (error) {
        Serial.print("deserializeJson() failed: ");
        Serial.println(error.c_str());
        return;
      }
/*
le json: {r1:0,r2:0,r3:0,r4:0,r5:0,r6:0,t:19.0} devrait être décodé comme ci dessous.
Et la pas de problème.
Mais comment récupérer tout çà ailleurs?
*/
      int  r1 = doc["r1"]; // 0
      int  r2 = doc["r2"]; // 0
      int  r3 = doc["r3"]; // 0
      int  r4 = doc["r4"]; // 0
      int  r5 = doc["r5"]; // 0
      int  r6 = doc["r6"]; // 0
      float t = doc["t"]; // 19
      /*     c'est ces 7 clés /valeurs ci dessus que je voudrais récupérer.
              J'ai tenté de déclarer un tableau dans  le fichier HandleWebServer.h qui dessert 
              cette fonction et c'est cette déclaration qui pose problème.
             Je suis incapable a comprendre les subtilités du langage c d'où mon désarroi.
  */

      sendMaj(request);                                                             // renvoi d'une mise à jour
    }
    else if (request->hasParam("maj"))                                              // LE JS possède ce param
    {
      Serial.println("MAJ");
      sendMaj(request);
    }
    
  });
``

d'où le questionnement posé dans cette partie du code

#ifndef HANDLE_WEB_SERVER_H
#define HANDLE_WEB_SERVER_H

#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
/*
   MON PROBLEME EST ICI:
      si je commente ces 2 lignes ci-dessous alors la compilation réussit
      si non j'obtiens:
          collect2.exe: error: ld returned 1 exit status
          exit status 1
          Erreur de compilation pour la carte ESP32-WROOM-DA Module
      idem pour la carte doit esp32 devkit v1
*/
float consigneTemp = 0;
int consigne[6];
/*
   et fini là 
* /
void configServer(void);
void sendMaj(AsyncWebServerRequest *request);


#endif

Dans mon premier post, il y a l'intégralité du programme en 5 fichiers que j'essaie d'adapter.
Le code modèle je l'ai trouvé ici:
https://www.developpez.net/forums/d2085877/general-developpement/programmation-systeme/embarque/contribuez/esp32-creer-serveur-web/
dans ce code l'auteur utilise dans le HTML un bouton , j'ai voulu en mettre 7 d'où l'utilisation d'un json dans le javascript.
Je n'arrive pas à récupérer les valeurs des clés ailleurs que dans la fonction:
myServer.on("/ajax", HTTP_GET, [](AsyncWebServerRequest * request)
{.......
merci de s'y intéresser.

voici la totalité du message d'erreur:


c:/users/domi/appdata/local/arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/gcc8_4_0-esp-2021r2-patch3/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: sketch\gestion_web_v0_3.ino.cpp.o:C:\Users\domi\Dropbox (Compte personnel)\arduino\nrf24 boitier radiateur\gestion_web_v0_3/HandleWebServer.h:8: multiple definition of `consigne'; sketch\HandleWebServer.cpp.o:C:\Users\domi\Dropbox (Compte personnel)\arduino\nrf24 boitier radiateur\gestion_web_v0_3/HandleWebServer.h:8: first defined here
Plusieurs bibliothèque trouvées pour "WiFi.h"
Utilisé : C:\Users\domi\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.5\libraries\WiFi
Non utilisé : C:\Program Files (x86)\Arduino\libraries\WiFi
Plusieurs bibliothèque trouvées pour "AsyncTCP.h"
Utilisé : C:\Users\domi\Documents\Arduino\libraries\AsyncTCP
Non utilisé : C:\Users\domi\Documents\Arduino\libraries\AsyncTCP-master
c:/users/domi/appdata/local/arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/gcc8_4_0-esp-2021r2-patch3/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: sketch\gestion_web_v0_3.ino.cpp.o:C:\Users\domi\Dropbox (Compte personnel)\arduino\nrf24 boitier radiateur\gestion_web_v0_3/HandleWebServer.h:7: multiple definition of `consigneTemp'; sketch\HandleWebServer.cpp.o:C:\Users\domi\Dropbox (Compte personnel)\arduino\nrf24 boitier radiateur\gestion_web_v0_3/HandleWebServer.h:7: first defined here
collect2.exe: error: ld returned 1 exit status
exit status 1
Erreur de compilation pour la carte ESP32-WROOM-DA Module

Le vrai message d'erreur est celui ci :

Tu as déclaré deux variables consigneTemp.

Cette ligne fait apparemment partie d'un fichier HandleWebServer.h, donc elle sera dupliquée pour chaque fichier .cpp ou .ino dans lequel HandleWebServer.h sera inclus.

Ma suggestion :
Dans HandleWebServer.cpp :

float consigneTemp; // ici la valeur ZERO est implicite, car c'est une variable globale

Dans HandleWebServer.h :

extern float consigneTemp;

Idem pour :

int consigne[6];

merci hbachetti de t'intéresser à mon problème.

Je vais essayer ton conseil.

merci hbachetti,
je viens de compiler avec tes suggestions et ça fonctionne.
Donc le problème est la déclaration des variables.

Que veut dire extern dans les déclarations de variables et quand doit-on les utiliser?

C'est là où je me sens ridicule en programmation.
Si c'est basique je comprends si non perdu...
Merci.

extern veut dire que la variable, ou la fonction, est déclarée (instanciée) ailleurs. Le compilateur ne s'en soucie donc pas. C'est le linker qui va constater, après la compilation de tous les module, qu'elle existe vraiment, ou pas, ou qu'elle existe en plusieurs exemplaires.
Dans ton code HandleWebServer.ccp déclare la variable physique, HandleWebServer.h la décrit aux modules qui incluent HandleWebServer.h.
Il est bon par ailleurs d'inclure HandleWebServer.h dans HandleWebServer.cpp, ce que tu as fait, car ainsi le compilateur peut vérifier la concordance entre les deux déclarations.

https://koor.fr/C/Tutorial/Qualificateurs.wp