Sonoff Ifan04 - ESPHome working code

You can’t use delay like that. Everything will stop working and you’ll likely trigger the WDT to reboot the device.

Have you tested that?

https://arduino-esp8266.readthedocs.io/en/3.0.0/reference.html#:~:text=Timing%20and%20delays¶&text=delay(ms)%20pauses%20the%20sketch,TCP/IP%20tasks%20to%20run

You would know better than I with ESPHome. I believe Arduino core wise delay(); triggers esp8266 yield(); behind the scenes which keeps it from hanging and allow it to do tasks like keeping WiFi up.

Otherwise, maybe do something like this instead:

You are blocking the loop() function which means that no other components can do anything.

I assume you’re referring to this:

Remember that there is a lot of code that needs to run on the chip besides the sketch when WiFi is connected. WiFi and TCP/IP libraries get a chance to handle any pending events each time the loop() function completes, OR when delay is called. If you have a loop somewhere in your sketch that takes a lot of time (>50ms) without calling delay, you might consider adding a call to delay function to keep the WiFi stack running smoothly.

The part you’re missing is the “besides the sketch”. All of esphome is in “the sketch”. So you are blocking all of esphome’s functions. No sensor updates, no binary sensor triggers, no communication with HA, etc.

Not missing any part as to why I said you would know better about the ESPHome parts.

I fully understand what you mean as to why I also offered the BlinkWithoutDelay URL above.

Here is a modified example of how that may work:

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;  // Will store last time relay ramp was updated

// Constants won't change:
const long interval = 10000;  // 10 second interval at which to trigger the relay ramp (milliseconds)

bool rampmed = false;

void IFan::set_med() {
  // Ramp up to high speed.
  digitalWrite(relay_1, HIGH);
  digitalWrite(relay_2, LOW);
  digitalWrite(relay_3, HIGH);
  beep(3);
  rampmed = true;
}

void loop() {

//... other code ...

  if (rampmed) {
    // Check to see if it's time to ramp down the relays; that is, if the difference
    // between the current time and the last time you ramped the relays is bigger than
    // the interval at which you want to ramp the relays.
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
      // Save the last time you ramped the relays.
      previousMillis = currentMillis;
      // Ramp down to medium speed.
      digitalWrite(relay_1, HIGH);
      digitalWrite(relay_2, HIGH);
      digitalWrite(relay_3, LOW);
      beep(2);
      rampmed = false;
    }
  }
}

:smiley:

…Guess that wouldn’t totally work as the loop wouldn’t call it again without hitting the button again haha. I’ll leave that one for him to further invest time into. Would probably have to add a boolean tracker that will just run in the loop and then rest itself after it runs.

Edit: Fixed the code for fun (not tested) play at your own risk @DelusionalAI .

Edit: Confirmed with @ssieb in discord which they replied with: “Yes, that’s a good way to do it.”

I2C wiring modification:



Note: The pin attached to the ground plane sinks a lot of heat and is pretty hard to solder to.

If anyone has trouble fitting this plastic case in the fan mounting bracket:

You can dremel/sand about half a mill off each bottom side of the Isosceles Trapezium and it may fit as the case is wider at the bottom than the top looking from the short end:

image

Worked for me your results may vary…

I2C 16 I/O Expander for fan light basic wall switch


I2C Backplane for Environment Sensor and 16 I/O Expander


YAML

i2c:
  id: i2c_component
  sda: 14
  scl: 16
  scan: true
# pcf8574 I2C 16 I/O Expander
pcf8574:
  - id: 'pcf8574_hub'
    address: 0x20
    pcf8575: true

switch:
  - platform: restart
    name: "T-PoE Restart"
  - platform: gpio
    name: "PCF8574 Pin #1"
    pin:
      pcf8574: pcf8574_hub
      # Use pin number 1
      number: 1
      # One of INPUT or OUTPUT
      mode:
        output: true
      inverted: false
  - platform: gpio
    name: "PCF8574 Pin #2"
    pin:
      pcf8574: pcf8574_hub
      # Use pin number 2
      number: 2
      # One of INPUT or OUTPUT
      mode:
        output: true
      inverted: false
binary_sensor:
  - platform: gpio
    name: "Input_0"
    pin:
      pcf8574: pcf8574_hub
      number: 0
      mode: INPUT
      inverted: False

Log

[05:56:18][C][i2c.arduino:039]:   SDA Pin: GPIO14
[05:56:18][C][i2c.arduino:040]:   SCL Pin: GPIO16
[05:56:18][C][i2c.arduino:041]:   Frequency: 50000 Hz
[05:56:18][C][i2c.arduino:044]:   Recovery: bus successfully recovered
[05:56:18][I][i2c.arduino:054]: Results from i2c bus scan:
[05:56:18][I][i2c.arduino:060]: Found i2c device at address 0x20
[05:56:18][C][pcf8574:021]: PCF8574:
[05:56:18][C][pcf8574:022]:   Address: 0x20
[05:56:18][C][pcf8574:023]:   Is PCF8575: YES
[05:56:18][C][switch.gpio:050]: GPIO Switch 'PCF8574 Pin #1'
[05:56:18][C][switch.gpio:051]:   Pin: 1 via PCF8574
[05:56:18][C][switch.gpio:073]:   Restore Mode: Restore (Defaults to OFF)
[05:56:18][C][switch.gpio:050]: GPIO Switch 'PCF8574 Pin #2'
[05:56:18][C][switch.gpio:051]:   Pin: 2 via PCF8574
[05:56:18][C][switch.gpio:073]:   Restore Mode: Restore (Defaults to OFF)
[05:56:18][C][gpio.binary_sensor:015]: GPIO Binary Sensor 'Input_0'
[05:56:18][C][gpio.binary_sensor:016]:   Pin: 0 via PCF8574
[05:56:26][D][binary_sensor:036]: 'Input_0': Sending state ON
[05:56:27][D][binary_sensor:036]: 'Input_0': Sending state OFF

Two methods of operation thanks to Kraxman

YAML

binary_sensor:
  # Physical button on the iFan04-L for testing not related to the PCF8575.
  - platform: gpio
    id: button_light
    pin: GPIO0
    on_press:
      then:
        - light.toggle: fan_light
  # PCF8575 enabled basic light switch.
  - platform: gpio
    name: "Input_0"
    pin:
      pcf8574: pcf8574_hub
      number: 0
      mode: INPUT
      inverted: true
    # Method 1: If the light is on turn it off, if the light is off turn it on, if another source changes the light  
    #           and the physical is in opposite state ignore and leave the current state.
    on_press:
      then:
        - light.turn_on: fan_light
    on_release:
      then:
        - light.turn_off: fan_light

    # Method 2: 3-Way Light Switch Emulation.
    #on_press:
    #  then:
    #    - light.toggle: fan_light
    #on_release:
    #  then:
    #    - light.toggle: fan_light

    # Method 2.1: Same as 2 just a different process. 
    # on_state:
    #  then:
    #    - light.toggle: fan_light

Hi All looks like some great work has gone on in here since I first had a go at this :+1:

Time for a new fan for me so time to add another Sonoff - I’ve got a Hunter fan that came with the usual RF remote add on kit that you see in the US but inside the wiring loom of the fan I found there was 1 additional capacitor & i’m wondering if I can remove it and then just use the 3 found inside the ifan for speed control.
I have seen inisde the older pullcord types that have 2 or 3 but this new fan has no cords for lights or fan speed just the reverse switch and 1 cap is using the RF remote for all controls.

I should be ok to just cut it out and join the wiring ?


Thanks

All the fans I have been in have a capacitor in them like that. Usually you add the sonoff controller in addition to what is there. What I would do is find where the controller is in that fan and remove it and connect the Sonoff to the same wires where it was. As they usually all operate on the same principle wires Motor/Light/Neutral.

NonaSuomy - I used your experience and finally got mine working! So thank you, and the other contributors, for all of your work! Has anyone considered or tried getting Alexa/Google Home integration to control the speed of the fan(s)? Currently with Alexa turning on the fan works but only on the maximum speed. It would be great to be able to say Low/Med/High. Clearly it’s already coded based on the lovelace card Low/Med/High buttons, how do you translate that to a voice command. This is all new to me and I’m not quite sure how to work on it and/or where to start. Is it in the YAML or part of the github code?

1 Like

This is assuming you already have Alexa setup in HA. Install the Alexa app on your mobile device and do something like this:

Step 1.)
image

Step 2.)
image

Step 3.)
image

Step 4.) Click create routine…

Step 5.)
image

Step 6.) Click add action…
image

Step 7.)
image

Step 8.)
image

Step 9.)

Step 10.)

Step 11.)

Received my iFan04 tonight. No matter what I’ve tried, I cannot get it to flash any code whatsoever.

  • Double-checked pin header soldering
  • Used meter continuity to verify pins are soldered correctly.
  • Swapped RX and TX multiple times.
  • Held the button down briefly and for the entire duration of attempts.
  • Provided 3.3VDC to the board

Anyone have any documented, reliable suggestions for flashing this board?
It helps when you power the board with 110VAC.
Also helps when you depress the button while applying the power.
It finally flashed. :wink:

I had the same problem you did Fred. I think it’s post #57 where NonaSuomy documented he had issues with flashing and it only worked with 110v.

As a side note, sonoff 1ch relays work fine with 3.3v applied, sonoff 4ch relays need 110v.

1 Like

Yup, thanks! :slight_smile: Mentioned again in post #72, as well.

1 Like

With some help from vincentscode, bearer and kraxman got the bleeding edge ENS160+AHT21 module stuff functioning that I mentioned above!

# ENS160 + AHT21 Module https://www.aliexpress.us/item/3256804712105923.html

# Define the I2C device for iFan04 to the Test Points on the back of the board.
# GPIO04 - TP11 D_RX (SDA)
# GPIO05 - TP10 D_TX (SCL)
i2c:
  id: i2c_component
  sda: 4
  scl: 5
  scan: true

external_components:
  - source: github://pr#4243
    components:
      - ens160

sensor:
  - platform: aht10
    id: aht21_sensor
    address: 0x38
    temperature:
      name: "AHT21 Sensor Temperature"
      id: aht21_sensor_temp
    humidity:
      name: "AHT21 Sensor Humidity"
      id: aht21_sensor_humi
    update_interval: 55s

  - platform: ens160
    id: ens160_sensor
    address: 0x53
    eco2:
      name: "ENS160 Sensor eCO2"
      id: ens160_sensor_eco2
    tvoc:
      name: "ENS160 Sensor Total Volatile Organic Compounds"
      id: ens160_sensor_tvoc
    aqi:
      name: "ENS160 Sensor Air Quality Index"
      id: ens160_sensor_aqi
    # Inject the temp/humi data from the AHT21 into the ENS160 attached module for compensation.
    # Makes the gas results more accurate.
    temperature: aht21_sensor_temp
    humidity: aht21_sensor_humi
    update_interval: 60s
