Tracker solaire DIY perturbé par perte Wifi

Bonjour à tous,

J’ai tout câbler comme sur le schéma de câblage précédent :

Voici la photo :

Mais impossible de faire rentrer le vérin de tangage (brancher sur le vérin A), et quand je regarde les + et - du vérin au bornier du DBH-12V canal A, j’ai toujours une tension positive qui varie en fonction de l’inclinaison de l’IMU mais toujours positive (évidemment, si je change de sens les pointes de mon multimètre, ça devient négatif :wink:)

Voici mon code :

Code du traqueur solaire à base de MPU6050 et DBH-12V
# 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

  # Variables de l'installation
  repos_tangage: '-30.0'          # angle tangage repos mode Nuit/Tempête par rapport à l'horizontal
  repos_roulis: '-20.0'           # angle roulis repos mode Nuit/Tempête par rapport à l'horizontal
  precision_offset: '2.0'         # angle cône de suivi du soleil
  max_speed_offset: '12.0'        # si la consigne est à plus de 12°, vitesse vérin à 100%
  offset_tangage: '0.0'           # permet d'ajuster des erreurs sur le tangage
  offset_roulis: '0.0'            # permet d'ajuster des erreurs sur le roulis
  boucle_asservissement: '100ms'  # vitesse de la boucle d'asservissement
  boucle_mesure: '10s'            # vitesse de la boucle de mesure des roulis et tangage
  offset_azimuth: '-5.0'          # mon système fonctionne qui si tu as l'axe du roulis colinéaire avec l'axe Nord-Sud, ça permet d'ajuster l'erreur
  offset_accX: '0.11'             # Calibration grossière de l'IMU, à faire sur table nivelé au tout début
  offset_accY: '-0.28'            # Calibration grossière de l'IMU, à faire sur table nivelé au tout début
  offset_accZ: '-0.54'            # Calibration grossière de l'IMU, à faire sur table nivelé au tout début
  vitesse_mini_verins: '60'       # Vitesse minimales d'approche des vérins
  angle_min_roulis: '-60.0'       # Butée physique de l'angle minimum en degré que peut atteindre le roulis
  angle_max_roulis: '60.0'        # Butée physique de l'angle maximum en degré que peut atteindre le roulis
  angle_min_tangage: '35.0'       # Butée physique de l'angle minimum en degré que peut atteindre le tangage
  angle_max_tangage: '53.0'       # Butée physique de l'angle maximum en degré que peut atteindre le tangage
  offset_anemo: 0                 # Calibration 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)

  # CONSTANTES
  PUSHED_TO_HA: 'False'
  KEEP_INTERNE: 'True'            # Basculer cette constante à False pour faire remonter tous les sensors dans HA
  PI: '3.14159265359'
  
  # GPIO
  GPIO_SDA: '21'
  GPIO_SCL: '22'
  GPIO_TANGAGE_FORWARD: GPIO33    # GPIO utilisé pour l'avance du vérin E/O, à brancher sur IN1A
  GPIO_TANGAGE_REVERSE: GPIO32    # GPIO utilisé pour le recul du vérin E/O, à brancher sur IN2A
  GPIO_ROULIS_FORWARD: GPIO26     # GPIO utilisé pour l'avance du vérin N/S, à brancher sur IN1B
  GPIO_ROULIS_REVERSE: GPIO25     # GPIO utilisé pour le recul du vérin N/S, à brancher sur IN2B
  GPIO_ANEMOMETRE: GPIO34         # GPIO utilisé pour le branchement de la borne + de l'anémomètre
  FREQ_PWM: '20000Hz'

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



# 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

# Définition de la communication avec le H-Bridge DBH-12V
output:
  - platform: ledc
    id: tangage_forward_pin
    pin: 
      number: ${GPIO_TANGAGE_FORWARD}                 # GPIO39
    frequency: ${FREQ_PWM}                            # 50000 Hz
  - platform: ledc
    id: tangage_reverse_pin
    pin: 
      number: ${GPIO_TANGAGE_REVERSE}                 # GPIO37
    frequency: ${FREQ_PWM}                            # 50000 Hz
  - platform: ledc
    id: roulis_forward_pin
    pin: 
      number: ${GPIO_ROULIS_FORWARD}                 # GPIO33
    frequency: ${FREQ_PWM}                            # 50000 Hz
  - platform: ledc
    id: roulis_reverse_pin
    pin: 
      number: ${GPIO_ROULIS_REVERSE}                 # GPIO18
    frequency: ${FREQ_PWM}                            # 50000 Hz

