SparkFun Forums 

Where electronics enthusiasts find answers.

General project discussion / help
Did you make a robotic coffee pot which implements HTCPCP and decafs unauthorized users? Show it off here!
By heulen
#196955
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.
By jremington
#196981
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.
By heulen
#197010
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
By paulvha
#197012
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 ??
By Michele31415
#198694
I just got a CCS811/BME280 combo and I'm getting weird readings too. I gave it the 48 hour breakin and waited 20 minutes for warup. The readings start off looking reasonable (around 500 ppm CO2 and mid-singe digit VOCs) But the readings slowly creep up more or less linearly from there. After one day, it's showing CO2 of 5000+ and VOC of 500! And still climbing. In the same room, nothing in the environent has changed. If I power-cycle it, it goes back to the 500 CO2 and 1-4 VOC and then the whole process starts over again.

Did I get a defective unit?
By Gushu
#198893
Hey,


I received the CCS811/BME280 unit last week and followed the hookup guide: https://learn.sparkfun.com/tutorials/cc ... okup-guide

Now having similar issues. The temp reading is constantly 5-7 degree C more than should be (it's more like 22 degrees C where I am)- the rest seems ok - though maybe being thrown off by the wrong temp reading as the CCS811 uses BME280 readings to improve its accuracy.

This is the print out I get about every other second:
CO2[491]ppm TVOC[13]ppb temp[29.6]C pressure[101115.52]Pa altitude[18.05]m humidity[34]%

Waiting to hear back from Sparkfun on this.. if anyone has a solution I'd be happy to hear!
By rkmase
#199342
I just wanted to report that I am also getting the "creeping eco2" level issue that everyone else is getting. Has anyone figure out a solution? I have actually used a gas tube to measure the general co2 levels and am getting 500-1000 ppm, while my CCS811 is currently reading anywhere from 2000-5000. Can also reproduce the error outside, where co2 levels should be nowhere near 1000.
By dgirman
#199445
Input to you question....

I have the SparkFun SCD30 CO2 module. After 3 days of continuous operation (IIC bus used), I powered the module off, then on.

As you see with your CCS811 reading in the 1000s' range, I'm now seeing similar values from the SCD30.

So ... I compared this to my CCS811 readings and a second SCD30 module, which report normal values. These modules report within the normal range. Same code and MPU.

The SCD module also has a temperature and humidity reading, which read normal for both modules.

My first thought is a CO2 sensor issue. :)