'Simple' task with fan speed is driving me nuts!

The following validates without error

          - fan.turn_on:
              speed: LOW
              id: weed_fan

Complete listing

esphome:
  name: test_fan
  platform: ESP32
  board: esp-wrover-kit

wifi:
  ssid: "TESTSSID"
  password: "strongpassword"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Test Fan Fallback Hotspot"
    password: "92Ll0OCu5Zpn"

captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: "strongotapassword"

ota:
  password: "strongotapassword"

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: True

sensor:
  - platform: bme280
    temperature:
      name: "Weedbox Temperature"
      id: weed_temperature
      oversampling: 16x
    pressure:
      name: "Weedbox Pressure"
      id: weed_pressure
    humidity:
      name: "Weedbox Humidity"
      id: weed_humidity
      filters:
        offset: -0
      on_value_range:
      - below: 30
        then:
          - fan.turn_off: weed_fan
      - above: 30.1
        below: 30.5
        then:
          - fan.turn_on:
              speed: LOW
              id: weed_fan
      - above: 30.6
        below: 31
        then:
          - fan.turn_on: 
              speed: MEDIUM
              id: weed_fan
      - above: 31.1
        then:
          - fan.turn_on:
              speed: HIGH
              id: weed_fan
    address: 0x76
    update_interval: 60s

  - platform: wifi_signal
    name: "Weedbox Wifi Signal"
    id: weedbox_wifi_signal
    update_interval: 60s
    force_update: false
    unit_of_measurement: dB
    icon: mdi:wifi
    accuracy_decimals: 0

  - platform: uptime
    name: "Weedbox Uptime"
    id: weed_uptime
    filters:
      lambda: return x / 3600;

output:
  - platform: ledc
    pin: GPIO19
    id: weed_pwm

fan:
  - platform: speed
    output: weed_pwm
    name: "Weed Fan"
    id: weed_fan
    speed:
      low: 0.33
      medium: 0.66
      high: 1

binary_sensor:
  - platform: status
    name: "Weedbox Status"

text_sensor:
  - platform: wifi_info
    mac_address:
      name: "Weedbox Mac Address"
      icon: mdi:network

switch:
  - platform: restart
    name: "Weedbox Restart"

display:
  - platform: lcd_pcf8574
    dimensions: 16x2
    address: 0x27
    lambda: |-
      it.printf(0, 0, "T:%.1f", id(weed_temperature).state);
      it.printf(8, 0, "H:%.1f", id(weed_humidity).state);
      it.printf(0, 1, "U:%.1f", id(weed_uptime).state);
      it.printf(8, 1, "F:%.0f", id(weed_fan).state);
1 Like

Well, that works like a hot damn! Thank you!

There was obviously something I was missing and it may have been the capitalisation but in between trying - output.set_level and - fan.set_level and - fan.turn_on I was just missing the correct sequence which, in hindsight, now seems so easy. After a couple of solid days on it, it may have just been that I was too close to the problem to see the solution.

I have a couple of enhancements and fixes that I want to do with this code after which I’ll produce a write-up that should help others with this problem and also provide a useful project to run it in. As you can see from the names in the code, this project is a heaterless dehumidifier for weed.

So, fancy having a crack at the last part of the code? :laughing:

Current is:

display:
  - platform: lcd_pcf8574
    dimensions: 16x2
    address: 0x27
    lambda: |-
      it.printf(0, 0, "T:%.1f", id(weed_temperature).state);
      it.printf(8, 0, "H:%.1f", id(weed_humidity).state);
      it.printf(0, 1, "U:%.1f", id(weed_uptime).state);
      it.printf(8, 1, "F:%x", id(weed_fan).speed);

What I get on the display is one of: 0 (for off), 0 (for low), 1 (for medium), 2 (for high).

I would like to get off, low, medium, high.

I get the feeling that the speed_fan code is using a char for the speed setting rather than an integer. If I use “F:%X” then I get a number returned, if I use “F:%s” then validates OK but it bitches at me during compile (below) and then won’t connect to wifi or display anything at all;

