0.1.1 MHZCO2

This commit is contained in:
rob tillaart 2023-01-07 15:48:40 +01:00
parent c530223132
commit aa6e97729c
19 changed files with 915 additions and 0 deletions

View File

@ -0,0 +1,28 @@
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

4
libraries/MHZCO2/.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,4 @@
# These are supported funding model platforms
github: RobTillaart

View 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

View 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

View 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$"

View File

@ -0,0 +1,24 @@
# Change Log MHZCO2
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.1] - 2023-06-01
- rename to MHZCO2
- create derived classes
- move code from .h to .cpp
- add lastMeasurement()
- improve documentation
- refactor, rewrite, optimize
## [0.1.x] - 2020-09-01
- add PWM and analog examples for MHZ19B
## [0.1.0] - 2020-05-05
- initial version (MHZ19B)

21
libraries/MHZCO2/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-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.

212
libraries/MHZCO2/MHZCO2.cpp Normal file
View File

@ -0,0 +1,212 @@
//
// FILE: MHZCO2.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: Arduino Library for MHZ series CO2 sensors
// DATE: 2020-05-05
// URL: https://github.com/RobTillaart/MHZCO2
#include "MHZCO2.h"
MHZCO2::MHZCO2()
{
_startTime = millis();
}
void MHZCO2::begin(Stream * str)
{
_str = str;
}
uint32_t MHZCO2::uptime()
{
return millis() - _startTime;
}
void MHZCO2::setPPM(uint16_t PPM)
{
_PPM = PPM;
uint8_t data[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
data[8] = PPM >> 8;
data[7] = PPM & 0xFF;
data[8] = checksum(data);
send(data, 9);
}
uint16_t MHZCO2::getPPM()
{
return _PPM;
}
int MHZCO2::measure()
{
_lastMeasurement = millis();
uint8_t data[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
data[8] = checksum(data);
send(data, 9);
int rv = receive(data);
// allow partial good data?
if ((rv == MHZCO2_OK) || (rv == MHZCO2_ERROR_CRC))
{
_CO2 = data[2] * 256 + data[3];
_temperature = data[4] - 40;
_accuracy = data[5];
_minCO2 = data[6] * 256 + data[7];
}
return rv;
}
uint32_t MHZCO2::lastMeasurement()
{
return _lastMeasurement;
}
int MHZCO2::getCO2()
{
return _CO2;
};
int MHZCO2::getTemperature()
{
return _temperature;
};
int MHZCO2::getAccuracy()
{
return _accuracy;
}
int MHZCO2::getMinCO2()
{
return _minCO2;
};
void MHZCO2::calibrateZero()
{
uint8_t data[9] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
data[8] = checksum(data);
send(data, 9);
}
void MHZCO2::calibrateSpan(uint16_t span)
{
uint8_t data[9] = {0xFF, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
data[3] = span >> 8;
data[4] = span & 0xFF;
data[8] = checksum(data);
send(data, 9);
}
void MHZCO2::calibrateAuto(bool mode)
{
uint8_t data[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
if (mode) data[3] = 0xA0;
data[8] = checksum(data);
send(data, 9);
}
/////////////////////////////////////////////////
//
// PROTECTED
//
void MHZCO2::send(uint8_t * data, uint8_t len)
{
_str->write(data, len);
_str->flush();
}
int MHZCO2::receive(uint8_t * answer)
{
uint32_t start = millis();
while (_str->available() == 0)
{
if (millis() - start > 1000) return MHZCO2_TIMEOUT;
}
for (uint8_t i = 0; i < 9; i++)
{
answer[i] = _str->read();
}
// verify checksum
if (answer[8] != checksum(answer)) return MHZCO2_ERROR_CRC;
return MHZCO2_OK;
}
uint8_t MHZCO2::checksum(uint8_t *arr)
{
uint8_t sum = 0;
for (uint8_t i = 1; i < 8; i++)
{
sum += arr[i];
}
sum = 0xFF - sum;
return sum + 1;
}
/* slightly faster
uint8_t MHZCO2::checksum(uint8_t *arr)
{
uint8_t sum = 0xFF;
for (uint8_t i = 1; i < 8; i++) sum -= arr[i];
return sum + 1;
}
*/
/////////////////////////////////////////////////////////
//
// DERIVED CLASSES
//
MHZ1311A::MHZ1311A() : MHZCO2()
{
}
MHZ19::MHZ19() : MHZCO2()
{
}
MHZ19B::MHZ19B() : MHZCO2()
{
}
MHZ19C::MHZ19C() : MHZCO2()
{
}
MHZ19D::MHZ19D() : MHZCO2()
{
}
MHZ19E::MHZ19E() : MHZCO2()
{
}
MTP40F::MTP40F() : MHZCO2()
{
}
// -- END OF FILE --

112
libraries/MHZCO2/MHZCO2.h Normal file
View File

@ -0,0 +1,112 @@
#pragma once
//
// FILE: MHZCO2.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.1
// PURPOSE: Arduino Library for MHZ series CO2 sensors
// DATE: 2020-05-05
// URL: https://github.com/RobTillaart/MHZCO2
//
#include "Arduino.h"
#define MHZCO2_LIB_VERSION (F("0.1.1"))
#define MHZCO2_OK 0
#define MHZCO2_TIMEOUT -10
#define MHZCO2_ERROR_CRC -11
class MHZCO2
{
public:
MHZCO2();
// Stream is either a HW or SW serial.
void begin(Stream * str);
uint32_t uptime();
// PPM = 2000, 5000, 10000 (other values unknown)
// check datasheet
void setPPM(uint16_t PPM);
uint16_t getPPM();
// MEASUREMENT
int measure();
uint32_t lastMeasurement();
int getCO2();
int getTemperature();
int getAccuracy();
int getMinCO2();
// CALIBRATION
// USE WITH CARE => READ DATASHEET!
void calibrateZero();
void calibrateSpan(uint16_t span);
void calibrateAuto(bool mode = true);
protected:
Stream * _str;
uint32_t _startTime;
uint32_t _lastMeasurement;
uint16_t _PPM = 0;
int _CO2 = 0;
int _temperature = 0;
int _accuracy = 0;
int _minCO2 = 0;
void send(uint8_t * data, uint8_t len);
int receive(uint8_t * answer);
uint8_t checksum(uint8_t * arr);
};
/////////////////////////////////////////////////////////
//
// DERIVED CLASSES
//
class MHZ1311A : public MHZCO2
{
public:
MHZ1311A();
};
class MHZ19 : public MHZCO2
{
public:
MHZ19();
};
class MHZ19B : public MHZCO2
{
public:
MHZ19B();
};
class MHZ19C : public MHZCO2
{
public:
MHZ19C();
};
class MHZ19D : public MHZCO2
{
public:
MHZ19D();
};
class MHZ19E : public MHZCO2
{
public:
MHZ19E();
};
class MTP40F : public MHZCO2
{
public:
MTP40F();
};
// -- END OF FILE --

110
libraries/MHZCO2/README.md Normal file
View File

@ -0,0 +1,110 @@
[![Arduino CI](https://github.com/RobTillaart/MHZCO2/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![Arduino-lint](https://github.com/RobTillaart/MHZCO2/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/MHZCO2/actions/workflows/arduino-lint.yml)
[![JSON check](https://github.com/RobTillaart/MHZCO2/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/MHZCO2/actions/workflows/jsoncheck.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/MHZCO2/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/MHZCO2.svg?maxAge=3600)](https://github.com/RobTillaart/MHZCO2/releases)
# MHZCO2
Arduino Library for MHZ series CO2 sensors.
## Description
The MHZCO2 is an experimental library for the MHZ19B CO2 sensor with a Serial (RS232-TTL) interface and compatibles.
The library offers a base class and derived classes to prepare for specific functionality.
The base class is based upon the MHZ19B specification. This might change in the future as compatibles might differ on detail.
Reference: user manual MHZ129B 2019-04-25 version 1.4
#### Compatibles
This list is not verified although these devices should be compatible based upon datasheet.
| type | precision | notes |
|:-----------|:----------:|:--------|
| MHZ1311A | 50ppm + 5% | energy saving version
| MHZ19 | 50ppm + 5% |
| MHZ19B | 50ppm + 3% | test device
| MHZ19C | 50ppm + 5% |
| MHZ19D | 50ppm + 5% |
| MHZ19E | 50ppm + 5% |
| MTP40F | |
Note: The calibration of the MHZ1311A is different than MHZ19x series
If there are compatible devices missing in this list, please let me know.
#### Links
- https://emariete.com/en/sensor-co2-mh-z19b/
- https://emariete.com/en/sensor-co2-low-consumption-mh-z1311a-winsen/
- https://revspace.nl/MHZ19
## Connection
- check datasheet of your sensor.
## Interface
#### Constructor
- **MHZCO2()** base class constructor.
- **MHZ19()** constructor. Also 19B,C,D,E
- **MTP40F()** constructor.
- **void begin(Stream \* str)** set the Serial port to use, e.g Serial1.
- **uint32_t uptime()** returns milliseconds since 'instantiation'.
#### Range
- **void setPPM(uint16_t PPM)** PPM = 2000, 5000, 10000
- **uint16_t getPPM()** returns (cached) PPM value
#### Measure
- **int measure()** workhorse, send command to read the sensor.
- **uint32_t lastMeasurement()** timestamp in millis of last measurement
- **int getCO2()** returns CO2 PPM last measurement.
- **int getTemperature()** returns temperature last measurement.
- **int getAccuracy()** returns accuracy last measurement.
- **int getMinCO2()** returns minCO2 last measurement.
The latter two might not be supported by all MH sensors.
#### Calibration
**WARNING:** use with care, read the datasheet as these commands may disrupt your sensor.
- **void calibrateZero()** Only use when sensor is at least 30 minutes in **400** PPM environment.
- **void calibrateSpan(uint16_t span)** Only use when sensor is at least 30 minutes in **2000** PPM environment.
- **void calibrateAuto(bool mode = true)**
## Future
#### Must
- improve documentation
- buy hardware MHZ19B / MHZ19C
- test with hardware
- verify checksum
- verify timeout
#### Should
- plotter example
- check 3000 PPM
#### Could
- extend unit tests
- add type info for derived classes?
- save RAM? possible?
#### Won't

View File

@ -0,0 +1,61 @@
//
// FILE: MHZCO2_PWM.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo MHZ library / sensor
// DATE: 2020-09-01
/*
DATASHEET P.?
Conversion between PWM output and concentration,
use interrupt pin 3 of Arduino UNO.
*/
#include "Arduino.h"
// adjust to calibrate.
const float MAX_CONCENTRATION = 2000.0;
volatile uint16_t width;
void IRQ()
{
static uint32_t start = 0;
int v = digitalRead(3);
if (v == HIGH) start = millis();
else width = millis() - start;
}
uint16_t PWM_conc()
{
noInterrupts();
uint16_t TimeHigh = width; // milliseconds
interrupts();
uint16_t concentration = round(((TimeHigh - 2) * MAX_CONCENTRATION) * 0.001);
return concentration;
}
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
attachInterrupt(digitalPinToInterrupt(3), IRQ, CHANGE);
}
void loop()
{
Serial.println(PWM_conc());
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,43 @@
//
// FILE: MHZCO2_analog.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo MHZ library / sensor
// DATE: 2020-09-01
/*
DATASHEET P.7
Conversion between analog voltage output and concentration,
take 0.4V ~ 2V as an example:
Vo(V) = 0.4V + (2.0V - 0.4V) * C(concentration ppm) / range(ppm)
C = (Vo - 0.4 V ) * range / 1.6;
*/
void setup()
{
Serial.begin(115200);
// Serial.println(__FILE__);
}
void loop()
{
uint16_t C = concentration(A0, 2000);
Serial.println(C);
}
uint16_t concentration(int port, uint16_t maxC)
{
float volt = analogRead(port) * (5.0 / 1023.0);
uint16_t C = (volt - 0.4) * maxC / 1.6;
return C;
}
// -- END OF FILE --

View File

@ -0,0 +1,28 @@
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

View File

@ -0,0 +1,42 @@
//
// FILE: MHZCO2_serial1.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo for MEGA / 2560 with Serial1
// DATE: 2020-09-01
#include "MHZCO2.h"
MHZ19B MHZ19B;
void setup()
{
Serial.begin(115200);
// Serial.println(__FILE__);
MHZ19B.begin(&Serial1);
Serial1.begin(9600);
}
void loop()
{
MHZ19B.measure();
Serial.print("CO2: ");
Serial.println(MHZ19B.getCO2());
Serial.print("MCO2: ");
Serial.println(MHZ19B.getMinCO2());
Serial.print("Temp: ");
Serial.println(MHZ19B.getTemperature());
Serial.print("Accu: ");
Serial.println(MHZ19B.getAccuracy());
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,48 @@
//
// FILE: MHZCO2_sw_serial.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo
// DATE: 2020-09-01
#include "SoftwareSerial.h"
#include "MHZCO2.h"
const int TX = 4;
const int RX = 5;
SoftwareSerial ss(TX, RX);
MHZ19B MHZ19B;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
MHZ19B.begin(&ss);
ss.begin(9600);
}
void loop()
{
MHZ19B.measure();
Serial.print("CO2: ");
Serial.println(MHZ19B.getCO2());
Serial.print("MCO2: ");
Serial.println(MHZ19B.getMinCO2());
Serial.print("Temp: ");
Serial.println(MHZ19B.getTemperature());
Serial.print("Accu: ");
Serial.println(MHZ19B.getAccuracy());
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,37 @@
# Syntax Colouring Map For MHZCO2
# Data types (KEYWORD1)
MHZCO2 KEYWORD1
MHZ1311A KEYWORD1
MHZ19 KEYWORD1
MHZ19B KEYWORD1
MHZ19C KEYWORD1
MHZ19D KEYWORD1
MTP40F KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
uptime KEYWORD2
setPPM KEYWORD2
getPPM KEYWORD2
measure KEYWORD2
getCO2 KEYWORD2
getTemperature KEYWORD2
getAccuracy KEYWORD2
getMinCO2 KEYWORD2
calibrateZero KEYWORD2
calibrateSpan KEYWORD2
calibrateAuto KEYWORD2
# Constants (LITERAL1)
MHZCO2_LIB_VERSION LITERAL1
MHZCO2_OK LITERAL1
MHZCO2_TIMEOUT LITERAL1
MHZCO2_ERROR_CRC LITERAL1

View File

@ -0,0 +1,23 @@
{
"name": "MHZCO2",
"keywords": "MHZ1311A,MHZ19,MHZ19B,MHZ19C,MHZ19D,MHZ19E,MTP40F",
"description": "Arduino Library for MHZ series CO2 sensors.",
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/MHZCO2.git"
},
"version": "0.1.1",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",
"headers": "MHZCO21.h"
}

View File

@ -0,0 +1,11 @@
name=MHZCO2
version=0.1.1
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino Library for MHZ series CO2 sensors.
paragraph=MHZ1311A,MHZ19,MHZ19B,MHZ19C,MHZ19D,MHZ19E,MTP40F
category=Sensors
url=https://github.com/RobTillaart/MHZCO2
architectures=*
includes=MHZCO2.h
depends=

View File

@ -0,0 +1,63 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// DATE: 2022-12-01
// PURPOSE: Arduino Library for MHZ series CO2 sensors
// https://github.com/RobTillaart/MHZCO2
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual)
// assertNotEqual(expected, actual)
// assertLess(expected, actual)
// assertMore(expected, actual)
// assertLessOrEqual(expected, actual)
// assertMoreOrEqual(expected, actual)
// assertTrue(actual)
// assertFalse(actual)
// assertNull(actual)
#include <ArduinoUnitTests.h>
#include "Arduino.h"
#include "MHZCO2.h"
unittest_setup()
{
fprintf(stderr, "MHZCO2_LIB_VERSION: %s\n", (char *) MHZCO2_LIB_VERSION);
}
unittest_teardown()
{
}
unittest(test_constants)
{
assertEqual( 0, MHZCO2_OK);
assertEqual( -10, MHZCO2_TIMEOUT);
assertEqual( -11, MHZCO2_ERROR_CRC);
}
unittest(test_constructor)
{
MHZCO2 A;
MHZ19B B;
MTP40F C;
assertEqual( 0, A.uptime());
assertEqual( 0, B.uptime());
assertEqual( 0, C.uptime());
}
unittest_main()
// -- END OF FILE --