1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CO2濃度センサ(CJMCU-811/CCS811)のCO2の検出

Last updated at Posted at 2021-07-12

はじめに

みんなさん、CO2濃度に気になっているのでしょうか。普通に換気の良い環境において、CO2がそんなに気にしなくても大丈夫と思いますが、冬になるとなかなか窓を開けたくない時やビニールハウスの農作物栽培などの場合はCO2濃度が室外と顕著的な差が出るかもしれません。今度はCJMCU-811(CCS811も呼ばれる)というセンサーを使って、室内のCO2濃度を図れるように紹介します。

環境

  • IoTデバイス(ESP-WROOM-32)
  • ファームウェア(MicroPython1.15)
  • CO2濃度センサ (CJMCU-811/CCS811)

ファームウェアはmicropythonのホームページにダウンロードすることができます。作業する前にちゃんとファームウェアが一致することを確認する方がおすすめです。micropython以外にもAdafruit CircuitPythonなどがありますので、内蔵するモジュールが異なるため実行できない可能性が高いです。Adafruit CircuitPythonにもCCS811のモジュールがあるので、ラズパイを使っている人はAdafruitのCCS811githubページを参照すればいいと思います。

配置図

今度はBMP280と併用しています。CO2濃度センサそのままでCO2濃度を検出できますが、環境によって誤差が大きいらしいので、温度と気圧などで補正することがよりいい精度がもらえます。
配置図は下記のようにやっています。
Pin配置図.jpg

ESP32のPinは裏面に書いてあります。
ESP32 Pin.jpg

ソースコード

ソースコードはNotthemarsianさんが作ったmicropythonに使えるモジュールを参照して編集しました。
まずはCCS811のデータを取得するモジュールは下記のように示します。

CCS811.py
"""
ams CCS811 Indoor Air Quality Sensor
Date:  April 2018
License: This code is public domain
Based on CCS811 datasheet. Inspired by Adafruit and Sparkfun libraries
Tested with Adafruit CCS811 Air quality breakout. Product ID: 3566
Tested on NodeMCU and Wemos D1 Mini (ESP8266) MicroPython v1.8.7-7-gb5a1a20a3
and MicroPython v1.9.3-8-g63826ac5c on 2017-11-01; ESP module with ESP8266
"""

from machine import SoftI2C

class CCS811(object):
    """CCS811 gas sensor. Measures eCO2 in ppm and TVOC in ppb"""

    def __init__(self, i2c=None, addr=90):
        self.i2c = i2c
        self.addr = addr      # 0x5A = 90, 0x5B = 91
        self.tVOC = 0
        self.eCO2 = 0
        self.mode = 1       # Constant power mode; measurement every second
        self.error = False

        # Check if sensor is vailable at i2c bus address
        devices = i2c.scan()
        if self.addr not in devices:
            raise ValueError('CCS811 not found. Please check wiring. Pull nWake to ground.')
        # See figure 22 in datasheet: Bootloader Register Map
        # Check HW_ID register (0x20) - correct value 0x81
        hardware_id = self.i2c.readfrom_mem(self.addr, 0x20, 1)
        if (hardware_id[0] != 0x81):
            raise ValueError('Wrong Hardware ID.')
        # Check Status Register (0x00) to see if valid application present-
        status = self.i2c.readfrom_mem(self.addr, 0x00, 1)
        # See figure 12 in datasheet: Status register: Bit 4: App valid
        if not (status[0] >> 4) & 0x01:
            raise ValueError('Application not valid.')
        # Application start. Write with no data to App_Start (0xF4)
        self.i2c.writeto(self.addr, bytearray([0xF4]))
        # Set drive mode 1 - see Figure 13 in datasheet: Measure Mode Register (0x01)
        self.i2c.writeto_mem(self.addr, 0x01, bytearray([0b00011000]))

    def __string__(self):
        return 'eCO2: %d ppm, TVOC: %d ppb' % (s.eCO2, s.tVOC)
        # doesn't seem to work

    def data_ready(self):
        """returns true if new data was downloaded. Values in .eCO2 and .tVOV"""
        status = self.i2c.readfrom_mem(self.addr, 0x00, 1)
        # bit 3 in the status register: data_ready
        if (status[0] >> 3) & 0x01:
            # datasheet Figure 14: Algorithm Register Byte Order (0x02)
            register = self.i2c.readfrom_mem(self.addr, 0x02, 4)
            co2HB = register[0]
            co2LB = register[1]
            tVOCHB = register[2]
            tVOCLB = register[3]
            self.eCO2 = ((co2HB << 8) | co2LB)
            self.tVOC = ((tVOCHB << 8) | tVOCLB)
            return True
        else:
            return False

    def get_baseline(self):
        register = self.i2c.readfrom_mem(self.addr,0x11,2)
        HB = register[0]
        LB = register[1]
        #baseline = (HB << 8) | LB
        return HB, LB

    def put_baseline(self,HB,LB):
        register = bytearray([0x00,0x00])
        register[0] = HB
        register[1] = LB
        self.i2c.writeto_mem(self.addr,0x11,register)
    
    def put_envdata(self,humidity,temp):
        envregister = bytearray([0x00,0x00,0x00,0x00])
        envregister[0] = int(humidity) << 1
        t = int(temp//1)
        tf = temp % 1
        t_H = (t+25) << 9
        t_L = int(tf*512)
        t_comb = t_H | t_L
        envregister[2] = t_comb >> 8
        envregister[3] = t_comb & 0xFF
        self.i2c.writeto_mem(self.addr,0x05,envregister)
        #return envregister

上記のモジュールが内蔵する前提として、実行のサンプルは下記の通りです。
Pinのところはよく確認してください。ここは前の配置図によってsclはPin(19)とsdaはPin(18)に繋がることが分かります。

CCS811example.py
"""Example usage basic driver CCS811.py"""

from machine import Pin, SoftI2C
import time
import CCS811

def main():
    i2c = SoftI2C(scl=Pin(19), sda=Pin(18))
    # Adafruit sensor breakout has i2c addr: 90; Sparkfun: 91
    s = CCS811.CCS811(i2c=i2c, addr=90)
    time.sleep(1)
    while True:
        if s.data_ready():
            print('eCO2: %d ppm, TVOC: %d ppb' % (s.eCO2, s.tVOC))
            time.sleep(1)

main()

実行結果

実行結果は下記の通りです。
CCS811.JPG
これは一秒ごとに取っています。これを変更したいならtime.sleep()内の数値を書き換えればできます。今回はBMP280温度センサーも使っていますから、これから温度による補正する方法を見つけたいと思います。

参考資料

Notthemarsianさんが作ったCCS811モジュール: Notthemarsian github link

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?