IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Arduino Discussion :

ESP32 et BLE


Sujet :

Arduino

  1. #21
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    Merci bien

    Je vais essayer de digérer tout çà et je reviens vers vous avec un nouveau croquis

    pascal

  2. #22
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    Bonsoir

    Voilà mon croquis modifié à partir de vos conseils:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
     
    // EMETTEUR uPesy ESP32 Wroom DevKit
    #include <SPI.h>
    #include <TFT_eSPI.h>
    #include <XPT2046_Touchscreen.h> // https : //github. com/PaulStoffregen/XPT2046_Touchscreen
    #include <esp_now.h>
    #include <WiFi.h>
     
    TFT_eSPI tft = TFT_eSPI();
     
    // Touchscreen pins
    #define XPT2046_IRQ 36   // T_IRQ
    #define XPT2046_MOSI 32  // T_DIN
    #define XPT2046_MISO 39  // T_OUT
    #define XPT2046_CLK 25   // T_CLK
    #define XPT2046_CS 33    // T_CS
     
    SPIClass touchscreenSPI = SPIClass(VSPI);
    XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
     
    #define SCREEN_WIDTH 320
    #define SCREEN_HEIGHT 240
    #define FONT_SIZE 2
     
    // definition des boutons
    #define BUTTON_WIDTH  80
    #define BUTTON_HEIGHT 60
    #define BUTTON_MARGIN 10
     
    // Structure pour représenter un bouton
    struct Button {
      int x, y;
      bool state;
    };
     
    Button buttons[9]; // 9 boutons
     
    // Coordonnées de l'écran tactile : (x, y) et pression (z)
    int x, y, z;
     
    // Imprimer les informations de l'écran tactile concernant X, Y et la pression (Z) sur le moniteur série
    void printTouchToSerial(int touchX, int touchY, int touchZ) {
      Serial.print("X = ");
      Serial.print(touchX);
      Serial.print(" | Y = ");
      Serial.print(touchY);
      Serial.print(" | Pressure = ");
      Serial.print(touchZ);
      Serial.println();
    }
     
    /******************************************************/
     
    //******************** Adresse Mac PC01
    //uint8_t broadcastAddress1[] = {0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54};
     
    uint8_t broadcastAddress[][6] = {
    	{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54},		// adr mac1
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac2
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   // adr mac3
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   // adr mac4
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   // adr mac5
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   // adr mac6
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   // adr mac7
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac8
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac9
     
    };
     
    // ************** la structure du récepteur
    typedef struct struct_message {
      char a[32];
      bool b;
    } struct_message;
    // Create a struct_message called myData
    struct_message myData;
    esp_now_peer_info_t peerInfo;
    // *****************************rappel lorsque les données sont envoyées
    void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
      Serial.print("\r\nÉtat de l'envoi du dernier paquet:\t");
      Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Succès" : "Échec");
    }
     
    int touchX, touchY, touchZ , i;
     
    //************************************************
    // SETUP
    //************************************************
    void setup()
    {
      Serial.begin(115200);
     
      // Set device as a Wi-Fi Station
      WiFi.mode(WIFI_STA);
      // Init ESP-NOW
      if (esp_now_init() != ESP_OK) {
        Serial.println("Erreur d'initialisation de l'ESP-NOW");
        return;
      }
      // Une fois que ESPNow est Init avec succès, nous nous inscrirons pour l'envoi du CB .
      // obtenir l'état du paquet transmis.
      esp_now_register_send_cb(OnDataSent);
      // Enregistrer un pair
      //memcpy(peerInfo.peer_addr, broadcastAddress, 6);
      memcpy(peerInfo.peer_addr, broadcastAddress, 6);
      peerInfo.channel = 0;  
      peerInfo.encrypt = false;
      // Ajouter un pair        
      if (esp_now_add_peer(&peerInfo) != ESP_OK){
        Serial.println("Échec de l'ajout d'un pair");
        return;
      }
      // Start the SPI for the touchscreen and init the touchscreen
      touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
      touchscreen.begin(touchscreenSPI);
      // Set the Touchscreen rotation in landscape mode
      // Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 3: touchscreen.setRotation(3);
      touchscreen.setRotation(3);
     
      // Start the tft display
      tft.init();
      // Set the TFT display rotation in landscape mode
      tft.setRotation(1);
     
      // Clear the screen before writing to it
      tft.fillScreen(TFT_BLACK);
     
      // Initialisation des positions des boutons
      int buttonX = 20;
      int buttonY = 20;
      for (int i = 0; i < 9; ++i) 
      {
        buttons[i].x = buttonX;
        buttons[i].y = buttonY;
        buttons[i].state = false;
     
        // Affichage des boutons
        drawButton(buttonX, buttonY, buttons[i].state);
     
        buttonX += BUTTON_WIDTH + BUTTON_MARGIN;
        if (buttonX + BUTTON_WIDTH > tft.width()) {
          buttonX = 20;
          buttonY += BUTTON_HEIGHT + BUTTON_MARGIN;
        }
      }
     
    }
     
    //************************************************
    // LOOP
    //************************************************
    void loop()
    {
     
      // Checks if Touchscreen was touched, and prints X, Y and Pressure (Z) info on the TFT display and Serial Monitor
      if (touchscreen.tirqTouched() && touchscreen.touched()) 
      {
        // Get Touchscreen points
        TS_Point p = touchscreen.getPoint();
        // Calibrate Touchscreen points with map function to the correct width and height
        touchX = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
        touchY = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
        touchZ = p.z;
     
        //printTouchToSerial(touchX, touchY, touchZ);
     
         for (int i = 0; i < 9; ++i)
         {
            if (touchX >= buttons[i].x && touchX <= buttons[i].x + BUTTON_WIDTH &&
                touchY >= buttons[i].y && touchY <= buttons[i].y + BUTTON_HEIGHT) 
            {
              // Si un bouton est touché, inverse son état et met à jour l'affichage
              buttons[i].state = !buttons[i].state;
              drawButton(buttons[i].x, buttons[i].y, buttons[i].state);
              if (buttons[i].state == 1)
              {
                Serial.print("Btn "); Serial.print(i+1); Serial.println(" enfoncé");
                snprintf(myData.a, sizeof myData.a, "PC%02d=> ON", i+1);
                myData.b =  true;
                esp_err_t result = esp_now_send(broadcastAddress[i], (uint8_t *) &myData, sizeof(myData));
                if (result == ESP_OK) {
                    Serial.print("PC0");Serial.print(i+1); Serial.println(" Envoyé avec succès");
                }else {
                    Serial.print("Erreur d'envoi des données PC0");Serial.print(i+1);
                } 
              } 
              if (buttons[i].state == 0)
              {
                Serial.print("Btn "); Serial.print(i+1); Serial.println(" relaché");
                snprintf(myData.a, sizeof myData.a, "PC%02d=> OFF", i+1);
                myData.b =  false;
                esp_err_t result = esp_now_send(broadcastAddress[i], (uint8_t *) &myData, sizeof(myData));
                if (result == ESP_OK) {
                    Serial.print("PC0");Serial.print(i+1); Serial.println(" Envoyé avec succès");
                }else {
                    Serial.print("Erreur d'envoi des données PC0");Serial.print(i+1);
                } 
              } 
            }  
        //break;
        delay(200);
        }     
      }
    }
     
    // Dessine un bouton à une position donnée avec un état donné
    void drawButton(int x, int y, bool state) {
      uint16_t color = state ? TFT_RED : TFT_GREEN;
      tft.fillRect(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, color);
      tft.drawRect(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, TFT_WHITE);
     
    }
    le logiciel a l'air de "tomber en marche"
    l'appui sur le Btn2 engendre une erreur car pour l'instant je n'ai qu'1 seul ESP01s et donc 1 seule adresse en test

    votre avis ?

    pascal
    Images attachées Images attachées  

  3. #23
    Membre expérimenté Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2011
    Messages : 640
    Points : 1 610
    Points
    1 610
    Par défaut
    Je ne m'y connais pas trop, mais les adresses pour mac2 à mac9 sont toutes {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ??? Je doute que soit une adresse mac valide. Alors, fatalement, quand tu appuies sur le bouton pour activer mac2, esp_now_send() n'est pas content.

    Mais je ne pense pas que le programme "tombe en marche". esp_now_send() a bien fonctionné (pas comme tu l'aurais souhaité, mais ça c'est autre chose), et le programme continue à attendre un appui sur un boutton.

    Comme tu n'as qu'un seul esp branché, initialise les adresses de mac2 à mac9 avec celle de mac1. Edit: si tu testes comme cela, active et désactive une mac address avant d'en tester une autre, sinon ça pourrait être le souk.
    On écrit "J'ai tort" ; "tord" est la conjugaison du verbre "tordre" à la 3ème personne de l'indicatif présent

  4. #24
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    merci edgarjacobs

    je voulais juste faire un essai avec la 1ere adresse , il est vrai que j'aurai pu copier les autres
    par contre,
    je souhaiterai savoir comment je peux mettre ces adresses de façon auto
    Je m'explique :
    actuellement le logiciel prévoit à terme 9 adresses possibles mais comment puis-je faire à l'aide d'un fichier "config.txt" par ex
    pour initialiser ces adresses au fur et à mesure du raccordements des PC
    en gros si je mets une nouvelle PC , comment puis-je faire pour la faire reconnaitre de façon automatique par le logiciel

    pascal

  5. #25
    Membre expérimenté Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2011
    Messages : 640
    Points : 1 610
    Points
    1 610
    Par défaut
    actuellement le logiciel prévoit à terme 9 adresses possibles mais comment puis-je faire à l'aide d'un fichier "config.txt" par ex
    pour initialiser ces adresses au fur et à mesure du raccordements des PC
    en gros si je mets une nouvelle PC , comment puis-je faire pour la faire reconnaitre de façon automatique par le logiciel
    Ça, sur micro-contrôleur, c'est totalement en-dehors de mes compétences. Ce que je peux te dire, c'est que ça va changer la physionnomie de ton programme, car Button buttons[9] va devenir Button *buttons, et après avoir lu (sur une carte sd je suppose) le nombre de boutons, tu vas devoir faire un malloc(). Maintenant, je réagis en tant que programmeur C sur pc, il se peut que je me plante complètement. Edit: et dites-le moi svp, je continue à apprendre. Merci.
    On écrit "J'ai tort" ; "tord" est la conjugaison du verbre "tordre" à la 3ème personne de l'indicatif présent

  6. #26
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    merci edgarjacobs

    Je crois que je vais revenir à des choses plus "modestes"
    si par ex , je mets dans un fichier "data/config.txt" ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #Adresse1; 0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54;
    #Adresse2; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF;
    ....
    #Adresse9;0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF;
    comment l'intégrer dans la structure actuelle

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    uint8_t broadcastAddress[][6] = {
    	{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54},		// adr mac1
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac2
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},               // adr mac3
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},               // adr mac4
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},               // adr mac5
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},               // adr mac6
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},               // adr mac7
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac8
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac9
     
    };
    çà semble plus facile de modifier un fichier .txt que de recompiler à chaque modification d'adresse

    pascal

  7. #27
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 787
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 787
    Points : 5 598
    Points
    5 598
    Par défaut
    si c'est sur un ESP32 vous pourriez stocker le fichier de configuration en mémoire flash en utilisant LittleFS ou SPIFF

    il faudra bien sûr dans ce cas rajouter des fonctions de lecture du fichier dans le setup pour la configuration

  8. #28
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    J'ai tenté de faire çà à partir d'un fichier "config.txt"

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    // Adresses MAC
     
    #adresse1;{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54};
    #adresse2;{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54};
    #adresse3;{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54};
    #adresse4;{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54};
    #adresse5;{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54};
    #adresse6;{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54};
    #adresse7;{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54};
    #adresse8;{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54};
    #adresse9;{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54};
    avec une structure de lecture comme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    uint8_t broadcastAddress[][1] = {
    	&adresse1,		// adr mac1
    	&adresse2,		// adr mac2
    	&adresse3,    // adr mac3
            &adresse4,    // adr mac4
            &adresse5,    // adr mac5
            &adresse6,    // adr mac6
            &adresse7,    // adr mac7
    	&adresse8, 	  // adr mac8
            &adresse9,		// adr mac9
    };
    mais le compilateur ne veut pas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Compilation error: 'adresse1' was not declared in this scope
    en outre dans la déclaration de SPIFFS.h et FS.h

    le compilateur cherche :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
     if(!SPIFFS.begin(true)){
        Serial.println("Une erreur s'est produite lors du montage de SPIFFS");
        return;
      } 
     
      File file = SPIFFS.open("/config.txt");
     
      if(!file){
        Serial.println("Échec de l'ouverture du fichier en lecture");
        return;
      }
     
      Serial.println("Contenu du fichier:");
      while(file.available()){
          Serial.write(file.read());
      }
      file.close();
    mais au final
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     error: 'file' was not declared in this scope
    Faut-il téléverser préalablement le fichier "config.txt" pour vérifier le croquis ?

  9. #29
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 787
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 787
    Points : 5 598
    Points
    5 598
    Par défaut
    pas besoin charger config.txt avant de compiler, la vérification du code se fait sur le respect du C++

    pouvez vous poster votre code complet (un petit programme) qui lirait ce fichier de config ? (qui est bien compliqué vous devriez simplement séparer les champs par un espace ou une virgule)

  10. #30
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    Voici le croquis complet :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
     
    // EMETTEUR uPesy ESP32 Wroom DevKit
    #include <SPI.h>
    #include <TFT_eSPI.h>
    #include <XPT2046_Touchscreen.h> // https : //github. com/PaulStoffregen/XPT2046_Touchscreen
    #include <esp_now.h>
    #include <WiFi.h>
    #include "FS.h"
    #include "SPIFFS.h"
     
    TFT_eSPI tft = TFT_eSPI();
     
    // Touchscreen pins
    #define XPT2046_IRQ 36   // T_IRQ
    #define XPT2046_MOSI 32  // T_DIN
    #define XPT2046_MISO 39  // T_OUT
    #define XPT2046_CLK 25   // T_CLK
    #define XPT2046_CS 33    // T_CS
     
    SPIClass touchscreenSPI = SPIClass(VSPI);
    XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
     
    #define SCREEN_WIDTH 320
    #define SCREEN_HEIGHT 240
    #define FONT_SIZE 2
     
    // definition des boutons
    #define BUTTON_WIDTH  80
    #define BUTTON_HEIGHT 60
    #define BUTTON_MARGIN 10
     
    // Structure pour représenter un bouton
    struct Button {
      int x, y;
      bool state;
    };
     
    Button buttons[9]; // 9 boutons
     
    // Coordonnées de l'écran tactile : (x, y) et pression (z)
    int x, y, z;
     
    // Imprimer les informations de l'écran tactile concernant X, Y et la pression (Z) sur le moniteur série
    void printTouchToSerial(int touchX, int touchY, int touchZ) {
      Serial.print("X = ");
      Serial.print(touchX);
      Serial.print(" | Y = ");
      Serial.print(touchY);
      Serial.print(" | Pressure = ");
      Serial.print(touchZ);
      Serial.println();
    }
     
    /******************************************************/
     
    //******************** Adresse Mac 
     
    uint8_t broadcastAddress[][1] = {
    	&adresse1,		// adr mac1
    	&adresse2,		// adr mac2
    	&adresse3,    // adr mac3
      &adresse4,    // adr mac4
      &adresse5,    // adr mac5
      &adresse6,    // adr mac6
      &adresse7,    // adr mac7
    	&adresse8, 	  // adr mac8
      &adresse9,		// adr mac9
    };
     
    // ************** la structure du récepteur
    typedef struct struct_message {
      char a[32];
      bool b;
    } struct_message;
    // Create a struct_message called myData
    struct_message myData;
    esp_now_peer_info_t peerInfo;
    // *****************************rappel lorsque les données sont envoyées
    void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
      Serial.print("\r\nÉtat de l'envoi du dernier paquet:\t");
      Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Succès" : "Échec");
    }
     
    int touchX, touchY, touchZ , i;
     
    //************************************************
    // SETUP
    //************************************************
    void setup()
    {
      Serial.begin(115200);
     
      if(!SPIFFS.begin(true)){
        Serial.println("Une erreur s'est produite lors du montage de SPIFFS");
        return;
      } 
     
      File file = SPIFFS.open("/config.txt");
     
      if(!file){
        Serial.println("Échec de l'ouverture du fichier en lecture");
        return;
      }
     
      Serial.println("Contenu du fichier:");
      while(file.available()){
          Serial.write(file.read());
      }
      file.close();
     
      // Set device as a Wi-Fi Station
      WiFi.mode(WIFI_STA);
      // Init ESP-NOW
      if (esp_now_init() != ESP_OK) {
        Serial.println("Erreur d'initialisation de l'ESP-NOW");
        return;
      }
      // Une fois que ESPNow est Init avec succès, nous nous inscrirons pour l'envoi du CB .
      // obtenir l'état du paquet transmis.
      esp_now_register_send_cb(OnDataSent);
      // Enregistrer un pair
      memcpy(peerInfo.peer_addr, broadcastAddress, 1);
      peerInfo.channel = 0;  
      peerInfo.encrypt = false;
      // Ajouter un pair        
      if (esp_now_add_peer(&peerInfo) != ESP_OK){
        Serial.println("Échec de l'ajout d'un pair");
        return;
      }
      // Start the SPI for the touchscreen and init the touchscreen
      touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
      touchscreen.begin(touchscreenSPI);
      // Set the Touchscreen rotation in landscape mode
      // Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 3: touchscreen.setRotation(3);
      touchscreen.setRotation(3);
     
      // Start the tft display
      tft.init();
      // Set the TFT display rotation in landscape mode
      tft.setRotation(1);
     
      // Clear the screen before writing to it
      tft.fillScreen(TFT_BLACK);
     
      // Initialisation des positions des boutons
      int buttonX = 20;
      int buttonY = 20;
      for (int i = 0; i < 9; ++i) 
      {
        buttons[i].x = buttonX;
        buttons[i].y = buttonY;
        buttons[i].state = false;
     
        // Affichage des boutons
        drawButton(buttonX, buttonY, buttons[i].state);
     
        buttonX += BUTTON_WIDTH + BUTTON_MARGIN;
        if (buttonX + BUTTON_WIDTH > tft.width()) {
          buttonX = 20;
          buttonY += BUTTON_HEIGHT + BUTTON_MARGIN;
        }
      }
     
    }
     
    //************************************************
    // LOOP
    //************************************************
    void loop()
    {
     
      // Checks if Touchscreen was touched, and prints X, Y and Pressure (Z) info on the TFT display and Serial Monitor
      if (touchscreen.tirqTouched() && touchscreen.touched()) 
      {
        // Get Touchscreen points
        TS_Point p = touchscreen.getPoint();
        // Calibrate Touchscreen points with map function to the correct width and height
        touchX = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
        touchY = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
        touchZ = p.z;
     
        //printTouchToSerial(touchX, touchY, touchZ);
     
         for (int i = 0; i < 9; ++i)
         {
            if (touchX >= buttons[i].x && touchX <= buttons[i].x + BUTTON_WIDTH &&
                touchY >= buttons[i].y && touchY <= buttons[i].y + BUTTON_HEIGHT) 
            {
              // Si un bouton est touché, inverse son état et met à jour l'affichage
              buttons[i].state = !buttons[i].state;
              drawButton(buttons[i].x, buttons[i].y, buttons[i].state);
              if (buttons[i].state == 1)
              {
                Serial.print("Btn "); Serial.print(i+1); Serial.println(" enfoncé");
                snprintf(myData.a, sizeof myData.a, "PC%02d=> ON", i+1);
                myData.b =  true;
                esp_err_t result = esp_now_send(broadcastAddress[i], (uint8_t *) &myData, sizeof(myData));
                if (result == ESP_OK) {
                    Serial.print("PC0");Serial.print(i+1); Serial.println(" Envoyé avec succès");
                }else {
                    Serial.print("Erreur d'envoi des données PC0");Serial.print(i+1);
                } 
              } 
              if (buttons[i].state == 0)
              {
                Serial.print("Btn "); Serial.print(i+1); Serial.println(" relaché");
                snprintf(myData.a, sizeof myData.a, "PC%02d=> OFF", i+1);
                myData.b =  false;
                esp_err_t result = esp_now_send(broadcastAddress[i], (uint8_t *) &myData, sizeof(myData));
                if (result == ESP_OK) {
                    Serial.print("PC0");Serial.print(i+1); Serial.println(" Envoyé avec succès");
                }else {
                    Serial.print("Erreur d'envoi des données PC0");Serial.print(i+1);
                } 
              } 
            }  
        //break;
        delay(200);
        }     
      }
    }
     
    // Dessine un bouton à une position donnée avec un état donné
    void drawButton(int x, int y, bool state) {
      uint16_t color = state ? TFT_RED : TFT_GREEN;
      tft.fillRect(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, color);
      tft.drawRect(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, TFT_WHITE);
     
    }

  11. #31
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 787
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 787
    Points : 5 598
    Points
    5 598
    Par défaut
    quand on explore de nouvelles choses, le mieux c'est de faire d'abord un code dédié et quand ça fonctionne et qu'on a bien compris, on l'intègre dans le reste du code.

    Simplifions le fichier config.txt avec ce format

    NOM suivi de 6 octets en notation hexa

    disons que vos PC ont des noms de capitales et que le fichier est stocké en UTF8 (format que sait afficher un Arduino dans la console série)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    Paris 0xb4 0xe6 0x2d 0x78 0x83 0x54
    Londres 0xb5 0xe6 0x2d 0x78 0x83 0x54
    Berlin 0xb6 0xe6 0x2d 0x78 0x83 0x54
    Rome 0xb7 0xe6 0x2d 0x78 0x83 0x54
    Madrid 0xb8 0xe6 0x2d 0x78 0x83 0x54
    Oslo 0xb9 0xe6 0x2d 0x78 0x83 0x54
    Varsovie 0xba 0xe6 0x2d 0x78 0x83 0x54
    Prague 0xbb 0xe6 0x2d 0x78 0x83 0x54
    Amsterdam 0xbc 0xe6 0x2d 0x78 0x83 0x54
    pour stocker cela en mémoire on aura besoin d'une structure qui contiendra le nom

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    const byte tailleMaxNom = 16; // y compris le caractère null final
    struct ClientPC {
      char nom[tailleMaxNom];
      byte  adresse[6];
    };
    et on aura besoin d'un tableau pour ranger ce que l'on lit avec un nombre maximum prédéterminé maximum de clients (c'est plus simple que de construire dynamiquement le tableau avec de l'allocation mémoire) et une autre variable qui dira combien réellement on en a utilisé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    const size_t nbMaxClients = 20;     // nombre maximum de clients
    ClientPC clients[nbMaxClients]; 
    size_t nbClientsPC = 0;            // le nombre de clients lu dans le fichier de config
    ici j'alloue un tableau de 20 clients max et je dis que pour le moment ce tableau contient 0 entrées valides (on n'a pas encore lu le fichier de config).

    Pour lire le fichier de configuration, le plus simple c'est de lire une ligne dans un buffer mémoire, ça se fait avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fichierConfig.readBytesUntil('\n', ...
    et pour faire l'analyse on peut utiliser sscanf() qui permet d'extraire des valeurs d'une chaîne de caractère avec %s et un octet en hexadécimal avec %hhx.

    Il y aura une petite subtilité pour lire le nom du client PC pour ne pas déborder la mémoire allouée (tailleMaxNom - 1 pour conserver 1 caractère pour le nul final) on n'utilisera pas directement %s qui est le format standard pour lire une chaîne de caractères jusqu'au prochain espace mais il faut utiliser %15s pour dire qu'on a au max 15 caractères.

    Le souci est que ce 15 qui correspond à (tailleMaxNom - 1) est variable et on ne peut pas coller 15 en dur dans le sscanf() donc on fabrique la chaîne qui définit le format grace à snprintf() ce qui nous permet d'injecter le 15 au bon endroit

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      char format[64]; // pour bâtir dynamiquement le format en tenant compte de la longueur max du nom (reco de Kernighan et Pike dans "The Practice of Programming")
      snprintf(format, sizeof format, "%%%ds %%hhx %%hhx %%hhx %%hhx %%hhx %%hhx", tailleMaxNom - 1); // lire une chaine de taille max tailleMaxNom - 1 suivie de 6 octets en hexa
    (on met le % en double pour insérer un caractère % dans la chaîne, car % est un caractère réservé dans le format)

    voilà donc une fois cela bien compris et défini, il ne reste qu'à écrire un petit bout de code qui lit une ligne, fait le scanf() et rempli la structure puis passe à l'index suivant


    voici à quoi ça pourrait ressembler
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
     
    #include <SPIFFS.h>
     
    const size_t tailleMaxNom = 16;     // y compris le caractère nul final
     
    struct ClientPC {
      char nom[tailleMaxNom];           // le nom du client PC
      uint8_t adresse[6];               // les 6 octets de son adresse
    };
     
    const size_t nbMaxClients = 20;     // nombre maximum de clients
    ClientPC clients[nbMaxClients]; 
    size_t nbClientsPC = 0;            // le nombre de clients lu dans le fichier de config
     
     
    void setup() {
      Serial.begin(115200);
     
      if (!SPIFFS.begin(true)) {
        Serial.println("Une erreur s'est produite lors du montage de SPIFFS...");
        return;
      }
     
      File fichierConfig = SPIFFS.open("/config.txt", FILE_READ);
      if (!fichierConfig) {
        Serial.println("Impossible d'ouvrir le fichier en lecture");
        return;
      }
     
     
      char bufferLigne[100]; // Buffer pour stocker chaque ligne
      char format[64]; // pour bâtir dynamiquement le format en tenant compte de la longueur max du nom (reco de Kernighan et Pike dans "The Practice of Programming")
      snprintf(format, sizeof format, "%%%ds %%hhx %%hhx %%hhx %%hhx %%hhx %%hhx", tailleMaxNom - 1); // lire une chaine de taille max tailleMaxNom - 1 suivie de 6 octets en hexa
     
      nbClientsPC = 0;
      while (fichierConfig.available() && nbClientsPC < nbMaxClients) {   // tant qu'on peut lire quelque chose et qu'on a de la place pour stocker
        memset(bufferLigne, '\0', sizeof bufferLigne); // Effacer le buffer
        fichierConfig.readBytesUntil('\n', bufferLigne, sizeof bufferLigne); // Lire la ligne dans le buffer
        memset(clients[nbClientsPC].nom, '\0', sizeof clients[nbClientsPC].nom); // on efface le nom pour être tranquille
        int nbChampsLus = sscanf(bufferLigne, format,
                                 clients[nbClientsPC].nom,
                                 &clients[nbClientsPC].adresse[0], &clients[nbClientsPC].adresse[1],
                                 &clients[nbClientsPC].adresse[2], &clients[nbClientsPC].adresse[3],
                                 &clients[nbClientsPC].adresse[4], &clients[nbClientsPC].adresse[5]);
     
        if (nbChampsLus == 7) {
          // la lecture des 7 champs (le nom et 8 octets sous forme hexadécimale) s'est bien passée
          nbClientsPC++;
        } else {
          // on arrête de lire là
          break;
        }
      }
      fichierConfig.close();
     
      // Affichage des adresses lues depuis le fichier
      for (size_t i = 0; i < nbClientsPC; i++) {
        Serial.printf("%3zu %-*s : ", i+1, tailleMaxNom - 1, clients[i].nom); // l'index sur 3 caractères, le nom sur tailleMaxNom - 1 cadrée à gauche
        for (int j = 0; j < 6; j++) Serial.printf("0x%02X ", clients[i].adresse[j]);
        Serial.println();
      }
    }
     
    void loop() {}
    vous copiez cela dans un sketch que vous sauvez
    dans le répertoire du sketch vous créez un répertoire data (écrit comme cela exactement)
    dans ce répertoire vous mettez le fichier de config
    config.txt

    vous configurez l'IDE pour votre ESP32 et utilisez l'utilitaire qui charge ce qu'il y a dans data dans la partition SPIFF de votre carte

    vous compilez et chargez le code et ouvrez la console série à 115200 bauds

    si tout s'est bien passé vous verrez s'afficher

    1 Paris : 0xB4 0xE6 0x2D 0x78 0x83 0x54
    2 Londres : 0xB5 0xE6 0x2D 0x78 0x83 0x54
    3 Berlin : 0xB6 0xE6 0x2D 0x78 0x83 0x54
    4 Rome : 0xB7 0xE6 0x2D 0x78 0x83 0x54
    5 Madrid : 0xB8 0xE6 0x2D 0x78 0x83 0x54
    6 Oslo : 0xB9 0xE6 0x2D 0x78 0x83 0x54
    7 Varsovie : 0xBA 0xE6 0x2D 0x78 0x83 0x54
    8 Prague : 0xBB 0xE6 0x2D 0x78 0x83 0x54
    9 Amsterdam : 0xBC 0xE6 0x2D 0x78 0x83 0x54


    ce qui veut dire qu'on bien lu le fichier et rempli les 9 premières structures avec les infos du fichier de configuration.


    voilà - une fois que vous comprenez bien tout ce que fait le code, vous pourrez l'intégrer dans votre projet.

  12. #32
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    Bonjour Jay M

    quand on explore de nouvelles choses, le mieux c'est de faire d'abord un code dédié et quand ça fonctionne et qu'on a bien compris, on l'intègre dans le reste du code.
    c'est noté , j'avais pris pourtant un tuto mais j'ai du oublier quelque chose

    j'ai quelques questions si vous le permettez :

    1) le fichier "fichierConfig" est dimensionné nul part semble-t--il et pourtant il ne plante pas le compilateur , dans mon exemple précédent, j'avais un fichier nommé 'file' et
    çà bloquait le compilateur par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    error: 'file' was not declared in this scope
    pourquoi ?

    2) vous considérez la bonne réception du fichier à la 7ieme ligne
    pourquoi pas dès la seconde par ex ?

    3)J'ai eu des difficultés sur Arduino IDE 2.3.2 car je me suis aperçu ( un peu tardivement) que ne n'avait pas la possibilité d'uploader des fichiers SPIFFS à partir du menu
    alors que je l'ai bien avec la version Arduino 1.8.19 ?

    4)( voir image) J'ai fait une modification dans le fichier config.txt à la 5ieme ligne pour voir si le prog prenait les changements et visiblement çà fonctionne par contre
    j'ai une erreur 112 dont je ne connais pas la signification

    je vais maintenant essayer d’intégrer tout ceci dans mon prg

    merci encore
    pascal
    Images attachées Images attachées  

  13. #33
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    j'ai intégré la modification du fichier "config.txt"
    dans le croquis final
    mais naturellement la compilation ne se passe pas comme prévue

    initialement j'avais çà pour transmettre les adresses MAC
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    /******************************************************/
    /*
    uint8_t broadcastAddress[][6] = {
    	{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54},		// adr mac1
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac2
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},              // adr mac3
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},              // adr mac4
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},              // adr mac5
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},              // adr mac6
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},              // adr mac7
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac8
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac9
    };
    */
    maintenant j'ai çà :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    struct ClientPC {
      char nom[tailleMaxNom];           // le nom du client PC
      uint8_t adresse[6];               // les 6 octets de son adresse
    };
    j'ai remplacé le tableau broadcastAddress[][] par clients[].adresse[] mais sans succès...
    j'ai une erreur :


    D:\5- ELECTRONIQUE\SonOFF\Emetteur\E_V20240515a\E_V20240515a.ino: In function 'void loop()':
    E_V20240515a:236:154: error: invalid conversion from 'int' to 'const uint8_t*' {aka 'const unsigned char*'} [-fpermissive]
    esp_err_t result = esp_now_send(clients[i].adresse[0]&clients[i].adresse[1]&clients[i].adresse[2]&clients[i].adresse[3]&clients[i].adresse[4]&clients[i].adresse[5], (uint8_t *) &myData, sizeof(myData));
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~

    E_V20240515a:248:154: error: invalid conversion from 'int' to 'const uint8_t*' {aka 'const unsigned char*'} [-fpermissive]
    esp_err_t result = esp_now_send(clients[i].adresse[0]&clients[i].adresse[1]&clients[i].adresse[2]&clients[i].adresse[3]&clients[i].adresse[4]&clients[i].adresse[5], (uint8_t *) &myData, sizeof(myData));
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
    In file included from D:\5- ELECTRONIQUE\SonOFF\Emetteur\E_V20240515a\E_V20240515a.ino:7:
    C:\Users\Utilisateur\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.15/tools/sdk/esp32/include/esp_wifi/include/esp_now.h:189:39: note: initializing argument 1 of 'esp_err_t esp_now_send(const uint8_t*, const uint8_t*, size_t)'
    esp_err_t esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len);
    ~~~~~~~~~~~~~~~^~~~~~~~~

    exit status 1
    invalid conversion from 'int' to 'const uint8_t*' {aka 'const unsigned char*'} [-fpermissive]
    ci-dessous le code actuel

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
     
    // EMETTEUR uPesy ESP32 Wroom DevKit //
     
    #include <SPIFFS.h>
    #include <SPI.h>
    #include <TFT_eSPI.h>
    #include <XPT2046_Touchscreen.h> // https : //github. com/PaulStoffregen/XPT2046_Touchscreen
    #include <esp_now.h>
    #include <WiFi.h>
     
     
    TFT_eSPI tft = TFT_eSPI();
     
    // Touchscreen pins
    #define XPT2046_IRQ 36   // T_IRQ
    #define XPT2046_MOSI 32  // T_DIN
    #define XPT2046_MISO 39  // T_OUT
    #define XPT2046_CLK 25   // T_CLK
    #define XPT2046_CS 33    // T_CS
     
    SPIClass touchscreenSPI = SPIClass(VSPI);
    XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
     
    #define SCREEN_WIDTH 320
    #define SCREEN_HEIGHT 240
    #define FONT_SIZE 2
     
    // definition des boutons
    #define BUTTON_WIDTH  80
    #define BUTTON_HEIGHT 60
    #define BUTTON_MARGIN 10
     
    // Structure pour représenter un bouton
    struct Button {
      int x, y;
      bool state;
    };
     
    Button buttons[9]; // 9 boutons
     
    // Coordonnées de l'écran tactile : (x, y) et pression (z)
    int x, y, z;
     
    // Imprimer les informations de l'écran tactile concernant X, Y et la pression (Z) sur le moniteur série
    void printTouchToSerial(int touchX, int touchY, int touchZ) {
      Serial.print("X = ");
      Serial.print(touchX);
      Serial.print(" | Y = ");
      Serial.print(touchY);
      Serial.print(" | Pressure = ");
      Serial.print(touchZ);
      Serial.println();
    }
     
    /******************************************************/
    /*
    uint8_t broadcastAddress[][6] = {
    	{0xb4, 0xe6, 0x2d, 0x78, 0x83, 0x54},		// adr mac1
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac2
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   // adr mac3
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   // adr mac4
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   // adr mac5
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   // adr mac6
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   // adr mac7
    	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac8
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},		// adr mac9
     
    };
    */
     
    // *** Structure Fichier Config.txt ********************************
    const size_t tailleMaxNom = 16;     // y compris le caractère nul final
    struct ClientPC {
      char nom[tailleMaxNom];           // le nom du client PC
      uint8_t adresse[6];               // les 6 octets de son adresse
    };
     
    const size_t nbMaxClients = 20;     // nombre maximum de clients
    ClientPC clients[nbMaxClients]; 
    size_t nbClientsPC = 0;            // le nombre de clients lu dans le fichier de config
     
    //******************************************************************* 
     
    // ***** Structure du récepteur *************************************
    typedef struct struct_message {
      char a[32]; // ex PC1 
      bool b;     // Etat = 0 ou 1
    } struct_message;
    // Créer une struct_message appelée myData
    struct_message myData;
    esp_now_peer_info_t peerInfo;
    void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
      Serial.print("\r\nÉtat de l'envoi du dernier paquet:\t");
      Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Succès" : "Échec");
    }
    //*********************************************************************
     
    int touchX, touchY, touchZ , i;
     
     
    //************************************************
    // SETUP
    //************************************************
    void setup()
    {
      Serial.begin(115200);
     
      if(!SPIFFS.begin(true)){
        Serial.println("Une erreur s'est produite lors du montage de SPIFFS");
        return;
      } 
     
      File fichierConfig = SPIFFS.open("/config.txt", FILE_READ);
      if (!fichierConfig) {
        Serial.println("Impossible d'ouvrir le fichier en lecture");
        return;
      }
     
      char bufferLigne[100]; // Buffer pour stocker chaque ligne
      char format[64]; // pour bâtir dynamiquement le format en tenant compte de la longueur max du nom (reco de Kernighan et Pike dans "The Practice of Programming")
      snprintf(format, sizeof format, "%%%ds %%hhx %%hhx %%hhx %%hhx %%hhx %%hhx", tailleMaxNom - 1); // lire une chaine de taille max tailleMaxNom - 1 suivie de 6 octets en hexa
     
      nbClientsPC = 0;
      while (fichierConfig.available() && nbClientsPC < nbMaxClients) {   // tant qu'on peut lire quelque chose et qu'on a de la place pour stocker
        memset(bufferLigne, '\0', sizeof bufferLigne); // Effacer le buffer
        fichierConfig.readBytesUntil('\n', bufferLigne, sizeof bufferLigne); // Lire la ligne dans le buffer
        memset(clients[nbClientsPC].nom, '\0', sizeof clients[nbClientsPC].nom); // on efface le nom pour être tranquille
        int nbChampsLus = sscanf(bufferLigne, format,
                                 clients[nbClientsPC].nom,
                                 &clients[nbClientsPC].adresse[0], &clients[nbClientsPC].adresse[1],
                                 &clients[nbClientsPC].adresse[2], &clients[nbClientsPC].adresse[3],
                                 &clients[nbClientsPC].adresse[4], &clients[nbClientsPC].adresse[5]);
     
        if (nbChampsLus == 7) {
          // la lecture des 7 champs (le nom et 8 octets sous forme hexadécimale) s'est bien passée
          nbClientsPC++;
        } else {
          // on arrête de lire là
          break;
        }
      }
      fichierConfig.close();
     
     
       // Affichage des adresses lues depuis le fichier
      for (size_t i = 0; i < nbClientsPC; i++) {
        Serial.printf("%3zu %-*s : ", i+1, tailleMaxNom - 1, clients[i].nom); // l'index sur 3 caractères, le nom sur tailleMaxNom - 1 cadrée à gauche
        for (int j = 0; j < 6; j++) Serial.printf("0x%02X ", clients[i].adresse[j]);
        Serial.println();
      }
     
      // Set device as a Wi-Fi Station
      WiFi.mode(WIFI_STA);
      // Init ESP-NOW
      if (esp_now_init() != ESP_OK) {
        Serial.println("Erreur d'initialisation de l'ESP-NOW");
        return;
      }
      // Une fois que ESPNow est Init avec succès, nous nous inscrirons pour l'envoi du CB .
      // obtenir l'état du paquet transmis.
      esp_now_register_send_cb(OnDataSent);
      // Enregistrer un pair
      //memcpy(peerInfo.peer_addr, broadcastAddress, 1);
      memcpy(peerInfo.peer_addr, clients , 6);
      peerInfo.channel = 0;  
      peerInfo.encrypt = false;
      // Ajouter un pair        
      if (esp_now_add_peer(&peerInfo) != ESP_OK){
        Serial.println("Échec de l'ajout d'un pair");
        return;
      }
      // Start the SPI for the touchscreen and init the touchscreen
      touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
      touchscreen.begin(touchscreenSPI);
      // Set the Touchscreen rotation in landscape mode
      // Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 3: touchscreen.setRotation(3);
      touchscreen.setRotation(3);
     
      // Start the tft display
      tft.init();
      // Set the TFT display rotation in landscape mode
      tft.setRotation(1);
     
      // Clear the screen before writing to it
      tft.fillScreen(TFT_BLACK);
     
      // Initialisation des positions des boutons
      int buttonX = 20;
      int buttonY = 20;
      for (int i = 0; i < 9; ++i) 
      {
        buttons[i].x = buttonX;
        buttons[i].y = buttonY;
        buttons[i].state = false;
     
        // Affichage des boutons
        drawButton(buttonX, buttonY, buttons[i].state);
     
        buttonX += BUTTON_WIDTH + BUTTON_MARGIN;
        if (buttonX + BUTTON_WIDTH > tft.width()) {
          buttonX = 20;
          buttonY += BUTTON_HEIGHT + BUTTON_MARGIN;
        }
      }
     
    }
     
    //************************************************
    // LOOP
    //************************************************
    void loop()
    {
     
      // Checks if Touchscreen was touched, and prints X, Y and Pressure (Z) info on the TFT display and Serial Monitor
      if (touchscreen.tirqTouched() && touchscreen.touched()) 
      {
        // Get Touchscreen points
        TS_Point p = touchscreen.getPoint();
        // Calibrate Touchscreen points with map function to the correct width and height
        touchX = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
        touchY = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
        touchZ = p.z;
     
        //printTouchToSerial(touchX, touchY, touchZ);
     
         for (int i = 0; i < 9; ++i)
         {
            if (touchX >= buttons[i].x && touchX <= buttons[i].x + BUTTON_WIDTH &&
                touchY >= buttons[i].y && touchY <= buttons[i].y + BUTTON_HEIGHT) 
            {
              // Si un bouton est touché, inverse son état et met à jour l'affichage
              buttons[i].state = !buttons[i].state;
              drawButton(buttons[i].x, buttons[i].y, buttons[i].state);
              if (buttons[i].state == 1){
                Serial.print("Btn "); Serial.print(i+1); Serial.println(" enfoncé");
                snprintf(myData.a, sizeof myData.a, "PC%02d=> ON", i+1);
                myData.b =  true;
                esp_err_t result = esp_now_send(clients[i].adresse[0]&clients[i].adresse[1]&clients[i].adresse[2]&clients[i].adresse[3]&clients[i].adresse[4]&clients[i].adresse[5], (uint8_t *) &myData, sizeof(myData)); //<=======================
                if (result == ESP_OK) {
                    Serial.print("PC");Serial.print(i+1); Serial.println(" Envoyé avec succès");
                }else {
                    Serial.print("Erreur d'envoi des données PC");Serial.print(i+1);
                } 
              } 
              if (buttons[i].state == 0)
              {
                Serial.print("Btn "); Serial.print(i+1); Serial.println(" relaché");
                snprintf(myData.a, sizeof myData.a, "PC%02d=> OFF", i+1);
                myData.b =  false;
                esp_err_t result = esp_now_send(clients[i].adresse[0]&clients[i].adresse[1]&clients[i].adresse[2]&clients[i].adresse[3]&clients[i].adresse[4]&clients[i].adresse[5], (uint8_t *) &myData, sizeof(myData));  // <=======================
                if (result == ESP_OK) {
                    Serial.print("PC");Serial.print(i+1); Serial.println(" Envoyé avec succès");
                }else {
                    Serial.print("Erreur d'envoi des données PC");Serial.print(i+1);
                } 
              } 
            }  
        //break;
        delay(200);
        }     
      }
    }
     
    // Dessine un bouton à une position donnée avec un état donné
    void drawButton(int x, int y, bool state) {
      uint16_t color = state ? TFT_RED : TFT_GREEN;
      tft.fillRect(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, color);
      tft.drawRect(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, TFT_WHITE);
     
    }

  14. #34
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 787
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 787
    Points : 5 598
    Points
    5 598
    Par défaut
    Je reviens vers vous dans l’après midi pour vos questions - suis en déplacement

  15. #35
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 787
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 787
    Points : 5 598
    Points
    5 598
    Par défaut
    Citation Envoyé par cobra38 Voir le message
    1) le fichier "fichierConfig" est dimensionné nul part semble-t--il et pourtant il ne plante pas le compilateur , dans mon exemple précédent, j'avais un fichier nommé 'file' et çà bloquait le compilateur par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    error: 'file' was not declared in this scope
    pourquoi ?
    le compilateur n'a pas besoin de connaître la taille du fichier, juste qu'il faut une variable qui va permettre d'accéder au fichier. Dans votre code précédent, je pense qu'il y avait des erreurs dans la compilation avant ce message sur file, il faut toujours traiter les premières erreurs en premier car souvent le compilateur ensuite est "perdu" et les erreurs peuvent ne pas être pertinentes.

    Citation Envoyé par cobra38 Voir le message
    2) vous considérez la bonne réception du fichier à la 7ieme ligne
    pourquoi pas dès la seconde par ex ?
    je suppose que vous faites référence à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        if (nbChampsLus == 7) {
    ce n'est pas le nombre de lignes, mais le nombre d'éléments décodés dans une ligne car on attend 7 valeurs - le nom et les 6 octets. si la fonction sscanf() n'a pas réussi à lires ces 7 champs ça veut dire que la ligne ne contenait pas quelque chose de compatible et j'arrête donc là la lecture.

    Citation Envoyé par cobra38 Voir le message
    3)J'ai eu des difficultés sur Arduino IDE 2.3.2 car je me suis aperçu ( un peu tardivement) que ne n'avait pas la possibilité d'uploader des fichiers SPIFFS à partir du menu
    alors que je l'ai bien avec la version Arduino 1.8.19 ?
    C'est maintenant dispo pour l'IDE 2.x en littlefs cf https://github.com/earlephilhower/ar...ases/tag/1.1.6

    Citation Envoyé par cobra38 Voir le message
    4)( voir image) J'ai fait une modification dans le fichier config.txt à la 5ieme ligne pour voir si le prog prenait les changements et visiblement çà fonctionne par contre
    j'ai une erreur 112 dont je ne connais pas la signification
    Je ne sais pas ce qu'est cette erreur. il semble que ce soit lié à la flash, peut-être un secteur qui n'est pas bon...

    -----

    Pour l'autre question
    j'ai remplacé le tableau broadcastAddress[][] par clients[].adresse[] mais sans succès...
    pour copier les 6 bits d'adresse du client à l'index i dans peerInfo.peer_addr il faut faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    memcpy(peerInfo.peer_addr, clients[i].adresse , 6);

  16. #36
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    pour progresser
    j'ai "essayé" de procéder à quelques modifications

    j'ai rajouté une variable :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    char broadcastAddress[18];
    ensuite j'ai modifié le croquis comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    if (buttons[i].state == 1){
                Serial.print("Btn "); Serial.print(i+1); Serial.println(" enfoncé");
                snprintf(myData.a, sizeof myData.a, "PC%02d=> ON", i+1);
                myData.b =  true;
                snprintf(broadcastAddress, sizeof(broadcastAddress), "%02x:%02x:%02x:%02x:%02x:%02x",
                         clients[i].adresse[0],clients[i].adresse[1],clients[i].adresse[2],clients[i].adresse[3],clients[i].adresse[4],clients[i].adresse[5]);
                Serial.print("Packet envoyé à: "); Serial.println(broadcastAddress);         
                esp_err_t result = esp_now_send( broadcastAddress,(uint8_t *)&myData, sizeof(myData));  // <========= Erreur ici 
                if (result == ESP_OK) {
                    Serial.print("PC");Serial.print(i+1); Serial.println(" Envoyé avec succès");
                }else {
                    Serial.print("Erreur d'envoi des données PC");Serial.print(i+1);
                }
    pour mémoire le fichierConfig est :
    Adresse1 0xb4 0xe6 0x2d 0x78 0x83 0x54
    Adresse2 0xb5 0xe6 0x2d 0x78 0x83 0x54
    Adresse3 0xb6 0xe6 0x2d 0x78 0x83 0x54
    Adresse4 0xb7 0xe6 0x2d 0x78 0x83 0x54
    Adresse5 0xb8 0xe6 0x2d 0x78 0x83 0x54
    Adresse6 0xb9 0xe6 0x2d 0x78 0x83 0x54
    Adresse7 0xba 0xe6 0x2d 0x78 0x83 0x54
    Adresse8 0xbb 0xe6 0x2d 0x78 0x83 0x54
    Adresse9 0xbc 0xe6 0x2d 0x78 0x83 0x54

    Je me heurte toujours à la même erreur

    invalid conversion from 'char*' to 'uint8_t' {aka 'unsigned char'} [-fpermissive]
    Si je définie :
    => char broadcastAddress[18]; => j'arrive à lire la ligne : snprintf(broadcastAddress,.....)
    mais je ne peux pas envoyer les données
    => uint8_t broadcastAddress[18] => je ne peux plus lire la ligne snprintf(broadcastAddress,.....)

  17. #37
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    merci mille fois Jay M

    mais je patauge sérieux
    j'ai toujours la même erreur à la compilation

    invalid conversion from 'uint8_t*' {aka 'unsigned char*'} to 'size_t' {aka 'unsigned int'} [-fpermissive]
    J'ai modifié comme suit la ligne pour prendre en compte les x adresses MAC fonction du nb de Clients éxistants:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
     for (size_t i = 0; i < nbClientsPC; i++) {
       memcpy(peerInfo.peer_addr, clients[i].adresse , 6);
      }
    ensuite dans la partie "envoi" après chaque appui du bouton, j'ai mis ceci pour tenir compte des 6 adresses MAC:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     esp_err_t result = esp_now_send(&clients[i].adresse[0],&clients[i].adresse[1],&clients[i].adresse[2],&clients[i].adresse[3],&clients[i].adresse[4],&clients[i].adresse[5],(uint8_t *)&myData, sizeof(myData));
    mais j'ai toujours le même problème à la compilation
    mais par contre si je mets ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    esp_err_t result = esp_now_send( clients[i].adresse,(uint8_t *)&myData, sizeof(myData));
    Le croquis se compile mais :
    Btn 1 enfoncé
    15:47:26.474 -> Erreur d'envoi des données PC1

  18. #38
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 787
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 787
    Points : 5 598
    Points
    5 598
    Par défaut
    la fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    esp_now_send( broadcastAddress,(uint8_t *)&myData, sizeof(myData));  // <========= Erreur ici
    attend un pointeur sur un tableau de 6 octets comme premier paramètre, pas une chaîne de texte

    pourquoi voulez vous construire cela avec un snprintf() ?

    la tableau clients contient tout ce qu'il faut déjà

    si vous voulez envoyer à l'adresse du client i il faut donner le tableau des 6 octets comme premier paramètre.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    esp_now_send(clients[i].adresse, (uint8_t *)&myData, sizeof myData);

  19. #39
    Membre actif
    Inscrit en
    Juillet 2004
    Messages
    801
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 801
    Points : 231
    Points
    231
    Par défaut
    pourquoi voulez vous construire cela avec un snprintf() ?
    Je pensais qu'il fallait préalablement formater la chaine "adresse" avant de l'envoyer
    car le formatage avec "0x" intervient à l'impression par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    .... Serial.printf("0x%02X ", clients[i].adresse[j]);
    or la chaîne "adresse" ne doit-elle pas obligatoirement posséder "Ox" devant chaque adresse au moment de l'envoi ?
    ce serait peut-être la cause de mon problème ?

  20. #40
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 787
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 787
    Points : 5 598
    Points
    5 598
    Par défaut
    la doc est dispo:

    https://docs.espressif.com/projects/...uint8_t6size_t

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    esp_err_t esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len)
    Send ESPNOW data.

    Attention
    1. If peer_addr is not NULL, send data to the peer whose MAC address matches peer_addr

    Attention
    2. If peer_addr is NULL, send data to all of the peers that are added to the peer list

    Attention
    3. The maximum length of data must be less than ESP_NOW_MAX_DATA_LEN

    Attention
    4. The buffer pointed to by data argument does not need to be valid after esp_now_send returns

    Parameters
    peer_addr -- peer MAC address
    data -- data to send
    len -- length of data
    Returns
    ESP_OK : succeed
    ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
    ESP_ERR_ESPNOW_ARG : invalid argument
    ESP_ERR_ESPNOW_INTERNAL : internal error
    ESP_ERR_ESPNOW_NO_MEM : out of memory, when this happens, you can delay a while before sending the next data
    ESP_ERR_ESPNOW_NOT_FOUND : peer is not found
    ESP_ERR_ESPNOW_IF : current Wi-Fi interface doesn't match that of peer
    ESP_ERR_ESPNOW_CHAN: current Wi-Fi channel doesn't match that of peer

Discussions similaires

  1. Mesh Network via BLE
    Par matthieuboubou dans le forum Projets
    Réponses: 0
    Dernier message: 21/04/2015, 14h10
  2. [WD18] Bluetooth 4.0 et BLE
    Par ihih45 dans le forum WinDev
    Réponses: 4
    Dernier message: 21/04/2015, 13h40
  3. Reconnaissance des grains de blés coupés
    Par arken dans le forum Traitement d'images
    Réponses: 5
    Dernier message: 08/04/2011, 11h04
  4. Comment sauvegarder bles sources des procedures stockées?
    Par tonton67 dans le forum Développement
    Réponses: 2
    Dernier message: 24/02/2009, 08h53
  5. cas des grains de blé
    Par Maxence45 dans le forum Excel
    Réponses: 13
    Dernier message: 07/11/2007, 19h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo