Integrate WiFi LoRA V2 with SCD41 CO2 sensor

I connect the WiFi LoRa32 V2 to SCD41 CO2 sensor from Sensirion via I2C bus. (https://github.com/Sensirion/arduino-i2c-scd4x)

I can display the CO2, temp and humidity sensor reading via COM port on PC. However, when I tried to display the reading on local OLED (SSD1306) display, there seems to have some conflict. Once I initialize the OLED and display some text, I can no longer communicate with the SCD41 via I2C. I always got communication error message. What is the I2C address for the OLED of WiFi LoRa32? Any suggestions to resolve the problem?

The IIC address of the display is 0x3C.

Thanks. (BTW, I search online and found the SSD1306 OLED driver is -write address is 0x78
-read address is 0x79 for ESP32. https://embeddeddiaries.com/esp32-oled-sd1306-i2c-interface/, not sure who is right?)

The I2C address of SCD41 is 0x62. So the problem is probably not due to address conflict.

Any suggestion how to resolve this problem?

You can use this example to view the IIC address of the display:

What are the pins used by your sensor?

Hi Shaffer:

Thank you very much for the utility code. It is very helpful.
I modified the code to use wire1.xxxx functions to communicate with sensor and wire.xxxx functions to communicate with OLED display, everything works now. Thank you very much for your help!!

Hi Peter,

I’m interested in setting up a similar system. Could you please share your code?

Thanks!

Here is the code:

// SCD41_CO2_V201
// Add local OLED display of sensor readdings & battery voltage
// Add LED lights to indicate reading period
// SDA(GPIO21) & SCL(GPIO#22) are for I2C to SCD41
// GPIO#21 is set to (LOW)initially to connect Vbat to GPIO#37 (ADC1_1)
// After Vbat is measured in the setup, GPIO#21 is reconfigured for I2C to connect to SCD41
//
// SDA_OLED (GPIO#04) & SCL(GPIO#15)are for I2C to OLED display
// use wire1.xxxx functions to communicate with SCD41
// and wire.xxxx function to communicate with Oled SSD1306
//
// SCD41_CO2_v200
// Example8_SCD4x_BLE_gadget_with_RHT.ino
// This code is included in Sensirion’s Arduino Snippets
// It worked right away with WiFi LoRa32 V2 board using iOS apps Myambience
// Check https://github.com/Sensirion/arduino-snippets for the most recent version.

#include “esp_timer.h”
#include “Sensirion_GadgetBle_Lib.h”
#include “heltec.h”
#include <Wire.h>

#define Fbattery 3700 //The default battery is 3700mv when the battery is fully charged.

//float XS = 0.00225; //The returned reading is multiplied by this XS to get the battery voltage.
float XS = 0.00225*1.354; // multiply by a factor of 1.354 to factor in the load from I2C bus SDA line
uint16_t MUL = 1000;
uint16_t MMUL = 100;
uint16_t c;

// SCD41
const int16_t SCD_ADDRESS = 0x62;

// GadgetBle workflow
static int64_t lastMmntTime = 0;
static int mmntIntervalUs = 5000000;
GadgetBle gadgetBle = GadgetBle(GadgetBle::DataType::T_RH_CO2);

void setup() {
// sdetup to check battery voltage
// Heltec.begin(true /DisplayEnable Enable/, false /LoRa Enable/, true /Serial Enable/);

Serial.begin(115200);
// wait for serial connection from PC
// comment the following line if you’d like the output
// without waiting for the interface being ready
while(!Serial);

Serial.print("SCD41_CO2_V201 device ID = ");
// Initialize the GadgetBle Library
gadgetBle.begin();
Serial.println(gadgetBle.getDeviceIdString());
Serial.print("Vbat = ");
// setup & measure Vbat
pinMode(21,OUTPUT); // setup GPIO#21 to tuern on connection from Vbat to ADC1_1 (pin#37)
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(21,LOW);
adcAttachPin(13);
analogSetClockDiv(255); // 1338mS
delay(1000);
c = analogRead(37)XSMUL;
// Serial.println(analogRead(37));
Serial.println©;

// init I2C
// Heltec.begin(true, false, true);
Wire.begin(SDA_OLED, SCL_OLED); //Scan OLED’s I2C address via I2C0
Wire1.begin(SDA, SCL); //If there have other device on I2C1, scan the device address via I2C1
// Wire.begin();

// wait until sensors are ready, > 1000 ms according to datasheet
delay(1000);

// initialize OLED display
Heltec.display->init();
Heltec.display->flipScreenVertically();
Heltec.display->setFont(ArialMT_Plain_16);
Heltec.display->clear();

Heltec.display->drawString(8, 0, "SCD41-V201");
Heltec.display->drawString(12, 45, "Renata LLC");
Heltec.display->display();

delay(3000);
Heltec.display->clear(); 
Heltec.display->drawString(0, 10, "Vbat=");
Heltec.display->drawString(50, 10, (String)c);
Heltec.display->drawString(90, 10, "mV");
if (c < 3200)
  Heltec.display->drawString(0, 50, "Low Battery");

Heltec.display->display();

// output format
Serial.println(“Temp(F)\tRH(%)\tCO2(ppm)”);
delay(5000);

// start scd measurement in periodic mode, will update every 2 s
Wire1.beginTransmission(SCD_ADDRESS);
Wire1.write(0x21);
Wire1.write(0xb1);
Wire1.endTransmission();

// wait for first measurement to be finished
delay(2000);
}

void loop() {
if (esp_timer_get_time() - lastMmntTime >= mmntIntervalUs) {
measure_and_report();
}

gadgetBle.handleEvents();
delay(3);
}

void measure_and_report() {
float co2, temperature, humidity, temp1;
uint8_t data[12], counter;

// send read data command
Wire1.beginTransmission(SCD_ADDRESS);
Wire1.write(0xec);
Wire1.write(0x05);
Wire1.endTransmission();

// read measurement data: 2 bytes co2, 1 byte CRC,
// 2 bytes T, 1 byte CRC, 2 bytes RH, 1 byte CRC,
// 2 bytes sensor status, 1 byte CRC
// stop reading after 12 bytes (not used)
// other data like ASC not included
Wire1.requestFrom(SCD_ADDRESS, 12);
counter = 0;
while (Wire1.available()) {
data[counter++] = Wire1.read();
}

// floating point conversion according to datasheet
co2 = (float)((uint16_t)data[0] << 8 | data[1]);
// convert T in deg C & F
temperature = -45 + 175 * (float)((uint16_t)data[3] << 8 | data[4]) / 65536;
temp1 = ((-45 + 175 * (float)((uint16_t)data[3] << 8 | data[4]) / 65536)/5)*9 + 32;
// convert RH in %
humidity = 100 * (float)((uint16_t)data[6] << 8 | data[7]) / 65536;

Serial.print(temp1);
Serial.print(" \t");
Serial.print(humidity);
Serial.print("\t");
Serial.print(co2);
Serial.println();

gadgetBle.writeCO2(co2);
gadgetBle.writeTemperature(temperature);
gadgetBle.writeHumidity(humidity);

gadgetBle.commit();
lastMmntTime = esp_timer_get_time();

digitalWrite(LED_BUILTIN,HIGH);

// display in OLED
Heltec.display->clear();
Heltec.display->setFont(ArialMT_Plain_24);
Heltec.display->drawString(5, 0, String(int(co2), DEC) + " ppm");
Heltec.display->setFont(ArialMT_Plain_16);
// Heltec.display->drawString(15, 45, “Vbat " + (String)c);
Heltec.display->drawString(15, 40, String(int(temp1),DEC) + " F”);
Heltec.display->drawString(65, 40, String(int(humidity),DEC) + " %");
Heltec.display->display();
delay(150);
digitalWrite(LED_BUILTIN,LOW);
}