Hi guys, I hooked up a senseair sunrise co2 sensor to my arduino. Connecting the sensor only to the arduino works fine, however, when I connect an sd/rtc sensor shield to the arduino and use the same pins, the readings would fail (Overview | Adafruit Data Logger Shield | Adafruit Learning System is the shield I use).
I am pretty sure there is some interference in I2C, but I am too smooth brained to detect it. I can provice the code if needed.
Cheers!
Edit: here is my code:
#include <Wire.h>
#include "RTClib.h"
#include <SPI.h>
#include <SD.h>
#define WIRE_WORKAROUND (0)
#define DISABLE_ABC (0) /* It could be necessary to disable ABC if sensor will be tested with CO2 concentrations below 400ppm. */
/*sunrise configuration */
const int SUNRISE_EN = 8;/* Define serial EN pin */
const uint8_t SUNRISE_ADDR = 0x68;/* Sunrise communication address, both for Modbus and I2C */
const int ATTEMPTS = 5;/* Amount of wakeup attempts before time-out */
const int EEPROM_UPDATE_DELAY_MS = 25;/* It takes 25ms to write one EE register */
/* Register Addresses */
const uint8_t ERROR_STATUS = 0x01;
const uint8_t MEASUREMENT_MODE = 0x95;
const uint8_t METER_CONTROL = 0xA5;
const uint8_t START_MEASUREMENT = 0xC3;
const uint8_t ABC_TIME = 0xC4;
/* Measurement modes */
const uint16_t CONTINUOUS = 0x0000;
const uint16_t SINGLE = 0x0001;
/* Delays in milliseconds*/
const int STABILIZATION_MS = 35;
const int WAIT_FOR_PIN_MS = 2000;
/* Reading period, in milliseconds. Default is 4 seconds */
int give_sensor_info = 0;
int show_error = 0;
int readPeriodMs = 1000;
/* Array for storing sensor state data */
uint8_t state[24];
/* logger shield configuration */
const byte chipSelect = 10;
RTC_DS1307 rtc;
File myFile;
char dateBuffer[12];
char sensBuffer[10];
void setup() {
Serial.begin(115200);
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.println("Initialization complete\n");
if (give_sensor_info == 1) {
/* Read the sensor's configs */
Serial.println("Sensor Measurement Configurations:");
read_sensor_config(SUNRISE_ADDR);
Serial.println();
#if (DISABLE_ABC ==1)
setABC(SUNRISE_ADDR, false);
#endif
change_measurement_mode(SUNRISE_ADDR); /* Change measurement mode if continuous */
Serial.println("Saving Sensor State"); /* Initial measurement */
save_state(SUNRISE_ADDR);
}
if (! rtc.begin()) {
Serial.println("No RTC");
Serial.flush();
abort();
}
if (! rtc.isrunning()) {
Serial.println("RTC was off. Setting new time.");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
digitalWrite(SUNRISE_EN, LOW);
delay(readPeriodMs);
}
void loop() {
static int pin_value = HIGH;
static unsigned long last_abc_stamp = 0;
DateTime time = rtc.now();
//Full Timestamp
Serial.println(String("DateTime::TIMESTAMP_FULL:\t") + time.timestamp(DateTime::TIMESTAMP_FULL));
/* Read measurements */
read_sensor_measurements(SUNRISE_ADDR);
delay(readPeriodMs);
/* Indicate working state */
digitalWrite(LED_BUILTIN, pin_value);
pin_value = ((pin_value == HIGH) ? LOW : HIGH);
}
/*
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Functions used to start the sensor and communicate with it.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
*/
int WireRequestFrom(uint8_t dev_addr, uint8_t bytes_numbers, uint8_t offset_to_read, bool stop) {
/* Workaround regarding BAD implementations of Wire.endTransmission(false) function */
int error;
#if (WIRE_WORKAROUND == 1)
error = Wire.requestFrom((uint8_t)dev_addr, (uint8_t)bytes_numbers /* how many bytes */, (uint32_t)offset_to_read /* from address*/, (uint8_t)1/* Address size - 1 byte*/, stop /* STOP*/);
#else
Wire.beginTransmission(dev_addr);
Wire.write(offset_to_read); //starting register address, from which read data
Wire.endTransmission(false);
error = Wire.requestFrom((uint8_t)dev_addr, (uint8_t)bytes_numbers /* how many bytes */, (uint8_t)stop /* STOP*/);
#endif
return error;
}
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);
}
void save_state(uint8_t target) {
/**
@brief Reads the sensors current state data.
@param target: The sensor's communication address
@note If host device has no state data, it is very important
that host do not write '0' to address 0xC2 - 0xDB the
first time it starts a measurement.
@retval None
*/
/* Function variables */
int error;
int numBytes = 24;
/* Drive EN pin HIGH */
digitalWrite(SUNRISE_EN, HIGH);
/* Wait for sensor start-up and stabilization */
delay(STABILIZATION_MS);
/* Wakeup */
if (!(_wakeup(target))) {
Serial.print("Failed to wake up sensor.");
/* FATAL ERROR */
digitalWrite(SUNRISE_EN, LOW);
while (true);
}
/* Request state data */
error = WireRequestFrom(target, numBytes /* how many bytes */, ABC_TIME /* from address*/, 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);
}
/* Read and save state data */
for (int n = 0 ; n < numBytes ; n++) {
state[n] = Wire.read();
}
/* Drive EN pin LOW */
digitalWrite(SUNRISE_EN, LOW);
Serial.println("Saved Sensor State Successfully\n");
}
bool _wakeup(uint8_t target) {
/**
@brief Wakes up the sensor by initializing a write operation
with no data.
@param target: The sensor's communication address
@note This example shows a simple way to wake up the sensor.
@retval true if successful, false if failed
*/
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) {
/* Function variables */
int error;
int numBytes = 7;
/* Wakeup */
if (!(_wakeup(target))) {
Serial.print("Failed to wake up sensor.");
return;
}
/* Request values */
error = WireRequestFrom(target, numBytes /* how many bytes */, MEASUREMENT_MODE /* from address*/, 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;
/* ABCPeriod */
byteHi = Wire.read();
byteLo = Wire.read();
uint16_t abcPeriod = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;
/* Most propable that the sensor will not go into sleep mode, but to be insure...*/
/* Wakeup */
if (!(_wakeup(target))) {
Serial.print("Failed to wake up sensor.");
return;
}
/* Request values */
error = WireRequestFrom(target, 1, METER_CONTROL /* from address*/, true /* STOP*/);
if (error != 1 ) {
Serial.print("Failed to write to target. Error code : ");
Serial.println(error);
return;
}
uint8_t meterControl = Wire.read();
Serial.print("Measurement Mode: ");
Serial.println(measMode);
readPeriodMs = measPeriod * 1000;
Serial.print("Measurement Period, sec: ");
Serial.println(measPeriod);
Serial.print("Number of Samples: ");
Serial.println(numSamples);
if ((0U == abcPeriod) || (0xFFFFU == abcPeriod) || (meterControl & 0x02U)) {
Serial.println("ABCPeriod: disabled");
} else {
Serial.print("ABCPeriod, hours: ");
Serial.println(abcPeriod);
}
Serial.print("MeterControl: ");
Serial.println(meterControl, HEX);
#if (DISABLE_ABC ==0)
/* If we do not implicity disable ABC try to enable it in case if someone forget to do that...*/
if ((meterControl & 0x02U) != 0) {
setABC(target, true);
}
#endif
}
void change_measurement_mode(uint8_t target) {
/**
@brief Changes the sensor's current measurement mode, if it's
currently in single mode.
@param target: The sensor's communication address
@note This example shows a simple way to change the sensor's
measurement mode. The sensor has to be manually restarted after the
changes.
@retval None
*/
/* 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 = WireRequestFrom(target, numBytes /* how many bytes */, MEASUREMENT_MODE /* from address*/, 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);
delay(EEPROM_UPDATE_DELAY_MS);
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 setABC(uint8_t target, bool enable) {
/* Wakeup */
if (!(_wakeup(target))) {
Serial.print("Failed to wake up sensor.");
return;
}
/* Request values */
int error = WireRequestFrom(target, 1, METER_CONTROL /* from address*/, true /* STOP*/);
if (error != 1 ) {
Serial.print("Failed to write to target. Error code : ");
Serial.println(error);
return;
}
uint8_t meterControl = Wire.read();
if (enable) {
Serial.println("Enabling ABC...");
meterControl &= (uint8_t)~0x02U;
} else {
Serial.println("Disabling ABC...");
meterControl |= 0x02U;
}
/* Wakeup */
if (!(_wakeup(target))) {
Serial.print("Failed to wake up sensor.");
return;
}
Wire.beginTransmission(target);
Wire.write(METER_CONTROL);
Wire.write(meterControl);
error = Wire.endTransmission(true);
delay(EEPROM_UPDATE_DELAY_MS);
if (error != 0) {
Serial.print("Failed to send request. Error code: ");
Serial.println(error);
/* FATAL ERROR */
while (true);
}
}
void read_sensor_measurements(uint8_t target) {
/**
@brief Reads and prints the sensor's current CO2 value and
error status.
@param target: The sensor's communication address
@note This example shows a simple way to read the sensor's
CO2 measurement and error status.
@retval None
*/
/* Function variables */
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];
}
/* Drive EN pin HIGH */
digitalWrite(SUNRISE_EN, HIGH);
/* Wait for sensor start-up and stabilization */
delay(STABILIZATION_MS);
/* Wakeup */
if (!_wakeup(target)) {
Serial.print("Failed to wake up sensor.");
return;
}
/* Write measurement command and sensor state to 0xC3 */
Wire.beginTransmission(target);
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;
}
/* Wait until ready pin goes low */
delay(WAIT_FOR_PIN_MS);
/* Wakeup */
if (!(_wakeup(target))) {
Serial.print("Failed to wake up sensor.");
digitalWrite(SUNRISE_EN, LOW);
return;
}
/* Request values */
error = WireRequestFrom(target, numRegRead /* how many bytes */, ERROR_STATUS /* from address*/, true /* STOP*/);
if (error != numRegRead ) {
Serial.print("Failed to read values. Error code: ");
Serial.println(error);
digitalWrite(SUNRISE_EN, LOW);
return;
}
/* Read values */
/* Error status */
uint8_t eStatus = Wire.read();
/* Reserved */
uint8_t byteHi = Wire.read();
uint8_t byteLo = Wire.read();
byteHi = Wire.read();
byteLo = Wire.read();
/* CO2 value */
byteHi = Wire.read();
byteLo = Wire.read();
uint16_t co2Val = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;
/* Wakeup */
if (!_wakeup(target)) {
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 = WireRequestFrom(target, numRegState /* how many bytes */, ABC_TIME /* from address*/, 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();
}
/* Drive EN pin LOW */
digitalWrite(SUNRISE_EN, LOW);
/* Print values */
Serial.print("CO2: ");
Serial.print(co2Val);
Serial.println(" ppm");
if (show_error == 1) {
Serial.print("Error Status: 0x");
Serial.println(eStatus, HEX);
Serial.println();
}
}
Edit 2: The output of the sensor config reading gives zeros only, the co2 values are wrong. When removing the logger shield, sensor config is properly read and co2 values are correct,.