I built a smart heat recovery system!

Written by Ingmar Verheij on December 24th, 2017. Posted in Other

Begin 2015 I’ve started on my first IoT project; building a smart heat recovery system (or warmte terugwin unit – WTW). Back then I wrote an article (in Dutch, see here) explaining why I felt this was necessary and my initial approach. I never could have expected the journey I started, I had opened Pandora’s box.

I must admit it has been a fun ride with many ups and some downs (otherwise there would have been no ups)! I’ve learned so many things, ranging from hardware design, PCB’s, software coding and the IoT ecosystem in general. Mainly it’s been a journey refreshing my memory, some things I haven’t done since I was 17 and I was eager on learning it again.

In this article I want to share with you the result of *almost* 3 years of learning; my IoT project is finished


Whenever the humidity is too high in the bathroom – someone’s taking a shower / bath – the heat recovery system automatically boosts to the highest state (3). When the humidity is low enough it goes back to the desired state. Additionally, a push notification is send to my phone when the filter requires changing.

I ended up adding a mechanical fan to suck more air out of the bathroom, switched with a smart power plug.



  • RenoventHR module ***
  • Automation Engine
  • Smart Humidity Sensor
  • Smart power plug


COTS components

Let’s start with the easy part. I’ve used common of the shelf (COTS) hardware to read the humidity in the bathroom and switch the added mechanical fan (bonus). Since I had some equipment from Xiaomi laying around I ended up using:

  • Xiaomi Mi Smart Temperature and Humidity Sensor
  • Xiaomi Mi Zigbee Smart Socket Plug
  • Xiaomi Mi Smart Home Gateway 2

Having connected modules doesn’t make a solution smart, only when the components are connected with each other (with some logic) it can truly become smart. For that I needed “home automation” software, I went with Home Assistant as that’s widely distributed and supports many hardware out of the box. Later in this article I’ll share the logic to make this a smart solution and the configuration associated.


RenoventHR module

This is the actual module I’ve built. My baby.

It is named RenoventHR since as connect this module to a Brink RenoventHR 

When designing this module, I had the following design requirements:

  • Mode/state information should be visible on the device;
  • Mode/state should be configurable manually (physical, with buttons);
  • Mode/state should be configurable via a mobile device;
  • Mode/state should be configurable via automation system (REST + MQTT);
  • Filter change indicator should be readable via automation system (REST + MQTT);
  • Connected via Wi-Fi;
  • Configuration should be configurable (no passwords in the code);
  • Hardware should appear OEM (no loose wires);
  • Needless to say, the WAF (wife acceptance factor) is important.

I’ve tried to make the solution replicable, for you (the reader) but also for the people in my neighborhood with similar systems. The module consists of two parts; hardware and software. Let’s start with the hardware as that’s the part I’m most proud of, also because that’s where the learning curve was highest.



The module is a custom-built PCB which I designed in AutoDesk EAGLE. After many (…) prototypes on breadboards I finally ordered some PCBs. Though the first attempt was unsuccessful, revision 0.4 is now in production. Not bad.

The design is based around an ESP8266, a low-cost but very powerful and flexible module which features Wi-Fi and GPIO pins. The onboard EEPROM is used to store the configuration and current mode/state (so it persists on a reboot). To simplify the design the module is using the Adafruit HUZZAH ESP8266 which costs roughly $10. Because of my requirements I found the amount of available GPIO pins on the ESP8266 was not enough, so I added a 8 bit I/O port expander (MCP23008). This port expanded also comes with configurable interrupt pins which was just what I needed. To mimic the physical switch and the 4 modes (0,1,2 and 4) it features two relays (one SPST and one SPDT). Manually switching up and down is down with two push buttons and changing the operation mode (from normal to setup-mode) is done with a rocker switch. A small 0.96” OLED screen is attached to show the state/mode and the IP address of the module. Mini USB provides a steady 5V to the module.

I’ve uploaded the files: schematic, board and part list on GitHub. Feel free to use them, feel encouraged to share if you used (or modified) them. The link is below.



Another thing I like about the ESP8266 is that I can run “Arduino code” on it. Not only is the IDE and syntax easy, there’s a huge community that provides a wealth of information. I won’t run you through the entire code here (it’s on GitHub) but I’ll share the functionality with you.

I’ve added some comments in the code to explain the functionality and used the Hungarian notation for variables (well, my interpretation of it 😆 ).


Operating mode

The device has two operating modes: normal and setup. In the setup mode the device acts as a Wi-Fi Access Point (SSID RenoventHR) and serves a webpage. The IP address is shown on the OLED so the user knows where to point the browser to.

On the webpage the following can be configured:

  • SSID: The SSID of the Wi-Fi network the device should connect to in normal
  • Passphrase: The passphrase for the Wi-Fi network
  • MQTT broker (optional): The address of the MQTT broker the device should connect to in normal If no address is provided the device won’t try to connect.

Since I’m lazy I included a button to scan for available networks.

The data is stored in the EEPROM of the ESP8266. This way no user data is stored in the code and I can reconfigure it without the need to re-program.


The normal mode connects to the Wi-Fi network read from the EEPROM (during boot). It hosts a webserver to change the mode via a mobile device (AJAX webpage), via HTTP REST and MQTT. An interrupt is attached to the rocker switch so the device will automatically reboot and start in the other mode when the switch is switched. Getting an ESP8266 to reliable reboot wasn’t all that easy, but connecting GPIO16 to the reset pin and issuing ESP.deepsleep() does the trick.



Interfacing with the module can be done with 4 methods (these interfaces are only available in the normal operating mode):

  • Physically
  • Webpage
  • MQTT


There are two pushbuttons connected to the device to which interrupts are attached. After a button is pressed the wtwState is raised/lowered. Immediately the relays are switched to the desired state and – when configured – the new setting is broadcasted via MQTT.

The OLED display will display the current wtwState.



An AJAX webpage is available on the IP address of the module (which is visible on the OLED screen), the page is optimized for a mobile phone. Using this webpage, a user can manually change the wtwState. The page is dynamic, meaning that if the state is changed with another method this is reflected on the webpage.

If the filter needs to be changed this is visible on the webpage.



Status information about the module can be obtained by issuing the following HTTP REST command:

This will return a JSON file containing the following information:

  • wtwState: the current wtwState [0/1/2/3]
  • changeFilter: 0 = filter is good, 1 = filter needs to be changed

The wtwState can we set using a HTTP REST command:

where value is the new state. This will return the same JSON file as with /status


When the connection with the MQTT broker – for whatever reason – is disconnected it will retry every 5 seconds (configurable). The module publishes the following topics:

  • RenoventHR/wtwState: the current wtwState [0/1/2/3]
  • RenoventHR/changeFilter: 0 = filter is good, 1 = filter needs to be changed

The information is broadcasted every 5 seconds (configurable) but also when the state is changed using another method.

It also subscribes to the following topic:

  • RenoventHR/setWTWstate: Sets the new wtwState [0/1/2/3]



During the design of the module I’ve thought hard about the security implications.

Though the possible impact on a breach is limited – so a hacker can change the mode/state of my WTW, meh – there’s a bigger risk. Since the device is connected via Wi-Fi there’s a risk that:

  • Someone can sniff the credentials;
  • The credentials are read from the EEPROM of the module.

To mitigate the first risk as much as possible I’m uploading the latest available firmware of the ESP8266. For instance, the latest ESP8266 firmware is protected against KRACK (link). The second problem I took for granted. I could have encrypted the credentials before I stored them in the EEPROM, but since the software (and thus encryption keys) are on GitHub that’s kind off useless.

Another possible risk is that a malicious user could sniff unprotected traffic. I haven’t implemented HTTPS or SMQTT (using TLS), simply because this is a home automation module… not an enterprise module.

TIP: When you’re planning on running this module (or any other IoT device) my advice is to create a separate Wii SSID and network segment to reduce the risks. After all, when the Wi-Fi credentials are stolen from your unsafe IoT device… your network if exposed.


Home Assistant

To make the solution smart, in fact just to make it a solution, I needed a platform that allowed me to connect different components and add some intelligence. I went with Home Assistant as that’s widely distributed and supports many hardware out of the box. What’s also convenient is that it comes with a MQTT broker out of the box.


The configuration is stored in the YAML file format. This all starts with the configuration.yaml file, I’ve used some !include statements to split the configuration in separate files. Secrets are all stored in a separate file (secrets.yaml). The relevant parts of the files are provided below.



Being the base configuration file, it contains the most “random” information. The relevant parts are:

  • enabling the MQTT broker;
  • including files for automation, group, notify and sensors (described below);
  • Configuring the Xiaomi Agara gateway details
  • Define an alert which sends a notification every 12h if the filters needs to be changed;
  • Define a boolean (switch) to indicate the WTW is in boost mode;
  • Define a number (slider) to set the desired WTW state.

The last two are visible on the GUI (see screenshots)



In this section we define the automations (or logic) that take place on state changes. I’ve defined a number to make the system as user friendly as possible:

  • wtwBoostGUI: Changes the state of the WTW – by publishing a value via MQTT- and switches the mechanical fan to the state defined with the switch on the GUI. It also hides/shows the slider (for desired state) and sends a notification to inform me;
  • wtwDesiredState: Change the state of the WTW based on the input provided with the slider;
  • wtwFilterChange: Turns on a LED if the filter needs to be changed (and turns it off when its changed);
  • bathroom_humidity_high: Sets the wtw Boost switch to True if the humidity is higher than 65%.
  • bathroom_humidity_low: Sets the wtw Boost switch to False if the humidity is lower than 60%.



The customize section allows you to customize the look and feel of some sensors. In this case I’m changing the name to a more readable name and icon of the WTW state and Change Filter.



The GUI is mostly configured using the groups section. What components are showed and in what tabs is defined here. In the screenshot you’ll see I’ve put the boost and desired state on the front-page where the actual sensors are in tabs that correspondent to the actual floors.



Unlike the Xiaomi Agara gateway my module isn’t present by default, it needs to be defined in the sensor section. Since I’ve equipped the module with a MQTT interface I’m using two MQTT sensors and point them to the corresponding topics.


Source Files

If you want to access the PCB design (schematic, board and part list) or the Arduino code, you can find them on Github: https://github.com/



I know, in the introduction I told you I was finished. Well the module is finished and so is the solution, but I still need to design and print a case for the module. Once that’s finished (and I’m satisfied) I’ll upload another photo.


Ingmar Verheij

Ingmar Verheij works as a SE at Citrix Systems Inc. All written content is my own opinion, not my employers.

More Posts - Website

Follow Me:
TwitterLinkedInGoogle Plus

Tags: , , , , , ,

Trackback from your site.

Comments (2)

  • Ivo
    13 March 2018 at 08:33 |

    Hi Ingmar,
    I also build a “smart” system for my Brink Renovent ventilation. I just read your post after i build mine. but i notice that my filter signal is not stable. it is constantly changing (it looks likes spikes on the signal line), while the display on the ventilation remains the same. do you experience anything similar?

    • 13 March 2018 at 14:41 |

      Hi Ivo,
      Yes, I did have the same. Its very easy to resolve by adding a pull-down resistor (R7 in my schema).

Leave a comment



%d bloggers like this: