ExCOâ‚‚Mini
ExCOâ‚‚Mini is a library to read carbon dioxide and temperature data from the COâ‚‚Mini USB sensor, also known as the RAD-0301.
This library only reads data from the device. If you want to record that data somewhere, see e.g. ddco2 for recording to StatsD.
Device setup
ExCOâ‚‚Mini currently only supports Linux. Your device needs to show up as a /dev/hidraw*
device, and it needs to be able to compile a small C utility that uses Linux-specific HID ioctls.
To allow a regular user to access the device, you can set up a udev
rule, such as the following:
KERNEL=="hidraw*", ATTRS{idVendor}=="04d9", ATTRS{idProduct}=="a052", GROUP="co2meter", MODE="0640", SYMLINK+="co2mini"
(Don't forget to udevadm control --reload-rules
.)
When the device is plugged in, this will set the device's group to co2meter
, set group read (g+r
) permissions on it, and create a /dev/co2mini
symlink to it. (Write permissions are not required.)
Note that the device will often report abnormally high readings immediately after being plugged in, then settle down to a more reasonable number.
Installation
ExCOâ‚‚Mini is available in Hex.
If you haven't already, start a project with mix new
.
Then, add ex_co2_mini
to your list of dependencies in mix.exs
:
def deps do
[
{:ex_co2_mini, "~> 0.1.3"}
]
end
Run mix deps.get
to pull ExCOâ‚‚Mini into your project, and you're good to go.
Usage
# Connect to the device and start reading data:
{:ok, reader} = ExCO2Mini.Reader.start_link(device: "/dev/co2mini")
# Subscribe to the Reader to start receiving raw data:
ExCO2Mini.Reader.subscribe(reader)
# Receive messages (do this repeatedly):
receive do
{^reader, {key, value}} -> Logger.debug("received #{key}=#{value}")
end
# Attach a Collector to the Reader to get data on demand:
{:ok, collector} = ExCO2Mini.Collector.start_link(reader: reader)
# Give it a few seconds to collect data:
Process.sleep(5_000)
# Now you should be able to retrieve COâ‚‚ (parts per million)
# and temperature (degrees Celsius) data:
co2 = ExCo2Mini.Collector.co2_ppm(collector)
temp = ExCO2Mini.Collector.temperature(collector)
Logger.info("CO₂ = #{co2} ppm, temperature = #{temp} °C")
Supervised Usage
If you want to set up a Reader and a Collector in a Supervisor, you can give them names and tell each to connect to the other. This will ensure that they continue to communicate even if one or the other is restarted:
children = [
{ExCO2Mini.Reader,
[
name: MyApp.Reader,
subscribers: [MyApp.Collector],
send_from_name: true,
device: "/dev/co2mini"
]},
{ExCO2Mini.Collector,
[
name: MyApp.Collector,
reader: MyApp.Reader,
subscribe_as_name: true
]}
]
Supervisor.start_link(
children,
strategy: :one_for_one,
name: MyApp.Supervisor
)
Using send_from_name
will ensure that the Reader sends messages using its name
option — i.e. messages will look like {MyApp.Reader, {key, value}}
— and subscribe_as_name
will ensure that the Collector subscribes using its own name
value (so the Reader can track it if it restarts).
See ddco2 for an example of this usage.
Documentation
Full documentation can be found at https://hexdocs.pm/ex_co2_mini.
Legal stuff
Copyright © 2019, Adrian Irving-Beer.
Parts of this code are based on code and data from the "Reverse-Engineering a low-cost USB CO₂ monitor" project. My thanks go out to Henryk Plötz, whose reverse engineering made this project possible.
ExCOâ‚‚Mini is released under the Apache 2 License and is provided with no warranty. This library is aimed at hobbyists and home enthusiasts, and should be used in non-life-critical situations only.