Problème EEPROM et variable (long code)

Bonjour à tous,

Tout d'abord petite présentation du projet :
J'ai récupéré une télécommande PTZ qui à l'origine servait pour des caméras de vidéosurveillance en RS232 ou RS485.
Ayant des caméras PTZ sony et Aver de visioconférence, j'ai supprimé les circuits interne de la télécommande, soudé des fils sur les boutons de la télécommande et soudé l'ensemble sur un Arduino Mega couplé à un shield Ethernet avec pour projet de créer une télécommande VISCA Over IP.
Tout fonctionne à une exception. La récupération de l'état de l'Iris en mode manuel.

Le problème :
Sur une page web j'affiche et je peux modifier les IP et Port et le type de caméra (Sony ou Aver, mais en réalité Sony ou autre car les caméras sony ont besoin d'une commande de reset en plus que les autres).
Tout ce que j'ai tenté pour récupéré l'Iris se solde par un effacement de la mémoire EEPROM des ports ou de la mémoire du type de caméra au démarrage ou au changement de valeur sur cette page web.

Le code fourni ci-dessous fonctionne car la récupération Iris est commenté. Si on décommente, cela ne fonctionne plus. J'ai fais des tests et des tests sans en trouver la cause. La partie commentée se trouve quasi à la fin du code.

Dernière info qui a son importance, je ne suis pas un expert en code, vous risquez d'avoir mal aux yeux, j'ai réalisé ce code à l'aide de post trouvé sur le net et de chatgpt.

Egalement, j'avais réussi une première fois à ce que tout fonctionne et j'ai subit un crash de SSD qui m'a forcé à récupéré une ancienne sauvegarde qui date juste avant l'ajout de l'Iris malheureusement).

Si le code ne passe pas car trop long, je posterai le .ino

Merci à ceux qui prendront du temps pour m'aider, j'envisage par la suite de partager ce code car j'ai longuement cherché après une solution existante mais sans succès, donc si quelqu'un se sent également par la suite de contribuer au projet pour en simplifier le code et le mettre plus beau, je partagerai le code existant avec plaisir.

EDIT : suppression de l'ensemble du code, je laisse que les parties utiles à la question, celui-ci ne pouvant servir, car non fonctionnel et inutile pour le thread. Comme indiqué dans le dernier message, si quelqu'un souhaite ce code (mal structuré, non codé dans les règles de l'art et avec des redondances pour certaines choses) je partage sans problème, ou si quelqu'un veut prendre le temps de refaire quelque chose de propre.



// Commandes VISCA Iris Close ou Open ou Query
const byte CMD_IRIS_CLOSE[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x01, 0x81, 0x01, 0x04, 0x0B, 0x02, 0xFF };
const byte CMD_IRIS_OPEN[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x01, 0x81, 0x01, 0x04, 0x0B, 0x03, 0xFF };
//const byte CMD_IRIS_QUERY[] = { 0x01, 0x10, 0x00, 0x05, 0x00, 0x00, 0x02, 0x01, 0x81, 0x09, 0x04, 0x4B, 0xFF };
const byte viscaIrisFBpositionCommand[] = { 0x01, 0x10, 0x00, 0x05, 0x00, 0x00, 0x02, 0x01, 0x81, 0x09, 0x04, 0x4B, 0xFF };

//tableau pour équivalent IRIS (peut faire parti du problème)
const char* sonyIrisValues[] = {
  "F0", "F14", "F11", "F9.6", "F8", "F6.8", "F5.6", "F4.8", "F4", "F3.4",
  "F2.8", "F2.4", "F2", "F1.6"
};

const char* averIrisValues[] = {
  "F0", "F14", "F11", "F8", "F6.8", "F5.6", "F4.8", "F4", "F3.4",
  "F2.8", "F2.4", "F2", "F1.8", "F1.6"
};


// Déclare un tableau pour stocker l'état de l'iris pour chaque caméra
byte etatIrisEnCours[9];  // Supposons qu'il y a 9 caméras maximum

//fonction pour auto manu Iris
void gererCommandeIrisAuto(int etatIrisActuel) {
  if (etatIrisActuel != 0x02 && etatIrisActuel != 0x03) {
#if DEBUG
    Serial.print("État inattendu reçu dans gererCommandeIrisAuto : ");
    Serial.println(etatIrisActuel, HEX);
#endif
    return;  // Ne rien faire si l'état est invalide
  }

  // Vérifie si la caméra actuelle est de type Aver ou Sony
  bool isCameraAver = typeCamAver[choixCamActuel - 1];
#if DEBUG
  Serial.print("isCameraAver: ");
  Serial.println(isCameraAver);  // Débogage pour vérifier la valeur
#endif
  byte* commandTGIrisAuto;
  size_t commandSizeTGIrisAuto;
  byte* commandTGIrisManuel;
  size_t commandSizeTGIrisManuel;
  byte* commandFBTGIrisAuto;
  size_t commandSizeFBTGIrisAuto;

 
  if (digitalRead(irisclose) == LOW)  // Bouton irisclose pressé
  {
    // Vérifie si la caméra actuelle est de type Aver ou Sony
    sendViscaCommandIris(CMD_IRIS_CLOSE, sizeof(CMD_IRIS_CLOSE));
    udpActive3 = true;
    queryIrisState();
    lastButtonPressTime = millis();  // Enregistrer le moment du bouton pressé
    delay(200);
    Serial.print("Etat Iris en cours :");
    Serial.println(etatIrisEnCours[choixCamActuel - 1]);
  }

  if (digitalRead(irisopen) == LOW)  // Bouton irisopen pressé
  {
    sendViscaCommandIris(CMD_IRIS_OPEN, sizeof(CMD_IRIS_OPEN));
    udpActive3 = true;
    queryIrisState();
    lastButtonPressTime = millis();  // Enregistrer le moment du bouton pressé
    delay(200);
    Serial.print("Etat Iris en cours :");
    Serial.println(etatIrisEnCours[choixCamActuel - 1]);
  }
}



//fonction pour afficher l'état Iris auto manu sur LCD
void afficheEtatIrisManuAuto() {
  int index = choixCamActuel - 1;
  lcd.setCursor(8, 1);
  lcd.print("  ");
  lcd.setCursor(8, 1);
  lcd.print(etatIrisAuto[index] == 0x02 ? "IA" : "IM");
}

//fonction pour récupérer l'état de l'Iris en mode manuel (qui fonctionne jusqu'à la ligne 2246 ->    etatIrisEnCours[choixCamActuel - 1] = combinedValue; )
void queryIrisState() {
#if DEBUG
  Serial.println("Entering queryIrisState function.");
#endif
  if (choixCamActuel == -1) {
#if DEBUG
    Serial.println("No camera selected, exiting queryIrisState.");
#endif
    return;
  }
  Udp.begin(cameraPort);

#if DEBUG
  Serial.println("Sending VISCA Reset command...");
#endif
  if (cameraIP == IPAddress(0, 0, 0, 0) || cameraPort == 0) {
#if DEBUG
    Serial.println("Error: Invalid Camera IP or Port!");
#endif
    return;
  }

  // Sending reset command to camera
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaResetCommand, sizeof(viscaResetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println("Reset command sent. Sending FB Iris command...");
#endif

  // Sending FB Iris command
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaIrisFBpositionCommand, sizeof(viscaIrisFBpositionCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println("Iris Position FB command sent. Listening for responses...");
#endif

  // Listen for responses for a short period
  unsigned long startTime = millis();
  bool irisPositionAcknowledged = false;

  while (millis() - startTime < 1000) {  // Attendre jusqu'à 1 seconde
    int packetSize = Udp.parsePacket();
    if (packetSize) {
      byte incomingPacket[UDP_TX_PACKET_MAX_SIZE];
      Udp.read(incomingPacket, packetSize);

// Affiche la taille du paquet
#if DEBUG
      Serial.print("Packet size received: ");
      Serial.println(packetSize);
#endif

// Afficher le contenu du paquet
#if DEBUG
      Serial.print("Response from camera (in hex): ");
      for (int i = 0; i < packetSize; i++) {
        if (incomingPacket[i] < 0x10) Serial.print("0");
        Serial.print(incomingPacket[i], HEX);
        Serial.print(" ");
      }
      Serial.println();
#endif

      // Condition ajustée pour la taille 15 et extraction des octets 13 et 14
      if (packetSize == 15 && incomingPacket[8] == 0x90 && incomingPacket[9] == 0x50) {
        irisHigh = incomingPacket[12];
        irisLow = incomingPacket[13];
        irisPositionAcknowledged = true;
        break;  // Arrêter après la première correspondance
      }
    }
  }

// Afficher les valeurs extraites même si elles ne sont pas confirmées
#if DEBUG
  Serial.print("Iris High = ");
  if (irisHigh < 0x10) Serial.print("0");
  Serial.print(irisHigh, HEX);
  Serial.print(", Iris Low = ");
  if (irisLow < 0x10) Serial.print("0");
  Serial.println(irisLow, HEX);
#endif

  if (irisPositionAcknowledged) {
    //premier test non concluant
    /*    bool autrechose = (typeCamAver[choixCamActuel - 1]);
    Serial.println(typeCamAver[choixCamActuel -1]);
    Serial.println(autrechose);
    if (!autrechose)
    {
      Serial.print("nous avons autre chose égale à :");
      Serial.println(autrechose);
    }
    else {
      Serial.print("nous avons autre chose égale à :");
      Serial.println(autrechose);
    }*/
    //fin du premier test non concluant
#if DEBUG
    Serial.print("Iris Position confirmed. Values extracted: High = ");
    if (irisHigh < 0x10) Serial.print("0");
    Serial.print(irisHigh, HEX);
    Serial.print(", Low = ");
    if (irisLow < 0x10) Serial.print("0");
    Serial.println(irisLow, HEX);
#endif


    // Combinaison des deux bytes pour obtenir une valeur unique
    byte combinedValue = (irisHigh << 4) | (irisLow & 0x0F);  // Décalage de byte13 et ajout de byte14
    etatIrisEnCours[choixCamActuel - 1] = combinedValue;

    //debut d'un enieme test qui ne fonctionne pas

    /*  // Calcul de l'indice de l'Iris
int irisIndex =-1;

  if (!autrechose) {
    // Cas spécifique pour Sony
    switch (combinedValue) {
      case 0x00: irisIndex = 0; break; // F0
      case 0x01: irisIndex = 0; break; // F0 ou F14 on ne sait pas
      case 0x05: irisIndex = 1; break; // F14
      case 0x06: irisIndex = 2; break; // F11
      case 0x07: irisIndex = 3; break; // F9.6
      case 0x08: irisIndex = 4; break; // F8
      case 0x09: irisIndex = 5; break; // F6.8
      case 0x0A: irisIndex = 6; break; // F5.6
      case 0x0B: irisIndex = 7; break; // F4.8
      case 0x0C: irisIndex = 8; break; // F4
      case 0x0D: irisIndex = 9; break; // F3.4
      case 0x0E: irisIndex = 10; break; // F2.8
      case 0x0F: irisIndex = 11; break; // F2.4
      case 0x10: irisIndex = 12; break; // F2
      case 0x11: irisIndex = 13; break; // F1.6
    }
    Serial.print("le test que c'est une cam sony");
  }
  // Cas pour Aver
  else {
    switch (combinedValue) {
      case 0x00: irisIndex = 0; break; // F0
      case 0x01: irisIndex = 1; break; // F14
      case 0x02: irisIndex = 2; break; // F11
      case 0x03: irisIndex = 3; break; // F8
      case 0x04: irisIndex = 4; break; // F6.8
      case 0x05: irisIndex = 5; break; // F5.6
      case 0x06: irisIndex = 6; break; // F4.8
      case 0x07: irisIndex = 7; break; // F4
      case 0x08: irisIndex = 8; break; // F3.4
      case 0x09: irisIndex = 9; break; // F2.8
      case 0x0A: irisIndex = 10; break; // F2.4
      case 0x0B: irisIndex = 11; break; // F2
      case 0x0C: irisIndex = 12; break; // F1.8
      case 0x0D: irisIndex = 13; break; // F1.6
    }
    Serial.print("le test que c'est une cam Aver");
  }


  // Affichage de la valeur de l'iris en fonction de l'index
  if (irisIndex >= 0) {
    if (!autrechose) {
      Serial.print("IRIS Value uneval sony: ");
//      Serial.println(sonyIrisValues[irisIndex]);
    } else {
      Serial.print("IRIS Value uneval aver: ");
//      Serial.println(averIrisValues[irisIndex]);
    }
  } else {
    Serial.println("Error: Invalid Iris value detected.");
  }
*/

    //fin du enieme test qui ne fonctionne pas

    lcd.setCursor(13, 1);
    lcd.print("ok");
  }
}


``

Quel arduino ?
Quand vous compilez (en mode verbose) avez vous un message qui dit que la mémoire est quasi pleine ?

J'ai découvert hier qu'on pouvait compilé avec les warning à All, je découvre le verbose aujourd'hui. Merci déjà pour cette info

Rien qui me dit que la mémoire est pleine.

J'ai souvenirs qu'avant mon crash de SSD et le code complet qui fonctionné j'avais 91% de mémoire prise, là je suis à 85%

Le croquis utilise 44838 octets (17%) de l'espace de stockage de programmes. Le maximum est de 253952 octets.
Les variables globales utilisent 6981 octets (85%) de mémoire dynamique, ce qui laisse 1211 octets pour les variables locales. Le maximum est de 8192 octets.

EDIT : J'ai un Arduino Mega (plus précisément 2560, car j'ai pas précisé cela dans le premier message)

Je suppose aussi un problème dans la gestion de mémoire, car dans le code que je décommente, il n'y a pas d'enregistrement dans l'EEPROM normalement, donc je n'arrive pas à comprendre pourquoi ça "déborde" dès que j'utilise ce code.

int irisIndex =-1;

  if (!autrechose) {
    // Cas spécifique pour Sony
    switch (combinedValue) {
      case 0x00: irisIndex = 0; break; // F0
      case 0x01: irisIndex = 0; break; // F0 ou F14 on ne sait pas
      case 0x05: irisIndex = 1; break; // F14
      case 0x06: irisIndex = 2; break; // F11
      case 0x07: irisIndex = 3; break; // F9.6
      case 0x08: irisIndex = 4; break; // F8
      case 0x09: irisIndex = 5; break; // F6.8
      case 0x0A: irisIndex = 6; break; // F5.6
      case 0x0B: irisIndex = 7; break; // F4.8
      case 0x0C: irisIndex = 8; break; // F4
      case 0x0D: irisIndex = 9; break; // F3.4
      case 0x0E: irisIndex = 10; break; // F2.8
      case 0x0F: irisIndex = 11; break; // F2.4
      case 0x10: irisIndex = 12; break; // F2
      case 0x11: irisIndex = 13; break; // F1.6
    }
    Serial.print("le test que c'est une cam sony");
  }
  // Cas pour Aver
  else {
    switch (combinedValue) {
      case 0x00: irisIndex = 0; break; // F0
      case 0x01: irisIndex = 1; break; // F14
      case 0x02: irisIndex = 2; break; // F11
      case 0x03: irisIndex = 3; break; // F8
      case 0x04: irisIndex = 4; break; // F6.8
      case 0x05: irisIndex = 5; break; // F5.6
      case 0x06: irisIndex = 6; break; // F4.8
      case 0x07: irisIndex = 7; break; // F4
      case 0x08: irisIndex = 8; break; // F3.4
      case 0x09: irisIndex = 9; break; // F2.8
      case 0x0A: irisIndex = 10; break; // F2.4
      case 0x0B: irisIndex = 11; break; // F2
      case 0x0C: irisIndex = 12; break; // F1.8
      case 0x0D: irisIndex = 13; break; // F1.6
    }
    Serial.print("le test que c'est une cam Aver");
  }


  // Affichage de la valeur de l'iris en fonction de l'index
  if (irisIndex >= 0) {
    if (!autrechose) {
      Serial.print("IRIS Value uneval sony: ");
//      Serial.println(sonyIrisValues[irisIndex]);
    } else {
      Serial.print("IRIS Value uneval aver: ");
//      Serial.println(averIrisValues[irisIndex]);
    }
  } else {
    Serial.println("Error: Invalid Iris value detected.");
  }

Dans les noms de variables, rien qui ressemble à ce qui est utilisé dans la gestion des ports.

J'ai créé une variable qui s'appelle "autrechose" juste pour voir si cela venait du nom de la variable mais pareil.

85% c’est beaucoup suivant ce que font certaines bibliothèques (qui allouent des buffets au run Time).

Je lis sur mon iPhone donc pas simple de parcourir tout votre code mais regardez si vous n’avez pas des moyens de gratter un peu en RAM.

Par exemple utilisez la macro F() pour mettre le texte en mémoire flash. Au lieu de

Faites
Serial.print(F("IRIS Value uneval sony: "));

Remplacez cela pour toutes les chaînes de texte.

Si je ne me trompe pas, la valeur est déterminé par défaut à 24 octets dans la bibliothèque EthernetUDP.h

oui c'est ça , j'ai trouvé la réponse juste après avoir posé la question... (d'oùl'effacement rapide )
24 octets c'est sans impact significatif sur la RAM occupée
85% à la compilation c'est chaud

Je viens de faire un upload en mettant le debug à 0, j'ai une mémoire à 48% et je test (j'ai peut-être trop cleaner le code avant envoi, il doit me manquer un truc) je vérifie et fait un EDIT suivant le résultat.

Merci à tous les 2, c'est super cool déjà de prendre du temps sur ce long code.

Mettre les messages en mémoire Flash et non en RAM comme indiqué par J-M-L libérera peut être suffisamment de place dans cette dernière, (l'effet produit par l"élimination radicale des messages de debug montre la voie)

En effet, je n'ai pas l'impression d'avoir le problème, mais n'ayant plus de debug je ne sais pas si cela fonctionne correctement, je vais de ce pas modifier mes 236 print en printf et re-vérifier le tout. Je pensais pas que mettre des print poserait problème sur la mémoire et du coup j'en ai abusé !

Question subsidiaire, le debug 1 ou 0 aura toujours le même effet sur des printF?

Je reviens vers vous dès que j'ai fais tout ça !

Oui votre macro éliminera le code comme avant


Attention ce n’est pas printf() mais print(F("…"));

Bonjour,

Désolé pour le délai, mais l'affichage de l'iris n'était pas finalisé dans le code, il m'a fallut reprendre une partie pour revenir avec le résultat et pas dispo hier soir pour avancer dessus.

Je pensais qu'il y avait du mieux, mais le problème ne semble pas venir réellement d'un problème de mémoire, car j'obtiens un résultat inattendu.

Je demande à afficher les valeurs de l'iris en fonction de la réponse avec cette partie de code

  if (!isCameraAver) {
      if (irisHigh == 0x01 )
        {
          switch (irisLow) {
            case 0x00: irisIndex = 12; break; // F2
            case 0x01: irisIndex = 14; break; // F1.6
          }
        }
if (irisHigh == 0x00) {
    if (irisLow == 0x01) {
        if (irisIndex == 2) {
            irisIndex = 0; // F0
        } else if (irisIndex == 0) {
            irisIndex = 1; // F0
        }
    } else {
        switch (irisLow) {
            case 0x00: irisIndex = 0; break; // F0
            case 0x05: irisIndex = 1; break; // F14
            case 0x06: irisIndex = 2; break; // F11
            case 0x07: irisIndex = 3; break; // F9.6
            case 0x08: irisIndex = 4; break; // F8
            case 0x09: irisIndex = 5; break; // F6.8
            case 0x0A: irisIndex = 6; break; // F5.6
            case 0x0B: irisIndex = 7; break; // F4.8
            case 0x0C: irisIndex = 8; break; // F4
            case 0x0D: irisIndex = 9; break; // F3.4
            case 0x0E: irisIndex = 10; break; // F2.8
            case 0x0F: irisIndex = 11; break; // F2.4
        }
    }
}
      Serial.println(F("le test que c'est une cam sony"));
//    etatIrisEnCours[choixCamActuel - 1] = irisIndex;
      etatIrisEnCours[choixCamActuel - 1] = sonyIrisValues[irisIndex];
  }

Cela fonctionne correctement pour l'ensemble des case à l'exception si je reçoit 0x01
j'obtiens sur le LCD ou le monitor

Response from camera (in hex): 01 11 00 07 00 00 02 01 90 50 00 00 00 01 FF 
Iris High = 00, Iris Low = 01
Iris Position confirmed. Values extracted: High = 00, Low = 01
si 0 cam sony si 1 cam Aver :0
le test que c'est une cam sony
Etat Iris en cours :on' 

ce on' affiché pour l'état iris n'apparait dans mon code qu'à 3 endroits, tous les 3 dans la partie d'affichage de la page de configuration des caméras void showConfigCamera.

 // Case à cocher PAN
    client.print("<input type='checkbox' name='pan");
    client.print(i + 1);
    client.print("' value='on' ");
    if (inversePan[i]) client.print("checked");
    client.println("> Inverser PAN<br>");

    // Case à cocher TILT
    client.print("<input type='checkbox' name='tilt");
    client.print(i + 1);
    client.print("' value='on' ");
    if (inverseTilt[i]) client.print("checked");
    client.println("> Inverser TILT<br>");

    // Case à cocher Caméra Aver
    client.print("<input type='checkbox' name='typeCamAver");
    client.print(i + 1);
    client.print("' value='on' ");
    if (typeCamAver[i]) client.print("checked");
    client.println("> Caméra Aver<br></div><br>");

Le seul point commun au 2 codes est que je récupère typeCamAver

Je pense que l'utilisation du nom de la variable doit poser des problèmes dans mon code et je n'arrive pas à en trouver la raison.

Je rappelle que je suis pas programmeur de base donc je m'emmêle un peu avec les bool byte et char*, qui je pense doivent être responsable de mes "colisions".

Mais je suis maintenant à 51% de mémoire avec vos conseils ce qui est déjà une belle avancée de ce côté.

Je vous remet le code complet à jour dans le prochain post si vous voulez jeter un oeil.

Encore merci pour l'aide apporté jusqu'à maintenant.

voici le code complet à jour, la fonction queryirisState est maintenant dans le début du code un peu avant le setup

#include <SPI.h>
#include <Ethernet.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
#include <EthernetUdp.h>
#include <avr/wdt.h>

byte mac[] = { 0xA8, 0x61, 0x0A, 0xAE, 0x97, 0xD0 };
EthernetServer server(80);

#define DEBUG 1  // mettre à zero pour supprimer les messages dans le port série et ne pas dépendre du PC et du logiciel Arduino IDE pour démarrer

#define DHCP_EEPROM_ADDR 0        // emplacement pour DHCP on ou off
#define FIRSTSTART_EEPROM_ADDR 1  //emplacement pour déterminer si premier démarrage ou pas
#define IP_ADDR_START 2           //adresse ip de l'arduino
#define MASK_ADDR_START 6         // masque de l'arduino
#define GATEWAY_ADDR_START 10     // passerelle de l'arduino
#define DNS_ADDR_START 14         //DNS de l'arduino
// Adresses EEPROM pour les caméras
#define CAMERA_IP_ADDR_START 18    // Premier emplacement pour stocker les IP des caméras
#define CAMERA_PORT_ADDR_START 54  // Premier emplacement pour stocker les ports des caméras
#define CAMERA_IP_OFFSET 4         // Chaque IP prend 4 octets
#define CAMERA_PORT_OFFSET 2       // Chaque port prend 2 octets (uint16_t)
#define NUM_CAMERAS 9              // Nombre total de caméras

#define EEPROM_INVERSE_PAN_START 72    //premier emplacement pour stocker les inversePAN
#define EEPROM_INVERSE_TILT_START 81   //premier emplacement pour stocker les inverseTILT
#define EEPROM_TYPE_CAM_AVER_START 90  //premier emplacement pour stocker si c'est une cam de marque AVER

LiquidCrystal lcd(14, 15, 5, 4, 3, 2);  //ecran lCD 16*2

// Déclaration des boutons
const int zeropad = 6;
const int unpad = 23;
const int deuxpad = 25;
const int troispad = 27;
const int quatrepad = 29;
const int cinqpad = 31;
const int sixpad = 33;
const int septpad = 35;
const int huitpad = 37;
const int neufpad = 39;
const int choixcam = 41;
const int presetcam = 43;
const int clearBtn = 45;
const int pluszoom = 47;
const int moinszoom = 49;
const int plusfocus = A6;
const int moinsfocus = A14;
const int btnspeed = 7;
const int btnplus = 22;
const int btnmoins = 24;
const int togAF = 26;  // bouton autofocus on/off
const int btnsetup = 28;
const int btnsortiemenu = 30;
const int supprime_mem = 8;
const int btnenter = A8;
const int btnopt1 = A9;
const int btnopt2 = A10;
const int btnF1 = A11;
const int btnF2 = A12;
const int btnF3 = A13;
const int btnautopan = A4;
const int btnautopatrol = A5;
const int irisclose = A7;
const int irisopen = 34;




// Pins du joystick
const int tiltPin = A0;  // Haut/Bas
const int panPin = A1;   // Droite/Gauche

// Seuils du joystick
const int neutralMin = 500;
const int neutralMax = 510;

// Variables pour le joystick
int tiltValue, panValue;
byte tiltSpeed, panSpeed;

// Dernières commandes envoyées
byte lastTiltSpeed = 0x03;  // Neutre
byte lastPanSpeed = 0x03;   // Neutre


// Commandes UDP pour autofocus
byte resetCompteurCommande[] = { 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01 };
byte commandTGAF[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x02, 0x81, 0x01, 0x04, 0x38, 0x10, 0xFF };
byte commandFBTGAF[] = { 0x01, 0x10, 0x00, 0x05, 0x00, 0x00, 0x02, 0x01, 0x81, 0x09, 0x04, 0x38, 0xFF };
byte commandSonyTGIrisAuto[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x02, 0x81, 0x01, 0x04, 0x39, 0x00, 0xFF };
size_t sizeCommandSonyTGIrisAuto = sizeof(commandSonyTGIrisAuto);
byte commandAVERTGIrisAuto[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x02, 0x81, 0x01, 0x04, 0x39, 0x00, 0xFF };
size_t sizeCommandAVERTGIrisAuto = sizeof(commandAVERTGIrisAuto);  // Taille du tableau
byte commandSonyTGIrisManuel[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x02, 0x81, 0x01, 0x04, 0x39, 0x0B, 0xFF };
size_t sizeCommandSonyTGIrisManuel = sizeof(commandSonyTGIrisManuel);  // Taille du tableau
byte commandAVERTGIrisManuel[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x02, 0x81, 0x01, 0x04, 0x39, 0x0B, 0xFF };
size_t sizeCommandAVERTGIrisManuel = sizeof(commandAVERTGIrisManuel);  // Taille du tableau
byte commandSonyFBTGIrisAuto[] = { 0x01, 0x10, 0x00, 0x05, 0x00, 0x00, 0x02, 0x01, 0x81, 0x09, 0x04, 0x39, 0xFF };
size_t sizeCommandSonyFBTGIrisAuto = sizeof(commandSonyFBTGIrisAuto);  // Taille du tableau
byte commandAVERFBTGIrisAuto[] = { 0x01, 0x10, 0x00, 0x05, 0x00, 0x00, 0x02, 0x01, 0x81, 0x09, 0x04, 0x39, 0xFF };
size_t sizeCommandAVERFBTGIrisAuto = sizeof(commandAVERFBTGIrisAuto);  // Taille du tableau


// pour lecture de l'UDP entrant
byte packetBuffer[UDP_TX_PACKET_MAX_SIZE];  // Buffer pour les paquets reçus

// État des caméras (0 à 8 pour 9 caméras)
int etatAF[9] = { 0 };                                                           // Initialise l'état AF pour chaque caméra (0 = inconnu)
int etatIrisAuto[9] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };  // Initialise l'état AF pour chaque caméra (0 = inconnu)

// Dernier état connu pour éviter un rafraîchissement inutile
int dernierEtatAF[9] = { 0 };        // Initialise avec les mêmes valeurs que etatAF
int dernierEtatIrisAuto[9] = { 0 };  // Initialise avec les mêmes valeurs que etatAF

unsigned long lastUDPPacketTime = 0;  // Temps de dernier paquet UDP reçu
unsigned long udpTimeout = 1000;      // Timeout pour la réception UDP

int nouvelEtatAF = -1;        // Initialisation à une valeur par défaut l'état AF
int nouvelEtatIrisAuto = -1;  // Initialisation à une valeur par défaut l'état Iris Auto


unsigned long lastButtonPressTime = 0;         // Dernière fois où un bouton a été pressé
const unsigned long udpActiveDuration = 5000;  // Activer l'UDP pendant 5 secondes après un bouton
bool udpActive = false;                        // Indique si l'UDP est actif
const unsigned long udpActiveDuration2 = 5000;
bool udpActive2 = false;
const unsigned long udpActiveDuration3 = 5000;
bool udpActive3 = false;

// Variables pour stocker les octets extraits de l'Iris
byte irisHigh = 0;
byte irisLow = 0;





// États des boutons HIGH pour INPUT_PULLUP
bool lastPlusZoomState = HIGH;
bool lastMoinsZoomState = HIGH;
bool lastPlusFocusState = HIGH;
bool lastMoinsFocusState = HIGH;

// État de la vitesse et type de commande
enum CommandType { ZOOM,
                   FOCUS };
CommandType currentType = ZOOM;
int zoomSpeed = 2;   // Vitesse par défaut pour le zoom
int focusSpeed = 2;  // Vitesse par défaut pour le focus


unsigned long startTime;
bool cycleComplete = false;


unsigned long lastButtonPress = 0;
unsigned long debounceDelay = 200;  // Débounce de 200ms

bool modeChoixCam = false;        //initialise le mode au démarrage
bool modePresetCam = true;        //initialise le mode au démarrage
bool attenteChoixCamera = false;  //pas en mode choix cam donc pas d'attente de choix cam au démarrage
int choixCamActuel = -1;          // Variable pour stocker la caméra choisie
int presetNumber = 0;


//gestion save/recall preset
const unsigned long longPressDuration = 3000;  // Durée d'appui long en ms
unsigned long buttonPressStart1 = 0;           // Moment où le bouton est pressé
unsigned long buttonPressStart2 = 0;
unsigned long buttonPressStart3 = 0;
unsigned long buttonPressStart4 = 0;
unsigned long buttonPressStart5 = 0;
unsigned long buttonPressStart6 = 0;
unsigned long buttonPressStart7 = 0;
unsigned long buttonPressStart8 = 0;
unsigned long buttonPressStart9 = 0;

// Variables pour la gestion du temps d'affiche de l'* à l'enregistrement de preset
unsigned long presetStarTime = 0;  // Moment où l'étoile a été affichée
bool starDisplayed = false;        // Flag pour vérifier si l'étoile a été affichée

//UDP port d'écoute de l'arduino
EthernetUDP Udp;
uint16_t udpPort = 52381;

//int selectedCamera = -1;
IPAddress cameraIP;
uint16_t cameraPort;

// Commande VISCA pour remise à zéro
byte viscaResetCommand[] = { 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01 };

// valeur par défaut si pas de DHCP
IPAddress defaultIP(192, 168, 1, 100);
IPAddress defaultMask(255, 255, 0, 0);
IPAddress defaultGateway(192, 168, 1, 1);
IPAddress defaultDNS(8, 8, 8, 8);  // DNS par défaut


bool readDHCPFromEEPROM() {
  return EEPROM.read(DHCP_EEPROM_ADDR) == 1;
}

bool readInvertTiltFromEEPROM() {
  return EEPROM.read(EEPROM_INVERSE_TILT_START) == 1;
}

bool readInvertPanFromEEPROM() {
  return EEPROM.read(EEPROM_INVERSE_PAN_START) == 1;
}

bool readTypeCamAverFromEEPROM() {
  return EEPROM.read(EEPROM_TYPE_CAM_AVER_START) == 1;
}

void writeDHCPToEEPROM(bool isDHCPEnabled) {
  EEPROM.write(DHCP_EEPROM_ADDR, isDHCPEnabled ? 1 : 0);
}


// Fonction pour écrire un booléen pour PAN dans l'EEPROM
void writeInvertPanToEEPROM(bool value, int addr) {
  EEPROM.write(addr, value ? 1 : 0);
}

// Fonction pour écrire un booléen pour TILT dans l'EEPROM
void writeInvertTiltToEEPROM(bool value, int addr) {
  EEPROM.write(addr, value ? 1 : 0);
}

// Fonction pour écrire un booléen pour TILT dans l'EEPROM
void writeTypeCamAverToEEPROM(bool value, int addr) {
  EEPROM.write(addr, value ? 1 : 0);
}

// Fonction pour lire un booléen pour PAN depuis l'EEPROM
bool readInvertPanFromEEPROM(int addr) {
  return EEPROM.read(addr) == 1;
}

// Fonction pour lire un booléen pour TILT depuis l'EEPROM
bool readInvertTiltFromEEPROM(int addr) {
  return EEPROM.read(addr) == 1;
}

// Fonction pour lire un booléen pour TILT depuis l'EEPROM
bool readTypeCamAverFromEEPROM(int addr) {
  return EEPROM.read(addr) == 1;
}

// Variables pour stocker les états
bool inversePan[9];
bool inverseTilt[9];
bool typeCamAver[9];

bool readFirstStartFromEEPROM() {
  return EEPROM.read(FIRSTSTART_EEPROM_ADDR) != 1;
}

void writeFirstStartToEEPROM() {
  EEPROM.write(FIRSTSTART_EEPROM_ADDR, 1);
}

void printEEPROMValues() {
  IPAddress ip = readIPAddressFromEEPROM(IP_ADDR_START);
  IPAddress mask = readIPAddressFromEEPROM(MASK_ADDR_START);
  IPAddress gateway = readIPAddressFromEEPROM(GATEWAY_ADDR_START);
  IPAddress dns = readIPAddressFromEEPROM(DNS_ADDR_START);

#if DEBUG
  Serial.println(F("Valeurs stockées dans l'EEPROM :"));
  Serial.print(F("IP EEPROM: "));
  Serial.println(ip);
  Serial.print(F("Masque EEPROM : "));
  Serial.println(mask);
  Serial.print(F("Passerelle EEPROM : "));
  Serial.println(gateway);
  Serial.print(F("DNS EEPROM : "));
  Serial.println(dns);
#endif
}


uint16_t readPortFromEEPROM(int startAddr) {
  uint16_t port = (EEPROM.read(startAddr) << 8) | EEPROM.read(startAddr + 1);
  return port;
}

void writePortToEEPROM(uint16_t port, int startAddr) {
  EEPROM.write(startAddr, (port >> 8) & 0xFF);
  EEPROM.write(startAddr + 1, port & 0xFF);
}


void clearEEPROM() {
  for (size_t i = 0; i < EEPROM.length(); i++) {
    EEPROM.write(i, 0);
  }
}

// pour vidage ram via bouton reset
bool isButtonPressedFor5Seconds() {
  unsigned long buttonPressStartTime = 0;
  bool buttonState = digitalRead(supprime_mem);
  if (buttonState == LOW) {
    buttonPressStartTime = millis();

    while (digitalRead(supprime_mem) == LOW) {
      if (millis() - buttonPressStartTime >= 5000) {
        return true;
      }
    }
  }
  return false;
}

IPAddress readIPAddressFromEEPROM(int startAddr) {
  return IPAddress(EEPROM.read(startAddr), EEPROM.read(startAddr + 1), EEPROM.read(startAddr + 2), EEPROM.read(startAddr + 3));
}

void writeIPAddressToEEPROM(IPAddress ip, int startAddr) {
  for (int i = 0; i < 4; i++) {
    EEPROM.write(startAddr + i, ip[i]);
  }
}


bool isDHCPEnabled = readDHCPFromEEPROM();


// Fonction pour envoyer une commande VISCA over IP
void sendViscaJoystickCommand(byte* command, size_t length) {
  // Préfixe obligatoire pour VISCA over IP
  byte viscaJoystickPrefix[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x02 };


#if DEBUG
  Serial.print(F("Envoi de la commande : "));

  for (size_t i = 0; i < sizeof(viscaJoystickPrefix); i++) {
    Serial.print(F("0x"));
    Serial.print(viscaJoystickPrefix[i], HEX);
    Serial.print(F(" "));
  }
  for (size_t i = 0; i < length; i++) {
    Serial.print(F("0x"));
    Serial.print(command[i], HEX);
    Serial.print(F(" "));
  }
  Serial.println();
#endif
  // Envoyer le préfixe suivi de la commande
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaJoystickPrefix, sizeof(viscaJoystickPrefix));
  Udp.write(command, length);
  Udp.endPacket();
}

// Fonction pour envoyer la commande de reset d'incrémentation
void sendResetCommand() {
  byte resetCommand[] = { 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01 };
#if DEBUG
  Serial.println(F("Envoi de la commande RESET d'incrémentation."));
#endif
  Udp.begin(cameraPort);
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(resetCommand, sizeof(resetCommand));
  Udp.endPacket();
}

// Fonction pour envoyer une commande de mouvement
void sendMoveCommand(byte panSpeed, byte tiltSpeed, byte panDirection, byte tiltDirection) {
  sendResetCommand();  // Envoyer la commande de reset avant chaque mouvement
  byte moveCommand[] = { 0x81, 0x01, 0x06, 0x01, panSpeed, tiltSpeed, panDirection, tiltDirection, 0xFF };
  sendViscaJoystickCommand(moveCommand, sizeof(moveCommand));
}

// Fonction pour envoyer une commande d'arrêt
void sendStopCommand() {
  sendMoveCommand(0x00, 0x00, 0x03, 0x03);  // Stop PAN et TILT
}


// Fonction pour ajuster la vitesse
void adjustSpeed(int change) {
  switch (currentType) {
    case ZOOM:
      zoomSpeed = constrain(zoomSpeed + change, 0, 7);
#if DEBUG
      Serial.print(F("Vitesse Zoom: "));
      Serial.println(zoomSpeed);
#endif
      lcd.setCursor(0, 1);
      lcd.print("                ");
      lcd.setCursor(0, 1);
      lcd.print("Zoom");
      lcd.setCursor(5, 1);
      lcd.print(zoomSpeed);
      break;
    case FOCUS:
      focusSpeed = constrain(focusSpeed + change, 0, 7);
#if DEBUG
      Serial.print(F("Vitesse Focus: "));
      Serial.println(focusSpeed);
#endif
      lcd.setCursor(0, 1);
      lcd.print("                ");
      lcd.setCursor(0, 1);
      lcd.print("Focus ");
      lcd.setCursor(6, 1);
      lcd.print(focusSpeed);
      break;
  }
}

//fonction pour afficher la cam en cours avec actualisation AF (reste à ajouter l'IRIS quand fonctionnel)
void afficherEtatCamSelectionnee() {
  lcd.setCursor(0, 0);
  lcd.print("Cam ");
  lcd.print(choixCamActuel);
  lcd.print(" ");
  lcd.setCursor(6, 0);
  lcd.print("Pos ");
  lcd.setCursor(14, 0);
  lcd.print(dernierEtatAF[choixCamActuel] == 0x02 ? "AF" : "MF");
}

//gestion du bouton reboot sans vidage de l'EEPROM
unsigned long lastButtonPressF3 = 0;  // Pour vérifier la durée d'appui du bouton
bool isButtonPressedF3 = false;       // Flag pour vérifier l'état du bouton


// Commandes VISCA Iris Close ou Open ou Query
const byte CMD_IRIS_OPEN[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x01, 0x81, 0x01, 0x04, 0x0B, 0x02, 0xFF };
const byte CMD_IRIS_CLOSE[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x01, 0x81, 0x01, 0x04, 0x0B, 0x03, 0xFF };
//const byte CMD_IRIS_QUERY[] = { 0x01, 0x10, 0x00, 0x05, 0x00, 0x00, 0x02, 0x01, 0x81, 0x09, 0x04, 0x4B, 0xFF };
const byte viscaIrisFBpositionCommand[] = { 0x01, 0x10, 0x00, 0x05, 0x00, 0x00, 0x02, 0x01, 0x81, 0x09, 0x04, 0x4B, 0xFF };

//tableau pour équivalent IRIS (peut faire parti du problème)
const char* sonyIrisValues[] = {
  "F0", "F14", "F11", "F9.6", "F8", "F6.8", "F5.6", "F4.8", "F4", "F3.4",
  "F2.8", "F2.4", "F2", "F1.8", "F1.6"
};

const char* averIrisValues[] = {
  "F0", "F14", "F11", "F8", "F6.8", "F5.6", "F4.8", "F4", "F3.4",
  "F2.8", "F2.4", "F2", "F1.8", "F1.6"
};


// Déclare un tableau pour stocker l'état de l'iris pour chaque caméra
//byte etatIrisEnCours[9];  // Supposons qu'il y a 9 caméras maximum
const char* etatIrisEnCours[9];


//mise en place du code ici


int irisIndex =-1;

void queryIrisState() {
byte isCameraAver = typeCamAver[choixCamActuel - 1];
int irisIndex =-1;
#if DEBUG
  Serial.println(F("Entering queryIrisState function."));
#endif
  if (choixCamActuel == -1) {
#if DEBUG
    Serial.println(F("No camera selected, exiting queryIrisState."));
#endif
    return;
  }
  Udp.begin(cameraPort);

#if DEBUG
  Serial.println(F("Sending VISCA Reset command..."));
#endif
  if (cameraIP == IPAddress(0, 0, 0, 0) || cameraPort == 0) {
#if DEBUG
    Serial.println(F("Error: Invalid Camera IP or Port!"));
#endif
    return;
  }

  // Sending reset command to camera
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaResetCommand, sizeof(viscaResetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("Reset command sent. Sending FB Iris command..."));
#endif

  // Sending FB Iris command
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaIrisFBpositionCommand, sizeof(viscaIrisFBpositionCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("Iris Position FB command sent. Listening for responses..."));
#endif

  // Listen for responses for a short period
  unsigned long startTime = millis();
  bool irisPositionAcknowledged = false;

  while (millis() - startTime < 1000) {  // Attendre jusqu'à 1 seconde
    int packetSize = Udp.parsePacket();
    if (packetSize) {
      byte incomingPacket[UDP_TX_PACKET_MAX_SIZE];
      Udp.read(incomingPacket, packetSize);

// Affiche la taille du paquet
#if DEBUG
      Serial.print("Packet size received: ");
      Serial.println(packetSize);
#endif

// Afficher le contenu du paquet
#if DEBUG
      Serial.print(F("Response from camera (in hex): "));
      for (int i = 0; i < packetSize; i++) {
        if (incomingPacket[i] < 0x10) Serial.print(F("0"));
        Serial.print(incomingPacket[i], HEX);
        Serial.print(F(" "));
      }
      Serial.println();
#endif

      // Condition ajustée pour la taille 15 et extraction des octets 13 et 14
      if (packetSize == 15 && incomingPacket[8] == 0x90 && incomingPacket[9] == 0x50) {
        irisHigh = incomingPacket[12];
        irisLow = incomingPacket[13];
        irisPositionAcknowledged = true;
        break;  // Arrêter après la première correspondance
      }
    }
  }

// Afficher les valeurs extraites même si elles ne sont pas confirmées
#if DEBUG
  Serial.print(F("Iris High = "));
  if (irisHigh < 0x10) Serial.print(F("0"));
  Serial.print(irisHigh, HEX);
  Serial.print(F(", Iris Low = "));
  if (irisLow < 0x10) Serial.print(F("0"));
  Serial.println(irisLow, HEX);
#endif

  if (irisPositionAcknowledged) {

#if DEBUG
    Serial.print(F("Iris Position confirmed. Values extracted: High = "));
    if (irisHigh < 0x10) Serial.print(F("0"));
    Serial.print(irisHigh, HEX);
    Serial.print(F(", Low = "));
    if (irisLow < 0x10) Serial.print(F("0"));
    Serial.println(irisLow, HEX);
#endif

  Serial.print(F("si 0 cam sony si 1 cam Aver :"));
  Serial.println(isCameraAver);

  if (!isCameraAver) {
      if (irisHigh == 0x01 )
        {
          switch (irisLow) {
            case 0x00: irisIndex = 12; break; // F2
            case 0x01: irisIndex = 14; break; // F1.6
          }
        }
if (irisHigh == 0x00) {
    if (irisLow == 0x01) {
        if (irisIndex == 2) {
            irisIndex = 0; // F0
        } else if (irisIndex == 0) {
            irisIndex = 1; // F0
        }
    } else {
        switch (irisLow) {
            case 0x00: irisIndex = 0; break; // F0
            case 0x05: irisIndex = 1; break; // F14
            case 0x06: irisIndex = 2; break; // F11
            case 0x07: irisIndex = 3; break; // F9.6
            case 0x08: irisIndex = 4; break; // F8
            case 0x09: irisIndex = 5; break; // F6.8
            case 0x0A: irisIndex = 6; break; // F5.6
            case 0x0B: irisIndex = 7; break; // F4.8
            case 0x0C: irisIndex = 8; break; // F4
            case 0x0D: irisIndex = 9; break; // F3.4
            case 0x0E: irisIndex = 10; break; // F2.8
            case 0x0F: irisIndex = 11; break; // F2.4
        }
    }
}
      Serial.println(F("le test que c'est une cam sony"));
//    etatIrisEnCours[choixCamActuel - 1] = irisIndex;
      etatIrisEnCours[choixCamActuel - 1] = sonyIrisValues[irisIndex];
  }
  else{
          switch (irisLow) {
          case 0x00: irisIndex = 0; break; //F0
          case 0x01: irisIndex = 1; break; //F14 ou F0
          case 0x02: irisIndex = 2; break; //F11
          case 0x03: irisIndex = 3; break; //F8
          case 0x04: irisIndex = 4; break; //F6.8
          case 0x05: irisIndex = 5; break; //F5.6
          case 0x06: irisIndex = 6; break; //F4.8
          case 0x07: irisIndex = 7; break; //F4
          case 0x08: irisIndex = 8; break; //F3.4
          case 0x09: irisIndex = 9; break; //F2.8
          case 0x0A: irisIndex = 10; break; //2.4
          case 0x0B: irisIndex = 11; break; //F2
          case 0x0C: irisIndex = 12; break; //F1.8
          case 0x0D: irisIndex = 13; break; //F1.6
          }
        Serial.println(F("le test que c'est une cam Aver"));
        etatIrisEnCours[choixCamActuel - 1] = averIrisValues[irisIndex];
//        Serial.println(etatIrisEnCours[choixCamActuel - 1]);
  }
}
}

