{"id":98196,"date":"2020-08-11T10:01:26","date_gmt":"2020-08-11T10:01:26","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=98196"},"modified":"2025-03-14T16:46:47","modified_gmt":"2025-03-14T16:46:47","slug":"esp32-bme680-web-server-arduino","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-bme680-web-server-arduino\/","title":{"rendered":"ESP32 Web Server with BME680 &#8211; Weather Station (Arduino IDE)"},"content":{"rendered":"\n<p>This tutorial shows how to build a web server weather station with the ESP32 to display sensor readings from the BME680 environmental sensor: gas (air quality), temperature, humidity and pressure. The readings are updated automatically on the web server using Server-Sent Events (SSE). The ESP32 will be programmed using Arduino IDE.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"1200\" height=\"675\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-BME680-Gas-sensor-humidity-barometric-pressure-ambient-temperature-gas-air-quality-Arduino-IDE-Web-Server.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 BME680 Gas sensor humidity barometric pressure ambient temperature gas air quality Arduino IDE Web Server\" class=\"wp-image-98197\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-BME680-Gas-sensor-humidity-barometric-pressure-ambient-temperature-gas-air-quality-Arduino-IDE-Web-Server.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-BME680-Gas-sensor-humidity-barometric-pressure-ambient-temperature-gas-air-quality-Arduino-IDE-Web-Server.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-BME680-Gas-sensor-humidity-barometric-pressure-ambient-temperature-gas-air-quality-Arduino-IDE-Web-Server.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-BME680-Gas-sensor-humidity-barometric-pressure-ambient-temperature-gas-air-quality-Arduino-IDE-Web-Server.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p>To build the web server we\u2019ll use the&nbsp;ESP Async Web Server library&nbsp;that provides an easy way to build an asynchronous web server.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">BME680 Environmental Sensor<\/h2>\n\n\n\n<p>The BME680 is an environmental sensor that combines gas, temperature, humidity and pressure sensors. The gas sensor can detect a broad range of gases like volatile organic compounds (VOC). For this reason, the BME680 can be used in indoor air quality control.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/BME680-Gas-sensor-humidity-barometric-pressure-ambient-temperature-gas-air-quality-front.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"BME680 Gas sensor humidity barometric pressure ambient temperature gas air quality front\" class=\"wp-image-98118\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/BME680-Gas-sensor-humidity-barometric-pressure-ambient-temperature-gas-air-quality-front.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/BME680-Gas-sensor-humidity-barometric-pressure-ambient-temperature-gas-air-quality-front.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>The BME680 contains a MOX (Metal-oxide) sensor that detects VOCs in the air. This sensor gives you a qualitative idea of the<strong> sum of VOCs\/contaminants<\/strong> in the surrounding air. As a raw signal, the BME680 outputs resistance values. These values change due to variations in VOC concentrations:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"1112\" height=\"726\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/BME680-Gas-Sensor-Resistance-How-It-Works.jpg?resize=1112%2C726&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"BME680 Gas Environmental Air Quality Sensor Resistance How It Works\" class=\"wp-image-98136\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/BME680-Gas-Sensor-Resistance-How-It-Works.jpg?w=1112&amp;quality=100&amp;strip=all&amp;ssl=1 1112w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/BME680-Gas-Sensor-Resistance-How-It-Works.jpg?resize=300%2C196&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/BME680-Gas-Sensor-Resistance-How-It-Works.jpg?resize=1024%2C669&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/BME680-Gas-Sensor-Resistance-How-It-Works.jpg?resize=768%2C501&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1112px) 100vw, 1112px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Higher <\/strong>concentration of VOCs \u00bb <strong>Lower <\/strong>resistance<\/li>\n\n\n\n<li><strong>Lower <\/strong>concentration of VOCs \u00bb <strong>Higher <\/strong>resistance<\/li>\n<\/ul>\n\n\n\n<p class=\"rntbox rntclblue\">For more information about the BME680, read our getting started guide: <a href=\"http:\/\/ESP32: BME680 Environmental Sensor using Arduino IDE (Gas, Pressure, Humidity, Temperature)\" title=\"https:\/\/randomnerdtutorials.com\/esp32-bme680-sensor-arduino\/\">ESP32: BME680 Environmental Sensor using Arduino IDE (Gas, Pressure, Humidity, Temperature)<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Parts Required<\/h2>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-Board-BME680-Gas-sensor-circuit-wiring-diagram-schematics.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Board BME680 Gas sensor circuit wiring diagram schematics\" class=\"wp-image-98142\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-Board-BME680-Gas-sensor-circuit-wiring-diagram-schematics.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-Board-BME680-Gas-sensor-circuit-wiring-diagram-schematics.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>To complete this tutorial you need the following parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/bme680-gas-sensor-module\/\" target=\"_blank\" rel=\"noreferrer noopener\">BME680 sensor module<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP32 <\/a>(read\u00a0<a href=\"https:\/\/makeradvisor.com\/esp32-development-boards-review-comparison\/\" target=\"_blank\" rel=\"noreferrer noopener\">Best ESP32 development boards<\/a>)<\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/mb-102-solderless-breadboard-830-points\/\" target=\"_blank\" rel=\"noreferrer noopener\">Breadboard<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/jumper-wires-kit-120-pieces\/\" target=\"_blank\" rel=\"noreferrer noopener\">Jumper wires<\/a><\/li>\n<\/ul>\n\n\n<p>You can use the preceding links or go directly to <a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\">MakerAdvisor.com\/tools<\/a> to find all the parts for your projects at the best price!<\/p><p style=\"text-align:center;\"><a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/10\/header-200.png?w=1200&#038;quality=100&#038;strip=all&#038;ssl=1\"><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Schematic &#8211; ESP32 with BME680<\/h2>\n\n\n\n<p>The BME680 can communicate using I2C or SPI communication protocols. In this tutorial, we&#8217;ll use I2C communication protocol.<\/p>\n\n\n\n<p>Follow the next schematic diagram to wire the BME680 to the ESP32 using the default I2C pins.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"669\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32_BME680_Wiring_Diagram_I2C.png?resize=786%2C669&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 BME680 Wiring Diagram I2C\" class=\"wp-image-98165\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32_BME680_Wiring_Diagram_I2C.png?w=786&amp;quality=100&amp;strip=all&amp;ssl=1 786w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32_BME680_Wiring_Diagram_I2C.png?resize=300%2C255&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32_BME680_Wiring_Diagram_I2C.png?resize=768%2C654&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 786px) 100vw, 786px\" \/><\/figure><\/div>\n\n\n<p class=\"rntbox rntclblue\">Recommended reading: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-pinout-reference-gpios\/\">ESP32 Pinout Reference &#8211; Which GPIO pins should you use?<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Preparing Arduino IDE<\/h2>\n\n\n\n<p>We&#8217;ll program the ESP32 board using Arduino IDE. So, make sure you have the ESP32 add-on installed. Follow the next tutorial:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/installing-esp32-arduino-ide-2-0\/\" title=\"\">Install the ESP32 Board in Arduino IDE<\/a><\/li>\n<\/ul>\n\n\n\n<p>You also need to install the following libraries.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_BME680\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit_BME680 library<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_Sensor\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit_Sensor library<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/ESPAsyncWebServer\" target=\"_blank\" rel=\"noopener\" title=\"\">ESPAsyncWebServer&nbsp;<\/a>by ESP32Async<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/AsyncTCP\" target=\"_blank\" rel=\"noopener\" title=\"\">AsyncTCP<\/a> by ESP32Async<\/li>\n<\/ul>\n\n\n\n<p>You can install the Libraries via the Arduino Library Manager. Go to\u00a0<strong>Sketch\u00a0<\/strong>>\u00a0<strong>Include Library<\/strong>\u00a0>\u00a0<strong>Manage Libraries<\/strong>\u00a0and search for the libraries&#8217; names to install them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">ESP32 BME680 Web Server Code<\/h2>\n\n\n\n<p>Open your Arduino IDE and copy the following code. To make it work, you need to insert your network credentials: SSID and password.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*********\r\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-bme680-sensor-arduino\/\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n*********\/\r\n\r\n#include &lt;Wire.h&gt;\r\n#include &lt;SPI.h&gt;\r\n#include &lt;Adafruit_Sensor.h&gt;\r\n#include &quot;Adafruit_BME680.h&quot;\r\n#include &lt;WiFi.h&gt;\r\n#include &quot;ESPAsyncWebServer.h&quot;\r\n\r\n\/\/ Replace with your network credentials\r\nconst char* ssid = &quot;REPLACE_WITH_YOUR_SSID&quot;;\r\nconst char* password = &quot;REPLACE_WITH_YOUR_PASSWORD&quot;;\r\n\r\n\/\/Uncomment if using SPI\r\n\/*#define BME_SCK 18\r\n#define BME_MISO 19\r\n#define BME_MOSI 23\r\n#define BME_CS 5*\/\r\n\r\nAdafruit_BME680 bme; \/\/ I2C\r\n\/\/Adafruit_BME680 bme(BME_CS); \/\/ hardware SPI\r\n\/\/Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);\r\n\r\nfloat temperature;\r\nfloat humidity;\r\nfloat pressure;\r\nfloat gasResistance;\r\n\r\nAsyncWebServer server(80);\r\nAsyncEventSource events(&quot;\/events&quot;);\r\n\r\nunsigned long lastTime = 0;  \r\nunsigned long timerDelay = 30000;  \/\/ send readings timer\r\n\r\nvoid getBME680Readings(){\r\n  \/\/ Tell BME680 to begin measurement.\r\n  unsigned long endTime = bme.beginReading();\r\n  if (endTime == 0) {\r\n    Serial.println(F(&quot;Failed to begin reading :(&quot;));\r\n    return;\r\n  }\r\n  if (!bme.endReading()) {\r\n    Serial.println(F(&quot;Failed to complete reading :(&quot;));\r\n    return;\r\n  }\r\n  temperature = bme.temperature;\r\n  pressure = bme.pressure \/ 100.0;\r\n  humidity = bme.humidity;\r\n  gasResistance = bme.gas_resistance \/ 1000.0;\r\n}\r\n\r\nString processor(const String&amp; var){\r\n  getBME680Readings();\r\n  \/\/Serial.println(var);\r\n  if(var == &quot;TEMPERATURE&quot;){\r\n    return String(temperature);\r\n  }\r\n  else if(var == &quot;HUMIDITY&quot;){\r\n    return String(humidity);\r\n  }\r\n  else if(var == &quot;PRESSURE&quot;){\r\n    return String(pressure);\r\n  }\r\n else if(var == &quot;GAS&quot;){\r\n    return String(gasResistance);\r\n  }\r\n}\r\n\r\nconst char index_html[] PROGMEM = R&quot;rawliteral(\r\n&lt;!DOCTYPE HTML&gt;&lt;html&gt;\r\n&lt;head&gt;\r\n  &lt;title&gt;BME680 Web Server&lt;\/title&gt;\r\n  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\r\n  &lt;link rel=&quot;stylesheet&quot; href=&quot;https:\/\/use.fontawesome.com\/releases\/v5.7.2\/css\/all.css&quot; integrity=&quot;sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr&quot; crossorigin=&quot;anonymous&quot;&gt;\r\n  &lt;link rel=&quot;icon&quot; href=&quot;data:,&quot;&gt;\r\n  &lt;style&gt;\r\n    html {font-family: Arial; display: inline-block; text-align: center;}\r\n    p {  font-size: 1.2rem;}\r\n    body {  margin: 0;}\r\n    .topnav { overflow: hidden; background-color: #4B1D3F; color: white; font-size: 1.7rem; }\r\n    .content { padding: 20px; }\r\n    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }\r\n    .cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }\r\n    .reading { font-size: 2.8rem; }\r\n    .card.temperature { color: #0e7c7b; }\r\n    .card.humidity { color: #17bebb; }\r\n    .card.pressure { color: #3fca6b; }\r\n    .card.gas { color: #d62246; }\r\n  &lt;\/style&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n  &lt;div class=&quot;topnav&quot;&gt;\r\n    &lt;h3&gt;BME680 WEB SERVER&lt;\/h3&gt;\r\n  &lt;\/div&gt;\r\n  &lt;div class=&quot;content&quot;&gt;\r\n    &lt;div class=&quot;cards&quot;&gt;\r\n      &lt;div class=&quot;card temperature&quot;&gt;\r\n        &lt;h4&gt;&lt;i class=&quot;fas fa-thermometer-half&quot;&gt;&lt;\/i&gt; TEMPERATURE&lt;\/h4&gt;&lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;temp&quot;&gt;%TEMPERATURE%&lt;\/span&gt; &amp;deg;C&lt;\/span&gt;&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=&quot;card humidity&quot;&gt;\r\n        &lt;h4&gt;&lt;i class=&quot;fas fa-tint&quot;&gt;&lt;\/i&gt; HUMIDITY&lt;\/h4&gt;&lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;hum&quot;&gt;%HUMIDITY%&lt;\/span&gt; &amp;percnt;&lt;\/span&gt;&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=&quot;card pressure&quot;&gt;\r\n        &lt;h4&gt;&lt;i class=&quot;fas fa-angle-double-down&quot;&gt;&lt;\/i&gt; PRESSURE&lt;\/h4&gt;&lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;pres&quot;&gt;%PRESSURE%&lt;\/span&gt; hPa&lt;\/span&gt;&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=&quot;card gas&quot;&gt;\r\n        &lt;h4&gt;&lt;i class=&quot;fas fa-wind&quot;&gt;&lt;\/i&gt; GAS&lt;\/h4&gt;&lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;gas&quot;&gt;%GAS%&lt;\/span&gt; K&amp;ohm;&lt;\/span&gt;&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;script&gt;\r\nif (!!window.EventSource) {\r\n var source = new EventSource('\/events');\r\n \r\n source.addEventListener('open', function(e) {\r\n  console.log(&quot;Events Connected&quot;);\r\n }, false);\r\n source.addEventListener('error', function(e) {\r\n  if (e.target.readyState != EventSource.OPEN) {\r\n    console.log(&quot;Events Disconnected&quot;);\r\n  }\r\n }, false);\r\n \r\n source.addEventListener('message', function(e) {\r\n  console.log(&quot;message&quot;, e.data);\r\n }, false);\r\n \r\n source.addEventListener('temperature', function(e) {\r\n  console.log(&quot;temperature&quot;, e.data);\r\n  document.getElementById(&quot;temp&quot;).innerHTML = e.data;\r\n }, false);\r\n \r\n source.addEventListener('humidity', function(e) {\r\n  console.log(&quot;humidity&quot;, e.data);\r\n  document.getElementById(&quot;hum&quot;).innerHTML = e.data;\r\n }, false);\r\n \r\n source.addEventListener('pressure', function(e) {\r\n  console.log(&quot;pressure&quot;, e.data);\r\n  document.getElementById(&quot;pres&quot;).innerHTML = e.data;\r\n }, false);\r\n \r\n source.addEventListener('gas', function(e) {\r\n  console.log(&quot;gas&quot;, e.data);\r\n  document.getElementById(&quot;gas&quot;).innerHTML = e.data;\r\n }, false);\r\n}\r\n&lt;\/script&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;)rawliteral&quot;;\r\n\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n\r\n  \/\/ Set the device as a Station and Soft Access Point simultaneously\r\n  WiFi.mode(WIFI_AP_STA);\r\n  \r\n  \/\/ Set device as a Wi-Fi Station\r\n  WiFi.begin(ssid, password);\r\n  while (WiFi.status() != WL_CONNECTED) {\r\n    delay(1000);\r\n    Serial.println(&quot;Setting as a Wi-Fi Station..&quot;);\r\n  }\r\n  Serial.print(&quot;Station IP Address: &quot;);\r\n  Serial.println(WiFi.localIP());\r\n  Serial.println();\r\n\r\n  \/\/ Init BME680 sensor\r\n  if (!bme.begin()) {\r\n    Serial.println(F(&quot;Could not find a valid BME680 sensor, check wiring!&quot;));\r\n    while (1);\r\n  }\r\n  \/\/ Set up oversampling and filter initialization\r\n  bme.setTemperatureOversampling(BME680_OS_8X);\r\n  bme.setHumidityOversampling(BME680_OS_2X);\r\n  bme.setPressureOversampling(BME680_OS_4X);\r\n  bme.setIIRFilterSize(BME680_FILTER_SIZE_3);\r\n  bme.setGasHeater(320, 150); \/\/ 320*C for 150 ms\r\n\r\n  \/\/ Handle Web Server\r\n  server.on(&quot;\/&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    request-&gt;send(200, &quot;text\/html&quot;, index_html, processor);\r\n  });\r\n\r\n  \/\/ Handle Web Server Events\r\n  events.onConnect([](AsyncEventSourceClient *client){\r\n    if(client-&gt;lastId()){\r\n      Serial.printf(&quot;Client reconnected! Last message ID that it got is: %u\\n&quot;, client-&gt;lastId());\r\n    }\r\n    \/\/ send event with message &quot;hello!&quot;, id current millis\r\n    \/\/ and set reconnect delay to 1 second\r\n    client-&gt;send(&quot;hello!&quot;, NULL, millis(), 10000);\r\n  });\r\n  server.addHandler(&amp;events);\r\n  server.begin();\r\n}\r\n\r\nvoid loop() {\r\n  if ((millis() - lastTime) &gt; timerDelay) {\r\n    getBME680Readings();\r\n    Serial.printf(&quot;Temperature = %.2f \u00baC \\n&quot;, temperature);\r\n    Serial.printf(&quot;Humidity = %.2f % \\n&quot;, humidity);\r\n    Serial.printf(&quot;Pressure = %.2f hPa \\n&quot;, pressure);\r\n    Serial.printf(&quot;Gas Resistance = %.2f KOhm \\n&quot;, gasResistance);\r\n    Serial.println();\r\n\r\n    \/\/ Send Events to the Web Server with the Sensor Readings\r\n    events.send(&quot;ping&quot;,NULL,millis());\r\n    events.send(String(temperature).c_str(),&quot;temperature&quot;,millis());\r\n    events.send(String(humidity).c_str(),&quot;humidity&quot;,millis());\r\n    events.send(String(pressure).c_str(),&quot;pressure&quot;,millis());\r\n    events.send(String(gasResistance).c_str(),&quot;gas&quot;,millis());\r\n    \r\n    lastTime = millis();\r\n  }\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_BME680_Web_Server.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>Insert your network credentials in the following variables and the code will work straight away.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \"REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How the Code Works<\/h2>\n\n\n\n<p>Read this section to learn how the code works, or skip to the <a href=\"#upload\">next section<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Including Libraries<\/h3>\n\n\n\n<p>Start by including the necessary libraries. The <span class=\"rnthl rntliteral\">Wire<\/span> library is needed for I2C communication protocol. We also include the <span class=\"rnthl rntliteral\">SPI<\/span> library if you want to use SPI communication instead. <\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;Wire.h&gt;\n#include &lt;SPI.h&gt;<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">Adafruit_Sensor<\/span> and <span class=\"rnthl rntliteral\">Adafruit_BME680<\/span> libraries are needed to interface with the BME680 sensor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;Adafruit_Sensor.h&gt;\n#include \"Adafruit_BME680.h\"<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">WiFi<\/span> and <span class=\"rnthl rntliteral\">ESPAsyncWebServer<\/span> libraries are used to create the web server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;WiFi.h&gt;\n#include \"ESPAsyncWebServer.h\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Network Credentials<\/h3>\n\n\n\n<p>Insert your network credentials in the following variables, so that the ESP32 can connect to your local network using Wi-Fi.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \"REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">I2C Communication<\/h3>\n\n\n\n<p>Create an <span class=\"rnthl rntliteral\">Adafruit_BME680<\/span> object called <span class=\"rnthl rntliteral\">bme<\/span> on the default ESP32 I2C pins.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Adafruit_BME680 bme; \/\/ I2C<\/code><\/pre>\n\n\n\n<p>If you want to use SPI communication instead, you need to define the ESP32 SPI pins on the following lines (to uncomment remove the <span class=\"rnthl rntliteral\">\/*<\/span> and <span class=\"rnthl rntliteral\">*\/<\/span>):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/*#define BME_SCK 18\n#define BME_MISO 19\n#define BME_MOSI 23\n#define BME_CS 15*\/<\/code><\/pre>\n\n\n\n<p>And then, create an <span class=\"rnthl rntliteral\">Adafruit_BME680<\/span> object using those pins (to uncomment remove the <span class=\"rnthl rntliteral\">\/\/<\/span>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Declaring Variables<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">temperature<\/span>, <span class=\"rnthl rntliteral\">humidity<\/span>, <span class=\"rnthl rntliteral\">pressure<\/span> and <span class=\"rnthl rntliteral\">gasResistance<\/span> float variables will be used to hold BME680 sensor readings.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>float temperature;\nfloat humidity;\nfloat pressure;\nfloat gasResistance;<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">lastTime<\/span> and the <span class=\"rnthl rntliteral\">timerDelay<\/span> variables will be used to update sensor readings every X number of seconds. As an example, we&#8217;ll get new sensor readings every 30 seconds (30000 milliseconds). You can change that delay time in the <span class=\"rnthl rntliteral\">timerDelay<\/span> variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>unsigned long lastTime = 0;\nunsigned long timerDelay = 30000;<\/code><\/pre>\n\n\n\n<p>Create an Async Web Server on port 80.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>AsyncWebServer server(80);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create Event Source<\/h3>\n\n\n\n<p>To automatically display the information on the web server when a new reading arrives, we\u2019ll use Server-Sent Events (SSE).<\/p>\n\n\n\n<p>The following line creates a new event source on <span class=\"rnthl rntliteral\">\/events<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>AsyncEventSource events(\"\/events\");<\/code><\/pre>\n\n\n\n<p>Server-Sent Events allow a web page (client) to get updates from a server. We\u2019ll use this to automatically display new readings on the web server page when new BME680 readings are available.<\/p>\n\n\n\n<p class=\"rntbox rntcred\"><strong>Important: <\/strong> Server-sent events are not supported on Internet Explorer.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Get BME680 Readings<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">getBME680Reading()<\/span> function gets gas, temperature, humidity and pressure readings from the BME680 sensor and saves them on the <span class=\"rnthl rntliteral\">gasResistance<\/span>, <span class=\"rnthl rntliteral\">temperature<\/span>, <span class=\"rnthl rntliteral\">humidity<\/span> and <span class=\"rnthl rntliteral\">pressure<\/span> variables.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void getBME680Readings(){\n  \/\/ Tell BME680 to begin measurement.\n  unsigned long endTime = bme.beginReading();\n  if (endTime == 0) {\n    Serial.println(F(\"Failed to begin reading :(\"));\n    return;\n  }\n  if (!bme.endReading()) {\n    Serial.println(F(\"Failed to complete reading :(\"));\n    return;\n  }\n  temperature = bme.temperature;\n  pressure = bme.pressure \/ 100.0;\n  humidity = bme.humidity;\n  gasResistance = bme.gas_resistance \/ 1000.0;\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Processor<\/h2>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">processor()<\/span> function replaces any placeholders on the HTML text used to build the web page with the current sensor readings.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>String processor(const String&amp; var){\n  getBME680Readings();\n  \/\/Serial.println(var);\n  if(var == \"TEMPERATURE\"){\n    return String(temperature);\n  }\n  else if(var == \"HUMIDITY\"){\n    return String(humidity);\n  }\n  else if(var == \"PRESSURE\"){\n    return String(pressure);\n  }\n else if(var == \"GAS\"){\n    return String(gasResistance);\n  }\n}<\/code><\/pre>\n\n\n\n<p>This allows us to display the current sensor readings on the web page when you access it for the first time. Otherwise, you would see a blank space until new readings were available (which can take some time depending on the delay time you&#8217;ve defined on the code).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Building the Web Page<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">index_html<\/span> variable contains all the HTML, CSS and JavaScript to build the web page. We won\u2019t go into detail on how the HTML and CSS works. We\u2019ll just take a look at how to handle the events sent by the server.<\/p>\n\n\n\n<p>Let&#8217;s take a quick look at the line that displays the temperature:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;h4&gt;&lt;i class=\"fas fa-thermometer-half\"&gt;&lt;\/i&gt; TEMPERATURE&lt;\/h4&gt;&lt;p&gt;&lt;span class=\"reading\"&gt;&lt;span id=\"temp\"&gt;%TEMPERATURE%&lt;\/span&gt; &amp;deg;C&lt;\/span&gt;&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>You can see that the <span class=\"rnthl rntliteral\">%TEMPERATURE%<\/span> placeholder is surrounded by <span class=\"rnthl rntliteral\">&lt;span id=&#8221;temp&#8221;&gt;&lt;\/span&gt;<\/span> tags. The HTML <span class=\"rnthl rntliteral\">id<\/span> attribute is used to specify a unique id for an HTML element.<\/p>\n\n\n\n<p>It is used to point to a specific style or it can be used by JavaScript to access and manipulate the element with that specific id. That&#8217;s what we&#8217;re going to do. <\/p>\n\n\n\n<p>For instance, when the web server receives a new event with the latest temperature reading, we&#8217;ll update the HTML element with the id &#8220;<span class=\"rnthl rntliteral\">temp<\/span>&#8221; with the new reading.<\/p>\n\n\n\n<p>A similar process is done to update the other readings.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Handle Events<\/h4>\n\n\n\n<p>Create a new <span class=\"rnthl rntliteral\">EventSource<\/span> object and specify the URL of the page sending the updates. In our case, it\u2019s <span class=\"rnthl rntliteral\">\/events<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (!!window.EventSource) {\n  var source = new EventSource('\/events');<\/code><\/pre>\n\n\n\n<p>Once you\u2019ve instantiated an event source, you can start listening for messages from the server with <span class=\"rnthl rntliteral\">addEventListener()<\/span>.<\/p>\n\n\n\n<p>These are the default event listeners, as shown here in the AsyncWebServer&nbsp;<a href=\"https:\/\/github.com\/me-no-dev\/ESPAsyncWebServer#setup-event-source-in-the-browser\" target=\"_blank\" rel=\"noreferrer noopener\">documentation<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>source.addEventListener('open', function(e) {\n  console.log(\"Events Connected\");\n}, false);\nsource.addEventListener('error', function(e) {\n if (e.target.readyState != EventSource.OPEN) {\n   console.log(\"Events Disconnected\");\n }\n}, false);\n\nsource.addEventListener('message', function(e) {\n console.log(\"message\", e.data);\n}, false);<\/code><\/pre>\n\n\n\n<p>Then, add the event listener for \u201c<span class=\"rnthl rntliteral\">temperature<\/span>\u201d.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>source.addEventListener('temperature', function(e) {<\/code><\/pre>\n\n\n\n<p>When a new temperature reading is available, the ESP32 sends an event (\u201c<span class=\"rnthl rntliteral\">temperature<\/span>\u201d) to the client. The following lines handle what happens when the browser receives that event.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>console.log(\"temperature\", e.data);\ndocument.getElementById(\"temp\").innerHTML = e.data;<\/code><\/pre>\n\n\n\n<p>Basically, print the new readings on the browser console, and put the received data into the element with the corresponding id (&#8220;<span class=\"rnthl rntliteral\">temp<\/span>&#8220;) on the web page.<\/p>\n\n\n\n<p>A similar processor is done for humidity, pressure and gas resistance.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>source.addEventListener('humidity', function(e) {\n  console.log(\"humidity\", e.data);\n  document.getElementById(\"hum\").innerHTML = e.data;\n}, false);\n \nsource.addEventListener('pressure', function(e) {\n  console.log(\"pressure\", e.data);\n  document.getElementById(\"pres\").innerHTML = e.data;\n}, false);\n \nsource.addEventListener('gas', function(e) {\n  console.log(\"gas\", e.data);\n  document.getElementById(\"gas\").innerHTML = e.data;\n}, false);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">setup()<\/h3>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">setup()<\/span>, initialize the Serial Monitor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.begin(115200);<\/code><\/pre>\n\n\n\n<p>Connect the ESP32 to your local network and print the ESP32 IP address.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Set device as a Wi-Fi Station\nWiFi.begin(ssid, password);\nwhile (WiFi.status() != WL_CONNECTED) {\n  delay(1000);\n  Serial.println(\"Setting as a Wi-Fi Station..\");\n}\nSerial.print(\"Station IP Address: \");\nSerial.println(WiFi.localIP());\nSerial.println();<\/code><\/pre>\n\n\n\n<p>Initialize the BME680 sensor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Init BME680 sensor\nif (!bme.begin()) {\n  Serial.println(F(\"Could not find a valid BME680 sensor, check wiring!\"));\n  while (1);\n}\n\/\/ Set up oversampling and filter initialization\nbme.setTemperatureOversampling(BME680_OS_8X);\nbme.setHumidityOversampling(BME680_OS_2X);\nbme.setPressureOversampling(BME680_OS_4X);\nbme.setIIRFilterSize(BME680_FILTER_SIZE_3);\nbme.setGasHeater(320, 150); \/\/ 320*C for 150 ms<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Handle Requests<\/h4>\n\n\n\n<p>When you access the ESP32 IP address on the root&nbsp;<span class=\"rnthl rntliteral\">\/<\/span> URL, send the text that is stored on the <span class=\"rnthl rntliteral\">index_html<\/span> variable to build the web page and pass the <span class=\"rnthl rntliteral\">processor<\/span> as argument, so that all placeholders are replaced with the latest sensor readings.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.on(\"\/\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send_P(200, \"text\/html\", index_html, processor);\n});<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Server Event Source<\/h4>\n\n\n\n<p>Set up the event source on the server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Handle Web Server Events\nevents.onConnect(&#091;](AsyncEventSourceClient *client){\n  if(client-&gt;lastId()){\n    Serial.printf(\"Client reconnected! Last message ID that it got is: %u\\n\", client-&gt;lastId());\n  }\n  \/\/ send event with message \"hello!\", id current millis\n  \/\/ and set reconnect delay to 1 second\n  client-&gt;send(\"hello!\", NULL, millis(), 10000);\n});\nserver.addHandler(&amp;events);<\/code><\/pre>\n\n\n\n<p>Finally, start the server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.begin();<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">loop()<\/h3>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">loop()<\/span>, get new sensor readings:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>getBME680Readings();<\/code><\/pre>\n\n\n\n<p>Print the new readings in the Serial Monitor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.printf(\"Temperature = %.2f \u00baC \\n\", temperature);\nSerial.printf(\"Humidity = %.2f % \\n\", humidity);\nSerial.printf(\"Pressure = %.2f hPa \\n\", pressure);\nSerial.printf(\"Gas Resistance = %.2f KOhm \\n\", gasResistance);\nSerial.println();<\/code><\/pre>\n\n\n\n<p>Finally, send events to the browser with the newest sensor readings to update the web page.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Send Events to the Web Server with the Sensor Readings\nevents.send(\"ping\",NULL,millis());\nevents.send(String(temperature).c_str(),\"temperature\",millis());\nevents.send(String(humidity).c_str(),\"humidity\",millis());\nevents.send(String(pressure).c_str(),\"pressure\",millis());\nevents.send(String(gasResistance).c_str(),\"gas\",millis());<\/code><\/pre>\n\n\n\n<p>The following diagram summarizes how Server-Sent Events work to update the web page.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"926\" height=\"828\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/Server-Sent-Events-BME680-Web-server-ESP32-Arduino-IDE.png?resize=926%2C828&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Update BME680 Web Server Readings ESP32 with Arduino IDE using Server Sent Events\" class=\"wp-image-98220\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/Server-Sent-Events-BME680-Web-server-ESP32-Arduino-IDE.png?w=926&amp;quality=100&amp;strip=all&amp;ssl=1 926w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/Server-Sent-Events-BME680-Web-server-ESP32-Arduino-IDE.png?resize=300%2C268&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/Server-Sent-Events-BME680-Web-server-ESP32-Arduino-IDE.png?resize=768%2C687&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 926px) 100vw, 926px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"upload\">Uploading the Code<\/h2>\n\n\n\n<p>Now, upload the code to your ESP32. Make sure you have the right board and COM port selected.<\/p>\n\n\n\n<p>After uploading, open the Serial Monitor at a baud rate of 115200. Press the ESP32 on-board RST\/EN button. The ESP32 IP address should be printed in the serial monitor.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"764\" height=\"447\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-IP-Address-Serial-Monitor.png?resize=764%2C447&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 IP Address Serial Monitor Arduino IDE\" class=\"wp-image-98217\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-IP-Address-Serial-Monitor.png?w=764&amp;quality=100&amp;strip=all&amp;ssl=1 764w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-IP-Address-Serial-Monitor.png?resize=300%2C176&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 764px) 100vw, 764px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>Open a browser in your local network and type the ESP32 IP address. You should get access to the ESP32 web server with the latest BME680 readings.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-BME680-Gas-sensor-Web-Server-Demonstration.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 BME680 Gas sensor Web Server Demonstration\" class=\"wp-image-98150\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-BME680-Gas-sensor-Web-Server-Demonstration.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-BME680-Gas-sensor-Web-Server-Demonstration.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>The readings are updated automatically using Server-Sent Events.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"378\" height=\"750\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-ESP8266-NodeMCU-Web-Server-with-BME680-Gas-sensor.jpg?resize=378%2C750&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 or ESP8266 NodeMCU Board Web Server Demonstration with BME680 Gas sensor\" class=\"wp-image-98151\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-ESP8266-NodeMCU-Web-Server-with-BME680-Gas-sensor.jpg?w=378&amp;quality=100&amp;strip=all&amp;ssl=1 378w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-ESP8266-NodeMCU-Web-Server-with-BME680-Gas-sensor.jpg?resize=151%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 151w\" sizes=\"(max-width: 378px) 100vw, 378px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up <\/h2>\n\n\n\n<p>In this tutorial you&#8217;ve learned how to build an asynchronous web server weather station with the ESP32 to display BME680 sensor readings &#8211; gas (air quality), temperature, humidity and pressure &#8211; and how to update the readings automatically on the web page using Server-Sent Events.<\/p>\n\n\n\n<p>We have other web server tutorials that you may like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-dht11-dht22-temperature-humidity-web-server-arduino-ide\/\">ESP32 <strong>DHT11\/DHT22 Web Server<\/strong> \u2013 Temperature and Humidity using Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-async-web-server-espasyncwebserver-library\/\">ESP32 Async <strong>Web Server \u2013 Control Outputs <\/strong>with Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-with-bme280-mini-weather-station\/\">ESP32 <strong>Web Server with BME280<\/strong> \u2013 Advanced Weather Station<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-ds18b20-temperature-arduino-ide\/\">ESP32 <strong>DS18B20<\/strong> Temperature Sensor with Arduino IDE (Single, Multiple, <strong>Web Server<\/strong>)<\/a><\/li>\n<\/ul>\n\n\n\n<p>We hope you\u2019ve found this project interesting. Learn more about the ESP32 with our resources:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/learn-esp32-with-arduino-ide\/\">Learn ESP32 with Arduino IDE (eBook + Video Course)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/micropython-programming-with-esp32-and-esp8266\/\">MicroPython Programming with ESP32 and ESP8266<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/projects-esp32\/\">More ESP32 Projects and Tutorials\u2026<\/a><\/li>\n<\/ul>\n\n\n\n<p>Thanks for reading.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to build a web server weather station with the ESP32 to display sensor readings from the BME680 environmental sensor: gas (air quality), temperature, humidity and pressure. &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32 Web Server with BME680 &#8211; Weather Station (Arduino IDE)\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-bme680-web-server-arduino\/#more-98196\" aria-label=\"Read more about ESP32 Web Server with BME680 &#8211; Weather Station (Arduino IDE)\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":98197,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[281,276,277,299,264],"tags":[],"class_list":["post-98196","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32-project","category-esp32","category-esp32-arduino-ide","category-0-esp32","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP32-BME680-Gas-sensor-humidity-barometric-pressure-ambient-temperature-gas-air-quality-Arduino-IDE-Web-Server.jpg?fit=1280%2C720&quality=100&strip=all&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/98196","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=98196"}],"version-history":[{"count":1,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/98196\/revisions"}],"predecessor-version":[{"id":167955,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/98196\/revisions\/167955"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/98197"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=98196"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=98196"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=98196"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}