Incorrect CO2 readings from S8 CO2 sensor [SOLVED]

EDIT: solved - see this post. TL;DR - the code in this post works and I’m an idiot.

Hi all,

I have an S8 CO2 sensor that I’ve used many times before successfully. However, trying to reuse the same code I used before gives me incorrect readings now. Not sure if this is an Arduino IDE issue (maybe board definitions, default libraries or something else changed?) or something wrong with the sensor I’m using. I’ve tried with two more S8 sensors, both yield the same results.

I’m using their example code on page 6 in this Application Note, and in this post, they say it’s the same code for both sensors (K-30 and S8), and I’ve tested this before as well and it did work previously.

Essentially, you send a command over UART (using a software serial library) and get a response back. Here’s the command and response from their communications application note:

in the example response above, the 4th byte (0x01) and 5th byte (0x90) combined gives 0x0190 = 400 ppm

but the response I’m getting is this:

The proper response format is there, just that the high and low bytes representing the actual value are quite off from what I’m expecting. This response was from the balcony, so was expecting to see something similar to the example response from their guide, i.e., close to fresh air ~400ppm.

I have even tried calibrating the sensor in fresh air and in zero co2 concentration air as well, and while calibration procedure works (the values change), it’s still reading the incorrect value. Zero calibrating the sensor it is making it max out at 65535 in normal air, and calibrating it in fresh air is making it read as shown in the screenshot above. In indoor environment, it’s reading around 70 ppm.

Again, I’ve used this code successfully before (maybe a year ago), and the only thing that’s updated in this setup is the Arduino IDE - I was using 1.6.x last year, and now I’m using 1.8.9.

Maybe the CO2 sensor is a newer batch and expects different commands or returns different response? I’m powering the sensor via USB, so it’s a little under 5V for power, wondering if that might be the issue. Datasheet says it can take 4.5 - 5.2V.

Here’s my code for testing:

#include <SoftwareSerial.h>

SoftwareSerial K_30_Serial(7,8);  // RX, TX                
byte readCO2[] = {0xFE, 0X44, 0X00, 0X08, 0X02, 0X9F, 0X25};  //Command packet to read Co2 (see app note)
byte response[] = {0,0,0,0,0,0,0};  //create an array to store the response

void setup()
{
  Serial.begin(115200);
  K_30_Serial.begin(9600);
}

void loop()
{
  float co2 = getCO2(readCO2);
  Serial.print("CO2 concentration: ");
  Serial.println(co2);Serial.println();
  delay(2000);
}

float getCO2(byte packet[])
{
  while(!K_30_Serial.available())  //keep sending request until we start to get a response
  {
    K_30_Serial.write(readCO2,7);
    delay(10);
  }
  
  int timeout=0;  //set a timeoute counter
  while(K_30_Serial.available() < 7 ) //Wait to get a 7 byte response
  {
    timeout++;  
    if(timeout > 10)    //if it takes to long there was probably an error
    {
      while(K_30_Serial.available())  //flush whatever we have
        K_30_Serial.read();
      break;                        //exit and try again
    }
    delay(50);
  }
  
  for (int i=0; i < 7; i++)
  {
    response[i] = K_30_Serial.read();
  } 

  Serial.print("Response from sensor: ");
  for (int i=0; i < 7; i++)
  {
    Serial.print(response[i], HEX);
    Serial.print(" ");
  } 
  Serial.println();
  
  byte high = response[3];                        //high byte for value is 4th byte in packet
  byte low = response[4];                         //low byte for value is 5th byte in packet

  float val = (high*256) + low;                //Combine high byte and low byte with this formula to get value
  return val;
}

FYI, I did try using NeoSWSerial library as well. Worked well before, now works similarly to how I posted here - i.e., incorrect readings.

any help here is appreciated. Thanks.

Does the checksum pass? If it does, that would imply that the serial is working fine, and the sensor is not.

I gotta be honest - I'm not sure how to calculate and verify checksums, especially for this. I've only followed example codes and theirs is vague at best.

Edit: here's CRC calculation from their datasheet:

No idea how they came up with this command to read the CO2 (in their example, used in code in first post):

0xFE, 0X44, 0X00, 0X08, 0X02, 0X9F, 0X25

where the last two bytes are CRC bytes.

Have you read the following document from the sensor manufacturer's website:

Modbus on SE 0119

Where it says:
Capture.PNG

Capture.PNG

I have not seen that note before. I called them up to verify this and apparently that note is meant for the 5% sensor, while I was attempting to use 1% sensor in the past.

So it turns out they shipped the wrong sensor where the readings are all scaled by a factor of 10 as you mentioned. There's no way to verify this by physically looking at the sensor, just by sending a device ID command. There was nothing wrong with the code or wiring, just an incorrect sensor on which I wasted 2 nights trying to diagnose.

Thanks for the help - marking this as solved. Gonna leave this up so people learn to cover all bases when stuck and in case others want to connect to an S8 CO2 sensor - the code in the first post is still valid as of 2019.