{"id":98394,"date":"2020-08-20T17:58:51","date_gmt":"2020-08-20T17:58:51","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=98394"},"modified":"2025-03-17T11:57:24","modified_gmt":"2025-03-17T11:57:24","slug":"esp8266-nodemcu-bme680-web-server-arduino","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp8266-nodemcu-bme680-web-server-arduino\/","title":{"rendered":"ESP8266 NodeMCU 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 ESP8266 NodeMCU 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 ESP8266 board 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\/ESP8266-NodeMCU-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=\"ESP8266 NodeMCU BME680 Gas sensor humidity barometric pressure ambient temperature gas air quality Arduino IDE Web Server\" class=\"wp-image-98395\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP8266-NodeMCU-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\/ESP8266-NodeMCU-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\/ESP8266-NodeMCU-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\/ESP8266-NodeMCU-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=\"https:\/\/randomnerdtutorials.com\/esp8266-nodemcu-bme680-sensor-arduino\/\">ESP8266 NodeMCU: 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\/ESP8266-NodeMCU-Board-BME680-Gas-sensor-circuit-wiring-diagram-schematics-1.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP8266 NodeMCU Board BME680 Gas sensor circuit wiring diagram schematics\" class=\"wp-image-98396\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP8266-NodeMCU-Board-BME680-Gas-sensor-circuit-wiring-diagram-schematics-1.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP8266-NodeMCU-Board-BME680-Gas-sensor-circuit-wiring-diagram-schematics-1.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\/esp8266-esp-12e-nodemcu-wi-fi-development-board\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP8266 <\/a>(read&nbsp;<a href=\"https:\/\/makeradvisor.com\/best-esp8266-wi-fi-development-board\/\" target=\"_blank\" rel=\"noreferrer noopener\">Best ESP8266 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; ESP8266 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 ESP8266 using the default I2C pins.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"831\" height=\"531\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP8266-BME680-Environmental-Sensor-Wiring-Diagram-I2C-1.png?resize=831%2C531&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP8266 NodeMCU BME680 Environmental Sensor Wiring Diagram I2C\" class=\"wp-image-98397\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP8266-BME680-Environmental-Sensor-Wiring-Diagram-I2C-1.png?w=831&amp;quality=100&amp;strip=all&amp;ssl=1 831w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP8266-BME680-Environmental-Sensor-Wiring-Diagram-I2C-1.png?resize=300%2C192&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP8266-BME680-Environmental-Sensor-Wiring-Diagram-I2C-1.png?resize=768%2C491&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 831px) 100vw, 831px\" \/><\/figure>\n\n\n\n<p class=\"rntbox rntclblue\">Recommended reading: <a href=\"https:\/\/randomnerdtutorials.com\/esp8266-pinout-reference-gpios\/\">ESP8266 Pinout Reference: 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 ESP8266 board using Arduino IDE. So, make sure you have the ESP8266 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-esp8266-nodemcu-arduino-ide-2-0\/\" title=\"\">Install the ESP8266 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 by ESP32Async<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/ESPAsyncTCP\" target=\"_blank\" rel=\"noopener\" title=\"\">ESPAsyncTCP&nbsp;by ESP32Async<\/a><\/li>\n<\/ul>\n\n\n\n<p>You can install these libraries using the Arduino Library Manager. Go to&nbsp;<strong>Sketch&nbsp;<\/strong>&gt;&nbsp;<strong>Include Library<\/strong>&nbsp;&gt;&nbsp;<strong>Manage Libraries<\/strong>&nbsp;and search for the libraries&#8217; names.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">ESP8266 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\">\/*********\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp8266-nodemcu-bme680-sensor-arduino\/\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n*********\/\n\n#include &lt;Wire.h&gt;\n#include &lt;SPI.h&gt;\n#include &lt;Adafruit_Sensor.h&gt;\n#include &quot;Adafruit_BME680.h&quot;\n#include &lt;ESP8266WiFi.h&gt;\n#include &quot;ESPAsyncWebServer.h&quot;\n\n\/\/ Replace with your network credentials\nconst char* ssid = &quot;REPLACE_WITH_YOUR_SSID&quot;;\nconst char* password = &quot;REPLACE_WITH_YOUR_PASSWORD&quot;;\n\n\/\/Uncomment if using SPI\n\/*#define BME_SCK 14\n#define BME_MISO 12\n#define BME_MOSI 13\n#define BME_CS 15*\/\n\nAdafruit_BME680 bme; \/\/ I2C\n\/\/Adafruit_BME680 bme(BME_CS); \/\/ hardware SPI\n\/\/Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);\n\nfloat temperature;\nfloat humidity;\nfloat pressure;\nfloat gasResistance;\n\nAsyncWebServer server(80);\nAsyncEventSource events(&quot;\/events&quot;);\n\nunsigned long lastTime = 0;  \nunsigned long timerDelay = 30000;  \/\/ send readings timer\n\nvoid getBME680Readings(){\n  \/\/ Tell BME680 to begin measurement.\n  unsigned long endTime = bme.beginReading();\n  if (endTime == 0) {\n    Serial.println(F(&quot;Failed to begin reading :(&quot;));\n    return;\n  }\n  if (!bme.endReading()) {\n    Serial.println(F(&quot;Failed to complete reading :(&quot;));\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}\n\nString processor(const String&amp; var){\n  getBME680Readings();\n  \/\/Serial.println(var);\n  if(var == &quot;TEMPERATURE&quot;){\n    return String(temperature);\n  }\n  else if(var == &quot;HUMIDITY&quot;){\n    return String(humidity);\n  }\n  else if(var == &quot;PRESSURE&quot;){\n    return String(pressure);\n  }\n  else if(var == &quot;GAS&quot;){\n    return String(gasResistance);\n  }\n  return String();\n}\n\nconst char index_html[] PROGMEM = R&quot;rawliteral(\n&lt;!DOCTYPE HTML&gt;&lt;html&gt;\n&lt;head&gt;\n  &lt;title&gt;BME680 Web Server&lt;\/title&gt;\n  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\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;\n  &lt;link rel=&quot;icon&quot; href=&quot;data:,&quot;&gt;\n  &lt;style&gt;\n    html {font-family: Arial; display: inline-block; text-align: center;}\n    p {  font-size: 1.2rem;}\n    body {  margin: 0;}\n    .topnav { overflow: hidden; background-color: #4B1D3F; color: white; font-size: 1.7rem; }\n    .content { padding: 20px; }\n    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }\n    .cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }\n    .reading { font-size: 2.8rem; }\n    .card.temperature { color: #0e7c7b; }\n    .card.humidity { color: #17bebb; }\n    .card.pressure { color: #3fca6b; }\n    .card.gas { color: #d62246; }\n  &lt;\/style&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=&quot;topnav&quot;&gt;\n    &lt;h3&gt;BME680 WEB SERVER&lt;\/h3&gt;\n  &lt;\/div&gt;\n  &lt;div class=&quot;content&quot;&gt;\n    &lt;div class=&quot;cards&quot;&gt;\n      &lt;div class=&quot;card temperature&quot;&gt;\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;\n      &lt;\/div&gt;\n      &lt;div class=&quot;card humidity&quot;&gt;\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;\n      &lt;\/div&gt;\n      &lt;div class=&quot;card pressure&quot;&gt;\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;\n      &lt;\/div&gt;\n      &lt;div class=&quot;card gas&quot;&gt;\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;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;script&gt;\nif (!!window.EventSource) {\n var source = new EventSource('\/events');\n \n source.addEventListener('open', function(e) {\n  console.log(&quot;Events Connected&quot;);\n }, false);\n source.addEventListener('error', function(e) {\n  if (e.target.readyState != EventSource.OPEN) {\n    console.log(&quot;Events Disconnected&quot;);\n  }\n }, false);\n \n source.addEventListener('message', function(e) {\n  console.log(&quot;message&quot;, e.data);\n }, false);\n \n source.addEventListener('temperature', function(e) {\n  console.log(&quot;temperature&quot;, e.data);\n  document.getElementById(&quot;temp&quot;).innerHTML = e.data;\n }, false);\n \n source.addEventListener('humidity', function(e) {\n  console.log(&quot;humidity&quot;, e.data);\n  document.getElementById(&quot;hum&quot;).innerHTML = e.data;\n }, false);\n \n source.addEventListener('pressure', function(e) {\n  console.log(&quot;pressure&quot;, e.data);\n  document.getElementById(&quot;pres&quot;).innerHTML = e.data;\n }, false);\n \n source.addEventListener('gas', function(e) {\n  console.log(&quot;gas&quot;, e.data);\n  document.getElementById(&quot;gas&quot;).innerHTML = e.data;\n }, false);\n}\n&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;)rawliteral&quot;;\n\nvoid setup() {\n  Serial.begin(115200);\n\n  \/\/ Set the device as a Station and Soft Access Point simultaneously\n  WiFi.mode(WIFI_AP_STA);\n  \n  \/\/ Set device as a Wi-Fi Station\n  WiFi.begin(ssid, password);\n  while (WiFi.status() != WL_CONNECTED) {\n    delay(1000);\n    Serial.println(&quot;Setting as a Wi-Fi Station..&quot;);\n  }\n  Serial.print(&quot;Station IP Address: &quot;);\n  Serial.println(WiFi.localIP());\n  Serial.println();\n\n  \/\/ Init BME680 sensor\n  if (!bme.begin()) {\n    Serial.println(F(&quot;Could not find a valid BME680 sensor, check wiring!&quot;));\n    while (1);\n  }\n  \/\/ Set up oversampling and filter initialization\n  bme.setTemperatureOversampling(BME680_OS_8X);\n  bme.setHumidityOversampling(BME680_OS_2X);\n  bme.setPressureOversampling(BME680_OS_4X);\n  bme.setIIRFilterSize(BME680_FILTER_SIZE_3);\n  bme.setGasHeater(320, 150); \/\/ 320*C for 150 ms\n\n  \/\/ Handle Web Server\n  server.on(&quot;\/&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\n    request-&gt;send(200, &quot;text\/html&quot;, index_html, processor);\n  });\n\n  \/\/ Handle Web Server Events\n  events.onConnect([](AsyncEventSourceClient *client){\n    if(client-&gt;lastId()){\n      Serial.printf(&quot;Client reconnected! Last message ID that it got is: %u\\n&quot;, client-&gt;lastId());\n    }\n    \/\/ send event with message &quot;hello!&quot;, id current millis\n    \/\/ and set reconnect delay to 1 second\n    client-&gt;send(&quot;hello!&quot;, NULL, millis(), 10000);\n  });\n  server.addHandler(&amp;events);\n  server.begin();\n}\n\nvoid loop() {\n  if ((millis() - lastTime) &gt; timerDelay) {\n    getBME680Readings();\n    Serial.printf(&quot;Temperature = %.2f \u00baC \\n&quot;, temperature);\n    Serial.printf(&quot;Humidity = %.2f % \\n&quot;, humidity);\n    Serial.printf(&quot;Pressure = %.2f hPa \\n&quot;, pressure);\n    Serial.printf(&quot;Gas Resistance = %.2f KOhm \\n&quot;, gasResistance);\n    Serial.println();\n\n    \/\/ Send Events to the Web Server with the Sensor Readings\n    events.send(&quot;ping&quot;,NULL,millis());\n    events.send(String(temperature).c_str(),&quot;temperature&quot;,millis());\n    events.send(String(humidity).c_str(),&quot;humidity&quot;,millis());\n    events.send(String(pressure).c_str(),&quot;pressure&quot;,millis());\n    events.send(String(gasResistance).c_str(),&quot;gas&quot;,millis());\n    \n    lastTime = millis();\n  }\n}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP8266\/ESP8266_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;ESP8266WiFi.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 ESP8266 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 ESP8266 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 ESP8266 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 14\n#define BME_MISO 12\n#define BME_MOSI 13\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 ESP8266 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 ESP8266 to your local network and print the ESP8266 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 ESP8266 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=\"950\" height=\"900\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/Server-Sent-Events-BME680-Web-server-ESP8266-NodeMCU.jpg?resize=950%2C900&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Update BME680 Web Server Readings ESP32 with Arduino IDE using Server Sent Events ESP8266 NodeMCU\" class=\"wp-image-98399\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/Server-Sent-Events-BME680-Web-server-ESP8266-NodeMCU.jpg?w=950&amp;quality=100&amp;strip=all&amp;ssl=1 950w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/Server-Sent-Events-BME680-Web-server-ESP8266-NodeMCU.jpg?resize=300%2C284&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-ESP8266-NodeMCU.jpg?resize=768%2C728&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 950px) 100vw, 950px\" \/><\/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 ESP8266. 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 ESP8266 on-board RST\/EN button. The 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 ESP8266 IP address. You should get access to the ESP8266 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\/ESP8266-NodeMCU-BME680-Gas-sensor-Web-Server-Demonstration.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP8266 NodeMCU BME680 Gas sensor Web Server Demonstration\" class=\"wp-image-98149\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP8266-NodeMCU-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\/ESP8266-NodeMCU-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 ESP8266 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\/esp8266-dht11dht22-temperature-and-humidity-web-server-with-arduino-ide\/\">ESP8266 <strong>DHT11\/DHT22<\/strong> Temperature and Humidity Web Server with Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp8266-web-server\/\">ESP8266 Web Server<strong> \u2013 Control Outputs <\/strong>with Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp8266-bme280-arduino-ide\/\">ESP8266 with BME280 using Arduino IDE (Pressure, Temperature, Humidity)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp8266-ds18b20-temperature-sensor-web-server-with-arduino-ide\/\">ESP8266 DS18B20 Temperature Sensor with Arduino IDE (Single, Multiple, Web Server)<\/a><\/li>\n<\/ul>\n\n\n\n<p>We hope you\u2019ve found this project interesting. Learn more about the ESP8266 with our resources:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/home-automation-using-esp8266\/\">Home Automation Using ESP8266<\/a><\/li>\n\n\n\n<li><a style=\"font-size: inherit; background-color: initial;\" 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-esp8266\/\">More ESP8266 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 ESP8266 NodeMCU to display sensor readings from the BME680 environmental sensor: gas (air quality), temperature, humidity and &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP8266 NodeMCU Web Server with BME680 &#8211; Weather Station (Arduino IDE)\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp8266-nodemcu-bme680-web-server-arduino\/#more-98394\" aria-label=\"Read more about ESP8266 NodeMCU Web Server with BME680 &#8211; Weather Station (Arduino IDE)\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":1,"featured_media":98395,"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":[265,214,246,300,240,264],"tags":[],"class_list":["post-98394","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp8266-project","category-esp8266","category-esp8266-arduino-ide","category-0-esp8266","category-esp8266-projects","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/07\/ESP8266-NodeMCU-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\/98394","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\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=98394"}],"version-history":[{"count":2,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/98394\/revisions"}],"predecessor-version":[{"id":168073,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/98394\/revisions\/168073"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/98395"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=98394"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=98394"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=98394"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}