Erriez MH-Z19B CO2 sensor library for Arduino  1.0.0
This is a MH-Z19B CO2 sensor library for Arduino with a small footprint.
ErriezMHZ19B.cpp
Go to the documentation of this file.
1 /*
2  * MIT License
3  *
4  * Copyright (c) 2020-2023 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 
49 ErriezMHZ19B::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 
189 int8_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 
281 int8_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 
345 int8_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 
409 uint8_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.
Definition: ErriezMHZ19B.h:51
#define MHZ19B_CMD_GET_VERSION
Command get firmware version (NOT DOCUMENTED)
Definition: ErriezMHZ19B.h:66
#define MHZ19B_CMD_GET_RANGE
Command get range detection (NOT DOCUMENTED)
Definition: ErriezMHZ19B.h:65
#define MHZ19B_CMD_SET_RANGE
Command set detection range.
Definition: ErriezMHZ19B.h:61
@ MHZ19B_RANGE_5000
Range 5000 ppm (Default)
Definition: ErriezMHZ19B.h:84
@ MHZ19B_RANGE_2000
Range 2000 ppm.
Definition: ErriezMHZ19B.h:83
#define MHZ19B_READ_INTERVAL_MS
Minimum response time between CO2 reads (EXPERIMENTALLY DEFINED)
Definition: ErriezMHZ19B.h:48
@ MHZ19B_RESULT_ERR_TIMEOUT
Response timeout.
Definition: ErriezMHZ19B.h:75
@ MHZ19B_RESULT_OK
Response OK.
Definition: ErriezMHZ19B.h:72
@ MHZ19B_RESULT_ERR_CRC
Response CRC error.
Definition: ErriezMHZ19B.h:74
@ MHZ19B_RESULT_ARGUMENT_ERROR
Response argument error.
Definition: ErriezMHZ19B.h:76
@ MHZ19B_RESULT_ERROR
Response error.
Definition: ErriezMHZ19B.h:73
#define MHZ19B_WARMING_UP_TIME_MS
3 minutes warming-up time after power-on before valid data returned
Definition: ErriezMHZ19B.h:45
#define MHZ19B_CMD_READ_CO2
Command read CO2 concentration.
Definition: ErriezMHZ19B.h:58
#define MHZ19B_CMD_GET_AUTO_CAL
Command get auto calibration status (NOT DOCUMENTED)
Definition: ErriezMHZ19B.h:64
#define MHZ19B_CMD_CAL_ZERO_POINT
Command calibrate zero point at 400ppm.
Definition: ErriezMHZ19B.h:59
#define MHZ19B_SERIAL_RX_TIMEOUT_MS
Response timeout between 15..120 ms at 9600 baud works reliable for all commands.
Definition: ErriezMHZ19B.h:54
#define MHZ19B_CMD_SET_AUTO_CAL
Command set auto calibration on/off.
Definition: ErriezMHZ19B.h:57
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.