patrickcollins12 / esphome-fan-controller

ESPHome Fan Controller
363 stars 37 forks source link

[Request] Possible to use two temperature sensors? #5

Closed martijn-brant closed 2 years ago

martijn-brant commented 2 years ago

Thanks for this repo.. the code and schematics work great for me. My prototype is working great however I have a question.. is it possible to add another DHT temperature probe? My AV cabinets have two zones and I've added a fan in each. One is for the AV Receiver (always on when using TV) and the other is for gaming consoles (only on sometimes). I'd rather not build two separate fan controllers for two compartments. I don't mind both sides spinning faster when only one zone is needing more cooling (e.g. the gaming side being cooled even if there are no consoles active).

I had to reassign the DHT to GPIO23 on my nodemcu-32s board else it wouldn't work. PWM is on GPIO15. I've looking at the code as to what would be needed to support a 2nd DHT but the code is a bit too advanced for me. Ideally it would report two temperature values to Home Assistant as well and act on the worst one. But I would be more than pleased to get the worst value of the two as well. (one being 29c and the other 35c.. having it act like it's 35c and adjust fan accordingly).

Could you help with this? Many thanks.

patrickcollins12 commented 2 years ago

glad it worked for you!

Well you could easily run two separate systems off the one esp32: two pids, two dht, two fans. You would just copy all of those sections in the yaml file and give each a different name.

However, to answer your question directly. Yes you can take the max of two sensors to create a 3rd ”template” sensor. All three sensors will be visible to home assistant.

I’m not at my computer at the moment, so I can’t provide direct code.

This example creates a 3rd template sensor which is an average.

https://community.home-assistant.io/t/average-temperature-based-on-multiple-sensors/220781/3

to create a max instead the lambda is something like:

lambda: |-
      return max(id(dht_temp_1).state, id(dht_temp_2).state);

let me know how you go.

martijn-brant commented 2 years ago

Ah yes, great idea just to duplicate the YAML values for the sensors/outputs. I added an additional GPIO for extra PWM (Fan 2) and extra DHT11. Works like a charm. This gives me dual-zone option on a single Nodemcu. Thanks for your code and your reply.

B08Z commented 1 year ago

Ah yes, great idea just to duplicate the YAML values for the sensors/outputs. I added an additional GPIO for extra PWM (Fan 2) and extra DHT11. Works like a charm. This gives me dual-zone option on a single Nodemcu. Thanks for your code and your reply.

Hello,

Could you share you working yaml. I am about to start and may end up with 3 fans as they are a little small ( not ideal I know but got to work with what you got.)

Anyway if you could share how you did dual fans and dual dht temp that would be great

bartaspoz commented 11 months ago

Ah yes, great idea just to duplicate the YAML values for the sensors/outputs. I added an additional GPIO for extra PWM (Fan 2) and extra DHT11. Works like a charm. This gives me dual-zone option on a single Nodemcu. Thanks for your code and your reply.

Hey! I would be also interested in the code supporting two zones :) Many thanks!

martijn-brant commented 11 months ago

Ah sorry I missed your first message @B08Z .. @bartaspoz here is the code:

#based on https://github.com/patrickcollins12/esphome-fan-controller
substitutions:
  friendly_name: Console Fan

esphome:
  name: console-fan

esp32:
  board: nodemcu-32s
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "xxx"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Console-Fan Fallback Hotspot"
    password: "xxx"

captive_portal:

globals:
  - id: dhttemp
    type: float
    restore_value: yes
    initial_value: '0'

number:

  ## RECEIVE kp,ki and kd parameters from input_text.kx helpers in 
  # Home Assistant. See the PID controller below
  # These helper values will get saved to flash thus permanently over-riding 
  # the initial values set in the PID below.

  # KP
  - platform: template
    name: $friendly_name kp
    icon: mdi:chart-bell-curve
    restore_value: true
    min_value: 0
    max_value: 50
    step: 0.001
    set_action: 
      lambda: |- 
        // ESP_LOGI("main", "!!!!!! kp from boot %d", id("console_fan_kp") );
        // id(console_thermostat).set_kp( id("$friendly_name kp") );
        id(console_thermostat).set_kp( x );

  # KI
  - platform: template
    name: $friendly_name ki
    icon: mdi:chart-bell-curve
    restore_value: true
    min_value: 0
    max_value: 50
    step: 0.0001
    set_action: 
      lambda: id(console_thermostat).set_ki( x );

  # KD
  - platform: template
    name: $friendly_name kd
    icon: mdi:chart-bell-curve
    restore_value: true
    min_value: -50
    max_value: 50
    step: 0.001
    set_action: 
      lambda: id(console_thermostat).set_kd( x );

  # KP2
  - platform: template
    name: $friendly_name kp2
    icon: mdi:chart-bell-curve
    restore_value: true
    min_value: 0
    max_value: 50
    step: 0.001
    set_action: 
      lambda: |- 
        // ESP_LOGI("main", "!!!!!! kp from boot %d", id("console_fan_kp") );
        // id(console_thermostat).set_kp( id("$friendly_name kp") );
        id(console_thermostat2).set_kp( x );

  # KI2
  - platform: template
    name: $friendly_name ki2
    icon: mdi:chart-bell-curve
    restore_value: true
    min_value: 0
    max_value: 50
    step: 0.0001
    set_action: 
      lambda: id(console_thermostat2).set_ki( x );

  # KD2
  - platform: template
    name: $friendly_name kd2
    icon: mdi:chart-bell-curve
    restore_value: true
    min_value: -50
    max_value: 50
    step: 0.001
    set_action: 
      lambda: id(console_thermostat2).set_kd( x );

text_sensor:

  # Send IP Address
  - platform: wifi_info
    ip_address:
      name: $friendly_name IP Address

  # Send Uptime in raw seconds
  - platform: template
    name: $friendly_name Uptime
    id: uptime_human
    icon: mdi:clock-start

sensor:

  # Send WiFi signal strength & uptime to HA
  - platform: wifi_signal
    name: $friendly_name WiFi Strength
    update_interval: 60s

  # This is a bit of overkill. It sends a human readable 
  # uptime string
  # 1h 41m 32s instead of 6092 seconds
  - platform: uptime
    name: $friendly_name Uptime
    id: uptime_sensor
    update_interval: 60s
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human
            # Custom C++ code to generate the result
            state: !lambda |-
              int seconds = round(id(uptime_sensor).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? to_string(days) + "d " : "") +
                (hours ? to_string(hours) + "h " : "") +
                (minutes ? to_string(minutes) + "m " : "") +
                (to_string(seconds) + "s")
              ).c_str();

