Hi everyone,
I'm having an issue with the ENS160 air quality sensor PiicoDev Air Quality Sensor ENS160 | Buy in Australia | CE08560 | Core Electronics in a project using ESP32. The sensor works fine on startup and successfully returns values for eCO2, TVOC, and AQI. However, there's a strange behavior when the LoRa module is power-cycled https://www.tinytronics.nl/nl/development-boards/microcontroller-boards/met-lora/lilygo-ttgo-t3-lora32-433mhz-v1.6.1-esp32 .
Here's the setup:
- ENS160 (I2C address 0x53)
- BME280 (I2C address 0x77) PiicoDev Atmospheric Sensor BME280 | Buy in Australia | CE07503 | Core Electronics
LoRa module (SX1276)
ESP32 with two I2C buses:
OLED on Wire (GPIO 21/22)- ENS160 and BME280 on
Wire1(GPIO 15 for SDA, GPIO 13 for SCL, running at 10kHz)
- ENS160 and BME280 on
Everything initializes correctly on the first boot, and ENS160 returns valid readings.
The problem:
When I unplug only the power (VCC) of the LoRa module and plug it back in, the system reboots (or I re-upload the code), but the ENS160 stops providing valid readings. It gets stuck in ens160.update() or returns only zero values for eCO2/TVOC/AQI. The BME280 still works fine.
This is the code I'm currently using:
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <LoRa.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>
#include <ScioSense_ENS16x.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
#define SCK 5
#define MISO 19
#define MOSI 27
#define SS 18
#define RST 14
#define DI0 26
#define SDA_SENSORS 15
#define SCL_SENSORS 13
#define BAND 868E6
#define ENS160_ADDR 0x53
#define BME280_ADDR 0x77
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_BME280 bme;
ENS160 ens160;
TwoWire I2CSensors = TwoWire(1);
void drawBorder() {
display.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
}
void scanI2C() {
byte error, address;
int nDevices = 0;
Serial.println("Scanning I2C bus...");
for(address = 1; address < 127; address++) {
I2CSensors.beginTransmission(address);
error = I2CSensors.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address < 16) {
Serial.print("0");
}
Serial.println(address, HEX);
nDevices++;
}
}
if (nDevices == 0) {
Serial.println("No I2C devices found");
} else {
Serial.println("I2C scan done");
}
}
void displayData(float temperature, float humidity, float pressure, uint16_t eco2, uint16_t tvoc, uint8_t aqi) {
display.clearDisplay();
display.setCursor(2, 2);
display.print(F("T:"));
display.print(temperature, 1);
display.println(F("C"));
display.setCursor(2, 12);
display.print(F("H:"));
display.print(humidity, 1);
display.println(F("%"));
display.setCursor(2, 22);
display.print(F("P:"));
display.print(pressure, 1);
display.println(F("hPa"));
display.setCursor(2, 32);
display.print(F("TVOC:"));
display.print(tvoc);
display.println(F(" ppb")); // Modified this line
display.setCursor(2, 42);
display.print(F("eCO2:"));
display.print(eco2);
display.println(F(" ppm")); // Modified this line
display.setCursor(2, 52);
display.print(F("AQI:"));
display.println(aqi);
display.display();
}
void setup() {
Serial.begin(115200);
delay(2000);
Serial.println("\nStarting...");
// Initialize OLED
Wire.begin(21, 22);
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
while (1);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// Initialize sensors I2C bus
I2CSensors.begin(SDA_SENSORS, SCL_SENSORS, 10000);
delay(1000);
// Scan I2C bus
Serial.println("Scanning I2C bus for devices...");
scanI2C();
// Initialize BME280 first
display.clearDisplay();
display.setCursor(15, 25);
display.println(F("Starting BME280..."));
display.display();
bool status = bme.begin(BME280_ADDR, &I2CSensors);
if (!status) {
status = bme.begin(0x76, &I2CSensors);
}
if (!status) {
Serial.println("BME280 initialization failed!");
display.clearDisplay();
display.setCursor(15, 25);
display.println(F("BME280 failed!"));
display.display();
delay(2000);
} else {
Serial.println("BME280 initialization successful!");
display.clearDisplay();
display.setCursor(15, 25);
display.println(F("BME280 OK!"));
display.display();
delay(1000);
}
// Initialize ENS160 second
display.clearDisplay();
display.setCursor(15, 25);
display.println(F("Starting ENS160..."));
display.display();
Serial.println("Trying to initialize ENS160...");
ens160.enableDebugging(Serial);
ens160.begin(&I2CSensors, ENS160_ADDR);
delay(500);
Serial.println("ENS160 begin done, trying init...");
int initAttempts = 0;
bool initSuccess = false;
while (initAttempts < 5) {
if (ens160.init()) {
initSuccess = true;
break;
}
Serial.print("Init attempt ");
Serial.print(initAttempts + 1);
Serial.println(" failed");
delay(1000);
initAttempts++;
}
if (!initSuccess) {
Serial.println("Trying alternate address...");
ens160.begin(&I2CSensors, 0x52);
delay(500);
initAttempts = 0;
while (initAttempts < 5) {
if (ens160.init()) {
initSuccess = true;
break;
}
Serial.print("Init attempt with alternate address ");
Serial.print(initAttempts + 1);
Serial.println(" failed");
delay(1000);
initAttempts++;
}
}
if (!initSuccess) {
Serial.println("ENS160 initialization failed after all attempts");
display.clearDisplay();
display.setCursor(15, 25);
display.println(F("ENS160 init failed"));
display.display();
delay(2000);
} else {
Serial.println("ENS160 initialization successful!");
display.clearDisplay();
display.setCursor(15, 25);
display.println(F("ENS160 OK!"));
display.display();
delay(1000);
}
Serial.println("Starting standard measure...");
ens160.startStandardMeasure();
delay(1000);
// Initialize LoRa
SPI.begin(SCK, MISO, MOSI, SS);
LoRa.setPins(SS, RST, DI0);
if (!LoRa.begin(BAND)) {
Serial.println("LoRa initialization failed!");
display.clearDisplay();
display.setCursor(15, 25);
display.println(F("LoRa Failed!"));
display.display();
while (1);
}
display.clearDisplay();
display.setCursor(15, 25);
display.println(F("System Ready!"));
display.display();
delay(2000);
}
void loop() {
float temperature = bme.readTemperature();
float humidity = bme.readHumidity();
float pressure = bme.readPressure() / 100.0F;
uint16_t eco2 = 0, tvoc = 0;
uint8_t aqi = 0;
// Add debug information for sensor status
Serial.println("\nChecking ENS160...");
ens160.wait();
if (ens160.update() == RESULT_OK) {
Serial.println("Update OK");
if (ens160.hasNewData()) {
eco2 = ens160.getEco2();
tvoc = ens160.getTvoc();
aqi = ens160.getAirQualityIndex_UBA();
// Add more detailed debug information
Serial.println("New data available:");
Serial.print("ECO2: "); Serial.print(eco2); Serial.println(" ppm");
Serial.print("TVOC: "); Serial.print(tvoc); Serial.println(" ppb");
Serial.print("AQI: "); Serial.println(aqi);
// Validate readings
if (eco2 == 0 && tvoc == 0 && aqi == 0) {
Serial.println("Warning: All values are 0, possible sensor issue");
// Try to reset the sensor
Serial.println("Attempting sensor reset...");
ens160.begin(&I2CSensors, ENS160_ADDR);
delay(100);
if (ens160.init()) {
Serial.println("Sensor reset successful");
ens160.startStandardMeasure();
delay(1000);
} else {
Serial.println("Sensor reset failed");
}
}
} else {
Serial.println("No new ENS160 data available");
}
} else {
Serial.println("ENS160 update failed");
// Try to recover
Serial.println("Attempting recovery...");
ens160.begin(&I2CSensors, ENS160_ADDR);
delay(100);
if (ens160.init()) {
Serial.println("Recovery successful");
ens160.startStandardMeasure();
delay(1000);
} else {
Serial.println("Recovery failed");
}
}
// Display data
displayData(temperature, humidity, pressure, eco2, tvoc, aqi);
// Send LoRa message
String message = String(temperature, 1) + "C," +
String(humidity, 1) + "%," +
String(pressure, 1) + "hPa," +
String(eco2) + "ppm," +
String(tvoc) + "ppb," +
String(aqi);
LoRa.beginPacket();
LoRa.print(message);
LoRa.endPacket();
Serial.println("Sent message: " + message);
delay(5000);
}
