Sonoff Ifan04 - ESPHome working code

Thanks again for the help on this! The device has been working great – flashed, fully installed in the fan, and integrated into Home Assistant. The feature set of the device works as expected, and was a great replacement for the failed Insteon FanLinc device.

There is one thing left to do, and I am unclear on how to do it. When I built and flashed the firmware, I did it from my personal Mac using a local install of esphome, with the device physically connected through the flasher to my Mac via USB-C. And, since the yaml file included Wi-Fi credentials, the device of course connected to the Wi-Fi network once it powered on. I was then able to adopt the device into Home Assistant and configured the ESPHome integration there for the device. But the device is not registered inside the ESPHome addon of Home Assistant, and so I can’t, for example, rebuild the firmware or update it. I feel like I should “adopt” it into the ESPHome instance inside of my Home Assistant machine, so I can flash it again in the future, etc. And I am unclear on how to do this…

1 Like

The ESPHome device needs to be on the same network CIDR as the ESPhome plugin. This is because it uses broadcast to notify devices that it’s around.

So if your wifi is on a different network than your home assistant you will need to forward the broadcast packets. Then ESPHome interface will show it as a new device detected on the network.

Hey everyone. Looking to jump into getting ESPHome on my iFan04-L this weekend.

Looks like I have similar experience to @duerrd561 with doing Bluetooth proxies and a couple other simple things. Never flashed a device this way before, but I’ve picked up a few things that it seemed like I would need. Let me know if this all looks right:



From what I understand,I just need to run cables to that adapter from that group of 4 contacts on the iFan04 PCB? Does it need to be connected to the wall/fan at all or can I just disconnect it and it gets power from the USB adapter for flashing?

I haven’t installed ESPHome to a device with a yaml config like this before but I think I can figure that part out when I get to it.

The last question is around functionality. From what I’ve read it seems like everything from the original remote should just continue to work, and the only config changes that I’ll really need to make is just network info and device name. Is that right?

I don’t think you’ll need the header pins (2nd pic). I used some wires from your first pic and chopped off the end one end and used the socket end to connect to the usb2ttl. Then Soldered to the fan04 temporarily (it’s super fiddily).

The ifan04 has pads so you either have to use a pin clamp (or similar setup) or just solder on as i mentioned.

Don’t forget to set hte voltage of the USB 2 TTL adapter correctly.

My Home Assistant and ESPHome device are on the exact same Wi-Fi network subnet, and Home Assistant totally sees the device and has it all setup. It just isn’t showing up (i.e. to be “adopted”) in the ESPHome addon running on Home Assistant. Make sense?

Yup makes sense…
Looks like others have experienced this problem before too… Esphome devices not showing up on dashboard but are working - #7 by zoogara

Unfortunately I don’t have this setup - so will let others weigh in.

1 Like

Thanks! That was what I was looking for, and the thread you sent me to essentially has a solve/workaround which is to manually create the yaml file from my Mac’s esphome directory into the config/esphome/ directory on HA. This makes total sense, and I’m sure it would get me where I wanted.

One thing that I realize is probably a blocker on this specific device is the fact that I had to download a git clone of that rh1rich/esphome-ifan04 project and then I used the yaml from it to build this device’s firmware. And since that yaml file includes references to dependencies also in that git clone, I wouldn’t be able to get the exact same setup by just creating the yaml file on HA under config/esphome/ – I’d need the dependencies as well.

I think something like this is what is needed:

packages:
  ifan04.esphome: github://rh1rich/esphome-ifan04/ifan04-test.yaml@main

However I don’t think the ifan04-test.yaml file is designed to work for this inheritance use case. Someone would probably need to port it to a new file that could then be imported as a package. Am I thinking about this correctly?

substitutions:
  device_name: office-fan
  device_displayname: Office Fan

esphome:
  name: $device_name
  friendly_name: $device_displayname
  includes:
    - ifan_remote.h

esp8266:
  board: esp8285
  framework:
    version: latest
  early_pin_init: true
  restore_from_flash: true

preferences:
  flash_write_interval: 5min

# Enable logging
# To use logging via uart (UART0 TX, GPIO01) you have to remove
# uart_bus and the IFanRemote custom text_sensor.
logger:
  baud_rate: 0
#  level: VERBOSE

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

ota:
  password: "NOPE"

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

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

captive_portal:

web_server:
  port: 80

# GPIO00 ... Button
# GPIO01 ... UART0 TX
# GPIO03 ... UART0 RX (RF remote control)
# GPIO04 ... I2C SDA (TP11 D_RX on PCB backside)
# GPIO05 ... I2C SCL (TP10 D_TX on PCB backside)
# GPIO09 ... Light Relay (D7)
# GPIO10 ... Buzzer
# GPIO12 ... Fan Relay 2 (D11, 3uF cap on IFAN04-H)
# GPIO13 ... LED
# GPIO14 ... Fan Relay 1 (D5, 2.5uF cap on IFAN04-H)
# GPIO15 ... Fan Relay 3 (D13, no cap)

status_led:
  pin:
    number: GPIO13
    inverted: true

i2c:
  sda: GPIO04
  scl: GPIO05
  frequency: 200kHz

# UART configuration for the RF remote control
# Logging via uart must be disabled to use this.
uart:
  rx_pin: GPIO03
  baud_rate: 9600
  id: uart_bus

output:
  - platform: gpio
    id: light_relay
    pin:
      number: GPIO09
      inverted: true
  - platform: gpio
    id: buzzer
    pin:
      number: GPIO10
      inverted: true
  - platform: gpio
    id: fan_relay_1
    pin: GPIO14
  - platform: gpio
    id: fan_relay_2
    pin: GPIO12
  - platform: gpio
    id: fan_relay_3
    pin: GPIO15
  - platform: template
    id: fan_relays
    type: float
    write_action:
      - if: # Fan off
          condition:
            lambda: return (state < 0.3);
          then:
            - if:
                condition:
                  lambda: return id(buzzer_enabled).state;
                then:
                  - output.turn_on: buzzer
                  - delay: 300ms
                  - output.turn_off: buzzer
            - output.turn_off: fan_relay_1
            - output.turn_off: fan_relay_2
            - output.turn_off: fan_relay_3
      - if: # Fan low speed
          condition:
            lambda: return ((state >= 0.3) && (state < 0.6));
          then:
            - if:
                condition:
                  lambda: return id(buzzer_enabled).state;
                then:
                  - output.turn_on: buzzer
                  - delay: 50ms
                  - output.turn_off: buzzer
            - output.turn_off: fan_relay_3
            - output.turn_on: fan_relay_1
            - output.turn_on: fan_relay_2
            - delay: 5s
            - output.turn_off: fan_relay_2
      - if: # Fan mid speed
          condition:
            lambda: return ((state >= 0.6) && (state < 0.9));
          then:
            - if:
                condition:
                  lambda: return id(buzzer_enabled).state;
                then:
                  - output.turn_on: buzzer
                  - delay: 50ms
                  - output.turn_off: buzzer
                  - delay: 100ms
                  - output.turn_on: buzzer
                  - delay: 50ms
                  - output.turn_off: buzzer
            - output.turn_off: fan_relay_3
            - output.turn_on: fan_relay_1
            - output.turn_on: fan_relay_2
      - if: # Fan high speed
          condition:
            lambda: return (state >= 0.9);
          then:
            - if:
                condition:
                  lambda: return id(buzzer_enabled).state;
                then:
                  - output.turn_on: buzzer
                  - delay: 50ms
                  - output.turn_off: buzzer
                  - delay: 100ms
                  - output.turn_on: buzzer
                  - delay: 50ms
                  - output.turn_off: buzzer
                  - delay: 100ms
                  - output.turn_on: buzzer
                  - delay: 50ms
                  - output.turn_off: buzzer
            - output.turn_off: fan_relay_2
            - output.turn_off: fan_relay_3
            - output.turn_on: fan_relay_1
            - delay: 5s
            - output.turn_on: fan_relay_3
            - output.turn_off: fan_relay_1

light:
  - platform: binary
    name: 'Light'
    id: light_comp
    output: light_relay

fan:
  - platform: speed
    name: 'Fan'
    id: fan_comp
    output: fan_relays
    speed_count: 3

switch:
  - platform: template
    name: 'Buzzer enabled'
    id: buzzer_enabled
    entity_category: config
    optimistic: True
    restore_mode: RESTORE_DEFAULT_OFF

sensor:
  - platform: wifi_signal
    name: 'WiFi signal'
  - platform: uptime
    name: 'Uptime'
    unit_of_measurement: s

binary_sensor:
  - platform: gpio
    name: "Button"
    pin:
      number: GPIO00
      inverted: true

text_sensor:
  - platform: version
    name: 'ESPHome Version'

