-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
drivers: Add driver for MH-Z19 (CO2 sensor) #3778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# MH-Z19 CO2 sensor driver for MicroPython. | ||
# MIT license; Copyright (c) 2018 Fredrik Strupe | ||
|
||
import machine | ||
import utime | ||
|
||
|
||
class TimeoutError(Exception): | ||
pass | ||
|
||
|
||
class MHZ19: | ||
'''MH-Z19 CO2 sensor driver''' | ||
|
||
def __init__(self, pin, max_value=5000): | ||
''' | ||
Args: | ||
pin: the pin that the PWM pin on the MH-Z19 is connected to. | ||
max_value: upper bound of measuring range. usually 2000 or 5000. | ||
''' | ||
self.pin = pin | ||
self.max_value = max_value | ||
|
||
def _wait_on_condition(self, cond, timeout=5000): | ||
start = utime.ticks_ms() | ||
while not cond(): | ||
if utime.ticks_diff(utime.ticks_ms(), start) > timeout: | ||
raise TimeoutError | ||
|
||
def pwm_read(self): | ||
'''Read CO2 value via PWM pin. | ||
|
||
Reading usually takes 1-2 seconds. | ||
|
||
Returns: | ||
CO2 value in ppm (parts per million), with an accuracy of | ||
±(50 + result * 0.05) ppm. | ||
Raises: | ||
TimeoutError: if the reading takes more than 5 seconds. | ||
''' | ||
# Wait until a new cycle starts | ||
self._wait_on_condition(lambda: self.pin.value() == 0) | ||
self._wait_on_condition(lambda: self.pin.value() == 1) | ||
|
||
# Measure high and low duration during cycle | ||
t_h = machine.time_pulse_us(self.pin, 1, 1500000) / 1000 | ||
t_l = machine.time_pulse_us(self.pin, 0, 1500000) / 1000 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you test that this works and gives accurate readings? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did and yes, it does. The results are slightly different than before because of the higher precision in the time readings, but this is negligible compared to the accuracy of the sensor itself. In the datasheet the pulses are given in 2 ms increments though, so it may be possible to round the reading to the nearest increment for more consistent readings. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, that's good to know that it works well. I think this is a good demonstration of how to use the |
||
|
||
return self.max_value * (t_h - 2) / (t_h + t_l - 4) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This second wait shouldn't be needed because the
time_pulse_us
that follows will wait until the pin goes high.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I must've overlooked that in the docs.