Two I2c devices on Heltec32 V3 problem

Hi. I have a problem with my project. I want to connect SCD30(temperature,humidity and co2 sensor) and BMP280(pressure and temperature sensor) to Heltec wifi lora 32 (V3.1). But it seems, two sensors can not work together even though they have different addresses (0x61 and 0x76). I tried to test them individually - they work fine. The I2c pins for Heltec32 Lora (V3.1) are 41-SDA and 42-SCL. I also tried to connect BMP280 and some accelerometer sensor together to Heltec - it worked. But with SCD30 it does not. What do you think is the issue? And how can I solve this? Thanks!
Here is the link for the code: https://github.com/Mansur04kz/Heltec32-wifi-lora-V3-with-SCD30-and-BMP280-sensors..git
Connection: https://app.cirkitdesigner.com/project/b9935865-f34b-4c33-aefd-0eef6f9453ef

I suggest connecting the pins of SDA,SCL to these pins GPIO(19, 20, 21, 26, 33, 34, 45, 47, 48).

That answer doesn’t make much sense.

Any two of the list?

No, this is incorrect. The i2C pins for Heltec 32 Lora V3 are 41 and 42. There are also other I2c Pins but they are for integrated oled and they are not soldered. Still, this question is open.

Ashley (Heltec staff member) is correct.

There are no I2C pins indicated on the pinout diagrams. The default pins are set to SDA = 41 and SCL = 42 in the Board Support Package but the ESP32 can use any available pins, you just have to tell it which. But there’s a wrinkle. The OLED is defined to the first bus, Wire.xxx, to use any other pins needs you to use Wire1.xxxx. It’s all in the docs and the I2C scanner example.

There is a possibility that your 60 second uplink interval will breach usage limits.

I also had a problem with the I2C bus in connection with the Heltec LoRa32 V3.1. The solution was: I used Wire2.

#include <Wire.h>

#define PIN_SDA 41
#define PIN_SCL 42

TwoWire Wire2(1);

Adafruit_BME680 bme(&Wire2); // I2C

