Skip to main content
  1. Posts/

ESP32-H2 with BME680 Sensor Part 2

··2171 words·11 mins·

In the ESP32-H2 with BME680 Sensor Part 1 post, we connected the ESP32-H2 Development Kit with the BME680 sensor and started exploring it using i2c-tools from the ESP-IDF examples. In this post, we will perform more useful tasks, such as reading temperature, humidity, pressure, and air quality. To accomplish this, we will utilize the excellent ESPHome framework, which already supports the BME680, saving us from reinventing the wheel.

Before we proceed, please ensure that you have the ESP32 development environment set up in Docker. You can find detailed instructions in the post titled ESP32 development in Docker.

It’s worth noting that the physical board we are using, ESP32-H2-DevKitM-1, is not currently supported by ESPHome. However, for now, we can use the closest available option, the esp32-c3-devkitm-1 board with the esp32h2 variant, which should work adequately.

ESPHome configuration esp32-h2-bme680.yaml:

# esp32-h2-bme680.yaml
esphome:
  name: prototype-02
esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: esp-idf
  variant: esp32h2

logger:

i2c:
  sda: 8
  scl: 9

sensor:
  - platform: bme680
    temperature:
      name: "BME680 Temperature"
    pressure:
      name: "BME680 Pressure"
    humidity:
      name: "BME680 Humidity"
    gas_resistance:
      name: "BME680 Gas Resistance"
    address: 0x77
    update_interval: 60s

For our setup, we will be using GPIO8 for the I2C SDA (Serial Data Line) and GPIO9 for the I2C SCL (Serial Clock Line). We will stick with the default configuration for the BME680 component.

To begin, let’s install the latest version of ESPHome in our Docker container:

$ git clone https://github.com/esphome/esphome.git /opt/esp/esphome
$ /opt/esp/esphome/script/setup
...
Virtual environment created. Run 'source venv/bin/activate' to use it.

ESPHome should be installed and ready to activate:

$ source /opt/esp/esphome/venv/bin/activate`

Once activated, let’s validate our configuration before proceeding:

(venv) $ esphome config esp32-h2-bme680.yaml
...
INFO Configuration is valid!

If the configuration is valid, we can now generate and compile the project:

(venv) $ esphome compile esp32-h2-bme680.yaml
...
INFO Successfully compiled program.

During the project compilation, ESPHome performs several tasks. It prepares the project structure by copying the necessary component source code based on the configuration. It also utilizes PlatformIO to build the project.

Now that we have successfully compiled the project, let’s proceed to upload the program, run it, and monitor the logs:

(venv) $ esphome run --device /dev/ttyUSB0 esp32-h2-bme680.yaml
NFO ESPHome 2024.1.0-dev
INFO Reading configuration esp32-h2-bme680.yaml...
WARNING GPIO8 is a Strapping PIN and should be avoided.
Attaching external pullup/down resistors to strapping pins can cause unexpected failures.
See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins
WARNING GPIO9 is a Strapping PIN and should be avoided.
Attaching external pullup/down resistors to strapping pins can cause unexpected failures.
See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins
INFO Generating C++ source...
INFO Compiling app...
Processing prototype-02 (board: esp32-c3-devkitm-1; framework: espidf; platform: platformio/espressif32@5.4.0)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
HARDWARE: ESP32C3 160MHz, 320KB RAM, 4MB Flash
 - framework-espidf @ 3.40406.240122 (4.4.6)
 - tool-cmake @ 3.16.4
 - tool-ninja @ 1.7.1
 - toolchain-esp32ulp @ 2.35.0-20220830
 - toolchain-riscv32-esp @ 8.4.0+2021r2-patch5
Reading CMake configuration...
No dependencies
RAM:   [          ]   2.2% (used 7356 bytes from 327680 bytes)
Flash: [=         ]  12.8% (used 235510 bytes from 1835008 bytes)
================================================================================================================================================= [SUCCESS] Took 5.10 seconds =================================================================================================================================================
INFO Successfully compiled program.
esptool.py v4.7.0
Serial port /dev/ttyUSB0
Connecting....
Chip is ESP32-H2 (revision v0.1)
Features: BLE, IEEE802.15.4
Crystal is 32MHz
MAC: 48:31:b7:ff:fe:c0:72:53
BASE MAC: 48:31:b7:c0:72:53
MAC_EXT: ff:fe
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Unexpected chip id in image. Expected 16 but value was 5. Is this image for a different chip model?
ERROR Running command failed: /home/pawel/Repo/delgany-lab/esphome-esp32-h2-bme680/esp32-h2-test/.esphome/build/prototype-02/.pioenvs/prototype-02/firmware.bin is not an ESP32-H2 image. Use --force to flash anyway.
ERROR Please try running esptool.py --before default_reset --after hard_reset --baud 460800 --port /dev/ttyUSB0 --chip esp32h2 write_flash -z --flash_size detect 0x10000 /home/pawel/Repo/delgany-lab/esphome-esp32-h2-bme680/esp32-h2-test/.esphome/build/prototype-02/.pioenvs/prototype-02/firmware.bin 0x0 /home/pawel/Repo/delgany-lab/esphome-esp32-h2-bme680/esp32-h2-test/.esphome/build/prototype-02/.pioenvs/prototype-02/bootloader.bin 0x8000 /home/pawel/Repo/delgany-lab/esphome-esp32-h2-bme680/esp32-h2-test/.esphome/build/prototype-02/.pioenvs/prototype-02/partitions.bin 0x9000 /home/pawel/Repo/delgany-lab/esphome-esp32-h2-bme680/esp32-h2-test/.esphome/build/prototype-02/.pioenvs/prototype-02/ota_data_initial.bin locally.
INFO Upload with baud rate 460800 failed. Trying again with baud rate 115200.
esptool.py v4.7.0
Serial port /dev/ttyUSB0
Connecting....
Chip is ESP32-H2 (revision v0.1)
Features: BLE, IEEE802.15.4
Crystal is 32MHz
MAC: 48:31:b7:ff:fe:c0:72:53
BASE MAC: 48:31:b7:c0:72:53
MAC_EXT: ff:fe
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Unexpected chip id in image. Expected 16 but value was 5. Is this image for a different chip model?
ERROR Running command failed: /home/pawel/Repo/delgany-lab/esphome-esp32-h2-bme680/esp32-h2-test/.esphome/build/prototype-02/.pioenvs/prototype-02/firmware.bin is not an ESP32-H2 image. Use --force to flash anyway.
ERROR Please try running esptool.py --before default_reset --after hard_reset --baud 115200 --port /dev/ttyUSB0 --chip esp32h2 write_flash -z --flash_size detect 0x10000 /home/pawel/Repo/delgany-lab/esphome-esp32-h2-bme680/esp32-h2-test/.esphome/build/prototype-02/.pioenvs/prototype-02/firmware.bin 0x0 /home/pawel/Repo/delgany-lab/esphome-esp32-h2-bme680/esp32-h2-test/.esphome/build/prototype-02/.pioenvs/prototype-02/bootloader.bin 0x8000 /home/pawel/Repo/delgany-lab/esphome-esp32-h2-bme680/esp32-h2-test/.esphome/build/prototype-02/.pioenvs/prototype-02/partitions.bin 0x9000 /home/pawel/Repo/delgany-lab/esphome-esp32-h2-bme680/esp32-h2-test/.esphome/build/prototype-02/.pioenvs/prototype-02/ota_data_initial.bin locally.

Unfortunately, the upload failed due to an incorrect chip ID being set. The expected chip ID was 16, but the value found was 5, indicating that the image is intended for a different chip model.

To confirm this, let’s search within the .esphome directory, where all the generated project files are located, for the chip ID setting:

(venv) $ grep -R CONFIG_IDF_FIRMWARE_CHIP_ID .
.esphome/build/prototype-02/sdkconfig.prototype-02:CONFIG_IDF_FIRMWARE_CHIP_ID=0x0005
...

As we can see, the chip ID is indeed set to 5, which is incorrect for our current board. Since PlatformIO is causing issues, we can bypass it and build our ESPHome project directly using ESP-IDF, as we have our version of ESP-IDF in the container. We won’t need to use ESPHome again, so we can deactivate the virtual environment:

(venv) $ deactivate
$ 

Next, let’s export the necessary ESP-IDF variables:

$ source $IDF_PATH/export.sh
Detecting the Python interpreter
Checking "python3" ...
Python 3.11.6
"python3" has been detected
Checking Python compatibility
Checking other ESP-IDF version.
Adding ESP-IDF tools to PATH...
Checking if Python packages are up to date...
Requirement files:
 - /opt/esp/idf/tools/requirements/requirements.core.txt
Python being checked: /opt/esp/python_env/idf5.3_py3.11_env/bin/python
Python requirements are satisfied.
Added the following directories to PATH:
  /opt/esp/idf/components/espcoredump
  /opt/esp/idf/components/partition_table
  /opt/esp/idf/components/app_update
  /opt/esp/tools/xtensa-esp-elf-gdb/12.1_20231023/xtensa-esp-elf-gdb/bin
  /opt/esp/tools/riscv32-esp-elf-gdb/12.1_20231023/riscv32-esp-elf-gdb/bin
  /opt/esp/tools/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin
  /opt/esp/tools/riscv32-esp-elf/esp-13.2.0_20230928/riscv32-esp-elf/bin
  /opt/esp/tools/esp32ulp-elf/2.35_20220830/esp32ulp-elf/bin
  /opt/esp/tools/openocd-esp32/v0.12.0-esp32-20230921/openocd-esp32/bin
  /opt/esp/tools/xtensa-esp-elf-gdb/12.1_20231023/xtensa-esp-elf-gdb/bin
  /opt/esp/tools/riscv32-esp-elf-gdb/12.1_20231023/riscv32-esp-elf-gdb/bin
  /opt/esp/tools/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin
  /opt/esp/tools/riscv32-esp-elf/esp-13.2.0_20230928/riscv32-esp-elf/bin
  /opt/esp/tools/esp32ulp-elf/2.35_20220830/esp32ulp-elf/bin
  /opt/esp/tools/openocd-esp32/v0.12.0-esp32-20230921/openocd-esp32/bin
Done! You can now compile ESP-IDF projects.
Go to the project directory and run:

  idf.py build

$ idf.py --version
ESP-IDF v5.3-dev-1353-gb3f7e2c8a4-dirty

To proceed, let’s navigate to the directory where the generated project source code is located:

$ cd .esphome/build/prototype-02/

To add the necessary build flags, similar to those found in platformio.ini, let’s modify the CMakeLists.txt file as follows:

add_compile_definitions(
    ESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_DEBUG
    USE_ESP_IDF
    USE_ESP32
    USE_ESP32_VARIANT_ESP32H2
)

Next, remove all PlatformIO-related files:

$ rm -rf README.txt partitions.csv platformio.ini post_build.py sdkconfig.prototype-02 sdkconfig.prototype-02.esphomeinternal version.txt .pioenvs

To generate a new sdkconfig file targeting ESP32-H2, use the following command:

$ idf.py set-target esp32h2
...
-- Configuring done (2.3s)
-- Generating done (0.2s)
-- Build files have been written to: build

Now, let’s try to build the project:

$ idf.py build
...
/opt/esp/tools/riscv32-esp-elf/esp-13.2.0_20230928/riscv32-esp-elf/bin/../lib/gcc/riscv32-esp-elf/13.2.0/../../../../riscv32-esp-elf/bin/ld: esp-idf/freertos/libfreertos.a(app_startup.c.obj): in function `main_task':
/opt/esp/idf/components/freertos/app_startup.c:206:(.text.main_task+0x76): undefined reference to `app_main'
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
...

The linker encountered an error and couldn’t find app_main. This missing function is located in core.cpp:

$ rg app_main -t cpp
src/esphome/components/esp32/core.cpp
73:extern "C" void app_main() {

It seems that the build system did not include core.cpp:

$ rg core.cpp
$

According to the ESP32 Build System documentation, the default component name is main, but in our case, ESPHome placed all the source files inside a src directory instead:

$ ls
CMakeLists.txt  build  sdkconfig  src

We have two options to resolve this:

  1. Rename the src directory to main and update the paths in the CMake configuration files.
  2. Add the src directory as an extra component according to the ESP32 Build System.

Let’s attempt option 1 and rename src to main, updating the src/CMakeLists.txt file accordingly:

$ mv src main
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/main/*.*)

After making this adjustment, let’s attempt to compile again:

$ idf.py build
...
ninja: build stopped: subcommand failed.
Missing "esphome/core/component.h" file name found in the following component(s)...

Now, the compilation is failing due to missing include directories. We can fix this by adding the INCLUDE_DIRS argument to the idf_component_register function in src/CMakeLists.txt:

idf_component_register(SRCS ${app_sources} INCLUDE_DIRS ".")

We commpile again:

$ idf.py build
...
ninja: build stopped: subcommand failed.
ninja failed with exit code 1, output of the command is in...

Unfortunately, compilation fails again. Upon reviewing the logs, we can identify the issue:

.esphome/build/prototype-02/main/esphome/components/logger/logger.cpp:334:27: error: invalid conversion from 'int' to 'uart_port_t' [-fpermissive]
  334 |         this->uart_num_ = -1;
      |                           ^~
      |                           |
      |                           int
...

The variable uart_num_ of type uart_port_t (/opt/esp/idf/components/hal/include/hal/uart_types.h) is being assigned the value -1. uart_num_ is of type uart_port_t and this is an enum (/opt/esp/idf/components/hal/include/hal/uart_types.h):

typedef enum {
    UART_NUM_0,                         /*!< UART port 0 */
    UART_NUM_1,                         /*!< UART port 1 */
...
} uart_port_t;

To resolve the issue mentioned by the compiler, let’s comment out the line that assigns -1 to uart_port_t in the main/esphome/components/logger/logger.cpp file in our project:

diff --git a/main/esphome/components/logger/logger.cpp b/main/esphome/components/logger/logger.cpp
index c8a3ba9..7bf9421 100644
--- a/main/esphome/components/logger/logger.cpp
+++ b/main/esphome/components/logger/logger.cpp
@@ -331,7 +331,7 @@ void Logger::pre_setup() {
 #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
     defined(USE_ESP32_VARIANT_ESP32H2)
       case UART_SELECTION_USB_SERIAL_JTAG:
-        this->uart_num_ = -1;
+        // this->uart_num_ = -1;
         this->init_usb_serial_jtag_();
         break;
 #endif  // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 ||

We can now proceed to run the compiled code. Here’s the command to execute:

$ idf.py build
...
Successfully created esp32h2 image.
...

With the successful compilation, we are ready to proceed with running the code.

$ idf.py -p /dev/ttyUSB0 flash monitor
...
I (311) sleep: Configure to isolate all GPIO pins in sleep state
I (318) sleep: Enable automatic switching of GPIO sleep configuration
I (325) main_task: Started on CPU0
I (325) main_task: C��[I][logger:359]: Log initialized
[I][app:029]: Running through setup()...
[C][i2c.idf:017]: Setting up I2C bus...
[I][i2c.idf:233]: Performing I2C bus recovery
[D][esp-idf:000]: I (335) gpio: GPIO[9]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0

[D][esp-idf:000]: I (345) gpio: GPIO[8]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0

E (5335) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (5335) task_wdt:  - loopTask (CPU 0)
E (5335) task_wdt: Tasks currently running:
E (5335) task_wdt: CPU 0: loopTask
E (5335) task_wdt: Print CPU 0 (current core) backtrace
Print CPU 0 (current core) registers
Stack dump detected
Core  0 register dump:
MEPC    : 0x42019c36  RA      : 0x42019fa0  SP      : 0x408140d0  GP      : 0x4080c5b0
0x42019c36: i2c_ll_master_clr_bus at /opt/esp/idf/components/hal/esp32h2/include/hal/i2c_ll.h:749 (discriminator 1)
 (inlined by) i2c_master_clear_bus at /opt/esp/idf/components/driver/i2c/i2c.c:656 (discriminator 1)

0x42019fa0: i2c_hw_fsm_reset at /opt/esp/idf/components/driver/i2c/i2c.c:679

TP      : 0x40808034  T0      : 0x400184be  T1      : 0x00002000  T2      : 0x3a6e4574
0x40808034: vTaskSuspendAll at /opt/esp/idf/components/freertos/FreeRTOS-Kernel/tasks.c:2476

0x400184be: memset in ROM

S0/FP   : 0x4080c1ac  S1      : 0x00000000  A0      : 0x00000000  A1      : 0x408140d8
A2      : 0x00000001  A3      : 0x00000800  A4      : 0x60004000  A5      : 0x00000001
A6      : 0x00000004  A7      : 0x00000001  S2      : 0x00000007  S3      : 0x00000003
S4      : 0x40810418  S5      : 0x00000001  S6      : 0x00000000  S7      : 0x00000000
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000
T3      : 0x00000001  T4      : 0x4081044c  T5      : 0x69617244  T6      : 0x6e65704f
MSTATUS : 0x00001000  MTVEC   : 0x00000014  MCAUSE  : 0x000000f0  MTVAL   : 0x0000013f
MHARTID : 0x00000050
...

Our application is failing to reset the watchdog timer in a timely manner. This issue appears to be similar to the one mentioned in ESP32-H2 with BME680 Sensor Part 1, where we encountered difficulties when attempting to execute the i2cdetect command that scans the I2C bus. In the ESPHome YAML configuration for the I2C component, the scan parameter is set to true by default. To address this problem, we can try disabling the scan feature without regenerating the entire project. Instead, we can make the necessary modifications in the generated main/main.cpp file.

diff --git a/main/main.cpp b/main/main.cpp
index 324655d..726e199 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -60,7 +60,7 @@ void setup() {
   i2c_idfi2cbus->set_scl_pin(2);
   i2c_idfi2cbus->set_scl_pullup_enabled(true);
   i2c_idfi2cbus->set_frequency(50000);
-  i2c_idfi2cbus->set_scan(true);
+  i2c_idfi2cbus->set_scan(false);
   // esp32:
   //   board: esp32-c3-devkitm-1
   //   framework:

Now, let’s verify if disabling the I2C scan in the main/main.cpp file resolves the watchdog issue.

$ idf.py -p /dev/ttyUSB0 flash monitor
...
[C][i2c.idf:017]: Setting up I2C bus...
[I][i2c.idf:233]: Performing I2C bus recovery
[D][esp-idf:000]: I (335) gpio: GPIO[2]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0

[D][esp-idf:000]: I (345) gpio: GPIO[1]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0

[C][bme680.sensor:074]: Setting up BME680...
[I][app:062]: setup() finished successfully!
[I][app:102]: ESPHome version 2024.1.0-dev compiled on Feb 12 2024, 21:15:33
[C][logger:447]: Logger:
[C][logger:448]:   Level: DEBUG
[C][logger:449]:   Log Baud Rate: 115200
[C][logger:451]:   Hardware UART: UART0
[C][i2c.idf:061]: I2C Bus:
[C][i2c.idf:062]:   SDA Pin: GPIO1
[C][i2c.idf:063]:   SCL Pin: GPIO2
[C][i2c.idf:064]:   Frequency: 50000 Hz
[C][i2c.idf:067]:   Recovery: bus successfully recovered
[C][bme680.sensor:215]: BME680:
[C][bme680.sensor:216]:   Address: 0x77
[C][bme680.sensor:220]:   IIR Filter: OFF
[C][bme680.sensor:221]:   Update Interval: 60.0s
[C][bme680.sensor:223]:   Temperature 'BME680 Temperature'
[C][bme680.sensor:223]:     Device Class: 'temperature'
[C][bme680.sensor:223]:     State Class: 'measurement'
[C][bme680.sensor:223]:     Unit of Measurement: '°C'
[C][bme680.sensor:223]:     Accuracy Decimals: 1
[C][bme680.sensor:224]:     Oversampling: 16x
[C][bme680.sensor:225]:   Pressure 'BME680 Pressure'
[C][bme680.sensor:225]:     Device Class: 'pressure'
[C][bme680.sensor:225]:     State Class: 'measurement'
[C][bme680.sensor:225]:     Unit of Measurement: 'hPa'
[C][bme680.sensor:225]:     Accuracy Decimals: 1
[C][bme680.sensor:226]:     Oversampling: 16x
[C][bme680.sensor:227]:   Humidity 'BME680 Humidity'
[C][bme680.sensor:227]:     Device Class: 'humidity'
[C][bme680.sensor:227]:     State Class: 'measurement'
[C][bme680.sensor:227]:     Unit of Measurement: '%'
[C][bme680.sensor:227]:     Accuracy Decimals: 1
[C][bme680.sensor:228]:     Oversampling: 16x
[C][bme680.sensor:229]:   Gas Resistance 'BME680 Gas Resistance'
[C][bme680.sensor:229]:     State Class: 'measurement'
[C][bme680.sensor:229]:     Unit of Measurement: 'Ω'
[C][bme680.sensor:229]:     Accuracy Decimals: 1
[C][bme680.sensor:229]:     Icon: 'mdi:gas-cylinder'
[C][bme680.sensor:233]:   Heater temperature=320°C duration=150ms
[D][bme680.sensor:332]: Got temperature=20.9°C pressure=999.1hPa humidity=42.9% gas_resistance=4959.3Ω
[D][sensor:093]: 'BME680 Temperature': Sending state 20.89842 °C with 1 decimals of accuracy
[D][sensor:093]: 'BME680 Pressure': Sending state 999.14557 hPa with 1 decimals of accuracy
[D][sensor:093]: 'BME680 Humidity': Sending state 42.85728 % with 1 decimals of accuracy
[D][sensor:093]: 'BME680 Gas Resistance': Sending state 4959.30908 Ω with 1 decimals of accuracy
...

We have achieved successful integration and activation of the BME680 sensor on the ESP32-H2-DevKitM-1 board by utilizing ESPHome code and directly compiling it with ESP-IDF.