[GUIDE] Controlling ITHO Daalderop fan with ESP8266 and CC1101

Introduction
The Itho Daalderop fan can be remotely controlled using an ESP8266, CC1101 and appropriate software. Several posts are floating around on the Internet and on this forum on how to build a wireless remote. Many of these posts have a large number of questions / comments and it is not clear for a newcomer what to do. This guide attempts to give a newcomer clear instructions on what to do. Since this guide is in the “Community Guides” section you can edit it. So please feel free to update the information.

Please note that there seems to be various hardware versions of the Itho fan. This influences the functionality of your wireless remote.

== High level steps ==

  1. Buy the ESP8266 and CC1101
  2. Select a software package
  3. Wire the ESP8266 and CC1101 together
  4. Generate the software image
  5. Flash the software image
  6. Configure software
  7. Register the new remote with your Itho fan
  8. Integration with Home Assistant

== Buy the ESP8266 and CC1101 ==
Here are some links where to buy the ESP8266

ESP8266

CC1101

DuPont cables

== Select a software package ==
Here are the various software packages available (in random order):

I am using a remote control made with the instructions in “Itho Ventilatie bedienen en monitoren met Home Assistant”

== Wire the ESP8266 and CC1101 together ==
Use the wiring scheme as described in your software package documentation. You will probably need DuPont cables for this.

If there is no description try the wiring scheme below (from GitHub - arjenhiemstra/IthoEcoFanRFT: Control the Itho Daalderop Eco Fan RFT using a microcontroller and CC1101 chip. The original code is fully tested using a STK500 and ATMega328P, this fork is modified for use with an Arduino or ESP8266. The following Itho commands are working: low, medium, full, 3 different timers, join and leave. The code will give you full remote control over the Itho Eco Fan RFT.):

Connections between the CC1101 and the ESP8266 or Arduino:

CC11xx pins    ESP pins Arduino pins  Description
*  1 - VCC        VCC      VCC           3v3
*  2 - GND        GND      GND           Ground
*  3 - MOSI       13=D7    Pin 11        Data input to CC11xx
*  4 - SCK        14=D5    Pin 13        Clock pin
*  5 - MISO/GDO1  12=D6    Pin 12        Data output from CC11xx / serial clock from CC11xx
*  6 - GDO2       04=D2    Pin  2        Programmable output
*  7 - GDO0       ?        Pin  ?        Programmable output
*  8 - CSN        15=D8    Pin 10        Chip select / (SPI_SS)

The wiring seems to be the same for all software packages…

== Generate the software image ==
Use the instructions supplied with your software package. If there are no instructions and the software is written in ESPHome see below for generic instructions.

Software written in ESPEasy: try to find a web site which supplies the binary software image.

ESPHome
ESPHome is used by some software packages to generate a software image
There are various ways to use ESPHome:

ESPHOME-Itho
ESPHOME-Itho does not give any instructions how to generate the image. They point to the page ESPHome ITHO control where there is a how-to:

– QUOTE –

Not tested from HA yet, just from CLI. Assumption: you have a machine ready with the ESP8266 and C1101 connected to it.

That machine is also ready to go as instructed per Getting Started with the ESPHome Command Line — ESPHome (pip install)

  • Copy itho.yaml and c1101h to a directory of choice
  • Run esphome itho.yaml run and watch the magic happen
  • Go into HA, choose integrations and add your ITHO
  • If everything goes well, you’ll see sensor.fanspeed, sensor.fantimer and a couple of switch.fansend*** popping up.
  • Now include the contents of HA_configuration.yaml in your Home-Assitant configuration and reload HA
  • Enjoy!

– ENDQUOTE –

== Flash the software image ==
Follow the instructions provided with your chosen software package. If there are no instructions you can follow the generic instructions below for ESPHOME based software

== Configure software ==
Follow the specific instructions given with the software package

== Register the remote ==
If your remote has a new ID, you will have to register the remote with the fan unit

  1. Place the remote near the fan unit
  2. Unplug the fan unit
  3. Wait at least 15 seconds
  4. Plug the fan unit in the mains
  5. Within two minutes perform in the remote software the “registration procedure”

If the fan unit accepts the remote you will hear the fan speed changing.

== Integration with Home Assistant ==
Follow the instructions of your chosen software package.

================================================

