Skip navigation
> RoadTest Reviews

Raspberry Pi Click Board Sensor Kit - Review


Product Performed to Expectations: 10
Specifications were sufficient to design with: 10
Demo Software was of good quality: 9
Product was easy to use: 8
Support materials were available: 8
The price to performance ratio was good: 10
TotalScore: 55 / 60
  • RoadTest: Raspberry Pi Click Board Sensor Kit
  • Buy Now
  • Evaluation Type: Development Boards & Tools
  • Was everything in the box required?: Yes
  • What were the biggest problems encountered?:

  • Detailed Review:




    First of all, I am grateful for being selected for this Raspberry Pi Click Board Sensor Kit roadtest. My project is a supplemental home air quality controller to my ecobee system.The RPi3 samples the air quality through the environment click board, i.e., VOC (Volatile Organic compounds gases) sensor. It also communicates to the ecobee system ( using ecobee API through the my home network system. If the indoor air quality is deteriorated, the RPi3 can command ecobee to turn on the ventilation system so fresh air can be brought into house to improve air quality.


    This review will go directly into the use of environment click board and ecobee API. You should read the first part RoadTest Review a Raspberry Pi 3 Model B ! - Review regarding the hardware setup and OS & software installation. To help you get a sense of the system I am working on, here's a simple diagram.




    Get Environment Click Board Work


    The environment click board integrates a Bosch air quality VOC sensor called BME680. Here's the brief description excerpted from its datasheet - "The BME680 is a digital 4-in-1 sensor with gas, humidity, pressure and temperature measurement based on proven sensing principles. The sensor module is housed in an extremely compact metal-lid LGA package with a footprint of only 3.0 × 3.0 mm2 with a maximum height of 1.00 mm (0.93 ± 0.07 mm). Its small dimensions and its low power consumption enable the integration in battery-powered or frequency-coupled devices, such as handsets or wearables." Even though all sensors are used in the algorithm to produce IAQ index numbers, I will only explicitly focus on the air quality sensor (i.e., VOC gas sensor). Excerpted from the datasheet, the below is the air quality color-coding table.

    Raspberry Pi 3 talks to the environment click board through I2C interface. Regarding how to configure the I2C interface on RPi3 for the click board, please read I2C Configuration for Environment Click Board. There are not many software options to work with the environment click board. I found three options:

    1. BME680 Python Library from PIMORONI for its product BME680 Breakout board

    2. MIKROE's library for the Environment Click Board

    3. Bosch Sensortec Environmental Cluster (BSEC) Software


    Among the above three options, option 1 is the easiest one to use, however, its example code provides totally opposite results to the above color-coding IAQ index. In other words, when air quality gets worse, the index numbers reported by this option is getting smaller. It gives totally wrong outcome. Option 2 seems to require proprietary compiler which I don't have. Option 3 is proprietary software from the sensor's manufacturer - Bosch. It's free (requires a free registration of license agreement) and no need of proprietary compiler. It also provides an example for Raspberry Pi3. The only drawback is the code written in C. I did a simple wrapper up so I can use it in Python. Since I didn't try option 2, I won't discuss more about this option.


    Option 1 - BME680 Python Library from PIMORONI


    You can download the Python library and example code for BME680 from Github here. Installation of the library is pretty simple, just type

    sudo pip3 install bme680


    You can take a peek of bme680 library in Python using help(bme680) command. One thing I need to point out is BME680's I2C address on the environment click board is I2C_ADDR_SECONDARY (0x77).


    In the downloaded package, there's an example Python code under examples folder. To run the example, just type the command (in bold font) below.

    pi@raspberrypi:~/bme680/examples $ python3 - Estimates indoor air quality.


    Runs the sensor for a burn-in period, then uses a

    combination of relative humidity and gas resistance

    to estimate indoor air quality as a percentage.


    Press Ctrl+C to exit!



    Collecting gas resistance burn-in data for 5 mins


    Gas: 63321.42561670191 Ohms

    Gas: 68917.07185561447 Ohms

    Gas: 68223.48852916383 Ohms


    Gas: 221028.08763224096 Ohms

    Gas: 220887.20702258317 Ohms

    Gas: 220325.4759325464 Ohms

    Gas baseline: 218968.9246939431 Ohms, humidity baseline: 40.00 %RH       (5 mins burn-in time is over)


    Gas: 219488.22 Ohms,humidity: 55.34 %RH,air quality: 93.61       (start reporting air quality numbers)

    Gas: 221028.09 Ohms,humidity: 55.12 %RH,air quality: 93.70

    Gas: 219210.54 Ohms,humidity: 55.37 %RH,air quality: 93.59


    Gas: 224900.98 Ohms,humidity: 55.30 %RH,air quality: 93.62

    Gas: 223883.93 Ohms,humidity: 55.37 %RH,air quality: 93.59

    Gas: 224609.45 Ohms,humidity: 55.22 %RH,air quality: 93.66

    Gas: 172625.38 Ohms,humidity: 55.28 %RH,air quality: 77.76       (start putting marker pen close to BME680 chip to mimic polluted air)

    Gas: 165493.46 Ohms,humidity: 55.22 %RH,air quality: 75.34

    Gas: 166588.40 Ohms,humidity: 55.40 %RH,air quality: 75.64

    Gas: 171283.30 Ohms,humidity: 55.28 %RH,air quality: 77.30       (air quality numbers contradict the IAQ color-coding table above, i.e., the polluted air should increase the corresponding air quality numbers)

    Gas: 176074.43 Ohms,humidity: 55.30 %RH,air quality: 78.94

    Gas: 188046.06 Ohms,humidity: 55.30 %RH,air quality: 83.03

    Gas: 194701.97 Ohms,humidity: 55.28 %RH,air quality: 85.32       (remove marker pen away from BME680 chip)

    Gas: 199063.72 Ohms,humidity: 55.33 %RH,air quality: 86.79

    Gas: 202791.27 Ohms,humidity: 55.34 %RH,air quality: 88.07

    Gas: 205314.29 Ohms,humidity: 55.19 %RH,air quality: 88.99

    Gas: 209535.03 Ohms,humidity: 55.19 %RH,air quality: 90.44

    Gas: 210937.96 Ohms,humidity: 55.23 %RH,air quality: 90.90

    Gas: 213405.98 Ohms,humidity: 55.28 %RH,air quality: 91.73

    Gas: 215932.43 Ohms,humidity: 55.21 %RH,air quality: 92.62

    Gas: 216201.86 Ohms,humidity: 55.26 %RH,air quality: 92.69

    Gas: 218519.42 Ohms,humidity: 55.22 %RH,air quality: 93.50

    Gas: 219071.97 Ohms,humidity: 55.15 %RH,air quality: 93.69       (polluted air fully dissolved)

    As described in the highlighted explanation part, the outcome of this software package is totally contradict to the color-coding table shown above. Thus, this software isn't much of use for measuring indoor air quality.


    As I described before, I will skip option 2 and focus on option 3.



    Option 2 - Skipped Because of Proprietary Compiler Used


    Option 3 - Bosch Sensortec Environmental Cluster (BSEC) Software


    First of all, you need to download Bosch's BSEC software from here - Scroll down to the middle of the webpage, click on the download button at the left hand side shown below.

    It will pop up a simple registration as the acceptance of license agreement.


    My strategy of using BSEC software is to complete the example code in C first. Next, wrapping minimum interfaces into Python so I can finish the rest of my project in Python.



    Prepare the Complete Example as a Shared Library


    The software package came in as a .zip file. After unzip it, it should have a folder structure as shown below.

    The algo folder contains the algorithm library for processing sensor data. The library is delivered as a statically linked library with interface header files. The API folder contains the BME680 sensor driver code. As the name suggests, Arduino folder contains the library and example for Arduino platform. Config folder contains a few pre-defined application configuration. example folder contains an application example skeleton. You just need to fill in a few missing parts to create your own application.


    BME680 provides two types of interfaces to the host controller - SPI and I2C. On our environment click board, I2C is used. Our current Raspberry Pi OS has pre-installed I2C library under /usr/lib/arm-linux-gnueabihf folder, however, the library name is followed by a version digit number which will make our code fail to link. Thus we need to either rename or create a symbolic link as shown below.

    ln -s /usr/lib/arm-linux-gnueabihf/ /usr/lib/arm-linux-gnueabihf/

    Since the BSEC software contains a lot more files than I need, I simply copied the required files into a separate folder. Here's the list of files:

    I have shared my code on Github, but the files listed above are from BSEC software package which is licensed by Bosch. You need to download it from here. As mentioned before, it is free and just need a quick registration for the license agreement.


    As mentioned before, the download software has an incomplete example project. You need to complete a few function bodies in bsec_iot_example.c file, such as

    int8_t bus_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data_ptr, uint16_t data_len)
        // ...
        // Please insert system specific function to write to the bus where BME680 is connected
        // ...
        return 0;
    int8_t bus_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data_ptr, uint16_t data_len)
        // ...
        // Please insert system specific function to read from bus where BME680 is connected
        // ...
        return 0;
    void sleep(uint32_t t_ms)
        // ...
        // Please insert system specific function sleep or delay for t_ms milliseconds
        // ...
    int64_t get_timestamp_us()
        int64_t system_current_time = 0;
        // ...
        // Please insert system specific function to retrieve a timestamp (in microseconds)
        // ...
        return system_current_time;
    void output_ready(int64_t timestamp, float iaq, uint8_t iaq_accuracy, float temperature, float humidity,
         float pressure, float raw_temperature, float raw_humidity, float gas, bsec_library_return_t bsec_status,
         float static_iaq, float co2_equivalent, float breath_voc_equivalent)
        // ...
        // Please insert system specific code to further process or display the BSEC outputs
        // ...


    You can find my code on the Github link mentioned before. I also provide a Makefile so you can easily create the shared library.



    Wrap the Shared Library to Python


    The original example code has a main function in bsec_iot_example.c file. To avoid the confusion with Python main function, I renamed the main function to


    int run(PyObject *bsec_output_func)


    The parameter bsec_output_func is a function pointer which eventually pointing to a Python function to read the air quality data (algorithm's output) and use it to control my home ventilation system through ecobee. The Python function will be called in output_ready() function which is the C callback function when the algorithm output is ready.


    There are many different ways to wrap C/C++ code to Python and I use SWIG. First you need to install the tool using the following command.

    sudo apt install swig


    First, I need to create a header file (air_quality_reader.h) containing the function(s) I'd like to use in Python. SWIG will use this header file to create a wrapper module.

    #include <Python.h>
    int run(PyObject *bsec_output_func);


    Next, I need to create an interface file (AirQualityReader.i). By default, the generated Python module has the same name of the interface file, i.e., AirQualityReader. Please note the header file created above is used in this interface file.

    %module AirQualityReader


    #include "air_quality_reader.h"


    %include "air_quality_reader.h"


    SWIG is used to create the wrapper using the following command. Two files will be generated - and AirQualityReader_wrap.c.

    $ swig -python AirQualityReader.i


    Last but not least, we need to create a shared library (.so) which contains all BSEC c code and Python wrapper. A Makefile has been provided on the Github page.

    $ gcc -fpic -I/usr/include/python3.7 -c AirQualityReader_wrap.c
    $ gcc -shared AirQualityReader_wrap.o bsec_code.o -o


    To use the AirQualityReader module in Python, here's an example code (file name

    import AirQualityReader
    def ecobeeControl(iaq: float, iaq_accuracy: int):
        print(f'IAQ {iaq:.0f}, IAQ Accuracy {iaq_accuracy:d}')


    To run the above example,

    $ python3


    This example outputs both in-door air quality index number and its accuracy. The accuracy numbers are range 0 to 3 as explained in the following table.


    Like we described in software option 1 above, the first a few minutes is used to collect sensor data and do an initial calibration. In this period, the accuracy number is 0 and IAQ numbers should be used. Here's an example of the output of software option 3.

    IAQ 25, IAQ Accuracy 0                          Collecting gas resistance burn-in data for 5 mins

    IAQ 25, IAQ Accuracy 0

    IAQ 25, IAQ Accuracy 0


    IAQ 25, IAQ Accuracy 0

    IAQ 25, IAQ Accuracy 0

    IAQ 25, IAQ Accuracy 0                         (5 mins burn-in time is over)

    IAQ 25, IAQ Accuracy 1

    IAQ 21, IAQ Accuracy 1

    IAQ 18, IAQ Accuracy 1

    IAQ 18, IAQ Accuracy 1

    IAQ 14, IAQ Accuracy 1

    IAQ 12, IAQ Accuracy 1

    IAQ 11, IAQ Accuracy 1

    IAQ 9, IAQ Accuracy 1

    IAQ 7, IAQ Accuracy 1

    IAQ 3, IAQ Accuracy 1

    IAQ 0, IAQ Accuracy 1

    IAQ 0, IAQ Accuracy 1

    IAQ 0, IAQ Accuracy 1


    IAQ 0, IAQ Accuracy 1

    IAQ 0, IAQ Accuracy 1

    IAQ 0, IAQ Accuracy 1

    IAQ 58, IAQ Accuracy 1                    (start putting marker pen close to BME680 chip to mimic polluted air)

    IAQ 250, IAQ Accuracy 2

    IAQ 419, IAQ Accuracy 2

    IAQ 500, IAQ Accuracy 2                  (air quality numbers agree with IAQ color-coding table above, i.e., the polluted air increased the corresponding air quality numbers)

    IAQ 500, IAQ Accuracy 2

    IAQ 500, IAQ Accuracy 2

    IAQ 500, IAQ Accuracy 2

    IAQ 500, IAQ Accuracy 2

    IAQ 448, IAQ Accuracy 2                  (remove marker pen away from BME680 chip)

    IAQ 403, IAQ Accuracy 2

    IAQ 356, IAQ Accuracy 2


    IAQ 117, IAQ Accuracy 2

    IAQ 105, IAQ Accuracy 2

    IAQ 96, IAQ Accuracy 2

    IAQ 87, IAQ Accuracy 2

    IAQ 80, IAQ Accuracy 2

    IAQ 74, IAQ Accuracy 2

    IAQ 69, IAQ Accuracy 2                    (polluted air gradually dissolved)


    IAQ 11, IAQ Accuracy 2

    IAQ 11, IAQ Accuracy 2

    IAQ 10, IAQ Accuracy 2

    IAQ 11, IAQ Accuracy 2

    IAQ 11, IAQ Accuracy 2



    Get ecobee Application Module Work


    This section shows how to connect the RPi to ecobee server and control my ecobee thermostat at home. In order to control your ecobee device, you need to go through a few steps: register the device, sign up as developer, create an ecobee application, obtain authorization code, add your application to My Apps list, obtain access token and refresh token and write Python code to access your device.


    Register ecobee Device and Create an Account


    To develop application for ecobee device, you have to register your device on You can find registration instructions from .


    Sign Up ecobee Developer Account

    To write application for ecobee devices, you have to register as an ecobee developer from here Use the same email address and password when you register your device to sign up as developer.

    Next go to and sign in using your username and password.



    Create ecobee Application


    After you sign in, you should see a page similar to the screenshot below. It shows your device information.


    If you click on the human icon at the top right cornet of the above screenshot, it will slide out a menu like the one shown below. Click on the Developer menu item, it will show a new window for creating an application.

    In the Developer page below, click on the Create New button to start creating a new application.


    Enter your application name and application summary similar to the one show below.


    Choose ecobee PIN as the Authorization Method.


    You can also select an image for the application icon.


    Enter some detailed description for your application and click on the Create button to create the application.


    Once the application is successfully created, it will show up on the application list at the left hand side of the window.


    Click on the application name you just created and record the API Key because you will need it for obtaining authentication codes next as well as requesting the access token and the refresh token. For privacy reason, part of the API key is blurred out in the screenshot below.



    Obtain Authorization Codes


    Open another web browser tab or window, go to this page and find the content shown below. Enter the API key you recorded in the last step then click on the Submit button. An authorization code will be generated (shown below and part of it has been masked out). Also please record the ecobeePin as well because you will need it when you add your application in the next section.

    Please note that you only have 10 minutes to add your application to My Apps list and obtain the access code and the refresh code in the next two sections.



    Add ecobee Application to My Apps List


    Go back to your ecobee account and Select My Apps menu item as shown below.


    Click on the Add Application button.


    Enter the ecobeePin you recorded in a previous step. Then click the Validate button.


    Next enter the authorization code your recorded in a previous step. Then click on the Add Application button.


    This is what looks like after you successfully add your application to My Apps list.



    Obtain Access Token and Refresh Token


    To access your ecobee device, you can make a request to the ecobee server with an unexpired access token. However, an access token will expire in an hour after it's been issued. To get a new access token, you need your API key as well as a refresh token. A refresh token will expire in a year after it's been issued. Every time you request a new access token, a refresh token will be issued at the same time. The old refresh token becomes invalid after the new refresh token has been issued.


    To obtain the refresh token the very first time, you need to enter the API Key and the Authorization code you recorded in previous steps. After you click on the Submit button, two tokens are generated as shown below.


    If your access token expires, you can use the following form to manually obtain a new pair of access token and refresh token. However, you probably want to use ecobee API to do the same using Python code. I will show you the code in the next section.



    Write Python Code to Control ecobee Device


    Obtain Access Token & Refresh Token

    ecobee provides developers the APIs to access your devices through Internet. To authenticate the access to the devices (i.e., using the APIs to communicate with your ecobee devices),  a valid access token is required. This token expires in an hour from the time is generated. The access token is not generated alone. Instead, a refresh token is also generated when we request the access token. The refresh token expires in a year after it's generated. To get a new pair of access and refresh tokens, you need to provide a valid refresh token plus your API key generated when you create your ecobee application (see Create ecobee Application section above).


    ecobee API documentation describes how to refresh tokens. I wrote Python code to do the job. One thing to keep in mind is everytime we get a new pair of tokens, we have to store the tokens and the expiring time into a file so we know when we need to refresh tokens. Thus, we create a file called tokens.json and you have to fill the file with your current pair of tokens and API key. Regarding how to obtain them the first time, please go back to the sections above.


        "ACCESS_EXPIRE": "time_stamp_when_refresh_tokens_plus_an_hour",

        "ACCESS_TOKEN": "access_token_in_response",

        "API_KEY": "your_API_key_here",

        "REFRESH_EXPIRE": "time_stamp_when_refresh_tokens_plus_a_year",

        "REFRESH_TOKEN": "refresh_token_in_response"



    Here's an example code of refreshing tokens and storing them into a file called tokens.json

    import io
    import time
    from datetime import datetime, timedelta
    import requests as reqs
    import json
    ecobee_base_url = ""
    tokenFileName = "tokens.json"
    def readJsonDataFromFile(filename):
        with open(filename, 'r') as token_file:
            data = json.load(token_file)
            return data
    def writeTokenFile(tokens, filename):
        with open(filename, 'w') as token_file:
            json.dump(tokens, token_file, sort_keys=True, indent=4)
    def refreshTokenRequest(tokens):
        req_data = {'grant_type': 'refresh_token', \
           'refresh_token': tokens['REFRESH_TOKEN'], \
           'client_id': tokens['API_KEY']}
        t = datetime.utcnow()
        access_token_expire = t + timedelta(0,0,0,0,0,1,0)
        refresh_token_expire = t + timedelta(365)
        r = + 'token', req_data)
        if r.status_code != 200:
            return False
        rj = r.json()
        tokens['REFRESH_TOKEN'] = rj['refresh_token']
        tokens['ACCESS_TOKEN'] = rj['access_token']
        tokens['ACCESS_EXPIRE'] = str(access_token_expire.timestamp())
        tokens['REFRESH_EXPIRE'] = str(refresh_token_expire.timestamp())
        return True
    def updateAccessToken(tokens):
        access_token_expire = datetime.fromtimestamp(float(tokens['ACCESS_EXPIRE']))
        if datetime.utcnow() > access_token_expire:
            if refreshTokenRequest(tokens):
                writeTokenFile(tokens, tokenFileName)
                return True
            return False
        return True
    myTokens = readJsonDataFromFile(tokenFileName)
    while (updateAccessToken(myTokens) == False):



    Control Fan through ecobee API


    I will use ecobee Set Hold API function to manually turn on the fan and use Resume Program to stop the fan (if the fan was off when I manually turned it on). All ecobee functions are implemented in HTTP request/response. We can use JSON object to represent the request function and parameters. For example, for Set Hold function, we created a JSON file called setHoldFanOn.json. For resume program, file name is resumeProgram.json.





        "selection": {




        "functions": [







              "fan": "on"






        "selection": {




        "functions": [










    Here's the Python code.


    api_versioni_url = "1/"
    thermostat_request_url = "thermostat"
    def controlThermostat(tokens, controlJsonFileName):
        full_url = ecobee_base_url + api_versioni_url + thermostat_request_url
        auth_headers = {'Content-Type': 'application/json;charset=UTF-8', 'Authorization': 'Bearer ' + tokens['ACCESS_TOKEN']}
        req_data = readJsonDataFromFile(controlJsonFileName)
        r =, headers = auth_headers, json=req_data)
        if r.status_code != 200:
            return False
        return True
    while (controlThermostat(myTokens, "setHoldFanOn.json") == False):
    while (controlThermostat(myTokens, "resumeProgram.json") == False):


    The base URL format for making API calls follows this convention:{version}/{requestURL}

    In the above Python code, our version is 1 and our request catagory is "thermostat".

    All HTTP requests must specify the Content-Type header and encoding in in the request:

    Content-Type: application/json;charset=UTF-8

    When making a request, the access token must be specified in the Authorization header. In the Python code, we use the access token read from tokens.json file.

    Authorization: Bearer Rc7JE8P7XUgSCPogLOx2VLMfITqQQrjg




    Put Together


    Once we get both the air quality sensor and ecobee API work, we can put them together. When the indoor air quality index goes above 100, we turn on the fan to bring fresh air into the house. When the air quality decreases below 50, we can stop the fan to save energy. Here's the Python code snippet to do that.


    def ecobeeControl(iaq: float, iaq_accuracy: int):
        while (updateAccessToken(myTokens) == False):
        if not ecobeeControl.systemReady :
            if iaq_accuracy > 0 :
                ecobeeControl.systemReady = True
                print("System is ready to use")
            if iaq > 100.0 and iaq_accuracy > 0 and not ecobeeControl.fanIsOn :
                while (controlThermostat(myTokens, "setHoldFanOn.json") == False):
                ecobeeControl.fanIsOn = True
                print(f'IAQ {iaq:.0f}, IAQ Accuracy {iaq_accuracy:d}, fan is turned on')
            if iaq < 50.0 and iaq_accuracy > 0 and ecobeeControl.fanIsOn :
                while (controlThermostat(myTokens, "resumeProgram.json") == False):
                ecobeeControl.fanIsOn = False
                print(f'IAQ {iaq:.0f}, IAQ Accuracy {iaq_accuracy:d}, fan is turned off')
    myTokens = readJsonDataFromFile(tokenFileName)
    print("System is in 5 minute burn-in time")
    # System is ready after 5 minute burn-in time and IAQ accurary is greater than 0
    ecobeeControl.systemReady = False
    ecobeeControl.fanIsOn = False


    When I put my mark pen close to BME680 sensor, I heard the fan being turned on and ecobee web interface showed the fan status On as shown below. After I moved the mark pen away from the sensor chip, in a minute or so, I didn't hear the fan noise anymore. My supplemental air quality control works perfect.

    The complete Python code is in file. If you're interested in building the same system, here's my source code Feel free to use and/or modify it.





    I am very happy this road test turns out to be a successful smart-home project for me. It adds indoor air quality control functionality to my existing ecobee thermostat system. I really enjoyed Raspberry Pi's versatile capability and easy to use. The high sensitivity and accuracy of the environment click board (i.e., Bosch BME680 sensor chip) is very impressive even though it isn't the easiest chip to use and it doesn't have tons of helpful information online.


Also Enrolling

Enrollment Closes: Oct 29 
Enrollment Closes: Oct 26 
Enrollment Closes: Oct 5 
Enrollment Closes: Oct 9 
Enrollment Closes: Oct 5