Indoor Air Quality (IAQ) Measurement with Bosch BME680 and STM32F103C8T6

In my personal opinion, the key feature of the Bosch BME680 is its ability to output an Indoor Air Quality (IAQ) using its built-in sensors for gas, pressure, humidity and temperature. Bosch Sensortec provides a software solution (BSEC: Bosch Software Environmental Cluster) that utilizes the 4-in-1 integrated sensors inside the BME680: This software solution provides an Indoor Air Quality (IAQ) index ranging from 0 to 500 which quantifies the quality of the air available in the surrounding.

STM32F103 and BME680

IAQ Index Classification

  • 0 .. 50 Good
  • 51 .. 100 Average
  • 101 .. 150 Little bad
  • 151 .. 200 Bad
  • 201 .. 300 Worse
  • 301 .. 500 Very bad

Goal

Get the BSEC software up and running using the Arduino IDE, an inexpensive microcontroller board STM32F103C8T6 aka “Blue Pill” and a Bosch BME680 on a breakout board like the one offered by Watterott. The BSEC software is provided as pre-compiled library for various microprocessor architectures, and it is a little bit tricky to get it up and running in this particular combination.

Prerequisites

I don’t want to go into details about working with STM32 microcontroller boards in the Arduino IDE. A good entry point is here: https://github.com/rogerclarkmelbourne/Arduino_STM32
  • You already can flash and run applications on STM32 board.
  • You already have the demo BME680 I2C example up and running that can be found at https://github.com/BoschSensortec/BME680_driver. The application prints temperature, pressure, humidity and gas resistance.
  • Admittedly, setting up the code for I2C communication using the Arduino “Wire” library can be challenging. If you need inspiration, you can look further down this blog post at “How to implement I2C Communication”.
  • You already have downloaded the BSEC software at https://www.bosch-sensortec.com/bst/products/all_products/bsec. The version refered to in this blog post is 1.3.4.1

Get BSEC example up and running

In the /doc folder of the BSEC software archive you find step-by-step instructions (Section 2) for an example. However, there are some pitfalls …

Pitfall #1: Where can I find all those files?

In Arduino IDE, create a new sketch named bsec_iot_example.

Into this directory, you need to copy a bunch of files from the BSEC software archive:

  • from /algo:
    bsec_datatypes.h
    bsec_interface.h
  • from /API:
    bme680.c
    bme680.h
    bme680_calculations.c
    bme680_calculations.h
    bme680_internal.h
    sensor_api_common_types.h
  • from /example
    bsec_integration.c
    bsec_integration.h
    bsec_iot_example.c – Don’t copy this
    bsec_iot_example.ino

Finally, after restarting the Arduino IDE , there should be 11 files – no more and no less.

Pitfall #2: How to implement I2C Communication?

Fortunately, the example provided in bsec_iot_example.ino provides an implementation for I2C communication in functions bus_read and bus_write. Also the setup() function does the required initializations.

But now the bad part: The Wire (I2C) library for the STM32 seems not to adhere to Arduino Wire Specification: The method Wire.endTransmission() returns the number of bytes read or written, instead to just return 0 on success. This is an issue for the example application, as it relies on the return codes of bus_read and bus_write:

From bus_write (bsec_iot_example.ino):

return (int8_t) Wire.endTransmission();

replace with

Wire.endTransmission();
return 0;

From bus_read (bsec_iot_example.ino):

comResult = Wire.endTransmission();
...
return comResult;

replace with

return 0;

Pitfall #3: What is the right pre-compiled BSEC library for STM32F103 boards?

The STM32F103C8T6 board is based on an ARM Cortex-M3 core. You can find the appropriate pre-compiled BSEC library in the following directory of the BSEC software archive:

/algo/bin/ARM_CORTEX/GCC/IAQ_OUT/Cortex_M3/libalgobsec.a

Pitfall #4: Where to put the pre-compiled BSEC library?

You won’t be able to build the example application unless you copy the pre-compiled BSEC library to a place where it can be found by the linker and instruct the linker to include it.

The instructions following are for version 1.8.5 of Arduino on a Windows PC.

You need to find out which directories and files the linker uses. Enable verbose output in the Arduino IDE to find out more.

  • Open the “Preferences” under File –> Preferences.
  • Check the option “Show verbose output during [x] compilation”

arduino_preferences

