Skip navigation
> RoadTest Reviews

IDT ZMOD4410 Indoor Air Quality Raspberry Pi HAT - Review


Product Performed to Expectations: 9
Specifications were sufficient to design with: 10
Demo Software was of good quality: 10
Product was easy to use: 8
Support materials were available: 8
The price to performance ratio was good: 10
TotalScore: 55 / 60
  • RoadTest: IDT ZMOD4410 Indoor Air Quality Raspberry Pi HAT
  • Buy Now
  • Evaluation Type: Development Boards & Tools
  • Was everything in the box required?: Yes
  • What were the biggest problems encountered?: HAL for Raspberry Pi was not implemented.

  • Detailed Review:

    My Use Cases

    I'm living in a suburb of Montreal, Canada, where we can have quite cold winters and hot summers. So it requires to spend a lot of money on heating and cooling to fill comfortable. One way to reduce costs and energy consumption is to exchange air with outside environment only when it is required. Otherwise concentration of CO2 and other VOC can have a negative consequences on health and productivity. So to achieve this equilibrium between energy cost and health/productivity we need to automate control of air exchange. And it requires accessible, reliable and affordable data  on air quality.


    A high level of CO2 and lover levels of oxygen can impair reaction, can cause sleepiness and other negative impacts. So my second use case is to add air quality sensor to alert driver in my project  Driver State Monitor with OpenCV and BeagleBone AI . Porting my code from BB AI to RPi should be relatively straightforward.


    And the last use case that comes to my mind is related to the main theme of 2020. It is very clear that a good ventilation is one of requirements to reduce spread of COVID-19 (and other airborne diseases). A high level of CO2 can be easily corelated with poor ventilation and high concentration of people. So to have a precise, simple and affordable way to generate an alert on poor ventilation in public places is a necessity, that I think will be in a high demand moving forward.


    After I've read specs of Avnet's Renesas ZMOD4410 Indoor Air Quality HAT for the Raspberry Pi I've decided participate in the roadtest as it seems supported all my requirements. Specifically I was looking to control my air exchanger based on air quality. My plan was to integrate ZMOD4410 Air Quality Pi Hat with Home Assistant (HA), which is a very popular open home automation platform with thousands integrations, so my air exchanger can be controlled by HA automation based on ZMOD4410 sensor data.


    Demo Application

    I've started my research by reading a nice blog Monitor Indoor Air Quality with the IDT ZMOD4410 Pi HAT by zebular13 . Monica used a demo application to work with the device and provided a setup procedure for RPi.

    I was able to reproduce her steps. But I've run it in a Docker container to eliminate impact on other RPi applications.

    FROM balenalib/raspberrypi3-debian-python:latest
    WORKDIR /home/pi/zmod
    ENV GS_VERSION=7.0.1
    ENV PYTHONPATH="${PYTHONPATH}:/usr/lib/python3/dist-packages"
    ENV DEBIAN_FRONTEND=noninteractive
    COPY requirements.txt ./
    RUN apt-get update -yqq \
            && apt-get upgrade -yqq \
            && apt-get install -yqq --no-install-recommends \
            apt-utils \
            build-essential \
            libffi-dev \
            libpq-dev \
            libssl-dev \
            python-pandas \
            python-pyqtgraph \
            curl \
            ffmpeg \
            libsm6 \
    RUN curl -SL$GS_VERSION.tgz \
        | tar xz
    RUN apt-get install -y python-pyside
    RUN python3 -m pip install --no-cache-dir pigpio
    RUN apt-get install -y pigpio
    WORKDIR /home/pi/zmod/GasSensorEvaluation/bin
    CMD ["sudo pigpiod"]


    You need to remove DISPLAY variable if you are attaching your screen directly to RPi or change IP address of DISPLAY value if you are using X-Server.


    I've used the following command to build and tag the image

    docker build -t arm32v7/pigpio .



    Once image was built and I've lunched it in a privileged mode, connected to the container shell and run the following commands:

    docker run -it --privileged -u root -v /sys:/sys -v /dev/mem:/dev/mem -v logs:/home/pi/zmod/GasSensorEvaluation/log arm32v7/pigpio /bin/sh
    sudo pigpiod
    ./GasSensorEvaluation -v warn -s


    Here is one of my observations over 24+ hours period.

    24 hours with zmod4410


    Integrating Avnet's Renesas ZMOD4410 Indoor Air Quality HAT for the Raspberry Pi with Home Assistant using Mosquitto MQTT Broker and Docker Compose

    My next step was integration with HA.There was several decision to make.

    1. I've decided to use Mosquitto MQTT broker to pass data from PI HAT to HA even HA can support I2C directly due proprietary Renesas SDK and libraries. It was simpler for me to use Mosquitto than wrapping Renesas C SDK  into Python library as required by HA. On other side HA supports MQTT natively. Another advantages of MQTT for me it is quite easy to debug it, and can be extended to publish zmod4410 sensor data from other devices, like Arduino.

    2. I decided to use docker-compose to run HA, Mosquitto and my custom code, which sends sensor data to MQTT broker. docker-compose has a small overhead, but provides isolation of workloads, automatic failover, simpler maintenance of different components. So I can update my code and rebuild its container without stopping or impacting anything else.


    Avnet ZMOD4410 Air Quality HAT for Raspberry Pi integration with Home Assistant using MQTT


    Home Assistant I2C vs MQTT


    As the base of my zmod bridge I've used a project shared by saicheong . I've did some modification in the code. I've asked Renesas support if I can share my code, which is using Renesas API. I think I can share it, but I want to get a confirmation that I'm not violating any terms defined in the license agreements  .


    UPDATE January 10th, 2021

    I've got permission to publish my code, which is using Renesas functions.

    The code for the project is now located on Github


    But the code to interact with MQTT broker is not using Renesas API. so I'm sharing it below.



    #include "zmodhat.h"
    #include <mosquitto.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    struct mosquitto *mosq = NULL;
    char *topic = NULL;
    void mqtt_setup() {
      char *host = (char *)"mqtt";
      // char *host = (char*)"localhost";
      // char *host = "";
      int port = 1883;
      int keepalive = 60;
      bool clean_session = true;
      topic = (char *)"office/sensor1";
      mosq = mosquitto_new(NULL, clean_session, NULL);
      if (!mosq) {
        fprintf(stderr, "Error: Out of memory.\n");
      if (mosquitto_connect(mosq, host, port, keepalive)) {
        fprintf(stderr, "Unable to connect.\n");
      int loop = mosquitto_loop_start(mosq);
      if (loop != MOSQ_ERR_SUCCESS) {
        fprintf(stderr, "Unable to start loop: %i\n", loop);
    int mqtt_send(char *msg) {
      return mosquitto_publish(mosq, NULL, topic, strlen(msg), msg, 0, 0);
    int main(int argc, char *argv[]) {
      if (zmodhat_init()) {
        fprintf(stderr, "ZMOD: INIT ERR");
      char *buf = (char *)malloc(96);
      while (1) {
        //fprintf(stdout, "%s\n", buf);
        int snd = mqtt_send(buf);
        if (snd != 0)
          fprintf(stderr, "mqtt_send error=%i for payload %s\n", snd, buf);
        usleep(10000000); // 10 seconds



    Building ZMOD to MQTT bridge

    The build of the code requires Mosquitto development and Renesas library. The first line installs Mosquitto library.

    sudo apt install libmosquitto-dev
    g++ -I/home/pi/zmod/dev/ -o zmod2mqtt zmod2mqtt.cpp zmodhat.cpp zmod4xxx.c lib_iaq_2nd_gen.a -lmosquitto


    Building Docker Image for ZMOD to MQTT bridge

    I've used balenalib/raspberrypi3-debian image as the base image. The image is optimized for use IoT devices including RPi.


    FROM balenalib/raspberrypi3-debian:latest-run
    WORKDIR /home/pi/zmod/zmod2mqtt
    RUN install_packages libmosquitto-dev
    COPY zmod2mqtt ./
    CMD ["./zmod2mqtt"]


    It takes a few minutes to build the image:

    pi@raspberrypi:~/zmod/zmod2mqtt $ docker build -t zmod2mqtt .
    Sending build context to Docker daemon  2.707MB
    Step 1/5 : FROM balenalib/raspberrypi3-debian:latest-run
     ---> 1eb1be4b4572
    Step 2/5 : WORKDIR /home/pi/zmod/zmod2mqtt
     ---> Using cache
     ---> 1e59abd9fa3f
    Step 3/5 : RUN install_packages libmosquitto-dev
     ---> Running in 58087d7d2ed7
    Here are a few details about this Docker image (For more information please visit
    Architecture: ARM v7
    OS: Debian Buster
    Variant: run variant
    Default variable(s): UDEV=off
    Extra features:
    - Easy way to install packages with `install_packages <package-name>` command
    - Run anywhere with cross-build feature  (for ARM only)
    - Keep the container idling with `balena-idle` command
    - Show base image details with `balena-info` command
    Reading package lists...
    Building dependency tree...
    Reading state information...
    The following package was automatically installed and is no longer required:
    Use 'apt autoremove' to remove it.
    The following additional packages will be installed:
    The following NEW packages will be installed:
      libmosquitto-dev libmosquitto1
    0 upgraded, 2 newly installed, 0 to remove and 33 not upgraded.
    Need to get 111 kB of archives.
    After this operation, 237 kB of additional disk space will be used.
    Get:1 buster/main armhf libmosquitto1 armhf 1.5.7-1+deb10u1 [57.8 kB]
    Get:2 buster/main armhf libmosquitto-dev armhf 1.5.7-1+deb10u1 [52.7 kB]
    debconf: delaying package configuration, since apt-utils is not installed
    Fetched 111 kB in 0s (343 kB/s)
    Selecting previously unselected package libmosquitto1:armhf.
    (Reading database ... 10103 files and directories currently installed.)
    Preparing to unpack .../libmosquitto1_1.5.7-1+deb10u1_armhf.deb ...
    Unpacking libmosquitto1:armhf (1.5.7-1+deb10u1) ...
    Selecting previously unselected package libmosquitto-dev:armhf.
    Preparing to unpack .../libmosquitto-dev_1.5.7-1+deb10u1_armhf.deb ...
    Unpacking libmosquitto-dev:armhf (1.5.7-1+deb10u1) ...
    Setting up libmosquitto1:armhf (1.5.7-1+deb10u1) ...
    Setting up libmosquitto-dev:armhf (1.5.7-1+deb10u1) ...
    Processing triggers for libc-bin (2.28-10) ...
    Removing intermediate container 58087d7d2ed7
     ---> bf6f3242f21d
    Step 4/5 : COPY zmod2mqtt ./
     ---> 95ea3f3e973b
    Step 5/5 : CMD ["./zmod2mqtt"]
     ---> Running in b5aa0f5ad33f
    Removing intermediate container b5aa0f5ad33f
     ---> fea6fb459c86
    Successfully built fea6fb459c86
    Successfully tagged zmod2mqtt:latest

    And to test it:

    docker run -it --rm --name my-zmod2mqtt zmod2mqtt:latest


    Configuration for docker-compose

    The docker-compose.yml file defines three services - zmod, mqtt and homeassistant. Each service requires some parameters, which defines its configuration, ports, networking, volumes dependencies.


    version: '3'
        container_name: home-assistant
        image: homeassistant/home-assistant:stable
          - /home/pi/homeassistant:/config
          - TZ=America/New_York
        restart: on-failure
        network_mode: host
        container_name: mqtt
        hostname: mqtt
        image: eclipse-mosquitto:latest
          - "1883:1883"
          - /home/pi/mosquitto/config:/mqtt/config:ro
          - /home/pi/mosquitto/log:/mqtt/log
          - /home/pi/mosquitto/data/:/mqtt/data
          - /etc/localtime:/etc/localtime:ro
          - TZ=America/New_York
        restart: on-failure
        container_name: zmod
        image: zmod2mqtt:latest
          - "/dev/i2c-1:/dev/i2c-1"
          - mqtt
          - TZ=America/New_York
        restart: on-failure


    Mosquitto MQTT Broker Configuration

    I've used basic Mosquitto settings. You may need to extend it with extra settings, for example related to security.


    # Place your local configuration in /mqtt/config/conf.d/
    pid_file /var/run/
    persistence true
    persistence_location /mqtt/data/
    user mosquitto
    # Port to use for the default listener.
    port 1883
    log_dest file /mqtt/log/mosquitto.log
    log_dest stdout
    include_dir /mqtt/config


    Home Assistant Configuration

    HA configuration file defines sensors. ZMOD sensors are defined starting line 13.


      # Name of the location where Home Assistant is running
      name: Home
      # metric for Metric, imperial for Imperial
      unit_system: metric
      # Pick yours from here:
      time_zone: America/Montreal
      # Customization file
      customize: !include customize.yaml
    # Sensors
      - platform: mqtt
        name: "Temperature"
        state_topic: "office/sensor1"
        unit_of_measurement: '°C'
        value_template: "{{ value_json.temperature }}"
      - platform: mqtt
        name: "Humidity"
        state_topic: "office/sensor1"
        unit_of_measurement: '%'
        value_template: "{{ value_json.humidity }}"
      - platform: mqtt
        name: "IAQ"
        state_topic: "office/sensor1"
        unit_of_measurement: 'UBA level'
        value_template: "{{ value_json.iaq }}"
      - platform: mqtt
        name: "eCO2"
        state_topic: "office/sensor1"
        unit_of_measurement: 'ppb'
        value_template: "{{ value_json.eco2 }}"
      - platform: mqtt
        name: "TVOC"
        state_topic: "office/sensor1"
        unit_of_measurement: 'ppb'
        value_template: "{{ value_json.tvoc }}"
      - platform: mqtt
        name: "Stabilization"
        state_topic: "office/sensor1"
        unit_of_measurement: '%'
        value_template: "{{ value_json.stabilization }}"


    Running HA, ZMOD and MQTT in  docker-compose

    Starting  docker-compose is very simple

    docker-compose up -d

    You may want to check state of containers

    pi@raspberrypi:~ $ docker ps -a
    CONTAINER ID        IMAGE                                    COMMAND                  CREATED             STATUS                     PORTS                    NAMES
    5b576c548fb9        07bb7ef43299                             "/usr/bin/ .…"   52 minutes ago      Up 51 minutes                                       zmod
    4aad875d6134        eclipse-mosquitto:latest                 "/docker-entrypoint.…"   42 hours ago        Up 52 minutes    >1883/tcp   mqtt
    46bd48ece05a        homeassistant/home-assistant:stable      "/init"                  43 hours ago        Up 52 minutes                                       home-assistant


    And look into logs of a specific container like  zmod :

    docker logs zmod


    To stop all three containers :

    docker-compose stop

    Home Assistant

    Dashboard with IAQ, TVOC, eCO2, Temperature, Humidity and other sensors

    HA runs WEB UI by default on HTTP port 8123. Out of the box it provides overview of current values of all sensors.

    Home Assistant Dashboard with IAQ, TVOC, eCO2, Temperature, Humidity sensors


    Home Assistant eCO2 and TVOC History

    And HA provides history of all sensors. This history is stored in SQLite database and can be extracted.

    Home Assistant eCO2 and TVOC History


    Sensors' readings before and after restart of zmod container

    I've noticed that output of TVOC,eCO2 and IAQ sensors are quite different before and after restart of the HAT and a period of 11 minutes of stabilization.

    It is not clear for me which readings represent actuals - before or after reset.

    It may be the result of IAQ 2nd generation firmware (package from October 19, 2020), I hope to clarify it with Renesas support.


    UPDATE January 10th, 2021

    As it was suggested by support I've read the datasheet for ZMOD4410 section 8.4 Conditioning and Stability and found the complete explanation. "The ZMOD4410 will respond to TVOC immediately upon start-up; however, a conditioning period of 48 hours followed by a sensor module restart in an ambient environment is recommended to improve stability and obtain maximum performance. Best results are achieved with continuous operation because the module algorithm can learn about the environment over time." So I shouldn't look at sensor values after restart. I think the "stabilization" value mislead me as I've assumed that after "stabilization" reached 100% its sensor data is ready for consumption. As well as per section 8.6 Accuracy and Consistency I should expect ±25% accuracy for TVOC and ±10% for IAQ without additional calibration.

    Sensors reading before and after restart of zmod container are very different

    Here is another example of such behavior. So it is a consistent behavior of the sensors with .the recommended IAQ 2nd generation firmware.

    Sensors reading before and after restart


    But temperature and humidity readings where very stable.

    Stabilization of sensors after reset takes ~11 minutes .It needs to be taken in consideration.

    eCO2 level jumps from 0 to 400 after stabilization process completed.


    Electronic Nose

    ZMOD4410 is very sensitive. here its reaction on a cup of chamomile tea.

    Reaction on a cup of chamomile tea


    And IAQ sensor definitely can recognize presence even of a single human.




    • Easy to install the HAT on RPi using recommended for RPi power supply.
    • Pre-calibrated sensors
    • Very sensitive
    • A lot of potentials to extend capabilities of "electronic nose"
    • Firmware and algorithm can be changed
    • Several firmware options provided for different use cases
    • C SDK is available for different platforms
    • A lot of documentation on C SDK
    • It is using a limited amount of compute and memory resources



    • No HAL provided for RPi
    • Policies on propriate software limit community investments
    • Time difference between Germany (IDT Renesas location) and Canada (my home) may impact time required to resolve challenges with support
    • IAQ (v2) value after reset can be very different compare to a calculated value before reset. May be it is result of a neural network used to calculate IAQ.
    • The HAT reduces air circulation around RPi CPU and may lead to overheating issues. But I never experienced it.
    • I'll need to add another component to get data on other important air quality indicator such as PM2.5 dust concentration.


    I very thankful to Element14 and Avnet for opportunity to participate in this roadtest. I'm thankful to Matthias from Renesas for proving assistance with C SDK.


    I'm very excited about possibilities which can be opened by this new generation of sensors, which can be updated and tuned to different applications. As I start collection of air quality data in my office I'm planning to add additional content about exploring correlation between air quality and health metrics like sleep and physical performance using ML time-series data classification.


Also Enrolling

Enrollment Closes: Feb 23 
Enrollment Closes: Feb 19 
Enrollment Closes: Jan 29