Tracker solaire DIY perturbé par perte Wifi

Pour tous ceux qui veulent se lancer dans le remplacement d’un contrôleur de vérins utilisant un un capteur d’illumination, voici le code de @Scorpix que j’ai utilisé avec les commentaires pour mieux le comprendre :wink:

# ESPHome configuration file for 
# solar-tracker module
# https://forum.hacf.fr/t/tracker-solaire-diy-perturbe-par-perte-wifi/47291/18

substitutions:
  esphome_name: solar-tracker
  logger_level: INFO
  logger_baud_rate: '0'
  # api_encryption_key: !secret nom_de_l_appareil_api_encryption_key
  # api_password: !secret nom_de_l_appareil_api_password
  static_ip: !secret ip_solar_tracker
  power_save_mode: none


  # CONSTANTES D'INSTALLATION
  PI: '3.14159265359'
  HOME_LAT: XX.XXXXX                  # Latitude du Traqueur
  HOME_LON: XX.XXXXX                  # Longitude du Traqueur

  # Définitions des butées
  # Butées physiques (en degré): Ces butées, une fois atteinte, déclenche le
  # passage en mode manuel pour éviter une casse matériel.
  seuil_manuel_min_roulis: '-60.0'    # angle minimum autorisé sur le roulis
  seuil_manuel_max_roulis: '60.0'     # angle maximum autorisé sur le roulis
  seuil_manuel_min_tangage: '-60.0'   # angle minimum autorisé sur le tangage
  seuil_manuel_max_tangage: '60.0'    # angle maximum autorisé sur le tangage
  # Butées logicielles (en degré) : Ces butées sont définies pour "caper" les
  # angles min et max tout en restant asservi.
  angle_min_roulis: '-50.0'           # angle minimum autorisé sur le roulis
  angle_max_roulis: '50.0'            # angle maximum autorisé sur le roulis
                                      # (max 49.0 vérifié le 13/08/25 à 08:34:31)
  angle_min_tangage: '-52.0'          # angle minimum autorisé sur le tangage 
  angle_max_tangage: '20.0'           # angle maximum autorisé sur le tangage
                                      # (max 16.1 vérifié le 13/08/25 à 06:48:01)
  # Définitions des boucles
  boucle_asservissement: '100ms'      # vitesse de la boucle d'asservissement
                                      # (utilisée pour les calculs internes de
                                      # l'asservissement de la position des vérins)
  boucle_mesure: '10s'                # vitesse de la boucle de mesure des
                                      # données remontées dans HA
  # Définitions des compensations
  # Calibration de l'IMU MPU6050 grossière de l'IMU, à faire sur table nivelée
  offset_accX: '0.02'                 # Calibration en X 
  offset_accY: '0.68'                 # Calibration en Y
  offset_accZ: '-0.05'                # Calibration en Z
  offset_azimuth: '-5.0'              # Variable d'ajustement des erreurs liée à
                                      # la non colinéarité avec l'axe Nord-Sud
  offset_tangage: '0.0'               # Ajustements des erreurs sur le tangage
  offset_roulis: '0.0'                # Ajustements des erreurs sur le roulis
  offset_anemo: 0                     # Calibration de l'anémomètre
  # Positions repos/tempête
  repos_tangage: '-0.0'               # angle tangage par rapport à l'horizontal
  repos_roulis: '-0.0'                # angle roulis par rapport à l'horizontal
  # Définitions du suivi
  setpoint_max_speed: '12.0'          # si la consigne est à plus de 12°, la
                                      # vitesse du vérin sera passée à 100%
  precision_tracking: '2.0'           # angle cône de suivi du soleil
  vitesse_mini_verins: '20'           # Vitesse minimales d'approche des vérins
  # Défintions de l'anémomètre
  rapport_kmh_v: 90                   # Rapport entre la vitesse du vent relevé
                                      # par l'anémomètre et la tension relevée
                                      # (1v - 25m/s => 90 km/h)
  # Définitions des remontées dans HA
  PUSHED_TO_HA: 'False'               # Remonte les données dans HA
  KEEP_INTERNE: 'True'                # Ne remonte pas les données dans HA
  
  # GPIO
  GPIO_SDA: '21'
  GPIO_SCL: '22'

  # FREQUENCES
  frequence_bus_i2c: '400kHz'       # Permet d'améliorer la stabilité de la 
                                    # liaison I2C, par default 50kHz
  frequence_pwm: '40000Hz'

packages:
  base: !include .config-base.yaml
  wifi: !include .config-wifi.yaml
  ota: !include .config-ota.yaml
  logger: !include .config-logger.yaml
  portal: !include .config-portal.yaml
  api: !include .config-api.yaml
  status: !include .config-status.yaml
  web_server: !include .config-web-server.yaml
  # mqtt: !include .config-mqtt.yaml

esp32:
  board: esp32dev
  framework:
    type: esp-idf
    version: latest
    advanced:
      compiler_optimization: PERF # Aide si le µC est à la peine à cause des calculs


# Module Definition

# Définition de la communication I2C pour le capteur MPU6050
i2c:
  sda: 
    number: ${GPIO_SDA}
  scl: 
    number: ${GPIO_SCL}
  scan: true
  id: bus_a
  frequency: $frequence_bus_i2c

# Définition des sorties PWM pour le H-Bridge DBH-12V
output:
  # Moteur A = Tangage
  # Moteur A Pin IN1A du pont en H (IN1A <=> GPIO13)
  - platform: ledc
    id: tangage_pin_IN1A
    pin: GPIO13
    frequency: $frequence_pwm

  # Moteur A Pin B du pont en H (IN2A <=> GPIO12)
  - platform: ledc
    id: tangage_pin_IN2A
    pin: GPIO12
    frequency: $frequence_pwm

  # Moteur B = Roulis
  # Moteur B Pin A du pont en H (IN1B <=> GPIO14)
  - platform: ledc
    id: roulis_pin_IN1B
    pin: GPIO14
    frequency: $frequence_pwm

  # Moteur B Pin B du pont en H (IN2B <=> GPIO27)
  - platform: ledc
    id: roulis_pin_IN2B
    pin: GPIO27
    frequency: $frequence_pwm
    
# Dans Esphome, le pilotage des vérins à travers un pont en H, se fait avec la 
# classe FAN.
# Option: 
#   un nombre incrémental à chaque action des vérins pour avoir une idée du 
#   nombre de fois par jour qu’ils sont mis en action.
fan:
  # Moteur A
  - platform: hbridge
    id: tangage_verin
    name: "Verin Tangage - H-Bridge"
    pin_a: tangage_pin_IN1A
    pin_b: tangage_pin_IN2A
    decay_mode: SLOW
    internal: ${PUSHED_TO_HA}
    on_turn_on: 
      then:
        - number.increment: number_tangage

  # Moteur B
  - platform: hbridge
    id: roulis_verin
    name: "Verin Roulis - H-Bridge"
    pin_a: roulis_pin_IN1B
    pin_b: roulis_pin_IN2B
    decay_mode: SLOW
    internal: ${PUSHED_TO_HA}
    on_turn_on: 
      then:
        - number.increment: number_roulis

# Boucle d'asservissement des positions des vérins
# Arguments :
#   - offset : écart entre la position du soleil et la position réel des 
#              panneaux sur l'axe choisi
# Description :
#   En fonction du signe, le pilotage se fait dans un sens ou dans l'autre 
#   (REVERSE/FORWARD).
#   En fonction de la valeur, la vitesse des vérins est plus ou moins importante.
#   Plus l'écart est important, plus la vitesse d'approche est grande, plus on
#   s'approche de la consigne, plus la vitesse est lente (ceci pour éviter les
#   chocs brutaux). Plus la $boucle_asservissement est petite, plus l'approche
#   sera douce. Une vitesse minimale (vitesse_mini_verins) est défini pour
#   assurer un bon fonctionnement des vérins. Cette vitesse minimale dépend du
#   vérin, elle est défini par expérimentation.
# Rappel :
#   - Roll  : Roulis
#   - Pitch : Tangage
script:
  - id: RollPilot
    mode: single
    parameters:
      offset: float
    then:
      - lambda: |-
          if (abs(offset)>$precision_tracking) {
            int speed = 0;
            if(abs(offset)>$setpoint_max_speed) {
              speed = 100;
            }
            else {
              speed = ${vitesse_mini_verins} + (40*abs(offset)/$setpoint_max_speed);
            }
            if (offset>0) {
              auto call = id(roulis_verin).turn_on();
              call.set_speed(speed);
              call.set_direction(FanDirection::FORWARD);
              call.perform();
            } else {
              auto call = id(roulis_verin).turn_on();
              call.set_speed(speed);
              call.set_direction(FanDirection::REVERSE);
              call.perform();
            }
          }
          else {
            auto call = id(roulis_verin).turn_off();
            call.perform();
          }
  - id: PitchPilot
    mode: single
    parameters:
      offset: float
    then:
      - lambda: |-
          if (abs(offset)>$precision_tracking) {
            int speed = 0;
            if(abs(offset)>$setpoint_max_speed) {
              speed = 100;
            }
            else {
              speed = ${vitesse_mini_verins} + (40*abs(offset)/$setpoint_max_speed);
            }
            if (offset>0) {
              auto call = id(tangage_verin).turn_on();
              call.set_speed(speed);
              call.set_direction(FanDirection::REVERSE);
              call.perform();
            } else {
              auto call = id(tangage_verin).turn_on();
              call.set_speed(speed);
              call.set_direction(FanDirection::FORWARD);
              call.perform();
            }
          }
          else {
            auto call = id(tangage_verin).turn_off();
            call.perform();
          }

# Boutons de contrôle manuel
button:
  # Bouton pour faire avancer le Verin N/S rapidement
  - platform: template
    name: "Verin N/S - Action - Avance - 80 %"
    on_press:
      - fan.turn_on:
          id: tangage_verin
          speed: 80
          direction: FORWARD

  # Bouton pour faire avancer le Verin N/S lentement
  - platform: template
    name: "Verin N/S - Action - Avance - 20 %"
    on_press:
      - fan.turn_on:
          id: tangage_verin
          speed: 20
          direction: FORWARD

  # Bouton pour faire reculer le Verin N/S rapidement
  - platform: template
    name: "Verin N/S - Action - Recule - 80 %"
    on_press:
      - fan.turn_on:
          id: tangage_verin
          speed: 80
          direction: REVERSE

  # Bouton pour faire reculer le Verin N/S lentement
  - platform: template
    name: "Verin N/S - Action - Recule - 20 %"
    on_press:
      - fan.turn_on:
          id: tangage_verin
          speed: 20
          direction: REVERSE

  # Bouton d'arrêt du Verin N/S
  - platform: template
    name: "Verin N/S - Action - Stop"
    on_press:
      - fan.turn_off: tangage_verin

  # Bouton pour faire tourner le Verin E/O vers l'avant rapidement
  - platform: template
    name: "Verin E/O - Action - Avance - 80 %"
    on_press:
      - fan.turn_on:
          id: roulis_verin
          speed: 80
          direction: FORWARD

  # Bouton pour faire tourner le Verin E/O vers l'avant lentement
  - platform: template
    name: "Verin E/O - Action - Avance - 20 %"
    on_press:
      - fan.turn_on:
          id: roulis_verin
          speed: 20
          direction: FORWARD

  # Bouton pour faire tourner le Verin E/O vers l'arrière rapidement
  - platform: template
    name: "Verin E/O - Action - Recule - 80 %"
    on_press:
      - fan.turn_on:
          id: roulis_verin
          speed: 80
          direction: REVERSE

  # Bouton pour faire tourner le Verin E/O vers l'arrière lentement
  - platform: template
    name: "Verin E/O - Action - Recule - 20 %"
    on_press:
      - fan.turn_on:
          id: roulis_verin
          speed: 20
          direction: REVERSE

  # Bouton d'arrêt du Verin E/O
  - platform: template
    name: "Verin E/O - Action - Stop"
    on_press:
      - fan.turn_off: roulis_verin

# Capteur de statut du moteur
binary_sensor:
  - platform: template
    name: "Verin N/S - Etat - En marche"
    lambda: |-
      return id(tangage_verin).state;
  - platform: template
    name: "Verin E/O - Etat - En marche"
    lambda: |-
      return id(roulis_verin).state;

# Description :
#   Affichage MAC Address pour debugging
# text_sensor:
#   - platform: wifi_info
#     mac_address:
#       name: $device_name_friendly MAC Address
#       id:   text_sensor_mac_address

# Incréments des actions des vérins
# Description :
#   Ces nombres sont créés pour connaitre le nombre de fois qu'un vérin est
#   actionné. Ces compteurs sont remis à 0 en fin de journée par la classe SUN.
#   Ils peuvent être remontés à HA via l'attribut 'internal' en le passant de
#   'KEEP_INTERNE' à 'PUSHED_TO_HA'
number:
  - platform: template
    internal: $KEEP_INTERNE
    id: number_tangage
    restore_value: True
    min_value: 0
    max_value: 9999
    step: 1
    optimistic: True
    on_value: 
      then:
        - lambda: "return id(sensor_number_tangage).publish_state(x);"

  - platform: template
    id: number_roulis
    internal: $KEEP_INTERNE
    restore_value: True
    min_value: 0
    max_value: 9999
    step: 1
    optimistic: True
    on_value: 
      then:
        - lambda: "return id(sensor_number_roulis).publish_state(x);"

