first commit

This commit is contained in:
stuce-bot 2025-06-30 20:47:33 +02:00
commit 5893b00dd2
1669 changed files with 1982740 additions and 0 deletions

View file

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

View file

@ -0,0 +1,105 @@
# M5Unit - ENV
## Overview
### SKU:U001 & U001-B & U001-C & U090 & U053-B & U053-D & U103
Contains M5Stack-**UNIT ENV & Hat ENV & UNIT BPS & UNIT CO2** series related case programs.
ENV is an environmental sensor with integrated SHT30 and QMP6988 internally to detect temperature, humidity, and atmospheric pressure data.
BPS is a barometer unit, which integrates the Bosch BMP280 pressure sensor to measure atmospheric pressure and estimate the altitude.
BPS(QMP6988) Unit is a barometer unit that uses QMP6988 barometric pressure sensor to measure atmospheric pressure and altitude estimation
CO2 is a photoacoustic Carbon Dioxide (CO2) Unit that will tell you the CO2 PPM (parts-per-million) composition of ambient air.
## Related Link
- [Unit ENVIV - Document & Datasheet](https://docs.m5stack.com/en/unit/ENV%E2%85%A3%20Unit)
- [Unit ENVIII - Document & Datasheet](https://docs.m5stack.com/en/unit/envIII)
- [Unit ENVII - Document & Datasheet](https://docs.m5stack.com/en/unit/envII)
- [Unit ENV - Document & Datasheet](https://docs.m5stack.com/en/unit/env)
- [Hat ENVIII - Document & Datasheet](https://docs.m5stack.com/en/hat/hat_envIII)
- [Hat ENVII - Document & Datasheet](https://docs.m5stack.com/en/hat/hat_envII)
- [Unit BPS - Document & Datasheet](https://docs.m5stack.com/en/unit/bps)
- [Unit BPS(QMP6988) - Document & Datasheet](https://docs.m5stack.com/en/unit/BPS(QMP6988))
- [Unit CO2 - Document & Datasheet](https://docs.m5stack.com/en/unit/co2)
## Required Libraries:
- [Adafruit_BMP280_Library](https://github.com/adafruit/Adafruit_BMP280_Library)
- [Adafruit_Sensor](https://github.com/adafruit/Adafruit_Sensor)
- [Sensirion I2C SCD4x](https://github.com/Sensirion/arduino-i2c-scd4x)
- [Sensirion I2C SHT4x](https://github.com/Sensirion/arduino-i2c-sht4x)
- [Sensirion Core](https://github.com/Sensirion/arduino-core)
## License
- [M5Unit-ENV - MIT](LICENSE)
---
## M5UnitUnified
Library for Unit ENV using [M5UnitUnified](https://github.com/m5stack/M5UnitUnified).
M5UnitUnified is a library for unified handling of various M5 units products.
### Supported units
- Unit CO2 (SKU:U103)
- Unit CO2L (SKU:U104)
- Unit ENVIII (SKU:U001-C)
- Unit ENVIV (SKU:U001-D)
- Unit ENVPro (SKU:U169)
- Unit TVOC (SKU:U088)
### SKU:U088
TVOC/eCO2 mini Unit is a digital multi-pixel gas sensor unit with integrated SGP30.
It mainly measures various VOC (volatile organic compounds) and H2 in the air. It can be programmed to detect TVOC (total volatile organic compounds) and eCO2 (equivalent carbon dioxide reading)Concentration measurement.
Typical measurement accuracy is 15% within the measurement range, the SGP30 reading is internally calibrated and output, which can maintain long-term stability. SGP30 uses I2C protocol communication with on-chip humidity compensation function, which can be turned on through an external humidity sensor.
If you need to obtain accurate results, you need to calibrate according to a known measurement source. SGP30 has a built-in calibration function. In addition, eCO2 is calculated based on the concentration of H2 and cannot completely replace "true" CO2 sensors for laboratory use.
## Related Link
See also examples using conventional methods here.
- [UnitTVOC/eCO2 & Datasheet](https://docs.m5stack.com/en/unit/tvoc)
### Include file
```cpp
#include <M5UnitUnifiedENV.h> // For UnitUnified
//#include <M5UnitENV.h> // When using M5UnitUnified, do not use it at the same time as conventional libraries
```
Supported units will be added in the future.
### Required Libraries:
- [M5UnitUnified](https://github.com/m5stack/M5UnitUnified)
- [M5Utility](https://github.com/m5stack/M5Utility)
- [M5HAL](https://github.com/m5stack/M5HAL)
The Bosch library is required by ENVPro to obtain values that cannot be obtained without using the Bosch library.
- [Bosch-BME68x-Library](https://github.com/boschsensortec/Bosch-BME68x-Library)
- [Bosch-BSEC2-Library](https://github.com/boschsensortec/Bosch-BSEC2-Library) (Excluding NanoC6)
### Examples
See also [examples/UnitUnified](examples/UnitUnified)
### Doxygen document
[GitHub Pages](https://m5stack.github.io/M5Unit-ENV/)
If you want to generate documents on your local machine, execute the following command
```
bash docs/doxy.sh
```
It will output it under docs/html
If you want to output Git commit hashes to html, do it for the git cloned folder.
#### Required
- [Doxyegn](https://www.doxygen.nl/)
- [pcregrep](https://formulae.brew.sh/formula/pcre2)
- [Git](https://git-scm.com/) (Output commit hash to html)

View file

@ -0,0 +1,41 @@
{
"build": {
"arduino": {
"memory_type": "qio_opi",
"ldscript": "esp32s3_out.ld",
"partitions": "default_8MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DARDUINO_M5STACK_ATOMS3R",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "dio",
"mcu": "esp32s3",
"variant": "m5stack_atoms3"
},
"connectivity": [
"bluetooth",
"wifi"
],
"frameworks": [
"arduino",
"espidf"
],
"name": "M5Stack AtomS3R",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.m5stack.com/en/core/AtomS3R",
"vendor": "M5Stack"
}

View file

@ -0,0 +1,33 @@
{
"build": {
"core": "esp32",
"extra_flags": [
"-DARDUINO_M5STACK_NANOC6"
],
"f_cpu": "160000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"mcu": "esp32c6",
"variant": "esp32c6"
},
"connectivity": [
"wifi"
],
"debug": {
"openocd_target": "esp32c6.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "M5Stack NanoC6",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194384,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.m5stack.com/en/core/M5NanoC6",
"vendor": "M5Stack"
}

View file

@ -0,0 +1,40 @@
{
"build": {
"arduino":{
"ldscript": "esp32_out.ld",
"partitions": "default_8MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DM5STACK_M5STICK_CPLUS2",
"-DBOARD_HAS_PSRAM",
"-mfix-esp32-psram-cache-issue",
"-mfix-esp32-psram-cache-strategy=memw",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "m5stick_c"
},
"connectivity": [
"wifi",
"bluetooth"
],
"frameworks": [
"arduino",
"espidf"
],
"name": "M5Stick-CPlus2",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"require_upload_port": true,
"speed": 1500000
},
"url": "https://docs.m5stack.com/en/core/M5StickC%20PLUS2",
"vendor": "M5Stack"
}

View file

@ -0,0 +1,12 @@
Import("env")
EXCLUDE_FILES = (env.GetProjectOption("custom_exclude_src_files") or []).split(' ')
print(EXCLUDE_FILES)
def skip_from_build(node):
np = node.get_path()
if any(ef in np for ef in EXCLUDE_FILES):
return None
return node
env.AddBuildMiddleware(skip_from_build, "*")

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
#!/bin/bash
# Please execute on repositry root
## Get version from library.properties
## Get git rev of HEAD
LIB_VERSION="$(pcregrep -o1 "^\s*version\s*=\s*(\*|\d+(\.\d+){0,3}(\.\*)?)" library.properties)"
#echo ${DOXYGEN_PROJECT_NUMBER}
DOXYGEN_PROJECT_NUMBER="${LIB_VERSION} git rev:$(git rev-parse --short HEAD)" doxygen docs/Doxyfile

View file

@ -0,0 +1,64 @@
/**
* @file ENV.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
DHT12 dht;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!dht.begin(&Wire, DHT12_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find DHT12");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (dht.update()) {
Serial.println("-----DHT12-----");
Serial.print("Temperature: ");
Serial.print(dht.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(dht.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file ENV_II.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV_II
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT3X sht3x;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,60 @@
/**
* @file ENV_III.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV_III
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
Serial.begin(115200);
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 21, 22, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,71 @@
/**
* @file ENV_IV.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV_IV
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT4X sht4;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!sht4.begin(&Wire, SHT40_I2C_ADDR_44, 21, 22, 400000U)) {
Serial.println("Couldn't find SHT4x");
while (1) delay(1);
}
// You can have 3 different precisions, higher precision takes longer
sht4.setPrecision(SHT4X_HIGH_PRECISION);
sht4.setHeater(SHT4X_NO_HEATER);
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht4.update()) {
Serial.println("-----SHT4X-----");
Serial.print("Temperature: ");
Serial.print(sht4.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht4.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,142 @@
/**
* @file ENV_PRO.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV_PRO
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* BME68x Sensor library: https://github.com/boschsensortec/Bosch-BME68x-Library
* BSEC2 Software Library: https://github.com/boschsensortec/Bosch-BSEC2-Library
*/
#include <bsec2.h>
/**
* @brief : This function checks the BSEC status, prints the respective error
* code. Halts in case of error
* @param[in] bsec : Bsec2 class object
*/
void checkBsecStatus(Bsec2 bsec);
/**
* @brief : This function is called by the BSEC library when a new output is
* available
* @param[in] input : BME68X sensor data before processing
* @param[in] outputs : Processed BSEC BSEC output data
* @param[in] bsec : Instance of BSEC2 calling the callback
*/
void newDataCallback(const bme68xData data, const bsecOutputs outputs,
Bsec2 bsec);
/* Create an object of the class Bsec2 */
Bsec2 envSensor;
/* Entry point for the example */
void setup(void) {
/* Desired subscription list of BSEC2 outputs */
bsecSensor sensorList[] = {
BSEC_OUTPUT_IAQ, BSEC_OUTPUT_RAW_TEMPERATURE,
BSEC_OUTPUT_RAW_PRESSURE, BSEC_OUTPUT_RAW_HUMIDITY,
BSEC_OUTPUT_RAW_GAS, BSEC_OUTPUT_STABILIZATION_STATUS,
BSEC_OUTPUT_RUN_IN_STATUS};
/* Initialize the communication interfaces */
Serial.begin(115200);
Wire.begin(21, 22);
/* Valid for boards with USB-COM. Wait until the port is open */
while (!Serial) delay(10);
/* Initialize the library and interfaces */
if (!envSensor.begin(BME68X_I2C_ADDR_HIGH, Wire)) {
checkBsecStatus(envSensor);
}
/* Subsribe to the desired BSEC2 outputs */
if (!envSensor.updateSubscription(sensorList, ARRAY_LEN(sensorList),
BSEC_SAMPLE_RATE_LP)) {
checkBsecStatus(envSensor);
}
/* Whenever new data is available call the newDataCallback function */
envSensor.attachCallback(newDataCallback);
Serial.println("BSEC library version " + String(envSensor.version.major) +
"." + String(envSensor.version.minor) + "." +
String(envSensor.version.major_bugfix) + "." +
String(envSensor.version.minor_bugfix));
}
/* Function that is looped forever */
void loop(void) {
/* Call the run function often so that the library can
* check if it is time to read new data from the sensor
* and process it.
*/
if (!envSensor.run()) {
checkBsecStatus(envSensor);
}
}
void newDataCallback(const bme68xData data, const bsecOutputs outputs,
Bsec2 bsec) {
if (!outputs.nOutputs) {
return;
}
Serial.println(
"BSEC outputs:\n\ttimestamp = " +
String((int)(outputs.output[0].time_stamp / INT64_C(1000000))));
for (uint8_t i = 0; i < outputs.nOutputs; i++) {
const bsecData output = outputs.output[i];
switch (output.sensor_id) {
case BSEC_OUTPUT_IAQ:
Serial.println("\tiaq = " + String(output.signal));
Serial.println("\tiaq accuracy = " +
String((int)output.accuracy));
break;
case BSEC_OUTPUT_RAW_TEMPERATURE:
Serial.println("\ttemperature = " + String(output.signal));
break;
case BSEC_OUTPUT_RAW_PRESSURE:
Serial.println("\tpressure = " + String(output.signal));
break;
case BSEC_OUTPUT_RAW_HUMIDITY:
Serial.println("\thumidity = " + String(output.signal));
break;
case BSEC_OUTPUT_RAW_GAS:
Serial.println("\tgas resistance = " + String(output.signal));
break;
case BSEC_OUTPUT_STABILIZATION_STATUS:
Serial.println("\tstabilization status = " +
String(output.signal));
break;
case BSEC_OUTPUT_RUN_IN_STATUS:
Serial.println("\trun in status = " + String(output.signal));
break;
default:
break;
}
}
}
void checkBsecStatus(Bsec2 bsec) {
if (bsec.status < BSEC_OK) {
Serial.println("BSEC error code : " + String(bsec.status));
} else if (bsec.status > BSEC_OK) {
Serial.println("BSEC warning code : " + String(bsec.status));
}
if (bsec.sensor.status < BME68X_OK) {
Serial.println("BME68X error code : " + String(bsec.sensor.status));
} else if (bsec.sensor.status > BME68X_OK) {
Serial.println("BME68X warning code : " + String(bsec.sensor.status));
}
}

View file

@ -0,0 +1,62 @@
/**
* @file Hat_ENVIII_M5StickC.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickC + Hat_ENVIII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
M5.begin();
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 0, 26, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 0, 26, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,62 @@
/**
* @file Hat_ENVIII_M5StickCPlus.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickCPlus + Hat_ENVIII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
M5.begin();
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 0, 26, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 0, 26, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,68 @@
/**
* @file Hat_ENVII_M5StickC.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickC + Hat_ENVII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
BMP280 bmp;
void setup() {
M5.begin();
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 0, 26, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 0, 26, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,68 @@
/**
* @file Hat_ENVII_M5StickCPlus.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickCPlus + Hat_ENVII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
BMP280 bmp;
void setup() {
M5.begin();
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 0, 26, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 0, 26, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitCO2
Required
- M5Unified: https://github.com/m5stack/M5Unified
*/
#include "main/PlotToSerial.cpp"

View file

@ -0,0 +1,99 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitCO2
*/
// #define USING_M5HAL // When using M5HAL
#include <M5Unified.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedENV.h>
namespace {
auto& lcd = M5.Display;
m5::unit::UnitUnified Units;
m5::unit::UnitCO2 unit;
} // namespace
void setup()
{
M5.begin();
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
#if defined(USING_M5HAL)
#pragma message "Using M5HAL"
// Using M5HAL
m5::hal::bus::I2CBusConfig i2c_cfg;
i2c_cfg.pin_sda = m5::hal::gpio::getPin(pin_num_sda);
i2c_cfg.pin_scl = m5::hal::gpio::getPin(pin_num_scl);
auto i2c_bus = m5::hal::bus::i2c::getBus(i2c_cfg);
if (!Units.add(unit, i2c_bus ? i2c_bus.value() : nullptr) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#else
#pragma message "Using Wire"
// Using TwoWire
Wire.end();
Wire.begin(pin_num_sda, pin_num_scl, 400000U);
if (!Units.add(unit, Wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#endif
M5_LOGI("M5UnitUnified has been begun");
M5_LOGI("%s", Units.debugInfo().c_str());
{
auto ret = unit.stopPeriodicMeasurement();
float offset{};
ret &= unit.readTemperatureOffset(offset);
uint16_t altitude{};
ret &= unit.readSensorAltitude(altitude);
uint16_t pressure{};
ret &= unit.readAmbientPressure(pressure);
bool asc{};
ret &= unit.readAutomaticSelfCalibrationEnabled(asc);
uint16_t ppm{};
ret &= unit.readAutomaticSelfCalibrationTarget(ppm);
ret &= unit.startPeriodicMeasurement();
M5.Log.printf(
" temp offset:%f\n"
" sensor altitude:%u\n"
"ambient pressure:%u\n"
" ASC enabled:%u\n"
" ASC target:%u\n",
offset, altitude, pressure, asc, ppm);
if (!ret) {
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
}
lcd.clear(TFT_DARKGREEN);
}
void loop()
{
M5.update();
Units.update();
if (unit.updated()) {
// Can be checked e.g. by serial plotters
M5.Log.printf(">CO2:%u\n>Temperature:%2.2f\n>Humidity:%2.2f\n", unit.co2(), unit.temperature(),
unit.humidity());
}
}

View file

@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitCO2L
Required
- M5Unified: https://github.com/m5stack/M5Unified
*/
#include "main/PlotToSerial.cpp"

View file

@ -0,0 +1,130 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitCO2L
*/
// #define USING_M5HAL // When using M5HAL
#include <M5Unified.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedENV.h>
namespace {
auto& lcd = M5.Display;
m5::unit::UnitUnified Units;
m5::unit::UnitCO2L unit;
} // namespace
using namespace m5::unit::scd4x;
void setup()
{
M5.begin();
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
#if defined(USING_M5HAL)
#pragma message "Using M5HAL"
// Using M5HAL
m5::hal::bus::I2CBusConfig i2c_cfg;
i2c_cfg.pin_sda = m5::hal::gpio::getPin(pin_num_sda);
i2c_cfg.pin_scl = m5::hal::gpio::getPin(pin_num_scl);
auto i2c_bus = m5::hal::bus::i2c::getBus(i2c_cfg);
if (!Units.add(unit, i2c_bus ? i2c_bus.value() : nullptr) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#else
#pragma message "Using Wire"
// Using TwoWire
Wire.end();
Wire.begin(pin_num_sda, pin_num_scl, 400000U);
if (!Units.add(unit, Wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#endif
M5_LOGI("M5UnitUnified has been begun");
M5_LOGI("%s", Units.debugInfo().c_str());
{
auto ret = unit.stopPeriodicMeasurement();
float offset{};
ret &= unit.readTemperatureOffset(offset);
uint16_t altitude{};
ret &= unit.readSensorAltitude(altitude);
uint16_t pressure{};
ret &= unit.readAmbientPressure(pressure);
bool asc{};
ret &= unit.readAutomaticSelfCalibrationEnabled(asc);
uint16_t ppm{};
ret &= unit.readAutomaticSelfCalibrationTarget(ppm);
uint16_t initialPeriod{}, standardPeriod{};
ret &= unit.readAutomaticSelfCalibrationInitialPeriod(initialPeriod);
ret &= unit.readAutomaticSelfCalibrationStandardPeriod(standardPeriod);
ret &= unit.startPeriodicMeasurement();
M5.Log.printf(
" temp offset:%f\n"
" sensor altitude:%u\n"
"ambient pressure:%u\n"
" ASC enabled:%u\n"
" ASC target:%u\n"
" initial period:%u\n"
" standard period:%u\n",
offset, altitude, pressure, asc, ppm, initialPeriod, standardPeriod);
if (!ret) {
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(1000);
}
}
}
lcd.clear(TFT_DARKGREEN);
}
void loop()
{
M5.update();
auto touch = M5.Touch.getDetail();
// Periodic
Units.update();
if (unit.updated()) {
// Can be checked e.g. by serial plotters
M5.Log.printf(">CO2:%u\n>Temperature:%2.2f\n>Humidity:%2.2f\n", unit.co2(), unit.temperature(),
unit.humidity());
}
// Single shot
if (M5.BtnA.wasClicked() || touch.wasClicked()) {
static bool all{}; // false: RHT only
all = !all;
M5.Log.printf("Try single shot %u, waiting measurement...\n", all);
unit.stopPeriodicMeasurement();
Data d{};
if (all) {
if (unit.measureSingleshot(d)) {
M5.Log.printf(" SingleAll: %u/%2.2f/%2.2f\n", d.co2(), d.temperature(), d.humidity());
}
} else {
if (unit.measureSingleshotRHT(d)) {
M5.Log.printf(" SingleRHT: %2.2f/%2.2f", d.temperature(), d.humidity());
}
}
unit.startPeriodicMeasurement();
}
}

View file

@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitENVIII
Required
- M5Unified: https://github.com/m5stack/M5Unified
*/
#include "main/PlotToSerial.cpp"

View file

@ -0,0 +1,153 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitENVIII
*/
#include <M5Unified.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedENV.h>
// #define USING_M5HAL // When using M5HAL
// Using single shot measurement If defined
// #define USING_SINGLE_SHOT
// Using combined unit if defined
#define USING_ENV3
namespace {
auto& lcd = M5.Display;
m5::unit::UnitUnified Units;
#if defined(USING_ENV3)
#pragma message "Using combined unit(ENV3)"
m5::unit::UnitENV3 unitENV3;
#else
#pragma message "Using each unit"
m5::unit::UnitSHT30 unitSHT30;
m5::unit::UnitQMP6988 unitQMP6988;
#endif
#if defined(USING_ENV3)
auto& sht30 = unitENV3.sht30;
auto& qmp6988 = unitENV3.qmp6988;
#else
auto& sht30 = unitSHT30;
auto& qmp6988 = unitQMP6988;
#endif
} // namespace
void setup()
{
M5.begin();
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
#if defined(USING_SINGLE_SHOT)
{
auto cfg = sht30.config();
cfg.start_periodic = false;
sht30.config(cfg);
}
{
auto cfg = qmp6988.config();
cfg.start_periodic = false;
qmp6988.config(cfg);
}
#endif
#if defined(USING_ENV3)
#if defined(USING_M5HAL)
#pragma message "Using M5HAL"
m5::hal::bus::I2CBusConfig i2c_cfg;
i2c_cfg.pin_sda = m5::hal::gpio::getPin(pin_num_sda);
i2c_cfg.pin_scl = m5::hal::gpio::getPin(pin_num_scl);
auto i2c_bus = m5::hal::bus::i2c::getBus(i2c_cfg);
if (!Units.add(unitENV3, i2c_bus ? i2c_bus.value() : nullptr) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#else
#pragma message "Using Wire"
Wire.end();
Wire.begin(pin_num_sda, pin_num_scl, 400000U);
if (!Units.add(unitENV3, Wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#endif
#else
#if defined(USING_M5HAL)
#pragma message "Using M5HAL"
m5::hal::bus::I2CBusConfig i2c_cfg;
i2c_cfg.pin_sda = m5::hal::gpio::getPin(pin_num_sda);
i2c_cfg.pin_scl = m5::hal::gpio::getPin(pin_num_scl);
auto i2c_bus = m5::hal::bus::i2c::getBus(i2c_cfg);
if (!Units.add(unitSHT30, i2c_bus ? i2c_bus.value() : nullptr) ||
!Units.add(unitQMP6988, i2c_bus ? i2c_bus.value() : nullptr) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#else
#pragma message "Using Wire"
Wire.begin(pin_num_sda, pin_num_scl, 400000U);
if (!Units.add(unitSHT30, Wire) || !Units.add(unitQMP6988, Wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#endif
#endif
M5_LOGI("M5UnitUnified has been begun");
M5_LOGI("%s", Units.debugInfo().c_str());
#if defined(USING_SINGLE_SHOT)
M5_LOGI("\n>>> Click BtnA to single shot measurement");
#endif
lcd.clear(TFT_DARKGREEN);
}
void loop()
{
M5.update();
Units.update();
#if defined(USING_SINGLE_SHOT)
if (M5.BtnA.wasClicked()) {
m5::unit::sht30::Data ds{};
if (sht30.measureSingleshot(ds)) {
M5.Log.printf(">SHT30Temp:%2.2f\n>Humidity:%2.2f\n", ds.temperature(), ds.humidity());
}
m5::unit::qmp6988::Data dq{};
if (qmp6988.measureSingleshot(dq)) {
M5.Log.printf(">QMP6988Temp:%2.2f\n>Pressure:%.2f\n", dq.temperature(), dq.pressure() * 0.01f);
}
}
#else
if (sht30.updated()) {
M5.Log.printf(">SHT30Temp:%2.2f\n>Humidity:%2.2f\n", sht30.temperature(), sht30.humidity());
}
if (qmp6988.updated()) {
M5.Log.printf(">QMP6988Temp:%2.2f\n>Pressure:%.2f\n", qmp6988.temperature(), qmp6988.pressure() * 0.01f);
}
#endif
}

View file

@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitENVIV
Required
- M5Unified: https://github.com/m5stack/M5Unified
*/
#include "main/PlotToSerial.cpp"

View file

@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitENVIV
*/
#include <M5Unified.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedENV.h>
#include <cmath>
#define USING_ENV4
namespace {
auto& lcd = M5.Display;
m5::unit::UnitUnified Units;
#if defined(USING_ENV4)
#pragma message "Using combined unit(ENV4)"
m5::unit::UnitENV4 unitENV4;
#else
#pragma message "Using each unit"
m5::unit::UnitSHT40 unitSHT40;
m5::unit::UnitBMP280 unitBMP280;
#endif
#if defined(USING_ENV4)
auto& sht40 = unitENV4.sht40;
auto& bmp280 = unitENV4.bmp280;
#else
auto& sht40 = unitSHT40;
auto& bmp280 = unitBMP280;
#endif
float calculate_altitude(const float pressure, const float seaLvhPa = 1013.25f)
{
return 44330.f * (1.0f - pow((pressure / 100.f) / seaLvhPa, 0.1903f));
}
} // namespace
void setup()
{
M5.begin();
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
{
using namespace m5::unit::bmp280;
auto cfg = bmp280.config();
cfg.osrs_pressure = Oversampling::X16;
cfg.osrs_temperature = Oversampling::X2;
cfg.filter = Filter::Coeff16;
cfg.standby = Standby::Time500ms;
bmp280.config(cfg);
}
#if defined(USING_ENV4)
Wire.end();
Wire.begin(pin_num_sda, pin_num_scl, 400000U);
if (!Units.add(unitENV4, Wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#else
Wire.end();
Wire.begin(pin_num_sda, pin_num_scl, 400000U);
if (!Units.add(unitSHT40, Wire) || !Units.add(unitBMP280, Wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#endif
M5_LOGI("M5UnitUnified has been begun");
M5_LOGI("%s", Units.debugInfo().c_str());
lcd.clear(TFT_DARKGREEN);
}
void loop()
{
M5.update();
Units.update();
if (sht40.updated()) {
M5.Log.printf(
">SHT40Temp:%.4f\n"
">Humidity:%.4f\n",
sht40.temperature(), sht40.humidity());
}
if (bmp280.updated()) {
auto p = bmp280.pressure();
M5.Log.printf(
">BMP280Temp:%.4f\n"
">Pressure:%.4f\n"
">Altitude:%.4f\n",
bmp280.temperature(), p * 0.01f /* To hPa */, calculate_altitude(p));
}
}

View file

@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitENVPro
Required
- M5Unified: https://github.com/m5stack/M5Unified
*/
#include "main/PlotToSerial.cpp"

View file

@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitENVPro
*/
#include <M5Unified.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedENV.h>
namespace {
auto& lcd = M5.Display;
m5::unit::UnitUnified Units;
m5::unit::UnitENVPro unit;
} // namespace
void setup()
{
M5.begin();
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
Wire.end();
Wire.begin(pin_num_sda, pin_num_scl, 400 * 1000U);
if (!Units.add(unit, Wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
M5_LOGI("M5UnitUnified has been begun");
M5_LOGI("%s", Units.debugInfo().c_str());
lcd.clear(TFT_DARKGREEN);
}
void loop()
{
M5.update();
Units.update();
if (unit.updated()) {
#if defined(UNIT_BME688_USING_BSEC2)
M5.Log.printf(">IAQ:%.2f\n>Temperature:%.2f\n>Pressure:%.2f\n>Humidity:%.2f\n>GAS:%.2f\n", unit.iaq(),
unit.temperature(), unit.pressure(), unit.humidity(), unit.gas());
#else
M5.Log.printf(">Temperature:%.2f\n>Pressure:%.2f\n>Humidity:%.2f\n>GAS:%.2f\n", unit.temperature(),
unit.pressure(), unit.humidity(), unit.gas());
m5::utility::delay(1000);
#endif
}
}

View file

@ -0,0 +1,9 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitTVOC
*/
#include "main/PlotToSerial.cpp"

View file

@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for UnitTVOC
*/
#include <M5Unified.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedENV.h>
#include <Wire.h>
namespace {
auto& lcd = M5.Display;
m5::unit::UnitUnified Units;
m5::unit::UnitTVOC unit;
} // namespace
void setup()
{
M5.begin();
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
Wire.end();
Wire.begin(pin_num_sda, pin_num_scl, 400 * 1000U);
if (!Units.add(unit, Wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
M5_LOGI("M5UnitUnified has been begun");
M5_LOGI("%s", Units.debugInfo().c_str());
M5_LOGW("SGP30 measurement starts 15 seconds after begin");
lcd.clear(TFT_DARKGREEN);
}
void loop()
{
M5.update();
Units.update();
// SGP30 measurement starts 15 seconds after begin.
if (unit.updated()) {
// Can be checked on serial plotters
M5.Log.printf("\n>CO2eq:%u\n>TVOC:%u", unit.co2eq(), unit.tvoc());
}
}

View file

@ -0,0 +1,43 @@
/**
* @file Unit_BPS1.1_M5Atom.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Atom + Unit BPS 1.1
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
QMP6988 qmp;
void setup() {
Serial.begin(115200);
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 26, 32, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
}
void loop() {
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,43 @@
/**
* @file Unit_BPS1.1_M5AtomS3.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5AtomS3 + Unit BPS 1.1
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
QMP6988 qmp;
void setup() {
Serial.begin(115200);
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 2, 1, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
}
void loop() {
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,43 @@
/**
* @file Unit_BPS1.1_M5AtomS3Lite.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5AtomS3Lite + Unit BPS 1.1
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
QMP6988 qmp;
void setup() {
Serial.begin(115200);
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 2, 1, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
}
void loop() {
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,43 @@
/**
* @file Unit_BPS1.1_M5Core.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit BPS 1.1
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
QMP6988 qmp;
void setup() {
Serial.begin(115200);
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 21, 22, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
}
void loop() {
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,45 @@
/**
* @file Unit_BPS1.1_M5Core2.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core2 + Unit BPS 1.1
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
QMP6988 qmp;
void setup() {
M5.begin();
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 32, 33, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
}
void loop() {
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,45 @@
/**
* @file Unit_BPS1.1_M5StickC.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickC + Unit BPS 1.1
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
QMP6988 qmp;
void setup() {
M5.begin();
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 32, 33, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
}
void loop() {
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,45 @@
/**
* @file Unit_BPS1.1_M5StickCPlus.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickCPlus + Unit BPS 1.1
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
QMP6988 qmp;
void setup() {
M5.begin();
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 32, 33, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
}
void loop() {
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,49 @@
/**
* @file Unit_BPS_M5Atom.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Atom + Unit BPS
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 26, 32, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,49 @@
/**
* @file Unit_BPS_M5AtomS3.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5AtomS3 + Unit BPS
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,49 @@
/**
* @file Unit_BPS_M5AtomS3Lite.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5AtomS3Lite + Unit BPS
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,49 @@
/**
* @file Unit_BPS_M5Core.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit BPS
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,51 @@
/**
* @file Unit_BPS_M5Core2.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core2 + Unit BPS
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
BMP280 bmp;
void setup() {
M5.begin();
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,51 @@
/**
* @file Unit_BPS_M5StickC.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickC + Unit BPS
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
BMP280 bmp;
void setup() {
M5.begin();
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,51 @@
/**
* @file Unit_BPS_M5StickCPlus.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickCPlus + Unit BPS
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
BMP280 bmp;
void setup() {
M5.begin();
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,51 @@
#include "M5UnitENV.h"
SCD4X scd4x;
void setup() {
Serial.begin(115200);
if (!scd4x.begin(&Wire, SCD4X_I2C_ADDR, 26, 32, 400000U)) {
Serial.println("Couldn't find SCD4X");
while (1) delay(1);
}
uint16_t error;
// stop potentially previously started measurement
error = scd4x.stopPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
}
// Start Measurement
error = scd4x.startPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
}
Serial.println("Waiting for first measurement... (5 sec)");
}
void loop() {
if (scd4x.update()) // readMeasurement will return true when
// fresh data is available
{
Serial.println();
Serial.print(F("CO2(ppm):"));
Serial.print(scd4x.getCO2());
Serial.print(F("\tTemperature(C):"));
Serial.print(scd4x.getTemperature(), 1);
Serial.print(F("\tHumidity(%RH):"));
Serial.print(scd4x.getHumidity(), 1);
Serial.println();
} else {
Serial.print(F("."));
}
delay(1000);
}

View file

@ -0,0 +1,51 @@
#include "M5UnitENV.h"
SCD4X scd4x;
void setup() {
Serial.begin(115200);
if (!scd4x.begin(&Wire, SCD4X_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find SCD4X");
while (1) delay(1);
}
uint16_t error;
// stop potentially previously started measurement
error = scd4x.stopPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
}
// Start Measurement
error = scd4x.startPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
}
Serial.println("Waiting for first measurement... (5 sec)");
}
void loop() {
if (scd4x.update()) // readMeasurement will return true when
// fresh data is available
{
Serial.println();
Serial.print(F("CO2(ppm):"));
Serial.print(scd4x.getCO2());
Serial.print(F("\tTemperature(C):"));
Serial.print(scd4x.getTemperature(), 1);
Serial.print(F("\tHumidity(%RH):"));
Serial.print(scd4x.getHumidity(), 1);
Serial.println();
} else {
Serial.print(F("."));
}
delay(1000);
}

View file

@ -0,0 +1,51 @@
#include "M5UnitENV.h"
SCD4X scd4x;
void setup() {
Serial.begin(115200);
if (!scd4x.begin(&Wire, SCD4X_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find SCD4X");
while (1) delay(1);
}
uint16_t error;
// stop potentially previously started measurement
error = scd4x.stopPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
}
// Start Measurement
error = scd4x.startPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
}
Serial.println("Waiting for first measurement... (5 sec)");
}
void loop() {
if (scd4x.update()) // readMeasurement will return true when
// fresh data is available
{
Serial.println();
Serial.print(F("CO2(ppm):"));
Serial.print(scd4x.getCO2());
Serial.print(F("\tTemperature(C):"));
Serial.print(scd4x.getTemperature(), 1);
Serial.print(F("\tHumidity(%RH):"));
Serial.print(scd4x.getHumidity(), 1);
Serial.println();
} else {
Serial.print(F("."));
}
delay(1000);
}

View file

@ -0,0 +1,51 @@
#include "M5UnitENV.h"
SCD4X scd4x;
void setup() {
Serial.begin(115200);
if (!scd4x.begin(&Wire, SCD4X_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find SCD4X");
while (1) delay(1);
}
uint16_t error;
// stop potentially previously started measurement
error = scd4x.stopPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
}
// Start Measurement
error = scd4x.startPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
}
Serial.println("Waiting for first measurement... (5 sec)");
}
void loop() {
if (scd4x.update()) // readMeasurement will return true when
// fresh data is available
{
Serial.println();
Serial.print(F("CO2(ppm):"));
Serial.print(scd4x.getCO2());
Serial.print(F("\tTemperature(C):"));
Serial.print(scd4x.getTemperature(), 1);
Serial.print(F("\tHumidity(%RH):"));
Serial.print(scd4x.getHumidity(), 1);
Serial.println();
} else {
Serial.print(F("."));
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file Unit_CO2_M5Core2.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core2 + Unit CO2
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SCD4X scd4x;
void setup() {
M5.begin();
if (!scd4x.begin(&Wire, SCD4X_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find SCD4X");
while (1) delay(1);
}
uint16_t error;
// stop potentially previously started measurement
error = scd4x.stopPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
}
// Start Measurement
error = scd4x.startPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
}
Serial.println("Waiting for first measurement... (5 sec)");
}
void loop() {
if (scd4x.update()) // readMeasurement will return true when
// fresh data is available
{
Serial.println();
Serial.print(F("CO2(ppm):"));
Serial.print(scd4x.getCO2());
Serial.print(F("\tTemperature(C):"));
Serial.print(scd4x.getTemperature(), 1);
Serial.print(F("\tHumidity(%RH):"));
Serial.print(scd4x.getHumidity(), 1);
Serial.println();
} else {
Serial.print(F("."));
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file Unit_CO2_M5StickC.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickC + Unit CO2
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SCD4X scd4x;
void setup() {
M5.begin();
if (!scd4x.begin(&Wire, SCD4X_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find SCD4X");
while (1) delay(1);
}
uint16_t error;
// stop potentially previously started measurement
error = scd4x.stopPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
}
// Start Measurement
error = scd4x.startPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
}
Serial.println("Waiting for first measurement... (5 sec)");
}
void loop() {
if (scd4x.update()) // readMeasurement will return true when
// fresh data is available
{
Serial.println();
Serial.print(F("CO2(ppm):"));
Serial.print(scd4x.getCO2());
Serial.print(F("\tTemperature(C):"));
Serial.print(scd4x.getTemperature(), 1);
Serial.print(F("\tHumidity(%RH):"));
Serial.print(scd4x.getHumidity(), 1);
Serial.println();
} else {
Serial.print(F("."));
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file Unit_CO2_M5StickCPlus.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickCPlus + Unit CO2
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SCD4X scd4x;
void setup() {
M5.begin();
if (!scd4x.begin(&Wire, SCD4X_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find SCD4X");
while (1) delay(1);
}
uint16_t error;
// stop potentially previously started measurement
error = scd4x.stopPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
}
// Start Measurement
error = scd4x.startPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
}
Serial.println("Waiting for first measurement... (5 sec)");
}
void loop() {
if (scd4x.update()) // readMeasurement will return true when
// fresh data is available
{
Serial.println();
Serial.print(F("CO2(ppm):"));
Serial.print(scd4x.getCO2());
Serial.print(F("\tTemperature(C):"));
Serial.print(scd4x.getTemperature(), 1);
Serial.print(F("\tHumidity(%RH):"));
Serial.print(scd4x.getHumidity(), 1);
Serial.println();
} else {
Serial.print(F("."));
}
delay(1000);
}

View file

@ -0,0 +1,63 @@
/**
* @file ENV_III.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.2
* @date 2024-07-18
*
*
* @Hardwares: M5Atom + Unit ENV_III
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
Serial.begin(115200);
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 26, 32, 400000U)) {
while (1) {
Serial.println("Couldn't find QMP6988");
delay(500);
}
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 26, 32, 400000U)) {
while (1) {
Serial.println("Couldn't find SHT3X");
delay(500);
}
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,63 @@
/**
* @file ENV_III.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.2
* @date 2024-07-18
*
*
* @Hardwares: M5AtomS3 + Unit ENV_III
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
Serial.begin(115200);
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 2, 1, 400000U)) {
while (1) {
Serial.println("Couldn't find QMP6988");
delay(500);
}
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 2, 1, 400000U)) {
while (1) {
Serial.println("Couldn't find SHT3X");
delay(500);
}
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,63 @@
/**
* @file ENV_III.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.2
* @date 2024-07-18
*
*
* @Hardwares: M5AtomS3Lite + Unit ENV_III
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
Serial.begin(115200);
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 2, 1, 400000U)) {
while (1) {
Serial.println("Couldn't find QMP6988");
delay(500);
}
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 2, 1, 400000U)) {
while (1) {
Serial.println("Couldn't find SHT3X");
delay(500);
}
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,60 @@
/**
* @file ENV_III.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV_III
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
Serial.begin(115200);
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 21, 22, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,61 @@
/**
* @file Unit_ENVIII_M5Core2.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core2 + Unit ENVIII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
M5.begin();
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 32, 33, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,61 @@
/**
* @file Unit_ENVIII_M5Station.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Station + Unit ENVIII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
M5.begin();
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 32, 33, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,62 @@
/**
* @file Unit_ENVIII_M5StickC.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickC + Unit ENVIII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
M5.begin();
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 32, 33, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,61 @@
/**
* @file Unit_ENVIII_M5StickCPlus.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickCPlus + Unit ENVIII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
QMP6988 qmp;
void setup() {
M5.begin();
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 32, 33, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (qmp.update()) {
Serial.println("-----QMP6988-----");
Serial.print(F("Temperature: "));
Serial.print(qmp.cTemp);
Serial.println(" *C");
Serial.print(F("Pressure: "));
Serial.print(qmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(qmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file ENV_II.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV_II
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT3X sht3x;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 26, 32, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 26, 32, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file ENV_II.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV_II
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT3X sht3x;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file ENV_II.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV_II
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT3X sht3x;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file ENV_II.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV_II
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT3X sht3x;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,68 @@
/**
* @file Unit_ENVII_M5Core2.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core2 + Unit ENVII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
BMP280 bmp;
void setup() {
M5.begin();
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,67 @@
/**
* @file Unit_ENVII_M5StickC.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickC + Unit ENVII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
BMP280 bmp;
void setup() {
M5.begin();
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,68 @@
/**
* @file Unit_ENVII_M5StickCPlus.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickCPlus + Unit ENVII
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
SHT3X sht3x;
BMP280 bmp;
void setup() {
M5.begin();
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht3x.update()) {
Serial.println("-----SHT3X-----");
Serial.print("Temperature: ");
Serial.print(sht3x.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht3x.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,71 @@
/**
* @file ENV_IV.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV_IV
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
SHT4X sht4;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!sht4.begin(&Wire, SHT40_I2C_ADDR_44, 21, 22, 400000U)) {
Serial.println("Couldn't find SHT4x");
while (1) delay(1);
}
// You can have 3 different precisions, higher precision takes longer
sht4.setPrecision(SHT4X_HIGH_PRECISION);
sht4.setHeater(SHT4X_NO_HEATER);
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (sht4.update()) {
Serial.println("-----SHT4X-----");
Serial.print("Temperature: ");
Serial.print(sht4.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(sht4.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,111 @@
/*
*******************************************************************************
* Copyright (c) 2023 by M5Stack
* Equipped with M5Core2 sample source code
* M5Core2
* Visit to get information: https://docs.m5stack.com/en/unit/ENV%E2%85%A3%20Unit
* 访: https://docs.m5stack.com/zh_CN/unit/ENV%E2%85%A3%20Unit
*
* Product: ENVIV_SHT40_BMP280.
* Date: 2023/8/24
*******************************************************************************
Please connect to Port,Read temperature, humidity and atmospheric pressure and
display them on the display screen
,湿
Libraries:
- [Adafruit_BMP280](https://github.com/adafruit/Adafruit_BMP280_Library)
- [Adafruit_Sensor](https://github.com/adafruit/Adafruit_Sensor)
- [SensirionI2CSht4x](https://github.com/Tinyu-Zhao/arduino-i2c-sht4x)
*/
#include <M5Core2.h>
#include <SensirionI2CSht4x.h>
#include <Adafruit_BMP280.h>
#include "Adafruit_Sensor.h"
// 初始化传感器
Adafruit_BMP280 bmp;
SensirionI2CSht4x sht4x;
float temperature, pressure,
humidity; // Store the vuale of pressure and Temperature. 存储压力和温度
void setup()
{
// 初始化传感器
M5.begin();
M5.Lcd.setTextSize(2);
Wire.begin(); // SDA = 16, SCL = 34
Serial.begin(115200);
while (!Serial)
{
delay(100);
}
while (!bmp.begin(
0x76))
{ // Init this sensor,True if the init was successful, otherwise
// false. 初始化传感器,如果初始化成功返回1
M5.Lcd.println("Could not find a valid BMP280 sensor, check wiring!");
Serial.println(F("BMP280 fail"));
}
M5.Lcd.clear(); // Clear the screen. 清屏
Serial.println(F("BMP280 test"));
uint16_t error;
char errorMessage[256];
sht4x.begin(Wire);
uint32_t serialNumber;
error = sht4x.serialNumber(serialNumber);
if (error)
{
Serial.print("Error trying to execute serialNumber(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
}
else
{
Serial.print("Serial Number: ");
Serial.println(serialNumber);
}
// 设置传感器的采样率和滤波器
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, // 模式:正常
Adafruit_BMP280::SAMPLING_X2, // 温度采样率2倍
Adafruit_BMP280::SAMPLING_X16, // 压力采样率16倍
Adafruit_BMP280::FILTER_X16, // 滤波器16倍
Adafruit_BMP280::STANDBY_MS_500); // 等待时间500毫秒
}
void loop()
{
uint16_t error;
char errorMessage[256];
delay(1000);
error = sht4x.measureHighPrecision(temperature, humidity);
if (error)
{
Serial.print("Error trying to execute measureHighPrecision(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
}
else
{
Serial.print("Temperature:");
Serial.print(temperature);
Serial.print("\t");
Serial.print("Humidity:");
Serial.println(humidity);
}
pressure = bmp.readPressure();
M5.Lcd.setCursor(0, 0); // 将光标设置在(0 ,0). Set the cursor to (0,0)
M5.Lcd.printf("Pressure:%2.0fPa\nTemperature:%2.0f^C", pressure,
temperature);
M5.Lcd.setCursor(0, 40);
M5.Lcd.print("humidity:");
M5.Lcd.print(humidity);
M5.Lcd.print("%");
delay(100);
}

View file

@ -0,0 +1,64 @@
/**
* @file ENV.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
DHT12 dht;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!dht.begin(&Wire, DHT12_I2C_ADDR, 26, 32, 400000U)) {
Serial.println("Couldn't find DHT12");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 26, 32, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (dht.update()) {
Serial.println("-----DHT12-----");
Serial.print("Temperature: ");
Serial.print(dht.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(dht.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,64 @@
/**
* @file ENV.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
DHT12 dht;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!dht.begin(&Wire, DHT12_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find DHT12");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (dht.update()) {
Serial.println("-----DHT12-----");
Serial.print("Temperature: ");
Serial.print(dht.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(dht.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,64 @@
/**
* @file ENV.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
DHT12 dht;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!dht.begin(&Wire, DHT12_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find DHT12");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 2, 1, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (dht.update()) {
Serial.println("-----DHT12-----");
Serial.print("Temperature: ");
Serial.print(dht.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(dht.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,64 @@
/**
* @file ENV.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core + Unit ENV
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
*/
#include "M5UnitENV.h"
DHT12 dht;
BMP280 bmp;
void setup() {
Serial.begin(115200);
if (!dht.begin(&Wire, DHT12_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find DHT12");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 21, 22, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (dht.update()) {
Serial.println("-----DHT12-----");
Serial.print("Temperature: ");
Serial.print(dht.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(dht.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file Unit_ENV_M5Core2.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5Core2 + Unit ENV
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
DHT12 dht;
BMP280 bmp;
void setup() {
M5.begin();
if (!dht.begin(&Wire, DHT12_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find DHT12");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (dht.update()) {
Serial.println("-----DHT12-----");
Serial.print("Temperature: ");
Serial.print(dht.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(dht.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file Unit_ENV_M5StickC.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickC + Unit ENV
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
DHT12 dht;
BMP280 bmp;
void setup() {
M5.begin();
if (!dht.begin(&Wire, DHT12_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find DHT12");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (dht.update()) {
Serial.println("-----DHT12-----");
Serial.print("Temperature: ");
Serial.print(dht.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(dht.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,66 @@
/**
* @file Unit_ENV_M5StickCPlus.ino
* @author SeanKwok (shaoxiang@m5stack.com)
* @brief
* @version 0.1
* @date 2024-01-30
*
*
* @Hardwares: M5StickCPlus + Unit ENV
* @Platform Version: Arduino M5Stack Board Manager v2.1.0
* @Dependent Library:
* M5UnitENV: https://github.com/m5stack/M5Unit-ENV
* M5Unified: https://github.com/m5stack/M5Unified
*/
#include <M5Unified.h>
#include "M5UnitENV.h"
DHT12 dht;
BMP280 bmp;
void setup() {
M5.begin();
if (!dht.begin(&Wire, DHT12_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find DHT12");
while (1) delay(1);
}
if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 32, 33, 400000U)) {
Serial.println("Couldn't find BMP280");
while (1) delay(1);
}
/* Default settings from datasheet. */
bmp.setSampling(BMP280::MODE_NORMAL, /* Operating Mode. */
BMP280::SAMPLING_X2, /* Temp. oversampling */
BMP280::SAMPLING_X16, /* Pressure oversampling */
BMP280::FILTER_X16, /* Filtering. */
BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if (dht.update()) {
Serial.println("-----DHT12-----");
Serial.print("Temperature: ");
Serial.print(dht.cTemp);
Serial.println(" degrees C");
Serial.print("Humidity: ");
Serial.print(dht.humidity);
Serial.println("% rH");
Serial.println("-------------\r\n");
}
if (bmp.update()) {
Serial.println("-----BMP280-----");
Serial.print(F("Temperature: "));
Serial.print(bmp.cTemp);
Serial.println(" degrees C");
Serial.print(F("Pressure: "));
Serial.print(bmp.pressure);
Serial.println(" Pa");
Serial.print(F("Approx altitude: "));
Serial.print(bmp.altitude);
Serial.println(" m");
Serial.println("-------------\r\n");
}
delay(1000);
}

View file

@ -0,0 +1,34 @@
{
"name": "M5Unit-ENV",
"description": "Library for M5Stack UNIT ENV",
"keywords": "M5Stack UNIT ENV,M5UnitUnified",
"authors": {
"name": "M5Stack",
"url": "http://www.m5stack.com"
},
"repository": {
"type": "git",
"url": "https://github.com/m5stack/M5Unit-ENV.git"
},
"dependencies":
{
"m5stack/M5UnitUnified": ">=0.1.0",
"boschsensortec/BME68x Sensor library": ">=1.3.40408",
"boschsensortec/bsec2": ">=1.10.2610"
},
"version": "1.3.0",
"frameworks": [
"arduino"
],
"headers": [
"M5UnitENV.h",
"M5UnitUnifiedENV.h"
],
"platforms": "espressif32",
"license": "MIT",
"export": {
"exclude": [
"docs/html"
]
}
}

View file

@ -0,0 +1,11 @@
name=M5Unit-ENV
version=1.3.0
author=M5Stack
maintainer=M5Stack
sentence=Library for M5Stack UNIT ENV
paragraph=See more on https://docs.m5stack.com/en/unit/envIII
category=Device Control
url=https://github.com/m5stack/M5Unit-ENV
architectures=esp32
includes=M5UnitENV.h, M5UnitUnifiedENV.h
depends=M5UnitUnified,M5Utility,M5HAL,bsec2,BME68x Sensor library

View file

@ -0,0 +1,203 @@
;-----------------------------------------------------------------------
; M5Unit-ENV
; For UnitTest and examples (Using M5UnitUnified)
;-----------------------------------------------------------------------
[platformio]
extra_configs = unit_co2_env.ini, unit_env3_env.ini, unit_env4_env.ini, unit_envpro_env.ini, unit_tvoc_env.ini
[env]
build_flags =-Wall -Wextra -Wreturn-local-addr -Werror=format -Werror=return-local-addr
lib_ldf_mode = deep
test_framework = googletest
test_build_src = true
lib_deps=m5stack/M5Unified
m5stack/M5UnitUnified
boschsensortec/BME68x Sensor library@>=1.3.40408
; --------------------------------
[bsec2]
lib_deps = boschsensortec/bsec2@>=1.10.2610
[m5base]
monitor_speed = 115200
monitor_filters = esp32_exception_decoder, time
upload_speed = 1500000
test_speed = 115200
test_ignore= native/*
[Core]
extends = m5base
board = m5stack-grey
;m5stack-grey
;m5stack-core-esp32-16M ;;6.8.0 or later
;m5stack-core-esp32
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
[Core2]
extends = m5base
board = m5stack-core2
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
[CoreS3]
extends = m5base
board = m5stack-cores3
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
[Fire]
extends = m5base
board = m5stack-fire
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
[StampS3]
;include M5Capsule,M5DinMeter
extends = m5base
board = m5stack-stamps3
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
[Dial]
extends = m5base
board = m5stack-stamps3
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
m5stack/M5Dial
[AtomMatrix]
extends = m5base
board = m5stack-atom
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
[AtomS3]
extends = m5base
board = m5stack-atoms3
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
; Using ./boards/m5stack-atoms3r.json
[AtomS3R]
extends = m5base
board = m5stack-atoms3r
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
; Using ./boards/m5stack-nanoc6.json
[NanoC6]
extends = m5base
board = m5stack-nanoc6
platform = https://github.com/platformio/platform-espressif32.git
platform_packages =
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.7
platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1
board_build.partitions = default.csv
lib_deps = ${env.lib_deps}
[StickCPlus]
extends = m5base
board = m5stick-c
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
; Using ./boards/m5stick-cplus2.json
[StickCPlus2]
extends = m5base
board = m5stick-cplus2
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
[Paper]
extends = m5base
board = m5stack-fire
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
[CoreInk]
extends = m5base
board = m5stack-coreink
lib_deps = ${env.lib_deps}
${bsec2.lib_deps}
[sdl]
build_flags = -O3 -xc++ -std=c++14 -lSDL2
-arch arm64 ; for arm mac
-I"/usr/local/include/SDL2" ; for intel mac homebrew SDL2
-L"/usr/local/lib" ; for intel mac homebrew SDL2
-I"${sysenv.HOMEBREW_PREFIX}/include/SDL2" ; for arm mac homebrew SDL2
-L"${sysenv.HOMEBREW_PREFIX}/lib" ; for arm mac homebrew SDL2
platform = native
test_filter= native/*
test_ignore= embedded/*
lib_deps = ${env.lib_deps}
; --------------------------------
;Choose framework
[arduino_latest]
platform = espressif32 @ 6.8.1
framework = arduino
[arduino_6_6_0]
platform = espressif32 @ 6.6.0
framework = arduino
[arduino_6_0_1]
platform = espressif32 @ 6.0.1
framework = arduino
[arduino_5_4_0]
platform = espressif32 @ 5.4.0
framework = arduino
[arduino_4_4_0]
platform = espressif32 @ 4.4.0
framework = arduino
;[arduino_3_5_0]
;platform = espressif32 @ 3.5.0
;framework = arduino
[esp-idf]
platform = espressif32 @ 6.8.1
framework = espidf
; --------------------------------
;Choose build options
[option_release]
build_type=release
build_flags = ${env.build_flags}
-DCORE_DEBUG_LEVEL=3
-DLOG_LOCAL_LEVEL=3
-DAPP_LOG_LEVEL=3
-DM5_LOG_LEVEL=3
[option_log]
build_type=release
build_flags = ${env.build_flags}
-DCORE_DEBUG_LEVEL=5
-DLOG_LOCAL_LEVEL=5
-DAPP_LOG_LEVEL=5
[option_debug]
build_type=debug
build_flags = ${env.build_flags}
-DCORE_DEBUG_LEVEL=5
-DLOG_LOCAL_LEVEL=5
-DAPP_LOG_LEVEL=5
-DDEBUG
[option_map]
build_type=release
build_flags = ${env.build_flags}
-DCORE_DEBUG_LEVEL=3
-DLOG_LOCAL_LEVEL=3
-DAPP_LOG_LEVEL=3
-DM5_LOG_LEVEL=0
-Wl,-Map,output.map
; Require at leaset C++14 after 1.13.0
[test_fw]
lib_deps = google/googletest@1.12.1

View file

@ -0,0 +1,237 @@
#include "BMP280.h"
bool BMP280::begin(TwoWire* wire, uint8_t addr, uint8_t sda, uint8_t scl,
long freq) {
_i2c.begin(wire, sda, scl, freq);
_addr = addr;
if (!_i2c.exist(_addr)) {
return false;
}
readCoefficients();
setSampling();
return true;
}
bool BMP280::update() {
readTemperature();
readPressure();
readAltitude();
return true;
}
float BMP280::readTemperature() {
int32_t var1, var2;
int32_t adc_T = read24(BMP280_REGISTER_TEMPDATA);
adc_T >>= 4;
var1 = ((((adc_T >> 3) - ((int32_t)_bmp280_calib.dig_T1 << 1))) *
((int32_t)_bmp280_calib.dig_T2)) >>
11;
var2 = (((((adc_T >> 4) - ((int32_t)_bmp280_calib.dig_T1)) *
((adc_T >> 4) - ((int32_t)_bmp280_calib.dig_T1))) >>
12) *
((int32_t)_bmp280_calib.dig_T3)) >>
14;
t_fine = var1 + var2;
float T = (t_fine * 5 + 128) >> 8;
cTemp = T / 100;
return cTemp;
}
float BMP280::readPressure() {
int64_t var1, var2, p;
// Must be done first to get the t_fine variable set up
int32_t adc_P = read24(BMP280_REGISTER_PRESSUREDATA);
adc_P >>= 4;
var1 = ((int64_t)t_fine) - 128000;
var2 = var1 * var1 * (int64_t)_bmp280_calib.dig_P6;
var2 = var2 + ((var1 * (int64_t)_bmp280_calib.dig_P5) << 17);
var2 = var2 + (((int64_t)_bmp280_calib.dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)_bmp280_calib.dig_P3) >> 8) +
((var1 * (int64_t)_bmp280_calib.dig_P2) << 12);
var1 =
(((((int64_t)1) << 47) + var1)) * ((int64_t)_bmp280_calib.dig_P1) >> 33;
if (var1 == 0) {
return 0; // avoid exception caused by division by zero
}
p = 1048576 - adc_P;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((int64_t)_bmp280_calib.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((int64_t)_bmp280_calib.dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((int64_t)_bmp280_calib.dig_P7) << 4);
pressure = p / 256;
return pressure;
}
/*!
* @brief Calculates the approximate altitude using barometric pressure and the
* supplied sea level hPa as a reference.
* @param seaLevelhPa
* The current hPa at sea level.
* @return The approximate altitude above sea level in meters.
*/
float BMP280::readAltitude(float seaLevelhPa) {
float pressure = readPressure(); // in Si units for Pascal
pressure /= 100;
altitude = 44330 * (1.0 - pow(pressure / seaLevelhPa, 0.1903));
return altitude;
}
void BMP280::setSampling(sensor_mode mode, sensor_sampling tempSampling,
sensor_sampling pressSampling, sensor_filter filter,
standby_duration duration) {
_measReg.mode = mode;
_measReg.osrs_t = tempSampling;
_measReg.osrs_p = pressSampling;
_configReg.filter = filter;
_configReg.t_sb = duration;
write8(BMP280_REGISTER_CONFIG, _configReg.get());
write8(BMP280_REGISTER_CONTROL, _measReg.get());
}
void BMP280::readCoefficients() {
_bmp280_calib.dig_T1 = read16_LE(BMP280_REGISTER_DIG_T1);
_bmp280_calib.dig_T2 = readS16_LE(BMP280_REGISTER_DIG_T2);
_bmp280_calib.dig_T3 = readS16_LE(BMP280_REGISTER_DIG_T3);
_bmp280_calib.dig_P1 = read16_LE(BMP280_REGISTER_DIG_P1);
_bmp280_calib.dig_P2 = readS16_LE(BMP280_REGISTER_DIG_P2);
_bmp280_calib.dig_P3 = readS16_LE(BMP280_REGISTER_DIG_P3);
_bmp280_calib.dig_P4 = readS16_LE(BMP280_REGISTER_DIG_P4);
_bmp280_calib.dig_P5 = readS16_LE(BMP280_REGISTER_DIG_P5);
_bmp280_calib.dig_P6 = readS16_LE(BMP280_REGISTER_DIG_P6);
_bmp280_calib.dig_P7 = readS16_LE(BMP280_REGISTER_DIG_P7);
_bmp280_calib.dig_P8 = readS16_LE(BMP280_REGISTER_DIG_P8);
_bmp280_calib.dig_P9 = readS16_LE(BMP280_REGISTER_DIG_P9);
}
/*!
* Calculates the pressure at sea level (QNH) from the specified altitude,
* and atmospheric pressure (QFE).
* @param altitude Altitude in m
* @param atmospheric Atmospheric pressure in hPa
* @return The approximate pressure in hPa
*/
float BMP280::seaLevelForAltitude(float altitude, float atmospheric) {
// Equation taken from BMP180 datasheet (page 17):
// http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
// Note that using the equation from wikipedia can give bad results
// at high altitude. See this thread for more information:
// http://forums.adafruit.com/viewtopic.php?f=22&t=58064
return atmospheric / pow(1.0 - (altitude / 44330.0), 5.255);
}
/*!
@brief calculates the boiling point of water by a given pressure
@param pressure pressure in hPa
@return temperature in °C
*/
float BMP280::waterBoilingPoint(float pressure) {
// Magnusformular for calculation of the boiling point of water at a given
// pressure
return (234.175 * log(pressure / 6.1078)) /
(17.08085 - log(pressure / 6.1078));
}
/*!
@brief Take a new measurement (only possible in forced mode)
@return true if successful, otherwise false
*/
bool BMP280::takeForcedMeasurement() {
// If we are in forced mode, the BME sensor goes back to sleep after each
// measurement and we need to set it to forced mode once at this point, so
// it will take the next measurement and then return to sleep again.
// In normal mode simply does new measurements periodically.
if (_measReg.mode == MODE_FORCED) {
// set to forced mode, i.e. "take next measurement"
write8(BMP280_REGISTER_CONTROL, _measReg.get());
// wait until measurement has been completed, otherwise we would read
// the values from the last measurement
while (read8(BMP280_REGISTER_STATUS) & 0x08) delay(1);
return true;
}
return false;
}
/*!
* @brief Resets the chip via soft reset
*/
void BMP280::reset(void) {
write8(BMP280_REGISTER_SOFTRESET, MODE_SOFT_RESET_CODE);
}
/*!
@brief Gets the most recent sensor event from the hardware status register.
@return Sensor status as a byte.
*/
uint8_t BMP280::getStatus(void) {
return read8(BMP280_REGISTER_STATUS);
}
/**************************************************************************/
/*!
@brief Writes an 8 bit value over I2C/SPI
*/
/**************************************************************************/
void BMP280::write8(byte reg, byte value) {
_i2c.writeByte(_addr, reg, value);
}
/*!
* @brief Reads an 8 bit value over I2C/SPI
* @param reg
* selected register
* @return value from selected register
*/
uint8_t BMP280::read8(byte reg) {
return _i2c.readByte(_addr, reg);
}
/*!
* @brief Reads a 16 bit value over I2C/SPI
*/
uint16_t BMP280::read16(byte reg) {
uint8_t buffer[2];
_i2c.readBytes(_addr, reg, buffer, 2);
return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]);
}
uint16_t BMP280::read16_LE(byte reg) {
uint16_t temp = read16(reg);
return (temp >> 8) | (temp << 8);
}
/*!
* @brief Reads a signed 16 bit value over I2C/SPI
*/
int16_t BMP280::readS16(byte reg) {
return (int16_t)read16(reg);
}
int16_t BMP280::readS16_LE(byte reg) {
return (int16_t)read16_LE(reg);
}
/*!
* @brief Reads a 24 bit value over I2C/SPI
*/
uint32_t BMP280::read24(byte reg) {
uint8_t buffer[3];
_i2c.readBytes(_addr, reg, buffer, 3);
return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8 |
uint32_t(buffer[2]);
}

View file

@ -0,0 +1,199 @@
#ifndef _BMP280_H
#define _BMP280_H
#include "Arduino.h"
#include "Wire.h"
#include "I2C_Class.h"
#define BMP280_I2C_ADDR 0x76
enum {
BMP280_REGISTER_DIG_T1 = 0x88,
BMP280_REGISTER_DIG_T2 = 0x8A,
BMP280_REGISTER_DIG_T3 = 0x8C,
BMP280_REGISTER_DIG_P1 = 0x8E,
BMP280_REGISTER_DIG_P2 = 0x90,
BMP280_REGISTER_DIG_P3 = 0x92,
BMP280_REGISTER_DIG_P4 = 0x94,
BMP280_REGISTER_DIG_P5 = 0x96,
BMP280_REGISTER_DIG_P6 = 0x98,
BMP280_REGISTER_DIG_P7 = 0x9A,
BMP280_REGISTER_DIG_P8 = 0x9C,
BMP280_REGISTER_DIG_P9 = 0x9E,
BMP280_REGISTER_CHIPID = 0xD0,
BMP280_REGISTER_VERSION = 0xD1,
BMP280_REGISTER_SOFTRESET = 0xE0,
BMP280_REGISTER_CAL26 = 0xE1, /**< R calibration = 0xE1-0xF0 */
BMP280_REGISTER_STATUS = 0xF3,
BMP280_REGISTER_CONTROL = 0xF4,
BMP280_REGISTER_CONFIG = 0xF5,
BMP280_REGISTER_PRESSUREDATA = 0xF7,
BMP280_REGISTER_TEMPDATA = 0xFA,
};
typedef struct {
uint16_t dig_T1; /**< dig_T1 cal register. */
int16_t dig_T2; /**< dig_T2 cal register. */
int16_t dig_T3; /**< dig_T3 cal register. */
uint16_t dig_P1; /**< dig_P1 cal register. */
int16_t dig_P2; /**< dig_P2 cal register. */
int16_t dig_P3; /**< dig_P3 cal register. */
int16_t dig_P4; /**< dig_P4 cal register. */
int16_t dig_P5; /**< dig_P5 cal register. */
int16_t dig_P6; /**< dig_P6 cal register. */
int16_t dig_P7; /**< dig_P7 cal register. */
int16_t dig_P8; /**< dig_P8 cal register. */
int16_t dig_P9; /**< dig_P9 cal register. */
} bmp280_calib_data;
class BMP280 {
public:
/** Oversampling rate for the sensor. */
enum sensor_sampling {
/** No over-sampling. */
SAMPLING_NONE = 0x00,
/** 1x over-sampling. */
SAMPLING_X1 = 0x01,
/** 2x over-sampling. */
SAMPLING_X2 = 0x02,
/** 4x over-sampling. */
SAMPLING_X4 = 0x03,
/** 8x over-sampling. */
SAMPLING_X8 = 0x04,
/** 16x over-sampling. */
SAMPLING_X16 = 0x05
};
/** Operating mode for the sensor. */
enum sensor_mode {
/** Sleep mode. */
MODE_SLEEP = 0x00,
/** Forced mode. */
MODE_FORCED = 0x01,
/** Normal mode. */
MODE_NORMAL = 0x03,
/** Software reset. */
MODE_SOFT_RESET_CODE = 0xB6
};
/** Filtering level for sensor data. */
enum sensor_filter {
/** No filtering. */
FILTER_OFF = 0x00,
/** 2x filtering. */
FILTER_X2 = 0x01,
/** 4x filtering. */
FILTER_X4 = 0x02,
/** 8x filtering. */
FILTER_X8 = 0x03,
/** 16x filtering. */
FILTER_X16 = 0x04
};
/** Standby duration in ms */
enum standby_duration {
/** 1 ms standby. */
STANDBY_MS_1 = 0x00,
/** 62.5 ms standby. */
STANDBY_MS_63 = 0x01,
/** 125 ms standby. */
STANDBY_MS_125 = 0x02,
/** 250 ms standby. */
STANDBY_MS_250 = 0x03,
/** 500 ms standby. */
STANDBY_MS_500 = 0x04,
/** 1000 ms standby. */
STANDBY_MS_1000 = 0x05,
/** 2000 ms standby. */
STANDBY_MS_2000 = 0x06,
/** 4000 ms standby. */
STANDBY_MS_4000 = 0x07
};
public:
bool begin(TwoWire *wire = &Wire, uint8_t addr = BMP280_I2C_ADDR,
uint8_t sda = 21, uint8_t scl = 22, long freq = 400000U);
bool update();
float pressure = 0;
float cTemp = 0;
float altitude = 0;
void reset(void);
uint8_t getStatus(void);
float readTemperature();
float readPressure(void);
float readAltitude(float seaLevelhPa = 1013.25);
float seaLevelForAltitude(float altitude, float atmospheric);
float waterBoilingPoint(float pressure);
bool takeForcedMeasurement();
void setSampling(sensor_mode mode = MODE_NORMAL,
sensor_sampling tempSampling = SAMPLING_X16,
sensor_sampling pressSampling = SAMPLING_X16,
sensor_filter filter = FILTER_OFF,
standby_duration duration = STANDBY_MS_1);
private:
/** Encapsulates the config register */
struct config {
/** Initialize to power-on-reset state */
config()
: t_sb(STANDBY_MS_1), filter(FILTER_OFF), none(0), spi3w_en(0) {
}
/** Inactive duration (standby time) in normal mode */
unsigned int t_sb : 3;
/** Filter settings */
unsigned int filter : 3;
/** Unused - don't set */
unsigned int none : 1;
/** Enables 3-wire SPI */
unsigned int spi3w_en : 1;
/** Used to retrieve the assembled config register's byte value. */
unsigned int get() {
return (t_sb << 5) | (filter << 2) | spi3w_en;
}
};
/** Encapsulates trhe ctrl_meas register */
struct ctrl_meas {
/** Initialize to power-on-reset state */
ctrl_meas()
: osrs_t(SAMPLING_NONE), osrs_p(SAMPLING_NONE), mode(MODE_SLEEP) {
}
/** Temperature oversampling. */
unsigned int osrs_t : 3;
/** Pressure oversampling. */
unsigned int osrs_p : 3;
/** Device mode */
unsigned int mode : 2;
/** Used to retrieve the assembled ctrl_meas register's byte value. */
unsigned int get() {
return (osrs_t << 5) | (osrs_p << 2) | mode;
}
};
void readCoefficients(void);
uint8_t spixfer(uint8_t x);
void write8(byte reg, byte value);
uint8_t read8(byte reg);
uint16_t read16(byte reg);
uint32_t read24(byte reg);
int16_t readS16(byte reg);
uint16_t read16_LE(byte reg);
int16_t readS16_LE(byte reg);
int32_t t_fine;
bmp280_calib_data _bmp280_calib;
config _configReg;
ctrl_meas _measReg;
private:
uint8_t _addr;
I2C_Class _i2c;
};
#endif

View file

@ -0,0 +1,23 @@
#include "DHT12.h"
bool DHT12::begin(TwoWire* wire, uint8_t addr, uint8_t sda, uint8_t scl,
long freq) {
_i2c.begin(wire, sda, scl, freq);
_addr = addr;
return _i2c.exist(_addr);
}
bool DHT12::update() {
uint8_t datos[5] = {0};
if (_i2c.readBytes(_addr, 0, datos, 5)) {
if (datos[4] != (datos[0] + datos[1] + datos[2] + datos[3])) {
return false;
}
cTemp = (datos[2] + (float)datos[3] / 10);
fTemp = ((datos[2] + (float)datos[3] / 10) * 1.8 + 32);
kTemp = (datos[2] + (float)datos[3] / 10) + 273.15;
humidity = (datos[0] + (float)datos[1] / 10);
return true;
}
return false;
}

View file

@ -0,0 +1,26 @@
#ifndef _DHT12_H
#define _DHT12_H
#include "Arduino.h"
#include "Wire.h"
#include "I2C_Class.h"
#define DHT12_I2C_ADDR 0x5c
class DHT12 {
public:
bool begin(TwoWire* wire = &Wire, uint8_t addr = DHT12_I2C_ADDR,
uint8_t sda = 21, uint8_t scl = 22, long freq = 400000U);
bool update();
float cTemp = 0;
float fTemp = 0;
float kTemp = 0;
float humidity = 0;
private:
uint8_t _addr;
I2C_Class _i2c;
};
#endif

View file

@ -0,0 +1,93 @@
#include "I2C_Class.h"
void I2C_Class::begin(TwoWire *wire, uint8_t sda, uint8_t scl, long freq) {
_wire = wire;
_sda = sda;
_scl = scl;
_freq = freq;
_wire->end();
_wire->begin(static_cast<int>(_sda), _scl, freq);
}
bool I2C_Class::exist(uint8_t addr) {
int error;
_wire->beginTransmission(addr);
error = _wire->endTransmission();
if (error == 0) {
return true;
}
return false;
}
bool I2C_Class::writeBytes(uint8_t addr, uint8_t reg, uint8_t *buffer,
uint8_t length) {
_wire->beginTransmission(addr);
_wire->write(reg);
_wire->write(buffer, length);
if (_wire->endTransmission() == 0) return true;
return false;
}
bool I2C_Class::readBytes(uint8_t addr, uint8_t reg, uint8_t *buffer,
uint8_t length) {
uint8_t index = 0;
_wire->beginTransmission(addr);
_wire->write(reg);
_wire->endTransmission();
if (_wire->requestFrom(addr, length)) {
for (uint8_t i = 0; i < length; i++) {
buffer[index++] = _wire->read();
}
return true;
}
return false;
}
bool I2C_Class::readU16(uint8_t addr, uint8_t reg_addr, uint16_t *value) {
uint8_t read_buf[2] = {0x00, 0x00};
bool result = readBytes(addr, reg_addr, read_buf, 2);
*value = (read_buf[0] << 8) | read_buf[1];
return result;
}
bool I2C_Class::writeU16(uint8_t addr, uint8_t reg_addr, uint16_t value) {
uint8_t write_buf[2];
write_buf[0] = value >> 8;
write_buf[1] = value & 0xff;
return writeBytes(addr, reg_addr, write_buf, 2);
}
bool I2C_Class::writeByte(uint8_t addr, uint8_t reg, uint8_t data) {
_wire->beginTransmission(addr);
_wire->write(reg);
_wire->write(data);
if (_wire->endTransmission() == 0) return true;
return false;
}
uint8_t I2C_Class::readByte(uint8_t addr, uint8_t reg) {
_wire->beginTransmission(addr);
_wire->write(reg);
_wire->endTransmission();
if (_wire->requestFrom(addr, 1)) {
return _wire->read();
}
return 0;
}
bool I2C_Class::writeBitOn(uint8_t addr, uint8_t reg, uint8_t data) {
uint8_t temp;
uint8_t write_back;
temp = readByte(addr, reg);
write_back = (temp | data);
return (writeByte(addr, reg, write_back));
}
bool I2C_Class::writeBitOff(uint8_t addr, uint8_t reg, uint8_t data) {
uint8_t temp;
uint8_t write_back;
temp = readByte(addr, reg);
write_back = (temp & (~data));
return (writeByte(addr, reg, write_back));
}

View file

@ -0,0 +1,30 @@
#ifndef _I2C_DEVICE_BUS_
#define _I2C_DEVICE_BUS_
#include "Arduino.h"
#include "Wire.h"
class I2C_Class {
private:
TwoWire* _wire;
uint8_t _scl;
uint8_t _sda;
long _freq;
public:
void begin(TwoWire* wire, uint8_t sda, uint8_t scl, long freq = 100000);
bool exist(uint8_t addr);
bool writeBytes(uint8_t addr, uint8_t reg, uint8_t* buffer, uint8_t length);
bool readBytes(uint8_t addr, uint8_t reg, uint8_t* buffer, uint8_t length);
bool readU16(uint8_t addr, uint8_t reg_addr, uint16_t* value);
bool writeU16(uint8_t addr, uint8_t reg_addr, uint16_t value);
bool writeByte(uint8_t addr, uint8_t reg, uint8_t data);
uint8_t readByte(uint8_t addr, uint8_t reg);
bool writeBitOn(uint8_t addr, uint8_t reg, uint8_t data);
bool writeBitOff(uint8_t addr, uint8_t reg, uint8_t data);
};
#endif

View file

@ -0,0 +1,16 @@
#if defined(M5_UNIT_UNIFIED_ENV_HPP)
#error "DO NOT USE it at the same time as M5UnitUnified libraries"
#endif
#ifndef _M5_UNIT_ENV_H_
#define _M5_UNIT_ENV_H_
#include "Arduino.h"
#include "DHT12.h"
#include "QMP6988.h"
#include "SHT3X.h"
#include "SHT4X.h"
#include "BMP280.h"
#include "SCD4X.h"
#endif

View file

@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file M5UnitUnifiedENV.h
@brief Main header of M5UnitENV using M5UnitUnfied
*/
#ifndef M5_UNIT_UNIFIED_ENV_H
#define M5_UNIT_UNIFIED_ENV_H
#ifdef __cplusplus
#include "M5UnitUnifiedENV.hpp"
#else
#error M5UnitUnifiedENV requires a C++ compiler, please change file extension to .cc or .cpp
#endif
#endif

View file

@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file M5UnitUnifiedENV.hpp
@brief Main header of M5UnitENV using M5UnitUnified
@mainpage M5Unit-ENV
Library for UnitENV using M5UnitUnified.
*/
#if defined(_M5_UNIT_ENV_H_)
#error "DO NOT USE it at the same time as conventional libraries"
#endif
#ifndef M5_UNIT_UNIFIED_ENV_HPP
#define M5_UNIT_UNIFIED_ENV_HPP
// CO2
#include "unit/unit_SCD40.hpp"
#include "unit/unit_SCD41.hpp"
// ENVIII
#include "unit/unit_SHT30.hpp"
#include "unit/unit_QMP6988.hpp"
#include "unit/unit_ENV3.hpp"
// ENVPro
#include "unit/unit_BME688.hpp"
// TVOC
#include "unit/unit_SGP30.hpp"
// ENVIV
#include "unit/unit_SHT40.hpp"
#include "unit/unit_BMP280.hpp"
#include "unit/unit_ENV4.hpp"
/*!
@namespace m5
@brief Top level namespace of M5stack
*/
namespace m5 {
/*!
@namespace unit
@brief Unit-related namespace
*/
namespace unit {
using UnitCO2 = m5::unit::UnitSCD40;
using UnitCO2L = m5::unit::UnitSCD41;
using UnitENVPro = m5::unit::UnitBME688;
using UnitTVOC = m5::unit::UnitSGP30;
} // namespace unit
} // namespace m5
#endif

View file

@ -0,0 +1,353 @@
#include <math.h>
#include "stdint.h"
#include "stdio.h"
#include "QMP6988.h"
// DISABLE LOG
#define QMP6988_LOG(format...)
#define QMP6988_ERR(format...)
// ENABLE LOG
// #define QMP6988_LOG Serial.printf
// #define QMP6988_ERR Serial.printf
int QMP6988::getCalibrationData() {
int status = 0;
// BITFIELDS temp_COE;
uint8_t a_data_uint8_tr[QMP6988_CALIBRATION_DATA_LENGTH] = {0};
int len;
for (len = 0; len < QMP6988_CALIBRATION_DATA_LENGTH; len += 1) {
status = _i2c.readBytes(_addr, QMP6988_CALIBRATION_DATA_START + len,
&a_data_uint8_tr[len], 1);
if (status == 0) {
QMP6988_LOG("qmp6988 read 0xA0 error!");
return status;
}
}
qmp6988.qmp6988_cali.COE_a0 =
(QMP6988_S32_t)(((a_data_uint8_tr[18] << SHIFT_LEFT_12_POSITION) |
(a_data_uint8_tr[19] << SHIFT_LEFT_4_POSITION) |
(a_data_uint8_tr[24] & 0x0f))
<< 12);
qmp6988.qmp6988_cali.COE_a0 = qmp6988.qmp6988_cali.COE_a0 >> 12;
qmp6988.qmp6988_cali.COE_a1 =
(QMP6988_S16_t)(((a_data_uint8_tr[20]) << SHIFT_LEFT_8_POSITION) |
a_data_uint8_tr[21]);
qmp6988.qmp6988_cali.COE_a2 =
(QMP6988_S16_t)(((a_data_uint8_tr[22]) << SHIFT_LEFT_8_POSITION) |
a_data_uint8_tr[23]);
qmp6988.qmp6988_cali.COE_b00 =
(QMP6988_S32_t)(((a_data_uint8_tr[0] << SHIFT_LEFT_12_POSITION) |
(a_data_uint8_tr[1] << SHIFT_LEFT_4_POSITION) |
((a_data_uint8_tr[24] & 0xf0) >>
SHIFT_RIGHT_4_POSITION))
<< 12);
qmp6988.qmp6988_cali.COE_b00 = qmp6988.qmp6988_cali.COE_b00 >> 12;
qmp6988.qmp6988_cali.COE_bt1 =
(QMP6988_S16_t)(((a_data_uint8_tr[2]) << SHIFT_LEFT_8_POSITION) |
a_data_uint8_tr[3]);
qmp6988.qmp6988_cali.COE_bt2 =
(QMP6988_S16_t)(((a_data_uint8_tr[4]) << SHIFT_LEFT_8_POSITION) |
a_data_uint8_tr[5]);
qmp6988.qmp6988_cali.COE_bp1 =
(QMP6988_S16_t)(((a_data_uint8_tr[6]) << SHIFT_LEFT_8_POSITION) |
a_data_uint8_tr[7]);
qmp6988.qmp6988_cali.COE_b11 =
(QMP6988_S16_t)(((a_data_uint8_tr[8]) << SHIFT_LEFT_8_POSITION) |
a_data_uint8_tr[9]);
qmp6988.qmp6988_cali.COE_bp2 =
(QMP6988_S16_t)(((a_data_uint8_tr[10]) << SHIFT_LEFT_8_POSITION) |
a_data_uint8_tr[11]);
qmp6988.qmp6988_cali.COE_b12 =
(QMP6988_S16_t)(((a_data_uint8_tr[12]) << SHIFT_LEFT_8_POSITION) |
a_data_uint8_tr[13]);
qmp6988.qmp6988_cali.COE_b21 =
(QMP6988_S16_t)(((a_data_uint8_tr[14]) << SHIFT_LEFT_8_POSITION) |
a_data_uint8_tr[15]);
qmp6988.qmp6988_cali.COE_bp3 =
(QMP6988_S16_t)(((a_data_uint8_tr[16]) << SHIFT_LEFT_8_POSITION) |
a_data_uint8_tr[17]);
QMP6988_LOG("<-----------calibration data-------------->\r\n");
QMP6988_LOG("COE_a0[%d] COE_a1[%d] COE_a2[%d] COE_b00[%d]\r\n",
qmp6988.qmp6988_cali.COE_a0, qmp6988.qmp6988_cali.COE_a1,
qmp6988.qmp6988_cali.COE_a2, qmp6988.qmp6988_cali.COE_b00);
QMP6988_LOG("COE_bt1[%d] COE_bt2[%d] COE_bp1[%d] COE_b11[%d]\r\n",
qmp6988.qmp6988_cali.COE_bt1, qmp6988.qmp6988_cali.COE_bt2,
qmp6988.qmp6988_cali.COE_bp1, qmp6988.qmp6988_cali.COE_b11);
QMP6988_LOG("COE_bp2[%d] COE_b12[%d] COE_b21[%d] COE_bp3[%d]\r\n",
qmp6988.qmp6988_cali.COE_bp2, qmp6988.qmp6988_cali.COE_b12,
qmp6988.qmp6988_cali.COE_b21, qmp6988.qmp6988_cali.COE_bp3);
QMP6988_LOG("<-----------calibration data-------------->\r\n");
qmp6988.ik.a0 = qmp6988.qmp6988_cali.COE_a0; // 20Q4
qmp6988.ik.b00 = qmp6988.qmp6988_cali.COE_b00; // 20Q4
qmp6988.ik.a1 = 3608L * (QMP6988_S32_t)qmp6988.qmp6988_cali.COE_a1 -
1731677965L; // 31Q23
qmp6988.ik.a2 = 16889L * (QMP6988_S32_t)qmp6988.qmp6988_cali.COE_a2 -
87619360L; // 30Q47
qmp6988.ik.bt1 = 2982L * (QMP6988_S64_t)qmp6988.qmp6988_cali.COE_bt1 +
107370906L; // 28Q15
qmp6988.ik.bt2 = 329854L * (QMP6988_S64_t)qmp6988.qmp6988_cali.COE_bt2 +
108083093L; // 34Q38
qmp6988.ik.bp1 = 19923L * (QMP6988_S64_t)qmp6988.qmp6988_cali.COE_bp1 +
1133836764L; // 31Q20
qmp6988.ik.b11 = 2406L * (QMP6988_S64_t)qmp6988.qmp6988_cali.COE_b11 +
118215883L; // 28Q34
qmp6988.ik.bp2 = 3079L * (QMP6988_S64_t)qmp6988.qmp6988_cali.COE_bp2 -
181579595L; // 29Q43
qmp6988.ik.b12 = 6846L * (QMP6988_S64_t)qmp6988.qmp6988_cali.COE_b12 +
85590281L; // 29Q53
qmp6988.ik.b21 = 13836L * (QMP6988_S64_t)qmp6988.qmp6988_cali.COE_b21 +
79333336L; // 29Q60
qmp6988.ik.bp3 = 2915L * (QMP6988_S64_t)qmp6988.qmp6988_cali.COE_bp3 +
157155561L; // 28Q65
QMP6988_LOG("<----------- int calibration data -------------->\r\n");
QMP6988_LOG("a0[%d] a1[%d] a2[%d] b00[%d]\r\n", qmp6988.ik.a0,
qmp6988.ik.a1, qmp6988.ik.a2, qmp6988.ik.b00);
QMP6988_LOG("bt1[%lld] bt2[%lld] bp1[%lld] b11[%lld]\r\n",
qmp6988.ik.bt1, qmp6988.ik.bt2, qmp6988.ik.bp1, qmp6988.ik.b11);
QMP6988_LOG("bp2[%lld] b12[%lld] b21[%lld] bp3[%lld]\r\n",
qmp6988.ik.bp2, qmp6988.ik.b12, qmp6988.ik.b21, qmp6988.ik.bp3);
QMP6988_LOG("<----------- int calibration data -------------->\r\n");
return 1;
}
QMP6988_S16_t QMP6988::convTx02e(qmp6988_ik_data_t* ik, QMP6988_S32_t dt) {
QMP6988_S16_t ret;
QMP6988_S64_t wk1, wk2;
// wk1: 60Q4 // bit size
wk1 = ((QMP6988_S64_t)ik->a1 * (QMP6988_S64_t)dt); // 31Q23+24-1=54 (54Q23)
wk2 = ((QMP6988_S64_t)ik->a2 * (QMP6988_S64_t)dt) >>
14; // 30Q47+24-1=53 (39Q33)
wk2 = (wk2 * (QMP6988_S64_t)dt) >> 10; // 39Q33+24-1=62 (52Q23)
wk2 = ((wk1 + wk2) / 32767) >> 19; // 54,52->55Q23 (20Q04)
ret = (QMP6988_S16_t)((ik->a0 + wk2) >> 4); // 21Q4 -> 17Q0
return ret;
}
QMP6988_S32_t QMP6988::getPressure02e(qmp6988_ik_data_t* ik, QMP6988_S32_t dp,
QMP6988_S16_t tx) {
QMP6988_S32_t ret;
QMP6988_S64_t wk1, wk2, wk3;
// wk1 = 48Q16 // bit size
wk1 =
((QMP6988_S64_t)ik->bt1 * (QMP6988_S64_t)tx); // 28Q15+16-1=43 (43Q15)
wk2 = ((QMP6988_S64_t)ik->bp1 * (QMP6988_S64_t)dp) >>
5; // 31Q20+24-1=54 (49Q15)
wk1 += wk2; // 43,49->50Q15
wk2 = ((QMP6988_S64_t)ik->bt2 * (QMP6988_S64_t)tx) >>
1; // 34Q38+16-1=49 (48Q37)
wk2 = (wk2 * (QMP6988_S64_t)tx) >> 8; // 48Q37+16-1=63 (55Q29)
wk3 = wk2; // 55Q29
wk2 = ((QMP6988_S64_t)ik->b11 * (QMP6988_S64_t)tx) >>
4; // 28Q34+16-1=43 (39Q30)
wk2 = (wk2 * (QMP6988_S64_t)dp) >> 1; // 39Q30+24-1=62 (61Q29)
wk3 += wk2; // 55,61->62Q29
wk2 = ((QMP6988_S64_t)ik->bp2 * (QMP6988_S64_t)dp) >>
13; // 29Q43+24-1=52 (39Q30)
wk2 = (wk2 * (QMP6988_S64_t)dp) >> 1; // 39Q30+24-1=62 (61Q29)
wk3 += wk2; // 62,61->63Q29
wk1 += wk3 >> 14; // Q29 >> 14 -> Q15
wk2 =
((QMP6988_S64_t)ik->b12 * (QMP6988_S64_t)tx); // 29Q53+16-1=45 (45Q53)
wk2 = (wk2 * (QMP6988_S64_t)tx) >> 22; // 45Q53+16-1=61 (39Q31)
wk2 = (wk2 * (QMP6988_S64_t)dp) >> 1; // 39Q31+24-1=62 (61Q30)
wk3 = wk2; // 61Q30
wk2 = ((QMP6988_S64_t)ik->b21 * (QMP6988_S64_t)tx) >>
6; // 29Q60+16-1=45 (39Q54)
wk2 = (wk2 * (QMP6988_S64_t)dp) >> 23; // 39Q54+24-1=62 (39Q31)
wk2 = (wk2 * (QMP6988_S64_t)dp) >> 1; // 39Q31+24-1=62 (61Q20)
wk3 += wk2; // 61,61->62Q30
wk2 = ((QMP6988_S64_t)ik->bp3 * (QMP6988_S64_t)dp) >>
12; // 28Q65+24-1=51 (39Q53)
wk2 = (wk2 * (QMP6988_S64_t)dp) >> 23; // 39Q53+24-1=62 (39Q30)
wk2 = (wk2 * (QMP6988_S64_t)dp); // 39Q30+24-1=62 (62Q30)
wk3 += wk2; // 62,62->63Q30
wk1 += wk3 >> 15; // Q30 >> 15 = Q15
wk1 /= 32767L;
wk1 >>= 11; // Q15 >> 7 = Q4
wk1 += ik->b00; // Q4 + 20Q4
// wk1 >>= 4; // 28Q4 -> 24Q0
ret = (QMP6988_S32_t)wk1;
return ret;
}
void QMP6988::reset() {
uint8_t ret = 0;
ret = _i2c.writeByte(_addr, QMP6988_RESET_REG, 0xe6);
if (ret == 0) {
QMP6988_LOG("reset fail!!! \r\n");
}
delay(20);
ret = _i2c.writeByte(_addr, QMP6988_RESET_REG, 0x00);
}
void QMP6988::setpPowermode(int power_mode) {
uint8_t data;
QMP6988_LOG("qmp_set_powermode %d \r\n", power_mode);
qmp6988.power_mode = power_mode;
_i2c.readBytes(_addr, QMP6988_CTRLMEAS_REG, &data, 1);
data = data & 0xfc;
if (power_mode == QMP6988_SLEEP_MODE) {
data |= 0x00;
} else if (power_mode == QMP6988_FORCED_MODE) {
data |= 0x01;
} else if (power_mode == QMP6988_NORMAL_MODE) {
data |= 0x03;
}
_i2c.writeByte(_addr, QMP6988_CTRLMEAS_REG, data);
QMP6988_LOG("qmp_set_powermode 0xf4=0x%x \r\n", data);
delay(20);
}
void QMP6988::setFilter(unsigned char filter) {
uint8_t data;
data = (filter & 0x03);
_i2c.writeByte(_addr, QMP6988_CONFIG_REG, data);
delay(20);
}
void QMP6988::setOversamplingP(unsigned char oversampling_p) {
uint8_t data;
_i2c.readBytes(_addr, QMP6988_CTRLMEAS_REG, &data, 1);
data &= 0xe3;
data |= (oversampling_p << 2);
_i2c.writeByte(_addr, QMP6988_CTRLMEAS_REG, data);
delay(20);
}
void QMP6988::setOversamplingT(unsigned char oversampling_t) {
uint8_t data;
_i2c.readBytes(_addr, QMP6988_CTRLMEAS_REG, &data, 1);
data &= 0x1f;
data |= (oversampling_t << 5);
_i2c.writeByte(_addr, QMP6988_CTRLMEAS_REG, data);
delay(20);
}
float QMP6988::calcAltitude(float pressure, float temp) {
float altitude;
altitude =
(pow((101325 / pressure), 1 / 5.257) - 1) * (temp + 273.15) / 0.0065;
QMP6988_LOG("altitude = %f\r\n", altitude);
return altitude;
}
float QMP6988::calcPressure() {
uint8_t err = 0;
QMP6988_U32_t P_read, T_read;
QMP6988_S32_t P_raw, T_raw;
uint8_t a_data_uint8_tr[6] = {0};
QMP6988_S32_t T_int, P_int;
// press
err = _i2c.readBytes(_addr, QMP6988_PRESSURE_MSB_REG, a_data_uint8_tr, 6);
if (err == 0) {
QMP6988_LOG("qmp6988 read press raw error! \r\n");
return 0.0f;
}
P_read = (QMP6988_U32_t)((((QMP6988_U32_t)(a_data_uint8_tr[0]))
<< SHIFT_LEFT_16_POSITION) |
(((QMP6988_U16_t)(a_data_uint8_tr[1]))
<< SHIFT_LEFT_8_POSITION) |
(a_data_uint8_tr[2]));
P_raw = (QMP6988_S32_t)(P_read - SUBTRACTOR);
T_read = (QMP6988_U32_t)((((QMP6988_U32_t)(a_data_uint8_tr[3]))
<< SHIFT_LEFT_16_POSITION) |
(((QMP6988_U16_t)(a_data_uint8_tr[4]))
<< SHIFT_LEFT_8_POSITION) |
(a_data_uint8_tr[5]));
T_raw = (QMP6988_S32_t)(T_read - SUBTRACTOR);
T_int = convTx02e(&(qmp6988.ik), T_raw);
P_int = getPressure02e(&(qmp6988.ik), P_raw, T_int);
qmp6988.temperature = (float)T_int / 256.0f;
qmp6988.pressure = (float)P_int / 16.0f;
return qmp6988.pressure;
}
float QMP6988::calcTemperature() {
uint8_t err = 0;
QMP6988_U32_t P_read, T_read;
QMP6988_S32_t P_raw, T_raw;
uint8_t a_data_uint8_tr[6] = {0};
QMP6988_S32_t T_int, P_int;
// press
err = _i2c.readBytes(_addr, QMP6988_PRESSURE_MSB_REG, a_data_uint8_tr, 6);
if (err == 0) {
QMP6988_LOG("qmp6988 read press raw error! \r\n");
return 0.0f;
}
P_read = (QMP6988_U32_t)((((QMP6988_U32_t)(a_data_uint8_tr[0]))
<< SHIFT_LEFT_16_POSITION) |
(((QMP6988_U16_t)(a_data_uint8_tr[1]))
<< SHIFT_LEFT_8_POSITION) |
(a_data_uint8_tr[2]));
P_raw = (QMP6988_S32_t)(P_read - SUBTRACTOR);
// temp
err =
_i2c.readBytes(_addr, QMP6988_TEMPERATURE_MSB_REG, a_data_uint8_tr, 3);
if (err == 0) {
QMP6988_LOG("qmp6988 read temp raw error! \n");
}
T_read = (QMP6988_U32_t)((((QMP6988_U32_t)(a_data_uint8_tr[3]))
<< SHIFT_LEFT_16_POSITION) |
(((QMP6988_U16_t)(a_data_uint8_tr[4]))
<< SHIFT_LEFT_8_POSITION) |
(a_data_uint8_tr[5]));
T_raw = (QMP6988_S32_t)(T_read - SUBTRACTOR);
T_int = convTx02e(&(qmp6988.ik), T_raw);
P_int = getPressure02e(&(qmp6988.ik), P_raw, T_int);
qmp6988.temperature = (float)T_int / 256.0f;
qmp6988.pressure = (float)P_int / 16.0f;
return qmp6988.temperature;
}
bool QMP6988::begin(TwoWire* wire, uint8_t addr, uint8_t sda, uint8_t scl,
long freq) {
_i2c.begin(wire, sda, scl, freq);
_addr = addr;
if (!_i2c.exist(_addr)) {
return false;
}
reset();
getCalibrationData();
setpPowermode(QMP6988_NORMAL_MODE);
setFilter(QMP6988_FILTERCOEFF_4);
setOversamplingP(QMP6988_OVERSAMPLING_8X);
setOversamplingT(QMP6988_OVERSAMPLING_1X);
return true;
}
bool QMP6988::update() {
pressure = calcPressure();
cTemp = calcTemperature();
altitude = calcAltitude(pressure, cTemp);
return true;
}

View file

@ -0,0 +1,153 @@
#ifndef __QMP6988_H
#define __QMP6988_H
#include "Arduino.h"
#include "Wire.h"
#include "I2C_Class.h"
#define QMP6988_SLAVE_ADDRESS_L (0x70)
#define QMP6988_SLAVE_ADDRESS_H (0x56)
#define QMP6988_U16_t unsigned short
#define QMP6988_S16_t short
#define QMP6988_U32_t unsigned int
#define QMP6988_S32_t int
#define QMP6988_U64_t unsigned long long
#define QMP6988_S64_t long long
#define QMP6988_CHIP_ID 0x5C
#define QMP6988_CHIP_ID_REG 0xD1
#define QMP6988_RESET_REG 0xE0 /* Device reset register */
#define QMP6988_DEVICE_STAT_REG 0xF3 /* Device state register */
#define QMP6988_CTRLMEAS_REG 0xF4 /* Measurement Condition Control Register */
/* data */
#define QMP6988_PRESSURE_MSB_REG 0xF7 /* Pressure MSB Register */
#define QMP6988_TEMPERATURE_MSB_REG 0xFA /* Temperature MSB Reg */
/* compensation calculation */
#define QMP6988_CALIBRATION_DATA_START \
0xA0 /* QMP6988 compensation coefficients */
#define QMP6988_CALIBRATION_DATA_LENGTH 25
#define SHIFT_RIGHT_4_POSITION 4
#define SHIFT_LEFT_2_POSITION 2
#define SHIFT_LEFT_4_POSITION 4
#define SHIFT_LEFT_5_POSITION 5
#define SHIFT_LEFT_8_POSITION 8
#define SHIFT_LEFT_12_POSITION 12
#define SHIFT_LEFT_16_POSITION 16
/* power mode */
#define QMP6988_SLEEP_MODE 0x00
#define QMP6988_FORCED_MODE 0x01
#define QMP6988_NORMAL_MODE 0x03
#define QMP6988_CTRLMEAS_REG_MODE__POS 0
#define QMP6988_CTRLMEAS_REG_MODE__MSK 0x03
#define QMP6988_CTRLMEAS_REG_MODE__LEN 2
/* oversampling */
#define QMP6988_OVERSAMPLING_SKIPPED 0x00
#define QMP6988_OVERSAMPLING_1X 0x01
#define QMP6988_OVERSAMPLING_2X 0x02
#define QMP6988_OVERSAMPLING_4X 0x03
#define QMP6988_OVERSAMPLING_8X 0x04
#define QMP6988_OVERSAMPLING_16X 0x05
#define QMP6988_OVERSAMPLING_32X 0x06
#define QMP6988_OVERSAMPLING_64X 0x07
#define QMP6988_CTRLMEAS_REG_OSRST__POS 5
#define QMP6988_CTRLMEAS_REG_OSRST__MSK 0xE0
#define QMP6988_CTRLMEAS_REG_OSRST__LEN 3
#define QMP6988_CTRLMEAS_REG_OSRSP__POS 2
#define QMP6988_CTRLMEAS_REG_OSRSP__MSK 0x1C
#define QMP6988_CTRLMEAS_REG_OSRSP__LEN 3
/* filter */
#define QMP6988_FILTERCOEFF_OFF 0x00
#define QMP6988_FILTERCOEFF_2 0x01
#define QMP6988_FILTERCOEFF_4 0x02
#define QMP6988_FILTERCOEFF_8 0x03
#define QMP6988_FILTERCOEFF_16 0x04
#define QMP6988_FILTERCOEFF_32 0x05
#define QMP6988_CONFIG_REG 0xF1 /*IIR filter co-efficient setting Register*/
#define QMP6988_CONFIG_REG_FILTER__POS 0
#define QMP6988_CONFIG_REG_FILTER__MSK 0x07
#define QMP6988_CONFIG_REG_FILTER__LEN 3
#define SUBTRACTOR 8388608
typedef struct _qmp6988_cali_data {
QMP6988_S32_t COE_a0;
QMP6988_S16_t COE_a1;
QMP6988_S16_t COE_a2;
QMP6988_S32_t COE_b00;
QMP6988_S16_t COE_bt1;
QMP6988_S16_t COE_bt2;
QMP6988_S16_t COE_bp1;
QMP6988_S16_t COE_b11;
QMP6988_S16_t COE_bp2;
QMP6988_S16_t COE_b12;
QMP6988_S16_t COE_b21;
QMP6988_S16_t COE_bp3;
} qmp6988_cali_data_t;
typedef struct _qmp6988_fk_data {
float a0, b00;
float a1, a2, bt1, bt2, bp1, b11, bp2, b12, b21, bp3;
} qmp6988_fk_data_t;
typedef struct _qmp6988_ik_data {
QMP6988_S32_t a0, b00;
QMP6988_S32_t a1, a2;
QMP6988_S64_t bt1, bt2, bp1, b11, bp2, b12, b21, bp3;
} qmp6988_ik_data_t;
typedef struct _qmp6988_data {
uint8_t slave;
uint8_t chip_id;
uint8_t power_mode;
float temperature;
float pressure;
float altitude;
qmp6988_cali_data_t qmp6988_cali;
qmp6988_ik_data_t ik;
} qmp6988_data_t;
class QMP6988 {
private:
qmp6988_data_t qmp6988;
uint8_t _addr;
I2C_Class _i2c;
// read calibration data from otp
int getCalibrationData();
QMP6988_S32_t getPressure02e(qmp6988_ik_data_t* ik, QMP6988_S32_t dp,
QMP6988_S16_t tx);
QMP6988_S16_t convTx02e(qmp6988_ik_data_t* ik, QMP6988_S32_t dt);
void reset();
public:
bool begin(TwoWire* wire = &Wire, uint8_t addr = QMP6988_SLAVE_ADDRESS_H,
uint8_t sda = 21, uint8_t scl = 22, long freq = 400000U);
bool update();
float pressure = 0;
float cTemp = 0;
float altitude = 0;
float calcAltitude(float pressure, float temp);
float calcPressure();
float calcTemperature();
void setpPowermode(int power_mode);
void setFilter(unsigned char filter);
void setOversamplingP(unsigned char oversampling_p);
void setOversamplingT(unsigned char oversampling_t);
};
#endif

View file

@ -0,0 +1,793 @@
/*
This is a library written for the SCD4X family of CO2 sensors
SparkFun sells these at its website: www.sparkfun.com
Do you like this library? Help support SparkFun. Buy a board!
https://www.sparkfun.com/products/18365
Written by Paul Clark @ SparkFun Electronics, June 2nd, 2021
The SCD41 measures CO2 from 400ppm to 5000ppm with an accuracy of +/- 40ppm +
5% of reading
This library handles the initialization of the SCD4X and outputs
CO2 levels, relative humidty, and temperature.
https://github.com/sparkfun/SparkFun_SCD4x_Arduino_Library
Development environment specifics:
Arduino IDE 1.8.13
SparkFun code, firmware, and software is released under the MIT License.
Please see LICENSE.md for more details.
*/
#include "SCD4X.h"
SCD4X::SCD4X(scd4x_sensor_type_e sensorType) {
// Constructor
_sensorType = sensorType;
}
bool SCD4X::begin(TwoWire *wire, uint8_t addr, uint8_t sda, uint8_t scl,
long freq, bool measBegin, bool autoCalibrate,
bool skipStopPeriodicMeasurements,
bool pollAndSetDeviceType) {
_i2c.begin(wire, sda, scl, freq);
_addr = addr;
_wire = wire;
if (!_i2c.exist(_addr)) {
return false;
}
bool success = true;
// If periodic measurements are already running, getSerialNumber will
// fail... To be safe, let's stop period measurements before we do anything
// else The user can override this by setting skipStopPeriodicMeasurements
// to true
if (skipStopPeriodicMeasurements == false)
success &= stopPeriodicMeasurement(); // Delays for 500ms...
char serialNumber[13]; // Serial number is 12 digits plus trailing NULL
success &= getSerialNumber(serialNumber); // Read the serial number. Return
// false if the CRC check fails.
if (pollAndSetDeviceType == true) {
scd4x_sensor_type_e sensorType;
success &= getFeatureSetVersion(&sensorType);
setSensorType(sensorType);
}
if (autoCalibrate ==
true) // Must be done before periodic measurements are started
{
success &= setAutomaticSelfCalibrationEnabled(true);
success &= (getAutomaticSelfCalibrationEnabled() == true);
} else {
success &= setAutomaticSelfCalibrationEnabled(false);
success &= (getAutomaticSelfCalibrationEnabled() == false);
}
if (measBegin == true) {
success &= startPeriodicMeasurement();
}
return (success);
}
bool SCD4X::update() {
return readMeasurement();
}
// Start periodic measurements. See 3.5.1
// signal update interval is 5 seconds.
bool SCD4X::startPeriodicMeasurement(void) {
if (periodicMeasurementsAreRunning) {
return (true); // Maybe this should be false?
}
bool success = sendCommand(SCD4x_COMMAND_START_PERIODIC_MEASUREMENT);
if (success) periodicMeasurementsAreRunning = true;
return (success);
}
// Stop periodic measurements. See 3.5.3
// Stop periodic measurement to change the sensor configuration or to save
// power. Note that the sensor will only respond to other commands after waiting
// 500 ms after issuing the stop_periodic_measurement command.
bool SCD4X::stopPeriodicMeasurement(uint16_t delayMillis, TwoWire &wirePort)
{
uint8_t i2cResult;
if (_wire != NULL) // If the sensor has been begun (_wire is not
// NULL) then _wire is used
{
_wire->beginTransmission(_addr);
_wire->write(SCD4x_COMMAND_STOP_PERIODIC_MEASUREMENT >> 8); // MSB
_wire->write(SCD4x_COMMAND_STOP_PERIODIC_MEASUREMENT & 0xFF); // LSB
i2cResult = _wire->endTransmission();
} else {
// If the sensor has not been begun (_wire is NULL) then wirePort
// is used (which will default to Wire)
wirePort.beginTransmission(_addr);
wirePort.write(SCD4x_COMMAND_STOP_PERIODIC_MEASUREMENT >> 8); // MSB
wirePort.write(SCD4x_COMMAND_STOP_PERIODIC_MEASUREMENT & 0xFF); // LSB
i2cResult = wirePort.endTransmission();
}
if (i2cResult == 0) {
periodicMeasurementsAreRunning = false;
if (delayMillis > 0) delay(delayMillis);
return (true);
}
return (false);
}
// Get 9 bytes from SCD4X. See 3.5.2
// Updates global variables with floats
// Returns true if data is read successfully
// Read sensor output. The measurement data can only be read out once per
// signal update interval as the buffer is emptied upon read-out. If no data
// is available in the buffer, the sensor returns a NACK. To avoid a NACK
// response, the get_data_ready_status can be issued to check data status
// (see chapter 3.8.2 for further details).
bool SCD4X::readMeasurement(void) {
// Verify we have data from the sensor
if (getDataReadyStatus() == false) return (false);
scd4x_unsigned16Bytes_t tempCO2;
tempCO2.unsigned16 = 0;
scd4x_unsigned16Bytes_t tempHumidity;
tempHumidity.unsigned16 = 0;
scd4x_unsigned16Bytes_t tempTemperature;
tempTemperature.unsigned16 = 0;
_wire->beginTransmission(_addr);
_wire->write(SCD4x_COMMAND_READ_MEASUREMENT >> 8); // MSB
_wire->write(SCD4x_COMMAND_READ_MEASUREMENT & 0xFF); // LSB
if (_wire->endTransmission() != 0) return (false); // Sensor did not ACK
delay(1); // Datasheet specifies this
_wire->requestFrom((uint8_t)_addr, (uint8_t)9);
bool error = false;
if (_wire->available()) {
byte bytesToCrc[2];
for (byte x = 0; x < 9; x++) {
byte incoming = _wire->read();
switch (x) {
case 0:
case 1:
tempCO2.bytes[x == 0 ? 1 : 0] =
incoming; // Store the two CO2 bytes in
// little-endian format
bytesToCrc[x] =
incoming; // Calculate the CRC on the two CO2 bytes
// in the order they arrive
break;
case 3:
case 4:
tempTemperature.bytes[x == 3 ? 1 : 0] =
incoming; // Store the two T bytes in little-endian
// format
bytesToCrc[x % 3] =
incoming; // Calculate the CRC on the two T bytes
// in the order they arrive
break;
case 6:
case 7:
tempHumidity.bytes[x == 6 ? 1 : 0] =
incoming; // Store the two RH bytes in
// little-endian format
bytesToCrc[x % 3] =
incoming; // Calculate the CRC on the two RH bytes
// in the order they arrive
break;
default: // x == 2, 5, 8
// Validate CRC
uint8_t foundCrc = computeCRC8(
bytesToCrc, 2); // Calculate what the CRC should be
// for these two bytes
if (foundCrc != incoming) // Does this match the CRC
// byte from the sensor?
{
error = true;
}
break;
}
}
} else {
return (false);
}
if (error) {
return (false);
}
// Now copy the int16s into their associated floats
co2 = (float)tempCO2.unsigned16;
temperature = -45 + (((float)tempTemperature.unsigned16) * 175 / 65536);
humidity = ((float)tempHumidity.unsigned16) * 100 / 65536;
// Mark our global variables as fresh
co2HasBeenReported = false;
humidityHasBeenReported = false;
temperatureHasBeenReported = false;
return (true); // Success! New data available in globals.
}
// Returns the latest available CO2 level
// If the current level has already been reported, trigger a new read
uint16_t SCD4X::getCO2(void) {
if (co2HasBeenReported == true) // Trigger a new read
readMeasurement(); // Pull in new co2, humidity, and temp into
// global vars
co2HasBeenReported = true;
return (uint16_t)co2; // Cut off decimal as co2 is 0 to 10,000
}
// Returns the latest available humidity
// If the current level has already been reported, trigger a new read
float SCD4X::getHumidity(void) {
if (humidityHasBeenReported == true) // Trigger a new read
readMeasurement(); // Pull in new co2, humidity, and temp into
// global vars
humidityHasBeenReported = true;
return humidity;
}
// Returns the latest available temperature
// If the current level has already been reported, trigger a new read
float SCD4X::getTemperature(void) {
if (temperatureHasBeenReported == true) // Trigger a new read
readMeasurement(); // Pull in new co2, humidity, and temp into
// global vars
temperatureHasBeenReported = true;
return temperature;
}
// Set the temperature offset (C). See 3.6.1
// Max command duration: 1ms
// The user can set delayMillis to zero f they want the function to return
// immediately. The temperature offset has no influence on the SCD4X CO2
// accuracy. Setting the temperature offset of the SCD4X inside the customer
// device correctly allows the user to leverage the RH and T output signal.
bool SCD4X::setTemperatureOffset(float offset, uint16_t delayMillis) {
if (periodicMeasurementsAreRunning) {
return (false);
}
if (offset < 0) {
return (false);
}
if (offset >= 175) {
return (false);
}
uint16_t offsetWord =
(uint16_t)(offset * 65536 / 175); // Toffset [°C] * 2^16 / 175
bool success =
sendCommand(SCD4x_COMMAND_SET_TEMPERATURE_OFFSET, offsetWord);
if (delayMillis > 0) delay(delayMillis);
return (success);
}
// Get the temperature offset. See 3.6.2
float SCD4X::getTemperatureOffset(void) {
if (periodicMeasurementsAreRunning) {
return (0.0);
}
float offset;
getTemperatureOffset(&offset);
return (offset);
}
// Get the temperature offset. See 3.6.2
bool SCD4X::getTemperatureOffset(float *offset) {
if (periodicMeasurementsAreRunning) {
return (false);
}
uint16_t offsetWord = 0; // offset will be zero if readRegister fails
bool success =
readRegister(SCD4x_COMMAND_GET_TEMPERATURE_OFFSET, &offsetWord, 1);
*offset = ((float)offsetWord) * 175.0 / 65536.0;
return (success);
}
// Set the sensor altitude (metres above sea level). See 3.6.3
// Max command duration: 1ms
// The user can set delayMillis to zero if they want the function to return
// immediately. Reading and writing of the sensor altitude must be done
// while the SCD4X is in idle mode. Typically, the sensor altitude is set
// once after device installation. To save the setting to the EEPROM, the
// persist setting (see chapter 3.9.1) command must be issued. Per default,
// the sensor altitude is set to 0 meter above sea-level.
bool SCD4X::setSensorAltitude(uint16_t altitude, uint16_t delayMillis) {
if (periodicMeasurementsAreRunning) {
return (false);
}
bool success = sendCommand(SCD4x_COMMAND_SET_SENSOR_ALTITUDE, altitude);
if (delayMillis > 0) delay(delayMillis);
return (success);
}
// Get the sensor altitude. See 3.6.4
uint16_t SCD4X::getSensorAltitude(void) {
if (periodicMeasurementsAreRunning) {
return (0);
}
uint16_t altitude = 0;
getSensorAltitude(&altitude);
return (altitude);
}
// Get the sensor altitude. See 3.6.4
bool SCD4X::getSensorAltitude(uint16_t *altitude) {
if (periodicMeasurementsAreRunning) {
return (false);
}
return (readRegister(SCD4x_COMMAND_GET_SENSOR_ALTITUDE, altitude, 1));
}
// Set the ambient pressure (Pa). See 3.6.5
// Max command duration: 1ms
// The user can set delayMillis to zero if they want the function to return
// immediately. The set_ambient_pressure command can be sent during periodic
// measurements to enable continuous pressure compensation.
// setAmbientPressure overrides setSensorAltitude
bool SCD4X::setAmbientPressure(float pressure, uint16_t delayMillis) {
if (pressure < 0) {
return (false);
}
if (pressure > 6553500) {
return (false);
}
uint16_t pressureWord = (uint16_t)(pressure / 100);
bool success =
sendCommand(SCD4x_COMMAND_SET_AMBIENT_PRESSURE, pressureWord);
if (delayMillis > 0) delay(delayMillis);
return (success);
}
// Perform forced recalibration. See 3.7.1
float SCD4X::performForcedRecalibration(uint16_t concentration) {
if (periodicMeasurementsAreRunning) {
return (0.0);
}
float correction = 0.0;
performForcedRecalibration(concentration, &correction);
return (correction);
}
// Perform forced recalibration. See 3.7.1
// To successfully conduct an accurate forced recalibration, the following
// steps need to be carried out:
// 1. Operate the SCD4X in the operation mode later used in normal sensor
// operation (periodic measurement,
// low power periodic measurement or single shot) for > 3 minutes in an
// environment with homogenous and constant CO2 concentration.
// 2. Issue stop_periodic_measurement. Wait 500 ms for the stop command to
// complete.
// 3. Subsequently issue the perform_forced_recalibration command and
// optionally read out the FRC correction
// (i.e. the magnitude of the correction) after waiting for 400 ms for
// the command to complete.
// A return value of 0xffff indicates that the forced recalibration has
// failed.
bool SCD4X::performForcedRecalibration(uint16_t concentration,
float *correction) {
if (periodicMeasurementsAreRunning) {
return (false);
}
uint16_t correctionWord;
bool success =
sendCommand(SCD4x_COMMAND_PERFORM_FORCED_CALIBRATION, concentration);
if (success == false) return (false);
delay(400); // Datasheet specifies this
_wire->requestFrom((uint8_t)_addr, (uint8_t)3);
bool error = false;
if (_wire->available()) {
byte bytesToCrc[2];
bytesToCrc[0] = _wire->read();
correctionWord = ((uint16_t)bytesToCrc[0]) << 8;
bytesToCrc[1] = _wire->read();
correctionWord |= (uint16_t)bytesToCrc[1];
byte incomingCrc = _wire->read();
uint8_t foundCrc = computeCRC8(bytesToCrc, 2);
if (foundCrc != incomingCrc) {
error = true;
}
} else {
return (false);
}
if (error) {
return (false);
}
*correction = ((float)correctionWord) -
32768; // FRC correction [ppm CO2] = word[0] 0x8000
if (correctionWord == 0xffff) // A return value of 0xffff indicates that
// the forced recalibration has failed
return (false);
return (true);
}
// Enable/disable automatic self calibration. See 3.7.2
// Set the current state (enabled / disabled) of the automatic
// self-calibration. By default, ASC is enabled. To save the setting to the
// EEPROM, the persist_setting (see chapter 3.9.1) command must be issued.
bool SCD4X::setAutomaticSelfCalibrationEnabled(bool enabled,
uint16_t delayMillis) {
if (periodicMeasurementsAreRunning) {
return (false);
}
uint16_t enabledWord = enabled == true ? 0x0001 : 0x0000;
bool success = sendCommand(
SCD4x_COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED, enabledWord);
if (delayMillis > 0) delay(delayMillis);
return (success);
}
// Check if automatic self calibration is enabled. See 3.7.3
bool SCD4X::getAutomaticSelfCalibrationEnabled(void) {
if (periodicMeasurementsAreRunning) {
return (false);
}
uint16_t enabled;
bool success = getAutomaticSelfCalibrationEnabled(&enabled);
if (success == false) {
return (false);
}
return (enabled == 0x0001);
}
// Check if automatic self calibration is enabled. See 3.7.3
bool SCD4X::getAutomaticSelfCalibrationEnabled(uint16_t *enabled) {
if (periodicMeasurementsAreRunning) {
return (false);
}
return (readRegister(SCD4x_COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED,
enabled, 1));
}
// Start low power periodic measurements. See 3.8.1
// Signal update interval will be 30 seconds instead of 5
bool SCD4X::startLowPowerPeriodicMeasurement(void) {
if (periodicMeasurementsAreRunning) {
return (false);
}
bool success =
sendCommand(SCD4x_COMMAND_START_LOW_POWER_PERIODIC_MEASUREMENT);
if (success) periodicMeasurementsAreRunning = true;
return (success);
}
// Returns true when data is available. See 3.8.2
bool SCD4X::getDataReadyStatus(void) {
uint16_t response;
bool success =
readRegister(SCD4x_COMMAND_GET_DATA_READY_STATUS, &response, 1);
if (success == false) return (false);
// If the least significant 11 bits of word[0] are 0 → data not ready
// else → data ready for read-out
if ((response & 0x07ff) == 0x0000) return (false);
return (true);
}
// Persist settings: copy settings (e.g. temperature offset) from RAM to
// EEPROM. See 3.9.1 Configuration settings such as the temperature offset,
// sensor altitude and the ASC enabled/disabled parameter are by default
// stored in the volatile memory (RAM) only and will be lost after a
// power-cycle. The persist_settings command stores the current
// configuration in the EEPROM of the SCD4X, making them persistent across
// power-cycling. To avoid unnecessary wear of the EEPROM, the
// persist_settings command should only be sent when persistence is required
// and if actual changes to the configuration have been made. The EEPROM is
// guaranteed to endure at least 2000 write cycles before failure.
bool SCD4X::persistSettings(uint16_t delayMillis) {
if (periodicMeasurementsAreRunning) {
return (false);
}
bool success = sendCommand(SCD4x_COMMAND_PERSIST_SETTINGS);
if (delayMillis > 0) delay(delayMillis);
return (success);
}
// Get 9 bytes from SCD4X. Convert 48-bit serial number to ASCII chars.
// See 3.9.2 Returns true if serial number is read successfully Reading out
// the serial number can be used to identify the chip and to verify the
// presence of the sensor.
bool SCD4X::getSerialNumber(char *serialNumber) {
if (periodicMeasurementsAreRunning) {
return (false);
}
_wire->beginTransmission(_addr);
_wire->write(SCD4x_COMMAND_GET_SERIAL_NUMBER >> 8); // MSB
_wire->write(SCD4x_COMMAND_GET_SERIAL_NUMBER & 0xFF); // LSB
if (_wire->endTransmission() != 0) return (false); // Sensor did not ACK
delay(1); // Datasheet specifies this
_wire->requestFrom((uint8_t)_addr, (uint8_t)9);
bool error = false;
if (_wire->available()) {
byte bytesToCrc[2];
int digit = 0;
for (byte x = 0; x < 9; x++) {
byte incoming = _wire->read();
switch (x) {
case 0: // The serial number arrives as: two bytes, CRC,
// two bytes, CRC, two bytes, CRC
case 1:
case 3:
case 4:
case 6:
case 7:
serialNumber[digit++] = convertHexToASCII(
incoming >> 4); // Convert each nibble to ASCII
serialNumber[digit++] = convertHexToASCII(incoming & 0x0F);
bytesToCrc[x % 3] = incoming;
break;
default: // x == 2, 5, 8
// Validate CRC
uint8_t foundCrc = computeCRC8(
bytesToCrc, 2); // Calculate what the CRC should be
// for these two bytes
if (foundCrc != incoming) // Does this match the CRC
// byte from the sensor?
{
error = true;
}
break;
}
serialNumber[digit] = 0; // NULL-terminate the string
}
} else {
return (false);
}
if (error) {
return (false);
}
return (true); // Success!
}
// PRIVATE: Convert serial number digit to ASCII
char SCD4X::convertHexToASCII(uint8_t digit) {
if (digit <= 9)
return (char(digit + 0x30));
else
return (char(digit + 0x41 - 10)); // Use upper case for A-F
}
// Perform self test. Takes 10 seconds to complete. See 3.9.3
// The perform_self_test feature can be used as an end-of-line test to check
// sensor functionality and the customer power supply to the sensor.
bool SCD4X::performSelfTest(void) {
if (periodicMeasurementsAreRunning) {
return (false);
}
uint16_t response;
bool success =
readRegister(SCD4x_COMMAND_PERFORM_SELF_TEST, &response, 10000);
return (success &&
(response == 0x0000)); // word[0] = 0 → no malfunction detected
}
// Peform factory reset. See 3.9.4
// The perform_factory_reset command resets all configuration settings
// stored in the EEPROM and erases the FRC and ASC algorithm history.
bool SCD4X::performFactoryReset(uint16_t delayMillis) {
if (periodicMeasurementsAreRunning) {
return (false);
}
bool success = sendCommand(SCD4x_COMMAND_PERFORM_FACTORY_RESET);
if (delayMillis > 0) delay(delayMillis);
return (success);
}
// Reinit. See 3.9.5
// The reinit command reinitializes the sensor by reloading user settings
// from EEPROM. Before sending the reinit command, the stop measurement
// command must be issued. If the reinit command does not trigger the
// desired re-initialization, a power-cycle should be applied to the SCD4X.
bool SCD4X::reInit(uint16_t delayMillis) {
if (periodicMeasurementsAreRunning) {
return (false);
}
bool success = sendCommand(SCD4x_COMMAND_REINIT);
if (delayMillis > 0) delay(delayMillis);
return (success);
}
// Low Power Single Shot. See 3.10.1
// In addition to periodic measurement modes, the SCD41 features a single
// shot measurement mode, i.e. allows for on-demand measurements. The
// typical communication sequence is as follows:
// 1. The sensor is powered up.
// 2. The I2C master sends a single shot command and waits for the indicated
// max. command duration time.
// 3. The I2C master reads out data with the read measurement sequence
// (chapter 3.5.2).
// 4. Steps 2-3 are repeated as required by the application.
bool SCD4X::measureSingleShot(void) {
if (_sensorType != SCD4x_SENSOR_SCD41) {
return (false);
}
if (periodicMeasurementsAreRunning) {
return (false);
}
bool success = sendCommand(SCD4x_COMMAND_MEASURE_SINGLE_SHOT);
return (success);
}
// On-demand measurement of relative humidity and temperature only.
// The sensor output is read using the read_measurement command
// (chapter 3.5.2). CO2 output is returned as 0 ppm.
bool SCD4X::measureSingleShotRHTOnly(void) {
if (_sensorType != SCD4x_SENSOR_SCD41) {
return (false);
}
if (periodicMeasurementsAreRunning) {
return (false);
}
bool success = sendCommand(SCD4x_COMMAND_MEASURE_SINGLE_SHOT_RHT_ONLY);
return (success);
}
scd4x_sensor_type_e SCD4X::getSensorType(void) {
return _sensorType;
}
void SCD4X::setSensorType(scd4x_sensor_type_e sensorType) {
_sensorType = sensorType;
}
bool SCD4X::getFeatureSetVersion(scd4x_sensor_type_e *sensorType) {
if (periodicMeasurementsAreRunning) {
return (false);
}
uint16_t featureSet;
bool success =
readRegister(SCD4x_COMMAND_GET_FEATURE_SET_VERSION, &featureSet, 1);
uint8_t typeOfSensor = ((featureSet & 0x1000) >> 12);
if (typeOfSensor == 0) {
*sensorType = SCD4x_SENSOR_SCD40;
} else if (typeOfSensor == 1) {
*sensorType = SCD4x_SENSOR_SCD41;
} else {
*sensorType = SCD4x_SENSOR_INVALID;
success = false;
}
return (success);
}
// Sends a command along with arguments and CRC
bool SCD4X::sendCommand(uint16_t command, uint16_t arguments) {
uint8_t data[2];
data[0] = arguments >> 8;
data[1] = arguments & 0xFF;
uint8_t crc = computeCRC8(
data, 2); // Calc CRC on the arguments only, not the command
_wire->beginTransmission(_addr);
_wire->write(command >> 8); // MSB
_wire->write(command & 0xFF); // LSB
_wire->write(arguments >> 8); // MSB
_wire->write(arguments & 0xFF); // LSB
_wire->write(crc);
if (_wire->endTransmission() != 0) return (false); // Sensor did not ACK
return (true);
}
// Sends just a command, no arguments, no CRC
bool SCD4X::sendCommand(uint16_t command) {
_wire->beginTransmission(_addr);
_wire->write(command >> 8); // MSB
_wire->write(command & 0xFF); // LSB
if (_wire->endTransmission() != 0) return (false); // Sensor did not ACK
return (true);
}
// Gets two bytes from SCD4X plus CRC.
// Returns true if endTransmission returns zero _and_ the CRC check is valid
bool SCD4X::readRegister(uint16_t registerAddress, uint16_t *response,
uint16_t delayMillis) {
_wire->beginTransmission(_addr);
_wire->write(registerAddress >> 8); // MSB
_wire->write(registerAddress & 0xFF); // LSB
if (_wire->endTransmission() != 0) return (false); // Sensor did not ACK
delay(delayMillis);
_wire->requestFrom((uint8_t)_addr,
(uint8_t)3); // Request data and CRC
if (_wire->available()) {
uint8_t data[2];
data[0] = _wire->read();
data[1] = _wire->read();
uint8_t crc = _wire->read();
*response = (uint16_t)data[0] << 8 | data[1];
uint8_t expectedCRC = computeCRC8(data, 2);
if (crc == expectedCRC) // Return true if CRC check is OK
return (true);
}
return (false);
}
// Given an array and a number of bytes, this calculate CRC8 for those bytes
// CRC is only calc'd on the data portion (two bytes) of the four bytes
// being sent From:
// http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html
// Tested with: http://www.sunshine2k.de/coding/javascript/crc/crc_js.html
// x^8+x^5+x^4+1 = 0x31
uint8_t SCD4X::computeCRC8(uint8_t data[], uint8_t len) {
uint8_t crc = 0xFF; // Init with 0xFF
for (uint8_t x = 0; x < len; x++) {
crc ^= data[x]; // XOR-in the next input byte
for (uint8_t i = 0; i < 8; i++) {
if ((crc & 0x80) != 0)
crc = (uint8_t)((crc << 1) ^ 0x31);
else
crc <<= 1;
}
}
return crc; // No output reflection
}

View file

@ -0,0 +1,233 @@
/*
This is a library written for the SCD4X family of CO2 sensors
SparkFun sells these at its website: www.sparkfun.com
Do you like this library? Help support SparkFun. Buy a board!
https://www.sparkfun.com/products/18365
Written by Paul Clark @ SparkFun Electronics, June 2nd, 2021
Revision by Alex Brudner @ SparkFun Electronics
The SCD41 measures CO2 from 400ppm to 5000ppm with an accuracy of +/- 40ppm +
5% of reading
This library handles the initialization of the SCD4X and outputs
CO2 levels, relative humidty, and temperature.
https://github.com/sparkfun/SparkFun_SCD4x_Arduino_Library
Development environment specifics:
Arduino IDE 1.8.13 and 2.1.0
SparkFun code, firmware, and software is released under the MIT License.
Please see LICENSE.md for more details.
*/
#ifndef __SCD4X_H__
#define __SCD4X_H__
// Uncomment the next #define if using an Teensy >= 3 or Teensy LC and want to
// use the dedicated I2C-Library for it Then you also have to include <i2c_t3.h>
// on your application instead of <Wire.h>
// #define USE_TEENSY3_I2C_LIB
// Uncomment the next #define to EXclude any debug logging from the code, by
// default debug logging code will be included
// #define SCD4x_ENABLE_DEBUGLOG 0 // OFF/disabled/excluded on demand
#include "Arduino.h"
#include "I2C_Class.h"
// The default I2C address for the SCD4X is 0x62.
#define SCD4X_I2C_ADDR 0x62
// Available commands
// Basic Commands
#define SCD4x_COMMAND_START_PERIODIC_MEASUREMENT 0x21b1
#define SCD4x_COMMAND_READ_MEASUREMENT 0xec05 // execution time: 1ms
#define SCD4x_COMMAND_STOP_PERIODIC_MEASUREMENT 0x3f86 // execution time: 500ms
// On-chip output signal compensation
#define SCD4x_COMMAND_SET_TEMPERATURE_OFFSET 0x241d // execution time: 1ms
#define SCD4x_COMMAND_GET_TEMPERATURE_OFFSET 0x2318 // execution time: 1ms
#define SCD4x_COMMAND_SET_SENSOR_ALTITUDE 0x2427 // execution time: 1ms
#define SCD4x_COMMAND_GET_SENSOR_ALTITUDE 0x2322 // execution time: 1ms
#define SCD4x_COMMAND_SET_AMBIENT_PRESSURE 0xe000 // execution time: 1ms
// Field calibration
#define SCD4x_COMMAND_PERFORM_FORCED_CALIBRATION \
0x362f // execution time: 400ms
#define SCD4x_COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED \
0x2416 // execution time: 1ms
#define SCD4x_COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED \
0x2313 // execution time: 1ms
// Low power
#define SCD4x_COMMAND_START_LOW_POWER_PERIODIC_MEASUREMENT 0x21ac
#define SCD4x_COMMAND_GET_DATA_READY_STATUS 0xe4b8 // execution time: 1ms
// Advanced features
#define SCD4x_COMMAND_PERSIST_SETTINGS 0x3615 // execution time: 800ms
#define SCD4x_COMMAND_GET_SERIAL_NUMBER 0x3682 // execution time: 1ms
#define SCD4x_COMMAND_PERFORM_SELF_TEST 0x3639 // execution time: 10000ms
#define SCD4x_COMMAND_PERFORM_FACTORY_RESET 0x3632 // execution time: 1200ms
#define SCD4x_COMMAND_REINIT 0x3646 // execution time: 20ms
#define SCD4x_COMMAND_GET_FEATURE_SET_VERSION 0x202F // execution time: 1ms
// Low power single shot - SCD41 only
#define SCD4x_COMMAND_MEASURE_SINGLE_SHOT 0x219d // execution time: 5000ms
#define SCD4x_COMMAND_MEASURE_SINGLE_SHOT_RHT_ONLY \
0x2196 // execution time: 50ms
typedef union {
int16_t signed16;
uint16_t unsigned16;
} scd4x_signedUnsigned16_t; // Avoid any ambiguity casting int16_t to uint16_t
typedef union {
uint16_t unsigned16;
uint8_t bytes[2];
} scd4x_unsigned16Bytes_t; // Make it easy to convert 2 x uint8_t to uint16_t
typedef enum {
SCD4x_SENSOR_SCD40 = 0,
SCD4x_SENSOR_SCD41,
SCD4x_SENSOR_INVALID
} scd4x_sensor_type_e;
class SCD4X {
private:
TwoWire *_wire;
uint8_t _addr;
I2C_Class _i2c;
public:
SCD4X(scd4x_sensor_type_e sensorType = SCD4x_SENSOR_SCD40);
bool begin(TwoWire *wire = &Wire, uint8_t addr = SCD4X_I2C_ADDR,
uint8_t sda = 21, uint8_t scl = 22, long freq = 400000U,
bool measBegin = true, bool autoCalibrate = true,
bool skipStopPeriodicMeasurements = false,
bool pollAndSetDeviceType = true
);
bool update(void);
bool startPeriodicMeasurement(void); // Signal update interval is 5 seconds
// stopPeriodicMeasurement can be called before .begin if required
// If the sensor has been begun (_i2cPort is not NULL) then _i2cPort is used
// If the sensor has not been begun (_i2cPort is NULL) then wirePort and
// address are used (which will default to Wire) Note that the sensor will
// only respond to other commands after waiting 500 ms after issuing the
// stop_periodic_measurement command.
bool stopPeriodicMeasurement(uint16_t delayMillis = 500,
TwoWire &wirePort = Wire);
bool readMeasurement(void); // Check for fresh data; store it. Returns true
// if fresh data is available
uint16_t getCO2(void); // Return the CO2 PPM. Automatically request fresh
// data is the data is 'stale'
float getHumidity(void); // Return the RH. Automatically request fresh data
// is the data is 'stale'
float getTemperature(void); // Return the temperature. Automatically
// request fresh data is the data is 'stale'
// Define how warm the sensor is compared to ambient, so RH and T are
// temperature compensated. Has no effect on the CO2 reading Default offset
// is 4C
bool setTemperatureOffset(
float offset,
uint16_t delayMillis = 1); // Returns true if I2C transfer was OK
float getTemperatureOffset(void); // Will return zero if offset is invalid
bool getTemperatureOffset(
float *offset); // Returns true if offset is valid
// Define the sensor altitude in metres above sea level, so RH and CO2 are
// compensated for atmospheric pressure Default altitude is 0m
bool setSensorAltitude(uint16_t altitude, uint16_t delayMillis = 1);
uint16_t getSensorAltitude(
void); // Will return zero if altitude is invalid
bool getSensorAltitude(
uint16_t *altitude); // Returns true if altitude is valid
// Define the ambient pressure in Pascals, so RH and CO2 are compensated for
// atmospheric pressure setAmbientPressure overrides setSensorAltitude
bool setAmbientPressure(float pressure, uint16_t delayMillis = 1);
float performForcedRecalibration(
uint16_t concentration); // Returns the FRC correction value
bool performForcedRecalibration(
uint16_t concentration,
float *correction); // Returns true if FRC is successful
bool setAutomaticSelfCalibrationEnabled(bool enabled = true,
uint16_t delayMillis = 1);
bool getAutomaticSelfCalibrationEnabled(void);
bool getAutomaticSelfCalibrationEnabled(uint16_t *enabled);
bool startLowPowerPeriodicMeasurement(
void); // Start low power measurements - receive data every 30 seconds
bool getDataReadyStatus(void); // Returns true if fresh data is available
bool persistSettings(
uint16_t delayMillis = 800); // Copy sensor settings from RAM to EEPROM
bool getSerialNumber(
char *serialNumber); // Returns true if serial number is read correctly
bool performSelfTest(void); // Takes 10 seconds to complete. Returns true
// if the test is successful
bool performFactoryReset(
uint16_t delayMillis =
1200); // Reset all settings to the factory values
bool reInit(uint16_t delayMillis =
20); // Re-initialize the sensor, load settings from EEPROM
bool measureSingleShot(void); // SCD41 only. Request a single measurement.
// Data will be ready in 5 seconds
bool measureSingleShotRHTOnly(void); // SCD41 only. Request RH and T data
// only. Data will be ready in 50ms
bool sendCommand(uint16_t command, uint16_t arguments);
bool sendCommand(uint16_t command);
bool readRegister(uint16_t registerAddress, uint16_t *response,
uint16_t delayMillis = 1);
uint8_t computeCRC8(uint8_t data[], uint8_t len);
bool getFeatureSetVersion(scd4x_sensor_type_e *sensorType);
scd4x_sensor_type_e getSensorType(
void); // Get the sensor type stored in the struct.
void setSensorType(
scd4x_sensor_type_e sensorType); // Set the sensor type for the device.
private:
// Sensor type
scd4x_sensor_type_e _sensorType;
// Global main datums
float co2 = 0;
float temperature = 0;
float humidity = 0;
// These track the staleness of the current data
// This allows us to avoid calling readMeasurement() every time individual
// datums are requested
bool co2HasBeenReported = true;
bool humidityHasBeenReported = true;
bool temperatureHasBeenReported = true;
// Keep track of whether periodic measurements are in progress
bool periodicMeasurementsAreRunning = false;
// Convert serial number digit to ASCII
char convertHexToASCII(uint8_t digit);
};
#endif

View file

@ -0,0 +1,43 @@
#include "SHT3X.h"
bool SHT3X::begin(TwoWire* wire, uint8_t addr, uint8_t sda, uint8_t scl,
long freq) {
_i2c.begin(wire, sda, scl, freq);
_addr = addr;
_wire = wire;
return _i2c.exist(_addr);
}
bool SHT3X::update() {
unsigned int data[6];
// Start I2C Transmission
_wire->beginTransmission(_addr);
// Send measurement command
_wire->write(0x2C);
_wire->write(0x06);
// Stop I2C transmission
if (_wire->endTransmission() != 0) return false;
delay(200);
// Request 6 bytes of data
_wire->requestFrom(_addr, (uint8_t)6);
// Read 6 bytes of data
// cTemp msb, cTemp lsb, cTemp crc, humidity msb, humidity lsb, humidity crc
for (int i = 0; i < 6; i++) {
data[i] = _wire->read();
};
delay(50);
if (_wire->available() != 0) return false;
// Convert the data
cTemp = ((((data[0] * 256.0) + data[1]) * 175) / 65535.0) - 45;
fTemp = (cTemp * 1.8) + 32;
humidity = ((((data[3] * 256.0) + data[4]) * 100) / 65535.0);
return true;
}

View file

@ -0,0 +1,25 @@
#ifndef __SHT3X_H_
#define __SHT3X_H_
#include "Arduino.h"
#include "I2C_Class.h"
#include "Wire.h"
#define SHT3X_I2C_ADDR 0x44
class SHT3X {
public:
bool begin(TwoWire* wire = &Wire, uint8_t addr = SHT3X_I2C_ADDR,
uint8_t sda = 21, uint8_t scl = 22, long freq = 400000U);
bool update(void);
float cTemp = 0;
float fTemp = 0;
float humidity = 0;
private:
TwoWire* _wire;
uint8_t _addr;
I2C_Class _i2c;
};
#endif

View file

@ -0,0 +1,96 @@
#include "SHT4X.h"
bool SHT4X::begin(TwoWire* wire, uint8_t addr, uint8_t sda, uint8_t scl,
long freq) {
_i2c.begin(wire, sda, scl, freq);
_addr = addr;
_wire = wire;
return _i2c.exist(_addr);
}
bool SHT4X::update() {
uint8_t readbuffer[6];
uint8_t cmd = SHT4x_NOHEAT_HIGHPRECISION;
uint16_t duration = 10;
if (_heater == SHT4X_NO_HEATER) {
if (_precision == SHT4X_HIGH_PRECISION) {
cmd = SHT4x_NOHEAT_HIGHPRECISION;
duration = 10;
}
if (_precision == SHT4X_MED_PRECISION) {
cmd = SHT4x_NOHEAT_MEDPRECISION;
duration = 5;
}
if (_precision == SHT4X_LOW_PRECISION) {
cmd = SHT4x_NOHEAT_LOWPRECISION;
duration = 2;
}
}
if (_heater == SHT4X_HIGH_HEATER_1S) {
cmd = SHT4x_HIGHHEAT_1S;
duration = 1100;
}
if (_heater == SHT4X_HIGH_HEATER_100MS) {
cmd = SHT4x_HIGHHEAT_100MS;
duration = 110;
}
if (_heater == SHT4X_MED_HEATER_1S) {
cmd = SHT4x_MEDHEAT_1S;
duration = 1100;
}
if (_heater == SHT4X_MED_HEATER_100MS) {
cmd = SHT4x_MEDHEAT_100MS;
duration = 110;
}
if (_heater == SHT4X_LOW_HEATER_1S) {
cmd = SHT4x_LOWHEAT_1S;
duration = 1100;
}
if (_heater == SHT4X_LOW_HEATER_100MS) {
cmd = SHT4x_LOWHEAT_100MS;
duration = 110;
}
_i2c.writeByte(_addr, cmd, 1);
delay(duration);
_wire->requestFrom(_addr, (uint8_t)6);
for (uint16_t i = 0; i < 6; i++) {
readbuffer[i] = _wire->read();
}
if (readbuffer[2] != crc8(readbuffer, 2) ||
readbuffer[5] != crc8(readbuffer + 3, 2)) {
return false;
}
float t_ticks = (uint16_t)readbuffer[0] * 256 + (uint16_t)readbuffer[1];
float rh_ticks = (uint16_t)readbuffer[3] * 256 + (uint16_t)readbuffer[4];
cTemp = -45 + 175 * t_ticks / 65535;
humidity = -6 + 125 * rh_ticks / 65535;
humidity = min(max(humidity, (float)0.0), (float)100.0);
return true;
}
void SHT4X::setPrecision(sht4x_precision_t prec) {
_precision = prec;
}
sht4x_precision_t SHT4X::getPrecision(void) {
return _precision;
}
void SHT4X::setHeater(sht4x_heater_t heat) {
_heater = heat;
}
sht4x_heater_t SHT4X::getHeater(void) {
return _heater;
}

View file

@ -0,0 +1,80 @@
#ifndef __SHT4X_H_
#define __SHT4X_H_
#include "Arduino.h"
#include "I2C_Class.h"
#include "Wire.h"
#include "utility.h"
#define SHT40_I2C_ADDR_44 0x44
#define SHT40_I2C_ADDR_45 0x45
#define SHT41_I2C_ADDR_44 0x44
#define SHT41_I2C_ADDR_45 0x45
#define SHT45_I2C_ADDR_44 0x44
#define SHT45_I2C_ADDR_45 0x45
#define SHT4x_DEFAULT_ADDR 0x44 /**< SHT4x I2C Address */
#define SHT4x_NOHEAT_HIGHPRECISION \
0xFD /**< High precision measurement, no heater */
#define SHT4x_NOHEAT_MEDPRECISION \
0xF6 /**< Medium precision measurement, no heater */
#define SHT4x_NOHEAT_LOWPRECISION \
0xE0 /**< Low precision measurement, no heater */
#define SHT4x_HIGHHEAT_1S \
0x39 /**< High precision measurement, high heat for 1 sec */
#define SHT4x_HIGHHEAT_100MS \
0x32 /**< High precision measurement, high heat for 0.1 sec */
#define SHT4x_MEDHEAT_1S \
0x2F /**< High precision measurement, med heat for 1 sec */
#define SHT4x_MEDHEAT_100MS \
0x24 /**< High precision measurement, med heat for 0.1 sec */
#define SHT4x_LOWHEAT_1S \
0x1E /**< High precision measurement, low heat for 1 sec */
#define SHT4x_LOWHEAT_100MS \
0x15 /**< High precision measurement, low heat for 0.1 sec */
#define SHT4x_READSERIAL 0x89 /**< Read Out of Serial Register */
#define SHT4x_SOFTRESET 0x94 /**< Soft Reset */
typedef enum {
SHT4X_HIGH_PRECISION,
SHT4X_MED_PRECISION,
SHT4X_LOW_PRECISION,
} sht4x_precision_t;
/** Optional pre-heater configuration setting */
typedef enum {
SHT4X_NO_HEATER,
SHT4X_HIGH_HEATER_1S,
SHT4X_HIGH_HEATER_100MS,
SHT4X_MED_HEATER_1S,
SHT4X_MED_HEATER_100MS,
SHT4X_LOW_HEATER_1S,
SHT4X_LOW_HEATER_100MS,
} sht4x_heater_t;
class SHT4X {
public:
bool begin(TwoWire* wire = &Wire, uint8_t addr = SHT40_I2C_ADDR_44,
uint8_t sda = 21, uint8_t scl = 22, long freq = 400000U);
bool update(void);
float cTemp = 0;
float humidity = 0;
void setPrecision(sht4x_precision_t prec);
sht4x_precision_t getPrecision(void);
void setHeater(sht4x_heater_t heat);
sht4x_heater_t getHeater(void);
private:
TwoWire* _wire;
uint8_t _addr;
I2C_Class _i2c;
sht4x_precision_t _precision = SHT4X_HIGH_PRECISION;
sht4x_heater_t _heater = SHT4X_NO_HEATER;
};
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,594 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file unit_BMP280.cpp
@brief BMP280 Unit for M5UnitUnified
*/
#include "unit_BMP280.hpp"
#include <M5Utility.hpp>
#include <limits> // NaN
#include <array>
using namespace m5::utility::mmh3;
using namespace m5::unit::types;
using namespace m5::unit::bmp280;
using namespace m5::unit::bmp280::command;
namespace {
constexpr uint8_t CHIP_IDENTIFIER{0x58};
constexpr uint8_t RESET_VALUE{0xB6};
constexpr uint32_t NOT_MEASURED{0x800000};
constexpr PowerMode mode_table[] = {PowerMode::Sleep, PowerMode::Forced, PowerMode::Forced, // duplicated
PowerMode::Normal
};
constexpr Oversampling osrs_table[] = {
Oversampling::Skipped, Oversampling::X1, Oversampling::X2, Oversampling::X4, Oversampling::X8, Oversampling::X16,
Oversampling::X16, // duplicated
Oversampling::X16, // duplicated
};
constexpr Oversampling osrss_table[][2] = {
// Pressure, Temperature
{Oversampling::X1, Oversampling::X1}, {Oversampling::X2, Oversampling::X1}, {Oversampling::X4, Oversampling::X1},
{Oversampling::X8, Oversampling::X1}, {Oversampling::X16, Oversampling::X2},
};
constexpr Standby standby_table[] = {
Standby::Time0_5ms, Standby::Time62_5ms, Standby::Time125ms, Standby::Time250ms,
Standby::Time500ms, Standby::Time1sec, Standby::Time2sec, Standby::Time4sec,
};
constexpr uint32_t interval_table[] = {0, 62, 125, 250, 500, 1000, 2000, 4000};
constexpr Filter filter_table[] = {
Filter::Off, Filter::Coeff2, Filter::Coeff4, Filter::Coeff8, Filter::Coeff16,
};
struct UseCaseSetting {
OversamplingSetting osrss;
Filter filter;
Standby st;
};
constexpr UseCaseSetting uc_table[] = {
{OversamplingSetting::UltraHighResolution, Filter::Coeff4, Standby::Time62_5ms},
{OversamplingSetting::StandardResolution, Filter::Coeff16, Standby::Time0_5ms},
{OversamplingSetting::UltraLowPower, Filter::Off, Standby::Time4sec},
{OversamplingSetting::StandardResolution, Filter::Coeff4, Standby::Time125ms},
{OversamplingSetting::LowPower, Filter::Off, Standby::Time0_5ms},
{OversamplingSetting::UltraHighResolution, Filter::Coeff16, Standby::Time0_5ms},
};
struct CtrlMeas {
//
inline Oversampling osrs_p() const
{
return osrs_table[(value >> 2) & 0x07];
}
inline Oversampling osrs_t() const
{
return osrs_table[(value >> 5) & 0x07];
}
inline PowerMode mode() const
{
return mode_table[value & 0x03];
}
//
inline void osrs_p(const Oversampling os)
{
value = (value & ~(0x07 << 2)) | ((m5::stl::to_underlying(os) & 0x07) << 2);
}
inline void osrs_t(const Oversampling os)
{
value = (value & ~(0x07 << 5)) | ((m5::stl::to_underlying(os) & 0x07) << 5);
}
inline void mode(const PowerMode m)
{
value = (value & ~0x03) | (m5::stl::to_underlying(m) & 0x03);
}
uint8_t value{};
};
struct Config {
//
inline Standby standby() const
{
return standby_table[(value >> 5) & 0x07];
}
inline Filter filter() const
{
return filter_table[(value >> 2) & 0x07];
}
//
inline void standby(const Standby s)
{
value = (value & ~(0x07 << 5)) | ((m5::stl::to_underlying(s) & 0x07) << 5);
}
inline void filter(const Filter f)
{
value = (value & ~(0x07 << 2)) | ((m5::stl::to_underlying(f) & 0x07) << 2);
}
uint8_t value{};
};
struct Calculator {
inline float temperature(const int32_t adc_P, const int32_t adc_T, const Trimming* t)
{
return t ? compensate_temperature_f(adc_T, *t) : std::numeric_limits<float>::quiet_NaN();
}
inline float pressure(const int32_t adc_P, const int32_t adc_T, const Trimming* t)
{
if (!t) {
return std::numeric_limits<float>::quiet_NaN();
}
if (t_fine == 0) {
(void)compensate_temperature_f(adc_T, *t); // For t_fine
}
return compensate_pressure_f(adc_P, *t);
}
private:
float compensate_temperature(const int32_t adc_T, const Trimming& trim)
{
int32_t var1{}, var2{};
var1 = ((((adc_T >> 3) - ((int32_t)trim.dig_T1 << 1))) * ((int32_t)trim.dig_T2)) >> 11;
var2 = (((((adc_T >> 4) - ((int32_t)trim.dig_T1)) * ((adc_T >> 4) - ((int32_t)trim.dig_T1))) >> 12) *
((int32_t)trim.dig_T3)) >>
14;
t_fine = var1 + var2; // [*1]
float T = (t_fine * 5 + 128) >> 8;
return T * 0.01f;
}
float compensate_pressure(const int32_t adc_P, const Trimming& trim)
{
int64_t var1{}, var2{}, p{};
var1 = ((int64_t)t_fine) - 128000; // (*1) using it!
var2 = var1 * var1 * (int64_t)trim.dig_P6;
var2 = var2 + ((var1 * (int64_t)trim.dig_P5) << 17);
var2 = var2 + (((int64_t)trim.dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)trim.dig_P3) >> 8) + ((var1 * (int64_t)trim.dig_P2) << 12);
var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)trim.dig_P1) >> 33;
if (var1) {
p = 1048576 - adc_P;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((int64_t)trim.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((int64_t)trim.dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((int64_t)trim.dig_P7) << 4);
return p / 256.f;
}
return 0.0f;
}
float compensate_temperature_f(const int32_t adc_T, const Trimming& trim)
{
float var1{}, var2{}, T{};
var1 = (((float)adc_T) / 16384.0f - ((float)trim.dig_T1) / 1024.0f) * ((float)trim.dig_T2);
var2 = ((((float)adc_T) / 131072.0f - ((float)trim.dig_T1) / 8192.0f) *
(((float)adc_T) / 131072.0f - ((float)trim.dig_T1) / 8192.0f)) *
((float)trim.dig_T3);
t_fine = (int32_t)(var1 + var2); // [*2]
T = (var1 + var2) / 5120.0f;
return T;
}
float compensate_pressure_f(const int32_t adc_P, const Trimming& trim)
{
float var1{}, var2{}, P{};
var1 = ((float)t_fine / 2.0f) - 64000.0f; // (*2)
var2 = var1 * var1 * ((float)trim.dig_P6) / 32768.0f;
var2 = var2 + var1 * ((float)trim.dig_P5) * 2.0f;
var2 = (var2 / 4.0f) + (((float)trim.dig_P4) * 65536.0f);
var1 = (((float)trim.dig_P3) * var1 * var1 / 524288.0f + ((float)trim.dig_P2) * var1) / 524288.0f;
var1 = (1.0f + var1 / 32768.0f) * ((float)trim.dig_P1);
if (var1 == 0.0f) {
return 0;
}
P = 1048576.0f - (float)adc_P;
P = (P - (var2 / 4096.0f)) * 6250.0f / var1;
var1 = ((float)trim.dig_P9) * P * P / 2147483648.0f;
var2 = P * ((float)trim.dig_P8) / 32768.0f;
P = P + (var1 + var2 + ((float)trim.dig_P7)) / 16.0f;
return P;
}
////
int32_t t_fine{};
};
} // namespace
namespace m5 {
namespace unit {
namespace bmp280 {
float Data::celsius() const
{
int32_t adc_P = (int32_t)(((uint32_t)raw[0] << 16) | ((uint32_t)raw[1] << 8) | ((uint32_t)raw[2]));
int32_t adc_T = (int32_t)(((uint32_t)raw[3] << 16) | ((uint32_t)raw[4] << 8) | ((uint32_t)raw[5]));
Calculator c{};
// adc_T is NOT_MEASURED if orsr Skipped (Not measured)
return (adc_T != NOT_MEASURED) ? c.temperature(adc_P >> 4, adc_T >> 4, trimming)
: std::numeric_limits<float>::quiet_NaN();
}
float Data::fahrenheit() const
{
return celsius() * 9.0f / 5.0f + 32.f;
}
float Data::pressure() const
{
int32_t adc_P = (int32_t)(((uint32_t)raw[0] << 16) | ((uint32_t)raw[1] << 8) | ((uint32_t)raw[2]));
int32_t adc_T = (int32_t)(((uint32_t)raw[3] << 16) | ((uint32_t)raw[4] << 8) | ((uint32_t)raw[5]));
Calculator c{};
// adc_T/P is NOT_MEASURED if orsr Skipped (Not measured)
return (adc_T != NOT_MEASURED && adc_P != NOT_MEASURED) ? c.pressure(adc_P >> 4, adc_T >> 4, trimming)
: std::numeric_limits<float>::quiet_NaN();
}
} // namespace bmp280
const char UnitBMP280::name[] = "UnitBMP280";
const types::uid_t UnitBMP280::uid{"UnitBMP280"_mmh3};
const types::attr_t UnitBMP280::attr{attribute::AccessI2C};
bool UnitBMP280::begin()
{
auto ssize = stored_size();
assert(ssize && "stored_size must be greater than zero");
if (ssize != _data->capacity()) {
_data.reset(new m5::container::CircularBuffer<Data>(ssize));
if (!_data) {
M5_LIB_LOGE("Failed to allocate");
return false;
}
}
uint8_t id{};
if (!softReset() || !readRegister8(CHIP_ID, id, 0) || id != CHIP_IDENTIFIER) {
M5_LIB_LOGE("Can not detect BMP280 %02X", id);
return false;
}
if (!read_trimming(_trimming)) {
M5_LIB_LOGE("Failed to read trimming");
return false;
}
M5_LIB_LOGV(
"Trimming\n"
"T:%u/%d/%d\n"
"P:%u/%d/%d/%d/%d/%d/%d/%d/%d",
// T
_trimming.dig_T1, _trimming.dig_T2, _trimming.dig_T3,
// P
_trimming.dig_P1, _trimming.dig_P2, _trimming.dig_P3, _trimming.dig_P4, _trimming.dig_P5, _trimming.dig_P6,
_trimming.dig_P7, _trimming.dig_P8, _trimming.dig_P9);
return _cfg.start_periodic
? startPeriodicMeasurement(_cfg.osrs_pressure, _cfg.osrs_temperature, _cfg.filter, _cfg.standby)
: true;
}
void UnitBMP280::update(const bool force)
{
_updated = false;
if (inPeriodic()) {
elapsed_time_t at{m5::utility::millis()};
if (force || !_latest || at >= _latest + _interval) {
Data d{};
// _updated = is_data_ready() && read_measurement(d);
_updated = read_measurement(d);
if (_updated) {
// auto dur = at - _latest;
// M5_LIB_LOGW(">DUR:%ld\n", dur);
_latest = at;
_data->push_back(d);
}
}
}
}
bool UnitBMP280::start_periodic_measurement(const bmp280::Oversampling osrsPressure,
const bmp280::Oversampling osrsTemperature, const bmp280::Filter filter,
const bmp280::Standby st)
{
if (inPeriodic()) {
M5_LIB_LOGD("Periodic measurements are running");
return false;
}
Config c{};
c.standby(st);
c.filter(filter);
CtrlMeas cm{};
cm.osrs_p(osrsPressure);
cm.osrs_t(osrsTemperature);
return writeRegister8(CONFIG, c.value) && writeRegister8(CONTROL_MEASUREMENT, cm.value) &&
start_periodic_measurement();
}
bool UnitBMP280::start_periodic_measurement()
{
if (inPeriodic()) {
M5_LIB_LOGD("Periodic measurements are running");
return false;
}
Config c{};
_periodic = readRegister8(CONFIG, c.value, 0) && writePowerMode(PowerMode::Normal);
if (_periodic) {
_latest = 0;
_interval = interval_table[m5::stl::to_underlying(c.standby())];
}
return _periodic;
}
bool UnitBMP280::stop_periodic_measurement()
{
if (inPeriodic() && writePowerMode(PowerMode::Sleep)) {
_periodic = false;
return true;
}
return false;
}
bool UnitBMP280::measureSingleshot(bmp280::Data& d, const bmp280::Oversampling osrsPressure,
const bmp280::Oversampling osrsTemperature, const bmp280::Filter filter)
{
if (inPeriodic()) {
M5_LIB_LOGD("Periodic measurements are running");
return false;
}
if (osrsTemperature == Oversampling::Skipped) {
return false;
}
Config c{};
c.filter(filter);
CtrlMeas cm{};
cm.osrs_p(osrsPressure);
cm.osrs_t(osrsTemperature);
return writeRegister8(CONFIG, c.value) && writeRegister8(CONTROL_MEASUREMENT, cm.value) && measure_singleshot(d);
}
bool UnitBMP280::measure_singleshot(bmp280::Data& d)
{
if (inPeriodic()) {
M5_LIB_LOGD("Periodic measurements are running");
return false;
}
if (writePowerMode(PowerMode::Forced)) {
auto start_at = m5::utility::millis();
auto timeout_at = start_at + 2 * 1000; // 2sec
bool done{};
do {
PowerMode pm{};
done = readPowerMode(pm) && (pm == PowerMode::Sleep) && is_data_ready();
if (done) {
break;
}
m5::utility::delay(1);
} while (!done && m5::utility::millis() <= timeout_at);
return done && read_measurement(d);
}
return false;
}
bool UnitBMP280::readOversampling(Oversampling& osrsPressure, Oversampling& osrsTemperature)
{
CtrlMeas cm{};
if (readRegister8(CONTROL_MEASUREMENT, cm.value, 0)) {
osrsPressure = cm.osrs_p();
osrsTemperature = cm.osrs_t();
return true;
}
return false;
}
bool UnitBMP280::writeOversampling(const Oversampling osrsPressure, const Oversampling osrsTemperature)
{
if (inPeriodic()) {
M5_LIB_LOGD("Periodic measurements are running");
return false;
}
CtrlMeas cm{};
if (readRegister8(CONTROL_MEASUREMENT, cm.value, 0)) {
cm.osrs_p(osrsPressure);
cm.osrs_t(osrsTemperature);
return writeRegister8(CONTROL_MEASUREMENT, cm.value);
}
return false;
}
bool UnitBMP280::writeOversamplingPressure(const Oversampling osrsPressure)
{
if (inPeriodic()) {
M5_LIB_LOGD("Periodic measurements are running");
return false;
}
CtrlMeas cm{};
if (readRegister8(CONTROL_MEASUREMENT, cm.value, 0)) {
cm.osrs_p(osrsPressure);
return writeRegister8(CONTROL_MEASUREMENT, cm.value);
}
return false;
}
bool UnitBMP280::writeOversamplingTemperature(const Oversampling osrsTemperature)
{
if (inPeriodic()) {
M5_LIB_LOGD("Periodic measurements are running");
return false;
}
CtrlMeas cm{};
if (readRegister8(CONTROL_MEASUREMENT, cm.value, 0)) {
cm.osrs_t(osrsTemperature);
return writeRegister8(CONTROL_MEASUREMENT, cm.value);
}
return false;
}
bool UnitBMP280::writeOversampling(const bmp280::OversamplingSetting osrss)
{
auto idx = m5::stl::to_underlying(osrss);
return writeOversampling(osrss_table[idx][0], osrss_table[idx][1]);
}
bool UnitBMP280::readPowerMode(PowerMode& m)
{
CtrlMeas cm{};
if (readRegister8(CONTROL_MEASUREMENT, cm.value, 0)) {
m = cm.mode();
return true;
}
return false;
}
bool UnitBMP280::writePowerMode(const PowerMode m)
{
CtrlMeas cm{};
if (readRegister8(CONTROL_MEASUREMENT, cm.value, 0)) {
cm.mode(m);
// Datasheet says
// If the device is currently performing ameasurement,
// execution of mode switching commands is delayed until the end of the currentlyrunning measurement period
bool can{};
auto timeout_at = m5::utility::millis() + 1000;
do {
can = is_data_ready();
if (can) {
break;
}
m5::utility::delay(1);
} while (!can && m5::utility::millis() <= timeout_at);
return can && writeRegister8(CONTROL_MEASUREMENT, cm.value);
}
return false;
}
bool UnitBMP280::readFilter(Filter& f)
{
Config c{};
if (readRegister8(CONFIG, c.value, 0)) {
f = c.filter();
return true;
}
return false;
}
bool UnitBMP280::writeFilter(const Filter& f)
{
if (inPeriodic()) {
M5_LIB_LOGD("Periodic measurements are running");
return false;
}
PowerMode pm{};
if (!readPowerMode(pm) || pm != PowerMode::Sleep) {
// Datasheet says
// Writes to the config register in normal mode may be ignored. In sleep mode writes are not ignored
M5_LIB_LOGE("Invalid power mode %02X", pm);
return false;
}
Config c{};
if (readRegister8(CONFIG, c.value, 0)) {
c.filter(f);
return writeRegister8(CONFIG, c.value);
}
return false;
}
bool UnitBMP280::readStandbyTime(Standby& s)
{
Config c{};
if (readRegister8(CONFIG, c.value, 0)) {
s = c.standby();
return true;
}
return false;
}
bool UnitBMP280::writeStandbyTime(const Standby s)
{
if (inPeriodic()) {
M5_LIB_LOGD("Periodic measurements are running");
return false;
}
Config c{};
if (readRegister8(CONFIG, c.value, 0)) {
c.standby(s);
return writeRegister8(CONFIG, c.value);
}
return false;
}
bool UnitBMP280::writeUseCaseSetting(const bmp280::UseCase uc)
{
const auto& tbl = uc_table[m5::stl::to_underlying(uc)];
return writeOversampling(tbl.osrss) && writeFilter(tbl.filter) && writeStandbyTime(tbl.st);
}
bool UnitBMP280::softReset()
{
if (writeRegister8(SOFT_RESET, RESET_VALUE)) {
auto timeout_at = m5::utility::millis() + 100; // 100ms
uint8_t s{0xFF};
do {
if (readRegister8(GET_STATUS, s, 0) && (s & 0x01 /* im update */) == 0x00) {
_periodic = false;
return true;
}
} while ((s & 0x01) && m5::utility::millis() < timeout_at);
return false;
}
return false;
}
//
bool UnitBMP280::read_trimming(Trimming& t)
{
return readRegister(TRIMMING_DIG, t.value, m5::stl::size(t.value), 0);
}
bool UnitBMP280::is_data_ready()
{
uint8_t s{0xFF};
return readRegister8(GET_STATUS, s, 0) && ((s & 0x09 /* Measuring, im update */) == 0x00);
}
bool UnitBMP280::read_measurement(bmp280::Data& d)
{
d.trimming = nullptr;
// Datasheet says
// Shadowing will only work if all data registers are read in a single burst read.
// Therefore, the user must use burst reads if he does not synchronize data readout with themeasurement cycle
if (readRegister(GET_MEASUREMENT, d.raw.data(), d.raw.size(), 0)) {
d.trimming = &_trimming;
return true;
}
return false;
}
} // namespace unit
} // namespace m5

View file

@ -0,0 +1,434 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file unit_BMP280.hpp
@brief BMP280 Unit for M5UnitUnified
*/
#ifndef M5_UNIT_ENV_UNIT_BNP280_HPP
#define M5_UNIT_ENV_UNIT_BNP280_HPP
#include <M5UnitComponent.hpp>
#include <m5_utility/container/circular_buffer.hpp>
#include <limits> // NaN
namespace m5 {
namespace unit {
/*!
@namespace bmp280
@brief For BMP280
*/
namespace bmp280 {
/*!
@enum PowerMode
@brief Operation mode
*/
enum class PowerMode : uint8_t {
Sleep, //!< No measurements are performed
Forced, //!< Single measurements are performed
Normal = 0x03, //!< Periodic measurements are performed
};
/*!
@enum Oversampling
@brief Oversampling factor
*/
enum class Oversampling : uint8_t {
Skipped, //!< Skipped (No measurements are performed)
X1, //!< x1
X2, //!< x2
X4, //!< x4
X8, //!< x8
X16, //!< x16
};
/*!
@enum OversamplingSetting
@brief Oversampling Settings
*/
enum class OversamplingSetting : uint8_t {
UltraLowPower, //!< 16 bit / 2.62 Pa, 16 bit / 0.0050 C
LowPower, //!< 17 bit / 1.31 Pa, 16 bit / 0.0050 C
StandardResolution, //!< 18 bit / 0.66 Pa, 16 bit / 0.0050 C
HighResolution, //!< 19 bit / 0.33 Pa, 16 bit / 0.0050 C
UltraHighResolution, //!< 20 bit / 0.16 Pa, 17 bit / 0.0025 C
};
/*!
@enum Filter
@brief Filter setting
*/
enum class Filter : uint8_t {
Off, //!< Off filter
Coeff2, //!< co-efficient 2
Coeff4, //!< co-efficient 4
Coeff8, //!< co-efficient 8
Coeff16, //!< co-efficient 16
};
/*!
@enum Standby
@brief Measurement standby time for power mode Normal
*/
enum class Standby : uint8_t {
Time0_5ms, //!< 0.5 ms
Time62_5ms, //!< 62.5 ms
Time125ms, //!< 125 ms
Time250ms, //!< 250 ms
Time500ms, //!< 500 ms
Time1sec, //!< 1 second
Time2sec, //!< 2 seconds
Time4sec, //!< 4 seconds
};
/*!
@enum UseCase
@brief Preset settings
*/
enum class UseCase : uint8_t {
LowPower, //!< Handheld device low-power
Dynamic, //!< Handheld device dynamic
Weather, //!< Weather monitoring
Elevator, //!< Elevator / floor change detection
Drop, //!< Drop detection
Indoor, //!< Indoor navigation
};
/*!
@union Trimmming
@brief Trimming parameter
*/
union Trimming {
uint8_t value[12 * 2]{};
struct {
//
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
//
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
// uint16_t reserved;
} __attribute__((packed));
};
/*!
@struct Data
@brief Measurement data group
*/
struct Data {
std::array<uint8_t, 6> raw{}; //!< RAW data [0,1,2]:pressure [3,4,5]:temperature
const Trimming* trimming{}; //!< For calculate
//! temperature (Celsius)
inline float temperature() const
{
return celsius();
}
float celsius() const; //!< temperature (Celsius)
float fahrenheit() const; //!< temperature (Fahrenheit)
float pressure() const; //!< pressure (Pa)
};
} // namespace bmp280
/*!
@class UnitBMP280
@brief Pressure and temperature sensor unit
*/
class UnitBMP280 : public Component, public PeriodicMeasurementAdapter<UnitBMP280, bmp280::Data> {
M5_UNIT_COMPONENT_HPP_BUILDER(UnitBMP280, 0x76);
public:
/*!
@struct config_t
@brief Settings for begin
*/
struct config_t {
//! Start periodic measurement on begin?
bool start_periodic{true};
//! Pressure oversampling if start on begin
bmp280::Oversampling osrs_pressure{bmp280::Oversampling::X16};
//! Temperature oversampling if start on begin
bmp280::Oversampling osrs_temperature{bmp280::Oversampling::X2};
//! Filter if start on begin
bmp280::Filter filter{bmp280::Filter::Coeff16};
//! Standby time if start on begin
bmp280::Standby standby{bmp280::Standby::Time1sec};
};
explicit UnitBMP280(const uint8_t addr = DEFAULT_ADDRESS)
: Component(addr), _data{new m5::container::CircularBuffer<bmp280::Data>(1)}
{
auto ccfg = component_config();
ccfg.clock = 400 * 1000U;
component_config(ccfg);
}
virtual ~UnitBMP280()
{
}
virtual bool begin() override;
virtual void update(const bool force = false) override;
///@name Settings for begin
///@{
/*! @brief Gets the configration */
inline config_t config()
{
return _cfg;
}
//! @brief Set the configration
inline void config(const config_t& cfg)
{
_cfg = cfg;
}
///@}
///@name Measurement data by periodic
///@{
//! @brief Oldest measured temperature (Celsius)
inline float temperature() const
{
return !empty() ? oldest().temperature() : std::numeric_limits<float>::quiet_NaN();
}
//! @brief Oldest measured temperature (Celsius)
inline float celsius() const
{
return !empty() ? oldest().celsius() : std::numeric_limits<float>::quiet_NaN();
}
//! @brief Oldest measured temperature (Fahrenheit)
inline float fahrenheit() const
{
return !empty() ? oldest().fahrenheit() : std::numeric_limits<float>::quiet_NaN();
}
//! @brief Oldest measured pressure (Pa)
inline float pressure() const
{
return !empty() ? oldest().pressure() : std::numeric_limits<float>::quiet_NaN();
}
///@}
///@name Periodic measurement
///@{
/*!
@brief Start periodic measurement
@param osrsPressure Oversampling factor for pressure
@param osrsTemperature Oversampling factor for temperature
@param filter Filter coeff
@param st Standby time
@return True if successful
@warning Measuring pressure requires measuring temperature
*/
inline bool startPeriodicMeasurement(const bmp280::Oversampling osrsPressure,
const bmp280::Oversampling osrsTemperature, const bmp280::Filter filter,
const bmp280::Standby st)
{
return PeriodicMeasurementAdapter<UnitBMP280, bmp280::Data>::startPeriodicMeasurement(
osrsPressure, osrsTemperature, filter, st);
}
//! @brief Start periodic measurement using current settings
inline bool startPeriodicMeasurement()
{
return PeriodicMeasurementAdapter<UnitBMP280, bmp280::Data>::startPeriodicMeasurement();
}
/*!
@brief Stop periodic measurement
@return True if successful
*/
inline bool stopPeriodicMeasurement()
{
return PeriodicMeasurementAdapter<UnitBMP280, bmp280::Data>::stopPeriodicMeasurement();
}
///@}
///@name Single shot measurement
///@{
/*!
@brief Measurement single shot
@param[out] data Measuerd data
@param osrsPressure Oversampling factor for pressure
@param osrsTemperature Oversampling factor for temperature
@param filter Filter coeff
@return True if successful
@warning During periodic detection runs, an error is returned
@warning Measuring pressure requires measuring temperature
@warning Each setting is overwritten
*/
bool measureSingleshot(bmp280::Data& d, const bmp280::Oversampling osrsPressure,
const bmp280::Oversampling osrsTemperature, const bmp280::Filter filter);
//! @brief Measurement single shot using current settings
inline bool measureSingleshot(bmp280::Data& d)
{
return measure_singleshot(d);
}
///@}
///@name Settings
///@{
/*!
@brief Read the oversampling conditions
@param[out] osrsPressure Oversampling for pressure
@param[out] osrsTemperature Oversampling for temperature
@return True if successful
*/
bool readOversampling(bmp280::Oversampling& osrsPressure, bmp280::Oversampling& osrsTemperature);
/*!
@brief Write the oversampling conditions
@param osrsPressure Oversampling for pressure
@param osrsTemperature Oversampling for temperature
@return True if successful
@warning During periodic detection runs, an error is returned
*/
bool writeOversampling(const bmp280::Oversampling osrsPressure, const bmp280::Oversampling osrsTemperature);
/*!
@brief Write the oversampling conditions for pressure
@param osrsPressure Oversampling for pressure
@return True if successful
@warning During periodic detection runs, an error is returned
*/
bool writeOversamplingPressure(const bmp280::Oversampling osrsPressure);
/*!
@brief Write the oversampling conditions for temperature
@param osrsTemperature Oversampling for temperature
@return True if successful
@warning During periodic detection runs, an error is returned
*/
bool writeOversamplingTemperature(const bmp280::Oversampling osrsTemperature);
/*!
@brief Write the oversampling by OversamplingSetting
@param osrss OversamplingSetting
@return True if successful
@warning During periodic detection runs, an error is returned
*/
bool writeOversampling(const bmp280::OversamplingSetting osrss);
/*!
@brief Read the IIR filter co-efficient
@param[out] f filter
@return True if successful
*/
bool readFilter(bmp280::Filter& f);
/*!
@brief Write the IIR filter co-efficient
@param f filter
@return True if successful
@warning During periodic detection runs, an error is returned
*/
bool writeFilter(const bmp280::Filter& f);
/*!
@brief Read the standby time
@param[out] s standby time
@return True if successful
*/
bool readStandbyTime(bmp280::Standby& s);
/*!
@brief Write the standby time
@param s standby time
@return True if successful
@warning During periodic detection runs, an error is returned
*/
bool writeStandbyTime(const bmp280::Standby s);
/*!
@brief Read the power mode
@param[out] m Power mode
@return True if successful
*/
bool readPowerMode(bmp280::PowerMode& m);
/*!
@brief Write the power mode
@param m Power mode
@return True if successful
@warning Note that the measurement mode is changed
@warning It is recommended to use start/stopPeriodicMeasurement or similar to change the measurement mode
*/
bool writePowerMode(const bmp280::PowerMode m);
/*!
@brief Write the settings based on use cases
@param uc UseCase
@return True if successful
@warning During periodic detection runs, an error is returned
*/
bool writeUseCaseSetting(const bmp280::UseCase uc);
///@}
/*!
@brief Soft reset
@return True if successful
*/
bool softReset();
protected:
bool start_periodic_measurement(const bmp280::Oversampling osrsPressure, const bmp280::Oversampling osrsTemperature,
const bmp280::Filter filter, const bmp280::Standby st);
bool start_periodic_measurement();
bool stop_periodic_measurement();
bool read_measurement(bmp280::Data& d);
bool measure_singleshot(bmp280::Data& d);
bool read_trimming(bmp280::Trimming& t);
bool is_data_ready();
M5_UNIT_COMPONENT_PERIODIC_MEASUREMENT_ADAPTER_HPP_BUILDER(UnitBMP280, bmp280::Data);
protected:
std::unique_ptr<m5::container::CircularBuffer<bmp280::Data>> _data{};
config_t _cfg{};
bmp280::Trimming _trimming{};
};
///@cond
namespace bmp280 {
namespace command {
constexpr uint8_t CHIP_ID{0xD0};
// constexpr uint8_t CHIP_VERSION{0xD1};
constexpr uint8_t SOFT_RESET{0xE0};
constexpr uint8_t GET_STATUS{0xF3};
constexpr uint8_t CONTROL_MEASUREMENT{0xF4};
constexpr uint8_t CONFIG{0xF5};
constexpr uint8_t GET_MEASUREMENT{0XF7}; // 6bytes
constexpr uint8_t GET_PRESSURE{0xF7}; // 3byts
constexpr uint8_t GET_PRESSURE_MSB{0xF7}; // 7:0
constexpr uint8_t GET_PRESSURE_LSB{0xF8}; // 7:0
constexpr uint8_t GET_PRESSURE_XLSB{0xF9}; // 7:4
constexpr uint8_t GET_TEMPERATURE{0XFA}; // 3 bytes
constexpr uint8_t GET_TEMPERATURE_MSB{0XFA}; // 7:0
constexpr uint8_t GET_TEMPERATURE_LSB{0XFB}; // 7:0
constexpr uint8_t GET_TEMPERATURE_XLSB{0XFC}; // 7:4
constexpr uint8_t TRIMMING_DIG{0x88}; // 12 bytes
constexpr uint8_t TRIMMING_DIG_T1{0x88};
constexpr uint8_t TRIMMING_DIG_T2{0x8A};
constexpr uint8_t TRIMMING_DIG_T3{0x8C};
constexpr uint8_t TRIMMING_DIG_P1{0x8E};
constexpr uint8_t TRIMMING_DIG_P2{0x90};
constexpr uint8_t TRIMMING_DIG_P3{0x92};
constexpr uint8_t TRIMMING_DIG_P4{0x94};
constexpr uint8_t TRIMMING_DIG_P5{0x96};
constexpr uint8_t TRIMMING_DIG_P6{0x98};
constexpr uint8_t TRIMMING_DIG_P7{0x9A};
constexpr uint8_t TRIMMING_DIG_P8{0x9C};
constexpr uint8_t TRIMMING_DIG_P9{0x9A};
constexpr uint8_t TRIMMING_DIG_RESERVED{0xA0};
} // namespace command
} // namespace bmp280
///@endcond
} // namespace unit
} // namespace m5
#endif

View file

@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file unit_ENV3.cpp
@brief ENV III Unit for M5UnitUnified
*/
#include "unit_ENV3.hpp"
#include <M5Utility.hpp>
namespace m5 {
namespace unit {
using namespace m5::utility::mmh3;
using namespace m5::unit::types;
const char UnitENV3::name[] = "UnitENV3";
const types::uid_t UnitENV3::uid{"UnitENV3"_mmh3};
const types::attr_t UnitENV3::attr{attribute::AccessI2C};
UnitENV3::UnitENV3(const uint8_t addr) : Component(addr)
{
// Form a parent-child relationship
auto cfg = component_config();
cfg.max_children = 2;
component_config(cfg);
_valid = add(sht30, 0) && add(qmp6988, 1);
}
std::shared_ptr<Adapter> UnitENV3::ensure_adapter(const uint8_t ch)
{
if (ch > 2) {
M5_LIB_LOGE("Invalid channel %u", ch);
return std::make_shared<Adapter>(); // Empty adapter
}
auto unit = child(ch);
if (!unit) {
M5_LIB_LOGE("Not exists unit %u", ch);
return std::make_shared<Adapter>(); // Empty adapter
}
auto ad = asAdapter<AdapterI2C>(Adapter::Type::I2C);
return ad ? std::shared_ptr<Adapter>(ad->duplicate(unit->address())) : std::make_shared<Adapter>();
}
} // namespace unit
} // namespace m5

View file

@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*!
@file unit_ENV3.hpp
@brief ENV III Unit for M5UnitUnified
*/
#ifndef M5_UNIT_ENV_UNIT_ENV3_HPP
#define M5_UNIT_ENV_UNIT_ENV3_HPP
#include <M5UnitComponent.hpp>
#include "unit_SHT30.hpp"
#include "unit_QMP6988.hpp"
namespace m5 {
namespace unit {
/*!
@class UnitENV3
@brief ENV III is an environmental sensor that integrates SHT30 and QMP6988
@details This unit itself has no I/O, but holds SHT30 and QMP6988 instance
*/
class UnitENV3 : public Component {
// Must not be 0x00 for ensure and assign adapter to children
M5_UNIT_COMPONENT_HPP_BUILDER(UnitENV3, 0xFF /* Dummy address */);
public:
UnitSHT30 sht30; //!< @brief SHT30 instance
UnitQMP6988 qmp6988; //!< @brief QMP6988 instance
explicit UnitENV3(const uint8_t addr = DEFAULT_ADDRESS);
virtual ~UnitENV3()
{
}
virtual bool begin() override
{
return _valid;
}
protected:
virtual std::shared_ptr<Adapter> ensure_adapter(const uint8_t ch);
private:
bool _valid{}; // Did the constructor correctly add the child unit?
Component* _children[2]{&sht30, &qmp6988};
};
} // namespace unit
} // namespace m5
#endif

Some files were not shown because too many files have changed in this diff Show more