mirror of https://github.com/arendst/Tasmota.git
Add ENS16x (air quality) and ENS210 (temp & RH) sensors (#19479)
* Add files via upload Added ENS16x library enabling read-out of ENS160 and ENS161 sensor component (follow-up of CCS811 and iAQcore) Added ENS210 library to read out ENS210 temperature & humidity sensor * Add files via upload Add air quality sensor readout for ENS160 and ENS161 checking two possible I2C addresses (follow up sensor for CCS811 and iAQcore) Add temperature and humidity sensor readout checking two possible I2C addresses * Update BUILDS.md Add USE_ENS16x and USE_ENS210 * Update decode-status.py Add USE_ENS16x and ENS210 * Update I2CDEVICES.md Add USE_ENS16x and USE_ENS210 * Update my_user_config.h Add USE_ENS16x and USE_ENS210 * Update support_features.ino Add USE_ENS16x and USE_ENS210 * Update tasmota_configurations.h Add USE_ENS16x and ENS210 * Update tasmota_configurations_ESP32.h Add USE_ENS16x and USE_ENS210 * Update xsns_111_ens16x.ino Corrected I2X number * Update xsns_112_ens210.ino Corrected I2C number * Disable USE_ENS16x and USE_ENS210 by default * Added code size information * cut down in libs * optimize tasmota side * fix ens16x web display * final fix on alternate addresses * update code & RAM usage --------- Co-authored-by: Barbudor <barbudor@barbudor.net>
This commit is contained in:
parent
0d7c2dee72
commit
5d97a73ddf
|
@ -132,6 +132,8 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up
|
|||
| USE_MPR121 | - | - / - | - | - | - | - |
|
||||
| USE_CCS811 | - | - / - | - | x | - | - |
|
||||
| USE_CCS811_V2 | - | - / x | - | - | - | - |
|
||||
| USE_ENS16x | - | - / - | - | - | - | - |
|
||||
| USE_ENS210 | - | - / - | - | - | - | - |
|
||||
| USE_MPU6050 | - | - / - | - | - | - | - |
|
||||
| USE_DS3231 | - | - / - | - | - | - | - |
|
||||
| USE_MGC3130 | - | - / - | - | - | - | - |
|
||||
|
|
|
@ -120,3 +120,6 @@ Index | Define | Driver | Device | Address(es) | Description
|
|||
81 | USE_PCA9557 | xdrv_69 | PCA95xx | 0x18 - 0x1F | 8-bit I/O expander as virtual button/switch/relay
|
||||
82 | USE_SGP4X | xsns_109 | SGP4X | 0x59 | Gas (TVOC/NOx index)
|
||||
83 | USE_MAX17043 | xsns_110 | MAX17043 | 0x36 | Fuel-gauge for 3.7 Volt Lipo battery
|
||||
84 | USE_ENS16x | xsns_111 | ENS16x | 0x52 - 0x53 | Gas (TVOC, eCO2) and air quality sensor
|
||||
85 | USE_ENS210 | xsns_112 | ENS210 | 0x43 - 0x44 | Temperature and humidity sensor
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Sciosense
|
||||
|
||||
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.
|
|
@ -0,0 +1,38 @@
|
|||
# ScioSense ENS16x
|
||||
Arduino library for the ENS160 and ENS161 digital four channel MOX gas sensor with I2C interface from ScioSense
|
||||
|
||||
## Introduction
|
||||
This project is an Arduino *library*. It implements a driver with examples for the ENS160 and ENS161.
|
||||
The ENS16x chip is a digital gas sensor for TVOC and eCO2 with an I2C interface.
|
||||
The driver in this Arduino library is based on the code supplied by *Sciosense*, the manufacturer of the chip.
|
||||
|
||||
Note that the ENS16x requires a supply voltage of 1.71V .. 1.98V.
|
||||
The ENS16x also requires a IO voltage of 1.71V .. 3.6V.
|
||||
|
||||
## Links
|
||||
The ENS16x is made by [Sciosense](http://www.sciosense.com).
|
||||
- The datasheet of the ENS160 is available through the website. The datasheet of ENS161 is not yet released but can be provided on request
|
||||
|
||||
## Prerequisites
|
||||
It is assumed that
|
||||
- The Arduino IDE has been installed.
|
||||
If not, refer to "Install the Arduino Desktop IDE" on the
|
||||
[Arduino site](https://www.arduino.cc/en/Guide/HomePage).
|
||||
- The library directory is at its default location.
|
||||
For me, that is `C:\Users\sciosense\Documents\Arduino\libraries`.
|
||||
|
||||
## Installation
|
||||
- Visit the project page for the Arduino ENS16x library.
|
||||
- Click the green button Clone or download on the right side.
|
||||
- From the pop-up choose Download ZIP.
|
||||
- In Arduino IDE, select Sketch > Include Library > Manage Libraries ... and browse to the just downloaded ZIP file.
|
||||
- When the IDE is ready this README.md should be located at e.g. `C:\Users\sciosense\Documents\Arduino\libraries\ScioSense_ENS16x\README.md`.
|
||||
|
||||
## Build an example
|
||||
To build an example sketch
|
||||
- (Re)start Arduino.
|
||||
- Open File > Example > Examples from Custom Libraries > ENS16x > ENS16xbasic_normal
|
||||
- Make sure Tools > Board lists the correct board.
|
||||
- Select Sketch > Verify/Compile.
|
||||
|
||||
### ScioSense is a Joint Venture of ams AG
|
|
@ -0,0 +1,65 @@
|
|||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
# https://spencer.bliven.us/index.php/2012/01/18/arduino-ide-keywords/
|
||||
# KEYWORD1 Classes, datatypes, and C++ keywords
|
||||
# KEYWORD2 Methods and functions
|
||||
# KEYWORD3 setup and loop functions, as well as the Serial keywords
|
||||
# LITERAL1 Constants
|
||||
# LITERAL2 Built-in variables (unused by default)
|
||||
|
||||
|
||||
#######################################
|
||||
# Classes, datatypes (KEYWORD1)
|
||||
#######################################
|
||||
ENS160 KEYWORD1
|
||||
ens160 KEYWORD1
|
||||
ENS161 KEYWORD1
|
||||
ens161 KEYWORD1
|
||||
ScioSense_ENS16x KEYWORD1
|
||||
ScioSense_ens16x KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
setI2C KEYWORD2
|
||||
available KEYWORD2
|
||||
revENS16x KEYWORD2
|
||||
setMode KEYWORD2
|
||||
|
||||
initCustomMode KEYWORD2
|
||||
addCustomStep KEYWORD2
|
||||
|
||||
measure KEYWORD2
|
||||
measureRaw KEYWORD2
|
||||
set_envdata KEYWORD2
|
||||
set_envdata210 KEYWORD2
|
||||
getMajorRev KEYWORD2
|
||||
getMinorRev KEYWORD2
|
||||
getBuild KEYWORD2
|
||||
|
||||
getAQI KEYWORD2
|
||||
getTVOC KEYWORD2
|
||||
geteCO2 KEYWORD2
|
||||
getAQIS KEYWORD2
|
||||
getHP0 KEYWORD2
|
||||
getHP0BL KEYWORD2
|
||||
getHP1 KEYWORD2
|
||||
getHP1BL KEYWORD2
|
||||
getHP2 KEYWORD2
|
||||
getHP2BL KEYWORD2
|
||||
getHP3 KEYWORD2
|
||||
getHP3BL KEYWORD2
|
||||
getMISR KEYWORD2
|
||||
|
||||
######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
ENS16x_I2CADDR_0 LITERAL1
|
||||
ENS16x_I2CADDR_1 LITERAL1
|
||||
ENS16x_OPMODE_IDLE LITERAL1
|
||||
ENS16x_OPMODE_STD LITERAL1
|
||||
ENS161_OPMODE_LP LITERAL1
|
||||
ENS16x_OPMODE_CUSTOM LITERAL1
|
|
@ -0,0 +1,9 @@
|
|||
name=ScioSense ENS16x
|
||||
version=8.0.0
|
||||
author=Christoph Friese
|
||||
maintainer=ScioSense
|
||||
sentence=Arduino library for the ENS160 and ENS161 digital four channel MOX gas sensor with I2C interface from ScioSense
|
||||
paragraph=This library controls the ENS160 and ENS161. The main feature of this library is performing a single shot measurement, retrieving the measurement data.
|
||||
category=Device Control
|
||||
url=https://github.com/sciosense/ens16x
|
||||
architectures=*
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
ScioSense_ENS16x.h - Library for the ENS160 & ENS161 sensor with I2C interface from ScioSense
|
||||
2023 Jul 03 v8 Christoph Friese Update to cover ENS160 and ENS161
|
||||
2023 Mar 23 v7 Christoph Friese Bugfix measurement routine, prepare next release
|
||||
2021 Nov 25 v6 Martin Herold Custom mode timing fixed
|
||||
2021 July 29 v5 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams
|
||||
2021 Feb 04 v4 Giuseppe de Pinto Custom mode fixed
|
||||
2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams
|
||||
2020 Feb 15 v2 Giuseppe Pasetti Corrected firmware flash option
|
||||
2019 May 05 v1 Christoph Friese Created
|
||||
|
||||
based on application note "ENS160 Software Integration.pdf" rev 0.01
|
||||
*/
|
||||
|
||||
#include "ScioSense_ENS16x.h"
|
||||
#include "math.h"
|
||||
|
||||
ScioSense_ENS16x::ScioSense_ENS16x(uint8_t slaveaddr) {
|
||||
this->_slaveaddr = slaveaddr;
|
||||
|
||||
this->_ADDR = 0;
|
||||
this->_nINT = 0;
|
||||
this->_nCS = 0;
|
||||
}
|
||||
|
||||
#ifndef ENS16x_DISABLE_ENHANCED_FEATURES
|
||||
ScioSense_ENS16x::ScioSense_ENS16x(uint8_t ADDR, uint8_t nCS, uint8_t nINT) {
|
||||
this->_slaveaddr = ENS16x_I2CADDR_0;
|
||||
|
||||
this->_ADDR = ADDR;
|
||||
this->_nINT = nINT;
|
||||
this->_nCS = nCS;
|
||||
}
|
||||
|
||||
ScioSense_ENS16x::ScioSense_ENS16x(uint8_t slaveaddr, uint8_t ADDR, uint8_t nCS, uint8_t nINT) {
|
||||
this->_slaveaddr = slaveaddr;
|
||||
|
||||
this->_ADDR = ADDR;
|
||||
this->_nINT = nINT;
|
||||
this->_nCS = nCS;
|
||||
}
|
||||
|
||||
// Function to redefine I2C pins
|
||||
void ScioSense_ENS16x::setI2C(uint8_t sda, uint8_t scl) {
|
||||
this->_sdaPin = sda;
|
||||
this->_sclPin = scl;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Init I2C communication, resets ENS16x and checks its PART_ID. Returns false on I2C problems or wrong PART_ID.
|
||||
bool ScioSense_ENS16x::begin(bool debug)
|
||||
{
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
debugENS16x = debug;
|
||||
|
||||
//Set pin levels
|
||||
if (this->_ADDR > 0) {
|
||||
pinMode(this->_ADDR, OUTPUT);
|
||||
digitalWrite(this->_ADDR, LOW);
|
||||
}
|
||||
if (this->_nINT > 0) pinMode(this->_nINT, INPUT_PULLUP);
|
||||
if (this->_nCS > 0) {
|
||||
pinMode(this->_nCS, OUTPUT);
|
||||
digitalWrite(this->_nCS, HIGH);
|
||||
}
|
||||
#endif
|
||||
|
||||
//init I2C
|
||||
_i2c_init();
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.println("begin() - I2C init done");
|
||||
}
|
||||
#endif
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
this->_available = false;
|
||||
this->_available = this->reset();
|
||||
|
||||
this->_available = this->checkPartID();
|
||||
|
||||
if (this->_available) {
|
||||
this->_available = this->setMode(ENS16x_OPMODE_IDLE);
|
||||
this->_available = this->clearCommand();
|
||||
this->_available = this->getFirmware();
|
||||
}
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.println("ENS16x in idle mode");
|
||||
}
|
||||
#endif
|
||||
return this->_available;
|
||||
}
|
||||
|
||||
// Sends a reset to the ENS16x. Returns false on I2C problems.
|
||||
bool ScioSense_ENS16x::reset(void)
|
||||
{
|
||||
uint8_t result = this->write8(_slaveaddr, ENS16x_REG_OPMODE, ENS16x_OPMODE_RESET);
|
||||
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.print("reset() result: ");
|
||||
Serial.println(result == 0 ? "ok" : "nok");
|
||||
}
|
||||
#endif
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
// Reads the part ID and confirms valid sensor
|
||||
bool ScioSense_ENS16x::checkPartID(void) {
|
||||
uint8_t i2cbuf[2];
|
||||
uint16_t part_id;
|
||||
bool result = false;
|
||||
|
||||
this->read(_slaveaddr, ENS16x_REG_PART_ID, i2cbuf, 2);
|
||||
part_id = i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8);
|
||||
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.print("checkPartID() result: ");
|
||||
if (part_id == ENS160_PARTID) Serial.println("ENS160 ok");
|
||||
else if (part_id == ENS161_PARTID) Serial.println("ENS161 ok");
|
||||
else Serial.println("nok");
|
||||
}
|
||||
#endif
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
if (part_id == ENS160_PARTID) { this->_revENS16x = 0; result = true; }
|
||||
else if (part_id == ENS161_PARTID) { this->_revENS16x = 1; result = true; }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Initialize idle mode and confirms
|
||||
bool ScioSense_ENS16x::clearCommand(void) {
|
||||
uint8_t status;
|
||||
uint8_t result;
|
||||
|
||||
result = this->write8(_slaveaddr, ENS16x_REG_COMMAND, ENS16x_COMMAND_NOP);
|
||||
result = this->write8(_slaveaddr, ENS16x_REG_COMMAND, ENS16x_COMMAND_CLRGPR);
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.print("clearCommand() result: ");
|
||||
Serial.println(result == 0 ? "ok" : "nok");
|
||||
}
|
||||
#endif
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
status = this->read8(_slaveaddr, ENS16x_REG_DATA_STATUS);
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.print("clearCommand() status: 0x");
|
||||
Serial.println(status, HEX);
|
||||
}
|
||||
#endif
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
// Read firmware revisions
|
||||
bool ScioSense_ENS16x::getFirmware() {
|
||||
uint8_t i2cbuf[3];
|
||||
uint8_t result;
|
||||
|
||||
this->clearCommand();
|
||||
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
result = this->write8(_slaveaddr, ENS16x_REG_COMMAND, ENS16x_COMMAND_GET_APPVER);
|
||||
result = this->read(_slaveaddr, ENS16x_REG_GPR_READ_4, i2cbuf, 3);
|
||||
|
||||
this->_fw_ver_major = i2cbuf[0];
|
||||
this->_fw_ver_minor = i2cbuf[1];
|
||||
this->_fw_ver_build = i2cbuf[2];
|
||||
|
||||
if (this->_fw_ver_major > 6) this->_revENS16x = 1;
|
||||
else this->_revENS16x = 0;
|
||||
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.println(this->_fw_ver_major);
|
||||
Serial.println(this->_fw_ver_minor);
|
||||
Serial.println(this->_fw_ver_build);
|
||||
Serial.print("getFirmware() result: ");
|
||||
Serial.println(result == 0 ? "ok" : "nok");
|
||||
}
|
||||
#endif
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
// Set operation mode of sensor
|
||||
bool ScioSense_ENS16x::setMode(uint8_t mode) {
|
||||
uint8_t result;
|
||||
|
||||
//LP only valid for rev>0
|
||||
if ((mode == ENS161_OPMODE_LP) and (_revENS16x == 0)) result = 1;
|
||||
else result = this->write8(_slaveaddr, ENS16x_REG_OPMODE, mode);
|
||||
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
//if (debugENS16x) {
|
||||
Serial.print("setMode() activate result: ");
|
||||
Serial.println(result == 0 ? "ok" : "nok");
|
||||
//}
|
||||
#endif
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
#ifndef ENS16x_DISABLE_ENHANCED_FEATURES
|
||||
// Initialize definition of custom mode with <n> steps
|
||||
bool ScioSense_ENS16x::initCustomMode(uint16_t stepNum) {
|
||||
uint8_t result;
|
||||
|
||||
if (stepNum > 0) {
|
||||
this->_stepCount = stepNum;
|
||||
|
||||
result = this->setMode(ENS16x_OPMODE_IDLE);
|
||||
result = this->clearCommand();
|
||||
|
||||
result = this->write8(_slaveaddr, ENS16x_REG_COMMAND, ENS16x_COMMAND_SETSEQ);
|
||||
} else {
|
||||
result = 1;
|
||||
}
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef ENS16x_DISABLE_ENHANCED_FEATURES
|
||||
// Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate
|
||||
bool ScioSense_ENS16x::addCustomStep(uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3) {
|
||||
uint8_t seq_ack;
|
||||
uint8_t temp;
|
||||
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.print("setCustomMode() write step ");
|
||||
Serial.println(this->_stepCount);
|
||||
}
|
||||
#endif
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
temp = (uint8_t)(((time / 24)-1) << 6);
|
||||
if (measureHP0) temp = temp | 0x20;
|
||||
if (measureHP1) temp = temp | 0x10;
|
||||
if (measureHP2) temp = temp | 0x8;
|
||||
if (measureHP3) temp = temp | 0x4;
|
||||
this->write8(_slaveaddr, ENS16x_REG_GPR_WRITE_0, temp);
|
||||
|
||||
temp = (uint8_t)(((time / 24)-1) >> 2);
|
||||
this->write8(_slaveaddr, ENS16x_REG_GPR_WRITE_1, temp);
|
||||
|
||||
this->write8(_slaveaddr, ENS16x_REG_GPR_WRITE_2, (uint8_t)(tempHP0/2));
|
||||
this->write8(_slaveaddr, ENS16x_REG_GPR_WRITE_3, (uint8_t)(tempHP1/2));
|
||||
this->write8(_slaveaddr, ENS16x_REG_GPR_WRITE_4, (uint8_t)(tempHP2/2));
|
||||
this->write8(_slaveaddr, ENS16x_REG_GPR_WRITE_5, (uint8_t)(tempHP3/2));
|
||||
|
||||
this->write8(_slaveaddr, ENS16x_REG_GPR_WRITE_6, (uint8_t)(this->_stepCount - 1));
|
||||
|
||||
if (this->_stepCount == 1) {
|
||||
this->write8(_slaveaddr, ENS16x_REG_GPR_WRITE_7, 128);
|
||||
} else {
|
||||
this->write8(_slaveaddr, ENS16x_REG_GPR_WRITE_7, 0);
|
||||
}
|
||||
delay(ENS16x_BOOTING);
|
||||
|
||||
seq_ack = this->read8(_slaveaddr, ENS16x_REG_GPR_READ_7);
|
||||
delay(ENS16x_BOOTING); // Wait to boot after reset
|
||||
|
||||
if ((ENS16x_SEQ_ACK_COMPLETE | this->_stepCount) != seq_ack) {
|
||||
this->_stepCount = this->_stepCount - 1;
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// Perform prediction measurement and stores result in internal variables
|
||||
bool ScioSense_ENS16x::measure(bool waitForNew) {
|
||||
uint8_t i2cbuf[8];
|
||||
uint8_t status;
|
||||
bool newData = false;
|
||||
|
||||
// Set default status for early bail out
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) Serial.println("Start measurement");
|
||||
#endif
|
||||
|
||||
if (waitForNew) {
|
||||
do {
|
||||
delay(10);
|
||||
status = this->read8(_slaveaddr, ENS16x_REG_DATA_STATUS);
|
||||
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.print("Status: ");
|
||||
Serial.println(status);
|
||||
}
|
||||
#endif
|
||||
} while (!IS_NEWDAT(status));
|
||||
} else {
|
||||
status = this->read8(_slaveaddr, ENS16x_REG_DATA_STATUS);
|
||||
}
|
||||
|
||||
// Read predictions
|
||||
if (IS_NEWDAT(status)) {
|
||||
newData = true;
|
||||
this->read(_slaveaddr, ENS16x_REG_DATA_AQI, i2cbuf, 7);
|
||||
_data_aqi = i2cbuf[0];
|
||||
_data_tvoc = i2cbuf[1] | ((uint16_t)i2cbuf[2] << 8);
|
||||
_data_eco2 = i2cbuf[3] | ((uint16_t)i2cbuf[4] << 8);
|
||||
if (_revENS16x > 0) _data_aqis = ((uint16_t)i2cbuf[5]) | ((uint16_t)i2cbuf[6] << 8);
|
||||
else _data_aqis = 0;
|
||||
}
|
||||
|
||||
return newData;
|
||||
}
|
||||
|
||||
#ifndef ENS16x_DISABLE_ENHANCED_FEATURES
|
||||
// Perfrom raw measurement and stores result in internal variables
|
||||
bool ScioSense_ENS16x::measureRaw(bool waitForNew) {
|
||||
uint8_t i2cbuf[8];
|
||||
uint8_t status;
|
||||
bool newData = false;
|
||||
|
||||
// Set default status for early bail out
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) Serial.println("Start measurement");
|
||||
#endif
|
||||
|
||||
if (waitForNew) {
|
||||
do {
|
||||
delay(10);
|
||||
status = this->read8(_slaveaddr, ENS16x_REG_DATA_STATUS);
|
||||
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.print("Status: ");
|
||||
Serial.println(status);
|
||||
}
|
||||
#endif
|
||||
} while (!IS_NEWGPR(status));
|
||||
} else {
|
||||
status = this->read8(_slaveaddr, ENS16x_REG_DATA_STATUS);
|
||||
}
|
||||
|
||||
if (IS_NEWGPR(status)) {
|
||||
newData = true;
|
||||
|
||||
// Read raw resistance values
|
||||
this->read(_slaveaddr, ENS16x_REG_GPR_READ_0, i2cbuf, 8);
|
||||
_hp0_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8)));
|
||||
_hp1_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8)));
|
||||
_hp2_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8)));
|
||||
_hp3_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8)));
|
||||
|
||||
// Read baselines
|
||||
this->read(_slaveaddr, ENS16x_REG_DATA_BL, i2cbuf, 8);
|
||||
_hp0_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8)));
|
||||
_hp1_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8)));
|
||||
_hp2_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8)));
|
||||
_hp3_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8)));
|
||||
|
||||
this->read(_slaveaddr, ENS16x_REG_DATA_MISR, i2cbuf, 1);
|
||||
_misr = i2cbuf[0];
|
||||
}
|
||||
|
||||
return newData;
|
||||
}
|
||||
|
||||
// Writes t (degC) and h (%rh) to ENV_DATA. Returns false on I2C problems.
|
||||
bool ScioSense_ENS16x::set_envdata(float t, float h) {
|
||||
|
||||
uint16_t t_data = (uint16_t)((t + 273.15f) * 64.0f);
|
||||
|
||||
uint16_t rh_data = (uint16_t)(h * 512.0f);
|
||||
|
||||
return this->set_envdata210(t_data, rh_data);
|
||||
}
|
||||
|
||||
// Writes t and h (in ENS210 format) to ENV_DATA. Returns false on I2C problems.
|
||||
bool ScioSense_ENS16x::set_envdata210(uint16_t t, uint16_t h) {
|
||||
//uint16_t temp;
|
||||
uint8_t trh_in[4];
|
||||
|
||||
//temp = (uint16_t)((t + 273.15f) * 64.0f);
|
||||
trh_in[0] = t & 0xff;
|
||||
trh_in[1] = (t >> 8) & 0xff;
|
||||
|
||||
//temp = (uint16_t)(h * 512.0f);
|
||||
trh_in[2] = h & 0xff;
|
||||
trh_in[3] = (h >> 8) & 0xff;
|
||||
|
||||
uint8_t result = this->write(_slaveaddr, ENS16x_REG_TEMP_IN, trh_in, 4);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
void ScioSense_ENS16x::_i2c_init() {
|
||||
#ifndef ENS16x_DISABLE_ENHANCED_FEATURES
|
||||
if (this->_sdaPin != this->_sclPin)
|
||||
Wire.begin(this->_sdaPin, this->_sclPin);
|
||||
else
|
||||
#endif
|
||||
Wire.begin();
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
uint8_t ScioSense_ENS16x::read8(uint8_t addr, byte reg) {
|
||||
uint8_t ret;
|
||||
this->read(addr, reg, &ret, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t ScioSense_ENS16x::read(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t num) {
|
||||
uint8_t pos = 0;
|
||||
uint8_t result = 0;
|
||||
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.print("I2C read address: 0x");
|
||||
Serial.print(addr, HEX);
|
||||
Serial.print(" - register: 0x");
|
||||
Serial.println(reg, HEX);
|
||||
}
|
||||
#endif
|
||||
|
||||
//on arduino we need to read in 32 byte chunks
|
||||
while(pos < num){
|
||||
|
||||
uint8_t read_now = min((uint8_t)32, (uint8_t)(num - pos));
|
||||
Wire.beginTransmission((uint8_t)addr);
|
||||
|
||||
Wire.write((uint8_t)reg + pos);
|
||||
result = Wire.endTransmission();
|
||||
Wire.requestFrom((uint8_t)addr, read_now);
|
||||
|
||||
for(int i=0; i<read_now; i++){
|
||||
buf[pos] = Wire.read();
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
uint8_t ScioSense_ENS16x::write8(uint8_t addr, byte reg, byte value) {
|
||||
uint8_t result = this->write(addr, reg, &value, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t ScioSense_ENS16x::write(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t num) {
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
if (debugENS16x) {
|
||||
Serial.print("I2C write address: 0x");
|
||||
Serial.print(addr, HEX);
|
||||
Serial.print(" - register: 0x");
|
||||
Serial.print(reg, HEX);
|
||||
Serial.print(" - value:");
|
||||
for (int i = 0; i<num; i++) {
|
||||
Serial.print(" 0x");
|
||||
Serial.print(buf[i], HEX);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
|
||||
Wire.beginTransmission((uint8_t)addr);
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.write((uint8_t *)buf, num);
|
||||
uint8_t result = Wire.endTransmission();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
ScioSense_ENS16x.h - Library for the ENS160 & ENS161 sensor with I2C interface from ScioSense
|
||||
2023 Jul 03 v8 Christoph Friese Update to cover ENS160 and ENS161
|
||||
2023 Mar 23 v7 Christoph Friese Bugfix measurement routine, prepare next release
|
||||
2021 Nov 25 v6 Martin Herold Custom mode timing fixed
|
||||
2021 July 29 v5 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams
|
||||
2021 Feb 04 v4 Giuseppe de Pinto Custom mode fixed
|
||||
2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams
|
||||
2020 Feb 15 v2 Giuseppe Pasetti Corrected firmware flash option
|
||||
2019 May 05 v1 Christoph Friese Created
|
||||
|
||||
based on application note "ENS160 Software Integration.pdf" rev 0.01
|
||||
*/
|
||||
|
||||
#ifndef __SCIOSENSE_ENS16x_H_
|
||||
#define __SCIOSENSE_ENS16x_H_
|
||||
|
||||
#define ENS16x_DISABLE_DEBUG
|
||||
#define ENS16x_DISABLE_ENHANCED_FEATURES
|
||||
|
||||
#if (ARDUINO >= 100)
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
// Chip constants
|
||||
#define ENS160_PARTID 0x0160
|
||||
#define ENS161_PARTID 0x0161
|
||||
#define ENS16x_BOOTING 10
|
||||
|
||||
// 7-bit I2C slave address of the ENS16x
|
||||
#define ENS16x_I2CADDR_0 0x52 //ADDR low
|
||||
#define ENS16x_I2CADDR_1 0x53 //ADDR high
|
||||
|
||||
// ENS16x registers for version V0
|
||||
#define ENS16x_REG_PART_ID 0x00 // 2 byte register
|
||||
#define ENS16x_REG_OPMODE 0x10
|
||||
#define ENS16x_REG_CONFIG 0x11
|
||||
#define ENS16x_REG_COMMAND 0x12
|
||||
#define ENS16x_REG_TEMP_IN 0x13
|
||||
#define ENS16x_REG_RH_IN 0x15
|
||||
#define ENS16x_REG_DATA_STATUS 0x20
|
||||
#define ENS16x_REG_DATA_AQI 0x21
|
||||
#define ENS16x_REG_DATA_TVOC 0x22
|
||||
#define ENS16x_REG_DATA_ECO2 0x24
|
||||
#define ENS16x_REG_DATA_BL 0x28
|
||||
#define ENS16x_REG_DATA_T 0x30
|
||||
#define ENS16x_REG_DATA_RH 0x32
|
||||
#define ENS16x_REG_DATA_MISR 0x38
|
||||
#define ENS16x_REG_GPR_WRITE_0 0x40
|
||||
#define ENS16x_REG_GPR_WRITE_1 ENS16x_REG_GPR_WRITE_0 + 1
|
||||
#define ENS16x_REG_GPR_WRITE_2 ENS16x_REG_GPR_WRITE_0 + 2
|
||||
#define ENS16x_REG_GPR_WRITE_3 ENS16x_REG_GPR_WRITE_0 + 3
|
||||
#define ENS16x_REG_GPR_WRITE_4 ENS16x_REG_GPR_WRITE_0 + 4
|
||||
#define ENS16x_REG_GPR_WRITE_5 ENS16x_REG_GPR_WRITE_0 + 5
|
||||
#define ENS16x_REG_GPR_WRITE_6 ENS16x_REG_GPR_WRITE_0 + 6
|
||||
#define ENS16x_REG_GPR_WRITE_7 ENS16x_REG_GPR_WRITE_0 + 7
|
||||
#define ENS16x_REG_GPR_READ_0 0x48
|
||||
#define ENS16x_REG_GPR_READ_4 ENS16x_REG_GPR_READ_0 + 4
|
||||
#define ENS16x_REG_GPR_READ_6 ENS16x_REG_GPR_READ_0 + 6
|
||||
#define ENS16x_REG_GPR_READ_7 ENS16x_REG_GPR_READ_0 + 7
|
||||
|
||||
//ENS16x data register fields
|
||||
#define ENS16x_COMMAND_NOP 0x00
|
||||
#define ENS16x_COMMAND_CLRGPR 0xCC
|
||||
#define ENS16x_COMMAND_GET_APPVER 0x0E
|
||||
#define ENS16x_COMMAND_SETTH 0x02
|
||||
#define ENS16x_COMMAND_SETSEQ 0xC2
|
||||
|
||||
#define ENS16x_OPMODE_RESET 0xF0
|
||||
#define ENS16x_OPMODE_DEP_SLEEP 0x00
|
||||
#define ENS16x_OPMODE_IDLE 0x01
|
||||
#define ENS16x_OPMODE_STD 0x02
|
||||
#define ENS161_OPMODE_LP 0x03
|
||||
#define ENS16x_OPMODE_CUSTOM 0xC0
|
||||
|
||||
#define ENS16x_BL_CMD_START 0x02
|
||||
#define ENS16x_BL_CMD_ERASE_APP 0x04
|
||||
#define ENS16x_BL_CMD_ERASE_BLINE 0x06
|
||||
#define ENS16x_BL_CMD_WRITE 0x08
|
||||
#define ENS16x_BL_CMD_VERIFY 0x0A
|
||||
#define ENS16x_BL_CMD_GET_BLVER 0x0C
|
||||
#define ENS16x_BL_CMD_GET_APPVER 0x0E
|
||||
#define ENS16x_BL_CMD_EXITBL 0x12
|
||||
|
||||
#define ENS16x_SEQ_ACK_NOTCOMPLETE 0x80
|
||||
#define ENS16x_SEQ_ACK_COMPLETE 0xC0
|
||||
|
||||
#define IS_ENS16x_SEQ_ACK_NOT_COMPLETE(x) (ENS16x_SEQ_ACK_NOTCOMPLETE == (ENS16x_SEQ_ACK_NOTCOMPLETE & (x)))
|
||||
#define IS_ENS16x_SEQ_ACK_COMPLETE(x) (ENS16x_SEQ_ACK_COMPLETE == (ENS16x_SEQ_ACK_COMPLETE & (x)))
|
||||
|
||||
#define ENS16x_DATA_STATUS_NEWDAT 0x02
|
||||
#define ENS16x_DATA_STATUS_NEWGPR 0x01
|
||||
|
||||
#define IS_NEWDAT(x) (ENS16x_DATA_STATUS_NEWDAT == (ENS16x_DATA_STATUS_NEWDAT & (x)))
|
||||
#define IS_NEWGPR(x) (ENS16x_DATA_STATUS_NEWGPR == (ENS16x_DATA_STATUS_NEWGPR & (x)))
|
||||
#define IS_NEW_DATA_AVAILABLE(x) (0 != ((ENS16x_DATA_STATUS_NEWDAT | ENS16x_DATA_STATUS_NEWGPR ) & (x)))
|
||||
|
||||
#define CONVERT_RS_RAW2OHMS_I(x) (1 << ((x) >> 11))
|
||||
#define CONVERT_RS_RAW2OHMS_F(x) (pow (2, (float)(x) / 2048))
|
||||
|
||||
class ScioSense_ENS16x {
|
||||
|
||||
public:
|
||||
ScioSense_ENS16x(uint8_t slaveaddr = ENS16x_I2CADDR_0); // Constructor using slave address (5A or 5B)
|
||||
#ifndef ENS16x_DISABLE_ENHANCED_FEATURES
|
||||
ScioSense_ENS16x(uint8_t ADDR, uint8_t nCS, uint8_t nINT); // Constructor with pin definition
|
||||
ScioSense_ENS16x(uint8_t slaveaddr, uint8_t ADDR, uint8_t nCS, uint8_t nINT); // Constructor with slave address and pin definition
|
||||
#endif
|
||||
|
||||
void setI2C(uint8_t sda, uint8_t scl); // Function to redefine I2C pins
|
||||
|
||||
bool begin(bool debug=false); // Init I2C communication, resets ENS16x and checks its PART_ID. Returns false on I2C problems or wrong PART_ID.
|
||||
bool available() { return this->_available; } // Report availability of sensor
|
||||
uint8_t revENS16x() { return this->_revENS16x; } // Report version of sensor (0: ENS16x, 1: ENS161)
|
||||
bool setMode(uint8_t mode); // Set operation mode of sensor
|
||||
|
||||
#ifndef ENS16x_DISABLE_ENHANCED_FEATURES
|
||||
bool initCustomMode(uint16_t stepNum); // Initialize definition of custom mode with <n> steps
|
||||
bool addCustomStep(uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3);
|
||||
// Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate
|
||||
#endif
|
||||
|
||||
bool measure(bool waitForNew = true); // Perform measurement and stores result in internal variables
|
||||
#ifndef ENS16x_DISABLE_ENHANCED_FEATURES
|
||||
bool measureRaw(bool waitForNew = true); // Perform raw measurement and stores result in internal variables
|
||||
bool set_envdata(float t, float h); // Writes t (degC) and h (%rh) to ENV_DATA. Returns "0" if I2C transmission is successful
|
||||
bool set_envdata210(uint16_t t, uint16_t h); // Writes t and h (in ENS210 format) to ENV_DATA. Returns "0" if I2C transmission is successful
|
||||
#endif
|
||||
uint8_t getMajorRev() { return this->_fw_ver_major; } // Get major revision number of used firmware
|
||||
uint8_t getMinorRev() { return this->_fw_ver_minor; } // Get minor revision number of used firmware
|
||||
uint8_t getBuild() { return this->_fw_ver_build; } // Get build revision number of used firmware
|
||||
|
||||
uint8_t getAQI() { return this->_data_aqi; } // Get AQI value of last measurement
|
||||
uint16_t getTVOC() { return this->_data_tvoc; } // Get TVOC value of last measurement
|
||||
uint16_t geteCO2() { return this->_data_eco2; } // Get eCO2 value of last measurement
|
||||
uint16_t getAQIS() { return this->_data_aqis; } // Get AQI500 value of last measurement
|
||||
uint32_t getHP0() { return this->_hp0_rs; } // Get resistance of HP0 of last measurement
|
||||
uint32_t getHP1() { return this->_hp1_rs; } // Get resistance of HP1 of last measurement
|
||||
uint32_t getHP2() { return this->_hp2_rs; } // Get resistance of HP2 of last measurement
|
||||
uint32_t getHP3() { return this->_hp3_rs; } // Get resistance of HP3 of last measurement
|
||||
uint32_t getHP0BL() { return this->_hp0_bl; } // Get baseline resistance of HP0 of last measurement
|
||||
uint32_t getHP1BL() { return this->_hp1_bl; } // Get baseline resistance of HP1 of last measurement
|
||||
uint32_t getHP2BL() { return this->_hp2_bl; } // Get baseline resistance of HP2 of last measurement
|
||||
uint32_t getHP3BL() { return this->_hp3_bl; } // Get baseline resistance of HP3 of last measurement
|
||||
uint8_t getMISR() { return this->_misr; } // Return status code of sensor
|
||||
|
||||
private:
|
||||
uint8_t _ADDR;
|
||||
uint8_t _nINT;
|
||||
uint8_t _nCS;
|
||||
uint8_t _sdaPin = 0;
|
||||
uint8_t _sclPin = 0;
|
||||
|
||||
#ifndef ENS16x_DISABLE_DEBUG
|
||||
bool debugENS16x = false;
|
||||
#endif
|
||||
|
||||
bool reset(); // Sends a reset to the ENS16x. Returns false on I2C problems.
|
||||
bool checkPartID(); // Reads the part ID and confirms valid sensor
|
||||
bool clearCommand(); // Initialize idle mode and confirms
|
||||
bool getFirmware(); // Read firmware revisions
|
||||
|
||||
bool _available = false; // ENS16x available
|
||||
uint8_t _revENS16x = 0; // ENS160 or ENS161 connected? (FW >7)
|
||||
|
||||
uint8_t _fw_ver_major;
|
||||
uint8_t _fw_ver_minor;
|
||||
uint8_t _fw_ver_build;
|
||||
|
||||
uint16_t _stepCount; // Counter for custom sequence
|
||||
|
||||
uint8_t _data_aqi;
|
||||
uint16_t _data_tvoc;
|
||||
uint16_t _data_eco2;
|
||||
uint16_t _data_aqis;
|
||||
uint32_t _hp0_rs;
|
||||
uint32_t _hp0_bl;
|
||||
uint32_t _hp1_rs;
|
||||
uint32_t _hp1_bl;
|
||||
uint32_t _hp2_rs;
|
||||
uint32_t _hp2_bl;
|
||||
uint32_t _hp3_rs;
|
||||
uint32_t _hp3_bl;
|
||||
uint16_t _temp;
|
||||
int _slaveaddr;
|
||||
uint8_t _misr;
|
||||
|
||||
//Isotherm, HP0 252°C / HP1 350°C / HP2 250°C / HP3 324°C / measure every 1008ms
|
||||
uint8_t _seq_steps[1][8] = {
|
||||
{ 0x7C, 0x0A, 0x7E, 0xAF, 0xAF, 0xA2, 0x00, 0x80 },
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* General functions */
|
||||
/****************************************************************************/
|
||||
void _i2c_init();
|
||||
|
||||
uint8_t read8(uint8_t addr, byte reg);
|
||||
uint8_t read(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t num);
|
||||
|
||||
uint8_t write8(uint8_t addr, byte reg, byte value);
|
||||
uint8_t write(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t num);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,28 @@
|
|||
# ENS210
|
||||
Arduino library for the ENS210 temperature & humidity sensor with I2C interface from ScioSense
|
||||
|
||||
## Introduction
|
||||
This project is an Arduino *library*. It implements a driver with examples for the ENS210.
|
||||
The ENS210 chip is a digital temperature & humidity sensor with an I2C interface.
|
||||
The driver in this Arduino library is based on the code supplied by *Sciosense*, the manufacturer of the chip.
|
||||
|
||||
Note that the ENS210 requires a supply voltage of 1.71V .. 1.98V.
|
||||
|
||||
## Links
|
||||
The ENS210 is made by [Sciosense](http://www.sciosense.com).
|
||||
- The datasheet of the ENS210 is not yet released
|
||||
|
||||
## Prerequisites
|
||||
It is assumed that
|
||||
- The Arduino IDE has been installed.
|
||||
If not, refer to "Install the Arduino Desktop IDE" on the
|
||||
[Arduino site](https://www.arduino.cc/en/Guide/HomePage).
|
||||
- The library directory is at its default location.
|
||||
For me, Christoph, that is `C:\Users\christoph\Documents\Arduino\libraries`.
|
||||
|
||||
## Build an example
|
||||
To build an example sketch
|
||||
- (Re)start Arduino.
|
||||
- Open File > Example > Examples from Custom Libraries > ENS210 > ENS210basic.
|
||||
- Make sure Tools > Board lists the correct board.
|
||||
- Select Sketch > Verify/Compile.
|
|
@ -0,0 +1,53 @@
|
|||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
# https://spencer.bliven.us/index.php/2012/01/18/arduino-ide-keywords/
|
||||
# KEYWORD1 Classes, datatypes, and C++ keywords
|
||||
# KEYWORD2 Methods and functions
|
||||
# KEYWORD3 setup and loop functions, as well as the Serial keywords
|
||||
# LITERAL1 Constants
|
||||
# LITERAL2 Built-in variables (unused by default)
|
||||
|
||||
|
||||
#######################################
|
||||
# Classes, datatypes (KEYWORD1)
|
||||
#######################################
|
||||
ENS210 KEYWORD1
|
||||
ens210 KEYWORD1
|
||||
sciosense_ens210 KEYWORD1
|
||||
Sciosense_ens210 KEYWORD1
|
||||
ScioSense_ens210 KEYWORD1
|
||||
sciosense_ENS210 KEYWORD1
|
||||
Sciosense_ENS210 KEYWORD1
|
||||
ScioSense_ENS210 KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
setSingleMode KEYWORD2
|
||||
available KEYWORD2
|
||||
getPartID KEYWORD2
|
||||
getHighUID KEYWORD2
|
||||
measure KEYWORD2
|
||||
getTempKelvin KEYWORD2
|
||||
getTempCelsius KEYWORD2
|
||||
getTempFahrenheit KEYWORD2
|
||||
getHumidityPercent KEYWORD2
|
||||
getAbsoluteHumidityPercent KEYWORD2
|
||||
getStatusT KEYWORD2
|
||||
getDataT KEYWORD2
|
||||
getStatusH KEYWORD2
|
||||
getDataH KEYWORD2
|
||||
status_str KEYWORD2
|
||||
correction_set KEYWORD2
|
||||
correction_get KEYWORD2
|
||||
|
||||
######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
ENS210_STATUS_I2CERROR LITERAL1
|
||||
ENS210_STATUS_CRCERROR LITERAL1
|
||||
ENS210_STATUS_INVALID LITERAL1
|
||||
ENS210_STATUS_OK LITERAL1
|
|
@ -0,0 +1,9 @@
|
|||
name=ScioSense ENS210
|
||||
version=3.0.0
|
||||
author=Christoph Friese
|
||||
maintainer=Christoph Friese
|
||||
sentence=Arduino library for the ENS210 digital temperature & humidity sensor with I2C interface from ScioSense
|
||||
paragraph=This library controls the ENS210. The main feature of this library is performing a single shot measurement, retrieving the measurement data.
|
||||
category=Device Control
|
||||
url=https://github.com/sciosense/ens210
|
||||
architectures=*
|
|
@ -0,0 +1,648 @@
|
|||
/*
|
||||
ScioSense_ENS210.h - Library for the ENS210 relative humidity and temperature sensor with I2C interface from ScioSense
|
||||
2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams
|
||||
2018 Aug 28 v2 Christoph Friese Adjusted I2C communication
|
||||
2017 Aug 01 v1 Maarten Pennings Created
|
||||
*/
|
||||
|
||||
#include "ScioSense_ENS210.h"
|
||||
#include <assert.h>
|
||||
|
||||
// Compute the CRC-7 of 'val' (should only have 17 bits)
|
||||
// https://en.wikipedia.org/wiki/Cyclic_redundancy_check#Computation
|
||||
static uint32_t crc7( uint32_t val )
|
||||
{
|
||||
// Setup polynomial
|
||||
uint32_t pol= CRC7POLY;
|
||||
// Align polynomial with data
|
||||
pol = pol << (DATA7WIDTH-CRC7WIDTH-1);
|
||||
// Loop variable (indicates which bit to test, start with highest)
|
||||
uint32_t bit = DATA7MSB;
|
||||
// Make room for CRC value
|
||||
val = val << CRC7WIDTH;
|
||||
bit = bit << CRC7WIDTH;
|
||||
pol = pol << CRC7WIDTH;
|
||||
// Insert initial vector
|
||||
val |= CRC7IVEC;
|
||||
// Apply division until all bits done
|
||||
while( bit & (DATA7MASK<<CRC7WIDTH) ) {
|
||||
if( bit & val ) val ^= pol;
|
||||
bit >>= 1;
|
||||
pol >>= 1;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
ScioSense_ENS210::ScioSense_ENS210(uint8_t slaveaddr) {
|
||||
this->_slaveaddress = slaveaddr;
|
||||
}
|
||||
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
void ScioSense_ENS210::setI2C(uint8_t sda, uint8_t scl) {
|
||||
this->_sdaPin = sda;
|
||||
this->_sclPin = scl;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Init I2C communication, resets ENS210 and checks its PART_ID. Returns false on I2C problems or wrong PART_ID.
|
||||
// Stores solder correction.
|
||||
bool ScioSense_ENS210::begin(bool debug) {
|
||||
bool result;
|
||||
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
debugENS210 = debug;
|
||||
#endif
|
||||
|
||||
//init I2C
|
||||
_i2c_init();
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.println("ens210 debug - I2C init done");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Record solder correction
|
||||
this->_soldercorrection= 0;
|
||||
this->_available = false;
|
||||
this->_singleMode = true;
|
||||
this->_t_data = -1;
|
||||
this->_h_data = -1;
|
||||
this->_partID = 0;
|
||||
|
||||
this->lowpower(false);
|
||||
|
||||
result = this->getversion();
|
||||
|
||||
if(this->_partID == ENS210_PARTID ) {
|
||||
this->_available = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
void ScioSense_ENS210::changeAddr(uint8_t oldAddr, uint8_t newAddr) {
|
||||
uint8_t i2cbuf[2];
|
||||
|
||||
_i2c_init();
|
||||
|
||||
//Disable low power mode --> always on
|
||||
uint8_t result = this->write8(oldAddr, 0x10, 0x00);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Low Power off result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// read status register
|
||||
result = this->read(oldAddr, 0x11, i2cbuf, 1);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Read status result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
for (uint8_t i=0; i<1; i++) {
|
||||
Serial.print("\t0x");
|
||||
Serial.print(i2cbuf[i],HEX);
|
||||
}
|
||||
Serial.println();
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// enable low level access. PASSWORD_WRITE_ENABLE
|
||||
result = this->write8(oldAddr, 0x10, 0x30);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("PASSWORD_WRITE_ENABLE result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// read status register
|
||||
result = this->read(oldAddr, 0x11, i2cbuf, 1);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Read status result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
for (uint8_t i=0; i<1; i++) {
|
||||
Serial.print("\t0x");
|
||||
Serial.print(i2cbuf[i],HEX);
|
||||
}
|
||||
Serial.println();
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// low level access password. Password is 00 00 00 00
|
||||
uint8_t passwdCmd[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
result = this->write(oldAddr, 0x48, passwdCmd, 5);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Write password result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// read low-level mode sensor output data
|
||||
result = this->read(oldAddr, 0x46, i2cbuf, 2);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Read low level result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
for (uint8_t i=0; i<2; i++) {
|
||||
Serial.print("\t0x");
|
||||
Serial.print(i2cbuf[i],HEX);
|
||||
}
|
||||
Serial.println();
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// write values to parameter field and data1&2 field
|
||||
uint8_t newAddrCmd[5] = {0x00,0x00,0x22,0x00,0x41};
|
||||
newAddrCmd[4] = newAddr;
|
||||
result = this->write(oldAddr, 0x41, newAddrCmd, 5);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Write parameters result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// write SEN_CMD to execute low-level command specified, 0xCE means “APB_WRITE”
|
||||
result = this->write8(oldAddr, 0x40, 0xCE);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Execute result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// Read low-level mode sensor output data to check if programming succeeded. Expected response: 0x00 0xAC
|
||||
result = this->read(oldAddr, 0x46, i2cbuf, 2);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Read back result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
for (uint8_t i=0; i<2; i++) {
|
||||
Serial.print("\t0x");
|
||||
Serial.print(i2cbuf[i],HEX);
|
||||
}
|
||||
Serial.println();
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// reset ENS210
|
||||
result = this->write8(oldAddr, 0x10, 0xFF);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Reset result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// reset ENS210
|
||||
result = this->write8(oldAddr, 0x10, 0xFF);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Reset result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
#endif
|
||||
delay(100);
|
||||
|
||||
// reset ENS210
|
||||
result = this->write8(newAddr, 0x10, 0xFF);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
Serial.print("Reset result: 0x");
|
||||
Serial.println(result,HEX);
|
||||
#endif
|
||||
delay(100);
|
||||
}
|
||||
#endif //#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
|
||||
// Sends a reset to the ENS210. Returns false on I2C problems.
|
||||
bool ScioSense_ENS210::reset(void) {
|
||||
uint8_t result = this->write8(_slaveaddress, ENS210_REG_SYS_CTRL, 0x80);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("ens210 debug - reset: 0x");
|
||||
Serial.println(result,HEX);
|
||||
}
|
||||
#endif
|
||||
delay(ENS210_BOOTING); // Wait to boot after reset
|
||||
return result==0;
|
||||
}
|
||||
|
||||
|
||||
// Sets ENS210 to low (true) or high (false) power. Returns false on I2C problems.
|
||||
bool ScioSense_ENS210::lowpower(bool enable) {
|
||||
uint8_t power = enable ? 0x01: 0x00;
|
||||
uint8_t result = this->write8(this->_slaveaddress, ENS210_REG_SYS_CTRL, power);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("ens210 debug - lowpower: 0x");
|
||||
Serial.println(result,HEX);
|
||||
}
|
||||
#endif
|
||||
delay(ENS210_BOOTING); // Wait boot-time after power switch
|
||||
return result==0;
|
||||
}
|
||||
|
||||
|
||||
// Reads PART_ID and UID of ENS210. Returns false on I2C problems.
|
||||
bool ScioSense_ENS210::getversion() {
|
||||
bool ok;
|
||||
uint8_t i2cbuf[8];
|
||||
uint8_t result;
|
||||
|
||||
// Must disable low power to read PART_ID or UID
|
||||
ok= lowpower(false); if(!ok) goto errorexit;
|
||||
|
||||
// Read the PART_ID
|
||||
result = this->read(this->_slaveaddress, ENS210_REG_PART_ID, i2cbuf, 2);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("ens210 debug - PART_ID I2C result: 0x");
|
||||
Serial.println(result, HEX);
|
||||
}
|
||||
#endif
|
||||
//this->_partID = (uint16_t)(i2cbuf[1]*256U + i2cbuf[0]*1U);
|
||||
this->_partID = (uint16_t)(((uint16_t)i2cbuf[1] << 8) | ((uint16_t)i2cbuf[0]));
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("PART_ID: 0x");
|
||||
Serial.println(this->_partID,HEX);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Read the REV
|
||||
result = this->read(this->_slaveaddress, ENS210_REG_REV, i2cbuf, 2);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("ens210 debug - REV I2C result: 0x");
|
||||
Serial.println(result, HEX);
|
||||
}
|
||||
#endif
|
||||
this->_rev = (uint16_t)(((uint16_t)i2cbuf[1] << 8) | ((uint16_t)i2cbuf[0]));
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("REV: 0x");
|
||||
Serial.println(this->_rev,HEX);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Read the UID
|
||||
result = this->read(_slaveaddress, ENS210_REG_UID,i2cbuf,8);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("ens210 debug - UID 0x");
|
||||
Serial.println(result,HEX);
|
||||
}
|
||||
#endif
|
||||
this->_uID = (uint64_t)((uint64_t)i2cbuf[7]<<56 | (uint64_t)i2cbuf[6]<<48 | (uint64_t)i2cbuf[5]<<40 | (uint64_t)i2cbuf[4]<<32 | (uint64_t)i2cbuf[3]<<24 | (uint64_t)i2cbuf[2]<<16 | (uint64_t)i2cbuf[1]<<8 | (uint64_t)i2cbuf[0]);
|
||||
|
||||
//for( int i=0; i<8; i++) ((uint8_t*)this->_uID)[i]=i2cbuf[i]; //((uint8_t*)this->_uID)[i]=i2cbuf[i];
|
||||
this->_uIDhi = (uint32_t)(this->_uID >> 32);
|
||||
this->_uIDlo = (uint32_t)(this->_uID & 0xFFFFFFFF);
|
||||
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("UID hi 0x");
|
||||
Serial.print(this->_uIDhi,HEX);
|
||||
Serial.print(" - 0x");
|
||||
Serial.println(this->_uIDlo,HEX);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Go back to default power mode (low power enabled)
|
||||
ok= lowpower(true); if(!ok) goto errorexit;
|
||||
|
||||
// Success
|
||||
return true;
|
||||
|
||||
errorexit:
|
||||
// Try to go back to default mode (low power enabled)
|
||||
ok= lowpower(true);
|
||||
|
||||
// Hopefully enabling low power was successful; but there was an error before that anyhow
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configures ENS210 measurement mode
|
||||
// false for continuous mode / true for single shot measurement. Returns false on I2C problems.
|
||||
bool ScioSense_ENS210::setSingleMode(bool enable)
|
||||
{
|
||||
this->_singleMode = enable;
|
||||
uint8_t mode = enable ? 0x00: 0x03;
|
||||
uint8_t result = this->write8(_slaveaddress, ENS210_REG_SENS_RUN, mode);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("ens210 debug - start mode 0x");
|
||||
Serial.print(mode,HEX);
|
||||
Serial.print(" - result 0x");
|
||||
Serial.println(result,HEX);
|
||||
}
|
||||
#endif
|
||||
return result==0;
|
||||
}
|
||||
|
||||
// Performs one single shot temperature and relative humidity measurement.
|
||||
void ScioSense_ENS210::measure() //int * t_data, int * t_status, int * h_data, int * h_status )
|
||||
{
|
||||
bool ok;
|
||||
// Set default status for early bail out
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) Serial.println("Start measurement");
|
||||
#endif
|
||||
this->_t_status = ENS210_STATUS_I2CERROR;
|
||||
this->_h_status = ENS210_STATUS_I2CERROR;
|
||||
|
||||
// Start a single shot measurement
|
||||
if (this->_singleMode)
|
||||
{
|
||||
uint8_t result = this->write8(_slaveaddress, ENS210_REG_SENS_RUN, 0x00);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("ens210 debug - start single 0x");
|
||||
Serial.println(result,HEX);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//Trigger measurement
|
||||
uint8_t result = this->write8(_slaveaddress, ENS210_REG_SENS_START, 0x03);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("ens210 debug - trigger measurement 0x");
|
||||
Serial.println(result,HEX);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Wait for measurement to complete
|
||||
if (this->_singleMode) delay(ENS210_THCONV_SINGLE_MS);
|
||||
else delay(ENS210_THCONV_CONT_MS);
|
||||
|
||||
// Get the measurement data
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) Serial.println("Start measurement");
|
||||
#endif
|
||||
ok = readValue(); if(!ok) return;
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) Serial.println("Measurement ok");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Reads measurement data from the ENS210. Returns false on I2C problems.
|
||||
bool ScioSense_ENS210::readValue() //uint32_t *t_val, uint32_t *h_val)
|
||||
{
|
||||
uint8_t i2cbuf[6];
|
||||
uint8_t valid;
|
||||
uint32_t crc;
|
||||
uint32_t payload;
|
||||
uint8_t crc_ok;
|
||||
|
||||
// Read T_VAL and H_VAL
|
||||
uint8_t result = this->read(_slaveaddress, ENS210_REG_T_VAL, i2cbuf, 6);
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("ens210 debug - readValue:");
|
||||
Serial.println(result);
|
||||
Serial.print(i2cbuf[0],HEX); Serial.print("\t");
|
||||
Serial.print(i2cbuf[1],HEX); Serial.print("\t");
|
||||
Serial.print(i2cbuf[2],HEX); Serial.print("\t");
|
||||
Serial.print(i2cbuf[3],HEX); Serial.print("\t");
|
||||
Serial.print(i2cbuf[4],HEX); Serial.print("\t");
|
||||
Serial.print(i2cbuf[5],HEX); Serial.println("\t");
|
||||
}
|
||||
#endif
|
||||
// Retrieve and pack bytes into t_val and h_val
|
||||
uint32_t t_val= (uint32_t)((uint32_t)i2cbuf[2]<<16 | (uint32_t)i2cbuf[1]<<8 | (uint32_t)i2cbuf[0]);
|
||||
this->_t_data = (t_val>>0) & 0xffff;
|
||||
valid = (t_val>>16) & 0x1;
|
||||
crc = (t_val>>17) & 0x7f;
|
||||
payload = (t_val>>0 ) & 0x1ffff;
|
||||
crc_ok = crc7(payload)==crc;
|
||||
if( !crc_ok ) this->_t_status = ENS210_STATUS_CRCERROR;
|
||||
else if( !valid ) this->_t_status = ENS210_STATUS_INVALID;
|
||||
else this->_t_status = ENS210_STATUS_OK;
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("_t_data 0x");
|
||||
Serial.print(t_val,HEX);
|
||||
Serial.print(" - 0x");
|
||||
Serial.println(this->_t_data,HEX);
|
||||
Serial.print("Valid: ");
|
||||
Serial.print(valid);
|
||||
Serial.print(" Status: ");
|
||||
Serial.println(this->_t_status);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t h_val= (uint32_t)((uint32_t)i2cbuf[5]<<16 | (uint32_t)i2cbuf[4]<<8 | (uint32_t)i2cbuf[3]);
|
||||
this->_h_data = (h_val>>0) & 0xffff;
|
||||
|
||||
valid = (h_val>>16) & 0x1;
|
||||
crc = (h_val>>17) & 0x7f;
|
||||
payload = (h_val>>0 ) & 0x1ffff;
|
||||
crc_ok= crc7(payload)==crc;
|
||||
if( !crc_ok ) this->_h_status= ENS210_STATUS_CRCERROR;
|
||||
else if( !valid ) this->_h_status= ENS210_STATUS_INVALID;
|
||||
else this->_h_status= ENS210_STATUS_OK;
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("_h_data 0x");
|
||||
Serial.print(h_val,HEX);
|
||||
Serial.print(" - 0x");
|
||||
Serial.println(this->_h_data,HEX);
|
||||
Serial.print("Valid: ");
|
||||
Serial.print(valid);
|
||||
Serial.print(" Status: ");
|
||||
Serial.println(this->_h_status);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
// Converts a status (ENS210_STATUS_XXX) to a human readable string.
|
||||
const char * ScioSense_ENS210::status_str( int status )
|
||||
{
|
||||
switch( status ) {
|
||||
case ENS210_STATUS_I2CERROR : return "i2c-error";
|
||||
case ENS210_STATUS_CRCERROR : return "crc-error";
|
||||
case ENS210_STATUS_INVALID : return "data-invalid";
|
||||
case ENS210_STATUS_OK : return "ok";
|
||||
default : return "unknown-status";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
// Convert raw `t_data` temperature to Kelvin (also applies the solder correction).
|
||||
// The output value is in Kelvin multiplied by parameter `multiplier`.
|
||||
float ScioSense_ENS210::getTempKelvin()
|
||||
{
|
||||
// Force 32 bits
|
||||
float t = this->_t_data & 0xFFFF;
|
||||
// Compensate for soldering effect
|
||||
t-= _soldercorrection;
|
||||
// Return m*K. This equals m*(t/64) = (m*t)/64
|
||||
// Note m is the multiplier, K is temperature in Kelvin, t is raw t_data value.
|
||||
// Uses K=t/64.
|
||||
return t/64; //IDIV(t,64);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Convert raw `t_data` temperature to Celsius (also applies the solder correction).
|
||||
// The output value is in Celsius multiplied by parameter `multiplier`.
|
||||
float ScioSense_ENS210::getTempCelsius()
|
||||
{
|
||||
//assert( (1<=multiplier) && (multiplier<=1024) );
|
||||
// Force 32 bits
|
||||
float t= this->_t_data & 0xFFFF;
|
||||
// Compensate for soldering effect
|
||||
t-= _soldercorrection;
|
||||
// Return m*C. This equals m*(K-273.15) = m*K - 27315*m/100 = m*t/64 - 27315*m/100
|
||||
// Note m is the multiplier, C is temperature in Celsius, K is temperature in Kelvin, t is raw t_data value.
|
||||
// Uses C=K-273.15 and K=t/64.
|
||||
return t/64 - 27315L/100; //IDIV(t,64) - IDIV(27315L,100);
|
||||
}
|
||||
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
// Convert raw `t_data` temperature to Fahrenheit (also applies the solder correction).
|
||||
float ScioSense_ENS210::getTempFahrenheit()
|
||||
{
|
||||
float t= this->_t_data & 0xFFFF;
|
||||
// Compensate for soldering effect
|
||||
t-= _soldercorrection;
|
||||
// Return m*F. This equals m*(1.8*(K-273.15)+32) = m*(1.8*K-273.15*1.8+32) = 1.8*m*K-459.67*m = 9*m*K/5 - 45967*m/100 = 9*m*t/320 - 45967*m/100
|
||||
// Note m is the multiplier, F is temperature in Fahrenheit, K is temperature in Kelvin, t is raw t_data value.
|
||||
// Uses F=1.8*(K-273.15)+32 and K=t/64.
|
||||
return 9*t/320 - 45967/100; //IDIV(9*t,320) - IDIV(45967L,100);
|
||||
// The first multiplication stays below 32 bits (t:16, multiplier:11, 9:4)
|
||||
// The second multiplication stays below 32 bits (multiplier:10, 45967:16)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Convert raw `h_data` relative humidity to %RH.
|
||||
float ScioSense_ENS210::getHumidityPercent()
|
||||
{
|
||||
float h= this->_h_data & 0xFFFF;
|
||||
// Return m*H. This equals m*(h/512) = (m*h)/512
|
||||
// Note m is the multiplier, H is the relative humidity in %RH, h is raw h_data value.
|
||||
// Uses H=h/512.
|
||||
return h/512; //IDIV(h, 512);
|
||||
}
|
||||
|
||||
|
||||
// Convert raw `h_data` absolute humidity to %RH.
|
||||
#define MOLAR_MASS_OF_WATER 18.01534
|
||||
#define UNIVERSAL_GAS_CONSTANT 8.21447215
|
||||
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
float ScioSense_ENS210::getAbsoluteHumidityPercent()
|
||||
{
|
||||
//taken from https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
|
||||
//precision is about 0.1°C in range -30 to 35°C
|
||||
//August-Roche-Magnus 6.1094 exp(17.625 x T)/(T + 243.04)
|
||||
//Buck (1981) 6.1121 exp(17.502 x T)/(T + 240.97)
|
||||
//reference https://www.eas.ualberta.ca/jdwilson/EAS372_13/Vomel_CIRES_satvpformulae.html // Use Buck (1981)
|
||||
|
||||
return (6.1121 * pow(2.718281828,(17.67* this->getTempCelsius())/(this->getTempCelsius() + 243.5))* this->getHumidityPercent() *MOLAR_MASS_OF_WATER)/((273.15+ this->getTempCelsius() )*UNIVERSAL_GAS_CONSTANT);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
// Sets the solder correction (default is 50mK) - only used by the `toXxx` functions.
|
||||
void ScioSense_ENS210::correction_set(int correction)
|
||||
{
|
||||
assert( -1*64<correction && correction<+1*64 ); // A correction of more than 1 Kelvin does not make sense (but the 1K is arbitrary)
|
||||
this->_soldercorrection = correction;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* General functions */
|
||||
/****************************************************************************/
|
||||
|
||||
void ScioSense_ENS210::_i2c_init() {
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
if (this->_sdaPin != this->_sclPin)
|
||||
Wire.begin(this->_sdaPin, this->_sclPin);
|
||||
else
|
||||
#endif
|
||||
Wire.begin();
|
||||
}
|
||||
/**************************************************************************/
|
||||
|
||||
uint8_t ScioSense_ENS210::read8(uint8_t addr, byte reg)
|
||||
{
|
||||
uint8_t ret;
|
||||
this->read(addr, reg, &ret, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t ScioSense_ENS210::read(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t num)
|
||||
{
|
||||
uint8_t pos = 0;
|
||||
uint8_t result = 0;
|
||||
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("I2C read address: 0x");
|
||||
Serial.print(addr, HEX);
|
||||
Serial.print(" - register: 0x");
|
||||
Serial.println(reg, HEX);
|
||||
}
|
||||
#endif
|
||||
|
||||
//on arduino we need to read in 32 byte chunks
|
||||
while(pos < num){
|
||||
|
||||
uint8_t read_now = 32; //min((uint8_t)32, (uint8_t)(num - pos));
|
||||
Wire.beginTransmission((uint8_t)addr);
|
||||
|
||||
Wire.write((uint8_t)reg + pos);
|
||||
result = Wire.endTransmission();
|
||||
Wire.requestFrom((uint8_t)addr, read_now);
|
||||
|
||||
//for(int i=0; i<read_now; i++){
|
||||
for(int i=0; i<num; i++){
|
||||
buf[pos] = Wire.read();
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("Pos: ");
|
||||
Serial.print(pos);
|
||||
Serial.print(" - Value: 0x");
|
||||
Serial.println(buf[pos], HEX);
|
||||
}
|
||||
#endif
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
uint8_t ScioSense_ENS210::write8(uint8_t addr, byte reg, byte value)
|
||||
{
|
||||
uint8_t result = this->write(addr, reg, &value, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t ScioSense_ENS210::write(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t num)
|
||||
{
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
if (debugENS210) {
|
||||
Serial.print("I2C write address: 0x");
|
||||
Serial.print(addr, HEX);
|
||||
Serial.print(" - register: 0x");
|
||||
Serial.print(reg, HEX);
|
||||
Serial.print(" - value: 0x");
|
||||
for (int i = 0; i<num; i++) {
|
||||
Serial.print(" 0x");
|
||||
Serial.print(buf[i], HEX);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
|
||||
Wire.beginTransmission((uint8_t)addr);
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.write((uint8_t *)buf, num);
|
||||
uint8_t result = Wire.endTransmission();
|
||||
return result;
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
ScioSense_ENS210.h - Library for the ENS210 relative humidity and temperature sensor with I2C interface from ScioSense
|
||||
2020 Apr 06 v3 Chrsitoph Friese Changed nomenclature to ScioSense as product shifted from ams
|
||||
2018 Aug 28 v2 Christoph Friese Adjusted I2C communication
|
||||
2017 Aug 01 v1 Maarten Pennings Created
|
||||
*/
|
||||
|
||||
#ifndef __SCIOSENSE_ENS210_H_
|
||||
#define __SCIOSENSE_ENS210_H_
|
||||
|
||||
#define ENS210_DISABLE_DEBUG
|
||||
#define ENS210_DISABLE_ENHANCED_FEATURES
|
||||
|
||||
#if (ARDUINO >= 100)
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
#define ENS210_I2CADDR 0x43 //ADDR low
|
||||
|
||||
// Chip constants
|
||||
#define ENS210_PARTID 0x0210 // The expected part id of the ENS210
|
||||
#define ENS210_BOOTING 2 // Booting time in ms (also after reset, or going to high power)
|
||||
#define ENS210_THCONV_SINGLE_MS 130 // Conversion time in ms for single shot T/H measurement
|
||||
#define ENS210_THCONV_CONT_MS 238 // Conversion time in ms for continuous T/H measurement
|
||||
|
||||
// Addresses of the ENS210 registers
|
||||
#define ENS210_REG_PART_ID 0x00
|
||||
#define ENS210_REG_REV 0x02
|
||||
#define ENS210_REG_UID 0x04
|
||||
#define ENS210_REG_SYS_CTRL 0x10
|
||||
#define ENS210_REG_SYS_STAT 0x11
|
||||
#define ENS210_REG_SENS_RUN 0x21
|
||||
#define ENS210_REG_SENS_START 0x22
|
||||
#define ENS210_REG_SENS_STOP 0x23
|
||||
#define ENS210_REG_SENS_STAT 0x24
|
||||
#define ENS210_REG_T_VAL 0x30
|
||||
#define ENS210_REG_H_VAL 0x33
|
||||
|
||||
// Division macro (used in conversion functions), implementing integer division with rounding.
|
||||
// It supports both positive and negative dividends (n), but ONLY positive divisors (d).
|
||||
//#define IDIV(n,d) ((n)>0 ? ((n)+(d)/2)/(d) : ((n)-(d)/2)/(d))
|
||||
|
||||
// 7654 3210
|
||||
// Polynomial 0b 1000 1001 ~ x^7+x^3+x^0
|
||||
// 0x 8 9
|
||||
#define CRC7WIDTH 7 // A 7 bits CRC has polynomial of 7th order, which has 8 terms
|
||||
#define CRC7POLY 0x89 // The 8 coefficients of the polynomial
|
||||
#define CRC7IVEC 0x7F // Initial vector has all 7 bits high
|
||||
// Payload data
|
||||
#define DATA7WIDTH 17
|
||||
#define DATA7MASK ((1UL<<DATA7WIDTH)-1) // 0b 0 1111 1111 1111 1111
|
||||
#define DATA7MSB (1UL<<(DATA7WIDTH-1)) // 0b 1 0000 0000 0000 0000
|
||||
|
||||
// Measurement status as output by `measure()` and `extract()`.
|
||||
// Note that the ENS210 provides a "value" (`t_val` or `h_val` each 24 bit).
|
||||
// A "value" consists of a payload (17 bit) and a CRC (7 bit) over that payload.
|
||||
// The payload consists of a valid flag (1 bit) and the actual measurement "data" (`t_data` or `h_data`, 16 bit)
|
||||
#define ENS210_STATUS_I2CERROR 4 // There was an I2C communication error, `read`ing the value.
|
||||
#define ENS210_STATUS_CRCERROR 3 // The value was read, but the CRC over the payload (valid and data) does not match.
|
||||
#define ENS210_STATUS_INVALID 2 // The value was read, the CRC matches, but the data is invalid (e.g. the measurement was not yet finished).
|
||||
#define ENS210_STATUS_OK 1 // The value was read, the CRC matches, and data is valid.
|
||||
|
||||
class ScioSense_ENS210 {
|
||||
|
||||
public:
|
||||
ScioSense_ENS210(uint8_t slaveaddr = ENS210_I2CADDR);
|
||||
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
void setI2C(uint8_t sda, uint8_t scl);
|
||||
void changeAddr(uint8_t oldAddr, uint8_t newAddr);
|
||||
#endif
|
||||
|
||||
bool begin(bool debug=false); // init i2c interface, get partID und uID. Returns false on I2C problems or wrong PART_ID.
|
||||
bool setSingleMode(bool enable); // false for continuous mode / true for single shot measurement. Returns false on I2C problems.
|
||||
|
||||
bool available() { return this->_available; }
|
||||
uint16_t getPartID() { return this->_partID; }
|
||||
uint16_t getRev() { return this->_rev; }
|
||||
uint32_t getHighUID(bool high) { uint32_t result = high ? this->_uIDhi : this->_uIDlo; return result; }
|
||||
void measure(); // perfrom measurement and stores result in internal variables
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
float getTempKelvin (); // Converts and returns data (from `measure`) in Kelvin
|
||||
float getTempFahrenheit (); // Converts and returns data (from `measure`) in Fahrenheit
|
||||
#endif
|
||||
float getTempCelsius (); // Converts and returns data (from `measure`) in Celsius
|
||||
float getHumidityPercent(); // Converts and returns data (from `measure`) in %RH
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
float getAbsoluteHumidityPercent(); // Converts and returns data (from `measure`) in %aH
|
||||
#endif
|
||||
char getStatusT() { return this->_t_status; } // Converts a status (ENS210_STATUS_XXX) to a human readable string.
|
||||
uint32_t getDataT() { return this->_t_data; }
|
||||
char getStatusH() { return this->_h_status; } // Converts a status (ENS210_STATUS_XXX) to a human readable string.
|
||||
uint32_t getDataH() { return this->_h_data; }
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
static const char * status_str( int status ); // Converts a status (ENS210_STATUS_XXX) to a human readable string.
|
||||
#endif
|
||||
|
||||
// Optionally set a solder `correction` (units: 1/64K, default from `begin` is 0).
|
||||
// See "Effect of Soldering on Temperature Readout" in "Design-Guidelines" from
|
||||
// https://download.ams.com/ENVIRONMENTAL-SENSORS/ENS210/Documentation
|
||||
#ifndef ENS210_DISABLE_ENHANCED_FEATURES
|
||||
void correction_set(int correction=50*64/1000); // Sets the solder correction (default is 50mK) - only used by the `toXxx()` functions.
|
||||
int correction_get() {return this->_soldercorrection;} // Gets the solder correction.
|
||||
#endif
|
||||
|
||||
private:
|
||||
#ifndef ENS210_DISABLE_DEBUG
|
||||
bool debugENS210 = false;
|
||||
#endif
|
||||
bool reset(void); // Sends a reset to the ENS210. Returns false on I2C problems.
|
||||
bool lowpower(bool enable); // Sets ENS210 to low (true) or high (false) power. Returns false on I2C problems.
|
||||
bool getversion(); // Reads PART_ID and UID of ENS210. Returns false on I2C problems.
|
||||
bool readValue(); // Reads measurement data from the ENS210. Returns false on I2C problems.
|
||||
|
||||
bool _available = false; // ENS210 available
|
||||
uint16_t _partID; // Part ID of ENS210, should be 0x210
|
||||
uint16_t _rev; // Revision 0 MRA2.6 / 1 MRA2.12
|
||||
uint64_t _uID; // Unique ID of this specific ENS210
|
||||
uint32_t _uIDhi; // First 32bit of unique ID of this specific ENS210
|
||||
uint32_t _uIDlo; // Second 32bit of unique ID of this specific ENS210
|
||||
bool _singleMode = true; // Measurement mode: true single shot / false continuous
|
||||
uint32_t _t_data;
|
||||
uint8_t _t_status;
|
||||
uint32_t _h_data;
|
||||
uint8_t _h_status;
|
||||
uint8_t _slaveaddress = 0x43; // Slave address of ENS210
|
||||
uint8_t _soldercorrection; // Correction due to soldering (in 1/64K); subtracted from `t_data` by conversion functions.
|
||||
|
||||
uint8_t _sdaPin = 0;
|
||||
uint8_t _sclPin = 0;
|
||||
|
||||
/****************************************************************************/
|
||||
/* General functions */
|
||||
/****************************************************************************/
|
||||
|
||||
uint8_t write8(uint8_t addr, byte reg, byte value);
|
||||
uint8_t read8(uint8_t addr, byte reg);
|
||||
|
||||
uint8_t read(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t num);
|
||||
uint8_t write(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t num);
|
||||
void _i2c_init();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -111,6 +111,8 @@
|
|||
//#define USE_MPR121 // [I2cDriver23] Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code)
|
||||
#define USE_CCS811 // [I2cDriver24] Enable CCS811 sensor (I2C address 0x5A) (+2k2 code)
|
||||
//#define USE_CCS811_V2 // [I2cDriver24] Enable CCS811 sensor (I2C addresses 0x5A and 0x5B) (+2k8 code)
|
||||
//#define USE_ENS16x // [I2cDriver85] Enable ENS160 or ENS161 sensor (I2C addresses 0x52 and 0x53) (+3k1 of code and 524 of RAM)
|
||||
//#define USE_ENS210 // [I2cDriver86] Enable ENS210 sensor (I2C addresses 0x43 and 0x44) (+4k0 of code and 944 of RAM)
|
||||
//#define USE_MPU6050 // [I2cDriver25] Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+3K3 of code and 188 Bytes of RAM)
|
||||
//#define USE_MGC3130 // [I2cDriver27] Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem)
|
||||
//#define USE_MAX44009 // [I2cDriver28] Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code)
|
||||
|
|
|
@ -451,6 +451,8 @@
|
|||
//#define USE_MPR121 // [I2cDriver23] Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code)
|
||||
//#define USE_CCS811 // [I2cDriver24] Enable CCS811 sensor (I2C address 0x5A) (+2k2 code)
|
||||
//#define USE_CCS811_V2 // [I2cDriver24] Enable CCS811 sensor (I2C addresses 0x5A and 0x5B) (+2k8 code)
|
||||
//#define USE_ENS16x // [I2cDriver85] Enable ENS160 and ENS161 sensor (I2C addresses 0x52 and 0x53) (+3k1 of code and 524 of RAM)
|
||||
//#define USE_ENS210 // [I2cDriver86] Enable ENS210 sensor (I2C addresses 0x43 and 0x44) (+4k0 of code and 944 of RAM)
|
||||
//#define USE_MPU6050 // [I2cDriver25] Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+3K3 of code and 188 Bytes of RAM)
|
||||
//#define USE_MGC3130 // [I2cDriver27] Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem)
|
||||
//#define USE_MAX44009 // [I2cDriver28] Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code)
|
||||
|
@ -686,6 +688,8 @@
|
|||
//#define USE_MPR121 // [I2cDriver23] Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code)
|
||||
//#define USE_CCS811 // [I2cDriver24] Enable CCS811 sensor (I2C address 0x5A) (+2k2 code)
|
||||
#define USE_CCS811_V2 // [I2cDriver24] Enable CCS811 sensor (I2C addresses 0x5A and 0x5B) (+2k8 code)
|
||||
//#define USE_ENS16x // [I2cDriver85] Enable ENS160 and ENS161 sensor (I2C addresses 0x52 and 0x53) (+3k1 of code and 524 of RAM)
|
||||
//#define USE_ENS210 // [I2cDriver86] Enable ENS210 sensor (I2C addresses 0x43 and 0x44) (+4k0 of code and 944 of RAM)
|
||||
#define USE_MPU_ACCEL // [I2cDriver58] Enable MPU6886, MPU9250 6-axis MotionTracking sensor (I2C address 0x68)
|
||||
//#define USE_MPU6050 // [I2cDriver25] Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+3K3 of code and 188 Bytes of RAM)
|
||||
//#define USE_MGC3130 // [I2cDriver27] Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem)
|
||||
|
|
|
@ -628,6 +628,8 @@
|
|||
// #define USE_MPR121 // [I2cDriver23] Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code)
|
||||
// #define USE_CCS811 // [I2cDriver24] Enable CCS811 sensor (I2C address 0x5A) (+2k2 code)
|
||||
// #define USE_CCS811_V2 // [I2cDriver24] Enable CCS811 sensor (I2C addresses 0x5A and 0x5B) (+2k8 code)
|
||||
// #define USE_ENS16x // [I2cDriver85] Enable ENS160 and ENS161 sensor (I2C addresses 0x52 and 0x53) (+1.9kB of code and 12B of RAM)
|
||||
// #define USE_ENS210 // [I2cDriver86] Enable ENS210 sensor (I2C addresses 0x43) (+1.7kB of code and 12B of RAM)
|
||||
// #define USE_MPU6050 // [I2cDriver25] Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+3K3 of code and 188 Bytes of RAM)
|
||||
// #define USE_MPU6050_DMP // Enable in MPU6050 to use the DMP on the chip, should create better results (+8k6 of code)
|
||||
// #define USE_MGC3130 // [I2cDriver27] Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem)
|
||||
|
|
|
@ -903,8 +903,12 @@ void ResponseAppendFeatures(void)
|
|||
#if defined(USE_I2C) && defined(USE_MAX17043)
|
||||
feature9 |= 0x02000000;
|
||||
#endif
|
||||
// feature9 |= 0x04000000;
|
||||
// feature9 |= 0x08000000;
|
||||
#if defined(USE_I2C) && defined(USE_ENS16x)
|
||||
feature9 |= 0x04000000; //xsns_111_ens16x.ino
|
||||
#endif
|
||||
#if defined(USE_I2C) && defined(USE_ENS210)
|
||||
feature9 |= 0x08000000; //xsns_112_ens210.ino
|
||||
#endif
|
||||
|
||||
// feature9 |= 0x10000000;
|
||||
// feature9 |= 0x20000000;
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
xsns_98_ENS16x.ino - ENS16x gas and air quality sensor support for Tasmota
|
||||
|
||||
Copyright (C) 2021 Christoph Friese and Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_I2C
|
||||
#ifdef USE_ENS16x
|
||||
|
||||
/*********************************************************************************************\
|
||||
* ENS16x - Gas (TVOC - Total Volatile Organic Compounds) and Air Quality (CO2)
|
||||
*
|
||||
* Source: ScioSense
|
||||
*
|
||||
* I2C Address: 0x52
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_111 111
|
||||
#define XI2C_84 84 // See I2CDEVICES.md
|
||||
|
||||
#define ENS16x_EVERYNSECONDS 5
|
||||
#define ENS16x_DEVICE_NAME "EN16X"
|
||||
#define ENS16x_LOG "E16"
|
||||
#define ENS16x_MAX_COUNT 2
|
||||
|
||||
#include "ScioSense_ENS16x.h"
|
||||
|
||||
typedef struct ENS16xData_s {
|
||||
ScioSense_ENS16x *ens16x;
|
||||
uint16_t TVOC;
|
||||
uint16_t eCO2;
|
||||
uint16_t AQIS;
|
||||
|
||||
uint8_t ready;
|
||||
uint8_t tcnt;
|
||||
uint8_t ecnt;
|
||||
} ENS16xDATA_t;
|
||||
|
||||
ENS16xDATA_t *ENS16xData[ENS16x_MAX_COUNT] = {nullptr, };
|
||||
uint8_t ENS16xCount = 0;
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void ens16xDetect(void)
|
||||
{
|
||||
ENS16xCount = 0;
|
||||
ENS16xDATA_t *pENS16X;
|
||||
ScioSense_ENS16x *ens16x;
|
||||
uint8_t i2caddr = ENS16x_I2CADDR_0;
|
||||
for (uint8_t i = 0 ; i < ENS16x_MAX_COUNT; i++, i2caddr++) {
|
||||
if (!I2cActive(i2caddr)) {
|
||||
ens16x = new ScioSense_ENS16x(i2caddr);
|
||||
if (!ens16x) {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR(ENS16x_LOG":@%02X create error!"), i2caddr);
|
||||
continue;
|
||||
}
|
||||
if (!(ens16x->begin())) {
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(ENS16x_LOG":@%02X begin error!"), i2caddr);
|
||||
continue;
|
||||
}
|
||||
if (!(ens16x->setMode(ENS16x_OPMODE_STD))) {
|
||||
AddLog(LOG_LEVEL_ERROR, PSTR(ENS16x_LOG":@%02X setmode error!"), i2caddr);
|
||||
continue;
|
||||
}
|
||||
pENS16X = (ENS16xDATA_t*)calloc(1, sizeof(ENS16xDATA_t));
|
||||
if (!pENS16X) {
|
||||
AddLog(LOG_LEVEL_ERROR, PSTR(ENS16x_LOG":@%02X Memory error!"), i2caddr);
|
||||
continue;
|
||||
}
|
||||
pENS16X->ens16x = ens16x;
|
||||
ENS16xData[ENS16xCount] = pENS16X;
|
||||
ENS16xCount++;
|
||||
I2cSetActiveFound(i2caddr, ENS16x_DEVICE_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ens16xUpdate(void) // Perform every n second
|
||||
{
|
||||
for (uint8_t i = 0 ; i < ENS16xCount; i++) {
|
||||
ENS16xDATA_t *pENS16X = ENS16xData[i];
|
||||
pENS16X->tcnt++;
|
||||
if (pENS16X->tcnt >= ENS16x_EVERYNSECONDS) {
|
||||
pENS16X->tcnt = 0;
|
||||
pENS16X->ready = 0;
|
||||
if (pENS16X->ens16x->available()) {
|
||||
pENS16X->ens16x->measure();
|
||||
pENS16X->TVOC = pENS16X->ens16x->getTVOC();
|
||||
pENS16X->eCO2 = pENS16X->ens16x->geteCO2();
|
||||
if (pENS16X->ens16x->revENS16x() > 0) pENS16X->AQIS = pENS16X->ens16x->getAQIS();
|
||||
else pENS16X->AQIS = pENS16X->ens16x->getAQI();
|
||||
|
||||
pENS16X->ready = 1;
|
||||
pENS16X->ecnt = 0;
|
||||
}
|
||||
} else {
|
||||
// failed, count up
|
||||
pENS16X->ecnt++;
|
||||
if (pENS16X->ecnt > 6) {
|
||||
// after 30 seconds, restart
|
||||
pENS16X->ens16x->begin();
|
||||
if (pENS16X->ens16x->available()) {
|
||||
pENS16X->ens16x->setMode(ENS16x_OPMODE_STD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char HTTP_SNS_ENS16x[] PROGMEM =
|
||||
"{s}%s AQI{m}%d {e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
"{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"
|
||||
"{s}%s " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}";
|
||||
|
||||
void ens16xShow(bool json)
|
||||
{
|
||||
char name[20];
|
||||
for (uint8_t i = 0 ; i < ENS16xCount; i++) {
|
||||
ENS16xDATA_t *pENS16X = ENS16xData[i];
|
||||
if (pENS16X->ready) {
|
||||
snprintf_P(name, sizeof(name), (ENS16xCount > 1) ? PSTR("%s%c%d") : PSTR("%s"), ENS16x_DEVICE_NAME, IndexSeparator(), i +1);
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"%s\":{\"AQIS\":%d,\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d}"), name, pENS16X->AQIS, pENS16X->eCO2, pENS16X->TVOC);
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_ENS16x, name, pENS16X->AQIS, name, pENS16X->eCO2, name, pENS16X->TVOC);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns111(uint32_t function)
|
||||
{
|
||||
if (!I2cEnabled(XI2C_84)) { return false; }
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (FUNC_INIT == function) {
|
||||
ens16xDetect();
|
||||
}
|
||||
else if (ENS16xCount) {
|
||||
switch (function) {
|
||||
case FUNC_EVERY_SECOND:
|
||||
ens16xUpdate();
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
ens16xShow(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
ens16xShow(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_ENS16x
|
||||
#endif // USE_I2C
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
xsns_99_ens210.ino - ENS210 gas and air quality sensor support for Tasmota
|
||||
|
||||
Copyright (C) 2021 Christoph Friese and Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_I2C
|
||||
#ifdef USE_ENS210
|
||||
|
||||
/*********************************************************************************************\
|
||||
* ENS210 - Temperature & Humidity sensor
|
||||
*
|
||||
* Source: ScioSense
|
||||
*
|
||||
* I2C Address: 0x43
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_112 112
|
||||
#define XI2C_85 85 // See I2CDEVICES.md
|
||||
|
||||
#define ENS210_EVERYNSECONDS 5
|
||||
#define ENS210_DEVICE_NAME "EN210"
|
||||
#define ENS210_LOG "E21"
|
||||
|
||||
#include "ScioSense_ENS210.h"
|
||||
|
||||
typedef struct ENS210DATA_s {
|
||||
ScioSense_ENS210 *ens210;
|
||||
|
||||
float temperature;
|
||||
float humidity;
|
||||
|
||||
uint8_t ready;
|
||||
uint8_t tcnt;
|
||||
uint8_t ecnt;
|
||||
} ENS210DATA_t;
|
||||
|
||||
ENS210DATA_t *ENS210data = nullptr;
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void ens210Detect(void)
|
||||
{
|
||||
uint8_t i2caddr = ENS210_I2CADDR;
|
||||
if (!I2cActive(i2caddr))
|
||||
{
|
||||
ScioSense_ENS210 *ens210 = new ScioSense_ENS210(i2caddr);
|
||||
if (!ens210) {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR(ENS210_LOG ":@%02X create error!"), i2caddr);
|
||||
return;
|
||||
}
|
||||
if (!ens210->begin()) {
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(ENS210_LOG ":@%02X begin error!"), i2caddr);
|
||||
return;
|
||||
}
|
||||
if (!ens210->available()) {
|
||||
AddLog(LOG_LEVEL_ERROR, PSTR(ENS210_LOG ":@%02X available error!"), i2caddr);
|
||||
return;
|
||||
}
|
||||
ens210->setSingleMode(false);
|
||||
ENS210data = (ENS210DATA_t *)calloc(1, sizeof(ENS210DATA_t));
|
||||
if (!ENS210data) {
|
||||
AddLog(LOG_LEVEL_ERROR, PSTR(ENS210_LOG ":@%02X Memory error!"), i2caddr);
|
||||
return;
|
||||
}
|
||||
ENS210data->ens210 = ens210;
|
||||
I2cSetActiveFound(i2caddr, ENS210_DEVICE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
void ens210Update(void) // Perform every n second
|
||||
{
|
||||
ENS210data->tcnt++;
|
||||
if (ENS210data->tcnt >= ENS210_EVERYNSECONDS) {
|
||||
ENS210data->tcnt = 0;
|
||||
ENS210data->ready = 0;
|
||||
if (ENS210data->ens210->available()) {
|
||||
ENS210data->ens210->measure();
|
||||
|
||||
ENS210data->temperature = ENS210data->ens210->getTempCelsius();
|
||||
ENS210data->humidity = ENS210data->ens210->getHumidityPercent();
|
||||
ENS210data->ready = 1;
|
||||
ENS210data->ecnt = 0;
|
||||
} else {
|
||||
// failed, count up
|
||||
ENS210data->ecnt++;
|
||||
if (ENS210data->ecnt > 6) {
|
||||
// after 30 seconds, restart
|
||||
ENS210data->ens210->begin();
|
||||
if (ENS210data->ens210->available()) {
|
||||
ENS210data->ens210->setSingleMode(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ens210Show(bool json)
|
||||
{
|
||||
if (ENS210data->ready) {
|
||||
TempHumDewShow(json, (0 == TasmotaGlobal.tele_period), ENS210_DEVICE_NAME, ENS210data->temperature, ENS210data->humidity);
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns112(uint32_t function)
|
||||
{
|
||||
if (!I2cEnabled(XI2C_85)) { return false; }
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (FUNC_INIT == function) {
|
||||
ens210Detect();
|
||||
}
|
||||
else if (ENS210data) {
|
||||
switch (function) {
|
||||
case FUNC_EVERY_SECOND:
|
||||
ens210Update();
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
ens210Show(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
ens210Show(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_ENS210
|
||||
#endif // USE_I2C
|
|
@ -299,7 +299,7 @@ a_features = [[
|
|||
"USE_DISPLAY_TM1650","USE_PCA9632","USE_TUYAMCUBR","USE_SEN5X",
|
||||
"USE_BIOPDU","USE_MCP23XXX_DRV","USE_PMSA003I","USE_LOX_O2",
|
||||
"USE_GDK101","USE_GM861","USE_TC74","USE_PCA9557",
|
||||
"USE_SGP4X","USE_MAX17043","","",
|
||||
"USE_SGP4X","USE_MAX17043","USE_ENS16x","USE_ENS210",
|
||||
"","","",""
|
||||
]]
|
||||
|
||||
|
|
Loading…
Reference in New Issue