# Commands from the RF remote control are sent via
#  UART0 RX (= GPIO03)
# These commands are translated into hex strings
#  by the IFanRemote custom text_sensor (ifan_remote.h).
# The first 2 bytes (AA55) and the checksum byte
#  at the end, are removed.
#
# 1 (Light on/off)  = (AA55) 0104000104 (0A)
# 2 (Mute/buzzer)   = (AA55) 0106000101 (09)
# 3 (High speed)    = (AA55) 0104000103 (09)
# 4 (Medium speed)  = (AA55) 0104000102 (08)
# 5 (Fan off)       = (AA55) 0104000100 (06)
# 6 (Low speed)     = (AA55) 0104000101 (07)
# 7 (RF clearing)   = (AA55) 0101000102 (05)
# 8 (Wi-Fi pairing) = (AA55) 0101000102 (05)

  - platform: custom
    lambda: |-
      auto ifan_remote_sensor = new IFanRemote(id(uart_bus));
      App.register_component(ifan_remote_sensor);
      return {ifan_remote_sensor};
    text_sensors:
      name: 'RC command'
      on_value:
        then:
          - if: # Light on/off
              condition:
                lambda: return x == "0104000104";
              then:
                - light.toggle: light_comp
          - if: # Buzzer on/off
              condition:
                lambda: return x == "0106000101";
              then:
                - switch.toggle: buzzer_enabled
                - output.turn_on: buzzer
                - delay: 10ms
                - output.turn_off: buzzer
          - if: # Fan off
              condition:
                lambda: return x == "0104000100";
              then:
                - fan.turn_off: fan_comp
          - if: # Fan low speed
              condition:
                lambda: return x == "0104000101";
              then:
                - fan.turn_on:
                    id: fan_comp
                    speed: 1
          - if: # Fan mid speed
              condition:
                lambda: return x == "0104000102";
              then:
                - fan.turn_on:
                    id: fan_comp
                    speed: 2
          - if: # Fan high speed
              condition:
                lambda: return x == "0104000103";
              then:
                - fan.turn_on:
                    id: fan_comp
                    speed: 3

This is what i’m using for my yaml config. I also SSHed into esphome and uploaded the ifan_remote.h file to the config directory that also stores the yaml .

I saw a number of people here had trouble getting the iFan04 into flash mode, how did you solve it? I’ve tried different USB cables, installing all recommend drivers for ESP and the adapter I’m using, but no luck.

When I plug just the adapter into my PC it recognizes it, but as soon as I try to connect the iFan04 nothing comes up. The LED comes on and it starts going into pairing mode if I just connect it to the adapter and plug it into my PC, but nothing happens when trying to hold the power button while giving it power.

Any ideas?

I had no issues doing it - but did have to hold the button on the ifan04 when plugging in the usb to the computer.

Ya that’s what I’ve been doing. Was there any LED or sound indication that it was in flashing mode?

Nope - I usually test it’s in the right mode by dumping the firmware first. I have had some challenges identifying the device on the computer.

From that single frontside picture the USB TTL device doesn’t have a regulator on it which means you might have missed that it is basically a requirement for this board. Sonoff Ifan04 - ESPHome working code - #61 by NonaSuomy

i have used that same adapter to flash all three of my boards and didn’t have any issues.

Registered here just to thank you for this!

It finally worked after spending the entire afternoon trying to flash it. I tried with a Prolific TTL and later switched to a Arduino UNO as an adapter, both with an external bench power supply. In the end the problem was forgetting to hv the 3 grounds together.

TY

Did you ever try this? Any luck?

Nope, seems they made an updated version of it https://www.tindie.com/products/bugrovs2012/i2c-mosfet-trailing-edge-ac-dimmer-light/
but here is some future depreciated code using a custom component if you have one and want to give it a go. This is for their 4 channel, possible that it is the same to control the two channel or maybe contact them and see if they will make a proper component to make it work. Krida i2c dimmer by dcsim0n · Pull Request #5773 · esphome/esphome · GitHub

4channel_krida.h

#include "esphome.h"
using namespace esphome;

class MyCustomFloatOutput : public Component, public FloatOutput {
  public:

    int i2c_addr;
    const int ch_addr[4] = {128, 129, 130, 131}; //AC dimmer channels
    int channel; // from 0 to -3
    int error;
    bool debug;

  void setup() override {

    debug = 0; // set debug off/on
    i2c_addr = 39; // set i2c address

    Wire.begin();
    Wire.beginTransmission(i2c_addr);
    for (int i = 0; i < 4; i++){ // loop through all channels
        Wire.write( ch_addr[i] );
        Wire.write(100); //off
    }
    error = Wire.endTransmission();
    if (debug == 1){log_err(error);} //debug

  }

