3ative / PWM-Fan-controller

An ESPHome Automatic PWM Fan controller with Alarm
MIT License
25 stars 5 forks source link

Added Speed Report, MaxPWM, StartingTemperature #5

Closed lethuer closed 11 months ago

lethuer commented 11 months ago

Hi ! I added some features to the code which I want to share:

Here is the code: FanController.txt

substitutions:
  name: "Media Centre" # Location and Entity Names (Capitals and Spaces Allowed)

globals:
  - id: fan_started
    type: bool
    initial_value: "false"

preferences:
  flash_write_interval: 10000s

number:
  # Slider to set Min Temperature where fan is still running
  - platform: template
    id: min_temperature
    name: $name Min Temperature
    optimistic: True
    min_value: 10
    max_value: 50
    initial_value: 30
    step: 1
    restore_value: true # Restore setting between reboots
    # Slider to set Max Temperature where fan is running at full speed, also used for Alarm
  - platform: template
    id: max_temperature
    name: $name Max Temperature
    optimistic: True
    min_value: 30
    max_value: 70
    initial_value: 50
    step: 1
    restore_value: true # Restore setting between reboots
    # Slider to set Max PWM duty cycle
  - platform: template
    id: max_pwm
    name: $name Max PWM
    optimistic: True
    min_value: 0
    max_value: 100
    initial_value: 50
    step: 1
    restore_value: true # Restore setting between reboots
    # Slider to set start temperature
  - platform: template
    id: start_temp
    name: $name Start Temp
    optimistic: True
    min_value: 20
    max_value: 40
    initial_value: 35
    step: 1
    restore_value: true # Restore setting between reboots

binary_sensor:
  - platform: homeassistant
    entity_id: input_boolean.balkonkraftwerk_manuelle_luftersteuerung
    name: manuelle Lueftersteuerung
    id: manuelle_Lueftersteuerung
    on_state:
      - if:
          condition:
            binary_sensor.is_on: manuelle_Lueftersteuerung
          then:
            - light.turn_on:
                id: d4_light
                effect: slow
          else:
            - light.turn_on:
                id: d4_light
                effect: fast
  - platform: template
    id: Alarm
    name: $name Alarm
    icon: mdi:fire
  - platform: status
    name: RF Bridge Status

sensor:
- platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
  name: WiFi Signal dB
  id: wifi_signal_db
  update_interval: 10s
  entity_category: "diagnostic"
- platform: copy # Reports the WiFi signal strength in %
  source_id: wifi_signal_db
  name: "WiFi Signal Percent"
  filters:
    - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
  unit_of_measurement: "Signal %"
  entity_category: "diagnostic"    
- platform: uptime
  name: Lüfter Balkonkraftwerk Uptime
- platform: homeassistant
  entity_id: input_number.helper_temp_sensor
  name: $name Temperature
  id: temp_sensor
  accuracy_decimals: 0

  on_value:
    - if:
        condition:
          lambda: |-
            return id(temp_sensor).state >= id(start_temp).state;
        then:
          - globals.set:
              id: fan_started
              value: "true"
    - lambda: !lambda |
        if (manuelle_Lueftersteuerung->state == false && id(fan_started) == true)
        {
          //auto pct = map(x, id(min_temperature).state-1, id(max_temperature).state, 50, 100); //#Adjust fan speed(50-100%). Else, turn it off and set the speed to 1
          //if (pct>50)
          //auto pct = map(x, id(min_temperature).state-1, id(max_temperature).state, 0, 100); //#Adjust fan speed(0-100%). Else, turn it off and set the speed to 1
          //if (pct>1)
          auto pct = map(x, id(min_temperature).state-1, id(max_temperature).state, 0, id(max_pwm).state); //#Adjust fan speed(0-up to 100%). Else, turn it off and set the speed to 1
          if ((pct>1) && (pct<id(max_pwm).state))
          {
            auto call = id(the_fan).turn_on(); call.set_speed(pct); call.perform();
          } 
          else
            if (pct>=id(max_pwm).state)
            {
              auto call = id(the_fan).turn_on(); call.set_speed(id(max_pwm).state); call.perform();
            }
            else 
            {
              auto call = id(the_fan).turn_off(); call.set_speed(0); call.perform();
              id(fan_started) = false;
            }
        }
    # ALARM If over 'Max Temp' for 10 Seconds: Turn on 'binary_sensor: Alarm' & Flash the On-Board LED. Else, turn them off
    - if:
        condition:
          for:
            time: 10s
            condition:
              lambda: |-
                return id(temp_sensor).state > id(max_temperature).state;
        then:
          - light.turn_on:
              id: d4_light
              effect: flashfast
          - lambda: !lambda |-
              id(Alarm).publish_state(true);
        else:
          - lambda: !lambda |-
              id(Alarm).publish_state(false);
          #- light.turn_off: d4_light
          - if:
              condition:
                binary_sensor.is_on: manuelle_Lueftersteuerung
              then: 
                - light.turn_on:
                    id: d4_light
                    effect: slow
              else:
                - light.turn_on:
                    id: d4_light
                    effect: fast
- platform: pulse_counter
  pin:
    number: D7 #(D6 defect)
    inverted: false
    mode: 
      input: true
      pullup: false
  name: "Lüfterdrehzahl Fan1"
  count_mode: 
    rising_edge: INCREMENT
    falling_edge: DISABLE
  use_pcnt: false #Only supported on ESP32
  update_interval: 5s
  filters:
    - multiply: 0.5 #2pulses per revolution
  unit_of_measurement: rpm
- platform: pulse_counter
  pin:
    number: D3 #(D6 defect)
    inverted: false
    mode: 
      input: true
      pullup: false
  name: "Lüfterdrehzahl Fan2"
  count_mode: 
    rising_edge: INCREMENT
    falling_edge: DISABLE
  use_pcnt: false #Only supported on ESP32
  update_interval: 5s
  filters:
    - multiply: 0.5 #2pulses per revolution
  unit_of_measurement: rpm

light:
  # On-Board LED
  - platform: monochromatic
    id: d4_light
    output: d4_light_pwm
    effects:
      - pulse:
          name: flashfast
          transition_length: 0.0s
          update_interval: 0.04s
      - pulse:
          name: slow
          #transition_length: 1s # defaults to 1s
          update_interval: 2s
      - pulse:
          name: fast
          transition_length: 0.5s
          update_interval: 0.5s

switch:
# Fan power (MOSFET)
- platform: gpio
  pin: D8
  id: fan_power
  name: Fan Power
  internal: false

fan:
  - platform: speed
    id: the_fan
    output: fan_pwm
    name: $name Fan
    speed_count: 100
    on_turn_on:
      - switch.turn_on: fan_power
    on_turn_off:
      - switch.turn_off: fan_power

output:
  - platform: esp8266_pwm
    id: fan_pwm
    pin:
      number: D5
    min_power: 0 #Slowest Speed for Noctua Fans is 5% - Set to 0 for 'Other' Fans
    frequency: 25kHz #Pulse the fan fast to prevent noise

  - platform: esp8266_pwm
    id: d4_light_pwm
    pin:
      number: D4
      inverted: true

Here is the schematic: Schematic_Fansteuerung_2023-09-29.pdf

I have two small problems to which I would like to ask for help:

  1. After powering up and if Starting temperature (35 degrees) is reached for the first time fans turn on with 100%. Afterwards speed is set according to temperature.
  2. If temperature drops below Stop temperature (30 degrees) fans turn off but still sending 1% percentage (duty cycle).

image image

3ative commented 11 months ago

All my projects and their code are presented as tutorials. The idea being you are free to copy and update where you see fit. Feel free to open a PR and adjust/update as required.

lethuer commented 11 months ago

I added my code, maybe you could help me or you have an idea which could be the problem ?

3ative commented 11 months ago

I added my code, maybe you could help me or you have an idea which could be the problem ?

I hope you can appreciate I don't have much spare time to debug peoples' code. Soz.

lethuer commented 11 months ago

my code is just a copy from yours and duty cycle part is just adjusted to my needs... so i think my questions are really specific and my detected issues could be present in your code aswell :)

btw: I labeled my original post with "help wanted", but obviously this label isn't visible. how to label this issue correctly ?

3ative commented 11 months ago

"call.set_speed(0)" is an invalid option for the "Fan:" component - Which is one of the reasons I added the MOSFET.

lethuer commented 11 months ago

call.set_speed(0)

This was my first try to get this solved but it didn't changed anything.

After powering up the esp I get a percentage value of 0 until the starting temperature is reached for the first time, so it should be possible to write 0% on shutting off the fan ?

image

lethuer commented 11 months ago

Maybe I have a workaround for my issues:

  1. to avoid turning on with 100% duty cycle after powering up and reaching startup temperature for the first time I modified the fan component like below;

    fan:
    - platform: speed
    id: the_fan
    output: fan_pwm
    name: $name Fan
    speed_count: 100
    on_turn_on:
      - switch.turn_on: fan_power
      - logger.log: "Fan Turned On"
      **- fan.turn_on:
          id: the_fan
          speed: 1 #turn on the fan with initially 1% instead of 100%
      #- output.set_level: # alternative approach for turning on with initially 1% instead of 100%
      #    id: fan_pwm
      #    level: 1**
    on_turn_off:
      - switch.turn_off: fan_power
      - logger.log: "Fan Turned Off"
  2. regarding my second issue with 0% duty cycle indeed it seems to be impossible to write 0%. because I'm using the duty cycle value in a graph and I want to see 0% if fan is turned off I created an automation inside homeassistant with a input number helper which writes 0% if fan is off and takes over the percentage value if fan is on.

    alias: Balkonkraftwerk Helper Lüfterzustand PWM
    description: ""
    trigger:
    - platform: state
    entity_id:
      - fan.wr_lufter_balkonkraftwerk_media_centre_fan
    condition: []
    action:
    - if:
      - condition: state
        entity_id: fan.wr_lufter_balkonkraftwerk_media_centre_fan
        state: "off"
    then:
      - service: input_number.set_value
        data_template:
          entity_id: input_number.balkonkraftwerk_helper_lufter_pwm
          value: 0
    - if:
      - condition: state
        entity_id: fan.wr_lufter_balkonkraftwerk_media_centre_fan
        state: "on"
    then:
      - service: input_number.set_value
        data_template:
          entity_id: input_number.balkonkraftwerk_helper_lufter_pwm
          value: >-
            {{ state_attr('fan.wr_lufter_balkonkraftwerk_media_centre_fan',
            'percentage') }}
    mode: single

I will see if that works...

3ative commented 11 months ago

You have changed both the circuit and the code, this is now different from my design and tutorial.