Now try to build the example application in the Arduino IDE. Of course it won’t work at this point in time, as some methods from the pre-compiled BSEC library cannot be found.

But there should be some output available now from the building process in the bottom half of the IDE screen. Look out for “Linking everything together…”.

Linking everything together…
“C:\Users\wolfgang\AppData\Local\Arduino15\packages\arduino\tools\arm-none-eabi-gcc\4.8.3-2014q1/bin/arm-none-eabi-g++” -Os -Wl,–gc-sections -mcpu=cortex-m3 “-TC:\Program Files\Arduino\hardware\Arduino_STM32-master\STM32F1\variants\generic_stm32f103c/ld/jtag_c8.ld” “-Wl,-Map,C:\Users\wolfgang\AppData\Local\Temp\arduino_build_20308/bme680_iaq.ino.map” “-LC:\Program Files\Arduino\hardware\Arduino_STM32-master\STM32F1\variants\generic_stm32f103c/ld” -o “C:\Users\wolfgang\AppData\Local\Temp\arduino_build_20308/bme680_iaq.ino.elf” “-LC:\Users\wolfgang\AppData\Local\Temp\arduino_build_20308” -lm -lgcc -mthumb -Wl,–cref -Wl,–check-sections -Wl,–gc-sections -Wl,–unresolved-symbols=report-all -Wl,–warn-common -Wl,–warn-section-align -Wl,–warn-unresolved-symbols -Wl,–start-group “C:\Users\wolfgang\AppData\Local\Temp\arduino_build_20308\sketch\bme680.c.o” “C:\Users\wolfgang\AppData\Local\Temp\arduino_build_20308\sketch\bme680_calculations.c.o” “C:\Users\wolfgang\AppData\Local\Temp\arduino_build_20308\sketch\bsec_integration.c.o” …

This gives us two insights and follow-up tasks:

  • Look out for the value of option “-L”. This indicates the directories where the linker will look for libraries. In my case, it is C:\Program Files\Arduino\hardware\Arduino_STM32-master\STM32F1\variants\generic_stm32f103c\ld.
    Copy the pre-compiled BSEC library libalgobsec.a to this directory.
  • Look out for the value of option “-T”. This indicates where the linker script is located. In my case, the linker script is located at C:\Program Files\Arduino\hardware\Arduino_STM32-master\STM32F1\variants\generic_stm32f103c/ld/jtag_c8.ld.
  • In my case, the linker script included another script, common.inc./* Let common.inc handle the real work. */
    INCLUDE common.inc

    Open this common.inc in an editor and search for

    GROUP(libgcc.a libc.a libm.a)

    Put libalgobsec.a in front of libm.a, resulting in the line

    GROUP(libgcc.a libc.a libalgobsec.a libm.a)

     

  • Find the platform.txt file for the STM32F103 board. Already having found the paths for the platform specific linker script, in my case it is located in directory C:\Program Files\Arduino\hardware\Arduino_STM32-master\STM32F1.Open platform.txt in an editor and search for recipe.c.combine.pattern. This is a kind of “template” how to create the command and options for the linker. In my case, it looks as follows:## Combine gc-sections, archives, and objectsrecipe.c.combine.pattern=”{compiler.path}{compiler.c.elf.cmd}” {compiler.c.elf.flags} -mcpu={build.mcu} “-T{build.variant.path}/{build.ldscript}” “-Wl,-Map,{build.path}/{build.project_name}.map” {compiler.c.elf.extra_flags} -o “{build.path}/{build.project_name}.elf” “-L{build.path}” -lm -lgcc -mthumb -Wl,–cref -Wl,–check-sections -Wl,–gc-sections -Wl,–unresolved-symbols=report-all -Wl,–warn-common -Wl,–warn-section-align -Wl,–warn-unresolved-symbols -Wl,–start-group {object_files} “{build.path}/{archive_file}” -Wl,–end-group
    Put “-lalgobsec” in front of “-lm” to instruct the linker to also include the pre-compiled BSEC software library.

    ## Combine gc-sections, archives, and objects
    recipe.c.combine.pattern=”{compiler.path}{compiler.c.elf.cmd}” {compiler.c.elf.flags} -mcpu={build.mcu} “-T{build.variant.path}/{build.ldscript}” “-Wl,-Map,{build.path}/{build.project_name}.map” {compiler.c.elf.extra_flags} -o “{build.path}/{build.project_name}.elf” “-L{build.path}” -lalgobsec -lm -lgcc -mthumb -Wl,–cref -Wl,–check-sections -Wl,–gc-sections -Wl,–unresolved-symbols=report-all -Wl,–warn-common -Wl,–warn-section-align -Wl,–warn-unresolved-symbols -Wl,–start-group {object_files} “{build.path}/{archive_file}” -Wl,–end-group

Having done these modifications, this BSEC software library should now be linked into the example application. It may be necessary to restart the IDE to make it work. You may also want to check the linker output when you compile, to make sure that it really contains the -lalgobsec flag.

Note: Please be aware that this BSEC software library is now linked into each and every sketch you do on this STM32 platform. Of course you don’t want that, so don’t forget to clean up this Arduino setup if you do projects that don’t require this library.

Pitfall #5: What is this strange I2C code in bme680.c ?

In file bme680.c, search for string __KERNEL__.

In two places you find code like this:

#ifndef __KERNEL__
 bme680_buffer_restruct_burst_write(data_u8,
 0x70,
 BME680_SENS_CONF_LEN,
 (BME680_SENS_CONF_LEN * 2)-1);

com_status = (enum bme680_return_type)
 bme680->bme680_bus_write(bme680->dev_addr,
 BME680_ADDR_SENSOR_CONFIG,
 data_u8,
 (BME680_SENS_CONF_LEN * 2)-1);

#else

com_status = (enum bme680_return_type)
 bme680->bme680_bus_write(bme680->dev_addr,
 BME680_ADDR_SENSOR_CONFIG,
 data_u8,
 BME680_SENS_CONF_LEN);
#endif

Actually __KERNEL__ is not defined, so the first branch of the #ifndef statement is executed. But it is totally unclear to me why there should be any need to restructure the message before sending over I2C, and actually only the code in the #else section works.

So the recommendation is to remove this #ifndef – #else – #endif construct, and just keep the code in the #else section.

So in this one of two places in bme680.c, just stick with this:

com_status = (enum bme680_return_type)
 bme680->bme680_bus_write(bme680->dev_addr,
 BME680_ADDR_SENSOR_CONFIG,
 data_u8,
 BME680_SENS_CONF_LEN);

Again, don’t forget to remove this #ifndef – #else – #endif construct in the second place in file bme680.c, too.

 

Pitfall #6: The example application build, but why is there no IAQ output?

You managed to build the example application and flash it to the STM32 board, you see output lines in the serial monitor, but the values for IAQ stay 0.00 (0).

No need to panic, just wait 5 – 10 minutes, then there should be a value for IAQ.

...
[294910.00] T: 23.81| rH: 42.20| IAQ: 0.00 (0)
[297909.00] T: 23.81| rH: 42.21| IAQ: 0.00 (0)
[300908.00] T: 23.81| rH: 42.19| IAQ: 0.00 (0)
[303907.00] T: 23.81| rH: 42.18| IAQ: 25.00 (3)
[306906.00] T: 23.81| rH: 42.16| IAQ: 23.11 (3)
[309905.00] T: 23.81| rH: 42.18| IAQ: 25.12 (3)
[312904.00] T: 23.81| rH: 42.20| IAQ: 22.67 (3)
[315903.00] T: 23.81| rH: 42.22| IAQ: 34.15 (3)
[318902.00] T: 23.81| rH: 42.46| IAQ: 38.45 (3)
[321901.00] T: 23.81| rH: 42.73| IAQ: 25.17 (3)
[324900.00] T: 23.82| rH: 42.81| IAQ: 29.15 (3)
[327899.00] T: 23.82| rH: 43.01| IAQ: 38.77 (3)
[330898.00] T: 23.83| rH: 43.36| IAQ: 28.27 (3)
[333897.00] T: 23.83| rH: 43.23| IAQ: 28.39 (3)

In my case, first IAQ with value 25.00 after 304 seconds.

Pitfall #7: Why are there no more temperature and humidity measurements after 51 days?

There is a possible flaw in the function that returns the current time in microseconds.

From get_timestamp_us (bsec_iot_example.ino):

int64_t get_timestamp_us()
{
   return (int64_t) millis() * 1000;
}

From the Arduino specification: millis() returns the number of milliseconds since the Arduino board began running the current program. This number will overflow (go back to zero), after approximately 50 days.

For an alternative implementation, I chose to use function call micros() instead of millis(), as any overflow effects will already show up after 71 minutes and not after 51 days.

Alternative implementation of get_timestamp_us (bsec_iot_example.ino):

int64_t get_timestamp_us()
{
   static int64_t microseconds_since_start = 0;
 
   // micros() returns time (in microseconds) since the beginning of program execution. 
   // On overflow (after approximately 71 minutes), restarts at 0.
   uint32_t microseconds = micros();
   uint32_t microseconds_since_start_as_uint32 = microseconds_since_start & 0xFFFFFFFF;

   int64_t diff_since_last_call = microseconds - microseconds_since_start_as_uint32;
   microseconds_since_start += diff_since_last_call;
 
   return microseconds_since_start;
}
Advertisements

9 thoughts on “Indoor Air Quality (IAQ) Measurement with Bosch BME680 and STM32F103C8T6

  1. Hallo Wolfgang,
    super Anleitung, nur haben die Kollegen wesentliche Teile aus dem neusten/ verfügbaren Release rausgenommen, oder vergessen:
    BSEC_1.4.5.1_Generic_Release_20171116.zip
    Folgende Files fehlen in dem Release :
    bme680_calculations.c
    bme680_calculations.h
    bme680_internal.h
    sensor_api_common_types.h
    bsec_integration.c
    bsec_integration.h

    Warum bist du eigentlich an dem Sensor dran? – http://luftdaten.info/ ?
    Wäre doch toll, wenn da der BME680 schnellstens auch die Unterstützung finden würde.

    Gibt es Unterlagen, wie man auch die 5 Gase separat ermitteln kann? Oder gar mehr, siehe die eNose unter http://www.maskau.dk/projects/electronic-nose

    Gruß aus Ludwigsburg
    Carsten

    • I was able to make it up and running using BSEC 1.4.5.1.
      The are just two differences comparing to this great tutorial:
      1) no need to use below files from /API:
      bme680_calculations.c
      bme680_calculations.h
      bme680_internal.h
      sensor_api_common_types.h
      Just copy new file API/bme680_defs.h to your project folder instead of above.

      2) The file libalgobsec.a is now placed in folder: BSEC_1.4.5.1_Generic_Release_20171214/algo/bin/gcc/Cortex_M3.

      I didn’t success fully, because I have bumped into “Error while initializing BME680” – anyway it proves that project was compiled and started. Probably did I some mistake or my CJMCU-680 dos not work with BSEC software. I have no clue, because it is my very first STM32F103C8T6 project. I will try to make it up and running using Arduino, because previously I was able to read some basic output with a help of ‘BME680_Library’.

  2. Hello, I try to connect BME680 to arduino mega 2560 board, but I have some problems. I follow your article, but, it’s different that your case. My current problem, that I cannot set
    “GROUP(libgcc.a libc.a libalgobsec.a libm.a)”
    – because I haven’t such file like in your case. There are another things that a different, but I found what I have to change. At this moment, arduino compiler, didn’t know the -libalgobsec flag, and I think it is about settings that I mentioned above. For sure it is in another place, but I have no idea where to find it. I’m also search all folders of Arduino, but still nothing. Maybe it is in another way. On arduino.cc also didn’t find answer. Could you help me, please

  3. Hi again. So I succeeded to make link for my Arduino MEGA. Now, I have another question. I didn’t find in BME680 documentation, the variables, where you can set the number of pins to be connected to arduino, how do you set it?

  4. Compiles for me with this environment, AVR atmega2560 avr6 architecture, Arduino-Makefile and BSEC algo/bin/Normal_version/avr/AVR8_megaAVR/ (the library is for avr6 architecture)
    ——8<——–Makefile——
    ARDUINO_DIR = /opt/arduino-1.8.5
    USER_LIB_PATH = /arduino/libraries
    ARDUINO_LIBS = Wire
    BOARD_TAG = mega
    BOARD_SUB = atmega2560
    MONITOR_PORT = /dev/ttyUSB0
    OTHER_LIBS = libalgobsec.a

    include /opt/Arduino-Makefile/Arduino.mk
    ——-8<——
    The list of files
    ~/arduino/bsec# ls
    bme680.c bsec_integration.c build-mega-atmega2560
    bme680_defs.h bsec_integration.h libalgobsec.a
    bme680.h bsec_interface.h libalgobsec.a.Size.log
    bsec_datatypes.h bsec_iot_example.ino Makefile

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s