mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
0.1.0 ACD10
This commit is contained in:
parent
d06dc4dfcd
commit
392b865332
30
libraries/ACD10/.arduino-ci.yml
Normal file
30
libraries/ACD10/.arduino-ci.yml
Normal file
@ -0,0 +1,30 @@
|
||||
platforms:
|
||||
rpipico:
|
||||
board: rp2040:rp2040:rpipico
|
||||
package: rp2040:rp2040
|
||||
gcc:
|
||||
features:
|
||||
defines:
|
||||
- ARDUINO_ARCH_RP2040
|
||||
warnings:
|
||||
flags:
|
||||
|
||||
packages:
|
||||
rp2040:rp2040:
|
||||
url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
|
||||
|
||||
compile:
|
||||
# Choosing to run compilation tests on 2 different Arduino platforms
|
||||
platforms:
|
||||
- uno
|
||||
# - due
|
||||
# - zero
|
||||
# - leonardo
|
||||
- m4
|
||||
- esp32
|
||||
- esp8266
|
||||
# - mega2560
|
||||
- rpipico
|
||||
|
||||
libraries:
|
||||
- "printHelpers"
|
4
libraries/ACD10/.github/FUNDING.yml
vendored
Normal file
4
libraries/ACD10/.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: RobTillaart
|
||||
|
13
libraries/ACD10/.github/workflows/arduino-lint.yml
vendored
Normal file
13
libraries/ACD10/.github/workflows/arduino-lint.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
name: Arduino-lint
|
||||
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: arduino/arduino-lint-action@v1
|
||||
with:
|
||||
library-manager: update
|
||||
compliance: strict
|
17
libraries/ACD10/.github/workflows/arduino_test_runner.yml
vendored
Normal file
17
libraries/ACD10/.github/workflows/arduino_test_runner.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Arduino CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
runTest:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.6
|
||||
- run: |
|
||||
gem install arduino_ci
|
||||
arduino_ci.rb
|
18
libraries/ACD10/.github/workflows/jsoncheck.yml
vendored
Normal file
18
libraries/ACD10/.github/workflows/jsoncheck.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: JSON check
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '**.json'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: json-syntax-check
|
||||
uses: limitusus/json-syntax-check@v1
|
||||
with:
|
||||
pattern: "\\.json$"
|
||||
|
360
libraries/ACD10/ACD10.cpp
Normal file
360
libraries/ACD10/ACD10.cpp
Normal file
@ -0,0 +1,360 @@
|
||||
//
|
||||
// FILE: ACD10.cpp
|
||||
// AUTHOR: Rob Tillaart
|
||||
// DATE: 2023-09-25
|
||||
// VERSION: 0.1.0
|
||||
// PUPROSE: Arduino library for for I2C ACD10 CO2 sensor
|
||||
// URL: https://github.com/RobTillaart/ACD10
|
||||
// http://www.aosong.com/en/products-77.html
|
||||
|
||||
|
||||
#include "ACD10.h"
|
||||
|
||||
|
||||
ACD10::ACD10(TwoWire *wire)
|
||||
{
|
||||
_wire = wire;
|
||||
_error = 0;
|
||||
_lastRead = 0;
|
||||
_concentration = 0;
|
||||
_temperature = 0;
|
||||
_start = millis();
|
||||
}
|
||||
|
||||
|
||||
#if defined (ESP8266) || defined(ESP32)
|
||||
bool ACD10::begin(uint8_t sda, uint8_t scl)
|
||||
{
|
||||
_wire->begin(sda, scl);
|
||||
if (! isConnected())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool ACD10::begin()
|
||||
{
|
||||
_wire->begin();
|
||||
if (! isConnected())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ACD10::isConnected()
|
||||
{
|
||||
_wire->beginTransmission(_address);
|
||||
return (_wire->endTransmission() == 0);
|
||||
}
|
||||
|
||||
|
||||
uint8_t ACD10::getAddress()
|
||||
{
|
||||
return _address;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//
|
||||
// READ CO2
|
||||
//
|
||||
bool ACD10::preHeatDone()
|
||||
{
|
||||
return preHeatMillisLeft() == 0;
|
||||
}
|
||||
|
||||
|
||||
uint32_t ACD10::preHeatMillisLeft()
|
||||
{
|
||||
uint32_t delta = millis() - _start;
|
||||
if (delta >= 120000UL) return 0;
|
||||
return 120000UL - delta;
|
||||
}
|
||||
|
||||
|
||||
int ACD10::requestSensor()
|
||||
{
|
||||
uint8_t buf[2] = { 0x03, 0x00 };
|
||||
_requestStart = millis();
|
||||
return _command(buf, 2);
|
||||
}
|
||||
|
||||
|
||||
bool ACD10::requestReady()
|
||||
{
|
||||
if (_requestStart == 0) // no request pending.
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ((millis() - _requestStart) > _requestTime);
|
||||
}
|
||||
|
||||
|
||||
int ACD10::readSensor()
|
||||
{
|
||||
if (requestReady() == false)
|
||||
{
|
||||
return ACD10_NOT_READY;
|
||||
}
|
||||
|
||||
uint8_t buf[10];
|
||||
if (_request(buf, 9) != 0)
|
||||
{
|
||||
return ACD10_REQUEST_ERROR;
|
||||
}
|
||||
_requestStart = 0; // set no request pending.
|
||||
|
||||
// CRC CHECK
|
||||
if (buf[2] != _crc8(&buf[0], 2))
|
||||
{
|
||||
// Serial.println("CRC error 1");
|
||||
return ACD10_CRC_ERROR;
|
||||
}
|
||||
if (buf[5] != _crc8(&buf[3], 2))
|
||||
{
|
||||
// Serial.println("CRC error 2");
|
||||
return ACD10_CRC_ERROR;
|
||||
|
||||
}
|
||||
if (buf[8] != _crc8(&buf[6], 2))
|
||||
{
|
||||
// Serial.println("CRC error 3");
|
||||
return ACD10_CRC_ERROR;
|
||||
}
|
||||
|
||||
// DUMP
|
||||
// for (int i = 0; i < 9; i++)
|
||||
// {
|
||||
// if (buf[i] < 16) Serial.print("0");
|
||||
// Serial.print(buf[i], HEX);
|
||||
// Serial.print(" ");
|
||||
// }
|
||||
// Serial.println();
|
||||
|
||||
_concentration = buf[0];
|
||||
_concentration <<= 8;
|
||||
_concentration += buf[1];
|
||||
_concentration <<= 8;
|
||||
_concentration += buf[3];
|
||||
_concentration <<= 8;
|
||||
_concentration += buf[4];
|
||||
|
||||
_temperature = buf[6] * 256 + buf[7];
|
||||
_lastRead = millis();
|
||||
|
||||
return ACD10_OK;
|
||||
}
|
||||
|
||||
|
||||
uint32_t ACD10::getCO2Concentration()
|
||||
{
|
||||
return _concentration;
|
||||
}
|
||||
|
||||
|
||||
uint16_t ACD10::getTemperature()
|
||||
{
|
||||
return _temperature;
|
||||
}
|
||||
|
||||
|
||||
uint32_t ACD10::lastRead()
|
||||
{
|
||||
return _lastRead;
|
||||
}
|
||||
|
||||
|
||||
void ACD10::setRequestTime(uint8_t milliseconds)
|
||||
{
|
||||
_requestTime = milliseconds;
|
||||
}
|
||||
|
||||
|
||||
uint8_t ACD10::getRequestTime()
|
||||
{
|
||||
return _requestTime;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//
|
||||
// CALIBRATION
|
||||
//
|
||||
bool ACD10::setCalibrationMode(uint8_t mode)
|
||||
{
|
||||
if (mode > 1) return false;
|
||||
uint8_t buf[5] = { 0x53, 0x06, 0x00, 0x00, 0x00 };
|
||||
buf[3] = mode;
|
||||
buf[4] = _crc8(&buf[2], 2);
|
||||
// Serial.println(buf[4], HEX);
|
||||
_command(buf, 5);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint8_t ACD10::readCallibrationMode()
|
||||
{
|
||||
uint8_t buf[3] = { 0x53, 0x06, 0x00 };
|
||||
_command(buf, 2);
|
||||
_request(buf, 3);
|
||||
// if (buf[2] != _crc8(&buf[0], 2))
|
||||
// {
|
||||
// Serial.print(__FUNCTION__);
|
||||
// Serial.println(": CRC error");
|
||||
// }
|
||||
return buf[1];
|
||||
}
|
||||
|
||||
|
||||
bool ACD10::setManualCalibration(uint16_t value)
|
||||
{
|
||||
if ((value < 400) || (value > 5000)) return false;
|
||||
uint8_t buf[5] = { 0x52, 0x04, 0x00, 0x00, 0x00 };
|
||||
buf[3] = value && 0xFF;
|
||||
buf[2] = value >> 8;
|
||||
buf[4] = _crc8(&buf[2], 2);
|
||||
_command(buf, 5);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint16_t ACD10::readManualCalibration()
|
||||
{
|
||||
uint8_t buf[3] = { 0x52, 0x04, 0x00 };
|
||||
_command(buf, 2);
|
||||
_request(buf, 3);
|
||||
// if (buf[2] != _crc8(&buf[0], 2))
|
||||
// {
|
||||
// Serial.print(__FUNCTION__);
|
||||
// Serial.println(": CRC error");
|
||||
// }
|
||||
uint16_t value = buf[0] * 256 + buf[1];
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//
|
||||
// MISC
|
||||
//
|
||||
void ACD10::factoryReset()
|
||||
{
|
||||
uint8_t buf[3] = { 0x52, 0x02, 0x00};
|
||||
_command(buf, 3);
|
||||
}
|
||||
|
||||
|
||||
bool ACD10::readFactorySet()
|
||||
{
|
||||
uint8_t buf[3] = { 0x52, 0x02, 0x00 };
|
||||
_command(buf, 2);
|
||||
_request(buf, 3);
|
||||
// if (buf[2] != _crc8(&buf[0], 2))
|
||||
// {
|
||||
// Serial.print(__FUNCTION__);
|
||||
// Serial.println(": CRC error");
|
||||
// }
|
||||
return (buf[1] == 0x01);
|
||||
}
|
||||
|
||||
|
||||
void ACD10::readFirmwareVersion(char * arr)
|
||||
{
|
||||
uint8_t buf[2] = { 0xD1, 0x00 };
|
||||
_command(buf, 2);
|
||||
_request((uint8_t *) arr, 10);
|
||||
arr[10] = '\0';
|
||||
}
|
||||
|
||||
|
||||
void ACD10::readSensorCode(char * arr)
|
||||
{
|
||||
uint8_t buf[2] = { 0xD2, 0x01 };
|
||||
_command(buf, 2);
|
||||
_request((uint8_t *) arr, 10);
|
||||
arr[10] = '\0';
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//
|
||||
// DEBUG
|
||||
//
|
||||
int ACD10::getLastError()
|
||||
{
|
||||
int e = _error;
|
||||
_error = 0;
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////
|
||||
//
|
||||
// PRIVATE
|
||||
//
|
||||
|
||||
int ACD10::_command(uint8_t * arr, uint8_t size)
|
||||
{
|
||||
_wire->beginTransmission(_address);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
_wire->write(arr[i]);
|
||||
}
|
||||
_error = _wire->endTransmission();
|
||||
return _error;
|
||||
}
|
||||
|
||||
|
||||
int ACD10::_request(uint8_t * arr, uint8_t size)
|
||||
{
|
||||
int bytes = _wire->requestFrom(_address, size);
|
||||
if (bytes == 0)
|
||||
{
|
||||
_error = -1;
|
||||
return _error;
|
||||
}
|
||||
if (bytes < size)
|
||||
{
|
||||
_error = -2;
|
||||
return _error;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = _wire->read();
|
||||
}
|
||||
_error = 0;
|
||||
return _error;
|
||||
}
|
||||
|
||||
|
||||
uint8_t ACD10::_crc8(uint8_t * arr, uint8_t size)
|
||||
{
|
||||
uint8_t crc = 0xFF;
|
||||
for (int b = 0; b < size; b++)
|
||||
{
|
||||
crc ^= arr[b];
|
||||
for (int bit = 0x80; bit; bit >>= 1)
|
||||
{
|
||||
if (crc & 0x80)
|
||||
{
|
||||
crc <<= 1;
|
||||
crc ^= 0x31;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
||||
|
96
libraries/ACD10/ACD10.h
Normal file
96
libraries/ACD10/ACD10.h
Normal file
@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
//
|
||||
// FILE: ACD10.h
|
||||
// AUTHOR: Rob Tillaart
|
||||
// DATE: 2023-09-25
|
||||
// VERSION: 0.1.0
|
||||
// PUPROSE: Arduino library for for I2C ACD10 CO2 sensor
|
||||
// URL: https://github.com/RobTillaart/ACD10
|
||||
// http://www.aosong.com/en/products-77.html
|
||||
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Wire.h"
|
||||
|
||||
|
||||
#define ACD10_LIB_VERSION (F("0.1.0"))
|
||||
#define ACD10_DEFAULT_ADDRESS 0x2A
|
||||
|
||||
// ERROR CODES
|
||||
// values <> 0 are errors.
|
||||
#define ACD10_OK 0x00
|
||||
#define ACD10_CRC_ERROR 0x01
|
||||
#define ACD10_NOT_READY 0x10
|
||||
#define ACD10_REQUEST_ERROR 0x11
|
||||
|
||||
|
||||
class ACD10
|
||||
{
|
||||
public:
|
||||
ACD10(TwoWire *wire = &Wire);
|
||||
|
||||
#if defined (ESP8266) || defined(ESP32)
|
||||
bool begin(uint8_t sda, uint8_t scl);
|
||||
#endif
|
||||
bool begin();
|
||||
bool isConnected();
|
||||
uint8_t getAddress();
|
||||
|
||||
|
||||
// READ
|
||||
bool preHeatDone();
|
||||
uint32_t preHeatMillisLeft();
|
||||
|
||||
int requestSensor();
|
||||
bool requestReady();
|
||||
int readSensor();
|
||||
uint32_t getCO2Concentration();
|
||||
uint16_t getTemperature();
|
||||
uint32_t lastRead();
|
||||
|
||||
void setRequestTime(uint8_t milliseconds = 80);
|
||||
uint8_t getRequestTime();
|
||||
|
||||
|
||||
// CALIBRATION
|
||||
// 0 = manual 1 = auto
|
||||
bool setCalibrationMode(uint8_t mode);
|
||||
uint8_t readCallibrationMode();
|
||||
bool setManualCalibration(uint16_t value);
|
||||
uint16_t readManualCalibration();
|
||||
|
||||
|
||||
// MISC
|
||||
void factoryReset();
|
||||
bool readFactorySet();
|
||||
void readFirmwareVersion(char * arr); // should have length 11++
|
||||
void readSensorCode(char * arr); // should have length 11++
|
||||
|
||||
|
||||
// DEBUG
|
||||
int getLastError();
|
||||
|
||||
private:
|
||||
uint8_t _address = 0x2A;
|
||||
TwoWire* _wire;
|
||||
|
||||
int _command(uint8_t * arr, uint8_t size);
|
||||
int _request(uint8_t * arr, uint8_t size);
|
||||
uint8_t _crc8(uint8_t * arr, uint8_t size);
|
||||
|
||||
uint32_t _start = 0;
|
||||
uint32_t _lastRead = 0;
|
||||
uint32_t _concentration = 0; // why datasheet states 32 bit as 400-5000 fit in 16 bit??
|
||||
uint16_t _temperature = 0;
|
||||
uint8_t _requestTime = 80;
|
||||
uint32_t _requestStart = 0;
|
||||
uint8_t _error;
|
||||
};
|
||||
|
||||
|
||||
// -- END OF FILE --
|
||||
|
||||
|
||||
|
||||
|
||||
|
14
libraries/ACD10/CHANGELOG.md
Normal file
14
libraries/ACD10/CHANGELOG.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Change Log ACD10
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
|
||||
## [0.1.0] - 2023-09-25
|
||||
|
||||
- initial version
|
||||
|
||||
|
||||
|
21
libraries/ACD10/LICENSE
Normal file
21
libraries/ACD10/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023-2023 Rob Tillaart
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
260
libraries/ACD10/README.md
Normal file
260
libraries/ACD10/README.md
Normal file
@ -0,0 +1,260 @@
|
||||
|
||||
[![Arduino CI](https://github.com/RobTillaart/ACD10/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
|
||||
[![Arduino-lint](https://github.com/RobTillaart/ACD10/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/ACD10/actions/workflows/arduino-lint.yml)
|
||||
[![JSON check](https://github.com/RobTillaart/ACD10/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/ACD10/actions/workflows/jsoncheck.yml)
|
||||
[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/ACD10.svg)](https://github.com/RobTillaart/ACD10/issues)
|
||||
|
||||
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/ACD10/blob/master/LICENSE)
|
||||
[![GitHub release](https://img.shields.io/github/release/RobTillaart/ACD10.svg?maxAge=3600)](https://github.com/RobTillaart/ACD10/releases)
|
||||
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/ACD10.svg)](https://registry.platformio.org/libraries/robtillaart/ACD10)
|
||||
|
||||
|
||||
# ACD10
|
||||
|
||||
Arduino library for the ACD10 CO2 sensor (I2C).
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
**Experimental**
|
||||
|
||||
This library is to use the Aosong ACD10 CO2 sensor.
|
||||
Besides CO2 concentration it also provides a temperature reading.
|
||||
|
||||
The sensor can be read over I2C and over Serial, however this library
|
||||
only support the I2C interface.
|
||||
The device has a fixed I2C address of 0x2A (42).
|
||||
|
||||
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. It is not suitable for CO2 heavy "industrial" environments.
|
||||
|
||||
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,
|
||||
but note that this not necessarily implies 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.
|
||||
|
||||
Also important is the calibration of the sensor, although done in the factory,
|
||||
a CO2 sensor needs regular calibration. See datasheet for details.
|
||||
|
||||
The temperature range it can measure is: **TODO UNKNOWN YET**
|
||||
|
||||
The sensor must be powered with 5V and usese about 225 mW.
|
||||
This implies it uses 50 mA and needs a separate power supply. (Do not forget to connect GND.)
|
||||
The I2C communication supports 3-5V so any 3.3V MCU should be able to connect.
|
||||
(Do not forget pull up resistors).
|
||||
|
||||
|
||||
#### 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.
|
||||
|
||||
|
||||
#### Operating conditions
|
||||
|
||||
- temperature: 0°C~ +50°C ==> so keep away from freezing cold or direct sunlight.
|
||||
- humidity: 0% ~ 95% RH ==> non-condensing conditions.
|
||||
- Data refresh frequency: 2 seconds
|
||||
|
||||
|
||||
#### Hardware
|
||||
|
||||
```
|
||||
TOPVIEW ACD10
|
||||
+--------------------+
|
||||
pin 6 | o |
|
||||
pin 5 | o o | pin 1
|
||||
| o | pin 2
|
||||
| o | pin 3
|
||||
| o | pin 4
|
||||
| |
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
| pin | name | description | Notes |
|
||||
|:-----:|:--------:|:------------------|:-------:|
|
||||
| 1 | SDA/RX | I2C data | 3-5V
|
||||
| 2 | SCL/TX | I2C clock | 3-5V
|
||||
| 3 | GND | Ground |
|
||||
| 4 | VCC | Power +5V |
|
||||
| 5 | SET | select com mode | HIGH (or nc) => I2C, LOW => Serial
|
||||
| 6 | - | not connected |
|
||||
|
||||
If pin 5 is not connected, the default (HIGH) is to select I2C.
|
||||
If pin 5 is connected to GND, Serial / UART mode is selected.
|
||||
This latter 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/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).
|
||||
The I2C communication supports 3-5V so any 3.3V - 5.0V MCU should be able to connect.
|
||||
Do not forget pull up resistors on SDA and SCL lines.
|
||||
|
||||
|
||||
#### Multiple sensors.
|
||||
|
||||
The ACD10 sensor has a fixed I2C address 0x2A (42) so only
|
||||
one sensor per I2C bus can be used.
|
||||
If one needs more sensors there are some options.
|
||||
- One could use an I2C multiplexer - https://github.com/RobTillaart/TCA9548 (I2C 8 channel multiplexer)
|
||||
- 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 ACD10
|
||||
has a preheat time of 2 minutes.
|
||||
|
||||
|
||||
#### Performance I2C
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## Interface
|
||||
|
||||
```cpp
|
||||
#include "ACD10.h"
|
||||
```
|
||||
|
||||
#### Constructor
|
||||
|
||||
- **ACD10(uint8_t address = ACD10_DEFAULT_ADDRESS, TwoWire \*wire = &Wire)**
|
||||
- **bool begin(uint8_t sda, uint8_t scl)** (ESP32) initializes I2C,
|
||||
and checks if device is visible on the I2C bus.
|
||||
- **bool begin()** initializes I2C, and 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 address set in the constructor.
|
||||
|
||||
|
||||
#### 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.
|
||||
- **void setRequestTime(uint8_t milliseconds = 80)** set the time to make a measurement.
|
||||
Default = 80 milliseconds.
|
||||
This can be used to tweak / optimize performance a bit.
|
||||
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.
|
||||
|
||||
- **bool setCalibrationMode(uint8_t mode)** 0 = manual mode, 1 = automatic mode.
|
||||
returns false if mode out of range.
|
||||
- **uint8_t readCallibrationMode()** return set mode.
|
||||
- **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 is 11.
|
||||
- **uint32_t readSensorCode(char \* arr)** copies sensor code in array.
|
||||
Minimum length is 11.
|
||||
|
||||
|
||||
#### Debug
|
||||
|
||||
- **uint8_t getLastError()** returns last error of low level communication.
|
||||
|
||||
|
||||
## Future
|
||||
|
||||
#### Must
|
||||
|
||||
- improve documentation
|
||||
- get hardware to test
|
||||
|
||||
|
||||
#### Should
|
||||
|
||||
- investigate the acquisition time of 80 milliseconds
|
||||
- can it be made shorter by default?
|
||||
- improve error handling
|
||||
|
||||
|
||||
#### Could
|
||||
|
||||
- rethink function names?
|
||||
- create unit tests if possible
|
||||
|
||||
|
||||
#### 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,
|
||||
|
||||
|
41
libraries/ACD10/examples/ACD10_demo/ACD10_demo.ino
Normal file
41
libraries/ACD10/examples/ACD10_demo/ACD10_demo.ino
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// FILE: ACD10_demo.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// PUPROSE: test basic behaviour and performance
|
||||
|
||||
|
||||
#include "Wire.h"
|
||||
#include "ACD10.h"
|
||||
|
||||
|
||||
ACD10 mySensor;
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println(__FILE__);
|
||||
Serial.print("ACD10_LIB_VERSION: ");
|
||||
Serial.println(ACD10_LIB_VERSION);
|
||||
|
||||
Wire.begin();
|
||||
mySensor.begin();
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
mySensor.requestSensor();
|
||||
while (mySensor.requestReady() == false) delay(10);
|
||||
mySensor.readSensor();
|
||||
Serial.print(mySensor.getCO2Concentration());
|
||||
Serial.print("\t");
|
||||
Serial.print(mySensor.getTemperature());
|
||||
Serial.println();
|
||||
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
@ -0,0 +1,35 @@
|
||||
//
|
||||
// FILE: ACD10_isConnected.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// PUPROSE: test basic behaviour and performance
|
||||
|
||||
|
||||
#include "Wire.h"
|
||||
#include "ACD10.h"
|
||||
|
||||
|
||||
ACD10 mySensor;
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println(__FILE__);
|
||||
Serial.print("ACD10_LIB_VERSION: ");
|
||||
Serial.println(ACD10_LIB_VERSION);
|
||||
|
||||
Wire.begin();
|
||||
mySensor.begin();
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
Serial.println(mySensor.isConnected());
|
||||
Serial.println(mySensor.getAddress());
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
@ -0,0 +1,43 @@
|
||||
//
|
||||
// FILE: ACD10_preheat_test.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// PUPROSE: test basic behaviour and performance
|
||||
|
||||
|
||||
#include "Wire.h"
|
||||
#include "ACD10.h"
|
||||
|
||||
|
||||
ACD10 mySensor;
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println(__FILE__);
|
||||
Serial.print("ACD10_LIB_VERSION: ");
|
||||
Serial.println(ACD10_LIB_VERSION);
|
||||
|
||||
Wire.begin();
|
||||
mySensor.begin();
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
Serial.print(millis() / 1000);
|
||||
if (mySensor.preHeatDone())
|
||||
{
|
||||
Serial.println("\tOK");
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.print("\tWarming up\t");
|
||||
Serial.println(mySensor.preHeatMillisLeft());
|
||||
}
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
@ -0,0 +1,50 @@
|
||||
//
|
||||
// FILE: ACD10_readSensor.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// PUPROSE: test basic behaviour and performance
|
||||
|
||||
|
||||
#include "Wire.h"
|
||||
#include "ACD10.h"
|
||||
|
||||
|
||||
ACD10 mySensor;
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println(__FILE__);
|
||||
Serial.print("ACD10_LIB_VERSION: ");
|
||||
Serial.println(ACD10_LIB_VERSION);
|
||||
|
||||
Wire.begin();
|
||||
mySensor.begin();
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
// prepare a request every 5 seconds.
|
||||
if (millis() - mySensor.lastRead() > 5000) // millis
|
||||
{
|
||||
mySensor.requestSensor();
|
||||
}
|
||||
|
||||
// if request has had enough time read the sensor.
|
||||
if (mySensor.requestReady())
|
||||
{
|
||||
mySensor.readSensor();
|
||||
Serial.print(mySensor.getCO2Concentration());
|
||||
Serial.print("\t");
|
||||
Serial.print(mySensor.getTemperature());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
// do other things here.
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
@ -0,0 +1,42 @@
|
||||
//
|
||||
// FILE: ACD10_readSensorCode.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// PUPROSE: test basic behaviour and performance
|
||||
|
||||
|
||||
#include "Wire.h"
|
||||
#include "ACD10.h"
|
||||
|
||||
|
||||
ACD10 mySensor;
|
||||
char FWversion[12];
|
||||
char sensorCode[12];
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println(__FILE__);
|
||||
Serial.print("ACD10_LIB_VERSION: ");
|
||||
Serial.println(ACD10_LIB_VERSION);
|
||||
|
||||
Wire.begin();
|
||||
mySensor.begin();
|
||||
|
||||
mySensor.readFirmwareVersion(&FWversion[0]);
|
||||
Serial.print("FIRMWARE: \t");
|
||||
Serial.println(FWversion);
|
||||
|
||||
mySensor.readSensorCode(&sensorCode[0]);
|
||||
Serial.print("SENSORCODE: \t");
|
||||
Serial.println(sensorCode);
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
@ -0,0 +1,71 @@
|
||||
//
|
||||
// FILE: ACD10_readSensor_performance.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// PUPROSE: test basic behaviour and performance
|
||||
|
||||
|
||||
#include "Wire.h"
|
||||
#include "ACD10.h"
|
||||
|
||||
|
||||
ACD10 mySensor;
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println(__FILE__);
|
||||
Serial.print("ACD10_LIB_VERSION: ");
|
||||
Serial.println(ACD10_LIB_VERSION);
|
||||
|
||||
Wire.begin();
|
||||
mySensor.begin();
|
||||
|
||||
test(100000);
|
||||
test(200000);
|
||||
test(300000);
|
||||
test(400000);
|
||||
test(500000);
|
||||
test(600000);
|
||||
test(700000);
|
||||
test(800000);
|
||||
|
||||
Serial.println("\ndone...");
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void test(uint32_t speed)
|
||||
{
|
||||
Wire.setClock(speed);
|
||||
delay(100);
|
||||
|
||||
uint32_t start = micros();
|
||||
mySensor.requestSensor();
|
||||
uint32_t duration1 = micros() - start;
|
||||
|
||||
while(mySensor.requestReady() == false) delay(10);
|
||||
|
||||
start = micros();
|
||||
mySensor.readSensor();
|
||||
uint32_t duration2 = micros() - start;
|
||||
|
||||
Serial.print("| ");
|
||||
Serial.print(speed);
|
||||
Serial.print(" | ");
|
||||
Serial.print(duration1);
|
||||
Serial.print(" | ");
|
||||
Serial.print(duration2);
|
||||
Serial.print(" |");
|
||||
Serial.println();
|
||||
// reset I2C bus
|
||||
Wire.setClock(100000);
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
41
libraries/ACD10/keywords.txt
Normal file
41
libraries/ACD10/keywords.txt
Normal file
@ -0,0 +1,41 @@
|
||||
# Syntax Colouring Map For ACD10
|
||||
|
||||
# Data types (KEYWORD1)
|
||||
ACD10 KEYWORD1
|
||||
|
||||
|
||||
# Methods and Functions (KEYWORD2)
|
||||
begin KEYWORD2
|
||||
isConnected KEYWORD2
|
||||
getAddress KEYWORD2
|
||||
|
||||
preHeatDone KEYWORD2
|
||||
preHeatMillisLeft KEYWORD2
|
||||
|
||||
requestSensor KEYWORD2
|
||||
requestReady KEYWORD2
|
||||
readSensor KEYWORD2
|
||||
getCO2Concentration KEYWORD2
|
||||
getTemperature KEYWORD2
|
||||
lastRead KEYWORD2
|
||||
|
||||
setRequestTime KEYWORD2
|
||||
getRequestTime KEYWORD2
|
||||
|
||||
setCalibrationMode KEYWORD2
|
||||
readCallibrationMode KEYWORD2
|
||||
setManualCalibration KEYWORD2
|
||||
readManualCalibration KEYWORD2
|
||||
|
||||
factoryReset KEYWORD2
|
||||
readFactorySet KEYWORD2
|
||||
readFirmwareVersion KEYWORD2
|
||||
readSensorCode KEYWORD2
|
||||
|
||||
getLastError KEYWORD2
|
||||
|
||||
|
||||
# Constants (LITERAL1)
|
||||
ACD10_LIB_VERSION LITERAL1
|
||||
|
||||
|
23
libraries/ACD10/library.json
Normal file
23
libraries/ACD10/library.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "ACD10",
|
||||
"keywords": "CO2,environment",
|
||||
"description": "Arduino library for the ACD10 CO2 sensor.",
|
||||
"authors":
|
||||
[
|
||||
{
|
||||
"name": "Rob Tillaart",
|
||||
"email": "Rob.Tillaart@gmail.com",
|
||||
"maintainer": true
|
||||
}
|
||||
],
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/RobTillaart/ACD10.git"
|
||||
},
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"frameworks": "*",
|
||||
"platforms": "*",
|
||||
"headers": "ACD10.h"
|
||||
}
|
11
libraries/ACD10/library.properties
Normal file
11
libraries/ACD10/library.properties
Normal file
@ -0,0 +1,11 @@
|
||||
name=ACD10
|
||||
version=0.1.0
|
||||
author=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
sentence=Arduino library for the ACD10 CO2 sensor.
|
||||
paragraph=
|
||||
category=Sensors
|
||||
url=https://github.com/RobTillaart/ACD10
|
||||
architectures=*
|
||||
includes=ACD10.h
|
||||
depends=
|
66
libraries/ACD10/test/unit_test_001.cpp
Normal file
66
libraries/ACD10/test/unit_test_001.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
//
|
||||
// FILE: unit_test_001.cpp
|
||||
// AUTHOR: Rob Tillaart
|
||||
// DATE: 2023-09-25
|
||||
// PURPOSE: unit tests for the ACD10 library
|
||||
// https://github.com/RobTillaart/ACD10
|
||||
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
|
||||
//
|
||||
|
||||
// supported assertions
|
||||
// https://github.com/Arduino-CI/arduino_ci/blob/master/cpp/unittest/Assertion.h#L33-L42
|
||||
// ----------------------------
|
||||
// assertEqual(expected, actual)
|
||||
// assertNotEqual(expected, actual)
|
||||
// assertLess(expected, actual)
|
||||
// assertMore(expected, actual)
|
||||
// assertLessOrEqual(expected, actual)
|
||||
// assertMoreOrEqual(expected, actual)
|
||||
// assertTrue(actual)
|
||||
// assertFalse(actual)
|
||||
// assertNull(actual)
|
||||
// assertNotNull(actual)
|
||||
|
||||
#include <ArduinoUnitTests.h>
|
||||
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "ACD10.h"
|
||||
|
||||
|
||||
|
||||
unittest_setup()
|
||||
{
|
||||
fprintf(stderr, "ACD10_LIB_VERSION: %s\n", (char *) ACD10_LIB_VERSION);
|
||||
}
|
||||
|
||||
|
||||
unittest_teardown()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
unittest(test_constants)
|
||||
{
|
||||
assertEqual(ACD10_DEFAULT_ADDRESS, 0x2A);
|
||||
}
|
||||
|
||||
|
||||
unittest(test_constructor)
|
||||
{
|
||||
ACD10 acd;
|
||||
|
||||
assertEqual(acd.getAddress(), ACD10_DEFAULT_ADDRESS);
|
||||
assertEqual(acd.getRequestTime(), 80);
|
||||
assertEqual(acd.getLastError(), 0);
|
||||
assertEqual(acd.lastRead(), 0);
|
||||
assertEqual(acd.getCO2Concentration(), 0);
|
||||
assertEqual(acd.getTemperature(), 0);
|
||||
}
|
||||
|
||||
|
||||
unittest_main()
|
||||
|
||||
|
||||
// -- END OF FILE --
|
||||
|
Loading…
Reference in New Issue
Block a user