[23:03:09][C][aht10:135]: AHT10:
[23:03:09][C][aht10:136]:   Address: 0x38
[23:03:09][C][aht10:140]:   Temperature 'AHT21 Sensor Temperature'
[23:03:09][C][aht10:140]:     Device Class: 'temperature'
[23:03:09][C][aht10:140]:     State Class: 'measurement'
[23:03:09][C][aht10:140]:     Unit of Measurement: '°C'
[23:03:09][C][aht10:140]:     Accuracy Decimals: 2
[23:03:09][C][aht10:141]:   Humidity 'AHT21 Sensor Humidity'
[23:03:09][C][aht10:141]:     Device Class: 'humidity'
[23:03:09][C][aht10:141]:     State Class: 'measurement'
[23:03:09][C][aht10:141]:     Unit of Measurement: '%'
[23:03:09][C][aht10:141]:     Accuracy Decimals: 2
[23:03:09][C][ens160:191]: ENS160
[23:03:09][C][ens160:192]:   Address: 0x53
[23:03:09][C][ens160:193]:   Update Interval: 15.0s
[23:03:09][C][ens160:194]:   CO2 Sensor 'ENS160 Sensor eCO2'
[23:03:09][C][ens160:194]:     Device Class: 'carbon_dioxide'
[23:03:09][C][ens160:194]:     State Class: 'measurement'
[23:03:09][C][ens160:194]:     Unit of Measurement: 'ppm'
[23:03:09][C][ens160:194]:     Accuracy Decimals: 0
[23:03:09][C][ens160:194]:     Icon: 'mdi:molecule-co2'
[23:03:09][C][ens160:195]:   TVOC Sensor 'ENS160 Sensor Total Volatile Organic Compounds'
[23:03:09][C][ens160:195]:     Device Class: 'volatile_organic_compounds'
[23:03:09][C][ens160:195]:     State Class: 'measurement'
[23:03:09][C][ens160:195]:     Unit of Measurement: 'ppb'
[23:03:09][C][ens160:195]:     Accuracy Decimals: 0
[23:03:09][C][ens160:195]:     Icon: 'mdi:radiator'
[23:03:09][C][ens160:196]:   AQI Sensor 'ENS160 Sensor Air Quality Index'
[23:03:09][C][ens160:196]:     Device Class: 'aqi'
[23:03:09][C][ens160:196]:     State Class: 'measurement'
[23:03:09][C][ens160:196]:     Unit of Measurement: 'index'
[23:03:09][C][ens160:196]:     Accuracy Decimals: 0
[23:03:09][C][ens160:196]:     Icon: 'mdi:chemical-weapon'
...
[23:03:10][D][sensor:127]: 'AHT21 Sensor Temperature': Sending state 22.74418 °C with 2 decimals of accuracy
[23:03:10][D][sensor:127]: 'AHT21 Sensor Humidity': Sending state 27.45819 % with 2 decimals of accuracy
[23:03:13][D][sensor:127]: 'ENS160 Sensor eCO2': Sending state 404.00000 ppm with 0 decimals of accuracy
[23:03:13][D][sensor:127]: 'ENS160 Sensor Total Volatile Organic Compounds': Sending state 25.00000 ppb with 0 decimals of accuracy
[23:03:13][D][sensor:127]: 'ENS160 Sensor Air Quality Index': Sending state 1.00000 index with 0 decimals of accuracy

image

Hello guys,

Thanks for the work here.
I’ve installed cpyarger’s version on my iFan04-H.
My fan is quite specific as it is an air extractor for my basement.

I have an issue as the switch works perfectly for maximum speed 3, but it didn’t work for speed 1 and 2.
I’ve modified the ifan.cpp file with the mixed relays

void IFan::set_low() {
  digitalWrite(relay_1, HIGH);
  digitalWrite(relay_2, LOW);
  digitalWrite(relay_3, LOW);
  beep();
}
void IFan::set_med() {
  digitalWrite(relay_1, HIGH);
  digitalWrite(relay_2, HIGH);
  digitalWrite(relay_3, LOW);
  beep(2);
}
void IFan::set_high() {
  digitalWrite(relay_1, HIGH);
  digitalWrite(relay_2, LOW);
  digitalWrite(relay_3, HIGH);

With this, the situation seems better on speed 2, but it’s not enough to maintain a normal speed, it’s very very slow, almost stopped, and the fan motor is doing a big electric noise and it will destroyed the electric motor long term.

How can I set up a higher speed for speed 2 ?

Sorry not sure if these are meant for all fan use or just large ceiling fans with capacitors in them. Did the device have a speed control before? Some motors state that they should not be used with dimmers etc as they will burnout the coils so be cautious if you care. You could try to turn on all 3 relays and see if that does anything different.

Hello Nona,

My fan is a motor/capactor kind of fan, just like a ceiling one. Its max power is 600W.
Today it is piloted with a 10-postions rotative button.

I’ve tried to integrate the iFan04 at level 10, and also directly from a 220V source.
I’ve tried to change the mixed relays strategy but as soon as I activate relay3 in a speed level (the only one working to get a moving speed), this level is going almost max speed. Relay1+2 mode alone is not enough.
The 3 levels proposed by the iFan04 are probably not adapted to the 600W of my fan.
Do you know what is the power of your standard ceiling fan?

I realize that my 10-positions rotative button is wired for each step.
I should probably use a Shelly2PM (or several) as a switch on the positions I’m interested in to pilot.
It would have been easier.

I’m also thinking about changing the capacitors on the iFan04 (2,5µF and 3µF) by bigger ones : 10µF is the biggest I’ve found. It seems some people who had high speed issues solved the problem by changing to smaller ones. In my case, I need more speed.
What do you think ?