ENS160 stops working after LoRa module is power-cycled

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:

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);
}

You should never change wiring while a system is powered.

Nor should you ever connect unpowered modules to powered modules via I/O pins. That can lead to the destruction or malfunction of either module.

On a LORA power cycle, the ESP32 must also be reset so you can run the startup LORA code.

Can you simply cycle the power on everything at the same time. Different things at different times can cause latch up in some ICs. Posting an annotated schematic will help us help you. Be sure to show all connections, power, ground, and power sources.

I will take a SWAG and say the I2C bus is what is locking your system.

LoRa is connected with a common ground to the BME280. The 3.3V line from the sensor is powered by the 3.3V output from the LoRa module. SDA is connected to pin 15, and SCL to pin 13 https://www.tinytronics.nl/image/cache/catalog/products_2023/lilygo-ttgo-t3-lora32-868mhz-v1.6.1-esp32-600x600w.jpg. The BME280 and ENS160 sensors are connected in a daisy-chain configuration.

are you sure than the power supply of the Lilygo ESP32 module has sufficient output current to support your sensors, LoRa module, OLED, etc
try an external power supply?

Yes, I checked with a multimeter, and each of the two modules receives around 3.2V, but there seem to be some fluctuations. I also noticed that the indicator LEDs on the sensors are losing light intensitiy which might suggest an unstable power supply.

see post #4

Obvious question really, why are you power cycling the LoRa module ?