# 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:
  - platform: hbridge
    id: tangage_verin
    icon: mdi:sun-compass
    name: "Verin tangage"
    pin_b: tangage_forward_pin
    pin_a: tangage_reverse_pin
    decay_mode: slow
    internal: ${PUSHED_TO_HA}
    on_turn_on: 
      then:
        - number.increment: number_tangage

  - platform: hbridge
    id: roulis_verin
    icon: mdi:sun-compass
    name: "Verin roulis"
    pin_b: roulis_forward_pin
    pin_a: roulis_reverse_pin
    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_offset) {
            int speed = 0;
            if(abs(offset)>$max_speed_offset) {
              speed = 100;
            }
            else {
              speed = ${vitesse_mini_verins} + (40*abs(offset)/$max_speed_offset);
            }
            if (offset>0) {
              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_on();
              call.set_speed(speed);
              call.set_direction(FanDirection::FORWARD);
              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_offset) {
            int speed = 0;
            if(abs(offset)>$max_speed_offset) {
              speed = 100;
            }
            else {
              speed = ${vitesse_mini_verins} + (40*abs(offset)/$max_speed_offset);
            }
            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();
          }

# 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. Il peuvent être remontés à HA via l'attribut
#   'internal' en le passant de 'KEEP_INTERNE' à 'PUSHED_TO_HA'
number:
  - 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);"
  - 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);"

# Déclaration de capteurs
# Ici sont récupérées les données de l’IMU, qui restent en interne pour ne pas
# surcharger la liaison wifi avec HA.
# Un peu de filtrage avec « sliding_window_moving_average » pour éviter les actions
# des vérins sans cesse à la moindre vibration.
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_asservissement

  # 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.
  - platform: mpu6050
    address: 0x68
    update_interval: 500ms

    # 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: "solar-tracker Accel X"
      filters:
       - sliding_window_moving_average:
          window_size: 10
          send_every: 5
    accel_y:
      id: accel_y
      name: "solar-tracker Accel Y"
      filters:
       - sliding_window_moving_average:
          window_size: 10
          send_every: 5
    accel_z:
      id: accel_z
      name: "solar-tracker Accel Z"
      filters:
       - 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: "solar-tracker 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: "solar-tracker 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: "solar-tracker 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: "solar-tracker 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: "solar-tracker 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: "solar-tracker 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 'angle_min_roulis'
  #   'angle_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: PV roulis
    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:                       # si calcul d'un angle supérieur au réalisable mécanique, alors problème, arrêt de l'asservissement
      - above: $angle_max_roulis          # 60.0 
        then:
          - switch.turn_on: manual_mode
      - below: $angle_min_roulis          # -60.0
        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 'angle_min_tangage'
  #   'angle_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: PV tangage
    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:                       # si calcul d'un angle supérieur au réalisable mécanique, alors problème, arrêt de l'asservissement
      - above: $angle_max_tangage         # 35.0
        then:
          - switch.turn_on: manual_mode
      - below: $angle_min_tangage         # -53.0
        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: PV roulis
    unit_of_measurement: °
    accuracy_decimals: 1
    update_interval: $boucle_mesure
    lambda: return id(PV_roulis).state;

  - platform: template
    name: PV tangage
    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: Offset Roulis
    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 # vérification du mode manuel, 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: Offset Tangage
    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 # vérification du mode manuel, 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 'max_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 'min_value'.
  #   Toute valeur supérieure sera bornée par la valeur de 'max_value'.
  #   Les valeurs de 'min_value' et 'max_value' sont les butées logicielles légérement inférieures
  #   au butées physiques définies par 'angle_min_roulis' et 'angle_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: sunRoulis
    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: -52
          max_value: 52
          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 'min_value'.
  #   Toute valeur supérieure sera bornée par la valeur de 'max_value'.
  #   Les valeurs de 'min_value' et 'max_value' sont les butées logicielles légérement inférieures
  #   au butées physiques définies par 'angle_min_tangage' et 'angle_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: sunTangage
    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: -50
          max_value: 30
          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_asservissement
    lambda: return (id(anemo_tension).state)*$rapport_kmh_v;
    filters: 
    - clamp:
        min_value: 5
        max_value: 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_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: 49.49147
  longitude: 0.44665
  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

Désolé pour la longueur, mais ne sachant pas pourquoi je n’arrive pas à inverser le sens de la tension au bornes du DBH-12V, j’ai préféré tout mettre.

Si vous préférez que je partage juste une partie, n’hésitez pas à m’indiquer laquelle vous souhaitez.

Pour info, dans HA, je vois bien les cards Fan des 2 vérins et lorsque je bouge l’IMU, on voit bien l’inversion des sens sur la carte avec les vitesses qui diminuent à l’approche de la consigne et l’inversion, une fois la consigne dépassée.

Tu as la datasheet ?

En recherchant rapidement, j’ai trouvé un exemple :
How to Use DBH-12 H Bridge Motor Driver: Pinouts, Specs, and Examples | Cirkit Designer

J’ai l’impression que IN1A et IN2A doivent être utilisé en LOW ou HIGH pour choisir le sens, et ENA pour le PMW pour la vitesse du moteur.
Du coup idem pour IN1B, IN2B et ENB.

Pour essayer ça, faut retoucher le code un petit peu.

Dans H-bridge Fan — ESPHome
Tu as l’option ENABLE_PIN.
ça ressemble bien à ton cas.
Essaie de l’ajouter et de brancher ton GPIO là dessus.

Bonjour, je confirme qu’il faut raccorder les entrées « Enable » au +5V, sinon les sorties du pont en H sont désactivées.

J’ai ajouter le enable_pin dans la configuration du fan mais d’après ce que je comprend, je dois indiquer l’id d’un output:thinking:

Comment je dois renseigner ces 2 nouveaux output ?
Est-ce que c’est aussi sur une platform ledc ?
Est-ce qu’il faut aussi une fréquence sur ces sorties ?

un truc dans ce genre ?

# Définition de la communication avec le H-Bridge DBH-12V
output:
  - platform: ledc
    id: tangage_forward_pin
    pin: GPIO13
    frequency: ${FREQ_PWM}
  - platform: ledc
    id: tangage_reverse_pin
    pin: GPIO14
    frequency: ${FREQ_PWM}
  - platform: ledc
    id: roulis_forward_pin
    pin: GPIO12
    frequency: ${FREQ_PWM}
  - platform: ledc
    id: roulis_reverse_pin
    pin: GPIO27
    frequency: ${FREQ_PWM}
   - platform: ledc
    id: tangage_enable_pin
    pin: GPIO26
    frequency: ${FREQ_PWM}
  - platform: ledc
    id: roulis_enable_pin
    pin: GPIO25
    frequency: ${FREQ_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:
  - platform: hbridge
    id: tangage_verin
    icon: mdi:sun-compass
    name: "Verin tangage"
    pin_b: tangage_forward_pin
    pin_a: tangage_reverse_pin
    enable_pin: tangage_enable_pin
    decay_mode: slow
    internal: ${PUSHED_TO_HA}
    on_turn_on: 
      then:
        - number.increment: number_tangage

  - platform: hbridge
    id: roulis_verin
    icon: mdi:sun-compass
    name: "Verin roulis"
    pin_b: roulis_forward_pin
    pin_a: roulis_reverse_pin
    enable_pin: roulis_enable_pin
    decay_mode: slow 
    internal: ${PUSHED_TO_HA}
    on_turn_on: 
      then:
        - number.increment: number_roulis

Avec ce code et l’ajout des pin EN, lorsque je passe en mode manuel sur HA avec la carte Fan, comme ceci, je vois 12.04v aux bornes du DBH-12V motor A quelque soit la position du slider :

Et si je cliques sur le bouton d’inversion du sens, je vois 1.51v (que je peux faire varier avec le slider :


Ce qui est étonnant, c’est qu’à 4% par exemple, j’ai 11.44v et à 100%, j’ai 0v

Bonjour,

Si ça peut aider voici le code de mes ponts en H qui fonctionne parfaitement :

output:
  - platform: ledc
    id: NS_forward_pin
    pin: 32
    frequency: 30000 Hz
  - platform: ledc
    id: NS_reverse_pin
    pin: 33
    frequency: 30000 Hz
  - platform: ledc
    id: EO_forward_pin
    pin: 26
    frequency: 30000 Hz
  - platform: ledc
    id: EO_reverse_pin
    pin: 27
    frequency: 30000 Hz

fan:
  - platform: hbridge
    id: NS_verin
    icon: mdi:sun-compass
    name: "Verin NS"
    pin_b: NS_forward_pin
    pin_a: NS_reverse_pin
    decay_mode: slow
    internal: False
    on_turn_on: 
      then:
        - number.increment: cycle_NS

  - platform: hbridge
    id: EO_verin
    icon: mdi:sun-compass
    name: "Verin EO"
    pin_b: EO_forward_pin
    pin_a: EO_reverse_pin
    decay_mode: slow 
    internal: False
    on_turn_on: 
      then:
        - number.increment: cycle_EO

Les entrées enable ne sont pas dans le code, il suffit normalement de les raccorder au +VCC.

Quand tu parles du « +VCC », c’est celui de l’ESP32 ou du DBH12V ?

Parce que celui du DBH12V, c’est du 12v dans mon cas

C’est bien celui de l’esp, +5V

Bon, j’ai essayé avec le code Arduino de cette page : How to Use DBH-12 H Bridge Motor Driver: Pinouts, Specs, and Examples | Cirkit Designer, sans le DBH-12V connecté, j’ai bien des valeurs cohérentes aux bornes de l’ESP, dés que je branche le DBH-12V, les tensions sont complètement étranges… :thinking:
Je soupçonne que le DBH-12V a pris un coup lors d’un problème sur le branchement d’un LM2596 qui a fait un « clac » à sa mise sous tension. Je l’avais remplacé mais apparemment, le problème était surement plus important…
Je recommande un autre DBH-12V et je vérifierai avec le nouveau.

J’aurai bien pris 2 x DVR8871 comme @Scorpix mais ils n’acceptent que 3.6A en crête et mes vérins sont annoncés pour 10A en crête, et pour les ponts H 43A de @Julien_Galliot, ils sont un peu plus compliqués à intégrer avec leur radiateur sous la carte, je devrais redessiner et imprimer le nouveau support de carte (peut-être le plan B si le DBH-12V ne fonctionne pas)

Donc obliger d’attendre 15 jours, le temps que la carte me soit livrée, :sob:

Bonjour à tous,

Nouvelle carte DBH-12V reçue, donc on reprend les essais…

Après avoir reflashé un autre ESP32 avec le code suivant, j’ai vu la chose suivante au multimètre (pas d’oscillo désolé) :

  • entre GND et GPIO13 : +3.33v =2s=> 0v =2s=> +3.33v =2s=> 0v …
  • entre GND et GPIO14 : 0v =2s=> +3.33v =2s=> 0v =2s=> +3.33v …
  • entre GND et GPIO26 : +1.65v constant
  • entre GPIO13 et GPIO14 : +3.33v =2s=> +3.33v =2s=> +3.33v =2s=> -3.33v …

Ça semble cohérent avec le code :thinking:

code de l'ESP32
// Define motor driver pins
#define IN1 13
#define IN2 14
#define ENA 26 // PWM pin for speed control

void setup() {
  // Set motor driver pins as outputs
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(ENA, OUTPUT);
}

void loop() {
  // Set motor A direction to forward
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  // Set speed to 50%
  analogWrite(ENA, 127);
  delay(2000); // Run for 2 seconds

  // Set motor A direction to reverse
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  // Set speed to 50%
  analogWrite(ENA, 127);
  delay(2000); // Run for 2 seconds

  // Stop motor A
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  delay(1000); // Stop for 1 second
}

Maintenant, il faut que je câble l’ESP32 avec le DBH-12V sans avoir d’erreur :sweat:

Avant de cabler, j’ai voulu voir les tensions aux bornes du DBH-12V lorsqu’il est alimenter en 12v sur PWR+ et PWR- :

  • entre GNDA et IN1A : 0v
  • entre GNDA et IN2A : 0v
  • entre GNDA et ENA : 13.67v :scream:

Etrange effectivement, tu as la datasheet pour vérifier ?

Je n’ai trouver que ça :

J’espère que ça peut aider

La datasheet du IR2104(S)
Il semble accepter des tensions jusqu’à 12Vdc.

Ce qui veut dire que c’est normal, la tension de 13.67v sur la borne ENA lorsque j’alimente le DBH-12V en 12v ?

Le +13.8V est fait par le Step Up MC34063A, qui alimente les autres modules.

ça semble bizarre que tu retrouves cette tension aux bornes de EN.

Quand tu as fait tes mesures, tu avais alimenté en 5VDC côté gauche sur les entrées (V+ et GND)?

Non, j’avais juste alimenter en 12v sur le PWR+ et -

Et je peux pas alimenter sur le V+, car si j’alimente le PWR en 12v, je retrouve 12v sur le V+/GND.
L’ESP32 n’accepte que du 3.3v sur ces GPIO.
Je relie le GND du DBH-12V à l’ESP32 par un de ces GPIO GND, et uniquement les IN1/IN2 et apparemment les EN.

Voici le schéma de câblage :

Et mon code complet
# 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

  # Variables de l'installation
  repos_tangage: '-30.0'          # angle tangage repos mode Nuit/Tempête par rapport à l'horizontal
  repos_roulis: '-20.0'           # angle roulis repos mode Nuit/Tempête par rapport à l'horizontal
  precision_offset: '2.0'         # angle cône de suivi du soleil
  max_speed_offset: '12.0'        # si la consigne est à plus de 12°, vitesse vérin à 100%
  offset_tangage: '0.0'           # permet d'ajuster des erreurs sur le tangage
  offset_roulis: '0.0'            # permet d'ajuster des erreurs sur le roulis
  boucle_asservissement: '100ms'  # vitesse de la boucle d'asservissement
  boucle_mesure: '10s'            # vitesse de la boucle de mesure des roulis et tangage
  offset_azimuth: '-5.0'          # mon système fonctionne qui si tu as l'axe du roulis colinéaire avec l'axe Nord-Sud, ça permet d'ajuster l'erreur
  offset_accX: '0.11'             # Calibration grossière de l'IMU, à faire sur table nivelé au tout début
  offset_accY: '-0.28'            # Calibration grossière de l'IMU, à faire sur table nivelé au tout début
  offset_accZ: '-0.54'            # Calibration grossière de l'IMU, à faire sur table nivelé au tout début
  vitesse_mini_verins: '60'       # Vitesse minimales d'approche des vérins
  angle_min_roulis: '-60.0'       # Butée physique de l'angle minimum en degré que peut atteindre le roulis
  angle_max_roulis: '60.0'        # Butée physique de l'angle maximum en degré que peut atteindre le roulis
  angle_min_tangage: '35.0'       # Butée physique de l'angle minimum en degré que peut atteindre le tangage
  angle_max_tangage: '53.0'       # Butée physique de l'angle maximum en degré que peut atteindre le tangage
  offset_anemo: 0                 # Calibration 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)

  # CONSTANTES
  PUSHED_TO_HA: 'False'
  KEEP_INTERNE: 'True'            # Basculer cette constante à False pour faire remonter tous les sensors dans HA
  PI: '3.14159265359'
  
  # GPIO
  GPIO_SDA: '21'
  GPIO_SCL: '22'
  GPIO_TANGAGE_FORWARD: GPIO13    # GPIO utilisé pour l'avance du vérin E/O, à brancher sur IN1A D13
  GPIO_TANGAGE_REVERSE: GPIO14    # GPIO utilisé pour le recul du vérin E/O, à brancher sur IN2A D14
  GPIO_ROULIS_FORWARD: GPIO12     # GPIO utilisé pour l'avance du vérin N/S, à brancher sur IN1B D12
  GPIO_ROULIS_REVERSE: GPIO27     # GPIO utilisé pour le recul du vérin N/S, à brancher sur IN2B D27
  GPIO_TANGAGE_ENABLE: GPIO26     # GPIO utilisé pour l'activation du vérin E/O, à brancher sur ENA  D26
  GPIO_ROULIS_ENABLE: GPIO25      # GPIO utilisé pour l'activation du vérin N/S, à brancher sur ENB  D25
  GPIO_ANEMOMETRE: GPIO34         # GPIO utilisé pour le branchement de la borne + de l'anémomètre
  FREQ_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



# 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

# Définition de la communication avec le H-Bridge DBH-12V
output:
  - platform: ledc
    id: tangage_forward_pin
    pin: GPIO13
    frequency: ${FREQ_PWM}
  - platform: ledc
    id: tangage_reverse_pin
    pin: GPIO14
    frequency: ${FREQ_PWM}
  - platform: ledc
    id: roulis_forward_pin
    pin: GPIO12
    frequency: ${FREQ_PWM}
  - platform: ledc
    id: roulis_reverse_pin
    pin: GPIO27
    frequency: ${FREQ_PWM}
  - platform: ledc
    id: tangage_enable_pin
    pin: GPIO26
    frequency: ${FREQ_PWM}
  - platform: ledc
    id: roulis_enable_pin
    pin: GPIO25
    frequency: ${FREQ_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:
  - platform: hbridge
    id: tangage_verin
    icon: mdi:sun-compass
    name: "Verin tangage"
    pin_b: tangage_forward_pin
    pin_a: tangage_reverse_pin
    enable_pin: tangage_enable_pin
    decay_mode: slow
    internal: ${PUSHED_TO_HA}
    on_turn_on: 
      then:
        - number.increment: number_tangage

  - platform: hbridge
    id: roulis_verin
    icon: mdi:sun-compass
    name: "Verin roulis"
    pin_b: roulis_forward_pin
    pin_a: roulis_reverse_pin
    enable_pin: roulis_enable_pin
    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_offset) {
            int speed = 0;
            if(abs(offset)>$max_speed_offset) {
              speed = 100;
            }
            else {
              speed = ${vitesse_mini_verins} + (40*abs(offset)/$max_speed_offset);
            }
            if (offset>0) {
              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_on();
              call.set_speed(speed);
              call.set_direction(FanDirection::FORWARD);
              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_offset) {
            int speed = 0;
            if(abs(offset)>$max_speed_offset) {
              speed = 100;
            }
            else {
              speed = ${vitesse_mini_verins} + (40*abs(offset)/$max_speed_offset);
            }
            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();
          }

# 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. Il peuvent être remontés à HA via l'attribut
#   'internal' en le passant de 'KEEP_INTERNE' à 'PUSHED_TO_HA'
number:
  - 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);"
  - 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);"

# Déclaration de capteurs
# Ici sont récupérées les données de l’IMU, qui restent en interne pour ne pas
# surcharger la liaison wifi avec HA.
# Un peu de filtrage avec « sliding_window_moving_average » pour éviter les actions
# des vérins sans cesse à la moindre vibration.
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_asservissement

  # 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.
  - platform: mpu6050
    address: 0x68
    update_interval: 500ms

    # 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: "solar-tracker Accel X"
      filters:
       - sliding_window_moving_average:
          window_size: 10
          send_every: 5
    accel_y:
      id: accel_y
      name: "solar-tracker Accel Y"
      filters:
       - sliding_window_moving_average:
          window_size: 10
          send_every: 5
    accel_z:
      id: accel_z
      name: "solar-tracker Accel Z"
      filters:
       - 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: "solar-tracker 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: "solar-tracker 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: "solar-tracker 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: "solar-tracker 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: "solar-tracker 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: "solar-tracker 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 'angle_min_roulis'
  #   'angle_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: PV roulis
    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:                       # si calcul d'un angle supérieur au réalisable mécanique, alors problème, arrêt de l'asservissement
      - above: $angle_max_roulis          # 60.0 
        then:
          - switch.turn_on: manual_mode
      - below: $angle_min_roulis          # -60.0
        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 'angle_min_tangage'
  #   'angle_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: PV tangage
    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:                       # si calcul d'un angle supérieur au réalisable mécanique, alors problème, arrêt de l'asservissement
      - above: $angle_max_tangage         # 35.0
        then:
          - switch.turn_on: manual_mode
      - below: $angle_min_tangage         # -53.0
        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: PV roulis
    unit_of_measurement: °
    accuracy_decimals: 1
    update_interval: $boucle_mesure
    lambda: return id(PV_roulis).state;

  - platform: template
    name: PV tangage
    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: Offset Roulis
    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 # vérification du mode manuel, 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: Offset Tangage
    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 # vérification du mode manuel, 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 'max_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 'min_value'.
  #   Toute valeur supérieure sera bornée par la valeur de 'max_value'.
  #   Les valeurs de 'min_value' et 'max_value' sont les butées logicielles légérement inférieures
  #   au butées physiques définies par 'angle_min_roulis' et 'angle_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: sunRoulis
    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: -52
          max_value: 52
          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 'min_value'.
  #   Toute valeur supérieure sera bornée par la valeur de 'max_value'.
  #   Les valeurs de 'min_value' et 'max_value' sont les butées logicielles légérement inférieures
  #   au butées physiques définies par 'angle_min_tangage' et 'angle_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: sunTangage
    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: -50
          max_value: 30
          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_asservissement
    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_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: 49.49147
  longitude: 0.44665
  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

Au cas où, j’aurai raté un truc :thinking:

Alors, nouveau test avec le code et le câblage …

Test 1 :

Multimètre branché sur les bornes des vérins, mise en manuel sur HA.

Tests sur Vérin de Tangage :

Etape Capture Tension relevée
Vérin Tangage éteint sous HA 0v (normal :wink:)
Vérin Tangage allumé sous HA (clic sur le bouton en haut) 12.06v (pas normal car je suis à 33% :sob:)
Vérin Tangage allumé sous HA, clic sur inversion (bouton en bas à droite) 8.03v (pas complètement normal car 33% de 12v, ça fait 3.96v :unamused:)
Vérin Tangage allumé sous HA, déplacement du % 3.95v (pas complètement normal car 67% de 12v, ça fait 8.04v :thinking:, ça ressemble à 8.03v ce qui voudrait dire que le % est « inversé »)
Vérin Tangage allumé sous HA, clic sur inversion (bouton en bas à droite) 12.06v (pas normal car je suis à 67% de 12v, ça fait devrait faire 8.04v :unamused:)
Vérin Tangage éteint sous HA (clic sur le bouton en haut) 0v (normal :wink:)

Tests sur Vérin de Roulis:

Etape Capture Tension relevée
Vérin Roulis éteint sous HA 0v (normal :wink:)
Vérin Roulis allumé sous HA (clic sur le bouton en haut) -12.06v (pas normal car je suis à 68% :sob:)
Vérin Roulis allumé sous HA, clic sur inversion (bouton en bas à droite) -3.86v (pas complètement normal car 68% de -12v, ça fait -8.16v :unamused:)
Vérin Roulis allumé sous HA, déplacement du % -8.10v (pas complètement normal car 33% de -12v, ça fait -3.96v :thinking:, ça ressemble à -8.16v ce qui voudrait dire que le % est « inversé »)
Vérin Roulis allumé sous HA, clic sur inversion (bouton en bas à droite) -12.06v (pas normal car je suis à 33% de 12v, ça fait devrait faire -3.96v :unamused:)
Vérin Roulis éteint sous HA (clic sur le bouton en haut) 0v (normal :wink:)

En bref, j’ai l’impression que je pilote la tension que dans un sens sur chaque sortie au lieu des 2 sens … :thinking: et je ne comprends pas pourquoi…
J’ai bien câbler les pin ENA et ENB comme sur le schéma de câblage.

Avant de positionner tous à l’extérieur et câbler les vrai vérins, je préfèrerai comprendre le fonctionnement et notamment en manuel (le plus simple :grin:)

Si vous avez des idées, je suis preneur

Après une mauvaise nuit, je reprend mes tests …

Test 2 :

J’ai vu que, dans le code, mes pin_a et pin_b étaient inversés. D’après la doc du composant fan, le pin_a devrait être connecté au GPIO connecté au IN1 et pareil pour le pin_b sur le IN2.
J’ai donc corrigé le code :

Composant fan
# 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:
  - platform: hbridge
    id: tangage_verin
    icon: mdi:sun-compass
    name: "Verin tangage"
    pin_a: tangage_forward_pin
    pin_b: tangage_reverse_pin
    enable_pin: tangage_enable_pin
    decay_mode: slow
    internal: ${PUSHED_TO_HA}
    on_turn_on: 
      then:
        - number.increment: number_tangage

  - platform: hbridge
    id: roulis_verin
    icon: mdi:sun-compass
    name: "Verin roulis"
    pin_a: roulis_forward_pin
    pin_b: roulis_reverse_pin
    enable_pin: roulis_enable_pin
    decay_mode: slow 
    internal: ${PUSHED_TO_HA}
    on_turn_on: 
      then:
        - number.increment: number_roulis

Mais pareil … Je n’arrive pas à inverser le sens de la tension. J’arrive bien à gérer la variation de tension mais pas l’inverser.

Test 3 :

Autre test, j’ai vérifié au bornier de l’ESP32, j’ai bien, par exemple sur le vérin de tangage, un basculement de la tension 0v => 3.33v entre les GPIO13 (IN1A) et GPIO14 (IN2A) lorsque j’inverse le sens de rotation sur HA, et le GPIO26 (ENA) reste à 3.33v.
Idem pour le le vérin de roulis.

Test 4 :

Autre test (on est plus à 1 près), j’ai branché le DBH-12V sur un autre ESP32 dans lequel j’ai uploadé le code suivant :

Code du Cirkit Designer
// Define motor driver pins
#define IN1 13
#define IN2 14
#define ENA 26 // PWM pin for speed control

void setup() {
  // Set motor driver pins as outputs
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(ENA, OUTPUT);
}

void loop() {
  // Set motor A direction to forward
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  // Set speed to 50%
  analogWrite(ENA, 127);
  delay(2000); // Run for 2 seconds

  // Set motor A direction to reverse
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  // Set speed to 50%
  analogWrite(ENA, 127);
  delay(2000); // Run for 2 seconds

  // Stop motor A
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  delay(1000); // Stop for 1 second
}

Et j’ai bien l’inversion des sens de tension (passage de 10.7v => -10.7v => 10.7v …), donc ce n’est pas le DBH-12V qui est en cause.

Test 5 :

J’ai changé d’ESP32 et je lui ai uploadé un code simple :

Code simple
# 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

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



# Définition des sorties PWM pour le pont en H
output:
  # Pin A du pont en H (direction 1)
  - platform: ledc
    id: moteur_pin_a
    pin: GPIO13
    frequency: 40000Hz
    
  # Pin B du pont en H (direction 2)  
  - platform: ledc
    id: moteur_pin_b
    pin: GPIO12
    frequency: 40000Hz
    
  # Pin Enable du pont en H
  - platform: ledc
    id: moteur_enable
    pin: GPIO26
    frequency: 40000Hz

# Configuration du moteur via le composant fan hbridge
fan:
  - platform: hbridge
    id: moteur_principal
    name: "Moteur H-Bridge"
    pin_a: moteur_pin_a      # Direction avant (pins inversées)
    pin_b: moteur_pin_b      # Direction arrière (pins inversées)
    enable_pin: moteur_enable
    decay_mode: slow

# Boutons de contrôle manuel
button:
  # Bouton pour faire tourner le moteur vers l'avant
  - platform: template
    name: "Moteur Avant"
    on_press:
      - fan.turn_on:
          id: moteur_principal
          speed: 80
          direction: FORWARD

  # Bouton pour faire tourner le moteur vers l'arrière
  - platform: template
    name: "Moteur Arrière"
    on_press:
      - fan.turn_on:
          id: moteur_principal
          speed: 80
          direction: REVERSE

  # Bouton d'arrêt du moteur
  - platform: template
    name: "Arrêt Moteur"
    on_press:
      - fan.turn_off: moteur_principal

# Capteur de statut du moteur
binary_sensor:
  - platform: template
    name: "Moteur en marche"
    lambda: |-
      return id(moteur_principal).state;

Et sur le bouton Moteur Avant, j’ai +12v, mais sur Moteur Arrière, j’ai +2.4v.
Donc ça ne vient pas non plus de l’ESP :thinking:

Dans le code source Esphome : hbridge_fan

Au début il y a :

// constant IN1/IN2, PWM on EN => power control, fast current decay
// constant IN1/EN, PWM on IN2 => power control, slow current decay
void HBridgeFan::set_hbridge_levels_(float a_level, float b_level, float enable) {
  this->pin_a_->set_level(a_level);
  this->pin_b_->set_level(b_level);
  this->enable_->set_level(enable);
  ESP_LOGD(TAG, "Setting speed: a: %.2f, b: %.2f, enable: %.2f", a_level, b_level, enable);
}

Tu te trouves plutôt dans la première situation de configuration.
Essaies de mettre le paramètre DECAY_MODE à FAST (au lieu de slow)

Refais les tests, et prends note des LOG, ça permettra de voir comment sont pilotées les 3 sorties.

Voici les logs de mon test :

Avec les relevés au multimètre branché aux bornes du canal A du DBH-12V :

  • Moteur Avant pressé (11:34:50) : +11v
  • Arrêt Moteur pressé (11.34:54) : 0v
  • Moteur Arrière pressé (11:34:56) : 0v
Code utilisé pour le test
# 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: DEBUG
  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

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



# Définition des sorties PWM pour le pont en H
output:
  # Pin A du pont en H (direction 1)
  - platform: ledc
    id: moteur_pin_a
    pin: GPIO13
    frequency: 40000Hz
    
  # Pin B du pont en H (direction 2)  
  - platform: ledc
    id: moteur_pin_b
    pin: GPIO12
    frequency: 40000Hz
    
  # Pin Enable du pont en H
  - platform: ledc
    id: moteur_enable
    pin: GPIO26
    frequency: 40000Hz

# Configuration du moteur via le composant fan hbridge
fan:
  - platform: hbridge
    id: moteur_principal
    name: "Moteur H-Bridge"
    pin_a: moteur_pin_a
    pin_b: moteur_pin_b
    enable_pin: moteur_enable
    decay_mode: FAST

# Boutons de contrôle manuel
button:
  # Bouton pour faire tourner le moteur vers l'avant
  - platform: template
    name: "Moteur Avant"
    on_press:
      - fan.turn_on:
          id: moteur_principal
          speed: 80
          direction: FORWARD

  # Bouton pour faire tourner le moteur vers l'arrière
  - platform: template
    name: "Moteur Arrière"
    on_press:
      - fan.turn_on:
          id: moteur_principal
          speed: 80
          direction: REVERSE

  # Bouton d'arrêt du moteur
  - platform: template
    name: "Arrêt Moteur"
    on_press:
      - fan.turn_off: moteur_principal

# Capteur de statut du moteur
binary_sensor:
  - platform: template
    name: "Moteur en marche"
    lambda: |-
      return id(moteur_principal).state;

Après avoir épluché tous les posts possibles, je suis tombé sur un post qui indiquait un câblage un peu étrange.

Brancher le EN sur le 3.3v en permanence donc la sortie 3.3v (chose que @Julien_Galliot m’avait indiqué de faire mais sur le +5v, ce que j’avais peur car les GPIO sont en 3.3v).

Mon test :

Avec les relevés au multimètre branché aux bornes du canal A du DBH-12V :

  • Moteur Avant pressé (12:44:24) : -9.4v
  • Arrêt Moteur pressé (12.44:44) : 0v
  • Moteur Arrière pressé (12:44:53) : +9.4v
Avec ce code
# 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: DEBUG
  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

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



# Définition des sorties PWM pour le pont en H
output:
  # Pin A du pont en H (direction 1)
  - platform: ledc
    id: moteur_pin_a
    pin: GPIO04
    frequency: 40000Hz
    
  # Pin B du pont en H (direction 2)  
  - platform: ledc
    id: moteur_pin_b
    pin: GPIO05
    frequency: 40000Hz
    
  # Pin Enable du pont en H
  # - platform: ledc
  #   id: moteur_enable
  #   pin: GPIO26
  #   frequency: 40000Hz

# Configuration du moteur via le composant fan hbridge
fan:
  - platform: hbridge
    id: moteur_principal
    name: "Moteur H-Bridge"
    pin_a: moteur_pin_a
    pin_b: moteur_pin_b
    # enable_pin: moteur_enable
    decay_mode: SLOW

# Boutons de contrôle manuel
button:
  # Bouton pour faire tourner le moteur vers l'avant
  - platform: template
    name: "Moteur Avant"
    on_press:
      - fan.turn_on:
          id: moteur_principal
          speed: 80
          direction: FORWARD

  # Bouton pour faire tourner le moteur vers l'arrière
  - platform: template
    name: "Moteur Arrière"
    on_press:
      - fan.turn_on:
          id: moteur_principal
          speed: 80
          direction: REVERSE

  # Bouton d'arrêt du moteur
  - platform: template
    name: "Arrêt Moteur"
    on_press:
      - fan.turn_off: moteur_principal

# Capteur de statut du moteur
binary_sensor:
  - platform: template
    name: "Moteur en marche"
    lambda: |-
      return id(moteur_principal).state;

Ça semble fonctionner avec ce câblage…

Reste plus qu’à modifier tout ça pour intégrer le code complet de @Scorpix .

ce n’est pas intuitif, mais si ça fonctionne …
En tout cas étrange pour avoir 80%, de devoir faire une différence de 1.00 et 0.20
J’ai fait le test à l’oscilloscope, et quand je mets 80% dans un sens, j’ai un rapport cyclique de 0.8 sur l’une des sorties et 0 sur l’autre.
Mais peut être que le premier étage de comparaison sur ton DBH, fait ce job.