Erriez MH-Z19B CO2 sensor library for Arduino 1.0.1
This is a MH-Z19B CO2 sensor library for Arduino with a small footprint.
Loading...
Searching...
No Matches
ErriezMHZ19B.cpp
Go to the documentation of this file.
1/*
2 * MIT License
3 *
4 * Copyright (c) 2020-2026 Erriez
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
41#include "ErriezMHZ19B.h"
42
43
49ErriezMHZ19B::ErriezMHZ19B(Stream *serial) : _serial(serial), _tLastReadCO2(0)
50{
51}
52
59{
60 _serial = nullptr;
61}
62
71{
72 // Check valid PPM range
73 if (getRange() > 0) {
74 return true;
75 }
76
77 // Sensor not detected, or invalid range returned
78 // Try recover by calling setRange(MHZ19B_RANGE_5000);
79 return false;
80}
81
97{
98 // Wait at least 3 minutes after power-on
99 if (millis() < MHZ19B_WARMING_UP_TIME_MS) {
100#ifdef MHZ19B_SMART_WARMING_UP
101 static int16_t _lastCO2 = -1;
102 int16_t co2;
103
104 // Sensor returns valid data after CPU reset and keep sensor powered
105 co2 = readCO2();
106 if (_lastCO2 == -1) {
107 _lastCO2 = co2;
108 } else {
109 if (_lastCO2 != co2) {
110 // CO2 value changed since last read, no longer warming-up
111 _tLastReadCO2 = 0;
112 return false;
113 }
114 }
115#endif
116 // Warming-up
117 return true;
118 }
119
120 // Not warming-up
121 return false;
122}
123
134{
135 // Minimum CO2 read interval (Built-in LED flashes)
136 if ((millis() - _tLastReadCO2) > MHZ19B_READ_INTERVAL_MS) {
137 return true;
138 }
139
140 return false;
141}
142
159{
160 int16_t result;
161
162 // Set timestamp
163 _tLastReadCO2 = millis();
164
165 // Send command "Read CO2 concentration"
167
168 // Check result
169 if (result == MHZ19B_RESULT_OK) {
170 // 16-bit CO2 value in response Bytes 2 and 3
171 result = (rxBuffer[2] << 8) | rxBuffer[3];
172 }
173
174 return result;
175}
176
189int8_t ErriezMHZ19B::getVersion(char *version, uint8_t versionLen)
190{
191 int8_t result;
192
193 // Argument check
194 if (versionLen < 5) {
196 }
197
198 // Clear version
199 memset(version, 0, 5);
200
201 // Send command "Read firmware version" (NOT DOCUMENTED)
203
204 // Check result
205 if (result == MHZ19B_RESULT_OK) {
206 // Copy 4 ASCII characters to version array like "0443"
207 for (uint8_t i = 0; i < 4; i++) {
208 // Version in response Bytes 2..5
209 version[i] = rxBuffer[i + 2];
210 }
211 }
212
213 return result;
214}
215
222{
223 // Send "Set range" command
225 0x00, 0x00, 0x00, (MHZ19B_RANGE_2000 >> 8), (MHZ19B_RANGE_2000 & 0xff));
226}
227
234{
235 // Send "Set range" command
237 0x00, 0x00, 0x00, (MHZ19B_RANGE_5000 >> 8), (MHZ19B_RANGE_5000 & 0xff));
238}
239
253{
254 int16_t result;
255
256 // Send command "Read range" (NOT DOCUMENTED)
258
259 // Check result
260 if (result == MHZ19B_RESULT_OK) {
261 // Range is in Bytes 4 and 5
262 result = (rxBuffer[4] << 8) | rxBuffer[5];
263
264 // Check range according to documented specification
265 if ((result != MHZ19B_RANGE_2000) && (result != MHZ19B_RANGE_5000)) {
266 result = MHZ19B_RESULT_ERROR;
267 }
268 }
269
270 return result;
271}
272
281int8_t ErriezMHZ19B::setAutoCalibration(bool calibrationOn)
282{
283 // Send command "Set Automatic Baseline Correction (ABC logic function)"
284 return sendCommand(MHZ19B_CMD_SET_AUTO_CAL, (calibrationOn ? 0xA0 : 0x00));
285}
286
297{
298 int8_t result;
299
300 // Send command "Get Automatic Baseline Correction (ABC logic function)" (NOT DOCUMENTED)
302
303 // Check result
304 if (result == MHZ19B_RESULT_OK) {
305 // Response is located in Byte 7: 0 = off, 1 = on
306 result = rxBuffer[7] & 0x01;
307 }
308
309 return result;
310}
311
322{
323 // Send command "Zero Point Calibration"
325}
326
345int8_t ErriezMHZ19B::sendCommand(uint8_t cmd, byte b3, byte b4, byte b5, byte b6, byte b7)
346{
347 uint8_t txBuffer[MHZ19B_SERIAL_RX_BYTES] = { 0xFF, 0x01, cmd, b3, b4, b5, b6, b7, 0x00 };
348 int8_t result = MHZ19B_RESULT_OK;
349 unsigned long tStart;
350
351 // Check serial initialized
352 if (_serial == nullptr) {
353 return MHZ19B_RESULT_ERROR;
354 }
355
356 // Add CRC Byte
357 txBuffer[8] = calcCRC(txBuffer);
358
359 // Clear receive buffer
360 while (_serial->available()) {
361 _serial->read();
362 }
363
364 // Write serial data
365 _serial->write(txBuffer, sizeof(txBuffer));
366
367 // Flush serial data
368 _serial->flush();
369
370 // Clear receive buffer
371 memset(rxBuffer, 0, sizeof(rxBuffer));
372
373 // Wait until all data received from sensor with receive timeout protection
374 tStart = millis();
375 while (_serial->available() < MHZ19B_SERIAL_RX_BYTES) {
376 if ((millis() - tStart) >= MHZ19B_SERIAL_RX_TIMEOUT_MS) {
378 }
379 }
380
381 // Read response from serial buffer
382 _serial->readBytes(rxBuffer, MHZ19B_SERIAL_RX_BYTES);
383
384 // Check received Byte[0] == 0xFF and Byte[1] == transmit command
385 if ((rxBuffer[0] != 0xFF) || (rxBuffer[1] != cmd)) {
386 result = MHZ19B_RESULT_ERROR;
387 }
388
389 // Check received Byte[8] CRC
390 if (rxBuffer[8] != calcCRC(rxBuffer)) {
391 result = MHZ19B_RESULT_ERR_CRC;
392 }
393
394 // Return result
395 return result;
396}
397
398// ----------------------------------------------------------------------------
399// Private functions
400// ----------------------------------------------------------------------------
401
409uint8_t ErriezMHZ19B::calcCRC(uint8_t *data)
410{
411 byte crc = 0;
412
413 // Calculate CRC on 8 data Bytes
414 for (uint8_t i = 1; i < 8; i++) {
415 crc += data[i];
416 }
417 crc = 0xFF - crc;
418 crc++;
419
420 // Return calculated CRC
421 return crc;
422}
MH-Z19B CO2 sensor library for Arduino.
#define MHZ19B_SERIAL_RX_BYTES
Fixed 9 Bytes response.
#define MHZ19B_CMD_GET_VERSION
Command get firmware version (NOT DOCUMENTED)
#define MHZ19B_CMD_GET_RANGE
Command get range detection (NOT DOCUMENTED)
#define MHZ19B_CMD_SET_RANGE
Command set detection range.
@ MHZ19B_RANGE_5000
Range 5000 ppm (Default)
@ MHZ19B_RANGE_2000
Range 2000 ppm.
#define MHZ19B_READ_INTERVAL_MS
Minimum response time between CO2 reads (EXPERIMENTALLY DEFINED)
@ MHZ19B_RESULT_ERR_TIMEOUT
Response timeout.
@ MHZ19B_RESULT_OK
Response OK.
@ MHZ19B_RESULT_ERR_CRC
Response CRC error.
@ MHZ19B_RESULT_ARGUMENT_ERROR
Response argument error.
@ MHZ19B_RESULT_ERROR
Response error.
#define MHZ19B_WARMING_UP_TIME_MS
3 minutes warming-up time after power-on before valid data returned
#define MHZ19B_CMD_READ_CO2
Command read CO2 concentration.
#define MHZ19B_CMD_GET_AUTO_CAL
Command get auto calibration status (NOT DOCUMENTED)
#define MHZ19B_CMD_CAL_ZERO_POINT
Command calibrate zero point at 400ppm.
#define MHZ19B_SERIAL_RX_TIMEOUT_MS
Response timeout between 15..120 ms at 9600 baud works reliable for all commands.
#define MHZ19B_CMD_SET_AUTO_CAL
Command set auto calibration on/off.
int8_t setAutoCalibration(bool calibrationOn)
Enable or disable automatic calibration.
int8_t sendCommand(uint8_t cmd, byte b3=0, byte b4=0, byte b5=0, byte b6=0, byte b7=0)
Send serial command to sensor and read response.
int8_t getAutoCalibration()
Get status automatic calibration (NOT DOCUMENTED)
int8_t startZeroCalibration()
Start Zero Point Calibration manually at 400ppm.
bool isWarmingUp()
Check if sensor is warming-up after power-on.
ErriezMHZ19B(Stream *serial)
Constructor with serial Stream.
bool detect()
Detect MHZ19B sensor.
int8_t setRange2000ppm()
Set CO2 range 2000 ppm.
bool isReady()
Check minimum interval between CO2 reads.
int16_t getRange()
Get CO2 range in PPM (NOT DOCUMENTED)
int8_t getVersion(char *version, uint8_t versionLen)
Get firmware version (NOT DOCUMENTED)
int8_t setRange5000ppm()
Set CO2 range 5000 ppm.
~ErriezMHZ19B()
Destructor.
int16_t readCO2()
Read CO2 from sensor.