В данной статье мы научимся взаимодействовать с BME680 и ESP32 с помощью расширения ESP-IDF. Мы рассмотрим датчик BME680, его схему подключения к плате ESP32, а также настройку проекта в VS Code с расширением ESP-IDF для получения данных о температуре окружающей среды, атмосферном давлении, относительной влажности и содержании летучих органических соединений (ЛОС) или качестве воздуха в помещении (IAQ) с помощью этого датчика.
Также на нашем сайте мы рассматривали подключение к плате ESP32 и других аналогичных датчиков с помощью ESP-IDF:
Датчик BME680
BME680 — это маломощный датчик «четыре в одном», объединяющий датчики температуры, давления, влажности и газа. Он работает от напряжения 1,8–3,8 В и взаимодействует с другими микроконтроллерами по протоколам I2C и SPI. Этот датчик используется в таких областях, как отслеживание качества воздуха, измерение влажности, отслеживание погодных условий, домашняя автоматизация и управление GPS-навигацией.
Схема расположения контактов
Ниже представлена схема расположения контактов датчика BME680:
| Название контакта | Описание контакта |
| VCC | Контакт для питания датчика |
| GND | Общий провод (земля) |
| SCL | Микросхема BME680 предоставляет два интерфейса для получения показаний датчика: SPI и I2C. Этот вывод отвечает за передачу тактового сигнала по последовательному интерфейсу как для I2C, так и для SPI. |
| SDA | При использовании протокола I2C этот вывод работает как SDA (Serial Data pin), а при использовании протокола SPI — как SDI (Serial Data In), также известный как MOSI («Master out Slave in»). |
| SDO | Это вывод SDO (вывод данных ведомого устройства) / MISO (вход ведущего устройства, выход ведомого устройства) для связи по протоколу SPI. Этот вывод используется только для интерфейса SPI. |
| CS | Это контакт выбора микросхемы, используемый в SPI-интерфейсе. Он служит входом для датчика. |
Как видим, распиновка у датчика достаточно продуманная.
Контакты I2C ESP32
Мы будем использовать интерфейс I2C для получения показаний датчика BME680 с помощью ESP32 и ESP-IDF. Теперь давайте обсудим контакты I2C ESP32. На приведенной ниже схеме показана распиновка ESP32, где контакты I2C по умолчанию выделены красным цветом. Это контакты I2C по умолчанию модуля ESP32 DEVKIT. Однако, если вы хотите использовать другие контакты GPIO в качестве контактов I2C, вам придется установить их в коде.
Для более подробной информации по этим вопросам можете прочитать статью про использование интерфейса I2C в ESP32 с помощью ESP-IDF.
Подключение датчика BME680 к ESP32
Для подключения датчика BME680 к плате ESP32 нам потребуются следующие компоненты:
- Плата ESP32.
- Датчик BME680.
- Соединительные провода.
- Макетная плата.
Подключение BME680 к плате ESP32 довольно простое, так как оно включает в себя лишь соединение 4 контактов (GND, VCC, SDA и SCL) с ESP32. Необходимо соединить вывод VCC с контактом 3,3 В, заземление с общим заземлением, контакт SCL датчика с контактом SCL платы ESP32, а контакт SDA датчика с контактом SDA платы ESP32.
В ESP32 по умолчанию для вывода SDA используется GPIO21, а для SCL — GPIO22.
Соединения между устройствами указаны в таблице ниже:
| Датчик BME680 | ESP32 |
| GND | GND |
| VCC | 3.3V |
| SDA | GPIO21(I2C SDA) |
| SCL | GPIO22 (I2C SCL) |
Использование ESP-IDF для подключения BME680 к ESP32
Мы разработаем проект в VS Code с расширением ESP-IDF для измерения температуры окружающей среды, барометрического давления, относительной влажности и содержания летучих органических соединений (ЛОС) или качества воздуха в помещении (IAQ) с помощью датчика окружающей среды BME60.
Код будет считывать показания датчиков и выводить их в терминал ESP-IDF каждые 1 секунду. Мы будем использовать библиотеку BME680 ESP-IDF, предоставленную UncleRus на GitHub.
Создание примера проекта
Откройте VS Code и перейдите в меню View > Command Palette («Вид» > «Палитра команд»). Введите в строке поиска ESP-IDF: New Project и нажмите Enter.
Укажите название проекта и каталог, а также плату и целевое устройство ESP-IDF. Для платы ESP-IDF мы выбрали опцию custom board («пользовательская плата»). Для целевого устройства ESP-IDF мы выбрали модуль ESP32. Нажмите кнопку ‘Choose Template’ («Выбрать шаблон»), чтобы продолжить.
В разделе «Расширение» выберите опцию ESP-IDF:
На вкладке get-started («Начало работы») мы щелкнем по пункту «sample_project». Затем нажмем ‘Create project using template sample_project’) («Создать проект, используя шаблон sample_project»).
Вы получите уведомление о создании проекта. Чтобы открыть проект в новом окне, нажмите «Да».
Это откроет проект, который мы создали на вкладке «Проводник». Внутри папки проекта находится несколько папок. Это справедливо для каждого проекта, который вы будете создавать с помощью ESP-IDF Explorer.
Для начала добавим необходимые заголовочные файлы для библиотек BME680, требуемых для этого проекта. Создайте новую папку с именем ‘components’ и добавьте в неё папки bme680, esp_idf_lib_helpers и i2cdev, скопировав их по ссылке ниже:
Теперь перейдите к файлу main.c. В папке main находится исходный код, то есть файл main.c будет находиться здесь. Перейдите в папку main > main.c и откройте его. Скопируйте приведенный ниже код из этого файла и сохраните его.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
#include <stdio.h> #include <freertos/FreeRTOS.h> #include <freertos/task.h> #include <esp_system.h> #include <bme680.h> #include <string.h> #define BME680_I2C_ADDR 0x77 #define PORT 0 #define CONFIG_EXAMPLE_I2C_MASTER_SDA 21 #define CONFIG_EXAMPLE_I2C_MASTER_SCL 22 #ifndef APP_CPU_NUM #define APP_CPU_NUM PRO_CPU_NUM #endif void bme680_test(void *pvParameters) { bme680_t sensor; memset(&sensor, 0, sizeof(bme680_t)); ESP_ERROR_CHECK(bme680_init_desc(&sensor, BME680_I2C_ADDR, PORT, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); // init the sensor ESP_ERROR_CHECK(bme680_init_sensor(&sensor)); // Changes the oversampling rates to 4x oversampling for temperature // and 2x oversampling for humidity. Pressure measurement is skipped. bme680_set_oversampling_rates(&sensor, BME680_OSR_4X, BME680_OSR_2X, BME680_OSR_2X); // Change the IIR filter size for temperature and pressure to 7. bme680_set_filter_size(&sensor, BME680_IIR_SIZE_7); // Change the heater profile 0 to 200 degree Celsius for 100 ms. bme680_set_heater_profile(&sensor, 0, 200, 100); bme680_use_heater_profile(&sensor, 0); // Set ambient temperature to 10 degree Celsius bme680_set_ambient_temperature(&sensor, 10); // as long as sensor configuration isn't changed, duration is constant uint32_t duration; bme680_get_measurement_duration(&sensor, &duration); TickType_t last_wakeup = xTaskGetTickCount(); bme680_values_float_t values; while (1) { // trigger the sensor to start one TPHG measurement cycle if (bme680_force_measurement(&sensor) == ESP_OK) { // passive waiting until measurement results are available vTaskDelay(duration); // get the results and do something with them if (bme680_get_results_float(&sensor, &values) == ESP_OK) printf("BME680 Sensor: %.2f °C, %.2f %%, %.2f hPa, %.2f Ohm\n", values.temperature, values.humidity, values.pressure, values.gas_resistance); } // passive waiting until 1 second is over vTaskDelayUntil(&last_wakeup, pdMS_TO_TICKS(1000)); } } void app_main() { ESP_ERROR_CHECK(i2cdev_init()); xTaskCreatePinnedToCore(bme680_test, "bme680_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); } |
Данный код можно также скачать по следующей ссылке.
Как работает код?
Для начала мы подключим необходимые для этого проекта библиотеки esp-idf. Это включает в себя драйвер i2c, драйвер gpio, библиотеку логирования ESP, библиотеку BME680 и библиотеку FreeRTOS.
|
1 2 3 4 5 6 |
#include <stdio.h> #include <freertos/FreeRTOS.h> #include <freertos/task.h> #include <esp_system.h> #include <bme680.h> #include <string.h> |
Эти строки определяют адрес BME680, который равен 0x77, порт I2C и номера контактов I2C ESP32, которые равны 21 (SDA) и 22 (SCL).
|
1 2 3 4 |
#define BME680_I2C_ADDR 0x77 #define PORT 0 #define CONFIG_EXAMPLE_I2C_MASTER_SDA 21 #define CONFIG_EXAMPLE_I2C_MASTER_SCL 22 |
Мы создадим задачу FreeRTOS и привяжем её к ядру 0 ESP32. Эта задача будет снимать показания датчика BME680 каждую секунду. Для этого зададим имя для APP_CPU_NUM.
|
1 |
#define APP_CPU_NUM PRO_CPU_NUM |
Необходимо инициализировать библиотеку i2c, причём это следует делать до вызова любой функции библиотеки bme680 esp-idf.
|
1 |
ESP_ERROR_CHECK(i2cdev_init()); |
Функция xTaskCreatePinnedToCore() создает задачу FreeRTOS и привязывает ее к ядру 0, что означает, что задача «bme680_test» будет выполняться на ядре 0 ESP32. Если вы не знаете, как создавать задачи FreeRTOS в esp-idf, вы можете обратиться к этой статье.
|
1 |
xTaskCreatePinnedToCore(bme680_test, "bme680_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); |
Функция считывания показаний датчика BME680
Задача bme680_test() инициализирует датчик BME680, определяет порт I2C, к которому подключен датчик, устанавливает частоту дискретизации для показаний температуры, влажности, давления и газа, устанавливает размер фильтров для температуры и давления, устанавливает продолжительность измерения и т. д.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
bme680_t sensor; memset(&sensor, 0, sizeof(bme680_t)); ESP_ERROR_CHECK(bme680_init_desc(&sensor, BME680_I2C_ADDR, PORT, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); // init the sensor ESP_ERROR_CHECK(bme680_init_sensor(&sensor)); // Changes the oversampling rates to 4x oversampling for temperature // and 2x oversampling for humidity. Pressure measurement is skipped. bme680_set_oversampling_rates(&sensor, BME680_OSR_4X, BME680_OSR_2X, BME680_OSR_2X); // Change the IIR filter size for temperature and pressure to 7. bme680_set_filter_size(&sensor, BME680_IIR_SIZE_7); // Change the heater profile 0 to 200 degree Celsius for 100 ms. bme680_set_heater_profile(&sensor, 0, 200, 100); bme680_use_heater_profile(&sensor, 0); // Set ambient temperature to 10 degree Celsius bme680_set_ambient_temperature(&sensor, 10); // as long as sensor configuration isn't changed, duration is constant uint32_t duration; bme680_get_measurement_duration(&sensor, &duration); TickType_t last_wakeup = xTaskGetTickCount(); bme680_values_float_t values; while (1) { // trigger the sensor to start one TPHG measurement cycle if (bme680_force_measurement(&sensor) == ESP_OK) { // passive waiting until measurement results are available vTaskDelay(duration); // get the results and do something with them if (bme680_get_results_float(&sensor, &values) == ESP_OK) printf("BME680 Sensor: %.2f °C, %.2f %%, %.2f hPa, %.2f Ohm\n", values.temperature, values.humidity, values.pressure, values.gas_resistance); } // passive waiting until 1 second is over vTaskDelayUntil(&last_wakeup, pdMS_TO_TICKS(1000)); } |
Теперь давайте рассмотрим функцию каждой строки по отдельности.
Здесь мы создаём экземпляр структуры bme680_t, которая используется для настройки и инициализации датчика. После этого инициализируем все члены структуры sensor нулями с помощью команды memset.
|
1 2 |
bme680_t sensor; memset(&sensor, 0, sizeof(bme680_t)); |
|
1 2 3 4 5 6 7 8 9 10 |
typedef struct { i2c_dev_t i2c_dev; //!< I2C device descriptor bool meas_started; //!< Indicates whether measurement started uint8_t meas_status; //!< Last sensor status (for internal use only) bme680_settings_t settings; //!< Sensor settings bme680_calib_data_t calib_data; //!< Calibration data of the sensor } bme680_t; |
Функция bme680_init_desc() инициализирует дескриптор устройства BME680, передавая структуру датчика, I2C-адрес BME680 и номера контактов I2C ESP32.
|
1 |
ESP_ERROR_CHECK(bme680_init_desc(&sensor, BME680_I2C_ADDR, PORT, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); |
Метод bme680_init_sensor() инициализирует структуру данных датчика BME680, выполняет проверку датчика, программно сбрасывает датчик и настраивает его с помощью следующих параметров по умолчанию:
- Коэффициент передискретизации для температуры, давления и влажности = osr_1x
- Размер фильтра для давления и температуры = iir_size 3
- Профиль нагревателя 0 с температурой 320 °C и длительностью 150 мс.
|
1 |
ESP_ERROR_CHECK(bme680_init_sensor(&sensor)); |
Функция bme680_set_oversampling_rates() устанавливает частоту передискретизации для измерений температуры, влажности, давления и газа. С помощью этой функции можно установить индивидуальную частоту дискретизации для температуры, давления и влажности. Функция bme680_init_desc() устанавливает частоту дискретизации по умолчанию равной 1x. Но с помощью этой функции мы можем изменить её. Возможные значения частоты передискретизации: 1x (по умолчанию), 2x, 4x, 8x, 16x.
|
1 |
bme680_set_oversampling_rates(&sensor, BME680_OSR_4X, BME680_OSR_2X, BME680_OSR_2X); |
Функция bme680_set_filter_size() определяет размер IRR-фильтров, поскольку BME680 имеет внутренний IRR-фильтр, который можно использовать для минимизации влияния кратковременных изменений выходных значений датчика, которые могут быть вызваны внешними помехами. IRR-фильтр регулирует уменьшение полосы пропускания выходного сигнала датчика.
|
1 |
bme680_set_filter_size(&sensor, BME680_IIR_SIZE_7); |
Датчик BME680 имеет встроенный разъем для измерения газа и поддерживает до 10 профилей нагревателя. Каждый профиль состоит из заданной температуры (целевой температуры) и продолжительности нагрева. Эта функция задает профиль нагревателя. Входными аргументами являются структура описания датчика, профиль нагревателя, целевая температура и продолжительность нагрева. Функция bme680_set_heater_profile() активирует измерение газа с заданным профилем нагревателя.
|
1 2 |
bme680_set_heater_profile(&sensor, 0, 200, 100); bme680_use_heater_profile(&sensor, 0); |
Функция bme680_set_ambient_temperature() задает температуру окружающей среды для расчета сопротивления нагревателя. Это связано с тем, что алгоритм расчета сопротивления нагревателя учитывает температуру окружающей среды датчика.
|
1 |
bme680_set_ambient_temperature(&sensor, 10); |
Функция bme680_get_measurement_duration() определяет продолжительность измерения, проводимого устройством BME680, в единицах тактов RTOS.
|
1 2 3 4 |
uint32_t duration; bme680_get_measurement_duration(&sensor, &duration); TickType_t last_wakeup = xTaskGetTickCount(); |
Структура bme680_values_float_t будет заполняться значениями с датчика при вызове функции bme680_get_results_float() для получения данных о температуре, влажности, давлении и газе с датчика.
|
1 2 3 4 5 6 7 |
typedef struct { float temperature; //!< temperature in degree C (Invalid value -327.68) float pressure; //!< barometric pressure in hPascal (Invalid value 0.0) float humidity; //!< relative humidity in % (Invalid value 0.0) float gas_resistance; //!< gas resistance in Ohm (Invalid value 0.0) } bme680_values_float_t; |
|
1 |
bme680_values_float_t values; |
Внутри цикла while мы вызываем функцию bme680_get_results_float() для получения показаний датчика BME680 и вывода их на терминал ESP-IDF.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
while (1) { // trigger the sensor to start one TPHG measurement cycle if (bme680_force_measurement(&sensor) == ESP_OK) { // passive waiting until measurement results are available vTaskDelay(duration); // get the results and do something with them if (bme680_get_results_float(&sensor, &values) == ESP_OK) printf("BME680 Sensor: %.2f °C, %.2f %%, %.2f hPa, %.2f Ohm\n", values.temperature, values.humidity, values.pressure, values.gas_resistance); } // passive waiting until 1 second is over vTaskDelayUntil(&last_wakeup, pdMS_TO_TICKS(1000)); } |
Компиляция и загрузка кода на ESP32 с помощью ESP-IDF
Для прошивки микросхемы введите следующую команду в последовательном терминале. Не забудьте заменить COM-порт на тот, через который подключена ваша плата.
|
1 |
idf.py -p COMX flash monitor |
После успешной прошивки кода вы сможете просматривать температуру окружающей среды, барометрическое давление, относительную влажность, а также содержание летучих органических соединений (ЛОС) или качество воздуха в помещении (IAQ). Если вы будете держать датчик в руке, значения на нем будут меняться.
