CCS811 Weird readings.

General project discussion / help
Did you make a robotic coffee pot which implements HTCPCP and decafs unauthorized users? Show it off here!

Moderator: phalanx

Post Reply
heulen
Posts: 2
Joined: Tue Nov 14, 2017 12:40 am

CCS811 Weird readings.

Post by heulen » Tue Nov 14, 2017 1:00 am

Hi,

First of all, I'm sorry if I'm posting this thread in the wrong board. This is my first post so I'm not really experienced here. I hope to be soon!

I've been working on a Raspberry Pi project where I gather the readings of a CCS811 and a DHT11 sensor and then send them to a MySQL database for readout later.

I use a singular Python script containing the following functions:
  • A 4 way DIP switch to generate a unique ID for the Raspberry Pi (Goal is to use multiple sensors in multiple rooms).
  • A function to only send the data if there is internet connection, if there is no internet connection a LED will start blinking rapidly.
  • Function where I read out values of CCS811 and DHT11 and send it via GET request to DB.
  • Logging all data to a local textfile on the Raspberry PI.
  • After receiving very volatile readings from the CCS811 I started using a Arduino to measure the voltage from the Raspberry pi, singling out the problems that could be causing the weird readings.
However, after looking at the voltage and realizing voltage levels aren't anything out of the order and still getting weird readings.. I'm getting stuck. I really do not know what the matter is and I'm starting to think my sensor might be faulty.

Here is the Python script I use to do all of this. I hope someone can possibly point me in the right direction as to how I can fix this.

Code: Select all

#!/usr/bin/python
# -*- coding: utf-8 -*-
# By Olivier van Heulen
# Student at Scalda College
# Working on Kelvin
# 206374@student.scalda.nl

import RPi.GPIO as GPIO
import dht11
import smbus
import time
import urllib3.request
from datetime import datetime
import socket

bus = smbus.SMBus(1)
adress = 0x04

GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
GPIO.setup(14, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(15, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_UP)
instance = dht11.DHT11(pin=0x04)

from CCS811_RPi import CCS811_RPi

ccs811 = CCS811_RPi()

http = urllib3.PoolManager()
url = 'removed for post'

# Do you want to preset sensor baseline? If yes set the value here, otherwise set False

INITIALBASELINE = False

# Set MEAS_MODE (measurement interval)

configuration = 0b100000



# Set read interval for retriveving last measurement data from the sensor

pause = 60

print 'Checking hardware ID...'
print ccs811.checkHWID()
hwid = ccs811.checkHWID()
print hwid
if hwid == hex(129):
    print 'Hardware ID is correct'
else:
    print ('Incorrect hardware ID ', hwid, ', should be 0x81')


def internet(host='8.8.8.8', port=53, timeout=3):
    try:
        socket.setdefaulttimeout(timeout)
        socket.socket(socket.AF_INET,
                      socket.SOCK_STREAM).connect((host, port))
        return True
    except Exception, ex:
        return False


# print 'MEAS_MODE:',ccs811.readMeasMode()

ccs811.configureSensor(configuration)
print ('MEAS_MODE:', ccs811.readMeasMode())
print ('STATUS: ', bin(ccs811.readStatus()))
print '---------------------------------'

# Use these lines if you need to pre-set and check sensor baseline value

if INITIALBASELINE > 0:
    ccs811.setBaseline(INITIALBASELINE)
    print ccs811.readBaseline()

print 'Waiting 60 seconds for sensor to initialize'
time.sleep(60)
print '---------------------------------'
if internet() == True:
    while 1:
        x = 0
        if GPIO.input(14) == 1:
            x = x + 1
        if GPIO.input(15) == 1:
            x = x + 2
        if GPIO.input(18) == 1:
            x = x + 0x04
        if GPIO.input(27) == 1:
            x = x + 8
        statusbyte = ccs811.readStatus()

        print ('STATUS: ', bin(statusbyte))
        print ('Raspberry ID according to DIP: ', x)
        error = ccs811.checkError(statusbyte)
        if error:
            print ('ERROR:', ccs811.checkError(statusbyte))

        if not ccs811.checkDataReady(statusbyte):
            GPIO.output(17, 1)
            print 'No new samples are ready'
            print '---------------------------------'
            time.sleep(0.1)
            GPIO.output(17, 0)
            time.sleep(0.1)
            GPIO.output(17, 1)
            time.sleep(0.1)
            GPIO.output(17, 0)
            time.sleep(pause)
            continue
        result1 = instance.read()
        print (result1.temperature)
        print (result1.humidity)
        if result1.temperature > 0:
            if result1.humidity > 0:
                print("dht gave reading, compensating.")
                ccs811.setCompensation(result1.temperature, result1.humidity)
        else:
            print("dht didnt give reading, no compensation this time.")
            continue;
        result = ccs811.readAlg()
        if not result:
            print 'Invalid result received'
            time.sleep(pause)
            continue
        GPIO.output(17, 1)
        baseline = ccs811.readBaseline()

        print ('eCO2: ', result['eCO2'], ' ppm')
        print ('TVOC: ', result['TVOC'], 'ppb')

        # print 'Status register: ',bin(result['status'])

        print ('Last error ID: ', result['errorid'])

        # print 'RAW data: ',result['raw']
        # print 'Baseline: ',baseline

        ppm = result['eCO2']


        with open('/home/pi/logoffline.txt', 'a') as logger:
            byte_data = bus.read_i2c_block_data(adress, 2)
            voltage = float((byte_data[0] << 8) + byte_data[1])
            print byte_data[0]
            print byte_data[1]
            print 2 * (5 * voltage / 1023)
            logger.write(str(datetime.now().strftime('%Y-%m-%d %H:%M:%S'
                                                     ) + ' eCO2: ' + str(result['eCO2']) + ' TVOC: '
                             + str(result['TVOC']) + ' error ID: '
                             + str(result['errorid']) + ' Voltage: '
                             + str(2 * (5 * voltage / 1023)) + '\n'))
            logger.close
        if result1.is_valid():
            temperature = result1.temperature
            humidity = result1.humidity

        try:

            # r=http.request('GET',url,fields={'id':x, 'temp':temperature,'hum' :humidity,'co2':ppm})

            print r.data.decode('utf-8')
            print '---------------------------------'
        except:
            print 'Error connecting to server, skiping this reading'
            continue
            print '---------------------------------'

            time.sleep(0.5)
            GPIO.output(17, 0)
            time.sleep(pause)
        if internet() == False:
            print 'no internet'
            while internet() == False:
                GPIO.output(17, 1)
                time.sleep(0.1)
                GPIO.output(17, 0)
                time.sleep(0.1)

                print 'still no internett'


and also the library I use for the CCS811:

Code: Select all

#
# CCS811_RPi
#
# Petr Lukas
# July, 11 2017
#
# Version 1.0

import struct, array, time, io, fcntl

# I2C Address
CCS811_ADDRESS = (0x5B)

# Registers
CCS811_HW_ID = (0x20)
CSS811_STATUS = (0x00)
CSS811_APP_START = (0xF4)
CSS811_MEAS_MODE = (0x01)
CSS811_ERROR_ID = (0xE0)
CSS811_RAW_DATA = (0x03)
CSS811_ALG_RESULT_DATA = (0x02)
CSS811_BASELINE = (0x11)
CSS811_ENV_DATA = (0x05)

# Errors ID
ERROR = {}
ERROR[0] = 'WRITE_REG_INVALID'
ERROR[1] = 'READ_REG_INVALID'
ERROR[2] = 'MEASMODE_INVALID'
ERROR[3] = 'MAX_RESISTANCE'
ERROR[4] = 'HEATER_FAULT'
ERROR[5] = 'HEATER_SUPPLY'

I2C_SLAVE = 0x0703

CCS811_fw = 0
CCS811_fr = 0


class CCS811_RPi:
    def __init__(self, twi=1, addr=CCS811_ADDRESS):
        global CCS811_fr, CCS811_fw

        CCS811_fr = io.open("/dev/i2c-" + str(twi), "rb", buffering=0)
        CCS811_fw = io.open("/dev/i2c-" + str(twi), "wb", buffering=0)

        # set device address
        fcntl.ioctl(CCS811_fr, I2C_SLAVE, CCS811_ADDRESS)
        fcntl.ioctl(CCS811_fw, I2C_SLAVE, CCS811_ADDRESS)
        time.sleep(0.015)

    # public functions
    def checkHWID(self):
        s = [CCS811_HW_ID]  # Hardware ID
        s2 = bytearray(s)
        CCS811_fw.write(s2)
        time.sleep(0.0625)

        data = CCS811_fr.read(1)

        buf = array.array('B', data)
        return hex(buf[0])

    def readStatus(self):
        time.sleep(0.015)

        s = [CSS811_STATUS]
        s2 = bytearray(s)
        CCS811_fw.write(s2)
        time.sleep(0.0625)

        data = CCS811_fr.read(1)
        buf = array.array('B', data)
        return buf[0]

    def checkError(self, status_byte):
        time.sleep(0.015)
        error_bit = ((status_byte) >> 0) & 1
        if (not error_bit):
            return False

        s = [CSS811_ERROR_ID]
        s2 = bytearray(s)
        CCS811_fw.write(s2)
        time.sleep(0.0625)

        data = CCS811_fr.read(1)
        buf = array.array('B', data)
        return ERROR[int(buf[0])]

    def configureSensor(self, configuration):
        # Switch sensor to application mode
        s = [CSS811_APP_START]
        s2 = bytearray(s)
        CCS811_fw.write(s2)
        time.sleep(0.0625)

        s = [CSS811_MEAS_MODE, configuration, 0x00]
        s2 = bytearray(s)
        CCS811_fw.write(s2)
        time.sleep(0.015)
        return

    def readMeasMode(self):
        time.sleep(0.015)
        s = [CSS811_MEAS_MODE]
        s2 = bytearray(s)
        CCS811_fw.write(s2)
        time.sleep(0.0625)

        data = CCS811_fr.read(1)
        buf = array.array('B', data)
        return bin(buf[0])

    def readRaw(self):
        time.sleep(0.015)
        s = [CSS811_RAW_DATA]
        s2 = bytearray(s)
        CCS811_fw.write(s2)
        time.sleep(0.0625)

        data = CCS811_fr.read(2)
        buf = array.array('B', data)
        return (buf[0] * 256 + buf[1])

    def readAlg(self):
        time.sleep(0.015)
        s = [CSS811_ALG_RESULT_DATA]
        s2 = bytearray(s)
        CCS811_fw.write(s2)
        time.sleep(0.0625)

        data = CCS811_fr.read(8)
        buf = array.array('B', data)
        result = {}
        # Read eCO2 value and check if it is valid
        result['eCO2'] = buf[0] * 256 + buf[1]
        if (result['eCO2'] > 8192):
            print('Invalid eCO2 value')
            return False
        # Read TVOC value and check if it is valid
        result['TVOC'] = buf[2] * 256 + buf[3]
        if (result['TVOC'] > 1187):
            print('Invalid TVOC value')
            return False

        result['status'] = buf[4]

        # Read the last error ID and check if it is valid
        result['errorid'] = buf[5]
        if (result['errorid'] > 5):
            print('Invalid Error ID')
            return False

        result['raw'] = buf[6] * 256 + buf[7]
        return result

    def readBaseline(self):
        time.sleep(0.015)
        s = [CSS811_BASELINE]
        s2 = bytearray(s)
        CCS811_fw.write(s2)
        time.sleep(0.0625)

        data = CCS811_fr.read(2)
        buf = array.array('B', data)
        return (buf[0] * 256 + buf[1])

    def checkDataReady(self, status_byte):
        ready_bit = ((status_byte) >> 3) & 1
        if (ready_bit):
            return True
        else:
            return False

    def setCompensation(self, temperature, humidity):
        temperature = round(temperature, 2)
        humidity = round(humidity, 2)
        print('Setting compensation to ', temperature, ' C and ', humidity, ' %')
        hum1 = int(humidity // 0.5)
        hum2 = int(humidity * 512 - hum1 * 256)

        temperature = temperature + 25
        temp1 = int(temperature // 0.5)
        temp2 = int(temperature * 512 - temp1 * 256)

        s = [CSS811_ENV_DATA, hum1, hum2, temp1, temp2, 0x00]
        s2 = bytearray(s)
        CCS811_fw.write(s2)

        return

    def setBaseline(self, baseline):
        print('Setting baseline to ', baseline)
        buf = [0, 0]
        s = struct.pack('>H', baseline)
        buf[0], buf[1] = struct.unpack('>BB', s)
        print(buf[0])
        print(buf[1])

        s = [CSS811_BASELINE, buf[0], buf[1], 0x00]
        s2 = bytearray(s)
        CCS811_fw.write(s2)
        time.sleep(0.015)
TL;DR: I am using a CCS811 sensor to read Co2 sensors but the readings I am getting are not making any sense whatsoever. I am not sure what I'm doing wrong.

jremington
Support Volunteer
Posts: 2103
Joined: Fri Jun 15, 2007 9:41 pm
Location: Eugene, Or

Re: CCS811 Weird readings.

Post by jremington » Fri Nov 17, 2017 9:39 am

Did you give the CCS811 sensor a 48 hour burn-in time, as required?
After power up, do you give the sensor 20 minutes to warm up and stabilize, before reading it?
Are the header pins on the breakout board soldered cleanly, with no shorts between pins?

Tutorial: https://learn.sparkfun.com/tutorials/cc ... okup-guide

Please post examples of the readings.

heulen
Posts: 2
Joined: Tue Nov 14, 2017 12:40 am

Re: CCS811 Weird readings.

Post by heulen » Tue Nov 21, 2017 1:59 am

jremington wrote:
Fri Nov 17, 2017 9:39 am
Did you give the CCS811 sensor a 48 hour burn-in time, as required?
After power up, do you give the sensor 20 minutes to warm up and stabilize, before reading it?
Are the header pins on the breakout board soldered cleanly, with no shorts between pins?

Tutorial: https://learn.sparkfun.com/tutorials/cc ... okup-guide

Please post examples of the readings.
Yes, I gave the sensor a burn in time.
I am reading the sensor from the very moment it boots up but I'm just not really using the first 20 minutes of measurements. Is it necessary to not read the sensor the first 20 minutes?
These are some readings over the course of 4 days. Measuring every 60 seconds. The spikes to the end of the graph are from testing some functions so you should ignore those.
Image

This is what really interests me, it looks as if there is a certain formula being put in the graph here..
Image

paulvha
Posts: 130
Joined: Sat Nov 11, 2017 2:39 am

Re: CCS811 Weird readings.

Post by paulvha » Tue Nov 21, 2017 4:12 am

hi
I have ordered this board as well and expect it end of the week. I had a look at your code and read some of the reference material and Arduino library. The one aspect that stands out for me on this moment is the compensation.
SetCompensation : Looking to that library they use env[0], env[1], env[2] and env[3], where your code talks about hum1, hum2, temp1, temp2. The difference is that in the Arduino they set env[1] (hum2) and env[3] (temp2) to zero because "CCS811 only supports increments of 0.5 so bits 7-0 will always be zero". Looking at the datasheet (page 19), the Arduino library looks correct to me. Your code that Hum2 and temp2 have values set. Could there be your issue ?
Another aspect I would look at in your code around capturing the compensation. Not sure what instance.read() returns, looks to be a delta instead of current measurement as it compares to zero ???, but even then, why do a "continue" with the "else" that when there is NO compensation measured and skip the rest of the code ??

Post Reply