Help with BME688/ENS160 Basic Webserver

I have some strange issues going on with my project, including multiple reboots before the Web server loads, with Stack Smashing Protect errors. *See full code and errors below. Be warned I'm not a coder and just fiddle with these for fun and to learn a bit when I can.

I can get the Webserver to start if I temporarily pull the 3v3 wire off the BME688 (it stays powered on, I'm guessing due to SCL/SDA? BME688 and ENS160 are wired to one I2C bus. Microcontroller is a LOLIN D32. Full code and logs below:

#include <Arduino.h>
#include <Wire.h>
#include "SparkFun_ENS160.h"
#include <WiFi.h>
#include <WebServer.h>
#include <NTPClient.h>
#include <Time.h>
#include <Secrets.h>
#include <WiFiUdp.h>
#include <bme68xLibrary.h>


//define WiFi Network name and password with secrets.h
const char* ssid = SECRET_SSID;
const char* password = SECRET_PASS;

//Int and Float values to be used for Webserver later
int AQI, VOCs, CO2;
String formattedTime;
String Date;
int Day;
int Month;
int Year;

//declare Webserver and ntpUPD
WebServer server(80);

WiFiUDP ntpUDP;

//NTP Client and initializing time
int gtmOffset = -5;
NTPClient timeClient(ntpUDP, "pool.ntp.org", gtmOffset*60*60, 60000);

//Week Days
String weekDays[7]={"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

//Month names
String months[12]={"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};

//declare ENS160 sensor and entStatus, as well as float relative humidity and temp in Celcius values for later use
SparkFun_ENS160 myENS; 

bool printedCompensation = false;
int ensStatus; 

float rh;
float tempC;
float tempF;
float gasR;
float press;
float pressMillis;

//declare and initalize BME688

#define I2C_ADDR 0x76

Bme68x bme;

void setup()
{
	Wire.begin();

	Serial.begin(115200);

	Serial.println("Connecting to ");
  Serial.println(ssid);

  //connect to your local wi-fi network
  WiFi.begin(ssid, password);

  //check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  Serial.println(WiFi.localIP());

  server.on("/", handle_OnConnect);
  server.onNotFound(handle_NotFound);

  server.begin();
  Serial.println("HTTP server started");

  timeClient.begin();

    // Initialize I2C operation
  bme.begin(I2C_ADDR, Wire);

  if(bme.checkStatus())
  {
    if (bme.checkStatus() == BME68X_ERROR)
    {
      Serial.println("Sensor error:" + bme.statusString());
      return;
    }
    else if (bme.checkStatus() == BME68X_WARNING)
    {
      Serial.println("Sensor Warning:" + bme.statusString());
    }
  }
  
  /* Set the default configuration for temperature, pressure and humidity */
  bme.setTPH();

  /* Set the heater configuration to 320 deg C for 150ms for Forced mode */
  bme.setHeaterProf(320, 150);

	//initialize ENS sensor
	if( !myENS.begin() )
	{
		Serial.println("Could not communicate with the ENS160, check wiring.");
		while(1);
	}

  Serial.println("Example 6 Burn In Time for the ENS160.");

	// Reset the indoor air quality sensor's settings.
	if( myENS.setOperatingMode(SFE_ENS160_RESET) )
		Serial.println("Ready.");

	delay(100);

	// Device needs to be set to idle to apply any settings.
	// myENS.setOperatingMode(SFE_ENS160_IDLE);

	// Set to standard operation
	// Others include SFE_ENS160_DEEP_SLEEP and SFE_ENS160_IDLE
	myENS.setOperatingMode(SFE_ENS160_STANDARD);

	// There are four values here: 
	// 0 - Operating ok: Standard Operation
	// 1 - Warm-up: occurs for 3 minutes after power-on.
	// 2 - Initial Start-up: Occurs for the first hour of operation.
  // and only once in sensor's lifetime.
	// 3 - No Valid Output
  Serial.println("Waiting for the device to warm up, this will take ~3 minutes.");
	Serial.println("Gas Sensor Status Flag (0 - Standard, 1 - Warm up, 2 - Initial Start Up): ");
	while(true)
  {
    ensStatus = myENS.getFlags();

    if((ensStatus == 2) | (ensStatus == 0))
    {
      Serial.println("ENS160 is warmed up!");
      break;
    }
    
    if(ensStatus == 3)
    {
      Serial.println("Invalid Ouput...Freezing.");
      while(1);
    }

    Serial.print(".");
    delay(250);
  }

  Serial.println();
    
}

void loop() {

	server.handleClient();

}

void handle_OnConnect() {

  timeClient.update();

  String formattedTime = timeClient.getFormattedTime();

  time_t epochTime = timeClient.getEpochTime();
  int currentHour = timeClient.getHours();
  int currentMinute = timeClient.getMinutes();
  int currentSecond = timeClient.getSeconds();
  String weekDay = weekDays[timeClient.getDay()];

  //Get a time structure
  struct tm *ptm = gmtime ((time_t *)&epochTime);

  int monthDay = ptm->tm_mday;
  int currentMonth = ptm->tm_mon+1;
  String currentMonthName = months[currentMonth-1];
  int currentYear = ptm->tm_year+1900;
 
  formattedTime = timeClient.getFormattedTime(); 
  
  Date = String(currentMonth) + "-" + String(monthDay) + "-" + String(currentYear);

  //get BME688 data and send it to ENS prior to getting measurements
  bme68xData data;

  bme.setOpMode(BME68X_FORCED_MODE);
  delayMicroseconds(bme.getMeasDur());
  
  if (bme.fetchData())
  {
    bme.getData(data);
  }
  
  rh = (data.humidity);
  tempC = (data.temperature);
  tempF = (((data.temperature)*1.8)+32);
  gasR = (data.gas_resistance);
  press = (data.pressure);
  pressMillis = ((data.pressure)/100);

  Serial.print("Relative Humidity (%): ");
  Serial.println(rh);
  Serial.print("Temperature (Celsius): ");
  Serial.println(tempC);

  delay(100);

  // Give values to Air Quality Sensor.
  myENS.setTempCompensationCelsius(tempC);
  myENS.setRHCompensationFloat(rh);

  delay(500);

  if (myENS.checkDataStatus()) {

    if (printedCompensation == false) {
      printedCompensation = true;
      delay(500);
    }
  }

  int AQI = myENS.getAQI();
  int VOCs = myENS.getTVOC();
  int CO2 = myENS.getECO2();

  server.send(200, "text/html", SendHTML(AQI,VOCs,CO2,rh,tempC,tempF,gasR,press,pressMillis,formattedTime,Date)); 
}

void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}

String SendHTML(int AQI, int VOCs, int CO2, float rh, float tempC, float tempF, float gasR, float press, float pressMillis, String TimeWeb, String DateWeb){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>Garage Environmental Monitoring</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
  ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr += "<script>\n";
  ptr += "setInterval(loadDoc,1000);\n";
  ptr += "function loadDoc() {\n";
  ptr += "var xhttp = new XMLHttpRequest();\n";
  ptr += "xhttp.onreadystatechange = function() {\n";
  ptr += "if (this.readyState == 4 && this.status == 200) {\n";
  ptr += "document.body.innerHTML =this.responseText}\n";
  ptr += "};\n";
  ptr += "xhttp.open(\"GET\", \"/\", true);\n";
  ptr += "xhttp.send();\n";
  ptr += "}\n";
  ptr += "</script>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<div id=\"webpage\">\n";
  ptr +="<h1>Garage ESP32 Air Quality</h1>\n";
  ptr +="<h2>Monitoring System</h2>\n";
  ptr +="<p>Air Quality(1-5): ";
  ptr +=AQI;
  ptr +="</p>";
  ptr +="<p>Total Volatile Organic Compounds: ";
  ptr +=VOCs;
  ptr +=" ppb</p>";
  ptr +="<p>CO2 Concentration: ";
  ptr +=CO2;
  ptr +=" ppm</p>";
  ptr +="<p>Humidity: ";
  ptr +=rh;
  ptr +=" %</p>";
  ptr +="<p>Temp (Celcius): ";
  ptr +=tempC;
  ptr +=" &deg;C</p>";
  ptr +="<p>Temp (Fahrenheit): ";
  ptr +=tempF;
  ptr +=" &deg;F</p>";
  ptr +="<p>Pressure: ";
  ptr +=press;
  ptr +=" Pa</p>";
  ptr +="<p>Pressure: ";
  ptr +=pressMillis;
  ptr +=" mb</p>";
  ptr +="<p>Gas Resistance: ";
  ptr +=gasR;
  ptr +="</p>";
  ptr +="<p>Time: ";
  ptr +=(String)TimeWeb;
  ptr +="</p>";
  ptr +="<p>Date: ";
  ptr +=(String)DateWeb;
  ptr +="</p>";
  ptr +="</div>\n";
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

Here is serial output during reboot events I mentioned:

12:36:44.119 -> Ready.
12:36:44.213 -> Waiting for the device to warm up, this will take ~3 minutes.
12:36:44.259 -> Gas Sensor Status Flag (0 - Standard, 1 - Warm up, 2 - Initial Start Up): 
12:36:44.259 -> ENS160 is warmed up!
12:36:44.259 -> 
12:37:04.108 -> Relative Humidity (%): 1.96
12:37:04.108 -> Temperature (Celsius): ovf
12:37:05.188 -> 
12:37:05.188 -> Stack smashing protect failure!
12:37:05.188 -> 
12:37:05.188 -> 
12:37:05.188 -> Backtrace: 0x40082825:0x3ffb1f20 0x4008c5b5:0x3ffb1f40 0x40082836:0x3ffb1f60 0x400ddbd9:0x3ffb1f80 0x400d2d4d:0x3ffb1fc0 0x400d31fa:0x3ffb2010 0x0034352b:0x3ffb2110 |<-CORRUPTED
12:37:05.188 -> 
12:37:05.188 -> 

Stack Smashing means you have a bug in your code that is writing to memory that doesn't logically belong to that function. Try adding debug prints showing the stack variables, I don't remember them so just google it.

Anyone know how I would do the above-mentioned advice from @sonofcy? I know how to use Serial to print. What exactly do I need to do serial debug prints?

Serial debug just means using Serial.print(ln) to print out certain information at various places in the sketch. Have a look at this library and the example sketch Stack
Screenshot 2024-09-15 at 10.41.57

Also study this section of the documentation https://docs.arduino.cc/learn/programming/memory-guide/#measuring-memory-usage-in-arduino-boards

Thanks for the help. Will look into this and let you know what I find. :slight_smile:

I have a hunch. I see a lot of String use, try to reduce/eliminate that. I don't know your code well enough to know, but maybe you do. Look at the function/procedures that are executed the most often and eliminate those Strings first.

I'm thinking it's something with the way I'm using BME688. If I take it out, the Web server runs fine with just the ENS160. Thanks again.

I doubt it, but good luck with that.

1 Like

I made these changes and it's running normally now:

#include <Arduino.h>
#include <Wire.h>
#include "SparkFun_ENS160.h"
#include <WiFi.h>
#include <WebServer.h>
#include <NTPClient.h>
#include <Time.h>
#include <Secrets.h>
#include <WiFiUdp.h>
#include <bme68xLibrary.h>


//define WiFi Network name and password with secrets.h
const char* ssid = SECRET_SSID;
const char* password = SECRET_PASS;

//Int and Float values to be used for Webserver later
int AQI, VOCs, CO2;
String formattedTime;
String Date;
int Day;
int Month;
int Year;

//declare Webserver and ntpUPD
WebServer server(80);

WiFiUDP ntpUDP;

//NTP Client and initializing time
int gtmOffset = -5;
NTPClient timeClient(ntpUDP, "pool.ntp.org", gtmOffset*60*60, 60000);

//Week Days
String weekDays[7]={"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

//Month names
String months[12]={"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};

//declare ENS160 sensor and entStatus, as well as float relative humidity and temp in Celcius values for later use
SparkFun_ENS160 myENS; 

bool printedCompensation = false;
int ensStatus; 

float rh;
float tempC;
float tempF;
float gasR;
float press;
float pressMillis;

//declare and initalize BME688

#define I2C_ADDR 0x76

Bme68x bme;

void setup()
{
	Wire.begin();

	Serial.begin(115200);

	Serial.println("Connecting to ");
  Serial.println(ssid);

  //connect to your local wi-fi network
  WiFi.begin(ssid, password);

  //check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  Serial.println(WiFi.localIP());

  server.on("/", handle_OnConnect);
  server.onNotFound(handle_NotFound);

  server.begin();
  Serial.println("HTTP server started");

  timeClient.begin();

    // Initialize I2C operation
  bme.begin(I2C_ADDR, Wire);

  if(bme.checkStatus())
  {
    if (bme.checkStatus() == BME68X_ERROR)
    {
      Serial.println("Sensor error:" + bme.statusString());
      return;
    }
    else if (bme.checkStatus() == BME68X_WARNING)
    {
      Serial.println("Sensor Warning:" + bme.statusString());
    }
  }
  Serial.println("BME688 is initialized successfully ...");
  
  /* Set the default configuration for temperature, pressure and humidity */
  bme.setTPH();

  /* Set the heater configuration to 320 deg C for 150ms for Forced mode */
  bme.setHeaterProf(320, 150);

	//initialize ENS sensor
	if( !myENS.begin() )
	{
		Serial.println("Could not communicate with the ENS160, check wiring.");
		while(1);
	}

  Serial.println("The ENS160 sensor has been initialized...");

	// Reset the indoor air quality sensor's settings.
	if( myENS.setOperatingMode(SFE_ENS160_RESET) )
		Serial.println("Sensor reset completed. Ready.");

	delay(100);

	// Device needs to be set to idle to apply any settings.
	// myENS.setOperatingMode(SFE_ENS160_IDLE);

	// Set to standard operation
	// Others include SFE_ENS160_DEEP_SLEEP and SFE_ENS160_IDLE
	myENS.setOperatingMode(SFE_ENS160_STANDARD);
  Serial.println("ENS160 operation mode is set to standard...");
    
}

void loop() {

	server.handleClient();

}

void handle_OnConnect() {

  timeClient.update();

  String formattedTime = timeClient.getFormattedTime();

  time_t epochTime = timeClient.getEpochTime();
  int currentHour = timeClient.getHours();
  int currentMinute = timeClient.getMinutes();
  int currentSecond = timeClient.getSeconds();
  String weekDay = weekDays[timeClient.getDay()];

  //Get a time structure
  struct tm *ptm = gmtime ((time_t *)&epochTime);

  int monthDay = ptm->tm_mday;
  int currentMonth = ptm->tm_mon+1;
  String currentMonthName = months[currentMonth-1];
  int currentYear = ptm->tm_year+1900;
 
  formattedTime = timeClient.getFormattedTime(); 
  
  Date = String(currentMonth) + "-" + String(monthDay) + "-" + String(currentYear);

  //get BME688 data and send it to ENS prior to getting measurements
  bme68xData data;

  bme.setOpMode(BME68X_FORCED_MODE);
  delayMicroseconds(bme.getMeasDur());
  Serial.println("BME688 operation mode is set to FORCED...");
  
  if (bme.fetchData())
    {
     bme.getData(data);
     Serial.println("Timestamp: " + String(millis()) + ", ");
     Serial.print("Temperature: " + String(data.temperature) + ", ");
     Serial.print("Pressure: " + String(data.pressure) + ", ");
      Serial.println("Humidity: " + String(data.humidity) + ", ");
      Serial.print("Gas: " + String(data.gas_resistance) + ", ");
      Serial.print("Status: ");
      Serial.println(data.status, HEX);
      Serial.println(); // New line
      Serial.println("Fetched BME688 readings...");
    }

  rh = (data.humidity);
  tempC = (data.temperature);
  tempF = (((data.temperature)*1.8)+32);
  gasR = (data.gas_resistance);
  press = (data.pressure);
  pressMillis = ((data.pressure)/100);

  delay(100);

  // Give values to Air Quality Sensor.
  myENS.setTempCompensationCelsius(tempC);
  myENS.setRHCompensationFloat(rh);

  if (myENS.checkDataStatus()) {

    if (printedCompensation == false) {
      printedCompensation = true;
      delay(500);
    }
  }

  int AQI = myENS.getAQI();
  int VOCs = myENS.getTVOC();
  int CO2 = myENS.getECO2();

  server.send(200, "text/html", SendHTML(AQI,VOCs,CO2,rh,tempC,tempF,gasR,press,pressMillis,formattedTime,Date)); 
}

void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}

String SendHTML(int AQI, int VOCs, int CO2, float rh, float tempC, float tempF, float gasR, float press, float pressMillis, String TimeWeb, String DateWeb){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>Garage Environmental Monitoring</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
  ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr += "<script>\n";
  ptr += "setInterval(loadDoc,1000);\n";
  ptr += "function loadDoc() {\n";
  ptr += "var xhttp = new XMLHttpRequest();\n";
  ptr += "xhttp.onreadystatechange = function() {\n";
  ptr += "if (this.readyState == 4 && this.status == 200) {\n";
  ptr += "document.body.innerHTML =this.responseText}\n";
  ptr += "};\n";
  ptr += "xhttp.open(\"GET\", \"/\", true);\n";
  ptr += "xhttp.send();\n";
  ptr += "}\n";
  ptr += "</script>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<div id=\"webpage\">\n";
  ptr +="<h1>Garage ESP32 Air Quality</h1>\n";
  ptr +="<h2>Monitoring System</h2>\n";
  ptr +="<p>Air Quality(1-5): ";
  ptr +=AQI;
  ptr +="</p>";
  ptr +="<p>Total Volatile Organic Compounds: ";
  ptr +=VOCs;
  ptr +=" ppb</p>";
  ptr +="<p>CO2 Concentration: ";
  ptr +=CO2;
  ptr +=" ppm</p>";
  ptr +="<p>Humidity: ";
  ptr +=rh;
  ptr +=" %</p>";
  ptr +="<p>Temp (Celcius): ";
  ptr +=tempC;
  ptr +=" &deg;C</p>";
  ptr +="<p>Temp (Fahrenheit): ";
  ptr +=tempF;
  ptr +=" &deg;F</p>";
  ptr +="<p>Pressure: ";
  ptr +=press;
  ptr +=" Pa</p>";
  ptr +="<p>Pressure: ";
  ptr +=pressMillis;
  ptr +=" mb</p>";
  ptr +="<p>Gas Resistance: ";
  ptr +=gasR;
  ptr +="</p>";
  ptr +="<p>Time: ";
  ptr +=(String)TimeWeb;
  ptr +="</p>";
  ptr +="<p>Date: ";
  ptr +=(String)DateWeb;
  ptr +="</p>";
  ptr +="</div>\n";
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

All the bme code is still there, do a find bme with both the case and whole word ticked. Now comment out every line with bme in it and you have got rid of it. Have a look



I just noticed you are using a 3rd party library (notice the name inconsistency) instead of the official Arduino Library.
I wish you the best, but I will now mute this thread.

Understood. I appreciate your help regardless. Not sure why, but it is working fine now after multiple restarts of the LOLIN so I am happy with it.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.