“src/main.cpp: In lambda function:
src/main.cpp:407:46: warning: format ‘%s’ expects argument of type ‘char*’, but argument 5 has type ‘int’ [-Wformat=]
it.printf(8, 1, “F:%s”, weed_fan->speed);”

Any ideas?

I should add that I’ve considered the use of if/else statements to derive the text state of the fan but with both ‘off’ and ‘low’ both returning zero, I seem to be at a hurdle that my skills are unable to get me over.

So, further investigation reveals this:
weed_fan.state returns either ON or OFF
Weed_fan.speed returns 0,0,1,2 for off, low, medium, high

I tried this:

display:
  - platform: lcd_pcf8574
    dimensions: 16x2
    address: 0x27
    lambda: |-
      it.printf(0, 0, "T:%.1f", id(weed_temperature).state);
      it.printf(8, 0, "H:%.1f", id(weed_humidity).state);
      it.printf(0, 1, "U:%.1f", id(weed_uptime).state);
#      it.printf(8, 1, "F:%u", id(weed_fan).state);
      if (id(weed_fan).state = "off") {
        it.print(8, 1, "F: OFF");
      } else
        if (id(weed_fan).speed = '0') {
          it.print(8, 1, "F: LOW");
        } else
          if (id(weed_fan).speed = '1') {
            it.print(8, 1, "F: MED");
          } else
            it.print(8, 1, "F: HIGH");

and got this:

