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