########################################################
# START THE FAN CONTROLLER SETUP

  - platform: template
    name: $friendly_name p term
    id: p_term
    unit_of_measurement: "%"
    accuracy_decimals: 2

  - platform: template
    name: $friendly_name i term
    id: i_term
    unit_of_measurement: "%"
    accuracy_decimals: 2

  - platform: template
    name: $friendly_name d term
    id: d_term
    unit_of_measurement: "%"
    accuracy_decimals: 2

  - platform: template
    name: $friendly_name output value
    unit_of_measurement: "%"
    id: o_term
    accuracy_decimals: 2

  - platform: template
    name: $friendly_name error value
    id: e_term
    accuracy_decimals: 2

  - platform: template
    name: $friendly_name zero 
    id: zero_value
    update_interval: 60s
    lambda: |-
      return 0;

  - platform: template
    name: $friendly_name zero percent
    unit_of_measurement: "%"
    id: zero_value_percent
    update_interval: 60s
    lambda: |-
      return 0;

  # GET TEMP/HUMIDITY FROM DHT11
  - platform: dht
    pin: GPIO21
    temperature:
      name: "Temperature"
      id: console_fan_temperature
      accuracy_decimals: 3

      # If you don't smooth the output readings 
      # the PID controller over reacts to small changes.
      filters:
        # - sliding_window_moving_average:
        #     window_size: 6
        #     send_every: 1
        #     send_first_at: 1
         - exponential_moving_average:  
             alpha: 0.1
             send_every: 1

    humidity:
      name: "Humidity"
      id: console_fan_humidity

    # the DHT11 can only be read every 1s. Use 1.3s to be safe.
    # Note that 15 x 1.3s means that the true temperature may 
    # take 19.5s to emerge
    update_interval: 1.3s

  - platform: dht
    pin: GPIO22
    temperature:
      name: "Temperature 2"
      id: console_fan_temperature2
      accuracy_decimals: 3

      # If you don't smooth the output readings 
      # the PID controller over reacts to small changes.
      filters:
        # - sliding_window_moving_average:
        #     window_size: 6
        #     send_every: 1
        #     send_first_at: 1
         - exponential_moving_average:  
             alpha: 0.1
             send_every: 1

    humidity:
      name: "Humidity 2"
      id: console_fan_humidity2

    # the DHT11 can only be read every 1s. Use 1.3s to be safe.
    # Note that 15 x 1.3s means that the true temperature may 
    # take 19.5s to emerge
    update_interval: 1.3s

  # Take the "COOL" value of the pid and send 
  # it to the frontend to graph the output voltage
  - platform: pid
    name: "Fan Speed (PWM Voltage)"
    climate_id: console_thermostat
    type: COOL

  - platform: pid
    name: "Fan Speed 2 (PWM Voltage)"
    climate_id: console_thermostat2
    type: COOL

output:
  # Wire this pin (15) into the PWM pin of your 12v fan
  # ledc is the name of the pwm output system on an esp32
  - platform: ledc
    id: console_fan_speed
    pin: GPIO02

    # 25KHz is standard PC fan frequency, minimises buzzing
    frequency: "25000 Hz" 

    # my fans stop working below 13% powerful.
    # also they're  powerful and loud, cap their max speed to 80%
    min_power: 0%
    max_power: 75%

  - platform: ledc
    id: console_fan_speed2
    pin: GPIO04

    # 25KHz is standard PC fan frequency, minimises buzzing
    frequency: "25000 Hz" 

    # my fans stop working below 13% powerful.
    # also they're  powerful and loud, cap their max speed to 80%
    min_power: 0%
    max_power: 75%

# Good for debugging, you can manually set the fan speed.
fan:
  - platform: speed
    output: console_fan_speed
    name: "Console Fan Speed"

  - platform: speed
    output: console_fan_speed2
    name: "Console Fan Speed 2"

# Expose a PID-controlled Thermostat
# Manual: https://esphome.io/components/climate/pid.html
climate:
  - platform: pid
    name: "Console Fan Thermostat"
    id: console_thermostat
    sensor: console_fan_temperature

    # It is summer right now, so 30c is a decent target.
    default_target_temperature: 30°C
    cool_output: console_fan_speed

    on_state:
      - sensor.template.publish:
          id: p_term
          state: !lambda 'return -id(console_thermostat).get_proportional_term() * 100.0;'
      - sensor.template.publish:
          id: i_term
          state: !lambda 'return -id(console_thermostat).get_integral_term()* 100.0;'
      - sensor.template.publish:
          id: d_term
          state: !lambda 'return -id(console_thermostat).get_derivative_term()* 100.0;'
      - sensor.template.publish:
          id: o_term
          state: !lambda 'return -id(console_thermostat).get_output_value()* 100.0;'
      - sensor.template.publish:
          id: e_term
          state: !lambda 'return -id(console_thermostat).get_error_value();'

    # dummy heater. enable this if using autotune
    # heat_output: console_heat_speed

    # The extents of the HA Thermostat
    visual:
      min_temperature: 20 °C
      max_temperature: 50 °C

    # See the README for setting up these parameters.
    # These are over ridden by the number templates above.
    control_parameters:
      kp: 0.15
      ki: 0.004
      kd: 0.5
      max_integral: 0.0

  - platform: pid
    name: "Console Fan Thermostat 2"
    id: console_thermostat2
    sensor: console_fan_temperature2

    # It is summer right now, so 30c is a decent target.
    default_target_temperature: 30°C
    cool_output: console_fan_speed2

    on_state:
      - sensor.template.publish:
          id: p_term
          state: !lambda 'return -id(console_thermostat2).get_proportional_term() * 100.0;'
      - sensor.template.publish:
          id: i_term
          state: !lambda 'return -id(console_thermostat2).get_integral_term()* 100.0;'
      - sensor.template.publish:
          id: d_term
          state: !lambda 'return -id(console_thermostat2).get_derivative_term()* 100.0;'
      - sensor.template.publish:
          id: o_term
          state: !lambda 'return -id(console_thermostat2).get_output_value()* 100.0;'
      - sensor.template.publish:
          id: e_term
          state: !lambda 'return -id(console_thermostat2).get_error_value();'

    # dummy heater. enable this if using autotune
    # heat_output: console_heat_speed

    # The extents of the HA Thermostat
    visual:
      min_temperature: 20 °C
      max_temperature: 50 °C

    # See the README for setting up these parameters.
    # These are over ridden by the number templates above.
    control_parameters:
      kp: 0.15
      ki: 0.004
      kd: 0.5
      max_integral: 0.0

switch:
  # Expose an ESP32 restart button to HA
  - platform: restart
    name: "Console Fan ESP32 Restart"
Bond246 commented 10 months ago

Hello,

i want to hijack this closed thread because i'm trying to do the same. The first step i've done was to copy the DHT sensor yaml-part, pasted it down below and changed the IDs and names.

But whatever i do the 2nd sensor is always "NA". No values. I've already tried different GPIO Inputs, used different sensors. Attached the working sensor to the 2nd GPIO and so much more.

What could be wrong? Instead of the DHT11 i'm using DHT22 sensors for my project.

sensor:
  # GET TEMP/HUMIDITY FROM DHT22
  - platform: dht
    pin: GPIO18
    model: DHT22
    temperature:
      name: "Temperature Unten"
      id: console_fan_temperature_1
      accuracy_decimals: 3

      # If you don't smooth the temperature readings 
      # the PID controller over reacts to small changes.
      filters:
         - exponential_moving_average:  
             alpha: 0.1
             send_every: 1

    humidity:
      name: "Humidity Unten"
      id: console_fan_humidity_1

    # the DHT11 can only be read every 1s. Use 1.3s to be safe.
    update_interval: 1.3s

  # GET TEMP/HUMIDITY FROM DHT22
  - platform: dht
    pin: GPIO23
    model: DHT22
    temperature:
      name: "Temperature Oben"
      id: console_fan_temperature_2
      accuracy_decimals: 3

      # If you don't smooth the temperature readings 
      # the PID controller over reacts to small changes.
      filters:
         - exponential_moving_average:  
             alpha: 0.1
             send_every: 1

    humidity:
      name: "Humidity Oben"
      id: console_fan_humidity_2

    # the DHT11 can only be read every 1s. Use 1.3s to be safe.
    update_interval: 1.3s

Log looks like this:

<html><body>
<!--StartFragment-->

22:57:34 | [W] | [component:204] | Component dht.sensor took a long time for an operation (0.06 s).
-- | -- | -- | --
22:57:34 | [W] | [component:205] | Components should block for at most 20-30ms.
22:57:35 | [W] | [dht:169] | Requesting data from DHT failed!
22:57:35 | [W] | [dht:060] | Invalid readings! Please check your wiring (pull-up resistor, pin number).

<!--EndFragment-->
</body>
</html>

But as already said, if i wire the 2nd sensor the the first GPIO it works on the first GPIO. I've also tried to add a 10kO resistor as pull-up on the data-out, don't know if needed for the DHT22, but its also not working with it.