Code:
#include <Wire.h>
#include <SoftwareSerial.h>
const int SUNRISE_EN = 8; //serial EN pin
const uint8_t SUNRISE_ADDR = 0x68; // communication address
const int ATTEMPTS = 5; // Amount of wakeup attempts before time-out
const uint8_t ERROR_STATUS = 0x01; // Register Addresses
const uint8_t MEASUREMENT_MODE = 0x95;
const uint8_t START_MEASUREMENT = 0xC3;
const uint8_t ABC_TIME = 0xC4;
const uint16_t CONTINUOUS = 0x0000; // Measurement modes
const uint16_t SINGLE = 0x0001;
/* Delays in milliseconds*/
const int STABILIZATION_MS = 35;
const int WAIT_FOR_PIN_MS = 2000;
int readPeriodMs = 4000; // Reading period, in milliseconds. Default is 4 seconds
uint8_t state[24]; // Array for storing sensor state data
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
void reInitI2C() { // Initialize I2C bus and pins
/* Initialize I2C and use default pins defined for the board */
Wire.begin();
/* Setup I2C clock to 100kHz */
Wire.setClock(100000);
}
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
bool _wakeup(uint8_t target) // Wakes up the sensor by initializing a write operation with no data.
{
int attemps = ATTEMPTS;
int error;
do {
uint8_t byte_0;
/* */
Wire.beginTransmission(target);
error = Wire.endTransmission(true);
} while(((error != 0 /*success */) && (error != 2 /*Received NACK on transmit of address*/) && (error != 1 /* BUG in STM32 library*/)) && (--attemps > 0));
/* STM32 driver can stack under some conditions */
if(error == 4) {
/* Reinitialize I2C*/
reInitI2C();
return false;
}
return (attemps > 0);
}
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
void read_sensor_config(uint8_t target) { // Reads and prints the sensor's current measurement mode, measurement period and number of samples.
/* Function variables */
int error;
int numBytes = 5;
/* Wakeup */
if(!(_wakeup(target))) {
Serial.print("Failed to wake up sensor.");
return;
}
/* Request values */
error = Wire.requestFrom((uint8_t)target, (uint8_t)numBytes /* how many bytes */, (uint32_t)MEASUREMENT_MODE /* from address*/, (uint8_t)1/* Address size - 1 byte*/, true /* STOP*/);
if(error != numBytes ) {
Serial.print("Failed to write to target. Error code : ");
Serial.println(error);
return;
}
/* Read values */
/* Measurement mode */
uint8_t measMode = Wire.read();
/* Measurement period */
uint8_t byteHi = Wire.read();
uint8_t byteLo = Wire.read();
uint16_t measPeriod = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;
/* Number of samples */
byteHi = Wire.read();
byteLo = Wire.read();
uint16_t numSamples = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;
Serial.print("Measurement Mode: ");
Serial.println(measMode);
readPeriodMs = measPeriod * 1000;
Serial.print("Measurement Period: ");
Serial.println(measPeriod);
Serial.print("Number of Samples: ");
Serial.println(numSamples);
}
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
void change_measurement_mode(uint8_t target) { // Changes the sensor's current measurement mode, if it's currently in single mode.
/* Function variables */
int error;
int numBytes = 1;
/* Wakeup */
if(!(_wakeup(target))) {
Serial.print("Failed to wake up sensor.");
/* FATAL ERROR */
while(true);
}
/* Read Value */
error = Wire.requestFrom((uint8_t)target, (uint8_t)numBytes /* how many bytes */, (uint32_t)MEASUREMENT_MODE /* from address*/, (uint8_t)1/* Address size - 1 byte*/, true /* STOP*/);
if(error != numBytes ) {
Serial.print("Failed to read measurement mode. Error code: ");
Serial.println(error);
/* FATAL ERROR */
while(true);
}
/* Change mode if continuous */
if(Wire.read() != SINGLE) {
Serial.println("Changing Measurement Mode to Single...");
/* Wakeup */
if(!_wakeup(target)) {
Serial.print("Failed to wake up sensor.");
/* FATAL ERROR */
while(true);
}
Wire.beginTransmission(target);
Wire.write(MEASUREMENT_MODE);
Wire.write(SINGLE);
error = Wire.endTransmission(true);
if(error != 0) {
Serial.print("Failed to send request. Error code: ");
Serial.println(error);
/* FATAL ERROR */
while(true);
}
Serial.println("Sensor restart is required to apply changes");
/* Turn-off sensor */
digitalWrite(SUNRISE_EN, LOW);
/* Wait for sensor restart */
delay(STABILIZATION_MS);
/* Turn-on sensor */
digitalWrite(SUNRISE_EN, HIGH);
}
}
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
void save_state(uint8_t target) { // Reads the sensors current state data
/* Function variables */
int error;
int numBytes = 24;
digitalWrite(SUNRISE_EN, HIGH); // Drive EN pin HIGH
delay(STABILIZATION_MS); // Wait for sensor start-up and stabilization
if(!(_wakeup(target))) { // Wakeup
Serial.print("Failed to wake up sensor.");
/* FATAL ERROR */
digitalWrite(SUNRISE_EN, LOW);
while(true);
}
/* Request state data */
error = Wire.requestFrom((uint8_t)target, (uint8_t)numBytes /* how many bytes */, (uint32_t)ABC_TIME /* from address*/, (uint8_t)1/* Address size - 1 byte*/, true /* STOP*/);
if(error != numBytes ) {
Serial.print("Failed to read measurements command. Error code: ");
Serial.println(error);
digitalWrite(SUNRISE_EN, LOW);
/* FATAL ERROR */
while(true);
}
for(int n = 0 ; n < numBytes ; n++) { // Read and save state data
state[n] = Wire.read();
}
digitalWrite(SUNRISE_EN, LOW); // Drive EN pin LOW
Serial.println("Saved Sensor State Successfully\n");
}
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
void read_sensor_measurements(uint8_t target) { // Reads and prints the sensor's current CO2 value and error status.
int error;
int numRegCmd = 25;
int numRegRead = 7;
int numRegState = 24;
uint8_t cmdArray[25];
cmdArray[0] = 0x01;
for(int n = 1 ; n < numRegCmd ; n++) {
cmdArray[n] = state[n-1];
}
digitalWrite(SUNRISE_EN, HIGH); // Drive EN pin HIGH
delay(STABILIZATION_MS); // Wait for sensor start-up and stabilization
if(!_wakeup(target)) { // Wakeup
Serial.print("Failed to wake up sensor.");
return;
}
Wire.beginTransmission(target); // rite measurement command and sensor state to 0xC3
Wire.write(START_MEASUREMENT);
for(int reg_n =0; reg_n < numRegCmd; reg_n++) {
Wire.write(cmdArray[reg_n]);
}
error = Wire.endTransmission(true);
if(error != 0) {
Serial.print("Failed to send measurement command. Error code: ");
Serial.println(error);
digitalWrite(SUNRISE_EN, LOW);
return;
}
delay(WAIT_FOR_PIN_MS); // Wait until ready pin goes low
if(!(_wakeup(target))) { // Wakeup
Serial.print("Failed to wake up sensor.");
digitalWrite(SUNRISE_EN, LOW);
return;
}
/* Request values */
error = Wire.requestFrom((uint8_t)target, (uint8_t)numRegRead /* how many bytes */, (uint32_t)ERROR_STATUS /* from address*/, (uint8_t)1/* Address size - 1 byte*/, true /* STOP*/);
if(error != numRegRead ) {
Serial.print("Failed to read values. Error code: ");
Serial.println(error);
digitalWrite(SUNRISE_EN, LOW);
return;
}
uint8_t eStatus = Wire.read(); // Error status
uint8_t byteHi = Wire.read(); // Reserved
uint8_t byteLo = Wire.read();
byteHi = Wire.read();
byteLo = Wire.read();
byteHi = Wire.read(); // CO2 value
byteLo = Wire.read();
uint16_t co2Val = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;
if(!_wakeup(target)) { // Wakeup
Serial.print("Failed to wake up sensor.");
digitalWrite(SUNRISE_EN, LOW);
return;
}
/* Read sensor state data from 0xC4-0xDB and save it for next measurement */
error = Wire.requestFrom((uint8_t)target, (uint8_t)numRegState /* how many bytes */, (uint32_t)ABC_TIME /* from address*/, (uint8_t)1/* Address size - 1 byte*/, true /* STOP*/);
if(error != numRegState) {
Serial.print("Failed to read measurements command. Error code: ");
Serial.println(error);
digitalWrite(SUNRISE_EN, LOW);
return;
}
for(int n = 0 ; n < numRegState ; n++) {
state[n] = Wire.read();
}
digitalWrite(SUNRISE_EN, LOW); // Drive EN pin LOW
Serial.print("CO2: "); // Print values
Serial.print(co2Val);
Serial.println(" ppm");
Serial.print("Error Status: 0x");
Serial.println(eStatus, HEX);
Serial.println();
}
//███████████████████████████████████████████████████████████████
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
pinMode(SUNRISE_EN, OUTPUT);
digitalWrite(SUNRISE_EN, HIGH);
delay(STABILIZATION_MS); // Wait for sensor start-up and stabilization
reInitI2C(); // Initialize I2C and use default pins defined for the board
Serial.begin(115200);
Serial.println("Initialization complete\n");
Serial.println("Sensor Measurement Configurations:");
read_sensor_config(SUNRISE_ADDR); // Read the sensor's configs
Serial.println();
change_measurement_mode(SUNRISE_ADDR); // Change measurement mode if continuous
digitalWrite(SUNRISE_EN, LOW);
Serial.println("Saving Sensor State");
save_state(SUNRISE_ADDR); // Initial measurement
delay(readPeriodMs);
}
//███████████████████████████████████████████████████████████████
void loop() {
static int pin_value = HIGH;
static unsigned long last_abc_stamp = 0;
if(3600000 < (unsigned long)((long)millis() - (long)last_abc_stamp)) { // When an hour has passed, increase ABC Time
/* Use ABC time stored in the sensor state */
uint16_t abc_time = ((int16_t)(int8_t) state[0] << 8) | (uint16_t)state[1];
abc_time = abc_time + 1;
state[0] = abc_time >> 8;
state[1] = abc_time & 0x00FF;
last_abc_stamp = millis();
Serial.println("ABC time incremented.");
}
read_sensor_measurements(SUNRISE_ADDR); // Read measurements
Serial.println("\nWaiting...\n");
delay(readPeriodMs); // Delay between readings
digitalWrite(LED_BUILTIN, pin_value); // Indicate working state
pin_value = ((pin_value == HIGH) ? LOW : HIGH);
}