src/main.cpp: In lambda function:
src/main.cpp:421:34: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
       if (weed_fan->state = "off") {
                                  ^
src/main.cpp:424:29: error: invalid conversion from 'char' to 'esphome::fan::FanSpeed' [-fpermissive]
         if (weed_fan->speed = '0') {
                             ^
src/main.cpp:424:34: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
         if (weed_fan->speed = '0') {
                                  ^
src/main.cpp:427:31: error: invalid conversion from 'char' to 'esphome::fan::FanSpeed' [-fpermissive]
           if (weed_fan->speed = '1') {
                               ^
src/main.cpp:427:36: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
           if (weed_fan->speed = '1') {
                                    ^
*** [/data/esp32_weeddryer/.pioenvs/esp32_weeddryer/src/main.cpp.o] Error 1

I’ve discovered more things that don’t work but regrettably, nothing that does. I tried the parentheses around (off) but then I get an error that it isn’t declared in the scope. I’m not a coder at all (you guessed that already) and I feel that these errors are running up against areas that I’m aware of but do not have the knowledge yet to solve. Interestingly, I have another project that is stalling because I can’t figure out how to use a value that is stored as a char in a string.

I was able to get an ON/OFF display using the following:

if (id(weed_fan).state) {
  it.print(8, 1, "F:ON");
} else {
  it.print(8, 1, "F:OFF"); 

That almost worked - I think - but with the TRUE state being primary, it left me nowhere that was obvious to put the rest of the code to sort the fan speed. If the first action was based on fan state being OFF then the rest of the code would flow from that. Still, it worked and I’m taking it as a win at this point. I was thrown off for a while because that line of code that I commented out with a hash caused an issue and threw a scalar error.

So, I tried this:

display:
  - platform: lcd_pcf8574
    dimensions: 16x2
    address: 0x27
    lambda: |-
      it.printf(0, 0, "T:%.1f", id(weed_temperature).state);
      it.printf(8, 0, "H:%.1f", id(weed_humidity).state);
      it.printf(0, 1, "U:%.1f", id(weed_uptime).state);
      if (id(weed_fan).state)
        then
        if (id(weed_fan).speed = '0') {
          it.print(8, 1, "F:LOW");
        } else
        if (id(weed_fan).speed = '1') {
          it.print(8, 1, "F:MED");
        } else
        if (id(weed_fan).speed = '2') {
          it.print(8, 1, "F:HIGH");
      } else {
        it.print(8, 1, "F:OFF");
      }
        

but once again, the errors are back. I’ll give it another shot tomorrow.

and as per this post

try this - it validates ok

display:
  - platform: lcd_pcf8574
    dimensions: 16x2
    address: 0x27
    lambda: |-
      if (id(weed_fan).state = OFF) {it.print(8, 1, "F: OFF");}
      else if (id(weed_fan).speed = '0') {it.print(8, 1, "F: LOW");}
      else if (id(weed_fan).speed = '1') {it.print(8, 1, "F: MED");}
      else {it.print(8, 1, "F: HIGH");}
      it.printf(0, 0, "T:%.1f", id(weed_temperature).state);
      it.printf(8, 0, "H:%.1f", id(weed_humidity).state);
      it.printf(0, 1, "U:%.1f", id(weed_uptime).state);

Me neither, but YAML is pedantic, you had the correct state in your post you just changed it in your code :man_facepalming: :crazy_face:

I changed that but it was still complaining, so I took your code and deleted out all the nested ifs (because its too easy to run into trouble with them) and ran with just one if/else.

It still complained so I got rid of your previous statements in the lambda and just ran with the one if so that I could get the validator to tell me where the error was, but that worked ok it didn’t complain about anything so I added the non if statements back in and that worked ok.

Then I googled “esphome nested if” and found that thread and added the nested ifs back in - let me know if it works for you.

Yep I kept getting scalar errors too

Interesting that it compiles OK for you. I still get the errors below:

src/main.cpp:318:3: warning: multi-line comment [-Wcomment]
   //   lambda: !lambda "if (id(weed_fan).state = OFF) {it.print(8, 1, \"F: OFF\");}\nelse\
   ^
src/main.cpp: In lambda function:
src/main.cpp:404:29: error: 'OFF' was not declared in this scope
       if (weed_fan->state = OFF) {it.print(8, 1, "F: OFF");}
                             ^
src/main.cpp:405:32: error: invalid conversion from 'char' to 'esphome::fan::FanSpeed' [-fpermissive]
       else if (weed_fan->speed = '0') {it.print(8, 1, "F: LOW");}
                                ^
src/main.cpp:405:37: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
       else if (weed_fan->speed = '0') {it.print(8, 1, "F: LOW");}
                                     ^
src/main.cpp:406:32: error: invalid conversion from 'char' to 'esphome::fan::FanSpeed' [-fpermissive]
       else if (weed_fan->speed = '1') {it.print(8, 1, "F: MED");}
                                ^
src/main.cpp:406:37: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
       else if (weed_fan->speed = '1') {it.print(8, 1, "F: MED");}
                                     ^
*** [/data/esp32_weeddryer/.pioenvs/esp32_weeddryer/src/main.cpp.o] Error 1

I’ve tried lower case off, “OFF”, “off”, (OFF) and (off) in addition to the combinations behind state = , state=

I also tried cutting out the last few lines and just working with the If part but to no success.

I never compiled it - just validated it, let me try

Yep craps out when compiling

RTFMing Lambdas aren’t validated :man_facepalming: :crazy_face:

I’m wondering if it isn’t an issue with the lambda syntax but instead an issue with the data type in the conditions. I don’t know how to tell what data type is OFF and what data type is 0,1,2.

This validates and compiles - it throws a warning about expecting a double when fan_speed is defined as int but give it a try.

esphome:
  name: test_fan
  platform: ESP32
  board: esp-wrover-kit

wifi:
  ssid: "TESTSSID"
  password: "strongpassword"

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

captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: "strongotapassword"

ota:
  password: "strongotapassword"

globals:
  - id: fan_speed
    type: int

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: True

sensor:
  - platform: bme280
    temperature:
      name: "Weedbox Temperature"
      id: weed_temperature
      oversampling: 16x
    pressure:
      name: "Weedbox Pressure"
      id: weed_pressure
    humidity:
      name: "Weedbox Humidity"
      id: weed_humidity
      filters:
        offset: -0
      on_value_range:
      - below: 30
        then:
          - fan.turn_off:
              id: weed_fan
          - lambda: |-
              id(fan_speed) = 0;
      - above: 30.1
        below: 30.5
        then:
          - fan.turn_on:
              speed: LOW
              id: weed_fan
          - lambda: |-
              id(fan_speed) = 1;
      - above: 30.6
        below: 31
        then:
          - fan.turn_on: 
              speed: MEDIUM
              id: weed_fan
          - lambda: |-
              id(fan_speed) = 2;
      - above: 31.1
        then:
          - fan.turn_on:
              speed: HIGH
              id: weed_fan
          - lambda: |-
              id(fan_speed) = 3;
    address: 0x76
    update_interval: 60s

  - platform: wifi_signal
    name: "Weedbox Wifi Signal"
    id: weedbox_wifi_signal
    update_interval: 60s
    force_update: false
    unit_of_measurement: dB
    icon: mdi:wifi
    accuracy_decimals: 0

  - platform: uptime
    name: "Weedbox Uptime"
    id: weed_uptime
    filters:
      lambda: return x / 3600;

output:
  - platform: ledc
    pin: GPIO19
    id: weed_pwm

fan:
  - platform: speed
    output: weed_pwm
    name: "Weed Fan"
    id: weed_fan
    speed:
      low: 0.33
      medium: 0.66
      high: 1

binary_sensor:
  - platform: status
    name: "Weedbox Status"

text_sensor:
  - platform: wifi_info
    mac_address:
      name: "Weedbox Mac Address"
      icon: mdi:network

switch:
  - platform: restart
    name: "Weedbox Restart"

display:
  - platform: lcd_pcf8574
    dimensions: 16x2
    address: 0x27
    lambda: |-
      it.printf(0, 0, "T:%.1f", id(weed_temperature).state);
      it.printf(8, 0, "H:%.1f", id(weed_humidity).state);
      it.printf(0, 1, "U:%.1f", id(weed_uptime).state);
      it.printf(8, 1, "F:%f", id(fan_speed));

If it works for you change

globals:
  - id: fan_speed
    type: int

to

globals:
  - id: fan_speed
    type: double

I see what you did there… :slight_smile:

It does validate AND compile but the display reads out a decimal fraction - F:0.0000 and throws a bunch of warnings about LCD out of print range in the logs. That would be expected when it can’t fit the entire output on the display.

I considered a similar approach using a template but you implemented into the sensor which is a nice touch.

Net result is that it still isn’t producing the required output on the display. I’ve tried playing around with int, double and char as the global type and also the printf function but with no success. I recognise that the 0.0000 is coming from the ‘f’ on “F:%f” but ‘d’ and ‘u’ both result in an unchanging ‘F:0’ on the display and the one I really want - F:%s - results in the wifi not connecting and nothing at all on the display.

As I think we were calling integers from the code you added under sensor:, I tried encapsulating the fan speed numbers in single quotes ’ ’ but that didn’t work either.

and if you go back to

it.printf(8, 1, "F:%f", id(weed_fan).speed);

Global is set to int, no char quotes on fan speeds under sensor.

it.printf(8, 1, “F:%u”, id(weed_fan).speed); - note the change from f to u for unsigned decimal integer.

It validates AND compiles without error but I’m back to 0,0,1,2 on the display.

:man_shrugging: Yeah sorry Mark

No apologies, I am deeply grateful for you help so far. Just one more thing to learn how to solve. It’s not a show-stopper at all, more like the cherry-on-the-cake.

    lambda: |-
      it.printf(8, 1, "F: %s", id(weed_fan).speed ? "ONE" : "TWO" );
      it.printf(0, 0, "T:%.1f", id(weed_temperature).state);
      it.printf(8, 0, "H:%.1f", id(weed_humidity).state);
      it.printf(0, 1, "U:%.1f", id(weed_uptime).state);

What does that give you?

This is the source