Itho FAN versions
There seems to be various Itho FAN versions (source ITHO Daalderop FAN RF remote with C1101 and ESP8266 - #114 by J0J0):

  1. built before 2012
  2. built after 2012

More info on:

Known Issues

  • There are various hardware versions of the Itho FAN which may not be compatible with the software you have chosen.

Hardware descriptions

Itho Fan

Background information

8 Likes

Perhaps you want to automatically switch on the Itho fan when your are showering in the bathroom.
I am using two humidity sensors and an automation for this.
Sensor 1 is in the bathroom. Sensor 2 is placed in another part of the house (eg. the bedroom). I use two sensors because the relative humidity varies during the 4 seasons of the year.

I am placing my automation below as inspiration. You have to tweak the code to adjust it to your specific setup

alias: bathroom automation Itho
description: >-
  Compare the humidity difference between a reference sensor (sensor humidity_01) and a bathroom sensor (sensor humidity_02).
  If the difference is greater than the value in 'input_number.itho_trigger_on' switch the Itho on
  If the difference is less than value in 'input_number.itho_trigger_off' switch the Itho off
  Modify the following for your setup:
    sensor.humidity_01 and sensor.humidity_02: report humidity
    helpers input_number.itho_trigger_on and input_number.itho_trigger_off: thresholds when to control the Itho fan
    helper input_boolean.automation_turned_on_itho shows me when the automation swith
    script.telegram_jarvislogger send me a message when something happens. Replace with your messaging solution
    of choice. You can re-use the formulas in the message text.
    mqtt.publish is used to send commands to the Itho fan. Replace with your Itho fan controls
trigger:
  - platform: template
    id: bathroom-moist
    value_template: |-
      {{ ( states('input_boolean.automation_turned_on_itho') == 'off' )
        and (
          (states('sensor.humidity_01') | default(0) | float(0)) - 
          (states('sensor.humidity_02') | default(0) | float(0)) >= (states('input_number.itho_trigger_on') | default(9) | float(0))
        )
      }}
  - platform: template
    id: bathroom-dry
    value_template: |-
      {{ ( states('input_boolean.automation_turned_on_itho') == 'on' )
        and (
        (states('sensor.humidity_01') | default(0) | float(0)) - 
        (states('sensor.humidity_02') | default(0) | float(0)) <= (states('input_number.itho_trigger_off') | default(11) | float(0))
        )
      }}
    for: "00:02:00"
condition:
  - condition: not
    conditions:
      - condition: or
        conditions:
          - condition: state
            entity_id: sensor.humidity_01
            state: unavailable
          - condition: state
            entity_id: sensor.humidity_02
            state: unavailable
          - condition: state
            entity_id: input_number.itho_trigger_on
            state: unavailable
          - condition: state
            entity_id: input_number.itho_trigger_off
            state: unavailable
action:
  - choose:
      - conditions:
          - condition: trigger
            id: bathroom-moist
        sequence:
          - service: script.telegram_jarvislogger
            data:
              message: >-
                Bathroom automation turns on itho fan at {{
                states('sensor.humidity_01') }}%, reference {{
                states('sensor.humidity_02') }}%, trigger on {{
                states('input_number.itho_trigger_on') }}
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.automation_turned_on_itho
            data: {}
          - service: mqtt.publish
            data:
              topic: home/itho/command
              payload: high
      - conditions:
          - condition: trigger
            id: bathroom-dry
        sequence:
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.automation_turned_on_itho
            data: {}
          - choose:
              - conditions:
                  - condition: time
                    after: "17:00"
                    before: "19:00"
                sequence:
                  - service: script.telegram_jarvislogger
                    data:
                      message: >-
                        Bathroom automation: bathroom is dry during 'cooking
                        time'. Bathroom humidity {{
                        states('sensor.humidity_01') }}%, reference
                        {{ states('sensor.humidity_02') }}%, trigger
                        off {{ states('input_number.itho_trigger_off') }}
            default:
              - service: mqtt.publish
                data:
                  topic: home/itho/command
                  payload: medium
              - service: script.telegram_jarvislogger
                data:
                  message: >-
                    Bathroom automation - bathroom is dry at {{
                    states('sensor.humidity_01') }}%, turning off
                    Itho fan. Reference {{
                    states('sensor.humidity_02') }}%, trigger off {{
                    states('input_number.itho_trigger_off') }}
    default: []
