Skip to content

Commit f534018

Browse files
committed
1.5.3
1 parent 0e58746 commit f534018

File tree

15 files changed

+274
-242
lines changed

15 files changed

+274
-242
lines changed

README.md

+16-26
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
![Version](https://img.shields.io/badge/Version-v1.5.2-green.svg)
2-
3-
> See extras for I2C version using the SC16IS750 UART bridge (slightly outdated, can be updated on request).
1+
![Version](https://img.shields.io/badge/Version-v1.5.3-green.svg)
42

53
# MH-Z19 & MH-Z19B
64
A library for the MH-Z19 & MH-Z19B CO2 sensor on the Arduino platform which unlocks significant commands. Supports Hardware/Softeware serial and arduino based boads.
75

8-
### Recovery for Dysfunctional Sensors:
9-
See examples for the recovery code. *Note, Only use if your sensor is not recoverable by other means as it is somewhat untested, however from feedback and personal experience, it can often work.
6+
#### Recovery for Dysfunctional Sensors:
7+
See examples for the recovery code. *Note, Only use if your sensor is not recoverable by other means as it recklessly calls span.
108

119
### Features:
1210
* Automatically sends "autocalibration off".
@@ -15,17 +13,17 @@ See examples for the recovery code. *Note, Only use if your sensor is not recove
1513
* Communication error checking
1614
* Examples
1715

18-
>^*Transmittance is based upon the backwards projection of the raw value (which decreases with CO2) - see examples or [My Notes (Ravings)](https://myopenacuk-my.sharepoint.com/:x:/g/personal/jsd328_my_open_ac_uk/Ebyx4qxCBHxIk_bOBOtLkM4B40Dt9TZFd3CdI7Pv3NssMw?e=8Lr8bZ)*
16+
>*[My original notes (somewhat ravings) are here](https://myopenacuk-my.sharepoint.com/:x:/g/personal/jsd328_my_open_ac_uk/Ebyx4qxCBHxIk_bOBOtLkM4B40Dt9TZFd3CdI7Pv3NssMw?e=8Lr8bZ)*
1917
2018
### Commands
2119
---
2220

23-
| Additions | Existing | Testing |
24-
| :---: | :---: | :---: |
25-
| CO2 Unlimited | CO2 Limited | Temp @ 0.06C° Resolution |
26-
| CO2 as Raw | Temperature as Whole Integer | (please test the above, see Experimental) |
27-
| Custom Range / Span | Request CO2 Calibration | Custom ABC |
28-
| Reset Sensor | ABC On / Off | Zero Calibration (range byte) |
21+
| Additions | Existing |
22+
| :---: | :---: |
23+
| CO2 Unlimited | CO2 Limited |
24+
| CO2 as Raw | Temperature as Whole Integer |
25+
| Custom Range / Span | Request CO2 Calibration |
26+
| Reset Sensor | ABC On / Off |
2927
| Get Temperature Adjustment | Retrieve Accuracy |
3028
| Get Firmware Version | |
3129
| Get Background CO2 Value | |
@@ -43,7 +41,7 @@ If you are having issues with specific boards, please contact me (find my detail
4341

4442
### "A Bit About the Sensor"
4543
---
46-
**Advice:** The MH-Z19 works best in the Range of 2000ppm, outside of this accuracy begins to fall away. This is supported by documentation by also by features such as the Analog Out. I would suggest keeping to this range if you need accuracy and a maximum of 5000ppm.
44+
**Advice:** The MH-Z19 works best in the Range of 2000ppm, outside of this accuracy begins to fall away; this is supported by documentation. I would suggest keeping to this range if you require accuracy.
4745

4846
**Relevant Datasheets**
4947

@@ -54,33 +52,26 @@ If you are having issues with specific boards, please contact me (find my detail
5452
* The Chinese datasheet for the JST MH-Z19B version (more detailed): [MH-Z19B JST](https://datasheet.lcsc.com/szlcsc/1901021600_Zhengzhou-Winsen-Elec-Tech-MH-Z19_C242514.pdf).
5553

5654
**Auto Calibration:**
57-
The MH-Z19 is a sensor that is designed to be powered on and rarely turned off. The sensor calibrates over time (if autocalibration is on) tuning the Zero at the end of each "ABC" (auto calibration) period (0 - 24hrs currently). After 3 weeks, a value is stored to represent it's accuracy, this can be requested using getAccuracy(). ABC must be disabled by sending the appropriate command before the end of the ABC period to ensure it remains off - this is handled by the library.
55+
The MH-Z19 is a sensor that is designed to be powered on and rarely turned off. The sensor calibrates over time (if autocalibration is on) using the lowest CO2 observed in the prior 24 hours). After 3 weeks, a value is stored with an accuracy rating, this can be requested using getAccuracy(). ABC must be disabled each day, however this is handled by the library.
5856

5957
**Calibration:**
6058
If you plan to manually calibrate sensor (in my experience this is often be better) then it's important to be aware that Zero calibration does not refer to 0ppm (often a nitrogen environment), instead it refers to 400ppm.
6159

6260
**Background Calibration:** It's currently unclear how to change this, if possible at all. The value stored on the MH-Z19 and is set to 400ppm. This is used as the zeroing point.
6361

64-
**Span:** This should be sent after calibrateZero() is used and if readings after reset have failed. I would advise to be set to 2000 as it represents the segment of radiation that interferes with the active element of the sensor.
65-
6662
**Zero Calibration:** This can be made in two ways: By pulling the zero HD low (0V) for 7 Secs, or be sending command 135 (0x87). As above, the Zero refers to the background CO2 value of 400ppm, not 0ppm. Currently testing is the ability to send an adjustment with command at byte 7, however it is unclear the affects this has.
6763

68-
**Range:** This is essentially your CO2 scale high/low. 2000ppm is the most accurate and advised. Changing the value will adjust all your readings slightly. However, more importantly this limits the received value from command 134 (0x86). Therefore, it can be useful to use a software alarm.
64+
**Range:** This is essentially your highest and lowest CO2 being measured. 2000ppm is advised. Changing the value usually requires span calibration (diffiuclt), however if you intend to measure abvoe 2000 ppm this can increase accuracy.
6965

70-
All parameters can be bypassed using command 132 (0x84), however this requires manual calculations from the Raw value.
66+
**Span:** I highly recommend avoiding this command unless you have the equipment to do so. It requires the sensor to be at the ppm you are setting it to, e.g. 2000ppm. Roughly, it's difference between lowest and highest range points, for this sensor, it's the same value as range. From tiral and error, it's usually best sent last in the calibrations sequence.
7167

7268
**Alarm:** The analog output is located on the brown wire on the JST version. On the non-JST version it can be found on the far side, beside the Rx pin. It's unclear at the moment how to change the threshold and is not affected by Range. However, it is possible to attach an amplifier to the Analog Out pin and create an interrupt.
7369

7470
**Analog Out:** An additional feature of MH-Z19. The output in mV corresponds to ppm when using a range of 2000ppm. Alternatively, calculations can be made to adjust the value (See Examples).
7571

7672
### Main Priorities:
7773
---
78-
79-
- [x] Include most useful and working recovered commands
80-
81-
- [ ] Confirm corret units of CO2 Raw
82-
83-
- [ ] Determine the functioning of sending byte 7 with Zero Calibration and it's relationship with Range
74+
- [ ] Reduce memory usage
8475

8576
### Additional Disclaimer
8677
---
@@ -100,8 +91,7 @@ This library was originaly inspired by Strange-V's work! https://github.com/stra
10091

10192
### Feedback
10293
---
103-
This is one of my first long pieces of code after starting the journey 6 months ago, I'm still learning the ropes - so constructive feedback is more than welcome; jdwifwaf@gmail.com
94+
This is one of my first pieces of code, so lots of room for imporvement, feel free to provide constructive feedback; jdwifwaf@gmail.com
10495

105-
Also, feel free to improve on the project and propose appropriate changes.
10696

10797
>If this library was particularly helpful, and you feel like funding a replacement sensor (brutalised from testing!) [![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square&logo=appveyor)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9MJYH22A92LWG&source=url)

examples/BasicUsage/BasicUsage.ino

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
#include <Arduino.h>
22
#include "MHZ19.h"
3-
#include <SoftwareSerial.h> // Remove if using HardwareSerial or Arduino package without SoftwareSerial support
3+
#include <SoftwareSerial.h> // Remove if using HardwareSerial
44

55
#define RX_PIN 10 // Rx pin which the MHZ19 Tx pin is attached to
66
#define TX_PIN 11 // Tx pin which the MHZ19 Rx pin is attached to
77
#define BAUDRATE 9600 // Device to MH-Z19 Serial baudrate (should not be changed)
88

99
MHZ19 myMHZ19; // Constructor for library
10-
1110
SoftwareSerial mySerial(RX_PIN, TX_PIN); // (Uno example) create device to MH-Z19 serial
12-
//HardwareSerial mySerial(1); // (ESP32 Example) create device to MH-Z19 serial
1311

1412
unsigned long getDataTimer = 0;
1513

@@ -18,7 +16,6 @@ void setup()
1816
Serial.begin(9600); // Device to serial monitor feedback
1917

2018
mySerial.begin(BAUDRATE); // (Uno example) device to MH-Z19 serial start
21-
//mySerial.begin(BAUDRATE, SERIAL_8N1, RX_PIN, TX_PIN); // (ESP32 Example) device to MH-Z19 serial start
2219
myMHZ19.begin(mySerial); // *Serial(Stream) refence must be passed to library begin().
2320

2421
myMHZ19.autoCalibration(); // Turn auto calibration ON (OFF autoCalibration(false))

examples/Calibration/Calibration.ino

+36-87
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,84 @@
11
/*
2-
NOTE - the order of functions is important for correct
3-
calibration.
2+
# THIS SKETCH IS NOT NEEDED WITH AUTOCALIBRATION #
3+
4+
Unlike typical sensors, calibration() refers to the zero point where CO2 is 400ppm.
5+
This 400ppm comes from the average atmospheric value of 400ppm (or atleast was).
46
5-
Where other CO2 sensors require an nitrogen atmosphere to "zero"
6-
the sensor CO2 reference, the MHZ19 "zero" (confusingly) refers to the
7-
background CO2 level hardcoded into the device at 400ppm (getBackgroundCO2()
8-
sends a command to the device to retrieve the value);
7+
Depending on the sensor model, the harcoded value can usually be found by
8+
calling getBackgroundCO2();
99
10-
The best start for your sensor is to wait till CO2 values are as close to background
11-
levels as possible (currently an average of ~418ppm). Usually at night time and outside
12-
if possible, otherwise when the house has been unoccupied for as long as possible such.
10+
So if you intend to manually calibrate your sensor, it's usually best to do so at
11+
night and outside after 20 minutes of run time.
12+
13+
Instead if you're using autocalibration, then the sensor takes the lowest value observed
14+
in the last 24 hours and adjusts it's self accordingly over a few weeks.
1315
1416
HOW TO USE:
1517
1618
----- Hardware Method -----
1719
By pulling the zero HD low (0V) for 7 Secs as per the datasheet.
1820
1921
----- Software Method -----
20-
Run this example while in an ideal environment (I.e. restarting the device). Once
21-
restarted, disconnect MHZ19 from device and upload a new sketch to avoid
22-
recalibration.
22+
Run this sketch, disconnect MHZ19 from device after sketch ends (20+ minutes) and upload new
23+
code to avoid recalibration.
2324
2425
----- Auto calibration ----
25-
If you are using auto calibration, the sensor will adjust its self every 24 hours
26-
(note here, the auto calibration algorithm uses the lowest observe CO2 value
27-
for that set of 24 hours as the zero - so, if your device is under an environment
28-
which does not fall to these levels, consider turning this off in your setup code).
26+
As mentioned above if this is set to true, the sensor will adjust it's self over a few weeks
27+
according to the lowest observed CO2 values each day. *You don't need to run this sketch!
2928
30-
If autoCalibration is set to "false", then getting the background calibration levels
31-
correct at first try is essential.
3229
*/
3330

3431
#include <Arduino.h>
3532
#include "MHZ19.h"
36-
#include <SoftwareSerial.h> // Remove if using HardwareSerial or non-uno library compatable device
33+
#include <SoftwareSerial.h> // Remove if using HardwareSerial or non-uno library compatable device
3734

38-
#define RX_PIN 10
35+
#define RX_PIN 10
3936
#define TX_PIN 11
40-
#define BAUDRATE 9600 // Native to the sensor (do not change)
37+
#define BAUDRATE 9600
4138

4239
MHZ19 myMHZ19;
4340
SoftwareSerial mySerial(RX_PIN, TX_PIN); // Uno example
44-
//HardwareSerial mySerial(1); // ESP32 Example
4541

46-
unsigned long getDataTimer = 0;
42+
unsigned long timeElapse = 0;
4743

4844
void verifyRange(int range);
4945

5046
void setup()
5147
{
5248
Serial.begin(9600);
5349

54-
mySerial.begin(BAUDRATE); // Uno example: Begin Stream with MHZ19 baudrate
55-
//mySerial.begin(BAUDRATE, SERIAL_8N1, RX_PIN, TX_PIN); // ESP32 Example
56-
57-
myMHZ19.begin(mySerial); // *Important, Pass your Stream reference
58-
59-
/* ### setRange(value)###
60-
Basic:
61-
setRange(value) - set range to value (advise 2000 or 5000).
62-
setRange() - set range to 2000.
63-
64-
Advanced:
65-
Use verifyRange(int range) from this code at the bottom.
66-
*/
67-
68-
myMHZ19.setRange(2000);
69-
70-
/* ###calibrateZero()###
71-
Basic:
72-
calibrateZero() - request zero calibration
73-
74-
Advanced:
75-
In Testing.
76-
*/
77-
78-
myMHZ19.calibrateZero();
79-
80-
/* ### setSpan(value)###
81-
Basic:
82-
setSpan(value) - set span to value (strongly recommend 2000)
83-
setSpan() - set span to 2000;
84-
85-
*/
86-
87-
myMHZ19.setSpan(2000);
50+
mySerial.begin(BAUDRATE); // sensor serial
51+
myMHZ19.begin(mySerial); // pass to library
8852

89-
/* ###autoCalibration(false)###
90-
Basic:
91-
autoCalibration(false) - turns auto calibration OFF. (automatically sent before defined period elapses)
92-
autoCalibration(true) - turns auto calibration ON.
93-
autoCalibration() - turns auto calibration ON.
94-
95-
Advanced:
96-
autoCalibration(true, 12) - turns autocalibration ON and calibration period to 12 hrs (maximum 24hrs).
97-
*/
98-
99-
myMHZ19.autoCalibration(false);
100-
Serial.print("ABC Status: "); myMHZ19.getABC() ? Serial.println("ON") : Serial.println("OFF");
101-
53+
myMHZ19.autoCalibration(false); // make sure auto calibration is off
54+
Serial.print("ABC Status: "); myMHZ19.getABC() ? Serial.println("ON") : Serial.println("OFF"); // now print it's status
55+
56+
Serial.println("Waiting 20 minutes to stabalise...");
57+
/* if you don't need to wait (it's already been this amount of time), remove the next 2 lines */
58+
timeElapse = 12e5; // 20 minutes in milliseconds
59+
while(millis() < timeElapse) {}; // wait this duration
60+
61+
Serial.println("Calibrating..");
62+
myMHZ19.calibrate(); // Take a reading which be used as the zero point for 400 ppm
63+
10264
}
10365

10466
void loop()
10567
{
106-
if (millis() - getDataTimer >= 2000) // Check if interval has elapsed (non-blocking delay() equivilant)
68+
if (millis() - timeElapse >= 2000) // Check if interval has elapsed (non-blocking delay() equivilant)
10769
{
10870
int CO2;
10971
CO2 = myMHZ19.getCO2();
11072

11173
Serial.print("CO2 (ppm): ");
11274
Serial.println(CO2);
11375

114-
int8_t Temp; // Buffer for temperature
115-
Temp = myMHZ19.getTemperature(); // Request Temperature (as Celsius)
76+
int8_t Temp; // Buffer for temperature
77+
Temp = myMHZ19.getTemperature(); // Request Temperature (as Celsius)
11678

11779
Serial.print("Temperature (C): ");
11880
Serial.println(Temp);
11981

120-
getDataTimer = millis(); // Update inerval
82+
timeElapse = millis(); // Update inerval
12183
}
12284
}
123-
124-
void verifyRange(int range)
125-
{
126-
Serial.println("Requesting new range.");
127-
128-
myMHZ19.setRange(range); // request new range write
129-
130-
if (myMHZ19.getRange() == range) // Send command to device to return it's range value.
131-
Serial.println("Range successfully applied."); // Success
132-
133-
else
134-
Serial.println("Failed to apply range."); // Failed
135-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
# THIS SKETCH IS NOT NEEDED WITH AUTOCALIBRATION #
3+
4+
Please don't run this unless you are aware of the function Range and Span carry out.
5+
I would recommend avoiding this unless you have to.
6+
7+
Very rough explanation below.
8+
9+
Range:
10+
The range of which you intend to measure. I.e 0 - 2000 ppm, 0 - 4000 ppm.
11+
If you intend to increase it, be aware the sensor accuracy drops the wider the range.
12+
13+
Changing this, usually requires changing Span which is not recommended.
14+
15+
Span (Zero):
16+
The difference between the low and high range. In this case, that's 0 (we don't set this)
17+
and Range. So, it works out to be the same as the range.
18+
19+
There is a BIG difference however - Span is a zero command, meaning it needs a reference
20+
CO2 value like calibration(), which matches the span.
21+
22+
I.e. a span of 2000, needs a CO2 environemnt of 2000 for it to work.
23+
*/
24+
25+
/* final note, I've found it's best to calibrate in this order: setRange(2000) -> calibrate() -> zeroSpan(2000) */
26+
27+
#define EXAMPLE_HAS_RANGE 1 // include range example
28+
#define EXAMPLE_HAS_SPAN 0 // include span example
29+
30+
#include <Arduino.h>
31+
#include "MHZ19.h"
32+
#include <SoftwareSerial.h> // Uno example
33+
34+
#define RX_PIN 10
35+
#define TX_PIN 11
36+
#define BAUDRATE 9600
37+
38+
MHZ19 myMHZ19;
39+
SoftwareSerial mySerial(RX_PIN, TX_PIN); // Uno example
40+
41+
unsigned long timeElapse = 0;
42+
43+
void verifyRange(int range);
44+
45+
void setup()
46+
{
47+
Serial.begin(9600);
48+
49+
mySerial.begin(BAUDRATE); // Serial for the sensor
50+
myMHZ19.begin(mySerial); // Pass it to the library
51+
52+
myMHZ19.autoCalibration(false); // make sure auto calibration is off for this example
53+
Serial.print("ABC Status: "); myMHZ19.getABC() ? Serial.println("ON") : Serial.println("OFF"); // now print it's status
54+
55+
#if EXAMPLE_HAS_RANGE
56+
/* Setting the range appears to need no more than the value */
57+
myMHZ19.setRange(2000); // Set range to 2000
58+
Serial.println("Range set");
59+
#endif
60+
61+
#if EXAMPLE_HAS_SPAN
62+
/* For this you need a CO2 environment that matches your span. In doing so, you should wait 20 minutes before
63+
requesting the zero point */
64+
Serial.println("Waiting 20 minutes to stabalise...");
65+
timeElapse = 12e5; // 20 minutes in milliseconds
66+
while(millis() < timeElapse) {}; // wait this duration
67+
myMHZ19.zeroSpan(2000); // Set span to 2000
68+
#endif
69+
70+
}
71+
72+
void loop()
73+
{
74+
if (millis() - timeElapse >= 2000) // Check if interval has elapsed (non-blocking delay() equivilant)
75+
{
76+
int CO2;
77+
CO2 = myMHZ19.getCO2();
78+
79+
Serial.print("CO2 (ppm): ");
80+
Serial.println(CO2);
81+
82+
int8_t Temp; // Buffer for temperature
83+
Temp = myMHZ19.getTemperature(); // Request Temperature (as Celsius)
84+
85+
Serial.print("Temperature (C): ");
86+
Serial.println(Temp);
87+
88+
timeElapse = millis(); // Update inerval
89+
}
90+
}

0 commit comments

Comments
 (0)