void initializeBME688() {

if (!bme.begin(0x77, &Wire2)) {

Serial.println(“Could not find a valid BME688 sensor, check wiring, address, sensor ID!”);

display.clear();

display.drawString(0, 0, “BME688 not found!”);

display.display();

while (1) delay(10);

}

void setup() {

Wire2.begin(41, 42);
}

Here is your corrected code:

  • Added proper sensor data structure to store readings
  • Created dedicated initialization function for both sensors
  • Improved error handling and sensor status checking
  • Implemented proper timing management for both sensors
  • Added data validity checking before transmission
  • Improved debug output formatting

#include <Wire.h>
#include “LoRaWan_APP.h”
#include “Adafruit_SCD30.h”
#include <BMP280_DEV.h>

#define ACTIVE_REGION LORAMAC_REGION_EU868

Adafruit_SCD30 scd30;
BMP280_DEV bmp280;

uint8_t devEui[] = { ******* }; // was hidden
uint8_t appEui[] = { ****** }; // was hidden
uint8_t appKey[] = { ******* }; // was hidden

uint16_t userChannelsMask[6] = { 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 };
bool isTxConfirmed = false;
uint8_t confirmedNbTrials = 4;
bool overTheAirActivation = true;
DeviceClass_t loraWanClass = CLASS_A;
bool loraWanAdr = true;
uint8_t nwkSKey[] = { 0x15, 0xb1, 0xd0, 0xef, 0xa4, 0x63, 0xdf, 0xbe, 0x3d, 0x11, 0x18, 0x1e, 0x1e, 0xc7, 0xda, 0x85 };
uint8_t appSKey[] = { 0xd7, 0x2c, 0x78, 0x75, 0x8c, 0xdc, 0xca, 0xbf, 0x55, 0xee, 0x4a, 0x77, 0x8d, 0x16, 0xef, 0x67 };
uint32_t devAddr = (uint32_t)0x007e6ae1;

uint32_t appTxDutyCycle = 60000; // 60 seconds
uint8_t appPort = 2;

// Timing variables
unsigned long previousSensorRead = 0;
const unsigned long SENSOR_READ_INTERVAL = 2000; // 2 seconds
bool sensorDataReady = false;

// Sensor data storage
struct {
float co2;
float temperature;
float humidity;
float pressure;
bool isValid;
} sensorData;

void initializeSensors() {
// Initialize SCD30 with more specific settings
if (!scd30.begin()) {
Serial.println(“Error: SCD30 not found!”);
while (1) delay(1000);
}
scd30.setMeasurementInterval(2); // Set to 2 seconds to match our interval

// Initialize BMP280 with more specific settings
if (!bmp280.begin(0x76)) {
    Serial.println("Error: BMP280 not found!");
    while (1) delay(1000);
}
bmp280.setTimeStandby(TIME_STANDBY_2000MS);
bmp280.startNormalConversion();

Serial.println("Both sensors initialized successfully");

}

bool readSensors() {
sensorData.isValid = false;
float temp1, altitude; // temporary variables for BMP280

// First check if SCD30 has data ready
if (scd30.dataReady()) {
    if (!scd30.read()) {
        Serial.println("Error reading from SCD30");
        return false;
    }
    
    // Now read BMP280
    if (!bmp280.getMeasurements(temp1, sensorData.pressure, altitude)) {
        Serial.println("Error reading from BMP280");
        return false;
    }
    
    // If we got here, both sensors read successfully
    sensorData.co2 = scd30.CO2;
    sensorData.temperature = scd30.temperature;
    sensorData.humidity = scd30.relative_humidity;
    sensorData.isValid = true;
    
    return true;
}
return false;

}

static void prepareTxFrame(uint8_t port) {
if (!sensorData.isValid) {
Serial.println(“No valid sensor data available for transmission”);
return;
}

// Convert floating point values to fixed point for transmission
uint16_t co2_fixed = (uint16_t)(sensorData.co2);
uint16_t temp_fixed = (uint16_t)(sensorData.temperature * 10);
uint16_t hum_fixed = (uint16_t)(sensorData.humidity * 10);
uint16_t pres_fixed = (uint16_t)(sensorData.pressure / 10);

// Prepare LoRa payload (10 bytes total)
appDataSize = 10;

// Pack the data
appData[0] = (co2_fixed >> 8) & 0xFF;
appData[1] = co2_fixed & 0xFF;
appData[2] = (temp_fixed >> 8) & 0xFF;
appData[3] = temp_fixed & 0xFF;
appData[4] = (hum_fixed >> 8) & 0xFF;
appData[5] = hum_fixed & 0xFF;
appData[6] = (pres_fixed >> 8) & 0xFF;
appData[7] = pres_fixed & 0xFF;
appData[8] = 0; // Reserved for future use
appData[9] = 0; // Reserved for future use

}

void setup() {
Serial.begin(115200);
Wire.begin(41, 42);

// Initialize sensors
initializeSensors();

// Initialize LoRaWAN
Mcu.begin(HELTEC_BOARD, SLOW_CLK_TPYE);
LoRaWAN.init(loraWanClass, ACTIVE_REGION);

deviceState = DEVICE_STATE_INIT;
Serial.println("Setup completed");

}

void loop() {
unsigned long currentMillis = millis();

// Regular sensor reading
if (currentMillis - previousSensorRead >= SENSOR_READ_INTERVAL) {
    previousSensorRead = currentMillis;
    
    if (readSensors()) {
        // Print data for debugging
        Serial.println("\n--- Sensor Readings ---");
        Serial.printf("CO2: %.1f ppm\n", sensorData.co2);
        Serial.printf("Temperature: %.2f °C\n", sensorData.temperature);
        Serial.printf("Humidity: %.1f %%\n", sensorData.humidity);
        Serial.printf("Pressure: %.1f hPa\n", sensorData.pressure);
    }
}

// Handle LoRaWAN state machine
switch (deviceState) {
    case DEVICE_STATE_INIT:
        LoRaWAN.setDefaultDR(3);
        deviceState = DEVICE_STATE_JOIN;
        break;
        
    case DEVICE_STATE_JOIN:
        LoRaWAN.join();
        break;
        
    case DEVICE_STATE_SEND:
        if (sensorData.isValid) {
            prepareTxFrame(appPort);
            LoRaWAN.send();
        }
        deviceState = DEVICE_STATE_CYCLE;
        break;
        
    case DEVICE_STATE_CYCLE:
        txDutyCycleTime = appTxDutyCycle + randr(-APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND);
        LoRaWAN.cycle(txDutyCycleTime);
        deviceState = DEVICE_STATE_SLEEP;
        break;
        
    case DEVICE_STATE_SLEEP:
        LoRaWAN.sleep(CLASS_A);
        break;
        
    default:
        deviceState = DEVICE_STATE_INIT;
        break;
}

}

Except for this - this has LEGAL implications.

How do you figure? It was written by ai - not proprietary.

How do I figure that you didn’t correct it all?

Well there’s little chance I’m going to know that you used AI - and in not saying so you are passing off others work AND if the OP has issues, no point coming back to you to get fixes.

Not going to waste time testing it, but as it’s not using Wire1, its effectively blocking the display by hijacking the display’s connections.

As to the timing, that’s illegal for some spreading factors and for some networks breaches the terms of use.

Which is why AI is mostly useless for actual expertise, knowledge and putting in the hours.

Seems AI can’t format posts either :wink:

I missformatted the post.

I HAVE put in the time and can code (sans AI) in multiple languages very proficiently (I would gladly go toe to toe in a competition if you’d like).

I tested the code before I provided it and it worked just fine.

In the future, rather than trying to start an argument, you could have just said, “Unfortunately the timing could cause some issues…etc”…I really don’t understand why people feel the need to be jerks (when they are not standing in front of you)…Someone should take your computer away until you learn how to play nice.

These pins are all ones I have tried and can be used correctly as SDA and SCL pins. If you really try every pin, you will find that many pins have built-in functions and cannot be used as output pins.