mode: single

  • I use Zigbee Aqara temperature sensors which occasionally drop out of my Zigbee network. To prevent my automation from doing weird things there is a “condition” block which checks if all used sensors are available
  • the trigger ‘bathroom-dry’ contains a ‘for:’ statement to ensure that the humidity difference is low enough for at least 2 minutes. This ensures that the fan will not start to jitter (turn on and off all the time)
  • between 17:00 and 19:00 the fan is not automatically turned off. Most of the time I am cooking during that time and I want to leave the Itho fan turned on
  • I have another automation which turns off the Itho fan to ‘low’ each day at 2:00. This is handy because the fan will be set to a known state each day. And when I am on holiday the fan will be running in the ‘low’ setting
1 Like

Hi All,

When I compile with ESPhome i get this error:

 ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 3460, room 16 
tail 4
chksum 0xcc
load 0x3fff20b8, len 40, room 4 
tail 4
chksum 0xc9
csum 0xc9
v00067530
~ld
[I][logger:258]: Log initialized
[C][ota:469]: There have been 2 suspected unsuccessful boot attempts.
[I][app:029]: Running through setup()...
[D][switch:013]: 'FanSendLow' Turning ON.

When disable the ‘FanSendLow’ part its stuck on setup part.
Im using this information:

Hi there,
I’m using the ESPeasy solution for over 4 years now and works perfectly. However, my Itho box died on me and got a new one. This new version has an automatic state (measures humidity) and a new remote to come with it to trigger the automatic state (I did not buy the new remote).
Does anyone know more about this new feature and the software for the ESP?

Just recently jumped on the bandwagon but this is exactly a use-case I have in mind when I started this journey. Great to see it’s a somewhat realistic goal. I have ordered the hardware and would use two Netatmo humidity readings for this. Inspirational, thanks!

1 Like

@mchangsp thank you for your tutorial. just migrated my epseasy fan controller to esphome. i just have a small thing. my esphome device is not displayed on the main esphome webpage within home assistant. i can only access it when i go to the sidebar, select Settings > Devices & Services> esphome etc . Does anybody know how i can also add it to the main esphome homepage? so i can easy access the logs and update the controller.

1 Like

This is my first ESPhome project, since I prefer Zigbee to WiFi, but until now it is not succesful. I used a NodeMCU V3 board as the ESP. I managed to get this project working at the ESPhome level (ESP device is present in home assistant and reacting to commands AFAICT), but it doesn’t seem to do anything to my ventilation unit. I did a few attempts at joining the remote to the unit (unplug, wait, replug, toggle ‘Join’ switch on the ESP device interface), but no response. I triple checked the wiring from the ESP to the CC1101 but that seems to be all good.
Any suggestions?

@allard77 did you have success? if no, what is the current problem you have?

@barrelful, I didn’t have any success. I kind of got stuck because everything I could easily check (the ESP doing what I told it, the wiring) was working, but there was no reaction from the fan unit whatsoever. In the end I just gave up, it’s more of a nice to have kind of thing for me and I didn’t want to invest more time. Maybe I had a faulty RF chip or something, I didn’t take it to an oscilloscope to see if it was actually emitting signals.

Thanks for posting this guide! I got it mostly to work. I’m using the Climate build of ESPEasy which includes the Itho package.

I did notice frequent disconnect issues. Has anyone seen this and knows what the remedy is?

Never mind. Appeared to be an unstable ESP8266. I flashed another board and this one is stable. First time around I have seen this. Have many of these in production.

Hi there,

I use this integration for months (maybe years) now. Without any problem.
But since a few days the control is reversed. Low sets the fan to High and High to Low.

Nothing has been changed. Maybe an update of EspHome or Home Assistant…
Anyone else experiencing this issue?

ESP Home: 2024.8.3
Home Assistant: 2024.9.1

I don’t have this issue… Use it also for years.

Trying to get this running on an older ESP8266 (AI Tinker ESP8266MOD which appears to be an ESP07) but when I compile I get the following error:


In file included from src/main.cpp:48:
src/itho.h: In member function 'virtual void FanRecv::setup()':
src/itho.h:95:15: error: 'D1' was not declared in this scope; did you mean 'y1'?
   95 |       pinMode(D1, INPUT);
      |               ^~
      |               y1
/config/esphome/fancontrol.yaml: In lambda function:
/config/esphome/fancontrol.yaml:45:23: error: 'D1' was not declared in this scope; did you mean 'y1'?
   45 |           detachInterrupt(D1);
      |                       ^~
      |                       y1

