Skip to content

Commit 484d251

Browse files
RafaelLeeImgbessman
authored andcommitted
Add driver for eCO2 and eTVOC sensor CCS811
1 parent 222a798 commit 484d251

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

pslab/external/ccs811.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import time
2+
from pslab.bus import I2CSlave
3+
4+
5+
class CCS811(I2CSlave):
6+
MODE_IDLE = 0 # Idle (Measurements are disabled in this mode)
7+
MODE_CONTINUOUS = 1 # Constant power mode, IAQ measurement every 1s
8+
MODE_PULSE = 2 # Pulse heating mode IAQ measurement every 10 seconds
9+
MODE_LOW_POWER = 3 # Low power pulse heating mode IAQ measurement every 60 seconds
10+
MODE_CONTINUOUS_FAST = 4 # Constant power mode, sensor measurement every 250ms
11+
12+
_ADDRESS = 0x5A
13+
14+
# Figure 14: CCS811 Application Register Map
15+
_STATUS = 0x00 # STATUS # R 1 byte Status register
16+
# MEAS_MODE # R/W 1 byte Measurement mode and conditions register Algorithm result. The most significant 2 bytes contain a up to ppm estimate of the equivalent CO2 (eCO2) level, and
17+
_MEAS_MODE = 0x01
18+
_ALG_RESULT_DATA = 0x02 # ALG_RESULT_DATA # R 8 bytes the next two bytes contain a ppb estimate of the total VOC level. Raw ADC data values for resistance and current source
19+
_RAW_DATA = 0x03 # RAW_DATA # R 2 bytes used. Temperature and humidity data can be written to
20+
_ENV_DATA = 0x05 # ENV_DATA # W 4 bytes enable compensation Thresholds for operation when interrupts are only
21+
_THRESHOLDS = 0x10 # THRESHOLDS # W 4 bytes generated when eCO2 ppm crosses a threshold The encoded current baseline value can be read. A
22+
# BASELINE # R/W 2 bytes previously saved encoded baseline can be written.
23+
_BASELINE = 0x11
24+
_HW_ID = 0x20 # HW_ID # R 1 byte Hardware ID. The value is 0x81
25+
_HW = 0x21 # HW Version # R 1 byte Hardware Version. The value is 0x1X Firmware Boot Version. The first 2 bytes contain the
26+
_FW_BOOT_VERSION = 0x23 # FW_Boot_Version # R 2 bytes firmware version number for the boot code. Firmware Application Version. The first 2 bytes contain
27+
# FW_App_Version # R 2 bytes the firmware version number for the application code
28+
_FW_APP_VERSION = 0x24
29+
# Internal_State # R 1 byte Internal Status register Error ID. When the status register reports an error its
30+
_INTERNAL_STATE = 0xA0
31+
# ERROR_ID # R 1 byte source is located in this register If the correct 4 bytes ( 0x11 0xE5 0x72 0x8A) are written
32+
_ERROR_ID = 0xE0
33+
# SW_RESET # W 4 bytes to this register in a single sequence the device will reset and return to BOOT mode.
34+
_SW_RESET = 0xFF
35+
36+
# Figure 25: CCS811 Bootloader Register Map
37+
# Address Register R/W Size Description
38+
_STATUS = 0x00
39+
_HW_ID = 0x20
40+
_HW_Version = 0x21
41+
_APP_ERASE = 0xF1
42+
_APP_DATA = 0xF2
43+
_APP_VERIFY = 0xF3
44+
_APP_START = 0xF4
45+
_SW_RESET = 0xFF
46+
47+
def __init__(self, address=None, device=None):
48+
super().__init__(address or self._ADDRESS, device)
49+
self.fetchID()
50+
self.softwareReset()
51+
52+
def softwareReset(self):
53+
self.write([0x11, 0xE5, 0x72, 0x8A], self._SW_RESET)
54+
55+
def fetchID(self):
56+
hardware_id = (self.read(1, self._HW_ID))[0]
57+
time.sleep(0.02) # 20ms
58+
hardware_version = (self.read(1, self._HW_Version))[0]
59+
time.sleep(0.02) # 20ms
60+
boot_version = (self.read(2, self._FW_BOOT_VERSION))[0]
61+
time.sleep(0.02) # 20ms
62+
app_version = (self.read(2, self._FW_APP_VERSION))[0]
63+
64+
return {
65+
"hardware_id": hardware_id,
66+
"hardware_version": hardware_version,
67+
"boot_version": boot_version,
68+
"app_version": app_version,
69+
}
70+
71+
def app_erase(self):
72+
ignore = self.write([0xE7, 0xA7, 0xE6, 0x09], self._APP_ERASE)
73+
time.sleep(0.3)
74+
75+
def app_start(self):
76+
ignore = self.write([], self._APP_START)
77+
78+
def set_measure_mode(self, mode):
79+
self.write([mode << 4], self._MEAS_MODE)
80+
81+
def get_measure_mode(self):
82+
print(self.read(10, self._MEAS_MODE))
83+
84+
def get_status(self):
85+
status = (self.read(1, self._STATUS))[0]
86+
return status
87+
88+
def decode_status(self, status):
89+
s = ""
90+
if (status & (1 << 7)) > 0:
91+
s += "Sensor is in application mode"
92+
else:
93+
s += "Sensor is in boot mode"
94+
if (status & (1 << 6)) > 0:
95+
s += ", APP_ERASE"
96+
if (status & (1 << 5)) > 0:
97+
s += ", APP_VERIFY"
98+
if (status & (1 << 4)) > 0:
99+
s += ", APP_VALID"
100+
if (status & (1 << 3)) > 0:
101+
s += ", DATA_READY"
102+
if (status & 1) > 0:
103+
s += ", ERROR"
104+
return s
105+
106+
def decode_error(self, error_id):
107+
if (error_id & (1 << 0)) > 0:
108+
s += ", The CCS811 received an I²C write request addressed to this station but with invalid register address ID"
109+
if (error_id & (1 << 1)) > 0:
110+
s += ", The CCS811 received an I²C read request to a mailbox ID that is invalid"
111+
if (error_id & (1 << 2)) > 0:
112+
s += ", The CCS811 received an I²C request to write an unsupported mode to MEAS_MODE"
113+
if (error_id & (1 << 3)) > 0:
114+
s += ", The sensor resistance measurement has reached or exceeded the maximum range"
115+
if (error_id & (1 << 4)) > 0:
116+
s += ", The Heater current in the CCS811 is not in range"
117+
if (error_id & (1 << 5)) > 0:
118+
s += ", The Heater voltage is not being applied correctly"
119+
return "Error: " + s[2:]
120+
121+
def measure(self):
122+
data = self.read(8, self._ALG_RESULT_DATA)
123+
eCO2 = data[0] * 256 + data[1]
124+
eTVOC = data[2] * 256 + data[3]
125+
status = data[4]
126+
error_id = data[5]
127+
raw_data = 256 * data[6] + data[7]
128+
raw_current = raw_data >> 10
129+
raw_voltage = (raw_data & ((1 << 10) - 1)) * (1.65 / 1023)
130+
131+
result = {"eCO2": eCO2, "eTVOC": eTVOC, "status": status, "error_id": error_id}
132+
133+
if error_id > 0:
134+
raise RuntimeError(self.decodeError(error_id))
135+
return result

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy