В этом проекте на основе технологии Интернета вещей (IoT) мы соединим датчик BME680 с модулем ESP32 или NodeMCU ESP8266. Мы создадим собственную метеостанцию и систему мониторинга IAQ (Index of Air Quality - индекс качества воздуха). Проект основан на протоколе MQTT с использованием интегрированного датчика окружающей среды BME680. Платформа MQTT, которую мы будем использовать, — это Ubidots. Используя платформу Ubidots, мы можем отправлять данные в облако с любого устройства с доступом в Интернет.
MQTT — это легкий протокол, очень популярный для устройств IoT. MQTT позволяет отправлять команды для регулирования выходов, считывать неопубликованные данные с узла датчика и многое другое. Таким образом, он значительно упрощает установление связи между несколькими устройствами. BME680 измеряет температуру, влажность, высоту, давление, точку росы и индекс качества воздуха (AQI) и публикует их в облаке Ubidots через MQTT. Панель управления Ubidots будет отображать данные датчиков в реальном времени в красивых виджетах.
Что касается аппаратной части, нам просто нужен датчик BME680 и модуль WiFi ESP8266 или ESP32. Для настройки оборудования достаточно 4-проводного соединения I2C между датчиком и контроллером. ESP32/ESP8266 подключается к сети WiFi и с помощью ключа API Ubidots данные об окружающей среде загружаются на панель управления Ubidots через фиксированный интервал времени. Вы можете получить доступ к панели управления как с компьютера, так и с помощью смартфона из любой точки мира.
Ранее комбинированный датчик BME680 мы использовали в следующих проектах на нашем сайте:
Если вы хотите больше узнать о работе с протоколом с MQTT, то можете посмотреть следующие статьи:
- подключение NodeMCU ESP8266 к MQTT брокеру с помощью Arduino IDE;
- установка Mosquitto MQTT брокера на Raspberry Pi для технологии IoT.
Необходимые компоненты
- Модуль ESP32 (купить на AliExpress) или NodeMCU ESP8266 (купить на AliExpress).
- Датчик BME680 (купить на AliExpress).
- Макетная плата.
- Соединительные провода.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Интегрированный датчик окружающей среды BME680
BME680 — это цифровой датчик 4-в-1 с измерением газа, влажности, давления и температуры на основе проверенных принципов измерения. BME680 — это улучшенная и усовершенствованная версия предыдущих версий датчиков, таких как BMP180, BMP280 или BME280. Датчик газа на BME680 может обнаруживать широкий спектр летучих органических соединений для мониторинга качества воздуха в помещении. Датчик имеет высокую линейность и точность.
BME680 специально разработан для мобильных приложений и носимых устройств, где размер и низкое энергопотребление являются критическими требованиями. Для измерения качества воздуха газовый датчик внутри BME680 может обнаруживать широкий спектр газов, таких как летучие органические соединения (volatile organic compounds, VOC).
Датчик работает в диапазоне от 1,7 В до 3,6 В. Потребляемая мощность этого модуля в режиме ожидания составляет от 0,29 до 0,8 мкА , а в спящем режиме потребление мощности составляет от 0,15 до 1 мкА. Диапазон измерения температуры датчика BME680 составляет от -40 до +85 ℃. Диапазон измерения влажности составляет 0-100% . Он может измерять индекс качества воздуха (IAQ) от 0 до 500 PPM.
Адрес I2C датчика по умолчанию — 0x76 , но его можно изменить на 0x77, просто подключив SDO к GND. Чтобы узнать больше о датчике BME680, вы можете проверить спецификацию BME680.
Схема проекта
Подключение ESP8266 и BME680
Схема подключения датчика BME680 к плате NodeMCU ESP8266 представлена на рисунке ниже. Для сборки схемы можно использовать макетную плату.
Подключите контакты VCC и GND датчика BME680 к контактам NodeMCU ESP8266 3.3V и GND. Аналогично подключите контакты BME680 SDA и SCL к контактам ESP8266 D2 и D1 соответственно. Вы можете включить устройство, используя порт Micro-USB 5 В платы NodeMCU или литий-ионную батарею 3.7 В.
Подключение ESP32 и BME680
Схема подключения датчика BME680 к плате ESP32 представлена на рисунке ниже. Для сборки схемы можно использовать макетную плату.
Подключите контакты VCC и GND датчика BME680 к контактам ESP32 3.3V и GND. Аналогично подключите контакты SDA и SCL BME680 к контактам GPIO21 и GPIO22 ESP32 соответственно. Вы можете включить устройство, используя порт Micro-USB 5 В ESP32 или литий-ионную батарею 3.7 В.
Настройка Ubidots
Нам нужно настроить панель управления Ubidots чтобы получать данные BME680 от ESP32 или ESP8266 по протоколу MQTT. Чтобы настроить Ubidots, посетите https://ubidots.com/ и создайте новую учетную запись, используя свой адрес электронной почты.
После входа в панель инструментов Ubidots нам понадобится API Key. Чтобы получить API key, нажмите в правом верхнем углу панели на опцию профиля. Вы увидите опцию под названием «API Credentials». Нажмите на нее.
Теперь, когда вы нажимаете на Default Token и копируете API Token вы потом сможете вставить этот API Token в код программы.
Исходный код программы
Исходные коды программ для BME680 с ESP32 и ESP8266 приведены ниже. Но перед этим вам понадобятся несколько библиотек. Вы можете скачать эти библиотеки по следующим ссылкам.
1. Библиотека PubSubClient: https://github.com/knolleary/pubsubclient
2. Библиотека BME680 Adafruit: https://github.com/adafruit/Adafruit_BME680
3. Унифицированная библиотека датчиков Adafruit: https://github.com/adafruit/Adafruit_Sensor
Измените SSID и пароль WiFi в строках ниже на свои SSID и пароль.
1 2 |
#define WIFISSID "***************************" // Put your WifiSSID here #define PASSWORD "***************************" // Put your wifi password here |
Измените токен аутентификации Ubidots в следующей строке кода. Используйте токен, который вы получили при создании учетной записи Ubidots, как описано выше.
Исходный код: ESP32 + BME680 + MQTT
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
#include <Wire.h> #include <WiFi.h> #include <PubSubClient.h> #include <Adafruit_Sensor.h> #include "Adafruit_BME680.h" #define SEALEVELPRESSURE_HPA (1013.25) Adafruit_BME680 bme; // I2C #define WIFISSID "realme C15" // Put your WifiSSID here #define PASSWORD "12341234" // Put your wifi password here #define TOKEN "BBFF-***************CWBI27sv" // Put your Ubidots' TOKEN #define MQTT_CLIENT_NAME "ESP32_BME680_Station" // MQTT client Name, please enter your own 8-12 alphanumeric character ASCII string; /**************************************** Define Constants ****************************************/ #define VARIABLE_LABEL1 "Temperature" // Assing the variable label #define VARIABLE_LABEL2 "Humidity" // Assing the variable label #define VARIABLE_LABEL3 "Pressure" #define VARIABLE_LABEL4 "Altitude" #define VARIABLE_LABEL5 "DewPoint" #define VARIABLE_LABEL6 "Gas" #define DEVICE_LABEL "ESP32" char mqttBroker[] = "industrial.api.ubidots.com"; char payload[1000]; char topic1[150]; char topic2[150]; char topic3[150]; char topic4[150]; char topic5[150]; char topic6[150]; // Space to store values to send char str_Temperature[10]; char str_Humidity[10]; char str_Pressure[10]; char str_Altitude[10]; char str_DewPoint[10]; char str_Gas[10]; /**************************************** Auxiliar Functions ****************************************/ WiFiClient ubidots; PubSubClient client(ubidots); void callback(char* topic, byte* payload, unsigned int length) { char p[length + 1]; memcpy(p, payload, length); p[length] = NULL; String message(p); Serial.write(payload, length); Serial.println(topic); } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.println("Attempting MQTT connection..."); // Attemp to connect if (client.connect(MQTT_CLIENT_NAME, TOKEN, "")) { Serial.println("Connected"); } else { Serial.print("Failed, rc="); Serial.print(client.state()); Serial.println(" try again in 2 seconds"); // Wait 2 seconds before retrying delay(2000); } } } /**************************************** Main Functions ****************************************/ void setup() { Serial.begin(115200); while (!Serial); Serial.println(F("BME680 test")); if (!bme.begin()) { Serial.println("Could not find a valid BME680 sensor, check wiring!"); while (1); } // Set up oversampling and filter initialization bme.setTemperatureOversampling(BME680_OS_8X); bme.setHumidityOversampling(BME680_OS_2X); bme.setPressureOversampling(BME680_OS_4X); bme.setIIRFilterSize(BME680_FILTER_SIZE_3); bme.setGasHeater(320, 150); // 320*C for 150 ms WiFi.begin(WIFISSID, PASSWORD); Serial.println(); Serial.print("Waiting for WiFi Connection .............."); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } Serial.println(""); Serial.println("WiFi Connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); client.setServer(mqttBroker, 1883); client.setCallback(callback); } void loop() { if (!client.connected()) { reconnect(); } if (! bme.performReading()) { Serial.println("Failed to perform reading :("); return; } float temperature = (bme.temperature); float humidity = (bme.humidity); float pressure = (bme.pressure / 100.0); float altitude = (bme.readAltitude(SEALEVELPRESSURE_HPA)); double dewPoint = (dewPointFast(temperature, humidity)); float gas = (bme.gas_resistance / 1000.0); Serial.print("Temperature = "); Serial.print(temperature); Serial.println(" *C"); Serial.print("Humidity = "); Serial.print(humidity); Serial.println(" %"); Serial.print("Pressure = "); Serial.print(pressure); Serial.println(" hPa"); Serial.print("Approx. Altitude = "); Serial.print(altitude); Serial.println(" m"); Serial.print("Dew Point = "); Serial.print(dewPoint); Serial.println(" *C"); Serial.print("Gas = "); Serial.print(gas); Serial.println(" KOhms"); dtostrf(temperature, 4, 2, str_Temperature); dtostrf(humidity, 4, 2, str_Humidity); dtostrf(pressure, 4, 2, str_Pressure); dtostrf(altitude, 4, 2, str_Altitude); dtostrf(dewPoint, 4, 2, str_DewPoint); dtostrf(gas, 4, 2, str_Gas); sprintf(topic1, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL1); sprintf(payload, "%s {\"value\": %s}}", payload, str_Temperature); Serial.println("Publishing temperature to Ubidots Cloud"); client.publish(topic1, payload); sprintf(topic2, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL2); sprintf(payload, "%s {\"value\": %s}}", payload, str_Humidity); Serial.println("Publishing humidity to Ubidots Cloud"); client.publish(topic2, payload); sprintf(topic3, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL3); sprintf(payload, "%s {\"value\": %s}}", payload, str_Pressure); Serial.println("Publishing Pressure data to Ubidots Cloud"); client.publish(topic3, payload); sprintf(topic4, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL4); sprintf(payload, "%s {\"value\": %s}}", payload, str_Altitude); Serial.println("Publishing Altitude data to Ubidots Cloud"); client.publish(topic4, payload); sprintf(topic5, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL5); sprintf(payload, "%s {\"value\": %s}}", payload, str_DewPoint); Serial.println("Publishing Dew Point data to Ubidots Cloud"); client.publish(topic5, payload); sprintf(topic6, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL6); sprintf(payload, "%s {\"value\": %s}}", payload, str_Gas); Serial.println("Publishing Gas data to Ubidots Cloud"); client.publish(topic6, payload); Serial.println(); client.loop(); delay(5000); } double dewPointFast(double celsius, double humidity) { double a = 17.271; double b = 237.7; double temp = (a * celsius) / (b + celsius) + log(humidity * 0.01); double Td = (b * temp) / (a - temp); return Td; } |
Исходный код: ESP8266 + BME680 + MQTT
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
#include <Wire.h> #include <ESP8266WiFi.h> #include <PubSubClient.h> #include <Adafruit_Sensor.h> #include "Adafruit_BME680.h" #define SEALEVELPRESSURE_HPA (1013.25) Adafruit_BME680 bme; // I2C #define WIFISSID "realme C15" // Put your WifiSSID here #define PASSWORD "12341234" // Put your wifi password here #define TOKEN "BBF************************WBI27sv" // Put your Ubidots' TOKEN #define MQTT_CLIENT_NAME "ESP8266_BME680_Station" // MQTT client Name, please enter your own 8-12 alphanumeric character ASCII string; /**************************************** Define Constants ****************************************/ #define VARIABLE_LABEL1 "Temperature" // Assing the variable label #define VARIABLE_LABEL2 "Humidity" // Assing the variable label #define VARIABLE_LABEL3 "Pressure" #define VARIABLE_LABEL4 "Altitude" #define VARIABLE_LABEL5 "DewPoint" #define VARIABLE_LABEL6 "Gas" #define DEVICE_LABEL "ESP8266" char mqttBroker[] = "industrial.api.ubidots.com"; char payload[1000]; char topic1[150]; char topic2[150]; char topic3[150]; char topic4[150]; char topic5[150]; char topic6[150]; // Space to store values to send char str_Temperature[10]; char str_Humidity[10]; char str_Pressure[10]; char str_Altitude[10]; char str_DewPoint[10]; char str_Gas[10]; /**************************************** Auxiliar Functions ****************************************/ WiFiClient ubidots; PubSubClient client(ubidots); void callback(char* topic, byte* payload, unsigned int length) { char p[length + 1]; memcpy(p, payload, length); p[length] = NULL; String message(p); Serial.write(payload, length); Serial.println(topic); } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.println("Attempting MQTT connection..."); // Attemp to connect if (client.connect(MQTT_CLIENT_NAME, TOKEN, "")) { Serial.println("Connected"); } else { Serial.print("Failed, rc="); Serial.print(client.state()); Serial.println(" try again in 2 seconds"); // Wait 2 seconds before retrying delay(2000); } } } /**************************************** Main Functions ****************************************/ void setup() { Serial.begin(115200); while (!Serial); Serial.println(F("BME680 test")); if (!bme.begin()) { Serial.println("Could not find a valid BME680 sensor, check wiring!"); while (1); } // Set up oversampling and filter initialization bme.setTemperatureOversampling(BME680_OS_8X); bme.setHumidityOversampling(BME680_OS_2X); bme.setPressureOversampling(BME680_OS_4X); bme.setIIRFilterSize(BME680_FILTER_SIZE_3); bme.setGasHeater(320, 150); // 320*C for 150 ms WiFi.begin(WIFISSID, PASSWORD); Serial.println(); Serial.print("Waiting for WiFi Connection .............."); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } Serial.println(""); Serial.println("WiFi Connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); client.setServer(mqttBroker, 1883); client.setCallback(callback); } void loop() { if (!client.connected()) { reconnect(); } if (! bme.performReading()) { Serial.println("Failed to perform reading :("); return; } float temperature = (bme.temperature); float humidity = (bme.humidity); float pressure = (bme.pressure / 100.0); float altitude = (bme.readAltitude(SEALEVELPRESSURE_HPA)); double dewPoint = (dewPointFast(temperature, humidity)); float gas = (bme.gas_resistance / 1000.0); Serial.print("Temperature = "); Serial.print(temperature); Serial.println(" *C"); Serial.print("Humidity = "); Serial.print(humidity); Serial.println(" %"); Serial.print("Pressure = "); Serial.print(pressure); Serial.println(" hPa"); Serial.print("Approx. Altitude = "); Serial.print(altitude); Serial.println(" m"); Serial.print("Dew Point = "); Serial.print(dewPoint); Serial.println(" *C"); Serial.print("Gas = "); Serial.print(gas); Serial.println(" KOhms"); dtostrf(temperature, 4, 2, str_Temperature); dtostrf(humidity, 4, 2, str_Humidity); dtostrf(pressure, 4, 2, str_Pressure); dtostrf(altitude, 4, 2, str_Altitude); dtostrf(dewPoint, 4, 2, str_DewPoint); dtostrf(gas, 4, 2, str_Gas); sprintf(topic1, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL1); sprintf(payload, "%s {\"value\": %s}}", payload, str_Temperature); Serial.println("Publishing temperature to Ubidots Cloud"); client.publish(topic1, payload); sprintf(topic2, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL2); sprintf(payload, "%s {\"value\": %s}}", payload, str_Humidity); Serial.println("Publishing humidity to Ubidots Cloud"); client.publish(topic2, payload); sprintf(topic3, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL3); sprintf(payload, "%s {\"value\": %s}}", payload, str_Pressure); Serial.println("Publishing Pressure data to Ubidots Cloud"); client.publish(topic3, payload); sprintf(topic4, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL4); sprintf(payload, "%s {\"value\": %s}}", payload, str_Altitude); Serial.println("Publishing Altitude data to Ubidots Cloud"); client.publish(topic4, payload); sprintf(topic5, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL5); sprintf(payload, "%s {\"value\": %s}}", payload, str_DewPoint); Serial.println("Publishing Dew Point data to Ubidots Cloud"); client.publish(topic5, payload); sprintf(topic6, "%s%s", "/v1.6/devices/", DEVICE_LABEL); sprintf(payload, "%s", ""); sprintf(payload, "{\"%s\":", VARIABLE_LABEL6); sprintf(payload, "%s {\"value\": %s}}", payload, str_Gas); Serial.println("Publishing Gas data to Ubidots Cloud"); client.publish(topic6, payload); Serial.println(); client.loop(); delay(5000); } double dewPointFast(double celsius, double humidity) { double a = 17.271; double b = 237.7; double temp = (a * celsius) / (b + celsius) + log(humidity * 0.01); double Td = (b * temp) / (a - temp); return Td; } |
Результаты тестирования
Приведенный выше код для ESP32 и BME680 для MQTT Ubidots удовлетворяет всем требованиям проекта. Вы можете подключить датчик BME680 либо к ESP32, либо к NodeMCU ESP8266. Вы можете загрузить код в модуль WiFi. После загрузки кода плата ESP32/ESP8266 попытается подключиться к локальной сети WiFi, данные для подключения которой указаны в программе. После подключения к сети WiFi вы можете открыть Serial Monitor (окно монитора последовательной связи).
Последовательный монитор будет отображать температуру, влажность, давление, высоту, точку росы, а также IAQ (индекс качества воздуха). Таким образом, вы можете измерять не только параметры окружающей среды, но и IAQ с помощью ESP32 и BME680. Если есть успешное подключение по Wi-Fi и дальнейшее подключение к Ubidots, данные будут автоматически загружаться после каждого установленного интервала.
Дальнейшая настройка панели инструментов Ubidots
Изначально на панели инструментов Ubidots не будет отображаться ни одного устройства. Но как только устройство будет подключено к компьютеру и включено, вы увидите устройство, появившееся в разделе устройств Ubidots.
Теперь щелкните по устройству, которое отображается на панели инструментов Ubidots. В моем случае отображается esp32, поскольку я использую плату ESP32 для подключения по протоколу MQTT. На этой панели инструментов вы увидите данные о температуре, влажности, давлении, высоте, точке росы и IAQ. Соединение MQTT работает в режиме реального времени. Поэтому данные изменяются через определенный интервал.
Теперь вернитесь к панели управления. В правом верхнем углу панели управления нажмите на символ «+». Вы увидите множество виджетов. Вы можете нажать и создать виджет для отображения всех отдельных параметров от температуры до качества воздуха в помещении. Например, вы можете выбрать Gauge для отображения влажности.
После выбора датчика или любого другого виджета выберите переменную из списка, нажав “Add Variables“ («Добавить переменные»). Затем дайте ей любое имя и нажмите зеленую кнопку, чтобы продолжить.
Сделайте то же самое для всех остальных 5 переменных. Наконец, после проектирования панели с помощью виджетов, вы получите красивую панель в реальном времени, как показано на изображении ниже.
Вы также можете посетить мобильную версию панели инструментов Ubidots, на мобильном экране также будут отображаться зарегистрированные данные BME680 на дисплее виджетов.
Итак, вот как мы можем соединить интегрированный датчик окружающей среды BME680 с модулем Wifi ESP32 или ESP8266 и измерить IAQ и все другие параметры окружающей среды. Аналогично, отправка данных на панель управления Ubidots с помощью MQTT брокера требует высокого уровня навыков программирования. Вы можете отправлять данные с других датчиков на платформу Ubidots, используя тот же метод.