I assume it has something to do with the board I use but I have no idea where to start looking. Any suggestions?

Update: switching to the esp07s tag allowed me to compile but now it won’t boot anymore. Anyone else has experience with these older ESP8266 boards?

is there anyone with enough knowledge of the code in HA.

i have it like this:

  - name: "Badkamer Fan Snelheid"
    state_topic: "ITHO/Fan/State"
    value_template: >
      {% if value == "0" %}Standby{% endif %}
      {% if value == "1" %}Laag{% endif %}
      {% if value == "2" %}Medium{% endif %}
      {% if value == "3" %}Hoog{% endif %}
      {% if value == "4" %}Full{% endif %}
      {% if value|int >= 11 %}Hoog(T){% endif %}

And:

fan:
  - name: Badkamer Ventilatie
    command_topic: "ITHO/Fan/cmd"
    state_topic : "ITHO/Fan/State"
    state_value_template: "{% if value|float==0 %}State 0{% endif %}{% if value|float >0 %}State 1{% endif %}"
    payload_on: "State 1"
    payload_off: "State 0"
    optimistic: true
    preset_modes:
      - "off"     
      - "low"
      - "medium"
      - "high"
      - "full"
    preset_mode_command_topic: "ITHO/Fan/cmd"
    preset_mode_command_template: >
      {% if value == 'low' %}
      State 1
      {% elif value == 'medium' %}
      State 2
      {% elif value == 'high' %}
      State 3
      {% elif value == 'full' %}
      State 4
      {% else %}
      State 0
      {% endif %}
    preset_mode_state_topic: "ITHO/Fan/State"
    preset_mode_value_template: >
      {% if value_json.fan == low %}
      State 1
      {% elif value_json.fan == medium %}
      State 2
      {% elif value_json.fan == high %}
      State 3
      {% elif value_json.fan == full %}
      State 4
      {% else %}
      State 0
      {% endif %}

it works, but i always have these errors in the log of HA:

'2' received on topic ITHO/Fan/State. 'State 1' is not a valid preset mode

Can anyone help me rewrite that code so it is in line with HA expects ?

thanks!

When I’m at home I will send my config parts to you. Have no errors and work great.

1 Like

That would be great. Thank you!

This is my config. Im using ESPhome. So its different then yours.

My FAN Integration:

- platform: template
  fans:
    mechanical_ventilation:
      friendly_name: "Mechanische afzuiging"

      value_template: >
        {{ "off" if states('sensor.fan_speed') == 'off' else "on" }}

      percentage_template: >
        {% set speed = {'off': 0, 'low': 33, 'medium': 66, 'high': 100} %}
        {{ speed.get(states('sensor.fan_speed'), 0) }}

      turn_on:
        action: switch.turn_on
        data:
          entity_id: switch.fan_high
      turn_off:
        action: switch.turn_on
        data:
          entity_id: switch.fan_standby
      set_percentage:
        action: switch.turn_on
        data_template:
          entity_id: >
            {% set id_mapp = {0: 'switch.fan_standby', 33:'switch.fan_low', 66:'switch.fan_medium', 100:'switch.fan_high'} %}
            {{id_mapp[percentage]}}
      speed_count: 2

My ESPHome config:

substitutions:
  devicename: meek-itho
  friendly: ITHO
  ip: 192.168.100.150

esphome:
  name: ${devicename}
  platform: ESP8266
  board: nodemcuv2
  includes: 
    - lib/scriptman.h
  libraries:
    - SPI
    - Ticker
    - https://github.com/Scriptman/ESPHome_ITHO_Eco_Fan_CC1101.git
    
  on_boot:
    then:
      - lambda: |-
          Idlist[0]={"65:99:96:55:96:a9:9a:56","Kitchen"};
          Idlist[1]={"6a:59:6a:55:96:a9:9a:56","Bathroom"};
          Idlist[2]={"ID3","ID3"};
          Mydeviceid="Home Assistant";
          id(swfan_low).turn_on(); //This ensures fan is at low-speed at boot

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  reboot_timeout: 0s
  fast_connect: true
  manual_ip:
    static_ip: ${ip}
    gateway: 192.168.100.1
    subnet: 255.255.255.0
    dns1: 192.168.100.1
  ap:
    ssid: ${devicename}
    password: !secret password
    channel: 4

api:
  encryption:
    key: !secret api_key
  reboot_timeout: 15min
          
time:
  - platform: homeassistant
    id: homeassistant_time

captive_portal:

web_server:
  port: 80
  
preferences:
  flash_write_interval: 1min
  
logger:
  level: INFO
  
ota:
  - platform: esphome
    password: !secret password

safe_mode:
  reboot_timeout: 0s

button:
  - platform: factory_reset
    name: "${friendly} - Reset"

binary_sensor:
  - platform: status
    name: "${friendly} - Connection status"

sensor:
  - platform: wifi_signal
    id: "wifi_db_signal"
    name: "${friendly} - Wifi Signal"
    update_interval: 5min

  - platform: uptime
    name: "${friendly} - Uptime"
    update_interval: 5min

  - platform: template
    name: "${friendly} WiFi Percentage"
    id: wifi_percentage
    entity_category: diagnostic
    state_class: measurement
    update_interval: 10s
    unit_of_measurement: "%"
    accuracy_decimals: 0
    icon: mdi:wifi
    lambda: >
      auto signal = id(wifi_db_signal).state;
      float perc = 0;
      if (signal < -92.0) 
        perc = 100.0; 
      else if (signal > -21.0) 
        perc = 1.0; 
      else 
        perc = round(( -0.0154 * signal * signal )-( 0.3794 * signal ) + 98.182 );

      if(perc <= 0)
        return 0.0;
      else if(perc >= 100)
        return 100.0;
      else
        return perc;

switch:
  - platform: custom
    lambda: |-
      auto fansendstandby = new FanSendStandby();
      App.register_component(fansendstandby);
      return {fansendstandby};
    switches:
      name: "Fan Standby"
      id: swfan_standby
      icon: mdi:fan
      
  - platform: custom
    lambda: |-
      auto fansendlow = new FanSendLow();
      App.register_component(fansendlow);
      return {fansendlow};
    switches:
      name: "Fan Low"
      id: swfan_low
      icon: mdi:fan

  - platform: custom
    lambda: |-
      auto fansendmedium = new FanSendMedium();
      App.register_component(fansendmedium);
      return {fansendmedium};
    switches:
      name: "Fan Medium"
      id: swfan_medium
      icon: mdi:fan

  - platform: custom
    lambda: |-
      auto fansendhigh = new FanSendHigh();
      App.register_component(fansendhigh);
      return {fansendhigh};
    switches:
      name: "Fan High"
      id: swfan_high
      icon: mdi:fan

  - platform: custom
    lambda: |-
      auto fansendt1 = new FanSendIthoTimer1();
      App.register_component(fansendt1);
      return {fansendt1};
    switches:
      name: "Fan Timer 10min"

  - platform: custom
    lambda: |-
      auto fansendt2 = new FanSendIthoTimer2();
      App.register_component(fansendt2);
      return {fansendt2};
    switches:
      name: "Fan Timer 20min"

  - platform: custom
    lambda: |-
      auto fansendt3 = new FanSendIthoTimer3();
      App.register_component(fansendt3);
      return {fansendt3};
    switches:
      name: "Fan Timer 30min"

  - platform: custom
    lambda: |-
      auto fansendjoin = new FanSendIthoJoin();
      App.register_component(fansendjoin);
      return {fansendjoin};
    switches:
      name: "Fan Pair"

  - platform: safe_mode
    name: "${friendly} - safe mode"
    id: "${friendly}_safe_mode"

text_sensor:
  - platform: wifi_info
    ip_address:
      name: "${friendly} - IP"
      icon: mdi:lan
    ssid:
      name: "${friendly} - SSID"
      icon: mdi:lan
    bssid:
      name: "${friendly} - BSSID"
      icon: mdi:lan
    mac_address:
      name: "${friendly} - MAC"
      icon: mdi:lan

  - platform: version
    name: "${friendly} - Version"
    hide_timestamp: true

  - platform: custom
    lambda: |-
      auto fanrecv = new FanRecv();
      App.register_component(fanrecv);
      return {fanrecv->fanspeed,fanrecv->fantimer,fanrecv->Lastid};
    text_sensors:
      - name: "Fan Speed"
        icon: "mdi:transfer"  
      - name: "Timer"
        icon: "mdi:timer"
      - name: "Fan Remote"
        icon: "mdi:id-card"

Oof! Thats much more and different than mine. Going to look at it tonight. But not sure that ill figure this out :sweat_smile: