4-pin fan with d1mini and esphome: how to read current rpm of the fan?

Dear community,

A am building a little house for my new 3D-Printer. The case needs to have a fan - obviously.

I thought of using a D1mini, DTH-11, a relay and a ARCTIC P14 PWM PST-fan. (5V power supply and 12V power supply)

Well, it was all working fine, until i tried to read the fan speed from the fan itself. At first, it was all fine - and now, the d1mini is dead. I am not sure, if it is due to my last update, wrong code, or wrong wiring. Before I try it again with a new d1mini, I would like to check with you guys, if you can point at my mistake. Any help is very welcome.

Wiring: (sorry, did not find a free tool to do some nice schematics)


red wire is load, black is ground
white is sending pwm to the fan
green should be reading pwm from the fan

As the fan runs on 12V and at least ground and the green wire are mixing voltages: was this the problem?

Code:

# Variablen
substitutions:
  device_name: "3d-drucker-lufter"
  friendly_name: "3D Drucker"
  device_description: "3D Drucker Steuerung der Geschwindigkeit des Gehäuselüfters"
  temperature_threshold_low: "22" # At what temperature, in celcius, should the fan turn on to its minimum speed
  temperature_threshold_high: "30" # At what temperature, in celcius, should the fan turn on to its maximum speed
  minimum_fan_speed: "7" # What is the minimum fan speed, as a percentage
  



# ESPHome Core Configuration
esphome:
  name: '${device_name}'
  friendly_name: '${friendly_name}'
  comment: '${device_description}'

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: ""

ota:
  password: ""

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "3D-Drucker-Lufter"
    password: ""

captive_portal:
    

# enable web interface on device
web_server:
  port: 80



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:
  - platform: dht
    pin: D2
    temperature:
      name: "Temperatur"
      id: temperature_sensor
      on_value:
        then:
          - script.execute: set_fan_state
    humidity:
      name: "Feuchtigkeit"
      id: humidity_sensor
    update_interval: 30s


# Sensor Taupunkt berechnen 
#  - platform: template
#    name: "Taupunkt"
#    lambda: |-
#      return (243.5*(log(id(humidity_sensor).state/100)+((17.67*id(temperature_sensor).state)/
#      (243.5+id(temperature_sensor).state)))/(17.67-log(id(humidity_sensor).state/100)-
#      ((17.67*id(temperature_sensor).state)/(243.5+id(temperature_sensor).state))));
#    unit_of_measurement: °C
#    update_interval: 120s
#    icon: 'mdi:thermometer-water'


# Fan Speed
  - platform: pulse_counter
    pin:
      number: D6
      mode:
        input: true
        pullup: true
    name: PWM Fan RPM
    id: fan_pulse
    unit_of_measurement: 'RPM'
    filters:
      - multiply: 0.5
    count_mode:
      rising_edge: INCREMENT
      falling_edge: DISABLE
    update_interval: 3s



# PWM output for the fan speed control
output:
  - platform: esp8266_pwm
    pin: D0
    frequency: 25000 Hz
    id: pwm_output


# Hidden switch object to control the relay
switch:
  - platform: gpio
    name: "fan_relay"
    id: fan_relay
    pin: D3
    internal: true


# The actual fan entity presented to Home Assistant
fan:
  - platform: speed
    output: pwm_output
    name: '${friendly_name} Lüfter'
    id: "the_fan"
    on_turn_on:
    - switch.turn_on: fan_relay
    on_turn_off:
    - switch.turn_off: fan_relay
    


# Sets the speed of the fan based on a linear calculation
# between the high and low temperature thresholds and
# the minimum specified fan speed
script:
  - id: set_fan_state
    then:
      - if:
          condition:
            lambda: |-
              return id(temperature_sensor).state < id(${temperature_threshold_low});
          then:
            - fan.turn_off: the_fan
          else:
            - fan.turn_on:
                id: the_fan
                speed: !lambda |-
                  if (id(temperature_sensor).state >= id(${temperature_threshold_high})) {
                    // Over upper threshold, fan speed at maximum
                    ESP_LOGD("Fan speed calc", "Temperature is above or equal to upper threshold so setting to max");
                    return 100;
                  }
                  else {
                    float calc_speed = ((100-id(${minimum_fan_speed})) / (id(${temperature_threshold_high})-id(${temperature_threshold_low})))*(id(temperature_sensor).state-id(${temperature_threshold_low}))+id(${minimum_fan_speed});
                    ESP_LOGD("Fan speed calc", "calculated speed = %f", calc_speed);
                    return calc_speed;
                  }

The whole thing is mostly based on this video.

Can you use a voltmeter to see what the voltage is on the green wire.

Anything more then 3.3V has the potential to damage the ESP.

Hi @agk1190,

I just checked with my multimeter: the readings are jumping around a little bit: 0.6 - 0.7 volt. As this is PWM, I am not sure, if these readings are correct?

Somewhere in the community, I have read that I should use the internal pullup resistor and that I have to connect the two “grounds” from the two power supplies. As I did both, the RPM readings became plausible.

I tried to do activate the internal resistor like this: “pullup: true”

# Fan Speed
  - platform: pulse_counter
    pin:
      number: D6
      mode:
        input: true
        pullup: true
    name: PWM Fan RPM
    id: fan_pulse
    unit_of_measurement: 'RPM'
    filters:
      - multiply: 0.5
    count_mode:
      rising_edge: INCREMENT
      falling_edge: DISABLE
    update_interval: 3s

Any other idea, why I broke my d1mini?

EDIT: I search the web for specifications of the fan, but got none for the signal/pulse. ChatGPT tells me that it probably is 5V and to put a 10kOhm resistor between the GPIO-PIN and ground. Does this make sense?

Hi @schneich,

  1. My bad, I wasn’t thinking. You can’t measure PWM max voltage with a voltmeter.
  2. Yes, you need a common ground between the ESP and the fan’s power supply.

Did the ESP die after enabling the internal pullup resistor?

Regarding your edit: A single resistor to GND won’t reduce the voltage going to the GPIO pin. You need to create a voltage divider using 2 resistors. I would strongly recommend using a voltage divider to stay on the safe side until you know what the RPM Sense pin voltage is.

My best guess as to why the ESP stopped working is either too much voltage on the GPIO pin or too much current drawn from the internal pull up resistor.

Hi @agk1190,

thank you for your input. I quickly googled a “voltage devider”. There seems to be an easy formula to calculate the output voltage - if you know the input voltage. :slight_smile:


(Source)

How do I measure the max. voltage of the PWM? Oscilloscope? (I don’t have one…)

Lets say, I can drop the voltage on the RPM-wire to 3V. The pulse will still be there, just not that high any more? The d1mini should still be able to do accurate readings?

Hi @schneich,

Yes, the easiest way to get the max voltage is an oscilloscope. Or datasheet if one exists.

At this point, you need 2 pieces of information to continue:

  1. The max voltage of the RPM pin.
  2. The configuration of the RPM pin. i.e., does the fan pull the line to GND or does it apply voltage.

You mentioned in post #3 enabling the internal pull-up resistor. Did the ESP die after making this change?