  void write_state(float state) override {   // state is the amount this output should be on, from 0.0 to 1.0
    
    int value = state * 1024; // convert it to an integer first
    value = map(value, 0, 1024, 100, 0); //convert to 0-100%
  
    Wire.beginTransmission(i2c_addr);
    Wire.write( ch_addr[channel] );
    Wire.write(value);
    Wire.endTransmission();
    error = Wire.endTransmission();
    if (debug == 1){log_err(error);}
  }

    void log_err(int error){
    if (error==0){ ESP_LOGD ("custom", "I2C Transmissio: success");} // 0: success.
    else if (error==1){ ESP_LOGD ("custom", "I2C Transmissio: data too long to fit in transmit buffer");} // 1: data too long to fit in transmit buffer.
    else if (error==2){ ESP_LOGD("custom", "I2C Transmissio: received NACK on transmit of address");} // 2: received NACK on transmit of address.
    else if (error==3){ ESP_LOGD("custom", "I2C Transmissio: received NACK on transmit of data");} // 3: received NACK on transmit of data.
    else if (error==4){ ESP_LOGD("custom", "I2C Transmissio: other error");} // 4: other error.
    else if (error==5){ ESP_LOGD("custom", "I2C Transmissio: timeout");} // 5: timeout
    }

};

krida_4CH_AC_Dimmer.yaml

esphome:
  name: test-4ch
  includes:
	#path to your library
    - "/config/esphome/libraries/4channel_krida.h"
esp8266:
  board: nodemcuv2

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "xxxxxxxxxxxxx"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Name"
    password: "xxxxxxxxxxxxx"

captive_portal:

i2c:
    sda: D2
    scl: D1
    #scan: true

output:
- platform: custom
  type: float
  lambda: |-
    auto my_custom_float_output = new MyCustomFloatOutput();
    App.register_component(my_custom_float_output);
    my_custom_float_output->channel = 0;
    return {my_custom_float_output};
  outputs:
    id: ch_1

- platform: custom
  type: float
  lambda: |-
    auto my_custom_float_output = new MyCustomFloatOutput();
    App.register_component(my_custom_float_output);
    my_custom_float_output->channel = 1;
    return {my_custom_float_output};
  outputs:
    id: ch_2

- platform: custom
  type: float
  lambda: |-
    auto my_custom_float_output = new MyCustomFloatOutput();
    App.register_component(my_custom_float_output);
    my_custom_float_output->channel = 2;
    return {my_custom_float_output};
  outputs:
    id: ch_3
    
- platform: custom
  type: float
  lambda: |-
    auto my_custom_float_output = new MyCustomFloatOutput();
    App.register_component(my_custom_float_output);
    my_custom_float_output->channel = 3;
    return {my_custom_float_output};
  outputs:
    id: ch_4
    
    
fan:
  - platform: speed
    output: ch_1
    name: "4ch 1"
    
  - platform: speed
    output: ch_2
    name: "4ch 2"
    
  - platform: speed
    output: ch_3
    name: "4ch 3"
    
  - platform: speed
    output: ch_4
    name: "4ch 4"  

Looks like someone was using that to control a fan as well…

I later built some Analog led strip lights and was going to modify the fan lamp shroud to contain those instead.

Follow my later I2C stuff where I found the right I2C pins on the back of the board for connection details.

Hi everybody!
did somebody create a esphome custom component for the I2C MOSFET dimmer from krida?

that’s the arduino code:

would be nice to control the device with esp32 as I2C master

unfortunately no info from seller. little more guides and support couldn’t do so much harm :wink: especially regarding home assistant & esphome known as really very popular, more or less nothing to find …

@haydon So my Ifan04 has been functioning great with esphome since completing the process describe above here. And I’ve even got it managed through the esphome addon in HA now. So all good there.

I’m encountering an error when trying to use the device in HA Scenes, and wondered if you might have any suggestions. When I try to activate a Scene which has the Ifan04 fan set to a speed greater than 0 (i.e. on vs. off), the following error is thrown:

Preset mode is not valid, valid preset modes are: .

This wasn’t happening when I first set the device up in scenes and tested them. But it is happening now, and so the fan doesn’t run with the other fans I’m trying to synchronize it to.

Any thoughts on how I might further troubleshoot this?

I actually bought the one above previously for another project and have been unsuccessful with it. The regulator and CH340 is a key note thanks! I’d love to buy from Amazon instead of Ali to avoid the very long wait. I think this one has the regulator. Would someone be willing to confirm or deny my intuition here? Amazon.com: DSD TECH SH-U07B USB to TTL Adatper with CH340C Chip (2PCS) : Electronics