# Déclaration de capteurs
sensor:
  # Description :
  #   Récupération de la tension sur l'anémomètre
  # Utilisation :
  #   - calcul de la vitesse du vent 'vent_kmh'
  - platform: adc
    pin: GPIO34
    name: "Anémomètre"
    id: anemo_tension
    internal: $KEEP_INTERNE
    filters:
    - offset : $offset_anemo
    - clamp:
        min_value: 0.1
        max_value: 3.0
        ignore_out_of_range: False
    attenuation : 12db # Range 0,075 V ~ 3,12 V
    accuracy_decimals: 2
    unit_of_measurement: V
    update_interval: $boucle_mesure #SCORPIX surcharge CPU (avant : $boucle_asservissement) tu remettras plus rapide quand tu auras trouvé comment fonctionne ton anémomètre

  # Description :
  #   Déclaration des capteurs d'actions des vérins.
  # Utilisation :
  #   Ces capteurs sont utilisés par les numbers 'number_roulis' et
  #   'number_tangage'
  - platform: template
    name: action verin roulis
    id: sensor_number_roulis

  - platform: template
    name: action verin tangage
    id: sensor_number_tangage

  # Description
  #   Déclaration des capteurs d'accélération du MPU6050
  # Paramètrages :
  #   - address         : adresse du capteur sur le bus I2C
  #   - update_interval : intervalle de vérification du capteur. Tous les
  #                       capteurs du MPU6050 seront rafraichis à cette fréquence.
  #                       Elle doit être inférieure ou égale à la variable
  #                       $boucle_asservissement
  - platform: mpu6050
    address: 0x68
    update_interval: $boucle_asservissement

    # Description
    #   Déclaration des capteurs d'accélération du MPU6050
    # Utilisation :
    #   Ces capteurs 'accel_x', 'accel_y', 'accel_z' sont utilisés pour le calcul
    #   des angles des panneaux dans les capteurs 'PV_roulis' et 'PV_tangage'
    accel_x:
      id: accel_x
      name: "MPU Accel X"
      internal: $PUSHED_TO_HA
      filters:
        # - multiply: -1.0
        - offset: $offset_accX
        - clamp:
            min_value: -9.81
            max_value: 9.81
        - exponential_moving_average:
            alpha: 0.20
            send_every: 2
      #  - sliding_window_moving_average:
      #     window_size: 10
      #     send_every: 5
    accel_y:
      id: accel_y
      name: "MPU Accel Y"
      internal: $PUSHED_TO_HA
      filters:
        # - multiply: -1.0
        - offset: $offset_accY
        - clamp:
            min_value: -9.81
            max_value: 9.81
        - exponential_moving_average:
            alpha: 0.20
            send_every: 2
      #  - sliding_window_moving_average:
      #     window_size: 10
      #     send_every: 5
    accel_z:
      id: accel_z
      name: "MPU Accel Z"
      internal: $PUSHED_TO_HA
      filters:
        - multiply: -1.0
        - offset: $offset_accZ
        - timeout: 1s
        - clamp:
            min_value: 0.01                 # IMU à l'envers, tombé
            max_value: 9.81
            ignore_out_of_range: false
        - exponential_moving_average:
            alpha: 0.20
            send_every: 2
      #  - sliding_window_moving_average:
      #     window_size: 10
      #     send_every: 5

  # Description
  #   Déclaration du capteur de température du MPU6050
  # Utilisation :
  #   Ce capteur est utilisé pour remonter la température à HA par l'intermédiaire
  #   du capteur 'MPU température'
    temperature:
      id: MPU_temp
      name: "solar-tracker Temperature"
      filters:
       - sliding_window_moving_average:
          window_size: 10
          send_every: 5

  # Description
  #   Calcul de l'offset
  # Utilisation :
  #   Le calcul des offset permet d'étalonner le capteur pour qu'il indique les
  #   bonnes valeurs d'accélération en m/s² une fois positionner sur l'installation.
  #   Une fois le capteur IMU positionné sur le traqueur, et le traqueur en 
  #   position horizontale avec l'axe Z colinéaire avec l'axe vertical, l'axe
  #   des X colinéaire avec l'axe Nord/Sud et l'axe des Y colinéaire avec l'axe
  #   Est/Ouest, relevez ces valeurs d'offset.
  #   Ces capteurs ne servent qu'à l'étalonnage, il faut les commenter une fois 
  #   l'étalonnage effectué.
  # - platform: template
  #   id: accel_x_offset
  #   name: "MPU Accel X offset"
  #   unit_of_measurement: m/s²
  #   accuracy_decimals: 2
  #   update_interval: $boucle_asservissement
  #   lambda: return 9.81 - id(accel_x).state;
  # - platform: template
  #   id: accel_y_offset
  #   name: "MPU Accel Y offset"
  #   unit_of_measurement: m/s²
  #   accuracy_decimals: 2
  #   update_interval: $boucle_asservissement
  #   lambda: return 9.81 - id(accel_y).state;
  # - platform: template
  #   id: accel_z_offset
  #   name: "MPU Accel Z offset"
  #   unit_of_measurement: m/s²
  #   accuracy_decimals: 2
  #   update_interval: $boucle_asservissement
  #   lambda: return 9.81 - id(accel_z).state;

  # Description
  #   Déclaration des capteurs d'accélération du MPU6050 corrigé par l'offset
  # Utilisation :
  #   Ces capteurs 'accel_x', 'accel_y', 'accel_z' sont utilisés pour l'étalonnage 
  #   des valeurs d'offset.
  #   Une fois les offsets renseignés (voir plus haut 'Calcul de l'offset'),
  #   vérifiez que les valeurs remontées par ces capteurs sont bien 9.81 m/s²
  #   lorsque le traqueur est en position à plat.
  # - platform: template
  #   id: accel_x_corrected
  #   name: "MPU Accel X with offset"
  #   unit_of_measurement: m/s²
  #   accuracy_decimals: 2
  #   update_interval: $boucle_asservissement
  #   lambda: return id(accel_x).state + $offset_accX;
  # - platform: template
  #   id: accel_y_corrected
  #   name: "MPU Accel Y with offset"
  #   unit_of_measurement: m/s²
  #   accuracy_decimals: 2
  #   update_interval: $boucle_asservissement
  #   lambda: return id(accel_y).state + $offset_accY;
  # - platform: template
  #   id: accel_z_corrected
  #   name: "MPU Accel Z with offset"
  #   unit_of_measurement: m/s²
  #   accuracy_decimals: 2
  #   update_interval: $boucle_asservissement
  #   lambda: return id(accel_z).state + $offset_accZ;

  # Description
  #   Calcul de l'angle des panneaux par rapport à l'horizontal sur l'axe E/O.
  #   Ce capteur est calculé à la fréquence définie par la variable
  #   'boucle_asservissement' , souvent très rapide, quelques millsecondes. Pour
  #   éviter la surcharge du WiFi, on les garde en interne de l'ESP par
  #   'internal: $KEEP_INTERNE'. Si l'angle calculé n'est pas compris entre les
  #   butées logicielles 'seuil_manuel_min_roulis'et 'seuil_manuel_max_roulis',
  #   le mode manuel est activé, ce qui inhibe l'asservissement. Ceci pour
  #   protéger la mécanique des vérins si, par exemple, le capteur tombe.
  # Utilisation :
  #   - Remonté dans HA via le capteur 'PV roulis'
  #   - Par le capteur 'OffsetRoll'
  - platform: template
    id: PV_roulis
    name: Roulis PV
    internal: $KEEP_INTERNE
    unit_of_measurement: °
    update_interval: $boucle_asservissement
    lambda: return atan2(id(accel_y).state, id(accel_z).state)*180/$PI;
    filters:
      - offset : $offset_roulis
    on_value_range:
      - above: $seuil_manuel_max_roulis
        then:
          - switch.turn_on: manual_mode
      - below: $seuil_manuel_min_roulis
        then:
          - switch.turn_on: manual_mode
            
  # Description
  #   Calcul de l'angle des panneaux par rapport à l'horizontal sur l'axe N/S.
  #   Ce capteur est calculé à la fréquence définie par la variable
  #   'boucle_asservissement', souvent très rapide, quelques millsecondes. Pour
  #   éviter la surcharge du WiFi, on les garde en interne de l'ESP par
  #   'internal: $KEEP_INTERNE'. Si l'angle calculé n'est pas compris entre les
  #   butées logicielles 'seuil_manuel_min_tangage' et 'seuil_manuel_max_tangage',
  #   le mode manuel est activé, ce qui inhibe l'asservissement. Ceci pour
  #   protéger la mécanique des vérins si, par exemple, le capteur tombe.
  # Utilisation :
  #   - Remonté dans HA via le capteur 'PV tangage'
  #   - Par le capteur 'OffsetPitch'
  - platform: template
    id: PV_tangage
    name: Tangage PV
    internal: $KEEP_INTERNE
    unit_of_measurement: °
    update_interval: $boucle_asservissement
    lambda: return atan2(id(accel_x).state, id(accel_z).state)*180/$PI;
    filters:
      - offset : $offset_tangage
    on_value_range:
      - above: $seuil_manuel_max_tangage
        then:
          - switch.turn_on: manual_mode
      - below: $seuil_manuel_min_tangage
        then:
          - switch.turn_on: manual_mode

  # Description
  #   Ces capteurs sont des copies des capteurs qui sont remonter dans HA à la
  #   fréquence choisie par la variable 'boucle_mesure'
  # Utilisation :
  #   - Remontés dans HA
  - platform: template
    name: Roulis PV
    unit_of_measurement: °
    accuracy_decimals: 1
    update_interval: $boucle_mesure
    lambda: return id(PV_roulis).state;

  - platform: template
    name: Tangage PV
    unit_of_measurement: °
    accuracy_decimals: 1
    update_interval: $boucle_mesure
    lambda: return id(PV_tangage).state;

  - platform: template
    name: MPU température
    device_class: temperature
    update_interval: 60s
    lambda: return id(MPU_temp).state;

  - platform: template
    name: Vitesse vent
    unit_of_measurement: Km/h
    accuracy_decimals: 0
    update_interval: $boucle_mesure
    lambda: return id(vent_kmh).state;

  # Description
  #   Calcul de l'offset du roulis entre la valeur du soleil et la valeur des
  #   panneaux. Ce calcul est effectué à la fréquence prévue par la variable
  #   'boucle_asservissement'. L'offset calculé est transmis au script RollPilot
  #   uniquement si le fonctionnement n'est pas en mode manuel.
  #   Ce calcul n'est pas remonté à HA ('internal: $KEEP_INTERNE')
  # Utilisation :
  #   - script RollPilot
  - platform: template
    id: OffsetRoll
    name: Roulis Offset
    internal: $KEEP_INTERNE
    update_interval: $boucle_asservissement
    lambda: return id(PV_roulis).state - id(sunRoulis).state;
    on_value: 
      - if:
          condition:
            - switch.is_off: manual_mode # si ON alors pas de pilotage
          then:
            - lambda: id(RollPilot)->execute(x);

  # Description
  #   Calcul de l'offset du tangage entre la valeur du soleil et la valeur des
  #   panneaux. Ce calcul est effectué à la fréquence prévue par la variable
  #   'boucle_asservissement'. L'offset calculé est transmis au script PitchPilot
  #   uniquement si le fonctionnement n'est pas en mode manuel.
  #   Ce calcul n'est pas remonté à HA ('internal: $KEEP_INTERNE')
  # Utilisation :
  #   - script PitchPilot
  - platform: template
    id: OffsetPitch
    name: Tangage Offset
    internal: $KEEP_INTERNE
    update_interval: $boucle_asservissement
    lambda: return id(PV_tangage).state - id(sunTangage).state;
    on_value: 
      - if:
          condition:
            - switch.is_off: manual_mode # si ON alors pas de pilotage
          then:
            - lambda: id(PitchPilot)->execute(x);

  # Description
  #   Récupération de l'élévation du soleil en bornant sa valeur via le filtre
  #   'clamp'. Toute valeur inférieure sera bornée par la valeur de 'min_value'.
  #   Toute valeur supérieure sera bornée par la valeur de 'max_value'.
  #   Les valeurs de 'min_value' et 'mxa_value' sont à définir en fonction de
  #   l'emplacement géographique du panneau.
  #   Ce calcul n'est pas remonté à HA ('internal: $KEEP_INTERNE')
  # Utilisation :
  #   - capteurs sunX, sunY et sunZ
  - platform: sun
    name: Sun Elevation
    id: sunElevation
    type: elevation
    internal: $KEEP_INTERNE
    filters: 
#      - lambda: |-
#          if(isnan(x)){return {};}
#          else{return x;}
      - clamp:
          min_value: 05
          max_value: 89
          ignore_out_of_range: False
      
  # Description
  #   Récupération de l'azimuth du soleil en bornant sa valeur via le filtre
  #   'clamp'. Toute valeur inférieure sera bornée par la valeur de 'min_value'.
  #   Toute valeur supérieure sera bornée par la valeur de 'max_value'.
  #   Les valeurs de 'min_value' et 'mxa_value' sont à définir en fonction de
  #   l'emplacement géographique du panneau.
  #   Ce calcul n'est pas remonté à HA ('internal: $KEEP_INTERNE')
  # Utilisation :
  #   - capteurs sunX et sunY
  - platform: sun
    name: Sun Azimuth
    id: sunAzimuth
    type: azimuth
    internal: $KEEP_INTERNE
    filters: 
#      - lambda: |-
#          if(isnan(x)){return {};}
#          else{return x;}
      - offset: $offset_azimuth
      - clamp:
          min_value: 60
          max_value: 310
          ignore_out_of_range: False

  # Description
  #   Calcul de la position du soleil dans le même repère que le capteur MPU6050.
  #   Ce calcul est effectué à la fréquence choisie par la variable 'boucle_mesure'
  #   Ce calcul n'est pas remonté à HA ('internal: $KEEP_INTERNE')
  # Utilisation :
  #   - capteurs sunRoulis et sunTangage
  - platform: template
    id: sunX
    name: sunX
    internal: $KEEP_INTERNE
    lambda: return  cos(id(sunElevation).state*$PI/180)*cos(id(sunAzimuth).state*$PI/180);
    update_interval: $boucle_mesure

  - platform: template
    id: sunY
    name: sunY
    internal: $KEEP_INTERNE
    lambda: return  -cos(id(sunElevation).state*$PI/180)*sin(id(sunAzimuth).state*$PI/180);
    update_interval: $boucle_mesure

  - platform: template
    id: sunZ
    name: sunZ
    internal: $KEEP_INTERNE
    lambda: return  sin(id(sunElevation).state*$PI/180);
    update_interval: $boucle_mesure

  # Description
  #   Calcul de la position sur le roulis du soleil en dégré en bornant sa
  #   valeur via le filtre 'clamp'. Si le mode repos/tempête est activé, le
  #   calcul renverra la position de repos/tempête de roulis. Toute valeur
  #   inférieure sera bornée par la valeur de 'angle_min_roulis'. Toute valeur
  #   supérieure sera bornée par la valeur de 'angle_max_roulis'. Les valeurs
  #   de 'angle_min_roulis' et 'angle_max_roulis' sont les butées logicielles
  #   légérement inférieures au butées physiques définies par
  #   'seuil_manuel_min_roulis' et 'seuil_manuel_max_roulis'. Si le soleil est
  #   en dehors de ces butées logicielles, l'asservissement est toujours actif
  #   mais en butée.
  #   Ce calcul est effectué à la fréquence de 10.1s (soit la variable boucle_messure + 0.1s)
  #   Ce calcul est remonté à HA ('internal: ${PUSHED_TO_HA}')
  # Utilisation :
  #   - capteurs OffsetRoll
  - platform: template
    id: sunRoulis
    name: Roulis soleil
    unit_of_measurement: °
    internal: ${PUSHED_TO_HA}
    update_interval: 10.1s
    lambda: |-
      if (id(storm_mode).state) return {$repos_roulis}; // Angle azimuth repos
      return -(-acos(id(sunY).state/sqrt( pow( id(sunY).state , 2) + pow( id(sunZ).state , 2)))*180/$PI+90);
    filters: 
      - clamp:
          min_value: $angle_min_roulis
          max_value: $angle_max_roulis
          ignore_out_of_range: False

  # Description
  #   Calcul de la position sur le tangage du soleil en dégré en bornant sa
  #   valeur via le filtre 'clamp'. Si le mode repos/tempête est activé, le
  #   calcul renverra la position de repos/tempête de tangage. Toute valeur
  #   inférieure sera bornée par la valeur de 'angle_min_tangage'. Toute valeur
  #   supérieure sera bornée par la valeur de 'angle_max_tangage'. Les valeurs
  #   de 'angle_min_tangage' et 'angle_max_tangage' sont les butées logicielles
  #   légérement inférieures au butées physiques définies par
  #   'seuil_manuel_min_tangage' et 'seuil_manuel_max_tangage'. Si le soleil est
  #   en dehors de ces butées logicielles, l'asservissement est toujours actif
  #   mais en butée.
  #   Ce calcul est effectué à la fréquence de 10.1s (soit la variable boucle_messure + 0.1s)
  #   Ce calcul est remonté à HA ('internal: ${PUSHED_TO_HA}')
  # Utilisation :
  #   - capteurs OffsetPitch
  - platform: template
    id: sunTangage
    name: Tangage soleil
    unit_of_measurement: °
    internal: ${PUSHED_TO_HA}
    update_interval: 10.15s
    lambda: |-
      if (id(storm_mode).state) return {$repos_tangage}; // Angle elevation repos
      return asin(id(sunX).state)/sqrt( pow( id(sunX).state , 2) + pow( id(sunZ).state , 2))*180/$PI;
    filters: 
      - clamp:
          min_value: $angle_min_tangage
          max_value: $angle_max_tangage
          ignore_out_of_range: False
    
  - platform: internal_temperature
    name: "ESP Temperature"

  # Description :
  #   Calcul de la vitesse du vent en Km/h
  #   Cette vitesse du vent permet la mise en mode tempête (traqueur en position
  #   de sécurité).
  # Utilisation :
  #   - calcul de la vitesse du vent 'vent_kmh'
  - platform: template
    id: vent_kmh
    unit_of_measurement: Km/h
    internal: $KEEP_INTERNE
    update_interval: $boucle_mesure #SCORPIX surcharge CPU (avant : $boucle_asservissement) tu remettras plus rapide quand tu auras trouvé comment fonctionne ton anémomètre
    lambda: return (id(anemo_tension).state)*$rapport_kmh_v;
    filters: 
    - clamp:
        min_value: 5
        max_value: 25               # 150
        ignore_out_of_range: False
    on_value_range:
      - above: 30
        then:
          - switch.turn_on: storm_mode

  # Description :
  #   Calcul de la vitesse du vent en Km/h
  #   Cette vitesse du vent permet la mise en mode tempête (traqueur en position
  #   de sécurité).
  # Utilisation :
  #   - calcul de la vitesse du vent 'vent_kmh'
  - platform: template
    name: Vent_kmh
    accuracy_decimals: 0
    unit_of_measurement: Km/h
    update_interval: $boucle_mesure #SCORPIX surcharge CPU (avant : $boucle_asservissement)
    lambda: return id(vent_kmh).state;

# Description :
#   Déclaration des boutons
switch:
  # Description :
  #   Déclaration du bouton pour passer en mode manuel. Le passage en mode
  #   manuel coupera l'asservissement automatique des panneaux.
  #   Le mode 'optimistic: True' indique que action sur ce bouton lance
  #   immédiatement la mise à jour de son état
  # Utilisation :
  #   - OffsetRoll, OffsetPitch
  #   - PV_Roulis, PV_Tangage
  - platform: template
    id: manual_mode
    optimistic: True
    name: "Mode manuel"
    icon: "mdi:hand-back-right-outline"
    on_turn_on:
    - fan.turn_off: tangage_verin
    - fan.turn_off: roulis_verin

  # Description :
  #   Déclaration du bouton pour passer en mode repos/tempête. Le passage en
  #   mode repos/tempête enverra une position fictive du soleil qui forcera le
  #   panneau à se mettre en position repos. Le mode 'optimistic: True'
  #   indique que action sur ce bouton lance immédiatement la mise à jour de son état.
  # Utilisation :
  #   - sunRoulis, sunTangage
  - platform: template
    id: storm_mode
    name: "Mode nuit/intemperie"
    icon: "mdi:weather-lightning-rainy"
    optimistic: True
    on_turn_on:
    - lambda: return id(sunTangage).publish_state($repos_tangage);  # Angle elevation repos
    - lambda: return id(sunRoulis).publish_state($repos_roulis);    # Angle azimuth repos

# Description :
#   Récupération de l'heure de Home Assistant.
# Utilisation :
#   Obligatoire pour la récupération de la classe Sun
time:
  - platform: homeassistant

# Description :
#   Récupération de la position du soleil.
# Utilisation :
#   Utiliser pour les capteurs sunElevation et sunAzimuth
sun:
  latitude: ${HOME_LAT}
  longitude: ${HOME_LON}
  id: my_sun
  on_sunrise:
    - then:
      - switch.turn_off: storm_mode       # Asservir vers position du levée du soleil
  on_sunset:
    - then:
      - switch.turn_on: storm_mode        # Mettre en position de repos/vent à presque horizontal, avec légère pente pour écoulement pluie
      - number.to_min: number_roulis      # RàZ le soir
      - number.to_min: number_tangage     # RàZ le soir

J’espère que les commentaires suffiront à configurer correctement votre installation.

Un grand merci @Scorpix pour son travail :clap: :clap:

Pour l’upgrade d’un traqueur Eco-Worthy complet (matériel compris), je vais faire un article dans ce fil de discussion : Traqueur Solaire