Sorry for not answering your question. I am not sure, if there is a conclusive answer.

  1. At first, I connected the tacho-line (-> the one that delivers the RPM of the fan). The data was rubbish. This setup was only running for 30s or so - perhaps 1 minute.
  2. Then I googled and found out, that there has to be a connection between the two grounds. And I read about activating the internal pullup resistor (-> PWM Fan Controller - #8 by fr8tra1n). I did both at the same time. Readings were good, until a minute or so later, the d1mini died.

I don’t know. Perhaps it was pure luck, the d1mini did not die during my first attempts. Perhaps, it was the activation of the internal pullup resistor.

The last answer in that discussion points at the same direction as you do: :slight_smile:

Did read it, but did not understand it at the time…

This info from another brand of 4 wire fan states that the tacho cct is an open collector output that requires a pull-up in the user device. To be compatible I would think all fans would have to use a similar cct.

Your connection with the internal pull-up should have worked and without would not have damaged the ESP.

Hi @gaz99,

thank you for that image and the details for the Noctua fans. As these fans are all made for use with motherboards in PCs, I think, you are right: they work probably all the same way. As my fan runs on 12V, I assume, the tacho is 12V as well. My gut feeling tells me that 12V is probably too much for the D1mini pin.
Before I try again, I would like to have a confirmation for the voltages of the tacho of my Arctic-fan. I opened a support request with Arctic and was promised to get an answer within 5 working days…

The 12V to the fan has no connection to the tacho o/p in an open collector circuit, the o/p voltage is set by what the pullup resistor is connected to.

Have a read up on what “Open Collector” means.

Hi @gaz99,

I had a quick look into “open collector”, but I have to admit that it needs a deeper reading. My basic knowledge about electronics is not that deep… up until now, it was enough to solder a sensor to a pin and get it running. :slight_smile:

If it should have worked: do you have any idea, why my d1mini is unresponsive? I can’t connect via WLAN, nor USB. (I have read some posts about a WLAN issue, but my d1mini seems to be dead. Could it be a bug in the current ESPhome version? Is there something in my code?)

Is there a (free) tool to easily draw a nice diagram, so that you can verify my plan on how to connect everything?

Thanks,
Chris

EDIT: I even tried to hard-reset the d1mini - unsuccessful.

Hi @gaz99,

I asked ChatGPT for a summary about “open collector”, did some reading and watched some videos. I believe, I have a better understanding now. Can you confirm or correct my assumptions?

  • In order to read the pulses coming from the fan, we need a wave form, as in your picture above. For a D1mini, the wave form should ideally be 3.3-5V in HIGH state, and 0V for LOW.
  • I tried to imaging, how those pulses are created in the fan. Well, there must be a little wheel turning. One half of that wheel is metallic, the other have is not. When the wheel turns, for half a turn, it closes a circuit (-> HIGH state), for the other half, it opens a circuit (-> LOW). The whole thing must be like a switch opening/closing depending on the speed of the fan.
  • As you have said, I need a pull up resistor, the “little wheel” must close the circuit to ground. Otherwise, it would not make sense.
  • I assume, the pull up resistor should be connected to the 5V of the D1mini - not the 12V of the fan.
  • I assume, when I activated the internal pull up resistor, it has connected the pin to 5V (or 3.3V) of the D1mini.
  • In my first bullet, I wrote coming from. It would be more accurate to say, the current comes from the D1mini and leaves through the ground of the fan, creating the pulses.

If this is all correct, I really don’t understand, why my D1mini got unresponsive.
Any suggestions, anybody?

Chris

While the pins on the ESP8266 is supposed to be 5V tolerant it is best to use 3.3V.
The ‘little wheel’ is probably a magnet and a hall effect sensor.
The pull up resistor connects to the 3.3V on the ESP or you can use the internal pull up.

No idea why your device seems to be dead, maybe try one of the other ESP flashing tools, I know one has a blank/erase function but I can’t remember which.

Hi,

I am still struggling with this. Could you check my wiring?

This is close to what I would like to solder in the end.

  • The D1mini will be powered directly via 5V USB power supply and the fan will be powered with a 12v led driver.
  • I could not find a 3pin DHT11 sensor for fritzing.
  • My relay looks a bit different, but is basically the same.

Any inputs are very much appreciated.

Chris

I had the same problem. I have an Arctic P12 Max as a fan and have now killed 2x NodeMCU and 1x D1 Mini. I first connected the tachometer cable directly to a pin, which worked fine. After a short time, however, the displayed values were much too high, so I changed the pin and everything repeated itself. With the D1 mini, the LED on the D1 flickered in time with the revolutions and than shot the fan. The microcontroller probably switched 12V through to the tacho cable. The D1 seems to be less robust than the NodeMCU. The pre-switched step-down converter also lit up when the power supply was not connected, as it received a return voltage via the microcontroller. My measurement on the tacho cable showed ~12V, so not every fan is dependent on the pull-up voltage. I will now use a voltage divider (10kOhm / 3.3kOhm) to reduce the voltage. 10kOhm at 12V, 3,3kOhm at ground and the tacho between. I assume that the microcontroller will then stay alive and the measurement will work.

I guess there was a failure in the wiring and my conclusion was wrong. I’ve posted my working soluting in my last comment.

Hi @PedroArkanson ,

Thank you for your input. Yesterday, I checked my wiring: I gave up on reading the PWM. I only connected the wire to send the fan speed.

The project is the fan for my 3D-Printer case. It is not online that much :slight_smile: - but seems to run stable so far.

I think I was wrong and the function is different than I had measured.
My working solution is now that I have connected the tacho cable from the fan directly to PINS D5 and D6 and also connected a 10kOhm resistor to the tacho cable and to the 5V output of the D1 mini as a pull-up. Maybe this is also a solution for you.

Hi,

4 Pin PC fans vary in their implementation for many reasons, including some variation in the motherboard spec (p9) that they’re meant to adhere to. Some implementations will fry the GPIO pin on your ESP outputting the PWM signal unless you implement protection…

The spec calls for:

  • Tacho output from Fan → PC
    • Two pulses per revolution
    • Open collector / open drain
      • collector/drain relates to the type of transistor (BJT or FET) sending the signal
      • basically means that the pin that you’re measuring is either connected to ground or left ‘floating’ (or ‘high Z’)
      • the sensing device should provide the active pull-up
    • Pull-up resistor to be such value that the BJT/FET sees 12v, max 12.6v when it is floating
      • Depending on the fan implementation, you may possibly experience some issues if the pull-up voltage is significantly less (ie 3.3v) - YMMV but generally it works ok (especially for newer fans)
      • You can simply set your GPIO pin to input / pull-up & it will generally work ok
      • A good way to test without oscilloscope but with volt meter -
        • setup and connect your ESP input GPIO to the fan’s Tacho output
        • connect multimeter, V measure, between ground and Tacho output
        • block the fan from rotating & then turn on the fan
        • rotate the fan slowly by hand and you should see a) measured voltage switch clearly between ~3.2v and 0v twice per rotation, and b) ESP should report the transitions

Now for the tricky bit…

  • PWM input from PC → Fan
    • The spec assumes that the device providing the PWM signal is also operating in open collector / open drain mode. Therefore it calls for a pull-up resistor within the fan - such pull-up to be no more than 5.25v (which can fry an ESP)
      • The spec does note, however, that it strongly encourages new fan designs to implement a max 3.3v pull-up specifically to avoid this kind of issue (ESP not being the only device that can get fried)
    • So, best thing to do before connecting the ESP PWM output to the fan - power up the fan and measure the voltage present at the fan’s PWM input pin.
      • If it’s above 3.3v then you’re gonna need protection
      • if it’s ~3.3v or below (but above about 1v) you are good to go — simply set the GPIO output mode to OUTPUT_OPEN_DRAIN, connect directly to the fan’s PWM input, and start sending that sweet 25kHz PWM signal :slight_smile:
      • If it’s zero volts then there’s a chance that the fan is not providing the pull-up & you’re going to have to do that yourself.
        • You can simply add a resistor (1k - 10k should be fine) from ESP 3.3v to the join of Fan PWM input and ESP PWM output (to provide the pull-up), and again set GPIO output mode to OUTPUT_OPEN_DRAIN
        • You could also potentially just drive it directly with push-pull mode (simply OUTPUT mode & only connect the GPIO to the PWM input) — but there is some risk that this may damage the ESP if the fan draws too much current when the ESP output is HIGH. A small current limiting resistor should be sufficient to protect the ESP - but you’ve then also got to check that when the ESP is LOW that the voltage on the fan’s PWM input pin falls below the zero threshold voltage (according to spec this is 0.8v). So you’ll need to tweak the resistor such that HIGH output is less current than the ESP can drive, and so that LOW output results in a sufficiently low voltage that the fan sees it as a logic state transition

If you need protection because the fan’s pull-up voltage is > 3.3v, then a simple transistor should be sufficient (plus small current limiting resistor from GPIO to transistor’s base, and maybe a pull-up resistor on the PWM input if necessary). Don’t forget to invert the ESP output if necessary.

Who would have thought that something so simple could be made so difficult huh? :rofl: