
[![Arduino CI](https://github.com/RobTillaart/ACD3100/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![Arduino-lint](https://github.com/RobTillaart/ACD3100/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/ACD3100/actions/workflows/arduino-lint.yml)
[![JSON check](https://github.com/RobTillaart/ACD3100/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/ACD3100/actions/workflows/jsoncheck.yml)
[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/ACD3100.svg)](https://github.com/RobTillaart/ACD3100/issues)

[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/ACD3100/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/ACD3100.svg?maxAge=3600)](https://github.com/RobTillaart/ACD3100/releases)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/ACD3100.svg)](https://registry.platformio.org/libraries/robtillaart/ACD3100)


# ACD3100

Arduino library for the ACD3100 CO2 sensor (I2C).


## Description

**Experimental**

The documentation is an adjusted copy of the ACD10 readme.md, as the
ACD3100 is very similar to the ACD10 but some details differ.

**Work in progress Work in progress Work in progress Work in progress**

This library is to use the Aosong ACD3100 CO2 sensor.
Besides CO2 concentration this sensor also provides a temperature reading.

The CO2 concentration supported by the sensor has a range from 400 ~ 5000 ppm ±(50ppm + 5% reading).
This makes the sensor applicable for outdoor and indoor measurements in
a normal building setting. 
The sensor is not suitable for CO2 heavy "industrial" environments. 
See also datasheet warning below.

**Warning** The temperature range the sensor can measure is **UNKNOWN**
as there is no documentation how to convert the raw data to meaningful one.
As far as information could tell this temperature is for Aosong internal use.
If you can decode the temperature bits please let me know.

The sensor can be read over I2C and over Serial.
This library only support the I2C interface (see hardware notes below).
The Serial mode is **NOT** supported by this library.

Feedback as always is welcome.

### Datasheet warning

_Do not apply this product to safety protection devices or emergency stop equipment, 
and any other applications that may cause personal injury due to the product's failure._


### Pre-heat period

When the sensor starts up it has a pre-heat period of 120 seconds.
The library provides functions to check the time since the constructor is called.
Note that this not necessarily implies that the sensor is ON.
During the preheat period one can make measurements but one should use those
carefully as these are less accurate than after the preheat period.


### Calibration

Also important is the calibration of the sensor, although done in the factory,
a CO2 sensor needs regular calibration. See datasheet for details.


### Power

The sensor must be powered with 5V and uses about 225 mW.
This implies the sensor uses 50 mA (@5V) and needs a separate power supply. 
One must connect GND from the power supply to the GND of the MCU.


### Operating conditions

|  parameter          |  value        |  notes  |
|:--------------------|:-------------:|:--------|
|  temperature        |  0°C~ +50°C   |  keep away from freezing cold or direct sunlight.
|  humidity           |  0% ~ 95% RH  |  keep in non-condensing conditions.
|  Refresh frequency  |  2 seconds    |


### Hardware


```
             TOPVIEW ACD3100
         +--------------------+
   pin 5 | o S                |
   pin 6 | o C            R o |  pin 1
   pin 7 | o D            T o |  pin 2
   pin 8 | o V            G o |  pin 3
   pin 9 | o G            5 o |  pin 4
         |                    |
         +--------------------+
```

**Note the code chars are on the bottom side of the sensor!**


|  pin  |  code  |   name   |  description      |  Notes  |
|:-----:|:------:|:--------:|:------------------|:-------:|
|   1   |   R    |  SDA/RX  |  I2C data         |  3-5V
|   2   |   T    |  SCL/TX  |  I2C clock        |  3-5V
|   3   |   G    |  GND     |  Ground           |
|   4   |   5    |  VCC     |  Power +5V        |  separate power supply needed.
|   5   |   S    |  SET     |  select com mode  |  HIGH (or n.c.) => I2C, LOW => Serial
|       |        |          |                   |
|   6   |   C    |          |                   |  unknown (clock?)
|   7   |   D    |          |                   |  unknown (data?)
|   8   |   V    |          |                   |  unknown (ground?)
|   9   |   G    |          |                   |  unknown (voltage?)


If pin 5 is not connected or connected to HIGH, **I2C** is selected (default).
If pin 5 is connected to GND (LOW), Serial / UART mode is selected.
This latter serial mode is **NOT** supported by this library.


### Related

- https://emariete.com/en/sensor-co2-mh-z19b/
- https://emariete.com/en/sensor-co2-low-consumption-mh-z1311a-winsen/
- https://revspace.nl/MHZ19
- https://www.co2.earth/ - current outdoor CO2 level can be used for calibrating.
- https://keelingcurve.ucsd.edu/ - historical outdoor CO2 level.
- https://github.com/RobTillaart/ACD10
- https://github.com/RobTillaart/ACD3100
- https://github.com/RobTillaart/MTP40C
- https://github.com/RobTillaart/MTP40F
- https://github.com/RobTillaart/Cozir


### Tested

TODO: Test on Arduino UNO and ESP32


## I2C

The device has a fixed I2C address of 0x2A (42) so only one sensor per I2C bus can be used.
The I2C communication supports 3-5V so any 3.3V MCU should be able to connect.
Do not forget appropriate pull up resistors on the I2C SDA and SCL lines.

If one needs more sensors there are some options.
- One could use an I2C multiplexer (see below)
- One could use an MCU with multiple I2C buses.
- One could use a (Two-Wire compatible) SW I2C (outside scope of this library).


Using the VCC as a Chip Select is not advised as the ACD3100
has a preheat time of 2 minutes. 
Every time the power is shut off the pre-heat would run again internally.
It is unclear what effect this has on the lifetime and quality of the sensor.


### I2C multiplexing

Sometimes you need to control more devices than possible with the default
address range the device provides.
This is possible with an I2C multiplexer e.g. TCA9548 which creates up 
to eight channels (think of it as I2C subnets) which can use the complete 
address range of the device. 

Drawback of using a multiplexer is that it takes more administration in 
your code e.g. which device is on which channel. 
This will slow down the access, which must be taken into account when
deciding which devices are on which channel.
Also note that switching between channels will slow down other devices 
too if they are behind the multiplexer.

- https://github.com/RobTillaart/TCA9548

See example **TCA9548_demo_ACD3100.ino**


### I2C Performance

Only test **readSensor()** as that is the main function.


|  Clock     |  time (us)  |  Notes  |
|:----------:|:-----------:|:--------|
|   100 KHz  |             |  default 
|   200 KHz  |             |
|   300 KHz  |             |
|   400 KHz  |             |
|   500 KHz  |             |
|   600 KHz  |             |


TODO: run performance sketch on hardware.


## Interface

```cpp
#include "ACD3100.h"
```

### Constructor

- **ACD3100(TwoWire \*wire = &Wire)** optional select I2C bus.
- **bool begin()** checks if device is visible on the I2C bus.
- **bool isConnected()** Checks if device address can be found on I2C bus.
- **uint8_t getAddress()** Returns the fixed address 0x2A (42).


### PreHeat

PreHeat functions assume the sensor is (and stays) connected to power.

- **bool preHeatDone()** returns true 120 seconds after constructor is called.
- **uint32_t preHeatMillisLeft()** returns the time in milliseconds
left before preHeat is complete.


### Request and Read

The interface of the sensor is made asynchronous as there is a delay needed
of around 80 milliseconds between a request for new data and the availability
of that new data.

- **int requestSensor()** request a new measurement / data.  
This must be called before the sensor can be read by **readSensor()**
- **bool requestReady()** has enough time passed since the call to **requestSensor()**
for the acquisition to happen and to call **readSensor()**?
- **int readSensor()** read the values from the sensor.
Returns status, 0 == OK, other values are error-codes.
- **uint32_t getCO2Concentration()** get the last read CO2 measurement in 
PPM from the device.
Multiple calls will give the same value until new measurement is made.
- **uint16_t getTemperature()** get the last read temperature from the device.
Multiple calls will give the same value until new measurement is made.
- **uint32_t lastRead()** returns the moment of last **readSensor()** in milliseconds
since start.
Note the sensor can be read only once every two seconds, less often is better.
The library does not guard this two seconds interval (yet).
- **void setRequestTime(uint8_t milliseconds = 80)** set the time to make a measurement.
Default = 80 milliseconds.
This can be used to tweak / optimize the performance, so use with care!
Use 5~10 milliseconds above the minimal value the sensor still works.
- **uint8_t getRequestTime()** returns the current request time in milliseconds.


### Calibration

Read the datasheet about calibration process (twice).
Incorrect calibration leads to incorrect output.

- **void setManualCalibration(uint16_t value)** as the range of the device is 
from 400 to 5000, the parameter value should be in this range.
- **uint16_t readManualCalibration()** read back the set manual calibration value.

Note: One should wait 5 milliseconds between the calibration calls (see datasheet).


### Miscellaneous

- **void factoryReset()** idem.
- **bool readFactorySet()** Read back if factory reset was successful.
- **uint32_t readFirmwareVersion(char \* arr)** copies firmware version in array.
Minimum length off arr is 11.
- **uint32_t readSensorCode(char \* arr)** copies sensor code in array.
Minimum length of arr is 11.


### Debug

- **uint8_t getLastError()** returns last error of low level communication.


## Future

#### Must

- improve documentation
- get hardware to test (See ACD10 #11)

#### Should

- keep in sync with ACD10 where possible.

#### Could

#### Wont


## Support

If you appreciate my libraries, you can support the development and maintenance.
Improve the quality of the libraries by providing issues and Pull Requests, or
donate through PayPal or GitHub sponsors.

Thank you,