void afficheMemIrisValue()
{
//  if (irisIndex >= 0) {
    lcd.setCursor(11, 1);
    lcd.print("     ");
//    if (etatIrisEnCours[choixCamActuel - 1] != 0)
//    {
      lcd.setCursor(11, 1);
      lcd.print(" ");
      lcd.print(etatIrisEnCours[choixCamActuel - 1]);
//    } 
//    else {
//      lcd.setCursor(11, 1);
//      lcd.print("Close");
//    }
//  } else {
//    Serial.println(F("Error: Invalid Iris value detected."));
//  }
}








// fin de mise en place du code





//fonction pour auto manu Iris
void gererCommandeIrisAuto(int etatIrisActuel) {
  if (etatIrisActuel != 0x02 && etatIrisActuel != 0x03) {
#if DEBUG
    Serial.print(F("État inattendu reçu dans gererCommandeIrisAuto : "));
    Serial.println(etatIrisActuel, HEX);
#endif
    return;  // Ne rien faire si l'état est invalide
  }

  // Vérifie si la caméra actuelle est de type Aver ou Sony
  bool isCameraAver = typeCamAver[choixCamActuel - 1];
#if DEBUG
  Serial.print(F("isCameraAver: "));
  Serial.println(isCameraAver);  // Débogage pour vérifier la valeur
#endif
  byte* commandTGIrisAuto;
  size_t commandSizeTGIrisAuto;
  byte* commandTGIrisManuel;
  size_t commandSizeTGIrisManuel;
  byte* commandFBTGIrisAuto;
  size_t commandSizeFBTGIrisAuto;

  // Sélection des commandes et tailles en fonction du type de caméra
  if (isCameraAver) {
    commandTGIrisAuto = commandAVERTGIrisAuto;
    commandSizeTGIrisAuto = sizeCommandAVERTGIrisAuto;

    commandTGIrisManuel = commandAVERTGIrisManuel;
    commandSizeTGIrisManuel = sizeCommandAVERTGIrisManuel;

    commandFBTGIrisAuto = commandAVERFBTGIrisAuto;
    commandSizeFBTGIrisAuto = sizeCommandAVERFBTGIrisAuto;
  } else {
    commandTGIrisAuto = commandSonyTGIrisAuto;
    commandSizeTGIrisAuto = sizeCommandSonyTGIrisAuto;

    commandTGIrisManuel = commandSonyTGIrisManuel;
    commandSizeTGIrisManuel = sizeCommandSonyTGIrisManuel;

    commandFBTGIrisAuto = commandSonyFBTGIrisAuto;
    commandSizeFBTGIrisAuto = sizeCommandSonyFBTGIrisAuto;
  }

  if (etatIrisActuel == 0x02) {  // Si l'iris est en mode Auto
#if DEBUG
    Serial.println(F("Mode Auto détecté : envoi des commandes correspondantes"));
#endif
    sendUDPViscaCommand(resetCompteurCommande, sizeof(resetCompteurCommande));
    delay(500);
    sendUDPViscaCommand(commandTGIrisManuel, commandSizeTGIrisManuel);  // Commande Manuel
    delay(500);
    sendUDPViscaCommand(resetCompteurCommande, sizeof(resetCompteurCommande));
    delay(500);
    sendUDPViscaCommand(commandFBTGIrisAuto, commandSizeFBTGIrisAuto);
    delay(500);
  } else if (etatIrisActuel == 0x03) {  // Si l'iris est en mode Manuel
#if DEBUG
    Serial.println(F("Mode Manuel détecté : envoi des commandes correspondantes"));
#endif
    sendUDPViscaCommand(resetCompteurCommande, sizeof(resetCompteurCommande));
    delay(500);
    sendUDPViscaCommand(commandTGIrisAuto, commandSizeTGIrisAuto);  // Commande Auto
    delay(500);
    sendUDPViscaCommand(resetCompteurCommande, sizeof(resetCompteurCommande));
    delay(500);
    sendUDPViscaCommand(commandFBTGIrisAuto, commandSizeFBTGIrisAuto);
    delay(500);
  }
}


// si on entre dans le menu, sauvegarde du LCD
// État des boutons
bool btnSetupPressed = false;
bool btnSortieMenuPressed = false;

// Variables pour gérer le menu
bool inSetupMenu = false;
int setupMenuState = 0;

void afficherSetupMenu() {
  lcd.clear();
  switch (setupMenuState) {
    case 0:  // Afficher l'adresse IP
      lcd.print(isDHCPEnabled ? "DHCP: ON      IP" : "DHCP: OFF     IP");
      lcd.setCursor(0, 1);
      lcd.print(Ethernet.localIP());
      break;

    case 1:  // Afficher le masque de sous-réseau
      lcd.print(isDHCPEnabled ? "DHCP: ON  MASQUE" : "DHCP: OFF MASQUE");
      lcd.setCursor(0, 1);
      lcd.print(Ethernet.subnetMask());
      break;

    case 2:  // Afficher la passerelle
      lcd.print(isDHCPEnabled ? "DHCP: ON      GW" : "DHCP: OFF     GW");
      lcd.setCursor(0, 1);
      lcd.print(Ethernet.gatewayIP());
      break;
  }
}

void setup() {
//#if DEBUG
  Serial.begin(9600);
//#endif

  for (int i = 0; i < 9; i++) {
    inversePan[i] = EEPROM.read(EEPROM_INVERSE_PAN_START + i);
    inverseTilt[i] = EEPROM.read(EEPROM_INVERSE_TILT_START + i);
    typeCamAver[i] = EEPROM.read(EEPROM_TYPE_CAM_AVER_START + i);
  }

  Udp.begin(udpPort);

  pinMode(supprime_mem, INPUT_PULLUP);
  pinMode(zeropad, INPUT_PULLUP);
  pinMode(unpad, INPUT_PULLUP);
  pinMode(deuxpad, INPUT_PULLUP);
  pinMode(troispad, INPUT_PULLUP);
  pinMode(quatrepad, INPUT_PULLUP);
  pinMode(cinqpad, INPUT_PULLUP);
  pinMode(sixpad, INPUT_PULLUP);
  pinMode(septpad, INPUT_PULLUP);
  pinMode(huitpad, INPUT_PULLUP);
  pinMode(neufpad, INPUT_PULLUP);
  pinMode(choixcam, INPUT_PULLUP);
  pinMode(presetcam, INPUT_PULLUP);
  pinMode(clearBtn, INPUT_PULLUP);
  pinMode(pluszoom, INPUT_PULLUP);
  pinMode(moinszoom, INPUT_PULLUP);
  pinMode(plusfocus, INPUT_PULLUP);
  pinMode(moinsfocus, INPUT_PULLUP);
  pinMode(btnspeed, INPUT_PULLUP);
  pinMode(btnplus, INPUT_PULLUP);
  pinMode(btnmoins, INPUT_PULLUP);
  pinMode(togAF, INPUT_PULLUP);
  pinMode(btnsetup, INPUT_PULLUP);
  pinMode(btnsortiemenu, INPUT_PULLUP);
  pinMode(supprime_mem, INPUT_PULLUP);
  pinMode(btnenter, INPUT_PULLUP);
  pinMode(btnopt1, INPUT_PULLUP);
  pinMode(btnopt2, INPUT_PULLUP);
  pinMode(btnF1, INPUT_PULLUP);
  pinMode(btnF2, INPUT_PULLUP);
  pinMode(btnF3, INPUT_PULLUP);
  pinMode(btnautopan, INPUT_PULLUP);
  pinMode(btnautopatrol, INPUT_PULLUP);
  pinMode(irisclose, INPUT_PULLUP);
  pinMode(irisopen, INPUT_PULLUP);
  printEEPROMValues();
  if (readFirstStartFromEEPROM()) {
#if DEBUG
    Serial.println(F("Premier démarrage, configuration par défaut..."));
#endif
    writeDHCPToEEPROM(true);
    writeIPAddressToEEPROM(defaultIP, IP_ADDR_START);
    writeIPAddressToEEPROM(defaultMask, MASK_ADDR_START);
    writeIPAddressToEEPROM(defaultGateway, GATEWAY_ADDR_START);
    writeIPAddressToEEPROM(defaultDNS, DNS_ADDR_START);
    writeFirstStartToEEPROM();
  }

  // Initialisation de l'écran LCD
  lcd.begin(16, 2);
  lcd.clear();
  startTime = millis();  // Initialiser le temps de démarrage pour le cycle de 30 secondes
  if (isDHCPEnabled) {
    if (Ethernet.begin(mac) == 0) {
#if DEBUG
      Serial.println(F("Échec de la configuration DHCP."));
#endif
      Ethernet.begin(mac, readIPAddressFromEEPROM(IP_ADDR_START), readIPAddressFromEEPROM(DNS_ADDR_START), readIPAddressFromEEPROM(GATEWAY_ADDR_START), readIPAddressFromEEPROM(MASK_ADDR_START));
    }
  } else {
    Ethernet.begin(mac, readIPAddressFromEEPROM(IP_ADDR_START), readIPAddressFromEEPROM(DNS_ADDR_START), readIPAddressFromEEPROM(GATEWAY_ADDR_START), readIPAddressFromEEPROM(MASK_ADDR_START));
  }

#if DEBUG
  Serial.print(F("Adresse IP : "));
  Serial.println(Ethernet.localIP());
  Serial.print(F("Masque de sous-réseau : "));
  Serial.println(Ethernet.subnetMask());
  Serial.print(F("Passerelle : "));
  Serial.println(Ethernet.gatewayIP());
  Serial.print(F("DNS : "));
  Serial.println(Ethernet.dnsServerIP());
#endif
  server.begin();

#if DEBUG
  // Affichage des valeurs de l'EEPROM entre les adresses 90 et 98 pour camera Aver ou Sony
  Serial.println(F("Lecture de l'EEPROM de l'adresse 90 à 98:"));

  for (int i = 90; i <= 98; i++) {
    byte val = EEPROM.read(i);  // Lire la valeur à l'adresse i
    Serial.print(F("Adresse "));
    Serial.print(i);
    Serial.print(F(": "));
    Serial.println(val, HEX);  // Afficher la valeur en hexadécimal
  }

  Serial.println(F("Initialisation terminée. Mode par défaut : presetcam."));
  Serial.println(F("init"));
  Serial.print(F("mode choix cam :"));
  Serial.println(modeChoixCam);
  Serial.print(F("mode preset cam :"));
  Serial.println(modePresetCam);
  Serial.print(F("choix cam actuel :"));
  Serial.println(choixCamActuel);
#endif

  // Lecture et affichage des données EEPROM
  for (int i = 0; i < 9; i++) {
    inversePan[i] = EEPROM.read(EEPROM_INVERSE_PAN_START + i) == 1;
    inverseTilt[i] = EEPROM.read(EEPROM_INVERSE_TILT_START + i) == 1;
    typeCamAver[i] = EEPROM.read(EEPROM_TYPE_CAM_AVER_START + i) == 1;
  }

#if DEBUG
  // Affichage condensé
  for (int i = 0; i < 9; i++) {
    Serial.print(F("Caméra "));
    Serial.print(i + 1);
    Serial.print(F(" - Pan: "));
    Serial.print(inversePan[i] ? "Inverse" : "Normal");
    Serial.print(F(", Tilt: "));
    Serial.println(inverseTilt[i] ? "Inverse" : "Normal");
    Serial.print(F("Cam type: "));
    Serial.println(typeCamAver[i] ? "Aver" : "Sony");
  }
#endif

  Udp.stop();  // pour ne pas le laissé ouvert tout le temps
  delay(1000);
}


void loop() {
  // Vérification si le bouton est pressé pendant 5 secondes pour réinitialiser l'EEPROM
  if (isButtonPressedFor5Seconds()) {
#if DEBUG
    Serial.println(F("Réinitialisation de l'EEPROM..."));
#endif
    clearEEPROM();
#if DEBUG
    Serial.println(F("EEPROM réinitialisée. Veuillez redémarrer l'Arduino."));
#endif
    delay(2000);
    asm volatile("  jmp 0");
  }



  // affichage en boucle pendant 30 secondes des infos réseau de l'arduino
  if (!cycleComplete) {
    unsigned long currentTime = millis();

    // Gestion de l'affichage réseau pendant 30 secondes
    if (currentTime - startTime < 30000) {
      static unsigned long lastUpdateTime = 0;
      static int currentStep = 0;

      // Mise à jour toutes les 3 secondes
      if (currentTime - lastUpdateTime >= 3000) {
        lastUpdateTime = currentTime;

        lcd.clear();
        lcd.setCursor(0, 0);

        switch (currentStep) {
          case 0:  // Afficher l'adresse IP
            lcd.print(isDHCPEnabled ? "DHCP: ON      IP" : "DHCP: OFF     IP");
            lcd.setCursor(0, 1);
            lcd.print(Ethernet.localIP());
            break;
          case 1:  // Afficher le masque de sous-réseau
            lcd.print(isDHCPEnabled ? "DHCP: ON  MASQUE" : "DHCP: OFF MASQUE");
            lcd.setCursor(0, 1);
            lcd.print(Ethernet.subnetMask());
            break;
          case 2:  // Afficher la passerelle
            lcd.print(isDHCPEnabled ? "DHCP: ON      GW" : "DHCP: OFF     GW");
            lcd.setCursor(0, 1);
            lcd.print(Ethernet.gatewayIP());
            break;
        }
        // Passer à l'étape suivante
        currentStep = (currentStep + 1) % 3;
      }
    } else {
      // Fin du cycle de 30 secondes
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Bienvenue :-)");
      delay(3000);  // Pause finale avant d'effacer
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Cam");
      lcd.setCursor(4, 0);
      lcd.print(choixCamActuel);
      lcd.setCursor(7, 0);
      lcd.print("Pos");
      lcd.setCursor(14, 0);
      lcd.print(nouvelEtatAF);
      lcd.setCursor(0, 1);
      lcd.print("Zoom");
      lcd.setCursor(5, 1);
      lcd.print(zoomSpeed);
      cycleComplete = true;  // Marquer la fin du cycle
    }
  }



  if (digitalRead(btnsetup) == LOW) {
    delay(200);
    if (!btnSetupPressed) {  // Si le bouton vient d'être pressé
      btnSetupPressed = true;

      if (!inSetupMenu) {
        // Entrer dans le menu de configuration
        inSetupMenu = true;
        setupMenuState = 0;  // Réinitialiser l'état du menu
      } else {
        // Passer à l'état suivant dans le menu
        setupMenuState = (setupMenuState + 1) % 3;
      }

      afficherSetupMenu();  // Met à jour l'écran en fonction de l'état
    }
  } else {
    btnSetupPressed = false;
  }

  // Gestion du bouton btnSortieMenu
  if (digitalRead(btnsortiemenu) == LOW) {
    if (!btnSortieMenuPressed) {  // Si le bouton vient d'être pressé
      btnSortieMenuPressed = true;

      if (inSetupMenu) {
        // Sortir du menu de configuration et restaurer l'écran
        inSetupMenu = false;
        afficherEtatCamSelectionnee();
        lcd.setCursor(0, 1);
        lcd.print("        ");  // Efface la ligne pour éviter des restes
        lcd.setCursor(0, 1);
        if (currentType == ZOOM) {
          lcd.print("Zoom ");
          lcd.setCursor(5, 1);
          lcd.print(zoomSpeed);  // Affiche la vitesse actuelle du Zoom
        } else {
          lcd.print("Focus ");
          lcd.setCursor(6, 1);
          lcd.print(focusSpeed);  // Affiche la vitesse actuelle du Focus
        }
        afficheEtatIrisManuAuto();
        afficheMemIrisValue();
        lcd.setCursor(10, 1);
        lcd.print("  ");
      }
    }
  } else {
    btnSortieMenuPressed = false;
  }


  // Lire l'état du bouton pour reboot
  bool currentButtonState = digitalRead(btnF3) == LOW;  // Bouton appuyé (PULLUP)

  // Vérifier si le bouton est pressé
  if (currentButtonState && !isButtonPressedF3) {
    // Premier appui, on marque le temps
    lastButtonPressF3 = millis();
    isButtonPressedF3 = true;
  }

  // Vérifier si le bouton est maintenu enfoncé pendant 3 secondes
  if (isButtonPressedF3 && currentButtonState && (millis() - lastButtonPressF3 >= 3000)) {
#if DEBUG
    // Appui maintenu pendant 3 secondes, redémarrage
    Serial.println(F("Rebooting Arduino..."));
#endif
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Reboot dans 3s");
    delay(1000);
    lcd.setCursor(0, 0);
    lcd.print("Reboot dans 2s");
    delay(1000);
    lcd.setCursor(0, 0);
    lcd.print("Reboot dans 1s");
    delay(1000);
    // Activer le Watchdog Timer pour un redémarrage immédiat
    wdt_enable(WDTO_15MS);  // Configure le WDT pour un redémarrage rapide (15ms)

    // Ne rien faire après avoir activé le WDT (l'Arduino redémarrera)
    while (true) {
      // Boucle infinie jusqu'au redémarrage
    }
  }

  // Si le bouton est relâché, on réinitialise le flag
  if (!currentButtonState && isButtonPressedF3) {
    isButtonPressedF3 = false;
  }

  // Lecture des valeurs du joystick
  tiltValue = analogRead(tiltPin);
  panValue = analogRead(panPin);

  // Calcul des vitesses
  tiltSpeed = map(constrain(abs(tiltValue - (neutralMin + neutralMax) / 2), 0, 513), 0, 513, 0, 24);
  panSpeed = map(constrain(abs(panValue - (neutralMin + neutralMax) / 2), 0, 513), 0, 513, 0, 24);

  // Détection de direction
  byte tiltDirection = (tiltValue < neutralMin) ? 0x01 : (tiltValue > neutralMax) ? 0x02 : 0x03;
  byte panDirection = (panValue < neutralMin) ? 0x01 : (panValue > neutralMax) ? 0x02 : 0x03;

  // Appliquer les inversions
  if (inversePan[choixCamActuel - 1]) {
    panDirection = (panDirection == 0x01) ? 0x02 : (panDirection == 0x02) ? 0x01 : 0x03;
  }
  if (inverseTilt[choixCamActuel - 1]) {
    tiltDirection = (tiltDirection == 0x01) ? 0x02 : (tiltDirection == 0x02) ? 0x01 : 0x03;
  }

  // Vérification des changements et envoi des commandes
  if (tiltDirection != 0x03 || panDirection != 0x03) {  // Si on bouge le joystick
    sendMoveCommand(panSpeed, tiltSpeed, panDirection, tiltDirection);
    lastTiltSpeed = tiltSpeed;
    lastPanSpeed = panSpeed;
  } else if (lastTiltSpeed != 0x03 || lastPanSpeed != 0x03) {  // Si on relâche le joystick
    sendStopCommand();
    lastTiltSpeed = 0x03;
    lastPanSpeed = 0x03;

#if DEBUG
    Serial.println(F("Commande STOP envoyée"));
#endif
  }

  // Gestion du bouton speed
  if (digitalRead(btnspeed) == LOW) {
    delay(200);  // Anti-rebond
    currentType = (currentType == ZOOM) ? FOCUS : ZOOM;
#if DEBUG
    Serial.println(currentType == ZOOM ? "Mode: Réglage de la vitesse du zoom" : "Mode: Réglage de la vitesse du focus");
#endif
    // Affiche le mode actif et sa vitesse
    lcd.setCursor(0, 1);
    lcd.print("          ");  // Efface la ligne pour éviter des restes
    lcd.setCursor(0, 1);
    if (currentType == ZOOM) {
      lcd.print("Zoom ");
      lcd.setCursor(5, 1);
      lcd.print(zoomSpeed);  // Affiche la vitesse actuelle du Zoom
    } else {
      lcd.print("Focus ");
      lcd.setCursor(6, 1);
      lcd.print(focusSpeed);  // Affiche la vitesse actuelle du Focus
    }
  }

  // Gestion des boutons + et - pour ajuster la vitesse
  if (digitalRead(btnplus) == LOW) {
    adjustSpeed(1);  // Augmente la vitesse
    delay(200);      // Anti-rebond
  }
  if (digitalRead(btnmoins) == LOW) {
    adjustSpeed(-1);  // Diminue la vitesse
    delay(200);       // Anti-rebond
  }

  // Gestion des boutons zoom et focus individuellement
  checkButton(pluszoom, lastPlusZoomState, "Zoom Tele Start", "Zoom Stop", zoomSpeed);
  checkButton(moinszoom, lastMoinsZoomState, "Zoom Wide Start", "Zoom Stop", zoomSpeed);
  checkButton(plusfocus, lastPlusFocusState, "Focus Near Start", "Focus Stop", focusSpeed);
  checkButton(moinsfocus, lastMoinsFocusState, "Focus Far Start", "Focus Stop", focusSpeed);


  delay(50);  // Petite pause pour éviter les commandes trop fréquentes

  unsigned long currentMillis = millis();  // Temps écoulé depuis le démarrage

  // Bouton choixcam : passer en mode choix de caméra
  if (digitalRead(choixcam) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
    modePresetCam = false;
    modeChoixCam = true;
    attenteChoixCamera = true;
#if DEBUG
    Serial.println(F("Mode choix de caméra activé."));
#endif
    lastButtonPress = currentMillis;
  }

  // Bouton presetcam : passer en mode presetcam
  if (digitalRead(presetcam) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
    modeChoixCam = false;
    modePresetCam = true;
#if DEBUG
    Serial.println(F("Mode choix de caméra activé."));
#endif
    lastButtonPress = currentMillis;
  }


  // Gérer la sélection de la caméra en mode choixcam
  if (modeChoixCam && attenteChoixCamera) {
    if (digitalRead(unpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      choixCamActuel = 1;
      #if DEBUG
      Serial.println(F("Caméra 1 sélectionnée."));
      #endif
      readCameraConfig(choixCamActuel);
      modeChoixCam = false;
      modePresetCam = true;  // Passer automatiquement en mode presetcam
      #if DEBUG
      Serial.println(F("Mode presetcam activé."));
      #endif
      lastButtonPress = currentMillis;
      // Met à jour l'affichage pour refléter l'état actuel de la caméra
      afficherEtatCamSelectionnee();
      afficheEtatIrisManuAuto();
      afficheMemIrisValue();
      /*      lcd.setCursor(10, 1);
      lcd.print("  ");*/

    } else if (digitalRead(deuxpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      choixCamActuel = 2;
      #if DEBUG
      Serial.println(F("Caméra 2 sélectionnée."));
      #endif
      readCameraConfig(choixCamActuel);
      modeChoixCam = false;
      modePresetCam = true;  // Passer automatiquement en mode presetcam
      #if DEBUG
      Serial.println(F("Mode presetcam activé."));
      #endif
      lastButtonPress = currentMillis;
      // Met à jour l'affichage pour refléter l'état actuel de la caméra
      afficherEtatCamSelectionnee();
      afficheEtatIrisManuAuto();
      afficheMemIrisValue();
    } else if (digitalRead(troispad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      choixCamActuel = 3;
      #if DEBUG
      Serial.println(F("Caméra 3 sélectionnée."));
      #endif
      readCameraConfig(choixCamActuel);
      modeChoixCam = false;
      modePresetCam = true;  // Passer automatiquement en mode presetcam
      #if DEBUG
      Serial.println(F("Mode presetcam activé."));
      #endif
      lastButtonPress = currentMillis;
      // Met à jour l'affichage pour refléter l'état actuel de la caméra
      afficherEtatCamSelectionnee();
      afficheEtatIrisManuAuto();
      afficheMemIrisValue();
    } else if (digitalRead(quatrepad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      choixCamActuel = 4;
      #if DEBUG
      Serial.println(F("Caméra 4 sélectionnée."));
      #endif
      readCameraConfig(choixCamActuel);
      modeChoixCam = false;
      modePresetCam = true;  // Passer automatiquement en mode presetcam
      #if DEBUG
      Serial.println(F("Mode presetcam activé."));
      #endif
      lastButtonPress = currentMillis;
      // Met à jour l'affichage pour refléter l'état actuel de la caméra
      afficherEtatCamSelectionnee();
      afficheEtatIrisManuAuto();
      afficheMemIrisValue();
    } else if (digitalRead(cinqpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      choixCamActuel = 5;
#if DEBUG
      Serial.println(F("Caméra 5 sélectionnée."));
#endif
      readCameraConfig(choixCamActuel);
      modeChoixCam = false;
      modePresetCam = true;  // Passer automatiquement en mode presetcam
#if DEBUG
      Serial.println(F("Mode presetcam activé."));
#endif
      lastButtonPress = currentMillis;
      // Met à jour l'affichage pour refléter l'état actuel de la caméra
      afficherEtatCamSelectionnee();
      afficheEtatIrisManuAuto();
      afficheMemIrisValue();
    } else if (digitalRead(sixpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      choixCamActuel = 6;
#if DEBUG
      Serial.println(F("Caméra 6 sélectionnée."));
#endif
      readCameraConfig(choixCamActuel);
      modeChoixCam = false;
      modePresetCam = true;  // Passer automatiquement en mode presetcam
#if DEBUG
      Serial.println(F("Mode presetcam activé."));
#endif
      lastButtonPress = currentMillis;
      // Met à jour l'affichage pour refléter l'état actuel de la caméra
      afficherEtatCamSelectionnee();
      afficheEtatIrisManuAuto();
      afficheMemIrisValue();
    } else if (digitalRead(septpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      choixCamActuel = 7;
#if DEBUG
      Serial.println(F("Caméra 7 sélectionnée."));
#endif
      readCameraConfig(choixCamActuel);
      modeChoixCam = false;
      modePresetCam = true;  // Passer automatiquement en mode presetcam
#if DEBUG
      Serial.println(F("Mode presetcam activé."));
#endif
      lastButtonPress = currentMillis;
      // Met à jour l'affichage pour refléter l'état actuel de la caméra
      afficherEtatCamSelectionnee();
      afficheEtatIrisManuAuto();
      afficheMemIrisValue();
    } else if (digitalRead(huitpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      choixCamActuel = 8;
#if DEBUG
      Serial.println(F("Caméra 8 sélectionnée."));
#endif
      readCameraConfig(choixCamActuel);
      modeChoixCam = false;
      modePresetCam = true;  // Passer automatiquement en mode presetcam
#if DEBUG
      Serial.println(F("Mode presetcam activé."));
#endif
      lastButtonPress = currentMillis;
      // Met à jour l'affichage pour refléter l'état actuel de la caméra
      afficherEtatCamSelectionnee();
      afficheEtatIrisManuAuto();
      afficheMemIrisValue();
    } else if (digitalRead(neufpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      choixCamActuel = 9;
#if DEBUG
      Serial.println(F("Caméra 9 sélectionnée."));
#endif
      readCameraConfig(choixCamActuel);
      modeChoixCam = false;
      modePresetCam = true;  // Passer automatiquement en mode presetcam
#if DEBUG
      Serial.println(F("Mode presetcam activé."));
#endif
      lastButtonPress = currentMillis;
      // Met à jour l'affichage pour refléter l'état actuel de la caméra
      afficherEtatCamSelectionnee();
      afficheEtatIrisManuAuto();
      afficheMemIrisValue();
    }
    // Quand on appuie sur le bouton 0 (escape), on quitte le mode choixcam
    else if (digitalRead(zeropad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      modeChoixCam = false;
      modePresetCam = true;  // Retour au mode presetcam
#if DEBUG
      Serial.println(F("Retour au mode presetcam."));
#endif
      lastButtonPress = currentMillis;
    }
  }


  // Gérer les presets en mode presetcam
  if (modePresetCam) {

    // Détection d'un appui sur unpad
    if (digitalRead(unpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      // Si c'est le début de l'appui, enregistrer le moment où l'appui commence
      if (buttonPressStart1 == 0) {
        buttonPressStart1 = currentMillis;  // Enregistrer le moment de l'appui
      }

      // Vérifier si l'appui est long
      if ((currentMillis - buttonPressStart1) >= longPressDuration) {
        presetNumber = 1;
      // Si l'appui est long, effectuer l'action correspondante
        #if DEBUG
        Serial.println(F("Appui long détecté sur le bouton 1"));
        #endif
        savePreset(choixCamActuel, presetNumber);  // Action spécifique pour l'appui long
        lcd.setCursor(9, 0);
        lcd.print(" ");
        lcd.print(presetNumber);
        //        lcd.print("*");
        // Enregistrer le moment où l'étoile a été affichée
        presetStarTime = millis();
        starDisplayed = true;             // Indiquer que l'étoile a été affichée
        buttonPressStart1 = 0;            // Réinitialiser pour éviter que l'action se répète
        lastButtonPress = currentMillis;  // Mettre à jour le moment de l'appui long
      
    } else if (digitalRead(unpad) == HIGH && buttonPressStart1 > 0) {
      // Si le bouton est relâché, vérifier la durée de l'appui
      if ((currentMillis - buttonPressStart1) < longPressDuration) {
        // Si l'appui est court, effectuer l'action pour un appui court
        #if DEBUG
        Serial.println(F("Appui court détecté sur le bouton 1"));
        #endif
        presetNumber = 1;
        recallPreset(choixCamActuel, presetNumber);
      }
      buttonPressStart1 = 0;            // Réinitialiser la variable de suivi d'appui
      lastButtonPress = currentMillis;  // Mettre à jour le moment du dernier appui
    }
    }

    // Détection d'un appui sur deuxpad
    if (digitalRead(deuxpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      // Si c'est le début de l'appui, enregistrer le moment où l'appui commence
      if (buttonPressStart2 == 0) {
        buttonPressStart2 = currentMillis;  // Enregistrer le moment de l'appui
      }

      // Vérifier si l'appui est long
      if ((currentMillis - buttonPressStart2) >= longPressDuration) {
        presetNumber = 2;
      // Si l'appui est long, effectuer l'action correspondante
        #if DEBUG
        Serial.println(F("Appui long détecté sur le bouton 2"));
        #endif
        savePreset(choixCamActuel, presetNumber);  // Action spécifique pour l'appui long
        lcd.setCursor(9, 0);
        lcd.print(" ");
        lcd.print(presetNumber);
        //        lcd.print("*");
        // Enregistrer le moment où l'étoile a été affichée
        presetStarTime = millis();
        starDisplayed = true;             // Indiquer que l'étoile a été affichée
        buttonPressStart2 = 0;            // Réinitialiser pour éviter que l'action se répète
        lastButtonPress = currentMillis;  // Mettre à jour le moment de l'appui long
      
    } else if (digitalRead(deuxpad) == HIGH && buttonPressStart2 > 0) {
      // Si le bouton est relâché, vérifier la durée de l'appui
      if ((currentMillis - buttonPressStart2) < longPressDuration) {
        // Si l'appui est court, effectuer l'action pour un appui court
        #if DEBUG
        Serial.println(F("Appui court détecté sur le bouton 2"));
        #endif
        presetNumber = 2;
        recallPreset(choixCamActuel, presetNumber);
      }
      buttonPressStart2 = 0;            // Réinitialiser la variable de suivi d'appui
      lastButtonPress = currentMillis;  // Mettre à jour le moment du dernier appui
    }
    }

    // Détection d'un appui sur unpad
    if (digitalRead(troispad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      // Si c'est le début de l'appui, enregistrer le moment où l'appui commence
      if (buttonPressStart3 == 0) {
        buttonPressStart3 = currentMillis;  // Enregistrer le moment de l'appui
      }

      // Vérifier si l'appui est long
      if ((currentMillis - buttonPressStart3) >= longPressDuration) {
        presetNumber = 3;
      // Si l'appui est long, effectuer l'action correspondante
        #if DEBUG
        Serial.println(F("Appui long détecté sur le bouton 3"));
        #endif
        savePreset(choixCamActuel, presetNumber);  // Action spécifique pour l'appui long
        lcd.setCursor(9, 0);
        lcd.print(" ");
        lcd.print(presetNumber);
        //        lcd.print("*");
        // Enregistrer le moment où l'étoile a été affichée
        presetStarTime = millis();
        starDisplayed = true;             // Indiquer que l'étoile a été affichée
        buttonPressStart3 = 0;            // Réinitialiser pour éviter que l'action se répète
        lastButtonPress = currentMillis;  // Mettre à jour le moment de l'appui long
      
    } else if (digitalRead(troispad) == HIGH && buttonPressStart3 > 0) {
      // Si le bouton est relâché, vérifier la durée de l'appui
      if ((currentMillis - buttonPressStart3) < longPressDuration) {
        // Si l'appui est court, effectuer l'action pour un appui court
        #if DEBUG
        Serial.println(F("Appui court détecté sur le bouton 3"));
        #endif
        presetNumber = 3;
        recallPreset(choixCamActuel, presetNumber);
      }
      buttonPressStart1 = 0;            // Réinitialiser la variable de suivi d'appui
      lastButtonPress = currentMillis;  // Mettre à jour le moment du dernier appui
    }
    }

    // Détection d'un appui sur unpad
    if (digitalRead(quatrepad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      // Si c'est le début de l'appui, enregistrer le moment où l'appui commence
      if (buttonPressStart4 == 0) {
        buttonPressStart4 = currentMillis;  // Enregistrer le moment de l'appui
      }

      // Vérifier si l'appui est long
      if ((currentMillis - buttonPressStart4) >= longPressDuration) {
        presetNumber = 4;
      // Si l'appui est long, effectuer l'action correspondante
        #if DEBUG
        Serial.println(F("Appui long détecté sur le bouton 4"));
        #endif
        savePreset(choixCamActuel, presetNumber);  // Action spécifique pour l'appui long
        lcd.setCursor(9, 0);
        lcd.print(" ");
        lcd.print(presetNumber);
        //        lcd.print("*");
        // Enregistrer le moment où l'étoile a été affichée
        presetStarTime = millis();
        starDisplayed = true;             // Indiquer que l'étoile a été affichée
        buttonPressStart4 = 0;            // Réinitialiser pour éviter que l'action se répète
        lastButtonPress = currentMillis;  // Mettre à jour le moment de l'appui long
      
    } else if (digitalRead(quatrepad) == HIGH && buttonPressStart4 > 0) {
      // Si le bouton est relâché, vérifier la durée de l'appui
      if ((currentMillis - buttonPressStart4) < longPressDuration) {
        // Si l'appui est court, effectuer l'action pour un appui court
        #if DEBUG
        Serial.println(F("Appui court détecté sur le bouton 4"));
        #endif
        presetNumber = 4;
        recallPreset(choixCamActuel, presetNumber);
      }
      buttonPressStart4 = 0;            // Réinitialiser la variable de suivi d'appui
      lastButtonPress = currentMillis;  // Mettre à jour le moment du dernier appui
    }
    }

    // Détection d'un appui sur unpad
    if (digitalRead(cinqpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      // Si c'est le début de l'appui, enregistrer le moment où l'appui commence
      if (buttonPressStart5 == 0) {
        buttonPressStart5 = currentMillis;  // Enregistrer le moment de l'appui
      }

      // Vérifier si l'appui est long
      if ((currentMillis - buttonPressStart5) >= longPressDuration) {
        presetNumber = 5;
      // Si l'appui est long, effectuer l'action correspondante
        #if DEBUG
        Serial.println(F("Appui long détecté sur le bouton 5"));
        #endif
        savePreset(choixCamActuel, presetNumber);  // Action spécifique pour l'appui long
        lcd.setCursor(9, 0);
        lcd.print(" ");
        lcd.print(presetNumber);
        //        lcd.print("*");
        // Enregistrer le moment où l'étoile a été affichée
        presetStarTime = millis();
        starDisplayed = true;             // Indiquer que l'étoile a été affichée
        buttonPressStart5 = 0;            // Réinitialiser pour éviter que l'action se répète
        lastButtonPress = currentMillis;  // Mettre à jour le moment de l'appui long
      
    } else if (digitalRead(cinqpad) == HIGH && buttonPressStart5 > 0) {
      // Si le bouton est relâché, vérifier la durée de l'appui
      if ((currentMillis - buttonPressStart5) < longPressDuration) {
        // Si l'appui est court, effectuer l'action pour un appui court
        #if DEBUG
        Serial.println(F("Appui court détecté sur le bouton 5"));
        #endif
        presetNumber = 5;
        recallPreset(choixCamActuel, presetNumber);
      }
      buttonPressStart5 = 0;            // Réinitialiser la variable de suivi d'appui
      lastButtonPress = currentMillis;  // Mettre à jour le moment du dernier appui
      }
    }

    // Détection d'un appui sur unpad
    if (digitalRead(sixpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      // Si c'est le début de l'appui, enregistrer le moment où l'appui commence
      if (buttonPressStart6 == 0) {
        buttonPressStart6 = currentMillis;  // Enregistrer le moment de l'appui
      }

      // Vérifier si l'appui est long
      if ((currentMillis - buttonPressStart6) >= longPressDuration) {
        presetNumber = 6;
      // Si l'appui est long, effectuer l'action correspondante
        #if DEBUG
        Serial.println(F("Appui long détecté sur le bouton 6"));
        #endif
        savePreset(choixCamActuel, presetNumber);  // Action spécifique pour l'appui long
        lcd.setCursor(9, 0);
        lcd.print(" ");
        lcd.print(presetNumber);
        //        lcd.print("*");
        // Enregistrer le moment où l'étoile a été affichée
        presetStarTime = millis();
        starDisplayed = true;             // Indiquer que l'étoile a été affichée
        buttonPressStart6 = 0;            // Réinitialiser pour éviter que l'action se répète
        lastButtonPress = currentMillis;  // Mettre à jour le moment de l'appui long
      }  
    } else if (digitalRead(sixpad) == HIGH && buttonPressStart6 > 0) {
      // Si le bouton est relâché, vérifier la durée de l'appui
      if ((currentMillis - buttonPressStart6) < longPressDuration) {
        // Si l'appui est court, effectuer l'action pour un appui court
        #if DEBUG
        Serial.println(F("Appui court détecté sur le bouton 6"));
        #endif
        presetNumber = 6;
        recallPreset(choixCamActuel, presetNumber);
      }
      buttonPressStart6 = 0;            // Réinitialiser la variable de suivi d'appui
      lastButtonPress = currentMillis;  // Mettre à jour le moment du dernier appui
    }

    // Détection d'un appui sur unpad
    if (digitalRead(septpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      // Si c'est le début de l'appui, enregistrer le moment où l'appui commence
      if (buttonPressStart7 == 0) {
        buttonPressStart7 = currentMillis;  // Enregistrer le moment de l'appui
      }

      // Vérifier si l'appui est long
      if ((currentMillis - buttonPressStart1) >= longPressDuration) {
        presetNumber = 7;
      // Si l'appui est long, effectuer l'action correspondante
        #if DEBUG
        Serial.println(F("Appui long détecté sur le bouton 7"));
        #endif
        savePreset(choixCamActuel, presetNumber);  // Action spécifique pour l'appui long
        lcd.setCursor(9, 0);
        lcd.print(" ");
        lcd.print(presetNumber);
        //        lcd.print("*");
        // Enregistrer le moment où l'étoile a été affichée
        presetStarTime = millis();
        starDisplayed = true;             // Indiquer que l'étoile a été affichée
        buttonPressStart7 = 0;            // Réinitialiser pour éviter que l'action se répète
        lastButtonPress = currentMillis;  // Mettre à jour le moment de l'appui long
      } 
    } else if (digitalRead(septpad) == HIGH && buttonPressStart7 > 0) {
      // Si le bouton est relâché, vérifier la durée de l'appui
      if ((currentMillis - buttonPressStart7) < longPressDuration) {
        // Si l'appui est court, effectuer l'action pour un appui court
        #if DEBUG
        Serial.println(F("Appui court détecté sur le bouton 7"));
        #endif
        presetNumber = 7;
        recallPreset(choixCamActuel, presetNumber);
      }
      buttonPressStart7 = 0;            // Réinitialiser la variable de suivi d'appui
      lastButtonPress = currentMillis;  // Mettre à jour le moment du dernier appui
    }

    // Détection d'un appui sur unpad
    if (digitalRead(huitpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      // Si c'est le début de l'appui, enregistrer le moment où l'appui commence
      if (buttonPressStart8 == 0) {
        buttonPressStart8 = currentMillis;  // Enregistrer le moment de l'appui
      }

      // Vérifier si l'appui est long
      if ((currentMillis - buttonPressStart1) >= longPressDuration) {
        presetNumber = 8;
      // Si l'appui est long, effectuer l'action correspondante
        #if DEBUG
        Serial.println(F("Appui long détecté sur le bouton 8"));
        #endif
        savePreset(choixCamActuel, presetNumber);  // Action spécifique pour l'appui long
        lcd.setCursor(9, 0);
        lcd.print(" ");
        lcd.print(presetNumber);
        //        lcd.print("*");
        // Enregistrer le moment où l'étoile a été affichée
        presetStarTime = millis();
        starDisplayed = true;             // Indiquer que l'étoile a été affichée
        buttonPressStart8 = 0;            // Réinitialiser pour éviter que l'action se répète
        lastButtonPress = currentMillis;  // Mettre à jour le moment de l'appui long
      
    } else if (digitalRead(huitpad) == HIGH && buttonPressStart8 > 0) {
      // Si le bouton est relâché, vérifier la durée de l'appui
      if ((currentMillis - buttonPressStart8) < longPressDuration) {
        // Si l'appui est court, effectuer l'action pour un appui court
        #if DEBUG
        Serial.println(F("Appui court détecté sur le bouton 8"));
        #endif
        presetNumber = 8;
        recallPreset(choixCamActuel, presetNumber);
      }
      buttonPressStart8 = 0;            // Réinitialiser la variable de suivi d'appui
      lastButtonPress = currentMillis;  // Mettre à jour le moment du dernier appui
    }

    // Détection d'un appui sur unpad
    if (digitalRead(neufpad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
      // Si c'est le début de l'appui, enregistrer le moment où l'appui commence
      if (buttonPressStart9 == 0) {
        buttonPressStart1 = currentMillis;  // Enregistrer le moment de l'appui
      }

      // Vérifier si l'appui est long
      if ((currentMillis - buttonPressStart9) >= longPressDuration) {
        presetNumber = 9;
      // Si l'appui est long, effectuer l'action correspondante
        #if DEBUG
        Serial.println(F("Appui long détecté sur le bouton 9"));
        #endif
        savePreset(choixCamActuel, presetNumber);  // Action spécifique pour l'appui long
        lcd.setCursor(9, 0);
        lcd.print(" ");
        lcd.print(presetNumber);
        //        lcd.print("*");
        // Enregistrer le moment où l'étoile a été affichée
        presetStarTime = millis();
        starDisplayed = true;             // Indiquer que l'étoile a été affichée
        buttonPressStart9 = 0;            // Réinitialiser pour éviter que l'action se répète
        lastButtonPress = currentMillis;  // Mettre à jour le moment de l'appui long
      }
    } else if (digitalRead(neufpad) == HIGH && buttonPressStart9 > 0) {
      // Si le bouton est relâché, vérifier la durée de l'appui
      if ((currentMillis - buttonPressStart9) < longPressDuration) {
        // Si l'appui est court, effectuer l'action pour un appui court
        #if DEBUG
        Serial.println(F("Appui court détecté sur le bouton 9"));
        #endif
        presetNumber = 9;
        recallPreset(choixCamActuel, presetNumber);
      }
      buttonPressStart9 = 0;            // Réinitialiser la variable de suivi d'appui
      lastButtonPress = currentMillis;  // Mettre à jour le moment du dernier appui
    


    } else if (digitalRead(zeropad) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
#if DEBUG
      Serial.println(F("Aucun preset sélectionné."));
#endif
      lastButtonPress = currentMillis;
    }
  }
  }

  //vérifier si 3 secondes se sont écoulées pour suppression de l'étoile d'indiquation de preset enregistré
  if (starDisplayed && millis() - presetStarTime >= 3000) {
    // Effacer l'étoile après 3 secondes
    lcd.setCursor(11, 0);
    lcd.print(" ");         // Effacer le caractère étoile
    starDisplayed = false;  // Réinitialiser le flag
  }



  // Gestion des requêtes client via le serveur Ethernet
  EthernetClient client = server.available();
  if (client) {
    String request = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        request += c;
        if (c == '\n' && request.length() > 0) {
          // Vérifie les différents types de requêtes
          if (request.indexOf("GET /config") >= 0) {
            showConfigPage(client);
          } else if (request.indexOf("GET /save") >= 0) {
            handleSaveRequest(client, request);
          } else if (request.indexOf("GET /sauvegardeCameraConfig") >= 0) {
            handleSaveCameraConfig(client, request);  // Sauvegarde de la config de caméra
          } else if (request.indexOf("GET /camera") >= 0) {
            showConfigCamera(client);
          } else {
            showHomePage(client);
          }
          break;
        }
      }
    }
    client.stop();
  }



  if (digitalRead(togAF) == LOW) {
#if DEBUG
    Serial.println(F("Bouton commande appuyé !"));
#endif
    sendUDPViscaCommand(resetCompteurCommande, sizeof(resetCompteurCommande));
    delay(500);
    sendUDPViscaCommand(commandTGAF, sizeof(commandTGAF));
    delay(500);
    sendUDPViscaCommand(commandFBTGAF, sizeof(commandFBTGAF));
    delay(500);

    // Activer temporairement la réception UDP
    udpActive = true;
    lastButtonPressTime = millis();  // Enregistrer le moment du bouton pressé
  }

  // Gérer la réception UDP seulement si active
  if (udpActive) {
    if (millis() - lastButtonPressTime < udpActiveDuration) {
      // Code de réception UDP
      int packetSize = Udp.parsePacket();
      if (packetSize > 0) {
        Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);

        // Vérifications et affichage des informations sur le paquet
        if (packetSize >= 2) {
          IPAddress remoteIP = Udp.remoteIP();
          if (remoteIP[0] != 0 && remoteIP[3] != 0) {
#if DEBUG
            Serial.print(F("Paquet reçu (taille "));
            Serial.print(packetSize);
            Serial.println(F(" octets) :"));

            Serial.print(F("De l'adresse : "));
            Serial.print(remoteIP);
            Serial.print(F(" : "));
            Serial.println(Udp.remotePort());

            Serial.print(F("Contenu : "));
#endif
            bool contenuVide = true;
            for (int i = 0; i < packetSize; i++) {
              if (packetBuffer[i] > 0) contenuVide = false;
#if DEBUG
              if (packetBuffer[i] < 0x10) Serial.print(F("0"));
              Serial.print(packetBuffer[i], HEX);
              Serial.print(F(" "));
#endif
            }
#if DEBUG
            Serial.println();
#endif

            if (!contenuVide && packetBuffer[0] == 0x01 && packetBuffer[1] == 0x11) {
              nouvelEtatAF = packetBuffer[packetSize - 2];
              if (dernierEtatAF[choixCamActuel] != nouvelEtatAF) {
                dernierEtatAF[choixCamActuel] = nouvelEtatAF;
                etatAF[choixCamActuel] = nouvelEtatAF;
#if DEBUG
                Serial.print(F("État AF de la caméra "));
                Serial.print(choixCamActuel);
                Serial.println(nouvelEtatAF == 0x02 ? "AF ON" : "MF OFF");
                afficherEtatAF(nouvelEtatAF);
#endif
                lcd.setCursor(14, 0);
                lcd.print(nouvelEtatAF == 0x02 ? "AF" : "MF");
              }
            }
          }
        }
      }
    } else {
      udpActive = false;  // Désactiver la réception UDP après le délai
    }
  }


  if (digitalRead(btnF1) == LOW) {  // Bouton appuyé
#if DEBUG
    Serial.println(F("Bouton IRIS Auto commande appuyé !"));
    Serial.print(F("État actuel de l'iris pour la caméra "));
    Serial.print(choixCamActuel);
    Serial.print(F(": "));
#endif
    // Utilisation de l'index corrigé
    int index = choixCamActuel - 1;
#if DEBUG
    Serial.println(etatIrisAuto[index], HEX);

    for (int i = 0; i < 9; i++) {
      Serial.print(F("Indice "));
      Serial.print(i);
      Serial.print(F(": "));
      Serial.println(dernierEtatIrisAuto[i], HEX);  // Affiche la valeur en hexadécimal
    }
#endif

    // Vérification de l'état de l'iris
    if (etatIrisAuto[index] == 0x02 || etatIrisAuto[index] == 0x03) {
      gererCommandeIrisAuto(etatIrisAuto[index]);
    } else {
#if DEBUG
      Serial.println(F("Erreur : état actuel de l'iris invalide, aucune commande envoyée."));
#endif
    }

    // Activer temporairement la réception UDP
    udpActive2 = true;
    lastButtonPressTime = millis();  // Enregistrer le moment du bouton pressé

    // Attendre un moment pour éviter les rebonds du bouton
    delay(200);
  }



  if (udpActive2) {
    if (millis() - lastButtonPressTime < udpActiveDuration2) {
      // Réception UDP
      int packetSize = Udp.parsePacket();
      if (packetSize > 0) {
        Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);

#if DEBUG
        // Affichage du paquet reçu pour debug
        Serial.print(F("Réponse reçue en hexadécimal : "));
        for (int i = 0; i < packetSize; i++) {
          if (packetBuffer[i] < 0x10) Serial.print(F("0"));
          Serial.print(packetBuffer[i], HEX);
          Serial.print(F(" "));
        }
        Serial.println();
#endif

        // Vérifications de base sur le paquet
        if (packetSize >= 4 && packetBuffer[packetSize - 1] == 0xFF) {
          uint8_t avantDernierOctet = packetBuffer[packetSize - 2];
#if DEBUG
          Serial.print(F("Avant-dernier octet : "));
          Serial.println(avantDernierOctet, HEX);
#endif

          // Vérification du type de caméra
          bool etatValide = false;

          // Vérifications selon le type de caméra
          if (avantDernierOctet == 0x00 || avantDernierOctet == 0x0B) {
            etatValide = true;
            nouvelEtatIrisAuto = (avantDernierOctet == 0x00) ? 0x02 : 0x03;
          }


          // Mise à jour de l'état
          if (etatValide) {
#if DEBUG
            Serial.print(F("Nouvel état Iris Auto : "));
            Serial.println(nouvelEtatIrisAuto, HEX);
#endif

            // Mise à jour en utilisant l'index corrigé
            int index = choixCamActuel - 1;
            if (dernierEtatIrisAuto[index] != nouvelEtatIrisAuto) {
              dernierEtatIrisAuto[index] = nouvelEtatIrisAuto;
              etatIrisAuto[index] = nouvelEtatIrisAuto;  // Mise à jour ici
#if DEBUG
              Serial.print(F("État Iris Auto mis à jour pour caméra "));
              Serial.println(choixCamActuel);
#endif
            } else {
#if DEBUG
              Serial.println(F("Aucun changement dans l'état Iris Auto."));
#endif
            }
          } else {
#if DEBUG
            Serial.println(F("Valeur non pertinente pour le type de caméra."));
#endif
          }
        } else {
#if DEBUG
          Serial.println(F("Paquet invalide ou structure non reconnue."));
#endif
        }
        afficheEtatIrisManuAuto();
      }
    } else {
      udpActive2 = false;  // Désactivation après le délai
    }
  }

  if (digitalRead(irisclose) == LOW)  // Bouton irisclose pressé
  {
    // Vérifie si la caméra actuelle est de type Aver ou Sony
    sendViscaCommandIris(CMD_IRIS_CLOSE, sizeof(CMD_IRIS_CLOSE));
    udpActive3 = true;
    queryIrisState();
    lastButtonPressTime = millis();  // Enregistrer le moment du bouton pressé
    delay(200);
    #if DEBUG
    Serial.print(F("Etat Iris en cours :"));
    Serial.println(etatIrisEnCours[choixCamActuel - 1]);
    #endif
    afficheMemIrisValue();
  }

  if (digitalRead(irisopen) == LOW)  // Bouton irisopen pressé
  {
    sendViscaCommandIris(CMD_IRIS_OPEN, sizeof(CMD_IRIS_OPEN));
    udpActive3 = true;
    queryIrisState();
    lastButtonPressTime = millis();  // Enregistrer le moment du bouton pressé
    delay(200);
    #if DEBUG
    Serial.print(F("Etat Iris en cours :"));
    Serial.println(etatIrisEnCours[choixCamActuel - 1]);
    #endif
    afficheMemIrisValue();
  }
}

//affiche la page web acueil avec les 2 boutons
void showHomePage(EthernetClient client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println();

  client.println("<html><body>");
  client.println("<br>");
  client.println("<h1 style='text-align:center;'>Télécommande PTZ VISCA OVER IP by HawaienProduction</h1>");
  client.println("<br>");
  client.println("<hr>");
  client.println("<br>");

  client.println("<form style='text-align:center;' action='/config' method='get'>");
  client.println("<button type='submit'>Configuration IP télécommande</button>");
  client.println("</form>");
  client.println("<br>");
  client.println("<form style='text-align:center;' action='/camera' method='get'>");
  client.println("<button type='submit'>Configuration IP Caméras</button>");
  client.println("</form>");
  client.println("</body></html>");
}

//affiche la page de gestion des ip des caméras à piloter
void showConfigCamera(EthernetClient client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println();

  client.println("<html><body>");
  client.println("<h1 style='text-align:center;'>Configuration IP Caméras</h1><br><hr><br>");

  client.println("<form action='/sauvegardeCameraConfig' method='get'>");

  for (int i = 0; i < 9; i++) {
    IPAddress camIP = readIPAddressFromEEPROM(CAMERA_IP_ADDR_START + (i * CAMERA_IP_OFFSET));
    uint16_t camPort = readPortFromEEPROM(CAMERA_PORT_ADDR_START + (i * CAMERA_PORT_OFFSET));

    client.print("<h3>Caméra ");
    client.print(i + 1);
    client.println("</h3>");

    client.print("IP Caméra ");
    client.print(i + 1);
    client.print(": <input type='text' name='ip");
    client.print(i);
    client.print("' value='");
    client.print(ipToString(camIP));
    client.println("'><br>");

    client.print("Port Caméra ");
    client.print(i + 1);
    client.print(": <input type='text' name='port");
    client.print(i);
    client.print("' value='");
    client.print(camPort);
    client.println("'><br><br>");

    client.print("<div><label>Caméra ");
    client.print(i + 1);
    client.println("</label><br>");

    // Case à cocher PAN
    client.print("<input type='checkbox' name='pan");
    client.print(i + 1);
    client.print("' value='on' ");
    if (inversePan[i]) client.print("checked");
    client.println("> Inverser PAN<br>");

    // Case à cocher TILT
    client.print("<input type='checkbox' name='tilt");
    client.print(i + 1);
    client.print("' value='on' ");
    if (inverseTilt[i]) client.print("checked");
    client.println("> Inverser TILT<br>");

    // Case à cocher Caméra Aver
    client.print("<input type='checkbox' name='typeCamAver");
    client.print(i + 1);
    client.print("' value='on' ");
    if (typeCamAver[i]) client.print("checked");
    client.println("> Caméra Aver<br></div><br>");
  }

  client.println("<input type='submit' value='Enregistrer'>");
  client.println("</form>");
  client.println("<br><form action='/' method='get'><button type='submit'>Page Accueil</button></form>");
  client.println("</body></html>");
}

//affiche la pag de config reseau de l'arduino
void showConfigPage(EthernetClient client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println();

  client.println("<html><body>");
  client.println("<br>");
  client.println("<h1 style='text-align:center;'>Configuration Réseau</h1>");
  client.println("<br>");
  client.println("<hr>");
  client.println("<br>");

  // Afficher les informations réseau actuelles
  client.println("<p>Adresse IP actuelle : ");
  client.print(Ethernet.localIP());
  //client.println("</p>");

  client.println("<p>Masque de sous-réseau actuel : ");
  client.print(Ethernet.subnetMask());
  //client.println("</p>");

  client.println("<p>Passerelle actuelle : ");
  client.print(Ethernet.gatewayIP());
  //  client.println("</p>");
  //  client.println("<p></p>");

  client.println("<p>DNS actuel : ");
  client.print(Ethernet.dnsServerIP());
  client.println("</p>");
  client.println("<p></p>");

  client.println("<br>");
  client.println("<hr>");
  client.println("<br>");

  client.println("<form action='/save' method='get'>");

  client.print("DHCP : <input type='checkbox' name='dhcp' ");
  if (readDHCPFromEEPROM()) client.print("checked");
  client.println("><br>");
  client.println("<br>");
  client.print("Adresse IP :&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type='text' name='ip' value='");
  client.print(ipToString(readIPAddressFromEEPROM(IP_ADDR_START)));
  client.println("'><br>");

  client.print("Masque de sous-réseau : <input type='text' name='mask' value='");
  client.print(ipToString(readIPAddressFromEEPROM(MASK_ADDR_START)));
  client.println("'><br>");

  client.print("Passerelle :&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type='text' name='gateway' value='");
  client.print(ipToString(readIPAddressFromEEPROM(GATEWAY_ADDR_START)));
  client.println("'><br>");

  client.print("DNS :&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type='text' name='dns' value='");
  client.print(ipToString(readIPAddressFromEEPROM(DNS_ADDR_START)));
  client.println("'><br>");
  client.println("<br>");

  client.println("<input type='submit' value='Enregistrer'>");
  client.println("</form>");

  client.println("<br>");
  client.println("<hr>");
  client.println("<br>");

  client.println("<form style='text-align:center;' action='/' method='get'>");
  client.println("<button type='submit'>Page Accueil</button>");
  client.println("</form>");


  client.println("</body></html>");
}

//gere la sauvegarde dans EEPROM du reseau de l'arduino
void handleSaveRequest(EthernetClient client, String request) {
  IPAddress ip, mask, gateway, dns;
  bool dhcp = request.indexOf("dhcp=on") != -1;

  if (extractIPAddress(request, "ip=", ip)) writeIPAddressToEEPROM(ip, IP_ADDR_START);
  if (extractIPAddress(request, "mask=", mask)) writeIPAddressToEEPROM(mask, MASK_ADDR_START);
  if (extractIPAddress(request, "gateway=", gateway)) writeIPAddressToEEPROM(gateway, GATEWAY_ADDR_START);
  if (extractIPAddress(request, "dns=", dns)) writeIPAddressToEEPROM(dns, DNS_ADDR_START);
  writeDHCPToEEPROM(dhcp);

  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println();
  client.println("<html><body><h1>Configuration enregistrée</h1>");
  client.println("<br>");
  client.print("<p>Veuillez redémarrer l'appareil et ouvrir une nouvelle page à l'adresse IP affichée sur l'écran LCD de l'appareil au redémarrage.</p>");
  client.println("</body></html>");
}

//gere la sauvegarde dans EEPROM des IP et port des caméras
void handleSaveCameraConfig(EthernetClient client, String request) {
  for (int i = 0; i < 9; i++) {
    IPAddress camIP;
    uint16_t camPort;
    // Traitement des paramètres dans la requête

    String panParam = "pan" + String(i + 1) + "=";
    String tiltParam = "tilt" + String(i + 1) + "=";
    String typeCaamAverParam = "typeCamAver" + String(i + 1) + "=";

    if (extractIPAddress(request, "ip" + String(i) + "=", camIP)) {
      writeIPAddressToEEPROM(camIP, CAMERA_IP_ADDR_START + (i * CAMERA_IP_OFFSET));
    }
    int portIndex = request.indexOf("port" + String(i) + "=");
    if (portIndex != -1) {
      camPort = request.substring(portIndex + 5 + String(i).length()).toInt();
      writePortToEEPROM(camPort, CAMERA_PORT_ADDR_START + (i * CAMERA_PORT_OFFSET));
    }

    if (request.indexOf("pan" + String(i + 1) + "=on") != -1) {
      inversePan[i] = true;
    } else {
      inversePan[i] = false;
    }

    if (request.indexOf("tilt" + String(i + 1) + "=on") != -1) {
      inverseTilt[i] = true;
    } else {
      inverseTilt[i] = false;
    }

    if (request.indexOf("typeCamAver" + String(i + 1) + "=on") != -1) {
      typeCamAver[i] = true;
    } else {
      typeCamAver[i] = false;
    }

    // Sauvegarde des options dans l'EEPROM
    writeInvertPanToEEPROM(inversePan[i], EEPROM_INVERSE_PAN_START + i);
    writeInvertTiltToEEPROM(inverseTilt[i], EEPROM_INVERSE_TILT_START + i);
    writeTypeCamAverToEEPROM(typeCamAver[i], EEPROM_TYPE_CAM_AVER_START + i);
  }

  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println();
  client.println("<html><body><h1>Configuration des caméras enregistrée</h1><br>");
  client.println("<p>Les informations de configuration des caméras ont été enregistrées avec succès.</p>");
  client.println("<form action='/' method='get'><button type='submit'>Page Accueil</button></form>");
  client.println("</body></html>");
}

String ipToString(IPAddress ip) {
  return String(ip[0]) + "." + String(ip[1]) + "." + String(ip[2]) + "." + String(ip[3]);
}

//lit l'ip et le port des cameras dans EEPROM pour affichage
void readCameraConfig(int choixCamActuel) {
  int ipStartAddr = CAMERA_IP_ADDR_START + ((choixCamActuel * CAMERA_IP_OFFSET) - 4);
  cameraIP = readIPAddressFromEEPROM(ipStartAddr);
#if DEBUG
  Serial.print(F("Camera "));
  Serial.print(choixCamActuel);
  Serial.print(F(" IP: "));
  Serial.println(cameraIP);
#endif

  int portAddr = CAMERA_PORT_ADDR_START + ((choixCamActuel * CAMERA_PORT_OFFSET) - 2);
  cameraPort = readPortFromEEPROM(portAddr);
#if DEBUG
  Serial.print(F("Camera "));
  Serial.print(choixCamActuel);
  Serial.print(F(" Port: "));
  Serial.println(cameraPort);
#endif
}

//fonction pour le rappel des preset
void recallPreset(int choixCamActuel, int presetNumber) {
#if DEBUG
  Serial.println(F("Entering recallPreset function."));
#endif
  if (choixCamActuel == -1) {
#if DEBUG
    Serial.println(F("No camera selected, exiting recallPreset."));
#endif
    return;
  }
  Udp.begin(cameraPort);
  byte presetHex = presetNumber - 1;
#if DEBUG
  Serial.print(F("Preset hex value: "));
  Serial.println(presetHex, HEX);
#endif

  byte viscaPresetCommand[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x02, 0x81, 0x01, 0x04, 0x3F, 0x02, presetHex, 0xFF };
#if DEBUG
  Serial.println(F("Sending VISCA Reset command..."));
#endif
  if (cameraIP == IPAddress(0, 0, 0, 0) || cameraPort == 0) {
#if DEBUG
    Serial.println(F("Error: Invalid Camera IP or Port!"));
#endif
    return;
  }

  // Sending reset command to camera
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaResetCommand, sizeof(viscaResetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("Reset command sent. Sending recall preset command..."));
#endif

  // Sending recall preset command
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaPresetCommand, sizeof(viscaPresetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("Recall preset command sent. Listening for responses..."));
#endif

  // Listen for responses for a short period
  unsigned long startTime = millis();
  bool presetAcknowledged = false;

  while (millis() - startTime < 1000) {  // Attendre jusqu'à 1 seconde
    int packetSize = Udp.parsePacket();
    if (packetSize) {
      byte incomingPacket[UDP_TX_PACKET_MAX_SIZE];
      Udp.read(incomingPacket, packetSize);

// Afficher la réponse reçue en hexadécimal pour le débogage
#if DEBUG
      Serial.print(F("Response from camera (in hex): "));
      for (int i = 0; i < packetSize; i++) {
        if (incomingPacket[i] < 0x10) {
          Serial.print(F("0"));
        }
        Serial.print(incomingPacket[i], HEX);
        Serial.print(F(" "));
      }
      Serial.println();
#endif

      // Vérifier si c'est la réponse attendue
      if (packetSize >= 10 && incomingPacket[8] == 0x90 && incomingPacket[9] == 0x51) {
        presetAcknowledged = true;
        break;  // On arrête de chercher dès qu'on trouve la bonne réponse
      }
    }
  }

  if (presetAcknowledged) {
#if DEBUG
    Serial.println(F("Preset change confirmed by camera."));
#endif
    lcd.setCursor(9, 0);
    lcd.print(" ");
    lcd.print(presetNumber);
  }
}

//fonction pour la sauvegarde des presets
void savePreset(int choixCamActuel, int presetNumber) {
#if DEBUG
  Serial.println(F("Entering savePreset function."));
#endif

  if (choixCamActuel == -1) {
#if DEBUG
    Serial.println(F("No camera selected, exiting savePreset."));
#endif
    return;
  }

  Udp.begin(cameraPort);
  byte presetHex = presetNumber - 1;  // Conversion du numéro de preset en hexadécimal (de 0x00 à 0x08)
#if DEBUG
  Serial.print(F("Preset hex value: "));
  Serial.println(presetHex, HEX);
#endif

  byte viscaSavePresetCommand[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x01, 0x81, 0x01, 0x04, 0x3F, 0x01, presetHex, 0xFF };

#if DEBUG
  Serial.println(F("Sending VISCA Reset command..."));
#endif
  if (cameraIP == IPAddress(0, 0, 0, 0) || cameraPort == 0) {
#if DEBUG
    Serial.println(F("Error: Invalid Camera IP or Port!"));
#endif
    return;
  }

  // Sending reset command to camera
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaResetCommand, sizeof(viscaResetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("Reset command sent. Sending recall preset command..."));
#endif

  // Sending recall preset command
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaSavePresetCommand, sizeof(viscaSavePresetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("save preset command sent. Listening for responses..."));
#endif

  // Listen for responses for a short period
  unsigned long startTime = millis();
  bool presetAcknowledged = false;

  while (millis() - startTime < 1000) {  // Attendre jusqu'à 1 seconde
    int packetSize = Udp.parsePacket();
    if (packetSize) {
      byte incomingPacket[UDP_TX_PACKET_MAX_SIZE];
      Udp.read(incomingPacket, packetSize);

// Afficher la réponse reçue en hexadécimal pour le débogage
#if DEBUG
      Serial.print(F("Response from camera (in hex): "));
      for (int i = 0; i < packetSize; i++) {
        if (incomingPacket[i] < 0x10) {
          Serial.print(F("0"));
        }
        Serial.print(incomingPacket[i], HEX);
        Serial.print(F(" "));
      }
      Serial.println();
#endif

      // Vérifier si c'est la réponse attendue
      if (packetSize >= 10 && incomingPacket[8] == 0x90 && incomingPacket[9] == 0x51) {
        presetAcknowledged = true;
        break;  // On arrête de chercher dès qu'on trouve la bonne réponse
      }
    }
  }

  if (presetAcknowledged) {
    presetStarTime = millis();
    starDisplayed = true;
#if DEBUG
    Serial.println(F("Save Preset change confirmed by camera."));
#endif
    lcd.setCursor(11, 0);
    lcd.print("*");

  } else {
#if DEBUG
    Serial.println(F("Preset change not confirmed or no valid response received."));
#endif
  }
}

// Fonction pour envoyer des commandes VISCA avec préfixe pour VISCA sur IP
void sendViscaPZCommand(const char* action, int speed) {
#if DEBUG
  Serial.print(F("Commande VISCA : "));
  Serial.print(action);
  Serial.print(F(" à vitesse "));
  Serial.println(speed);
#endif

  // Préfixe VISCA sur IP
  byte viscaPZPrefix[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x02 };

  // Commande de base VISCA
  byte viscaPZCommand[] = { 0x81, 0x01, 0x04, 0x00, 0x00, 0xFF };

  if (strcmp(action, "Zoom Tele Start") == 0) {
    viscaPZCommand[3] = 0x07;
    viscaPZCommand[4] = 0x21 + speed;  // Vitesse ajustée pour zoom télé
  } else if (strcmp(action, "Zoom Wide Start") == 0) {
    viscaPZCommand[3] = 0x07;
    viscaPZCommand[4] = 0x31 + speed;  // Vitesse ajustée pour zoom wide
  } else if (strcmp(action, "Focus Near Start") == 0) {
    viscaPZCommand[3] = 0x08;
    viscaPZCommand[4] = 0x21 + speed;  // Vitesse ajustée pour Focus Near
  } else if (strcmp(action, "Focus Far Start") == 0) {
    viscaPZCommand[3] = 0x08;
    viscaPZCommand[4] = 0x31 + speed;  // Vitesse ajustée pour Focus Far
  } else if (strcmp(action, "Zoom Stop") == 0) {
    viscaPZCommand[3] = 0x07;
    viscaPZCommand[4] = 0x00;  // Pas de vitesse pour l'arrêt
  } else if (strcmp(action, "Focus Stop") == 0) {
    viscaPZCommand[3] = 0x08;
    viscaPZCommand[4] = 0x00;  // Pas de vitesse pour l'arrêt
  }
#if DEBUG
  Serial.println(F("envoi de la commande Reset"));
#endif
  byte resetPZCommand[] = { 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01 };
  //  udp.begin(cameraPort); // Port local
  Udp.begin(cameraPort);
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(resetPZCommand, sizeof(resetPZCommand));
  Udp.endPacket();
#if DEBUG
  Serial.println(F("envoi de la commande apres reset"));
#endif
  // Envoie du paquet via UDP
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaPZPrefix, sizeof(viscaPZPrefix));
  Udp.write(viscaPZCommand, sizeof(viscaPZCommand));
  Udp.endPacket();
}


// Fonction pour vérifier un bouton et exécuter les actions correspondantes
void checkButton(int pin, bool& lastState, const char* pressCommand, const char* releaseCommand, int speed) {
  bool currentState = digitalRead(pin);
  if (currentState != lastState) {
    lastState = currentState;
    if (currentState == LOW) {
      sendViscaPZCommand(pressCommand, speed);
    } else {
      sendViscaPZCommand(releaseCommand, 0);
    }
  }
}


// Fonction pour envoyer une commande UDP
void sendUDPViscaCommand(byte* command, size_t commandSize) {
#if DEBUG
  Serial.println(F("Début envoi UDP..."));
  Serial.print(F("Commande envoyée en hexadécimal : "));
  for (size_t i = 0; i < commandSize; i++) {
    if (command[i] < 0x10) Serial.print(F("0"));  // Ajoute un zéro pour les valeurs inférieures à 0x10
    Serial.print(command[i], HEX);
    Serial.print(F(" "));
  }
  Serial.println();  // Nouvelle ligne après l'affichage de la commande
#endif
  Udp.begin(cameraPort);
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(command, commandSize);  // Utiliser la taille explicite
  Udp.endPacket();
#if DEBUG
  Serial.println(F("Fin envoi UDP."));
#endif
}

// Fonction pour envoyer des commandes VISCA avec reset compteur
void sendViscaCommandIris(const byte* command, size_t length) {
  Udp.begin(cameraPort);
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(resetCompteurCommande, sizeof(resetCompteurCommande));
  Udp.endPacket();

  delay(200);

  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(command, length);
  Udp.endPacket();
#if DEBUG
  Serial.print(F("Sent VISCA command: "));
  for (size_t i = 0; i < length; i++) {
    if (command[i] < 0x10) Serial.print(F("0"));
    Serial.print(command[i], HEX);
    Serial.print(F(" "));
  }
  Serial.println();
#endif
}


#if DEBUG
//fonction pour afficher l'état AF dans le monitor
void afficherEtatAF(int etat) {
  if (etat == 0x02) {
    Serial.println(F("Affichage : AF ON"));
  } else if (etat == 0x03) {
    Serial.println(F("Affichage : MF OFF"));
  }
}
#endif

#if DEBUG
//fonction pour afficher l'état Iris auto manu dans le monitor (a supprimer au nettoyage du code)
void afficherEtatIrisAuto(int etatIA) {
  if (etatIA == 0x02) {
    Serial.println(F("Affichage : IA"));
  } else if (etatIA == 0x03) {
    Serial.println(F("Affichage : IM"));
  }
}
#endif

//fonction pour afficher l'état Iris auto manu sur LCD
void afficheEtatIrisManuAuto() {
  int index = choixCamActuel - 1;
  lcd.setCursor(8, 1);
  lcd.print("  ");
  lcd.setCursor(8, 1);
  lcd.print(etatIrisAuto[index] == 0x02 ? "IA      " : "IM");
}




bool extractIPAddress(String request, String key, IPAddress& ip) {
  int start = request.indexOf(key);
  if (start == -1) return false;
  start += key.length();
  int end = request.indexOf("&", start);
  if (end == -1) end = request.length();
  String ipString = request.substring(start, end);
  int parts[4];
  if (sscanf(ipString.c_str(), "%d.%d.%d.%d", &parts[0], &parts[1], &parts[2], &parts[3]) == 4) {
    ip = IPAddress(parts[0], parts[1], parts[2], parts[3]);
    return true;
  }
  return false;
}

votre code est long et assez spaghetti :slight_smile: aidez nous à trouver ce dont vous parlez

comment / où recevez vous 0x01

Quelle variable ?


Souvent quand on a une erreur que l'on arrive pas à expliquer, qu'une variable semble changer de valeur par magie entre un affichage et son utilisation etc c'est qu'il ya un débordement mémoire quelque part.


c'est bien mais comme vous utilisez assez abondamment la classe String, vous pourriez avoir des soucis mémoire au run time.


sinon au lieu de gérer les offset à la main en EEPROM, ce qui peut conduire à des erreurs, vous devriez utiliser une structure qui combine l'intégralité de vos paramètres et vous laisseriez ainsi le soin au compilateur et bibliothèque de calculer ces offsets.

Comme ci :sweat_smile:

En effet ça manque d'info tout ça

Dans la fonction queryIrisState j'interroge la caméra en cours d'utilisation sur l'état de l'iris
Celle-ci me renvoi
01 11 00 07 00 00 02 01 90 50 00 00 00 01 FF

La partie qui m'intéresse sont les 2 derniers bytes avant FF
La valeur peut être 00 00, 00 01, 00 05, 00 06 ... 00 09, 00 0A ... 00 0F, 01 00, 01 01

Pour toute les valeurs reçus, j'affiche bien ce que le case de la fonction queryIrisState demande sauf pour la valeur 00 01 ou je reçois 'on

Concernant la variable, dans la fonction d'affichage de la page de configuration des caméra, il y a une partie qui ressemble à cela

// Case à cocher Caméra Aver
    client.print("<input type='checkbox' name='typeCamAver");
    client.print(i + 1);
    client.print("' value='on' ");
    if (typeCamAver[i]) client.print("checked");
    client.println("> Caméra Aver<br></div><br>");

Et dans le cas de 00 01, j'affiche pour

etatIrisEnCours[choixCamActuel - 1] = sonyIrisValues[irisIndex];

cette fameuse partie de code 'on (situé dans value='on' ) dans le monitor

ChoixCamActuel dépend de typeCamAver qui se trouve dans

client.print("<input type='checkbox' name='typeCamAver");

et que je récupère dans le code à l'aide de

byte isCameraAver = typeCamAver[choixCamActuel - 1];

Voici une version plus légère du code, j'ai supprimer une bonne partie de ce qui nous concerne pas dans mon problème

#include <SPI.h>
#include <Ethernet.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
#include <EthernetUdp.h>
#include <avr/wdt.h>

byte mac[] = { 0xA8, 0x61, 0x0A, 0xAE, 0x97, 0xD0 };
EthernetServer server(80);

#define DEBUG 1  // mettre à zero pour supprimer les messages dans le port série et ne pas dépendre du PC et du logiciel Arduino IDE pour démarrer

#define DHCP_EEPROM_ADDR 0        // emplacement pour DHCP on ou off
#define FIRSTSTART_EEPROM_ADDR 1  //emplacement pour déterminer si premier démarrage ou pas
#define IP_ADDR_START 2           //adresse ip de l'arduino
#define MASK_ADDR_START 6         // masque de l'arduino
#define GATEWAY_ADDR_START 10     // passerelle de l'arduino
#define DNS_ADDR_START 14         //DNS de l'arduino
// Adresses EEPROM pour les caméras
#define CAMERA_IP_ADDR_START 18    // Premier emplacement pour stocker les IP des caméras
#define CAMERA_PORT_ADDR_START 54  // Premier emplacement pour stocker les ports des caméras
#define CAMERA_IP_OFFSET 4         // Chaque IP prend 4 octets
#define CAMERA_PORT_OFFSET 2       // Chaque port prend 2 octets (uint16_t)
#define NUM_CAMERAS 9              // Nombre total de caméras

#define EEPROM_INVERSE_PAN_START 72    //premier emplacement pour stocker les inversePAN
#define EEPROM_INVERSE_TILT_START 81   //premier emplacement pour stocker les inverseTILT
#define EEPROM_TYPE_CAM_AVER_START 90  //premier emplacement pour stocker si c'est une cam de marque AVER

LiquidCrystal lcd(14, 15, 5, 4, 3, 2);  //ecran lCD 16*2

// Déclaration des boutons
const int zeropad = 6;
const int unpad = 23;
const int deuxpad = 25;
const int troispad = 27;
const int quatrepad = 29;
const int cinqpad = 31;
const int sixpad = 33;
const int septpad = 35;
const int huitpad = 37;
const int neufpad = 39;
const int choixcam = 41;
const int presetcam = 43;
const int clearBtn = 45;
const int pluszoom = 47;
const int moinszoom = 49;
const int plusfocus = A6;
const int moinsfocus = A14;
const int btnspeed = 7;
const int btnplus = 22;
const int btnmoins = 24;
const int togAF = 26;  // bouton autofocus on/off
const int btnsetup = 28;
const int btnsortiemenu = 30;
const int supprime_mem = 8;
const int btnenter = A8;
const int btnopt1 = A9;
const int btnopt2 = A10;
const int btnF1 = A11;
const int btnF2 = A12;
const int btnF3 = A13;
const int btnautopan = A4;
const int btnautopatrol = A5;
const int irisclose = A7;
const int irisopen = 34;







// Commandes UDP pour autofocus
byte resetCompteurCommande[] = { 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01 };



// pour lecture de l'UDP entrant
byte packetBuffer[UDP_TX_PACKET_MAX_SIZE];  // Buffer pour les paquets reçus

// État des caméras (0 à 8 pour 9 caméras)
int etatAF[9] = { 0 };                                                           // Initialise l'état AF pour chaque caméra (0 = inconnu)
int etatIrisAuto[9] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };  // Initialise l'état AF pour chaque caméra (0 = inconnu)

// Dernier état connu pour éviter un rafraîchissement inutile
int dernierEtatAF[9] = { 0 };        // Initialise avec les mêmes valeurs que etatAF
int dernierEtatIrisAuto[9] = { 0 };  // Initialise avec les mêmes valeurs que etatAF

unsigned long lastUDPPacketTime = 0;  // Temps de dernier paquet UDP reçu
unsigned long udpTimeout = 1000;      // Timeout pour la réception UDP

int nouvelEtatAF = -1;        // Initialisation à une valeur par défaut l'état AF
int nouvelEtatIrisAuto = -1;  // Initialisation à une valeur par défaut l'état Iris Auto


unsigned long lastButtonPressTime = 0;         // Dernière fois où un bouton a été pressé
const unsigned long udpActiveDuration = 5000;  // Activer l'UDP pendant 5 secondes après un bouton
bool udpActive = false;                        // Indique si l'UDP est actif
const unsigned long udpActiveDuration2 = 5000;
bool udpActive2 = false;
const unsigned long udpActiveDuration3 = 5000;
bool udpActive3 = false;

// Variables pour stocker les octets extraits de l'Iris
byte irisHigh = 0;
byte irisLow = 0;





unsigned long startTime;
bool cycleComplete = false;


unsigned long lastButtonPress = 0;
unsigned long debounceDelay = 200;  // Débounce de 200ms

bool modeChoixCam = false;        //initialise le mode au démarrage
bool modePresetCam = true;        //initialise le mode au démarrage
bool attenteChoixCamera = false;  //pas en mode choix cam donc pas d'attente de choix cam au démarrage
int choixCamActuel = -1;          // Variable pour stocker la caméra choisie
int presetNumber = 0;


//gestion save/recall preset
const unsigned long longPressDuration = 3000;  // Durée d'appui long en ms
unsigned long buttonPressStart1 = 0;           // Moment où le bouton est pressé
unsigned long buttonPressStart2 = 0;
unsigned long buttonPressStart3 = 0;
unsigned long buttonPressStart4 = 0;
unsigned long buttonPressStart5 = 0;
unsigned long buttonPressStart6 = 0;
unsigned long buttonPressStart7 = 0;
unsigned long buttonPressStart8 = 0;
unsigned long buttonPressStart9 = 0;

// Variables pour la gestion du temps d'affiche de l'* à l'enregistrement de preset
unsigned long presetStarTime = 0;  // Moment où l'étoile a été affichée
bool starDisplayed = false;        // Flag pour vérifier si l'étoile a été affichée

//UDP port d'écoute de l'arduino
EthernetUDP Udp;
uint16_t udpPort = 52381;

//int selectedCamera = -1;
IPAddress cameraIP;
uint16_t cameraPort;

// Commande VISCA pour remise à zéro
byte viscaResetCommand[] = { 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01 };

// valeur par défaut si pas de DHCP
IPAddress defaultIP(192, 168, 1, 100);
IPAddress defaultMask(255, 255, 0, 0);
IPAddress defaultGateway(192, 168, 1, 1);
IPAddress defaultDNS(8, 8, 8, 8);  // DNS par défaut


bool readDHCPFromEEPROM() {
  return EEPROM.read(DHCP_EEPROM_ADDR) == 1;
}

bool readInvertTiltFromEEPROM() {
  return EEPROM.read(EEPROM_INVERSE_TILT_START) == 1;
}

bool readInvertPanFromEEPROM() {
  return EEPROM.read(EEPROM_INVERSE_PAN_START) == 1;
}

bool readTypeCamAverFromEEPROM() {
  return EEPROM.read(EEPROM_TYPE_CAM_AVER_START) == 1;
}

void writeDHCPToEEPROM(bool isDHCPEnabled) {
  EEPROM.write(DHCP_EEPROM_ADDR, isDHCPEnabled ? 1 : 0);
}


// Fonction pour écrire un booléen pour PAN dans l'EEPROM
void writeInvertPanToEEPROM(bool value, int addr) {
  EEPROM.write(addr, value ? 1 : 0);
}

// Fonction pour écrire un booléen pour TILT dans l'EEPROM
void writeInvertTiltToEEPROM(bool value, int addr) {
  EEPROM.write(addr, value ? 1 : 0);
}

// Fonction pour écrire un booléen pour TILT dans l'EEPROM
void writeTypeCamAverToEEPROM(bool value, int addr) {
  EEPROM.write(addr, value ? 1 : 0);
}

// Fonction pour lire un booléen pour PAN depuis l'EEPROM
bool readInvertPanFromEEPROM(int addr) {
  return EEPROM.read(addr) == 1;
}

// Fonction pour lire un booléen pour TILT depuis l'EEPROM
bool readInvertTiltFromEEPROM(int addr) {
  return EEPROM.read(addr) == 1;
}

// Fonction pour lire un booléen pour TILT depuis l'EEPROM
bool readTypeCamAverFromEEPROM(int addr) {
  return EEPROM.read(addr) == 1;
}

// Variables pour stocker les états
bool inversePan[9];
bool inverseTilt[9];
bool typeCamAver[9];

bool readFirstStartFromEEPROM() {
  return EEPROM.read(FIRSTSTART_EEPROM_ADDR) != 1;
}

void writeFirstStartToEEPROM() {
  EEPROM.write(FIRSTSTART_EEPROM_ADDR, 1);
}

void printEEPROMValues() {
  IPAddress ip = readIPAddressFromEEPROM(IP_ADDR_START);
  IPAddress mask = readIPAddressFromEEPROM(MASK_ADDR_START);
  IPAddress gateway = readIPAddressFromEEPROM(GATEWAY_ADDR_START);
  IPAddress dns = readIPAddressFromEEPROM(DNS_ADDR_START);

#if DEBUG
  Serial.println(F("Valeurs stockées dans l'EEPROM :"));
  Serial.print(F("IP EEPROM: "));
  Serial.println(ip);
  Serial.print(F("Masque EEPROM : "));
  Serial.println(mask);
  Serial.print(F("Passerelle EEPROM : "));
  Serial.println(gateway);
  Serial.print(F("DNS EEPROM : "));
  Serial.println(dns);
#endif
}


uint16_t readPortFromEEPROM(int startAddr) {
  uint16_t port = (EEPROM.read(startAddr) << 8) | EEPROM.read(startAddr + 1);
  return port;
}

void writePortToEEPROM(uint16_t port, int startAddr) {
  EEPROM.write(startAddr, (port >> 8) & 0xFF);
  EEPROM.write(startAddr + 1, port & 0xFF);
}


void clearEEPROM() {
  for (size_t i = 0; i < EEPROM.length(); i++) {
    EEPROM.write(i, 0);
  }
}

// pour vidage ram via bouton reset
bool isButtonPressedFor5Seconds() {
  unsigned long buttonPressStartTime = 0;
  bool buttonState = digitalRead(supprime_mem);
  if (buttonState == LOW) {
    buttonPressStartTime = millis();

    while (digitalRead(supprime_mem) == LOW) {
      if (millis() - buttonPressStartTime >= 5000) {
        return true;
      }
    }
  }
  return false;
}

IPAddress readIPAddressFromEEPROM(int startAddr) {
  return IPAddress(EEPROM.read(startAddr), EEPROM.read(startAddr + 1), EEPROM.read(startAddr + 2), EEPROM.read(startAddr + 3));
}

void writeIPAddressToEEPROM(IPAddress ip, int startAddr) {
  for (int i = 0; i < 4; i++) {
    EEPROM.write(startAddr + i, ip[i]);
  }
}


bool isDHCPEnabled = readDHCPFromEEPROM();




// Commandes VISCA Iris Close ou Open ou Query
const byte CMD_IRIS_OPEN[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x01, 0x81, 0x01, 0x04, 0x0B, 0x02, 0xFF };
const byte CMD_IRIS_CLOSE[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x01, 0x81, 0x01, 0x04, 0x0B, 0x03, 0xFF };
//const byte CMD_IRIS_QUERY[] = { 0x01, 0x10, 0x00, 0x05, 0x00, 0x00, 0x02, 0x01, 0x81, 0x09, 0x04, 0x4B, 0xFF };
const byte viscaIrisFBpositionCommand[] = { 0x01, 0x10, 0x00, 0x05, 0x00, 0x00, 0x02, 0x01, 0x81, 0x09, 0x04, 0x4B, 0xFF };

//tableau pour équivalent IRIS (peut faire parti du problème)
const char* sonyIrisValues[] = {
  "F0", "F14", "F11", "F9.6", "F8", "F6.8", "F5.6", "F4.8", "F4", "F3.4",
  "F2.8", "F2.4", "F2", "F1.8", "F1.6"
};

const char* averIrisValues[] = {
  "F0", "F14", "F11", "F8", "F6.8", "F5.6", "F4.8", "F4", "F3.4",
  "F2.8", "F2.4", "F2", "F1.8", "F1.6"
};


// Déclare un tableau pour stocker l'état de l'iris pour chaque caméra
//byte etatIrisEnCours[9];  // Supposons qu'il y a 9 caméras maximum
const char* etatIrisEnCours[9];


//mise en place du code ici


int irisIndex = -1;

void queryIrisState() {
  byte isCameraAver = typeCamAver[choixCamActuel - 1];
  int irisIndex = -1;
#if DEBUG
  Serial.println(F("Entering queryIrisState function."));
#endif
  if (choixCamActuel == -1) {
#if DEBUG
    Serial.println(F("No camera selected, exiting queryIrisState."));
#endif
    return;
  }
  Udp.begin(cameraPort);

#if DEBUG
  Serial.println(F("Sending VISCA Reset command..."));
#endif
  if (cameraIP == IPAddress(0, 0, 0, 0) || cameraPort == 0) {
#if DEBUG
    Serial.println(F("Error: Invalid Camera IP or Port!"));
#endif
    return;
  }

  // Sending reset command to camera
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaResetCommand, sizeof(viscaResetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("Reset command sent. Sending FB Iris command..."));
#endif

  // Sending FB Iris command
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaIrisFBpositionCommand, sizeof(viscaIrisFBpositionCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("Iris Position FB command sent. Listening for responses..."));
#endif

  // Listen for responses for a short period
  unsigned long startTime = millis();
  bool irisPositionAcknowledged = false;

  while (millis() - startTime < 1000) {  // Attendre jusqu'à 1 seconde
    int packetSize = Udp.parsePacket();
    if (packetSize) {
      byte incomingPacket[UDP_TX_PACKET_MAX_SIZE];
      Udp.read(incomingPacket, packetSize);

// Affiche la taille du paquet
#if DEBUG
      Serial.print("Packet size received: ");
      Serial.println(packetSize);
#endif

// Afficher le contenu du paquet
#if DEBUG
      Serial.print(F("Response from camera (in hex): "));
      for (int i = 0; i < packetSize; i++) {
        if (incomingPacket[i] < 0x10) Serial.print(F("0"));
        Serial.print(incomingPacket[i], HEX);
        Serial.print(F(" "));
      }
      Serial.println();
#endif

      // Condition ajustée pour la taille 15 et extraction des octets 13 et 14
      if (packetSize == 15 && incomingPacket[8] == 0x90 && incomingPacket[9] == 0x50) {
        irisHigh = incomingPacket[12];
        irisLow = incomingPacket[13];
        irisPositionAcknowledged = true;
        break;  // Arrêter après la première correspondance
      }
    }
  }

// Afficher les valeurs extraites même si elles ne sont pas confirmées
#if DEBUG
  Serial.print(F("Iris High = "));
  if (irisHigh < 0x10) Serial.print(F("0"));
  Serial.print(irisHigh, HEX);
  Serial.print(F(", Iris Low = "));
  if (irisLow < 0x10) Serial.print(F("0"));
  Serial.println(irisLow, HEX);
#endif

  if (irisPositionAcknowledged) {

#if DEBUG
    Serial.print(F("Iris Position confirmed. Values extracted: High = "));
    if (irisHigh < 0x10) Serial.print(F("0"));
    Serial.print(irisHigh, HEX);
    Serial.print(F(", Low = "));
    if (irisLow < 0x10) Serial.print(F("0"));
    Serial.println(irisLow, HEX);
#endif

    Serial.print(F("si 0 cam sony si 1 cam Aver :"));
    Serial.println(isCameraAver);

    if (!isCameraAver) {
      if (irisHigh == 0x01) {
        switch (irisLow) {
          case 0x00: irisIndex = 12; break;  // F2
          case 0x01: irisIndex = 14; break;  // F1.6
        }
      }
      if (irisHigh == 0x00) {
        if (irisLow == 0x01) {
          if (irisIndex == 2) {
            irisIndex = 0;  // F0
          } else if (irisIndex == 0) {
            irisIndex = 1;  // F0
          }
        } else {
          switch (irisLow) {
            case 0x00: irisIndex = 0; break;   // F0
            case 0x05: irisIndex = 1; break;   // F14
            case 0x06: irisIndex = 2; break;   // F11
            case 0x07: irisIndex = 3; break;   // F9.6
            case 0x08: irisIndex = 4; break;   // F8
            case 0x09: irisIndex = 5; break;   // F6.8
            case 0x0A: irisIndex = 6; break;   // F5.6
            case 0x0B: irisIndex = 7; break;   // F4.8
            case 0x0C: irisIndex = 8; break;   // F4
            case 0x0D: irisIndex = 9; break;   // F3.4
            case 0x0E: irisIndex = 10; break;  // F2.8
            case 0x0F: irisIndex = 11; break;  // F2.4
          }
        }
      }
      Serial.println(F("le test que c'est une cam sony"));
      //    etatIrisEnCours[choixCamActuel - 1] = irisIndex;
      etatIrisEnCours[choixCamActuel - 1] = sonyIrisValues[irisIndex];
    } else {
      switch (irisLow) {
        case 0x00: irisIndex = 0; break;   //F0
        case 0x01: irisIndex = 1; break;   //F14 ou F0
        case 0x02: irisIndex = 2; break;   //F11
        case 0x03: irisIndex = 3; break;   //F8
        case 0x04: irisIndex = 4; break;   //F6.8
        case 0x05: irisIndex = 5; break;   //F5.6
        case 0x06: irisIndex = 6; break;   //F4.8
        case 0x07: irisIndex = 7; break;   //F4
        case 0x08: irisIndex = 8; break;   //F3.4
        case 0x09: irisIndex = 9; break;   //F2.8
        case 0x0A: irisIndex = 10; break;  //2.4
        case 0x0B: irisIndex = 11; break;  //F2
        case 0x0C: irisIndex = 12; break;  //F1.8
        case 0x0D: irisIndex = 13; break;  //F1.6
      }
      Serial.println(F("le test que c'est une cam Aver"));
      etatIrisEnCours[choixCamActuel - 1] = averIrisValues[irisIndex];
      //        Serial.println(etatIrisEnCours[choixCamActuel - 1]);
    }
  }
}



void afficheMemIrisValue() {
  //  if (irisIndex >= 0) {
  lcd.setCursor(11, 1);
  lcd.print("     ");
  //    if (etatIrisEnCours[choixCamActuel - 1] != 0)
  //    {
  lcd.setCursor(11, 1);
  lcd.print(" ");
  lcd.print(etatIrisEnCours[choixCamActuel - 1]);
  //    }
  //    else {
  //      lcd.setCursor(11, 1);
  //      lcd.print("Close");
  //    }
  //  } else {
  //    Serial.println(F("Error: Invalid Iris value detected."));
  //  }
}


void setup() {
  //#if DEBUG
  Serial.begin(9600);
  //#endif

  for (int i = 0; i < 9; i++) {
    inversePan[i] = EEPROM.read(EEPROM_INVERSE_PAN_START + i);
    inverseTilt[i] = EEPROM.read(EEPROM_INVERSE_TILT_START + i);
    typeCamAver[i] = EEPROM.read(EEPROM_TYPE_CAM_AVER_START + i);
  }

  Udp.begin(udpPort);

  pinMode(supprime_mem, INPUT_PULLUP);
  pinMode(zeropad, INPUT_PULLUP);
  pinMode(unpad, INPUT_PULLUP);
  pinMode(deuxpad, INPUT_PULLUP);
  pinMode(troispad, INPUT_PULLUP);
  pinMode(quatrepad, INPUT_PULLUP);
  pinMode(cinqpad, INPUT_PULLUP);
  pinMode(sixpad, INPUT_PULLUP);
  pinMode(septpad, INPUT_PULLUP);
  pinMode(huitpad, INPUT_PULLUP);
  pinMode(neufpad, INPUT_PULLUP);
  pinMode(choixcam, INPUT_PULLUP);
  pinMode(presetcam, INPUT_PULLUP);
  pinMode(clearBtn, INPUT_PULLUP);
  pinMode(pluszoom, INPUT_PULLUP);
  pinMode(moinszoom, INPUT_PULLUP);
  pinMode(plusfocus, INPUT_PULLUP);
  pinMode(moinsfocus, INPUT_PULLUP);
  pinMode(btnspeed, INPUT_PULLUP);
  pinMode(btnplus, INPUT_PULLUP);
  pinMode(btnmoins, INPUT_PULLUP);
  pinMode(togAF, INPUT_PULLUP);
  pinMode(btnsetup, INPUT_PULLUP);
  pinMode(btnsortiemenu, INPUT_PULLUP);
  pinMode(supprime_mem, INPUT_PULLUP);
  pinMode(btnenter, INPUT_PULLUP);
  pinMode(btnopt1, INPUT_PULLUP);
  pinMode(btnopt2, INPUT_PULLUP);
  pinMode(btnF1, INPUT_PULLUP);
  pinMode(btnF2, INPUT_PULLUP);
  pinMode(btnF3, INPUT_PULLUP);
  pinMode(btnautopan, INPUT_PULLUP);
  pinMode(btnautopatrol, INPUT_PULLUP);
  pinMode(irisclose, INPUT_PULLUP);
  pinMode(irisopen, INPUT_PULLUP);
  printEEPROMValues();
  if (readFirstStartFromEEPROM()) {
#if DEBUG
    Serial.println(F("Premier démarrage, configuration par défaut..."));
#endif
    writeDHCPToEEPROM(true);
    writeIPAddressToEEPROM(defaultIP, IP_ADDR_START);
    writeIPAddressToEEPROM(defaultMask, MASK_ADDR_START);
    writeIPAddressToEEPROM(defaultGateway, GATEWAY_ADDR_START);
    writeIPAddressToEEPROM(defaultDNS, DNS_ADDR_START);
    writeFirstStartToEEPROM();
  }

  // Initialisation de l'écran LCD
  lcd.begin(16, 2);
  lcd.clear();
  startTime = millis();  // Initialiser le temps de démarrage pour le cycle de 30 secondes
  if (isDHCPEnabled) {
    if (Ethernet.begin(mac) == 0) {
#if DEBUG
      Serial.println(F("Échec de la configuration DHCP."));
#endif
      Ethernet.begin(mac, readIPAddressFromEEPROM(IP_ADDR_START), readIPAddressFromEEPROM(DNS_ADDR_START), readIPAddressFromEEPROM(GATEWAY_ADDR_START), readIPAddressFromEEPROM(MASK_ADDR_START));
    }
  } else {
    Ethernet.begin(mac, readIPAddressFromEEPROM(IP_ADDR_START), readIPAddressFromEEPROM(DNS_ADDR_START), readIPAddressFromEEPROM(GATEWAY_ADDR_START), readIPAddressFromEEPROM(MASK_ADDR_START));
  }

#if DEBUG
  Serial.print(F("Adresse IP : "));
  Serial.println(Ethernet.localIP());
  Serial.print(F("Masque de sous-réseau : "));
  Serial.println(Ethernet.subnetMask());
  Serial.print(F("Passerelle : "));
  Serial.println(Ethernet.gatewayIP());
  Serial.print(F("DNS : "));
  Serial.println(Ethernet.dnsServerIP());
#endif
  server.begin();

#if DEBUG
  // Affichage des valeurs de l'EEPROM entre les adresses 90 et 98 pour camera Aver ou Sony
  Serial.println(F("Lecture de l'EEPROM de l'adresse 90 à 98:"));

  for (int i = 90; i <= 98; i++) {
    byte val = EEPROM.read(i);  // Lire la valeur à l'adresse i
    Serial.print(F("Adresse "));
    Serial.print(i);
    Serial.print(F(": "));
    Serial.println(val, HEX);  // Afficher la valeur en hexadécimal
  }

  Serial.println(F("Initialisation terminée. Mode par défaut : presetcam."));
  Serial.println(F("init"));
  Serial.print(F("mode choix cam :"));
  Serial.println(modeChoixCam);
  Serial.print(F("mode preset cam :"));
  Serial.println(modePresetCam);
  Serial.print(F("choix cam actuel :"));
  Serial.println(choixCamActuel);
#endif

  // Lecture et affichage des données EEPROM
  for (int i = 0; i < 9; i++) {
    inversePan[i] = EEPROM.read(EEPROM_INVERSE_PAN_START + i) == 1;
    inverseTilt[i] = EEPROM.read(EEPROM_INVERSE_TILT_START + i) == 1;
    typeCamAver[i] = EEPROM.read(EEPROM_TYPE_CAM_AVER_START + i) == 1;
  }

#if DEBUG
  // Affichage condensé
  for (int i = 0; i < 9; i++) {
    Serial.print(F("Caméra "));
    Serial.print(i + 1);
    Serial.print(F(" - Pan: "));
    Serial.print(inversePan[i] ? "Inverse" : "Normal");
    Serial.print(F(", Tilt: "));
    Serial.println(inverseTilt[i] ? "Inverse" : "Normal");
    Serial.print(F("Cam type: "));
    Serial.println(typeCamAver[i] ? "Aver" : "Sony");
  }
#endif

  Udp.stop();  // pour ne pas le laissé ouvert tout le temps
  delay(1000);
}


void loop() {

  unsigned long currentMillis = millis();  // Temps écoulé depuis le démarrage

  // Bouton choixcam : passer en mode choix de caméra
  if (digitalRead(choixcam) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
    modePresetCam = false;
    modeChoixCam = true;
    attenteChoixCamera = true;
#if DEBUG
    Serial.println(F("Mode choix de caméra activé."));
#endif
    lastButtonPress = currentMillis;
  }

  // Bouton presetcam : passer en mode presetcam
  if (digitalRead(presetcam) == LOW && (currentMillis - lastButtonPress >= debounceDelay)) {
    modeChoixCam = false;
    modePresetCam = true;
#if DEBUG
    Serial.println(F("Mode choix de caméra activé."));
#endif
    lastButtonPress = currentMillis;
  }


  // Gestion des requêtes client via le serveur Ethernet
  EthernetClient client = server.available();
  if (client) {
    String request = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        request += c;
        if (c == '\n' && request.length() > 0) {
          // Vérifie les différents types de requêtes
          if (request.indexOf("GET /config") >= 0) {
            showConfigPage(client);
          } else if (request.indexOf("GET /save") >= 0) {
            handleSaveRequest(client, request);
          } else if (request.indexOf("GET /sauvegardeCameraConfig") >= 0) {
            handleSaveCameraConfig(client, request);  // Sauvegarde de la config de caméra
          } else if (request.indexOf("GET /camera") >= 0) {
            showConfigCamera(client);
          } else {
            showHomePage(client);
          }
          break;
        }
      }
    }
    client.stop();
  }



  if (digitalRead(btnF1) == LOW) {  // Bouton appuyé
#if DEBUG
    Serial.println(F("Bouton IRIS Auto commande appuyé !"));
    Serial.print(F("État actuel de l'iris pour la caméra "));
    Serial.print(choixCamActuel);
    Serial.print(F(": "));
#endif
    // Utilisation de l'index corrigé
    int index = choixCamActuel - 1;
#if DEBUG
    Serial.println(etatIrisAuto[index], HEX);

    for (int i = 0; i < 9; i++) {
      Serial.print(F("Indice "));
      Serial.print(i);
      Serial.print(F(": "));
      Serial.println(dernierEtatIrisAuto[i], HEX);  // Affiche la valeur en hexadécimal
    }
#endif


    if (digitalRead(irisclose) == LOW)  // Bouton irisclose pressé
    {
      // Vérifie si la caméra actuelle est de type Aver ou Sony
      sendViscaCommandIris(CMD_IRIS_CLOSE, sizeof(CMD_IRIS_CLOSE));
      udpActive3 = true;
      queryIrisState();
      lastButtonPressTime = millis();  // Enregistrer le moment du bouton pressé
      delay(200);
#if DEBUG
      Serial.print(F("Etat Iris en cours :"));
      Serial.println(etatIrisEnCours[choixCamActuel - 1]);
#endif
      afficheMemIrisValue();
    }

    if (digitalRead(irisopen) == LOW)  // Bouton irisopen pressé
    {
      sendViscaCommandIris(CMD_IRIS_OPEN, sizeof(CMD_IRIS_OPEN));
      udpActive3 = true;
      queryIrisState();
      lastButtonPressTime = millis();  // Enregistrer le moment du bouton pressé
      delay(200);
#if DEBUG
      Serial.print(F("Etat Iris en cours :"));
      Serial.println(etatIrisEnCours[choixCamActuel - 1]);
#endif
      afficheMemIrisValue();
    }
  }
}

//affiche la page web acueil avec les 2 boutons
void showHomePage(EthernetClient client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println();

  client.println("<html><body>");
  client.println("<br>");
  client.println("<h1 style='text-align:center;'>Télécommande PTZ VISCA OVER IP by HawaienProduction</h1>");
  client.println("<br>");
  client.println("<hr>");
  client.println("<br>");

  client.println("<form style='text-align:center;' action='/config' method='get'>");
  client.println("<button type='submit'>Configuration IP télécommande</button>");
  client.println("</form>");
  client.println("<br>");
  client.println("<form style='text-align:center;' action='/camera' method='get'>");
  client.println("<button type='submit'>Configuration IP Caméras</button>");
  client.println("</form>");
  client.println("</body></html>");
}

//affiche la page de gestion des ip des caméras à piloter
void showConfigCamera(EthernetClient client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println();

  client.println("<html><body>");
  client.println("<h1 style='text-align:center;'>Configuration IP Caméras</h1><br><hr><br>");

  client.println("<form action='/sauvegardeCameraConfig' method='get'>");

  for (int i = 0; i < 9; i++) {
    IPAddress camIP = readIPAddressFromEEPROM(CAMERA_IP_ADDR_START + (i * CAMERA_IP_OFFSET));
    uint16_t camPort = readPortFromEEPROM(CAMERA_PORT_ADDR_START + (i * CAMERA_PORT_OFFSET));

    client.print("<h3>Caméra ");
    client.print(i + 1);
    client.println("</h3>");

    client.print("IP Caméra ");
    client.print(i + 1);
    client.print(": <input type='text' name='ip");
    client.print(i);
    client.print("' value='");
    client.print(ipToString(camIP));
    client.println("'><br>");

    client.print("Port Caméra ");
    client.print(i + 1);
    client.print(": <input type='text' name='port");
    client.print(i);
    client.print("' value='");
    client.print(camPort);
    client.println("'><br><br>");

    client.print("<div><label>Caméra ");
    client.print(i + 1);
    client.println("</label><br>");

    // Case à cocher PAN
    client.print("<input type='checkbox' name='pan");
    client.print(i + 1);
    client.print("' value='on' ");
    if (inversePan[i]) client.print("checked");
    client.println("> Inverser PAN<br>");

    // Case à cocher TILT
    client.print("<input type='checkbox' name='tilt");
    client.print(i + 1);
    client.print("' value='on' ");
    if (inverseTilt[i]) client.print("checked");
    client.println("> Inverser TILT<br>");

    // Case à cocher Caméra Aver
    client.print("<input type='checkbox' name='typeCamAver");
    client.print(i + 1);
    client.print("' value='on' ");
    if (typeCamAver[i]) client.print("checked");
    client.println("> Caméra Aver<br></div><br>");
  }

  client.println("<input type='submit' value='Enregistrer'>");
  client.println("</form>");
  client.println("<br><form action='/' method='get'><button type='submit'>Page Accueil</button></form>");
  client.println("</body></html>");
}

//affiche la pag de config reseau de l'arduino
void showConfigPage(EthernetClient client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println();

  client.println("<html><body>");
  client.println("<br>");
  client.println("<h1 style='text-align:center;'>Configuration Réseau</h1>");
  client.println("<br>");
  client.println("<hr>");
  client.println("<br>");

  // Afficher les informations réseau actuelles
  client.println("<p>Adresse IP actuelle : ");
  client.print(Ethernet.localIP());
  //client.println("</p>");

  client.println("<p>Masque de sous-réseau actuel : ");
  client.print(Ethernet.subnetMask());
  //client.println("</p>");

  client.println("<p>Passerelle actuelle : ");
  client.print(Ethernet.gatewayIP());
  //  client.println("</p>");
  //  client.println("<p></p>");

  client.println("<p>DNS actuel : ");
  client.print(Ethernet.dnsServerIP());
  client.println("</p>");
  client.println("<p></p>");

  client.println("<br>");
  client.println("<hr>");
  client.println("<br>");

  client.println("<form action='/save' method='get'>");

  client.print("DHCP : <input type='checkbox' name='dhcp' ");
  if (readDHCPFromEEPROM()) client.print("checked");
  client.println("><br>");
  client.println("<br>");
  client.print("Adresse IP :&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type='text' name='ip' value='");
  client.print(ipToString(readIPAddressFromEEPROM(IP_ADDR_START)));
  client.println("'><br>");

  client.print("Masque de sous-réseau : <input type='text' name='mask' value='");
  client.print(ipToString(readIPAddressFromEEPROM(MASK_ADDR_START)));
  client.println("'><br>");

  client.print("Passerelle :&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type='text' name='gateway' value='");
  client.print(ipToString(readIPAddressFromEEPROM(GATEWAY_ADDR_START)));
  client.println("'><br>");

  client.print("DNS :&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type='text' name='dns' value='");
  client.print(ipToString(readIPAddressFromEEPROM(DNS_ADDR_START)));
  client.println("'><br>");
  client.println("<br>");

  client.println("<input type='submit' value='Enregistrer'>");
  client.println("</form>");

  client.println("<br>");
  client.println("<hr>");
  client.println("<br>");

  client.println("<form style='text-align:center;' action='/' method='get'>");
  client.println("<button type='submit'>Page Accueil</button>");
  client.println("</form>");


  client.println("</body></html>");
}

//gere la sauvegarde dans EEPROM du reseau de l'arduino
void handleSaveRequest(EthernetClient client, String request) {
  IPAddress ip, mask, gateway, dns;
  bool dhcp = request.indexOf("dhcp=on") != -1;

  if (extractIPAddress(request, "ip=", ip)) writeIPAddressToEEPROM(ip, IP_ADDR_START);
  if (extractIPAddress(request, "mask=", mask)) writeIPAddressToEEPROM(mask, MASK_ADDR_START);
  if (extractIPAddress(request, "gateway=", gateway)) writeIPAddressToEEPROM(gateway, GATEWAY_ADDR_START);
  if (extractIPAddress(request, "dns=", dns)) writeIPAddressToEEPROM(dns, DNS_ADDR_START);
  writeDHCPToEEPROM(dhcp);

  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println();
  client.println("<html><body><h1>Configuration enregistrée</h1>");
  client.println("<br>");
  client.print("<p>Veuillez redémarrer l'appareil et ouvrir une nouvelle page à l'adresse IP affichée sur l'écran LCD de l'appareil au redémarrage.</p>");
  client.println("</body></html>");
}

//gere la sauvegarde dans EEPROM des IP et port des caméras
void handleSaveCameraConfig(EthernetClient client, String request) {
  for (int i = 0; i < 9; i++) {
    IPAddress camIP;
    uint16_t camPort;
    // Traitement des paramètres dans la requête

    String panParam = "pan" + String(i + 1) + "=";
    String tiltParam = "tilt" + String(i + 1) + "=";
    String typeCaamAverParam = "typeCamAver" + String(i + 1) + "=";

    if (extractIPAddress(request, "ip" + String(i) + "=", camIP)) {
      writeIPAddressToEEPROM(camIP, CAMERA_IP_ADDR_START + (i * CAMERA_IP_OFFSET));
    }
    int portIndex = request.indexOf("port" + String(i) + "=");
    if (portIndex != -1) {
      camPort = request.substring(portIndex + 5 + String(i).length()).toInt();
      writePortToEEPROM(camPort, CAMERA_PORT_ADDR_START + (i * CAMERA_PORT_OFFSET));
    }

    if (request.indexOf("pan" + String(i + 1) + "=on") != -1) {
      inversePan[i] = true;
    } else {
      inversePan[i] = false;
    }

    if (request.indexOf("tilt" + String(i + 1) + "=on") != -1) {
      inverseTilt[i] = true;
    } else {
      inverseTilt[i] = false;
    }

    if (request.indexOf("typeCamAver" + String(i + 1) + "=on") != -1) {
      typeCamAver[i] = true;
    } else {
      typeCamAver[i] = false;
    }

    // Sauvegarde des options dans l'EEPROM
    writeInvertPanToEEPROM(inversePan[i], EEPROM_INVERSE_PAN_START + i);
    writeInvertTiltToEEPROM(inverseTilt[i], EEPROM_INVERSE_TILT_START + i);
    writeTypeCamAverToEEPROM(typeCamAver[i], EEPROM_TYPE_CAM_AVER_START + i);
  }

  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println();
  client.println("<html><body><h1>Configuration des caméras enregistrée</h1><br>");
  client.println("<p>Les informations de configuration des caméras ont été enregistrées avec succès.</p>");
  client.println("<form action='/' method='get'><button type='submit'>Page Accueil</button></form>");
  client.println("</body></html>");
}

String ipToString(IPAddress ip) {
  return String(ip[0]) + "." + String(ip[1]) + "." + String(ip[2]) + "." + String(ip[3]);
}

//lit l'ip et le port des cameras dans EEPROM pour affichage
void readCameraConfig(int choixCamActuel) {
  int ipStartAddr = CAMERA_IP_ADDR_START + ((choixCamActuel * CAMERA_IP_OFFSET) - 4);
  cameraIP = readIPAddressFromEEPROM(ipStartAddr);
#if DEBUG
  Serial.print(F("Camera "));
  Serial.print(choixCamActuel);
  Serial.print(F(" IP: "));
  Serial.println(cameraIP);
#endif

  int portAddr = CAMERA_PORT_ADDR_START + ((choixCamActuel * CAMERA_PORT_OFFSET) - 2);
  cameraPort = readPortFromEEPROM(portAddr);
#if DEBUG
  Serial.print(F("Camera "));
  Serial.print(choixCamActuel);
  Serial.print(F(" Port: "));
  Serial.println(cameraPort);
#endif
}

//fonction pour le rappel des preset
void recallPreset(int choixCamActuel, int presetNumber) {
#if DEBUG
  Serial.println(F("Entering recallPreset function."));
#endif
  if (choixCamActuel == -1) {
#if DEBUG
    Serial.println(F("No camera selected, exiting recallPreset."));
#endif
    return;
  }
  Udp.begin(cameraPort);
  byte presetHex = presetNumber - 1;
#if DEBUG
  Serial.print(F("Preset hex value: "));
  Serial.println(presetHex, HEX);
#endif

  byte viscaPresetCommand[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x02, 0x81, 0x01, 0x04, 0x3F, 0x02, presetHex, 0xFF };
#if DEBUG
  Serial.println(F("Sending VISCA Reset command..."));
#endif
  if (cameraIP == IPAddress(0, 0, 0, 0) || cameraPort == 0) {
#if DEBUG
    Serial.println(F("Error: Invalid Camera IP or Port!"));
#endif
    return;
  }

  // Sending reset command to camera
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaResetCommand, sizeof(viscaResetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("Reset command sent. Sending recall preset command..."));
#endif

  // Sending recall preset command
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaPresetCommand, sizeof(viscaPresetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("Recall preset command sent. Listening for responses..."));
#endif

  // Listen for responses for a short period
  unsigned long startTime = millis();
  bool presetAcknowledged = false;

  while (millis() - startTime < 1000) {  // Attendre jusqu'à 1 seconde
    int packetSize = Udp.parsePacket();
    if (packetSize) {
      byte incomingPacket[UDP_TX_PACKET_MAX_SIZE];
      Udp.read(incomingPacket, packetSize);

// Afficher la réponse reçue en hexadécimal pour le débogage
#if DEBUG
      Serial.print(F("Response from camera (in hex): "));
      for (int i = 0; i < packetSize; i++) {
        if (incomingPacket[i] < 0x10) {
          Serial.print(F("0"));
        }
        Serial.print(incomingPacket[i], HEX);
        Serial.print(F(" "));
      }
      Serial.println();
#endif

      // Vérifier si c'est la réponse attendue
      if (packetSize >= 10 && incomingPacket[8] == 0x90 && incomingPacket[9] == 0x51) {
        presetAcknowledged = true;
        break;  // On arrête de chercher dès qu'on trouve la bonne réponse
      }
    }
  }

  if (presetAcknowledged) {
#if DEBUG
    Serial.println(F("Preset change confirmed by camera."));
#endif
    lcd.setCursor(9, 0);
    lcd.print(" ");
    lcd.print(presetNumber);
  }
}

//fonction pour la sauvegarde des presets
void savePreset(int choixCamActuel, int presetNumber) {
#if DEBUG
  Serial.println(F("Entering savePreset function."));
#endif

  if (choixCamActuel == -1) {
#if DEBUG
    Serial.println(F("No camera selected, exiting savePreset."));
#endif
    return;
  }

  Udp.begin(cameraPort);
  byte presetHex = presetNumber - 1;  // Conversion du numéro de preset en hexadécimal (de 0x00 à 0x08)
#if DEBUG
  Serial.print(F("Preset hex value: "));
  Serial.println(presetHex, HEX);
#endif

  byte viscaSavePresetCommand[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x01, 0x81, 0x01, 0x04, 0x3F, 0x01, presetHex, 0xFF };

#if DEBUG
  Serial.println(F("Sending VISCA Reset command..."));
#endif
  if (cameraIP == IPAddress(0, 0, 0, 0) || cameraPort == 0) {
#if DEBUG
    Serial.println(F("Error: Invalid Camera IP or Port!"));
#endif
    return;
  }

  // Sending reset command to camera
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaResetCommand, sizeof(viscaResetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("Reset command sent. Sending recall preset command..."));
#endif

  // Sending recall preset command
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaSavePresetCommand, sizeof(viscaSavePresetCommand));
  Udp.endPacket();

#if DEBUG
  Serial.println(F("save preset command sent. Listening for responses..."));
#endif

  // Listen for responses for a short period
  unsigned long startTime = millis();
  bool presetAcknowledged = false;

  while (millis() - startTime < 1000) {  // Attendre jusqu'à 1 seconde
    int packetSize = Udp.parsePacket();
    if (packetSize) {
      byte incomingPacket[UDP_TX_PACKET_MAX_SIZE];
      Udp.read(incomingPacket, packetSize);

// Afficher la réponse reçue en hexadécimal pour le débogage
#if DEBUG
      Serial.print(F("Response from camera (in hex): "));
      for (int i = 0; i < packetSize; i++) {
        if (incomingPacket[i] < 0x10) {
          Serial.print(F("0"));
        }
        Serial.print(incomingPacket[i], HEX);
        Serial.print(F(" "));
      }
      Serial.println();
#endif

      // Vérifier si c'est la réponse attendue
      if (packetSize >= 10 && incomingPacket[8] == 0x90 && incomingPacket[9] == 0x51) {
        presetAcknowledged = true;
        break;  // On arrête de chercher dès qu'on trouve la bonne réponse
      }
    }
  }

  if (presetAcknowledged) {
    presetStarTime = millis();
    starDisplayed = true;
#if DEBUG
    Serial.println(F("Save Preset change confirmed by camera."));
#endif
    lcd.setCursor(11, 0);
    lcd.print("*");

  } else {
#if DEBUG
    Serial.println(F("Preset change not confirmed or no valid response received."));
#endif
  }
}

// Fonction pour envoyer des commandes VISCA avec préfixe pour VISCA sur IP
void sendViscaPZCommand(const char* action, int speed) {
#if DEBUG
  Serial.print(F("Commande VISCA : "));
  Serial.print(action);
  Serial.print(F(" à vitesse "));
  Serial.println(speed);
#endif

  // Préfixe VISCA sur IP
  byte viscaPZPrefix[] = { 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x02 };

  // Commande de base VISCA
  byte viscaPZCommand[] = { 0x81, 0x01, 0x04, 0x00, 0x00, 0xFF };

  if (strcmp(action, "Zoom Tele Start") == 0) {
    viscaPZCommand[3] = 0x07;
    viscaPZCommand[4] = 0x21 + speed;  // Vitesse ajustée pour zoom télé
  } else if (strcmp(action, "Zoom Wide Start") == 0) {
    viscaPZCommand[3] = 0x07;
    viscaPZCommand[4] = 0x31 + speed;  // Vitesse ajustée pour zoom wide
  } else if (strcmp(action, "Focus Near Start") == 0) {
    viscaPZCommand[3] = 0x08;
    viscaPZCommand[4] = 0x21 + speed;  // Vitesse ajustée pour Focus Near
  } else if (strcmp(action, "Focus Far Start") == 0) {
    viscaPZCommand[3] = 0x08;
    viscaPZCommand[4] = 0x31 + speed;  // Vitesse ajustée pour Focus Far
  } else if (strcmp(action, "Zoom Stop") == 0) {
    viscaPZCommand[3] = 0x07;
    viscaPZCommand[4] = 0x00;  // Pas de vitesse pour l'arrêt
  } else if (strcmp(action, "Focus Stop") == 0) {
    viscaPZCommand[3] = 0x08;
    viscaPZCommand[4] = 0x00;  // Pas de vitesse pour l'arrêt
  }
#if DEBUG
  Serial.println(F("envoi de la commande Reset"));
#endif
  byte resetPZCommand[] = { 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01 };
  //  udp.begin(cameraPort); // Port local
  Udp.begin(cameraPort);
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(resetPZCommand, sizeof(resetPZCommand));
  Udp.endPacket();
#if DEBUG
  Serial.println(F("envoi de la commande apres reset"));
#endif
  // Envoie du paquet via UDP
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(viscaPZPrefix, sizeof(viscaPZPrefix));
  Udp.write(viscaPZCommand, sizeof(viscaPZCommand));
  Udp.endPacket();
}


// Fonction pour vérifier un bouton et exécuter les actions correspondantes
void checkButton(int pin, bool& lastState, const char* pressCommand, const char* releaseCommand, int speed) {
  bool currentState = digitalRead(pin);
  if (currentState != lastState) {
    lastState = currentState;
    if (currentState == LOW) {
      sendViscaPZCommand(pressCommand, speed);
    } else {
      sendViscaPZCommand(releaseCommand, 0);
    }
  }
}


// Fonction pour envoyer une commande UDP
void sendUDPViscaCommand(byte* command, size_t commandSize) {
#if DEBUG
  Serial.println(F("Début envoi UDP..."));
  Serial.print(F("Commande envoyée en hexadécimal : "));
  for (size_t i = 0; i < commandSize; i++) {
    if (command[i] < 0x10) Serial.print(F("0"));  // Ajoute un zéro pour les valeurs inférieures à 0x10
    Serial.print(command[i], HEX);
    Serial.print(F(" "));
  }
  Serial.println();  // Nouvelle ligne après l'affichage de la commande
#endif
  Udp.begin(cameraPort);
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(command, commandSize);  // Utiliser la taille explicite
  Udp.endPacket();
#if DEBUG
  Serial.println(F("Fin envoi UDP."));
#endif
}

// Fonction pour envoyer des commandes VISCA avec reset compteur
void sendViscaCommandIris(const byte* command, size_t length) {
  Udp.begin(cameraPort);
  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(resetCompteurCommande, sizeof(resetCompteurCommande));
  Udp.endPacket();

  delay(200);

  Udp.beginPacket(cameraIP, cameraPort);
  Udp.write(command, length);
  Udp.endPacket();
#if DEBUG
  Serial.print(F("Sent VISCA command: "));
  for (size_t i = 0; i < length; i++) {
    if (command[i] < 0x10) Serial.print(F("0"));
    Serial.print(command[i], HEX);
    Serial.print(F(" "));
  }
  Serial.println();
#endif
}


#if DEBUG
//fonction pour afficher l'état AF dans le monitor
void afficherEtatAF(int etat) {
  if (etat == 0x02) {
    Serial.println(F("Affichage : AF ON"));
  } else if (etat == 0x03) {
    Serial.println(F("Affichage : MF OFF"));
  }
}
#endif

#if DEBUG
//fonction pour afficher l'état Iris auto manu dans le monitor (a supprimer au nettoyage du code)
void afficherEtatIrisAuto(int etatIA) {
  if (etatIA == 0x02) {
    Serial.println(F("Affichage : IA"));
  } else if (etatIA == 0x03) {
    Serial.println(F("Affichage : IM"));
  }
}
#endif

//fonction pour afficher l'état Iris auto manu sur LCD
void afficheEtatIrisManuAuto() {
  int index = choixCamActuel - 1;
  lcd.setCursor(8, 1);
  lcd.print("  ");
  lcd.setCursor(8, 1);
  lcd.print(etatIrisAuto[index] == 0x02 ? "IA      " : "IM");
}




bool extractIPAddress(String request, String key, IPAddress& ip) {
  int start = request.indexOf(key);
  if (start == -1) return false;
  start += key.length();
  int end = request.indexOf("&", start);
  if (end == -1) end = request.length();
  String ipString = request.substring(start, end);
  int parts[4];
  if (sscanf(ipString.c_str(), "%d.%d.%d.%d", &parts[0], &parts[1], &parts[2], &parts[3]) == 4) {
    ip = IPAddress(parts[0], parts[1], parts[2], parts[3]);
    return true;
  }
  return false;
}

Concernant ceci, il faut que je regarde comment je peux faire ça, car j'avoue que à l'instant T je n'ai aucune idée de comment faire.

essayez en mettant

  // Afficher les valeurs extraites même si elles ne sont pas confirmées
#if DEBUG
  Serial.print(F("Iris High = "));
  if (irisHigh < 0x10) Serial.print(F("0"));
  Serial.print(irisHigh, HEX);
  Serial.print(F(", Iris Low = "));
  if (irisLow < 0x10) Serial.print(F("0"));
  Serial.println(irisLow, HEX);
#endif

dans le

  if (irisPositionAcknowledged) {

sinon vous allez imprimer cela à chaque timeout ou quand la condition
if (packetSize == 15 && incomingPacket[8] == 0x90 && incomingPacket[9] == 0x50) {

n'était pas remplie et les valeurs de irisHigh et irisLow dateraient d'une lecture antérieure.

J'avance dans le debugage mais pas dans la résolution :sweat_smile:

J'ai identifié que dans le code suivant

  Serial.print(F("si 0 cam sony si 1 cam Aver :"));
  Serial.println(isCameraAver);
  Serial.print(F("l'irisIndex précédent est "));
  Serial.println(irisIndex);
 
 if (!isCameraAver) {
      if (irisHigh == 0x01 )
        {
          switch (irisLow) {
            case 0x00: irisIndex = 12; break; // F2
            case 0x01: irisIndex = 14; break; // F1.6
          }
        }
if (irisHigh == 0x00) {
        switch (irisLow) {
            case 0x00: irisIndex = 0; break; // F0
            case 0x01: if (irisIndex == 1)
            {
              irisIndex = 0;
            }
            else if (irisIndex == 0)
            {
              irisIndex = 1;
            }break;
            case 0x05: irisIndex = 1; break; // F14
            case 0x06: irisIndex = 2; break; // F11
            case 0x07: irisIndex = 3; break; // F9.6
            case 0x08: irisIndex = 4; break; // F8
            case 0x09: irisIndex = 5; break; // F6.8
            case 0x0A: irisIndex = 6; break; // F5.6
            case 0x0B: irisIndex = 7; break; // F4.8
            case 0x0C: irisIndex = 8; break; // F4
            case 0x0D: irisIndex = 9; break; // F3.4
            case 0x0E: irisIndex = 10; break; // F2.8
            case 0x0F: irisIndex = 11; break; // F2.4
        }
    }
}
      Serial.println(F("c'est une cam sony"));
      etatIrisEnCours[choixCamActuel - 1] = sonyIrisValues[irisIndex];
      Serial.print(F("l'irisIndex est "));
      Serial.println(irisIndex);
  }

Quand je reçois 00 01, l'irisIndex vaut -1
Au lancement du code, je comprends qu'il soit à -1 car je ne l'initialise à -1 mais si je reçoit
00 00, irisIndex passe bien à 0
00 05, irisIndex passe bien à 1

Mais quand je reçois 00 01, que irisIndex soit à 0 ou à 1 sa valeur passe à -1

Après un énième print pour le plus grand plaisir de J-M-L je découvre que irisIndex est toujours à -1 à chaque lecture du code (appui sur un bouton qui appel cette fonction)

Après analyse, j'initialise 2x la variable

int irisIndex =-1;

void queryIrisState() {
bool isCameraAver = typeCamAver[choixCamActuel - 1];
int irisIndex = -1;
#if DEBUG
  Serial.println(F("Entering queryIrisState function."));
#endif

En supprimant la seconde qui se trouve dans la fonction cela fonctionne correctement...

Pourquoi l'IDE Arduino ne m'affiche pas cela comme une erreur? Normalement on ne peux pas initialiser 2 fois une variable, si?

Je voulais encore vous remercier pour toute l'aide apportée et les explications.

Ayant besoin de cette télécommande très prochainement le code restera

Mais après mes événements de janvier et mars, je compte tenter de mettre le code propre.

Dans tous les cas, propre ou pas propre, si quelqu'un souhaite le code, je partage sans problème. De même si quelqu'un veut s'engager dans la remise au propre.

Parce que ce n'est pas une erreur.
En fait, la première déclaration définit une variable globale et la seconde une variable locale. Tu aurais une erreur si tu définissais la même variable dans le même espace de nom.
Donc à l'intérieur de queryIrisState() c'est la variable locale qui est utilisée mais du coup sa valeur n'est pas visible lorsque tu sors de queryIrisState() .


Non, tu définis 2 variables.
Initialiser une variable s'écrit comme cela
toto = 0;
Lorsque tu fait précéder le nom de la variable d'un type tu définis une variable. Cela ajoute une variable et réserve de la place en mémoire pour celle-ci.

Vous avez trouvé un souci :slight_smile:

On appelle cela la portée des variables ➜ scope

le premier

déclare une variable globale qui vaut -1

le second

déclare une variable locale à la fonction queryIrisState qui cache la variable globale. C'est autorisée en C ou C++.

➜ vous avez 2 variables et quand vous sortez de la fonction queryIrisState vous retrouvez la variable globale qui n'aura pas changé de valeur

il faut écrire

void queryIrisState() {
  ...
  irisIndex = -1; //<=== là on modifie bien la variable globale
  ...

EDIT: tiens je n'avais pas vu que @fdufnews avait déjà répondu, c'est apparu une fois que j'avais tapé ma réponse... désolé pour la double explication

J'avais fait une réponse un peu succincte et je l'ai complétée pendant que tu rédigeais la tienne. Du coup je n'ai pas vu le doublon.